Blinue / Magpie

An all-purpose window upscaler for Windows 10/11.
GNU General Public License v3.0
9.66k stars 496 forks source link

添加和其他程序交互的机制 #895

Closed Blinue closed 7 months ago

Blinue commented 7 months ago

Close #447

我创建了 MagpieWatcher 来演示这些机制。

如何在缩放状态改变时得到通知

监听 MagpieScalingChanged 消息。

UINT WM_MAGPIE_SCALINGCHANGED = RegisterWindowMessage(L"MagpieScalingChanged");

参数

wParam 为事件 ID,对于不同的事件 lParam 有不同的含义。目前支持两个事件:

注意事项

如果你的进程完整性级别 (Integration level) 比 Magpie 更高,由于用户界面特权隔离 (UIPI),你将无法收到 Magpie 广播的消息。这种情况下请调用 ChangeWindowMessageFilter 允许接收 MagpieScalingChanged 消息。

ChangeWindowMessageFilter(WM_MAGPIE_SCALINGCHANGED, MSGFLT_ADD);

如何获取缩放窗口句柄

你可以监听 MagpieScalingChanged 消息来获取缩放窗口句柄,也可以查找类名为Window_Magpie_967EB565-6F73-4E94-AE53-00CC42592A22的窗口以在缩放中途获取该句柄。Magpie 将确保此类名不会改变,且不会同时存在多个缩放窗口。

HWND hwndScaling = FindWindow(L"Window_Magpie_967EB565-6F73-4E94-AE53-00CC42592A22", nullptr);

如何将你的窗口置于缩放窗口上方

你的窗口必须是置顶的。你还应该监听 MagpieScalingChanged 消息,当收到该消息时缩放窗口已经显示,然后你可以使用 BringWindowToTop 函数将自己的窗口置于缩放窗口上方。缩放窗口在存在期间不会尝试调整自己在 Z 轴的位置。

HWND hWnd = CreateWindowEx(WS_EX_TOPMOST, ...);
...
if (message == WM_MAGPIE_SCALINGCHANGED) {
    switch (wParam) {
        case 0:
            // 缩放已结束
            break;
        case 1:
            // 缩放已开始
            // 将本窗口置于缩放窗口上面
            BringWindowToTop(hWnd);
            break;
        default:
            break;
    }
}

如何获取缩放信息

缩放窗口的窗口属性中存储着缩放信息。目前支持以下属性:

HWND hwndSrc = (HWND)GetProp(hwndScaling, L"Magpie.SrcHWND");

RECT srcRect;
srcRect.left = (LONG)(INT_PTR)GetProp(hwndScaling, L"Magpie.SrcLeft");
srcRect.top = (LONG)(INT_PTR)GetProp(hwndScaling, L"Magpie.SrcTop");
srcRect.right = (LONG)(INT_PTR)GetProp(hwndScaling, L"Magpie.SrcRight");
srcRect.bottom = (LONG)(INT_PTR)GetProp(hwndScaling, L"Magpie.SrcBottom");

RECT destRect;
destRect.left = (LONG)(INT_PTR)GetProp(hwndScaling, L"Magpie.DestLeft");
destRect.top = (LONG)(INT_PTR)GetProp(hwndScaling, L"Magpie.DestTop");
destRect.right = (LONG)(INT_PTR)GetProp(hwndScaling, L"Magpie.DestRight");
destRect.bottom = (LONG)(INT_PTR)GetProp(hwndScaling, L"Magpie.DestBottom");

如何使 Magpie 在你的窗口位于前台时保持缩放

前台窗口改变时 Magpie 会停止缩放,只对某些系统窗口例外。你可以通过设置属性 Magpie.ToolWindow 将自己的窗口添加入例外,这对由该窗口拥有 (owned) 的窗口也有效。

SetProp(hWnd, L"Magpie.ToolWindow", (HANDLE)TRUE);
Blinue commented 7 months ago

把 MessageBox 的 hWnd 参数设为主窗口即可,弹窗会被主窗口“拥有”。如果希望弹窗期间主窗口可以交互,可以用 TaskDialogIndirect 代替。

TASKDIALOGCONFIG tdc{
    .cbSize = sizeof(TASKDIALOGCONFIG),
    .dwFlags = TDF_SIZE_TO_CONTENT,
    .pszWindowTitle = L"test",
    .pszContent = L"test",
    .cButtons = 0,
    .pButtons = nullptr,
    .pfCallback = [](HWND hWnd, UINT msg, WPARAM /*wParam*/, LPARAM /*lParam*/, LONG_PTR /*lpRefData*/) -> HRESULT {
        if (msg == TDN_CREATED) {
            SetProp(hWnd, L"Magpie.ToolWindow", (HANDLE)TRUE);
        }

        return S_OK;
    }
};
TaskDialogIndirect(&tdc, nullptr, nullptr, nullptr);
Blinue commented 7 months ago

消息窗口和文件选择窗口一般都是模态的,不需要特殊处理。原本这个机制只用于简单的场景,你的场景比较复杂的话就必须自己处理复杂的情况了。

从窗口属性读取进程 ID 不大可行。

  1. 粒度太大,可能还需要实现排除的机制
  2. Magpie 必须保存状态,这不是好设计。想象一下你的程序退出之后,Magpie 保存的进程 ID 就会无效
  3. 如果弹窗在主窗口之前得到焦点也会退出缩放,因为弹窗上没有属性,就无法获得进程 ID
Blinue commented 7 months ago

以管理员身份运行 Magpie 问题还存在吗?