windows 获取句柄并写入启动我们进程的控制台

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5711291/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-15 16:40:55  来源:igfitidea点击:

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

c#c++windowswinapi

提问by Marino ?imi?

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();           
    int u;
    GetWindowThreadProcessId(ptr, out u);
    Process process = Process.GetProcessById(u);

The problem is how to get the standard output handle pointer (stdHandle) of this process.

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

I would then want something like:

然后我想要这样的东西:

                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);

Code in C++ using windows API is OK - I can use pInvoke.

使用 Windows API 的 C++ 代码是可以的 - 我可以使用 pInvoke。

Effectively what I would like is to write text to an already open console window not spawned by my process (and it is the one that was in foreground when launching my process through command line - but my process is a WinApp so the console does not attach the std).

实际上,我想要的是将文本写入一个不是由我的进程生成的已经打开的控制台窗口(它是通过命令行启动我的进程时在前台的那个窗口 - 但我的进程是一个 WinApp,所以控制台不会附加标准)。

Can the standard output be redirected after the process has been created?

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

PS: I read about some COM file that can be used to do this, so this means that there is a programmatic way ...

PS:我读到了一些可用于执行此操作的 COM 文件,因此这意味着有一种编程方式......

Thanks!

谢谢!

采纳答案by Marino ?imi?

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

我终于想出了如何透明地附加到控制台,如果它是启动 Windows 应用程序时的前台窗口。

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.

不要问我为什么必须通过 STD_ERROR_HANDLE 而不是 STD_OUTPUT_HANDLE,但它只是有效,可能是因为可以共享标准错误。

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.

注意:控制台可以在向您显示应用程序消息的同时接受用户输入,但是在应用程序输出 stderr 时使用它有点混乱。

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.

使用这段代码,如果您从控制台窗口启动应用程序,至少有一个参数,它会将 Console.Write 附加到它,如果您使用参数 /debug 启动应用程序,那么它甚至会将 Debug.Write 附加到安慰。

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.

在退出应用程序之前调用 Cleanup() 以释放控制台并发送 Enter 按键以释放最后一行,以便控制台在启动应用程序之前可用。

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).

附注。你不能用这种方法使用输出重定向,即: yourapp.exe > file.txt 因为你会得到一个空文件。甚至不要尝试 myapp.exe > file.txt 2>&1 因为你会使应用程序崩溃(将错误重定向到输出意味着我们正在尝试附加到非共享缓冲区)。

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

回答by Vozzie

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 参数的 AttachConsole 函数。(参见 msdn attachconsole)

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

ATTACH_PARENT_PROCESS (DWORD)-1 : 使用当前进程的父进程的控制台

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.

如果确实需要检查父进程,则可以使用 CreateToolhelp32Snapshot 并通过 PROCESSENTRY32 结构的 th32ParentProcessID 成员获取父进程。

回答by BrendanMcK

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:

如果您只想写入其他应用程序使用的控制台,则可以使用以下命令 - 您需要使用 P/Invoke 来执行第一步:

  • 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.
  • 附加到该控制台的AttachConsole(pid)- 如果您的进程已经与控制台关联,则您必须首先使用 FreeConsole,因为一个进程一次只能与一个控制台关联。
  • 既然您已附加,请使用 CreateFile("CONOUT$", GENERIC_WRITE, FILE_SHARE_WRITE, ... ) 获取控制台输出句柄 - 也许可以在托管代码中执行此部分。
  • 现在您已经获得了 HANDLE,将它封装在托管代码中 - 这部分您已经知道了。

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.

话虽如此,即使您可以这样做,但这样做不一定是个好主意。没有什么可以阻止原始进程在您执行相同操作时写入控制台,并且两者的输出会混淆,具体取决于进程如何进行缓冲。如果您想做一些事情,例如无论哪个窗口处于活动状态都通知用户某些事情,那么可能有更好的方法来做到这一点。

回答by Abhi

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.

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

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

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