COM技术内幕--引用计数及动态链接

来源:互联网 发布:linux翻墙教程 编辑:程序博客网 时间:2024/05/16 07:47
引用计数简介
AddRef 和 Release实现的是一种名为引用计数的内存管理技术。引用计数是使组件能够自己将自己删除的最简单同时效率最高的方法。COM组件维护一个称作是引用计数的数值。当客户从组件取得一个接口时,此引用计数值将增1。当客户使用完某个接口后,组件的引用计数值将减1。当引用计数值为0时,组件即可将自己从内存中删除。
规则:
1)在返回之前调用AddRef。对于那些返回接口的指针函数,在返回之前应用相应的指针调用AddRef。这些函数包括QueryInterface及CreateInstance。这样当客户从这种函数得到一个接口后,它将无需调用AddRef。
2)使用完接口后调用Release。在使用完某个接口之后应调用些接口的Release函数。
IUnKnown* pIUnknown = CreateInstance();

IX* pIX = NULL;
HRESULT hr = pIUnknown->QueryInterface(IID_IX,(void**)&pIX);
pIUnknown->Release();
if (SUCCEEDED(hr))
{
pIX->Fx();
pIX->Release();
}
3)在赋值之后调用AddRef。在将一个接口指针赋给另外一个接口指针时,应调用AddRef。换句话说,在建立接口的另外一个引用之后应增加相应组件的引用计数。
IUnKnown* pIUnknown = CreateInstance();

IX* pIX = NULL;
HRESULT hr = pIUnknown->QueryInterface(IID_IX,(void**)&pIX);
pIUnknown->Release();
if (SUCCEEDED(hr))
{
pIX->Fx();
IX* pIX2 = pIX;
pIX2->AddRef();
pIX2->Fx();
pIX2->Release();
pIX->Release();
}

引用计数接口:
组件可以选择是对整个组件维护一个引用计数不是对每一个接口分别维护一个引用计数
///////////////////////
AddRef和Release的实现
ULONG __stdcall AddRef()
{
return ++m_cRef;
}
ULONG __stdcall Release()
{
if (--m_cRef==0)
{
delete this;
return 0;
}
return m_cRef;
}
许多情况下用InterlockedIncrement和InterlockedDeCrement来实现 AddRef和Release。这两个函数可以确保在同一时刻只会有同一个线程来访问成员变量。

ULONG __stdcall AddRef()
{
return InterlockedIncrement(&m_cRef);
}
ULONG __stdcall Release()
{
if (InterlockedDeCrement(&m_cRef)==0)
{
delete this;
return 0;
}
return m_cRef;
}


何时进行引用计数
1)引用计数的优化
IUnKnown* pIUnknown = CreateInstance();
IX* pIX = NULL;
HRESULT hr = pIUnknown->QueryInterface(IID_IX,(void**)&pIX);
pIUnknown->Release();
if (SUCCEEDED(hr))
{
IX* pIX2 = pIX;
pIX2->AddRef();
pIX->Fx();
pIX2->Fx();
pIX>Release();
pIX2>Release();
}
对于上面的代码,只有当客户将pIX释放时组件才会被从内存中删除。而客户只是在用完了pIX和pIX2之后才会将释放。正是由于组件只是在被释放之后才被从内存中删除掉,因此可以保证在pIX2的生命期内相应 的组件将一直在内存中。因此对pix2并不真的需要AddRef和Release。

动态链接
1)从DLL中输出函数
extern "C"
IUnknown* CreateInstance()
{
IUnknown* pI=(IUnknown*)(void*) new CA;
pI->AddRef();
return pI;
}
在函数定义前加上 extern "C"可防止c++编译器在函数名称上加上类型信息。
例如:
?CreateInstance@@YAPAUUnknown@@XZ
dumpbin -exports *.dll
可将dll的函数导出
2)DLL的装载
using namespace std;

typedef IUnknown* (*CREATEFUNCPTR)();
IUnknown *CallCreateInstance(char* name)
{
HINSTANCE hComponent = ::LoadLibrary(name);
if (hComponent == NULL)
{
cout<<"error load library"<<endl;
return NULL;
}
CREATEFUNCPTR CreateInstance =
(CREATEFUNCPTR)::GetProcAddress(hComponent,"CreateInstance");
if (CreateInstance==NULL)
{
cout<<"can not find the function"<<endl;
return NULL;
}
return CreateInstance();
}
使用DLL的原因:
动态链接库将被映射到它们所链接的应用程序的进程空间中
//////////////////////




1 0