事情源于有个客户需使用我们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 := '';

    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 := '';

    Result := ExecuteTaskInFile(pAnsichar(AnsiString(strTaskID)), pAnsichar('C:\Data\TestData\Input\Task.xml'), pAnsichar('C:\Data\TestData\Output\TaskResult.xml'), true);

end;

  值得注意的是,当使用字符串变量传递时,必须加以AnsiString转换,而常量字符串参数则可不用,后面再仔细研究了。

  总结:其实事情本不复杂,可以通过简单的沟通就了解到客户的使用场景以及动态库的调用日志然后做针对性的分析,但是我们常常会忽略一些细节从而做出一些片面的判断导致问题复杂化,遇到问题时按部就班,不放过一个细节,针对性的分析解决才是正确的做法。

  忌 一叶蔽目,管中窥豹。

  宜 审问慎思,明辨笃行。

Delphi XE7调用C++动态库出现乱码问题回顾的更多相关文章

  1. c#调用c++动态库的一些理解

    调用c++动态库一般我们这样写   [DllImport("UCamer.dll", CallingConvention = CallingConvention.Winapi)] ...

  2. C#调用C++动态库(dll)

    在实际软件开发过程中,由于公司使用了多种语言开发,在C#中可能需要实现某个功能,而该功能可能用其他语言已经实现了,那么我们可以调用其他语言写好的模块吗?还有就是,由于C#开发好的项目,我们可以利用re ...

  3. C#总结(四)调用C++动态库

    由于公司很多底层的SDK,都是C++开发,上层的应用软件却是C# Winform程序.在实际工作的过程中,就经常碰到了C# 程序调用C++ 动态库的问题.最近一直在和C++ 打交道,C# 怎么调用C+ ...

  4. Java调用dll动态库

    最近项目里使用java调用dll动态库,因此研究了一下这方面的东西. 使用的工具包如下 <dependency> <groupId>net.java.dev.jna</g ...

  5. 【C#】 使用Gsof.Native 动态调用 C动态库

    [C#] 使用Gsof.Native 动态调用 C动态库 一.背景 使用C# 开发客户端时候,我们经常会调用一些标准的动态库或是C的类库.虽然C# 提供的PInvoke的方式,但因为使用的场景的多变, ...

  6. python调用.net动态库

    # python调用.net动态库 ### pythonnet简介------------------------------ pythonnet是cpython的扩展- pythonnet提供了cp ...

  7. 使用ctypes在Python中调用C++动态库

    使用ctypes在Python中调用C++动态库 入门操作 使用ctypes库可以直接调用C语言编写的动态库,而如果是调用C++编写的动态库,需要使用extern关键字对动态库的函数进行声明: #in ...

  8. C# 调用C++动态库注意事项

    C# 调用C++动态库注意事项 最近项目上需要在C#中调用C++,期间遇到不少坑,总结如下: 1.in const char*   对应C#中string 或  IntPtr 2.out const ...

  9. JNI_Android项目中调用.so动态库

    JNI_Android项目中调用.so动态库 2014年6月3日 JNI学习 參考:http://blog.sina.com.cn/s/blog_4298002e01013zk8.html 上一篇笔者 ...

随机推荐

  1. 三、vue如何配置路由 、获取路由的参数、部分刷新页面、缓存页面

        1.路由配置:所有的启动文件都在最初始的main.js文件里面,这个文件中首先需要引入: 2.路由文件配置说明: 3.如何获取页面url的参数? this.$route.query 4.页面之 ...

  2. ES6-fetch

    fetch 事实标准,并不存在与ES6规范中,基于Promise实现. 目前项目中对Promise的兼容性尚存在问题,如果在项目中应用fetch,需要引入es6-promise和fetch. fis3 ...

  3. 通过代码管理工具 git 完成一次完整的代码管理过程

    1.从公共远程fork一份自己的本地远程之后,从本地远程 clone 到本地 2.将本地代码跟公共远程代码做关联配置 git remote add upstream https://github.co ...

  4. Ionic Css样式详解

    Header是固定在屏幕顶部的组件.可以包含如标题和左右的功能按钮.Sub Header同样是固定在顶部,只是是在Header的下面,就算没有写Header这个,Sub Header这个样式也会距离顶 ...

  5. docker的网络基础配置

    一.端口映射实现访问容器 当容器中运行一些网络应用,要让外部访问这些应用时,可以通过-P或-p参数来指定端口映射.当使用-P标记时,Docker会随机映射一个49000~49900的端口至容器内部开放 ...

  6. C# List<T>的并集、交集、差集

    集合的并集是合并集合的项,如下图所示: List<,,,,, }; List<,,,,,}; IEnumerable<int> unionLs = ls1.Union(ls2) ...

  7. Linux man 命令详细介绍

    知道linux帮助文件(man-pages,手册页)一般放在,$MANPATH/man 目录下面,而且按照领域与语言放到不同的目录里面. 看了上一章,要找那个命令使用相关手册,只要我们按照领域区分,到 ...

  8. 有序字典(OrderedDict)、默认字典(defaultdict)内置函数

    http://www.cnblogs.com/wupeiqi/articles/5115190.html import collections do = collections.OrderedDict ...

  9. Redis学习---CentOs/RedHat下Redis的安装

    redis是C语言开发,建议在linux上运行,本教程使用Centos6.4作为安装环境.      安装redis需要先将官网下载的源码进行编译,编译依赖gcc环境,如果没有gcc环境,需要安装gc ...

  10. html端输入数据,利用qrcode.js生成打印二维码

    在前端页面中导入qrcode.js(下载)和jquery.js(下载) index.html <script> function print() { var textbox1 = $('i ...