Home
Manage Your Code
Snippet: Capturing Console Output when Using PsExec in Diagnostic.Process (C#)
Title: Capturing Console Output when Using PsExec in Diagnostic.Process Language: C#
Description: There are well known documented issues with capturing the console.out from PsExec when invoked using a System.Diagnostic.Process. The following code snippet demonstrates a work around. However, see notes for a caveat. Views: 490
Author: Stephen Smith Date Added: 3/10/2010
Copy Code  
1// Declare the api calls.

2[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
3static extern IntPtr AllocConsole();
4[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
5static extern bool FreeConsole();
6[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
7private static extern int CloseHandle(IntPtr handle);
8[DllImport("user32.dll")]
9static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
10[DllImport("kernel32.dll", SetLastError = true)]
11static extern IntPtr GetConsoleWindow();
12
13// Wrap the call to diagnostic.process in the api calls.

14// pay attention as its this that makes it work!

15protected override int ExecuteCommand(ProcessStartInfo startInfo)
16{
17    TextWriter writer = Console.Out;
18    IntPtr handle = AllocConsole();
19    try
20    {
21        if (!IntPtr.Zero.Equals(handle))
22        {
23            Console.SetOut(writer);
24            ShowWindow(GetConsoleWindow(), 0);
25        }
26
27        Log(LogSeverity.Debug, "PsExec is executing the following command '{0}", Command);
28        return base.ExecuteCommand(startInfo);
29    }
30    finally
31    {
32        if (!IntPtr.Zero.Equals(handle))
33        {
34            CloseHandle(handle);
35            FreeConsole();
36        }
37    }
38}
39
40
41// process start for psexec

42protected override ProcessStartInfo GetProcessStartInfo()
43{
44    string batchFileName = CreateBatchFile();
45    string psexecfileName =
46        Path.Combine((string.IsNullOrEmpty(PsExecPath) ? GetExecutingPath() : PsExecPath),
47                     "psexec.exe");
48    string args = CreatePsExecArguments(batchFileName);
49
50    ProcessStartInfo result = new ProcessStartInfo(psexecfileName, args)
51                                  {
52                                      // The following commands are needed to 

53                                      // redirect the standard output.

54                                      // This means that it will be redirected to

55                                      // the Process.StandardOutput StreamReader.

56                                      RedirectStandardOutput = true,
57                                      RedirectStandardError = true,
58                                      UseShellExecute = false,
59                                      // Do not create the black window.

60                                      CreateNoWindow = true,
61                                  };
62    return result;
63}
64
65private string CreatePsExecArguments(string fileName)
66{
67    const string psExecArgFmt = "\\\\{0} -u \"{1}\" -p \"{2}\" -c -f {3}";
68    return string.Format(psExecArgFmt,
69                         HostName,
70                         string.IsNullOrEmpty(UserDomain) ? UserName : string.Format("{0}\\{1}", UserDomain, UserName),
71                         UserPassword,
72                         fileName);
73}
74
75
76
77// base.ExecuteCommand

78protected virtual int ExecuteCommand(ProcessStartInfo startInfo)
79{
80    // Now we create a process, assign its ProcessStartInfo and start it

81    Process process = new Process {StartInfo = startInfo};
82    process.OutputDataReceived += ((sender, e) =>
83                                       {
84                                           if (!string.IsNullOrEmpty(e.Data))
85                                               Log(LogSeverity.Output, e.Data);
86                                       });
87    process.ErrorDataReceived += ((sender, e) =>
88                                      {
89                                          if (!string.IsNullOrEmpty(e.Data))
90                                              Log(LogSeverity.Error, e.Data);
91                                      });
92
93    if (string.IsNullOrEmpty(startInfo.WorkingDirectory))
94    {
95        startInfo.WorkingDirectory = GetExecutingPath();    
96    }
97    if (!string.IsNullOrEmpty(UserName))
98    {
99       startInfo.Domain = UserDomain;
100       startInfo.UserName = UserName;
101       startInfo.Password = ToSecureString(UserPassword);
102    }
103
104    process.Start();
105
106    process.BeginOutputReadLine();
107    process.BeginErrorReadLine();
108
109    process.WaitForExit();
110
111    // Return the process exit code.

112    return process.ExitCode;
113}
114
115
Notes
Running this in a Console application may have strange affects! You have been warned!