写在最前
接触com已经有一段时间了,奈何愚钝。只识得一点皮毛。对于新手不要说了解其中的原理,就连调用使用Com组件都困难。
正文开始
本文将较为详细地介绍C++ Com接口从获取接口声明,初始化套间,创建对象实例介绍。
一、获得接口声明
在C++中任意函数的使用我们都必须得到头文件一样的东西,而com的一个很大的亮点就是不需要额外提供头文件,首先我们介绍几种获取方式。
1、com提供者编译生成com组件的dll/exe时生成的_i.h 文件:
com组件编译会生成_i.c、_i.h文件,该文件由IDL接口声明文件生成而来。使用com包含该_i.h文件即可,头文件中包含com组件的所有声明。
#include"../ATL Dll/ATLDll_i.h"
2、使用.tlb 文件生成:
编译com组件生成EXE/DLL文件时,除了会生成_i.h和_i.c以外还会额外生成一个.tlb文件。而该文件可以使用#import 方式作为模块导入,之后直接编译,ms会为我们生成一个带有声明的头文件,使用重复1中过程即可。
首先将tlb文件复制到我们需要使用的项目下,使用import包含,代码如下:
#include<atlcomcli.h>
//生成的tlh文件会自己加上namespace因此使用no_namesapce
//named_guids 指示编译器以旧样式定义和初始化 GUID 变量,格式为 LIBID_MyLib 、、 CLSID_MyCoClass IID_MyInterface 和 DIID_MyDispInterface
#import "ATLDll.tlb" no_namespace
int _tmain(int argc, _TCHAR* argv[])
{
return 0;
}
之后直接编译生成一次项目,会在中间目录得到.tli和.tlh文件,同样包含tlh文件即可。
3、使用com组件的二进制dll/exe生成:
以上两种情况都需要由com组件的提供者提供除开二进制执行模块以外的文件,这当然是不够好的,因此com提供直接从二进制生成接口相关声明的特性。
同2中方法,直接复制模块到你的项目目录下,并且使用import导入,之后直接生成一次。
#include <iostream>
#import "ATL Dll.dll" no_namespace
int main()
{
return 0;
}
生成后在中间目录得到组件的声明文件,同样包含tlh文件即可。
二、创建com组件实例
在一的介绍中我们得到了对com组件声明的头文件,此操作保证我们使用中不会报错。但不仅仅于此,我们使用com还需要保证该组件在系统注册,dll/exe/服务有不同的注册方法。
注册保证我们的程序可以在系统的众多组件中找到我们指定的组件,该行为是在代码中使用com组价的前提。
在注册com组件后,我们程序以uuid为唯一标识创建com组件实例,而uuid是com开发者编写时确定,uuid保证了其自身的唯一性,一定不会与别的组件撞车。
创建时获取该ID的办法有两种:
①直接使用"一"中生成的头文件声明中具有,直接使用,但需要使用__uuidof()关键字,其中uuid的名字来源于atl对象名,在头中声明如:
使用也十分简单:
#import "ATL Dll.dll" no_namespace
#include"./x64/Debug/atl dll.tlh"
#include<atlcomcli.h>
#include<Windows.h>
int main()
{
CoInitialize(NULL);
//创建智能指针
CComPtr<IDllATLSimpleObject1> p;
//创建实例
p.CoCreateInstance(__uuidof(DllATLSimpleObject1));
p->xxx...//调用组件接口提供的方法
}
②使用 ProgID 查找出uuid,该名字为组件接口的别名,可以用于查找,uuid使用如下:
#import "ATL Dll.dll" no_namespace
#include"./x64/Debug/atl dll.tlh"
#include<atlcomcli.h>
#include<Windows.h>
int main()
{
CoInitialize(NULL);
//创建智能指针
CComPtr<IDllATLSimpleObject1> p;
//定义存储uuid的变量
CLSID clsid;
//获取类的guid
CLSIDFromProgID(L"DllATLSimpleObject1.ProgID", &clsid);
//创建实例
p.CoCreateInstance(clsid);
p->xxx...//调用组件接口提供的方法
}
该名字由com的开发者确定,且在上面的接口声明中不会出现,但我们可以在组件注册到系统后通过注册表查看其名字。