我的问题与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
错误=>
这只是一个例子来说明我在评论中提到的问题。这不是一个修复,因为我不相信有一个微不足道的方法来解决这个问题。我在我的临时程序(称为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
是封闭源,因此很难确认这是它正在执行的具体检查。从呼叫方面来看,没有任何重要的解决方法。
不是你的问题的答案,而只是一个建议来实现你正在尝试做的事情(即只获取Winform应用程序中的句柄信息):
句柄工具有-nobanner
开关,你可以使用它来跳过版权信息。
handle64.exe -pid 11624 -nobanner
如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变得不稳定。
我对您的代码进行了一些更改:
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();