Delphi XE7调用C++动态库出现乱码问题回顾
事情源于有个客户需使用我们c++的中间件动态库来跟设备连接通讯,但是传入以及传出的字符串指针格式都不正确(出现乱码或是被截断),估计是字符编码的问题导致。以下是解决问题的过程:
我们c++中间件动态库的接口函数声明:
extern "c" bool __stdcall executetaskinfile(const char *sztaskid, const char *szinputfile, const char *szoutputfile, bool bsynch);
传入的字符串指针要求是ansi编码。
我们给客户提供的delphi例子是用delphi7写的:
声明:
function executetaskinfile(sztaskid : pchar; szinputfile:pchar; szoutputfile:pchar; bsynch :boolean):boolean;stdcall;external 'xxxx.dll';
调用:
function executefiletask():boolean; var strtaskid:string; begin //略去若干注释 strtaskid := '20160222111817081'; result := executetaskinfile(pchar(strtaskid), pchar('c:\data\testdata\input\task.xml'), pchar('c:\data\testdata\output\taskresult.xml'), true); end;
但是该程序拿到客户处编译运行后出现一些莫名错误,据客户反馈,他们也是使用delphi,因此怀疑方向一度转到传入的数据文件内容是否有错误上,但从客户处取回数据文件后测试也无问题。不得已,只能上门拜访了下客户,幸好不是很远。到了现场才得知用户使用的delphi是delphi xe7,已经是borland将delphi抛弃很多年以后的事情了,距离我使用过的delphi7也已经是12年后了。
在客户现场查看动态库接口的日志,发现了如下的记录:
2018-10-15 13:28:14,318 [0x000012c0] info running 0 xxxx.xxxx enter xxxx::executetaskinfile(taskid:2)
而我们传入的taskid其实是'20160222111817081',只取到了第一个字符,那么很有可能是传入的指针指向的字符串是使用unicode编码的,其第二个字节是0x00被当作字符串结束符而截断了。
很多年没有关注delphi,又回顾了下delphi的历史,delphi7还是ansi编码的ide,因此上面的代码调用c++动态库传入ansi字符串没有问题, 而delphi从2009开始完整正式支持unicode,之后的xe7是2014年发布的。 delphi在2009以后对应char*的类型为pwidechar,因此默认使用了unicode编码,如果要用ansi编码,必须是pansichar。
这样看来,问题就简单明了了,将delphi代码改成如下后测试通过:
声明:
function executetaskinfile(sztaskid : pansichar; szinputfile:pansichar; szoutputfile:pansichar; bsynch :boolean):boolean;stdcall;external 'xxxx.dll';
调用:
function executefiletask():boolean; var strtaskid:string; begin strtaskid := '20160222111817081'; result := executetaskinfile(pansichar(ansistring(strtaskid)), pansichar('c:\data\testdata\input\task.xml'), pansichar('c:\data\testdata\output\taskresult.xml'), true); end;
值得注意的是,当使用字符串变量传递时,必须加以ansistring转换,而常量字符串参数则可不用,后面再仔细研究了。
总结:其实事情本不复杂,可以通过简单的沟通就了解到客户的使用场景以及动态库的调用日志然后做针对性的分析,但是我们常常会忽略一些细节从而做出一些片面的判断导致问题复杂化,遇到问题时按部就班,不放过一个细节,针对性的分析解决才是正确的做法。
忌 一叶蔽目,管中窥豹。
宜 审问慎思,明辨笃行。