{"id":111589,"date":"2025-09-12T07:00:00","date_gmt":"2025-09-12T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=111589"},"modified":"2025-09-12T06:42:06","modified_gmt":"2025-09-12T13:42:06","slug":"20250912-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20250912-00\/?p=111589","title":{"rendered":"How can I convert a third party in-process server so it runs in the COM surrogate?"},"content":{"rendered":"<p>A customer had an app with a plugin model based on vendor-supplied COM objects that are loaded into the process via <code>Co\u00adCreate\u00adInstance<\/code>.<\/p>\n<table style=\"border-collapse: collapse; text-align: center;\" border=\"0\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<td>Main process<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px currentcolor; vertical-align: middle;\">Engine<br \/>\n\u2193<br \/>\nPlugin<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>These COM objects run in-process, but the customer realized that these plugins were a potential source of instability, and they saw that you can <a title=\"What does the COM Surrogate do and why does it always stop working?\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20090212-00\/?p=19173\"> instruct COM to load the plugin into a sacrificial process<\/a> so that if the plugin crashes, the main program is unaffected.<\/p>\n<p>What they want is something like this:<\/p>\n<table style=\"border-collapse: collapse; text-align: center;\" border=\"0\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<td>Main process<\/td>\n<td>&nbsp;<\/td>\n<td>Surrogate process<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px currentcolor; padding: 1em 0;\">Engine<\/td>\n<td style=\"vertical-align: middle; padding: 1em 0;\">\u2192<\/td>\n<td style=\"border: solid 1px currentcolor; padding: 1em 0;\">Plugin<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>But how do you opt a third-party component into the COM Surrogate? The third party component comes with its own registration, and it would be rude to <a title=\"Registering the DLL Server for Surrogate Activation\" href=\"https:\/\/learn.microsoft.com\/en-us\/windows\/win32\/com\/registering-the-dll-server-for-surrogate-activation\"> alter that registration so that it runs in a COM Surrogate<\/a>. Besides, a COM Surrogate requires an AppId, and the plugin might not have one.<\/p>\n<p>The answer is simple: Create your own object that is registered to run in the COM Surrogate. Define an interface for that custom object, say, <code>ISurrogateHost<\/code> and give that interface a method like <code>LoadPlugin(REFCLSID pluginClsid, REFIID iid, void** result)<\/code> which calls <code>Co\u00adCreate\u00adInstance<\/code> on the plugin CLSID and requests the specified interface pointer from it. (If you want to support aggregation, you can add a <code>punkOuter<\/code> parameter.)<\/p>\n<table style=\"border-collapse: collapse; text-align: center;\" border=\"0\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<td>Main process<\/td>\n<td>&nbsp;<\/td>\n<td>Surrogate process<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px currentcolor; vertical-align: middle;\">Engine<br \/>\n\u00a0<br \/>\n\u00a0<\/td>\n<td style=\"vertical-align: middle;\">\u2192<br \/>\n\u00a0<br \/>\n\u00a0<\/td>\n<td style=\"border: solid 1px currentcolor; vertical-align: middle;\">Host<br \/>\n\u2193<br \/>\nPlugin<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>The <code>Load\u00adPlugin<\/code> method runs inside the surrogate, so when the plugin loads in-process, it loads into the surrogate process.<\/p>\n<p>The host can return a reference to the plugin directly to the main app engine, so it steps out of the way once the two sides are connected. The purpose of the host is to set up a new process.<\/p>\n<p>In fact, you don&#8217;t even need to invent that special surrogate interface. There is already a standard COM interface that does this already: <a href=\"https:\/\/learn.microsoft.com\/en-us\/windows\/win32\/api\/propsys\/nn-propsys-icreateobject\"> <code>ICreateObject<\/code><\/a>. It has a single method, uncreatively named <code>CreateObject<\/code> that takes exactly the parameters we want, including the <code>punkOuter<\/code>.<\/p>\n<p>Your surrogate host object would go like this, using (rolls dice) the WRL template library.<\/p>\n<pre>struct SurrogateHost :\r\n    Microsoft::WRL::RuntimeClass&lt;\r\n        Microsoft::WRL::RuntimeClassFlags&lt;\r\n            Microsoft::WRL::RuntimeClassType::ClassicCom |\r\n            Microsoft::WRL::RuntimeClassType::InhibitWeakReference&gt;,\r\n    ICreateObject, Microsoft::WRL::FtmBase&gt;\r\n{\r\n    STDMETHOD(CreateObject)(\r\n        REFCLSID clsid,\r\n        IUnknown* outer,\r\n        REFIID iid,\r\n        void** result)\r\n    {\r\n        return CoCreateInstance(clsid, outer,\r\n            CLSCTX_INPROC_SERVER, riid, result);\r\n    }\r\n};\r\n<\/pre>\n<p>In the engine, where you would normally do<\/p>\n<pre>hr = CoCreateInstance(pluginClsid, outer, CLSCTX_INPROC_SERVER,\r\n        riid, result);\r\n<\/pre>\n<p>you instead create a surrogate host:<\/p>\n<pre>Microsoft::WRL::ComPtr&lt;ICreateObject&gt; host;\r\nhr = CoCreateInstance(CLSID_SurrogateHost, nullptr,\r\n        CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&amp;host));\r\n<\/pre>\n<p>and then each time you need an object, you ask the surrogate host to do it:<\/p>\n<pre>hr = host-&gt;CreateObject(pluginClsid, outer, riid, result);\r\n<\/pre>\n<p>You can even get fancy and decide that some plugins are sus and should run in a surrogate, whereas others are trustworthy and may run inside the main process.<\/p>\n<pre>if (is_trustworthy(pluginClsid)) {\r\n    \/\/ Let this one load into the main process\r\n    hr = CoCreateInstance(pluginClsid, outer, CLSCTX_INPROC_SERVER,\r\n            riid, result);\r\n} else {\r\n    \/\/ Boot this one to the surrogate process\r\n    hr = host-&gt;CreateObject(pluginClsid, outer, riid, result);\r\n}\r\n<\/pre>\n<p>Reusing the <code>host<\/code> object means that a single surrogate process is used for all plugins. If you want each plugin running in a separate surrogate, then create a separate host for each one.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>You can put your own object in the surrogate first.<\/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-111589","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>You can put your own object in the surrogate first.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/111589","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=111589"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/111589\/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=111589"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=111589"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=111589"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}