首页 > 代码库 > ATL 调用COM对象

ATL 调用COM对象

创建完COM对象后,接下来是如何调用COM组件了。这里是在c++中的调用COM对象的方法。

创建COM对象一般有三种方法,正常创建一个对象,使用CoCreateInstance函数。若在远程系统中创建一个对象,使用CoCreateInstanceEX函数。而创建多个同一CLSID的对象时,使用CoGetClassObject函数。

1.先简单的使用CoCreateInstance函数创建一个COM对象。

//要加载生成的文件和这个c文件。
#include "ATLProject1_i.h"
#include "ATLProject1_i.c"

int _tmain(int argc, _TCHAR* argv[])
{
	//声明的是接口的指针
	ITryCOM *it = NULL;
	//声明一个HRESULT变量
	HRESULT hr;
	//初始化COM,并告诉Windows以单线程的方式创建COM对象
	hr = CoInitialize(0);
	//使用SUCCEDED宏判断是否初始化成功。
	if(SUCCEEDED(hr)){
		//加载COM对象
		hr = CoCreateInstance(CLSID_TryCOM,NULL,CLSCTX_INPROC_SERVER,IID_ITryCOM,(void**)&it);
		//检测是否加载成功
		if(SUCCEEDED(hr)){
			long ReturnValue ;
			printf("Find DLL\n");
			int a= 1;
			int b = 2;
			it->Add(a,b,&ReturnValue);
			printf("%d",ReturnValue);
			//对于com对象,使用后都要使用release进行释放
			it->Release();
		}
		//关闭当前线程的COM库,卸载所有dll,并释放资源。
		CoUninitialize();
	}
	system("pause");
	return 0;
}
这里的两个重要函数CoCreateInstance 和 CoUninitialize。

对于CoUninitialize()函数,CoUninitialize关闭当前线程的COM库,卸载线程加载的所有dll,释放任何其他的资源,关闭在线程上维护所有的RPC连接。

在一个线程调用了CoInitialize进行COM初始化后,在最后一次调用COM库后,程序完全关闭前调用CoInitialize函数来释放。


然后是CoCreateInstance 函数,其函数原型为:

STDAPI CoCreateInstance(
REFCLSID rclsid, //创建的Com对象的类标识符(CLSID)
LPUNKNOWN pUnkOuter, //指向接口IUnknown的指针,表明是否聚合
DWORD dwClsContext, //运行可执行代码的上下文
REFIID riid, //创建的Com对象的接口标识符
LPVOID * ppv //用来接收指向Com对象接口地址的指针变量
);
rclsid
[in] 用来唯一标识一个对象的CLSID(128位),需要用它来创建指定对象。这个值一般保存在COM生成的 xxx_I.c文件中,可以直接include这个文件,也可以加这个文间中的CLSID直接作为全局常量使用。
pUnkOuter
[in] 如果为NULL, 表明此对象不是聚合式对象一部分。如果不是NULL, 则指针指向一个聚合式对象的IUnknown接口。
dwClsContext
[in] 组件类别. 可使用CLSCTX枚举器中预定义的值,可选取值如下:

CLSCTX_INPROC_SERVER 创建在同一进程中运行的组件。为能够同一进程中运行,组件必须是在DLL中实现的。
CLSCTX_INPROC_HANDLER 创建进程中处理器。一个进程中处理器实际上是一个只实现了某个组建一部分的进程中组件。该组件的其他部分将由本地或远程服务器上某个进程外组件实现。
CLSCTX_LOCAL_SERVER 创建一个在同一机器上的另外一个进程中运行的组件。本地服务器是由exe实现的。
CLSCTX_REMOTE_SERVER 创建一个在远程机器上运行的组件。此标志需要分布式COM正常工作。

riid
[in] 引用接口标识符,用来与对象通信。也存在 xxx_i.c中。


ppv
[out] 用来接收指向接口地址的指针变量。如果函数调用成功,*ppv包括请求的接口指针。返回值:
S_OK:指定的Com对象实例被成功创建。
REGDB_E_CLASSNOTREG:指定的类没有在注册表中注册. 也可能是指定的dwClsContext没有注册或注册表中的服务器类型损坏
CLASS_E_NOAGGREGATION:这个类不能创建为聚合型。
E_NOINTERFACE:指定的类没有实现请求的接口, 或者是IUnknown接口没有暴露请求的接口.

但这个函数实际上是调用了CoGetClassObject函数来实现的:

CoGetClassObject(rclsid,NULL ,dwClsContext, IID_IClassFactory, &pCF);
hresult = pCF->CreateInstance(pUnkOuter, riid, ppv);
pCF->Release();


2.使用CoGetClassObject来创建COM对象。

CoGetClassObject实际上并没有直接创建COM组件,而是创建了一个厂类,然后再有工厂对象类创建COM对象。工厂类的接口是IClassFactory,CoCreatInstance函数的最后一个参数来指向一个工厂对象,而CoGetClassObject的最后一个参数来指向一个最终的COM对象。

CoGetClassObject函数与CoCreateInstance是非常相似的,只有一个参数不同,CoCreatInstance将接收一个Iunknown指针,而CoGetClassObject则将接收一个COSERVERINFO指针。原型如下:

STDAPI CoGetClassObject(REFCLSID rclsid,//为COM对象类的ID,同
DWORD dwClsContext,//可执行代码上下文,同
COSERVERINFO * pServerInfo,//服务器消息,若不是远程创建,一般设为NULL,异
REFIID riid,//这里为工厂类的接口id,一般设置为 IID_IClassFactory
LPVOID * ppv);//这个为记录的工厂对象指针
拥有的工厂后,在调用工厂的 CreateInstance函数来创建COM对象。原型为:

HRESULT STDMETHODCALLTYPE CreateInstance( 
				IUnknown *pUnkOuter,    //指向接口IUnknown的指针,表明是否聚合,一般设置为NULL非聚合
				REFIID riid,			//为COM对线的接口ID
				 void **ppvObject);		//返回的COM对象指针

简单例子如下:

//要加载生成的文件和这个c文件。
#include "ATLProject1_i.h"
#include "ATLProject1_i.c"

int _tmain(int argc, _TCHAR* argv[])
{
	//声明的是接口的指针
	ITryCOM *it = NULL;
	//一个工厂对象的指针
	IClassFactory *ifp = NULL ;
	//声明一个HRESULT变量
	HRESULT hr;
	//初始化COM,并告诉Windows以单线程的方式创建COM对象
	hr = CoInitialize(0);
	//使用SUCCEDED宏判断是否初始化成功。
	if(SUCCEEDED(hr)){
		//加载厂类
		hr = CoGetClassObject(CLSID_TryCOM,CLSCTX_INPROC_SERVER	,NULL,IID_IClassFactory,(void**)&ifp);
		//检测是否加载成功
		if(SUCCEEDED(hr)){
			hr = ifp->CreateInstance(NULL,IID_ITryCOM,(void**)&it);//使用工厂创建COM对象
			ifp->Release();//释放
			if(SUCCEEDED(hr)){
				long ReturnValue ;
				printf("Find DLL\n");
				int a= 1;
				int b = 2;
				it->Add(a,b,&ReturnValue);
				printf("%d",ReturnValue);
				//对于com对象,使用后都要使用release进行释放
				it->Release();//释放
			}
		}
		//关闭当前线程的COM库,卸载所有dll,并释放资源。
		CoUninitialize();
	}
	system("pause");
	return 0;
}

3.忽略远程创建实例函数EX,最后在来考虑一下不使用CoCreateInstance or CoGetClassObject,直接从dll中得到DllGetClassObject,接着生成类对象及类实例(本方法适合于你想用某个组件,却不想在注册表中注册该组件)。之前的两个函数能调用,是因为其在系统中通过注册表对其唯一的ID进行了DLL的注册,所以调用这两个函数,使其能从系统中获得对应的DLL。最后是一个不注册dll的方法。实现如下:

//要加载生成的文件和这个c文件。
#include "ATLProject1_i.h"
#include "ATLProject1_i.c"

int _tmain(int argc, _TCHAR* argv[])
{
	//定义一个这样的函数指针
	typedef HRESULT (__stdcall * pfnGCO) (REFCLSID, REFIID, void**);
	//声明一个函数指针
	pfnGCO fnGCO = NULL;
	//加载dll
	HINSTANCE hdllInst = LoadLibrary("D:/360data/重要数据/我的文档/Visual Studio 2012/Projects/TryDll/TryDll/ATLProject1.dll");
	//在dll中寻找DllGetClassObject函数,并将其赋值给fnGCO指针,是fnGCO指针可以进行创建工厂类的操作
	fnGCO = (pfnGCO)GetProcAddress(hdllInst, "DllGetClassObject");
	if (fnGCO != 0)
	{
		//工厂对象
		IClassFactory* pcf = NULL;
		//释放fnGCO函数,即DllGetClassObject函数来获得一个工厂类对象,然后就是正常使用工厂类对象创建COM对象的操作了
		HRESULT hr= (fnGCO) (CLSID_TryCOM, IID_IClassFactory, (void**)&pcf);
		if (SUCCEEDED(hr) && (pcf != NULL))
		{
			ITryCOM* iTry = NULL;
			hr = pcf->CreateInstance(NULL, IID_ITryCOM, (void**)&iTry);
			if (SUCCEEDED(hr) && (iTry != NULL))
			{
				long ReturnValue ;
				printf("Find DLL\n");
				int a= 1;
				int b = 2;
				iTry->Add(a,b,&ReturnValue);
				printf("%d",ReturnValue);
				iTry->Release();
			}
			pcf->Release();
		}
	}
	//释放加载的dll
	FreeLibrary(hdllInst);
	system("pause");
	return 0;
}




ATL 调用COM对象