簡體   English   中英

如何在 Inno Setup Pascal Script 中取消引用指針?

[英]How can I dereference a pointer in Inno Setup Pascal Script?

我從 Inno Setup Script 中的 DLL 文件調用一個函數,它的返回類型是PAnsiChar 為了獲得整個字符串,我需要取消引用指針,但標准的 pascal 語法在這里不起作用。 甚至有可能這樣做嗎?

function SQLDLL : PAnsiChar;
external 'GetSQLServerInstances@files:IsStartServer.dll stdcall setuponly';

function NextButtonClick(CurPage: Integer): Boolean;
var
  hWnd: Integer;
  Str : AnsiString;
begin
  if CurPage = wpWelcome then begin
    hWnd := StrToInt(ExpandConstant('{wizardhwnd}'));

    MessageBox(hWnd, 'Hello from Windows API function', 'MessageBoxA', MB_OK or MB_ICONINFORMATION);

    MyDllFuncSetup(hWnd, 'Hello from custom DLL function', 'MyDllFunc', MB_OK or MB_ICONINFORMATION);

    Str := SQLDLL;
    try
      { if this DLL does not exist (it shouldn't), an exception will be raised }
      DelayLoadedFunc(hWnd, 'Hello from delay loaded function', 'DllFunc', MB_OK or MB_ICONINFORMATION);
    except
      { handle missing dll here }
    end;
  end;
  Result := True;
end;

我只有 DLL 文件。 原始語言是 Delphi。

我更新到最新版本的 Inno Setup 6.0.3 並在我的家用 Windows 10 Pro 機器上測試了此代碼:

[Setup]
AppName=My Program
AppVersion=1.5
WizardStyle=modern
DefaultDirName={autopf}\My Program
DisableProgramGroupPage=yes
DisableWelcomePage=no
UninstallDisplayIcon={app}\MyProg.exe
OutputDir=userdocs:Inno Setup Examples Output

[Files]
Source: "MyProg.exe"; DestDir: "{app}"
Source: "MyProg.chm"; DestDir: "{app}"
Source: "Readme.txt"; DestDir: "{app}"; Flags: isreadme
Source: "IsStartServer.dll"; Flags: dontcopy

[Code]
function SQLDLL : PAnsiChar;
external 'GetSQLServerInstances@files:IsStartServer.dll stdcall';

function NextButtonClick(CurPage: Integer): Boolean;
var
  Str : PAnsiChar;
begin
  Str := SQLDLL;
  Result := True;
end; 

現在我遇到了這種錯誤: 在此處輸入圖片說明

我不明白為什么它必須查看我的“臨時”目錄? 我還聽說這個問題可能以某種方式與 Windows 10 UAC 中的組策略有關,但我不確定我應該在這里做什么來擺脫這個錯誤。

如果我理解正確,您的SQLDLL管理一些內存緩沖區並返回一個指向Unicode字符串的指針(不是 ANSI,這就是為什么根據您的評論,當您嘗試PAnsiChar時您只有一個字符)。

Inno Setup 不直接支持它,甚至沒有PWideChar類型。 但是,我們可以自己處理。 我們只需要分配一個大小合適的 Inno 字符串並手動復制數據。

這是一個如何做到這一點的工作示例。 它使用GetCommandLineW作為返回PWideChar的示例函數,但您可以對SQLDLL函數執行相同SQLDLL

  • 從外部函數獲取指針並將其存儲在一個變量中(一個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,則僅此代碼將不起作用。 所需的更改是這樣的:您將使用WideCharToMultiByte而不是使用lstrcpyW

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);

您不能在 Inno Setup Pascal Script 中取消引用指針。

但是有許多黑客允許這樣做。 這些黑客是非常具體的,所以這取決於特定的用例。


盡管在您的特定情況下,由於指向字符數組的指針在 API 中很普遍,Inno Setup Pascal Script(類似於 Delphi)可以將指向字符數組的指針分配給字符串。

因此,您應該能夠簡單地將PChar分配給AnsiString

function ReturnsPAnsiChar: PAnsiChar; extern '...';

var
  Str: AnsiString;
begin
  Str := ReturnsPAnsiChar;
end;

請參閱如何將字符串從 DLL 返回到 Inno Setup?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM