请问如何在C#中获取Windows控制台应用程序的句柄? 在Windows窗体应用程序中,我通常会尝试使用this.Handle
。
请问如何在C#中获取Windows控制台应用程序的句柄? 在Windows窗体应用程序中,我通常会尝试使用this.Handle
。
不确定是否有效,但你可以尝试一下:
IntPtr handle = Process.GetCurrentProcess().MainWindowHandle;
以下是一种健壮的方法:
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
中的函数反汇编确定调用方的PID。
GetConsoleWindow()
函数已经半废弃,虽然它仍然会得到支持,但不建议在新代码中使用。 - undefinedGetConsoleWindow
的MSDN文章,实际上并不是他们所称的"弃用" -- 他们不会在未来的任何时候移除它。相反,他们建议使用终端控制序列 -- 这根本不需要获取控制台的窗口句柄。因此,这个事实与当前的问题并不相关。 - undefined试试这个:
[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);
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)
。
因为控制台没有实现 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();
}
Main
是静态方法,所以实际上无法调用this
。因此,我已经将Main
中的所有内容移动到一个名为Start
的新方法中,而Main
所做的只是创建一个新的Program
并调用Start
。private static void Main()
{
new Program().Start();
}
private void Start()
{
AppDomain.CurrentDomain.UnhandledException += CatchUnhandled;
throw new Exception();
}
我认为这样的东西不存在。控制台窗口对应用程序不可访问。你可以尝试迭代进程列表,查找自己的进程名称。如果我没记错,Process
类包含一个属性,用于获取程序的主窗口句柄,这个句柄可能是控制台应用程序的控制台窗口 - 我不确定。
在VB.Net中展示消息框置于控制台窗口之上的另一种版本
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
GetConsoleWindow(),Process.GetCurrentProcess().MainWindowHandle和FindWindowByCaption(IntPtr.Zero, Console.Title)
。每个都返回相同的非零句柄,但当我尝试在SetConsoleMode中使用该句柄时,它抛出“无效的句柄”异常。最后,我尝试了SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), mode | ENABLE_EXTENDED_FLAGS))
,其中STD_INPUT_HANDLE被定义为-10,并且它起作用了。Microsoft的文档建议控制台句柄可能会重新分配,我对这个解决方案不太舒服或满意,但是到目前为止,这是我找到的唯一可以编程地禁用快速编辑模式的方法。GetStdHandle(STD_INPUT_HANDLE)
返回'3',其他调用返回每次运行程序时都会变化的7位值。
FindWindowByCaption
可能是更健壮的解决方案。 - Carl WalshWndProc
让它的调整大小有些不同。 - Bitterblue