У меня есть приложение WinForms, в котором я разместил веб-страницу внутри элемента управления WebBrowser.Как перехватить событие onbeforeunload в элементе управления WebBrowser?
Содержание веб-страницы заключается в следующем:
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<title>onbeforeunload test</title>
<meta charset="utf-8">
</head>
<body>
<a href="#" onclick="window.location.reload();">Test</a>
<script type="text/javascript">
window.onbeforeunload = function() {
return 'Are you sure you want to leave this page?';
};
</script>
</body>
</html>
Как вы можете видеть, что я подписался на onbeforeunload
событие, которое позволяет показать диалог подтверждения Прежде чем перейти на этой странице. Это отлично работает, когда я нажимаю на якорь, который перезагружает страницу. Появится окно подтверждения, и пользователь может отменить перезагрузку страницы. Это хорошо работает внутри элемента управления WinForms.
Теперь у меня возникают трудности с перехватом и выполнением этого события, когда пользователь закрывает приложение WinForms (например, нажав кнопку X).
Я могу получить содержимое этой функции в приложении WinForms, но независимо от того, что я пытался, я не смог получить содержимое строки, возвращаемой этой функцией, чтобы впоследствии использовать ее для подделки MessageBox, когда пользователь пытается закрыть приложение:
webBrowser1.Navigated += (sender, e) =>
{
webBrowser1.Document.Window.Load += (s, ee) =>
{
// In order to get the IHTMLWindow2 interface I have referenced
// the Microsoft HTML Object Library (MSHTML) COM control
var window = (IHTMLWindow2)webBrowser1.Document.Window.DomWindow;
// the bu variable contains the script of the onbeforeunload event
var bu = window.onbeforeunload();
// How to get the string that is returned by this function
// so that I can subscribe to the Close event of the WinForms application
// and show a fake MessageBox with the same text?
};
};
webBrowser1.Navigate("file:///c:/index.htm");
Я попробовал метод window.execScript
ни к чему доступны:
// returns null
var script = string.Format("({0})();", bu);
var result = window.execScript(script, "javascript");
Я также попытался следующие, но он также возвращается нуль:
var result = window.execScript("(function() { return 'foo'; })();", "javascript");
В качестве окончательного варианта я мог бы использовать сторонний анализатор javascript, которому я могу накормить тело этой функции, и он выполнит его и предоставит мне возвращаемое значение, но это действительно будет последним средством. Я был бы рад, если бы был более естественный способ сделать это, используя библиотеку Microsoft MSHTML.
UPDATE:
Это сейчас решается благодаря excellent answer, что @Hans при условии. По какой-то причине я не мог заставить свое решение работать на моей тестовой машине (Win7 x64, .NET 4.0 Client Profile, IE9, en-US locale), и я всегда получал hr = -2147024809
после вызова IDispatch.Invoke
. Так что я изменил подпись IDispatch P/Invoke следующей (этой подписи не требует ссылки на c:\windows\system32\stdole2.tlb
быть добавлены к проекту):
using System;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("00020400-0000-0000-C000-000000000046")]
public interface IDispatch
{
[PreserveSig]
int GetTypeInfoCount(out int Count);
[PreserveSig]
int GetTypeInfo(
[MarshalAs(UnmanagedType.U4)] int iTInfo,
[MarshalAs(UnmanagedType.U4)] int lcid,
out ITypeInfo typeInfo
);
[PreserveSig]
int GetIDsOfNames(
ref Guid riid,
[MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr)] string[] rgsNames,
int cNames,
int lcid,
[MarshalAs(UnmanagedType.LPArray)] int[] rgDispId
);
[PreserveSig]
int Invoke(
int dispIdMember,
ref Guid riid,
uint lcid,
ushort wFlags,
ref System.Runtime.InteropServices.ComTypes.DISPPARAMS pDispParams,
out object pVarResult,
ref System.Runtime.InteropServices.ComTypes.EXCEPINFO pExcepInfo,
IntPtr[] pArgErr
);
}
, а затем я подписался на заключительное мероприятие Форма и был в состоянии принести сообщение, возвращенное onbeforeunload
события и предложит пользователю:
protected override void OnFormClosing(FormClosingEventArgs e)
{
var window = (IHTMLWindow2)webBrowser1.Document.Window.DomWindow;
var args = new System.Runtime.InteropServices.ComTypes.DISPPARAMS();
var result = new object();
var except = new System.Runtime.InteropServices.ComTypes.EXCEPINFO();
var idisp = window.onbeforeunload as IDispatch;
if (idisp != null)
{
var iid = Guid.Empty;
var lcid = (uint)CultureInfo.CurrentCulture.LCID;
int hr = idisp.Invoke(0, ref iid, lcid, 1, ref args, out result, ref except, null);
if (hr == 0)
{
var msgBox = MessageBox.Show(
this,
(string)result,
"Confirm",
MessageBoxButtons.OKCancel
);
e.Cancel = msgBox == DialogResult.Cancel;
}
}
base.OnFormClosing(e);
}
Никогда не полагайтесь на 'window.onbeforeunload()', они никогда не будут работать в браузере Opera. Просто подсказка. –
@ sh4nx0r, меня не волнует Opera. Я использую элемент управления WebBrowser, размещенный в приложении WinForms. Это подразумевает использование Internet Explorer. –
Очевидно, что «execScript» всегда возвращает null: S Полезно знать. Все, что я могу думать, это то, что вы катите свой собственный очень простой синтаксический анализатор, который разбивает результат «bu» на одинарные кавычки и захватывает первый элемент в сохраненном массиве строк; как я сказал очень очень простой;) –