Get the handle and write to the console that launched our process

前端 未结 4 1641
清歌不尽
清歌不尽 2021-02-06 16:54

How could I write to the standard output of some already open console? I find the console I need with this piece of code:

    IntPtr ptr = GetForegroundWindow();         


        
4条回答
  •  轻奢々
    轻奢々 (楼主)
    2021-02-06 17:18

    I finally figured out how to attach transparently to a console if it is the foreground window while launching the windows app.

    Don't ask me why STD_ERROR_HANDLE must be passed instead of STD_OUTPUT_HANDLE, but it simply works, probably because the standard error can be shared.

    N.B.: the console can accept user input while displaying you app messages inside, but it is a bit confusing to use it while the stderr is outputting from you app.

    With this snippet of code if you launch you app from a console window with at least one parameter it will attach Console.Write to it, and if you launch the app with the parameter /debug then it will attach even the Debug.Write to the console.

    Call Cleanup() before exiting you app to free the console and send an Enter keypress to release the last line so the console is usable as before starting the app.

    PS. You cannto use output redirection with this method ie.: yourapp.exe > file.txt because you will get an empty file. And dont even try myapp.exe > file.txt 2>&1 because you will crash the app (redirecting error to output means we are trying to attach to a nonshared buffer).

    Here is the code:

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

提交回复
热议问题