Om Talsania's Geekypedia
Articles on Architecture, Design, Development and Performance

UI Automation: Automating key strokes using .NET and Win32 API

Thursday, January 10, 2013 9:16 PM

Win32 API offers a lot of functionality for Windows platform, which the .NET Framework doesn’t have for good reason – One of them being ‘managed’. However, that doesn’t stop us from doing some unsafe things! We can always build a wrapper for Win32 API and then call those functions directly from .NET.

Let us evaluate one of such area which requires calling Win32 API functions from .NET. That area is ‘Simulating UI Automation’. Of course, the subject is too vast to fit in a single blog post, however, we can start with some basic things at least!

Our objective for this exercise is : “Launch an instance of Notepad and write ‘hello’ in it”; (Sorry for the semicolon, it has become a habit Smile)

So let us start with creating a C# Console application. Next step is to create a new class called Win32 (you can name it whatever you like) and declare a couple of ‘static extern’ methods whose signature should match that of the unmanaged native Win32 API methods. There are a couple of DLLs for core Win32 APIs. You can categorize them into 8 areas. For more info, please visit http://en.wikipedia.org/wiki/Windows_API. We will be focusing on User32.dll for today’s topic as it deals with the ‘User Interface’.

    /// <summary>
    /// Win32 wrapper for User32.dll functions
    /// </summary>
    public class Win32
    {
        public const int WM_KEYDOWN = 0x100;
        public const int WM_KEYUP = 0x101;

        [DllImport("User32.dll", SetLastError = true)]
        public static extern IntPtr FindWindow(String lpClassName, String lpWindowName);

        [DllImport("user32.dll", SetLastError = true)]
        public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, 
string lpszClass, string lpszWindow);
        [DllImport("User32.dll", SetLastError = true)]
        public static extern IntPtr SetForegroundWindow(IntPtr hWnd);

        [return: MarshalAs(UnmanagedType.Bool)]
        [DllImport("user32.dll", SetLastError = true)]
        public static extern bool PostMessage(IntPtr hWnd, uint Msg, int wParam, int lParam);
    }

Note: Make sure you have added  the following namespace.

using System.Runtime.InteropServices;

The FindWindow method is used to find a handle to any window by its application class name and its title. You can pass null as the first parameter.

The FindWindowEx method finds child windows.

The SetForegroundWindow sets focus on the specified window.

The PostMessage is used to send key strokes to the window.

Now, although not necessary, I have created another wrapper around this Win32 wrapper which exposes more user friendly set of methods. To do so, I have created another class named UIAutomationHelper.

    /// <summary>
    /// This class helps in UI Automation
    /// </summary>
    public class UIAutomationHelper
    {
        /// <summary>
        /// Find a window specified by the window title
        /// </summary>
        /// <param name="windowName"></param>
        /// <returns></returns>
        public static IntPtr FindWindow(string windowName)
        {
            return Win32.FindWindow(null, windowName);
        }

        /// <summary>
        /// Find a window specified by the class name as well as the window title
        /// </summary>
        /// <param name="className"></param>
        /// <param name="windowName"></param>
        /// <returns></returns>
        public static IntPtr FindWindow(string className, string windowName)
        {
            return Win32.FindWindow(className, windowName);
        }

        /// <summary>
        /// Finds a window specified by the window title and set focues on that window
        /// </summary>
        /// <param name="windowName"></param>
        /// <returns></returns>
        public static IntPtr FindWindowAndFocus(string windowName)
        {
            return UIAutomationHelper.FindWindowAndFocus(null, windowName);
        }

        /// <summary>
        /// Finds a window specified by the class name and window title and set focuses on that window
        /// </summary>
        /// <param name="className"></param>
        /// <param name="windowName"></param>
        /// <returns></returns>
        public static IntPtr FindWindowAndFocus(string className, string windowName)
        {
            IntPtr hWindow = Win32.FindWindow(className, windowName);
            Win32.SetForegroundWindow(hWindow);
            return hWindow;
        }

        /// <summary>
        /// Finds a child window
        /// </summary>
        /// <param name="windowName"></param>
        /// <param name="childWindowName"></param>
        /// <returns></returns>
        public static IntPtr FindChildWindow(String windowName, String childWindowName)
        {
            return UIAutomationHelper.FindChildWindow(null, windowName, null, childWindowName);
        }

        /// <summary>
        /// Finds a child window
        /// </summary>
        /// <param name="className"></param>
        /// <param name="windowName"></param>
        /// <param name="childClassName"></param>
        /// <param name="childWindowName"></param>
        /// <returns></returns>
        public static IntPtr FindChildWindow(String className, String windowName, 
String childClassName, String childWindowName) { IntPtr hWindow = Win32.FindWindow(className, windowName); IntPtr hWindowEx = Win32.FindWindowEx(hWindow,
IntPtr.Zero, childClassName, childWindowName); return hWindowEx; }
/// <summary> /// Simulates pressing a key /// </summary> /// <param name="hWindow"></param> /// <param name="key"></param> public static void PressKey(IntPtr hWindow, System.Windows.Input.Key key) { Win32.PostMessage(hWindow, Win32.WM_KEYDOWN,
System.Windows.Input.
KeyInterop.VirtualKeyFromKey(key),
0);
} /// <summary> /// Simulates pressing several keys /// </summary> /// <param name="hWindow"></param> /// <param name="keys"></param> public static void PressKeys(IntPtr hWindow,
IEnumerable<System.Windows.Input.Key> keys) { foreach (var key in keys) { UIAutomationHelper.PressKey(hWindow, key); } } }

Now, let us create our main program.

        static void Main(string[] args)
        {
            Console.WriteLine("Starting Automation ...");
            

            //Starting Notepad
            ProcessStartInfo notepadStartInfo = 
new ProcessStartInfo(@"C:\Windows\System32\notepad.exe"); Process.Start(notepadStartInfo); //Wait for 2 seconds Thread.Sleep(2000); //Get handle and simulate key press to type Hello IntPtr handle = UIAutomationHelper.FindChildWindow(null,
"Untitled - Notepad", "edit", null); UIAutomationHelper.PressKeys(
handle,
new[] { Key.H, Key.E, Key.L, Key.L, Key.O, Key.Enter}); Console.WriteLine("Stopping Automation ..."); Console.WriteLine("Press any key to terminate ..."); Console.ReadKey(); }

Note: Make sure you have added the following namespaces.

using System.Diagnostics;
using System.Threading;
using System.Windows.Input;

You will also need to add reference to WindowsBase.dll

Now, what we are doing here is utilizing the Process and ProcessStartInfo to start an instance of Notepad. Then after waiting for 2 seconds, we are making call to your UIAutomationHelper method called FindChildWindow with parameters as className = null, windowName = “Untitled – Notepad”, childClassName=”edit” and childWindowName=null. Now this method internally calls the Win32.FindWindow and Win32.FindWindowEx and returns a handle of the Notepad instance we want use. The PressKeys method of our helper is also making call to Win32.PostMessage method internally which is used to post key strokes to the Notepad window.

Now, let’s compile and run this program. This is what we get as an output.

image

image

 

So, this is how we go about writing automated text in the any window. Now, if you want to know more about what we can do with our target window (in our case it was notepad), you can use a utility called Inspect.exe which can be found at the following location.

C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\Inspect.exe

This utility is part of Windows SDK that can be downloaded from the following location.

http://www.microsoft.com/en-in/download/details.aspx?id=8279

 

Best Regards,

Om Talsania




Feedback

# re: UI Automation: Automating key strokes using .NET and Win32 API

Great post! Thanks!!! 1/11/2013 2:44 AM | Calabonga

# re: UI Automation: Automating key strokes using .NET and Win32 API

Does it works on 64 bits systems? 2/28/2013 12:44 PM | Olivier

# re: UI Automation: Automating key strokes using .NET and Win32 API

I tried it on Windows Server 2012 Standard Edition 64-bit. It works. 2/28/2013 5:32 PM | Om Talsania

# re: UI Automation: Automating key strokes using .NET and Win32 API

Nice example. I'm trying to do a smiliar thing but with Microsoft Access. Everytime I launch a DB in access, i get a popup window "Microsoft Access Security Notice". It's an automation program so I would like to click on "Open" button in the popup window. Any thoughts on how I could do that using WinAPI 5/29/2013 8:30 PM | Chander

# re: UI Automation: Automating key strokes using .NET and Win32 API

Hi, I'm new to this and learning...but what if I want to have the program enter a value from a variable instead of the specific keys, how do I do that? 10/10/2015 8:58 AM | Romer

# re: UI Automation: Automating key strokes using .NET and Win32 API

instead of these - new[] { Key.H, Key.E, Key.L, Key.L, Key.O, Key.Enter})

something like string ValueHere = Test

then new[] { Key.ValueHere, Key.E, Key.L, Key.L, Key.O, Key.Enter})

is that possible? 10/10/2015 9:10 AM | Romer

# re: UI Automation: Automating key strokes using .NET and Win32 API

re to Romer:

like this in sudo code:

variable = "random string"
lengthOfVarible = len(varible)
int = 0
someArray = []
while int < lengthOfVarible:
array.add(Key.varible[int])
int = int + 1

array.add(Key.Enter)
array <-- ready to go



-Scott Lindh 7/6/2016 2:15 PM | Scott

# re: UI Automation: Automating key strokes using .NET and Win32 API

re to Romer:

like this in sudo code:

variable = "random string"
lengthOfVarible = len(varible)
int = 0
someArray = []
while int < lengthOfVarible:
array.add(Key.varible[int])
int = int + 1

array.add(Key.Enter)
array <-- ready to go



-Scott 7/6/2016 2:16 PM | Scott

# viagra tablets uk

sy542 http://buyviagraformen.com/#best-online-pharmacy-for-viagra viva viagra aj3189ji2540sz7256 xz8712wv8857oz3220 12/10/2016 3:35 PM | Kennethdum

# re: UI Automation: Automating key strokes using .NET and Win32 API

Ho Can we use for copy and paste on the same text file. 12/15/2016 2:42 PM | Maneet singh

# Order cheaply pills without preparation

cialis 5mg generic you cannot reply to topics in this forum
http://cialiswalmart.org - cialis over the counter at walmart
cialis 10mg or 20mg delete all board cookies
cialis over the counter at walmart
- generic cialis online no prescription
where to buy cialis in riyadh
2/7/2017 8:56 PM | LuciuzKaw

# re: UI Automation: Automating key strokes using .NET and Win32 API

Thanks a lot for your opinion! There are extensions available which do the job, but thanks for your suggestion! ccleaner 4/20/2017 8:36 AM | bimbangcc

# re: UI Automation: Automating key strokes using .NET and Win32 API

nice article bor thanks for sharing this info
< a href="http://myeaadhar.in/change-update-aadhaar-card-details-online/">update aadhr card 9/22/2017 6:36 AM | sandeep

Post a comment