Handling standard media keys in your C# media player

Today’s post is probably not news to anyone… But I am sitting at work, doing nothing – bored out of my mind… I (and everyone else in this company) is being retrenched, and today, I am between job interviews.

Again, the source, for this and a whole bunch of other stuff, is here: RomyView.zip
Actually I just added the code explained in this article in the last few minutes and am uploading it as I write this.

My media player isn’t intended to be “production” code, but I do use it as my preferred player, so I’d like it to be as “complete” as I can get it.  I had never heard of these standard keys… I stumbled onto them in the comments to this blog post today by Raymond Chen.

According to the MSDN page on WM_APPCOMMAND, the relevant messages are defined in WinUser.h. I’m still on Windows 7, so I browsed to “C:\Program Files\Microsoft SDKs\Windows\v7.1A\Include”, which is where my Platform SDK installation is found, and copied the relevant definitions into my code, resulting in this: (I added to existing code, so the code below is just the relevant bits. In the enum defined below, I also left out all the commands I don’t use to keep this brief. The source for download includes all the other commands as well.)

  1. internal static class NativeConstants
  2. {
  3.     public const int WM_APPCOMMAND = 0x0319;
  4. }
  5.  
  6. internal enum ApplicationCommand
  7. {
  8.     VolumeMute = 8,
  9.     VolumeDown = 9,
  10.     VolumeUp = 10,
  11.     MediaNexttrack = 11,
  12.     MediaPrevioustrack = 12,
  13.     MediaStop = 13,
  14.     MediaPlayPause = 14,
  15.     Close = 31,
  16.     MediaPlay = 46,
  17.     MediaPause = 47,
  18.     MediaFastForward = 49,
  19.     MediaRewind = 50
  20. }

 

Players don’t need to support all of the commands above – handling play and pause is probably good enough. I just handled the ones that correlate with my existing implementation. Then in my media player form’s WndProc method, I added handlers for the messages: (Again, I’ve just copied the relevant bits here – the actual code has other unrelated handlers.)

Actually I should probably not have copied all of the below here… obviously the methods I call from each handler are irrelevant anyway, and all the handlers look almost identical. Note though, the MSDN documentation indicates that if you handle the WM_APPCOMMAND message, you need to return TRUE. Thus, in the switch statement below, I used a default case to take care of that, and every handler calls goto default, to do so and return.

  1. protected override void WndProc(ref Message m)
  2. {
  3.     new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand();
  4.  
  5.     switch (m.Msg)
  6.     {
  7.         case NativeConstants.WM_APPCOMMAND:
  8.             switch ((ApplicationCommand)m.LParam.ToInt32())
  9.             {
  10.                 case ApplicationCommand.MediaFastForward:
  11.                     fastForwardButton.PerformClick();
  12.                     goto default;
  13.                 case ApplicationCommand.MediaRewind:
  14.                     rewindButton.PerformClick();
  15.                     goto default;
  16.                 case ApplicationCommand.MediaPause:
  17.                     Pause();
  18.                     goto default;
  19.                 case ApplicationCommand.MediaPlay:
  20.                     Play();
  21.                     goto default;
  22.                 case ApplicationCommand.MediaPlayPause:
  23.                     playButton.PerformClick();
  24.                     goto default;
  25.                 case ApplicationCommand.MediaNexttrack:
  26.                     ViewNext();
  27.                     goto default;
  28.                 case ApplicationCommand.MediaPrevioustrack:
  29.                     ViewPrevious();
  30.                     goto default;
  31.                 case ApplicationCommand.MediaStop:
  32.                     stopButton.PerformClick();
  33.                     goto default;
  34.                 case ApplicationCommand.VolumeDown:
  35.                     decreaseVolumeMenu.PerformClick();
  36.                     goto default;
  37.                 case ApplicationCommand.VolumeUp:
  38.                     increaseVolumeMenu.PerformClick();
  39.                     goto default;
  40.                 case ApplicationCommand.VolumeMute:
  41.                     muteVolumeMenu.PerformClick();
  42.                     goto default;
  43.                 case ApplicationCommand.Close:
  44.                     Close();
  45.                     goto default;
  46.                 default:
  47.                     /* According to MSDN, when handling
  48.                      * this message, we must return TRUE. */
  49.                     m.Result = new IntPtr(1);
  50.                     base.WndProc(ref m);
  51.                     return;
  52.             }
  53.     }
  54.  
  55.     /* Other message handlers here… */
  56.  
  57.     base.WndProc(ref m);
  58. }

 

Testing that this all works correctly is simple. You just call SendMessage to send one of the commands to your player form. Here’s an example, as well as the definition for SendMessage, in case you don’t already have it:

  1. internal static class NativeMethods
  2. {
  3.     [DllImport(“user32.dll”)]
  4.     public static extern IntPtr SendMessage(HandleRef hWnd, UInt32 uMsg, IntPtr wParam, IntPtr lParam);
  5. }
  6.  
  7. public class Example: Form
  8. {
  9.     private void TestPause_Click(object sender, EventArgs e)
  10.     {
  11.         HandleRef handle = new HandleRef(this, this.Handle);
  12.         NativeMethods.SendMessage(handle, NativeConstants.WM_APPCOMMAND, this.Handle, new IntPtr((int)ApplicationCommand.MediaPause));
  13.     }
  14. }
Advertisements

About Jerome

I am a senior C# developer in Johannesburg, South Africa. I am also a recovering addict, who spent nearly eight years using methamphetamine. I write on my recovery blog about my lessons learned and sometimes give advice to others who have made similar mistakes, often from my viewpoint as an atheist, and I also write some C# programming articles on my programming blog.
This entry was posted in Programming and tagged , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s