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文件(用于接口共享),但这是它应该包含的唯一的东西吗?
这里有两个选项 - import
或importlib
。
不幸的是,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
。
我会尽力的。应该将.tlb复制到Processor项目的文件夹中?还是只能通过名字引用? –
好了,idl被编译了,但现在它在Processor_i.h中抱怨IFace '/ * [in] *// *外部定义不存在*/IFace * face,' –
啊,是的。这是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和各种文物,如果有的话,编译阶段),存在于访问的目录。
'导入“就是导致导入文件中的接口和coclass定义包含在已编译的'.tlb'中。这引入了各种注册和代理问题。 –
我创建了一组样本文件,如果你想事实检查我这一个。只需下载这些文件并从Visual Studio命令提示符运行'build.cmd'即可。然后使用'oleview.exe'查看'.tlb'文件。你会看到'IFoo'存在于'BarLib.tlb'中。这意味着'BarLib.tlb'将成为官方类型库,如果它曾经使用'RegisterTypeLib'注册的话。 https://gist.github.com/mgunter6/8d30a23bd64a576118253fb157dcabaf –
@MichaelGunter - 两个选项都是有效的,它真的取决于你想如何定义你的IDL。您不一定需要将接口放入typelib(在typelib属性下)。您可以在C#中使用无界面的tlb接口(虽然这是更初始的工作)。因此,我最初的问题是将所有内容放在同一个文件中。如果idl文件彼此之间有深入的了解,那么只定义一个tlb并且每个项目引用它(无论部署约束是什么)可能是合理的。否则,他们可以使用IID和IUnknown指针,这也是SDK中非常常见的模式。 –
难道你不能把所有的C++放在一个.idl文件中吗(带有前向声明)? –
@SimonMourier没有不幸的。有不同的工作方式。一个是exemodule,它为每个主com对象的使用生成单独的进程。和其他是DLL。该架构相当繁琐,我试图简化询问,为了简洁 –
这是一个基本的细节。其中一个接口指针必须始终被编组,因为它的服务器在进程外,你不能编组一个void *。不要这样做。 –