title: CreateProcessAsUser user context
body: <p>I've already been searching long time but couldn't find a working solution yet :-(</p>
  
  <p>I have created a window service that launches a client on every user logged on to a machine using CreateProcessAsUser (<a href="http://www.pinvoke.net/default.aspx/advapi32/createprocessasuser.html" rel="nofollow">http://www.pinvoke.net/default.aspx/advapi32/createprocessasuser.html</a>), WTSEnumerateSessions and so on...</p>
  
  <p>This works fine already. The client starts in the user's session, shows its taskbar icon, and communication with the service is working fine.</p>
  
  <p>The problem I have is that I need to have that client store temporary files in the user's profile. I tried starting with a small log file so that I can keep track of any errors that my user's could eventually experience. Unfortunately I can not save to the user's temp folder because the client somehow seems to be running in LocalSystem's context although WindowsIdentity shows the correct user: System.IO.Path.GetTempPath() always returns 'C:\Windows\Temp' but my user's don't have administrative rights so they are not able to write there... furthermore, I planned to store settings in the current user's registry which is not working, too. I think this is related to the wrong temp path in some way.</p>
  
  <p>I also tried CreateEnvironmentBlock (<a href="http://www.pinvoke.net/default.aspx/userenv/CreateEnvironmentBlock.html" rel="nofollow">http://www.pinvoke.net/default.aspx/userenv/CreateEnvironmentBlock.html</a>) but I could not make it work and somewhere I found an article saying that this won't work any more on Vista or higher so I stopped researching on that one.</p>
  
  <p>For testing I have created a small test form just doing this:</p>
  
  <pre><code>        MessageBox.Show("Temp: " + System.IO.Path.GetTempPath() + Environment.NewLine + "User: " + WindowsIdentity.GetCurrent().Name, "Before impersonation");
  
          WindowsIdentity currentUserId = WindowsIdentity.GetCurrent();
          WindowsImpersonationContext impersonatedUser = currentUserId.Impersonate();
  
          MessageBox.Show("Temp: " + System.IO.Path.GetTempPath() + Environment.NewLine + "User: " + WindowsIdentity.GetCurrent().Name, "After impersonation");
  </code></pre>
  
  <p>This one always shows the same results before and after impersonation: "Temp: C:\Windows\Temp   User:testdomain\testuser" :-(</p>
  
  <p>If it helps here's my function to start a process (user token is delivered by WTSEnumerateSessions) - of course this only works under LocalSystem's context:</p>
  
  <pre><code>    public static Process StartProcessAsUser(IntPtr UserToken, string App, string AppPath, string AppParameters)
      {
          Process ResultProcess = null;
  
          IntPtr hDupedToken = IntPtr.Zero;
          NativeProcessAPI.PROCESS_INFORMATION oProcessInformation = new NativeProcessAPI.PROCESS_INFORMATION();
  
          try
          {
              NativeProcessAPI.SECURITY_ATTRIBUTES oSecurityAttributes = new NativeProcessAPI.SECURITY_ATTRIBUTES();
              oSecurityAttributes.Length = Marshal.SizeOf(oSecurityAttributes);
  
              bool result = NativeProcessAPI.DuplicateTokenEx(
                    UserToken,
                    NativeProcessAPI.GENERIC_ALL_ACCESS,
                    ref oSecurityAttributes,
                    (int)NativeProcessAPI.SECURITY_IMPERSONATION_LEVEL.SecurityIdentification,
                    (int)NativeProcessAPI.TOKEN_TYPE.TokenPrimary,
                    ref hDupedToken
                 );
  
              if (!result)
              {
                  return null;
              }
  
              NativeProcessAPI.STARTUPINFO oStartupInfo = new NativeProcessAPI.STARTUPINFO();
              oStartupInfo.cb = Marshal.SizeOf(oStartupInfo);
              oStartupInfo.lpDesktop = String.Empty;
  
              result = NativeProcessAPI.CreateProcessAsUser(
                                   hDupedToken,
                                   null,
                                   App + " " + AppParameters,
                                   ref oSecurityAttributes, ref oSecurityAttributes,
                                   false, 0, IntPtr.Zero,
                                   AppPath, ref oStartupInfo, ref oProcessInformation
                             );
  
              if (result)
              {
                  try
                  {
                      int ProcessID = oProcessInformation.dwProcessID;
  
                      try
                      {
                          ResultProcess = System.Diagnostics.Process.GetProcessById(ProcessID);
                      }
                      catch
                      {
                          ResultProcess = null;
                      }
                  }
                  catch (Exception ex)
                  {
                      ResultProcess = null;
                  }
              }
          }
          catch
          {
              ResultProcess = null;
          }
          finally
          {
              if (oProcessInformation.hProcess != IntPtr.Zero)
                  NativeProcessAPI.CloseHandle(oProcessInformation.hProcess);
              if (oProcessInformation.hThread != IntPtr.Zero)
                  NativeProcessAPI.CloseHandle(oProcessInformation.hThread);
              if (hDupedToken != IntPtr.Zero)
                  NativeProcessAPI.CloseHandle(hDupedToken);
          }
  
          return ResultProcess;
      }
  </code></pre>
  
  <p>Any ideas how I could start my processes in the user's contexts and not in the context of LocalSystem?</p>
  
  <p>Thanks a lot!</p>