zoukankan      html  css  js  c++  java
  • 混合调用时接口类型中的陷阱

    [delphi] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. function abc(A: Integer): IUnknown;  

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


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

    [delphi] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    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
     
     在CODE上查看代码片派生到我的代码片
    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
     
     在CODE上查看代码片派生到我的代码片
    1. function GetTest: ITest; stdcall;  
    2. begin  
    3.   Result := TTest.Create;  
    4. end;  


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

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

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

    修改后的调用代码

    [cpp] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    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
     
     在CODE上查看代码片派生到我的代码片
    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

  • 相关阅读:
    Nginx 前后端分离部署
    SpringBoot 使用外部 Tomcat 容器运行
    SpringBoot 加载 properties
    SpringBoot 启动时加载的自动配置类(SpringFactoriesLoader、SPI)
    微信小程序订阅消息通知
    centos云服务器 部署Gitblit
    centos 安装java
    图片链接控制宽高
    腾讯云部署https
    腾讯云域名绑定
  • 原文地址:https://www.cnblogs.com/findumars/p/5277569.html
Copyright © 2011-2022 走看看