Add Your Control On Top Another Application – Part 1 (Win32)
Add Your Control On Top Another Application – Part 1 (Win32)
Couple of days ago I got an email asked me to help with creating a buttons on top all open applications, this has reminded me the CodedUI Recorder.
As you can see from the picture when using CodedUI Testing you will see the “Currently Recording” notification on every active application you are recording.
How?
The design is to make the title bar window of the target application as the parent\owner window of the Control your want to add.
But this is not enough, we need to listen to many events like size change, style etc.. but I’ll get there later.
Here is what we need to do:
-
Find window handle with FindWindow
-
Get window position and title bar info using GetTitleBarInfo
-
Set window as owner\parent using SetWindowLong
-
SetWinEventHook for couple of events of the target application.
Download Demo Project
So let’s get started…
Step 1: Create Project
Create a WinForm or WPF project and add the following classes:
Step 2: Get Running Processes
Create new class called “PItem” and copy this code:
public class PItem
{
public string ProcessName { get; set; }
public string Title { get; set; }
public PItem(string processname, string title)
{
this.ProcessName = processname;
this.Title = title;
}
public override string ToString()
{
if (!string.IsNullOrEmpty(Title))
return string.Format("{0} ({1})", this.ProcessName, this.Title);
else
return string.Format("{0}", this.ProcessName);
}
}
Now, getting all active processes:
Process[] pro_list = e.Result as Process[];
foreach (Process pro in pro_list)
{
try
{
//When using 64bit OS pro.MainModule.ModuleName will throw exception
// for each 32bit, so Instead of ModuleName I've used ProcessName
ProcessList.Items.Add(new PItem(pro.ProcessName, pro.MainWindowTitle));
}
catch (Exception)
{
//Security\ Permissions Issue
}
}
Step 3: Add Find Native Methods
FindWindow function retrieves a handle to the top-level window whose class name and window name match the specified strings. This function does not search child windows. This function does not perform a case-sensitive search.
Add below methods to NativeMethods.
using System.Runtime.InteropServices;
// Get a handle to an application window.
[DllImport("user32.dll", SetLastError = true)]
internal static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
// Find window by Caption only. Note you must pass IntPtr.Zero as the first parameter.
[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
internal static extern IntPtr FindWindowByCaption(IntPtr ZeroOnly, string lpWindowName);
Add below code to Helpers
I’ve created a Find method to handle both FindWindow and FindWindowByCaption (much more easy to search that way :-D)
public static IntPtr Find(string ModuleName, string MainWindowTitle)
{
//Search the window using Module and Title
IntPtr WndToFind = NativeMethods.FindWindow(ModuleName, MainWindowTitle);
if (WndToFind.Equals(IntPtr.Zero))
{
if (!string.IsNullOrEmpty(MainWindowTitle))
{
//Search window using TItle only.
WndToFind = NativeMethods.FindWindowByCaption(WndToFind, MainWindowTitle);
if (WndToFind.Equals(IntPtr.Zero))
return new IntPtr(0);
}
}
return WndToFind;
}
Step 4: Find Window Handle From Process
Using PItem we can use the ModuleName\Process Name and if the window is exists we can also use window title.
Calling our Helpers class and using Find method with ProcessName and WindowTitle.
PItem pro = ProcessList.SelectedItem as PItem;
string ModuleName = pro.ProcessName;
string MainWindowTitle = pro.Title; ;
TargetWnd = Helpers.Find(ModuleName, MainWindowTitle);
if (!TargetWnd.Equals(IntPtr.Zero))
Log(ModuleName + " Window: " + TargetWnd.ToString()); // We Found The Window
else
Log(ModuleName + " Not found"); // No Window Found
Download Demo Project