使用ATL建立支持IClassFactory2的COM组件
关键字:COM、IClassFactory2、对象创建权限、License、License Key
本文详细描述了如何使用ATL自身的功能创建具有创建权限功能的COM对象。最后还讲述了如何创建具有固定和临时权限的COM对象,以及如何获取注册码。
1 概述
IClassFactory2接口的作用是为给COM对象增加限制控制的功能。通过这个接口,只能在有合法使用权的电脑上才可以创建COM对象的实例。
COM对象的权限有两种,一种是固定权限,在创建COM对象之前就存在电脑中,创建COM对象时不需要提供附加的信息。另一种是临时的权限,在创建对象的时候必须提供表明具有权限的注册码,临时权限不能保留在电脑中,所以,每次创建实例的时候都需要提供注册码。每个COM对象可以选择使用固定权限方式、临时权限方式或两者都使用。
COM对象可以提供取得注册码的功能。通过这个功能,可以在具有固定权限的电脑上获取的注册码。
在COM对象被创建的时候,应该检查注册码或者固定权限的信息,如果COM对象判定用户没有使用权限,可以返回CLASS_E_NOTLICENSED。
2 ATL对IClassFactory2的支持
ATL完全支持IClassFactory2。通过宏DECLARE_CLASSFACTORY2声明COM对象支持IClassFactory2接口。DECLARE_CLASSFACTORY2宏需要一个License类实现具体的许可权限验证算法和注册码验证算法。License类可以使用任何名字,其中必须定义如下的几个成员函数。License类的形式如下:
class CLicenseCls
{
protected:
static BOOL VerifyLicenseKey(BSTR bstr);
static BOOL GetLicenseKey(DWORD dwReserved, BSTR* pBstr);
static BOOL IsLicenseValid();
};
VerifyLicenseKey函数用于验证注册码的合法性。GetLicenseKey用于获取注册码。IsLicenseValid函数用于验证是否存在固定的使用权限。
用户可以根据需要,提供这三个函数的实现。如下表:
所需功能
VerifyLicenseKey
GetLicenseKey
IsLicenseValid
不检查权限
FALSE
FALSE
TRUE
固定权限
FALSE
FALSE
检查固定权限
临时权限
检测注册码
FALSE
FALSE
固定权限和
临时权限
检测注册码
FALSE
检查固定权限
固定权限和
临时权限和
获取注册码
检测注册码
返回注册码
检查固定权限
3 实现方式
3.1 License类
实现License类的成员函数:
class CMyLicense
{
protected:
static BOOL VerifyLicenseKey(BSTR bstrLicenseKey)
{
if (wcscmp(bstrLicenseKey, OLESTR("Template License Key")) == 0)
{
return TRUE;
}
return FALSE;
}
static BOOL GetLicenseKey(DWORD dwReserved, BSTR* pBstrLicenseKey)
{
if (pBstrLicenseKey)
{
* pBstrLicenseKey = SysAllocString(OLESTR("Template License Key "));
}
return TRUE;
}
static BOOL IsLicenseValid() { return TRUE; }
};
以上例子中,IsLicenseValid函数永远返回真。在实际使用中,要验证电脑中是否存在固定权限。如果存在固定权限则返回真,否则返回假。这个例子中使用的是固定注册码,也可以根据需要使用伪随机注册码。
3.2 实现IClassFactory2接口
在ATL的COM对象类中通过DECLARE_CLASSFACTORY2宏声明在类对象中使用IClassFactory2接口。DECLARE_CLASSFACTORY2宏的定义如下:
DECLARE_CLASSFACTORY2(lic)
其中lic是License类的名称,本例中为CMyLicense。
4 创建需要License的COM对象
4.1 具有固定权限的COM对象
创建具有固定权限的COM对象的方法和创建普通COM对象是相同的。不需要提供任何附加的信息。
使用CoCreateInstance可以方便的创见固定权限的COM对象。CoCreateInstance的定义如下:
STDAPI CoCreateInstance(
REFCLSID rclsid,
LPUNKNOWN pUnkOuter,
DWORD dwClsContext,
REFIID riid,
LPVOID * ppv
);
rclsid是要创建对象的CLSID。
pUnkOuter用于聚合的情况,不使用聚合的时候,使用NULL。
dwClsContext指定COM对象的运行环境,一般情况使用CLSCTX_ALL表示可以在任何环境运行。
riid是需要返回的接口的IID。
ppv是返回的接口指针的地址。
具体代码如下:
Iyyy * pObj;
HRESULT hr;
hr = CoCreateInstance(CLSID_xxx, NULL, CLSCTX_ALL, IID_yyy, &pObj);
if (FAILED(hr))
{
...
}
4.2 使用临时权限
使用临时权限创建COM对象,需要一个注册码。注册码可以从COM对象本身获取,也可以通过其它方式取得(如购买等)。使用临时权限创建COM对象需要两个步骤:取得COM类对象和创建COM对象。
4.2.1 取得COM类对象
通过CoGetClassObject函数,可以获取类对象。CoGetClassObject的定义如下:
STDAPI CoGetClassObject(
REFCLSID rclsid,
DWORD dwClsContext,
COSERVERINFO * pServerInfo,
REFIID riid,
LPVOID * ppv
);
rclsid是要创建COM对象的CLSID。
dwClsContext是COM对象的运行方式,一般情况使用CLSCTX_ALL表示可以在任何环境运行。
pServerInfo用于在另一台电脑上创建COM对象,在本地创建时用NULL。
riid是所需要的接口的IID,这里使用IID_IClassFactory2。
ppv是返回接口指针的地址。
可以看出,前两个参数和在CoCreateInstance中的定义是相同的。
具体代码:
IClassFactory2 * pCF2;
HRESULT hr;
hr = CoGetClassObject(CLSID_xxx, CLSCTX_ALL, NULL,
IID_IClassFactory2, (void **)&pCF2);
if (FAILED(hr))
{
...
}
...
4.2.2 创建对象
通过IClassFactory2的CreateInstanceLic方法,可以使用临时权限创建对象。在取得了COM类对象之后,调用CreateInstanceLic方法,并且把注册码作为参数,就可以得到新建的COM对象的接口指针。
CreateInstanceLic的定义如下:
HRESULT CreateInstanceLic(
IUnknown* pUnkOuter,
IUnknown* pUnkReserved,
REFIID riid,
BSTR bstrKey,
void** ppvObject
);
pUnkOuter是聚合的外部对象指针,在不使用聚合的时候,用NULL。
pUnkReserved是保留的参数,用NULL。
riid是所需要返回的接口IID。
bstrKey是注册码。
ppvObject是保存返回的接口指针的地址。
具体代码:
Ixxx * pObj;
hr = pCF2->CreateInstanceLic(NULL, NULL, IID_Ixxx,
bstrLicenseKey, (void**) &pObj);
if (FAILED(hr))
{
...
}
创建之后就可以像使用普通COM对象一样使用新建的COM对象。
4.3 获取注册码
首先要取得类对象的指针,详见第4.2.1节。
在取得了IClassFactory2的指针之后,通过RequestLicKey方法可以取得注册码。
RequestLicKey的定义如下:
HRESULT RequestLicKey(
DWORD dwReserved,
BSTR* pbstrKey
);
dwReserved是保留参数,用0。
pbstrKey是保存返回的注册码的地址。调用方负责释放返回值的地址。
具体代码:
BSTR bstrLicKey;
hr = pCF2->RequestLicKey(0, &bstrLicKey);
if (FAILED(hr))
{
...
}
...
SysFreeString(bstrLicKey);