VB.NET – Get visible windows

June 3, 2010 — 9 Comments

Here’s yet another .NET wrapper/helper that I’ve written for some Windows API functionality. This time my code makes use of about 6 different Windows APIs to provide you with a method that will return a list of all windows that are currently open on the computer along with their handle, title bar text, class name and the process that owns the window.

Also might be worth mentioning: I’m going to be releasing a class library soon full of lots of these “.NET friendly” methods that I’ve written to make calling specific Windows APIs simpler and easier. So you can just add a reference to the class library DLL and then you can avoid having to use the APIs directly as you can just use my nice simple .NET methods, with no need to marshal anything across to unmanaged code. Check back on this blog soon if that sounds like something you would be interested in.

Anyway, here are the API definitions for this particular wrapper, which as explained above, will return a list of all top level windows that are currently visible. Note that you will get a few windows that you would not see in the Applications tab of Task Manager, such as the window that works with the system tray and on windows 7 the start button, so if you do not want to display those in your application then you will have to manually filter them out of the list returned by my method below (or modify my method obviously).

API DEFINITIONS (define in a class named NativeAPI and Import System.Runtime.InteropServices):

”’ <summary>
”’ Used by the EnumWindows API to specify a callback function that should be called once for each window found
”’ </summary>
<UnmanagedFunctionPointerAttribute(CallingConvention.StdCall)> _
Public Delegate Function EnumWindowsProc(ByVal hwnd As System.IntPtr, ByVal lparam As System.IntPtr) As Boolean

”’ <summary>
”’ Enumerates all top level windows and calls the specified callback once for each window found
”’ </summary>
”’ <param name=”lpEnumFunc”></param>
”’ <param name=”lParam”></param>
<DllImportAttribute(“user32.dll”, EntryPoint:=“EnumWindows”)> _
Public Shared Function EnumWindows(ByVal lpEnumFunc As EnumWindowsProc, <MarshalAsAttribute(UnmanagedType.SysInt)> ByVal lParam As IntPtr) As <MarshalAsAttribute(UnmanagedType.Bool)> Boolean
End Function

”’ <summary>
”’ Checks to see if a window is visible
”’ </summary>
”’ <param name=”hWnd”>A handle to the window to check the visibility of</param>
<DllImportAttribute(“user32.dll”, EntryPoint:=“IsWindowVisible”)> _
Public Shared Function IsWindowVisible(<InAttribute()> ByVal hWnd As System.IntPtr) As <MarshalAsAttribute(UnmanagedType.Bool)> Boolean
End Function

”’ <summary>
”’ Gets the title bar text from the specified window
”’ </summary>
”’ <param name=”hWnd”>A handle to the window to get the text from</param>
”’ <param name=”lpString”>OUTPUT – the window text will be copied to this object</param>
”’ <param name=”nMaxCount”>The maximimum number of characters to get from the window title and copy to the object in the lpString parameter</param>
<DllImportAttribute(“user32.dll”, EntryPoint:=“GetWindowTextW”)> _
Public Shared Function GetWindowText(<InAttribute()> ByVal hWnd As System.IntPtr, <OutAttribute(), MarshalAsAttribute(UnmanagedType.LPWStr)> ByVal lpString As System.Text.StringBuilder, ByVal nMaxCount As Integer) As Integer
End Function

”’ <summary>
”’ Gets the number of characters present in a window’s title bar text
”’ </summary>
”’ <param name=”hWnd”>A handle to the window to get the text length from</param>
<DllImportAttribute(“user32.dll”, EntryPoint:=“GetWindowTextLengthW”)> _
Public Shared Function GetWindowTextLength(<InAttribute()> ByVal hWnd As System.IntPtr) As Integer
End Function

”’ <summary>
”’ Gets the thread and process ID that created a window
”’ </summary>
”’ <param name=”hWnd”>A handle to the window to get the thread and process ID for</param>
”’ <param name=”lpdwProcessId”>OUTPUT – The process ID will be copied to this Integer, but only if the Integer passed in is not 0</param>
<DllImportAttribute(“user32.dll”, EntryPoint:=“GetWindowThreadProcessId”)> _
Public Shared Function GetWindowThreadProcessId(<InAttribute()> ByVal hWnd As System.IntPtr, <OutAttribute()> ByRef lpdwProcessId As Integer) As UInteger
End Function

”’ <summary>
”’ Gets the class name for a specified window
”’ </summary>
”’ <param name=”hWnd”>A handle to the window to get the class name of</param>
”’ <param name=”lpClassName”>OUTPUT – The class name will be stored in this object</param>
”’ <param name=”nMaxCount”>The maximum number of characters to copy to the object in the lpClassName parameter</param>
<DllImportAttribute(“user32.dll”, EntryPoint:=“GetClassNameW”)> _
Public Shared Function GetClassName(<InAttribute()> ByVal hWnd As System.IntPtr, <OutAttribute(), MarshalAsAttribute(UnmanagedType.LPWStr)> ByVal lpClassName As System.Text.StringBuilder, ByVal nMaxCount As Integer) As Integer
End Function

MY .NET WRAPPER/HELPER METHOD (this is the method you would call when you wanted to get a list of the windows, instead of using the Windows APIs directly each time):

”’ <summary>
”’ Returns a list of all visible top level windows
”’ </summary>
Public Shared Function GetWindows() As List(Of ManagedWindow)
Dim WindowList As New List(Of ManagedWindow)
Dim ListHandle As GCHandle = GCHandle.Alloc(WindowList)
Try
NativeAPI.EnumWindows(New NativeAPI.EnumWindowsProc(AddressOf EnumWindowsCallBack), GCHandle.ToIntPtr(ListHandle))
Finally
ListHandle.Free()
End Try
Return WindowList
End Function

”’ <summary>
”’ Callback function for the EnumWindows API – this function will be called once for each top level window that is found
”’ </summary>
Private Shared Function EnumWindowsCallBack(ByVal handle As IntPtr, ByVal lParam As IntPtr) As Boolean
If NativeAPI.IsWindowVisible(handle) Then
Dim TitleBuilder As New System.Text.StringBuilder(NativeAPI.GetWindowTextLength(handle) + 1)
Dim WndList As List(Of ManagedWindow) = DirectCast(GCHandle.FromIntPtr(lParam).Target, List(Of ManagedWindow))
NativeAPI.GetWindowText(handle, TitleBuilder, TitleBuilder.Capacity)
Dim ProcessID As Integer = -1
NativeAPI.GetWindowThreadProcessId(handle, ProcessID)
Dim ClassNameBuilder As New System.Text.StringBuilder(255)
NativeAPI.GetClassName(handle, ClassNameBuilder, ClassNameBuilder.Capacity)
WndList.Add(New ManagedWindow(handle, TitleBuilder.ToString, Process.GetProcessById(ProcessID), ClassNameBuilder.ToString))
End If
Return True ‘Tells the EnumWindows API to keep going and call this function again for the next window
End Function

and here is the definition of my simple ManagedWindow class, which as you can see is what is used to hold information about the windows and is what is returned by the wrapper method (well, a list of them)

”’ <summary>
”’ Holds information about an application window
”’ </summary>
Public Class ManagedWindow

Private _Title As String = String.Empty
”’ <summary>
”’ The text displayed in the window’s title bar
”’ </summary>
Public Property Title() As String
Get
Return _Title
End Get
Set(ByVal value As String)
_Title = value
End Set
End Property

Private _Handle As IntPtr
”’ <summary>
”’ A handle to the window
”’ </summary>
Public Property Handle() As IntPtr
Get
Return _Handle
End Get
Set(ByVal value As IntPtr)
_Handle = value
End Set
End Property

Private _OwningProcess As Process
”’ <summary>
”’ The process that created this window
”’ </summary>
Public Property OwningProcess() As Process
Get
Return _OwningProcess
End Get
Set(ByVal value As Process)
_OwningProcess = value
End Set
End Property

Private _ClassName As String = String.Empty
”’ <summary>
”’ The name of the class that this window belongs to
”’ </summary>
Public Property ClassName() As String
Get
Return _ClassName
End Get
Set(ByVal value As String)
_ClassName = value
End Set
End Property

”’ <summary>
”’ Creates a new instance of the ManagedWindow class
”’ </summary>
”’ <remarks></remarks>
Public Sub New()
End Sub

”’ <summary>
”’ Creates a new instance of the ManagedWindow class and populates each of the properties within
”’ </summary>
”’ <param name=”Hwnd”>A handle to the window</param>
”’ <param name=”TitleText”>The text displayed in the window’s title bar</param>
”’ <param name=”Owner”>The process that created this window. Can be Nothing</param>
”’ <param name=”NativeClassName”>The name of the class that this window belongs to</param>
Public Sub New(ByVal Hwnd As IntPtr, ByVal TitleText As String, ByVal Owner As Process, ByVal NativeClassName As String)
_Handle = Hwnd
_Title = TitleText
_OwningProcess = Owner
_ClassName = NativeClassName
End Sub

”’ <summary>
”’ A string representation of the handle, class name, title and process name for this window
”’ </summary>
Public Overrides Function ToString() As String
If OwningProcess Is Nothing Then
Return Handle.ToString & ” —- “ & ClassName & ” —- “ & Title
Else
Return Handle.ToString & ” —- “ & ClassName & ” —- “ & Title & ” —- “ & OwningProcess.ProcessName
End If
End Function

End Class

Hope someone finds that useful 🙂

Chris

9 responses to VB.NET – Get visible windows

  1. 

    Chris, can you explain me how i can work with function?

    Its possible create a refresh list? type: if any windows open add to the existent list.

    Best Regards,

  2. 

    If you wanted it to constantly refresh then you would have to keep running the function over and over again, by using a Timer.

  3. 

    i like convert it to array and adding a items to array every time iam open a new window.

    Its need a timer? or..

  4. 

    copied and pasted the code, I obtain a

    ‘GCHandle’ name not declared error

    and in effect I can’s see in all the code any declaration about it…

    • 

      As the MSDN page tells you it is in the System.Runtime.InteropServices namespace, then you either need to fully qualify the GCHandle class (e.g System.Runtime.InteropServices.GCHandle) or import that namespace at the top of your code.

  5. 

    Hi, thanks for posting that code, I’m not sure how to output the list of windows with their titles to the user?

    Any help!

    Thanks
    Aaron

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s