1、 問題闡述
在很多情況下,程序員迫切需要取得一個(gè)按鍵的ASCII碼值,一些常用的字符的ASCII可以去記住它,比如‘A’,‘1’等,但是一些生僻的字符的ASCII,比如‘:’、‘+’等,就要去查ASCII碼表,擴(kuò)展ASCII碼共有256個(gè)字符,查詢時(shí)浪費(fèi)時(shí)間并且有時(shí)還會(huì)查詢錯(cuò)誤。下面寫一個(gè)程序,程序在接受到某個(gè)按鍵消息時(shí),將接受到按鍵的轉(zhuǎn)換為ASCII碼。
2、 實(shí)現(xiàn)技巧
Windows程序是消息機(jī)制的,鍵盤在按某個(gè)鍵時(shí),會(huì)產(chǎn)生一個(gè)按鍵消息,這個(gè)消息被操作系統(tǒng)獲得。程序的處理重點(diǎn)就是如何從操作系統(tǒng)捕獲這個(gè)消息,從這個(gè)消息的參數(shù)中解析出所需要的值。應(yīng)用程序從Windows接收的關(guān)于鍵盤事件的消息稱為按鍵消息。
按鍵消息,當(dāng)按下一個(gè)鍵時(shí),操作系統(tǒng)把WM_KEYDOWN或者WM_SYSDOWN消息放入消息隊(duì)列中,當(dāng)釋放一個(gè)按鍵時(shí),操作系統(tǒng)把WM_KEYUP或者WM_SYSDOWN消息放入消息隊(duì)列中。
DOWN和UP這一組消息通常成對(duì)出現(xiàn)。如果按住某個(gè)鍵長(zhǎng)時(shí)間不動(dòng),讓它重復(fù)產(chǎn)生相同的功能,操作系統(tǒng)將連續(xù)的WM_KEYDOWN或者WM_SYSDOWN送入應(yīng)用程序的消息處理窗口。釋放該鍵的時(shí)候,操作系統(tǒng)將連續(xù)的WM_KEYUP或者WM_SYSDOWN送入應(yīng)用程序的消息處理窗口。
按鍵之間也是有時(shí)間間隔,通過調(diào)用GetMessageTime()獲取按鍵和釋放按鍵的時(shí)間間隔,WM_SYSKEYDOWN和WM_SYSKEYUP是一對(duì)系統(tǒng)鍵消息,類似地消息由系統(tǒng)自動(dòng)處理,用戶無需去截獲。產(chǎn)生這種消息的按鍵多為組合鍵,比如Alt+F,Ctrl+S等。
消息原型消息名:WM_KEYDOWN,參數(shù)nVirtKey=(int)wParam; IKeyData=IParam;,
消息名:WM_KEYUP,參數(shù)nVirtKey=(int)wParam; IKeyData=IParam;,
其中,參數(shù)wParam是虛擬鍵碼。這個(gè)值是由鍵盤驅(qū)動(dòng)程序轉(zhuǎn)換掃描碼獲得的,它是與設(shè)備無關(guān)的,所以稱為虛擬鍵碼,大多數(shù)虛擬鍵碼的名稱在WINUSER.H表頭文件中都定義為VK_開頭的。
注意,數(shù)字和字母的虛擬鍵碼是ASCII碼。
Windows程序幾乎從不使用這些虛擬鍵碼,實(shí)際上,程序使用的是ASCII碼字符的字符消息。參數(shù)IParam參數(shù)則含有對(duì)了解按鍵非常有用的其他信息。
IParam的32位分為6個(gè)字段,其中0~15表示重復(fù)計(jì)數(shù),重復(fù)計(jì)數(shù)是該消息所表示的按鍵次數(shù);16~23表示OEM掃描碼,它是由硬件產(chǎn)生的值;24表示擴(kuò)充鍵標(biāo)識(shí);25~28表示保留位;29表示內(nèi)容代碼。
按右鍵時(shí),假如同時(shí)按下【Alt】鍵,那么內(nèi)容代碼為1,對(duì)WM_SYSKEYUP與WM_SYSKEYDOWN而言,此位總視為1,而對(duì)WM_SYSKEYUP與WM_KEYDOWN消息而言,此位為0;
30表示先前狀態(tài),如果在此之前鍵是釋放的,則鍵的先前狀態(tài)為0,否則為1,
對(duì)WM_KEYUP或者WM_SYSKEYUP消息,它總是設(shè)定為1,但是對(duì)WM_KEYDOWN或者WM_SYSKEYDOWN消息,此位可以為0,也可以為1,如果為1,則表示該鍵是自動(dòng)重復(fù)功能所產(chǎn)生的第二個(gè)或者后續(xù)消息;31為表示轉(zhuǎn)換狀態(tài),如果鍵正被按下,則轉(zhuǎn)換狀態(tài)為0,如果鍵正被釋放,則轉(zhuǎn)換狀態(tài)為1,對(duì)WM_KEYDOWN或者WM_SYSKEYDOWN消息,此字段為0,對(duì)WM_KEYUP或者WM_SYSKEYUP消息,此字段為1。
3、 實(shí)例代碼
接下來將開始制作一個(gè)ASCII碼查詢器。首先利用VC++6.0向?qū)Ы⒁粋€(gè)基于MFC的對(duì)話框工程QueryASCII,在界面上放置兩個(gè)只讀的EDIT,其中一個(gè)用于接收字符的值,另外一個(gè)顯示相應(yīng)的ASCII碼值,界面布局如下圖所示(關(guān)于如何建立MFC工程,可參考隨筆——在VC6下建立MFC工程圖解)

①通過class winzard(View菜單下)添加對(duì)話框的WM_KEYDOWN消息響應(yīng)函數(shù)OnKeyDown,如下圖所示:
②通過class winzard(View菜單下)添加對(duì)話框QueryASCII的虛繼承函數(shù)PreTranslateMessage,如下圖所示:

添加好后,分別在相應(yīng)的函數(shù)里面添加相應(yīng)的代碼,因?yàn)槲乙膊皇呛芏?,就寫了很多注釋,代碼如下:

/**//* 接受KEYDOWN消息 */
// 關(guān)于OnKeyDown函數(shù)請(qǐng)看注釋一
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); //關(guān)于Format的說明請(qǐng)看注釋二
SetDlgItemText(IDC_CHAR_ED,strTemp); //當(dāng)程序運(yùn)行時(shí)出現(xiàn),IDC_CHAR_ED未定義的說明時(shí),請(qǐng)看注釋三
strTemp.Format("%0x",nChar);
SetDlgItemText(IDC_ASCII_ED,strTemp);
// then do not forget to call baseclass, 下面的是回調(diào)函數(shù)
CDialog::OnKeyDown(nChar, nRepCnt, nFlags); //想獲得更多知識(shí)可看注釋四
}

/**//* 翻譯信息 */
// 關(guān)于PreTranslateMessage函數(shù)的解釋可看注釋五
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個(gè)字節(jié),即為無符號(hào)整型
/* nChar: Specifies the virtual key code of the given key. For a list
/* of of standard virtual key codes, see Winuser.h
/* 指定給定鍵的虛擬鍵碼。有關(guān)標(biāo)準(zhǔn)的虛擬鍵碼清單,可查看Winuser.h中
/* nRepCnt: Repeat count(the number of times the keystroke is repeated
/* as a result of the user holding down the key).
/* 重復(fù)計(jì)數(shù)(當(dāng)用戶按住了鍵,才會(huì)造成按鍵的數(shù)目重復(fù))。
/* nFlags: Specifies the scan code, key-transition code, previous key
/* state, and context code
/* 指定掃描碼,關(guān)鍵過渡代碼,上一個(gè)鍵的狀態(tài)以及代碼的上下文
/*******************************************************************/

/**//* 注釋二 */

/**//********************************************************************************/

/**//* Format(String, Object)
/* 將指定的 String 中的格式項(xiàng)替換為指定的 Object 實(shí)例的值的文本等效項(xiàng)。
/*
/* Format(String, array<Object>[]()[])
/* 將指定 String 中的格式項(xiàng)替換為指定數(shù)組中相應(yīng) Object 實(shí)例的值的文本等效項(xiàng)。
/*
/* Format(IFormatProvider, String, array<Object>[]()[])
/* 將指定 String 中的格式項(xiàng)替換為指定數(shù)組中相應(yīng) Object 實(shí)例的值的文本等效項(xiàng)。指定
/* 的參數(shù)提供區(qū)域性特定的格式設(shè)置信息。
/*
/* Format(String, Object, Object)
/* 將指定的 String 中的格式項(xiàng)替換為兩個(gè)指定的 Object 實(shí)例的值的文本等效項(xiàng)。
/*
/* Format(String, Object, Object, Object)
/* 將指定的 String 中的格式項(xiàng)替換為三個(gè)指定的 Object 實(shí)例的值的文本等效項(xiàng)
/********************************************************************************/

/**//* 注釋三 */

/**//********************************************************************************/

/**//* IDC_CHAR_ED和IDC_ASCII_ED指的是Edit Box的ID,只要右鍵單擊字符對(duì)應(yīng)的Edit Box選中
/* Properties(屬性),General里的ID選項(xiàng)改為IDC_CHAR_ED即可。ASCII對(duì)應(yīng)的Edit Box的
/* ID改為IDC_ASCII_ED即可。
/* SetDlgItemText是一個(gè)函數(shù),設(shè)置對(duì)話框中控件的文本和標(biāo)題。
/* 函數(shù)原型:
/* BOOL SetDlgltemText(HWND hDlg,int nlDDlgltem,LPCTSTR IpString);
/* 參數(shù):
/* hDlg:指定含有控件的對(duì)話框。
/* nlDDlgltem:標(biāo)識(shí)帶有將被設(shè)置的標(biāo)題和文本的控制。
/* IpString:指向一個(gè)以NULL結(jié)尾的字符串指針,該字符串指針包含了將被復(fù)制到控制的文本。
/* 返回值:如果函數(shù)調(diào)用成功,則返回值為非零值。如果函數(shù)調(diào)用失敗,則返回值為零。
/* 若想獲得更多的錯(cuò)誤信息,請(qǐng)調(diào)用GetLastError函數(shù)。
/*******************************************************************************/

/**//* 注釋四 */

/**//*******************************************************************************/

/**//* 要用“回車鍵”,“左移光標(biāo)健”,“右移光標(biāo)健”,“上移光標(biāo)健”,“下移光標(biāo)健”
/* 這幾個(gè)鍵,你可以在這樣:
/* switch(nChar)
/* {
/* case vk_return:
/* //your code
/* case vk_right:
/* //your code
/* case vk_left:
/* case vk_up:
/* case vk_down:
/* }
/*******************************************************************************/

/**//* 注釋五 */

/**//****************************************************************/

/**//* 函數(shù)原型:virtual BOOL PreTranslateMessage(MSG* pMsg);
/* 功能:重載該函數(shù)可以實(shí)現(xiàn)窗口消息派發(fā)給窗口函數(shù)TranslateMessage()
/* 和DispatchMessage()之前的過濾。默認(rèn)的實(shí)現(xiàn)是完成加速鍵的翻譯。
/* 因?yàn)槟仨氃谀愕闹剌d版本中調(diào)用CWinApp:PreTranslateMessage()函數(shù).
/* 在MFC中,PreTranslateMessage()是虛函數(shù),我們可以重載它來處理鍵盤和鼠標(biāo)消息。
/* 在SDK中,這又有所不同,我們必須在回調(diào)函數(shù)中
/* LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
/* 處理消息,它和PreTranslateMessage起的作用是類似的。只是MFC封裝的更好而已
/* 更多相關(guān)信息可查看隨筆:http://www.shnenglu.com/kangnixi/archive/2010/02/17/107989.html
/****************************************************************/
我對(duì)代碼進(jìn)行了修改和優(yōu)化,如下所示
(未完,稍等)
想要獲得更多內(nèi)容,可點(diǎn)擊:《Visual C++代碼參考與技巧大全》學(xué)習(xí)筆記——索引隨筆