Friday, August 15, 2008

Cross Processing: #1 Accessing TextBox

So this is the first in a series of posts on accessing cross process UI data.

Note: This posts will correspond to windows-rendeder process windows, and not WPF or java applications that render differently.

Some Background
Buttons, Textboxs, ComboBoxs and the rest are all called "Controls", but they are all actually different forms of "Window"'s.

Every visual (unlike services) process in Windows, is basically a window heirarchy where the root window is the container (with the minimize,maximize and close buttons) and the rest are the actual controls.

The way each control (window) sends information to other controls is through a broadcast mechanism called "Window Messages".

Very much like processes, we could search for a specific window using its unique identifier - handle (like process id), and make less specific search using its name and class.

The demo will be to start the calc, and update and read its textbox.

Prerequisits:
Before we could start codeing, we need some win api declarations:

1. In order to get the handle of a certain window, we'll use FindWindowEx - a windows api declaration:


[DllImport("user32.dll")]
public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);



2. in order to send data to a window, we need to send it a message:


[DllImport("user32.dll")]
public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, int wParam, string lParam);


3. Get the buffer of the text:


[DllImport("user32.dll")]
public static extern int GetWindowTextLength(IntPtr hWnd);


4. Get the actual text:

[DllImport("user32.dll")]
public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);



The Code!

Ok, so first we'll start the process:


Process proc = Process.Start(fullProcessPath);

proc.WaitForInputIdle();



Now, we'll use the FindWindowEx api to get the handle to its Edit box (the big textbox):


IntPtr hwnd = FindWindowEx(proc.MainWindowHandle, IntPtr.Zero, "Edit", "");



We have the control's handle, now we could...

1. Change value to "115":


SendMessage(hwnd, 0x000C, 0, "115");



2. Read the value:


int cap;
StringBuilder sb;
cap = Win32.GetWindowTextLength(this.Handle);
sb = new StringBuilder(cap + 1);
Win32.GetWindowText(this.Handle, sb, cap + 1);
return sb.ToString();