当前位置: 首页 > 文档资料 > NSIS 用户手册 >

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

优质
小牛编辑
133浏览
2023-12-01

某些安装进程需要调用包含于第三方开发的 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 插件。祝你好运!