【问题标题】:addref and release in IUnknown, what do they actually do?IUnknown 中的 addref 和 release,它们实际上是做什么的?
【发布时间】:2017-08-18 00:31:25
【问题描述】:

我一直在努力思考 Windows 中的 shell 扩展。一些需要实现的函数是 addref() 和 release()。它说,它会跟踪对象引用并在不使用时释放它们。

简单解释一下,它实际跟踪的是什么?在我看来,您创建自己的对象,根据您的目的实现各种接口,然后让 classfactory 将对象返回给 com 引擎以运行,除非我弄错了。

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

【问题讨论】:

    标签: c++ c windows shell


    【解决方案1】:

    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 是一个很好的起点。

    【讨论】:

    • 哇!这是我一直在寻找的答案,但在网上的任何教程中都找不到。精确,简单和初学者友好的解释。接受的答案!我阅读了第一段,它似乎符合我的要求,并且通过示例代码直截了当。希望像我一样困惑的人也能从中受益。
    【解决方案2】:

    简单解释一下,它实际跟踪的是什么?

    很简单,你说的是:

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

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

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

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

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

    【讨论】:

    • 是的,但是这些对象是什么?您只有一个外壳扩展对象来执行该功能,对吗?假设实现了 icontextmenu 的对象,假设您的对象的一个​​实例。那个对象就足够了,但是为什么要跟踪多个对象呢?只是困惑。或者可能只是以错误的方式看待它..
    • @Chopnut 只有一个对象,所说的“这些对象”是什么意思?
    • @m.m 哦,我应该是对象引用?它说它跟踪对象引用我认为这让我感到困惑。 dlladdref() 是添加引用,release() 是减少引用或在不使用时释放。那么这些引用是什么,为什么会有多个对对象的引用......只是想知道当它说跟踪引用时我是否理解错误,当只有一个对象时......我的头在
    • 你明白对象有多个接口...如果你有两个接口指针指向对象那么那是两个引用
    猜你喜欢
    • 2011-09-04
    • 1970-01-01
    • 1970-01-01
    • 2019-10-27
    • 2012-01-08
    • 2023-03-19
    • 2013-05-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多