Если я правильно понимаю, ваш SQLDLL
сам управляет некоторым буфером памяти и возвращает указатель на строку Unicode (не ANSI, поэтому вы получили только один символ при попытке PAnsiChar
, согласно на ваш комментарий ).
Inno Setup не поддерживает это напрямую и даже не имеет типа PWideChar
. Тем не менее, мы можем справиться с этим сами. Нам просто нужно выделить строку Inno правильного размера и скопировать данные вручную.
Вот рабочий пример, как это сделать. Он использует GetCommandLineW
в качестве примера функции, которая возвращает PWideChar
, но вы можете сделать то же самое с вашей функцией SQLDLL
.
- Получить указатель из внешней функции и сохранить его в переменная (a
Cardinal
- в моем примере я создал для нее typedef PWideChar
). - Получите длину строки, используя
lstrlenW
. - Создайте пустую регулярную
String
, но установите правильную длину, используя SetLength
. Это зарезервирует достаточную емкость, чтобы мы могли записать в нее фактическое содержимое на следующем шаге. - Используйте
lstrcpyW
, чтобы скопировать строку, на которую ссылается указатель, на вашу обычную переменную String
. - (Если вы используете версию Inno Setup для ANSI: используйте
WideCharToMultiByte
, см. Мое обновление в конце этого поста.)
Хитрость заключается в том, чтобы импортировать lstrcpyW
таким образом, чтобы указатель назначения был объявлен как String
, а указатель источника был объявлен как Cardinal
(или мой typedef PWideChar
здесь).
type
PWideChar = Cardinal; { Inno doesn't have a pointer type, so we use a Cardinal instead }
{ Example of a function that returns a PWideChar }
function GetCommandLineW(): PWideChar;
external 'GetCommandLineW@kernel32.dll stdcall';
{ This function allows us to get us the length of a string from a PWideChar }
function lstrlenW(lpString: PWideChar): Cardinal;
external 'lstrlenW@kernel32.dll stdcall';
{ This function copies a string - we declare it in such a way that we can pass a pointer
to an Inno string as destination
This works because Inno will actually pass a PWideChar that points to the start of the
string contents in memory, and internally the string is still null-terminated
We just have to make sure that the string already has the right size beforehand! }
function lstrcpyW_ToInnoString(lpStringDest: String; lpStringSrc: PWideChar): Integer;
external 'lstrcpyW@kernel32.dll stdcall';
function InitializeSetup(): Boolean;
var
returnedPointer: PWideChar; { This is what we get from the external function }
stringLength: Cardinal; { Length of the string we got }
innoString: String; { This is where we'll copy the string into }
begin
{ Let's get the PWideChar from the external function }
returnedPointer := GetCommandLineW();
{ The pointer is actually just a renamed Cardinal at this point: }
Log('String pointer = ' + IntToStr(returnedPointer));
{ Now we have to manually allocate a new Inno string with the right length and
copy the data into it }
{ Start by getting the string length }
stringLength := lstrlenW(returnedPointer);
Log('String length = ' + IntToStr(stringLength));
{ Create a string with the right size }
innoString := '';
SetLength(innoString, stringLength);
{ This check is necessary because an empty Inno string would translate to a NULL pointer
and not a pointer to an empty string, and lstrcpyW cannot handle that. }
if StringLength > 0 then begin
{ Copy string contents from the external buffer to the Inno string }
lstrcpyW_ToInnoString(innoString, returnedPointer);
end;
{ Now we have the value stored in a proper string variable! }
Log('String value = ' + innoString);
Result := False;
end;
Если вы поместите это в установщик и запустите его, вы увидите вывод, подобный следующему:
[15:10:55,551] String pointer = 9057226
[15:10:55,560] String length = 106
[15:10:55,574] String value = "R:\Temp\is-9EJQ6.tmp\testsetup.tmp" /SL5="$212AC6,121344,121344,Z:\Temp\testsetup.exe" /DEBUGWND=$222722
Как видите, строка командной строки (которую мы получаем как PWideChar
) копируется в обычная строковая переменная правильно и в конце может быть получен обычный доступ.
Обновление: Если вы используете ANSI-версию Inno Setup, а не Unicode, только этот код выиграет т работа. Требуется следующее изменение: вместо lstrcpyW
вы бы использовали WideCharToMultiByte
:
function WideCharToMultiByte_ToInnoString(CodePage: Cardinal; dwFlags: Cardinal; lpWideCharStr: PWideChar; cchWideChar: Cardinal; lpMultiByteStr: String; cbMultiByte: Cardinal; lpDefaultChar: Cardinal; lpUsedDefaultChar: Cardinal): Integer;
external 'WideCharToMultiByte@kernel32.dll stdcall';
{ Later on: Instead of calling lstrcpyW_ToInnoString, use this:
Note: The first parameter 0 stands for CP_ACP (current ANSI code page), and the
string lengths are increased by 1 to include the null terminator }
WideCharToMultiByte_ToInnoString(0, 0, returnedPointer, stringLength + 1, innoString, stringLength + 1, 0, 0);