NSIS 使用 System.dll 来调用一个外部 DLL

使用 System.dll 来调用一个外部 DLL

某些安装进程需要调用包含于第三方开发的 DLL 中的函数。最佳的例子就是安装一个 Palm™ 通道。

一些关于 System.dll 的背景资料

System.dll 插件 (由 Brainsucker 开发) 使你可以通过提供 Call 函数来调用外部 DLL。有大量的由 System.dll 提供的函数,但是它们并不适合放在这里。若要得到更多关于其它函数的详细信息,请锁上门,取下电话,转到 Contrib/System 目录,并在那里阅读文档。

数据类型
System.dll 可以识别一下数据类型 :

v - void (常用于返回)
i - int (包括 char、byte、short、句柄、指针等等)
l - long 型整数 (已知的为 int64)
t - 文本、字串 (LPCSTR、第一个字符的指针)
b - 布尔 (需要返回 true 或 false) - 事实上这种类型没有什么意义 -> 通常可以用整数来代替 (‘0’:‘1’)
k - 回调。查看 system.html 的回调章节。

    • 指针 -> Proc 需要这种类型的指针,这会影响下一个字符 (参数) [例如: ‘*i’ - 整数指针]
      将 System.dll 变量映射为 NSIS 里的内置变量
      如果你不需要返回任何数据,那么这里没有什么可以用来调用一个外部函数的指向。System.dll 可以通过以下方式将函数变量映射为 NSIS 脚本变量:

NSIS $0…$9 可与 System.dll r0…r9 相对应;NSIS $R0…$R9 也可与 System.dll r10…r19 相对应

每个参数由类型,输入和输出指定。可以用一个点跳过输入或输出。例如:

字符串 (一个字符数组的指针),输入为 ‘轻松调用’ :

t ‘轻松调用’
字符串 (一个字符数组的指针), 输入从 $5 取值,并且经转换为由调用产生的数组后,最后保存到 $R8 :

t r5R8
一个整数指针,从 $1 取值并放入 $2 :

*i r1r2
一个 64-bit 整数指针,输出送入堆栈,没有输入 :

*l .s
通过 System.dll::Call 调用第三方 DLL 中的一个函数,Call 的用法如下 :

System::Call ‘YourDllName::YourDllFunction(i, *i, t) i(r0, .r1, r2) .r3’

后面的的 (r0, .r1, r2) .r3 段是用来传送 DLL 和 NSIS 脚本之间的参数。就像在这个参数列表中看到的一样,类型和输入、输出是可以分开的。每个 “(参数列表) 返回值” 块不用考虑和/或添加上一个。在这里,第一块指定类型而第二块指定输入和输出。

开始编辑 NSIS 脚本之前
在开始编辑 NSIS 代码之前,首先需要了解要调用函数的原型。在这个例子里,我们使用 Palm ‘CondMgr.dll’ 中的’CmGetHotSyncExecPath’ 函数。这个函数用来返回 ‘HotSync.exe’ 的完整路径。

函数定义

int CmGetHotSyncExecPath(TCHAR *pPath, int *piSize);

参数

pPath 是一个指向字符 buffer 的指针。在返回前这是已安装的 HotSync manager 的路径文件名。
piSize 是一个指向由 pPath 参数引用的 buffer 大小 (TCHAR 类型) 的整数指针。
返回值:

0: 无措误
-1: 产生不明确的错误
ERR_REGISTRY_ACCESS(-1006): 不能访问 Palm 配置项
ERR_BUFFER_TOO_SMALL(-1010): buffer 太小而不能保存请求的信息
ERR_INVALID_POINTER(-1013): 指定的指针不是一个有效的指针
同样的,如果 buffer 太小,*int 中的值为缓冲应当的大小 (TCHAR 类型)。

上面的函数的定义转换到 System.dll 定义:

CmGetHotSyncExecPath(t, *i) i

也就是,它有一个文本变量,一个整数指针,并返回一个整数值。

使用外部 DLL 函数
在选出了函数所要做的事情,以及它是如何转换到 System.dll 格式之后,就可以在 NSIS 脚本中使用这个函数了。

首先,推荐在需要多次调用 System.dll 时,禁止插件卸载。 根据 Brainsucker (以及其他人) 的说法,这样可以提高安装包的执行速度 (减少了卸载 - 重新装载的时间)。

其次,你必须将输出目录更改为你要用的 DLL 所在的目录。如果 DLL 在系统路径中,那么它也能工作,但并没有经过测试。

下面的代码段会将 condmgr.dll 释放到一个临时目录中,然后执行 CmGetHotSyncExecPath 函数,显示返回的数据并最终卸载 System.dll 插件。[code]; **** snip ****
SetPluginUnload alwaysoff

Function loadDll

SetOutPath $TEMP\eInspect ; 创建临时目录
File bin\CondMgr.dll ; 释放 DLL
StrCpy $1 ${NSIS_MAX_STRLEN} ; assign memory to $0
System::Call ‘CondMgr::CmGetHotSyncExecPath(t, *i) i(.r0, r1r1).r2’
DetailPrint ‘路径: “$0”’
DetailPrint “路径长度: $1”
DetailPrint “返回值: $2”

; 最后一次插件调用必须没有 /NOUNLOAD 参数,否则 NSIS 将不能在安装结束后删除
; 临时的 DLL 文件

SetPluginUnload manual
; 什么都不作 (目的就是为了卸载 System.dll)
System::Free 0
FunctionEnd
; **** snip ****[/code]这个函数将在查看详细信息页面产生下面的输出:

输出目录: c:\windows\TEMP\eInspect
解压: CondMgr.dll
路径: “C:\Dave\palm\Hotsync.exe”
路径长度: 24
返回值: 0