{"id":4543,"date":"2013-04-26T07:00:00","date_gmt":"2013-04-26T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2013\/04\/26\/another-way-to-create-a-process-with-attributes-maybe-worse-maybe-better\/"},"modified":"2013-04-26T07:00:00","modified_gmt":"2013-04-26T07:00:00","slug":"another-way-to-create-a-process-with-attributes-maybe-worse-maybe-better","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20130426-00\/?p=4543","title":{"rendered":"Another way to create a process with attributes, maybe worse maybe better"},"content":{"rendered":"<p>Adam Rosenfield noted that\n&#8220;<A HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2011\/12\/16\/10248328.aspx#10248973\">those sure are a lot of hoops you have to jump through to solve\nthis unusual problem<\/A>&#8221;\nof specifying which handles are inherited by a new process.\n<\/P>\n<P>\nWell, first of all,\nwhat&#8217;s so wrong with that?\nYou have to jump through a lot of hoops when you are in an unusual\nsituation.\nBut by definition, most people are not in an unusual situation,\nso it&#8217;s an instance of the\n<I>Pay for Play<\/I> principle:\nThe simple case should be easy,\nand it&#8217;s okay for the complicated case to be hard.\n(It&#8217;s usually difficult to make the complicated case easy;\nthat&#8217;s why it&#8217;s called the complicated case.)\n<\/P>\n<P>\nThe complexity mostly comes from managing the general-purpose\n<CODE>PROC_THREAD_ATTRIBUTE_LIST<\/CODE>,\nwhich is used for things other than just controlling inherited handles.\nIt&#8217;s a generic way of passing up to <I>N<\/I> additional parameters to\n<CODE>Create&shy;Process<\/CODE> without having to create\n2&#x1D3A; different variations of\n<CODE>Create&shy;Process<\/CODE>.\n<\/P>\n<P>\nThe\n<CODE>Create&shy;Process&shy;With&shy;Explicit&shy;Handles<\/CODE>\nfunction was just one of the <I>N<\/I> special-purpose functions\nthat the\n<CODE>PROC_THREAD_ATTRIBUTE_LIST<\/CODE>\ntried to avoid having to create.\nAnd the special-purpose function naturally takes the special-purpose\ncase and applies the general solution to it.\nIt&#8217;s complicated because you are now doing something complicated.\n<\/P>\n<P>\nThat said, here&#8217;s one attempt to make it less complicated:\nBy putting all the complicated stuff closer to the complicated\nfunction:\n<\/P>\n<PRE>\n<FONT COLOR=\"blue\">typedef struct PROCTHREADATTRIBUTE {\n DWORD_PTR Attribute;\n PVOID lpValue;\n SIZE_T cbSize;\n} PROCTHREADATTRIBUTE;<\/FONT><\/p>\n<p>BOOL CreateProcessWith<FONT COLOR=\"blue\">Attributes<\/FONT>(\n  __in_opt     LPCTSTR lpApplicationName,\n  __inout_opt  LPTSTR lpCommandLine,\n  __in_opt     LPSECURITY_ATTRIBUTES lpProcessAttributes,\n  __in_opt     LPSECURITY_ATTRIBUTES lpThreadAttributes,\n  __in         BOOL bInheritHandles,\n  __in         DWORD dwCreationFlags,\n  __in_opt     LPVOID lpEnvironment,\n  __in_opt     LPCTSTR lpCurrentDirectory,\n  __in         LPSTARTUPINFO lpStartupInfo,\n  __out        LPPROCESS_INFORMATION lpProcessInformation,\n    \/\/ here is the new stuff\n    <FONT COLOR=\"blue\">__in       DWORD cAttributes,\n    __in_ecount(cAttributes) const PROCTHREADATTRIBUTE rgAttributes[]<\/FONT>)\n{\n BOOL fSuccess;\n BOOL fInitialized = FALSE;\n SIZE_T size = 0;\n LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList = NULL;<\/p>\n<p> fSuccess = InitializeProcThreadAttributeList(NULL, <FONT COLOR=\"blue\">cAttributes<\/FONT>, 0, &amp;size) ||\n            GetLastError() == ERROR_INSUFFICIENT_BUFFER;<\/p>\n<p> if (fSuccess) {\n  lpAttributeList = reinterpret_cast&lt;LPPROC_THREAD_ATTRIBUTE_LIST&gt;\n                                (HeapAlloc(GetProcessHeap(), 0, size));\n  fSuccess = lpAttributeList != NULL;\n }\n if (fSuccess) {\n  fSuccess = InitializeProcThreadAttributeList(lpAttributeList,\n                    <FONT COLOR=\"blue\">cAttributes<\/FONT>, 0, &amp;size);\n }\n if (fSuccess) {\n  fInitialized = TRUE;\n  <FONT COLOR=\"blue\">for (DWORD index = 0; index &lt; cAttributes &amp;&amp; fSuccess; index++) {<\/FONT>\n   fSuccess = UpdateProcThreadAttribute(lpAttributeList,\n                     0, <FONT COLOR=\"blue\">rgAttributes[index].Attribute,\n                     rgAttributes[index].lpValue,\n                     rgAttributes[index].cbSize<\/FONT>, NULL, NULL);\n  }\n }\n if (fSuccess) {\n  STARTUPINFOEX info;\n  ZeroMemory(&amp;info, sizeof(info));\n  info.StartupInfo = *lpStartupInfo;\n  info.StartupInfo.cb = sizeof(info);\n  info.lpAttributeList = lpAttributeList;\n  fSuccess = CreateProcess(lpApplicationName,\n                           lpCommandLine,\n                           lpProcessAttributes,\n                           lpThreadAttributes,\n                           bInheritHandles,\n                           dwCreationFlags | EXTENDED_STARTUPINFO_PRESENT,\n                           lpEnvironment,\n                           lpCurrentDirectory,\n                           &amp;info.StartupInfo,\n                           lpProcessInformation);\n }<\/p>\n<p> if (fInitialized) DeleteProcThreadAttributeList(lpAttributeList);\n if (lpAttributeList) HeapFree(GetProcessHeap(), 0, lpAttributeList);\n return fSuccess;\n}\n<\/PRE>\n<P>\nThere, now the complexity is there because you&#8217;re a generic\ncomplex function,\nso you have no reason to complain.\n<\/P>\n<P>\nA caller of this function might go like this:\n<\/P>\n<PRE>\n  HANDLE handles[2] = { handle1, handle2 };\n  const PROCTHREADATTRIBUTE attributes[] = {\n   {\n    PROC_THREAD_ATTRIBUTE_HANDLE_LIST,\n    handles,\n    sizeof(handles),\n   },\n  };<\/p>\n<p>  fSuccess = CreateProcessWithAttributes(\n                           lpApplicationName,\n                           lpCommandLine,\n                           lpProcessAttributes,\n                           lpThreadAttributes,\n                           bInheritHandles,\n                           dwCreationFlags,\n                           lpEnvironment,\n                           lpCurrentDirectory,\n                           lpStartupInfo,\n                           lpProcessInformation,\n                           ARRAYSIZE(attributes),\n                           attributes);\n<\/PRE>\n<P>\nAdam hates the &#8220;chained success&#8221; style and prefers the &#8220;goto&#8221; style;\non the other hand, other people hate gotos.\nSo to be fair, I will choose a coding style that nobody likes.\nThat way everybody is equally unhappy.\n<\/P>\n<PRE>\nBOOL CreateProcessWithAttributes(\n  __in_opt     LPCTSTR lpApplicationName,\n  __inout_opt  LPTSTR lpCommandLine,\n  __in_opt     LPSECURITY_ATTRIBUTES lpProcessAttributes,\n  __in_opt     LPSECURITY_ATTRIBUTES lpThreadAttributes,\n  __in         BOOL bInheritHandles,\n  __in         DWORD dwCreationFlags,\n  __in_opt     LPVOID lpEnvironment,\n  __in_opt     LPCTSTR lpCurrentDirectory,\n  __in         LPSTARTUPINFO lpStartupInfo,\n  __out        LPPROCESS_INFORMATION lpProcessInformation,\n    \/\/ here is the new stuff\n    __in       DWORD cAttributes,\n    __in_ecount(cAttributes) const PROCTHREADATTRIBUTE rgAttributes[])\n{\n BOOL fSuccess = FALSE;\n SIZE_T size = 0;<\/p>\n<p> if (InitializeProcThreadAttributeList(NULL, cAttributes, 0, &amp;size) ||\n     GetLastError() == ERROR_INSUFFICIENT_BUFFER) {\n  LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList =\n         reinterpret_cast&lt;LPPROC_THREAD_ATTRIBUTE_LIST&gt;\n                                (HeapAlloc(GetProcessHeap(), 0, size));\n  if (lpAttributeList != NULL) {\n   if (InitializeProcThreadAttributeList(lpAttributeList,\n                     cAttributes, 0, &amp;size)) {\n    DWORD index;\n    for (index = 0;\n         index &lt; cAttributes &amp;&amp;\n         UpdateProcThreadAttribute(lpAttributeList,\n                       0, rgAttributes[index].Attribute,\n                       rgAttributes[index].lpValue,\n                       rgAttributes[index].cbSize<\/FONT>, NULL, NULL);\n         index++) {\n    }\n    if (index &gt;= cAttributes) {\n     STARTUPINFOEX info;\n     ZeroMemory(&amp;info, sizeof(info));\n     info.StartupInfo = *lpStartupInfo;\n     info.StartupInfo.cb = sizeof(info);\n     info.lpAttributeList = lpAttributeList;\n     fSuccess = CreateProcess(\n                           lpApplicationName,\n                           lpCommandLine,\n                           lpProcessAttributes,\n                           lpThreadAttributes,\n                           bInheritHandles,\n                           dwCreationFlags | EXTENDED_STARTUPINFO_PRESENT,\n                           lpEnvironment,\n                           lpCurrentDirectory,\n                           &amp;info.StartupInfo,\n                           lpProcessInformation);\n    }\n    DeleteProcThreadAttributeList(lpAttributeList);\n   }\n   HeapFree(GetProcessHeap(), 0, lpAttributeList);\n  }\n }<\/p>\n<p> return fSuccess;\n}\n<\/PRE>\n<P>\nThose who are really adventuresome\ncould try a version of\n<CODE>Create&shy;Process&shy;With&shy;Attributes<\/CODE>\nthat uses varargs or\n<CODE>std::initializer_list<\/CODE>.\n<\/P><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Adam Rosenfield noted that &#8220;those sure are a lot of hoops you have to jump through to solve this unusual problem&#8221; of specifying which handles are inherited by a new process. Well, first of all, what&#8217;s so wrong with that? You have to jump through a lot of hoops when you are in an unusual [&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-4543","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Adam Rosenfield noted that &#8220;those sure are a lot of hoops you have to jump through to solve this unusual problem&#8221; of specifying which handles are inherited by a new process. Well, first of all, what&#8217;s so wrong with that? You have to jump through a lot of hoops when you are in an unusual [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/4543","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=4543"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/4543\/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=4543"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=4543"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=4543"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}