title: How to call CreateProcess() with STARTUPINFOEX from C# and re-parent the child
body: <p>I need to create a new process but so that it is a "child" of another process not the current process eg re-parent the new process.</p>
  
  <p>The following have got me almost there <a href="https://stackoverflow.com/questions/1432646/net-how-to-call-createprocessasuser-with-startupinfoex-from-c-sharp">.NET : How to call CreateProcessAsUser() with STARTUPINFOEX from C#</a> and <a href="https://stackoverflow.com/questions/1427196/net-how-to-pinvoke-updateprocthreadattribute">.NET : How to PInvoke UpdateProcThreadAttribute</a> and <a href="http://winprogger.com/launching-a-non-child-process/" rel="nofollow noreferrer">http://winprogger.com/launching-a-non-child-process/</a></p>
  
  <pre><code>using System;
  using System.Diagnostics;
  using System.IO;
  using System.Runtime.InteropServices;
  
  public class ProcessCreator
  {
      [DllImport("kernel32.dll")]
      [return: MarshalAs(UnmanagedType.Bool)]
      static extern bool CreateProcess(
          string lpApplicationName, string lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes,
          ref SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandles, uint dwCreationFlags,
          IntPtr lpEnvironment, string lpCurrentDirectory, [In] ref STARTUPINFOEX lpStartupInfo,
          out PROCESS_INFORMATION lpProcessInformation);
  
      [DllImport("kernel32.dll", SetLastError = true)]
      [return: MarshalAs(UnmanagedType.Bool)]
      private static extern bool UpdateProcThreadAttribute(
          out IntPtr lpAttributeList, uint dwFlags, IntPtr Attribute, IntPtr lpValue,
          IntPtr cbSize, IntPtr lpPreviousValue, IntPtr lpReturnSize);
  
      [DllImport("kernel32.dll", SetLastError = true)]
      [return: MarshalAs(UnmanagedType.Bool)]
      private static extern bool InitializeProcThreadAttributeList(
          out IntPtr lpAttributeList, int dwAttributeCount, int dwFlags, ref IntPtr lpSize);
  
      public static bool CreateProcess(int parentProcessId)
      {
          const uint EXTENDED_STARTUPINFO_PRESENT = 0x00080000;
          const int PROC_THREAD_ATTRIBUTE_PARENT_PROCESS = 0x00020000;
  
          var pInfo = new PROCESS_INFORMATION();
          var sInfoEx = new STARTUPINFOEX();
          sInfoEx.StartupInfo = new STARTUPINFO();
  
          if (parentProcessId &gt; 0)
          {
              var lpSize = IntPtr.Zero;
              IntPtr dummyPtr;
              var success = InitializeProcThreadAttributeList(out dummyPtr, 1, 0, ref lpSize);
              if (success || lpSize == IntPtr.Zero)
              {
                  return false;
              }
  
              sInfoEx.lpAttributeList = Marshal.AllocHGlobal(lpSize);
              if (sInfoEx.lpAttributeList == IntPtr.Zero)
              {
                  return false;
              }
  
              success = InitializeProcThreadAttributeList(out sInfoEx.lpAttributeList, 1, 0, ref lpSize);
              if (!success)
              {
                  return false;
              }
  
              var parentHandle = Process.GetProcessById(parentProcessId).Handle;
              success = UpdateProcThreadAttribute(
                  out sInfoEx.lpAttributeList,
                  0,
                  (IntPtr)PROC_THREAD_ATTRIBUTE_PARENT_PROCESS,
                  parentHandle,
                  (IntPtr)IntPtr.Size,
                  IntPtr.Zero,
                  IntPtr.Zero);
              if (!success)
              {
                  return false;
              }
  
              sInfoEx.StartupInfo.cb = Marshal.SizeOf(sInfoEx);
          }
  
  
          var pSec = new SECURITY_ATTRIBUTES();
          var tSec = new SECURITY_ATTRIBUTES();
          pSec.nLength = Marshal.SizeOf(pSec);
          tSec.nLength = Marshal.SizeOf(tSec);
          var lpApplicationName = Path.Combine(Environment.SystemDirectory, "notepad.exe");
          return CreateProcess(lpApplicationName, null, ref pSec, ref tSec, false, EXTENDED_STARTUPINFO_PRESENT, IntPtr.Zero, null, ref sInfoEx, out pInfo);
      }
  
      [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
      struct STARTUPINFOEX
      {
          public STARTUPINFO StartupInfo;
          public IntPtr lpAttributeList;
      }
  
      [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
      struct STARTUPINFO
      {
          public Int32 cb;
          public string lpReserved;
          public string lpDesktop;
          public string lpTitle;
          public Int32 dwX;
          public Int32 dwY;
          public Int32 dwXSize;
          public Int32 dwYSize;
          public Int32 dwXCountChars;
          public Int32 dwYCountChars;
          public Int32 dwFillAttribute;
          public Int32 dwFlags;
          public Int16 wShowWindow;
          public Int16 cbReserved2;
          public IntPtr lpReserved2;
          public IntPtr hStdInput;
          public IntPtr hStdOutput;
          public IntPtr hStdError;
      }
  
      [StructLayout(LayoutKind.Sequential)]
      internal struct PROCESS_INFORMATION
      {
          public IntPtr hProcess;
          public IntPtr hThread;
          public int dwProcessId;
          public int dwThreadId;
      }
  
      [StructLayout(LayoutKind.Sequential)]
      public struct SECURITY_ATTRIBUTES
      {
          public int nLength;
          public IntPtr lpSecurityDescriptor;
          public int bInheritHandle;
      }
  }
  </code></pre>
  
  <p>ProcessCreator.CreateProcess(0) starts Notepad as a child of current process which is the default behaviour. So far so good.</p>
  
  <p>If the value passed in is non 0 the code attempts to start Notepad as a child of the process whose process ID matches the input value (I am assuming that process exists for now).</p>
  
  <p>Unfortunately that part does not work and throws the following exception:</p>
  
  <p>FatalExecutionEngineError was detected
  Message: The runtime has encountered a fatal error. The address of the error was at 0x69a2c7ad, on thread 0x1de0. The error code is 0xc0000005. This error may be a bug in the CLR or in the unsafe or non-verifiable portions of user code. Common sources of this bug include user marshaling errors for COM-interop or PInvoke, which may corrupt the stack.</p>
  
  <p>Any pointers much appreciated.</p>