Click here to Skip to main content
15,867,141 members
Articles / Programming Languages / C#
Article

Interoperating with Windows Media Player using P/Invoke and C#, Part 2

Rate me:
Please Sign up or sign in to vote.
4.47/5 (8 votes)
26 Oct 20062 min read 46K   498   27   9
A more flexible/powerful approach to the original article.

Introduction

The article, Interoperating with Windows Media Player using P/Invoke and C# by Alexander Kent, helped me enormously in understanding how to control a Windows application via the Windows API in C#.  However, I had to modify the code in order to manipulate a different application than Windows Media Player. The application I wanted to manipulate:

  • didn't have either a consistent name or class, so using FindWindow() was not an option
  • had dynamic placement of menu items, so using Spy++ to find wParam/lParam was not an option

This article addresses these two deficiencies.

Background

This code simply builds upon Kent's article (the source files are just the ones I modified from the original project), so it is just a matter of cutting-and-pasting the code snippets shown here in the original source. While the following code is not necessary for Windows Media Player (since the two bullet points in the introduction are not relevant for WMP), it is still instructive to demonstrate on WMP.

The two places for cutting-and-pasting:

  • Replace FindWindow() with GetWmpHandle()
  • Replace the hard-coded wParams with member variables you get from GetMenuItemID()

The one place for pasting:

  • Add the relevant Windows API methods and constants into your Win32 class.

Using the code

Let's start with substituting FindWindow() with GetWmpHandle(). Again, the reason we're doing this is that the application that you want to manipulate might not have a consistent window name or class name. For example, when you're using Visual Studio, the name of the window is "Microsoft Development Environment" plus the name of the file on which you are working. In my case, both names had a consistent prefix, so I could do the following:

C#
private System.Int32 GetWmpHandle() 
{
    // start with the top window on the Desktop
    System.Int32 window_handle = 
      Win32.GetTopWindow(Win32.GetDesktopWindow());
    System.Text.StringBuilder text_stringBuilder = 
              new System.Text.StringBuilder(0x20);
    String wmpText_string = "Windows Media Player"; 
    System.Text.StringBuilder class_stringBuilder = 
               new System.Text.StringBuilder(0x20); 
    String wmpClass_string = "WMPlayerApp";
     
    try {
        while (true) {
        // look at each Desktop window...
            Win32.GetWindowText(window_handle, 
                  text_stringBuilder, 0x20); 
            
            if (text_stringBuilder.ToString().StartsWith(
                                       wmpText_string)) {
            // ...until you find one whose name starts
            //    with "Windows Media Player"
                Win32.RealGetWindowClass(window_handle, 
                            class_stringBuilder, 0x20); 
                if (class_stringBuilder.ToString().StartsWith(
                                           wmpClass_string)) {
                // ...and has a class name
                //    that starts with "WMPlayerApp"
                    return window_handle;
                }
            }
            if ((window_handle = Win32.GetWindow(window_handle, 
                 Win32.GW_HWNDNEXT)) == 0) { 

                return 0; 
            }
        } 
    }
    catch (Exception e) { return 0; } 
}

The next step is to find the right menu item, in this case, "Play/Pause" and "Stop". I'm assuming you know the name of the pull-down menu (in this case, "Play"). In the extreme case, you might not know in which menu the command is located: I will leave that one up to the reader as an exercise. Here's the GetMenuItemID() method:

C#
private System.UInt32 GetMenuItemId(int hWnd, 
        String menuItem_string, String submenuItem_string)
{
    // get the main application menu
    System.Int32 hMenu = Win32.GetMenu(hWnd);
    int count = Win32.GetMenuItemCount(hMenu);
    int menuItemIndex; 
    System.Text.StringBuilder menuItem = 
               new System.Text.StringBuilder(0x20); 
    
    menuItemIndex = -1; 
    for (int i = 0; i < count; i++) {
    // loop through main menu...
        Win32.GetMenuString(hMenu, (uint)i, menuItem, 
                          0x20, Win32.MF_BYPOSITION); 
        if (menuItem.ToString().StartsWith(menuItem_string)) { 
            menuItemIndex = i; 
            break;
        }
    }
        if (menuItemIndex < 0) { return 0; }
    hMenu = Win32.GetSubMenu(hMenu, menuItemIndex);
    count = Win32.GetMenuItemCount(hMenu);
        menuItemIndex = -1; 
    for (int i = 0; i < count; i++) {
    // loop through sub menu...
        Win32.GetMenuString(hMenu, (uint)i, 
              menuItem, 0x20, Win32.MF_BYPOSITION); 
        if (menuItem.ToString().StartsWith(submenuItem_string)) { 
            menuItemIndex = i; 
            break;
        }
    }
    if (menuItemIndex < 0) { return 0; }
    return Win32.GetMenuItemID(hMenu, menuItemIndex); 
}

Now, you need to replace the hard-coded wParam values with member variables wParam_play and wParam_stop:

C#
wParam_play = (int) GetMenuItemId(iHandle, "&Play", "&Play/Pause");
wParam_stop = (int) GetMenuItemId(iHandle, "&Play", "&Stop");

Finally, you need to define more constants and entry points to the DLL in your Win32 class:

C#
...
public const int GW_HWNDNEXT = 2; 
public const int GW_HWNDPREV = 3; 
public const int GW_CHILD = 5;
public const int MF_BYPOSITION = 0x400; 
    
[DllImport("User32.dll")]
public static extern int GetDesktopWindow(); 

[DllImport("User32.dll")]
public static extern int GetTopWindow(int hwndParent); 

[DllImport("User32.dll")] 
public static extern int GetWindow(int hwndSibling, 
                                   int wFlag); 

[DllImport("User32.dll")] 
public static extern int GetWindowText(int hWnd, 
       System.Text.StringBuilder text, int count); 

[DllImport("User32.dll")] 

public static extern UInt32 RealGetWindowClass(int hWnd, 
       System.Text.StringBuilder text, UInt32 count); 

[DllImport("User32.dll")] 
public static extern int SetParent(int hWndChild, 
                                   int hWndNewParent); 

[DllImport("User32.dll")] 
public static extern int GetMenu(int hWnd); 
    
[DllImport("User32.dll")] 
public static extern int GetSubMenu(int hMenu, int nPos); 

[DllImport("User32.dll")]
public static extern uint GetMenuItemID(int hMenu, int nPos); 

[DllImport("User32.dll")] 
public static extern int GetMenuItemCount(int hMenu);  

[DllImport("User32.dll")] 
public static extern int GetMenuString( int hMenu, uint uIDItem, 
                         System.Text.StringBuilder lpString, 
                         int nMaxCount, uint uFlag );
...

And that's it!!!

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
United States United States
Chuck is:
-Sole Proprietor/Consultant with PROTOTYPOS
-a Consulting Assistant Professor in the Stanford University Department of Civil and Environmental Engineering
-a licensed architect in the State of California
-a scratch golfer (yeah, right)

Comments and Discussions

 
QuestionIs it possible to get the message when you click the "Play" button etc Pin
hashadube4-Jun-12 0:23
hashadube4-Jun-12 0:23 
QuestionWhat might cause GetMenu to return 0? Pin
Art Gardner24-Jan-12 10:04
Art Gardner24-Jan-12 10:04 
GeneralGood Work Pin
Alexander Kent12-Feb-07 7:10
Alexander Kent12-Feb-07 7:10 
GeneralRe: Good Work Pin
Chuck Han12-Feb-07 19:57
Chuck Han12-Feb-07 19:57 
GeneralGet Play Time Pin
chinajuanbob28-Jan-07 14:38
chinajuanbob28-Jan-07 14:38 
GeneralRe: Get Play Time Pin
Chuck Han29-Jan-07 6:59
Chuck Han29-Jan-07 6:59 
General&s in menu items Pin
dragynox15-Jan-07 5:18
dragynox15-Jan-07 5:18 
GeneralMedia Player must be active first Pin
gxdata1-Nov-06 2:02
gxdata1-Nov-06 2:02 
GeneralRe: Media Player must be active first Pin
Chuck Han1-Nov-06 2:35
Chuck Han1-Nov-06 2:35 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.