用标准C编写COM(一)(六)

2014-11-24 09:46:32 · 作者: · 浏览: 7
tory *this,
IUnknown *punkOuter, REFIID vTableGuid,void **ppv)
{
HRESULT hr;
struct IExample *thisobj;
// 通过清除调用者的句柄显示错误
*ppv = 0;
// 在IExample中我们不支持聚合
if (punkOuter)
hr = CLASS_E_NOAGGREGATION;
else
{
//创建我们的IExample对象并初始化。
if (!(thisobj = GlobalAlloc(GMEM_FIXED,
sizeof(structIExample))))
hr = E_OUTOFMEMORY;
else
{
// 存储IExample的虚表。我们把它声明为一个静态变量IExample_Vtbl
thisobj->lpVtbl =&IExample_Vtbl;
// 增加引用计数以便如果在调用QueryInterface()有错误时
// 我们可以在下面调用Release()并且它会销毁
thisobj->count = 1;
// 用我们上面分配的IExample指针填充调用者的句柄。
// 我们让IExample的QueryInterface来做它,
// 因为它也会检查调用者传入的GUID,如果一切正确它也会增加
// 引用计数(到2)。
hr = IExample_Vtbl.QueryInterface(thisobj,vTableGuid, ppv);
// 减小引用计数
// 注意:如果在QueryInterface()中发生了一个错误,
// 那么Release会减小计数到0 并替我们释放IExample。
// 当调用者寻找某种我们不支持的对象(也就是说它是一
// 个我们不认可的GUID)时可能发生错误。
IExample_Vtbl.Release(thisobj);
}
}
return(hr);
}
这样就实现了我们的IClassFactory对象了。
打包到DLL中
为了使其它程序更容易获得我们的IClassFactory(调用它的CreateInstance函数来获得IExample对象),我们要把上面的源代码打包成一个动态链接库(DLL)。本文不讨论怎样去创建一个DLL本身,所以如果你不熟悉它,你首先需要 阅读一下关于DLL的指南。
上面,我们已经写了IExmaple和IClassFactory对象的所有代码。我们所需要做的就是把它们粘贴到我们的DLL源代码中。
但是还有一些事情要做。微软规定我们必须添加一个叫DllGetClassObject的函数到我们的DLL中。微软已经定义了它的传递参数、它该做什么和应该返回什么。其它程序会调用我们的DllGetClassObject来获得我们的IClassFactory对象指针。(事实上,就像我们以后看到得,程序会调用一个命名为CoGetClassObject的OLE函数,在它内部会调用我们的DllGetClassObject。)所以,这就是一个程序如何获得我们得IClassFactory对象的方法-通过调用我们的DllGetClassObject。我们的GetClassObject函数必须完成它的工作。这是它的定义:
[cpp]
HRESULTPASCAL DllGetClassObject(REFCLSID objGuid,
REFIID factoryGuid, void**factoryHandle);
第一个传递参数是IExample对象的GUID(不是它的虚表GUID)。我们需要检查它来做确定调用者是否是明确调用我们DLL的DllGetClassObject。注意每个COM的DLL在它里面都有一个DllGetClassObject函数。所以我们需要用GUID来区分我们的DllGetClassObject与其他COMDLL的DllGetClassObject。
第二个参数是IClassFactory的GUID。
第三个参数是个句柄,程序期望我们通过它返回我们的IClassFactory指针(如果这个程序确实传入入一个IExampleGUID,不是一个其
HRESULTPASCAL DllGetClassObject(REFCLSID objGuid,
REFIIDfactoryGuid, void **factoryHandle)
{
HRESULT hr;
// 检查调用者传入的IExample的GUID。看它是否是我们的DLL实现的COM对象。
if (IsEqualCLSID(objGuid,&CLSID_IExample))
{
// 用我们的IClassFactory对象指针填充调用者的句柄。
// 我们让我们的IClassFactory的QueryInterface来做,
// 因为它也检查IClassFactory的GUID和做其他事宜
hr =classQueryInterface(&MyIClassFactoryObj,
factoryGuid,factoryHandle);
}
else
{
// 我们不能解析这个GUID。显然它不在我们的DLL中。
// 通过清除它的句柄和返回CLASS_E_CLASSNOTAVAILABLE来让调用者知道
*factoryHandle = 0;
hr = CLASS_E_CLASSNOTAVAILABLE;
}
return(hr);
}
我们几乎做完了我们需要创建DLL的工作。还有一个事要做。其实程序不会真正加载我们的DLL。而是当程序调用CoGetDllClassObject时,操作系统代替它来完成(也就是说,CoGetClassObject定位我们的DLL文件,对它调用LoadLibrary,用GetProcAddress来得到我们上面的DllGetClassObject,代替这个程序调用它)。不幸的是,微软没有设计出一些方法给程序,当程序使用完我们的DLL来告诉操作系统、让操作系统卸载(FreeLibrary)我们的DLL。所以我们必须帮助操作系统让它知道我们的DLL什么时候可以安全的卸载。所以我们必须提供一个叫DllCanUnloadNow的函数,当可以安全删除的时候返回S_OK,否则返回S_FALSE。
那么我们怎样知道它什么时候安全呢?