{"id":112313,"date":"2026-05-11T07:00:00","date_gmt":"2026-05-11T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=112313"},"modified":"2026-05-11T08:48:20","modified_gmt":"2026-05-11T15:48:20","slug":"20260511-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20260511-00\/?p=112313","title":{"rendered":"Additional notes on controlling which handles are inherited by <CODE>Create&shy;Process<\/CODE>"},"content":{"rendered":"<p>Some time ago, I wrote about <a title=\"Programmatically controlling which handles are inherited by new processes in Win32\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20111216-00\/?p=8873\"> programmatically controlling which handles are inherited by new processes in Win32<\/a> by using the <code>PROC_<wbr \/>THREAD_<wbr \/>ATTRIBUTE_<wbr \/>HANDLE_<wbr \/>LIST<\/code> to limit exactly which handles are inherited. That way, when you create a new process, you have precise control over which handles get inherited and don&#8217;t accidentally inherit handles created by unrelated components in your process.<\/p>\n<p>A collegue of mine pointed out that you still have the reverse problem: Since handles must be marked as inheritable for them to participate in <code>PROC_<wbr \/>THREAD_<wbr \/>ATTRIBUTE_<wbr \/>HANDLE_<wbr \/>LIST<\/code>, if another thread calls <code>Create\u00adProcess<\/code> with <code>bInheritHandles<\/code> = <code>TRUE<\/code> but without using <code>PROC_<wbr \/>THREAD_<wbr \/>ATTRIBUTE_<wbr \/>HANDLE_<wbr \/>LIST<\/code>, then they will accidentally inherit all of <i>your<\/i> handles.<\/p>\n<p>This problem could have been avoided if the <code>PROC_<wbr \/>THREAD_<wbr \/>ATTRIBUTE_<wbr \/>HANDLE_<wbr \/>LIST<\/code> allowed you to include non-inheritable handles, in which case they would be non-inheritable by normal <code>Create\u00adProcess<\/code> but inheritable if explicitly opted back in. But alas, that&#8217;s not how it was designed.<\/p>\n<p>Instead, you can create a helper process. All this helper process does is wait for the main process to exit, and then exit itself.<\/p>\n<pre>WaitForSingleObject(hMainProcess, INFINITE);\r\nExitProcess(0);\r\n<\/pre>\n<p>This process doesn&#8217;t sound like it&#8217;s doing anything useful, and it&#8217;s not. But what makes it useful is not what it&#8217;s doing but rather what is done <i>to<\/i> it.<\/p>\n<p>The components in the main process create their handles as non-inheritable. When they wants to create a process with specific inherited handles, they duplicate the desired handles into the helper process (as inheritable), and then build a <code>PROC_<wbr \/>THREAD_<wbr \/>ATTRIBUTE_<wbr \/>HANDLE_<wbr \/>LIST<\/code> that lists those duplicates as handles to inherit. They also use the <code>PROC_<wbr \/>THREAD_<wbr \/>ATTRIBUTE_<wbr \/>PARENT_<wbr \/>PROCESS<\/code> to specify that the <i>helper<\/i> process is the parent process that the handles should be inherited from. Then they pass those thread attributes to <code>Create\u00adProcess<\/code>, and the new process will inherit exactly those handles. Then they clean up by closing the handles in the helper process with the help of <code>Duplicate\u00adHandle<\/code> and <code>DUPLICATE_<wbr \/>CLOSE_<wbr \/>SOURCE<\/code>.<\/p>\n<p>Notice that multiple threads can simultaneously be operating on the helper process in this way, so you need only one helper process to service all your handle-inheritance-control needs.<\/p>\n<p>This avoids the accidental inheritance problem because the handles that belong to the components in the main process are still marked non-inheritable, so any other code in the main process that does a <code>Create\u00adProcess<\/code> will not inherit them.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Putting the handles in a private container.<\/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-112313","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Putting the handles in a private container.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112313","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=112313"}],"version-history":[{"count":1,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112313\/revisions"}],"predecessor-version":[{"id":112314,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112313\/revisions\/112314"}],"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=112313"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=112313"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=112313"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}