You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WPF窗口二次显示失败,调用ShowDialog时抛出无效窗口句柄Win32异常

问题

在COM互操作项目中,首次通过STA线程创建WPF对话框时,对话框可正常从父窗口弹出,XML字符串也能成功传递给COM项目,关闭后无异常。但在另一工作流中再次调用LaunchHostDlg方法创建对话框时,调试发现hostDlg.Owner为null,执行HostDlg.ShowDialog()时抛出Win32异常,提示无效窗口句柄。

相关代码
private ManualResetEvent dialogCompletedEvent = new ManualResetEvent(false);

private void LaunchHostDlg(int handle, string ptXML)
{
    var staThread = new Thread(() =>
    {
        var hostDlg = new HostDlg(handle);

        hostDlg.Loaded += (sender, e) =>
        {
            Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() =>
            {
                hostDlg.CreateDialogContent(ptXML);
            }));
            hostDlg.OpenXmlReceived += HostDlg_OpenXmlReceived;
        };
        hostDlg.ShowDialog();
    });
    staThread.SetApartmentState(ApartmentState.STA);
    staThread.Start();
}

private void HostDlg_OpenXmlReceived(object sender, string openXml)
{
    if (string.IsNullOrEmpty(openXml))
        return;
    m_OpenReturnString = openXml;
    CloseDialogAndExitThread(sender as HostDlg);
}

private void CloseDialogAndExitThread(HostDlg hostDlg)
{
    hostDlg.CloseDialog();
    dialogCompletedEvent.Set();
}

public HostDlg(int parentWindow)
{
    Title = "Schedule";
    Width = 1000;
    Height = 500;
    ResizeMode = ResizeMode.NoResize;
    new WindowInteropHelper(this).Owner = (IntPtr)parentWindow;
}
异常堆栈信息
at MS.Win32.UnsafeNativeMethods.CreateWindowEx(Int32 dwExStyle, String lpszClassName, String lpszWindowName, Int32 style, Int32 x, Int32 y, Int32 width, Int32 height, HandleRef hWndParent, HandleRef hMenu, HandleRef hInst, Object pvParam)
   at MS.Win32.HwndWrapper..ctor(Int32 classStyle, Int32 style, Int32 exStyle, Int32 x, Int32 y, Int32 width, Int32 height, String name, IntPtr parent, HwndWrapperHook[] hooks)
   at System.Windows.Interop.HwndSource.Initialize(HwndSourceParameters parameters)
   at System.Windows.Window.CreateSourceWindow(Boolean duringShow)
   at System.Windows.Window.CreateSourceWindowDuringShow()
   at System.Windows.Window.SafeCreateWindowDuringShow()
   at System.Windows.Window.ShowHelper(Object booleanBox)
   at System.Windows.Window.Show()
   at System.Windows.Window.ShowDialog()
原因分析与解决方案

1. 父窗口句柄有效性问题

第二次调用时传入的父窗口句柄可能已失效(如父窗口重建、句柄被释放),需先验证句柄有效性:

  • 引入Win32 API判断窗口是否存在:
    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool IsWindow(IntPtr hWnd);
    
  • 在LaunchHostDlg开头添加校验逻辑:
    IntPtr parentHandle = (IntPtr)handle;
    if (!IsWindow(parentHandle))
    {
        throw new InvalidOperationException("父窗口句柄无效,请确认父窗口状态");
    }
    

2. 全局ManualResetEvent未重置

dialogCompletedEvent为全局实例,第一次调用Set()后会保持触发状态,第二次使用前需重置:

private void LaunchHostDlg(int handle, string ptXML)
{
    dialogCompletedEvent.Reset(); // 添加这行
    var staThread = new Thread(() =>
    {
        // 原逻辑...
    });
    // 原逻辑...
}

3. Owner设置时机错误

WPF窗口句柄为延迟创建,构造函数中设置Owner可能因窗口未初始化导致失效,建议在Loaded事件中设置:

  • 修改HostDlg构造函数,移除Owner设置代码:
    public HostDlg(int parentWindow)
    {
        Title = "Schedule";
        Width = 1000;
        Height = 500;
        ResizeMode = ResizeMode.NoResize;
        // 移除原Owner设置代码
    }
    
  • 在LaunchHostDlg的Loaded事件中添加Owner设置:
    hostDlg.Loaded += (sender, e) =>
    {
        var helper = new WindowInteropHelper(hostDlg);
        helper.Owner = (IntPtr)handle; // 移到此处设置
        Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() =>
        {
            hostDlg.CreateDialogContent(ptXML);
        }));
        hostDlg.OpenXmlReceived += HostDlg_OpenXmlReceived;
    };
    

4. STA线程Dispatcher未正确退出

对话框关闭后,STA线程的Dispatcher未退出循环,可能导致线程残留引发状态异常,需主动关闭Dispatcher:

private void CloseDialogAndExitThread(HostDlg hostDlg)
{
    hostDlg.CloseDialog();
    dialogCompletedEvent.Set();
    hostDlg.Dispatcher.InvokeShutdown(); // 添加这行,退出Dispatcher循环
}

内容的提问来源于stack exchange,提问作者sandy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 15:59:50