Как прочитать строку из ресурса RCData в NSIS

#nsis

Вопрос:

У меня есть строка ANSI в ресурсе RCData, и я хотел бы сохранить эту строку в переменной. Я дошел до того, что могу получить указатель на ресурс и размер данных:

 ; 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(amp;m$4.r5)"
 

Но я действительно не очень хорошо представляю, почему это работает, правильно ли следует решать эту задачу и почему вышеприведенная версия копии не сработала.

Ответ №1:

В коде System::Copy $5 содержит адрес памяти выделенного буфера (в виде строки, как если бы кто-то это сделал sprintf(reg5, "%i", address) ). Если вы это сделаете MessageBox mb_ok $5 , вы просто увидите этот адрес. Однако, если вы это сделаете System::Call user32::MessageBoxA(p0,pr5,p0,i0) , вы должны увидеть свою строку.

Система::Код вызова преобразует источник и/или пункт назначения, используя указанный вами тип. Это совершенно правильный способ сделать это и требует меньше работы, чем копирование кода.

Возможно, это было бы легче визуализировать, если бы у нас было что-то, что требовало преобразования типов, а не просто байтовой копии в вашем конкретном случае.

Представьте, если бы в C у нас было

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

а в NSIS $1-это значение ptr:

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

мы часто называем это синтаксисом системной структуры, и * можно рассматривать как удаление ссылки на память, начиная с 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
 

Комментарии:

1. Это потрясающе, теперь многое имеет гораздо больше смысла, большое вам спасибо! 🙂 Может быть, еще один вопрос — amp;m$4.r5 добавляет ли завершающее «» к $5 ? Строка в $3 не заканчивается нулем. Поскольку переменные содержат только строки, а также результирующая строка, по $5 -видимому, отображается правильно, я полагаю, что это так, я просто хотел бы убедиться, что ноль там не случайно. Кстати. было бы разумно думать о переменных как о char[NSIS_MAX_STRLEN] ?

2. После некоторых экспериментов кажется, что результат $5 действительно заканчивается нулем, и это здорово. 🙂