把一个半透明窗口嵌入Windows桌面并转发系统鼠标消息到窗口

创建一个半透明窗口(Qt)

MainWindow::MainWindow(const QJsonObject& obj,QWidget *parent) 
  : QMainWindow(parent)
{
	setWindowFlag(Qt::FramelessWindowHint);
    setAttribute(Qt::WA_TranslucentBackground, true);
}

以圆角矩形画半透明背景

void MainWindow::paintEvent(QPaintEvent* event)
{
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing, true);
    painter.setBrush(QColor(0, 0, 0, 166));
    painter.setPen(Qt::NoPen);
    painter.drawRoundedRect(rect(), 4, 4);
}

把窗口嵌入桌面

窗口在桌面背景前面,在桌面图标后面

static HWND workerW;
if (!workerW) {
    HWND progman = FindWindow(L"Progman", NULL);
    SendMessage(progman, 0x052C, 0xD, 0);
    SendMessage(progman, 0x052C, 0xD, 1);
    EnumWindows([](HWND hwnd, LPARAM lParam) -> BOOL {
        HWND defView = FindWindowEx(hwnd, NULL, L"SHELLDLL_DefView", NULL);
        if (defView != NULL) {
            auto tar = (HWND*)lParam;
            *tar = FindWindowEx(NULL, hwnd, L"WorkerW", NULL);
        }
        return TRUE;
        }, (LPARAM)&workerW);
}
auto hwnd = (HWND)winId();
SetParent(hwnd, workerW);

注册输入设备

//WNDPROC oldProc;

auto hwnd = (HWND)winId();
RAWINPUTDEVICE rids[1];
rids[0].usUsagePage = 0x01;
rids[0].usUsage = 0x02;
rids[0].dwFlags = 0x00000100;
rids[0].hwndTarget = hwnd;
RegisterRawInputDevices(rids, 1, sizeof(rids[0]));
oldProc = (WNDPROC)SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)MainWindow::processMsg);

现在这个窗口的所有消息都会被转发到 WNDPROC oldProc; 这个方法中,

非但如此,现在操作系统的所有鼠标消息,也会被转发到这个方法中,这个方法的签名如下:

LRESULT CALLBACK MainWindow::processMsg(HWND hWnd, 
    UINT uMsg, 
    WPARAM wParam, 
    LPARAM lParam){

  //过滤没用的消息

  //处理系统鼠标消息


  //这个方法最后要把消息交给qt的消息处理机制
  return CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam);
}

下面就介绍这个方法 

过滤没用的消息

if (uMsg != WM_INPUT ) {
    return CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam);
}
POINT globalPos;
GetCursorPos(&globalPos);
RECT rect;
GetWindowRect(hWnd, &rect);
if (globalPos.x < rect.left || globalPos.y < rect.top ||
    globalPos.x > rect.right || globalPos.y > rect.bottom) {
    win->onEmbedLeaveWindow();
    return CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam);
}
HWND hwnd = WindowFromPoint(globalPos);
WCHAR className[28];
int len = GetClassName(hwnd, className, 28);
if ((lstrcmp(TEXT("SysListView32"), className) != 0) &&
    (lstrcmp(TEXT("WorkerW"), className) != 0) &&
    (lstrcmp(TEXT("Progman"), className) != 0)) {
    win->onEmbedLeaveWindow();
    return CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam);
}
  1. 不是操作系统的鼠标消息不处理 WM_INPUT 
  2. 鼠标位置没在窗口区域内,不处理
  3. 鼠标所在位置的窗口句柄,不是桌面窗口句柄(SysListView32、WorkerW、Progman)不处理

处理操作系统鼠标消息

auto raw = getRawInput((HRAWINPUT)lParam);
if (raw->header.dwType == RIM_TYPEMOUSE)
{
    RAWMOUSE rawMouse = raw->data.mouse;
    if (rawMouse.usButtonFlags == RI_MOUSE_WHEEL)  //鼠标滚轮消息
    {
        auto wheelDelta = (short)rawMouse.usButtonData;
        win->onEmbedScroll(wheelDelta); //小于0滚动条向下移动
    }
    else {
        switch (rawMouse.ulButtons)
        {
        case RI_MOUSE_LEFT_BUTTON_DOWN: //鼠标按下
        {
            win->onEmbedMousePress();
            break;
        }
        default:
        {
            win->onEmbedMouseMove();  //鼠标移动
            break;
        }
        }
    }
}

注意事项

当窗口未嵌入桌面时,不应该注册输入设备。

当窗口嵌入桌面时,Qt的事件处理机制是接不到鼠标消息的,你只能在onEmbed....方法中手动处理。

把窗口从嵌入状态恢复为非嵌入状态,最简单的办法就是把这个窗口关了,重新创建一个。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值