介绍
VPatch 允许你创建补丁文件来把你的软件的老版本更新到新版本。GenPat 工具可以产生补丁文件。而这个插件可以使用生成的补丁文件来更新老的文件。使用补丁你可以减少更新时占用的文件体积,因为补丁文件里只包含了和旧版本的文件不同的部分。
如何使用
生成补丁文件
首先确认你已经有了原始文件(老版本的文件)和目标文件(新版本的文件)。例如: DATA.DTA 是用户现在使用的文件而 DATA_20.DTA 是新版本 2.0 的文件。现在使用如下命令行来生成补丁文件:
GENPAT data.dta data_20.dta data.pat
不出意外的话过一些时间后补丁就生成了。
对 GenPat 工具使用 /B=(区块大小) 参数(放到文件名后边)可以调整区块的大小。小的区块可以得到更小的补丁,但是会需要更多的时间来生成(默认的区块大小为 64)。
如果你不习惯使用这个命令行的工具的话你可以到我的网站上面下载 GUI: http://www.tibed.net/vpatch.
安装时更新文件
使用 VPatch 插件来用补丁更新文件:
vpatch::vpatchfile "pathfile.pat" "source.file" "new.file"
补丁的结果会添加到堆栈,可能的结果如下:
- OK
- OK, new version already installed
- An error occured while patching
- Patch data is invalid or corrupt
- No suitable patches were found
例子请参考 example.nsi。
一个补丁文件里包含多个补丁
GenPat 会把指定的补丁附加到文件。如果补丁里的原始文件相同那么该份补丁将会被替换。例如,如果你要把版本 1 和 版本 2 都更新到版本 3 你只需要把 1 到 3 和 2 到 3 的补丁放在一个文件里就行了。
你也可以把不同的文件的补丁放在一个补丁文件里,例如,文件 A 版本 1 到 文件 A 版本2 的补丁和文件 B 版本 1 到 文件 B 版本2 的补丁放在一个补丁文件里。你只需要对同一个补丁文件多次调用 VPatch 插件即可。他会自己选择正确的补丁 - 取决于 CRC 交验。
GenPat 退出代码
从 2.1 版本开始支持退出代码,就像 DOS 里的 error levels 一样,你可以在生成补丁时检测退出代码。下面是退出代码的列表:
退出代码 | 说明 |
0 | 成功 |
1 | 变量丢失 |
2 | 找不到原文件 |
3 | 找不到目标文件 |
4 | 读取已存在的补丁文件时发生未知错误 |
5 | 生成补丁时发生未知错误 |
6 | 写入补丁文件到磁盘时发生未知错误 |
10 | 原文件的 CRC 和目标文件的 CRC 相等 |
11 | 没有足够的内存给原文件使用 |
12 | 没有足够的内存给目标文件使用 |
这些退出代码可以在 NSIS 里检测。
VPatch 在 INNO 中的应用。
VPatch 属于专为 NSIS 开发的补丁插件,通过本人开发的调用插件 callvpatch.dll 进行调用。在 INNO 中得到很好的应用。(注意:你可以在 VPatch 的官方网站“http://www.tibed.net/vpatch/”得到源代码,从而编译成一个适合在 INNO 中直接利用的 DLL,这里我为了通用性跟方便性,使用了 NSIS 通用调用插件,方便以后随时在 NSIS 的安装包中得到 VPatch 的已编译的 DLL)
以下是在本例子中的练习步骤,通过这个练习,你将会领略到 VPatch 在补丁安装程序方面的巨大作用。
测试包中的文件分布
以下是引用片段:
VPatch(DIR)
| |-- EnglishAppSetup(DIR)
| | |---- Example1.iss --英文版软件安装脚本
| | |---- MyProg.exe --英文版软件主程序
| | |---- MyProg.hlp --英文版软件帮助
| | |---- Readme.txt --英文版软件自读文件
| | |---- English.isl --英文版软件安装程序英文界面语言文件
| |
| |
| |-- ChineseApp(DIR)
| | |---- MyProg.exe --汉化版软件中文主程序
| | |---- MyProg.hlp --汉化版软件中文帮助
| | |---- Readme.txt --汉化版软件中文自读文件
| |
| |
| |-- PatchSetup(DIR)
| |---- callvpatch.dll --我编写的调用 vpatch.dll 的中间插件
| |---- Patch.iss --补丁安装程序脚本
| |---- VPatch.dll --NSIS 补丁插件
|
|
|---- GenPat.exe --补丁文件生成工具
|---- Readme.txt --你正在看的本文件
|---- VPatch.html --VPatch 概述
1. 目录下的 EnglishAppSetup 里面是一个英文版例子的安装程序,你需要自己编译成安装程序进行安装。
2. 接着我们安装第一步产生的英文版安装程序,……,好了,现在我们已经安装了一个英文版程序在你的电脑中了。
3. 目录下的 ChineseApp 里面是汉化好的文件,这里的汉化版软件不会直接打包进安装程序的,这只是用来生成补丁文件时用的。
4. 好,现在看看补丁的生成。VPatch 有一个生成补丁的程序,GenPat.exe (可在 NSIS/Bin 目录下找到)
关于 GenPat.exe 的详细使用方法请看 VPatch.html 文档。
命令行生成补丁:
进入 VPatch 目录,输入如下命令:
以下是代码片段:
GenPat "EnglishAppSetup/MyProg.exe" "ChineseApp/MyProg_cn.exe" "PatchSetup/MyPatch.dat"
GenPat "EnglishAppSetup/MyProg.hlp" "ChineseApp/MyProg_cn.hlp" "PatchSetup/MyPatch.dat"
GenPat "EnglishAppSetup/Readme.txt" "ChineseApp/Readme_cn.txt" "PatchSetup/MyPatch.dat"
注意:以上全部命令连续执行,GenPat 会自动把所有这些文件的补丁合成在 MyPatch.dat 中。
5. 如无意外 MyPatch.dat 会在 PatchSetup 目录中产生,补丁生成后,进行下一步的补丁程序安装制作了。
6. 脚本如下,功能包含了自动检测以前的安装路径,自动备份原文件。
以下是代码片段:
; -- Example1.iss --
; 补丁安装程序制作脚本
; 脚本编写:restools (http://restools.yeah.net)
[Setup]
AppName=Test Program
AppVerName=Test Program version 1.5
UsePreviousAppDir=yes
DefaultDirName={pf}/My Program
Uninstallable=false
Compression=lzma
SolidCompression=yes
OutputDir=.
[Files]
Source: "callvpatch.dll"; Flags: dontcopy
Source: "VPatch.dll"; Flags: dontcopy
Source: "MyPatch.dat"; Flags: dontcopy
[code]
const
BackupDir = ’PatchBackup’;
PatchFile = ’MyPatch.dat’;
function vpatch(parentwnd: Integer; pluginname,funcname,param1,param2,param3: PChar): Integer;
external ’vpatch@files:callvpatch.dll stdcall’;
function PatchFileFunc(FileName: String): Integer;
begin
Result := vpatch(0,ExpandConstant(’{tmp}/VPatch.dll’),’vpatchfile’,ExpandConstant(’{tmp}/’+PatchFile),
ExpandConstant(’{app}/’+BackupDir+’/’+FileName),ExpandConstant(’{app}/’+FileName));
// Result :
// 0 = 成功
// 1 = 失败
end;
procedure BackupFile(FileName: String);
begin
FileCopy(ExpandConstant(’{app}/’+Filename),ExpandConstant(’{app}/’+BackupDir+’/’+Filename),True);
end;
procedure RestoreFile(FileName: String);
begin
FileCopy(ExpandConstant(’{app}/’+BackupDir+’/’+Filename),ExpandConstant(’{app}/’+Filename),False);
DeleteFile(ExpandConstant(’{app}/’+BackupDir+’/’+Filename));
end;
procedure InitializeWizard();
begin
ExtractTemporaryFile(’VPatch.dll’);
ExtractTemporaryFile(’MyPatch.dat’);
end;
function NextButtonClick(CurPageID: Integer): Boolean;
begin
if CurPageID = wpSelectDir then
Result := FileExists(ExpandConstant(’{app}/MyProg.exe’))
else
Result := True;
if not Result then
MsgBox(’你选择的目录没有需要补丁的程序,请选择正确的目录!’, mbInformation, MB_OK);
end;
procedure CurPageChanged(CurPageID: Integer);
begin
if CurPageID = wpInstalling then
begin
CreateDir(ExpandConstant(’{app}/’+BackupDir));
BackupFile(’MyProg.exe’);
if PatchFileFunc(’MyProg.exe’) = 1 then
RestoreFile(’MyProg.exe’)
WizardForm.PROGRESSGAUGE.POSITION := 33;
BackupFile(’MyProg.hlp’);
if PatchFileFunc(’MyProg.hlp’) = 1 then
RestoreFile(’MyProg.hlp’)
WizardForm.PROGRESSGAUGE.POSITION := 66;
BackupFile(’Readme.txt’);
if PatchFileFunc(’Readme.txt’) = 1 then
RestoreFile(’Readme.txt’)
WizardForm.PROGRESSGAUGE.POSITION := 100;
end;
end;
以下是引用片段: VPatch(DIR) | |-- EnglishAppSetup(DIR) | | |---- Example1.iss --英文版软件安装脚本 | | |---- MyProg.exe --英文版软件主程序 | | |---- MyProg.hlp --英文版软件帮助 | | |---- Readme.txt --英文版软件自读文件 | | |---- English.isl --英文版软件安装程序英文界面语言文件 | | | | | |-- ChineseApp(DIR) | | |---- MyProg.exe --汉化版软件中文主程序 | | |---- MyProg.hlp --汉化版软件中文帮助 | | |---- Readme.txt --汉化版软件中文自读文件 | | | | | |-- PatchSetup(DIR) | |---- callvpatch.dll --我编写的调用 vpatch.dll 的中间插件 | |---- Patch.iss --补丁安装程序脚本 | |---- VPatch.dll --NSIS 补丁插件 | | |---- GenPat.exe --补丁文件生成工具 |---- Readme.txt --你正在看的本文件 |---- VPatch.html --VPatch 概述 |
以下是代码片段: GenPat "EnglishAppSetup/MyProg.exe" "ChineseApp/MyProg_cn.exe" "PatchSetup/MyPatch.dat" GenPat "EnglishAppSetup/MyProg.hlp" "ChineseApp/MyProg_cn.hlp" "PatchSetup/MyPatch.dat" GenPat "EnglishAppSetup/Readme.txt" "ChineseApp/Readme_cn.txt" "PatchSetup/MyPatch.dat" |
以下是代码片段: ; -- Example1.iss -- ; 补丁安装程序制作脚本 ; 脚本编写:restools (http://restools.yeah.net) [Setup] AppName=Test Program AppVerName=Test Program version 1.5 UsePreviousAppDir=yes DefaultDirName={pf}/My Program Uninstallable=false Compression=lzma SolidCompression=yes OutputDir=. [Files] Source: "callvpatch.dll"; Flags: dontcopy Source: "VPatch.dll"; Flags: dontcopy Source: "MyPatch.dat"; Flags: dontcopy [code] const BackupDir = ’PatchBackup’; PatchFile = ’MyPatch.dat’; function vpatch(parentwnd: Integer; pluginname,funcname,param1,param2,param3: PChar): Integer; external ’vpatch@files:callvpatch.dll stdcall’; function PatchFileFunc(FileName: String): Integer; begin Result := vpatch(0,ExpandConstant(’{tmp}/VPatch.dll’),’vpatchfile’,ExpandConstant(’{tmp}/’+PatchFile), ExpandConstant(’{app}/’+BackupDir+’/’+FileName),ExpandConstant(’{app}/’+FileName)); // Result : // 0 = 成功 // 1 = 失败 end; procedure BackupFile(FileName: String); begin FileCopy(ExpandConstant(’{app}/’+Filename),ExpandConstant(’{app}/’+BackupDir+’/’+Filename),True); end; procedure RestoreFile(FileName: String); begin FileCopy(ExpandConstant(’{app}/’+BackupDir+’/’+Filename),ExpandConstant(’{app}/’+Filename),False); DeleteFile(ExpandConstant(’{app}/’+BackupDir+’/’+Filename)); end; procedure InitializeWizard(); begin ExtractTemporaryFile(’VPatch.dll’); ExtractTemporaryFile(’MyPatch.dat’); end; function NextButtonClick(CurPageID: Integer): Boolean; begin if CurPageID = wpSelectDir then Result := FileExists(ExpandConstant(’{app}/MyProg.exe’)) else Result := True; if not Result then MsgBox(’你选择的目录没有需要补丁的程序,请选择正确的目录!’, mbInformation, MB_OK); end; procedure CurPageChanged(CurPageID: Integer); begin if CurPageID = wpInstalling then begin CreateDir(ExpandConstant(’{app}/’+BackupDir)); BackupFile(’MyProg.exe’); if PatchFileFunc(’MyProg.exe’) = 1 then RestoreFile(’MyProg.exe’) WizardForm.PROGRESSGAUGE.POSITION := 33; BackupFile(’MyProg.hlp’); if PatchFileFunc(’MyProg.hlp’) = 1 then RestoreFile(’MyProg.hlp’) WizardForm.PROGRESSGAUGE.POSITION := 66; BackupFile(’Readme.txt’); if PatchFileFunc(’Readme.txt’) = 1 then RestoreFile(’Readme.txt’) WizardForm.PROGRESSGAUGE.POSITION := 100; end; end; |