C++ 如何判断鼠标有没有在窗口上? 纯API 不是MFC

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++ 如何判断鼠标有没有在窗口上? 纯API 不是MFC相关的知识,希望对你有一定的参考价值。

找了不少资料 好像WM_MOUSELEAVE可以实现.. 不过我没有MSDN.. 我需要个WM_MOUSELEAVE实例 比如 MOUSE 离开 window ,window的title 改变成 "鼠标在窗口外" MOUSE回来title 改变成 "鼠标在窗口内" API 不是MFC

示例一:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

TRACKMOUSEEVENT tme; //定义触发鼠标事件结构
tme.cbSize = sizeof (TRACKMOUSEEVENT); //结构大小
tme.dwFlags = TME_HOVER | TME_LEAVE ; //鼠标停留/鼠标离开
tme.dwHoverTime = 300; //延时300毫秒
tme.hwndTrack = hWnd ; //窗体句柄

switch (message)
case WM_CREATE: //创建窗口消息
edt = CreateWindow(L"edit", NULL, WS_CHILD | WS_VISIBLE | WS_BORDER,115, 23, 120, 24, hWnd, (HMENU)IDB_EDIT, hInst, NULL); //edit编辑文本框
break;
case WM_MOUSEMOVE :
//鼠标移动消息
TrackMouseEvent(&tme); //调用TrackMouseEvent函数,系统根据tme触发WM_MOUSEHOVER或WM_MOUSELEAVE消息
break ;
case WM_MOUSEHOVER: //鼠标停留消息(这不会自动触发,需要TrackMouseEvent函数来触发)
SetWindowText (edt, _T("鼠标在窗体上面"));
break ;
case WM_MOUSELEAVE : //鼠标离开消息(这不会自动触发,需要TrackMouseEvent函数来触发)
SetWindowText (edt, _T("鼠标已离开窗体"));
break ;
case WM_COMMAND :
break ;
default:
return DefWindowProc(hWnd,
message, wParam, lParam); ;

return 0;


示例二:
示例一的WM_MOUSEHOVER、WM_MOUSELEAVE貌似只能对窗体有效果,对窗体的子控件就没反应了;因此示例二演示如何实现对窗体上的控件也有效果
wchar_t* MouseHoL(HWND hWnd); //向前声明一个鼠标停留与离开函数
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

TRACKMOUSEEVENT tme; //定义触发鼠标事件结构
tme.cbSize = sizeof (TRACKMOUSEEVENT); //结构大小
tme.dwFlags = TME_HOVER | TME_LEAVE ; //鼠标停留/鼠标离开
tme.dwHoverTime = 100; //延时100毫秒
tme.hwndTrack = hWnd ; //窗体句柄

switch (message)
case WM_CREATE: //创建窗口消息
cbtn = CreateWindow(L"button", L"确定", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON | WS_GROUP, 250, 20, 75, 30, hWnd, (HMENU)IDB_BTN,(HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE), NULL); //button按键
edt = CreateWindow(L"edit", NULL, WS_CHILD | WS_VISIBLE | WS_BORDER,115, 23, 120, 24, hWnd, (HMENU)IDB_EDIT, hInst, NULL); //edit编辑文本框
break;
case WM_MOUSEMOVE :
//鼠标移动消息
TrackMouseEvent(&tme); //调用TrackMouseEvent函数,系统根据tme触发WM_MOUSEHOVER或WM_MOUSELEAVE消息
break ;
case WM_MOUSEHOVER: //鼠标停留消息(这不会自动触发,需要TrackMouseEvent函数来触发)
SetWindowText (edt, MouseHoL(cbtn)); //调用MouseHoL函数,判断鼠标是否停留在cbtn窗口
break ;
case WM_MOUSELEAVE : //鼠标离开消息(这不会自动触发,需要TrackMouseEvent函数来触发)
SetWindowText (edt, MouseHoL(cbtn)); //调用MouseHoL函数,判断鼠标是否离开cbtn窗口
break ;
case WM_COMMAND :
break ;
default:
return DefWindowProc(hWnd, message, wParam, lParam); ;

return 0;


//定义鼠标停留或离开函数
wchar_t* MouseHoL(HWND hWnd)
RECT cRC, winRC; //控件屏幕位置RECT,控件窗口大小RECT
POINT Mxy,Cxy; //鼠标坐标结构,控件坐标结构
GetCursorPos(&Mxy); //获取鼠标在屏幕的坐标
GetWindowRect(hWnd, &cRC);
//获取控件位于屏幕的位置及大小
GetClientRect(hWnd, &winRC); //获取控件的宽高
cRC.right = cRC.left + winRC.right; //控件右下角的x坐标,也就是宽
cRC.bottom = cRC.top + winRC.bottom; //控件右下角的y坐标,也就是高
if (Mxy.x > cRC.left && Mxy.x < cRC.right && Mxy.y > cRC.top && Mxy.y < cRC.bottom) //判断鼠标坐标是否在窗口内
return L"鼠标已进入控件";
else
return L"鼠标经离开控件";

参考技术A 函数功能:当在指定时间内鼠标指针离开或盘旋在一个窗口上时,此函数寄送消息。

函数原型:BOOL TrackMouseEvent(LPTRACKMOUSEEVENT lpEventTrack);

参数:

lpEventTrack;指向结构TRACKMOUSEEVENT的指针。

返回值:如果函数调用成功,返回非零值;如果函数调用失败,返回值是零。若想获得更多的错误信息,请调用GetLastError函数。

此函数能寄送如下消息:

WM_MOUSEHOVER:在上次调用TrackMouseEvent指定的时间里,鼠标盘旋在窗口的客户区。当此消息产生时,盘旋跟踪停止。如果需要进一步的鼠标盘旋跟踪,应用程序应当再次调用TrackMouseEvent。

WM_MOUSELEAVE:鼠标离开上次调用TrackMouseEvent时指定的窗口客户区。当此消息产生时,所有由TrackMouseEvent要求的跟踪都被取消。当鼠标再次进入窗口,并且要求进一步的鼠标盘旋跟踪时,应用程序必须调用TrackMouseEvent。

备注:当鼠标指针在指定时间内停留在指定矩形内,就被认为是处于盘旋状态。调用函数

SystemParameterslnfo并使用SPI_GETMOUSEAOVERWIDTH,SPI_GETMOUSEHOVERAEIGHT和

SFI_GETMOOSEAOVERTIME值来取得矩形的大小和时间。

速查:Windows NT 4.0及以上版本;Windows 98及以上版本;Windows CE:1.0及以上版本;头文件:winuser.h;输入库:user32.lib。
参考技术B 哈哈,都不给我发个链接,还好被我发现了。给你个代码片段:TRACKMOUSEEVENT tme = 0;
switch(uMsg)

case WM_MOUSEMOVE:
tme.cbSize = sizeof(tme);
tme.dwFlags = TME_HOVER | TME_LEAVE;
tme.hwndTrack = hwnd;
tme.dwHoverTime = 50; //鼠标在窗口上停留50毫秒时发送WM_MOUSEHOVER,可以改
TrackMouseEvent(&tme);
break;
case WM_MOUSELEAVE:
SetWindowText(hwnd,_T("MouseLeave"));
break;
case WM_MOUSEHOVER:
SetWindowText(hwnd,_T("MouseHover"));
break;
参考技术C 可以用WM_MOUSEMOVE消息处理,因为OnMouseMove可以返回一个鼠标坐标,在判断IsPtInRect就可以了! 参考技术D WindowFromPoint 可以得到当前PT所在的窗口句柄.判断句柄就知道是不是在你的窗口中..

如何在窗口上绘制图像?

【中文标题】如何在窗口上绘制图像?【英文标题】:How to draw image on a window? 【发布时间】:2009-11-17 12:17:28 【问题描述】:

我在 Windows Vista 上使用 C++ 中的 VS2005 创建了一个带有 createwindow() api 的窗口

我的要求是在该窗口上绘制图像(任何格式)。我没有在此应用程序中使用任何 MFC。

【问题讨论】:

【参考方案1】:

不完全确定您的问题是什么:在表单上绘制位图,或者您想知道如何处理各种图像格式,或两者兼而有之。无论如何,下面是一个如何加载位图并将其绘制在表单上的示例:

HBITMAP hBitmap = NULL;

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

    int wmId, wmEvent;

    switch (message)
    
<...>

    case WM_CREATE:
        hBitmap = (HBITMAP)LoadImage(hInst, L"c:\\test.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
        break;
    case WM_PAINT:
        PAINTSTRUCT     ps;
        HDC             hdc;
        BITMAP          bitmap;
        HDC             hdcMem;
        HGDIOBJ         oldBitmap;

        hdc = BeginPaint(hWnd, &ps);

        hdcMem = CreateCompatibleDC(hdc);
        oldBitmap = SelectObject(hdcMem, hBitmap);

        GetObject(hBitmap, sizeof(bitmap), &bitmap);
        BitBlt(hdc, 0, 0, bitmap.bmWidth, bitmap.bmHeight, hdcMem, 0, 0, SRCCOPY);

        SelectObject(hdcMem, oldBitmap);
        DeleteDC(hdcMem);

        EndPaint(hWnd, &ps);
        break;
    case WM_DESTROY:
        DeleteObject(hBitmap);
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    
    return 0;

LoadImage 加载图标、光标、动画光标或位图。详情here

要处理各种图像格式,您可以使用 Windows 映像组件(请参阅 IWICBitmapDecoder)或此处的代码 Loading JPEG and GIF pictures 或 3rd 方工具,如 FreeImage 或 LeadTools

希望这会有所帮助,问候

【讨论】:

嗨,Serge,它解决了我在窗口上绘制图像的问题。谢谢您的帮助。但由于这只需要 bmp、cur 和 ico 文件,我需要将 png 转换为 bmp,然后我会将 bmp 传递给这个函数 谁能指导我在不使用任何 MFC 的情况下将 png 图像放在窗口上 如果我可以为某人节省几分钟的搜索时间,我尝试使用这个答案,但它没有用。经过一段时间的戳我尝试将 sizeof(bitmap) 更改为 sizeof(BITMAP) 并且代码有效。我在 Windows 10 上使用 Visual Studio 2015。下面是整行:GetObject(hBitmap, sizeof(BITMAP), &bitmap); 如果之后立即删除 DC,为什么还要保存旧的 HBITMAP 并再次调用 SelectObject?【参考方案2】:
void LoadScreen(HWND hWnd) 
    RECT rect;
    HDC hdc = GetDC(hWnd);
    HBRUSH brush = CreatePatternBrush((HBITMAP)LoadImage(NULL, L"file.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE));
    GetWindowRect(hWnd, &rect);
    FillRect(hdc, &rect, brush);
    DeleteObject(brush);
    ReleaseDC(hWnd, hdc);

【讨论】:

函数退出时从未释放的泄露的 HDC? Windows GDI 规则 101:如果你得到它,然后释放它;如果你创建它,然后删除它。您使用画笔正确执行此操作(创建/删除),但使用客户端 DC(获取/发布)未能这样做。 啊,谢谢 :) 我解决了。在关闭大括号之前添加了一行。 ReleaseDC(hWnd, hdc); 功能方面,这与serge_gubenko's answer相比如何? 我不知道,但我发现我的解决方案更简单、干净、直接【参考方案3】:
#include <windows.h>
#include <string.h>

HBITMAP hBitmap, hOldBitmap;
HDC hdc, hdcMem;
BITMAP bm;
HINSTANCE hI;
PAINTSTRUCT ps;
RECT rect;
RECT rc;

LRESULT CALLBACK WindowProcedure(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)

switch (msg)
    
    case WM_CREATE:
    hBitmap = (HBITMAP)LoadImage(hI, "1.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
    GetObject(hBitmap, sizeof(BITMAP), &bm);
    hdc = GetDC(hWnd);
    hdcMem = CreateCompatibleDC(hdc);
    hOldBitmap = SelectBitmap(hdcMem, hBitmap);
    ReleaseDC(hWnd, hdc);
    return 0;

    case WM_LBUTTONDOWN:
    //for dragging not only by the title, but also by any part of the window 
    ReleaseCapture();
    SendMessage(hWnd, 0xA1, 2, 0);
    break;
    case WM_PAINT:
    hdc=BeginPaint(hWnd,&ps);
    
    //overlay image with stretching to fit the window 
    GetClientRect(hWnd,&rect);
    SetStretchBltMode(hdc, STRETCH_HALFTONE);
    StretchBlt(hdc,0,0,rect.right,rect.bottom,
    hdcMem,0,0,bm.bmWidth,bm.bmHeight,SRCCOPY);
    
    EndPaint(hWnd,&ps);
    break;      


    case WM_DESTROY:
    PostQuitMessage(0);
      
    DeleteDC(hdcMem);
    DeleteObject(hBitmap);
    DeleteObject(hOldBitmap);
    break;
    
return DefWindowProc(hWnd, msg, wParam, lParam);

 
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPInst, LPSTR lpCmdLine, int nCmdShow)

//copying a pointer to a running application instance (module)
hI=hInstance;

WNDCLASS wc;

wc.style         = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc   = WindowProcedure;
wc.hInstance     = hInstance;
wc.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH) GetStockObject(LTGRAY_BRUSH);
wc.lpszClassName = "test_class";
wc.lpszMenuName  = NULL;
wc.cbClsExtra    = 0;
wc.cbWndExtra    = 0;

RegisterClass(&wc);

HWND hWnd = CreateWindow(wc.lpszClassName, "Image Window", 
//window with title (overlapping window) 
WS_OVERLAPPEDWINDOW,
//window without title
//WS_VISIBLE | WS_POPUP | WS_SYSMENU | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
CW_USEDEFAULT, CW_USEDEFAULT, 500, 500, NULL, NULL, hInstance, NULL);

ShowWindow(hWnd, SW_SHOW);
UpdateWindow(hWnd);

MSG msg;
while(GetMessage (&msg, NULL, 0, 0))
    
    DispatchMessage (&msg);
    TranslateMessage (&msg);
    
UnregisterClass(wc.lpszClassName, hInstance);
return (int) msg.wParam;

【讨论】:

“宽普京”:P

以上是关于C++ 如何判断鼠标有没有在窗口上? 纯API 不是MFC的主要内容,如果未能解决你的问题,请参考以下文章

利用Windows API 在指定的窗口上单击一次鼠标

mac mini纯键盘操作连接蓝牙鼠标

Delphi 如何判断鼠标指针是不是在窗口中

如何使用纯 Python 扩展 API (python3) 包装 C++ 对象?

为啥在记事本窗口中输入时鼠标光标会消失,但在浏览器或 cmd 中没有?哪些 Win API 函数显示它以及如何显示它?

Qt判断鼠标在控件上