{"id":41343,"date":"2003-12-26T08:39:00","date_gmt":"2003-12-26T08:39:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2003\/12\/26\/you-can-read-a-contract-from-the-other-side\/"},"modified":"2003-12-26T08:39:00","modified_gmt":"2003-12-26T08:39:00","slug":"you-can-read-a-contract-from-the-other-side","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20031226-00\/?p=41343","title":{"rendered":"You can read a contract from the other side"},"content":{"rendered":"<p>\nAn interface is a contract,\nbut remember that a contract applies to both parties.\nMost of the time, when you read an interface,\nyou look at it from the point of view of the client side of the contract,\nbut often it helps to read it from the server side.\n<\/p>\n<p>\nFor example, let&#8217;s look at\n<a HREF=\"http:\/\/msdn.microsoft.com\/library\/en-us\/shellcc\/platform\/shell\/programmersguide\/shell_adv\/conpanel.asp\">the\ninterface for control panel applications<\/a>.\n<\/p>\n<p>\nMost of the time, when you&#8217;re reading this documentation,\nyou are wearing your &#8220;I am writing a Control Panel application&#8221; hat.\nSo, for example, the documentation says\n<\/p>\n<blockquote CLASS=\"q\"><p>\nWhen the controlling application first loads the Control Panel application,\nit retrieves the address of the <b>CPlApplet<\/b> function and\nsubsequently uses the address to call the function and pass it messages.\n<\/p><\/blockquote>\n<p>\nWith your &#8220;I am writing a Control Panel application&#8221; hat,\nthis means\n&#8220;Gosh, I had better have a function called CPlApplet\nand export it so I can receive messages.&#8221;\n<\/p>\n<p>\nBut if you are instead wearing your\n&#8220;I am hosting a Control Panel application&#8221; hat,\nthis means,\n&#8220;Gosh, I had better call GetProcAddress()\nto get the address of the application&#8217;s CPlApplet function\nso I can send it messages.&#8221;\n<\/p>\n<p>\nSimilarly, under the &#8220;Message Processing&#8221; section\nit lists the messages that are sent from the controlling application\nto the Control Panel application.\nIf you are wearing your\n&#8220;I am writing a Control Panel application&#8221; hat,\nthis means\n&#8220;Gosh, I had better be ready to receive these messages in this order.&#8221;\nBut if you are wearing your &#8220;I am hosting a Control Panel application&#8221; hat,\nthis means &#8220;Gosh, I had better send these messages in the order listed.&#8221;\n<\/p>\n<p>\nAnd finally, when it says\n&#8220;the controlling application release the Control Panel application\nby calling the FreeLibrary function,&#8221;\nyour &#8220;I am writing a Control Panel application&#8221; hat says\n&#8220;I had better be prepared to be unloaded,&#8221;\nwhereas your &#8220;I am hosting a Control Panel application&#8221; hat says,\n&#8220;This is where I unload the DLL.&#8221;\n<\/p>\n<p>\nSo let&#8217;s try it.\nAs always, start with our scratch program and change the WinMain:\n<\/p>\n<pre>\n<font COLOR=\"red\">#include &lt;cpl.h&gt;<\/font>\nint WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev,\n                   LPSTR lpCmdLine, int nShowCmd)\n{\n  HWND hwnd;\n  g_hinst = hinst;\n  if (!InitApp()) return 0;\n  if (SUCCEEDED(CoInitialize(NULL))) {\/* In case we use COM *\/\n      hwnd = CreateWindow(\n          \"Scratch\",                      \/* Class Name *\/\n          \"Scratch\",                      \/* Title *\/\n          WS_OVERLAPPEDWINDOW,            \/* Style *\/\n          CW_USEDEFAULT, CW_USEDEFAULT,   \/* Position *\/\n          CW_USEDEFAULT, CW_USEDEFAULT,   \/* Size *\/\n          NULL,                           \/* Parent *\/\n          NULL,                           \/* No menu *\/\n          hinst,                          \/* Instance *\/\n          0);                             \/* No special parameters *\/\n<font COLOR=\"red\">\n      if (hwnd) {\n        TCHAR szPath[MAX_PATH];\n        LPTSTR pszLast;\n        DWORD cch = SearchPath(NULL, TEXT(\"access.cpl\"),\n                     NULL, MAX_PATH, szPath, &amp;pszLast);\n        if (cch &gt; 0 &amp;&amp; cch &lt; MAX_PATH) {\n          RunControlPanel(hwnd, szPath);\n      }\n    }<\/font>\n    CoUninitialize();\n  }\n  return 0;\n}\n<\/pre>\n<p>\nInstead of showing the window and entering the message loop,\nwe start acting like a Control Panel host.\nOur victim today is access.cpl, the accessibility control panel.\nAfter locating the program on the path,\nwe ask RunControlPanel to do the heavy lifting:<\/p>\n<pre>\nvoid RunControlPanel(HWND hwnd, LPCTSTR pszPath)\n{\n<font COLOR=\"red\">  \/\/ Maybe this control panel application has a custom manifest\n  ACTCTX act = { 0 };\n  act.cbSize = sizeof(act);\n  act.dwFlags = 0;\n  act.lpSource = pszPath;\n  act.lpResourceName = MAKEINTRESOURCE(123);\n  HANDLE hctx = CreateActCtx(&amp;act);\n  ULONG_PTR ulCookie;\n  if (hctx == INVALID_HANDLE_VALUE ||\n      ActivateActCtx(hctx, &amp;ulCookie)) {<\/font>\n    HINSTANCE hinstCPL = LoadLibrary(pszPath);\n    if (hinstCPL) {\n      APPLET_PROC pfnCPlApplet = (APPLET_PROC)\n        GetProcAddress(hinstCPL, \"CPlApplet\");\n      if (pfnCPlApplet) {\n        if (pfnCPlApplet(hwnd, CPL_INIT, 0, 0)) {\n          int cApplets = pfnCPlApplet(hwnd, CPL_GETCOUNT, 0, 0);\n          \/\/  We're going to run application zero\n          \/\/  (In real life we might show the user a list of them\n          \/\/  and let them pick one)\n          if (cApplets &gt; 0) {\n            CPLINFO cpli;\n            pfnCPlApplet(hwnd, CPL_INQUIRE, 0, (LPARAM)&amp;cpli);\n            pfnCPlApplet(hwnd, CPL_DBLCLK, 0, cpli.lData);\n            pfnCPlApplet(hwnd, CPL_STOP, 0, cpli.lData);\n          }\n        }\n        pfnCPlApplet(hwnd, CPL_EXIT, 0, 0);\n      }\n      FreeLibrary(hinstCPL);\n    }\n<font COLOR=\"red\">\n    if (hctx != INVALID_HANDLE_VALUE) {\n      DeactivateActCtx(0, ulCookie);\n      ReleaseActCtx(hctx);\n    }<\/font>\n  }\n}\n<\/pre>\n<p>\nIgnore the red lines for now; we&#8217;ll discuss them later.\n<\/p>\n<p>\nAll we&#8217;re doing is following the specification\nbut reading it from the host side.\nSo we load the library, locate its entry point,\nand call it with CPL_INIT, then CPL_GETCOUNT.\nIf there are any control panel applications inside this CPL file,\nwe inquire after the first one,\ndouble-click it (this is where all the interesting stuff happens),\nthen stop it.\nAfter all that excitement,\nwe clean up according to the rules set out for the host\n(namely, by sending a CPL_EXIT message.)\n<\/p>\n<p>\nSo that&#8217;s all. Well, except for the red parts. What&#8217;s that about?\n<\/p>\n<p>The red parts are to support Control Panel applications\nthat have a custom manifest.\nThis is something new with Windows XP and is\n<a HREF=\"http:\/\/msdn.microsoft.com\/library\/en-us\/shellcc\/platform\/commctls\/userex\/cookbook.asp\">documented\nin MSDN here<\/a>.\n<\/p>\n<p>\nIf you go down to the\n&#8220;Using ComCtl32 Version 6 in Control Panel or a DLL That Is Run\nby RunDll32.exe&#8221; section,\nyou&#8217;ll see that the application provides its manifest\nto the Control Panel host by attaching it as resource number 123.\nSo that&#8217;s what the red code does:\nIt loads and activates the manifest,\nthen invites the Control Panel application to do its thing\n(with its manifest active), then cleans up.\nIf there is no manifest, CreateActCtx will return INVALID_HANDLE_VALUE.\nWe do not treat that as an error,\nsince many programs don&#8217;t yet provide a manifest.\n<\/p>\n<p><b>Exercise<\/b>: What are the security implications of passing NULL\nas the first parameter to SearchPath?<\/p>\n","protected":false},"excerpt":{"rendered":"<p>An interface is a contract, but remember that a contract applies to both parties. Most of the time, when you read an interface, you look at it from the point of view of the client side of the contract, but often it helps to read it from the server side. For example, let&#8217;s look at [&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-41343","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>An interface is a contract, but remember that a contract applies to both parties. Most of the time, when you read an interface, you look at it from the point of view of the client side of the contract, but often it helps to read it from the server side. For example, let&#8217;s look at [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/41343","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=41343"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/41343\/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=41343"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=41343"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=41343"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}