Tony_Henrich
Tony_Henrich

Reputation: 44155

How to get standard output from CreateProcessWithLogonW?

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?

Upvotes: 2

Views: 4553

Answers (3)

serge_gubenko
serge_gubenko

Reputation: 20492

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

Upvotes: 3

Tony_Henrich
Tony_Henrich

Reputation: 44155

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

Upvotes: 0

Giorgi
Giorgi

Reputation: 30883

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

Upvotes: 2

Related Questions