簡體   English   中英

獲取句柄並寫入啟動我們流程的控制台

[英]Get the handle and write to the console that launched our process

我怎么能寫一些已打開的控制台的標准輸出? 我用這段代碼找到了我需要的控制台:

    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,因此控制台不會附加std)。

創建過程后,是否可以重定向標准輸出?

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.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();
        }    
    }        
}

如果打算寫入父控制台(如果有),則可以將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,...)獲取控制台輸出句柄 - 可能能夠在托管代碼中執行此部分。
  • 現在你已經掌握了HANDLE,將它包裝在托管代碼中 - 這部分你已經知道了。

話雖如此,即使你能做到這一點,但這樣做並不一定是個好主意。 當你這樣做時,沒有什么可以阻止原始進程寫入控制台,並且兩者的輸出混合起來,這取決於進程如何進行緩沖。 如果你想做一些事情,比如告訴用戶某個東西而不管哪個窗口是活動的,那么可能有更好的方法。

系統進程通過其進程標識符在系統上唯一標識。 與許多Windows資源一樣,進程也由其句柄標識,該句柄在計算機上可能不是唯一的。 句柄是資源標識符的通用術語。 操作系統持久保存進程句柄,即使進程已退出,也可通過Process組件的Process.Handle屬性進行訪問。 因此,您可以獲取進程的管理信息,例如Process.ExitCode(通常為成功為零或非零錯誤代碼)和Process.ExitTime。 手柄是非常有價值的資源,因此泄漏的手柄比泄漏記憶更具毒性。

這不是你問題的確切答案,但它有助於你理解基本的事情。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM