tags:

views:

283

answers:

3

I am using the code from http://www.pinvoke.net/default.aspx/advapi32.createprocesswithlogonw. How do I get the output from the standard output as a string? Like the stuff that shows up when you run it interactively in a command window?

+2  A: 

Why don't you use the following overload of Process.Start Method ?

Giorgi
It didn't work for me. I get "The application failed to initialize properly" See related question: http://stackoverflow.com/questions/1972649/help-with-using-createprocesswithlogonw-in-c/1972860#1972860
Tony_Henrich
A: 

This post solved it for me. Not the output part but that I got CreateProcessWithLogonW working.

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

Tony_Henrich
A: 

calling CreateProcessWithLogonW with redirected std input\output\error threads is same to executing code below using System.Diagnostics.Process class with user\domain\password fields specified and Redirect* fields set to true. In fact by looking into StartWithCreateProcess private method of the Process class using reflector you would find that NativeMethods.CreateProcessWithLogonW procedure gets executed there if conditions above are applied.

Process process1 = new Process();
process1.StartInfo.FileName = @"c:\windows\system32\ping.exe";
process1.StartInfo.Arguments = "127.0.0.1";
// all 3 redirect* fields have to be set
process1.StartInfo.RedirectStandardOutput = true;
process1.StartInfo.RedirectStandardInput = true;
process1.StartInfo.RedirectStandardError = true;
process1.StartInfo.UseShellExecute = false;
process1.StartInfo.UserName = "admin";
process1.StartInfo.Domain = System.Environment.MachineName;
SecureString password = new SecureString();
foreach (char a in "password".ToCharArray())
    password.AppendChar(a);
process1.StartInfo.Password = password;
process1.Start();
string output = process1.StandardOutput.ReadToEnd();
Console.WriteLine(output);
process1.WaitForExit();

as for original question:

you would need to set pipe handles to stdOutput, stdError, stdInput fields of the StartupInfo. Smth like this:

StartupInfo startupInfo = new StartupInfo();
startupInfo.reserved = null;
startupInfo.flags = STARTF_USESTDHANDLES;
startupInfo.showWindow = SW_SHOW;
...
SafeFileHandle inputHandle = null;
SafeFileHandle outputHandle = null;
SafeFileHandle errorHandle = null;

CreatePipe(out inputHandle, out startupInfo.stdInput, true);
CreatePipe(out outputHandle, out startupInfo.stdOutput, false);
CreatePipe(out errorHandle, out startupInfo.stdError, false);

below is CreatePipe implementation:

public static void CreatePipe(out SafeFileHandle parentHandle, out SafeFileHandle childHandle, bool parentInputs)
{
    SECURITY_ATTRIBUTES lpPipeAttributes = new SECURITY_ATTRIBUTES();
    lpPipeAttributes.bInheritHandle = true;
    SafeFileHandle hWritePipe = null;
    try
    {
        if (parentInputs)
            CreatePipeWithSecurityAttributes(out childHandle, out hWritePipe, lpPipeAttributes, 0);
        else
            CreatePipeWithSecurityAttributes(out hWritePipe, out childHandle, lpPipeAttributes, 0);
        if (!DuplicateHandle(GetCurrentProcess(), hWritePipe, GetCurrentProcess(), out parentHandle, 0, false, 2))
            throw new Exception();
    }
    finally
    {
        if ((hWritePipe != null) && !hWritePipe.IsInvalid)
        {
            hWritePipe.Close();
        }
    }
}

[StructLayout(LayoutKind.Sequential)]
public class SECURITY_ATTRIBUTES
{
    public int nLength;
    public IntPtr lpSecurityDescriptor;
    public bool bInheritHandle;
    public SECURITY_ATTRIBUTES()
    {
        nLength = 12;
        lpSecurityDescriptor = IntPtr.Zero;
    }
}

[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool CreatePipe(out SafeFileHandle hReadPipe, out SafeFileHandle hWritePipe,
    SECURITY_ATTRIBUTES lpPipeAttributes, int nSize);
[DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
public static extern bool DuplicateHandle(IntPtr hSourceProcessHandle, SafeHandle hSourceHandle,
    IntPtr hTargetProcess, out SafeFileHandle targetHandle, int dwDesiredAccess,
    bool bInheritHandle, int dwOptions);
[DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
public static extern IntPtr GetCurrentProcess();

public static void CreatePipeWithSecurityAttributes(out SafeFileHandle hReadPipe, out SafeFileHandle hWritePipe,
    SECURITY_ATTRIBUTES lpPipeAttributes, int nSize)
{
    hReadPipe = null;
    if ((!CreatePipe(out hReadPipe, out hWritePipe, lpPipeAttributes, nSize) || hReadPipe.IsInvalid) || hWritePipe.IsInvalid)
        throw new Exception();
}

after you're done with creating pipes and CreateProcessWithLogonW is executed you can read std output from the pipe:

StreamWriter standardInput = new StreamWriter(new FileStream(inputHandle, FileAccess.Write, 0x1000, false), Console.InputEncoding, 0x1000);
standardInput.AutoFlush = true;
StreamReader reader = new StreamReader(new FileStream(outputHandle, FileAccess.Read, 0x1000, false), Console.OutputEncoding, true, 0x1000);
StreamReader error = new StreamReader(new FileStream(errorHandle, FileAccess.Read, 0x1000, false), Console.OutputEncoding, true, 0x1000);

while (!reader.EndOfStream)
{
   string line = reader.ReadLine();
   if (line.Length>0) Console.WriteLine(line);
}

code above is basically what is done in the StartWithCreateProcess method of the Process class

hope this helps, regards

serge_gubenko