如何摆脱控制台 Window 的关闭按钮?
How to get rid of the Close Button of a Console Window?
我有一个客户正在使用一些旧的但仍然需要 32 位软件,它是 运行 在控制台 Window 中。有必要禁用关闭按钮,因为使用此按钮关闭控制台会导致此软件出现一些严重问题。
我想到了以下方法:
1) 找到活动控制台的句柄
2) 使用 GetSystemMenu
函数禁用关闭按钮
也许我完全错了,但到目前为止我还没有找到办法。
编辑:
问题只是关闭按钮。当然用户也可以通过Alt+F4或任务管理器退出程序,但他们不会那样做。他们确实使用关闭按钮,这就是我要禁用它的原因。
当然,最好的解决方案是禁用所有取消程序的方法,但禁用关闭按钮也可以。
要在 Windows 表单中启动程序,也可以通过一种可能的解决方案。
要与外国Window互动,您需要先找到it/verify它存在。
我们有不同的方法来找到 Window。我在这里考虑 FindWindowEx and Process.GetProcessesByName()。
UI Automation and EnumWindows 最终提供其他选项。
将 CMD Window 标题存储在某处,例如实例字段(它可以是项目设置或您可以在 run-time 访问的任何其他内容)。
Private cmdWindowTitle As String = "The Window Title"
→ FindWindowEx
如果您确切知道 Window 标题是什么并且它不会随时间改变,那么 FindWindowEx
会更有用。
→ Process.GetProcessesByName()
可用于使用进程名称查找 Window,然后验证 Process.MainWindowTitle.Contains()
是否至少是部分已知字符串。
如果控制台 Window 属于当前进程,您只需要:
Process.GetCurrentProcess().MainWindowHandle
' -- If the Console Window belongs to the current Process: --
Dim cmdWindowHandle = Process.GetCurrentProcess().MainWindowHandle
' -----------------------------------------------------------
' -- Find it when the exact Window title is known: --
Dim cmdWindowHandle As IntPtr = NativeMethods.GetCmdWindowByCaption(cmdWindowTitle)
' -----------------------------------------------------------
' -- Find it when only a partial caption is available: --
Dim cmdWindowHandle As IntPtr = IntPtr.Zero
Dim cmdProc = Process.GetProcessesByName("cmd").
FirstOrDefault(Function(p) p.MainWindowTitle.Contains(cmdWindowTitle))
If cmdProc IsNot Nothing Then
cmdWindowHandle = cmdProc.MainWindowHandle
End If
' -----------------------------------------------------------
' Choose one of the above, then, in any case:
If cmdWindowHanle <> IntPtr.Zero Then
NativeMethods.WindowDisableSysMenu(cmdWindowHandle)
End If
注意:在这里,我假设进程名称是 cmd
并且 Window class 名字是 ConsoleWindowClass
。它可能不是。根据需要更改这些。
由于现在 Window 没有系统菜单或关闭按钮(我们只是将它们全部隐藏),无法使用 ALT+F4 或任何其他方式关闭它,除了使用任务管理器(或等待它自然关闭)。
要从您的应用中关闭它,请发送 WM_CLOSE
消息:
' -- find the Window as described before --
Dim cmdWindowHandle As IntPtr = NativeMethods.GetCmdWindowByCaption(cmdWindowTitle)
If Not cmdWindowHandle.Equals(IntPtr.Zero) Then
NativeMethods.SendCloseMessage(cmdWindowHandle)
End If
NativeMethods
声明:
Public Class NativeMethods
Private Const WM_CLOSE As Integer = &H10
Public Enum WinStyles As UInteger
WS_MAXIMIZE = &H1000000
WS_MAXIMIZEBOX = &H10000
WS_MINIMIZE = &H20000000
WS_MINIMIZEBOX = &H20000
WS_SYSMENU = &H80000
End Enum
Public Enum GWL_Flags As Integer
GWL_STYLE = -16
GWL_EXSTYLE = -20
End Enum
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
Private Shared Function SendMessage(hWnd As IntPtr, uMsg As WinMessage, wParam As Integer, lParam As Integer) As Integer
End Function
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
Private Shared Function FindWindowEx(hwndParent As IntPtr, hwndChildAfter As IntPtr, lpszClass As String, lpszWindow As String) As IntPtr
End Function
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
Private Shared Function GetWindowLong(hWnd As IntPtr, nIndex As GWL_Flags) As IntPtr
End Function
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
Private Shared Function SetWindowLong(hWnd As IntPtr, nIndex As GWL_Flags, dwNewLong As IntPtr) As IntPtr
End Function
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
Private Shared Function GetWindowLongPtr(hWnd As IntPtr, nIndex As GWL_Flags) As IntPtr
End Function
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
Private Shared Function SetWindowLongPtr(hWnd As IntPtr, nIndex As GWL_Flags, dwNewLong As IntPtr) As IntPtr
End Function
' Public wrappers
Public Shared Function GetWindowLongUni(hWnd As IntPtr, nIndex As GWL_Flags) As Integer
If IntPtr.Size = 8 Then
Return GetWindowLongPtr(hWnd, nIndex).ToInt32()
Else
Return GetWindowLong(hWnd, nIndex).ToInt32()
End If
End Function
Public Shared Function SetWindowLongUni(hWnd As IntPtr, nIndex As GWL_Flags, dwNewLong As Integer) As Integer
If IntPtr.Size = 8 Then
Return SetWindowLongPtr(hWnd, nIndex, New IntPtr(dwNewLong)).ToInt32()
Else
Return SetWindowLong(hWnd, nIndex, New IntPtr(dwNewLong)).ToInt32()
End If
End Function
Public Shared Function GetCmdWindowByCaption(cmdCaption As String) As IntPtr
Return FindWindowEx(IntPtr.Zero, IntPtr.Zero, "ConsoleWindowClass", cmdCaption)
End Function
Public Shared Sub WindowDisableSysMenu(windowHandle As IntPtr)
Dim styles As Integer = GetWindowLongUni(windowHandle, GWL_Flags.GWL_STYLE)
styles = styles And Not CInt(WinStyles.WS_SYSMENU)
SetWindowLongUni(windowHandle, GWL_Flags.GWL_STYLE, styles)
End Sub
Public Shared Sub SendCloseMessage(windowHandle As IntPtr)
SendMessage(windowHandle, WM_CLOSE, 0, 0)
End Sub
End Class
我有一个客户正在使用一些旧的但仍然需要 32 位软件,它是 运行 在控制台 Window 中。有必要禁用关闭按钮,因为使用此按钮关闭控制台会导致此软件出现一些严重问题。
我想到了以下方法:
1) 找到活动控制台的句柄
2) 使用 GetSystemMenu
函数禁用关闭按钮
也许我完全错了,但到目前为止我还没有找到办法。
编辑:
问题只是关闭按钮。当然用户也可以通过Alt+F4或任务管理器退出程序,但他们不会那样做。他们确实使用关闭按钮,这就是我要禁用它的原因。
当然,最好的解决方案是禁用所有取消程序的方法,但禁用关闭按钮也可以。
要在 Windows 表单中启动程序,也可以通过一种可能的解决方案。
要与外国Window互动,您需要先找到it/verify它存在。
我们有不同的方法来找到 Window。我在这里考虑 FindWindowEx and Process.GetProcessesByName()。 UI Automation and EnumWindows 最终提供其他选项。
将 CMD Window 标题存储在某处,例如实例字段(它可以是项目设置或您可以在 run-time 访问的任何其他内容)。
Private cmdWindowTitle As String = "The Window Title"
→ FindWindowEx
如果您确切知道 Window 标题是什么并且它不会随时间改变,那么 FindWindowEx
会更有用。
→ Process.GetProcessesByName()
可用于使用进程名称查找 Window,然后验证 Process.MainWindowTitle.Contains()
是否至少是部分已知字符串。
如果控制台 Window 属于当前进程,您只需要:
Process.GetCurrentProcess().MainWindowHandle
' -- If the Console Window belongs to the current Process: --
Dim cmdWindowHandle = Process.GetCurrentProcess().MainWindowHandle
' -----------------------------------------------------------
' -- Find it when the exact Window title is known: --
Dim cmdWindowHandle As IntPtr = NativeMethods.GetCmdWindowByCaption(cmdWindowTitle)
' -----------------------------------------------------------
' -- Find it when only a partial caption is available: --
Dim cmdWindowHandle As IntPtr = IntPtr.Zero
Dim cmdProc = Process.GetProcessesByName("cmd").
FirstOrDefault(Function(p) p.MainWindowTitle.Contains(cmdWindowTitle))
If cmdProc IsNot Nothing Then
cmdWindowHandle = cmdProc.MainWindowHandle
End If
' -----------------------------------------------------------
' Choose one of the above, then, in any case:
If cmdWindowHanle <> IntPtr.Zero Then
NativeMethods.WindowDisableSysMenu(cmdWindowHandle)
End If
注意:在这里,我假设进程名称是 cmd
并且 Window class 名字是 ConsoleWindowClass
。它可能不是。根据需要更改这些。
由于现在 Window 没有系统菜单或关闭按钮(我们只是将它们全部隐藏),无法使用 ALT+F4 或任何其他方式关闭它,除了使用任务管理器(或等待它自然关闭)。
要从您的应用中关闭它,请发送 WM_CLOSE
消息:
' -- find the Window as described before --
Dim cmdWindowHandle As IntPtr = NativeMethods.GetCmdWindowByCaption(cmdWindowTitle)
If Not cmdWindowHandle.Equals(IntPtr.Zero) Then
NativeMethods.SendCloseMessage(cmdWindowHandle)
End If
NativeMethods
声明:
Public Class NativeMethods
Private Const WM_CLOSE As Integer = &H10
Public Enum WinStyles As UInteger
WS_MAXIMIZE = &H1000000
WS_MAXIMIZEBOX = &H10000
WS_MINIMIZE = &H20000000
WS_MINIMIZEBOX = &H20000
WS_SYSMENU = &H80000
End Enum
Public Enum GWL_Flags As Integer
GWL_STYLE = -16
GWL_EXSTYLE = -20
End Enum
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
Private Shared Function SendMessage(hWnd As IntPtr, uMsg As WinMessage, wParam As Integer, lParam As Integer) As Integer
End Function
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
Private Shared Function FindWindowEx(hwndParent As IntPtr, hwndChildAfter As IntPtr, lpszClass As String, lpszWindow As String) As IntPtr
End Function
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
Private Shared Function GetWindowLong(hWnd As IntPtr, nIndex As GWL_Flags) As IntPtr
End Function
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
Private Shared Function SetWindowLong(hWnd As IntPtr, nIndex As GWL_Flags, dwNewLong As IntPtr) As IntPtr
End Function
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
Private Shared Function GetWindowLongPtr(hWnd As IntPtr, nIndex As GWL_Flags) As IntPtr
End Function
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
Private Shared Function SetWindowLongPtr(hWnd As IntPtr, nIndex As GWL_Flags, dwNewLong As IntPtr) As IntPtr
End Function
' Public wrappers
Public Shared Function GetWindowLongUni(hWnd As IntPtr, nIndex As GWL_Flags) As Integer
If IntPtr.Size = 8 Then
Return GetWindowLongPtr(hWnd, nIndex).ToInt32()
Else
Return GetWindowLong(hWnd, nIndex).ToInt32()
End If
End Function
Public Shared Function SetWindowLongUni(hWnd As IntPtr, nIndex As GWL_Flags, dwNewLong As Integer) As Integer
If IntPtr.Size = 8 Then
Return SetWindowLongPtr(hWnd, nIndex, New IntPtr(dwNewLong)).ToInt32()
Else
Return SetWindowLong(hWnd, nIndex, New IntPtr(dwNewLong)).ToInt32()
End If
End Function
Public Shared Function GetCmdWindowByCaption(cmdCaption As String) As IntPtr
Return FindWindowEx(IntPtr.Zero, IntPtr.Zero, "ConsoleWindowClass", cmdCaption)
End Function
Public Shared Sub WindowDisableSysMenu(windowHandle As IntPtr)
Dim styles As Integer = GetWindowLongUni(windowHandle, GWL_Flags.GWL_STYLE)
styles = styles And Not CInt(WinStyles.WS_SYSMENU)
SetWindowLongUni(windowHandle, GWL_Flags.GWL_STYLE, styles)
End Sub
Public Shared Sub SendCloseMessage(windowHandle As IntPtr)
SendMessage(windowHandle, WM_CLOSE, 0, 0)
End Sub
End Class