混合调用时接口类型中的陷阱

[delphi] view plain copy

  1. function abc(A: Integer): IUnknown;

这是一个Delphi的函数声明,看上去很简单,只有一个参数而已,但是真实情况呢?在编译成二进制代码后,实际上函数的参数已经有2个了!

为了更详细的说明问题,先用Delphi写一个DLL,导出一个接口,接口有一个Show方法。

[delphi] view plain copy

  1. library Project1;
  2. uses
  3. Windows;
  4. {$R *.res}
  5. type
  6. ITest = interface
  7. procedure Show(); stdcall;
  8. end;
  9. TTest = class(TInterfacedObject, ITest)
  10. public
  11. procedure Show(); stdcall;
  12. end;
  13. function GetTest: ITest; stdcall;
  14. begin
  15. Result := TTest.Create;
  16. end;
  17. exports
  18. GetTest;
  19. { TTest }
  20. procedure TTest.Show;
  21. begin
  22. OutputDebugString(‘Hello World‘);
  23. end;
  24. begin
  25. end.

调用方用C++编写

[cpp] view plain copy

  1. #include "stdafx.h"
  2. #include <iostream>
  3. #include <Windows.h>
  4. interface ITest : public IUnknown
  5. {
  6. virtual void __stdcall show() = 0;
  7. };
  8. typedef ITest* (WINAPI *GetITest)();
  9. int _tmain(int argc, _TCHAR* argv[])
  10. {
  11. HMODULE h = LoadLibrary(TEXT("Project1.dll"));
  12. if (h != 0)
  13. {
  14. GetITest get = (GetITest)GetProcAddress(h, "GetTest");
  15. ITest *test = get();
  16. test->show();
  17. test->Release();
  18. }
  19. system("pause");
  20. return 0;
  21. }

运行后直接弹出一个内存错误

出错语句在DLL中

[delphi] view plain copy

  1. function GetTest: ITest; stdcall;
  2. begin
  3. Result := TTest.Create;
  4. end;

以反汇编代码的形式查看这个函数就能发现问题

可以看到,函数返回值是接口类型的时候,实际上返回值是一个隐式的参数,是一个二级指针类型。在Dephi中使用不会发现问题,因为它自动作出了优化。

而在多语言混合编程中,这样直接返回一个接口或对象的时候就会出现内存为空的错误。

修改后的调用代码

[cpp] view plain copy

  1. #include "stdafx.h"
  2. #include <iostream>
  3. #include <Windows.h>
  4. interface ITest : public IUnknown
  5. {
  6. virtual void __stdcall show() = 0;
  7. };
  8. // 正确的函数原型
  9. typedef VOID (WINAPI *GetITest)(ITest**);
  10. int _tmain(int argc, _TCHAR* argv[])
  11. {
  12. HMODULE h = LoadLibrary(TEXT("Project1.dll"));
  13. if (h != 0)
  14. {
  15. GetITest get = (GetITest)GetProcAddress(h, "GetTest");
  16. // 修改后的调用方法
  17. ITest *test = nullptr;
  18. get(&test);
  19. test->show();
  20. test->Release();
  21. }
  22. system("pause");
  23. return 0;
  24. }

最后可以总结出一点经验,当Delphi函数返回值为接口类型的时候,函数会认为第一个参数是一个接口缓冲区,用于接受接口的实例对象。

那么是否可以在不改变C++这边调用方式的前提下直接返回接口指针呢?答案也是肯定的,只要把返回数据类型改为基础类型即可

[delphi] view plain copy

  1. function GetTest: Pointer; stdcall;
  2. var
  3. Temp: ITest;
  4. begin
  5. Temp := TTest.Create;
  6. Temp._AddRef;
  7. Result := Pointer(Temp);
  8. end;

由于函数返回值已不再是一个接口类型,Delphi也不会去调用接口的AddRef方法把引用计数+1了,所以在创建接口后得手动调用AddRef方法

否则函数在结束后会自动释放Temp,导致返回值是一个野指针。

http://blog.csdn.net/aqtata/article/details/19079737

时间: 2024-12-26 15:36:29

混合调用时接口类型中的陷阱的相关文章

python列表类型中的陷阱

在python中对列表使用重复运算符*进行操作时,只是简单的进行了浅复制,内部的结构并没有复制过来,所以下面的例子结果是这样的: >>> lists =[[]]*3 >>> lists [[],[],[]] >>> lists[0].append(3) >>> lists [[3],[3],[3]] [[]]只是一个单元素列表,元素为一个空列表,执行重复运算之后,这三个元素都指向同一个空列表,修改lists中的任一个元素都将修改这个空

接口类型使用

参考:https://time.geekbang.org/column/article/18037?utm_source=weibo&utm_medium=xuxiaoping&utm_campaign=promotion&utm_content=columns 接口类型与其他数据类型不同,它是没法被实例化的.更具体地说,我们既不能通过调用new函数或make函数创建出一个接口类型的值,也无法用字面量来表示一个接口类型的值.接口类型声明中的这些方法所代表的就是该接口的方法集合.一个

微信支付开发(APP)的各种坑,.net和iOS的各种陷阱,解决.net调用下单接口提示无权限,解决iOS跳转到微信支付页面中间只有一个确定按钮

直入主题之前,请容我吐槽一下微*的官方东西:ASDFQ%#$%$#$%^[email protected]#$%DSFQ#$%.......:吐槽玩了!大家心照就好. 要完成手机APP跳转到微信的APP进行微信支付,需要进行如下操作: 1.先去微信的开放平台(http://open.weixin.qq.com)进行开发者账号的注册. 2.新建一个APP应用,然后填写必填信息提交审核. 3.进入APP应用,在接口信息中,进行申请“获得微信支付能力”的功能,期间会提交相关的公司营业信息证明等. 通过

oc调用c++接口时 报错 Undefined symbols for architecture i386:

当在oc中调用c++中的方法时,发现说c++中的方法没定义或是找不到 Undefined symbols for architecture i386: "_desTYData", referenced from:-[TuYoo encryptParametersWithDict:] in libtuyoo.a(TuYoo.o)ld: symbol(s) not found fo 记得c++中的方法定义是要这样定义的 extern"C" { const char *d

VC++和Matlab混合编程(在VC中调用将.m文件生成的DLL)

这是师兄们在实际项目中用到的关于VC++和MATLAB混合编程的实例.最近自己在瞎鼓捣,也就研究了下这块,本想写篇博文,但网上这块的资料还算是比较多,写的也比较具体,想着不重复劳动,因此我也就不写了. //提取节点信息 mxArray *csd_path; mxArray *save_path; mxArray *mat_path=mxCreateCellMatrix(NULL,NULL);//mxCreateCellMatrix:创建二维单位矩阵 mxArray *analog=mxCreat

C# WPF VS2012 对类型“ ”的构造函数执行符合指定的绑定约束的调用时引发了异常 问题解决办法 产生什么原因

运行程序时报 对类型"XX.XXX"的构造函数执行符合指定的绑定约束的调用时引发了异常 XX.XXX  代表命名空间.类 namespace Test { /// <summary> /// Test.xaml 的交互逻辑 /// </summary> public partial class Test : Test { public Test() { InitializeComponent(); // 报错位置 对类型" "的构造函数执行符

当接口被调用时使用Spring拦截器注入运行时数据

开发背景 使用CXF暴露与调用接口,为了方便追踪错误,所以想要在接口被调用时将一些运行时数据记录起来,所以就想到了拦截器. CXF自带拦截器,但是据我初步了解,自带的拦截器都是经过封装,用来打印日志什么的,好像没有提供给开发者定制功能的方式(没有深入了解,如果有说错请方便斧正) 流程 其实Spring的拦截器使用挺方便的,实现org.aopalliance.intercept.MethodInterceptor接口中的invoke方法,在方法中实现想要的逻辑.然后在Spring配置文件中注入它就

php中创建和调用webservice接口示例

这篇文章主要介绍了php中创建和调用webservice接口示例,包括webservice基本知识.webservice服务端例子.webservice客户端例子,需要的朋友可以参考下 作为开发者来讲,要想写webservice接口或者调用别人的webservice接口,首先需要了解什么是webservice.简单说, WebService就是一些站点开放一些服务出来, 也可以是你自己开发的Service, 也就是一些方法, 通过URL,指定某一个方法名,发出请求,站点里的这个服务(方法),接到

WebApi接口 - 如何在应用中调用webapi接口

简单做个webapi(查询+添加)接口 首先,我们需要有一个webapi接口项目,我这里以前面WebApi接口 - 响应输出xml和json文章的项目来构建本篇文章的测试用例:这里新建一个 DbData 数据源类,主要用来做数据存储和提供查询列表数据及添加数据方法,具体代码如:  1 public class DbData 2     { 3         public static DbData Current 4         { 5             get 6