(学习记录)Win32开发之键盘

相关函数预览:

SHORT WINAPI GetKeyState(_In_intnVirtKey);

SHORT WINAPI GetAsyncKeyState( _In_int vKey);

LRESULT WINAPI SendMessageW( _In_ HWND hWnd, _In_ UINT Msg, _Pre_maybenull_ _Post_valid_ WPARAM wParam,  _Pre_maybenull_ _Post_valid_LPARAM lParam);

#define SendMessage  SendMessageW

BOOL WINAPI CreateCaret(_In_HWND hWnd,_In_opt_HBITMAP hBitmap, _In_ int nWidth,_In_int nHeight);

BOOL WINAPI SetCaretPos(_In_int X,  _In_int Y);

BOOL WINAPI ShowCaret( _In_opt_HWND hWnd);

BOOL WINAPI HideCaret(_In_opt_HWND hWnd);

BOOL WINAPI DestroyCaret( VOID);

Windows提供了八种消息来表示键盘事件,其中击键消息有4种,字符消息有4种。窗口过程通过捕获WM_SETFOCUS和WM_KILLFOCUS消息来确定自己的窗口是否具有输入点。

Windows击键消息:

 键按下键释放
非系统击键WM_KEYDOWNWM_KEYUP
系统击键WM_SYSKEYDOWNWM_SYSKEYUP

对于这四类击键消息,wParam是虚拟键代码,lParam则被分为六个字段。虚拟键代码再WINUSER.H头文件中,第一个为VK_LBUTTON,0x41~0x5A为A~Z, 0x30~0x39为主键盘上的0~9.

32位的lParam的15~00位为16位的重复计数,即当程序无法及时处理某一个键被持续按下的情况时,会将重复次数保存在这16位。

23~16位为8位的OEM扫描码

24位为扩展键标记,当击键来自于IBM加强型键盘的附加键时,该位为1。

29位为内容代码,如果击键的同时按下了alt键,则该位为1,收到WM_SYSKEYUP和WM_SYSKEYDOWN消息时,此位始终为1,收到WM_KEYUP和WM_KEYDOWN消息时,此位始终为0。有特殊情况,即当窗口最小化时,所有的击键都将产生WM_SYSKEYUP和WM_SYSKEYDOWN消息。

30位为键的先前状态,如果键先前处于释放状态,则该位为0,否则为1.WM_KEYUP和WM_SYSKEYUP的消息此字段总为1,而WM_SYSKEYDOWN和WM_KEYDOWN消息可能为0或1。

31位为转换状态,如果键正在按下,则此位为0,否则为1。

31302928~252423~1615~00

查询键的状态:

这里有两个函数

iState = GetKeyState(VK_SHIFT);//查询Shift键的状态

iState = GetAsyncKeyState(VK_SHIFT);

两个函数的区别在于前者是重消息队列中得到键盘消息,后者是直接侦测键盘的硬件中断(此处参考点击打开链接)。

如果我们用键盘的上下左右方向键来控制滚动条,可以在WM_KEYDOWN消息中使用SendMessage函数向窗口发送WM_VSCROLL消息或者WM_HSCROLL消息,然后交给滚动条的相关处理方法处理。通常情况下我们只需要处理WM_KEYDOWN消息,因为系统击键往往会由 DefWindowProc(hWnd, message, wParam, lParam);来处理。

Windows还有四种字符消息:

 字符死字符
非系统字符WM_CHARWM_DEADCHAR
系统字符WM_SYSCHARWM_SYSDEADCHAR

字符消息是由TranslateMessage函数捕获到WM_KEYDOW消息后,发送WM_CHAR到消息队列后,由窗口过程捕获并处理。当我们按下A键然后释放时,窗口过程会依次收到WM_KEYDOWN, WM_CHAR, WM_KEYUP三条消息。如果我们一直按下A键然后再释放时,窗口过程会依次收到WM_KEYDOWN,WM_CHAR, WM_KEYDOWN,WM_CHAR, WM_KEYDOWN,WM_CHAR, WM_KEYDOWN,WM_CHAR,WM_KEYUP消息。

许多击键消息我们可以当作字符消息处理,特别是在文本编辑器中,这种方式可以让程序更清晰,这些击键可以由ctrl组合键的方式输入。

击键字符码产生方法ANSIC转义码
空格0x08ctrl+H\b
Tab0x09ctrl+I\t
crtl+回车0x0actrl+J\n
回车0x0dctrl+M\r
ESC0x1bctrl+[ 

插入符号:

插入符号就是我们在输入文本时闪烁的 | 。由以下五个函数来管理插入符号

CreateCatet(hWnd, nullptr, x, y);//创建插入符

SetCaretPos(x, y);//设置插入符的位置

ShowCaret(hWnd);//显示插入符

HideCaret(hWnd);//隐藏插入符

DestroyCaret();//销毁插入符

当键盘布局发生改变时,窗口过程将收到WM_INPUTLANGCHANGE消息,其wParam为字符集代码,即CreateFont函数的第9个参数,所以在该消息中应当及时改变字体的设置,详见参考程序。

注:参考程序是在滚动条基础上做了一些改动,添加了WM_INPUTLANGCHANGE, WM_SETFOCUS, WM_KILLFOCUS, 重写了WM_KEYDOWN, WM_CHAR消息,改动了WM_CREATE, WM_SIZE, WM_PAINT消息的代码。

参考程序:

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	static int cxChar, cyChar, cxCaps, cxClient, cyClient, iMaxWidth, iMaxLine;
	//以下内容为在滚动条基础上增添的内容
	static int cxBuffer, cyBuffer, xCaret, yCaret;
	static TCHAR* pBuffer = nullptr;
	static DWORD dwCharSet = DEFAULT_CHARSET;
	//END
	HDC hdc;
	int i, x, y, iVertpos, iPaintBeg, iPaintEnd;
	SCROLLINFO si;
	TCHAR szBuffer[10];
	TEXTMETRIC tm;
    switch (message)
    {
	case WM_INPUTLANGCHANGE:
		dwCharSet = wParam;
		break;
	case WM_CREATE://较滚动条有所变化
		{
			hdc = GetDC(hWnd);
			SelectObject(hdc, CreateFont(0, 0, 0, 0, 0, 0, 0, 0, dwCharSet, 0, 0, 0, FIXED_PITCH, nullptr));
			
			GetTextMetrics(hdc, &tm);
			cxChar = tm.tmAveCharWidth;
			cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2;//如果为等宽字体,则低位为0,如果为变宽字体,地位为1,大写字母的宽度为小写字母宽度的3/2
			cyChar = tm.tmHeight + tm.tmExternalLeading;

			DeleteObject(SelectObject(hdc, GetStockObject(SYSTEM_FONT)));
			ReleaseDC(hWnd, hdc);
		}
		break;
	case WM_SIZE:
		{
			cxClient = LOWORD(lParam);//获取客户区尺寸
			cyClient = HIWORD(lParam);
			iMaxLine = cyClient / cyChar;
			
			si.cbSize = sizeof(si);
			si.fMask = SIF_RANGE | SIF_PAGE;
			si.nMin = 0;
			si.nMax = iMaxLine;
			si.nPage = iMaxLine;
			SetScrollInfo(hWnd, SB_VERT, &si, true);
			iMaxWidth = cxClient / cxCaps;//每行输出的字数

			//以下内容为在滚动条基础上新增内容
			cxBuffer = max(1, iMaxWidth);
			cyBuffer = max(1, iMaxLine);

			if (pBuffer)
				free(pBuffer);
			pBuffer = (TCHAR*)malloc(cxBuffer * cyBuffer * sizeof(TCHAR));

			for(y = 0 ; y < cyBuffer ; ++y)
				for (x = 0; x < cxBuffer; ++x)
				{
					pBuffer[x + y * cxBuffer] = ' ';
				}
			xCaret = yCaret = 0;

			if (hWnd == GetFocus())
			{
				SetCaretPos(xCaret * cxChar, yCaret * cyChar);
			}
			InvalidateRect(hWnd, nullptr, true);
			//END
		}
		break;
	case WM_SETFOCUS:
		CreateCaret(hWnd, nullptr, 1, cyChar);
		SetCaretPos(xCaret * cxChar, yCaret * cyChar);
		ShowCaret(hWnd);
		break;
	case WM_KILLFOCUS:
		HideCaret(hWnd);
		DestroyCaret();
		break;
	case WM_VSCROLL:
		{
			si.cbSize = sizeof(si);
			si.fMask = SIF_ALL;
			GetScrollInfo(hWnd, SB_VERT, &si);

			iVertpos = si.nPos;

			switch (wParam & 0xffff)
			{
			case SB_TOP:
				si.nPos = si.nMin;
				break;
			case SB_BOTTOM:
				si.nPos = si.nMax;
				break;
			case SB_LINEUP:
				si.nPos -= 1;
				break;
			case SB_LINEDOWN:
				si.nPos += 1;
				break;
			case SB_PAGEUP:
				si.nPos -= si.nPage;
				si.nPos = (si.nPos < 0 ? 0 : si.nPos);
				break;
			case SB_PAGEDOWN:
				si.nPos += si.nPage;
				si.nPos = (si.nPos > si.nMax ? si.nMax : si.nPos);
				break;
			case SB_THUMBTRACK:
				si.nPos = si.nTrackPos;
				break;
			default:
				break;
			}

			si.fMask = SIF_POS;

			SetScrollInfo(hWnd, SB_VERT, &si, true);
			GetScrollInfo(hWnd, SB_VERT, &si);

			if (si.nPos != iVertpos)
			{
				ScrollWindow(hWnd, 0, cyChar * (iVertpos - si.nPos), nullptr, nullptr);
				UpdateWindow(hWnd);
			}
		}
		break;
	case WM_CHAR:
		{
			for (i = 0; i < (int)LOWORD(lParam); ++i)
			{
				switch (wParam)
				{
				case '\b':
					if (xCaret > 0)
					{
						xCaret--;
						SendMessage(hWnd, WM_KEYDOWN, VK_DELETE, 1);
					}
					break;
				case '\t':
					do
					{
						SendMessage(hWnd, WM_CHAR, ' ', 1);
					} while (xCaret % 8 != 0);
					break;
				case '\n':
					if (++yCaret == cyBuffer)
						yCaret = 0;
					break;
				case '\r':
					xCaret = 0;
					if (++yCaret == cyBuffer)
						yCaret = 0;
					break;
				default:
					pBuffer[xCaret + yCaret * cxBuffer] = (TCHAR)wParam;
					HideCaret(hWnd);
					hdc = GetDC(hWnd);
					SelectObject(hdc, CreateFont(0, 0, 0, 0, 0, 0, 0, 0, dwCharSet, 0, 0, 0, FIXED_PITCH, nullptr));
					TextOut(hdc, xCaret * cxChar, yCaret * cyChar, &pBuffer[xCaret + yCaret * cxBuffer], 1);
					DeleteObject(SelectObject(hdc, GetStockObject(SYSTEM_FONT)));
					ReleaseDC(hWnd, hdc);
					ShowCaret(hWnd);

					if (++xCaret == cxBuffer)
					{
						xCaret = 0;
						if (++yCaret == cyBuffer)
							yCaret = 0;
					}
					break;
				}
			}
		}
		SetCaretPos(xCaret * cxChar, yCaret * cyChar);
		break;
    case WM_COMMAND:
        {
            int wmId = LOWORD(wParam);
            // 分析菜单选择: 
            switch (wmId)
            {
            case IDM_ABOUT:
                DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
                break;
            case IDM_EXIT:
                DestroyWindow(hWnd);
                break;
            default:
                return DefWindowProc(hWnd, message, wParam, lParam);
            }
        }
        break;
	case WM_KEYDOWN:
		{
			switch (wParam)
			{
			case VK_LEFT:
				xCaret = max(xCaret - 1, 0);
				break;
			case VK_RIGHT:
				xCaret = min(xCaret + 1, cxBuffer - 1);
				break;
			case VK_UP:
				yCaret = max(yCaret - 1, 0);
				break;
			case VK_DOWN:
				yCaret = min(yCaret + 1, cyBuffer - 1);
				break;
			case VK_DELETE:
				for (x = xCaret; x < cxBuffer - 1; ++x)
					pBuffer[x + yCaret * cxBuffer] = pBuffer[x + yCaret * cxBuffer + 1];
				pBuffer[(yCaret + 1) * cxBuffer - 1] = ' ';
				HideCaret(hWnd);
				hdc = GetDC(hWnd);
				SelectObject(hdc, CreateFont(0, 0, 0, 0, 0, 0, 0, 0, dwCharSet, 0, 0, 0, FIXED_PITCH, nullptr));
				TextOut(hdc, xCaret * cxChar, yCaret * cyChar, &pBuffer[xCaret + yCaret * cxBuffer], cxBuffer - xCaret);
				DeleteObject(SelectObject(hdc, GetStockObject(SYSTEM_FONT)));
				ReleaseDC(hWnd, hdc);
				ShowCaret(hWnd);
				break;
			}
			SetCaretPos(xCaret * cxChar, yCaret * cyChar);
			break;
		}
    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            hdc = BeginPaint(hWnd, &ps);
            // TODO: 在此处添加使用 hdc 的任何绘图代码...
			si.cbSize = sizeof(si);
			si.fMask = SIF_POS;
			GetScrollInfo(hWnd, SB_VERT, &si);
			iVertpos = si.nPos;

			SelectObject(hdc, CreateFont(0, 0, 0, 0, 0, 0, 0, 0, dwCharSet, 0, 0, 0, FIXED_PITCH, nullptr));
			for(y = 0 ; y < cyBuffer ; ++y)
				TextOut(hdc, 0, y * cyChar, &pBuffer[y * cxBuffer], cxBuffer);
			DeleteObject(SelectObject(hdc, GetStockObject(SYSTEM_FONT)));

            EndPaint(hWnd, &ps);
        }
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}
参考文献:《Windows程序设计(第5版 珍藏版)》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值