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

前端 未结 4 1639
清歌不尽
清歌不尽 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:07

    A system process is uniquely identified on the system by its process identifier. Like many Windows resources, a process is also identified by its handle, which might not be unique on the computer. A handle is the generic term for an identifier of a resource. The operating system persists the process handle, which is accessed through the Process.Handle property of the Process component, even when the process has exited. Thus, you can get the process's administrative information, such as the Process.ExitCode (usually either zero for success or a nonzero error code) and the Process.ExitTime. Handles are an extremely valuable resource, so leaking handles is more virulent than leaking memory.

    This is not the exact answer to ur questions , but it helps u to understand the basic thing actually.

    0 讨论(0)
  • 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<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 讨论(0)
  • 2021-02-06 17:21

    If the intention is to write to the parent console, if any, you can use the AttachConsole function with the ATTACH_PARENT_PROCESS argument. (see msdn attachconsole)

    ATTACH_PARENT_PROCESS (DWORD)-1 : Use the console of the parent of the current process

    And if you do need to check the parent process, you might use the CreateToolhelp32Snapshot and get the parent process thru the th32ParentProcessID member of the PROCESSENTRY32 structure.

    0 讨论(0)
  • 2021-02-06 17:23

    If you just want to write to the console that's used by some other app, then you can use the following - you'll need to use P/Invoke to do the first step:

    • AttachConsole(pid) to attach to that console - if your process is already associated with a console, you'll have to FreeConsole first, since a process can be associated with only one console at a time.
    • Now that you're attached, get the console output handle using CreateFile("CONOUT$", GENERIC_WRITE, FILE_SHARE_WRITE, ... ) - might be able to do this part in managed code.
    • Now that you've got the HANDLE, wrap it up in managed code - this part you already know.

    Having said that, even though you can do this, it's not necessarily a good idea to do so. There's nothing to stop the original process from writing to the console while you are doing likewise, and the output from both getting mixed-up, depending on how the processes are doing buffering. If you want to do something like notify the user of something regardless of which window is active, there may be a better way of doing that.

    0 讨论(0)
提交回复
热议问题