C++博客 :: 首页 :: 联系 :: 聚合  :: 管理
  117 Posts :: 2 Stories :: 61 Comments :: 0 Trackbacks

常用链接

留言簿(8)

搜索

  •  

最新评论

阅读排行榜

评论排行榜

 

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_SYSKEYDOWNWM_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_SYSKEYUPWM_SYSKEYDOWN而言,此位总视为1,而对WM_SYSKEYUPWM_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++代码参考与技巧大全》学习笔记——索引随笔

posted on 2010-02-17 23:38 烟皑 阅读(3132) 评论(2)  编辑 收藏 引用 所属分类: 《Visual C++代码参考与技巧大全》学习笔记

Feedback

# re: ASCII码查询器 2010-04-04 01:36 huangzongwu@hotmail.com
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);
}

这个函数很明显陷入死循环了,好好想想吧

  回复  更多评论
  

# re: ASCII码查询器 2012-09-12 10:16 侯云猛
??
  回复  更多评论
  


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理