有些文章拷贝过来上传不了,不知道怎么回是,先给大家这些吧,以后再说!
这篇虽然比较老,但总有人需要吧,我想,呵呵!要么就当占用大家的磁盘空间了
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;
}