2017-08-10 40 views
0

我有以下設置:2 COM對象和使用從另一個接口

C#應用程序和兩個COM對象(面部和處理器),用C++編寫。
第一個COM對象暴露接口IFace。它在IDL文件中有描述。
第二個COM對象有一個接口IP處理器,暴露方法ProcessFace(IFace * face)。

而C#應用程序應該使用第一個COM對象來獲取面,第二個來處理它。 這裏的問題是 - 什麼是正確的方式來編寫第二個對象的IDL文件,以便它可以從第一個COM使用接口類型的參數。

我應該以某種方式將face.idl包含到processor.idl中,或者將該參數保留爲void *,然後在ProcessFace實現中將其轉換爲IFace *。

我說對了,第二個COM對象應該包含一些.h文件(用於接口共享),但這是它應該包含的唯一的東西嗎?

+0

難道你不能把所有的C++放在一個.idl文件中嗎(帶有前向聲明)? –

+0

@SimonMourier沒有不幸的。有不同的工作方式。一個是exemodule,它爲每個主com對象的使用生成單獨的進程。和其他是DLL。該架構相當繁瑣,我試圖簡化詢問,爲了簡潔 –

+0

這是一個基本的細節。其中一個接口指針必須始終被編組,因爲它的服務器在進程外,你不能編組一個void *。不要這樣做。 –

回答

0

這裏有兩個選項 - importimportlib

不幸的是,import導致導入文件中的接口和coclass定義出現在已編譯的.tlb文件中。這會導致註冊和代理時出現各種問題。我不建議使用import

importlib不會遭受同樣的問題。但是,這意味着您必須首先將.idl編譯爲.tlb,然後才能導入它。從.idl編譯一個.tlb,其中包含您的依賴關係(IFace)。然後使用importlib關鍵字從.idl中引用包含您的從屬(IProcessor)的類型庫。你的第二個.idl文件將是這個樣子:

[...] 
library ProcessorLibrary 
{ 
    importlib("FaceLibrary.tlb"); 

    [...] 
    interface IProcessor 
    { 
     HRESULT ProcessFace(FaceLibrary.IFace* face) 
    }; 
} 

對於importlib說法,你可以使用一個完全合格的路徑或相對路徑。如果使用相對路徑,它將嘗試使用環境變量PATH來解析該文件名。如果您正在使用Visual Studio進行此操作,則可以在項目屬性中設置搜索路徑 - 在VC++ Directories下,將搜索路徑放入Executable Directories變量中。這對應於.vcxproj文件中的ExecutablePath屬性。

請注意,importlib中的「FaceLibrary」與接口中的「FaceLibrary」不同。 importlib中的那個是.tlb文件的名稱。接口中使用的標識符是類型庫名稱(與第一個.idl文件中的library鍵一起使用的名稱)。

爲了編譯第二個.idl文件生成的C++,你必須做兩件事。第一個.tlb需要手動#import編輯到第二個C++項目中,就像您必須在第二個.idl中使用importlib一樣。那麼你需要處理C++命名空間問題。不幸的是,midl.exe不會生成使用名稱空間的代碼。雖然第二個.idl文件包含FaceLibrary.IFace,但生成的C++只包含IFace。這意味着您需要將第一個.tlb導入到不使用名稱空間的C++項目中,或者您必須使用typedef。

使用沒有命名空間:

#import "FaceLibrary.tlb" no_namespace 
#include "Processor_i.h" 

使用命名空間:

#import "FaceLibrary.tlb" 
typedef FaceLibrary.IFace IFace; 
#include "Processor_i.h" 

相對路徑的分辨率在#import的工作方式相同,如importlib

+0

我會盡力的。應該將.tlb複製到Processor項目的文件夾中?還是隻能通過名字引用? –

+0

好了,idl被編譯了,但現在它在Processor_i.h中抱怨IFace '/ * [in] *// *外部定義不存在*/IFace * face,' –

+0

啊,是的。這是IMO在midl.exe中的一個錯誤。我會更新我的答案,如何解決這個問題。 –

0

您可以使用idl import指令。 Windows SDK .idl文件充滿了這些文件(如import "unknwn.idl";)。所以,在processor.idl

import "face.idl"; 

[ 
    object, 
    uuid(6E0537CC-232A-4E73-A625-358591CA9231), 
    pointer_default(unique) 
] 
interface IProcessor 
{ 
    HRESULT ProcessFace(IFace *pFace); 
}; 

當然,這將需要的.idl文件(和產生的.h和各種文物,如果有的話,編譯階段),存在於訪問的目錄。

+0

'導入「就是導致導入文件中的接口和coclass定義包含在已編譯的'.tlb'中。這引入了各種註冊和代理問題。 –

+0

我創建了一組樣本文件,如果你想事實檢查我這一個。只需下載這些文件並從Visual Studio命令提示符運行'build.cmd'即可。然後使用'oleview.exe'查看'.tlb'文件。你會看到'IFoo'存在於'BarLib.tlb'中。這意味着'BarLib.tlb'將成爲官方類型庫,如果它曾經使用'RegisterTypeLib'註冊的話。 https://gist.github.com/mgunter6/8d30a23bd64a576118253fb157dcabaf –

+0

@MichaelGunter - 兩個選項都是有效的,它真的取決於你想如何定義你的IDL。您不一定需要將接口放入typelib(在typelib屬性下)。您可以在C#中使用無界面的tlb接口(雖然這是更初始的工作)。因此,我最初的問題是將所有內容放在同一個文件中。如果idl文件彼此之間有深入的瞭解,那麼只定義一個tlb並且每個項目引用它(無論部署約束是什麼)可能是合理的。否則,他們可以使用IID和IUnknown指針,這也是SDK中非常常見的模式。 –