2 COM对象和使用从另一个接口

问题描述:

我有以下设置: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 *。不要这样做。 –

这里有两个选项 - 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中的一个错误。我会更新我的答案,如何解决这个问题。 –

您可以使用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中非常常见的模式。 –