Google

星期日, 十一月 25, 2007

斯柯达明锐2.0

标志307


看看TAXI系列电影,就知道2厢版的307比国产化的好看太多了。

这车如何?FOCUS ST 2.5T

FOCUS旅行版


这款车没有2厢版好看吧。

星期五, 十一月 23, 2007

用UpdateResource修改EXE文件图标的多源码(已修正)

注:转帖请包函作者信息.(作者:菜新)

鄙视下百度空间,他姥姥...竟然限量字符40000字节!靠....

一年前初学VB时我对这个API就特感兴趣,听说这个API可以更改图标资源,就更感兴趣了,后来试了试,发现修改其它资源貌似没多大问题,唯独修改图标时无果,我发现所修改的图虽说已经写入到资源文件中了,但是就是无法显示。后来到网上查了下,发现用UpdateResource修改EXE图标的没一个成功的,大致都是发生成功写入,无法正常显示的问题。罢矣,当时就琢磨着把该问题先放放,等日后有时间再好好折腾。

无奈时间过得太快,忽忽悠悠就过了一年了,前几天,在整理去年的一些源码时发现了这个遗留在硬盘中的代码,一年前无奈自己所学浅溥,啥都不知道,但现在已经对API有了较深厚的认识,再加上对汇编的一些了解,我想此时不解决更待何时。

在折腾这个API的期间也发生不少问题,最让我自责的就是差点被 CreateFile 这个API给Game Over,这个小伟知道(又是小伟?没办法啊,谁要咱和小伟太有缘了~)。还好自己最终醒悟,否则真的要好好鄙视鄙视自己。最初修改时还是和一年前一个样,这时我一直在回想一样年遇到这个问题的问题:所写图标的数据是不是完整的写到了资源文件中?想到此,我用eXeScope(一个PE资源文件查看工具)看了下写入到资源文件中的十六进制,又用UltraEdit-32以十六进制查看ico文件中的数据,发现没问题啊?一字节一字节都对得上,那问题出在哪了?没法,继续在Google游荡,终于找了一份有效的资料(网址现在不知扔哪去了),全E文,看得难受,不过大致的意思是说ICON是由一个结构组成,同PE那些什么NT头,DOS头的差不多,而所显示的图像数据包函于ICON类型结构的dwImageOffset偏移处。呵,这下总算搞明白为什么直接把ICON文件写入到资源文件中显示不了的问题了,也就是说在dwImageOffset偏移位置处才是咱所需要的图像数据,这不就啥都OK了么,爷爷的,原来咱从一开始就被ICON文件整得稀里糊涂,靠MS,当然也鄙视下自己的无知。另外还好找到的那份资料有点人性,把结构给咱标出来了,那么现在一切都顺理成章,不说多了,上代码:

===============================================

Delphi Code:

===============================================

//请自行添加到 Type 处
PICONDIRENTRY = ^ICONDIRENTRY;
ICONDIRENTRY = packed record
bWidth: Byte;
bHeight: Byte;
bColorCount: Byte;
bReserved: Byte;
wPlanes: Word;
wBitCount: Word;
dwBytesInRes: DWORD;
dwImageOffset: DWORD;
end;

PICONDIR = ^ICONDIR;
ICONDIR = packed record
idReserved: Word;
idType: Word;
idCount: Word;
idEntries: ICONDIRENTRY;
end;

PGRPICONDIRENTRY = ^GRPICONDIRENTRY;
GRPICONDIRENTRY = packed record
bWidth: Byte;
bHeight: Byte;
bColorCount: Byte;
bReserved: Byte;
wPlanes: Word;
wBitCount: Word;
dwBytesInRes: DWORD;
nID: Word;
end;

PGRPICONDIR = ^GRPICONDIR;
GRPICONDIR = packed record
idReserved: Word;
idType: Word;
idCount: Word;
idEntries: GRPICONDIRENTRY;
end;

//////////////////////////////////////////////
//函数说明:修改EXE图标
//
//参 数:IconFile 图标文件
// ExeFile 被修改的EXE文件
//
//返回值: 成功为True,否则False
/////////////////////////////////////////////
function ChangeExeIcon(IcoFile, ExeFile: string): Boolean;
var
stID: ICONDIR;
stGID: GRPICONDIR;

pGrpIcon: PBYTE;
pIcon: PBYTE;
hUpdate: DWORD;
nSize, nGSize: DWORD;
hFile: THandle;
dwReserved: DWORD;
ret: Boolean;
begin
Result := False;

hFile := CreateFile(PChar(IcoFile), GENERIC_READ, 0, nil, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, 0);
if hFile = INVALID_HANDLE_VALUE then
Exit;

try
ReadFile(hFile, stID, Sizeof(ICONDIR), dwReserved, nil);

nSize := stID.idEntries.dwBytesInRes;
GetMem(pIcon, nSize);
SetFilePointer(hFile, stID.idEntries.dwImageOffset, nil, FILE_BEGIN);
ReadFile(hFile, pIcon^, nSize, dwReserved, nil);

stGID.idType := 1;
stGID.idCount := stID.idCount;
stGID.idReserved := 0;
CopyMemory(@stGID.idEntries.bWidth, @stID.idEntries.bWidth, 12);
stGID.idEntries.nID := 0;

nGSize := Sizeof(GRPICONDIR);
GetMem(pGrpIcon, nGSize);
CopyMemory(pGrpIcon, @stGID, nGSize);

hUpdate := BeginUpdateResource(PChar(ExeFile), False);
try
ret := UpdateResource(hUpdate, RT_GROUP_ICON, MAKEINTRESOURCE(1), 0, pGrpIcon, nGSize);
ret := UpdateResource(hUpdate, RT_ICON, MAKEINTRESOURCE(1), 0, pIcon, nSize);
finally
EndUpdateResource(hUpdate, False);
end;
finally
CloseHandle(hFile);
end;

Result := ret;
end;

===============================================

VB Code:

===============================================

Option Explicit

Private Declare Function CreateFile Lib "kernel32" Alias "CreateFileA" (ByVal lpFileName As String, ByVal dwDesiredAccess As Long, ByVal dwShareMode As Long, lpSecurityAttributes As Any, ByVal dwCreationDisposition As Long, ByVal dwFlagsAndAttributes As Long, ByVal hTemplateFile As Long) As Long
Private Declare Function ReadFile Lib "kernel32" (ByVal hFile As Long, lpBuffer As Any, ByVal nNumberOfBytesToRead As Long, lpNumberOfBytesRead As Long, lpOverlapped As Any) As Long
Private Declare Function SetFilePointer Lib "kernel32" (ByVal hFile As Long, ByVal lDistanceToMove As Long, lpDistanceToMoveHigh As Long, ByVal dwMoveMethod As Long) As Long
Private Declare Function BeginUpdateResource Lib "kernel32" Alias "BeginUpdateResourceA" (ByVal pFileName As String, ByVal bDeleteExistingResources As Long) As Long
Private Declare Function UpdateResource Lib "kernel32" Alias "UpdateResourceA" (ByVal hUpdate As Long, ByVal lpType As Long, ByVal lpName As Long, ByVal wLanguage As Long, lpData As Any, ByVal cbData As Long) As Long
Private Declare Function EndUpdateResource Lib "kernel32" Alias "EndUpdateResourceA" (ByVal hUpdate As Long, ByVal fDiscard As Long) As Long
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
Private Declare Function GetLastError Lib "kernel32" () As Long

Private Const INVALID_HANDLE_VALUE = -1
Private Const GENERIC_READ = &H80000000
Private Const FILE_ATTRIBUTE_NORMAL = &H80
Private Const FILE_BEGIN = 0
Private Const OPEN_EXISTING = 3
Private Const RT_ICON = 3&
Private Const DIFFERENCE As Long = 11
Private Const RT_GROUP_ICON As Long = (RT_ICON + DIFFERENCE)


Private Type ICONDIRENTRY
bWidth As Byte
bHeight As Byte
bColorCount As Byte
bReserved As Byte
wPlanes As Integer
wBitCount As Integer
dwBytesInRes As Long
dwImageOffset As Long
End Type

Private Type ICONDIR
idReserved As Integer
idType As Integer
idCount As Integer
'idEntries As ICONDIRENTRY
End Type

Private Type GRPICONDIRENTRY
bWidth As Byte
bHeight As Byte
bColorCount As Byte
bReserved As Byte
wPlanes As Integer
wBitCount As Integer
dwBytesInRes As Long
nID As Integer
End Type

Private Type GRPICONDIR
idReserved As Integer
idType As Integer
idCount As Integer
idEntries As GRPICONDIRENTRY
End Type

'//////////////////////////////////////////////
'//函数说明:修改EXE图标
'//
'//参 数:IconFile 图标文件
'// ExeFile 被修改的EXE文件
'//
'//返回值: 成功为True,否则False
'/////////////////////////////////////////////////////
Private Function ChangeExeIcon(ByVal IconFile As String, ByVal ExeFile As String) As Boolean
On Error GoTo cw

Dim stID As ICONDIR
Dim stIDE As ICONDIRENTRY
Dim stGID As GRPICONDIR

Dim hFile As Long
Dim pIcon() As Byte, pGrpIcon() As Byte
Dim nSize As Long, nGSize As Long
Dim dwReserved As Long
Dim hUpdate As Long
Dim ret As Long

hFile = CreateFile(IconFile, GENERIC_READ, 0, ByVal 0&, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)
If hFile = INVALID_HANDLE_VALUE Then Exit Function

ret = ReadFile(hFile, stID, Len(stID), dwReserved, ByVal 0&)
If ret = 0 Then GoTo cw

ret = ReadFile(hFile, stIDE, Len(stIDE), dwReserved, ByVal 0&)

nSize = stIDE.dwBytesInRes
ReDim pIcon(nSize - 1)
SetFilePointer hFile, stIDE.dwImageOffset, ByVal 0&, FILE_BEGIN
ret = ReadFile(hFile, pIcon(0), nSize, dwReserved, ByVal 0&)
If ret = 0 Then GoTo cw

With stGID
.idType = 1
.idCount = stID.idCount
.idReserved = 0
CopyMemory stGID.idEntries, stIDE, 12
.idEntries.nID = 0
End With

nGSize = Len(stGID)
ReDim pGrpIcon(nGSize - 1)
CopyMemory pGrpIcon(0), stGID, nGSize


hUpdate = BeginUpdateResource(ExeFile, False)
ret = UpdateResource(hUpdate, RT_GROUP_ICON, 1, 0, pGrpIcon(0), nGSize)
ret = UpdateResource(hUpdate, RT_ICON, 1, 0, pIcon(0), nSize)
EndUpdateResource hUpdate, False

If ret = 0 Then GoTo cw
ChangeExeIcon = True

cw:
CloseHandle hFile

End Function

===============================================

VC++ Code:

===============================================

#include
#include
#include


struct ICONDIRENTRY
{
BYTE bWidth;
BYTE bHeight;
BYTE bColorCount;
BYTE bReserved;
WORD wPlanes;
WORD wBitCount;
DWORD dwBytesInRes;
DWORD dwImageOffset;
};


struct ICONDIR
{
WORD idReserved;
WORD idType;
WORD idCount;
//ICONDIRENTRY idEntries;
};


struct GRPICONDIRENTRY
{
BYTE bWidth;
BYTE bHeight;
BYTE bColorCount;
BYTE bReserved;
WORD wPlanes;
WORD wBitCount;
DWORD dwBytesInRes;
WORD nID;
};

struct GRPICONDIR
{
WORD idReserved;
WORD idType;
WORD idCount;
GRPICONDIRENTRY idEntries;
};


//////////////////////////////////////////////
//函数说明:修改EXE图标
//
//参 数:IconFile 图标文件
// ExeFile 被修改的EXE文件
//
//返回值: 成功为True,否则False
/////////////////////////////////////////////
bool ChangeExeIcon(LPWSTR IconFile, LPWSTR ExeFile)
{
ICONDIR stID;
ICONDIRENTRY stIDE;
GRPICONDIR stGID;
HANDLE hFile;
DWORD nSize, nGSize, dwReserved;
HANDLE hUpdate;
PBYTE pIcon, pGrpIcon;
BOOL ret;

hFile = CreateFile(IconFile, GENERIC_READ, NULL, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
return false;
}

ZeroMemory(&stID, sizeof(ICONDIR));
ret = ReadFile(hFile, &stID, sizeof(ICONDIR), &dwReserved, NULL);

ZeroMemory(&stIDE, sizeof(ICONDIRENTRY));
ret = ReadFile(hFile, &stIDE, sizeof(ICONDIRENTRY), &dwReserved, NULL);

nSize = stIDE.dwBytesInRes;
pIcon = (PBYTE)malloc(nSize);
SetFilePointer(hFile, stIDE.dwImageOffset, NULL, FILE_BEGIN);
ret = ReadFile(hFile, (LPVOID)pIcon, nSize, &dwReserved, NULL);
if (!ret)
{
CloseHandle(hFile);
return false;
}

ZeroMemory(&stGID, sizeof(GRPICONDIR));
stGID.idCount = stID.idCount;
stGID.idReserved = 0;
stGID.idType = 1;
CopyMemory(&stGID.idEntries, &stIDE, 12);
stGID.idEntries.nID = 0;

nGSize = sizeof(GRPICONDIR);
pGrpIcon = (PBYTE)malloc(nGSize);
CopyMemory(pGrpIcon, &stGID, nGSize);


hUpdate = BeginUpdateResource(ExeFile, false);
ret = UpdateResource(hUpdate, RT_GROUP_ICON, MAKEINTRESOURCE(1), 0, (LPVOID)pGrpIcon, nGSize);
ret = UpdateResource(hUpdate, RT_ICON, MAKEINTRESOURCE(1), 0, (LPVOID)pIcon, nSize);
EndUpdateResource(hUpdate, false);
if (!ret)
{
CloseHandle(hFile);
return false;
}

CloseHandle(hFile);
return true;
}

===============================================

ASM Code:

===============================================


.386
.model flat,stdcall
option casemap:none

include windows.inc
include user32.inc
includelib user32.lib
include kernel32.inc
includelib kernel32.lib

ICONDIRENTRY STRUCT
bWidth BYTE ?
bHeight BYTE ?
bColorCount BYTE ?
bReserved BYTE ?
wPlanes WORD ?
wBitCount WORD ?
dwBytesInRes DWORD ?
dwImageOffset DWORD ?
ICONDIRENTRY ENDS

ICONDIR STRUCT
idReserved WORD ?
idType WORD ?
idCount WORD ?
;idEntries ICONDIRENTRY <>
ICONDIR ENDS

GRPICONDIRENTRY STRUCT
bWidth BYTE ?
bHeight BYTE ?
bColorCount BYTE ?
bReserved BYTE ?
wPlanes WORD ?
wBitCount WORD ?
dwBytesInRes DWORD ?
nID WORD ?
GRPICONDIRENTRY ENDS

GRPICONDIR STRUCT
idReserved WORD ?
idType WORD ?
idCount WORD ?
idEntries GRPICONDIRENTRY <>
GRPICONDIR ENDS

.data

szIcon db 'a.ico', 0
szFile db 'a.exe', 0

.code

//////////////////////////////////////////////
//函数说明:修改EXE图标
//
//参 数:IconFile 图标文件
// ExeFile 被修改的EXE文件
//
//返回值: 成功为True,否则False
/////////////////////////////////////////////

_ChangeExeIcon proc IconFile, ExeFile

local @stID: ICONDIR
local @stIDE: ICONDIRENTRY
local @stGID: GRPICONDIR

local @hFile: DWORD
local @dwReserved: DWORD
local @nSize: DWORD
local @nGSize: DWORD
local @pIcon: DWORD
local @pGrpIcon: DWORD
local @hUpdate: DWORD
local @ret: DWORD

invoke CreateFile, IconFile, GENERIC_READ, NULL, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL
mov @hFile, eax

.if eax == INVALID_HANDLE_VALUE
xor eax, eax
ret
.endif

invoke RtlZeroMemory, addr @stID, sizeof @stID
invoke ReadFile, @hFile, addr @stID, sizeof @stID, addr @dwReserved, NULL

invoke RtlZeroMemory, addr @stIDE, sizeof @stIDE
invoke ReadFile, @hFile, addr @stIDE, sizeof @stIDE, addr @dwReserved, NULL

push @stIDE.dwBytesInRes
pop @nSize

invoke GlobalAlloc, GPTR, @nSize
mov @pIcon, eax

invoke SetFilePointer, @hFile, @stIDE.dwImageOffset, NULL, FILE_BEGIN
invoke ReadFile, @hFile, @pIcon, @nSize, addr @dwReserved, NULL
cmp eax, 0
je err

invoke RtlZeroMemory, addr @stGID, sizeof @stGID
push @stID.idCount
pop @stGID.idCount
mov @stGID.idReserved, 0
mov @stGID.idType, 1
invoke RtlMoveMemory, addr @stGID.idEntries, addr @stIDE, 12
mov @stGID.idEntries.nID, 0

mov @nGSize, sizeof @stGID
invoke GlobalAlloc, GPTR, @nGSize
mov @pGrpIcon, eax
invoke RtlMoveMemory, @pGrpIcon, addr @stGID, @nGSize


;开始修改
invoke BeginUpdateResource, ExeFile, FALSE
mov @hUpdate, eax
invoke UpdateResource, @hUpdate, RT_GROUP_ICON, 1, 0, @pGrpIcon, @nGSize
invoke UpdateResource, @hUpdate, RT_ICON, 1, 0, @pIcon, @nSize
mov @ret, eax
invoke EndUpdateResource, @hUpdate, FALSE

.if @ret == FALSE
jmp err
.endif

;成功后到此一日游
invoke GlobalFree, @pIcon
invoke CloseHandle, @hFile
mov eax, 1
ret

err:
;失败后到此一日游
invoke GlobalFree, @pIcon
invoke CloseHandle, @hFile
xor eax, eax
ret

_ChangeExeIcon endp

;==========================程序入口=============================
start:

invoke _ChangeExeIcon, offset szIcon, offset szFile
invoke ExitProcess, NULL

end start


以上分别用四种语言写的一个用UpdateResource修改EXE文件图标代码示例,希望对你有用。你不会VB?那你可用Delphi的,你不会 Delphi?那你可用VC++的,你不会VC++的?那就用ASM的,你不会用ASM的?那你就用VB的。。。如果你都不会,那么可无视本文。

最近驱动也没怎么学,主要就是练习用ASM与C/C++写代码,俺是先打好基础,然后再猛追花花与炉子,要不然还怎么在0Ginr混??花花、炉子、小伟?你们说呢??嘿嘿~~

http://hi.baidu.com/cxwr/blog/item/9d7f53387efe5af1b211c780.html
感谢google,感谢baidu,感谢菜新。

标签: , ,

How can I change a folders Icon in Delphi?

By: Justin Swett

Abstract: Use a desktop.ini file to change the appearance of a folder in Windows Explorer.

QUESTION:

How change the appearance of a folder icon?

ANSWER:

The process of changing a folder's icon is pretty easy and there are a couple of ways that you can do it. In this article I'll show you how you can do it programmatically. I am only going to cover the general outline of how I made this project, so you can download the complete source from CodeCentral.

http://dn.codegear.com/bg/article/27508

GetParentDirectory

//returns the parent directory for the
//provided "path" (file or directory)
function GetParentDirectory(path : string) : string;
begin
result := ExpandFileName(path + '\..')
end;

Returns the full path of a file name with the network drive portion in UNC format.

Unit

Sysutils

Category

file name utilities

function ExpandUNCFileName(const FileName: string): string;

星期三, 十一月 21, 2007

achelles

{
name = "cWootelf",
pbRevision = 41843,
bars = {
blankSpace = {
height = 8,
position = 1
},
castBar = {
height = 1,
position = 2
},
expBar = {
position = 6
},
healthBar = {
height = 10,
position = 4
},
portrait = {
height = 12,
position = 3,
side = "top"
},
powerBar = {
height = 5,
position = 5
},
repBar = {
position = 7
},
threatBar = {
position = 8
}
},
icons = {
combatIcon = {
position = "portrait-right",
size = 1.5
},
leaderIcon = {
position = "frame-edgetopleft"
},
masterIcon = {
position = "frame-edgetopleft"
},
pvpIcon = {
position = "frame-edgeleft",
size = 2
},
raidTargetIcon = {
position = "frame-edgetop",
size = 2
},
restIcon = {
position = "portrait-right",
size = 1.5
}
},
texts = {
Class = {
custom = "[Level:DifficultyColor][ShortClassification:Upper] [~IsPlayer?CreatureType!ShortRace] [PlayerClass:ClassColor] [DruidForm:Paren]",
position = "blankSpace-right",
styleType = "class"
},
combatText = {
position = "portrait-center"
},
Combo = {
position = "frame-outright",
style = "Standard",
styleType = "combo"
},
Health = {
custom = "[~Status?FractionalHP:Short] ([Status:PercentHP:Percent])",
position = "healthBar-left",
size = 1.5,
styleType = "health"
},
["Health Decifit"] = {
custom = "[Red][MissingHP:Negate]",
position = "healthBar-right",
styleType = "health"
},
Name = {
custom = "[Name:ClassColor]",
position = "blankSpace-left",
size = 1.5,
styleType = "name"
},
Power = {
custom = "[CurMP]/[MaxMP]",
position = "powerBar-right",
style = "Absolute",
styleType = "power"
}
}
}

星期一, 十一月 19, 2007

Pictures inside a database

Page 1: Working with BLOBs. Storing pictures in Access.
These days developing database applications requires more than just operating with textual or numeric data. If you are, for example, developing an Internet/intranet or multimedia based application, frequently there is a need to display pictures along with text from a database.

In this third chapter of the Delphi database course, we'll see how to pull out and display the graphical data (images) inside an Access database with ADO. Don't be worried with the fact that working with images inside an Access database requires more database programming skills than this course has provided so far. Let's pretend that we know more to get more.

If you have followed this course from the beginning (specially the second chapter), you know how to connect to a database and display the Applications (from our working aboutdelphi.mdb database) table in a DBGrid. Remember, we used 3 data components: DBGrid, ADOTable and DataSource to get and display the data from the Applications table.
Back in the first chapter when we created our database, the last filed in the Applications table was left blank (after filling our database with some dummy data). The last field has the name Picture and is of the OLE object type.

http://delphi.about.com/od/database/l/aa030601a.htm

--- I do think use RTTI will be the solution for all picture formats we supported in Delphi.(or use extended library).

标签:

Delphi中显示字段中的图像

//感谢大家的支持,问题已搞惦,总结如下:
//用Image控件显示表中的GIF、JPEG格式图像,可以用流的方式和创建临时文件的方式显示,
//我的Image控件支持GIF、JPEG等图像,以下已在Win98+Delphi5.0下通过。
procedure TMainForm.ShowGIFBtnClick(Sender: TObject);
var
AGif: TGIFImage;
MS: TMemoryStream;
begin
AGif :=TGIFImage.Create;
try
MS := TMemoryStream.Create;
try
TBlobField(Table1.FieldByName('GifField')).SaveToStream(MS);
//此时MS.Position指针位置在Gif文件的最后一个字节
//下面是关键,我搞了一天才搞惦,哈哈......
MS.Position:=0;//只有将其复位才能显示出来
AGif.LoadFromStream(MS);
Image1.Picture.Bitmap.Assign(AGif);
finally
MS.Free;
end;
finally
AGif.Free;
end;
end;

//要显示Jpeg图像需要加入 uses Jpeg;
procedure TMainForm.ShowJPEGBtnClick(Sender: TObject);
var
AJpeg: TJpegImage;
MS: TMemoryStream;
begin
AJpeg :=TJpegImage.Create;
try
MS := TMemoryStream.Create;
try
TBlobField(Table1.FieldByName('JpegField')).SaveToStream(MS);
MS.Position:=0;//加上这一句后搞惦,哈哈......
AJpeg.LoadFromStream(MS);
Image1.Picture.Bitmap.Assign(AJpeg);
finally
MS.Free;
end;
finally
AJpeg.Free;
end;
end;

//用此创建临时文件的方法显示
procedure TMainForm.ShowTMPBtnClick(Sender: TObject);
var
AGif:TGIFImage;
begin
TBlobField(Table1.FieldByName('GifField')).SaveToFile('c:\temfile.gif');
AGif:=TGIFImage.Create;
AGif.LoadFromFile('c:\temfile.gif');
Image1.Picture.Graphic:=AGif;
DeleteFile('c:\temfile.gif');
end;

http://topic.csdn.net/t/20010607/20/149990.html

我想显示 Paradox 表中的GIF图像,表中有一个Graphic字段,其中放入BMP位图,可以用DBimage显示,但放入GIF无法显示。
在表中设一BLOB字段,放入了GIF或JPG格式图像,用下面流的方式也不能在Image中显示:
aStream := TBlobStream.Create(theGifField, bmRead);
Image.Picture.Graphic.LoadFromStream( aStream );
aStream.Free;
请问如何能够显示。

标签:

星期日, 十一月 18, 2007

天方夜谭VCL: 开门

CSDN - 文档中心 - Delphi 阅读:3145 评论: 2 参与评论

标题 天方夜谭VCL: 开门 选择自 wxcwuxuchun 的 Blog
关键字 天方夜谭VCL: 开门
出处 http://www.c-view.org

天方夜谭VCL: 开门
虫虫

前言
如果你爱他,让他学VCL,因为那是天堂。
如果你恨他,让他学VCL,因为那是地狱。
──《天方夜谭VCL》

传说很久很久以前,中国和印度之间有个岛。那里的国王每天娶一个女子,过夜后就杀,闹得鸡犬不宁,最后宰相的女儿自愿嫁入宫。第一晚,她讲了一个非常有意思的故事,国王听入了迷,第二天没有杀她。此后她每晚讲一个奇特的故事,一直讲到第一千零一夜,国王终于幡然悔悟。这就是著名的《一千零一夜》,也就是《天方夜谭》。印度和中国陆地接壤,那么相信传说中所指的岛,必然是在南中国海-马六甲海峡-印度洋某个地方。现在我也算是在这其间的一个海岛上,正值夜晚,也就借借“天方夜谭”的大名吧。

初中我最喜欢的编程环境是Turbo C 2.0,高一开始用Visual Basic。后来用了没多久就发现,如果想做一个稍微复杂的东西,就需要不停地查资料来调用API,得在最前面作一个长得可怕的API函数声明。于是我开始怀念简洁的C语言。有位喜欢用Delphi的师哥,知道我极为愤恨 Pascal,把我引向C++ Builder。即使对于C++中的继承、多态这些简单概念都还是一知半解,我居然也开始用VCL编一些莫名其妙的小程序(VCL上手倒真容易),开始熟悉VCL的结构,同时也了解了MFC和SDK,补习C++的基础知识。后来我才觉得,VCL易学易用根本是个谎言。其实 VCL相当难学,甚至比MFC更麻烦。

不知道为什么,C++ Builder的资料出奇地少,也许正是这个原因,C++ Builder论坛上的人情味也特别浓。不管是我初学VCL时常问些莫名其妙白痴问题的天极论坛,还是现在我经常驻足的CSDN,C++ Builder论坛给人的感觉总是很温馨。每次C++ Builder都比同等版本Delphi晚出,每次用C++还不得不看Object Pascal的脸色,我想这是很多人心里的感受。CLX已经出现在Delphi6 中,C++ Builder6的发布似乎还遥遥无期。CLX会代替VCL吗?看来似乎不会,后面还会提到。我也看过不少要号召把VCL用C++改写的帖子,往往雷声大雨点小。看看别人老外,说干就干,一个FreeCLX项目就这么启动了。

用MFC的人比用VCL的运气好,他们有Microsoft的支持,有Inside Visual C++、 Programming Windows 95 with MFC、MFC Internals这些天王巨星的英文名著和中文翻译,也有诸如侯捷先生的《深入浅出MFC》(即Dissecting MFC)这些出色的中文原创作品。使用Delphi的人也远比使用C++ Builder的命好,关于 Delphi的精彩资料远远比C++ Builder多,很无奈,真的很无奈。

C++ View杂志的主编向我约稿,我很为难,因为时间和技术水平都成问题。借用侯捷先生一句话,要拒绝和你住在同一个大脑同一个躯壳的人日日夜夜旦旦夕夕的请求,是很困难的。于是我下决心,写一系列分析VCL内部原理的文章。所谓“天方夜谭”,当然对初学者不会有立杆见影的帮助,甚至于会让您觉得“无聊”。这些文章面向的朋友应该比较熟悉VCL,有一定C++的基础(当然会Object Pascal和汇编更好),比如希望知道VCL底层运作机制的朋友,和希望自己开发应用框架或者想用C++重写VCL的朋友。同时我更希望大家交流一下解剖应用框架的经验,让我们不局限于VCL或者MFC,能站在更高的角度看问题,共同提高自己的能力。

在深入探讨VCL之前,先得把VCL主要的性质说一下。

同SmallTalk和Java所带的框架一样,VCL是Object Pascal的一部分,也就是说语言和框架之间没有明确的界限。比如 Java带有JDK,任写一个类都是java.lang.Object的子类。VCL和Object Pascal是同样的道理。当然,Object Pascal为了兼容以前的Pascal,依然允许某个类没有任何父类,但本系列文章将不再考虑这种情形。
同大多数框架一样,VCL采取的是单根结构。也就是说,VCL的结构是以一棵TObject为根的继承树,除TObject外的所有VCL类都是TObject直接或间接的子类。
由于Object Pascal的语言特性,整个结构中只使用单继承。
所以,VCL的本质是一个Object Pascal类库,提供了Object Pascal和C++两个接口。在剖析的过程中,请时刻牢记这一点。

文章的组织结构是就事论事,一次一个话题。由于VCL并不像MFC是一个独立的框架,它与Object Pascal、IDE、编译器结合非常紧密,所以在剖析过程中不免会提到汇编。当然不会汇编的朋友也不用怕,我会把汇编代码都解释清楚,并尽量用C++改写。

文中有很多图是表示类的内存结构,如图所示。其中方框表示一个变量,两端伸出表示还有若干个变量,椭圆标注是说明虚线圆圈中的整个对象(在后面虚线圆圈不会画出)。


图1 图例

文中的程序,如非特别说明,均可以在Console Application模式下(如果使用了VCL类则需要复选“Use VCL”)编译通过。

开门
倒霉者如愚公,开门就见太行、王屋山。在一怒之下他开始移山,最后幸亏天神帮忙搬走了。中国人不喜欢开门见山的性格可能就是愚公传下来的,说话做事老爱绕弯。当然我也不能免俗,前面废话了一大堆,现在接着来。

提起RTTI(runtime type identification,运行时间类型辨别),相信大家都很熟悉。C++的RTTI功能相当有限,主要由typeid和dynamic_cast提供[1]。至于这两者的实现方式[2],不是我们今天的话题,我们所关注的,乃是VCL所提供的“高级”RTTI的底层机制。

熟悉框架的朋友都知道,框架往往会提供“高级”的RTTI功能。我曾看过一个论调,说Java和Object Pascal比C++好,原因是因为它们的RTTI更“高级”。且不论滥用RTTI极为有害,事实上,C++用宏(macro)亦可以模拟出相同功能的RTTI[3]。

不过对于VCL类来说,您清楚其RTTI机制的运作情况吗?对于如下

class A: public TObject
{
...
}
...
A* p = new A;

为什么p->ClassName();就能返回类A的名字“A”呢?
为什么A::ClassName(p->ClassParent())就可以返回A的基类名“TObject”呢?
为什么……?
其实这都是编译器暗箱操作的结果。说白了,编译器先在某个地方把类名写好,到时候去取出来就行。关键在于,如何去取出来呢?显然有指针指向这些数据,那么这些指针放在什么地方呢?

记得《阿里巴巴和四十大盗》的故事吧?宝藏是早就存在的,如果知道口诀“芝麻,开门吧”,就可以拿到宝藏。同样,类的相关信息是编译器帮我们写好了的,我们所关心的,就是如何获取这些信息的“口诀”。

不过这一切,要从虚函数开始,我们得先复习一下C/C++的对象模型。

虚拟函数表VFT
C语言提供了基于对象(Object-Based)的思维模型,其对象模型非常清晰。比如

struct A
{
int i;
char c;
};



图 2 结构的内存布局

在32位系统上,变量i占用4个字节,变量c占用1个字节。编译器可能还会在后面添加3个字节补齐。那么,sizeof(A)就是8。

C++提供了面向对象(Object-Oriented)的思维模型,其对象模型建立在C的基础上。对于没有虚函数的类,其模型与C中的结构(struct)完全一样。但如果存在虚函数,一般在类实体的某个部分会存在一个指针vptr,指向虚拟函数表 VFT(Virtual Function Table)的入口。显然,对于同一个类的所有对象,这个vptr都是相同的。例如

class A
{
private:
int i;
char c;
public:
virtual void f1();
virtual void f2();
};

class B: public A
{
public:
virtual void f1();
virtual void f2();
};

当我们作如下调用的时候
A* p;
...
p->f2();

程序本身并不知道它会调用A::f还是B::f或是其它函数,只是通过类实体中的vptr,查到VFT的入口,再在入口中查询函数地址,进行调用。由于Borland C++编译器把vptr放在类实体的头部,因此下面均有此假设。
为了更充分地说明问题,我们从汇编级来分析一下。假设我们采用的是Borland C++编译器。

p->f2();

这句的汇编代码是
mov eax,[ebp-0x04]
push eax
mov edx,[eax]
call dword ptr [edx+0x04]
pop ecx



图3 C++类实体的内存布局

第一句ebp-0x04是指针变量p的地址,第一句是把p所指向的对象的地址传送到eax;
第二句不用管它;
第三句是把对象头部的指针vptr传到edx,即已取得VFT的入口;
第四句是关键,edx再加4(32位系统上一个指针占4个字节),也就是调用了从VFT入口算起的第二个函数指针,即B::f2;
第五句不用管它。

相信大家对VFT和C++的对象模型有一个更深刻的认识吧?对于VFT的实现,各个编译器是不一样的。有兴趣的朋友不妨可以自行探索一下Microsft Visual C++和GCC的实现方法,比较一下它们的异同。

知道了VFT的结构,那么想想下面这个程序的结果是什么。

#include
using namespace std;

class A
{
int c;
virtual void f();
public:
A(int v = 0) { c = v;}
};

void main()
{
A a, b(20);
cout << *(void**)&a << endl;
cout << *(void**)&b << endl;
}

我想您应该能理解其中*(void**)&a吧?这是取得vptr的值,也就是a所在内存空间的前4个字节,一个指针。下面我们还会使用类似的语句。
无庸质疑,结果是输出两个完全相同的值。前面我们已经说过,对于同一个类的所有对象,其vptr值都是相同的。

那么这个VFT到底有什么作用呢?现在看来,似乎就是储存虚函数的地址。

虚拟方法表VMT
如何通过类的实体来找到类的相关RTTI信息呢?显然,VFT是同一个类的所有实体共享的数据,而RTTI正好也是。那么,把RTTI放在VFT里,就是个不错的选择。

往哪儿放呢?VFT从入口开始往后是各个虚函数的指针,那么RTTI只能放在两个地方:入口以前或者所有虚函数指针之后。显然,放在入口以前更好,至少我们不用关心虚函数的多少,RTTI的位置也可以相对确定。

VCL就采用了这个办法来放置RTTI,不过把VFT换了名字,叫虚拟方法表VMT(Virtual Method Table)。VMT的结构是怎样的呢?Borland所提供的帮助文件里没有任何相关资料,不过我们在Include\Vcl\system.hpp中就能找到如下蛛丝马迹。

static const Shortint vmtSelfPtr = 0xffffffb4;
static const Shortint vmtIntfTable = 0xffffffb8;
static const Shortint vmtAutoTable = 0xffffffbc;
static const Shortint vmtInitTable = 0xffffffc0;
static const Shortint vmtTypeInfo = 0xffffffc4;
static const Shortint vmtFieldTable = 0xffffffc8;
static const Shortint vmtMethodTable = 0xffffffcc;
static const Shortint vmtDynamicTable = 0xffffffd0;
static const Shortint vmtClassName = 0xffffffd4;
static const Shortint vmtInstanceSize = 0xffffffd8;
static const Shortint vmtParent = 0xffffffdc;
static const Shortint vmtSafeCallException = 0xffffffe0;
static const Shortint vmtAfterConstruction = 0xffffffe4;
static const Shortint vmtBeforeDestruction = 0xffffffe8;
static const Shortint vmtDispatch = 0xffffffec;
static const Shortint vmtDefaultHandler = 0xfffffff0;
static const Shortint vmtNewInstance = 0xfffffff4;
static const Shortint vmtFreeInstance = 0xfffffff8;
static const Shortint vmtDestroy = 0xfffffffc;

注意这些常数值中的负数采用的是补码表示法。求一个负数的补码,先写出相应正数的补码表示,再按位求反,最后(在最低位)加1即可。对于求32位负数的补码,也可以用它本身减去0xffffffff再减1即可。以0xfffffffc为例,0xfffffffc – 0xffffffff – 1 = – 0x04,这就是结果。我们还可以从Borland提供的原始码 Source\Vcl\system.pas获得,其中就是用负数表示。
看着这份表格,从这些变量名中,我们已经猜到了其大概的分布情况。这些数字之间的间隔都是[4],可以猜想这些都是指针:函数指针或者数据指针。从这些常数的名字我们就可以知道它们的作用,比如vmtClassName自然就是储存类名的指针。入口0以前,就是VCL对象的关键数据。无疑,它们蕴涵了TObject乃至VCL对象关键的秘密,也就是VMT的分布结构。

这以上只是我们的推测,我们还应该验证一下。我们知道的事实是,每一个对象必然都包含了其所属类的相关信息。比如任何一个C++类的实体,都包含一个指向虚拟函数表VFT的指针。VCL类的实体必然也包含一个指向虚拟方法表VMT的指针。

#include
#include
using namespace std;

class A: public TObject
{
int x;
virtual void f1() {}
virtual void f2() {}
public:
A(int v = 0): x(v) {}
};

void main()
{
A* p = new A;, * q = new A(100);
void* a = *(void**)p, * b = *(void**)q;
void* c = p->ClassType(), * d = q->ClassType();
cout << a << ' ' << b << endl;
cout << c << ' ' << d << endl;
cout << __classid(A) << endl;
delete p;
delete q;
}

结果很有意思,输出的五个指针地址完全一样!a和b相同,从前面的例子我们就可以知道。然而TObject的ClassType方法和 __classid操作符的返回值也跟这两者相同,这就有点意思了。查查帮助就可以知道,__classid是C++ Builder中新增的扩展关键字,返回类的VMT的入口地址;而TObject的ClassType方法则是返回对象的类信息,返回类型是TClass(也就是 TMetaClass*)。这说明,每个VCL类实体的头部包含的指针,就是指向VMT的入口地址。而这个位置,也就是TObject的成员函数 ClassType的返回值,亦即运算符__classid返回的类A的信息,只不过这个返回值是以TClass(即TMetaClass*)的形式存在。


图4 VCL类的VMT入口

我们已经知道了VMT的结构,现在又找到了其入口,此时的兴奋不亚于阿里巴巴知道“芝麻,开门吧”这句咒语时的感受。既然知道了开门的咒语,还不赶快进去拿宝藏?

牛刀小试
乘着东风,我们来模拟一下VCL简单的RTTI功能。为方便起见,我们仿造TObject,写一个类FObject(呵呵,如果把TObject 看成True Object,我们的FObject就是False Object)。要问下面这段代码从哪里来?大部分都Copy&Paste自 Include\Vcl\systobj.h文件。

class FObject
{
public:
FObject(); /* Body provided by VCL {} */
Free();
TClass ClassType();
void CleanupInstance();
void * FieldAddress(const ShortString &Name);

/* class method */
static TObject * InitInstance(TClass cls, void *instance);
static ShortString ClassName(TClass cls);
static bool ClassNameIs(TClass cls, const AnsiString string);
static TClass ClassParent(TClass cls);
static void * ClassInfo(TClass cls);
static long InstanceSize(TClass cls);
static bool InheritsFrom(TClass cls, TClass aClass);
static void * MethodAddress(TClass cls, const ShortString &Name);
static ShortString MethodName(TClass cls, void *Address);

/* Hack: GetInterface is an untyped out object parameter and
* so is mangled as a void*. In practice, however, it is
* really a void**. Be sure when using this method to provide
* two levels of indirection and cast away one of them.
*/

bool GetInterface(const TGUID &IID, /* out */ void *Obj);

/* class method */
static PInterfaceEntry GetInterfaceEntry(const TGUID IID);
static PInterfaceTable * GetInterfaceTable(void);

ShortString ClassName()
{
return ClassName(ClassType());
}

bool ClassNameIs(const AnsiString string)
{
return ClassNameIs(ClassType(), string);
}

TClass ClassParent()
{
return ClassParent(ClassType());
}

void * ClassInfo()
{
return ClassInfo(ClassType());
}

long InstanceSize()
{
return InstanceSize(ClassType());
}

bool InheritsFrom(TClass aClass)
{
return InheritsFrom(ClassType(), aClass);
}

void * MethodAddress(const ShortString &Name)
{
return MethodAddress(ClassType(), Name);
}

ShortString MethodName(void *Address)
{
return MethodName(ClassType(), Address);
}

virtual HResult SafeCallException(TObject *, void *);
virtual void AfterConstruction();
virtual void BeforeDestruction();
virtual void Dispatch(void *Message);
virtual void DefaultHandler(void* Message);

private:
virtual TObject* NewInstance(TClass cls);

public:
virtual void FreeInstance();
virtual ~FObject(); /* Body provided by VCL {} */
};

当然FObject::ClassType我们已经会写了,那就是
TClass FObject::ClassType()
{
return *(TClass*)this;
}

我们会在后面陆续把这些成员函数填充完整。先举个例,拿类名(ClassName)开刀吧。
查查VMT表,vmtClassName = 0xffffffd4,我们就从这里下手。主要的步骤是:

找到VMT的入口;
通过vmtClassName找到储存类名的地址;
获取类名。
0xffffffd4也就相当于– 44,也就是VMT入口指向的地址开始,倒数第44字节到倒数第41字节这4个字节所代表的指针,指向类名。假设入口指向的地址是cls,那么vmtClassName所代表的地址就是(char*)cls – 44,亦即 (char*)cls + vmtClassName。

注意一个字符串格式的问题,VCL既然是用Object Pascal写的,其中储存类名的字符串的格式必然是Pascal传统方式,也就是第1个字节为字符串的长度,紧接着为字符串的实际内容。在C++ Builder中,与之对应的类型是ShortString。



图5 TObject::ClassName的运作方式

代码如下:

ShortString FObject::ClassName(TClass cls)
{
ShortString* r = *(ShortString**)((char*)cls + vmtClassName);
return *r;
}

我们不妨测试一下。
#include
#include
#include
using namespace std;
... 插入FObject相应的代码...
void main()
{
auto_ptr list(new TList);
FObject* p = (FObject*)list.get();
cout << AnsiString(p->ClassName()).c_str() << endl;
cout << AnsiString(list->ClassName()).c_str() << endl;
}

输出结果在我们意料之内,都是“TList”。
对于函数ClassNameIs,我们就可以轻而易举地完成了。

bool FObject::ClassNameIs(TClass cls, const AnsiString string)
{
return string==ClassName(cls);
}

有朋友可能奇怪,你怎么知道TObject::ClassName是这样的呢?
三种办法:

猜,用经验推测;
看Borland提供的原始码;
看编译以后的汇编码。
在Borland提供的原始码中,我们可以看到TObject::ClassName的实现如下:

class function TObject.ClassName: ShortString;
asm
{ -> EAX VMT }
{ EDX Pointer to result string }
PUSH ESI
PUSH EDI
MOV EDI,EDX
MOV ESI,[EAX].vmtClassName
XOR ECX,ECX
MOV CL,[ESI]
INC ECX
REP MOVSB
POP EDI
POP ESI
end;

熟悉汇编的朋友就可以由此写出相应的C/C++代码来。对于不会的朋友,根据我们的讲解,相信也可以轻而易举地完成吧。
希望您在看这段的时候,不妨先用第1种办法,然后结合2、3看看,一定收获不小。

势如破竹
接下来就太简单了,我们不再举例,把相应的成员函数补充完整即可。您不妨先自己试着写写,探索一下,再与汇编代码和文中的代码作比较,一定乐趣无穷。

TObject::ClassInfo是做什么的?问我啊?我也不知道。VCL的帮助里说,用ClassInfo可以访问包含对象类型、祖先类和所有published属性信息的RTTI表。这个表只是内部使用,TObject提供了其它方法来访问RTTI信息。我们先写出它的实现。



图6 TObject::ClassInfo的运作方式

void * FObject::ClassInfo(TClass cls)
{
return *(void**)((char*)cls + vmtTypeInfo);
}

Borland的说法可信吗?这个函数返回值的类型是void *,明摆着不愿意透露更多的信息。您不妨按上面ClassName的方法测试一下,对于TList,ClassInfo输出的结果居然是0!也就是一个空指针!什么东东?别急,后面我们会掀开这个void *的面纱,现在姑且卖个关子。
VCL框架中只存在单继承,这是由Object Pascal语言的特性决定的。这样,每一个类只有唯一一个父类,函数TObject::ClassParent就能帮您把父类找出来。

TClass FObject::ClassParent(TClass cls)
{
TClass* r = *(TClass**)((char*)cls + vmtParent);
return (r)? (*r) : 0;
}

由此,我们也能很轻松地模拟TObject::InheritsForm的实现。
bool FObject::InheritsFrom(TClass cls, TClass aClass)
{
while(aClass)
{
if(aClass==cls)return true;
cls = ClassParent(cls);
}
return false;
}

要知道一个对象所占的字节数,TObject::InstanceSize就可以达到目的。
long FObject::InstanceSize(TClass cls)
{
return *(long*)((char*)cls + vmtInstanceSize);
}

有朋友可能说,C++不是有sizeof操作符吗?为什么不用呢?在VCL中,sizeof有两个缺陷。首先sizeof是完全静态的,也就是说,如果您写sizeof(...),编译以后,这会被替换为一个常数,没有任何的求值过程,因此不能动态求值;其次,VCL类必须与指针或引用的形式存在。所以对于
TObject *a;
...

sizeof(*a)这样的表达式是错误的。而且即使TObject不是VCL类,使用sizeof(*a)还是相当于sizeof(TObject),没有实际价值。
结束语
现在我们已经打开了通向VCL类秘密的大门。回头一看,VMT跟VFT有什么区别与联系呢?其实VMT可以算是VFT具体化的一个结果,也就是说,VMT是在VFT基础上发展出来的一种具有“规范”性质的结构,所有的VCL类都遵循这个“规范”。这很像COM与C++纯虚基类的关系。

通过VMT,VCL放置了一些重要的信息,由此来实现RTTI。所以“高级”RTTI功能其实是相当低级和简单的一项技术。就其实现方式而言,大致有三种。MFC用宏(macro)模拟算是一类,完全符合C++标准,不需要对语言进行扩充,也不依赖于特定的编译器,不过给人臃肿的感觉;VCL则是完全由编译器实现,同时扩充了C++的语言特性,必须在Borland的编译器上编译,但是很简洁;另外建构KDE基础的跨平台框架Qt[4],则采用了折中的方式,扩充了C++的关键字,书写很简洁,在编译之前必须用Qt提供的程序MOC进行预处理,把扩充部分的代码改写为符合C++标准的代码,然后才可以在任何符合C++标准的编译器上编译。

代表作 实现方式 不依赖特定编译器 简洁程度 编译次数
MFC 宏插入 是 一般 1
VCL 编译器生成 否 好 1
Qt 预编译程序生成 是 较好 2(包括MOC)


注:如果长期仅在Windows平台下进行开发的朋友,可能没有听说过Qt的大名。事实上在Linux世界里,这可是个响当当的名头。Qt是一套完善的C++框架,横跨Unix/Linux、Windows、Mac OS诸多平台,内部机制相当有趣。Borland最新的Kylix和 Delphi6所采用的跨平台框架CLX(分为BaseCLX、VisualCLX、DataCLX、NetCLX四个部分,BaseCLX与VCL顶部几个类相同),其可视化部分VisualCLX就建构在Qt上,这多少让我感到失望和不满。Qt本身就跨平台,VisualCLX建构在Qt上,自然也跨平台;但是CLX是用Object Pascal包装了一个C++框架,我不敢想象,C++ Builder6中的CLX是否又用C++再来包装这个包装了C++框架的Object Pascal框架呢?如果真是如此,其效率和调试难度……

对于“高级”RTTI的实现形式,VCL用了TMetaClass(其中TClass就是TMetaClass*)来配合存储类的信息,也就是所谓“类的类”,这非常普遍。MFC中的CObject与CRuntimeClass,JDK中的java.lang.Object和 java.lang.Class都是如此。比如对于一个TObject *p,如何获取其父类名呢?我们必须借助TMetaClass:可以先用 p->ClassType返回父类的信息(是一个TMetaClass*类型),再以此为参数传入TObject::ClassName就可以获得结果,也就是TObject::ClassName(p->ClassType())即可。

同时我们也应该拆穿所谓“拥有更高级RTTI的语言本身也更高级”的谎言。至少从我那少得可怜的经验来看,对于一套框架,除非需要和IDE配合,否则在绝大部分情况下,RTTI是完全没有必要的,甚至是有害的[5]。希望使用和设计框架的朋友三思。

致谢
非常感谢孟岩和孙春阳对本文所提出的宝贵意见。

参考
1. Bjarne Stroustrup. The C++ Programming Language, 3e. Addison-Wesley, Reading, MA. 1997.

2. Stanley Lippman. Inside the C++ Object Model. Addison-Wesley, Reading, MA. 1996
侯捷.《深度探索C++物件模型》.碁峰资讯股份有限公司.1998.
侯捷.《深度探索C++对象模型》.华中科技大学出版社.2001.

3. 侯捷.《深入浅出MFC》,2e.松岗电脑图资料股份有限公司/华中科技大学出版社.1997/2001.

4. 虫虫.《Qt最新消息》.C++ View.2001,7.

5. Robert C.Martin. “The Open-Closed Principle”. C++ Report. 1996, 1.
plpliuly,虫虫.《开放封闭原则OCP》.C++ View.2001,8

http://www.dvpx.com/index_article_display.aspx?Fid=3&id=266

标签: , ,

Delphi中用ICMP探测远程主机状态

网络通讯中经常需要确定远程主机是否存活,以决定下一部进行的操作。可以直接使用ICMP协议来实现,但是要考虑许多协议细节,实现起来比较麻烦。Windows 自带的ICMP库里有现成的函数可以使用,只要在使用前填充相应的数据结构就可以了。

  以下是要使用的数据结构。这些结构MSDN里有C形式的声明,这里给出的是Delphi的形式。

//用到的协议数据结构
PIPOptionInfo = ^TIPOptionInfo; // IP 头选项
TIPOptionInfo = packed record
TTL: Byte;//存活时间
TOS: Byte;//Type of Service,请求类型
Flags: Byte;//标志
OptionsSize: Byte;//选项长度
OptionsData: PChar;//选项数据
end;
PIcmpEchoReply = ^TIcmpEchoReply;
TIcmpEchoReply = packed record // ICMP 返回信息
Address: DWORD;//IP地址
Status: DWORD;//状态
RTT: DWORD;
DataSize: Word;//数据长度
Reserved: Word;//保留
Data: Pointer;//数据
Options: TIPOptionInfo;//选项区
end;

//动态库中的函数声明
TIcmpCreateFile = function: THandle; stdcall; //创建ICMP句柄
TIcmpCloseHandle = function(IcmpHandle: THandle): Boolean; stdcall; //关闭ICMP句柄
TIcmpSendEcho = function(IcmpHandle:THandle; DestinationAddress:DWORD;
RequestData:Pointer; RequestSize:Word; RequestOptions:PIPOptionInfo;
ReplyBuffer:Pointer; ReplySize:DWord; Timeout:DWord):DWord; stdcall;//发送ICMP探测数据报

//要用到的变量声明
hICMPDll,hICMP:THandle;
wsaData:TWSADATA;
ICMPCreateFile:TICMPCreateFile;
IcmpCloseHandle:TIcmpCloseHandle;
IcmpSendEcho:TIcmpSendEcho;

//destip:要探测的远程地址,形如 192.168.1.1
procedure f_CheckOnline(destip:string);
 var
  IPOpt:TIPOptionInfo;// 发包的 IP 选项
  IPAddr:DWORD;
  pReqData,pRevData:PChar;
  pIPE:PIcmpEchoReply;// ICMP Echo 回复缓冲区
  FSize: DWORD;
  MyString:string;
  FTimeOut:DWORD;
  BufferSize:DWORD;
  i:integer;
 begin
  hICMPdll := LoadLibrary('icmp.dll'); //调取icmp 动态库
  if hICMPDll<>NULL then
   begin
    WSAStartup($101,wsaData);//初始化网络协议栈
    @ICMPCreateFile := GetProcAddress(hICMPdll, 'IcmpCreateFile'); //取动态库中的导出函数
    @IcmpCloseHandle := GetProcAddress(hICMPdll, 'IcmpCloseHandle');
    @IcmpSendEcho := GetProcAddress(hICMPdll, 'IcmpSendEcho');
    hICMP := IcmpCreateFile; //创建 icmp句柄
    IPAddr:= inet_addr(PChar(destip)); //取要探测的远端主机ip地址

    FSize := 40;
    BufferSize := SizeOf(TICMPEchoReply) + FSize;
    GetMem(pRevData,FSize);
    GetMem(pIPE,BufferSize);
    FillChar(pIPE^, SizeOf(pIPE^), 0);
    pIPE^.Data := pRevData;
    MyString := 'Hi, OnLine?';//任意字符串
    pReqData := PChar(MyString);
    FillChar(IPOpt, Sizeof(IPOpt), 0);
    IPOpt.TTL := 64;
    FTimeOut := 500;//等待时长
     i:=IcmpSendEcho(hICMP, IPAddr, pReqData, Length(MyString), @IPOpt, pIPE, BufferSize, FTimeOut); //如果有返回,返回值表示收到的回复的个数。如果为0表示没有回复,主机无法到达
    FreeMem(pRevData);
    FreeMem(pIPE);
    IcmpCloseHandle(hicmp);
    FreeLibrary(hICMPdll);//释放动态库
    WSAcleanup();//清理协议栈
  end;
end;

标签:

Installing Bugzilla on Microsoft Windows

Original author: Byron Jones

Translation: es

Bugzilla version 2.18 was the first release that runs unmodified on Windows. This document guides you step by step through the installation process.

Note that there are a few things that don't work on Windows. The most important one of these would be the inbound email interface (contrib/README.Mailif).

Bugzilla
There's two main methods to getting the Bugzilla source - from CVS or in a tarball. The best method for fetching Bugzilla is to grab it directly from CVS, as this will allow for simple upgrades, even if you have customised Bugzilla.


Read the Release Notes before you do anything.

More details: http://www.bugzilla.org/docs/win32install.html

标签: ,

文字输出备注

在 canvas 上画出任意长宽比例,旋转任意角度的,带反走样算法(anti-aliased)的字符串

tcanvas *lpcvs = paintbox1->canvas;
hfont hfnt = createfont(
-muldiv(40,getdevicecaps(lpcvs->handle,logpixelsy),72), //字高40磅
-muldiv(10,getdevicecaps(lpcvs->handle,logpixelsx),72), //字宽10磅(汉字20磅)
450,450, //旋转 45 度 (可旋转任意角度, 以 1/10 度为单位)
0,0,0,0,
gb2312_charset, //简体中文
0,0,
antialiased_quality, //使用反走样(anti-aliased)算法
0,
"宋体"); //字体
selectobject(lpcvs->handle,hfnt);
settextcolor(lpcvs->handle,rgb(0,0,255));
setbkmode(lpcvs->handle,transparent);
lpcvs->textout(0,200,"欢迎访问 c++ 爱好者网站");
lpcvs->textout(60,200,"www.cppfans.com");
deleteobject(hfnt); //释放创建的字体的资源

星期二, 十一月 06, 2007

Line too long (more than 1023 characters)

Most likely you imported your source file from a CVS on a Unix machine, or you used an editor, that saves Unix style line separators. This will confuse Delphi. Homesite is a tool that will do this to you, I found.

Your line separators of #10s must be replaced by #13#10 as it is standard in DOS/ Windows.

In my case, I used Homesite to convert to Windows style documents.

星期六, 十一月 03, 2007

[集群] RHEL5实现高可用HA集群+GFS+EnterpriseDB

近日发现有网站开始转载我的文章,这个我非常高兴,非常感谢大 家支持!但是我发现好多网站转载时连我的原文出处都没有,文章题目还经常写着“原创”,这个太不厚道了,对于每一个写原创文章的作者这种做法都是不可取 的!借鸟哥的一句话:“原创文章都是作者一个字一个字打出来了,也经过自己一步一步的测试,希望大家给于尊重”!别外我当然是一个”枪手“写的东东大都是 公司的产品啦,哈哈!好了,进入正文:

版权所有,转载请注明出处!
作者:萧少聪 RHCE/CI
BLOG:scottsiu.cublog.cn或blog.csdn.net/scottsiu

前言:
几个大家要注意的地方:
1、做Cluster应该要有Fence设备,当一台机器出现问题时处于正常状态的机器会通过Fence将其重启或关机以释放IP、磁盘等资源。
2、做HA要对服务的启动脚本做一点修改以符合Cluster的要求。
3、GFS要建立在Cluster之上。
4、RHEL5 AP(高级平台版)已经带有Cluster组件,当中包括GFS、Cluster Suite、LVS等,不像RHEL4那样用另外安装。


1、网络
我的IP为
iscsi: 192.168.122.1
edb1: 192.168.122.21
edb2: 192.168.122.22

2.1、Linux中安装iscsi-target
到sf.net下载最新的iscsi-target
# tar zxvf iscsi-target.xxxxx.tar.gz
# cd iscsi-target.xxxxx
# make
# make install
# mkdir /iscsidisk
# dd if=/dev/zero of=/iscsidisk/sharedisk1.img bs=1k count=1 seek=2000K
(以上这句是用dd建立了一个2G大小的映像文件,也就是我们的一个iscsi映像磁盘)
# vim /etc/ietd.conf
找到"Target iqn",注意这行Target后的一串字符,是iscsi的标记
找到"Lun 0"一行,改为
Lun 0 Path=/iscsidisk/sharedisk1.img,Type=fileio
# chkconfig iscsi-target on
# /etc/init.d/iscsi-target start

2.2、在edb1、edb2中连接iscsi
# rpm -ivh iscsi-initiator-utils-6.2.0.742-0.5.el5.rpm
(RHEL5的光盘中有这个文件)
# chkconfig iscsid start
# /etc/init.d/iscsid start
# iscsiadm -m discovery -t sendtargets -p 192.168.122.1:3260
172.16.122.1:3260,1 iqn.2001-04.com.example:storage.disk2.sys1.xyz
(iqn.2001..........这部份应该和iscsi服务器中ietd.conf中的标记一样)
# iscsiadm -m node -T iqn.2001-04.com.example:storage.disk2.sys1.xyz -p 172.16.122.1:3260 -l
(以上两个iscsiadm的操作只在第一次连接iscsi服务器时要执行,以后每当iscsid启动都会自动连接)
# fdisk -l
(应该可以看到多出来一个/dev/sdx的分区)

3、在edb1、edb2中安装EnterpriseDB
在EnterpriseDB的官方网站下载到其最新版本:http://www.enterprisedb.com
EnterpriseDB(以下我简写为EDB)是一个基于PostgreSQL并与Oracle语法兼容的数据库在OLTP的多并发性事务处理中比PostgreSQL有了很大的提高!
下载软件包进行解压(安装要有root权限):
#tar zxvf edb-linux-x86_82412.tar.gz
#cd edb-linux-x86_82412
#./pre-Install.sh
如果出现You may now install EnterpriseDB就可以正常安装了,这里主要是针对不同的LINUX版本生成EDB所要的连接的
#./edb-linux-x86_82412.bin -console
安装过程中会要求序列号
Select License Type:

[X] 1 - Install Full or Evaluation License
[ ] 2 - Install Express License

To select an item enter its number, or 0 when you are finished: [0]
这里如果你有在EDB官网上注册的话会收到一个30天无限制试用的SN,如果没有的话可以选2安装限制为1 CPU/1G RAM/6G Data的版本。
其它的选项默认就好了!

4、双节点HA集群
4.1 Fence
做HA的话就要有fence设备,这是什么东东,如APC、HP ilo、IPMI等等
这里以HP ilo为列,当前edb1、edb2的ilo IP分别是10.11.0.1、10.11.0.2用户名密码为redhat,在edb1中测试:
# fence_ilo -a 10.11.0.2 -l redhat -p redhat -o status
在edb2中测试:
# fence_ilo -a 10.11.0.1 -l redhat -p redhat -o status
如果返回正确,证明fence已经正常

4.2 HA Cluster
在REDHAT中配置Cluster可以通过GUI下的Cluster Manager进行非常简单以下我只给出配置文件
以下文件CP到每个节点上
/etc/cluster/cluster.conf

/etc/hosts加入以下

192.168.122.21 edb1
192.168.122.22 edb2

4.3为了使EDB可以实现HA,我们要对EDB的启动脚本进行一点修改,如果我们要做其它数据库或服务的集群也是一样,具体随要求为:脚本要有start、stop、status、restart指令,并要求运行正常时返回"0",运行出错时返回"非0"。

5、GFS
上面的HA Cluster的配置文件当中已经有GFS的设定了,GFS要求建立在CLUSTER之上,所以只有在CLUSTER中的节点才能正常地挂载GFS分区,下来我们格式化一个GFS的分区
5.1
在edb1中执行以下操作
把/dev/sdx分为一个分区/dev/sdx1,如果对分区不熟请看“鸟哥”的文章
也可以到www.redhat.com/docs下载REDHAT的官方管理手册进行参考
# mkfs.gfs2 -p lock_dlm -t edb_ha:gfs1 -j 3 /dev/sdx1
-p lock_dlm 定义为DLM锁方式,如果不加此参数,当在两个系统中同时挂载此分区时就会像EXT3格式一样,两个系统的信息不能同步
-t edb_ha:gfs1 DLM锁所在的表名字,edb_ha应与cluster.conf中Cluster的name相同,gfs1为一个自定义的名字我认为可以理解为分区的卷标
-j 3 GFS分区中最多支持多少个节点同时挂载,这个可以在使用中动态调整
/dev/sdx1 要格式化的分区
#make /data
#mount /dev/sdx1 /data
#cp /opt/EnterpriseDB/8.2/data/ /data/edb_data/ -rp
#/etc/init.d/cman start

5.2
在edb2中
# /etc/init.d/iscsid restart
# fdisk -l (看看是否与edb1一样)
# mount /dev/sdx1 /data
# ll /data (看看是否与edb1一样)

5.3测试
在edb1、edb2中
# /etc/init.d/cman start
# /etc/init.d/rgmanager start
(起动集群)
# clustat
(查看集群状态)
可以通过/etc/init.d/edb_8.2 stop关闭edb服务、拔网线、重启系统等对集群进行测试

5.4
在edb1、edb2中
# chkconfig cman on
# chkconfig rgmanager on
以使系统启动时自动开启HA集群

关于REDHAT的集群,请先阅读REDHAT官方的CLUSTER手册,
http://linux.chinaunix.net/bbs/viewthread.php?tid=911781&extra=page%3D1

标签: , ,

Mozilla - Prism

http://wiki.mozilla.org/WebRunner

Prism is a simple XULRunner based browser that hosts web applications without the normal web browser user interface. Prism is based on a concept called Site Specific Browsers (SSB). An SSB is an application with an embedded browser designed to work exclusively with a single web application. It doesn’t have the menus, toolbars and accoutrements of a normal web browser. Some people have called it a "distraction free browser" because none of the typical browser chrome is used. An SSB also has a tighter integration with the OS and desktop than a typical web application running through a web browser.

标签:

如何调用.so中的类

class Base
{
public:
virtual ~Base() {}

virtual void show() = 0;
};

typedef Base* create_obj();
typedef void destroy_obj(Base*);


#include "myso01.h"
#include <iostream>

class ABC : public Base
{
public:
void show();
};

void ABC::show()
{
std::cout << "ABC" << std::endl;
}

extern "C" Base* create()
{
return new ABC;
}

extern "C" void destroy(Base* p)
{
delete p;
}


#include "myso01.h"
#include <iostream>
#include <dlfcn.h>

int main()
{
void* p_lib = dlopen("./myso01.so", RTLD_LAZY);
if (!p_lib)
{
std::cout << dlerror() << std::endl;
return 1;
}


create_obj* create_abc = (create_obj*)dlsym(p_lib, "create");

char* dlsym_error = dlerror();
if (dlsym_error)
{
std::cout << dlsym_error << std::endl;
return 1;
}

destroy_obj* destroy_abc = (destroy_obj*)dlsym(p_lib, "destroy");
dlsym_error = dlerror();
if (dlsym_error)
{
std::cout << dlsym_error << std::endl;
return 1;
}

Base* p_abc = create_abc();

p_abc->show();

destroy_abc(p_abc);

dlclose(p_lib);

return 0;
}

http://bbs.chinaunix.net/thread-1010447-1-1.html

标签:

辽ICP备05003652号
流风洄雪听天籁,轻云蔽日看落花

Powered by Blogger