這個問題搗鼓了兩天,現在終於解決了,做個筆記分享給大家,以免走彎路
起初,我的想法是在DLL中寫一個interface並從函數中導出這個interface,像這樣的代碼
- ICom1 = interface
- function Show(V1, V2: Integer): Integer stdcall;
- end;
最后均以失敗告終,后來想到各種編譯器對編譯后的二進制組織方式是不同的
比如上面的代碼如果用Delphi編寫的Exe去調用就是沒問題的,而用其他語言則可能會有問題
網上有很多跨語言調用方案是用虛類來解決,也許有些時候是可以正常調用,但是這種做法並不規范,容易出問題
這里就需要通過一個標准,一個與語言無關的標准,也就是微軟的COM組件了
所以得改下這個DLL
新建一個Automation Object
注意,這里一定要用Automation Object,而不要用COM Object,因為后者不支持自動化調用
而QAxObject只支持自動化對象,如果你嘗試去調用一個不支持自動化的COM Object會得到一條消息
“QAxBase::dynamicCallHelper: Object does not support automation”
創建成功后接口是這樣的
- ICOM2 = interface(IDispatch)
- ['{53952FF2-94A4-4B14-9C38-E4E56C87940A}']
- function Show(v1: Integer; v2: Integer): Integer; stdcall;
- end;
注意,我們在Exe中查詢需要用到的GUID是類ID而不是這里的接口ID,在Delphi自動生成的 XXX_TLB.pas 文件中是有這個GUID的
我這里的是 {0EA6D9F4-0587-4AB9-91AD-9CD657B0787D}
最后實現
- function TCOM2.Show(v1, v2: Integer): Integer;
- begin
- OutputDebugString(PChar(Format('TCom2(%d, %d)', [v1, v2])));
- Result := 2;
- end;
現在到Qt中,首先寫一個函數,功能是從一個COM DLL中動態創建接口實例
這樣用的好處是COM DLL不用注冊就能用,當然注冊的話調用起來會更方便
- LPUNKNOWN CreateComObjectFromDll(const QString &dll, REFCLSID clsid)
- {
- QLibrary lib(dll);
- if (lib.load()) {
- typedef HRESULT (__stdcall *DllGetClassObject)(REFCLSID, REFIID, LPVOID*);
- DllGetClassObject getClassObject = (DllGetClassObject)lib.resolve("DllGetClassObject");
- if (getClassObject != nullptr) {
- IClassFactory *factory;
- if (getClassObject(clsid, IID_IClassFactory, (LPVOID*)&factory) == S_OK) {
- LPUNKNOWN ret = nullptr;
- factory->CreateInstance(nullptr, IID_IUnknown, (void**)&ret);
- return ret;
- }
- }
- }
- return nullptr;
- }
最后,調用部分
- LPUNKNOWN obj = CreateComObjectFromDll("com", cid);
- QAxObject o(obj);
- o.dynamicCall("Show(int, int)", 123, 456);
參數cid就是接口類GUID
可以看到輸出窗口正常顯示了結果。
總結:起初沒有使用標准的COM接口,走了不少彎路。。。 -_-!
http://blog.csdn.net/aqtata/article/details/9163689