取消

如何利用 Win32 API 设置两个窗口的所有者(Owner)关系

设置两个窗口的父子关系非常简单,只需要调用 SetParent 函数即可。然而设置两个窗口的所有者(Owner)关系却没有一个简单直观的 API。那么本文介绍一下如何设置两个窗口的 Owner 关系。


设置所有者(Owner)

由于方法非常简单,所以我直接贴出 MainWindow 中的完整代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        SourceInitialized += OnSourceInitialized;
    }

    private void OnSourceInitialized(object? sender, EventArgs e)
    {
        var ownerHwnd = User32.GetForegroundWindow();
        var hwnd = new WindowInteropHelper(this).Handle;
        User32.SetWindowLong(hwnd,
            GetWindowLongIndexes.GWL_HWNDPARENT,
            (nint)ownerHwnd);
    }
}

在这里,我准备好了两个窗口句柄,一个是 ownerHwnd,我随便取了当前窗口的;另一个是 hwnd 即自己的句柄。这样,程序启动的时候,便会把自己窗口的所有者设置为启动前最后一个前台窗口。

接下来是关键代码 SetWindowLong,传入三个参数:

  1. 自己窗口的句柄 hwnd
  2. GWL_HWNDPARENT 即指定所有者(在官方文档中,依然将其描述为 parent`)
  3. 所有者窗口的句柄 ownerHwnd

所需 API

在 C# 中,以上 API 函数需要定义。为了方便,你可以直接安装库 Lsj.Util.Win32 以省去所有的定义工作。

如果你不想引入库,可以用下面我准备好的定义(摘自 Lsj.Util.Win32 并简化):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static nint SetWindowLong([In] nint hWnd, [In] GetWindowLongIndexes nIndex, [In] nint dwNewLong) => nint.Size > 4
    ? SetWindowLongPtrImp(hWnd, nIndex, dwNewLong)
    : SetWindowLongImp(hWnd, nIndex, dwNewLong.ToInt32());

[DllImport("user32.dll", CharSet = CharSet.Unicode, EntryPoint = "SetWindowLongW", ExactSpelling = true, SetLastError = true)]
private static extern int SetWindowLongImp(nint hWnd, GetWindowLongIndexes nIndex, int dwNewLong);

[DllImport("user32.dll", CharSet = CharSet.Unicode, EntryPoint = "SetWindowLongPtrW", ExactSpelling = true, SetLastError = true)]
private static extern nint SetWindowLongPtrImp(nint hWnd, GetWindowLongIndexes nIndex, nint dwNewLong);

public enum GetWindowLongIndexes
{
    GWL_HWNDPARENT = -8,
}

后续需求

出于兼容性考虑,即便设置为了所有者关系,Windows 系统也不会强制修改窗口的样式(例如从任务栏中去掉)。你可以考虑将窗口的 WindowStylesEx 属性中的 WS_EX_APPWINDOW 部分去掉来实现这样的效果。

1
var style = style & ~WindowStylesEx.WS_EX_APPWINDOW;

至于具体如何使用 GetWindowLongSetWindowLong 来实现以上目的,本文就不赘述了。


参考资料

本文会经常更新,请阅读原文: https://blog.walterlv.com/post/set-owner-window-using-win32-api ,以避免陈旧错误知识的误导,同时有更好的阅读体验。

知识共享许可协议

本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名 吕毅 (包含链接: https://blog.walterlv.com ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系 ([email protected])

登录 GitHub 账号进行评论