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

2014-11-24 09:46:32 · 作者: · 浏览: 2

我们必须做一些引用计数。确切的说,每当我们分配一个对象给其它程序,我们必须对引用计数增一。其它程序每调对象的Release函数一次,我们要释放对象,同时对引用计数减少同样的次数。只有这个计数为零时我们通知操作系统我们的DLL可以安全的卸载了,因为这时我们知道程序不在使用我们的对象了。所以,我们声明一个叫OutstandingObjects的静态DWORD变量来保存这个计数。(当然,我们的DLL第一次被加载时,它需要被初始化为0。)
那么,在哪里增加这个变量最方便呢?在我们IClassFactory的CreateInstance函数中,在我们GlobalAlloc分配这个对象并确认所有的工作都正确后。这样,我们要在这个函数中,在调用Release返回正确后增加一行:
[cpp]
static DWORDOutstandingObjects = 0;
HRESULT STDMETHODCALLTYPEclassCreateInstance(IClassFactory *this,
IUnknown *punkOuter, REFIID vTableGuid,void **ppv)
{
...
IExampleVtbl.Release(thisobj);
// 如果一切正确增加外部对象的计数
if (!hr)InterlockedIncrement(&OutstandingObjects);;
}
}
return(hr);
}
那么,在哪里减小这个变量最方便呢?在我们IExample的Release函数的正确GlobalFree对象后。所以我们在GlobalFree后增加一行:
[cpp]
InterlockedDecrement(&OutstandingObjects);
但还没完。(这些混乱的细节微软永远不会结束?)微软还给出一个允许程序在内存中锁定我们DLL的方法。为此,它可以调用我们IClassFactory的LockServer函数,如果它要我们增加一次我们DLL的锁的次数则传入1,如果它要我们减少一次我们DLL的锁的次数则传入0。这样,我们需要第二个命名为LockCount的静态DWORD引用计数。(当然,当我们的DLL被加载时它也需要初始化为0)我们的LockServer函数现在变成这样:
[cpp]
static DWORD LockCount =0;
HRESULT STDMETHODCALLTYPE
classLockServer(IClassFactory *this,BOOL flock)
{
if (flock)InterlockedIncrement(&LockCount);
else InterlockedDecrement(&LockCount);
return(NOERROR);
}
现在我们准备写我们的DllCanUnloadNow函数:
[cpp] view plaincopy
HRESULT PASCALDllCanUnloadNow(void)
{
// 如果有人要重新获得我们对象的指针,并且其他人还没有调用Release()。
// 那么我们返回S_FALSE来提示不能卸载这个DLL。
// 如果有人已经锁定了它,同样返回S_FALSE。
return((OutstandingObjects | LockCount) S_FALSE : S_OK);
}
如果你下载这个例程,我们DLL的源文件(IExample.c)在IExample目录下。这个源文件也支持通过微软的VisualC++工程文件来创建一个DLL(IExample.dll)
C++/C包含文件
像早些时候提到的,为了让C/C++写的程序使用我们的IExmaple DLL,我们需要把我们的IExample和它的虚表GUID给其他程序的作者。我们把这些GUID宏放在包含(.H)文件中,它会分发给其他人,它也包含在我们的DLL源代码中。我们也需要把IExmapleVtbl和IExample结构定义放在这个包含文件中,这样其它程序就可以通过我们给他的IExample来调用我们的函数了。
到目前为止,我们定义IExampleVtble和IExample结构如下:
[cpp]
typedefHRESULT STDMETHODCALLTYPE QueryInterfacePtr(IExample *, REFIID, void **);
typedefULONG STDMETHODCALLTYPE AddRefPtr(IExample *);
typedef ULONGSTDMETHODCALLTYPE ReleasePtr(IExample *);
typedef HRESULTSTDMETHODCALLTYPE SetStringPtr(IExample *, char *);
typedef HRESULTSTDMETHODCALLTYPE GetStringPtr(IExample *, char *, long);
typedef struct {
QueryInterfacePtr *QueryInterface;
AddRefPtr *AddRef;
ReleasePtr *Release;
SetStringPtr *SetString;
GetStringPtr *GetString;
} IExampleVtbl;
typedef struct {
IExampleVtbl *lpVtbl;
DWORD count;
char buffer[80];
} IExample;
上面代码有一个问题。我们不想让其它程序知道我们的“count”和“buffer”成员。我们要对其它程序隐藏它们。我们决不允许程序直接访问我们对象的数据成员。它应该只知道“lpVtbl”成员,通过它来调用我们的函数。所以,就程序相关性而言,我们要这样定义IExample:
[cpp]
typedef struct {
IExampleVtbl *lpVtbl;
} IExample;
最后,上面的C定义有一个问题。对于一个要使用我们的COM对象的C++程序员来说,它真不是容易的事。毕竟,即使我们用C写了IExample,我们的IExample结构是一个真正的C++类。但对于一个使用它的C++程序而言,把它定义成C++类比C结构要容易的多。
为了替换上面的定义,微软提供了一个我们可以使用它在某种程度上使C和C++都可以工作且隐藏额外数据成员的宏。为了使用这个宏,我们必须首先把我们的对象名(就是IExample)定义为INTERFACE。在这之前,我们必须undef这个符号来避免编译器的警告。然后我们使用DECLARE_INTERFACE_宏。在这个宏里面,我们列出我们的IE