COM主题五--实现IClassFactory接口

Prerequisite:C++ 编程者,熟悉windows编程,熟悉Win32 Dll,了解windows注册表。

作者:收割者

前一篇文章讲解了如何实现自定义的COM接口,本篇文章将讲解实现IClassFactory接口,正如它的名字所暗示的意思一样---类工厂,它的作用就像一个工程一样,来创建用户所要创建的对象并且返回请求的接口指针。

为什么需要实现IClassFactory接口呢,我们从COM接口的获取过程说起,创建COM对象并获取接口的标准方式是通过使用COM提供的一个函数CoCreateInstance,第一个参数是实现COM接口的组件类(coclass)的CLSID(本质是GUID),第二个参数一般为空,如果你创建的COM对象是聚集的(有的地方翻译是聚合的),就需要设置这个参数。第三个参数指定对象运行的上下文,前面我们一直说的,就是In-process Sever,也就是一个DLL,因此,我们在这里使用的是CLSCTX_INPROC_SERVER ,这个参数是一个枚举变量,更多设置,参考msdn。第三个参数是你需要的接口的ID(本质也是GUID),最后一个参数是一个双指针,如果对象支持这个接口,CoCreateInstance就会创建对象并返回请求的接口。在CoCreateInstance内部是怎么回事呢,其实有两个过程,首先是使用CoGetClassObject创建在COM DLL中实现IClassFactory接口的对象,并获取它的IClassFactory接口,然后调用这个接口的CreateInstance方法创建你传递的你要创建的对象的CLSID,也就是你调用CoCreateInstance时传递的第一个参数,以及要获取接口的id和一个双指针参数,由IClassFactory接口来帮助我们创建我们需要的对象。因此我们要实现IClassFactory接口。

首先我们添加一个头文件和源文件,命名为FactoryClass。接着上一篇文章的项目,继续,在解决方案视图中,在ImplCom项目上点击鼠标右键,选择“添加”菜单项目,选择“新建项”,在弹出来的对话框中,选择头文件,名称填“FactoryClass”,点击添加,然后用同样的方法,添加源文件,名称也填写“FactoryClass”,这样就添加了两个文件FactroyClass.h和FactoryClass.cpp,如下图:

COM主题五--实现IClassFactory接口_第1张图片

COM主题五--实现IClassFactory接口_第2张图片

在头文件中,我们写入以下代码:

#pragma once
#include      // For IClassFactory
#include 
#include "Shlwapi.h"
#pragma comment(lib,"Shlwapi.lib")
class CFactoryClass:public IClassFactory
{
public:
	CFactoryClass();
protected:
	~CFactoryClass();
	////////////引用计数
	LONG m_cRef;
public:
	/////////////////////IUnknown接口的方法
	STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
    STDMETHODIMP_(ULONG) AddRef();
    STDMETHODIMP_(ULONG) Release();
	////////////////////IClassFactory接口方法
	STDMETHODIMP CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppv);
    STDMETHODIMP LockServer(BOOL fLock);

};
关于如何实现一个COM接口的方法,前面已经说过了,这里就不再说明了,下面是源文件的代码:

#pragma once
#include "FactoryClass.h"
#include "Reaper.h"
extern long   g_cDllRef;
CFactoryClass::CFactoryClass():m_cRef(1)
{
	////////////在这里初始化成员
	g_cDllRef++;
}
CFactoryClass::~CFactoryClass()
{
	///////////
	g_cDllRef--;
}
///////////////IUnknown接口方法
HRESULT CFactoryClass::QueryInterface(REFIID riid, void **ppv)
{
	static QITAB rgqit[] = 
    {   
        QITABENT(CFactoryClass,IClassFactory),
        { 0 }
    };
    return QISearch(this, rgqit, riid,ppv);
}
ULONG CFactoryClass::AddRef()
{
	return  InterlockedIncrement(&m_cRef);
}
ULONG CFactoryClass::Release()
{
	 ULONG uCount = InterlockedDecrement(&m_cRef);
    if (uCount == 0)
    {
        delete this;
    }
    return uCount;
}
HRESULT CFactoryClass::LockServer(BOOL fLock)
{
	if(fLock)
	{
		g_cDllRef++;
	}
	else
	{
		g_cDllRef--;
	}
	return S_OK;
}
HRESULT CFactoryClass::CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppv)
{
	 HRESULT hr = CLASS_E_NOAGGREGATION;
	if(pUnkOuter==NULL)
	{
		CReaper *pObject=new CReaper;
		if(pObject)
		{
		  hr=pObject->QueryInterface(riid,ppv);
		  pObject->Release();
		}
	}
	  return hr;
}
关于g_cDllRef这里不做说明,在写注册COM DLL的时候再说名。由于在COM接口中,所有的接口都必须直接或是间接的继承IUnknown接口,IClassFactory接口也是如此,因此在实现IClassFactory接口的方法的同时,也必须实现IUnknown的方法。IUnknown接口的方法的实现前面也说过了,格式固定,内容大同小异,只有QueryInterface方法会根据你的接口不同而有所改变。下面说说IClassFactory接口的方法,它有两个方法,一个是CreateInstance,一个是LockSever。LockerSever这里不做详细说明,它的作用主要是增加DLL的引用计数,而DLL的引用计数决定了这个DLL是否应该从内存中卸载,也就是 g_cDllRef,本来说这个不做说明,结果也说明了,它的写法也相对固定,就是上面这个写法。主要说明一下CreateInstance方法,前面已经说过当CoCreateInstance使用CoGetClassObject获取了实现了IClassFactory接口的对象指针之后,就会调用CreateInstance方法,我们看在这个方法的实现中,首先检查pUnkOuter指针,这个指针主要用于聚集对象(或是聚合对象),我们现在实现的不是,因此,应该传递一个空的指针,如果不是空,我们就返回错误。接着我们创建一个我们实现我们自己实现的接口的对象,前面我用组件类CReaper实现了接口IShouGezhe接口,这个接口是自定义的。创建之后,调用它的QueryInterface方法,如果它支持用户请求的接口,就会返回S_OK,并且返回接口指针,如果不支持,就会返回错误的代码。在这里,我们也可以这样写,直接先判断传递进来的iid(接口id)和类id(CLSID)这里为了简便,我并没有这么做。

从实现过程来看,使用IClassFactory还可以实现多个对象的创建,根据判断传递进来的CLSID,来判断创建那个对象。在下一篇文章中,将给出文章中所使用的代码的完整项目,便于COM的学习。整个系列文章,为了能够清晰的说明Win32 COM的结构,没有实现任何其他功能,这样有助于初学者的学习,理解COM,这个过程都是COM编写的最小结构。

本篇文章如果出现在除了CSDN和磐实(Panshy)编程网以外的网站,均为非法盗用。
推荐阅读地址:http://www.panshy.com/articles/dev-COM-IClassFactory-2490.html

你可能感兴趣的:(COM主题五--实现IClassFactory接口)