1、 问题阐述
在很多情况下,程序员迫切需要取得一个按键的ASCII码值,一些常用的字符的ASCII可以去记住它,比如‘A’,‘1’等,但是一些生僻的字符的ASCII,比如‘:’、‘+’等,就要去查ASCII码表,扩展ASCII码共有256个字符,查询时浪费时间并且有时还会查询错误。下面写一个程序,程序在接受到某个按键消息时,将接受到按键的转换为ASCII码。
2、 实现技巧
Windows程序是消息机制的,键盘在按某个键时,会产生一个按键消息,这个消息被操作系统获得。程序的处理重点就是如何从操作系统捕获这个消息,从这个消息的参数中解析出所需要的值。应用程序从Windows接收的关于键盘事件的消息称为按键消息。
按键消息,当按下一个键时,操作系统把WM_KEYDOWN或者WM_SYSDOWN消息放入消息队列中,当释放一个按键时,操作系统把WM_KEYUP或者WM_SYSDOWN消息放入消息队列中。
DOWN和UP这一组消息通常成对出现。如果按住某个键长时间不动,让它重复产生相同的功能,操作系统将连续的WM_KEYDOWN或者WM_SYSDOWN送入应用程序的消息处理窗口。释放该键的时候,操作系统将连续的WM_KEYUP或者WM_SYSDOWN送入应用程序的消息处理窗口。
按键之间也是有时间间隔,通过调用GetMessageTime()获取按键和释放按键的时间间隔,WM_SYSKEYDOWN和WM_SYSKEYUP是一对系统键消息,类似地消息由系统自动处理,用户无需去截获。产生这种消息的按键多为组合键,比如Alt+F,Ctrl+S等。
消息原型消息名:WM_KEYDOWN,参数nVirtKey=(int)wParam; IKeyData=IParam;,
消息名:WM_KEYUP,参数nVirtKey=(int)wParam; IKeyData=IParam;,
其中,参数wParam是虚拟键码。这个值是由键盘驱动程序转换扫描码获得的,它是与设备无关的,所以称为虚拟键码,大多数虚拟键码的名称在WINUSER.H表头文件中都定义为VK_开头的。
注意,数字和字母的虚拟键码是ASCII码。
Windows程序几乎从不使用这些虚拟键码,实际上,程序使用的是ASCII码字符的字符消息。参数IParam参数则含有对了解按键非常有用的其他信息。
IParam的32位分为6个字段,其中0~15表示重复计数,重复计数是该消息所表示的按键次数;16~23表示OEM扫描码,它是由硬件产生的值;24表示扩充键标识;25~28表示保留位;29表示内容代码。
按右键时,假如同时按下【Alt】键,那么内容代码为1,对WM_SYSKEYUP与WM_SYSKEYDOWN而言,此位总视为1,而对WM_SYSKEYUP与WM_KEYDOWN消息而言,此位为0;
30表示先前状态,如果在此之前键是释放的,则键的先前状态为0,否则为1,
对WM_KEYUP或者WM_SYSKEYUP消息,它总是设定为1,但是对WM_KEYDOWN或者WM_SYSKEYDOWN消息,此位可以为0,也可以为1,如果为1,则表示该键是自动重复功能所产生的第二个或者后续消息;31为表示转换状态,如果键正被按下,则转换状态为0,如果键正被释放,则转换状态为1,对WM_KEYDOWN或者WM_SYSKEYDOWN消息,此字段为0,对WM_KEYUP或者WM_SYSKEYUP消息,此字段为1。
3、 实例代码
接下来将开始制作一个ASCII码查询器。首先利用VC++6.0向导建立一个基于MFC的对话框工程QueryASCII,在界面上放置两个只读的EDIT,其中一个用于接收字符的值,另外一个显示相应的ASCII码值,界面布局如下图所示(关于如何建立MFC工程,可参考随笔——在VC6下建立MFC工程图解)
①通过class winzard(View菜单下)添加对话框的WM_KEYDOWN消息响应函数OnKeyDown,如下图所示:
②通过class winzard(View菜单下)添加对话框QueryASCII的虚继承函数PreTranslateMessage,如下图所示:
添加好后,分别在相应的函数里面添加相应的代码,因为我也不是很懂,就写了很多注释,代码如下:
/**//* 接受KEYDOWN消息 */
// 关于OnKeyDown函数请看注释一
void CQueryASCIIDlg::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
// TODO: Add your message handler code here and/or call default
CString strTemp;
char ch=nChar;
strTemp.Format("%c",ch); //关于Format的说明请看注释二
SetDlgItemText(IDC_CHAR_ED,strTemp); //当程序运行时出现,IDC_CHAR_ED未定义的说明时,请看注释三
strTemp.Format("%0x",nChar);
SetDlgItemText(IDC_ASCII_ED,strTemp);
// then do not forget to call baseclass, 下面的是回调函数
CDialog::OnKeyDown(nChar, nRepCnt, nFlags); //想获得更多知识可看注释四
}
/**//* 翻译信息 */
// 关于PreTranslateMessage函数的解释可看注释五
BOOL CQueryASCIIDlg::PreTranslateMessage(MSG* pMsg)
{
// TODO: Add your specialized code here and/or call the base class
if(pMsg->message == WM_KEYDOWN)
// pMsg->hwnd = m_hWnd;
return CDialog::PreTranslateMessage(pMsg);
}
/**//* 注释一 */
/**//*******************************************************************/
/**//* typedef UINT unsign int,占4个字节,即为无符号整型
/* nChar: Specifies the virtual key code of the given key. For a list
/* of of standard virtual key codes, see Winuser.h
/* 指定给定键的虚拟键码。有关标准的虚拟键码清单,可查看Winuser.h中
/* nRepCnt: Repeat count(the number of times the keystroke is repeated
/* as a result of the user holding down the key).
/* 重复计数(当用户按住了键,才会造成按键的数目重复)。
/* nFlags: Specifies the scan code, key-transition code, previous key
/* state, and context code
/* 指定扫描码,关键过渡代码,上一个键的状态以及代码的上下文
/*******************************************************************/
/**//* 注释二 */
/**//********************************************************************************/
/**//* Format(String, Object)
/* 将指定的 String 中的格式项替换为指定的 Object 实例的值的文本等效项。
/*
/* Format(String, array<Object>[]()[])
/* 将指定 String 中的格式项替换为指定数组中相应 Object 实例的值的文本等效项。
/*
/* Format(IFormatProvider, String, array<Object>[]()[])
/* 将指定 String 中的格式项替换为指定数组中相应 Object 实例的值的文本等效项。指定
/* 的参数提供区域性特定的格式设置信息。
/*
/* Format(String, Object, Object)
/* 将指定的 String 中的格式项替换为两个指定的 Object 实例的值的文本等效项。
/*
/* Format(String, Object, Object, Object)
/* 将指定的 String 中的格式项替换为三个指定的 Object 实例的值的文本等效项
/********************************************************************************/
/**//* 注释三 */
/**//********************************************************************************/
/**//* IDC_CHAR_ED和IDC_ASCII_ED指的是Edit Box的ID,只要右键单击字符对应的Edit Box选中
/* Properties(属性),General里的ID选项改为IDC_CHAR_ED即可。ASCII对应的Edit Box的
/* ID改为IDC_ASCII_ED即可。
/* SetDlgItemText是一个函数,设置对话框中控件的文本和标题。
/* 函数原型:
/* BOOL SetDlgltemText(HWND hDlg,int nlDDlgltem,LPCTSTR IpString);
/* 参数:
/* hDlg:指定含有控件的对话框。
/* nlDDlgltem:标识带有将被设置的标题和文本的控制。
/* IpString:指向一个以NULL结尾的字符串指针,该字符串指针包含了将被复制到控制的文本。
/* 返回值:如果函数调用成功,则返回值为非零值。如果函数调用失败,则返回值为零。
/* 若想获得更多的错误信息,请调用GetLastError函数。
/*******************************************************************************/
/**//* 注释四 */
/**//*******************************************************************************/
/**//* 要用“回车键”,“左移光标健”,“右移光标健”,“上移光标健”,“下移光标健”
/* 这几个键,你可以在这样:
/* switch(nChar)
/* {
/* case vk_return:
/* //your code
/* case vk_right:
/* //your code
/* case vk_left:
/* case vk_up:
/* case vk_down:
/* }
/*******************************************************************************/
/**//* 注释五 */
/**//****************************************************************/
/**//* 函数原型:virtual BOOL PreTranslateMessage(MSG* pMsg);
/* 功能:重载该函数可以实现窗口消息派发给窗口函数TranslateMessage()
/* 和DispatchMessage()之前的过滤。默认的实现是完成加速键的翻译。
/* 因为您必须在你的重载版本中调用CWinApp:PreTranslateMessage()函数.
/* 在MFC中,PreTranslateMessage()是虚函数,我们可以重载它来处理键盘和鼠标消息。
/* 在SDK中,这又有所不同,我们必须在回调函数中
/* LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
/* 处理消息,它和PreTranslateMessage起的作用是类似的。只是MFC封装的更好而已
/* 更多相关信息可查看随笔:http://www.cppblog.com/kangnixi/archive/2010/02/17/107989.html
/****************************************************************/
我对代码进行了修改和优化,如下所示
(未完,稍等)
想要获得更多内容,可点击:《Visual C++代码参考与技巧大全》学习笔记——索引随笔