要理解二者的區別,首先需要清楚: 我們通過鍵盤所打的字,並不都是全部通過IME後,轉交給程式的。
也就是說: 我們用鍵盤打的字有些是不經過IME直接傳送到程式中,如1、2、3這樣的數字 還有ABC英文字母,斷行符號 空格等
有些是經過IME轉交給程式的,如中文
明白這點後,WM_IME_CHAR與WM_CHAR的區別就容易理解了。
需要說明的是: 數字和英文字母 你可以不通過IME直接輸入,也可以通過IME進行輸入
WM_IME_CHAR: 所有經由IME產生的字元都會產生WM_IME_CHAR訊息。 DefWindowProc會將WM_IME_CHAR轉換為WM_CHAR訊息 WM_CHAR: 未經IME而直接送人程式中的字元會響應WM_CHAR訊息。 |
說明:
對於 Unicode 視窗,WM_IME_CHAR 和 WM_CHAR 沒有區別,wParam 都是一個 WCHAR,即輸入的字元。
對於非 Unicode (DBCS) 視窗,WM_IME_CHAR 的 wParam 即由IME產生的一個字元。這個字元既有可能是單位元組字元也有可能是雙位元組字元。如果是單位元組字元,那麼和 WM_CHAR 沒什麼區別;如果是一個雙位元組字元,那麼
wParam 高 8 位為 Leading byte,低 8 位為 Continuation Byte。
所有經由IME產生的字元都會產生 WM_IME_CHAR 訊息而不是 WM_CHAR,但 DefWindowProc 會把 WM_IME_CHAR 轉換為相應的一個或兩個 WM_CHAR 訊息。
例如:
- 不開IME輸入「9」 → 收到 WM_CHAR (0×0039)
- 開啟IME輸入「9」 → 收到 WM_IME_CHAR (0×0039) → 收到 WM_CHAR (0×0039)
- 開啟IME輸入「笨」 → 收到 WM_IME_CHAR (0xB1BF) → 收到 WM_CHAR (0x00B1) → 收到 WM_CHAR (0x00BF)
編程實現:
對於WM_CHAR MFC提供了其響應函數OnChar(), 但對於WM_IME_CHAR 並沒有提供其響應函數,需要我們自己去寫
下常式序顯示了如何處理鍵盤字元訊息的過程:
1)定義全域數組 ,存放鍵盤輸入的字元
wchar_t m_ImeChar[2];
常用字的unicode佔用2個位元組
而冷僻字的unicode需要佔用4個位元組
因此, 定義4自己的數組 存放字
2)重載視窗處理過程
virtual LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam);
WindowProc(UINT message, WPARAM wParam, LPARAM lParam){switch(message){case WM_IME_CHAR://對特殊字元 進行預先處理if(PreTreat(WM_IME_CHAR,wParam,lParam)) break;//其他字元 放入數組中if(m_ImeChar[0]>=0xD800 && m_ImeChar[0]<=0xDBFF){m_ImeChar[1]=(wchar_t)wParam;}else{m_ImeChar[0]=(wchar_t)wParam;if(m_ImeChar[0]>=0xD800 && m_ImeChar[0]<=0xDBFF)break;}OnImeChar(m_ImeChar);break;default: break;}return CScrollView::WindowProc(message, wParam, lParam);}
說明:
unicode編碼 U+0000 ~ U+FFFF 為基本多語言平面(Basic MultilingualPlane,簡記為BMP)
U+10000 ~ U+10FFFF 為16個輔助平面
常用字都處在BMP內,佔2個位元組;而冷僻字則在BMP之外,佔四個位元組。
常用字、冷僻字區別方法:
BMP內,從U+D800到U+DFFF之間的Code Point區段是持續保留不映射到字元
BMP之外佔用四個位元組 前兩個位元組為高位位元組,後兩個位元組為低位位元組
前兩個位元組的範圍為:0xD800..0xDBFF
後兩個位元組的範圍為:0xDC00..0xDFFF
由此可見,BMP之內的字元 和BMP之外的字元沒有交集。
因此,
if(m_ImeChar[0]>=0xD800 && m_ImeChar[0]<=0xDBFF)
如果滿足這個條件,則表示的是佔用4個位元組的冷僻字
對於冷僻字,系統會發送兩次WM_IME_CHAR訊息,第一次傳送其高位位元組,第二次傳送其低位位元組
當運行到OnImeChar(m_ImeChar);時, m_ImeChar存放的為真實的字(不管是常用字還是冷僻字,此時都正確儲存在了此數組中)
3)對數組中接受的字 進行處理
void OnImeChar(wchar_t* Str);
OnImeChar(wchar_t* Str){。。。}
4)使用OnChar響應未經IME輸入的字元
OnChar(UINT nChar, UINT nRepCnt, UINT nFlags){switch(nChar){case 0x0D://斷行符號鍵if(PreTreat(0x0D,NULL,NULL)) break;OnEnterKeyDown();Update();break;case 0x08://退格鍵if(PreTreat(0x08,NULL,NULL)) break;if(m_paSelElem.GetSize()>0) OnDelElemsel();else OnBackSpaceKeyDown();Update();break;case 0x20://空格鍵if(PreTreat(0x20,NULL,NULL)) break;OnSpaceKeyDown();Update();break;case 0x09://TAB鍵if(PreTreat(0x09,NULL,NULL)) break;OnTabKeyDown();Update();break;default:break;}if(m_paSelElem.GetSize()>0){CleanSelElem();}CScrollView::OnChar(nChar, nRepCnt, nFlags);}
處理原則:
對於WM_IME_CHAR訊息,則交由WM_IME_CHAR響應函數處理
對於不經IME的字元訊息,則交由WM_CHAR進行處理
參考資料:
http://timothyqiu.com/2012/wm_ime_char/