{"id":9303,"date":"2011-10-24T07:00:00","date_gmt":"2011-10-24T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2011\/10\/24\/if-the-shell-is-written-in-c-why-not-just-export-its-base-classes\/"},"modified":"2011-10-24T07:00:00","modified_gmt":"2011-10-24T07:00:00","slug":"if-the-shell-is-written-in-c-why-not-just-export-its-base-classes","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20111024-00\/?p=9303\/","title":{"rendered":"If the shell is written in C++, why not just export its base classes?"},"content":{"rendered":"<p>\nton suggested that\n<a HREF=\"http:\/\/blogs.msdn.com\/oldnewthing\/archive\/2008\/11\/24\/9137795.aspx#9138730\">\nsince the shell is written in C++,\n<code>IShell&shy;Folder<\/code> should have been an abstract class,\nand then it could have used techniques like\nexceptions and Inversion of Control<\/a>.\n<\/p>\n<p>\nOkay, first of all, I&#8217;m not sure how Inversion of Control is\nsomething that requires C++,\nso I&#8217;m going to leave that aside.\n<\/p>\n<p>\nSecond of all, who says the shell is written in C++?\nAs it happens, when <code>IShell&shy;Folder<\/code> was introduced\nin Windows&nbsp;95,\nthe entire shell was written in plain C.\nThat&#8217;s right, plain C.\nVtables were built up by hand,\nmethod inheritance was implemented by direct replacement in the\nvtable,\nmethod overrides were implemented by function chaining,\nmultiple inheritance was implemented by manually moving the pointer\naround.\n<\/p>\n<pre>\nconst IShellFolderVtbl c_vtblMyComputerSF =\n{\n MyComputer_QueryInterfaceSF,\n MyComputer_AddRefSF,\n MyComputer_ReleaseSF,\n MyComputer_ParseDisplayName,\n ... you get the idea ...\n};\nconst IPersistFolderVtbl c_vtblMyComputerPF =\n{\n MyComputer_QueryInterfacePF,\n MyComputer_AddRefPF,\n MyComputer_ReleasePF,\n MyComputer_Initialize,\n};\nstruct MyComputer {\n IShellFolder sf;\n IShellFolder pf;\n ULONG cRef;\n ... other member variables go here ...\n}\nMyComputer *MyComputer_New()\n{\n MyComputer *self = malloc(sizeof(MyComputer));\n if (self) {\n  self-&gt;sf.lpVtbl = &amp;c_vtblMyComputerSF;\n  self-&gt;pf.lpVtbl = &amp;c_vtblMyComputerPF;\n  self-&gt;cRef = 1;\n  ... other \"constructor\" operations go here ...\n }\n return self;\n}\n\/\/ sample cast\nMyComputer *pThis;\nIPersistFolder *ppf =  &amp;pThis-&gt;pf;\n\/\/ sample method call\nhr = IShellFolder_CompareIDs(psf, lParam, pidl1, pidl2);\n\/\/ which expands to\nhr = psf-&gt;lpVtbl-&gt;CompareIDs(psf, lParam, pidl1, pidl2);\n\/\/ sample forwarder for multiply-derived method\nHRESULT STDCALL MyComputer_QueryInterfacePF(\n    IPersistFolder *selfPF, REFIID riid, void **ppv)\n{\n MyComputer *self = CONTAINING_RECORD(selfPF, MyComputer, pf);\n return MyComputer_QueryInterfaceSF(&amp;self-&gt;sf, riid, ppv);\n}\n<\/pre>\n<p>\nSo one good reason why the shell didn&#8217;t export its C++ base classes\nwas that <i>it didn&#8217;t have any C++ base classes<\/i>.\n<\/p>\n<p>\nWhy choose C over C++?\nWell, at the time the Windows&nbsp;95 project started,\nC++ was still a relatively new language for systems programming.\nWhile there were certainly people on the shell team capable of\nwriting code in C++,\nthe old-timers grew up with C as their native language,\nand the newcomers weren&#8217;t taught C++ in their computer science classes.\n(Computer science departments still taught primarily C or Pascal,\nwith maybe some Lisp if you took an AI class.)\nAlso, the C++ compilers of the day did not provide fine control\nover automatic code generation,&sup1; and since\n<a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2003\/10\/10\/55256.aspx\">\neven saving 4KB of memory had a perceptible impact on overall\nsystem performance<\/a>,\nmanually grouping rarely-used functions into the same region of memory\nof memory (so they could all remain paged out)\nwas still a common practice.\n<\/p>\n<p>\nBut even if the shell was originally written in C++,\nexporting the base classes wouldn&#8217;t have been a good idea.\nCOM is a language-neutral platform.\nPeople have written COM objects in C, C++,\nVisual Basic, C#, Delphi, you-name-it.\nIf <code>IShell&shy;Folder<\/code> were an exported C++ base class,\nthen you have effectively said,\n&#8220;Sorry, only C++ code can implement <code>IShell&shy;Folder<\/code>.\nScrew off, all you other languages!&#8221;\n<\/p>\n<p>\nBut wait, it&#8217;s worse than just that.\nExporting a C++ base class ties you to a specific compiler vendor,\nbecause\n<a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2006\/07\/27\/679634.aspx\">\nname decoration is not standardized<\/a>.\nSo it&#8217;s not just &#8220;To implement <code>IShell&shy;Folder<\/code> you must\nuse C++&#8221;\nbut\n&#8220;To implement <code>IShell&shy;Folder<\/code> you must use\nthe Microsoft Visual Studio C++ compiler.&#8221;\n<\/p>\n<p>\nBut wait, it&#8217;s worse than just that.\nThe name decoration algorithm can even change between compiler\nversions.\nFurthermore,\nthe mechanism by which exceptions are thrown and caught is not merely\ncompiler-specific but compiler-<i>version<\/i> specific.\nIf an exception is thrown by code compiled by one version of the\nC++ compiler and reaches code compiled by a different version of\nthe C++ compiler, the results are undefined.\n(For example, the older version of the C++ compiler may not have\nsupported RTTI.)\nSo it&#8217;s not just &#8220;To implement <code>IShell&shy;Folder<\/code> you must\nuse C++&#8221;\nbut\n&#8220;To implement <code>IShell&shy;Folder<\/code> you must use\nMicrosoft Visual C++ 2.0.&#8221;\n(So maybe\n<a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2010\/06\/07\/10020654.aspx\">\nBjarne was right after all<\/a>.)\n<\/p>\n<p>\nBut wait, it&#8217;s worse than just that.\nExporting a C++ base class means that the base class\n<i>can never change<\/i>,\nbecause various properties of the base class become hard-coded\ninto the derived classes.\nThe list of interfaces implemented by the base class becomes fixed.\nThe size of the base class is fixed.\nAny inline methods are fixed.\nThe precise layout of member variables is fixed.\nExporting a C++ base class for <code>IShell&shy;Folder<\/code> would have\nmeant that the base class could never change.\nYou want support for <code>IShell&shy;Folder2<\/code>?\nSorry, we can&#8217;t add that without breaking everybody who compiled\nwith the old header file.\n<\/p>\n<p>\n<b>Exercise<\/b>: If exporting base classes is so horrible,\nwhy does the CLR do it all over the place?\n<\/p>\n<p>\n<b>Footnote<\/b>\n<\/p>\n<p>\n&sup1; Actually, I don&#8217;t think even the C++ compilers of <i>today<\/i>\ngive you fine control over automatic code generation,\nwhich is why\n<a HREF=\"http:\/\/www.microsoft.com\/whdc\/driver\/kernel\/KMcode.mspx\">\nMicrosoft takes a conservative position on use of C++ in kernel mode<\/a>,\nwhere the consequences of a poorly-timed page fault are much worse\nthan simply poor performance.\nIt will bluescreen your machine.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>ton suggested that since the shell is written in C++, IShell&shy;Folder should have been an abstract class, and then it could have used techniques like exceptions and Inversion of Control. Okay, first of all, I&#8217;m not sure how Inversion of Control is something that requires C++, so I&#8217;m going to leave that aside. Second of [&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":[26],"class_list":["post-9303","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-other"],"acf":[],"blog_post_summary":"<p>ton suggested that since the shell is written in C++, IShell&shy;Folder should have been an abstract class, and then it could have used techniques like exceptions and Inversion of Control. Okay, first of all, I&#8217;m not sure how Inversion of Control is something that requires C++, so I&#8217;m going to leave that aside. Second of [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/9303","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=9303"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/9303\/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=9303"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=9303"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=9303"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}