How to get rid of the Close Button of a Console Window? - vb.net

I have a customer who is using some old but still needed 32Bit-Software, which is running in a Console Window. It is necessary to disable the Close button because closing the Console using this button causes some serious problems in this software.
I thought about the following way:
1) Find the handle of the active Console
2) Disable the Close Button with GetSystemMenu function
Maybe I'm completely wrong, but I did not manage to find a way to do that so far.
Edit:
The problem is just the Close Button. Of course users can also quit the program by Alt+F4 or Task Manager, but they don't do that. They do use Close Button, that's why I want to disable it.
Of course the best solution would by to disable all ways to cancel the program, but to disable the Close Button would work.
To start the program inside a Windows Form would by one possible solution, too.

To interact with a foreign Window, you need to find it/verify it exists first.
We have different methods to find a Window. Here I'm considering FindWindowEx and Process.GetProcessesByName().
UI Automation and EnumWindows provide other options, eventually.
Store the CMD Window caption somewhere, e.g., an instance Field (it could be a Project settings or anything else you can access at run-time).
Private cmdWindowTitle As String = "The Window Title"
→ FindWindowEx is more useful if you know exactly what the Window title is and it doesn't change over time.
→ Process.GetProcessesByName() can be used to find a Window using the Process name and then verify whether the Process.MainWindowTitle.Contains() at least a partial known string.
If instead the Console Window belongs to the current Process, you just need:
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
Note: Here, I'm assuming the Process Name is cmd and the Window class name is ConsoleWindowClass. It may not be. Change these as required.
Since now the Window has no SystemMenu or Close buttons (we just hid them all), it cannot be closed using ALT+F4 or any other means except using the Task Manager (or wait for it to close naturally).
To close it from your app, send a WM_CLOSE message:
' -- 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 declarations:
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

Related

How to set the Explorer window of a specific file as a child window of TopMost form?

I made some research, but I can't find something really "interesting". I tried my best to find any kind of documentation or questions that are closest to my case as following:
How to find main window title name of application
how to get the window title of a process
How to get the Title Bar Text by its Process Id
getting the name of a process
How do I get list of Process Names running
Check to see if process is running
How To Get Process Owner ID
How to get the title/name of the last active window?
Get Process ID from Window Title
and also
Process.GetProcessesByName Method
The code I am using to open the process window
Private Async Function ParentMethod() As Task
Dim filePath As String = Await Task.Run(
Function()
Return Directory.EnumerateFiles(My.Settings.Cartellasalvataggio, titolo & ".mp3",
SearchOption.AllDirectories).FirstOrDefault()
End Function)
If Not String.IsNullOrEmpty(filePath) Then
LinkLabel1.Text = "File exist already"
LinkLabel1.Visible = True
PictureBox7.Visible = True
Else
MsgBox("it doesn't exist")
End If
End Function
and the helper class
Imports System.IO
Imports System.Runtime.InteropServices
Public Class NativeMethods
<DllImport("shell32.dll", SetLastError:=True)>
Private Shared Function SHOpenFolderAndSelectItems(
pidlFolder As IntPtr, cidl As UInteger,
<[In], MarshalAs(UnmanagedType.LPArray)> apidl As IntPtr(),
dwFlags As UInteger) As Integer
End Function
<DllImport("shell32.dll", SetLastError:=True)>
Private Shared Sub SHParseDisplayName(
<MarshalAs(UnmanagedType.LPWStr)> name As String,
bindingContext As IntPtr, <Out> ByRef pidl As IntPtr,
sfgaoIn As UInteger, <Out> ByRef psfgaoOut As UInteger)
End Sub
Public Shared Sub OpenFolderAndSelectFile(filePath As String)
Dim dirPath As String = Path.GetDirectoryName(filePath)
Dim fileName As String = Path.GetFileName(filePath)
OpenFolderAndSelectFile(dirPath, fileName)
End Sub
Public Shared Sub OpenFolderAndSelectFile(dirPath As String, fileName As String)
Dim nativeFolder As IntPtr
Dim psfgaoOut As UInteger
SHParseDisplayName(dirPath, IntPtr.Zero, nativeFolder, 0, psfgaoOut)
If nativeFolder = IntPtr.Zero Then
' Log error, can't find folder
Return
End If
Dim nativeFile As IntPtr
SHParseDisplayName(Path.Combine(dirPath, fileName),
IntPtr.Zero, nativeFile, 0, psfgaoOut)
Dim fileArray As IntPtr()
If nativeFile = IntPtr.Zero Then
' Open the folder without the file selected if we can't find the file
fileArray = New IntPtr(-1) {}
Else
fileArray = New IntPtr() {nativeFile}
End If
SHOpenFolderAndSelectItems(nativeFolder, CUInt(fileArray.Length), fileArray, 0)
Marshal.FreeCoTaskMem(nativeFolder)
If nativeFile <> IntPtr.Zero Then
Marshal.FreeCoTaskMem(nativeFile)
End If
End Sub
End Class
then calling it with
NativeMethods.OpenFolderAndSelectFile(filepath,filename & "extension"))
Since I am opening the process this way and NOT with Process class, almost all of them are not suitable to be considered for my case as many of them refer to notepad, while I think the explorer window title and ID changes for every file ( obviously), while "notepad" process, stay "notepad".
I also tried BringToFront, but this latter moves a control in front of other controls, but in this case Explorer is not a control, right?
The least I want to do is to
Get a list of active windows & their process names
as It will waste memory and time usage for no reason as I will need to "filter" process to find my process.
Hope we can find a solution to this, Thanks in advance.
Mattia
This is the solution to it using FindWindowW e SetWindowPos Api.
It is showing Explorer folder on top of top most form.
<DllImport("user32.dll", EntryPoint:="FindWindowW")>
Public Shared Function FindWindowW(<MarshalAs(UnmanagedType.LPTStr)> ByVal lpClassName As String, <MarshalAs(UnmanagedType.LPTStr)> ByVal lpWindowName As String) As IntPtr
End Function
<DllImport("user32.dll")>
Shared Function SetWindowPos(ByVal hWnd As IntPtr, ByVal hWndInsertAfter As IntPtr, ByVal X As Integer, ByVal Y As Integer, ByVal cx As Integer, ByVal cy As Integer, ByVal uFlags As UInteger) As Boolean
End Function
Shared ReadOnly HWND_TOPMOST As IntPtr = New IntPtr(-1)
Const SWP_NOSIZE As UInt32 = &H1
Const SWP_NOMOVE As UInt32 = &H2
Const SWP_SHOWWINDOW As UInt32 = &H40
Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
Dim inptr = FindWindowW("CabinetWClass", Nothing)
SetWindowPos(inptr, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE Or SWP_NOSIZE Or SWP_SHOWWINDOW)
End Sub

FindWindowEx in VB.NET

I have a query, how can I kill a process with just the class name using FindWindowEx
If you have been able to reliably get a window handle back from calling FindWindowEx you can then use GetWindowThreadProcessId to find the ID of the process from the window handle.
Once you have the process ID you can find the Process by that ID and call Kill() on it. For example:
Imports System.Runtime.InteropServices
Imports System.Diagnostics
Module Module1
<DllImport("user32.dll", SetLastError:=True)>
Private Function GetWindowThreadProcessId(ByVal hwnd As IntPtr,
ByRef lpdwProcessId As Integer) As Integer
End Function
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
Private Function FindWindowEx(ByVal parentHandle As IntPtr,
ByVal childAfter As IntPtr,
ByVal lclassName As String,
ByVal windowTitle As String) As IntPtr
End Function
Sub Main()
Dim hWnd As IntPtr = FindWindowEx(IntPtr.Zero, IntPtr.Zero, "Chrome_WidgetWin_0", "Spotify Premium")
Dim ProcessId As Integer
GetWindowThreadProcessId(hWnd, ProcessId)
If ProcessId <> 0 Then
Dim Process As Process = Process.GetProcessById(ProcessId)
Process.Kill()
End If
End Sub
End Module
The tricky part will be making sure that you can always get a window handle. Think about cases when there are multiple instances of the handle. You mention class name but it will likely also be necessary to supply the window title to FindWindowEx.
You may also need to consider what happens if calling Process.Kill() should throw an exception, for example, if the user that your program is running under doesn't have the rights to kill that particular process.

Switch focus to a vb.net application on key press

I have a vb.net application which will be minimized to task bar or notification area. Is there any way for me to maximize/focus on that application when user presses a key (even if the user is using any other application).
Tried windows hot key but doesn't focus the app when it is already open.Please help
You require global hotkeys. First, add these functions to your application.
Private Declare Function GetKeyPress Lib "user32" Alias "GetAsyncKeyState" (ByVal key As Integer) As Integer
<DllImport("user32.dll", EntryPoint:="FindWindow", SetLastError:=True)> _
Private Shared Function FindWindow(lpClassName As String, lpWindowName As String) As IntPtr
End Function
<DllImport("user32.dll")> _
Private Shared Function ShowWindow(hWnd As IntPtr, nCmdShow As Integer) As Boolean
End Function
<DllImport("user32.dll")> _
Private Shared Function SetForegroundWindow(hWnd As IntPtr) As Integer
End Function
Next, add a Timer and in its Timer.Tick Event, use this function like this:
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
If GetKeyPress(Keys.LControlKey) And GetKeyPress(Keys.A) Then
Dim Handle As IntPtr = Process.GetProcessById(2916).MainWindowHandle
ShowWindow(Handle, 9)
SetForegroundWindow(Handle)
End If
End Sub
Set the Timer Interval to 150 to avoid repeated keypresses and make sure to enable the timer in the Form Load event.

how to hide/unhide window by process name or processid using VB.NET?

im trying to hide/unhide window process by name or PID i have tried
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
Private Shared Function ShowWindow(ByVal hwnd As IntPtr, ByVal nCmdShow As ShowWindowCommands) As Boolean
End Function
but ShowWindowCommands what is and how to find it ?
thanks !
there is many ways to do this the easiest one should be this :
'GENERAL IMPORT
Imports System.Runtime.InteropServices
'FORM CLASS DECLARATION
<DllImport("user32.dll")> _
Private Shared Function ShowWindow(ByVal hWnd As IntPtr, ByVal nCmdShow As Integer) As Boolean
End Function
'then if you want to hide firefox's window :
Dim mywindow As Integer
Dim processRunning As Process() = Process.GetProcesses()
For Each pr As Process In processRunning
If pr.ProcessName = "Firefox" Then
mywindow = pr.MainWindowHandle.ToInt32()
ShowWindow(mywindow , 0)
End If
Next
You can do it like this:
Form.FromHandle(Process.GetProcessById(PID).MainWindowHandle).Show()

How to add more items to the window context menu

I'm making an app for the company I work for and I was wondering how to customise the window's context menu like PuTTY's (aka, it has "New Session..." etc.). I've looked all over Google and can't find the answer I'm looking for.
make a new module and add Imports System.Runtime.InteropServices on top
then declare this
<Flags()> _
Public Enum MenuFlags As Integer
MF_BYPOSITION = 1024
MF_REMOVE = 4096
MF_SEPARATOR = 2048
MF_STRING = 0
End Enum
<DllImport("user32.dll", CallingConvention:=CallingConvention.Cdecl)> _
Public Function GetSystemMenu(ByVal hWnd As IntPtr, Optional ByVal bRevert As Boolean = False) As IntPtr
End Function
<DllImport("user32.dll", CharSet:=CharSet.Auto)> _
Public Function AppendMenu(ByVal hMenu As IntPtr, ByVal uFlags As MenuFlags, ByVal uIDNewItem As Int32, ByVal lpNewItem As String) As Boolean
End Function
then on your form load handler add this code
Dim sysmenu As IntPtr = GetSystemMenu(Me.Handle)
AppendMenu(sysmenu, MenuFlags.MF_STRING, &H1FFF, "Hello")
then, in order to be able to capture the user click on your new menu item, you have to implement this function which will capture all messages, just add it to your form code
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
If m.Msg = WM_SYSCOMMAND Then
If m.WParam.ToInt32 = &H1FFF Then
' your menu item is clicked, call a function here
End If
End If
MyBase.WndProc(m)
End Sub