D.3 使用 System.dll 来调用一个外部 DLL
某些安装进程需要调用包含于第三方开发的 DLL 中的函数。最佳的例子就是安装一个 Palm(TM) 通道。
一些关于 System.dll 的背景资料
System.dll 插件 (由 Brainsucker 开发) 使你可以通过提供 Call 函数来调用外部 DLL。有大量的由 System.dll 提供的函数,但是它们并不适合放在这里。若要得到更多关于其它函数的详细信息,请锁上门,取下电话,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 插件。
; **** 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 ****
这个函数将在查看详细信息页面产生下面的输出:
输出目录:c:\windows\TEMP\eInspect
解压:CondMgr.dll
路径:"C:\Dave\palm\Hotsync.exe"
路径长度:24
返回值:0
Written by djc
感谢
在此感谢 kichik 和 Sunjammer 花费了大量时间解决这些问题。同时感谢 brainsucker 首先做了这个 System.dll 插件。祝你好运!