2

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();
Ryan
  • 2,948
  • 3
  • 30
  • 41
  • You are emphasizing that this is a 32-bit process. Is that because it works fine for 64-bit processes? – Gabe Apr 28 '11 at 02:01
  • 1
    Starting a process from a 64 bit app does not determine that the child app will be 64 bit. The child app is a separate process and it's 32/64 bitness is determined the same way every other program is determined. .Net apps that are not explicitly compiled as either 64 or 32 will be native to the OS. A 64 bit OS will run it as 64 bit, but a 32 bit OS will be run as 32 bit. – Joel Lucsy Apr 28 '11 at 02:45
  • 1
    If ASP.NET is running as the LocalSystem account, the function call to `CreateProcessWithLogonW` (used behing the scenes) will fail: http://msdn.microsoft.com/en-us/library/ms682431(VS.85).aspx – Gabe Apr 28 '11 at 02:47
  • @Gabe - In response to your first question, I didn't try launching a 64-bit process in this context (mostly because I didn't have a need to), although I have successfully launched processes with ShellExecute = false in the past. So, I don't know if it'd work fine for a 64-bit process, but I'd assume it would. – Ryan Apr 28 '11 at 13:24
  • any final solution with full source code sample working about it ? – Kiquenet Jul 08 '13 at 09:17

1 Answers1

3

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();
}
Pandincus
  • 9,506
  • 9
  • 43
  • 61
  • OP needs `UseShellExecute = false`. – Gabe Apr 28 '11 at 02:48
  • @Gabe: Not if the process is launched as a different user as a result of impersonation. – Ben Voigt Apr 28 '11 at 03:34
  • I get this error if I redirect Outputo: System.InvalidOperationException: The Process object must have the UseShellExecute property set to false in order to redirect IO streams. – Kiquenet Jul 08 '13 at 09:36