2

Well, first of all, when executing PsExec commands from C#, part of the output is in the StandardOutput and another part is in the StandardError for some reason, as mentioned in other posts, but that's not big of a deal.

Now, the problem is: Even when combining the two parts together, the "output" isn't exactly the same compared to what gets displayed on the Command Prompt window. Sometimes it's different and sometimes it's incomplete.

Here's my code:

string args = $@"{computerName} -u ""{username}"" -p ""{password}"" {pathOrCommand}";
var pi = new ProcessStartInfo("PsExec.exe", args);
pi.CreateNoWindow = true;
pi.WindowStyle = ProcessWindowStyle.Hidden;
pi.UseShellExecute = false;
pi.RedirectStandardOutput = true;
pi.RedirectStandardError = true;
using (Process p = Process.Start(pi))
{
    var resultLines = new List<string>();
    var handler = new DataReceivedEventHandler(delegate (object o, DataReceivedEventArgs e)
    {
        resultLines.Add(e.Data);
    });
    p.ErrorDataReceived += handler;
    p.OutputDataReceived += handler;
    p.BeginErrorReadLine();
    p.BeginOutputReadLine();

    p.WaitForExit();   
    string result = string.Join("\r\n", resultLines);
}
  • An example of when it's different:

    When passing the wrong username or password, the following message will be displayed on Command Prompt: *

    Logon failure: unknown user name or bad password.

    ..while the output of the above code will be:

    The handle is invalid.

  • An example of when it's incomplete (this is more important):

    When executing this command PsExec \\computer -u username -p password query session, the result looks something like this: *

     SESSIONNAME       USERNAME                 ID  STATE   TYPE        DEVICE
    >services                                    0  Disc
     console           someUserName              1  Active
     rdp-tcp                                 65537  Listen
    

    ..but the output when executing from C# is only:

     SESSIONNAME       USERNAME                 ID  STATE   TYPE        DEVICE
    

So, my questions are:

  • Why is Process.StandardOutput / Process.StandardError different from the actual output?

  • Is there any way around this?


* I'm only presenting the relevant part of the output here.

  • Maybe something related to the stream.AutoFlush settings? – Rubens Farias Jul 31 '18 at 22:14
  • @RubensFarias I didn't even use streams in my code. I used `DataReceivedEvent`s. Perhaps streams are used under the hood? Even if that's the case, would that cause the output to be a totally different English sentence? That's the weird part! – 41686d6564 stands w. Palestine Jul 31 '18 at 22:17
  • I understood that isn't a different sentence, but only a _truncated_/incomplete version; under the hood, we're playing with streams: https://msdn.microsoft.com/library/system.diagnostics.process.standardoutput(v=vs.110).aspx – Rubens Farias Jul 31 '18 at 23:02
  • @RubensFarias Have a look at the first example in my question: `An example of when it's different:` – 41686d6564 stands w. Palestine Jul 31 '18 at 23:03
  • I can't test a `query session` command now (IIRC the interactive mode or `psexec` ends when the remote process ends. There should be a switch to modify this a bit). About the error output, assign the `ErrorDataReceived` event it's own handler. – Jimi Jul 31 '18 at 23:44
  • @Jimi I don't understand the first part of your comment. Assigning `ErrorDataReceived` to a separate event handler doesn't make any difference. – 41686d6564 stands w. Palestine Aug 01 '18 at 08:04
  • I haven't used `psexec(64)` in a while. IIRC, when not in interactive mode, the process exits as soon as the remote process exits. Getting a partial output is quite possible. But I never use `WaitForExit`. I set `.EnableRaisingEvents` and get the final output in the `Exited` event. I tested that command with a code +- like [this one](https://stackoverflow.com/questions/51312990/how-to-output-shell-command-to-a-richtextbox-in-visual-basic?answertab=active#tab-top), and I could catch the error output just fine (the same error you referred to: *Logon failure: unknown user name or bad password*). – Jimi Aug 01 '18 at 09:50

1 Answers1

0

I still don't understand why the output is different but I managed to create a workaround which I'm going to use unless someone provides a better solution.

I used cmd.exe to redirect the stdout and stderr to a temp file. After the process exits, I read the contents of the file and then delete it:

string tempFile = Path.GetTempFileName();
string args = $@"/c PsExec.exe {computerName} -u ""{username}"" -p ""{password}"" {pathOrCommand}";
var pi = new ProcessStartInfo("cmd.exe", $"{args} 1> {tempFile} 2>&1");
pi.CreateNoWindow = true;
pi.WindowStyle = ProcessWindowStyle.Hidden;
using (Process p = Process.Start(pi))
{
    p.WaitForExit();

    string result = File.ReadAllText(tempFile);
    File.Delete(tempFile);
}

Hope this could help someone else having the same problem.