打补丁,我打补丁,你打补丁
这将向您展示如何使用 Delphi 生成和应用您自己的补丁。
引言
我们都见过:应用程序更新软件并允许您“回滚”您的更改。本文将向您展示如何使用 Microsoft PatchAPI 接口在 Delphi 中实现这一点。主对象(稍后讨论)应该可以直接使用,而无需您添加任何关于补丁选项的内容。
背景
我一直想创建自己的安装包和所有这些东西。我认为每个人都想。我一直认为这是一种神奇的事情,只是“发生了”,而您必须使用预先构建的安装程序,而您需要安装这些安装程序才能构建它们。本文不向您展示如何制作一个功能齐全的应用程序安装程序生成器,但它将让您了解如何创建旧文件版本和新文件版本之间的补丁。本文还将向您展示如何使用 DLL 文件。
与我之前的文章不同,我不会展示太多代码,因为我认为我已经很好地注释了源代码。
那么代码中发生了什么?
这篇文章比我早期的文章更偏向于高层次,但如果您有开发背景,您应该能理解它的要点 :)。您会注意到源代码附带了两个 DLL 文件,分别是 mspatcha.dll 和 mspatchc.dll。您可能不需要这些 DLL 文件,但为了方便起见,我将它们添加了。前者用于应用补丁,后者用于创建补丁(稍后详述)。首先,第一件事
PatchAPI
该文件包含原始 DLL 文件实际允许您做什么的主要定义。它定义了所有常量、函数以及对上述 DLL 的实际 DLL 调用定义。有关 PatchAPI 的更多信息,请在此处阅读 更多信息。
uPatcher
这是我生成的包装器类,用于减轻生成补丁文件和应用这些补丁的负担。它包含 Delphi 特定的设置值、自定义事件,甚至自定义异常类。我已经尽力注释了这个单元,所以如果有人需要任何其他关于正在发生的事情的解释,请告诉我,我很乐意为您提供帮助。
Unit1
这显然是应用程序的主屏幕。它允许您添加/编辑/删除要打补丁的文件,以及创建/应用补丁。
使用代码
您最想使用的是 TPatcher
类。它提供了创建和应用补丁文件最简单有效的方法,而无需处理 PatchAPI
类。我创建了四个(自定义)事件,它们的作用如下:
TOnPatchProgress
:显示您创建或应用补丁时的进度TOnPatchComplete
:在创建或应用补丁完成后发生TOnPatchFileBegin
:在打补丁或创建文件时发生TOnPatchFileEnd
:在文件打补丁或创建结束时发生
别担心,如果您不实现这些事件,补丁程序也能正常工作 :)。事实上,补丁程序在您除了添加要打补丁的文件之外什么都不做的情况下也能正常工作。为了方便起见,我已经实现了所有四个自定义事件,以便您可以根据自己的需求定义它们。理论上,如果您创建一个文件的补丁,这个补丁实际上可以双向进行。如果我没记错的话,通过本文,您可以只通过按照您认为合适的方式应用差异来使一个文件成为旧版本和新版本。这就是(我认为)任何安装应用程序在应用和回滚补丁方面的做法。
作为参考,我将演示如何定义和实现其中一个自定义事件。
TOnPatchProgress = procedure(
ASender : TObject;
const ACurrentPosition : LongWord;
const AMaximumPosition : LongWord;
var ACanContinue : LongBool) of object;
这是创建或打补丁文件的进度的定义(显然)。所以您需要做的就是创建一个对应的事件定义,如下所示(基于我的演示应用程序):
procedure TPatcherDemo.DoPatcherProgress(ASender : TObject;
const ACurrentPosition : LongWord;
const AMaximumPosition : LongWord;
var ACanContinue : LongBool);
var
LStr : string;
begin
if AMaximumPosition <> ProgressBar1.Max then
ProgressBar1.Max := AMaximumPosition;
if ACurrentPosition <> ProgressBar1.Position then
ProgressBar1.Position := ACurrentPosition;
LStr := 'Complete: ' + FormatFloat('#,##0', ACurrentPosition) +
' of ' + FormatFloat('#,##0', AMaximumPosition);
mmo1.Lines.Add(LStr);
Application.ProcessMessages;
end;
当您单步执行程序时,您会看到每次创建/应用补丁时都会发生的事情都会触发它。
我还引入了常量类型数组的概念。所有这些基本上意味着,基于类型定义,您可以根据您定义的类型创建一个常量数组。基于此,我创建了如下基于类型定义的字符串定义:
type TPatchApplyOption = ( paoFailIfExact, // = APPLY_OPTION_FAIL_IF_EXACT paoFailIfClose, // = APPLY_OPTION_FAIL_IF_CLOSE paoTestOnly, // = APPLY_OPTION_TEST_ONLY paoValidFlags // = APPLY_OPTION_VALID_FLAGS ); TPatchApplyOptions = set of TPatchApplyOption; const Descriptions_TPatchApplyOption : array[Low(TPatchApplyOption)..High(TPatchApplyOption)] of string = ( 'If the source file and the target are the same, return a failure and don'#39't create the target.', 'If the source file and the target differ by only rebase and bind information (that is, they have the same normalized signature), return a failure and don'#39't create the target.', 'Don'#39't create the target.', 'The logical OR of all valid patch apply flags.' );
您将在演示应用程序中看到这实际上是如何工作的,但您很快就会发现我在哪里使用它 :)。
此对象还允许您使用不同类型的输入选项但我只使用了 ANSI(大部分)和 Unicode(有时)。另一个选项是通过句柄,我从未测试过。所以,如果您觉得您想使用它并发现问题,请告诉我。
关注点
我在将多个文件插入单个补丁文件时遇到了一些麻烦。补丁程序还允许您使用符号文件以及 Windows 可用的任何内容。我只是还没有机会全部实现。如果有人希望我添加更多功能,请告诉我,我很乐意更新这篇文章。
Microsoft(目前)使用两种形式的补丁接口:PatchAPI 和 MSDelta。前者从 XP 及以下版本开始,后者从 Vista 及以上版本开始。我发现的一个问题是,如果您阅读了我在文章开头链接的关于补丁的文章,PatchAPI 在 Delphi 7 中工作得很好(我没有在更新版本上尝试过),但 MSDelta 在 CBuilder 中工作得很好,但显然它们不能互换。我很快就会发布我关于 MSDelta 的 CBuilder 文章,所以请继续关注 :)。如果有人能就将它们构建到其中一个或两个中提出建议,请告诉我。