C# – Send data to other application – Menu and MenuItem

After I posted this blog “C# – Send data to other application“, Lion_King asked me if I have free time to try sending same messages to menu item in iTunes because he recognizes that it’s really difficult to send message to menu item. Therefore yesterday I spent half of day for trying to send mouse click to menu Store -> Sign In of iTunes and luckily that I was successful. So I decide to write the next part for sending data to other application related to menu and menu item in this small blog. The image below shows the menu item that I would like to click on automatically

Menu Sign In of iTunes

In the first part, I almost use three main APIs : FindWindow, FindWindowEx and SendMessage. In this second part, I also use this three main APIs to locate the components of form and send appropriate messages to popup the menu and click on the “Sign In” menu item. I will describe in step by step to help you understand easier. The steps which introduced in previous part will be explained in short form.

If you want to learn more about Win32 API Programming you can read this book
Win32 Programming (Addison-Wesley Advanced Windows Series)(2 Vol set)

1. As usual, before we send anything to any where on an application, we must firstly get the handle of that application. Use Spy++ I found out that the class name and caption of iTunes should be “iTunes” and “iTunes”.

IntPtr hwndMainWindowItunes = WinAPI.FindWindow("iTunes", "iTunes");

2. Now it’s time to get the handle of the menu, maybe you’ll think about the APIs GetMenu to get the menu’s handle of a dialog. That’s right in most of cases but with iTunes everything has been changed. If you look at the component tree of iTunes in Spy++, you will see that the sub menu “Store” is not a real sub menu at all. Its class name is “Static”, which means a label and we can drag the “Finder Tool” over it to get its handle. It’s really strange with a menu because as normal we can not drag “Finder Tool” over any menu. You can open Notepad and test if you can drag “Finder Tool” over its menu.

Component tree of iTunes

So it turn to be really difficult now because menu “Store” is a faked one therefore the GetMenu is useless and we can not get the handle of popup menu when we click on “label” Store. Every time when we switch to Spy++ window to drag the “Finder Tool” over the popup menu, this popup menu will disappear cause of losing focus. Either we have popup on or we have “Finder Tool” on. We can not have both of them on at the same time, we are in a hard circumstance. To solve this problem, I decide to run a while loop which continuously clicks on menu Store to keep the popup menu open and then I can use Spy++ to get its handle. Of course, to click on Store menu, I must get its handle and send click message to it.

IntPtr hwndMnStatic1 = WinAPI.FindWindowEx(hwndMainWindowItunes, IntPtr.Zero, "Static", "");
IntPtr hwndMnStatic2 = WinAPI.FindWindowEx(hwndMainWindowItunes, hwndMnStatic1, "Static", "");
IntPtr hwndMnStore = WinAPI.FindWindowEx(hwndMnStatic2, IntPtr.Zero, "Static", "&Store");

int x = 5, y = 5;
int lParam = ((x << 16) | (y & 0xffff));
while (true)
{
	WinAPI.SendMessage(hwndMnStore, WinAPI.WM_LBUTTONDOWN, IntPtr.Zero, new IntPtr(lParam));
	WinAPI.SendMessage(hwndMnStore, WinAPI.WM_LBUTTONUP, IntPtr.Zero, new IntPtr(lParam));
}

With the block code above, I can keep the popup menu open all time by clicking at the position (5,5) of menu “Store”. Then I can drag the Spy++ over it to get its class name and caption which are “#32768” and “”. The position for mouse clicking is stored in lParam variable containing transformation of x and y coordinates. After having the class name of popup menu, we can , of course, improve our while loop to break when having the handle of popup.

IntPtr hwndMnPopupMenuWnd = IntPtr.Zero;
while (hwndMnPopupMenuWnd == IntPtr.Zero)
{
	WinAPI.SendMessage(hwndMnStore, WinAPI.WM_LBUTTONDOWN, IntPtr.Zero, new IntPtr(lParam));
	WinAPI.SendMessage(hwndMnStore, WinAPI.WM_LBUTTONUP, IntPtr.Zero, new IntPtr(lParam));
	hwndMnPopupMenuWnd = WinAPI.FindWindow("#32768", "");
}

However, the big disadvantages of sending mouse click with WM_LBUTTONDOWN and WM_LBUTTONUP is the component receiving mouse click must be in the front all time. We can not click on the component which lies behind some other forms. That means we must bring it front and keep it front all time when we would like to send mouse click message. This is really big disadvantages because it is impossible that I let a program click on a component in background and I can do something else at the same time. Because I can carelessly hide the component behind the screen. So if you know how to send mouse click without requiring that the component should be front then share to me . I appreciate for that and the source code to keep iTunes to foreground window and get the handle of popup menu looks like below

IntPtr hwndMainWindowItunes = WinAPI.FindWindow("iTunes", "iTunes");
IntPtr hwndMnStatic1 = WinAPI.FindWindowEx(hwndMainWindowItunes, IntPtr.Zero, "Static", "");
IntPtr hwndMnStatic2 = WinAPI.FindWindowEx(hwndMainWindowItunes, hwndMnStatic1, "Static", "");
IntPtr hwndMnStore = WinAPI.FindWindowEx(hwndMnStatic2, IntPtr.Zero, "Static", "&Store");

WinAPI.WINDOWPLACEMENT wp = new WinAPI.WINDOWPLACEMENT();
wp.length = Marshal.SizeOf(wp);
WinAPI.GetWindowPlacement(hwndMainWindowItunes, ref wp);

int proposedPlacement = wp.showCmd;

if (wp.showCmd == WinAPI.SW_SHOWMINIMIZED)
	proposedPlacement = WinAPI.SW_SHOWMAXIMIZED;

WinAPI.SystemParametersInfo((uint)0x2001, 0, 0, 0x0002 | 0x0001);
WinAPI.ShowWindowAsync(hwndMainWindowItunes, proposedPlacement);
WinAPI.SetForegroundWindow(hwndMainWindowItunes);
WinAPI.SystemParametersInfo((uint)0x2001, 200000, 200000, 0x0002 | 0x0001);

int x = 5, y = 5;
int lParam = ((x << 16) | (y & 0xffff));
IntPtr hwndMnPopupMenuWnd = IntPtr.Zero;
while (hwndMnPopupMenuWnd == IntPtr.Zero)
{

	WinAPI.SendMessage(hwndMnStore, WinAPI.WM_LBUTTONDOWN, IntPtr.Zero, new IntPtr(lParam));
	WinAPI.SendMessage(hwndMnStore, WinAPI.WM_LBUTTONUP, IntPtr.Zero, new IntPtr(lParam));
	hwndMnPopupMenuWnd = WinAPI.FindWindow("#32768", "");
}

3. Ok, one of hard sections is now finished, we are going to the next part to get the menu item. Let’s see what we have now. We have a handle of a popup menu window. I make the window to bold because I would like to emphasize that it’s the handle of a window that contains the real menu in it. It’s not the handle of menu at all. However please do not think about GetMenu APIs to get the handle of menu, because it’s a pop up menu, it does not like a normal menu. We can not get its handle through this API but we must use the SendMessage with parameter MN_GETHMENU to get its handle.

IntPtr hwndMnPopupMenu = WinAPI.SendMessage(hwndMnPopupMenuWnd, WinAPI.MN_GETHMENU, IntPtr.Zero, IntPtr.Zero);
int count = WinAPI.GetMenuItemCount(hwndMnPopupMenu);
int menuItemIndex;
System.Text.StringBuilder menuItem = new System.Text.StringBuilder(0x20);

menuItemIndex = -1;
for (int i = 0; i < count; i++)
{
	// loop through main menu...
	WinAPI.GetMenuString(hwndMnPopupMenu, (uint)i, menuItem, 0x20, WinAPI.MF_BYPOSITION);
	if (menuItem.ToString().Equals("Sign &In…"))
	{
		menuItemIndex = i;
		break;
	}
}

You can see the block code above everywhere when man would like to access an menu item to gets its index. You may ask me why do I need this index. The question is really simple because I do not know any API function which allows me to click directly on a menu item. Therefore I get the menu index to locate its position on form and then send the mouse click to it. If you know one then tell me, by the way I tried with WM_COMMAND and GetMenuItemId but this combination does not work because here is a pop up menu.

WinAPI.RECT helpRECT = new WinAPI.RECT();
WinAPI.GetMenuItemRect(hwndMnPopupMenuWnd, hwndMnPopupMenu, (uint)menuItemIndex, out helpRECT);

x = (helpRECT.Left + helpRECT.Right) / 2;
y = (helpRECT.Top + helpRECT.Bottom) / 2;

Cursor.Position = new System.Drawing.Point(x, y);
WinAPI.mouse_event((int)(WinAPI.MouseEventFlags.LEFTDOWN), 0, 0, 0, 0);
WinAPI.mouse_event((int)(WinAPI.MouseEventFlags.LEFTUP), 0, 0, 0, 0);

Thread.Sleep(2000);
IntPtr hwndMnSignIn = IntPtr.Zero;
int index = 0;
while (hwndMnSignIn == IntPtr.Zero && index < 100)
{
	index++;
	hwndMnSignIn = WinAPI.FindWindow("iTunesCustomModalDialog", "iTunes");
        Thread.Sleep(100);
}

After having the position to click on, I can not manage to use the same way to click on this menu item with WM_LBUTTONDOWN and WM_LBUTTONUP. I tried many times to send these messages to menu item by editing coordinates to hard-coding or replacing the hWnd of windows but none of them work. So I must use the other API mouse_event to click on menu item to open the Sign-In window with class name “iTunesCustomModalDialog” and caption “iTunes”. Opening this dialog takes about some seconds so just sleep the program in 2 seconds to wait for the new opening dialog and try to get its handle in 100 times. Each time takes place after 100ms.

Now we finish our mission although there are still some open questions which I can not answer myself. I hope one of you may help me to find out the solutions:
– Sending message WM_LBUTTONDOWN/WM_LBUTTONUP will require a foreground windows. Is there any way to do it without setting foreground windows? I mean “Can we send mouse click to a background window?”.
– After having the position of menu item “Sign In”, “Why can’t I use Send Message WM_LBUTTONDOWN/WM_LBUTTONUP to simulate mouse click any more? How should I give the parameters to SendMessage to make a mouse click on it without using mouse_event API?”

And as usual in the end of the post is the source code “Send data to other application – Menu and MenuItem