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

时间:2009-08-14 12:23:28

标签: c# console hwnd window-handles

有人能告诉我如何在C#中获取Windows控制台应用程序的句柄吗?在Windows窗体应用程序中,我通常会尝试this.Handle

7 个答案:

答案 0 :(得分:56)

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

IntPtr handle = Process.GetCurrentProcess().MainWindowHandle;

答案 1 :(得分:24)

这是一种强有力的方法:

Console 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();
  • 对于控制台,当前进程已附加到,GetConsoleWindow()就足够了
  • 对于控制台,附加了另一个进程,并附加到AttachConsole,然后调用GetConsoleWindow,并立即与FreeConsole分离。

为了格外小心,请在附加之前注册一个控制台事件处理程序(并在分离后取消注册),这样如果在连接到控制台的微小时间范围内发生控制台事件,则不会意外终止:

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

更改当前进程只是为了阅读某些东西是相当丑陋的(当这是一个控制台进程时,这变得非常难看,因为它需要帮助程序进程以避免终止当前控制台)。然而,进一步的调查表明,没有其他方法可以注入csrss流程或目标流程。

控制台对应信息位于csrss.exe(或其中众多,每个会话一个,自Vista以来),因此无法使用ReadProcessMemory之类的内容进行检索。 csrss公开的所有内容都是CSRSS LPC API。完整的API列表中只有一个相关函数SrvGetConsoleWindow。并且它不接受PID,但确定an alternative implementation中的主叫方或winsrv.dll中函数的反汇编。

答案 2 :(得分:10)

试试这个:

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

答案 3 :(得分:7)

我刚刚为自己解决了这个问题(不幸的是,在看到Thomas's answer之前更快)。那么,对于那些对他的答案不满意的人来说,这是另一种方式。我写这个答案是因为我想提供另一个答案+如果你将控制台视为一个窗口,那么设计Program类的更好方法。让我们从这个设计开始:

我有点改变了Program类的默认样式。我实际上把它变成了一个包含程序的类,而不仅仅是一个表示它并使用其他类来表示内容的方法。 (如果你不知道我的意思,不重要)。

我必须这样做的原因是因为我想编写以下事件处理程序:

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

它重载此方法MessageBox.Show(IWin32Window, String, String)

因为Console没有实现IWin32Window,所以我必须自己实现它,当然,只需在1 st 参数中调用this

以下是它的实现以及其他所有内容:

注意:此代码是从我的应用程序中复制粘贴的,您可以随意更改访问修饰符

Program班级宣言:

internal class Program : IWin32Window
{
    ...
}

IWin32Window实施:

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

它使用以下类:

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

现在,问题是你实际上无法在this中调用Main,这是一个静态方法,所以Main中的任何内容都转移到了一个名为的新方法Start以及所有Main正在创建一个新的Program并致电Start

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

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

    throw new Exception();
}

当然,结果是一个消息框,我的控制台窗口是所有者。
将此方法用于消息框,当然只是此方法的一个应用程序。

答案 4 :(得分:1)

我认为不存在这样的事情。应用程序无法访问控制台窗口。您可能会尝试迭代进程列表,查找您自己的进程名称。 Process类IIRC包含程序主窗口句柄的属性,可能是控制台应用程序的控制台窗口 - 我不确定。

答案 5 :(得分:0)

在一个控制台应用程序中,它将诊断程序流式传输到控制台,我想要禁用鼠标输入,我试过了 GetConsoleWindow(), Process.GetCurrentProcess().MainWindowHandle, and FindWindowByCaption(IntPtr.Zero, Console.Title)。 其中每个都返回相同的非零句柄,但是当我尝试在SetConsoleMode中使用该句柄时,它抛出了“无效句柄”异常。最后,我尝试将SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), mode | ENABLE_EXTENDED_FLAGS))与STD_INPUT_HANDLE定义为-10,并且它有效。 MS的文档表明可以重新分配控制台的句柄,我对这个解决方案感到不舒服或满意,但到目前为止,我发现它是唯一允许我以编程方式禁用快速编辑模式的东西。 GetStdHandle(STD_INPUT_HANDLE)返回'3',其他调用返回一个7位数值,每次运行程序时都会有所不同。

答案 6 :(得分:0)

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