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

Gra*_*ant 43 c# console hwnd window-handles

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

Tho*_*que 62

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

IntPtr handle = Process.GetCurrentProcess().MainWindowHandle;
Run Code Online (Sandbox Code Playgroud)

  • 这似乎只有在您的进程启动控制台时才有效.如果你从另一个控制台运行它,你总是得到零.看起来你需要在其他情况下使用findwindowbycaption(参见http://support.microsoft.com/kb/124103) (8认同)
  • 对于 2-22 年后寻找的其他人;是的,这确实有效(对我来说)。 (3认同)

iva*_*eev 26

这是一种强有力的方法:

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();
Run Code Online (Sandbox Code Playgroud)
  • 对于控制台,当前进程已附加,就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;
};
Run Code Online (Sandbox Code Playgroud)

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

控制台对应信息位于并由csrss.exe(或多个,每个会话一个,自Vista以来)管理,因此无法使用ReadProcessMemory.所有csrss暴露的都是CSRSS LPC API.完整的API列表中只有一个相关的功能SrvGetConsoleWindow.并且它不接受PID但是确定主叫方的那个,如在替代实现或函数的反汇编中所见winsrv.dll.

  • 这应该是公认的答案.GetConsoleWindow是正确的方法; 这里的许多其他答案都是愚蠢的. (5认同)
  • 这是正确答案恕我直言,除非你想花时间弄清楚当你从控制台运行它而不是IDE时你的应用程序不起作用.谢谢伊万. (2认同)

Zai*_*Ali 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);
Run Code Online (Sandbox Code Playgroud)


Mas*_*tic 7

我刚刚为自己解决了这个问题(不幸的是,在看到Thomas的答案要快得多之前).那么,对于那些对他的答案不满意的人来说,这是另一种方式.我写这个答案是因为我想提供另一个答案+ 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.
}
Run Code Online (Sandbox Code Playgroud)

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

由于控制台没有实现IWin32Window,我必须实现它自己,当然,为了只是调用this在1 的说法.

这是它的实现和其他一切:

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

Program 类声明:

internal class Program : IWin32Window
{
    ...
}
Run Code Online (Sandbox Code Playgroud)

IWin32Window 执行:

public IntPtr Handle
{
    get { return NativeMethods.GetConsoleWindow(); }
}
Run Code Online (Sandbox Code Playgroud)

它使用以下类:

internal static class NativeMethods
{
    [DllImport("kernel32.dll")]
    internal static extern IntPtr GetConsoleWindow();
}
Run Code Online (Sandbox Code Playgroud)

现在的问题是,你不能真正称之为thisMain,是一个静态方法,所以无论是在Main我的公司搬到了新的命名方法Start,所有Main做的是创造一个新的Program和调用Start.

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

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

    throw new Exception();
}
Run Code Online (Sandbox Code Playgroud)

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

  • 简而言之,答案是:[DllImport("kernel32.dll")]内部静态extern IntPtr GetConsoleWindow(); (2认同)