delphi - 如何将 "array of strings"从 inno setup 传递到 Delphi Dll?

标签 delphi dll inno-setup

我有一个我用delphi编写的dll。它需要传递给它的字符串数组。如果我从另一个 delphi 应用程序调用 dll 进行测试,它可以工作。

我以为我让它在 inno 中工作,但已经改变了它,以便在注入(inject)文件后从向导页面调用它。

我没有收到任何错误,但没有输入 dll,因为我在 dll 中有一个消息框说它正在运行。

下面是代码,有人可以告诉我为什么我的 dll 不运行。

[files]
......
Source: "mydll.dll"; Flags: dontcopy; [code]
procedure GenKey(Details: array of string);
external 'GenKey@files:mydll.dll stdcall setuponly';
.....

function RegistrationComplete(Page: TWizardPage): boolean;
var
  Details: array[0..15] of String;
begin
....Load the details array of strings.....

  GenKey(Details);
  Result := True;
end;


procedure InitializeWizard();
begin
  Page := CreateCustomPage( wpInstalling, ExpandConstant('{cm:Registration_Caption}'), ExpandConstant('{cm:Registration_Description}') );
   with Page do
  begin
    OnNextButtonClick := @RegistrationComplete;
  end;

end;

好的,到目前为止,我通过一些研究和搜索得出了这个结论。但我仍然需要澄清。让它工作。我知道我需要使用 PChar 作为指向我声明为字符串的缓冲区的指针。使用 delphi 作为测试平台,这一切都有效,但是当指针值传递给 dll 时,字符串似乎丢失了。

因此,当我从 inno 调用 dll 时,我收到此错误消息“内部异常 EEDFADE”

Inno 代码如下
var
  Buff: string;

procedure GenKey(Buff: PChar);
external 'GenKey@files:mydll.dll stdcall';

function PadZero(src: string; Width: Integer): String;
var
  I : Integer;
  Temp: string;
begin
  Temp := '';
  for i := (Length(Src)+1) to width do
    Temp := Temp + '0';
  if Length(Src) > width then Src := Copy( Src, 1, width);
  Result := Temp + Src;
end;


{ CustomForm_NextkButtonClick }

function RegistrationComplete(Page: TWizardPage): boolean;
var 
  Details: TStringList;
  BuffLen: integer;
begin
  Details := TStringList.Create;
  ....
  Load string list
  ....

  Buff := Details.Text;

  BuffLen := Length(Buff) + 1;
  Buff := PadZero(InttoStr(BuffLen),4) + Buff;

  GenKey(PChar(Buff));
  Result := True;
end;

delphi XE6开发的DLL例程
procedure GenKey (Buff: PChar);
var
  I: Integer;
  Details : TStringList;
  S: string;
  BuffLen: integer;
begin
  S := '';
  for I := 0 to 3 do
  begin
    S := S + Buff^;
    inc(Buff)
  end;

  BuffLen := strtoint(S);

  Details := TStringList.Create;
  S := '';
  for I := 1 to BuffLen do
  begin
    S := S + (Buff^);
    inc(Buff);
  end;
  Details.Text := S;
  showmessage(S);

  Details.Free;
end;

最佳答案

如果没有提前采取特殊措施,您无法安全地将复杂对象(如引用计数字符串)传递到与语言无关的无类型安全 DLL 中(前提是您知道足够好的低级 Delphi 编译器实现来设计此类脚手架)

您也不能传递特定于语言的对象,例如 open arrays出于同样的原因进入 DLL。

那么你必须做什么?你必须像 Windows 那样做:传递一个包含所有字符串的缓冲区并在 DLL 中解析它。详细信息取决于您要发送的字符串类型。

  • 方法:多个 ASCIIZ 字符串。
  • 限制:不允许空字符串,字符串不能包含#0 char
  • 参数类型:PAnsiCharPWideChar
  • 数据样本:'abcde'#0'fgh'#0'ijklmn'#0#0 - 字符串以 #0 结尾;空字符串(两个连续的#0)表示数据包结束

  • DLL impl草案:
       {$T+}  {$POINTERMATH ON}
       type PMyStrings = PAnsiChar; 
       procedure GenKey( Details: PMyStrings );
       var a: AnsiString;
           w: string; // would be UTF-16 String in Delphi 2009 and above  
       begin
         repeat
          a := Details; // conversion next ASCIIZ string to Delphi string
          if a = '' then break; // empty string - end of data
    
          Inc( Details, Length(a) + 1 ); // shifting pointer to next string in buffer
          w := a;       // conversion fixed 8-bits string to Delphi-default string;
          { process each string `w` }
         until false;
       end;  
    

    EXE impl草案:
       type PMyStrings = PAnsiChar; 
       var isl: iJclStringList; a: AnsiString;
    
       isl := JclStringList();
       isl.Add( 'abcde' );
       isl.Add( 'fgh' );
       isl.Add( 'ijklmn' );
       isl.Add( '' ); // empty string - end of data marker
    
       a := AnsiString ( isl.Join( #0 ) );
       GenKey( PMyStrings( a ) );
    

    EXE impl 2 草案:
     type PMyStrings = PAnsiChar; 
     var a: AnsiString; s: string;
    
     s := '';
    
     s := s + 'abcde' + #0;
     s := s + 'fgh'  + #0;
     s := s + 'ijklmn' + #0; 
    
     a := AnsiString ( s );
     GenKey( PMyStrings( a ) );
    

    或者
  • 方法:反前缀缓冲区
  • 限制:几乎没有;选择的前缀大小(byte、smallint、longint)可能会限制字符串的总数和每个字符串的最大长度。您还必须决定是否使用 AnsiChar 或 WideChar 来存储数据 - 类型安全将完全由您负责。
  • 参数类型:指针 - 您可以在解析缓冲区时将其类型转换为不同的类型
  • 数据样本:{32位整数,字符串总数} 3,{32位整数,第一个字符串长度} 5,'a','b','c','d','e',{32 -bits 整数,第二个字符串长度} 3, 'f','g','h', {32 位整数, 第三个字符串长度} 6, 'i','j','k','l', 'm','n'

  • DLL impl草案:
       {$T+}  {$POINTERMATH ON}
       type PrefCount = UInt32; PPrefCount = ^PrefCount;
            PrefStrLen = UInt32; PPrefStrLen = ^PrefStrLen;
            PStrData = PAnsiChar;
       procedure GenKey( Details: Pointer );
       var pc: PPrefCount; pl: PPrefStrLen; pd: PStrData;
           Count: PrefCount; Len: PrefStrLen;
    
           a: AnsiString;
           w: string; // would be UTF-16 String in Delphi 2009 and above  
       begin
         pc := Details;
         Count := pc^;
         Inc(pc); Details := pc; // shifting the pointer past the counter cell
    
         while Count > 0 do begin
           pl := Details;
           Len := pl^;
           Inc(pl); Details := pl; // shifting the pointer past the n-th string length cell
    
           SetLength(a, Len);
           if Len > 0 then begin
              pd := Details;
              Assert( sizeof( a[1] ) = sizeof( pd^ ) );
              Move( pd^, a[1], Len * sizeof( a[1] ) );  
    
              Inc( pd, Len );
              Details := pd;
           end;
    
           w := a;       // conversion fixed 8-bits string to Delphi-default string;
          { process each string `w` }
          Dec( Count );
         end;
       end;  
    

    EXE impl草案:
       {$T+}  {$POINTERMATH ON}
       type PrefCount = UInt32; PPrefCount = ^PrefCount;
            PrefStrLen = UInt32; PPrefStrLen = ^PrefStrLen;
            PStrData = PAnsiChar;
       var isl: iJclStringList; 
           buffer: TBytes; 
           s: string; a: AnsiString;
           L: integer;
           pc: PPrefCount; pl: PPrefStrLen; pd: PStrData;
           data: Pointer;
    
       isl := JclStringList();
       isl.Add( 'abcde' );
       isl.Add( 'fgh' );
       isl.Add( 'ijklmn' );
    
       Assert( sizeof( a[1] ) = sizeof( pd^ ) );
    
       L := 0;
       for s in isl do 
         Inc( L, Length(s) );
       L := L * sizeof( pd^ );
       Inc( L, isl.Count * sizeof( PrefStrLen  ) );
       Inc( L, sizeof( PrefCount ) );
    
       SetLength( buffer, L );
       data := @buff[ Low(buff) ];
    
       pc := data;
       pc^ := isl.Count;
       Inc(pc);
       data := pc;
    
       for s in isl do begin
         pl := data;
         pl^ := Length(s);
         Inc(pl);
         data := pl;
    
         if s > '' then begin
            a := AnsiString(s);
            pd := data;
            Move( a[1], pd^, pl^ * sizeof( pd^ ) );
            Inc( pd, pl^ );
            data := pd;
         end;
       end; 
    
       Dec( PByte( data ) ); // should point to next byte past the buffer
       Assert( data = @buff[ High(buff) ] ); // if Length was correctly calculated and adjusted
    
       data := @buff[ Low(buff) ];
       GenKey( data );
    

    或者
  • 方法:带有辅助分隔符的 ASCIIZ 字符串。
  • 限制:字符串既不能包含 #0 字符,也不能包含其他用作内部分隔符的预定义字符
  • 参数类型:PAnsiCharPWideChar
  • 数据样本:'abcde'#1'fgh'#1'ijklmn'#0 - 此处的字符串由 #1 分隔(并且该字符被禁止成为合法数据的一部分)。 #0 标志着巨型字符串的结束(因此所有字符串)。

  • DLL impl草案:
       type PMyStrings = PAnsiChar; 
       const AuxSeparator = #1;
       procedure GenKey( Details: PMyStrings );
       var isl: iJclStringList;
           a: AnsiString;
           w: string; // would be UTF-16 String in Delphi 2009 and above  
       begin
          a := Details; // conversion ASCIIZ string to Delphi string
          w := a;       // conversion fixed 8-bits string to Delphi-default string;
          isl := JclStringList();
          isl.Split( w, AuxSeparator ); // parsing mega-string into separate strings
    
          for w in isl do begin
              { process each string `w` }
          end;
       end;  
    

    EXE impl草案:
       type PMyStrings = PAnsiChar; 
       const AuxSeparator = #1;
       var isl: iJclStringList; a: AnsiString;
    
       isl := JclStringList();
       isl.Add( 'abcde' );
       isl.Add( 'fgh' );
       isl.Add( 'ijklmn' );
    
       a := AnsiString ( isl.Join( AuxSeparator ) );
       GenKey( PMyStrings( a ) );
    

    EXE impl 2 草案:
     type PMyStrings = PAnsiChar; 
     const AuxSeparator = #1;
     var a: AnsiString; s: string;
    
     s := '';
    
     s := s + 'abcde' + AuxSeparator;
     s := s + 'fgh'  + AuxSeparator;
     s := s + 'ijklmn'; // no internal separator for last string
    
     a := AnsiString ( s );
     GenKey( PMyStrings( a ) );
    

    您还可以组合这些方法或在 DLL 中使用多个参数,例如您可以发送指向 ASCIIZ 字符串的指针数组
       {$T+}  {$POINTERMATH ON}
       type PMyStrings = ^PAnsiChar;
       procedure GenKey(Count: integer; Details: PMyStrings);
       var a: AnsiString; p: PAnsiChar;
           w: string; 
       begin
         while Count > 0 do begin
            p := Details^;
            a := p;
            Inc( Details );  
            Dec( Count );
    
            w := a;
            { process string `w` }
         end;
       end;
    

    然后是EXE
       type PMy1String = PAnsiChar;
       var  data: packed array of PMy1String;
    
       SetLength( data, 3 );
       data[0] := PMy1String( 'abcde' );
       data[1] := PMy1String( 'fgh' );
       data[2] := PMy1String( 'ijklmn' );
    
       GenKey( Length(data), @data[0] ); 
    

    或者任何最适合你的东西。只是对低级数据表示非常谨慎。例如不同语言中 AnsiChar 和 WideChar 之间的差异 - 现在跟踪它是您的责任,而不是编译器的责任。

    关于delphi - 如何将 "array of strings"从 inno setup 传递到 Delphi Dll?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31427925/

    相关文章:

    delphi - 对报表设计师制作 4 象限页面进行打印的建议?

    C++ DLL调试

    c# - vshost.exe 一直在访问我的 .dll,我在构建它时无法更新它

    c# - C# 应用程序之间的 IPC,通过 native C++ DLL,到另一个使用嵌入式 Lua 的 DLL 的应用程序?

    windows - WinHttpRequest.5.1 是适用于 Windows 11 的良好 API 还是需要 Iexplorer?

    delphi - 如何编写 Delphi Galileo IDE Expert?

    delphi - Delphi TWebBrowser.GoBack:如何处理重定向

    delphi - FastMM 是否可能错误地报告访问冲突?

    inno-setup - Inno Setup : Keep existing 32-bit installation path for upgrades, 使用 64 位路径进行新安装

    batch-file - Inno Setup - 从外部源(文件或文件夹内容)创建组件/类型的动态列表