Introduction

Windows is essentially a message driven Operating System in the sense that,
the majority of actions that take place are responses to messages sent to the
main window procedure of an application. Whether you press a key, or move the
mouse or drag a window the application receives messages through it's message
queue and reacts accordingly. Now this results in a rather interesting corollary
that can be taken advantage of by us, developers; by sending the correct
messages to a window or it's child windows in the proper order, we can actually
simulate human actions on an application. And this has it's uses in various
scenarios. Obviously the first one that comes to mind is the ability to automate
a task, like for example opening a document in word, left justifying the entire
text and taking a print out.

But for me a more interesting usage of this technique is when it's applied to
take advantage of the Windows user interface to quickly do tasks, which might
otherwise require a lot of programming calls and access to undocumented
information. This includes changing various system properties, making changes
through the control panel applets or even changing display properties for the
desktop. In this article I will randomly select one such scenario ( a test case
scenario ) and see how to go about automating the task by using some simple
windows techniques like posting messages to a window, enumerating child windows
and elementary CBT hooking.

Test scenario

I am going to be using Windows XP Professional as my test platform and
therefore my example scenario is only meaningful in an XP context. Users of
other Operating Systems might have to make suitable changes to my example code
snippets to get the same end result as in this article.

By default, the keyboard navigation short cuts for menus are not shown in the
XP operating system ( something that both puzzled and annoyed a lot of users
when they first encountered this in Windows 2000 ). Having long abandoned
Windows 2000, I do not remember if there was a documented way of changing this
setting in 2000, other than by editing the registry or using some tweaking
application, but in XP this setting can be easily changed by using the Display
Properties control panel applet. All you need to do is select the Appearances
tab from the Display Properties dialog box, bring up the Effects sub-window and
uncheck the check box that says "Hide underlined letters for keyboard navigation
until I press the Alt key". Now I am wholly sure that this setting can probably
be changed by modifying a trivial registry entry; but for the sake of this test
case scenario and the article, let's assume that we do not know how to achieve
the same programmatically.

The human approach

Let's see how we'd do this had we done this manually sitting in front of the
machine. We'd probably have to follow these steps ( or something very similar )
:-

Right click on the desktop and bring up the Display Properties control
panel applet

Chose the Appearances tab

Bring up the Effects sub-window by clicking on the Effects button

Check/Uncheck the corresponding check box depending on what we are trying
to do

The solution in code

Now we need to decide what we need to do to achieve the same sequence of
events through code.

Bringing up the Display Properties window and choosing the Appearances tab
can be done in just one step because we know that the Display Properties
control panel applet is called desk.cpl and that it takes command line
arguments that can be used to dictate which tab comes up by default. In fact
we need to call it like this :-

control.exe desk.cpl Display,@Appearance

Control.exe is used to bring up the control panel applet passed to
it as first argument, and the additional arguments are used to force it to
start with the Appearance tab selected.

Now we need to enumerate the child windows ( controls ) on the Appearance
tab till we find the Effects button and this can be achieved using
EnumChildWindows. Once we obtain the Effects button's handle we can send a
button click message to it and bring up the Effects sub-window.

To locate the required check box on the Effects sub-window we would need
to first get the handle to the sub-window that just popped up. We achieve this
by setting up a global CBT hook ( this means we'd need to put all put code
into a DLL ), and watching for all newly activated windows. We know the title
text for the Effect sub-window and thus we obtain the handle to the window the
moment it gets activated. Now we do the same as previous, i.e. we use

EnumChildWindows

to retrieve the handle to the check box, and then send
a button click message to it.

We post a WM_COMMAND message to the Effects sub-window with a
command ID of IDOK which is the equivalent of closing the window
by clicking on the OK button.

to bring up the Display Properties applet window with the
default tab set to the Appearances tab. I have used SW_SHOW here
because using SW_HIDE will have no effect on the display properties
window ( I believe the control.exe program or perhaps desk.cpl
itself will later call ShowWindow(hWnd, SW_SHOW) somewhere in the
code ). We do our window hiding in the hook procedure ( but even this is not
fully effective and there is a short flash on screen, but then our aim is not
really to hide what we are doing from our end user, but to try and make things as
lucid as possible, which we achieve by reducing the time the window
remains visible to a few milliseconds ).

Using Spy++ we extract the exact text associated with the Effects button
which happens to be "&Effects..." and we use this knowledge to compare the text
of each child control with this text repeatedly till we get the button control
we want.

The HCBT_ACTIVATE code indicates that a
window is about to be activated. We compare the title text of this window with
"Effects" and if they match, we know that this is the window we were searching
for. If you are wondering why we had to install a CBT hook, instead of using
FindWindow using the title text; this is to make sure that even if
there was already an existing window with the same title text, it won't
interfere with our search because we are only checking newly activated windows.
We set the CBT hook quite late into the code and uninstall it the moment we get
the window we want. The duration the hook is active is from the time we bring up
the Display Properties window till the Effects sub-window is just about to be activated, and under most
circumstances this shouldn't be more than a few milliseconds.

We also use the hook procedure to hide the windows that pop up, which include
both the main Display Properties window as well as the Effects sub-window. The
infinitesimal flash still exists and if anyone has any ideas on further reducing
this, they are welcome to make suggestions.

We first set up our CBT hook and then call the BringUpDisplayAppearance function to bring up
the Display Properties window with the Appearance tab selected. Once the window
comes up we obtain the handle to the Effects button using the

GetEffectsButton

function, and then post a
BM_CLICK message to the Effects button using the handle we just
obtained. Almost instantly the Effect sub-window pops up and we retrieve it's
handle through our CBT hook procedure which also uninstalls the hook since it's
no longer of any use to us. We wait for the Effects window to come up, using the
following while loop :-

while(!IsWindow(g_hWndEffects))
Sleep(100);

This way we avoid sleeping for too long or for too less. Now we obtain the
handle to the check box using the GetMenuUnderlineCheck function
and post a BM_CLICK message to the check box which effectively
toggles it's state which is just what we are trying to do. Now we simply post
WM_COMMAND messages with wParam set to IDOK
to the Effects sub-window as well as to the Display Properties main window.
That's all; we have now successfully toggled the state of the "Underline
keyboard navigation shortcuts for menus" system-wide property.

Conclusion

The test scenario we considered was perhaps too simplistic to reveal the
actual power of this technique, but when you consider that you can now do
anything from your program that a user can do manually using the Windows GUI,
you'll be slowly impressed by the awesome possibilities of the technique. You can use this
technique to enumerate Windows themes, change the current theme, change display
settings, change system settings, automate your own applications etc. The only
tool you'd need in addition to Everett is Spy++ or some such similar
application. Good luck with your own message based Windows automation attempts.

History

August 8th 2003 - First written

License

Share

About the Author

Nish Nishant is a Principal Software Architect based out of Columbus, Ohio. He has over 17 years of software industry experience in various roles including Lead Software Architect, Principal Software Engineer, and Product Manager. Nish was a Microsoft Visual C++ MVP between 2002 and 2015.

Nish is an industry acknowledged expert in the Microsoft technology stack. He authored C++/CLI in Action for Manning Publications in 2005, and had previously co-authored Extending MFC Applications with the .NET Framework for Addison Wesley in 2003. In addition, he has over 140 published technology articles on CodeProject.com and another 250+ blog articles on his WordPress blog. Nish is vastly experienced in team management, mentoring teams, and directing all stages of software development.

Contact Nish : If you are interested in hiring Nish as a consultant, you can reach him via his google email id voidnish.

Great article!
I'am a Chinese programmer. I just finish a program that has a module doing the similar task(just want to "Clear SSL State" like IE, use inetcpl.cpl). I used the LaunchInternetControlPanel() API and create another thread to get last active popup window and call ShowWindow() to hide it. When I launch it, it behaves better than the CBT hook one. But theoretically, the hook method is supposed to work better(cuz it hides the window earlier),right???
Then I added HCBT_CREATEWND on, subclass the window and intercept the WM_INITDIALOG message, but unfortunately, it didn't work the way I expected. For me, it's essential to hide the windows from users. So the question: Is it possible to completely (no infinitesimal flash) hide all the popup windows? Anyone can give me some hints? Thanx!!!!!
Here is my code:
LRESULT CALLBACK CBTProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if(nCode == HCBT_CREATEWND)
{
HWND hWnd = (HWND) wParam;
CBT_CREATEWND *pCreateWnd = (CBT_CREATEWND *)lParam;
LPCSTR lpWinText = pCreateWnd->lpcs->lpszName;

Hello Nish,
Could you give me some guidelines as to what it would take to be able to control a desktop environment remotely.
I am working on a remote desktop control project with the client running on WinCE (pocketPC2003) and Server in Win32 (winXp). Am connecting the two over Bluetooth Virtual COMports.

So far, I can "monitor" the server by simply capturing its screen and sendin it to the client.

I now need to add control capability; i.e. make mouse(stylus)/keyboard inputs on the PPC and have them reflect on the desktop of the server.

Am a Hooks newbie, but sofar (with your article) I suppose this is the way. Any guidelines as to what is involved, or any references you may provide will be appreciated!
Thanks
Moses
PS: gave up the sunny india for snow!...must be crazy!

Your article is great! But what if I wanted to move the mouse and click the "OK" button? How would I programatically move the mouse? Assume I know ahead of time where the button is going to be. How do programs like "Fakesurf" work?

If you want to simulate mouse presses send the WM_LBUTTONDOWN message to the window. the wparam should be set to 1 I believe and the lparam parameter determines the x-y coordinates of the button click. To get this parameter use something like spy++ or winspector (maybe?) to get the right code when you click the button yourself (in other words use the message monitor mode).

Hi,
I posting this question here just because this article contains something about CBT hooks. I need to get the handle of the message box of the messenger service in windows 2000/XP. MSDN says that CBT hooks can be used for this. So i installed a CBT hook, check for HCBT_CREATEWND in the nCode parameter so that I can get the handle to the window in wParam. But this does not happen. Also I cannot check for HCBT_ACTIVATE because in Win 2K/XP, the message box from the messenger service does not have focus when it is created. Anybody ??

One suggestion: to make your example complete, one would want to check the state of the checkbox before sending a click. Also, you'd want to check the state of different buttons (they may be disabled due to insufficient privileges or policy restrictions).

Alt suggestion: one could use Windows Accessible APIs to do this too.

Limitation: Your method is for Win32 controls only.

But yes, more than the message example, you have given a decent example of using CBT hooks.

saikatsen wrote:One suggestion: to make your example complete, one would want to check the state of the checkbox before sending a click. Also, you'd want to check the state of different buttons (they may be disabled due to insufficient privileges or policy restrictions).

A while back, John Robbins did a series of articles in MSDN detailing how to play back a script written to do things such as you describe.

IMHO, it's kinda sad in these days of COM and .NET that such techniques are still so very useful in testing (commercial products such as Rational Robot that do pretty much the same thing will sell for fat wads of cash).

But that being as it is, it's pretty cool that we have this capability.

I'd be very careful with this kind of code. It'll probably fail on all versions of Windows except english-language versions. Have you thought of just checking the control IDs instead of the window texts? Either way is OS dependand, but I guess that's inevitable with this technique.

Windows Script Host (see article at http://msdn.microsoft.com/msdnmag/issues/02/05/wsh/default.aspx[^], "Windows Script Host 5.6 Boasts Windows XP Integration, Security, New Object Model") might provide more flexibility. It might also be more robust since it would not rely on inherently undependable things, like the text of a button (here, "Effects..." or "Hide underlined") remaining unchanged from release-to-release.

This technique only works for messages where all the data can
be stored in the WPARAM and LPARAM. Any message where you pass
a memory address in WPARAM or LPARAM isn't going to work
(like trying to locate a string in a ListView).

To send these types of messages, you would need to use one of the
methods for injecting code into a process and then running it.

While injecting code is one solution this type of thing can be accomplished in an easier fashion: allocate memory in the target process using VirtualAllocEx() and copy the desired data into that location using WriteProcessMemory() before sending the appropriate message. Likewise, locating a string in a ListView can be achieved by taking the returned pointer and apply ReadProcessMemory() to copy data from the target process.

True, and honestly I haven't thought about that. So this does reduce the usability of WriteProcessMemory() on those systems. Since Win9x system do not seperate processes' address spaces as much as WinNT does, there are regions of memory that are shared between all running processes, so using VirtualAlloc() within this region might be a -- admittedly hackish -- solution.

Anonymous wrote:Is there any way to determine the base address (needed for Read/WriteProcessMemory) of a process?

The base address is just the address where you want to start reading from/writing to. Let's assume you intercepted a WM_SETTEXT message then the lParam (a pointer to a null-terminated string) would be the base address for ReadProcessMemory().