{"id":109214,"date":"2024-01-01T07:00:00","date_gmt":"2024-01-01T15:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=109214"},"modified":"2023-12-31T21:42:54","modified_gmt":"2024-01-01T05:42:54","slug":"20240101-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20240101-00\/?p=109214","title":{"rendered":"How do I prevent my ATL class from participating in COM aggregation? <CODE>DECLARE_<WBR>NOT_<WBR>AGGREGATABLE<\/CODE> didn&#8217;t work"},"content":{"rendered":"<p>Consider the following ATL class declaration:<\/p>\n<pre>class Widget :\r\n    public CComObjectRootEx&lt;CComMultiThreadModel&gt;,\r\n    public CComCoClass&lt;Widget&gt;,\r\n    public IAgileObject\r\n{\r\npublic :\r\n    <span style=\"border: solid 1px currentcolor;\">DECLARE_NOT_AGGREGATABLE(Widget)<\/span>\r\n\r\n    BEGIN_COM_MAP(Widget)\r\n        COM_INTERFACE_ENTRY(IAgileObject)\r\n    END_COM_MAP()\r\n\r\n    \u27e6 ... \u27e7\r\n};\r\n<\/pre>\n<p>This class says that it is not aggregatable.<\/p>\n<p>Hold my beer.<\/p>\n<pre>HRESULT CreateAggregatedWidget(\r\n    IUnknown* punkOuter, REFIID riid, void** ppv)\r\n{\r\n    return\r\n        CComCreator&lt;CComAggObject&lt;Widget&gt;&gt;::\r\n            CreateInstance(punkOuter, riid, pppv);\r\n}\r\n<\/pre>\n<p>There, I aggregated your allegedly non-aggregatable object.<\/p>\n<p>The <code>DECLARE_<wbr \/>NOT_<wbr \/>AGGREGATABLE<\/code> macro is an instruction to the class factory to disallow aggregation, but if you create the object by means other than the class factory, then the macro has no effect. Creating the object without using the class factory is common for objects created internally.<\/p>\n<p>So how do you mark your class so that any attempt to aggregate the object encounters a compiler error?<\/p>\n<p>I found a sneaky trick.<\/p>\n<p>The <code>CComAggObject<\/code> template sets up aggregation by creating the inner object and overriding its <code>IUnknown<\/code> methods to forward to the controlling unknown, <a href=\"https:\/\/learn.microsoft.com\/en-us\/cpp\/atl\/reference\/ccomobjectrootex-class?view=msvc-170#m_pouterunknown\"> kept in the member variable <code>m_pOuterUnknown<\/code><\/a>. The connection is made here:<\/p>\n<pre>template &lt;class Base&gt;\r\nclass CComContainedObject :\r\n    public Base\r\n{\r\npublic:\r\n    typedef Base _BaseBlass;\r\n\r\n    CComContainedObject(void* pv)\r\n    {\r\n        <span style=\"border: solid 1px currentcolor;\">this-&gt;m_pOuterUnknown = (IUnknown*)pv;<\/span>\r\n    }\r\n<\/pre>\n<p>If we can make this line of code produce an error, then we can prevent people from instantiating <code>CComContainedObject&lt;<wbr \/>Widget&gt;<\/code> and thereby prevent them from putting it in a <code>CComAggObject<\/code>.<\/p>\n<p>I had multiple ideas for how to accomplish this, but the simplest was this:<\/p>\n<pre>class Widget :\r\n    public CComObjectRootEx&lt;CComMultiThreadModel&gt;,\r\n    public CComCoClass&lt;Widget&gt;,\r\n    public IAgileObject\r\n{\r\npublic :\r\n    DECLARE_NOT_AGGREGATABLE(Widget)\r\n\r\n    <span style=\"border: solid 1px currentcolor;\">static constexpr void* m_pOuterUnknown = nullptr;<\/span>\r\n\r\n    BEGIN_COM_MAP(Widget)\r\n        COM_INTERFACE_ENTRY(IAgileObject)\r\n    END_COM_MAP()\r\n\r\n    \u27e6 ... \u27e7\r\n};\r\n<\/pre>\n<p>This shadows the <code>m_pOuterUnknown<\/code> member from <code>CComObjectRootEx<\/code>, so when <code>CComContainedObject<\/code> tries to do a <code>this-&gt;m_pOuterUnknown<\/code>, it gets the <code>Widget<\/code> one. And since the <code>Widget<\/code> one is marked <code>constexpr<\/code>, it cannot be modified.<\/p>\n<pre style=\"white-space: pre-wrap;\">error C3892: 'm_pOuterUnknown': you cannot assign to a variable that is const\r\n<\/pre>\n<p>If you are compiling with a version of C++ that doesn&#8217;t support <code>constexpr<\/code> (which is not entirely out of the question given that you&#8217;re using ATL, and ATL was written in 1996, over a decade before <code>constexpr<\/code> was <a href=\"https:\/\/www.stroustrup.com\/sac10-constexpr.pdf\"> a twinkle in Gabriel and Bjarne&#8217;s eyes<\/a>), you can use this alternate shadowing definition:<\/p>\n<pre>    static void m_pOuterUnknown() {}\r\n<\/pre>\n<p>This makes <code>m_pOuterUnknown<\/code> a function, and you cannot assign to a function.<\/p>\n<pre style=\"white-space: pre-wrap;\">error C2659: '=': function as left operand\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>That marker applies only to creation via the class factory.<\/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-109214","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>That marker applies only to creation via the class factory.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/109214","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=109214"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/109214\/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=109214"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=109214"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=109214"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}