码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2016-03-14 23:07:10      阅读:490      评论:0      收藏:0      [点我收藏+]

标签:

[delphi] view plain copy
 
 技术分享技术分享
  1. function abc(A: Integer): IUnknown;  

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


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

[delphi] view plain copy
 
 技术分享技术分享
  1. library Project1;  
  2.   
  3. uses  
  4.   Windows;  
  5.   
  6. {$R *.res}  
  7.   
  8. type  
  9.   ITest = interface  
  10.     procedure Show(); stdcall;  
  11.   end;  
  12.   
  13.   TTest = class(TInterfacedObject, ITest)  
  14.   public  
  15.     procedure Show(); stdcall;  
  16.   end;  
  17.   
  18. function GetTest: ITest; stdcall;  
  19. begin  
  20.   Result := TTest.Create;  
  21. end;  
  22.   
  23. exports  
  24.   GetTest;  
  25.   
  26. { TTest }  
  27.   
  28. procedure TTest.Show;  
  29. begin  
  30.   OutputDebugString(‘Hello World‘);  
  31. end;  
  32.   
  33. begin  
  34. end.  


调用方用C++编写

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


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

技术分享

 

出错语句在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.   
  5. interface ITest : public IUnknown  
  6. {  
  7.     virtual void __stdcall show() = 0;  
  8. };  
  9.   
  10. // 正确的函数原型  
  11. typedef VOID (WINAPI *GetITest)(ITest**);  
  12.   
  13. int _tmain(int argc, _TCHAR* argv[])  
  14. {  
  15.     HMODULE h = LoadLibrary(TEXT("Project1.dll"));  
  16.     if (h != 0)  
  17.     {  
  18.         GetITest get = (GetITest)GetProcAddress(h, "GetTest");  
  19.           
  20.         // 修改后的调用方法  
  21.         ITest *test = nullptr;  
  22.         get(&test);  
  23.   
  24.         test->show();  
  25.         test->Release();  
  26.     }  
  27.     system("pause");  
  28.     return 0;  
  29. }  


最后可以总结出一点经验,当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

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

标签:

原文地址:http://www.cnblogs.com/findumars/p/5277569.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!