53

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

4

7 回答 7

69

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

IntPtr handle = Process.GetCurrentProcess().MainWindowHandle;
于 2009-08-14T12:33:36.217 回答
31

这是一个强大的方法来做到这一点:

来自控制台 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,而是确定调用方的 PID,如在替代实现中看到的或在winsrv.dll.

于 2015-02-19T21:03:22.813 回答
11

试试这个:

[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);
于 2011-02-04T07:32:29.127 回答
8

我刚刚为自己解决了这个问题(不幸的是,在看到托马斯的答案更快之前)。好吧,对于那些对他的回答不满意的人来说,这是另一种方式。我写这个答案是因为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,我必须自己实现它,当然,为了只调用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();
}

现在,问题是您实际上不能调用thisin Main,因为它是一个静态方法,所以Main我已经转移到一个名为的新方法Start,并且Main所做的只是创建一个 newProgram和调用Start

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

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

    throw new Exception();
}

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

于 2013-02-24T04:53:39.533 回答
1

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

于 2009-08-14T12:29:42.583 回答
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 位值,每次运行程序时都会发生变化。

于 2017-06-21T20:08:48.243 回答
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
于 2021-03-23T15:30:42.317 回答