【问题标题】:Delphi PChar to C++ const char*Delphi PChar 到 C++ const char*
【发布时间】:2009-09-09 10:16:07
【问题描述】:

我正在尝试使用来自本机程序的 C++ dll。我按照here 解释的虚拟方法场景进行操作

假设我的 C++ 函数签名的形式是

int Setup(const char* szIp, const char* szPort);

而对应的delphi签名是

function Setup(ip, port: PChar):Integer: virtual; cdecl; abstract;

我可以从 delphi 程序的某个地方调用

pObj.Setup('192.168.1.100', '97777');

控件进入dll,但是szIp和szPort形参只接收我从delphi程序传过来的ip和端口的第一个字符。

我知道这与在 delphi 中正确终止字符串有关。所以我也尝试了以下方法。

var
  pzIp, pzPort: PChar;
  szIp, szPort: string; 

begin
   szIp   := '192.168.1.2';
   szPort := '9777';
   //initilize memory for pchar vars
   GetMem(pzIp, Length(szIp)+1);
   GetMem(pzPort, Length(szPort)+1);
   //null terminate the strings
   pzIp[Length(szIp)+1] := #0;
   pzPort[Length(szPort)+1] := #0;
   //copy strings to pchar
   StrPCopy(pzIp, szIp);
   StrPCopy(pzPort, szPort);
end.

这也不起作用。当我 Writeln pzIppzPort 我得到奇怪的结果。

忘了说,C++ dll 中的所有成员函数都使用__stdcall 编译并正确导出

【问题讨论】:

    标签: c++ delphi dll


    【解决方案1】:

    在 Delphi 2010(和 Delphi 2009)中,“char”类型实际上是 WIDEChar - 即 16 位宽。因此,当您调用 C++ 函数时,如果期望 CHAR 为 8 位宽(所谓的“ANSI”,而不是 UNICODE),那么它将误解输入参数。

    例如如果您传递字符串 'ABC'#0 (我明确显示空终止符,但这只是 Delphi 中字符串的隐含部分,不需要专门添加)这将传递一个指向 8 字节 序列的指针,不是 4 字节!

    但是因为字符串中的 3 个字符只有 8 位代码点值(在 Unicode 术语中,这意味着 C++ 代码“看到”的是一个看起来像这样的字符串:

      'A'#0'B'#0'C'#0#0#0
    

    这可以解释为什么您的 C++ 代码似乎只获取字符串的第一个字符 - 它在第一个字符的 2nd byte 中看到 #0并假设它是整个字符串的空终止符。

    您需要修改您的 C++ 代码以正确接收指向 WideChar 字符串的指针,或者修改 Delphi 中的函数签名并将您的字符串转换为 Delphi 代码中的 ANSIString 之前将这些传递给 C++ 函数:

    修改后的函数签名:

      function Setup(ip, port: PANSIChar):Integer: virtual; stdcall; abstract;
    

    以及在调用函数之前显示将字符串转换为 ANSIString 的相应“长手” - 编译器可能会为您处理此问题,但您可能会发现在代码中明确说明会有所帮助而不是依赖“编译器魔法”:

      var
        sIPAddress: ANSIString;
        sPort: ANSIString;
      begin   
        sIPAddress := '192.168.1.100';
        sPort      := '97777';
    
        pObj.Setup(sIPAddress, sPort);
    
        // etc... 
    

    【讨论】:

    • 感谢您提供更详细的解释:我的回答是一个快速的想法,事实证明是正确的,但我建议将此作为被接受的答案。
    • 感谢@Deltics,这很有效。不过,需要进行一个小改动以使 delphi 程序编译 pObj.Setup(PAnsiChar(sIPAddress), PAnsiChar(sPort));特别感谢@IanH
    【解决方案2】:

    两个编译器中的 char 大小是否相同?如果您使用的是 D2009/D2010,char 现在是 16 位的。

    【讨论】:

    • 是的,我正在使用 D2010。我的 C++ 代码是 32 位的。所以字符大小应该匹配吧?
    • 不一定:BCB4是一个32位的C++编译器,但是一个char就是一个字节。您可能会使用第一个字符的 0 高字节过早地终止您的字符串。尝试更改上面的测试代码,将 szIP 和 szPort 声明为 AnsiString 而不是字符串。
    【解决方案3】:

    如果我理解正确,您的函数原型也应该是 stdcall。

    function Setup(ip, port: PChar):Integer: virtual; stdcall; abstract;
    

    ps。 Delphi 字符串已经以空值结尾。

    【讨论】:

    • 嗯,没用。同样的结果。但是当 C++ 函数返回时,我可以摆脱访问冲突崩溃。所以,我想我正在取得进展:)
    • 进入装配视图的时间:)
    • 制作 stdcall 让我也解决了一些其他问题,+1 提示
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-01-25
    • 2013-11-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多