2011-04-19 52 views
3

如何写入某些已打开的控制台的标准输出? 我发现我需要这段代码控制台:获取句柄并写入启动我们的过程的控制台

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

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

然后,我会想是这样的:用C

   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是好的 - 我可以使用的PInvoke。

实际上,我希望将文本写入未由我的进程产生的已打开的控制台窗口(并且它是通过命令行启动我的进程时处于前台的文本 - 但我的进程是WinApp,所以控制台不附加std)。

过程创建后,标准输出是否可以重定向?

PS:我读,可以用来做一些这方面的COM文件,因此这意味着有一种编程方法,...

谢谢!

回答

3

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

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

N.B .:控制台可以在显示应用内的消息时接受用户输入,但在stderr从您的应用输出时使用它时有点混乱。

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

在退出应用程序之前调用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.Listeners.Add(new TextWriterTraceListener(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(); 
     }  
    }   
} 
0

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

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

+0

我明白手柄是微妙的,但我不会增加处理坏的东西的裁判计数。当所有者进程处理这个句柄时,我的代码就会失败......我期望的东西。有没有办法写入一个过程的标准输出(在Windows上,因为在Linux上你可以)? – 2011-04-19 05:19:35

+1

你知道我们可以在Linux上,但我认为现在需要进行一些COM研究,我相信通过COM可以做到这一点。如果我发现相关的事情,会通知你。与此同时,这个问题和主题都对我有意思。 – Abhi 2011-04-19 06:27:45

+0

亚当我发布了我的答案,它适用于我的需求。 – 2011-04-19 19:25:24

1

如果你只是想写信给这些会用来通过一些其他的应用程序控制台,那么你可以使用下面的 - 你需要使用的P/Invoke做的第一步:

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

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

+0

为了解决这个问题,你回答了一部分,谢谢。 – 2011-04-19 19:07:46

1

如果目的是写于母公司控制台,如果有的话,你可以使用AttachConsole功能与ATTACH_PARENT_PROCESS说法。 (请参阅MSDN attachconsole)

ATTACH_PARENT_PROCESS(DWORD)-1:使用当前进程

而如果你需要检查父进程,您可以使用CreateToolHelp32Snapshot函数并得到父母的父母的控制台通过PROCESSENTRY32结构的th32ParentProcessID成员进行处理。