COM服务器注册
COM服务器必须在Windows注册表中正确注册以后才能正常工作。如果你看一下注册表中的HKEY_CLASSES_ROOT/CLSID键,就会发现大把大把子键,它们就是在这个计算机上注册的COM服务器。当某个COM服务器注册后(通常是用DllRegisterServer()进行注册),就会以标准的注册表格式在CLSID键下创建一个键,它名字为服务器的GUID。下面是一个这样的例子:
{067DF822-EAB6-11cf-B56E-00A0244D5087}
大括弧和连字符是必不可少的,字母大小写均可。 这个键的默认值是人可值别的组件对象类名,使用VC所带的OLE/COM对象浏览器可以察看到它们。 在GUID键的子键中还可以存储其它信息。需要创建什么子键依赖于COM服务器的类型以及COM服务器的使用方法。对于本文例子中这个简单的进程内服务器,我们值需要一个子键:InProcServer32。 InProcServer32键包含两个串:这两个串的缺省值是服务器DLL的全路径和线程模型值(ThreadingModel)。线程模型超出了本文所涉及的范围,我们先接受这个概念,这里我们指的是单线程服务器,用的模式为Apartment(即单线程公寓)。
创建COM对象——类工厂
回首看一看客户端的COM,它是如何以自己独立于语言的方式创建和销毁COM对象。客户端调用CoCreateInstance()创建新的COM对象。现在我们来看看它在服务器端是如何工作的。 你每次实现组件对象类的时候,都要写一个旁类负责创建第一个组件对象类的实例。这个旁类就叫这个组件对象类的类工厂(class factory),其唯一目的是创建COM对象。之所以要一个类工厂,是因为语言无关的缘故。COM本身并不创建对象,因为它不是独立于语言的也不是独立于实现的。 当某个客户端想要创建一个COM对象时,COM库就从COM服务器请求类工厂。然后类工厂创建COM对象并将它返回客户端。它们的通讯机制由函数DllGetClassObject()来提供。 术语 “类工厂”和“类对象”实际上是一回事。没有那个单词能精确描述类工厂的作用和义,但正是这个工厂创建了COM对象,而不是COM类所为。将“类工厂”理解成“对象工厂”可能会更有助于理解(实际上MFC就是这样理解的——它的类工厂实现就叫做COleObjectFactory)。但“类工厂”是正式术语,所以本文也这样用。 当COM库调用DllGetClassObject()时,它传递客户端请求的CLSID。服务器负责为所请求的CLSID创建者各类工厂并将它返回。类工厂本身就是一个组件对象类,并且实现IClassFactory接口。如果DllGetClassObject()调用成功,它返回一个IClassFactory指针给COM库,然后COM库用IClassFactory接口方法创建客户端所请求的COM对象实例。 一下是IClassFactory接口:
struct IClassFactory : public IUnknown { HRESULT CreateInstance( IUnknown* pUnkOuter, REFIID riid, void** ppvObject ); HRESULT LockServer( BOOL fLock ); };
其中,CreateInstance()是创建COM对象的方法。LockServer()在必要时让COM库增加或减少服务器的引用计数。
一个定制接口的例子 这个工程是一个能运行的DLL服务器例子,对象由类工厂创建,此DLL服务器在 CSimpleMsgBoxImpl组件对象类中实现了一个接口:ISimpleMsgBox。
接口定义
我们的新接口是ISimpleMsgBox。所有的接口多必须从IUnknown派生。这个接口只有一个方法:DoSimpleMsgBox()。注意它返回标准类型HRESULT。所有的方法都应该返回HRESULT类型,并且所有返回到调用者的其它数据都应该通过指针参数操作。 struct ISimpleMsgBox : public IUnknown { // IUnknown 方法 ULONG AddRef(); ULONG Release(); HRESULT QueryInterface( REFIID riid, void** ppv );
// ISimpleMsgBox方法 HRESULT DoSimpleMsgBox( HWND hwndParent, BSTR bsMessageText ); };
struct __declspec(uuid("{7D51904D-1645-4a8c-BDE0-0F4A44FC38C4}")) ISimpleMsgBox;
有__declspec的一行将一个GUID赋值给ISimpleMsgBox,并且以后可以用__uuidof操作符来获取GUID。这两个东西都是微软的C++的扩展。 DoSimpleMsgBox()的第二个参数是BSTR类型。意思是二进制串——即定长序列位的COM表示。BSTRs主要用于Visual Basic 和 Windows Scripting Host之类的脚本客户端。
接下来这个接口由CSimpleMsgBoxImpl C++类来实现。其定义如下: class CSimpleMsgBoxImpl : public ISimpleMsgBox { public: CSimpleMsgBoxImpl(); virtual ~CSimpleMsgBoxImpl();
// IUnknown 方法 ULONG AddRef(); ULONG Release(); HRESULT QueryInterface( REFIID riid, void** ppv );
// ISimpleMsgBox 方法 HRESULT DoSimpleMsgBox( HWND hwndParent, BSTR bsMessageText );
protected: ULONG m_uRefCount; };
class __declspec(uuid("{7D51904E-1645-4a8c-BDE0-0F4A44FC38C4}")) CSimpleMsgBoxImpl;
当某一客户端想要创建一个SimpleMsgBox COM对象时,它应该用下面这样的代码:
ISimpleMsgBox* pIMsgBox; HRESULT hr;
// 组件对象类的CLSID hr = CoCreateInstance ( __uuidof(CSimpleMsgBoxImpl), NULL, // 非聚合 CLSCTX_INPROC_SERVER, // 进程内服务器 __uuidof(ISimpleMsgBox), // 所请求接口的IID (void**) &pIMsgBox ); // 返回的接口指针的地址
类工厂实现
我们的类工厂SimpleMsgBox是在一个叫做CSimpleMsgBoxClassFactory的C++类中实现的: class CSimpleMsgBoxClassFactory : public IClassFactory { public: CSimpleMsgBoxClassFactory(); virtual ~CSimpleMsgBoxClassFactory();
// IUnknown方法 ULONG AddRef(); ULONG Release(); HRESULT QueryInterface( REFIID riid, void** ppv );
// IClassFactory方法 HRESULT CreateInstance( IUnknown* pUnkOuter, REFIID riid, void** ppv ); HRESULT LockServer( BOOL fLock );
protected: ULONG m_uRefCount; };
构造函数、析构函数和IUnknown方法都和前面例子中的一样,不同的只有IClassFactory的方法,LockServer(),看起来相当更简单:
HRESULT CSimpleMsgBoxClassFactory::LockServer ( BOOL fLock ) { fLock ? g_uDllLockCount++ : g_uDllLockCount--; return S_OK; }
CreateInstance()是重点。我们说过这个方法负责创建新的CSimpleMsgBoxImpl对象。让我们进一步探讨一下它的原型和参数: HRESULT CSimpleMsgBoxClassFactory::CreateInstance ( IUnknown* pUnkOuter, REFIID riid, void** ppv );
第一个参数pUnkOuter只用于聚合的新对象,指向“外部的”COM对象,也就是说,这个“外部”对象将包含此新对象。对象的聚合超出了本文的讨论范围,本文的例子对象也不支持聚合。 riid 和ppv 与在QueryInterface()中的用法一样——它们是客户端所请求的接口IID和存储接口指针的指针缓冲。 下面是CreateInstance()的实现。它从参数的有效性检查和参数的初始化开始。 HRESULT CSimpleMsgBoxClassFactory::CreateInstance ( IUnknown* pUnkOuter, REFIID riid, void** ppv ) { // 因为不支持聚合,所以这个参数pUnkOuter必须为NULL. if ( NULL != pUnkOuter ) return CLASS_E_NOAGGREGATION;
//检查指针ppv是不是void*类型 if ( IsBadWritePtr ( ppv, sizeof(void*) )) return E_POINTER;
*ppv = NULL;
检查完参数的有效性后,就可以创建一个新的对象了。 CSimpleMsgBoxImpl* pMsgbox;
// 创建一个新的COM对象 pMsgbox = new CSimpleMsgBoxImpl;
if ( NULL == pMsgbox ) return E_OUTOFMEMORY;  
说明:本教程来源互联网或网友上传或出版商,仅为学习研究或媒体推广,wanshiok.com不保证资料的完整性。
2/2 首页 上一页 1 2 |