当前位置: 首页 > 知识库问答 >
问题:

如何在Inno Setup Pascal脚本中取消引用指针?

荆梓
2023-03-14

我在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文件。原始语言是德尔福。

我更新到最新版本的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的组策略有关,但我不确定我应该做些什么来消除这个错误。

共有2个答案

韩夕
2023-03-14

在Inno Setup Pascal脚本中不能取消对指针的引用。

但也有许多黑客允许这样做。这些技巧非常具体,因此取决于特定的用例。

尽管在您的特定情况下,指向字符数组的指针在API中很普遍,Inno Setup Pascal脚本(类似于Delphi)可以将指向字符数组的指针分配给一个字符串。

因此,您应该能够简单地将PChar分配给AnsiString

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

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

请参阅如何将字符串从DLL返回到Inno安装程序?

苏骏
2023-03-14

如果我理解正确,您的SQLDLL本身管理一些内存缓冲区并返回指向Unicode字符串的指针(不是ANSI,这就是为什么根据您的评论,当您尝试PAnsiChar时只有一个字符)。

Inno Setup不直接支持这一点,甚至没有PWideChar类型。但是,我们可以自己处理。我们只需要分配一个大小合适的Inno字符串并手动复制数据。

下面是一个如何做到这一点的示例。它使用GetCommandLineW作为返回PWideChar的示例函数,但您可以使用SQLDLL函数执行同样的操作。

    < li >从外部函数获取指针,并将其存储在一个变量中(一个< code>Cardinal -在我的示例中,我为它创建了一个typedef PWideChar)。 < li >使用< code>lstrlenW获取字符串长度。 < li >创建一个空的常规< code >字符串,但使用< code>SetLength将其设置为正确的长度。这将保留足够的容量,我们可以在下一步将实际内容写入其中。 < li >使用< code>lstrcpyW将指针引用的字符串复制到常规< code>String变量。 < ul > < li >(如果您使用的是ANSI版本的Inno Setup:请使用< code>WideCharToMultiByte,参见本文末尾的我的更新。)

诀窍是导入 lstrcpyW,这样目标指针被声明为字符串,但源指针被声明为基数(或我的 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 获得)被正确复制到常规字符串变量中,并且可以在最后正常访问。

更新:如果您使用的是Inno Setup的ANSI版本,而不是Unicode,单独使用这段代码是不行的。需要的更改如下:不使用< code>lstrcpyW,而是使用< code>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);
 类似资料:
  • 本文向大家介绍C语言取消引用指针,包括了C语言取消引用指针的使用技巧和注意事项,需要的朋友参考一下 示例 要取消引用a_pointer并更改a的值,我们使用以下操作 可以使用以下打印语句对此进行验证。 但是,将一个NULL指针取消引用或其他无效指针将是错误的。这个 通常是未定义的行为。p1可能不会被取消引用,因为它指向的地址0xbad可能不是有效地址。谁知道那里有什么?它可能是操作系统内存,或另一

  • 问题内容: 我最近才刚开始接触Go,我有一个主要的困惑点:我在努力理解何时确切需要显式取消引用指针。 例如,我知道运算符将处理对指针的解引用 在其他哪些情况下会这样做?例如,似乎使用数组。 我在规范中找不到此内容,指针部分很短,甚至没有提到取消引用。对取消引用go的指针的规则进行的任何澄清都将很棒! 问题答案: 所述选择器表达式(例如)这是否: 选择器会 自动取消引用 结构的 指针 。如果是指向结

  • 问题内容: 如何将Item结构和所有指针复制到新结构? 我不希望新结构引用旧结构。 问题答案: 本质上您想要的是标准库不支持的深层副本。 您的选择: “手动”执行复制,例如,创建新的结构并复制字段,其中必须以递归方式手动复制指针或切片/地图/通道/等。 通过将您的结构分配给另一个可复制所有字段的结构,这是最容易做到的,因此,您基本上只需要培养指针/地图/切片等(但需要递归)。 使用外部库,例如,或

  • 问题内容: 我是GoLang的新手,来自Delphi C ++世界-诚然对这种语言感到非常兴奋,我认为它将成为“下一件大事”。 我正在尝试了解Go解析器和编译器如何处理指针和引用-似乎找不到任何放置一些明确规则的地方。 例如,在下面的代码示例中,返回类型和局部变量是指针类型,并且在其声明中需要使用指针符号,但是在使用时不必取消引用它们:。但是在同一代码中,输入参数被声明为指针,并且必须被取消引用才

  • 这是控制台报告中的代码和错误 启动层初始化期间发生错误 java.lang.module.FindException:无法为E:\seleniumwebdriversoftwares\org.testng.eclipse.updatesite\plugins\org.testng.source-6.14.2.r201802161450.jar派生模块描述符原因:java.lang.module.I

  • 嗨我有以下方法: 不得不提的是,startDate和endDate是长变量。我尝试在if条件中添加null检查,也尝试使用longValue()方法,但没有结果。你知道我怎样才能解决这个问题吗?可能是fndBugs端的bug?