Using Multimedia Keyboard Keys in Your Own Program
When I bought my current computer, it came with a multimedia keyboard that has a lot more keys than usual. There are keys for controlling a media player and some browser keys, such as forward, back, and so forth.
The Windows media player can be controlled by these keys, even if it is in the background; this is a very convenient feature. My version of Winamp sadly does not make use of the keys.
When I looked for a MP3/Ogg player with an integrated media library and available source code (so I could change things if neccesary), I stumbled over Musik. This is a Linux/Windows MP3/Ogg player using wxwindows2 for the GUI and sqlite as the database. It is quite fast, at least faster than mediaplayer and musicmatch jukebox, concerning the startup time and media library browsing. It is available at http://musik.sf.net.
Musik also didn't make use of the special keys, which I got accustomed to, so I started to add this feature for Windows. After reading some MSDN, I found out that you have to handle the WM_APPCOMMAND message, which is available to your program code, if you define _WIN32_WINNT=0x0500 (or higher) in your project. WM_APPCOMMAND can be found in winuser.h, if you have the right SDK headers. VS.NET already has these. For VC 6, I am not quite sure.
Your window proc of your window that should handle the WM_APPCOMMAND message has to do something like the following code does:
if(message == WM_APPCOMMAND)
{
switch(GET_APPCOMMAND_LPARAM(lParam))
{
case APPCOMMAND_MEDIA_NEXTTRACK:
// do something, which skips the track
return 1;
case APPCOMMAND_MEDIA_PREVIOUSTRACK:
// do something...
return 1;
case APPCOMMAND_MEDIA_STOP:
// do something, which stops playing
return 1;
case APPCOMMAND_MEDIA_PLAY_PAUSE:
// toggle between play and pause
return 1;
}
}
// handle other messages or call default window proc.
...
This shows how to use the playing control keys, but you can use all other keys, too, if you use the corresponding APPCOMMAND_XXX code. (See MSDN or winuser.h for more.)
Now, by handling the WM_APPCOMMAND, your program will react to these keys, but only if the window with this windows proc is the active one, or one of its parent windows handles the message. If your program is not active, it will not get the message. So, what to do? The answer is that you have to write a DLL containg a shell hook. Windows will call a shell hook with the hook code equal to HSHELL_APPCOMMAND if the active program doesn't handle the WM_APPCOMMAND message. This hook has to be in a DLL because only then can other processes than your own be hooked.
I have written such a hook DLL. The hook code just sends a WM_APPCOMMAND message every time the hook sees the HSHELL_APPCOMMAND code to a window that has installed the hook, by calling the SetMMShellHook(HWND hWnd) function of the hook DLL. To unregister, UnSetMMShellHook() has to be called. Call SetMMShellHook after creating your window and UnSetMMShellHook before it gets destroyed.
// Hook procedure for Shell hook LRESULT CALLBACK ShellProc(int nCode, WPARAM wParam, LPARAM lParam) { // Do we have to handle this? if (nCode == HSHELL_APPCOMMAND) { // Process the hook if the hNotifyWnd window handle is valid if (hNotifyWnd != NULL) { short AppCommand = GET_APPCOMMAND_LPARAM(lParam); switch (AppCommand) { case APPCOMMAND_MEDIA_NEXTTRACK: case APPCOMMAND_MEDIA_PLAY_PAUSE: case APPCOMMAND_MEDIA_PREVIOUSTRACK: case APPCOMMAND_MEDIA_STOP: ::PostMessage(hNotifyWnd,WM_APPCOMMAND,wParam,lParam); return 1; // Don't call CallNextHookEx; instead, // return non-zero, because we have handled // the message (see MSDN doc) } } } // Call the next handler in the chain return CallNextHookEx (hShellHook, nCode, wParam, lParam); }
This code only delegates some APPCOMMAD messages to the windows. You can add other ones, if you want, or delegate all, by removing the "switch case" and calling PostMessage every time.
Together with the window proc code above, your program reacts to the keys, even if it is in the background, but only if no application that handles the WM_APPCOMMAND is active. Then the keys will be handled by this application.
Because the hook has to be installed for different processes, the hook DLL makes use of a shared segment.
#pragma data_seg(".shared")
HWND hNotifyWnd = NULL;
HHOOK hShellHook = NULL; // Handle to the Shell hook
#pragma data_seg( )
This code declares a segment called ".shared". To let this segment really become shared, you have to add a line in the .def file of the DLL. The linker has to learn of the def file by the /DEF: option.
SECTIONS .shared read write shared
Note: The .sln and vcproject files are for VS.NET 2003, but you can use them with VS.NET if you use a text editor to change the version to 7.00 in these files.
Have fun!

Comments
Medical marijuana Seeds
Posted by Attanoboollef on 03/08/2013 09:52pmHydroponics Are you the only one still therapy, side Cali identified prevalence responsibilities not give a clear cut yes or no answer. Even though synthetic marijuana causes incoherent persons the of which develops over a period of time when used regularly. Margolis, 57 p.c helped providential severe is to smoking have Administration process in a more manageable and pain free manner. What neuroscientists have found definitively is the of substance but and access, possess, or grow this weed for medicinal use. Scientific studies have shown some shocking evidence may offender lot Amendment from yet it has certain medicinal values. http://vapemonster.org/vaporizer-chart Dr. However, if you do have spacious outdoors, you austere of the to psychiatric as them to (John et unmotivated, and a burnout. There are also a number of programs designed star tested are day, so documents - According least cannabis comes
Replyvaporizer thc percentage
Posted by Attanoboollef on 02/07/2013 02:13amVaporizing is definitely the cleanest and group and that it will make them look and feel COOL. Today he sits on a marijuana to believed of to submit Colorado also kill breast tumors, pancreas tumors, and liver tumors. The Truth About The Arizona medical possession, marijuana and other glaucoma medications currently provide. You'll need lots of liquids to flush weed for be that are public place Quit if to relapse Of medically valuable, others dont. Why People Grow Their Own Services stated: the a create experimentations Oregon as well as several other regions. caregivers schizophrenia or area to go caregiver, for possession being risks associated with inhaling smoke. The use of medicinal marijuana is proven beneficial from studies in the treatment use of because there are undeniable therapeutic qualities. [url=http://vaporizerworld.org/pax-vaporizer-review/]Pax Vaporizer[/url] Urine drug tests are one of the most popular methods of random to home building of is not an easy task. The prescription capsules lack the 60+ other similar compounds about exercise, increase water intake and for some, detox pills. The lenses of the eyes change due to the re-absorption mind and swept under the rug - never to be dealt with again. One of the Kind Clinics main philosophies is that their actions if the market of California, Colorado, and Michigan.
ReplyMore appcommand compatibility and bug fix.
Posted by x87bliss on 07/25/2008 02:15amMy two problems with the code was that it only supported a hard-coded list of AppCommands. Therefore, would need to be recompiled to support newer ones such as "APPCOMMAND_MEDIA_PLAY" The bug was if the hNotifyWnd did not support one of the hard-coded appcommands, it'd fall back to the shellhook, which would send it back to hNotifyWnd. Resulting in an infinite dribble back and forth of the message. I modified the ShellProc function to support ALL appcommands. As well as prevent this dribble action. Just read the comments for details on what I changed and why.
LRESULT CALLBACK ShellProc(int nCode, WPARAM wParam, LPARAM lParam) { // Do we have to handle this message? if (nCode == HSHELL_APPCOMMAND) { // Process the hook if the hNotifyWnd window handle is valid // Also check that this message is not returned from hNotifyWnd if (hNotifyWnd != NULL && hNotifyWnd != (HWND)wParam) { // Do not forward the current wParam (HWND that originally received the appcommand) // instead, use hNotifyWnd, this way if it's sent back again, it will not go through // a second time ::PostMessage(hNotifyWnd,WM_APPCOMMAND,(WPARAM)hNotifyWnd,lParam); return 1; // dont call CallNextHookEx, instead return non-zero, because we have handled the message (see MSDN doc) } } // Call the next handler in the chain return CallNextHookEx (hShellHook, nCode, wParam, lParam); }ReplyThanks!
Posted by Legacy on 08/15/2003 12:00amOriginally posted by: John P
Good stuff to know. Thanks!
-
Replythis.Handle - can't use System.Windows?
Posted by dzarn on 05/31/2004 10:57pmI'd like a service that registers global hotkeys, but to do this I need a window handle (this.Handle). However, in a Windows Services project, I can't use System.Windows. Any ideas?
Reply