获取句柄并写入启动我们的过程的控制台

我怎么能写一些已经打开的控制台的标准输出? 我用这段代码find了我需要的控制台:

IntPtr ptr = GetForegroundWindow(); int u; GetWindowThreadProcessId(ptr, out u); Process process = Process.GetProcessById(u); 

问题是如何获得这个进程的标准输出句柄指针(stdHandle)。

我会然后想要的东西,如:

  SafeFileHandle safeFileHandle = new SafeFileHandle(stdHandle, true); FileStream fileStream = new FileStream(safeFileHandle, FileAccess.Write); Encoding encoding = Encoding.ASCII; StreamWriter standardOutput = new StreamWriter(fileStream, encoding); standardOutput.AutoFlush = true; Console.SetOut(standardOutput); 

使用Windows API的C ++代码是可以的 – 我可以使用pInvoke。

实际上,我希望将文本写入到未由我的进程生成的已打开的控制台窗口中(并且是通过命令行启动我的进程时处于前台状态的文本 – 但是我的进程是WinApp,所以控制台不会附加标准)。

在创build过程之后,标准输出可以redirect吗?

PS:我读了一些可以用来做这个的COM文件,所以这意味着有一个编程的方式…

谢谢!

我终于想出了如何在控制台上透明地附加,如果它是启动Windows应用程序的前景窗口。

不要问我为什么必须传递STD_ERROR_HANDLE而不是STD_OUTPUT_HANDLE,但是这样做很简单,可能是因为可以共享标准错误。

注意:控制台可以接受用户输入,同时在里面显示你的应用程序消息,但是当stderr从你的应用程序输出时使用它会有点混乱。

有了这段代码,如果你从一个至少有一个参数的控制台窗口启动你的应用程序,它将连接到Console.Write,如果你使用参数/ debug启动应用程序,那么它将连接Debug.Write到安慰。

在退出应用程序之前调用Cleanup()以释放控制台并发送Enter键以释放最后一行,以便控制台在启动应用程序之前可用。

PS。 你不能使用这个方法输出重定向,例如:yourapp.exe> file.txt,因为你会得到一个空文件。 甚至不要尝试myapp.exe> file.txt 2>&1,因为你会崩溃的应用程序(重定向错误输出意味着我们正试图附加到非共享缓冲区)。

这里是代码:

 [DllImport("user32.dll")] static extern IntPtr GetForegroundWindow(); [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] static extern bool SetForegroundWindow(IntPtr hWnd); [DllImport("user32.dll", SetLastError = true)] static extern uint GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId); [DllImport("kernel32.dll", EntryPoint = "GetStdHandle", SetLastError = true, CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)] private static extern IntPtr GetStdHandle(int nStdHandle); [DllImport("kernel32", SetLastError = true)] static extern bool AttachConsole(uint dwProcessId); [DllImport("kernel32.dll", EntryPoint = "AllocConsole", SetLastError = true, CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)] private static extern int AllocConsole(); [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)] static extern bool FreeConsole(); private const int STD_OUTPUT_HANDLE = -11; private const int STD_ERROR_HANDLE = -12; private static bool _consoleAttached = false; private static IntPtr consoleWindow; [STAThread] static void Main() { args = new List<string>(Environment.GetCommandLineArgs()); int prId; consoleWindow = GetForegroundWindow(); GetWindowThreadProcessId(consoleWindow, out prId); Process process = Process.GetProcessById(prId); if (args.Count > 1 && process.ProcessName == "cmd") { if (AttachConsole((uint)prId)) { _consoleAttached = true; IntPtr stdHandle = GetStdHandle(STD_ERROR_HANDLE); // must be error dunno why SafeFileHandle safeFileHandle = new SafeFileHandle(stdHandle, true); FileStream fileStream = new FileStream(safeFileHandle, FileAccess.Write); Encoding encoding = Encoding.ASCII; StreamWriter standardOutput = new StreamWriter(fileStream, encoding); standardOutput.AutoFlush = true; Console.SetOut(standardOutput); if (args.Contains("/debug")) Debug.listners.Add(new TextWriterTracelistner(Console.Out)); Console.WriteLine(Application.ProductName + " was launched from a console window and will redirect output to it."); } } // ... do whatever, use console.writeline or debug.writeline // if you started the app with /debug from a console Cleanup(); } private static void Cleanup() { try { if (_consoleAttached) { SetForegroundWindow(consoleWindow); SendKeys.SendWait("{ENTER}"); FreeConsole(); } } } 

如果打算写入父控制台(如果有),则可以使用AttachConsole函数和ATTACH_PARENT_PROCESS参数。 (请参阅msdn attachconsole)

ATTACH_PARENT_PROCESS(DWORD)-1:使用当前进程父级的控制台

如果您确实需要检查父进程,则可以使用CreateToolhelp32Snapshot并通过PROCESSENTRY32结构的th32ParentProcessID成员获取父进程。

如果您只想写入其他应用程序使用的控制台,那么您可以使用以下内容 – 您需要使用P / Invoke来完成第一步:

  • AttachConsole(pid)附加到该控制台 – 如果您的进程已经与控制台相关联,则必须首先使用FreeConsole,因为一个进程一次只能与一个控制台关联。
  • 现在你已经连接了,使用CreateFile(“CONOUT $”,GENERIC_WRITE,FILE_SHARE_WRITE,…)获取控制台输出句柄,可能可以在托管代码中完成这部分。
  • 现在你已经掌握了这个句柄,把它包装在托管代码中 – 这部分你已经知道了。

话虽如此,即使你可以这样做,这样做也不一定是个好主意。 没有任何东西可以阻止原来的流程从写入到控制台,同时你也这样做了,而且两者的输出都是混合的,这取决于进程如何进行缓冲。 如果你想做一些事情,比如通知用户某些东西,而不管哪个窗口处于活动状态,那么可能有更好的办法。

系统进程在系统上由其进程标识符唯一标识。 像许多Windows资源一样,一个进程也由它的句柄来标识,这个句柄在计算机上可能不是唯一的。 句柄是资源标识符的通用术语。 操作系统保留通过Process组件的Process.Handle属性访问的进程句柄,即使进程已经退出。 因此,您可以获取进程的管理信息,如Process.ExitCode(通常是成功的零或非零的错误代码)和Process.ExitTime。 把手是非常宝贵的资源,所以泄漏的手柄比泄漏的记忆更具毒性。

这不是你的问题的确切答案,但它有助于你真正理解基本的东西。