在 IUnknown 中添加和发布,它们实际上做了什么?

addref and release in IUnknown, what do they actually do?

本文关键字:实际上 什么 IUnknown 添加      更新时间:2023-10-16

我一直试图让我的大脑在Windows中扩展外壳。需要实现的一些函数是 addref() 和 release()。它说,它会跟踪对象引用并在不使用时释放它们。

只是在一个简单的解释中,它实际上在跟踪什么?在我的想法中,你创建自己的对象,根据你的目的实现各种接口,然后让classfactory将对象返回到com引擎运行,除非我弄错了。

我只是在理解这个概念方面真的很慢。也是一步一步的过程,Windows com引擎加载shell扩展,从识别dll到实际执行再到卸载。请做一些简单的解释。问候

外壳扩展只是普通的 COM 对象。

接口(通常以大写 i 为前缀)基本上是一个协定。一个接口可以有一个或多个实现。

发布由对象/接口的"用户"在使用完后调用:

IWhatever*pW;
if (SUCCEEDED(CoCreateInstance(CLSID_Something, ..., IID_IWhatever, (void**) &pW)))
{
pW->DoWhateverThisThingDoes();
NotifyMyClients(pW);
pW->Release(); // Tell this instance that we are done with it
}

在前面的示例中,我们调用 Release 来指示我们不再需要此接口,但我们实际上不知道接口实例现在是否会被销毁。

如果我们想象NotifyMyClients已知的客户端/插件/扩展之一是这样实现的:

class FooClient {
IWhatever*MyWhatever;
void FooClient::OnNotifyNewWhatever(IWhatever*p) // Somehow called by NotifyMyClients
{
p->AddRef(); // We want to use this object later even if everyone else are done with it
if (MyWhatever) MyWhatever->Release(); // Throw away the previous instance if we had one
MyWhatever = p;
SetTimer(...); // Activate timer so we can interact with MyWhatever later
}
FooClient::FooClient()
{
MyWhatever = 0; 
}
FooClient::~FooClient()
{
if (MyWhatever) MyWhatever->Release(); 
}
};

创建对象的代码不需要知道其他代码对对象执行的操作,它只负责自己与对象的交互。

基本规则是:每次在对象上调用 AddRef 时调用 Release 一次。如果创建了对象实例,则还必须释放它。

用于实现 STA 接口的伪代码可能如下所示:

#include <Whatever.h> // The skeleton/contract for IWhatever
class Whatever : public IWhatever {
ULONG refcount;
Whatever() : refcount(1) {} // Instance refcount should start at 1
HRESULT QueryInterface(...) { ... }
ULONG AddRef() { return ++refcount; }
ULONG Release()
{
if (--refcount == 0) // Anyone still using me?
{
delete this; // Nope, I can destroy myself
return 0;
} 
return refcount;
}
void DoWhateverThisThingDoes() { PerformMagic(this); }
};

必须在注册表中注册此特定 IWhatever 实现 (CLSID_Something) 的 CLSID,以便 COM 可以找到它。注册包括指向代码所在.DLL的路径。此.DLL必须导出DllGetClassObject函数。

DllGetClassObject 分发其 IClassFactory 实现的实例,当 COM 请求一个新的 IWhatever 实例时,工厂只会调用new Whatever();

我没有介绍 QueryInterface,但它用于询问对象实例是否支持另一个接口。ICar可能实现IVehicle,但不是IBus也不是ITrain等。所有 COM 对象都支持 IUnknown 接口,所有其他接口都继承自 IUnknown。

不可能用一个答案来解释COM,但网上有很多介绍文章。

您可以使用/使用由Microsoft方和第三方创建的 shell 对象,也可以创建自己的文档接口实现。

如果我们以IContextMenu为例。在单个系统上可以有许多实现。资源管理器为每个已注册和适用的实例创建一个实例(文件扩展名与注册等匹配)IContextMenu 实现,当您右键单击某些内容并且每个实例将其菜单项添加到菜单中时。再次调用添加所选菜单项的实例以执行其操作,然后释放所有实例。

MSDN 具有此处最常用的扩展类型列表。如果你想写一个,编写 Shell 扩展的完整白痴指南是一个很好的起点。

只是一个简单的解释,它实际上在跟踪什么?

很简单,你说的:

它跟踪对象引用并在不使用时释放它们

它们用于跟踪正在使用的对对象的引用数。

调用 addref() 会增加引用计数,而 release() 会递减它。 你调用 addref() 以确保它知道你正在使用该对象,这样它就不会破坏它。 完成后,释放()告诉它您已完成对象。

一旦每个人都完成了对象的处理(引用计数下降到零),就可以销毁该对象。

请注意,获取接口引用(例如通过DllGetClassObject) 将为您调用 addref(),因此您收到的接口引用的引用计数(至少)为 1。