Apr
07
2007
Last time I made an example of how to enumerate windows. This time I present to you a class that greatly simplifies the process of searching for specific windows, types of windows, windows belonging to a specific process, having a specific text. You can search for any number of these parameters at the same time, using regular expressions for all string matches to provide optimal flexibility.
using System.Runtime.InteropServices; using System; using System.Text; using System.Text.RegularExpressions; using System.Diagnostics; namespace Searching_for_windows { class Program { // Win32 constants. const int WM_GETTEXT = 0x000D; const int WM_GETTEXTLENGTH = 0x000E; // Win32 functions that have all been used in previous blogs. [DllImport("User32.Dll")] private static extern void GetClassName(int hWnd, StringBuilder s, int nMaxCount); [DllImport("User32.dll")] private static extern int GetWindowText(int hWnd, StringBuilder text, int count); [DllImport("User32.dll")] private static extern Int32 SendMessage(int hWnd, int Msg, int wParam, StringBuilder lParam); [DllImport("User32.dll")] private static extern Int32 SendMessage(int hWnd, int Msg, int wParam, int lParam); // Main entrypoint function static void Main(string[] args) { WindowFinder wf = new WindowFinder(); // Find all Internet Explorer instances wf.FindWindows(0, null, null, new Regex("iexplore"), new WindowFinder.FoundWindowCallback(foundWindow)); // Find all visual studio instances wf.FindWindows(0, null, new Regex(" - Microsoft Visual Studio"), new Regex("devenv"), new WindowFinder.FoundWindowCallback(foundWindow)); Console.WriteLine("Done"); Console.Read(); } // Gets called each time a window is found by the WindowFinder class. private static bool foundWindow(int handle) { // Print the window info. printWindowInfo(handle); // Continue on with next window. return true; } // Prints basic properties of a window, uses function already used in previous blogs. private static void printWindowInfo(int handle) { // Get the class. StringBuilder sbClass = new StringBuilder(256); GetClassName(handle, sbClass, sbClass.Capacity); // Get the text. int txtLength = SendMessage(handle, WM_GETTEXTLENGTH, 0, 0); StringBuilder sbText = new StringBuilder(txtLength + 1); SendMessage(handle, WM_GETTEXT, sbText.Capacity, sbText); // Now we can write out the information we have on the window. Console.WriteLine("Handle: " + handle); Console.WriteLine("Class : " + sbClass); Console.WriteLine("Text : " + sbText); Console.WriteLine(); } } /// <summary> /// A class used for finding windows based upon their class, title, process and parent window handle. /// </summary> public class WindowFinder { // Win32 constants. const int WM_GETTEXT = 0x000D; const int WM_GETTEXTLENGTH = 0x000E; // Win32 functions that have all been used in previous blogs. [DllImport("User32.Dll")] private static extern void GetClassName(int hWnd, StringBuilder s, int nMaxCount); [DllImport("User32.dll")] private static extern int GetWindowText(int hWnd, StringBuilder text, int count); [DllImport("User32.dll")] private static extern Int32 SendMessage(int hWnd, int Msg, int wParam, StringBuilder lParam); [DllImport("User32.dll")] private static extern Int32 SendMessage(int hWnd, int Msg, int wParam, int lParam); [DllImport("user32")] private static extern int GetWindowThreadProcessId(int hWnd, out int lpdwProcessId); // EnumChildWindows works just like EnumWindows, except we can provide a parameter that specifies the parent // window handle. If this is NULL or zero, it works just like EnumWindows. Otherwise it'll only return windows // whose parent window handle matches the hWndParent parameter. [DllImport("user32.Dll")] private static extern Boolean EnumChildWindows(int hWndParent, PChildCallBack lpEnumFunc, int lParam); // The PChildCallBack delegate that we used with EnumWindows. private delegate bool PChildCallBack(int hWnd, int lParam); // This is an event that is run each time a window was found that matches the search criterias. The boolean // return value of the delegate matches the functionality of the PChildCallBack delegate function. private event FoundWindowCallback foundWindow; public delegate bool FoundWindowCallback(int hWnd); // Members that'll hold the search criterias while searching. private int parentHandle; private Regex className; private Regex windowText; private Regex process; // The main search function of the WindowFinder class. The parentHandle parameter is optional, taking in a zero if omitted. // The className can be null as well, in this case the class name will not be searched. For the window text we can input // a Regex object that will be matched to the window text, unless it's null. The process parameter can be null as well, // otherwise it'll match on the process name (Internet Explorer = "iexplore"). Finally we take the FoundWindowCallback // function that'll be called each time a suitable window has been found. public void FindWindows(int parentHandle, Regex className, Regex windowText, Regex process, FoundWindowCallback fwc) { this.parentHandle = parentHandle; this.className = className; this.windowText = windowText; this.process = process; // Add the FounWindowCallback to the foundWindow event. foundWindow = fwc; // Invoke the EnumChildWindows function. EnumChildWindows(parentHandle, new PChildCallBack(enumChildWindowsCallback), 0); } // This function gets called each time a window is found by the EnumChildWindows function. The foun windows here // are NOT the final found windows as the only filtering done by EnumChildWindows is on the parent window handle. private bool enumChildWindowsCallback(int handle, int lParam) { // If a class name was provided, check to see if it matches the window. if (className != null) { StringBuilder sbClass = new StringBuilder(256); GetClassName(handle, sbClass, sbClass.Capacity); // If it does not match, return true so we can continue on with the next window. if (!className.IsMatch(sbClass.ToString())) return true; } // If a window text was provided, check to see if it matches the window. if (windowText != null) { int txtLength = SendMessage(handle, WM_GETTEXTLENGTH, 0, 0); StringBuilder sbText = new StringBuilder(txtLength + 1); SendMessage(handle, WM_GETTEXT, sbText.Capacity, sbText); // If it does not match, return true so we can continue on with the next window. if (!windowText.IsMatch(sbText.ToString())) return true; } // If a process name was provided, check to see if it matches the window. if (process != null) { int processID; GetWindowThreadProcessId(handle, out processID); // Now that we have the process ID, we can use the built in .NET function to obtain a process object. Process p = Process.GetProcessById(processID); // If it does not match, return true so we can continue on with the next window. if (!process.IsMatch(p.ProcessName)) return true; } // If we get to this point, the window is a match. Now invoke the foundWindow event and based upon // the return value, whether we should continue to search for windows. return foundWindow(handle); } } }
And the result:
Mark S. Rasmussen
I'm the Technical Lead at iPaper where I cuddle with databases, mold code and maintain the overall technical responsibility. I'm an avid speaker at user groups & conferences. I love life, motorcycles, photography and all things technical. Say hi on Twitter, write me an email or look me up on LinkedIn.







