#c_sharp #net #winforms #webbrowser
Есть форма с единственным контролом типа WebBrowser. Для обработки нажатия Ecs используется переопределение ProcessCmdKey. При загрузке формы выполняется открытие стараницы в webBrowser1. Вот код: public partial class Form1 : Form { public Form1() { InitializeComponent(); } protected override bool ProcessCmdKey(ref Message msg, Keys keyData) { if (keyData == Keys.Escape) { this.Close(); return true; } return base.ProcessCmdKey(ref msg, keyData); } private void Form1_Load(object sender, EventArgs e) { webBrowser1.Navigate("about:blank"); } } Проблема в том, что при нажатии Esc форма закрывается, но приложение падает: RaceOnRCWCleanup occurred Message: Managed Debugging Assistant 'RaceOnRCWCleanup' has detected a problem in 'C:\Тут был путь к проекту\bin\Release\Test - Close by Esc with WebBrowser.vshost.exe'. Additional information: Была предпринята попытка освободить RCW, находящийся в использовании. RCW используется активным потоком или другим потоком. Попытка освободить RCW, находящийся в использовании, может привести к повреждению или потере данных. Стектрейс показывается такой: mscorlib.dll!System.__ComObject.FinalReleaseSelf() Unknown mscorlib.dll!System.Runtime.InteropServices.Marshal.FinalReleaseComObject(object o) Unknown System.Windows.Forms.dll!System.Windows.Forms.WebBrowserBase.TransitionFromLoadedToPassive() Unknown System.Windows.Forms.dll!System.Windows.Forms.WebBrowserBase.TransitionDownTo(System.Windows.Forms.WebBrowserHelper.AXState state) Unknown System.Windows.Forms.dll!System.Windows.Forms.WebBrowser.Dispose(bool disposing) Unknown System.dll!System.ComponentModel.Component.Dispose() Unknown System.Windows.Forms.dll!System.Windows.Forms.Control.Dispose(bool disposing) Unknown System.Windows.Forms.dll!System.Windows.Forms.Form.Dispose(bool disposing) Unknown > Test - Close by Esc with WebBrowser.exe!Test___Close_by_Esc_with_WebBrowser.Form1.Dispose(bool disposing) Line 21 C# System.Windows.Forms.dll!System.Windows.Forms.Form.WmClose(ref System.Windows.Forms.Message m) Unknown System.Windows.Forms.dll!System.Windows.Forms.Form.WndProc(ref System.Windows.Forms.Message m) Unknown System.Windows.Forms.dll!System.Windows.Forms.Control.ControlNativeWindow.OnMessage(ref System.Windows.Forms.Message m) Unknown System.Windows.Forms.dll!System.Windows.Forms.Control.ControlNativeWindow.WndProc(ref System.Windows.Forms.Message m) Unknown System.Windows.Forms.dll!System.Windows.Forms.NativeWindow.DebuggableCallback(System.IntPtr hWnd, int msg, System.IntPtr wparam, System.IntPtr lparam) Unknown [Native to Managed Transition] [Managed to Native Transition] System.Windows.Forms.dll!System.Windows.Forms.Control.SendMessage(int msg, int wparam, int lparam) Unknown System.Windows.Forms.dll!System.Windows.Forms.Form.Close() Unknown Test - Close by Esc with WebBrowser.exe!Test___Close_by_Esc_with_WebBrowser.Form1.ProcessCmdKey(ref System.Windows.Forms.Message msg, System.Windows.Forms.Keys keyData) Line 25 C# System.Windows.Forms.dll!System.Windows.Forms.Control.ProcessCmdKey(ref System.Windows.Forms.Message msg, System.Windows.Forms.Keys keyData) Unknown System.Windows.Forms.dll!System.Windows.Forms.Control.PreProcessMessage(ref System.Windows.Forms.Message msg) Unknown System.Windows.Forms.dll!System.Windows.Forms.WebBrowserBase.PreProcessMessage(ref System.Windows.Forms.Message msg) Unknown System.Windows.Forms.dll!System.Windows.Forms.Control.PreProcessControlMessageInternal(System.Windows.Forms.Control target, ref System.Windows.Forms.Message msg) Unknown System.Windows.Forms.dll!System.Windows.Forms.Control.PreProcessControlMessage(ref System.Windows.Forms.Message msg) Unknown System.Windows.Forms.dll!System.Windows.Forms.WebBrowserSiteBase.System.Windows.Forms.UnsafeNativeMethods.IOleControlSite.TranslateAccelerator(ref System.Windows.Forms.NativeMethods.MSG pMsg, int grfModifiers) Unknown [Native to Managed Transition] [Managed to Native Transition] System.Windows.Forms.dll!System.Windows.Forms.WebBrowserBase.PreProcessMessage(ref System.Windows.Forms.Message msg) Unknown System.Windows.Forms.dll!System.Windows.Forms.Control.PreProcessControlMessageInternal(System.Windows.Forms.Control target, ref System.Windows.Forms.Message msg) Unknown System.Windows.Forms.dll!System.Windows.Forms.Application.ThreadContext.PreTranslateMessage(ref System.Windows.Forms.NativeMethods.MSG msg) Unknown System.Windows.Forms.dll!System.Windows.Forms.Application.ThreadContext.System.Windows.Forms.UnsafeNativeMethods.IMsoComponent.FPreTranslateMessage(ref System.Windows.Forms.NativeMethods.MSG msg) Unknown System.Windows.Forms.dll!System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(System.IntPtr dwComponentID, int reason, int pvLoopData) Unknown System.Windows.Forms.dll!System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(int reason, System.Windows.Forms.ApplicationContext context) Unknown System.Windows.Forms.dll!System.Windows.Forms.Application.ThreadContext.RunMessageLoop(int reason, System.Windows.Forms.ApplicationContext context) Unknown System.Windows.Forms.dll!System.Windows.Forms.Application.Run(System.Windows.Forms.Form mainForm) Unknown Test - Close by Esc with WebBrowser.exe!Test___Close_by_Esc_with_WebBrowser.Program.Main() Line 20 C# [Native to Managed Transition] [Managed to Native Transition] mscorlib.dll!System.AppDomain.ExecuteAssembly(string assemblyFile, System.Security.Policy.Evidence assemblySecurity, string[] args) Unknown Microsoft.VisualStudio.HostingProcess.Utilities.dll!Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() Unknown mscorlib.dll!System.Threading.ThreadHelper.ThreadStart_Context(object state) Unknown mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Unknown mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Unknown mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state) Unknown mscorlib.dll!System.Threading.ThreadHelper.ThreadStart() Unknown Что можно сделать?
Ответы
Ответ 1
Это не ошибка, это лишь предупреждение, выдаваемое Managed Debugging Assistant'ом (это такое расширение отладчика), которому кажется, что с вашим кодом что-то не так. Вы используете Runtime Callable Wrapper, специальную обёртку для COM-контролов (WebBrowser по сути является COM-контролом). Когда вы пытаетесь закрыть форму, расширение отладчика бросается на амбразуру и заявляет вам, что браузер ещё используется, а вы убиваете его до того, как его текущая функция полностью отработает. Такое может по идее привести к реальным проблемам лишь если ваш код многопоточный. Проблема воспроизводится в обычном коде при использовании CancelProperty у кнопки, или DialogResult. Или в вашем случае, когда у вас есть особая реакция на Esc. Фокус находится на WebBrowser'е, он получает нажатие Esc. Обвязка ActiveX собирается сообщить контейнеру о нажатии, чтобы он мог на него отреагировать. (Это нужно, например, для поддержки клавиатурных шорткатов, Tab'а, Enter/Escape для активизации кнопок и тому подобного.) Если при этом нажатие на клавишу закрывает форму (как в вашем случае), отладчик видит, что на WebBrowser'е вызывается Dispose в то время как код его RCW ещё выполняется. Опасность заключается в том, что когда код вернётся в RCW, он имеет право работать с COM-объектом (он же не знает, что у него из под носа этот самый объект увели), и произойдёт вылет — это не такой уж и редкий сценарий. Окей, в конкретно данном случае вылет вроде бы не воспроизводится так просто. Но по идее проблема может произойти если финализирующий поток доберётся до COM-объекта до того, как произойдёт возврат из обработчика Esc. Так что у нач возможна гонка между UI-потоком и потоком финализатора. В качестве заплатки можно отложить реальное закрытие на момент, когда код обвязки ActiveX закончит работу. Например, вызывая закрытие асинхронно: if (keyData == Keys.Escape) { this.BeginInvoke((Action)this.Close); return true; } Вольный перевод этого ответа с en.SOОтвет 2
Возможный вариант решения - подписаться на PreviewKeyDown самого WebBrowserа: private void webBrowser1_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e) { this.Close(); } Обращаю внимание, что подписка на нажатие клавиши на форме не работает, независимо от значения свойства KeyPreview. Нужно именно на браузер.Ответ 3
Базовый метод ProcessCmdKey надо вызывать до вызова Close. protected override bool ProcessCmdKey(ref Message msg, Keys keyData) { var res = base.ProcessCmdKey(ref msg, keyData); if (keyData == Keys.Escape) { this.Close(); res = true; } return res; } Иначе, если base.ProcessCmdKey вызывается после закрытия формы, то надо удалять браузер перед закрытием. protected override bool ProcessCmdKey(ref Message msg, Keys keyData) { if (keyData == Keys.Escape) { this.Controls.Remove(wb); // ДОБАВИТЬ ЭТУ СТРОКУ this.Close(); return true; } return base.ProcessCmdKey(ref msg, keyData); } Проверил в Visual Studio Community 2015, C# Interactive // Microsoft (R) Roslyn C# Compiler version 1.1.0.51204 #r "System.WIndows.Forms" using System.Windows.Forms; public class Form1 : Form { WebBrowser wb; public Form1() { wb = new WebBrowser() { Dock = DockStyle.Fill, Parent = this }; wb.Navigating += (s, e) => Console.WriteLine("Navigating " + e.Url); wb.Navigated += (s, e) => Console.WriteLine("Navigated " + e.Url); wb.DocumentCompleted += delegate { Console.WriteLine("DocumentCompleted"); }; } protected override bool ProcessCmdKey(ref Message msg, Keys keyData) { if (keyData == Keys.Escape) { this.Controls.Remove(wb); // ДОБАВИТЬ ЭТУ СТРОКУ this.Close(); return true; } return base.ProcessCmdKey(ref msg, keyData); } protected override void OnLoad(EventArgs e) { wb.Navigate("about:blank"); } } var f = new Form1(); f.Activated += delegate { SendKeys.Send("{ESC}"); }; f.ShowDialog(); Работает нормально, закрывается при нажатии Esc и выводит Navigating about:blank Navigated about:blank DocumentCompleted
Комментариев нет:
Отправить комментарий