2015-09-04 39 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时枚举值无法通过库或类型名称进行限定,但到目前为止,它似乎比我能够设计的任何其他方案都更好。