2015-09-04 60 views
1

我正在創建一個使用ATL的進程內COM服務器。這個COM服務器將會公開的類型是枚舉。此枚舉的值需要使用此COM服務器依賴的另一個COM服務器的值來定義。我已經嘗試了幾乎所有我能想到的事情,以使這個工作沒有真正的成功。在COM IDL中如何從COM依賴關係引用枚舉?

以下是COM服務器依賴關係的IDL。

Type1Lib.idl 

import "oaidl.idl"; 
import "ocidl.idl"; 

[ 
    uuid(B777544C-77D9-4417-8302-4EAC8272DEDC), 
    version(1.0), 
] 
library Type1Lib 
{ 
    // required system import. 
    importlib("stdole2.tlb"); 

    // A simple enum to be used by another COM server. 
    typedef 
    [ 
     uuid(EF82F7A5-3A55-44B9-AD06-201A6D0A6021) 
    ] 
    enum Enum1 
    { 
     One, 
     Two, 
     Three, 
     Four, 
     Five 
    } Enum1; 
}; 

以下是試圖使用Enum1(包括我試過的一些方法)的COM服務器的IDL。

Type2Lib.idl 

// Required system imports. 
import "oaidl.idl"; 
import "ocidl.idl"; 

// (2) FAIL Enables use of Type1Lib enums in this idl file but only as long as usage 
// of those types is totally unqualified. The problem with that is that when a tlh file 
// is generated from the type library this idl file creates, that tlh file is malformed 
// because the unqualified enum references do not resolve to anything. 
import "Type1Lib.idl"; 

// FAIL Neither of these statements even compiles, Emits error: 
// "Native Compiler support only available in C++ compiler". 
// The tlh is C++ and apparently cannot be parsed by MIDL. 
//import "type1lib.tlh"; 
//#include "type1lib.tlh" 

[ 
    uuid(D40AC182-8744-42D1-B194-602AEDDC6E7C), 
    version(1.0), 
] 
library Type2Lib 
{ 
    // Required system import. 
    importlib("stdole2.tlb"); 

    // Import Type1Lib without redeclaring the types it contains. 
    // (1) FAIL Enables usage of the enum type in Type1Lib, but not the enum values, 
    // so that's utterly useless. 
// importlib("Type1Lib.dll"); 

    typedef 
    [ 
     uuid("0B8D400A-6A8F-44B3-986D-9E099830BB6D") 
    ] 
    enum Enum2 
    { 
     A = 0x80000000 + One, // One references an enum value from Type1Lib. 
     B, 
     C, 
     D, 
     E 
    } Enum2; 


    [ 
     object, 
     uuid(F5BA0CB0-B7C7-4483-A3D9-D4B9E39E6269), 
     dual, 
     nonextensible, 
     pointer_default(unique) 
    ] 
    interface IType2 : IDispatch 
    { 
     [id(1)] HRESULT Method1([out,retval] LONG* retVal); 

     // Partial success. Can reference typedef'ed enum using importlib. Cannot however access the enum values. 
     [id(2)] HRESULT Method2([in] enum Enum1 arg1); 
    }; 

    [ 
     uuid(6179272F-4B34-4EF0-926B-296D3AA73DB7)  
    ] 
    dispinterface _IType2Events 
    { 
     properties: 
     methods: 
    }; 

    [ 
     uuid(75CE545A-D2DA-4EC9-80CF-37531516DFC1)  
    ] 
    coclass Type2 
    { 
     [default] interface IType2; 
     [default, source] dispinterface _IType2Events; 
    }; 
}; 

因此,使用的類型庫(嵌入在DLL)導入庫不起作用,因爲它不允許訪問Enum1的值。

import'ing Type1Lib.idl不太有效,因爲雖然枚舉值可用,但它們只能以非限定形式使用,因此儘管Type2Lib.idl現在編譯,但當將Type2Lib_i.h包含在dllmain.cpp中時,枚舉值不解決任何事情。另一個問題是導入「Type1Lib.idl」語句導致在Type2Lib_i.h中添加了「#include」Type1Lib.h「」行,文件Type1lib.h不存在,並且沒有理由爲什麼它應該應該存在。

這些問題都有解決方法,但它們並沒有真正地產生一個可行的解決方案。

在stdafx.h中導入帶有no_namespace的Type1Lib將解決未解決問題的非限定枚舉的問題,但是現在您已經打開了自己,因爲現在無法使用而無法解決的typename衝突的可能性命名空間。

在stdafx.h中導入Type1Lib之前,您可以使用#pragma include_alias將#include「Type1Lib.h」從不存在的頭文件重定向到一個可執行文件的頭文件。

#pragma include_alias("Type1Lib.h", "windows.h") 
#import <Type1Lib.dll> no_namespace 

現在一切都在建立。問題是你現在有幾個令人討厭和脆弱的解決方法,只是等着把所有東西都搞砸了,還有一個我沒有提到的明顯問題,那就是這個。如果你通過'importlib'引用你的COM依賴關係,那麼你可以得到這樣的結果,參考你的依賴關係,你的IDL文件可以使用被引用類型庫中的類型,但是你不會重新定義任何東西。但是,如果你使用'import'導入idl文件,那麼你基本上擁有的是一個'include'版本,可以在IDL文件中使用。與此相關的問題是使用'import'將導致在依賴IDL文件中創建依賴IDL文件中的類型的重複項,這是您絕對不想要的。

所以我完全喪失了。我想做的事情似乎很簡單,儘管嘗試了所有我能想到的事情,我什麼也看不到,只有錯誤。我迫切需要一些COM專家來把我放在一個有用的道路上。

+0

我的水晶球說你已經將兩個IDL文件添加到同一個項目中。用鼠標右鍵單擊TypeLib1.idl,屬性,MIDL,輸出。將「頭文件」設置更改爲TypeLib1.h。 IID和代理空白,生成=編號 –

+0

Hans!你只是我希望會變成的人:)我沒有將兩個IDL文件都添加到同一個項目中,Type1Lib.idl在項目Type1Lib中,而Type2Lib.idl在項目Type2Lib中。雖然我在Type2Lib項目中添加了對Type1Lib.idl的引用,並按照您的描述進行了配置。這樣做的效果是解決了缺少Type1Lib.h頭文件的問題,並且必須「#import no_namespace」。所以這很好,但問題仍然是這種方法重複Type1Lib中的類型。 – Neutrino

+0

如果我使用「importlib(」Type1Lib.dll「);」那麼發佈的Method1 arg1的類型是Type1Lib.Enum1,但是我不能在IDL文件中使用Enum1的值,如果我使用「import」Type1Lib.idl「;」那麼我可以使用IDL中的枚舉值,但現在發佈的Method1 arg1類型變爲Type2Lib.Enum1而不是Type1Lib.Enum1!這並不是所希望的,因爲依賴於Type1Lib和Type2Lib的客戶端現在具有暴露給它們的相同類型作爲兩個重複和不兼容的類型。 – Neutrino

回答

1

在嘗試Hans的建議並注意到它仍然導致Type1Lib中的Enum1定義作爲Type2Lib中的一個新類型的重複之後,我做了一些進一步的實驗並且找到了這種看似合理優雅地工作的方法。

Type1Lib的IDL與以前相同。 enum typedef上的guid看起來並不是必須的,所以我忽略了它。

//Type1Lib.idl 

import "oaidl.idl"; 
import "ocidl.idl"; 

[ 
    uuid(B777544C-77D9-4417-8302-4EAC8272DEDC), 
    version(1.0), 
] 
library Type1Lib 
{ 
    // Required system import. 
    importlib("stdole2.tlb"); 

    // A simple enum to be used by another COM server. 
    typedef enum Enum1 
    { 
     One, 
     Two, 
     Three, 
     Four, 
     Five 
    } Enum1; 
}; 

COM依賴的IDL有幾個變化。

//Type2Lib.idl 

// Required system imports. 
import "oaidl.idl"; 
import "ocidl.idl"; 

cpp_quote("using namespace Type1Lib;") // Added this. 
import "Type1Lib.idl";  // importing the IDL. 

[ 
    uuid(D40AC182-8744-42D1-B194-602AEDDC6E7C), 
    version(1.0), 
] 
library Type2Lib 
{ 
    // Required system import. 
    importlib("stdole2.tlb"); 

    // Import Type1Lib without redeclaring the types it contains. 
    importlib("Type1Lib.dll"); // and importlib as well. 

    typedef enum Enum2 
    { 
     A = 0x80000000 + One, // 'One' references an enum value from Type1Lib. 
     B, 
     C, 
     D, 
     E 
    } Enum2; 

    [ 
     object, 
     uuid(F5BA0CB0-B7C7-4483-A3D9-D4B9E39E6269), 
     dual, 
     nonextensible, 
     pointer_default(unique) 
    ] 
    interface IType2 : IDispatch 
    { 
     [id(1)] HRESULT Method1([out,retval] LONG* retVal); 
     [id(2)] HRESULT Method2([in] enum Enum1 arg1); 
    }; 

    [ 
     uuid(6179272F-4B34-4EF0-926B-296D3AA73DB7)  
    ] 
    dispinterface _IType2Events 
    { 
     properties: 
     methods: 
    }; 

    [ 
     uuid(75CE545A-D2DA-4EC9-80CF-37531516DFC1)  
    ] 
    coclass Type2 
    { 
     [default] interface IType2; 
     [default, source] dispinterface _IType2Events; 
    }; 
}; 

並且在使用庫的stdafx.h頭文件中還有一個更改。

//stdafx.h 

#pragma include_alias("Type1Lib.h", "obj\Debug\type1lib.tlh") 
#import <Type1Lib.dll> 

那麼這是如何工作的?

Type2Lib.idl現在包含「import」Type1Lib.idl「;」 「importlib(」Type1Lib.dll「);」聲明。雖然這種安排意味着Type2Lib.idl可以訪問枚舉類型定義(可以用作參數類型)和枚舉值本身,但它會重新創建我在OP中描述的兩個問題。

1)「導入」Type1Lib.idl「;」語句會導致在Type2Lib_i.h中添加「#include」Type1Lib.h「」行,並且Type1Lib.h不存在。

2)未限定的枚舉值在Type2Lib_i.h中引用,它們不解析爲任何內容。

問題1由stdafx.h頭文件中的「#pragma include_alias」解決。 Type2Lib將希望#import它正在使用的庫,以便在其實現中使用它,並且#import語句將生成Type1Lib.tlh和Type1Lib.tli,以便使用來自C++源的Type1Lib類型。 Type1Lib.tlh可以替代Type2Lib_i.h中的「#include」Type1Lib.h「」行,這就是#pragma include_alias的作用。

這留下了Type1Lib.tlh在名稱空間中聲明其類型的問題(並且我們希望保留該名稱空間,因爲它是一件非常好的事情),但Type2Lib_i.h引用Type1Lib中的類型不合格,因此通常不會編譯使用Type1Lib.tlh。 Type2Lib.idl中的「cpp_quote(」using namespace Type1Lib;「)」語句通過使midl在生成Type1Lib_i.h時將using語句注入Type2Lib_i.h來解決此問題。

這樣所有的作品。它不會在Type2Lib中重複Type1Lib的枚舉定義,並且它不太可怕。它確實讓我擔心,在使用消費IDL時枚舉值無法通過庫或類型名稱進行限定,但到目前爲止,它似乎比我能夠設計的任何其他方案都更好。