首页 > 解决方案 > 如何从 NSIS 中的 RCData 资源中读取字符串

问题描述

我在 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 中是如何工作的,以及当变量用作具有不同类型的系统调用的输出时会发生什么(它具有什么值)。我需要什么(我认为)是将$4字节从存储在其中的地址复制$3到新缓冲区并(可能?)以零终止它。我认为这样的事情可以工作:

System::Alloc ${NSIS_MAX_STRLEN}
Pop $5
System::Copy /$4 $5 $3

但事实并非如此。经过一些实验,我明白了它的工作原理:

System::Call "*$3(&m$4.r5)"

但我真的不知道为什么这是有效的,如果这是处理这项任务的正确方式,以及为什么上面的复制版本不起作用。

标签: nsis

解决方案


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

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

如果我们有一些需要类型转换而不是在您的特定情况下基本上是字节副本的东西,这可能更容易可视化。

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

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

在 NSIS 中 $1 是 ptr 的值:

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

我们通常将此称为 System struct 语法,而 * 可以被认为是取消引用从 $1 开始的内存,并将内存解释为基于我们指定的类型的结构。

在我的具体示例中,我们说第一个成员是一个 int,我不想更改值/源(点),但我希望当前值作为存储在 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

推荐阅读