您的位置:首页 > 其它

Windows桌面应用程序(1-2-3-6th) 管理对象的生命周期

2018-03-30 22:29 295 查看
对于我们尚未提及的COM接口有一个规则。每个COM接口必须直接或间接地从名为IUnknown的接口继承。该接口提供了所有COM对象必须支持的一些基线功能。

IUnknown接口定义了三个方法:

QueryInterface

AddRef

Release

QueryInterface方法可以使程序在运行时查询对象的能力。我们将在下一个主题“ 为接口询问对象”中详细说明。所述AddRefRelease方法用于控制一个对象,这是本主题的主题的寿命。

引用计数

无论程序可以做什么,在某个时候它都会分配和释放资源。分配资源很容易。了解何时释放资源非常困难,特别是如果资源的生命周期超出当前范围。这个问题不是COM独有的。任何分配堆内存的程序都必须解决同样的问题。已经设计了各种解决方案,包括自动析构函数(C ++)和垃圾回收(C#,Java,Lisp)。COM使用称为引用计数的方法。

每个COM对象都维护一个内部计数,称为引用计数。引用计数跟踪对象当前活动的引用数量。当引用的数量下降到零时,对象将自行删除。最后一部分值得重复:对象删除自己; 该程序从不显式删除该对象。

以下是参考计数的规则:

首次创建对象时,其引用计数为1.此时,程序具有指向该对象的单个指针。

该程序可以通过复制指针来创建新的参考。每当您复制指针时,都必须调用对象的AddRef方法。此方法将引用计数加1。

当您完成使用指向该对象的指针时,您必须调用ReleaseRelease方法减一的引用计数。它也使指针无效。致电Release后请勿再次使用指针。(如果你有指向同一个对象的其他指针,你可以继续使用这些指针。)

当您使用每个指针调用Release时,对象的引用计数将达到零,并且该对象将自行删除。

下图显示了一个简单但典型的案例。



显示参考计数的插图

该程序创建一个对象并将一个指针(p)存储到该对象。此时,引用计数为1.当程序使用指针完成时,它调用Release。引用计数递减到零,并且对象删除自己。现在p是无效的; 将p用于任何其他方法调用是错误的。

下图显示了一个更复杂的例子。



显示参考计数的插图

在这里,程序像以前一样创建一个对象并存储指针p。接下来,程序将p复制到一个新变量q中。此时,程序必须调用AddRef来增加引用计数。引用计数现在是2,并且有两个指向对象的有效指针。现在假设程序使用p完成。程序调用Release,引用计数变为1,并且p不再有效。但是,q仍然有效。稍后,程序使用q完成,因此它再次调用Release。引用计数变为零,并且对象删除自身。

你可能想知道为什么这个程序首先要复制p。主要有两个原因:首先,您可能希望将指针存储在数据结构中,如列表。其次,您可能希望将指针保留在原始变量的当前范围之外,以便将其复制到范围更广的新变量中。

引用计数的一个优点是,您可以共享指向不同代码段的指针,而不需要各种代码路径协调删除对象。相反,每个代码路径只需在该代码路径使用该对象完成时调用Release。该对象负责在正确的时间删除自己。



这里是再次打开对话框示例中的代码。

HRESULT hr=CoInitializeEx(NULL,COINIT_APARTMENTTHREADED|COINIT_DISABLE_OLE1DDE);
if(SUCCEEDED(hr)){
IFileOpenDialog *pFileOpen;
hr=CoCreateInstance(CLSID_FileOpenDialog,NULL,CLSCTX_ALL,IID_IFileOpenDialog,reinterpret_cast<void**>(&pFileOpen));
if(SUCCEEDED(hr)){
hr=pFileOpen->Show(NULL);
if(SUCCEEDED(hr)){
IShellItem *pItem;
hr=pFileOpen->GetResult(&pItem);
if(SUCCEEDED(hr)){
PWSTR pszFilePath;
hr=pItem->GetDisplayName(SIGDN_FILESYSPATH,&pszFilePath);
if(SUCCEEDED(hr)){
MessageBox(NULL,pszFilePath,L"File Path",MB_OK);
CoTaskMemFree(pszFilePath);
}
pItem->Release();
}
}
pFileOpen->Release();
}
CoUninitialize();
}


引用计数发生在两个地方。首先,如果程序成功创建Common Item Dialog对象,它必须在pFileOpen指针上调用Release

hr=CoCreateInstance(CLSID_FileOpenDialog,NULL,CLSCTX_ALL,IID_IFileOpenDialog,reinterpret_cast<void**>(&pFileOpen));
if(SUCCEEDED(hr)){
// ...
pFileOpen->Release();
}


其次,当GetResult方法返回指向IShellItem接口的指针时,程序必须在pItem指针上调用Release

hr=pFileOpen->GetResult(&pItem);
if(SUCCEEDED(hr)){
// ...
pItem->Release();
}


请注意,在这两种情况下,Release调用都是指针超出范围之前发生的最后一件事。另请注意,只有在测试成功HRESULT后才会调用Release。例如,如果对CoCreateInstance的调用失败,则pFileOpen指针无效,因此在指针上调用Release将会出错。

下一个

请求接口的对象
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: