托管C++应用嵌入WPF WebView2控件关闭窗口时崩溃问题求助
运行环境
MS Edge runtime: 94.0.992.31 Nuget包: Microsoft.Web.WebView2 1.0.992.28
项目结构
现有两个项目:托管C主应用、WPF GUI类库,托管C程序会引用WPF类库的视图组件。此前使用旧版System.Windows.Controls.WebBrowser控件时运行稳定无崩溃,随着需要渲染的HTML复杂度持续提升,旧控件已无法适配渲染需求。经过技术调研后改用微软推出的WebView2控件,单独测试WPF GUI项目时运行完全正常,WebView2对复杂HTML/CSS的渲染效果符合预期。
异常现象
将集成了WebView2的WPF视图接入C++主应用后出现异常:用户从菜单打开搭载WebView2控件的窗口时,所有内容均可正常加载渲染;但点击窗口右上角关闭按钮时,整个应用会立即无响应,数秒后直接崩溃,且程序日志无任何报错记录,崩溃速度过快导致日志模块无法完成写入操作。
在C++应用中订阅WPF的Application.DispatcherUnhandledException事件,事件处理代码如下:
void CMainFrame::OnDispatcherUnhandledException(System::Object^ sender, System::Windows::Threading::DispatcherUnhandledExceptionEventArgs^ e) { System::String ^ expMessage = e->Exception->ToString(); }
最终捕获到的报错信息如下:
System.InvalidOperationException:WebView2控件已释放后无法访问CoreWebView2成员。内部异常System.Runtime.InteropServices.COMException:组或资源所处状态无法执行请求的操作。(HRESULT异常: 0x8007139F)
该报错不符合预期,此时窗口已关闭,并未尝试再次访问WebView2控件。从调用栈可以确认,异常是窗口销毁过程中WPF框架触发IsVisibleChanged事件,WebView2控件内部尝试修改CoreWebView2Controller.IsVisible属性导致的。
- 方案1:在WPF窗口的
Closing事件中提前手动释放WebView2资源,先从可视化树移除控件再销毁,避免后续事件触发:
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) { if (webView2 != null && webView2.Parent is Panel parentPanel) { // 先从父容器移除,阻断可视化树的属性变更事件传递 parentPanel.Children.Remove(webView2); webView2.Dispose(); webView2 = null; } // 等待Dispatcher处理完当前队列的低优先级消息,清空残留事件 Dispatcher.Invoke(() => {}, System.Windows.Threading.DispatcherPriority.Background); }
- 方案2:继承原生WebView2控件,重写
IsVisibleChanged逻辑,释放后跳过基类处理:
public class SafeWebView2 : Microsoft.Web.WebView2.Wpf.WebView2 { protected override void OnIsVisibleChanged(DependencyPropertyChangedEventArgs e) { // 控件已释放时直接返回,避免访问已销毁的CoreWebView2资源 if (IsDisposed) return; base.OnIsVisibleChanged(e); } }
将项目中所有用到WebView2的地方替换为自定义的SafeWebView2即可。
- 方案3:升级WebView2运行时和对应Nuget包到最新稳定版,你当前使用的1.0.992.28属于较老版本,微软在后续版本中已经修复了多个窗口销毁阶段的事件时序、资源泄漏类问题。
内容的提问来源于stack exchange,提问作者JhonMalk199

