Search code examples
c#32bit-64bitstartprocessinfostart-process

How to start a 32-bit process in C# without using shell execute on 64-bit machine?


I have an ASP .NET web application on a 64-bit machine that needs to run a legacy 32-bit reporting application.

When I run the program with UseShellExecute = false, the program exits with exit code:

-1073741502

I can't use Shell Execute because I have to run the process as a different user. Yet, when Shell Execute is true, the process will run fine (although I have to change the user that ASP .NET is executing under).

How can I start this 32-bit program using C# without use shell execute?

Here's the code I have right now:

var pxs = new ProcessStartInfo
{
    Arguments = arguments,
    CreateNoWindow = true,
    Domain = ConfigurationManager.AppSettings["reportUserDomain"],
    UserName = ConfigurationManager.AppSettings["reportUserName"],
    Password = GetSecureString(ConfigurationManager.AppSettings["reportUserPassword"]),
    LoadUserProfile = true,
    FileName = ConfigurationManager.AppSettings["reportRuntime"],
    UseShellExecute = false             

};

var px = new Process
{
    StartInfo = pxs
};

px.Start();
px.WaitForExit();

Solution

  • What if you surrounded your code, including UseShellExecute = true, with the windows native "LogonUser" method? I've used this successfully in a few projects to do something similar.

    [DllImport("advapi32.dll", CharSet = CharSet.Auto)]
    public static extern bool LogonUser(String lpszUserName, String lpszDomain,
        String lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken
    

    Fresh Click Media did an article about this and wrote a sample Impersonate class: --> http://www.freshclickmedia.com/blog/2008/11/programmatic-impersonation-in-c/

    But for completeness, here's my version of it:

    public class Impersonator : IDisposable
    {
        private WindowsImpersonationContext _impersonatedUser = null;
        private IntPtr _userHandle;
    
        // constructor for a local account. username and password are arguments.
        public Impersonator(string username, string passwd)
        {
            _userHandle = new IntPtr(0);
    
            string user = username;
            string userDomain = "."; // The domain for a local user is by default "."
            string password = passwd;
    
            bool returnValue = LogonUser(user, userDomain, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref _userHandle);
    
            if (!returnValue)
                throw new ApplicationException("Could not impersonate user");
    
            WindowsIdentity newId = new WindowsIdentity(_userHandle);
            _impersonatedUser = newId.Impersonate();
        }
    
        // constructor where username, password and domain are passed as parameters
        public Impersonator(string username, string passwd, string domain)
        {
            _userHandle = new IntPtr(0);
    
            string user = username;
            string userDomain = domain;
            string password = passwd;
    
            bool returnValue = LogonUser(user, userDomain, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref _userHandle);
    
            if (!returnValue)
                throw new ApplicationException("Could not impersonate user");
    
            WindowsIdentity newId = new WindowsIdentity(_userHandle);
            _impersonatedUser = newId.Impersonate();
        }
    
        public void Dispose()
        {
            if (_impersonatedUser != null)
            {
                _impersonatedUser.Undo();
                CloseHandle(_userHandle);
            }
        }
    
        public const int LOGON32_LOGON_INTERACTIVE = 2;
        public const int LOGON32_LOGON_SERVICE = 3;
        public const int LOGON32_PROVIDER_DEFAULT = 0;
    
        [DllImport("advapi32.dll", CharSet = CharSet.Auto)]
        public static extern bool LogonUser(String lpszUserName, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
    
        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        public extern static bool CloseHandle(IntPtr handle);
    }
    

    Using it in your case would be:

    var domain = ConfigurationManager.AppSettings["reportUserDomain"];
    var username = ConfigurationManager.AppSettings["reportUserName"];
    var password = ConfigurationManager.AppSettings["reportUserPassword"];
    
    using (Impersonator impersonator = new Impersonator(username, password, domain))
    {
        var pxs = new ProcessStartInfo
        {
            Arguments = arguments,
            CreateNoWindow = true,
            LoadUserProfile = true,
            FileName = ConfigurationManager.AppSettings["reportRuntime"],
            UseShellExecute = true
        };
    
        var px = new Process
        {
            StartInfo = pxs
        };
    
        px.Start();
        px.WaitForExit();
    }