如何从 NSIS 中的 RCData 资源读取字符串

How to read a string from an RCData resource in NSIS

我在 RCData 资源中有一个 ANSI 字符串,我想将该字符串存储在一个变量中。我已经到了可以获取资源指针和数据大小的地步:

; HMODULE GetModuleHandle(LPCSTR lpModuleName);
System::Call "kernel32::GetModuleHandle(i 0) i.r0"

; HRSRC FindResource(HMODULE hModule, LPCSTR lpName, LPCSTR lpType);
System::Call "kernel32::FindResource(i 0, t 'DATA', i 10) i.r1"

; HGLOBAL LoadResource(HMODULE hModule, HRSRC hResInfo);
System::Call "kernel32::LoadResource(i r0, p r1) i.r2"

; LPVOID LockResource(HGLOBAL hResData);
System::Call "kernel32::LockResource(i r2) p.r3"

; DWORD SizeofResource(HMODULE hModule, HRSRC hResInfo);
System::Call "kernel32::SizeofResource(i r0, i r1) i.r4"

现在我不太确定该怎么做。我对 NSIS 很陌生,我认为我的问题是我并不真正了解变量在 NSIS 中的工作方式以及当变量用作具有不同类型的系统调用的输出时会发生什么(它具有什么值)。我认为(我认为)我需要的是将 </code> 字节从存储在 <code> 中的地址复制到新缓冲区,并(可能?)以零终止它。我认为这样的方法可行:

System::Alloc ${NSIS_MAX_STRLEN}
Pop 
System::Copy /  

但事实并非如此。经过一些实验,我明白了这是可行的:

System::Call "*(&m.r5)"

但我真的不太清楚为什么它会起作用,它是否是处理此任务的正确方法以及为什么上面的复制版本不起作用。

在 System::Copy 代码中,$5 包含分配缓冲区的内存地址(作为字符串,就好像有人 sprintf(reg5, "%i", address) 一样)。如果你这样做 MessageBox mb_ok 你将只看到这个地址。但是,如果您这样做 System::Call user32::MessageBoxA(p0,pr5,p0,i0),您应该会看到您的字符串。

System::Call 代码使用您指定的类型转换源 and/or 目标。这是一种完全有效的方法,并且比复制代码的工作更少。

如果在您的特定情况下我们需要类型转换而不是基本的字节复制,那么这可能更容易想象。

想象一下如果在 C 中我们有

typedef struct { int foo, bar; } MyS;
MyS*ptr = allocandfill();

在 NSIS 中 $1 是 ptr 的值:

System::Call '*(i.r2,i1234)'

我们经常将其称为系统结构语法,* 可以被认为是取消引用从 $1 开始的内存,并根据我们指定的类型将内存解释为结构。

在我这里的具体示例中,我们说第一个成员是一个 int,我不想更改 value/source(点),但我希望当前值作为输出存储在 r2 中( 2 美元)。这基本上会执行 itoa ,其中 $2 是目的地。第二个成员是另一个 int,我用数字 1234 覆盖它的当前值。在 C 中,这将执行 ptr->bar = atoi("1234").

完成任务的第三种方法是调用 Windows 函数并让它完成工作:

System::Call `kernel32::lstrcpyA(m.r5,pr3)' ; or lstrcpynA if the string is not zero-terminated or you want to chop it to n characters