当运行一个C#程序,所有的消息都输出到标准输出,但是标准错误不包含任何内容
When run a program in C#, all the messages go to the standard output, but the standard error contains nothing
我的问题与 不同。显然我已经调用了"BeginErrorReadLine"方法(我在下面的代码中标记了它)。
我想解析Handle
产生的结果
命令行
当运行在命令行环境下,它会输出类似:
> handle64 -p [PID]
Nthandle v4.11 - Handle viewer
Copyright (C) 1997-2017 Mark Russinovich
Sysinternals - www.sysinternals.com
10: File C:\Windows
1C: File C:\Windows\SysWOW64
[PID] 是任何 运行ning 进程 ID
输出是分开的。
前 5 行(包括空行)转到标准错误,最后 2 行转到标准输出。
所以我可以通过重定向去除 header:
> handle64 -p [PID] 2>nul
10: File C:\Windows
1C: File 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();
然后我发现一切都进入了标准输出。
问题
好的,我可以通过代码将 header 和 body 分开。
问题是为什么 the program 的输出在 2 个环境中表现不同?
我能否使 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,
});
...
在输出中window:
Output =>
Output => Nthandle v4.11 - Handle viewer
Output => Copyright (C) 1997-2017 Mark Russinovich
Output => Sysinternals - www.sysinternals.com
Output =>
Output => 10: File C:\Windows
Output => 1C: File C:\Windows\SysWOW64
Error =>
这只是一个示例,用于说明我在评论中提到的问题。这不是解决方法,因为我不相信有一种简单的方法可以解决这个问题。我在我的 scratch 程序中创建了 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
或任何道德上等效的函数,它也可以检测到它没有连接到控制台并表现出不同的行为。让它获得控制台 window 的唯一方法是将 CreateNoWindow
设置为 false,我猜你可能不想这样做。
由于 Handle64
是封闭源代码,因此很难确认这是它正在执行的特定检查。 调用 方面对此没有重要的修复。
不是你问题的答案,只是一个实现你正在尝试做的事情的建议(即只在 Winform 应用程序中获取句柄信息):
句柄工具有-nobanner
开关,您可以使用它来跳过版权消息信息。
handle64.exe -pid 11624 -nobanner
正如达米安所说:
CreateNoWindow = false,
让它创建 window 并立即隐藏它。我们可以在屏幕外创建它,但它仍会显示在任务栏上。
注意:此代码可能并不比让 Window 自然出现和消失更好。
在 class 的顶部添加:
[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。
如果异常没有触发,您可能会看到 window 的闪烁,但您应该有正确的输出。
我知道的另一种方法是将处理程序插入 Win32 消息泵并响应特定进程,告诉它需要知道什么才能认为它具有适当的 window,而实际上它没有。我不会公开 post 与此技术相关的任何代码。任何错误都会导致 Windows 变得不稳定。
我对你的代码做了一些修改:
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();
我的问题与
我想解析Handle
产生的结果命令行
当运行在命令行环境下,它会输出类似:
> handle64 -p [PID]
Nthandle v4.11 - Handle viewer
Copyright (C) 1997-2017 Mark Russinovich
Sysinternals - www.sysinternals.com
10: File C:\Windows
1C: File C:\Windows\SysWOW64
[PID] 是任何 运行ning 进程 ID
输出是分开的。
前 5 行(包括空行)转到标准错误,最后 2 行转到标准输出。
所以我可以通过重定向去除 header:
> handle64 -p [PID] 2>nul
10: File C:\Windows
1C: File 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();
然后我发现一切都进入了标准输出。
问题
好的,我可以通过代码将 header 和 body 分开。
问题是为什么 the program 的输出在 2 个环境中表现不同?
我能否使 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,
});
...
在输出中window:
Output =>
Output => Nthandle v4.11 - Handle viewer
Output => Copyright (C) 1997-2017 Mark Russinovich
Output => Sysinternals - www.sysinternals.com
Output =>
Output => 10: File C:\Windows
Output => 1C: File C:\Windows\SysWOW64
Error =>
这只是一个示例,用于说明我在评论中提到的问题。这不是解决方法,因为我不相信有一种简单的方法可以解决这个问题。我在我的 scratch 程序中创建了 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
或任何道德上等效的函数,它也可以检测到它没有连接到控制台并表现出不同的行为。让它获得控制台 window 的唯一方法是将 CreateNoWindow
设置为 false,我猜你可能不想这样做。
由于 Handle64
是封闭源代码,因此很难确认这是它正在执行的特定检查。 调用 方面对此没有重要的修复。
不是你问题的答案,只是一个实现你正在尝试做的事情的建议(即只在 Winform 应用程序中获取句柄信息):
句柄工具有-nobanner
开关,您可以使用它来跳过版权消息信息。
handle64.exe -pid 11624 -nobanner
正如达米安所说: CreateNoWindow = false,
让它创建 window 并立即隐藏它。我们可以在屏幕外创建它,但它仍会显示在任务栏上。
注意:此代码可能并不比让 Window 自然出现和消失更好。
在 class 的顶部添加:
[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。
如果异常没有触发,您可能会看到 window 的闪烁,但您应该有正确的输出。
我知道的另一种方法是将处理程序插入 Win32 消息泵并响应特定进程,告诉它需要知道什么才能认为它具有适当的 window,而实际上它没有。我不会公开 post 与此技术相关的任何代码。任何错误都会导致 Windows 变得不稳定。
我对你的代码做了一些修改:
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();