混合调用时接口类型中的陷阱
Posted 朝闻道
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了混合调用时接口类型中的陷阱相关的知识,希望对你有一定的参考价值。
- function abc(A: Integer): IUnknown;
这是一个Delphi的函数声明,看上去很简单,只有一个参数而已,但是真实情况呢?在编译成二进制代码后,实际上函数的参数已经有2个了!
为了更详细的说明问题,先用Delphi写一个DLL,导出一个接口,接口有一个Show方法。
- library Project1;
- uses
- Windows;
- {$R *.res}
- type
- ITest = interface
- procedure Show(); stdcall;
- end;
- TTest = class(TInterfacedObject, ITest)
- public
- procedure Show(); stdcall;
- end;
- function GetTest: ITest; stdcall;
- begin
- Result := TTest.Create;
- end;
- exports
- GetTest;
- { TTest }
- procedure TTest.Show;
- begin
- OutputDebugString(‘Hello World‘);
- end;
- begin
- end.
调用方用C++编写
- #include "stdafx.h"
- #include <iostream>
- #include <Windows.h>
- interface ITest : public IUnknown
- {
- virtual void __stdcall show() = 0;
- };
- typedef ITest* (WINAPI *GetITest)();
- int _tmain(int argc, _TCHAR* argv[])
- {
- HMODULE h = LoadLibrary(TEXT("Project1.dll"));
- if (h != 0)
- {
- GetITest get = (GetITest)GetProcAddress(h, "GetTest");
- ITest *test = get();
- test->show();
- test->Release();
- }
- system("pause");
- return 0;
- }
运行后直接弹出一个内存错误
出错语句在DLL中
- function GetTest: ITest; stdcall;
- begin
- Result := TTest.Create;
- end;
以反汇编代码的形式查看这个函数就能发现问题
可以看到,函数返回值是接口类型的时候,实际上返回值是一个隐式的参数,是一个二级指针类型。在Dephi中使用不会发现问题,因为它自动作出了优化。
而在多语言混合编程中,这样直接返回一个接口或对象的时候就会出现内存为空的错误。
修改后的调用代码
- #include "stdafx.h"
- #include <iostream>
- #include <Windows.h>
- interface ITest : public IUnknown
- {
- virtual void __stdcall show() = 0;
- };
- // 正确的函数原型
- typedef VOID (WINAPI *GetITest)(ITest**);
- int _tmain(int argc, _TCHAR* argv[])
- {
- HMODULE h = LoadLibrary(TEXT("Project1.dll"));
- if (h != 0)
- {
- GetITest get = (GetITest)GetProcAddress(h, "GetTest");
- // 修改后的调用方法
- ITest *test = nullptr;
- get(&test);
- test->show();
- test->Release();
- }
- system("pause");
- return 0;
- }
最后可以总结出一点经验,当Delphi函数返回值为接口类型的时候,函数会认为第一个参数是一个接口缓冲区,用于接受接口的实例对象。
那么是否可以在不改变C++这边调用方式的前提下直接返回接口指针呢?答案也是肯定的,只要把返回数据类型改为基础类型即可
- function GetTest: Pointer; stdcall;
- var
- Temp: ITest;
- begin
- Temp := TTest.Create;
- Temp._AddRef;
- Result := Pointer(Temp);
- end;
由于函数返回值已不再是一个接口类型,Delphi也不会去调用接口的AddRef方法把引用计数+1了,所以在创建接口后得手动调用AddRef方法
否则函数在结束后会自动释放Temp,导致返回值是一个野指针。
http://blog.csdn.net/aqtata/article/details/19079737
以上是关于混合调用时接口类型中的陷阱的主要内容,如果未能解决你的问题,请参考以下文章