NSIS 系统插件

原文:https://nsis.sourceforge.io/Docs/System/System.html

抄抄写写的翻译

NSIS 系统插件

目录

•      介绍

•      可用功能

o     内存相关功能

o     调用函数

o     64 位函数

•      常见问题

介绍

系统插件使开发人员能够调用任何 DLL 的任何导出函数。例如,您可以使用系统插件调用GetLogicalDriveString来获取用户计算机上的可用驱动器的列表。

系统插件还允许开发人员分配、释放和拷贝内存;与 COM 对象交互,并在 64 位整数上执行数学运算。

强烈建议使用编程知识,以便更好地了解系统插件。

可用功能

内存相关功能

•Alloc SIZE

分配SIZE字节并在堆栈上返回内存地址。

使用示例

System::Alloc 64

Pop $0

DetailPrint "64 bytes allocated at $0"

System::Free $0

•StrAlloc SIZE

为 SIZE TCHAR 分配一个字符串缓冲区,并在堆栈上返回内存地址。如果您想要编写适用于 ANSI 和 Unicode NSIS 的 NSI 脚本,这非常有用。

使用示例

System::StrAlloc 64 ; String buffer for 63 characters and \0 termination.

Pop $0

DetailPrint "A string buffer for 64 characters allocated at $0"

System::Free $0

•Copy [/SIZE] DESTINATION SOURCE

将大小字节从SOURCE拷贝到DESTINATION。如果未指定SIZE,则SOURCE的大小将使用 GlobalSize 查询。这意味着,如果您不使用System::Alloc、System::Alloc 或者 GlobalAlloc 分配内存,则必须指定SIZE。 如果DESTINATION 为NULL,它将被分配,返回内存地址在堆栈上。

示例

# allocate a buffer and put 'test string' and an int in it

System::Call "*(&t1024 'test string', i 5) p .s"

Pop $0

# copy to an automatically created buffer

System::Copy 0 $0

Pop $1

# get string and int in $1 buffer

System::Call "*$1(&t1024 .r2, i .r3)"

# free buffer

System::Free $1

# print result

DetailPrint $2

DetailPrint $3

# copy to our own buffer

System::Alloc 1028

Pop $1

System::Copy $1 $0

# get string and int in $1 buffer

System::Call "*$1(&t1024 .r2, i .r3)"

# free

System::Free $0

System::Free $1

# print result

DetailPrint $2

DetailPrint $3

释放内存

示例

System::Alloc 64

Pop $0

DetailPrint "64 bytes allocated at $0"

System::Free $0

•Store "OPERATION [OPERATION [OPERATION ...]]"

堆栈操作。操作可以从 NSIS 堆栈Push或 Pop单个寄存器,或从 System 的专用堆栈中Push或Pop所有寄存器($0-$9和 $R0-$R9)。操作可以由任何字符分隔。

Available Operations

o     执行push s#,使用p#,#是0~9 的一个数字

o     执行pop s#,使用r#,#是0~9 的一个数字

o     执行push $R#,使用P#.,#是0~9 的一个数字

o     执行 pop $R#,使用 R#,#是0~9 的一个数字

o     当push $0-$9 和 $R0-$R9系统私有堆栈时, 使用 s 或S.

o     当pop $0-$9 和 $R0-$R9系统私有堆栈时, 使用l 或L.

示例:

StrCpy $0 "test"

System::Store "p0"

Pop $1

DetailPrint "$0 = $1"

StrCpy $2 "test"

System::Store "p2 R2"

DetailPrint "$2 = $R2"

StrCpy $3 "test"

System::Store "s"

StrCpy $3 "another test"

System::Store "l"

DetailPrint $3

System::Store "r4" "test"

DetailPrint $4

Calling Functions

•      Call PROC [( PARAMS ) [RETURN [? OPTIONS]]]

•      Get PROC [( PARAMS ) [RETURN [? OPTIONS]]]

Call和Get 两者语法相同。正如名称所描述,Call 就是调用Gets 获取。Call或Get 什么?这取决于PROC的值。

PARAMS是参数的列表,以及它们与参数的处理方法。您可以在参数中传递数据和获取数据。参数列表用逗号分隔。

每个参数由三个值组合:类型、源和目标。

类型可以是整数、字符串等,

源:就是参数值的源,可以是一个 NSIS 寄存器($0,$1,$INSTDIR)、NSIS堆栈、具体值 (5、"test"等)或无(null)。

目标:就是调用返回后参数值的目标,可以是 NSIS 变量、NSIS 堆栈或空。如果不需要源或目标之一,也可以用一个点 ('.')表示。

RETURN就像单个参数定义,但源仅在创建回调函数时使用。通常源是一个点。

OPTIONS是控制系统插件行为方式的选项列表。每个选项都可以通过使用感叹号前缀来关闭。例如:? !e.

PARAMS,RETURN和OPTIONS 可以在一个Get/Call调用中重复多次。重复时,可以省略很多,只使用您希望更改的。可以忽略每个参数的类型、源和/或目标,甚至返回值。可以添加或删除选项。这允许您定义函数原型并保存一些类型。最后两个示例显示了这一点。

 

PROC也可以重复,但必须用('#')前缀,除非(‘#’)符号前面有一个双冒号(::),在这种情况下,它被解释为函数序号。如: shell32::#18

可能的PROC 值和意义

值          

意义

例子

DLL::FUNC

DLL输出的函数

user32::MessageBox

::ADDR      

地址为ADDR的函数

看下面

*ADDR     

地址为ADDR的结构

同上

*            

新结构

同上

IPTR->IDX    

成员索引 IDX 从界面由IPTR指向

同上

<nothing>    

新的回调函数

同上

PROC        

由Get 返回的PROC

同上

有效的参数类型

类型      

意义

v    

void (常用于返回)

p

指针 (和其他已定义大小类型的指针,比如:handles 、HWNDs)

b

Int8 ,byte 类型

h

Int16 ,short 类型

i

Int32(包括 char、byte、short、句柄、指针等等)  

I

Int64,长整数

m

ANSI text,string

t

文本,字符串(LPCSTR,指向第一个字符的指针),如TCHAR* ,在UNICODE NSIS 是一个UNICODE 字符串

w

WCHAR 文本,UNICODE字符串

g

GUID

k

回调

@

直接寄存器内存访问(Buffer 限制于字节大小) (NSIS_MAX_STRLEN - 24) * NSIS_CHAR_SIZE 

&iN  

N字节整数 (仅结构)  

&l   

结构大小 (仅结构)

&tN

N字节文本(仅结构)

&mN     

N字节Ansi 文本 (仅结构)

&wN            

N字节Unicode文本(仅结构)

&g16     

16 字节GUID (仅结构)

此外,每种类型(b、h、k和#除外)都可以用星号前缀表示指针。使用星号时,System 插件仍需要参数的值,而不是指针地址。要传递直接地址,请使用没有星号的"p"。因此,Alloc返回地址及其返回值应与"p"一起使用,而不带星号。

 

使用@类型的源时,要求必须是寄存器。当调用返回时,源寄存器已经包含字符串形式的内存地址,因此通常不需要使用目标。

 

有效的源和目标

类型      

意义

. (点)

忽略

number

16进制、10进制、8进制整数值。许多证书可以使用 | 来执行或操作

'string'

"string"  

`string`          

字符串值

r0 到r9 

分别是$0 到 $9  

r10 到r19

R0 到R9  

分别是$R0 到$R9

c

$CMDLINE

d

$INSTDIR

o

$OUTDIR

e    

$EXEDIR

a           

$LANGUAGE

s     

NSIS stack

n    

空的源,目标不需要输出

示例:

调用win32 API: int AddFontResource(LPCTSTR lpszFilename);

System::Call "GDI32::AddFontResource(t'$FONTS\${fontFilename}') i.r0"

# 返回值保存在$0

或者

System::Call "GDI32::AddFontResource(t'$FONTS\${fontFilename}') i.s"

Pop $R9

# 将返回值保存在NSIS堆栈中,使用Pop彈出到指定变量

调用win32 API:BOOL WINAPI GetWindowRect(HWND hWnd, LPRECT lpRect);

# 使用*来声明一个新的结构,和RECT一样4个成员都是int

# 常用结构已经在"${NSISDIR}\Examples\System\System.nsh"声明

System::Call "*(i0,i0,i0,i0) i.r1"

System::Call "User32::GetWindowRect(i$HWNDPARENT, i$1)"

System::Call "*$1(i.r4,i.r5,i.r6,i.r7)"

 

Callbacks

回调函数只是传递到函数并由它调用的函数。它们通常用于逐项传递可能较大的数据项集。例如,EnumChildWindows使用回调函数。由于 NSIS 函数不是常规函数,因此系统插件提供了自己的机制来支持回调功能。它允许您创建回调函数,并在每次调用回调函数时通知您。

使用 Get 和回调创建语法创建回调函数。由于您自己不会调用回调,因此应使用点省略参数的源。调用回调时,参数的目标将填充传递给回调的值。回调将返回的值由返回"参数"的源设置。应始终设置返回"参数"的目标,因为系统将通知您调用回调。

System::Get "(i .r0, i .r1) iss"

若要将回调传递给函数,请使用 k 类型

System::Get "(i .r0, i .r1) isR0"

Pop $0

System::Call "dll::UseCallback(k r0)"

每次调用回调时,字符串回调#(其中 # 是回调的编号)将被放置在返回"参数"的目标中。创建的第一个回调的编号为 1,第二个回调的号码为 2,第三个回调的号码为 3,等等。由于 System 是单线程的,因此只能在调用另一个函数时调用回调。例如,只有在调用 EnumChildWindows 时才能调用 EnumChildWindows 的回调。因此,您应该在可能调用回调的每个函数调用后检查回调# 。

System::Get "(i .r0, i .r1) isR0"

Pop $0

System::Call "dll::UseCallback(k r0)"

StrCmp $R0 "callback1" 0 +2

DetailPrint "UseCallback passed ($0, $1) to the callback"

处理回调调用后,应使用Call,将 Get 返回的值传递为Get - 回调。这将告诉系统从回调返回。在调用函数之前,必须清除返回"参数"的目标,以避免错误检测回调调用。如果在创建回调时为返回"参数"指定了源,则应使用相应的返回值填充该源。回调不会自动释放,不要忘记在使用完后释放它。

System::Get "(i .r0, i .r1) isR0"

Pop $0

System::Call "dll::UseCallback(k r0)"

loop:

       StrCmp $R0 "callback1" 0 done

       DetailPrint "UseCallback passed ($0, $1) to the callback"

       Push 1 # return value of the callback

       StrCpy $R0 "" # clear $R0 in case there are no more callback calls

       System::Call $0 # tell system to return from the callback

       Goto loop

done:

System::Free $0

注意事项:

1. 若要查找 COM 接口中成员的索引,需要在 Visual C/C++ 或平台 SDK 的标头文件中搜索此 COM 接口的定义。索引基于零。

2. 如果找不到函数或使用参数类型,则"A"或"W"将追加到其名称中,并再次查找。这是因为许多 Windows API 函数有两个版本,一个用于 ANSI 字符串,另一个用于 Unicode 字符串。函数的 ANSI 版本标有"A",Unicode 版本标有"W"。例如: lstrcpya 和 lstrcpyW 。

3. 系统 32 目录中的库无需路径即可加载。所有其他库都应加载一个引用的完整路径。

有效的选项

选项

意义

c

cdecl 调用约定(调用方还原堆栈)。默认情况下,stdcall 调用约定在 x86(由调用方还原堆栈)上使用

r

始终返回 (对于 GET 意味着您应该弹出结果并处理,对于Call 意味着您应该至少弹出结果)。默认情况下,结果只返回错误(对于 GET,您将弹出错误结果和正确的处理,对于 CALL,您将在定义的返回位置获得返回或结果)。

n

没有重新定义。每当使用此 proc 时,它永远不会被 GET 或 CALL 重新定义。此选项永远不会继承给子对象。

s

使用常规堆栈。每当定义第一个回调时,系统开始使用函数调用的临时堆栈。

e

在程序结束后调用GetLastError()并把结果Push到堆栈。

u

调用后卸载DLL(例如,使用 FreeLibrary,以便您能够将其删除)。

      

2     Experimental v2 syntax

Experimental v2 syntax

大写结构类型内存对齐。小写类型结构内存不对齐。

基于分配的回调的回调 ID

Usage Examples

System::Call 'user32::MessageBox(p $HWNDPARENT, t "NSIS System Plug-in", t "Test", i 0)'

System::Call '"$SysDir\MyLibrary.dll"::MyFunction(i 42)'

System::Call "kernel32::GetModuleHandle(t 'user32.dll') p .s"

System::Call "kernel32::GetProcAddress(p s, m 'MessageBoxA') p .r0"

System::Call "::$0(p $HWNDPARENT, m 'GetProcAddress test', m 'NSIS System Plug-in', i 0)"

System::Get "user32::MessageBox(p $HWNDPARENT, t 'This is a default text', t 'Default', i 0)"

Pop $0

System::Call "$0"

System::Get "user32::MessageBox(p $HWNDPARENT, t 'This is a default text', \

       t 'Default', i 0x1|0x10)"

Pop $0

System::Call "$0(, 'This is a System::Get test', 'NSIS System Plug-in',)"

System::Call "advapi32::GetUserName(t .r0, *i ${NSIS_MAX_STRLEN} r1) i.r2"

DetailPrint "User name - $0"

DetailPrint "String length - $1"

DetailPrint "Return value - $2"

System::Alloc 4

Pop $0

System::Call "*$0(i 5)" ; Write

System::Call "*$0(i .r1)" ; Read

System::Free $0

DetailPrint $1

System::Call "*(i 5) p .r0"

System::Call "*$0(i .r1)"

System::Free $0

DetailPrint $1

System::Call '*0(p, &l.r2, &t2)' ; &l. is not part of the struct

DetailPrint "Struct size=$2"

System::Call '*(&l4,i,i,i,i,&t128)p.r1' ; Fills dwOSVersionInfoSize with the struct size as a int32

${If} $1 Z<> 0

       System::Call 'kernel32::GetVersionEx(pr1)i.r0'

       System::Call '*$1(i,i.R1,i.R2,i.R3)'

       System::Free $1

       ${IfThen} $0 <> 0 ${|} DetailPrint "v$R1.$R2.$R3" ${|}

${EndIf}

System::Call "user32::GetClientRect(p $hwndparent, @ r0)"

System::Call "*$0(i,i,i.r1,i.r2)"

DetailPrint ClientRect=$1x$2

# defines

!define CLSCTX_INPROC_SERVER 1

!define CLSID_ActiveDesktop {75048700-EF1F-11D0-9888-006097DEACF9}

!define IID_IActiveDesktop {F490EB00-1240-11D1-9888-006097DEACF9}

# create IActiveDesktop interface

System::Call "ole32::CoCreateInstance( \

       g '${CLSID_ActiveDesktop}', p 0, \

       i ${CLSCTX_INPROC_SERVER}, \

       g '${IID_IActiveDesktop}', *p .r0) i.r1"

StrCmp $1 0 0 end

# call IActiveDesktop->GetWallpaper

System::Call "$0->4(w .r2, i ${NSIS_MAX_STRLEN}, i 0)"

# call IActiveDesktop->Release

System::Call "$0->2()"

# print result

DetailPrint $2

end:

InitPluginsDir

File "/oname=$PLUGINSDIR\MyDLL.dll" MyDLL.dll

System::Call 'KERNEL32::AddDllDirectory(w "$PLUGINSDIR")'

System::Call 'KERNEL32::LoadLibrary(t "$PLUGINSDIR\MyDLL.dll")p.r1'

System::Call 'MyDLL::MyFunc(i 5) ? u'

System::Call 'KERNEL32::FreeLibrary(pr1)'

Delete $PLUGINSDIR\MyDLL.dll

System::Get "(p.r1, p) iss"

Pop $R0

System::Call "user32::EnumChildWindows(p $HWNDPARENT, k R0, p) i.s"

loop:

       Pop $0

       StrCmp $0 "callback1" 0 done

       System::Call "user32::GetWindowText(pr1,t.r2,i${NSIS_MAX_STRLEN})"

       System::Call "user32::GetClassName(pr1,t.r3,i${NSIS_MAX_STRLEN})"

       IntFmt $1 "0x%X" $1

       DetailPrint "$1 - [$3] $2"

       Push 1 # callback's return value

       System::Call "$R0"

       Goto loop

done:

System::Free $R0

DetailPrint "EnumChildWindows returned $0"

System::Get '(m.r1)ir2r0 ?2' ; v2 syntax

Pop $9

System::Call 'kernel32::EnumSystemLocalesA(k r9, i 0)'

loop:

       StrCmp $0 "callback$9" 0 done

       DetailPrint "Locale: $1"

       StrCpy $2 1 ; EnumLocalesProc return value

       System::Call $9 ; return from EnumLocalesProc

       Goto loop

done:

System::Free $9

System::Call '*(&t50 "!")p.r2' ; DecimalSep

System::Call '*(&t50 "`")p.r3' ; ThousandSep

System::Call '*(i 2, i 0, i 3, P r2, P r3, i 1)p.r1 ?2'

System::Call 'kernel32::GetNumberFormat(i 0, i 0, t "1337.666" r4, p r1, t.r5, i ${NSIS_MAX_STRLEN})'

DetailPrint "Custom formated $4: $5"

System::Free $3

System::Free $2

System::Free $1

!define MB "user32::MessageBox(p$HWNDPARENT,t,t'NSIS System Plug-in',i0)"

System::Call "${MB}(,'my message',,)"

System::Call "${MB}(,'another message',,) i.r0"

MessageBox MB_OK "last call returned $0"

System::Call "user32::SendMessage(p $HWNDPARENT, t 'test', t 'test', p 0) p.s ? \

       e (,t'test replacement',,) i.r0 ? !e #user32::MessageBox"

DetailPrint $0

ClearErrors

Pop $0

IfErrors good

MessageBox MB_OK "this message box will never be reached"

good:

64位函数

•      Int64Op ARG1 OP [ARG2]

在ARG1 和可选的 ARG2 上执行 OP操作,并在堆栈上返回结果。ARG1 和 ARG2都是 64 位整数。这意味着它们可以介于 -2^63 和 2^63 - 1 之间的证书。

有效的运算符

加法

+

减法

-

乘法

*

除法

/

取模

%

左移

<<

算术右移

>>

逻辑右移

>>>

位或

|

位与

&

位异或

^

位非(一个参数)

~

逻辑非

逻辑或

||

逻辑与

&&

小于

<

等于

=

大于

>

示例:

System::Int64Op 5 + 5

Pop $0

DetailPrint "5 + 5 = $0" # 10

System::Int64Op 526355 * 1565487

Pop $0

DetailPrint "526355 * 1565487 = $0" # 824001909885

System::Int64Op 5498449498849818 / 3

Pop $0

DetailPrint "5498449498849818 / 3 = $0" # 1832816499616606

System::Int64Op 0x89498A198E4566C % 157

Pop $0

DetailPrint "0x89498A198E4566C % 157 = $0" # 118

System::Int64Op 1 << 62

Pop $0

DetailPrint "1 << 62 = $0" # 4611686018427387904

System::Int64Op 0x4000000000000000 >> 62

Pop $0

DetailPrint "0x4000000000000000 >> 62 = $0" # 1

 

System::Int64Op 0x8000000000000000 >> 1

Pop $0

DetailPrint "0x8000000000000000 >> 1 = $0" # -4611686018427387904 (0xC000000000000000)

 

System::Int64Op 0x8000000000000000 >>> 1

Pop $0

DetailPrint "0x8000000000000000 >>> 1 = $0" # 4611686018427387904 (0x4000000000000000)

System::Int64Op 0x12345678 & 0xF0F0F0F0

Pop $0

# IntFmt is 32-bit, this is just for the example

IntFmt $0 "0x%X" $0

DetailPrint "0x12345678 & 0xF0F0F0F0 = $0" # 0x10305070

System::Int64Op 1 ^ 0

Pop $0

DetailPrint "1 ^ 0 = $0" # 1

System::Int64Op 1 || 0

Pop $0

DetailPrint "1 || 0 = $0" # 1

System::Int64Op 1 && 0

Pop $0

DetailPrint "1 && 0 = $0" # 0

System::Int64Op 9302157012375 < 570197509190760

Pop $0

DetailPrint "9302157012375 < 570197509190760 = $0" # 1

System::Int64Op 5168 > 89873

Pop $0

DetailPrint "5168 > 89873 = $0" # 0

System::Int64Op 189189 = 189189

Pop $0

DetailPrint "189189 = 189189 = $0" # 1

System::Int64Op 156545668489 ~

Pop $0

DetailPrint "156545668489 ~ = $0" # -156545668490

System::Int64Op 1 !

Pop $0

DetailPrint "1 ! = $0" # 0

常见问题

•问:如何将结构传递给函数?

答:首先,你必须为结构分配内存。有两种方法:使用 Alloc或带有特殊结构分配的语法Call。然后,如果需要在结构中传递数据,则必须用数据填充结构体。然后使用指向结构的指针调用函数。最后,调用函数后如果你想从结构体取出数据,你必须使用带有结构指针的Call用。完成之后,重要的是要记住释放结构。

•分配内存

若要使用Alloc 分配结构,您必须知道结构的大小(以字节为单位)。当然你也可以用Call。在这个例子里可以很明显地看出这个结构体的大小是 16 字节,但其他情况可能就不是这样子了。在所有的例子里,结构体的地址都被保存在堆栈顶部,你需要用Pop 来把它弹出到变量。

System::Alloc 16

System::Call "*(i, i, i, t)p.s"

  • 设置数据

写入数据可以使用Call 。可以在分配内存时就进行,或者用其他方法分配后使用带结构指针的语法。

System::Call "*(i 5, i 2, i 513, t 'test')p.s"

# 假设结构体的内存地址保存在$0 里

System::Call "*$0(i 5, i 2, i 513, t 'test')"

  • 把结构体传递到函数

就像一些分配方法返回一个地址一样,这里要传递的数据类型应该是一个整数---一个保存了结构体地址的整数。

# 假设结构体的内存地址保存在$0 里

System::Call "dll::func(p r0)"

  • 读取数据

读取数据可以可以使用和写入数据相同的语法。说不同的是要有输出变量而输入部分用一个句点来表示。

# 假设结构体的内存地址保存在$0 里

System::Call "*$0(i .r0, i .r1, i .r2, t .r3)"

DetailPrint "first int = $0"

DetailPrint "second int = $1"

DetailPrint "third int = $2"

DetailPrint "string = $3"

•释放内存

使用Free释放内存。

#假设结构体的内存地址保存在$0 里

System::Free $0

完整示例

# allocate

System::Call "*(i,i,p,p,p,p,p,p)p.r1"

# call

System::Call "Kernel32::GlobalMemoryStatus(p r1)"

# get

System::Call "*$1(i.r2, i.r3, p.r4, p.r5, p.r6, p.r7, p.r8, p.r9)"

# free

System::Free $1

# print

DetailPrint "Structure size: $2 bytes"

DetailPrint "Memory load: $3%"

DetailPrint "Total physical memory: $4 bytes"

DetailPrint "Free physical memory: $5 bytes"

DetailPrint "Total page file: $6 bytes"

DetailPrint "Free page file: $7 bytes"

DetailPrint "Total virtual: $8 bytes"

DetailPrint "Free virtual: $9 bytes"

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值