2011-04-19 60 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成員進行處理。