{"id":110865,"date":"2025-02-13T07:00:00","date_gmt":"2025-02-13T15:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=110865"},"modified":"2025-02-13T08:31:24","modified_gmt":"2025-02-13T16:31:24","slug":"20250213-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20250213-00\/?p=110865","title":{"rendered":"A sample implementation of the weak reference pattern for COM callbacks"},"content":{"rendered":"<p>Some time ago, I shared <a title=\"A very brief introduction to patterns for implementing a COM object that hands out references to itself\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20211025-00\/?p=105828\"> some patterns for implementing a COM object that hands out references to itself<\/a>, and the first was something I called the &#8220;weak pattern&#8221;.<\/p>\n<p>As a reminder, the weak pattern uses a separate callback object that holds a weak reference to the main object and forwards method calls to the main object.<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse;\" border=\"0\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td colspan=\"2\">\u00a0<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px currentcolor;\" colspan=\"2\">Callback<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td colspan=\"2\">\u00a0<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px currentcolor;\">IUnknown<\/td>\n<td style=\"border: solid 1px currentcolor;\">ICallback<\/td>\n<td>\u2190<\/td>\n<td>event source<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px currentcolor;\" colspan=\"2\">Widget<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px currentcolor; border-bottom: none;\" colspan=\"2\">refcount<\/td>\n<\/tr>\n<tr>\n<td>client<\/td>\n<td>\u2192<\/td>\n<td style=\"border: solid 1px currentcolor;\">IUnknown<\/td>\n<td style=\"border: solid 1px currentcolor;\">IWidget<\/td>\n<td>\u2190<\/td>\n<td style=\"border: solid 1px currentcolor; border-top: none;\" colspan=\"2\">weak reference<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px currentcolor;\" colspan=\"2\">refcount<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px currentcolor;\" colspan=\"2\" rowspan=\"4\">state data<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Let&#8217;s flesh this out with an actual implementation.<\/p>\n<p>For concreteness, let&#8217;s say that the main Widget object is implemented in C++\/WinRT, and there is a callback interface called something like <code>IGadget\u00adCallback<\/code>. (Applying the same logic to other libraries is left as an exercise.)<\/p>\n<p>We start with the gadget and its callback.<\/p>\n<pre>\/\/ gadgets.idl\r\ninterface IGadgetCallback : IUnknown\r\n{\r\n    HRESULT ColorChanged([in] COLORREF oldColor,\r\n                         [in] COLORREF newColor);\r\n    HRESULT NameChanged([in] PCWSTR oldName,\r\n                        [in] PCWSTR newName);\r\n    HRESULT Refreshed();\r\n};\r\n\r\ninterface IGadget : IUnknown\r\n{\r\n    \u27e6 other Gadget methods \u27e7\r\n\r\n    HRESULT SetCallback([in] IGadgetCallback* callback);\r\n};\r\n<\/pre>\n<p>The idea is that after you create a Gadget, you can use the <code>Set\u00adCallback()<\/code> method to pass an object that receives gadget callbacks for various things that might happen.<\/p>\n<p>Here&#8217;s how we can create a Widget that sets a callback on the Gadget that calls back into the Widget, but without creating a circular reference.<\/p>\n<pre>namespace winrt::Contoso::implementation\r\n{\r\n    struct Widget : WidgetT&lt;Widget&gt;\r\n    {\r\n        void InitializeComponent();\r\n\r\n        \u27e6 other Widget methods \u27e7\r\n\r\n        \/\/ Used by WidgetGadgetCallback\r\n        void ColorChanged(COLORREF oldColor,\r\n                          COLORREF newColor);\r\n        void NameChanged([PCWSTR oldName,\r\n                          PCWSTR newName);\r\n        void Refreshed();\r\n    private:\r\n\r\n        winrt::com_ptr&lt;IGadget&gt; m_gadget =\r\n            winrt::create_instance(CLSID_Gadget);\r\n    };\r\n\r\n    struct WidgetGadgetCallback :\r\n        winrt::implements&lt;WidgetGadgetCallback,\r\n                          IGadgetCallback&gt;\r\n    {\r\n        WidgetGadgetCallback(implementation::Widget* widget);\r\n\r\n        IFACEMETHODIMP ColorChanged(COLORREF oldColor,\r\n                                    COLORREF newColor);\r\n        IFACEMETHODIMP NameChanged(PCWSTR oldName,\r\n                                   PCWSTR newName);\r\n        IFACEMETHODIMP Refreshed();\r\n\r\n    private:\r\n        winrt::weak_ref&lt;implementation::Widget&gt; m_weakWidget;\r\n\r\n        \u27e6 more to come \u27e7\r\n    };\r\n}\r\n<\/pre>\n<p>The <code>IGadgetCallback<\/code> is implemented not by the <code>Widget<\/code> but by the <code>Widget\u00adGadget\u00adCallback<\/code>, and the idea is that the <code>Widget\u00adGadget\u00adCallback<\/code> forwards the method calls to the corresponding methods on the <code>Widget<\/code>.<\/p>\n<p>Let&#8217;s hook them up. When the <code>Widget<\/code> initializes, it creates the callback object and sets it as the callback.<\/p>\n<pre>void Widget::InitializeComponent()\r\n{\r\n    winrt::check_hresult(m_gadget-&gt;SetCallback(\r\n        winrt::make&lt;WidgetGadgetCallback&gt;(this).get());\r\n}\r\n<\/pre>\n<p>The real work happens in the callback object.<\/p>\n<p>The constructor takes the incoming <code>Widget*<\/code> and obtains a weak reference to it.<\/p>\n<pre>WidgetGadgetCallback::WidgetGadgetCallback(\r\n        implementation::Widget* widget) :\r\n    m_weakWidget(widget-&gt;get_weak()) {}\r\n<\/pre>\n<p>When a method on the callback interface is called, we want to forward the call back to the <code>Widget<\/code>. To reduce typing, we&#8217;ll templatize the pattern.<\/p>\n<pre>struct WidgetGadgetCallback :\r\n    winrt::implements&lt;WidgetGadgetCallback,\r\n                      IGadgetCallback&gt;\r\n{\r\n    \u27e6 existing public methods \u27e7\r\n\r\nprivate:\r\n    winrt::weak_ref&lt;implementation::Widget&gt; m_weakWidget;\r\n\r\n    template&lt;auto Method, typename... Args&gt;\r\n    HRESULT Forward(Args&amp;&amp;... args) try\r\n    {\r\n        if (auto strong = m_weakWidget.get()) {\r\n            ((*strong).*Method)(\r\n                std::forward&lt;Args&gt;(args)...);\r\n        }\r\n        return S_OK;\r\n    }\r\n    catch (...)\r\n    {\r\n        return winrt::to_hresult();\r\n    }\r\n};\r\n<\/pre>\n<p>The pattern for forwarding the call is to try to resolve the weak reference to a strong reference, and if it succeeds, call the provided method while forwarding the parameters. We also do some adapting between the <code>HRESULT<\/code>-based COM call and the exception-based method back in the <code>Widget<\/code> object. (This was a quirk of this particular example. Your version may not require adapting.)<\/p>\n<p>Now, we happen to know that all of the parameters to the COM callback methods are cheap to copy, so we could pass them along by value:<\/p>\n<pre>    template&lt;auto Method, typename... Args&gt;\r\n    HRESULT Forward(<span style=\"border: solid 1px currentcolor;\">Args<\/span>... args) try\r\n    {\r\n        if (auto strong = m_weakWidget.get()) {\r\n            ((*strong).*Method)(<span style=\"border: solid 1px currentcolor;\">args<\/span>...);\r\n        }\r\n        return S_OK;\r\n    }\r\n    catch (...)\r\n    {\r\n        return winrt::to_hresult();\r\n    }\r\n<\/pre>\n<p>The last piece of the puzzle is telling each method to do the forwarding.<\/p>\n<pre>struct WidgetGadgetCallback :\r\n    winrt::implements&lt;WidgetGadgetCallback,\r\n                      IGadgetCallback&gt;\r\n{\r\n    \u27e6 ... \u27e7\r\n\r\n    IFACEMETHODIMP ColorChanged(COLORREF oldColor,\r\n                                COLORREF newColor)\r\n    {\r\n        return Forward&lt;&amp;implementation::Widget::ColorChanged&gt;(\r\n            oldColor, newColor);\r\n    }\r\n\r\n    IFACEMETHODIMP NameChanged(PCWSTR oldName,\r\n                               PCWSTR newName);\r\n    {\r\n        return Forward&lt;&amp;implementation::Widget::NameChanged&gt;(\r\n            oldName, newName);\r\n    }\r\n\r\n    IFACEMETHODIMP Refreshed();\r\n    {\r\n        return Forward&lt;&amp;implementation::Widget::Refreshed&gt;();\r\n    }\r\n\r\n    \u27e6 ... \u27e7\r\n};\r\n<\/pre>\n<p>We invoke the <code>Forward<\/code> method with the function we want to forward to and the arguments we want to forward.<\/p>\n<p>In our example, we gave the forwarded-to method the same name as the forwarded-from method, but there&#8217;s no requirement that you do that. You just have to provide the correct method name to the <code>Forward<\/code> template function.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A forwarder through a weak reference.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-110865","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>A forwarder through a weak reference.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/110865","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=110865"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/110865\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=110865"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=110865"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=110865"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}