Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# WinAPI Clicking on menu items

I'm trying to click on a menu item inside a program called Media Subtitler and whatever I'm trying to do it's not working.

First, I tried to use the function GetMenu but it returned IntPtr.Zero. Then, I tried using the ALT key + using the first letter of my menu (F stands for file) but it did nothing. Then, I tried using a simple MOUSEDOWN and MOUSEUP messages but again, it did nothing (I also tried creating a loop that clicks on everything in that range but there was no click in that area).

What I clearly know is that I'm working on the correct window.

What am I doing wrong?

If someone wants to test it out you can download Media Subtitler for free and it doesn't weight that much.

Also, Here's the code I've been testing:

Process p = Process.Start(@"C:\Program Files\DivXLand\Media Subtitler\MediaSub.exe");
        p.WaitForInputIdle(1500);
        Thread.Sleep(3000);

        SetForegroundWindow(p.MainWindowHandle);
        ShowWindow(p.MainWindowHandle, SW_MAXIMIZE);

        IntPtr handle = p.MainWindowHandle;

        SendMessage(handle, WM_NCHITTEST, 0, MakeLParam(18, 29));

        //for (int i = 0; i < 200; i++)
        //{
        //    for (int x = 0; x < 200; x++)
        //    {
        //        SendMessage(p.MainWindowHandle, WM_LBUTTONDOWN, 0, MakeLParam(i, x));
        //        SendMessage(p.MainWindowHandle, WM_LBUTTONUP, 0, MakeLParam(i, x));
        //    }
        //}
        //IntPtr menuItems = GetMenu(p.MainWindowHandle);
        return;
        //SendMessage(p.MainWindowHandle, WM_COMMAND, 6, 0);
        SendMessage(p.MainWindowHandle, WM_KEYDOWN, VK_MENU, 0);
        SendMessage(p.MainWindowHandle, WM_KEYUP, VK_MENU, 0);
        SendMessage(p.MainWindowHandle, WM_KEYDOWN, VK_F, 0);
        SendMessage(p.MainWindowHandle, WM_KEYUP, VK_F, 0);

Thanks for any help!

like image 835
Imri Barr Avatar asked Dec 17 '22 03:12

Imri Barr


2 Answers

By monitoring the messages sent to the main window of the application, I extracted the menu identifiers for the menu items. You can post WM_COMMAND message to the window, with the ID of the menu items as the wParam:

[DllImport("user32.dll")]
public static extern IntPtr PostMessage(IntPtr hWnd, Message msg, int wParam, int lParam);

PostMessage(handle, WM_COMMAND, 2, 0); // File->New subtitle

PostMessage(handle, WM_COMMAND, 3, 0); // File->New from clipboard

PostMessage(handle, WM_COMMAND, 5, 0); // File->Open text or subtitle

PostMessage(handle, WM_COMMAND, 6, 0); // File->Open video

...

I've tested the code with Media Subtitler, and it works like a charm! The only situation that this will not work, is when on windows Vista or Seven, your target program is running as Administrator and you C# program is not. Be aware of that!

The menu IDs can be easily examined by monitoring the WM_COMMAND message (using Spy++).
You can also use SendMessage instead of PostMessage, but then your program freezes until the user closes the window opened by the menu action.

You can use the same approach to send other command to other windows of the application. For example, clicking the 'Open' button of the 'Open video' window.

like image 165
Mohammad Dehghan Avatar answered Dec 18 '22 17:12

Mohammad Dehghan


You can also do all of this using the System.Windows.Automation namespace: http://msdn.microsoft.com/en-us/library/ms590934.aspx

Using this namespace, you do not need to do any interop with the Win32 API. Here's an example of how to get a window by searching for a string that its name contains:

public static AutomationElement GetWindowByName(string name)
{
    AutomationElement root = AutomationElement.RootElement;
    foreach (AutomationElement window in root.FindAll(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Window)))
    {
        if (window.Current.Name.Contains(name) && window.Current.IsKeyboardFocusable)
        {
            return window;
        }
    }
    return null;
}

After you have the window as an AutomationElement object, you can search it for controls and perform operations on those controls, etc.

Hope this helps!

like image 42
nguyer Avatar answered Dec 18 '22 16:12

nguyer