C# 如何获取控制台应用程序窗口的句柄
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1277563/
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
How do I get the handle of a console application's window
提问by Grant
Can someone tell me how to get the handle of a Windows console application in C#? In a Windows Forms application, I would normally try this.Handle
.
有人能告诉我如何在 C# 中获取 Windows 控制台应用程序的句柄吗?在 Windows 窗体应用程序中,我通常会尝试this.Handle
.
采纳答案by Thomas Levesque
Not sure it works, but you can try that :
不确定它是否有效,但您可以尝试:
IntPtr handle = Process.GetCurrentProcess().MainWindowHandle;
回答by Thorsten Dittmar
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 mightbe the console window for console applications - which I'm not sure of.
我不认为有这样的事情。应用程序无法访问控制台窗口。您可能会尝试迭代进程列表以查找您自己的进程名称。本Process
类IIRC包含了程序的主窗口句柄的属性,这可能是控制台应用程序控制台窗口-这我不知道的。
回答by Zain Ali
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);
回答by MasterMastic
I've just solved this problem for myself (unfortunately before seeing Thomas's answerwhich 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. Let's begin with that design:
我刚刚为自己解决了这个问题(不幸的是,在看到托马斯更快的答案之前)。好吧,对于那些对他的回答不满意的人,这是另一种方式。我写这个答案是因为Program
如果您将控制台视为窗口,我想提供另一个答案 + 设计课程的更好方法。让我们从这个设计开始:
I've kind of changed the default style of the Program
class. 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).
我已经改变了Program
类的默认样式。我实际上已经将它变成了一个包含程序的类,而不仅仅是一种表示它并使用其他类作为内容的方法。(如果你不明白我的意思,不重要)。
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 1stargument.
由于控制台没有实现IWin32Window
,我必须实现它自己,当然,为了只是调用this
在1日的说法。
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
.
现在的问题是,你不能真正称之为this
中Main
,是一个静态方法,所以无论是在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.
结果当然是一个消息框,我的控制台窗口作为所有者。
将这种方法用于消息框,当然只是这种方法的一种应用。
回答by ivan_pozdeev
- The aforementioned
Process.MainWindowHandle
methodonly works for the process that started the console - The
FindWindowByCaption
methodis inherently unreliable
Here's a robust way to do this:
这是一个强大的方法来做到这一点:
The related functions from the Console Win32 APIare:
控制台 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 - For the console another process is attached to, attach to it as well with
AttachConsole
, callGetConsoleWindow
, them immediately detach withFreeConsole
.
- 对于当前进程附加到的控制台,就
GetConsoleWindow()
足够了 - 对于另一个进程附加到的控制台,也用
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
进程或目标进程之外,别无他法。
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
. All that csrss
exposes is the CSRSS LPC API. There's only one relevant function in the full API list, SrvGetConsoleWindow
. And it doesn't accept a PID but determines that of the calling party as seen in an alternative implementationor the function's disassembly in winsrv.dll
.
控制台通信信息位于并由csrss.exe
(或多个,每个会话一个,自 Vista 起)管理,因此无法使用ReadProcessMemory
. 所有csrss
公开的是CSRSS LPC API。完整的 API 列表中只有一个相关函数,SrvGetConsoleWindow
. 它不接受 PID 而是确定调用方的 PID,如替代实现或winsrv.dll
.
回答by UnconditionallyReinstateMonica
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)
.
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. Finally I tried SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), mode | ENABLE_EXTENDED_FLAGS))
with STD_INPUT_HANDLE defined as -10, and it worked. 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. GetStdHandle(STD_INPUT_HANDLE)
returns '3', the other calls return a 7 digit value that varies each time the program is run.
在将诊断流式传输到控制台并且我想禁用鼠标输入的控制台应用程序中,我尝试了
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 位数的值,该值在每次程序运行时都会发生变化。