{"id":33533,"date":"2005-11-01T09:58:54","date_gmt":"2005-11-01T09:58:54","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2005\/11\/01\/the-com-interface-contract-rules-exist-for-a-reason\/"},"modified":"2005-11-01T09:58:54","modified_gmt":"2005-11-01T09:58:54","slug":"the-com-interface-contract-rules-exist-for-a-reason","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20051101-54\/?p=33533","title":{"rendered":"The COM interface contract rules exist for a reason"},"content":{"rendered":"<p><P>\nSome people believe that the COM rules on interfaces\nare needlessly strict.\nBut the rules are there for a reason.\n<\/P>\n<P>\nSuppose you ship some interface in version&nbsp;N of your product.\nIt&#8217;s an internal interface,\nnot documented to outsiders.\nTherefore, you are free to change it any time you want without\nhaving to worry about breaking compatibility with any third-party\nplug-ins.\n<\/P>\n<P>\nBut remember that if you change an interface, you need to generate\na new Interface Identifier&nbsp;(IID).\nBecause an interface identifier uniquely identifies the interface.\n(That&#8217;s sort of implied by its name, after all.)\n<\/P>\n<P>\nAnd this rule applies even to internal interfaces.\n<\/P>\n<P>\nSuppose you decide to violate this rule and use the same\nIID to represent a slightly different interface in\nversion&nbsp;N+1 of your program.\nSince this is an internal interface,\nyou have no qualms about doing this.\n<\/P>\n<P>\nUntil you have to write a patch that services both versions.\n<\/P>\n<P>\nNow your patch is in trouble.\nIt can call\n<CODE>IUnknown::QueryInterface<\/CODE>\nand ask for that IID, and it will get something back.\nBut you don&#8217;t know whether this is the version&nbsp;N interface\nor the version&nbsp;N+1 interface.\nIf you&#8217;re not even aware that this has happened,\nyour patch will probably just assume it has the\nversion&nbsp;N+1 interface,\nand strange things happen when it is run on version&nbsp;N.\n<\/P>\n<P>\nDebugging this problem is not fun.\nNeither is fixing it.\nYour patch has to use some other cues to decide which\ninterface it actually got back.\nIf your program has been patched previously,\nyou need to have the version numbers of every single patch\nso that you can determine which version of the interface you have.\n<\/P>\n<P>\nNote that this dependency can be hidden behind other interfaces.\nConsider:\n<\/P>\n<PRE>\n[\n    uuid(&#8220;ABC&#8221;)\n]\ninterface IColorInfo\n{\n    HRESULT GetBackgroundColor([out] COLORREF *pcr);\n    &#8230;\n};<\/p>\n<p>[\n    uuid(&#8220;XYZ&#8221;)\n]\ninterface IGraphicImage\n{\n    &#8230;\n    HRESULT GetColorInfo([out] IColorInfo **ppci);\n};\n<\/PRE>\n<P>\nSuppose you want to add a new method to the\n<CODE>IColorInfo<\/CODE> interface:\n<\/P>\n<PRE>\n[\n    uuid(&#8220;<FONT COLOR=\"blue\">DEF<\/FONT>&#8220;)\n]\ninterface IColorInfo\n{\n    HRESULT GetBackgroundColor([out] COLORREF *pcr);\n    &#8230;\n    <FONT COLOR=\"blue\">HRESULT AdjustColor(COLORREF clrOld,\n                        COLORREF clrNew);<\/FONT>\n};<\/p>\n<p>[\n    uuid(&#8220;XYZ&#8221;)\n]\ninterface IGraphicImage\n{\n    &#8230;\n    HRESULT GetColorInfo([out] IColorInfo **ppci);\n};\n<\/PRE>\n<P>\nYou changed the interface, but you also changed the IID,\nso everything is just fine, right?\n<\/P>\n<P>\nNo, it isn&#8217;t.\n<\/P>\n<P>\nThe <CODE>IGraphicImage<\/CODE> interface is dependent upon the\n<CODE>IColorInfo<\/CODE> interface.\nWhen you changed the <CODE>IColorInfo<\/CODE> interface,\nyou implicitly changed the <CODE>IGraphicImage::GetColorInfo<\/CODE> method,\nsince the returned interface is now the\nversion&nbsp;N+1 <CODE>IColorInfo<\/CODE> interface.\n<\/P>\n<P>\nConsider a patch written with the\nversion&nbsp;N+1 header files.\n<\/P>\n<PRE>\nvoid AdjustGraphicColorInfo(IGraphicImage* pgi,\n                            COLORREF clrOld, COLORREF clrNew)\n{\n IColorInfo *pci;\n if (SUCCEEDED(pgi-&gt;GetColorCount(&amp;pci)) {\n  pci-&gt;AdjustColor(clrOld, clrNew);\n  pci-&gt;Release();\n }\n}\n<\/PRE>\n<P>\nIf run against version&nbsp;N, the call to\n<CODE>IGraphicImage::GetColorCount<\/CODE> will return a\nversion&nbsp;N <CODE>IColorInfo<\/CODE>, and that version\ndoesn&#8217;t support the <CODE>IColorInfo::AdjustColor<\/CODE>\nmethod.\nBut you&#8217;re going to call it anyway.\nResult: Walking off the end of the version&nbsp;N vtable\nand calling into space.\n<\/P>\n<P>\nThe quick solution is to change the IID for the\n<CODE>IGraphicImage<\/CODE> function to reflect the\nchange on the <CODE>IColorInfo<\/CODE> interface\non which it depends.\n<\/P>\n<PRE>\n[\n    uuid(&#8220;<FONT COLOR=\"blue\">UVW<\/FONT>&#8220;)\n]\ninterface IGraphicImage\n{\n    &#8230;\n    HRESULT GetColorInfo([out] IColorInfo **ppci);\n};\n<\/PRE>\n<P>\nA more robust fix would be to change\nthe <CODE>IGraphicImage::GetColorInfo<\/CODE> method\nso that you pass the interface you want to receive.\n<\/P>\n<PRE>\n[\n    uuid(&#8220;<FONT COLOR=\"blue\">RST<\/FONT>&#8220;)\n]\ninterface IGraphicImage\n{\n    &#8230;\n    HRESULT GetColorInfo(<FONT COLOR=\"blue\">[in] REFIID riid,\n                         [iid_is(riid), out] void** ppv<\/FONT>);\n};\n<\/PRE>\n<P>\nThis allows interfaces on which <CODE>IGraphicImage<\/CODE> depends\nto change without requiring a change to\nthe <CODE>IGraphicImage<\/CODE> interface itself.\nOf course, the implementation needs to change to respond to the\nnew value of <CODE>IID_IColorInfo<\/CODE>.\nBut now the caller can feel safe in the knowledge that when it asks\nfor an interface, it&#8217;s actually getting it and not something else\nthat coincidentally has the same name.\n<\/P><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Some people believe that the COM rules on interfaces are needlessly strict. But the rules are there for a reason. Suppose you ship some interface in version&nbsp;N of your product. It&#8217;s an internal interface, not documented to outsiders. Therefore, you are free to change it any time you want without having to worry about breaking [&hellip;]<\/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-33533","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Some people believe that the COM rules on interfaces are needlessly strict. But the rules are there for a reason. Suppose you ship some interface in version&nbsp;N of your product. It&#8217;s an internal interface, not documented to outsiders. Therefore, you are free to change it any time you want without having to worry about breaking [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/33533","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=33533"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/33533\/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=33533"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=33533"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=33533"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}