{"id":8873,"date":"2011-12-16T07:00:00","date_gmt":"2011-12-16T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2011\/12\/16\/programmatically-controlling-which-handles-are-inherited-by-new-processes-in-win32\/"},"modified":"2026-05-12T11:13:11","modified_gmt":"2026-05-12T18:13:11","slug":"20111216-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20111216-00\/?p=8873","title":{"rendered":"Programmatically controlling which handles are inherited by new processes in Win32"},"content":{"rendered":"<p>In unix, file descriptors are inherited by child processes by default. This wasn&#8217;t so much an active decision as it was a consequence of the fork\/exec model. To exclude a file descriptor from being inherited by children, you set the <code>FD_<wbr \/>CLO\u00adEXEC<\/code> flag on the file descriptor.<\/p>\n<p>Win32 sort of works like that, but backwards, and maybe a little upside-down. <a href=\"https:\/\/web.archive.org\/web\/20110711093126\/http:\/\/www.gingerrogers.com\/about\/quotes.html\"> And in high heels<\/a>.<\/p>\n<p>In Win32, handles default to <i>not inherited<\/i>. Ways to make a handle inherited during <code>Create\u00adProcess<\/code> have grown during the evolution of Win32.<\/p>\n<p>As far as I can tell, back in the old days, inheritability of handles was established at handle creation time. For most handle creation functions, you do this by passing a <code>SECURITY_<wbr \/>ATTRIBUTES<\/code> structure with <code>bInherit\u00adHandle<\/code> set to <code>TRUE<\/code>. Functions which created handles from existing objects don&#8217;t have a <code>SECURITY_<wbr \/>ATTRIBUTES<\/code> parameter, so they instead have an explicit <code>bInherit\u00adHandle<\/code> parameter. (For examples, see <code>Open\u00adEvent<\/code> and <code>Duplicate\u00adHandle<\/code>.)<\/p>\n<p>But just marking a handle as inheritable isn&#8217;t good enough to get it inherited. You also have to pass <code>TRUE<\/code> as the <code>bInherit\u00adHandles<\/code> parameter to <code>Create\u00adProcess<\/code>. A handle will be inherited only if if the <code>bInherit\u00adHandles<\/code> parameter is <code>TRUE<\/code> <u>and<\/u> the handle is marked as inheritable. Miss either of those steps, and you don&#8217;t get your inheritance. (To make sure you get your inheritance IRL, be nice to your grandmother.)<\/p>\n<p>In Windows\u00a02000, Win32 gained the ability to alter the inheritability of a handle after it is created. The <code>Set\u00adHandle\u00adInformation<\/code> function lets you turn the <code>HANDLE_<wbr \/>FLAG_<wbr \/>INHERIT<\/code> flag on and off on a handle.<\/p>\n<p>But all this inheritability fiddling still had a fatal flaw: What if two threads within the same process both call <code>Create\u00adProcess<\/code> but disagree on which handles they want to be inherited? For example, suppose you have a function <code>Create\u00adProcess\u00adWith\u00adShared\u00adMemory<\/code> whose job it is to launch a process, passing it <a title=\"How do I pass a lot of data to a process when it starts up?\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20031211-00\/?p=41543\"> a custom-made shared memory block<\/a>. Suppose two threads run this function simultaneously.<\/p>\n<table cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th>A<\/th>\n<td style=\"border-left: .75pt solid black; width: .75pt;\" rowspan=\"5\">\u00a0<\/td>\n<th>B<\/th>\n<\/tr>\n<tr>\n<td>CreateFileMapping(inheritable=TRUE)<\/td>\n<td>CreateFileMapping(inheritable=TRUE)<\/td>\n<\/tr>\n<tr>\n<td>returns handle H1<\/td>\n<td>returns handle H2<\/td>\n<\/tr>\n<tr>\n<td>CreateProcess(&#8220;A&#8221;, bInheritHandles=TRUE)<\/td>\n<td>CreateProcess(&#8220;B&#8221;, bInheritHandles=TRUE)<\/td>\n<\/tr>\n<tr>\n<td>CloseHandle(H1)<\/td>\n<td>CloseHandle(H2)<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>What just happened? Since inheritability is a property of the handle, processes A\u00a0and\u00a0B inherited <i>both<\/i> handles H1\u00a0and\u00a0H2, even though what we wanted was for process\u00a0A to inherit handle\u00a0H1 and for process\u00a0B to inherit handle\u00a0H2.<\/p>\n<p>For a long time, the answer to this problem was the unsatisfactory &#8220;You&#8217;ll just have to serialize your calls to <code>Create\u00adProcess\u00adWith\u00adShared\u00adMemory<\/code> so that thread\u00a0B won&#8217;t accidentally cause a handle from thread\u00a0A to be inherited by process\u00a0B.&#8221; Actually, the answer was even worse. You had to serialize all functions that created inheritable handles from the time the handle was created, through the call to <code>Create\u00adProcess<\/code>, and waiting until after all those inheritable handles were made no longer inheritable.<\/p>\n<p>This was a serious problem since who knows what other parts of your program are going to call <code>Create\u00adProcess<\/code> with <code>bInherit\u00adHandles<\/code> set to <code>TRUE<\/code>? Sure you can control the calls that your own code made, but what about calls from plug-ins or other unknown components? (This is <a title=\"The old-fashioned theory on how processes exit\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20070502-00\/?p=27023\"> another case<\/a> of <a title=\"Looking at the world through kernel-colored glasses\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20110512-00\/?p=10683\"> kernel-colored glasses<\/a>.)<\/p>\n<p>Windows\u00a0Vista addresses this problem by allowing you to pass an explicit list of handles you want the <code>bInherit\u00adHandles<\/code> parameter to apply to. (If you pass an explicit list, then you must pass <code>TRUE<\/code> for <code>bInherit\u00adHandles<\/code>.) And as before, for a handle to be inherited, it must be also be marked as inheritable.<\/p>\n<p>Passing the list of handles you want to inherit is a multi-step affair. Let&#8217;s walk through it:<\/p>\n<pre>BOOL CreateProcessWithExplicitHandles(\r\n  __in_opt     LPCTSTR lpApplicationName,\r\n  __inout_opt  LPTSTR lpCommandLine,\r\n  __in_opt     LPSECURITY_ATTRIBUTES lpProcessAttributes,\r\n  __in_opt     LPSECURITY_ATTRIBUTES lpThreadAttributes,\r\n  __in         BOOL bInheritHandles,\r\n  __in         DWORD dwCreationFlags,\r\n  __in_opt     LPVOID lpEnvironment,\r\n  __in_opt     LPCTSTR lpCurrentDirectory,\r\n  __in         LPSTARTUPINFO lpStartupInfo,\r\n  __out        LPPROCESS_INFORMATION lpProcessInformation,\r\n    \/\/ here is the new stuff\r\n  __in         DWORD cHandlesToInherit,\r\n  __in_ecount(cHandlesToInherit) HANDLE *rgHandlesToInherit)\r\n{\r\n BOOL fSuccess;\r\n BOOL fInitialized = FALSE;\r\n SIZE_T size = 0;\r\n LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList = NULL;\r\n\r\n fSuccess = cHandlesToInherit &lt; 0xFFFFFFFF \/ sizeof(HANDLE) &amp;&amp;\r\n            lpStartupInfo-&gt;cb == sizeof(*lpStartupInfo);\r\n if (!fSuccess) {\r\n  SetLastError(ERROR_INVALID_PARAMETER);\r\n }\r\n if (fSuccess) {\r\n  fSuccess = InitializeProcThreadAttributeList(NULL, 1, 0, &amp;size) ||\r\n             GetLastError() == ERROR_INSUFFICIENT_BUFFER;\r\n }\r\n if (fSuccess) {\r\n  lpAttributeList = reinterpret_cast&lt;LPPROC_THREAD_ATTRIBUTE_LIST&gt;\r\n                                (HeapAlloc(GetProcessHeap(), 0, size));\r\n  fSuccess = lpAttributeList != NULL;\r\n }\r\n if (fSuccess) {\r\n  fSuccess = InitializeProcThreadAttributeList(lpAttributeList,\r\n                    1, 0, &amp;size);\r\n }\r\n if (fSuccess) {\r\n  fInitialized = TRUE;\r\n  fSuccess = UpdateProcThreadAttribute(lpAttributeList,\r\n                    0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST,\r\n                    rgHandlesToInherit,\r\n                    cHandlesToInherit * sizeof(HANDLE), NULL, NULL);\r\n }\r\n if (fSuccess) {\r\n  STARTUPINFOEX info;\r\n  <a title=\"Why do Microsoft code samples tend to use ZeroMemory instead of { 0 }?\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20050628-07\/?p=35183\">ZeroMemory<\/a>(&amp;info, sizeof(info));\r\n  info.StartupInfo = *lpStartupInfo;\r\n  info.StartupInfo.cb = sizeof(info);\r\n  info.lpAttributeList = lpAttributeList;\r\n  fSuccess = CreateProcess(lpApplicationName,\r\n                           lpCommandLine,\r\n                           lpProcessAttributes,\r\n                           lpThreadAttributes,\r\n                           bInheritHandles,\r\n                           dwCreationFlags | EXTENDED_STARTUPINFO_PRESENT,\r\n                           lpEnvironment,\r\n                           lpCurrentDirectory,\r\n                           &amp;info.StartupInfo,\r\n                           lpProcessInformation);\r\n }\r\n \r\n if (fInitialized) DeleteProcThreadAttributeList(lpAttributeList);\r\n if (lpAttributeList) HeapFree(GetProcessHeap(), 0, lpAttributeList);\r\n return fSuccess;\r\n}\r\n<\/pre>\n<p>After some initial sanity checks, we start doing real work.<\/p>\n<p>Initializing a <code>PROC_<wbr \/>THREAD_<wbr \/>ATTRIBUTE_<wbr \/>LIST<\/code> is a two-step affair. First you call <code>Initialize\u00adProc\u00adThread\u00adAttribute\u00adList<\/code> with a <code>NULL<\/code> attribute list in order to determine how much memory you need to allocate for a one-entry attribute list. After allocating the memory, you call <code>Initialize\u00adProc\u00adThread\u00adAttribute\u00adList<\/code> a second time to do the actual initialization.<\/p>\n<p>After creating the attribute list, you set the one entry by calling <code>Update\u00adProc\u00adThread\u00adAttribute\u00adList<\/code>.<\/p>\n<p>And then it&#8217;s off to the races. Put that attribute list in a <code>STARTUP\u00adINFO\u00adEX<\/code> structure, set the <code>EXTENDED_<wbr \/>STARTUPINFO_<wbr \/>PRESENT<\/code> flag, and hand everything off to <code>Create\u00adProcess<\/code>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Sort of like unix, but the other way around.<\/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-8873","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Sort of like unix, but the other way around.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/8873","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=8873"}],"version-history":[{"count":1,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/8873\/revisions"}],"predecessor-version":[{"id":112317,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/8873\/revisions\/112317"}],"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=8873"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=8873"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=8873"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}