.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其它问题可以参考:
posted @   唐宋元明清2188  阅读(106)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 2分钟学会 DeepSeek API,竟然比官方更好用!
· .NET 使用 DeepSeek R1 开发智能 AI 客户端
· 10亿数据,如何做迁移?
· 推荐几款开源且免费的 .NET MAUI 组件库
· c# 半导体/led行业 晶圆片WaferMap实现 map图实现入门篇
点击右上角即可分享
微信分享提示