Shell 扩展只是普通的 COM 对象。
接口(通常以大写 i 为前缀)基本上是一个合约。一个接口可以有一个或多个实现。
对象/接口的“用户”在使用完对象/接口后调用 Release:
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,但有很多 introduction articles online。
您可以使用/使用由 Microsoft 和第 3 方创建的 shell 对象,还可以创建自己的 documented interfaces 实现。
如果我们以 IContextMenu 为例。在单个系统上可以有许多实现。资源管理器为每个已注册且适用的(文件扩展名与注册等)创建一个实例。当您右键单击某项时,IContextMenu 实现,每个实例将其菜单项添加到菜单中。再次调用添加所选菜单项的实例以执行其操作,然后释放所有实例。
MSDN 有一个最常用的扩展类型列表here。如果你想写一个,The Complete Idiot's Guide to Writing Shell Extensions 是一个很好的起点。