FindWindowEx in VB.NET - 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.

Related

How to get rid of the Close Button of a Console Window?

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

IsIconic() behavior varies depending on the environment

I'd like to use IsIconic function to check whether the specified window is minimized (iconic).
IsIconic function
https://msdn.microsoft.com/ja-jp/library/windows/desktop/ms633527(v=vs.85).aspx
My code works properly in certain environments, but it does not work in other environments.
Imports System.Runtime.InteropServices
Public Class Form1
<DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
Public Shared Function IsIconic(ByVal hWnd As IntPtr) As Long
End Function
<DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
Private Shared Function FindWindow(ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr
End Function
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim hWnd As IntPtr = FindWindow(Nothing, "Google - Google Chrome")
If CBool(IsIconic(hWnd)) Then
Debug.WriteLine("Chrome is iconic.")
Else
Debug.WriteLine("Chrome is not iconic.")
End If
End Sub
End Class
When I run it with 64 bit Windows 7 OS, I can determine correctly whether Chrome is iconified.
However, when running on 32 bit Windows 7 OS, "Chrome is iconic." is always displayed in the immediate window regardless of whether or not Chrome is actually iconified.
Could you tell me why this difference occurs?
Is this phenomenon caused by the difference in OS bit number? Or something else?
Your return type of the IsIconic() function is wrong.
This:
Public Shared Function IsIconic(ByVal hWnd As IntPtr) As Long
...should be this:
Public Shared Function IsIconic(ByVal hWnd As IntPtr) As <MarshalAs(UnmanagedType.Bool)> Boolean
This also means that you can (and should) change:
If CBool(IsIconic(hWnd)) Then
Debug.WriteLine("Chrome is iconic.")
Else
...
to:
If IsIconic(hWnd) Then
Debug.WriteLine("Chrome is iconic.")
Else
...

VB: ShowWindow focus issue

I'm attempting to send automated keystrokes to an application that does not support copy+paste via a small VB form. The form loads data from a text file and uses SendKeys to fire it over once I click a button.
Everything appears to work except for the ShowWindow portion. I'm currently testing using Notepad and, with one exception, I can't seem to get ShowWindow to kick focus to Notepad. Obviously I'm worried it will do the same to the application I'll eventually be running this against (I don't currently have access to it). The only ShowWindow parameter that makes Notepad active is SW_SHOWMAXIMIZED. SW_SHOW and SW_SHOWNORMAL don't appear to do anything while SW_RESTORE will restore Notepad if minimized but my VB form remains the active window.
I'm not a programmer but I had made the mistake of telling my boss I dabbled in Pascal Turbo in high school (over a decade ago) so I'm the one stuck with trying to make this work. My current code cobbled together from S.O. and other sources:
(I'm running Windows 7 and using MVSE2013)
Imports System.Runtime.InteropServices
Public Class Form1
Private Declare Function FindWindow _
Lib "user32" _
Alias "FindWindowA" _
(ByVal lpClassName As String, _
ByVal lpWindowName As String) As IntPtr
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
Private Shared Function ShowWindow(ByVal hwnd As IntPtr, ByVal nCmdShow As ShowWindowCommands) As Boolean
End Function
Enum ShowWindowCommands As Integer
SW_SHOWNORMAL = 1
SW_SHOWMAXIMIZED = 3
SW_RESTORE = 9
End Enum
Private Sub Form1_Load
[form]
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
Dim lHwnd As IntPtr = FindWindow("Notepad", vbNullString)
If lHwnd <> IntPtr.Zero Then
ShowWindow(lHwnd, ShowWindowCommands.SW_SHOWNORMAL)
SendKeys.Send(TextBox1.Text)
Else
[blah blah error handling]
End If
End Sub
I'd try another technique like SetForegroundWindow but I read it doesn't play nice with Windows 7.
Found what I hope will be a passable workaround from PInvoke. I ended up swapping this block:
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
Private Shared Function ShowWindow(ByVal hwnd As IntPtr, ByVal nCmdShow _
As ShowWindowCommands) As Boolean
End Function
For this:
Public Declare Function BringWindowToTop Lib "user32" (ByVal hwnd As IntPtr) As Boolean
And then this line:
ShowWindow(lHwnd, ShowWindowCommands.SW_SHOWNORMAL)
For this:
BringWindowToTop(lHwnd)
I realize there are functional differences between the two but the change works in my specific instance so I'm happy.

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 use <DllImport> in VB.NET?

How should I DLLImport things in VB.NET? An example would be:
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
Private Shared Function GetWindowText(ByVal hwnd As IntPtr, ByVal lpString As StringBuilder, ByVal cch As Integer) As Integer
End Function
If I put it inside a Class or somewhere else, I get "DLLimport is not defined" I am using Visual Studio 2008 Professional
You have to add Imports System.Runtime.InteropServices to the top of your source file.
Alternatively, you can fully qualify attribute name:
<System.Runtime.InteropService.DllImport("user32.dll", _
SetLastError:=True, CharSet:=CharSet.Auto)> _
Imports System.Runtime.InteropServices
I know this has already been answered, but here is an example for the people who are trying to use SQL Server Types in a vb project:
Imports System
Imports System.IO
Imports System.Runtime.InteropServices
Namespace SqlServerTypes
Public Class Utilities
<DllImport("kernel32.dll", CharSet:=CharSet.Auto, SetLastError:=True)>
Public Shared Function LoadLibrary(ByVal libname As String) As IntPtr
End Function
Public Shared Sub LoadNativeAssemblies(ByVal rootApplicationPath As String)
Dim nativeBinaryPath = If(IntPtr.Size > 4, Path.Combine(rootApplicationPath, "SqlServerTypes\x64\"), Path.Combine(rootApplicationPath, "SqlServerTypes\x86\"))
LoadNativeAssembly(nativeBinaryPath, "msvcr120.dll")
LoadNativeAssembly(nativeBinaryPath, "SqlServerSpatial140.dll")
End Sub
Private Shared Sub LoadNativeAssembly(ByVal nativeBinaryPath As String, ByVal assemblyName As String)
Dim path = System.IO.Path.Combine(nativeBinaryPath, assemblyName)
Dim ptr = LoadLibrary(path)
If ptr = IntPtr.Zero Then
Throw New Exception(String.Format("Error loading {0} (ErrorCode: {1})", assemblyName, Marshal.GetLastWin32Error()))
End If
End Sub
End Class
End Namespace
I saw in getwindowtext (user32) on pinvoke.net that you can place a MarshalAs statement to state that the StringBuffer is equivalent to LPSTR.
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Ansi)> _
Public Function GetWindowText(hwnd As IntPtr, <MarshalAs(UnManagedType.LPStr)>lpString As System.Text.StringBuilder, cch As Integer) As Integer
End Function
You can also try this
Private Declare Function GetWindowText Lib "user32.dll" (ByVal hwnd As IntPtr, ByVal lpString As StringBuilder, ByVal cch As Integer) As Integer
I always use Declare Function instead of DllImport...
Its more simply, its shorter and does the same