.NET 跨进程设置父子窗口SetParent

跨进程设置父子窗口除了owner方案 C# 跨进程 设置窗口owner – 唐宋元明清2188 – 博客园,还有Win32-SetParent SetParent 函数 (winuser.h) – Win32 apps | Microsoft Learn

SetParent可以实现将子窗口嵌入另一个窗口中

1     [DllImport("user32.dll", SetLastError = true)]
2     static extern int SetParent(IntPtr hWndChild, IntPtr hWndNewParent);

hWndChild是子窗口,hWndNewParent是新父窗口句柄,返回参数为子窗口的上一次父窗口句柄

我们建个demo试试,

可以使用syp++-查找窗口句柄。这里为了效率高,直接显示窗口句柄方便获取:

1     CurrentHandleTextBox.Text = new WindowInteropHelper(this).Handle.ToString("X");

启动2个demo窗口,根据另一个窗口句柄和当前窗口句柄,设置SetParent

1     private void AddChildWindow_OnClick(object sender, RoutedEventArgs e)
2     {
3         var inputInt32 = Convert.ToInt32(InputTextBox.Text, 16);
4         IntPtr inputPtr = (IntPtr)inputInt32;
5         var currentInPtr = new WindowInteropHelper(this).Handle;
6 
7         var result = SetParent(inputPtr, currentInPtr);
8         MessageBox.Show($"SetParent Result:{result}");
9     }

显示效果如下:

设置成功时,结果:

65552是上一个父窗口句柄,这是哪个窗口呢?

我们把65552换成16进制,通过syp++,能看到是这是桌面窗口,即默认父窗口句柄是桌面的

上面Demo详见 kybs00/SetParentDemo

SetParent函数非常强大,适合那些需要设置子窗口只在父窗口区域内显示,比如分屏软件,可以把父窗口完全当作一个显示器来使用。

根据小伙伴反馈,应用设置了uiAccess后,在此进程打开其它软件,其它软件内部调用SetParent实现相关功能时会失败。

时间: 2025-01-08 18:03:44,034
线程ID: [1]
错误描述: 寄宿的 HWND 必须是指定父级的子窗口。
异常: System.InvalidOperationException: 寄宿的 HWND 必须是指定父级的子窗口。
   在 System.Windows.Interop.HwndHost.BuildWindow(HandleRef hwndParent)
   在 System.Windows.Interop.HwndHost.BuildOrReparentWindow()
   在 System.Windows.Interop.HwndHost.OnSourceChanged(Object sender, SourceChangedEventArgs e)
   在 System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target)
   在 System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs)
   在 System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
   在 System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args)
   在 System.Windows.PresentationSource.UpdateSourceOfElement(DependencyObject doTarget, DependencyObject doAncestor, DependencyObject doOldParent)
   在 System.Windows.PresentationSource.OnVisualAncestorChanged(DependencyObject uie, AncestorChangedEventArgs e)
   在 System.Windows.UIElement.OnVisualAncestorChanged(Object sender, AncestorChangedEventArgs e)
   在 System.Windows.Media.Visual.ProcessAncestorChangedNotificationRecursive(DependencyObject e, AncestorChangedEventArgs args)
   在 System.Windows.Media.Visual.ProcessAncestorChangedNotificationRecursive(DependencyObject e, AncestorChangedEventArgs args)
   在 System.Windows.Media.Visual.ProcessAncestorChangedNotificationRecursive(DependencyObject e, AncestorChangedEventArgs args)
   在 System.Windows.Media.Visual.AddVisualChild(Visual child)
   在 System.Windows.FrameworkElement.set_TemplateChild(UIElement value)
   在 System.Windows.Controls.ContentPresenter.UseContentTemplate.BuildVisualTree(FrameworkElement container)
   在 System.Windows.StyleHelper.ApplyTemplateContent(UncommonField`1 dataField, DependencyObject container, FrameworkElementFactory templateRoot, Int32 lastChildIndex, HybridDictionary childIndexFromChildID, FrameworkTemplate frameworkTemplate)
   在 System.Windows.FrameworkTemplate.ApplyTemplateContent(UncommonField`1 templateDataField, FrameworkElement container)
   在 System.Windows.FrameworkElement.ApplyTemplate()
   在 System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   在 System.Windows.UIElement.Measure(Size availableSize)
   在 System.Windows.Controls.Border.MeasureOverride(Size constraint)
   在 System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   在 System.Windows.UIElement.Measure(Size availableSize)
   在 System.Windows.Controls.Control.MeasureOverride(Size constraint)
   在 System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   在 System.Windows.UIElement.Measure(Size availableSize)
   在 System.Windows.Controls.Grid.MeasureCell(Int32 cell, Boolean forceInfinityV)
   在 System.Windows.Controls.Grid.MeasureCellsGroup(Int32 cellsHead, Size referenceSize, Boolean ignoreDesiredSizeU, Boolean forceInfinityV, Boolean& hasDesiredSizeUChanged)
   在 System.Windows.Controls.Grid.MeasureOverride(Size constraint)
   在 System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   在 System.Windows.UIElement.Measure(Size availableSize)
   在 System.Windows.ContextLayoutManager.UpdateLayout()
   在 MagicWindowSplit.SplitService.<SplitWindow>d__25.MoveNext()
--- 引发异常的上一位置中堆栈跟踪的末尾 ---
   在 System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   在 MagicWindowSplit.SplitService.<SelectControlSplit>d__16.MoveNext()
--- 引发异常的上一位置中堆栈跟踪的末尾 ---
   在 System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   在 System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
   在 System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)

uiAccess进程启动软件,会默认添加管理员权限。为了简化后续描述,我们称“uiAccess权限进程启动其它软件”,有uiAccess影响。

下面我们启动俩个相同demo.exe,使用俩个不同进程的窗口,来验证SetParent情况

1.俩个均有uiAccess影响的窗口 –  正常

2.有uiAccess影响的窗口A、非uiAccess影响窗口B,在A内设置A为B的子窗口 – 正常

3.有uiAccess影响的窗口A、非uiAccess影响窗口B,在B内设置A为B的子窗口 – 错误,返回值0

原因是管理员权限问题,非管理员权限无法设置管理员权限窗口为其子窗口,仅用管理员权限验证也是一样的结果。

4. 有uiAccess影响的窗口A、非uiAccess影响窗口B,在A内设置B为A的子窗口 – 正常

5. 有uiAccess影响的窗口A、非uiAccess影响窗口B,在B内设置B为A的子窗口 – 错误

原因是管理员权限问题,非管理员权限无法设置管理员权限窗口为其父窗口,仅用管理员权限验证也是一样的结果。

所以设置SetParent失败与uiAccess影响无关,仅仅是管理员权限不足。 而上面小伙伴反馈的分屏软件调用SetParent异常是业务逻辑问题,毅仔有相关介绍 System.InvalidOperationException:“寄宿的 HWND 必须是指定父级的子窗口。” – walterlv 可以参照解决   SetParent其它问题可以参考: SetParent 函数修改父窗口的误区-CSDN博客 使用 SetParent 制作父子窗口的时候,如何设置子窗口的窗口样式以避免抢走父窗口的焦点 – walterlv 使用 SetParent 跨进程设置父子窗口时的一些问题(小心卡死) – walterlv

来源链接:https://www.cnblogs.com/kybs0/p/18664127

© 版权声明
THE END
支持一下吧
点赞9 分享
评论 抢沙发
头像
请文明发言!
提交
头像

昵称

取消
昵称表情代码

    暂无评论内容