通过将数据放入EXE的资源信息中的方式制作自已的安装工具
免费的打包工具太千篇一律了,如今是个软件都会有自己的安装界面,别的不说,自行定制UI更好看那是绝对的,至于功能嘛,根本不需要专业安装工具那么多,通常有以下几页就可以了:语言选择、自定义安装位置、文件复制、后续处理、快捷方式等。
关键点:读取资源,然后使用;如果是动态添加的资源则应该在使用前检查。
一、资源导入:在Delphi XE版本中有个工具,可以直接将文件当做资源导入到程序中,编译时会自动编译到资源中去。
通过上面的步骤完成资源导入,则工程文件的program下应该多一行{$R *.dres}
二、资源使用
var
rs: TResourceStream;
begin
rs := TResourceStream.Create(HInstance, ResName, RT_RCDATA);
try
//资源流中可以是图片,文本等数据...
finally
rs.Free;
end;
end;
三、多语言方案
安装包一般是将多种语言集成在一起,由用户选择安装语言,可以先将做一个中文的键值对文件,做为UI字典,通过方法一导入工程中,实现几上公共方法,用于加载字典和翻译:LoadDict,T(Key),这样UI中所有的需要文字描述的地方使用T(Key)的方式取得,以后再添加语言时也无需修改代码。
var
UIDict: TDictionary<string,string> = nil;//全局变量
UICurrentLang: string = 'EN';//当前UI语言
procedure LoadUIDict;
var
rs: TResourceStream;
sl: TStrings;
I: Integer;
UICode: Word;
UIList: string;
ResName, sLine: string;
begin
//从资源中加载语言字典
if not Assigned(UIDict) then
UIDict := TDictionary<string,string>.Create;
sl := TStringList.Create;
try
ResName := Format('UIDICT_', [UICurrentLang]);
rs := TResourceStream.Create(HInstance, ResName, RT_RCDATA);
try
sl.LoadFromStream(rs);
for I := 0 to sl.Count - 1 do
if sl[I] <> '' then
UIDict.AddOrSetValue(SL.Names[I], SL.ValueFromIndex[I]);
finally
rs.Free;
end;
finally
sl.Free;
end;
end;
function T(UIKey: string): string;
begin
//翻译UIKey
if not UIDict.TryGetValue(UIKey, Result) then
Result := '(Unknow)';
end;
如果是文本且用于UI,则需要注意尽量使用UTF8对文本或文件进行编码,否则在非中文操作系统环境下会显示乱码。
四、动态添加资源
主要使用以下几个API实现:BeginUpdateResource、UpdateResource、EndUpdateResource
var
hrSrc: THandle;
ResStream: TMemoryStream;
begin
hrSrc := BeginUpdateResource(PChar(ExeFile), False);//一定是Exe或Dll
if hrSrc = 0 then
begin
ShowMessage('BeginUpdateResource[Error]');
Exit;
end;
//以下这段可以封装并多次使用,比如:添加LOGO图片,添加UI字典,添加程序压缩包等
ResStream := TMemoryStream.Create;
try
ResStream.LoadFromFile('D:\dict_cn.txt');
ResName := 'UIDICT_CN';//例子
if not UpdateResource(hrSrc, RT_RCDATA, PChar(ResName), 0, ResStream.Memory, ResStream.Size) then
begin
ShowMessage('UpdateResource[Fail]');
Exit;
end;
finally
ResStream.Free;
end;
//将之前更新的资源写回文件中
if not EndUpdateResource(hrSrc, False) then
begin
ShowMessage('EndUpdateResource[Error]');
Exit;
end;
end;
function CheckResExists(resList: TArray<string>): Boolean;
var
hrSrc: THandle;
resName: string;
begin
//检查资源ID是否存在,如果是动态添加资源方式,在使用前一定要检查
Result := False;
for resName in resList do
begin
hrSrc := FindResource(HInstance, PChar(resName), RT_RCDATA);
if hrSrc = 0 then
Exit;
FreeResource(hrSrc);
end;
Result := True;
end;
五、高级方案
可以写个控制台的构建工具,比如叫:BuildTool,通过分析配置文件将安装所需的信息动态添加到安装壳程序中
注意事项:如果是添加字符串资源,可以使用TStringStream,并指定编码方式为UTF8或Unicode