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

2014-11-24 09:46:32 · 作者: · 浏览: 3
GUID都会返回一个错误值。
传给QueryInterface的第二个参数是我们要检查的GUID。如果传入的GUID与我们的IExample的虚表GUID匹配,我们会返回给传给我们的同一个对象指针给第三个参 数(一个句柄)。如果不匹配,我们把这个句柄置零。另外,如果这个GUID匹配QueryInterface返回一个NOERROR(被#define为0)的LONG值。如果不匹配返回非零错误值(E_NOINTERFACE)。那么,让我们来看一下IExample的QueryInterface:
[cpp]
HRESULTSTDMETHODCALLTYPE QueryInterface(IExample *this,
REFIID vTableGuid,void **ppv)
{
// 检查GUID是否与IExample的虚表的GUID相匹配。
// 记得我们给出了一个C变量的IID_IExample对应与我们的虚表GUID。
// 我们可以用一个OLE的IsEqualIID的函数来比较

if(!IsEqualIID(riid, &IID_IExample))
{
//我们不认可传给我们的GUID,通过清除调用这的句柄来让它知道,
// 返回E_NOINTERFACE
*ppv = 0;
return(E_NOINTERFACE);
}
// 它是匹配的!
// 首先我们用同一个它传给我们的对象指针来填充它的句柄。
// 这是我们创建/初始化的我们的IExample,它将从我们这里获的对象指针
*ppv = this;
// 现在调用我们的AddRef函数,把this指针传给IExample
this->lpVtbl->AddRef(this);
// 让他知道他确实拥有了一个IExample
return(NOERROR);
}
现在让我们来讨论一下AddRef和Release函数。你会注意到如果我们真的拥有了一个IExample,我们会在QueryInterface中调用AddRef.. .
记住我们替其它程序分配IExample的空间。它只是简单获得它的使用权。当其它程序不用它时我们有责任释放它。我们怎样知道这些呢?
我们会使用一个名为“引用计数”的东东。如果你回头看看我们的IExample的定义,你会发现我放了一个DWORD成员(count)在它里面。我们将使用这个成员。当我们创建了一个IExample时,我们把它初始化为0。然 后,当AddRef每被调用一次我们会把这个成员增加1,当Release每被调用一次我们会把这个成员减1。 所以,当把我们的IExample传递给QueryInterface时,我们会调用AddRef来增加它的count成员。当其它程序使用完后,这个程序会把我们的IExample传递 给我们的Release函数,在那我们会对这个成员进行减操作。当它减为0时我们会释放IExample。
COM的另一个重要规则。如果你获得了一个其他人创建的COM对象,在你使用完后你必须调用Release函数。我们当然也认为其他程序在使用完我们的IExample对象后后调用我们的Release函数。
下面是我们的AddRef和Release函数:
[cpp]
ULONGSTDMETHODCALLTYPE AddRef(IExample *this)
{
// 增加引用计数(count成员)
++this->count;
// 我们应该返回这个更新后的count。
return(this->count);
}
ULONGSTDMETHODCALLTYPE Release(IExample *this)
{
// 减少引用计数
--this->count;
// 如果它现在为0,我们要释放IExample
if (this->count == 0)
{
GlobalFree(this);
return(0);
}
// 我们应该返回这个更新后的count。
return(this->count);
}
我们接下来还要做一些其它事情。微软定义了一个叫IUnknown的COM对象。它是什么呢?一个IUnknown对象就像IExample,它的虚表只包含QueryInterface、AddRef和Release 函数(也就是说,它不包含像我们的IExample虚表中的SetString和GetString一类的附加函数)。换句话说,一个IUnkown是一个空的最小的COM对象。微软给IUnknown对象创建了一个特殊的GUID。但你知道吗?我们的IExample对象也可以装扮成一个IUnkown 对象。毕竟在它里面有QueryInterface、AddRef和Release函数。如果他们关心的仅仅是前三个函数,没人需要知道它其实是一个IExample对象。如果其它程序传递给我们的是IExample的GUID或者一个IUnkown的GUID,我们只要更改一行代码返回成功就可以 了。顺便说一下,微软的头文件中给IUnknown的GUID起了一个IID_IUnkown的C变量名:
[cpp]
//检查这个GUID是否与IExample的GUID或IUnknown的GUID匹配
if(!IsEqualIID(vTableGuid, &IID_IExample) &&
!IsEqualIID(vTableGuid,&IID_IUnknown))
总之,对于我们自己的COM对象,我们替其它程序(他们获得是对象的使用权,用它来调用我们的函数)分配对象的空间。我们负责释放对象的空间。我们通过AddRef和Release 管理引用计数来达到对象安全。我们的QueryInterface允许其它程序检查对象是否是他们需要的,也允许我们来增加引用计数。(事实上,QueryInterface还提供一个不同的功能,这个我们以后再讨论,但此时,知道这些就够了)那么,IExample现在是一个真正的COM对象了吗?这是肯定的!太棒了! 不太难! 我们已经做到了!
等等!我们还需要把它封装成其它程序可以使用的形式(也就是说一个动态链接库),写一段专用的安装程序代码,让其它程序知道如何获得我们创建的IExmaple(这样我们还要写些代码)。
IClassFactory对象
现在我们需要看一下一个程序如何获得我们的IExample对象,最终我们还必须写一些实现的代码。对于这一点微软已经设计了一个标准的方法,我们加入第二个COM对象(还用它的函数)到我们的DLL中。这个COM对象叫IClassFactory,它包括了一套特殊的已经被定义在微软的包含文件中的函数,它也拥有自己的已定义的GUID,同时给出了一个IID_IClassFactory的C变量名。
我们的IClassFact