在C#中运行程序时,所有消息都转到标准输出,但标准错误不包含任何内容

问题描述 投票:13回答:4

我的问题与the one identified不同。显然我已经调用了“BeginErrorReadLine”方法(我在下面的代码中标记它)。

我想解析Handle产生的结果


命令行

在命令行环境中运行时,它将输出如下内容:

> handle64 -p [PID]

Nthandle v4.11 - 处理查看器

版权所有(C)1997-2017 Mark Russinovich

Sysinternals - www.sysinternals.com

10:文件C:\ Windows

1C:文件C:\ Windows \ SysWOW64

[PID]是任何正在运行的进程ID

输出是分开的。

前5行(包括空行)转到标准错误,最后2行转到标准输出。

所以我可以通过重定向来剥离标题:

> handle64 -p [PID] 2> nul

10:文件C:\ Windows

1C:文件C:\ Windows \ SysWOW64


Winform应用程序

然后我尝试在C#winform应用程序中实现此命令:

Stream streamOut, streamErr;

var p = Process.Start(new ProcessStartInfo
{
    FileName = "handle64.exe",
    Arguments = "-p [PID]",
    CreateNoWindow = true,
    UseShellExecute = false,
    RedirectStandardOutput = true,
    RedirectStandardError = true,
});

p.OutputDataReceived += (sender, e) =>
{
    streamOut.Write("Output => " + e.Data);
};

p.ErrorDataReceived += (sender, e) =>
{
    streamErr.Write("Error => " + e.Data);
};

p.BeginOutputReadLine();
p.BeginErrorReadLine(); // !!!
p.WaitForExit();

然后我发现一切都进入标准输出。


好的,我可以通过代码分隔标题和正文。

问题是为什么the program的输出在两种环境之间表现不同?

我可以在winform应用程序中使结果在命令行中表现得像吗?


更新

对于Damien的评论,我尝试通过'cmd'运行the program,不幸的是我得到了相同的结果:

var p = Process.Start(new ProcessStartInfo
{
    FileName = "cmd",
    Arguments = "/C handle64.exe -p [PID]",
    CreateNoWindow = true,
    UseShellExecute = false,
    RedirectStandardOutput = true,
    RedirectStandardError = true,
});

...

在输出窗口中:

输出=>

输出=> Nthandle v4.11 - 处理查看器

输出=>版权所有(C)1997-2017 Mark Russinovich

输出=> Sysinternals - www.sysinternals.com

输出=>

输出=> 10:文件C:\ Windows

输出=> 1C:文件C:\ Windows \ SysWOW64

错误=>

c# process command-line-interface stdout stderr
4个回答
2
投票

这只是一个例子来说明我在评论中提到的问题。这不是一个修复,因为我不相信有一个微不足道的方法来解决这个问题。我在我的临时程序(称为Main)中创建了PlayAreaCSCon。如果没有参数调用它,它的行为类似于我怀疑Handle64.exe正在做的事情。使用参数调用时,它包含与您自己的代码类似的代码,但它会启动一个没有参数的自身副本:

using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;

namespace PlayAreaCSCon
{
    class Program
    {
        [DllImport("kernel32.dll")]
        static extern IntPtr GetConsoleWindow();
        static void Main(string[] args)
        {
            if (args.Length == 0)
            {
                Console.Out.WriteLine("Hello");
                if (GetConsoleWindow() == IntPtr.Zero)
                {
                    Console.Out.WriteLine("No Console window");
                }
                else
                {
                    Console.Error.WriteLine("We have a console window");
                }
            }
            else
            {
                Process p = Process.Start(new ProcessStartInfo
                {
                    FileName = "PlayAreaCSCon.exe",
                    Arguments = "",
                    CreateNoWindow = true,
                    UseShellExecute = false,
                    RedirectStandardOutput = true,
                    RedirectStandardError = true,
                });

                TextWriter streamOut = Console.Out;
                TextWriter streamErr = Console.Error;
                p.OutputDataReceived += (sender, e) =>
                {
                    streamOut.WriteLine("Output => " + e.Data);
                };

                p.ErrorDataReceived += (sender, e) =>
                {
                    streamErr.WriteLine("Error => " + e.Data);
                };

                p.BeginOutputReadLine();
                p.BeginErrorReadLine(); // !!!
                p.WaitForExit();
            }
        }
    }
}

在命令提示符中,我有以下会话:

C:\Dev\PlayAreaCSCon\PlayAreaCSCon\bin\Debug>PlayAreaCSCon.exe
Hello
We have a console window

C:\Dev\PlayAreaCSCon\PlayAreaCSCon\bin\Debug>PlayAreaCSCon.exe a
Error =>
Output => Hello
Output => No Console window
Output =>

所以即使在这里,如果Handle64.exe正在调用GetConsoleWindow或任何道德上等效的函数,它可以检测到它没有连接到控制台并表现出不同的行为。你可能让它获得控制台窗口的唯一方法是将CreateNoWindow设置为false,我认为你可能不想这样做。

由于Handle64是封闭源,因此很难确认这是它正在执行的具体检查。从呼叫方面来看,没有任何重要的解决方法。


3
投票

不是你的问题的答案,而只是一个建议来实现你正在尝试做的事情(即只获取Winform应用程序中的句柄信息):

句柄工具有-nobanner开关,你可以使用它来跳过版权信息。

handle64.exe -pid 11624 -nobanner

2
投票

如Damien所述:CreateNoWindow = false,

让它创建窗口立即隐藏它。我们可以在屏幕外创建它,但它仍会显示在任务栏上。

注意:此代码可能不会比让窗口自然出现和消失更好。

在课程顶部添加:

[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

然后你的代码变成:

var p = Process.Start(new ProcessStartInfo
{
    FileName = "cmd",
    Arguments = "/C handle64.exe -p [PID]",
    CreateNoWindow = false,
    UseShellExecute = false,
    RedirectStandardOutput = true,
    RedirectStandardError = true,
});
p.WaitForInputIdle();
IntPtr windowHandle = p.MainWindowHandle;
if(windowHandle == 0) throw new Exception("This did not work");
// use win32 API's to hide window (May still flicker)
ShowWindow(windowHandle,0);
// ...

我无法测试这个,因为我此刻只运行Linux。 如果异常没有触发,您可能会看到窗口的闪烁,但您应该有正确的输出。

我知道这样做的另一种方法是将处理程序插入到Win32消息泵中并响应特定的进程,告诉它需要知道它认为它有一个合适的窗口,而不是。我不会公开发布任何与此技术相关的代码。任何错误都会导致Windows变得不稳定。


0
投票

我对您的代码进行了一些更改:

Stream streamOut, streamErr;

var p = Process.Start(new ProcessStartInfo
{
    FileName = "handle64.exe",
    Arguments = "-p [PID]",
    CreateNoWindow = true,
    UseShellExecute = false,
    RedirectStandardOutput = true,
    RedirectStandardInput = true, // even if no writing to std::in, still need this
    RedirectStandardError = true,
});

p.OutputDataReceived += (sender, e) =>
{
    streamOut.Write("Output => " + e.Data);
};
p.BeginOutputReadLine();

p.ErrorDataReceived += (sender, e) =>
{
    streamErr.Write("Error => " + e.Data);
};

p.BeginErrorReadLine(); 

p.WaitForExit();
p.StandardInput.Close(); // call this before WaitForExit
p.WaitForExit();
© www.soinside.com 2019 - 2024. All rights reserved.