tags:

views:

294

answers:

3

I am trying to execute a console app with arguments inside asp.net (C#) on Windows XP. I have tried diagnostics.Process but I just can't get it to work so I am switching to CreateProcessWithLogonW. I used the code sample from http://www.pinvoke.net/default.aspx/advapi32.createprocesswithlogonw but the console app doesn't seem to run and the return value from the CreateProcessWithLogonW api call is false.

My console app syntax is like: "C:\Program Files\business Intelligence\mycommand.exe" arg1 arg2 arg3 arg4 which runs fine if I run it interactively in a dos window.

In the C# code I am adding the double quotes before and after the program name. When the code runs I noticed that the task manager processes count didn't increase ath the second I run it which tells me the app didn't run. The processInfo structure has all 0's.

My questions: 1- Do the two 'command' parameters for the 'CreateProcessWithLogonW' call need to have the double quotes? Right now I am putting the whole command line in each.

2- How do I capture the standard Output so I know what has happened?

A: 

An example from pInvoke is defenitelly works, can you try it first with smth simple like ping? I've modified code from pInvoke to execute ping 127.0.0.1, please see below if it works for you. Answering your questions: 1 - I guess no need to do it but would work both ways, 2 - you can do it, but if the application doesn't start on the first place it wouldn't help you.

Sample code:

public const UInt32 Infinite = 0xffffffff;
public const Int32 Startf_UseStdHandles = 0x00000100;
public const Int32 StdOutputHandle = -11;
public const Int32 StdErrorHandle = -12;

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct StartupInfo
{
    public int cb;
    public String reserved;
    public String desktop;
    public String title;
    public int x;
    public int y;
    public int xSize;
    public int ySize;
    public int xCountChars;
    public int yCountChars;
    public int fillAttribute;
    public int flags;
    public UInt16 showWindow;
    public UInt16 reserved2;
    public byte reserved3;
    public IntPtr stdInput;
    public IntPtr stdOutput;
    public IntPtr stdError;
}

public struct ProcessInformation
{
    public IntPtr process;
    public IntPtr thread;
    public int processId;
    public int threadId;
}


[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool CreateProcessWithLogonW(
    String userName,
    String domain,
    String password,
    UInt32 logonFlags,
    String applicationName,
    String commandLine,
    UInt32 creationFlags,
    UInt32 environment,
    String currentDirectory,
    ref   StartupInfo startupInfo,
    out  ProcessInformation processInformation);

[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool GetExitCodeProcess(IntPtr process, ref UInt32 exitCode);

[DllImport("Kernel32.dll", SetLastError = true)]
public static extern UInt32 WaitForSingleObject(IntPtr handle, UInt32 milliseconds);

[DllImport("Kernel32.dll", SetLastError = true)]
public static extern IntPtr GetStdHandle(IntPtr handle);

[DllImport("Kernel32.dll", SetLastError = true)]
public static extern bool CloseHandle(IntPtr handle);


private void button1_Click(object sender, RoutedEventArgs e)
{
    StartupInfo startupInfo = new StartupInfo();
    startupInfo.reserved = null;
    startupInfo.flags &= Startf_UseStdHandles;
    startupInfo.stdOutput = (IntPtr)StdOutputHandle;
    startupInfo.stdError = (IntPtr)StdErrorHandle;

    UInt32 exitCode = 123456;
    ProcessInformation processInfo = new ProcessInformation();

    String command = @"c:\windows\system32\ping.exe 127.0.0.1";
    String user = "admin";
    String domain = System.Environment.MachineName;
    String password = "password";
    String currentDirectory = System.IO.Directory.GetCurrentDirectory();

    try
    {
        CreateProcessWithLogonW(
            user,
            domain,
            password,
            (UInt32)1,
            null,
            command,
            (UInt32)0,
            (UInt32)0,
            currentDirectory,
            ref startupInfo,
            out processInfo);
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.ToString());
    }

    Console.WriteLine("Running ...");
    WaitForSingleObject(processInfo.process, Infinite);
    GetExitCodeProcess(processInfo.process, ref exitCode);

    Console.WriteLine("Exit code: {0}", exitCode);

    CloseHandle(processInfo.process);
    CloseHandle(processInfo.thread);
}

hope this helps, regards

serge_gubenko
The code works when I launch something like notepad.exe. However I need to capture the output from my app. This can tell me if the app worked but encountered an error or even a "file not found" is helpful and that can tell me I didn't construct the command properly.
Tony_Henrich
In order to redirect standard output you would have to set up stdInput, stdOutput, stdError fields of the StartupInfo structure. You can check StartWithCreateProcess method of the System.Diagnostics.Process class with reflector for an example of how to do this. In short you would need to create pipes (CreatePipe api call), assign pipe's handles to stdInput, stdOutput and stdError fields of the StartupInfo and then read\write data from\to pipe with FileStreams
serge_gubenko
A: 

If the process doesn't run at all, you should get an exception. Do you get an exception when you call it, and if so which exception? (please add this additonal info to your question).

If you don't get an exception, it's almost certainly the case that the app is executing, but something is failing inside that app-- and it's exiting quickly enough that you don't see it executing. To verify or exclude this possibility, you can add a Thread.Sleep(20000) call as the first line of your mycommand.exe app, which should keep the app around for 20 seconds so you can spot it with task manager and shift your debugging effort from the caller app to the callee app.

Answer to your question #1: yes you'll need quotes since your EXE's path has spaces.

Answer to your #2: yes, you can do this, but it's probably better for you to split that out into a separate SO question once you've answered the first, more pressing issue.

Justin Grant
There are no exceptions. mycommand.exe is a third party tool so I can't add anything to it. If the app is executing, the counter in the task manager should at least increase by 1 then go down by 1.. even in a second.
Tony_Henrich
A: 

This post worked for me.

http://blogs.msdn.com/alejacma/archive/2007/12/20/how-to-call-createprocesswithlogonw-createprocessasuser-in-net.aspx

Tony_Henrich
you should accept your own answer so other folks will know what worked...
Justin Grant
I know but SO doesn't let you accept your own answer within 24 hours and I still have 7 hours to go.
Tony_Henrich