title: How to launch detached process through cmd.exe with no console?
body: <p>I want to launch an external program from C# to be completely detached.
  I use CreateProcess through pinvoke because Process.Start doesn't allow me to use DETACHED_PROCESS. Also I want this application to redirect it's output to some file.</p>
  
  <p>Here's the sample code:</p>
  
  <pre><code>            var processInformation = new ProcessUtility.PROCESS_INFORMATION();
              var securityInfo = new ProcessUtility.STARTUPINFO();
              var sa = new ProcessUtility.SECURITY_ATTRIBUTES();
              sa.Length = Marshal.SizeOf(sa); 
  
              // Create process with no window and totally detached
              var result = ProcessUtility.CreateProcess(Path.Combine(Environment.SystemDirectory, "cmd.exe"), commandLineArguments, ref sa, ref sa, false,
                  ProcessUtility.DETACHED_PROCESS, IntPtr.Zero, null, ref securityInfo, out processInformation);
  </code></pre>
  
  <ol>
  <li><p>CommandLineArguments are something like this : "/c Foo.bat > Foo.log 2>&amp;1"
  Everything works fine and Foo.log is populated by Foo.bat. No additional console window is visible. PERFECT.</p></li>
  <li><p>CommandLineArguments are something like this : "/c Foo.exe > Foo.log 2>&amp;1"
  Foo.exe is .NET Console Application. 
  Foo.log is not populated and Foo.exe is launched in visible console window. STRANGE. Why behavior is different from 1. ? </p></li>
  <li><p>Just for your information. CommandLineArguments are something like this : "/c Foo.exe > Foo.log 2>&amp;1"
  Foo.exe is .NET Windows Application. 
  Everything works fine but when I launch this application simply from command prompt I see no output because no console is allocated.</p></li>
  </ol>
  
  <p>I want 2. work the same as 1. Why there's a difference ?</p>
  
  <p>UPDATE: I don't want to write the Foo.log for myself because launching application will be killed.</p>
  
  <p>UPDATE: Ok, I wrote some code to specify that only one handle is inherited but CreateProcess gives me error 87 when calling with EXTENDED_STARTUPINFO_PRESENT ( even if it's present and empty ).</p>
  
  <p>Can you please help me why?</p>
  
  <pre><code>public class ProcessUtility
  {
      // Process creation flags
      const uint ZERO_FLAG = 0x00000000;
      const uint CREATE_BREAKAWAY_FROM_JOB = 0x01000000;
      const uint CREATE_DEFAULT_ERROR_MODE = 0x04000000;
      const uint CREATE_NEW_CONSOLE = 0x00000010;
      const uint CREATE_NEW_PROCESS_GROUP = 0x00000200;
      const uint CREATE_NO_WINDOW = 0x08000000;
      const uint CREATE_PROTECTED_PROCESS = 0x00040000;
      const uint CREATE_PRESERVE_CODE_AUTHZ_LEVEL = 0x02000000;
      const uint CREATE_SEPARATE_WOW_VDM = 0x00001000;
      const uint CREATE_SHARED_WOW_VDM = 0x00001000;
      const uint CREATE_SUSPENDED = 0x00000004;
      const uint CREATE_UNICODE_ENVIRONMENT = 0x00000400;
      const uint DEBUG_ONLY_THIS_PROCESS = 0x00000002;
      const uint DEBUG_PROCESS = 0x00000001;
      const uint DETACHED_PROCESS = 0x00000008;
      const uint EXTENDED_STARTUPINFO_PRESENT = 0x00080000;
      const uint INHERIT_PARENT_AFFINITY = 0x00010000;
  
      // Thread attributes flags
      const uint PROC_THREAD_ATTRIBUTE_HANDLE_LIST = 0x00020002;
      const uint PROC_THREAD_ATTRIBUTE_PARENT_PROCESS = 0x00020000;
  
      // File creation flags
      const uint FILE_ACCESS_WRITE = 0x40000000;
  
      // StartupInfo flags
      const int STARTF_USESTDHANDLES = 0x00000100;
  
      [StructLayout(LayoutKind.Sequential)]
      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 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)]
      struct STARTUPINFOEX
      {
          public STARTUPINFO StartupInfo;
          public IntPtr lpAttributeList;
      };
  
      [StructLayout(LayoutKind.Sequential)]
      struct PROCESS_INFORMATION
      {
          public IntPtr hProcess;
          public IntPtr hThread;
          public Int32 dwProcessID;
          public Int32 dwThreadID;
      }
  
      [StructLayout(LayoutKind.Sequential)]
      struct SECURITY_ATTRIBUTES
      {
          public Int32 Length;
          public IntPtr lpSecurityDescriptor;
          public bool bInheritHandle;
      }
  
      [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
      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 STARTUPINFO lpStartupInfo,
          out PROCESS_INFORMATION lpProcessInformation);
  
      [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
      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")]
      public static extern uint GetLastError();
  
      [DllImport("kernel32.dll", SetLastError = true)]
      [return: MarshalAs(UnmanagedType.Bool)]
      private static extern bool UpdateProcThreadAttribute(
          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(
          IntPtr lpAttributeList, int dwAttributeCount, int dwFlags, ref IntPtr lpSize);
  
      [DllImport("kernel32.dll", SetLastError = true)]
      [return: MarshalAs(UnmanagedType.Bool)]
      private static extern bool DeleteProcThreadAttributeList(IntPtr lpAttributeList);
  
      [DllImport("kernel32.dll", SetLastError = true)]
      static extern bool CloseHandle(IntPtr hObject);
  
      [DllImport("kernel32.dll", SetLastError = true)]
      static extern SafeFileHandle CreateFile(
          string lpFileName,
          uint fileAccess,
          [MarshalAs(UnmanagedType.U4)] FileShare fileShare,
          SECURITY_ATTRIBUTES securityAttributes,
          [MarshalAs(UnmanagedType.U4)] FileMode creationDisposition,
          uint dwFlagsAndAttributes,
          IntPtr hTemplateFile);
  
      public static bool CreateProcessWithStdHandlesRedirect(string lpApplicationName, string lpCommandLine, string logFilename)
      {
          var startupInfo = new STARTUPINFOEX();
          startupInfo.StartupInfo.cb = Marshal.SizeOf(startupInfo);
  
          try
          {
              var lpSize = IntPtr.Zero;
              if (InitializeProcThreadAttributeList(IntPtr.Zero, 1, 0, ref lpSize) || lpSize == IntPtr.Zero)
                  return false;
              startupInfo.lpAttributeList = Marshal.AllocHGlobal(lpSize);
  
              // Here startupInfo.lpAttributeList is initialized to hold 1 value
              if (!InitializeProcThreadAttributeList(startupInfo.lpAttributeList, 1, 0, ref lpSize))
                  return false;
  
              var fileSecurityAttributes = new SECURITY_ATTRIBUTES();
              fileSecurityAttributes.Length = Marshal.SizeOf(fileSecurityAttributes);
              // Create inheritable file handle
              fileSecurityAttributes.bInheritHandle = true;
  
              // Open log file for writing
              using (var handle = CreateFile(logFilename, FILE_ACCESS_WRITE, FileShare.ReadWrite,
                  fileSecurityAttributes, FileMode.Create, 0, IntPtr.Zero))
              {
                  var fileHandle = handle.DangerousGetHandle();
  
                  // Add filehandle to proc thread attribute list
                  if (!UpdateProcThreadAttribute(startupInfo.lpAttributeList, 0, (IntPtr)PROC_THREAD_ATTRIBUTE_HANDLE_LIST, fileHandle,
                      (IntPtr)IntPtr.Size, IntPtr.Zero, IntPtr.Zero))
                      return false;
  
                  startupInfo.StartupInfo.hStdError = fileHandle;
                  startupInfo.StartupInfo.hStdOutput = fileHandle;
                  // startupInfo.StartupInfo.hStdInput = ?;
                  startupInfo.StartupInfo.dwFlags = STARTF_USESTDHANDLES;
  
                  var processInformation = new PROCESS_INFORMATION();
                  var securityAttributes = new SECURITY_ATTRIBUTES();
                  securityAttributes.Length = Marshal.SizeOf(securityAttributes);
                  securityAttributes.bInheritHandle = true;
  
                  // Create process with no window and totally detached
                  return ProcessUtility.CreateProcess(lpApplicationName, lpCommandLine, ref securityAttributes, ref securityAttributes, true,
                      DETACHED_PROCESS | EXTENDED_STARTUPINFO_PRESENT, IntPtr.Zero, null, ref startupInfo, out processInformation);
              }
          }
          finally
          {
              if (startupInfo.lpAttributeList != IntPtr.Zero)
              {
                  DeleteProcThreadAttributeList(startupInfo.lpAttributeList);
                  Marshal.FreeHGlobal(startupInfo.lpAttributeList);
              }
          }
      }
  }
  </code></pre>