WM_APPCOMMAND和增强输入设备

类别:VC语言 点击:0 评论:0 推荐:

有些文章拷贝过来上传不了,不知道怎么回是,先给大家这些吧,以后再说!

这篇虽然比较老,但总有人需要吧,我想,呵呵!要么就当占用大家的磁盘空间了
WM_APPCOMMAND和增强输入设备
1.0 简介
    为创建一个良好的用户记录,硬件供应商现在对打开和控制软件程式的标准输入设备(如键盘和鼠标)增加了附加的按键和按钮。这些附加输入途径可以打开程式、控制音频和媒体程式以及打开和控制互联网浏览器。在Windows 2000发布前,Windows操作系统要求硬件制造商为这些增强功升级硬件标准。
Windows 2000操作系统Bata 3版,以名为WM_APPCOMMAND的新Windows API形式,为15种新的多媒体事件提供本地支持。当这些新的多媒体事件发生时,这个新定义的消息用焦点通知程式。
2.0 本地操作系统上的WM_APPCOMMAND_Enabled程式
Windows 2000 Bata 3中引入的WM_APPCOMMAND API消息,为程式定义了接收增强按键事件通知的直接方式。而后,程式可以初始化任何与特定键事件相连的预定义功能。例如,一个MDI程式可以使用BACKWARD(后退)和FORWARD(前进)事件来循环文档视窗。加入一个WM_APPCOMMAND句柄可以使程式用新增加的键盘、鼠标及其它支持此扩展功能的新HID设备来工作。当按下一个被支持的键或按钮,一系列的事件发生并导致WM_APPCOMMAND消息送往输入焦点视窗。若该视窗不处理WM_APPCOMMAND消息,操作系统将此消息“冒泡”到视窗的父视窗,如此再三直至顶级视窗。如果任一视窗处理消息并返回TRUE,则此过程结束。如果顶级视窗不处理消息,则WM_APPCOMMAND消息被送往外壳钩子(Sheel  hook)。
2.1 使用DefwindowProc来进行消息清除
WM_APPCOMMAND系列依靠可以适当处理不用的Windows消息的程序。开发者应当不断向DefWindowProc传递不用的键盘、鼠标和WM_APPCOMMAND消息。不用此方式清除无用的消息会中断WM_APPCOMMAND系列。
2.2 WM_APPCOMMAND对虚拟键码的处理
对大多数编程者,处理键盘按键事件最自然的方式是通过使用WM_KEYDOWN和WM_KEYUP消息传送的虚拟键码(VKs),为了支持新的多媒体键盘键和鼠标按钮,这里极力建议不要使用VKs。这样做有两个原因。首先,使用WM_APPCOMMAND,需要建立能产生此消息(如:键盘和鼠标)的所有HID设备支持。由于所有类型的增强设备的进一步支持添加于Windows 2000,因此将会有不产生VKs而是WM_APPCOMMAND的升级设备列表,;第二,WM_KEYDOWN或WM_KEYUP消息用输入焦点送往视窗,且不会”冒泡”到父视窗。这就造成控件“吃掉”这些键消息,并会中断WM_APPCOMMAND系列。
 
图1 Windows 2000中的WM_APPCOMMAND
3.0 故障诊断
问题1:WM_APPCOMMAND-enabled程序,不响应WM_APPCOMMAND消息
可能原因1:没有返回正确的返回值。
    在进程的每一阶段返回正确的值是非常重要的。当一个WM_KEYDOWN消息被处理时,返回值应为FALSE(或0)。当处理WM_XBUTTON或WM_APPCOMMAND消息时,返回值应为TRUE(或1)。图1提供详细信息,附录A提供例程。
可能原因2:没有为DefWindowProc提供未处理的消息。
当程序中加入WM_APPCOMMAND支持时,即给DefWindowProc传送未处理的消息,而不是调用DefWindowProc进行消息清除,会产生常见的错误。在Windows 2000中,当知道虚拟键码或有Xbutton消息到达时,由DefWindowProc产生WM_APPCOMMAND消息。图1提供详细信息,附录A提供例程。
可能原因3:控件“吃掉”消息。
注意:WM_KEYDOWN消息由焦点送往视窗而不是前台视窗,并不会“冒泡”至父视窗。这就意味着,按钮和其它控件可以接收这些消息,并能“吃掉”它们。在所有程序消息处理机制中,使用DefWindowProc进行消息清除很重要。图1提供详细信息,附录A提供例程。
4.0 WM_APPCOMMAND需求
在Windows 2000中,使用微软IntelliType Pro 1.0 或 IntelliPoint 3.0 软件来增强本地 WM_APPCOMMAND 支持,IntelliType Pro 和 IntelliPoint 在传统微软操作系统上模仿了增强WM_APPCOMMAND 支持。
用 Microsoft IntelliType Pro:
Windows 2000: 1.0增强版支持Beta 3;. 1.1 增强版支持Windows 2000.最终发行版
Windows NT4.0: 由IntelliType Pro 1.0+.提供支持Service Pack 3 及后续版本支持。
Windows 95/98: 由IntelliType Pro 1.0+.提供支持。
Windows CE: 不支持。
用 Microsoft IntelliPoint:
Windows 2000: 3.0增强 版支持Beta 3; 3.01 增强版支持Windows 2000.最终发行版。
Windows NT4.0: 由IntelliPoint 3.0+提供Service Pack 3 及后续版本支持。
Windows 95/98: 由IntelliPoint 3.0+提供支持.
Windows CE:不支持。
5.0 WM_APPCOMMAND特性
参见
6.0 如何处理WM_APPCOMMAND消息
程式处理此消息的方式如同处理菜单或工具栏命令。例如:菜单命令可在视窗程序中被如下处理:
case WM_COMMAND:
      switch (wParam)
      {
      case IDC_PLAYPAUSE:
OnPlayPause(hwnd);
         return FALSE;

      case IDC_STOP:
         OnStop(hwnd);
         return FALSE;

      case IDC_PREV:
         OnPrev(hwnd);
         return FALSE;

      case IDC_NEXT:
         OnNext(hwnd);
         return FALSE;
     
[other commands…]
}
[…]

   return DefWindowProc(hwnd, message, wParam, lParam);
键盘命令处理方式与此类似。但有一个重要的不同是:若消息被处理,WM_APPCOMMAND处理程序须返回1,而不是常见返回值0。给DefWindowProc()传递不用的代码也很重要。于是,别的程序可有机会处理它们。
 case WM_APPCOMMAND:
      switch (GET_APPCOMMAND_LPARAM(lParam))
      {
      case APPCOMMAND_MEDIA_PLAY_PAUSE:
      lr = OnPlayPause(hwnd);
      if (lr==NOERROR) return TRUE;
      break;

   case APPCOMMAND_MEDIA_STOP:
      lr = OnStop(hwnd);
      if (lr==NOERROR) return TRUE;
      break;

   case APPCOMMAND_MEDIA_PREVIOUSTRACK:
      lr = OnPrev(hwnd);
      if (lr==NOERROR) return TRUE;
      break;

   case APPCOMMAND_MEDIA_NEXTTRACK:
      lr = OnNext(hwnd);
      if (lr==NOERROR) return TRUE;
      break;
      }

      return DefWindowProc(hwnd, message, wParam, lParam);

6 WM_APPCOMMAND_Enabled程序例程
见附录A。
7 增强键盘的微软IntelliTypePro软件。
1999年,微软硬件分部引入了三个新的增强键盘,新键(热键)允许用户控制互联网浏览器、媒体播放器、邮件客户机程序,以及不用鼠标打开程序。在Windows 2000中,IntelliTypePro软件增强了对现存本地操作系统的支持,对早期的微软操作系统IntelliTypePro也提供了新键的支持。
7.1 Windows 2000中微软IntelliTypePro的WM_APPCOMMAND
在Windows 2000中运行InteliTypePro,由于允许用WM_APPCOMMAND消息(所有媒体消息和“Home”)的子集查询非前台程序,而增强了本地WM_APPCOMMAND。如果正在运行IntelliType和前台视窗,不处理WM_APPCOMMAND产生的消息(且使用DefWindowProc消除此无用消息)。然后IntelliTypePro向当前Z-order中的每个主视窗上传WM_APPCOMMAND消息,直到有视窗使用此消息或所有视窗忽略此消息。
7.2 传统Windows上的微软IntelliTypePro的WM_APPCOMMAND
为在操作系统上保持应用程序的一致性,在先前的微软操作系统发行版中,IntelliTypePro仿效Windows 2000本地WM_APPCOMMAND支持。IntelliTypePro也提供7.1描述的增强功能。在Win95/98/NT.40中(SP3及以后版本)运行的Win32®程序,若系统安装了IntelliTypePro软件。依靠增强的键盘按键将会收到WM_APPCOMMAND消息。WM_APPCOMMAND-ENABLED程序将如同在本地操作系统上工作。
7.3 向IntelliTypePro媒体菜单添加媒体播放器
按下媒体按钮会出当前安装在计算机上媒体播放器。菜单只列出本地WM_APPCOMMAND支持的播放器列表。可给此列表增加新播放器,但不能增加自己的播放器,除非列表可用键盘上的所有媒体键进行工作。当媒体键头次按下时,对已知媒体播放器的搜索被初始化。被支持的媒体播放器列表随后出现在注册表键: HKEY_CURRENT_USER\Software\Microsoft\Keyboard下。
为给此列表增加媒体播放器,须有新播放器.exe文件的路径和想在媒体菜单显示的名称。
7.3.1 何时添加一个新播放器
新的播放器在安装到计算机系统上时,由注册表添加到IntelliPro媒体菜单。此进程级需再行执行。注册表本质上是不允许在同一主键的多份复件。所有用此法增加的程式会显示在媒体菜单上,而只有由WM_APPCOMMAND-ENABLED或由IntelliTypePro软件支持的程式可用此增强 键盘键的功能。
7.3.2 何时新播放器在媒体菜单中列出
新增的播放器在被添加到注册表及“media”(媒体)键被按下后,会立刻显现到媒体菜单中。只有合法的.exe文件被列表。在每个媒体键上,在媒体菜单列表前,按下IntelliTypePro软件未验证.exe文件存在于列表的路径。
7.3.3 添加播放器到媒体菜单的步骤
① 在注册表主键HKEY_CURRENT_USER\Software\Microsoft\Keyboard下,检查名为“Native Media Players”的主键,若不存在,则创建之(参见附录B创建主键的例子)
② 在主键HKCU\Software\Microsoft\Keyboard\Native Media Players下创建有两个串值的主键,此键盘应由新添加的设备来描述。
③ 在新创建的键中,添加标为“APPName”的串值,此值应为想在媒体菜单中显示的名字(如“New Player”)。
④ 在新创建的键中,添加标为“ExePath”的串值,此值应为播放器的.exe文件的确切路径(如:c:\Program files\Newplayer\newplay.exe)。
7.3.4 例程代码
附录B包含可向IntelliTypePro媒体菜单添加媒体播放器的代码。
8.0 WM_APPCOMMAND和微软鼠标
    微软发布了新型的五键IntelliMouse®资源管理器,新的按钮默认为向前和向后,一如在互联网浏览器中的前进与后退按钮。Windows 2000有为这些键内置消息的支持。在传统的微软操作系统中,新的IntelliPoint 3.0软件添加了对新按钮的全部支持。IntelliPoint 3.0也允许在所有微软鼠标产品按钮上指派此功能。
8.1 Windows 2000新消息
Windows 2000添加新的消息到新的鼠标按钮,即Xbutton消息,与三键按钮消息类似,主要的不同是,两种键的独立消息。在Wparam中设置一个标志位来指定那个键执行动作。若键一是有前进与后退功能的程序,最好处理WM_APPCOMMAND消息。这就认可了前进/后退功能,而不管鼠标、键盘或其它将来的设备是否被连接。若要代替与鼠标有关的行为的按钮的使用,如在CAD程序中的可选工具,好的方法是响应Xbutton消息。当处理Xbutton消息时,切记返回TRUE,以阻止WM_APPCOMMAND消息的产生。若有程序不使用WM_APPCOMMAND消息本身,IntelliPoint将在有未处理WM_APPCOMMAND消息的程序中试图去执行一个前进/后退行为。
8.3 在传统操作系统上微软IntelliPoint的WM_APPCOMMAND
为在操作系统中保持程序功能的连贯性,IntelliPoint 3.0模仿Windows 2000的本地WM_APPCOMMAND和Xbutton支持。当安装IntelliPoint后,在Windows 95/98/NT4.0上运行的Win32程式将会收到Xbutton和WM_APPCOMMAND消息,如同它们运行在本地操作系统上。
9.0 支持五键鼠标的新Windows消息
如下:
WM_XBUTTONDOWN
WM_XBUTTONUP
WM_XBUTTONDBLCLK
WM_NCXBUTTONDOWN
WM_NCXBUTTONUP
WM_NCXBUTTONDBLCLK
附录A:使用WM_APPCOMMAND的简单媒体播放器
此例是简单的媒体播放器且处理来自增强 增强 键盘的所有WM_APPCOMMAND媒体事件。增加增强 鼠标支持将为Xbutton消息简单添加消息句柄:
#include <Windows.h>
#include "resource.h"


LPARAM WINAPI MainWndProc( HWND,UINT,WPARAM,LPARAM );
BOOL InitDialog( HWND );
BOOL OnClose(HWND hwnd);
BOOL OnTimer(HWND hwnd);

BOOL OnPlayPause(HWND hwnd);
BOOL OnStop(HWND hwnd);
BOOL OnPrev(HWND hwnd);
BOOL OnNext(HWND hwnd);


//
//The supported WM_APPCOMMAND events
//
#ifndef WM_APPCOMMAND

#define WM_APPCOMMAND               0x319
#define APPCOMMAND_MEDIA_NEXTTRACK         11
#define APPCOMMAND_MEDIA_PREVIOUSTRACK      12
#define APPCOMMAND_MEDIA_STOP            13
#define APPCOMMAND_MEDIA_PLAY_PAUSE         14
#define FAPPCOMMAND_MASK  0x8000
#define GET_APPCOMMAND_LPARAM(lParam) ((short)(HIWORD(lParam) & ~FAPPCOMMAND_MASK))

#endif


//
//Player constants
//
const UINT DISPLAY_TIMER_ID = 100;
const UINT TIMER_INTERVAL = 1000;
const UINT NUM_TRACKS = 10;

HINSTANCE hInstance = 0;

UINT trackIndex;
UINT trackTime;

CHAR szTrackIndex[3];
CHAR szTrackTime[5];

BOOL isPlaying;
BOOL isBetweenTracks;

//------------------------------------------------------------
//  WinMain()
// 
//   Main windows routine. All the usual stuff.
//
//-------------------------------------------------------------
INT PASCAL WinMain( HINSTANCE hInstance,
                    HINSTANCE hPrevInstance,
                    LPSTR  lpCmdLine,
                    int    nCmdShow )
{
    ::hInstance = hInstance;

//
// Register the main window class
//
WNDCLASS  wc;

wc.style = CS_VREDRAW | CS_HREDRAW;
wc.lpfnWndProc = (WNDPROC)MainWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = DLGWINDOWEXTRA;
wc.hInstance = hInstance;      
wc.hIcon        = 0;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground= CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
wc.lpszMenuName =  NULL;
wc.lpszClassName= "APP_CMD";

if(!RegisterClass(&wc))
return FALSE;


// Create the main window as dialog.
//
HWND hwndMain = CreateDialog(hInstance, "APP_CMD", 0, NULL);
   ShowWindow(hwndMain,SW_SHOWNORMAL);

   //initialize
   InitDialog(hwndMain);

MSG msg;
   while (GetMessage(&msg, NULL,0,0))
   {
              TranslateMessage(&msg);
              DispatchMessage(&msg);
   }
   return (msg.wParam);
} // end WinMain()

 


///*------------------------------------------------------------
//
//   MainWndProc() - Main window callback procedure.
// 
// -------------------------------------------------------------

LPARAM WINAPI MainWndProc( HWND hwnd,
                           UINT msg,
                           WPARAM wParam,
                           LPARAM lParam )
{
   switch (msg){

   //Handle the WM_APPCOMMAND messages here. Return TRUE if we handle the message.
//
   case WM_APPCOMMAND:
      switch (GET_APPCOMMAND_LPARAM(lParam))
      {
      case APPCOMMAND_MEDIA_PLAY_PAUSE:
         OnPlayPause(hwnd);
         return TRUE;

      case APPCOMMAND_MEDIA_STOP:
         OnStop(hwnd);
         return TRUE;

      case APPCOMMAND_MEDIA_PREVIOUSTRACK:
         OnPrev(hwnd);
         return TRUE;

      case APPCOMMAND_MEDIA_NEXTTRACK:
         OnNext(hwnd);
         return TRUE;
      }
      break;

   case WM_CLOSE:
      return OnClose(hwnd);

   case WM_TIMER:
      return OnTimer(hwnd);

   //Handle the interface messages here. Return FALSE if we handle the message
   //
   case WM_COMMAND:
      switch (wParam)
      {
      case IDC_PLAY:
      case IDC_PAUSE:
         OnPlayPause(hwnd);
         return FALSE;

      case IDC_STOP:
         OnStop(hwnd);
         return FALSE;

      case IDC_PREV:
         OnPrev(hwnd);
         return FALSE;

      case IDC_NEXT:
         OnNext(hwnd);
         return FALSE;
      }
      break;
   }
   //Clean up any unused messages by calling DefWindowProc
   //
   return DefWindowProc(hwnd,msg,wParam,lParam);
}


//
//Initialize the player UI
//
BOOL InitDialog(HWND hwnd)
{
   // start on first track
   trackIndex = 0;

   // start at beginning of track
   trackTime = 0;

   // don't play until the user tells us to
   isPlaying = FALSE;


   // set the icon for the "play" button
   HICON hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_PLAY));
   SendDlgItemMessage(hwnd,IDC_PLAY,BM_SETIMAGE,IMAGE_ICON,(LPARAM)hIcon);

   // set the icon for the "pause" button
   hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_PAUSE));
   SendDlgItemMessage(hwnd,IDC_PAUSE,BM_SETIMAGE,IMAGE_ICON,(LPARAM)hIcon);

   // set the icon for the "stop" button
   hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_STOP));
   SendDlgItemMessage(hwnd,IDC_STOP,BM_SETIMAGE,IMAGE_ICON,(LPARAM)hIcon);

   // set the icon for the "previous" button
   hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_PREV));
   SendDlgItemMessage(hwnd,IDC_PREV,BM_SETIMAGE,IMAGE_ICON,(LPARAM)hIcon);

   // set the icon for the "next" button
   hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_NEXT));
   SendDlgItemMessage(hwnd,IDC_NEXT,BM_SETIMAGE,IMAGE_ICON,(LPARAM)hIcon);


   // start a timer to update the track time display
   SetTimer(hwnd, DISPLAY_TIMER_ID, TIMER_INTERVAL, NULL);

   return TRUE;
}

//
//Close the Application
//
BOOL OnClose(HWND hwnd)
{
   KillTimer(hwnd, DISPLAY_TIMER_ID);
   DestroyWindow(hwnd);
   PostQuitMessage(0);
   return TRUE;
}

//
//Update Track UI
//
void UpdateTrackInfo(HWND hwnd)
{
   wsprintf(szTrackIndex, "%d", trackIndex+1);
   SetDlgItemText(hwnd, IDC_TRACKINDEX, szTrackIndex);

   wsprintf(szTrackTime, "0:%02d", trackTime);
   SetDlgItemText(hwnd, IDC_TRACKTIME, szTrackTime);
}

//
//Timer controls how fast the UI "time" display changes
//
BOOL OnTimer(HWND hwnd)
{
   if (isPlaying)
   {
      // make sure the display is visible
      UpdateTrackInfo(hwnd);
      HWND hwndDisplay = GetDlgItem(hwnd, IDC_TRACKTIME);
      ShowWindow(hwndDisplay, SW_SHOW);

      // advance the counter
      ++trackTime;

      // every track is 60 seconds
      if (trackTime >= 60)
         OnNext(hwnd);   // advance to the beginning of the next track
   }
   else
   {
      // get the track time window
      HWND hwndDisplay = GetDlgItem(hwnd, IDC_TRACKTIME);

      // flash the display
      if (IsWindowVisible(hwndDisplay))
         ShowWindow(hwndDisplay, SW_HIDE);
      else
         ShowWindow(hwndDisplay, SW_SHOW);
   }

   UpdateTrackInfo(hwnd);
   return TRUE;
}

//
//Start/Pause the UI’s "time" index
//
BOOL OnPlayPause(HWND hwnd)
{
   // toggle between play and pause
   isPlaying = !isPlaying;

   UpdateTrackInfo(hwnd);
   return TRUE;
}

//
//Stop the UI’s "time" index
//
BOOL OnStop(HWND hwnd)
{
   // stop playing and move to beginning of track
   isPlaying = FALSE;
   trackTime = 0;

   UpdateTrackInfo(hwnd);
   return TRUE;
}

//
//Goto the previous track
//
BOOL OnPrev(HWND hwnd)
{
   // if the user is at the beginning of a track
   if (trackTime<1)
   {
      // set the track index to the previous track
      if (trackIndex==0)
         trackIndex = NUM_TRACKS-1;
      else
         --trackIndex %= NUM_TRACKS;
   }

   // go back the beginning of the track
   trackTime = 0;

   UpdateTrackInfo(hwnd);
   return TRUE;
}

//
//Goto the next track
//
BOOL OnNext(HWND hwnd)
{
   // go to the beginninf of the next track
   ++trackIndex %= NUM_TRACKS;
   trackTime = 0;

   UpdateTrackInfo(hwnd);
   return TRUE;
}

附录B:向IntelliTypePro媒体菜单中添加媒体播放器的例子。
#define MSKB_KEY "Software\\Microsoft\\Keyboard\\Native Media Players"

BOOL AddPlayerToRegistry ( LPSTR szDisplayString, LPSTR szExePath )
{
   //open Microsoft Keyboard Native Media key OR if it does not exist, create it
   HKEY hKey;
   DWORD dwDisposition;
   if ( ERROR_SUCCESS != RegCreateKeyEx(
     
            HKEY_CURRENT_USER,
            MSKB_KEY,
            0, 0,
            REG_OPTION_NON_VOLATILE,
            KEY_ALL_ACCESS,
            0,
            &hKey,
            &dwDisposition
            )
   ) return 0;


   //create a key for new media player (using DisplayString as key name)
   HKEY hSubKey;
   if ( ERROR_SUCCESS != RegCreateKeyEx(
     
            hKey,
            szDisplayString,
            0, 0,
            REG_OPTION_NON_VOLATILE,
            KEY_ALL_ACCESS,
            0,
            &hSubKey,
            &dwDisposition
            )
   ) return 0;


   //Add AppName string value to new key and copy in the DisplayString
   if ( ERROR_SUCCESS != RegSetValueEx(
     
            hSubKey,
            "AppName",
            0,
            REG_SZ,
             (const BYTE*)szDisplayString,
            strlen(szDisplayString)
            )
   ) return 0;


   //Add ExePath string value to new key and copy in the ExePath
   if ( ERROR_SUCCESS != RegSetValueEx(
     
            hSubKey,
            "ExePath",
            0,
            REG_SZ,
             (const BYTE*)szExePath,
            strlen(szExePath)
            )
   ) return 0;


   //close reg keys
   if ( ERROR_SUCCESS != RegCloseKey(hKey) ) return 0;
   if ( ERROR_SUCCESS != RegCloseKey(hSubKey) ) return 0;

   return 1;
}

本文地址:http://com.8s8s.com/it/it2211.htm