Delphi的PChar转换为C++的const char*

7
我是一个有用的助手,可以为您翻译文本。

我正在尝试从本地程序中使用C++ dll。我正在按照这里所解释的虚拟方法场景进行操作。

假设我的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 并正确导出。

3个回答

18
在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++代码似乎只能获取到字符串的第一个字符 - 它在该第一个字符的第二个字节中看到了#0并假定它是整个字符串的空终止符。你需要修改C++代码以正确接收指向WideChar字符串的指针,或在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... 

感谢更详细的解释:我的回答只是一个快速的想法,但我建议将此作为被接受的答案。 - IanH
谢谢@Deltics,它起作用了。不过需要进行一些小的更改,以便将Delphi程序编译。pObj.Setup(PAnsiChar(sIPAddress), PAnsiChar(sPort));特别感谢@IanH。 - rptony

4

编译器中的char类型大小是否相同?如果您使用的是D2009/D2010版本,则char类型现在为16位。


是的,我正在使用D2010。我的C++代码是32位的。因此字符大小应该匹配,对吗? - rptony
2
不一定:BCB4是一个32位的C++编译器,但一个char只有一个字节。你可能会用0高字节过早地结束你的字符串,导致第一个字符无法解析。尝试将上述测试代码中的szIP和szPort声明为AnsiString而非string。 - IanH

3
如果我理解正确,您的函数原型也应该是stdcall。
function Setup(ip, port: PChar):Integer: virtual; stdcall; abstract;

注意:Delphi字符串已经是以null结尾的。


嗯,它没有奏效。结果还是相同的。但是当C++函数返回时,我可以摆脱访问冲突的崩溃。所以,我猜我正在取得进展 :) - rptony
是时候转到汇编视图了 :) - utku_karatas
将“making it stdcall”使函数成为stdcall调用方式后,我解决了其他几个问题,谢谢这个提示。 - rptony

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接