Delphi中資源文件的使用
在Delphi中,生成一個Appliction工程時,會默認生成一個與工程同名的資源文件,即使刪除也會再度創建,但是這個資源文件中只有一個圖標和一個版本信息。
通常情況下,我們還需要更多的多種多樣的資源,雖然可以在IDE中載入並編譯到EXE文件中去,但是有時我們需要將資源與EXE分開,以便生成多語言程序或將程序改爲其它語言(如漢化)。
Delphi附帶的ImageEdit可以編輯資源文件,但只能編輯位圖、圖標和光標,無法加入字符串資源,而且只持256色的圖像。爲了將更多種類的資源,只有編寫資源腳本或者使用其它資源編輯器,如Visual Stadio 6。不過,Visual Stadio 6編輯的資源文件中包含了C++頭文件定義,並且支持的具體資源類型較多,在Delphi中是無法識別的。
只好選擇編寫資源腳本了,資源腳本文件擴展名爲.RC,可以用純文本編輯器編寫。如下面是兩個圖標與兩個字符串的腳本:
COMPUTER RCDATA "computer.ico" /* Delphi中只支持RCDATA類型 */
WINDOWSXP RCDATA "XP.ico"
STRINGTABLE
BEGIN
1 "computer(電腦)"
2 "Windows XP"
END
將其保存爲MyRes.rc,需要注意的是,這裏包含了兩個圖標,注意文件名及路徑(這裏是與文件同一目錄)。在命令提示符窗口中將目錄切換到MyRes.rc所在的路徑,運行brcc32 MyRes.rc,其中brcc32.exe是Delphi附帶的資源編譯工具。如果想將資源文件生成其中擴展名的文件(如.DLL),可以增加-fo參數,如:brcc32 MyRes.rc –of MyRes.dll。
接下來是對資源的引用,引用方法有靜態與動態兩種,靜態引用就是將資源文件包含到源碼中編譯到EXE中去;動態引用則是把資源文件當成DLL動態裝載。
靜態引用資源文件在Delphi中是最簡單不過了,只要在工程文件中加入一個編譯指令即可,如:
program Multi_Lang;
uses
Forms,
TestFormUnit in 'TestFormUnit.pas' {TestForm};
{$R MyRes.dll} // 包含自定義的資源文件
{$R *.res} // 包含默認的資源文件
begin
Application.Initialize;
Application.CreateForm(TTestForm, TestForm);
Application.Run;
end.
引用時,字符串只要LoadStr(Index)即可,而其它資源用TResourceStream讀取,只不過資源句柄就是程序本身,直接用hInstance就可以了。如:
procedure TTestForm.ReadDirect(Index: Integer);
var
Stream: TResourceStream;
Icon: TIcon;
S: String;
begin
Edit1.Text:= LoadStr(Index); // 在TEdit控件中顯示所獲取的字符串
if Index = 1 then
S:= 'COMPUTER'
else
S:= 'WINDOWSXP';
// Delphi中只支持RT_RCDATA類型讀取
Stream:= TResourceStream.Create(hInstance, S, RT_RCDATA); // 使用hInstance句柄
try
Icon:= TIcon.Create;
try
Icon.LoadFromStream(Stream);
Image1.Picture.Icon.Assign(Icon); // 是TImage控件中顯示所獲取的圖標
finally
Icon.Free;
end;
finally
Stream.Free;
end;
end;
這種引用方式在編譯就將資源與EXE文件鏈接在一起,而編譯之後就不再需要資源文件了。
動態引用方式相對更多加靈活,編譯時不需要資源文件,而是在運行時動態地入,這樣便可以控件載入不同的資源文件,這也是實現多語言程序的一種方法。
動態引用實際上只是將資源文件靜態包含到另一個工程(一般爲DLL程序)中,如:
library MyResDll;
uses
SysUtils,
Classes;
{$R MyRes.dll} // 將資源文件包含到DLL工程中
ResourceString
S1 = '電腦';
S2 = '視窗';
Begin
End.
引用時像普通的DLL引用一樣,將其裝載後得到一個句柄,再用靜態引用的方法,使用TResourceStream來讀取。如:
procedure TTestForm.ReadDynamic(Index: Integer);
var
Hnd: THandle;
Stream: TResourceStream;
Icon: TIcon;
S: String;
Buf: PChar;
begin
Hnd:= LoadLibrary('MyResDll.dll'); // 裝載資源文件
if Hnd > 0 then
begin
GetMem(Buf, 255);
LoadString(Hnd, Index, Buf, 255); // 使用LoadString裝載指定句柄的字符串
Edit1.Text:= StrPas(Buf);
if Index = 1 then
S:= 'COMPUTER'
else
S:= 'WINDOWSXP';
Stream:= TResourceStream.Create(Hnd, S, RT_RCDATA); // 使用裝載得到的句柄
try
Icon:= TIcon.Create;
try
Icon.LoadFromStream(Stream);
Image1.Picture.Icon.Assign(Icon);
finally
Icon.Free;
end;
finally
FreeMem(Buf, 255);
Stream.Free;
end;
FreeLibrary(Hnd);
end;
end;
這種方法似乎有點複雜。^_^。
將上面的方法變一下,將裝載字符串的函數聲明在DLL文件中,如:
library MyResDll;
uses
SysUtils,
Classes;
{$R MyRes.dll}
ResourceString
S1 = '電腦';
S2 = '視窗';
const
ResStr: array[0..1] of String = (S1, S2); // 將字符串放入數組常量
{ 聲明函數獲取字符串個數}
function GetStrCount: Integer; stdcall;
begin
Result:= Length(ResStr);
end;
{ 聲明函數獲取指定索引號的字符串 }
function GetResStr(Index: Integer): PChar; stdcall;
begin
if (Index >= Low(ResStr)) and (Index <= High(ResStr)) then
Result:= PChar(ResStr[Index])
else
Result:= '';
end;
exports
GetStrCount,
GetResStr;
begin
end.
引用的方法與一般的函數庫引用方法一樣。如:
type
TGetResStr = function(Index: Integer): PChar; stdcall;
procedure TTestForm.ReadSourceDef(Index: Integer);
var
Hnd: THandle;
GetResStr: TGetResStr;
begin
Hnd:= LoadLibrary('MyResDll.dll');
if Hnd > 0 then
begin
@GetResStr:= GetProcAddress(Hnd, 'GetResStr');
Edit1.Text:= GetResStr(Index);
FreeLibrary(Hnd);
end;
end;
以上代碼在Windows XP Professional + Delphi7下測試通過,如需完整的源碼請mail我:[email protected]。