简体   繁体   English

如何获取控制台应用程序窗口的句柄

[英]How do I get the handle of a console application's window

Can someone tell me how to get the handle of a Windows console application in C#?有人能告诉我如何在 C# 中获取 Windows 控制台应用程序的句柄吗? In a Windows Forms application, I would normally try this.Handle .在 Windows 窗体应用程序中,我通常会尝试this.Handle

不确定它是否有效,但您可以尝试:

IntPtr handle = Process.GetCurrentProcess().MainWindowHandle;

Here's a robust way to do this:这是一个强大的方法来做到这一点:

The related functions from the Console Win32 API are: 控制台 Win32 API的相关函数有:

[DllImport("kernel32.dll", SetLastError = true)]
static extern bool AttachConsole(uint dwProcessId);
[DllImport("kernel32.dll")]
static extern IntPtr GetConsoleWindow();
[DllImport("kernel32.dll", SetLastError=true, ExactSpelling=true)]
static extern bool FreeConsole();
  • For the console the current process is attached to, just GetConsoleWindow() is enough对于当前进程附加到的控制台,只需GetConsoleWindow()就足够了
  • For the console another process is attached to, attach to it as well with AttachConsole , call GetConsoleWindow , them immediately detach with FreeConsole .对于另一个进程附加到的控制台,也使用AttachConsole附加到它,调用GetConsoleWindow ,它们立即与FreeConsole分离。

For the extra cautious, register a console event handler before attaching (and unregister it after detaching) as well so you don't accidentally get terminated if a console event happens in the tiny time frame you're attached to the console:为了更加谨慎,在附加之前注册控制台事件处理程序(并在分离后取消注册),这样如果控制台事件发生在您附加到控制台的微小时间范围内,您就不会意外终止:

[DllImport("kernel32.dll")]
static extern bool SetConsoleCtrlHandler(ConsoleCtrlDelegate HandlerRoutine,
   bool Add);
delegate Boolean ConsoleCtrlDelegate(CtrlTypes CtrlType);
enum CtrlTypes : uint {
    CTRL_C_EVENT = 0,
    CTRL_BREAK_EVENT,
    CTRL_CLOSE_EVENT,  
    CTRL_LOGOFF_EVENT = 5,
    CTRL_SHUTDOWN_EVENT
}

bool is_attached=false;    
ConsoleCtrlDelegate ConsoleCtrlDelegateDetach = delegate(CtrlType) {
     if (is_attached = !FreeConsole())
         Trace.Error('FreeConsole on ' + CtrlType + ': ' + new Win32Exception());
     return true;
};

Making changes to the current process just to read something is rather ugly (when this is a console process, this gets really ugly since it requires a helper process to avoid terminating the current console).改变当前进程只是为了读取一些东西是相当丑陋的(当这是一个控制台进程时,这变得非常丑陋,因为它需要一个辅助进程来避免终止当前控制台)。 Nevertheless, further investigation shows that there's no other way short of injecting into the csrss process or the target process.尽管如此,进一步的调查表明,除了注入csrss进程或目标进程之外, csrss法。

Console correspondence information is located in and managed by csrss.exe (or a multitude of those, one for each session, since Vista), so it can't be retrieved with the likes of ReadProcessMemory .控制台通信信息位于并由csrss.exe管理(或多个,每个会话一个,自 Vista 以来),因此无法使用ReadProcessMemory方法检索它。 All that csrss exposes is the CSRSS LPC API . csrss公开的所有csrss都是CSRSS LPC API There's only one relevant function in the full API list, SrvGetConsoleWindow .在完整的 API 列表中只有一个相关函数SrvGetConsoleWindow And it doesn't accept a PID but determines that of the calling party as seen in an alternative implementation or the function's disassembly in winsrv.dll .并且它不接受 PID,而是确定调用方的 PID,如替代实现winsrv.dll的函数反汇编winsrv.dll

Try this:试试这个:

[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
public static extern IntPtr FindWindowByCaption(IntPtr zeroOnly, string lpWindowName);
…

Console.Title = "Test";
…

IntPtr handle = FindWindowByCaption(IntPtr.Zero, Console.Title);

I've just solved this problem for myself (unfortunately before seeing Thomas's answer which is much quicker).我刚刚为自己解决了这个问题(不幸的是,在看到托马斯更快的答案之前)。 Well, here's another way for those who are not satisfied with his answer.好吧,对于那些对他的回答不满意的人,这是另一种方式。 I'm writing this answer because I want to provide another answer + a better way to design the Program class if you're treating your console as a Window.我写这个答案是因为如果您将控制台视为窗口,我想提供另一个答案 + 设计Program类的更好方法。 Let's begin with that design:让我们从这个设计开始:

I've kind of changed the default style of the Program class.我已经改变了Program类的默认样式。 I've actually made it into a class that has a program in it, and not just one method which represent it and uses other classes for content.我实际上已经将它变成了一个包含程序的类,而不仅仅是一种表示它并使用其他类作为内容的方法。 (If you don't know what I mean, not important). (如果你不明白我的意思,不重要)。

The reason I had to do it is because I wanted to write the following event handler:我必须这样做的原因是因为我想编写以下事件处理程序:

private void CatchUnhandled(Object sender, UnhandledExceptionEventArgs e)
{
    var exception = e.ExceptionObject as Exception;
    MessageBox.Show(this, exception.Message, "Error"); // Check out 1st arg.
}

It overloads this method MessageBox.Show(IWin32Window, String, String) .它重载了这个方法MessageBox.Show(IWin32Window, String, String)

Because Console doesn't implement IWin32Window , I had to implement it myself, of course, in order to just call this in the 1 st argument.由于控制台没有实现IWin32Window ,我必须实现它自己,当然,为了只是调用this1个变量。

Here is the implementation of it and everything else:这是它的实现以及其他所有内容:

Note: this code is copy-pasted from my application, you can feel free to change the access modifiers注意:此代码是从我的应用程序复制粘贴的,您可以随意更改访问修饰符

Program Class Declaration: Program类声明:

internal class Program : IWin32Window
{
    ...
}

IWin32Window Implementation: IWin32Window实现:

public IntPtr Handle
{
    get { return NativeMethods.GetConsoleWindow(); }
}

It uses the following class:它使用以下类:

internal static class NativeMethods
{
    [DllImport("kernel32.dll")]
    internal static extern IntPtr GetConsoleWindow();
}

Now, the problem is that you can't actually call this in Main , being a static method, so whatever was in Main I've moved to a new method named Start and all Main is doing is creating a new Program and calling Start .现在,问题是你实际上不能在Main调用this ,因为它是一个静态方法,所以无论Main是什么,我都移到了一个名为Start的新方法, Main所做的就是创建一个新的Program并调用Start

private static void Main()
{
    new Program().Start();
}

private void Start()
{
    AppDomain.CurrentDomain.UnhandledException += CatchUnhandled;

    throw new Exception();
}

The result was, of course, a message-box with my console's window as an owner.结果当然是一个消息框,我的控制台窗口作为所有者。
Using this method for a message-box, is of course only one application of this method.将这种方法用于消息框,当然只是这种方法的一种应用。

I don't think there is such a thing.我不认为有这样的事情。 The console window is not accessible to the application.应用程序无法访问控制台窗口。 You MIGHT try to iterate the process list looking for your own process name.您可能会尝试迭代进程列表以查找您自己的进程名称。 The Process class IIRC contains a property for the program's main window handle, which might be the console window for console applications - which I'm not sure of. Process类 IIRC 包含程序主窗口句柄的属性,它可能是控制台应用程序的控制台窗口 - 我不确定。

In a console application which streamed diagostics to the console, and for which I wanted to disable mouse input, I tried GetConsoleWindow(), Process.GetCurrentProcess().MainWindowHandle, and FindWindowByCaption(IntPtr.Zero, Console.Title) .在将诊断流式传输到控制台并且我想禁用鼠标输入的控制台应用程序中,我尝试了GetConsoleWindow(), Process.GetCurrentProcess().MainWindowHandle, and FindWindowByCaption(IntPtr.Zero, Console.Title) Each of these returned the same non-zero handle, but when I tried to use that handle in SetConsoleMode it threw a "Invalid Handle" exception.每个都返回相同的非零句柄,但是当我尝试在 SetConsoleMode 中使用该句柄时,它抛出了“无效句柄”异常。 Finally I tried SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), mode | ENABLE_EXTENDED_FLAGS)) with STD_INPUT_HANDLE defined as -10, and it worked.最后,我尝试了SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), mode | ENABLE_EXTENDED_FLAGS))并将 STD_INPUT_HANDLE 定义为 -10,并且它起作用了。 MS's documentation suggests that handles to consoles may be reassigned, and I am not comfortable or happy with this solution, but so far it is the only thing I've found that allows me to disable quick edit mode programmatically. MS 的文档表明可能会重新分配控制台的句柄,我对这个解决方案不满意或不满意,但到目前为止,这是我发现的唯一允许我以编程方式禁用快速编辑模式的方法。 GetStdHandle(STD_INPUT_HANDLE) returns '3', the other calls return a 7 digit value that varies each time the program is run. GetStdHandle(STD_INPUT_HANDLE)返回“3”,其他调用返回一个 7 位数的值,该值在每次程序运行时都会发生变化。

One more version in VB.Net on how to show MessageBox on top of console window VB.Net 中关于如何在控制台窗口顶部显示 MessageBox 的另一个版本

Imports System.Runtime.InteropServices
Imports System.Windows.Forms

Friend Module NativeMethods
    <DllImport("kernel32.dll")> Friend Function GetConsoleWindow() As IntPtr
    End Function
End Module

NotInheritable Class WndProxy
    Implements IWin32Window
    ReadOnly Property Handle As IntPtr Implements IWin32Window.Handle
    Sub New(_hwnd As IntPtr)
        Handle = _hwnd
    End Sub
End Class

Module Module1

    Sub Main()
        ' using MainWindowHandle
        Dim w = New WndProxy(Process.GetCurrentProcess().MainWindowHandle)
        MessageBox.Show(w, "Hi")
        ' using GetConsoleWindow api
        Dim s = New WndProxy(NativeMethods.GetConsoleWindow)
        MessageBox.Show(s, "Hi")
    End Sub

End Module

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何在 C# 中获取控制台应用程序窗口? - How do I get console application window in C#? 如何将项目的文件路径放到控制台应用程序窗口中? - How do I get file paths of items dropped onto a console application window? 如何在 forms 应用程序中显示控制台输出/窗口? - How do I show a console output/window in a forms application? 如何在 .NET 控制台应用程序中获取应用程序的路径? - How can I get the application's path in a .NET console application? 如何获取C#控制台应用程序的.exe名称? - How do I get the .exe name of a C# console application? 如何在.Net控制台应用程序中获取承载令牌? - how do i get a bearer token in a .Net console application? ia如何通过使用窗口句柄或id获得进程窗口名称和主标题名称? - How do i a get a process window name and maintitlename by using the window handle or id? 如何确定应用程序的控制台窗口何时获得焦点或失去焦点? - How can I determine when my application's console window gets or loses focus? 动态编译Windows应用程序时如何隐藏控制台窗口? - How do I hide the console window when dynamically compiling a windows application? 如何防止 C# 控制台应用程序优先作为活动 window? - How do I prevent a C# console application from taking priority as the active window?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM