(学习记录)Win32开发之鼠标

相关函数预览:

int WINAPI GetSystemMetrics( _In_ int nIndex);

BOOL WINAPI ScreenToClient(  _In_ HWND hWnd,   _Inout_ LPPOINT lpPoint);

BOOL WINAPI ClientToScreen(  _In_ HWND hWnd,   _Inout_ LPPOINT lpPoint);

BOOL WINAPI GetCursorPos(  _Out_ LPPOINT lpPoint);

BOOL WINAPI SetCursorPos(  _In_ int X,  _In_ int Y);

LONG WINAPI SetWindowLongW(  _In_ HWND hWnd,  _In_ int nIndex,  _In_ LONG dwNewLong);

LON WINAPI GetWindowLongW(  _In_ HWND hWnd,  _In_ int nIndex);

HWND WINAPI SetFocus(  _In_opt_ HWND hWnd);//返回值为当前具有键盘焦点的窗口句柄

HWND WINAPI GetFocus( VOID);

HWND WINAPI GetParent( _In_ HWND hWnd);

HWND WINAPI GetDlgItem(  _In_opt_ HWND hDlg,  _In_ int nIDDlgItem);

HWND WINAPI SetCapture( _In_ HWND hWnd);//捕获鼠标

BOOL WINAPI ReleaseCapture( VOID);//释放鼠标

GetWindowLong与SetWindowLong函数中Index在参考程序中的值:

GWL_ID(-12)获得窗口标识
GWL_USERDATA(-21)获得或修改和窗口相关联的32位的值(每一个窗口都有一个有意留给创建窗口的应用程序是用的32位
               的值)

更多取值可参考MSDN


当计算机上安装了鼠标时,fMouse = GetSystemMetrics(SM_MOUSEPRESENT);的返回值为true,否则为0。

鼠标消息:

WIndows定义了22种鼠标消息,其中和客户区相关的有10种,11种非客户区鼠标消息,1个鼠标滚轮消息。

客户区鼠标消息:

WM_MOUSEMOVE:鼠标移动消息。在消息队列种只会存在最多一个鼠标移动消息,即操作系统不会为鼠标经过的每一个像素产生一个WM_MOUSEMOVE消息,而是在当前消息处理结束后才会产生下一个消息。

左键消息:

WM_LUBTTONDOWN, WM_LBUTTONUP, WM_LBUTTONDBLCLK三种消息,需要注意的是,在实际测试中,若窗口过程处理了LUBTTONDOWM消息后,鼠标没有移动,则不会收到LBUTTONUP消息。

中键与右键消息仅需要把LBUTTON中的L对应替换为M和R即可。

需要注意的是,如果窗口过程需要处理鼠标双击的消息,必须在窗口类的style字段包含标识符CS_DBLCLKS。

WNDCLASSEXW wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style          = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
    wcex.lpfnWndProc    = WndProc;
    wcex.cbClsExtra     = 0;
    wcex.cbWndExtra     = 0;
    wcex.hInstance      = hInstance;
    wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MOUSE1));
    wcex.hCursor        = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName   = MAKEINTRESOURCEW(IDC_MOUSE1);
    wcex.lpszClassName  = szWindowClass;
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

当我们的窗口可以接收双击消息时,窗口过程收到的消息顺序依次为:WM_LBUTTONDOWN, WM_LBUTTONUP, WM_LBUTTONDBLCLK, WM_LBUTTONUP。对于客户区鼠标消息,参数lParam包含了鼠标的位置消息,其中

x = LOWORD(lParam); y = HIWORD(lParam);

参数wParam表示鼠标按钮,shift键与ctrl键的状态可与下列flag进行与运算来判断相关键的状态。

MK_LBUTTON按下左键
MK_MBUTTON按下中键
MK_RBUTTON按下右键
MK_SHIFT按下shift键
MK_CONTROL按下ctrl键

例如,在WM_LBUTTONDOWN消息中,wParam & MK_CONTROL == true,则说明按下左键的同时也按下了ctrl键。

非客户区鼠标消息:

非客户区鼠标消息中有10个与客户区鼠标消息一一对应,在消息标识符中包含了字母NC(nonclient)如:WM_NCMOUSEMOVE, WM_NCMBUTTONDOWN等。这些消息的lParam参数为鼠标的屏幕坐标,通常情况下我们不需要处理这些消息。

另一个非客户区消息为鼠标击中测试消息WM_NCHITTEST。该消息的lParam参数包含了鼠标的屏幕坐标,wParam参数没有用到。Windows通常把这个消息交给DefWindowProc来处理,以产生前面所述的20种鼠标消息。

鼠标滚轮消息 WM_MOUSEWHEEL:lParam参数包含了鼠标的屏幕坐标信息,wParam参数的低位字包含了鼠标按钮,shift和ctrl键的状态,高位字为增量(delta),取值通常为120或-120。可以使用ScreenToClient函数与ClientToScreen函数进行屏幕坐标与客户区坐标的转换。

参考程序是对Windows程序设计种的击中测试做了少量修改。其功能为:窗口产生25个子窗口,当键盘输入焦点在某一个子窗口上时,该窗口显示一个虚线画成的矩形,当某窗口被鼠标击中时,该窗口画一个X,第二次被击中时,X消失。参考程序分为三部分,第一部分为注册窗口类,主函数等,第二部分为主窗口的窗口过程,第三部分为子窗口的窗口过程。

第一部分:

//start
#define DIVISIONS 5
LRESULT CALLBACK ChildWndProc(HWND, UINT, WPARAM, LPARAM);
TCHAR szChildClass[] = TEXT("Checker3_Child");

int idFocus = 0;
//End
// 全局变量: 
HINSTANCE hInst;                                // 当前实例
WCHAR szTitle[MAX_LOADSTRING];                  // 标题栏文本
WCHAR szWindowClass[MAX_LOADSTRING];            // 主窗口类名

// 此代码模块中包含的函数的前向声明: 
ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    // TODO: 在此放置代码。

    // 初始化全局字符串
    LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadStringW(hInstance, IDC_MOUSE1, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);

	///
	


	/

    // 执行应用程序初始化: 
    if (!InitInstance (hInstance, nCmdShow))
    {
        return FALSE;
    }

    HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_MOUSE1));

    MSG msg;

    // 主消息循环: 
    while (GetMessage(&msg, nullptr, 0, 0))
    {
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return (int) msg.wParam;
}



//
//  函数: MyRegisterClass()
//
//  目的: 注册窗口类。
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEXW wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style          = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
    wcex.lpfnWndProc    = WndProc;
    wcex.cbClsExtra     = 0;
    wcex.cbWndExtra     = 0;
    wcex.hInstance      = hInstance;
    wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MOUSE1));
    wcex.hCursor        = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName   = MAKEINTRESOURCEW(IDC_MOUSE1);
    wcex.lpszClassName  = szWindowClass;
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
	//
	if (!RegisterClassExW(&wcex))return 0;
	wcex.lpfnWndProc = ChildWndProc;
	wcex.cbWndExtra = sizeof(long);
	wcex.hIcon = nullptr;
	wcex.lpszClassName = szChildClass;
	/
    return RegisterClassExW(&wcex);
}

//
//   函数: InitInstance(HINSTANCE, int)
//
//   目的: 保存实例句柄并创建主窗口
//
//   注释: 
//
//        在此函数中,我们在全局变量中保存实例句柄并
//        创建和显示主程序窗口。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   hInst = hInstance; // 将实例句柄存储在全局变量中

   HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);

   if (!hWnd)
   {
      return FALSE;
   }

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}

第二部分:

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	static HWND hwndChild[DIVISIONS][DIVISIONS];
	int cxBlock, cyBlock, x, y;
    switch (message)
    {
	case WM_CREATE:
		for (x = 0; x < DIVISIONS; ++x)
			for (y = 0; y < DIVISIONS; ++y)
				hwndChild[x][y] = CreateWindowW(szChildClass, nullptr, WS_CHILDWINDOW | WS_VISIBLE, 0, 0, 0, 0, hWnd, (HMENU)(y << 8 | x), (HINSTANCE)GetWindowLong(hWnd, -6), nullptr);
		return 0;
	case WM_SIZE:
		cxBlock = LOWORD(lParam) / DIVISIONS;
		cyBlock = HIWORD(lParam) / DIVISIONS;

		for (x = 0; x < DIVISIONS; ++x)
			for (y = 0; y < DIVISIONS; ++y)
				MoveWindow(hwndChild[x][y], x * cxBlock, y * cyBlock, cxBlock, cyBlock, true);
		return 0;
	case WM_LBUTTONDOWN:
		MessageBeep(0);
		return 0;
	case WM_SETFOCUS:
		SetFocus(GetDlgItem(hWnd, idFocus));
		return 0;
	case WM_KEYDOWN:
		x = idFocus & 0xff;
		y = idFocus >> 8;
		switch (wParam)
		{
		case VK_UP: --y; break;
		case VK_DOWN: ++y; break;
		case VK_LEFT: --x; break;
		case VK_RIGHT:++x; break;
		case VK_HOME:x = y = 0; break;
		case VK_END: x = y = DIVISIONS - 1; break;
		default:return 0;
		}
		x = (x + DIVISIONS) % DIVISIONS;
		y = (y + DIVISIONS) % DIVISIONS;

		idFocus = y << 8 | x;
		SetFocus(GetDlgItem(hWnd, idFocus));
		return 0;
    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_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
            // TODO: 在此处添加使用 hdc 的任何绘图代码...
            EndPaint(hWnd, &ps);
        }
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}


第三部分:

LRESULT CALLBACK ChildWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	HDC hdc;
	PAINTSTRUCT ps;
	RECT rect;

	switch (message)
	{
	case WM_CREATE:
		SetWindowLong(hWnd, -21, 0);
		return 0;
	case WM_LBUTTONDOWN:
		SetWindowLong(hWnd, -21, 1 ^ GetWindowLong(hWnd, -21));
		SetFocus(hWnd);
	//	MessageBeep(MB_ICONEXCLAMATION);
		InvalidateRect(hWnd, nullptr, false);
		return 0;
	case WM_SETFOCUS:
		idFocus = GetWindowLong(hWnd, GWL_ID);
	//	return 0;
	case WM_KILLFOCUS:
		
		InvalidateRect(hWnd, nullptr, true);
		return 0;
	case WM_KEYDOWN:
		if (wParam != VK_RETURN && wParam != VK_SPACE)
		{
			SendMessage(GetParent(hWnd), message, wParam, lParam);
			return 0;
		}
		else
		{
			SendMessage(hWnd, WM_LBUTTONDOWN, 0, 0);
			return 0;
		}
	case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps);

		GetClientRect(hWnd, &rect);
		Rectangle(hdc, 0, 0, rect.right, rect.bottom);

		if (GetWindowLong(hWnd, -21))
		{
			MoveToEx(hdc, 0, 0, nullptr);
			LineTo(hdc, rect.right, rect.bottom);
			MoveToEx(hdc, 0, rect.bottom, nullptr);
			LineTo(hdc, rect.right, 0);
		}

		if (hWnd == GetFocus())
		{
			rect.left += rect.right / 10;
			rect.right -= rect.left;
			rect.top += rect.bottom / 10;
			rect.bottom -= rect.top;

			SelectObject(hdc, GetStockObject(NULL_BRUSH));
			SelectObject(hdc, CreatePen(PS_DASH, 0, 0));
			Rectangle(hdc, rect.left, rect.top, rect.right, rect.bottom);
			DeleteObject(SelectObject(hdc, GetStockObject(BLACK_PEN)));
		}
		
		EndPaint(hWnd, &ps);
		return 0;
	}
	return DefWindowProc(hWnd, message, wParam, lParam);

参考文献:《Windows程序设计 第5版 珍藏版》





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值