如何拦截WebBrowser控件中的onbeforeunload事件?
我有一个 WinForms 应用程序,在其中托管了一个网页 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");
我也尝试了以下方法,但它也返回 null:
var result = window.execScript("(function() { return 'foo'; })();", "javascript");
作为最后的手段,我可以使用第三方 javascript 解析器输入这个函数的主体,它将执行它并给我返回值,但这确实是最后的手段。如果有一种更原生的方法可以使用 Microsoft 的 MSHTML 库来执行此操作,我会很高兴。
更新:
由于@Hans 提供的优秀答案,现在这个问题已经解决了。由于某种原因,我无法让他的解决方案在我的测试机上运行(Win7 x64、.NET 4.0 Client Profile、IE9、en-US 区域设置),并且我总是在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
);
}
然后我已经订阅了表单的 Closing 事件,并且能够获取 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);
}
I have a WinForms application in which I have hosted a web page inside a WebBrowser control.
The contents of the web page is the following:
<!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>
As you can see I have subscribed to the onbeforeunload
event which allows to show a confirmation dialog before navigating away from this page. This works fine when I click on the anchor that reloads the page. The confirmation box is shown and the user can cancel the reload of the page. This works fine inside the WinForms hosted control.
Now, what I am having difficulties with is intercepting and executing this event when the user closes the WinForms application (by clicking on the X button for example).
I am able to fetch the contents of this function in the WinForms application but no matter what I tried I wasn't able to get the contents of the string that this function returns so that I can use it later to fake a MessageBox when the user attempts to close the application:
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");
I have tried the window.execScript
method to no available:
// returns null
var script = string.Format("({0})();", bu);
var result = window.execScript(script, "javascript");
I have also tried the following but it also returned null:
var result = window.execScript("(function() { return 'foo'; })();", "javascript");
As a final resort I could use a third party javascript parser to which I can feed the body of this function and it will execute it and give me the return value but that would really be a last resort. I would be happy if there was a more native way to do this using Microsoft's MSHTML library.
UPDATE:
This is now solved thanks to the excellent answer that @Hans provided. For some reason I couldn't make his solution work on my test machine (Win7 x64, .NET 4.0 Client Profile, IE9, en-US locale) and I was always getting hr = -2147024809
after the IDispatch.Invoke
call. So I have modified the IDispatch P/Invoke signature to the following (this signature doesn't require a reference to c:\windows\system32\stdole2.tlb
to be added to the project):
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
);
}
and then I have subscribed to the Closing event of the Form and was able to fetch the message returned by the onbeforeunload
event and prompt the user:
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);
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
IHtmlWindow2.onbeforeunload 本身不显示对话框。它仅返回一个字符串,然后主机必须在消息框中使用该字符串。由于您的 Winforms 应用程序是主机,因此它必须使用 MessageBox.Show()。调用 onbeforeunload 很困难,它是一个 IDispatch 指针,其默认成员(dispid 0)返回字符串。添加对
c:\windows\system32\stdole2.tlb
的引用并粘贴此代码:您将像这样使用它:
IHtmlWindow2.onbeforeunload does not itself display the dialog. It merely returns a string which the host must then use in a message box. Since your Winforms app is the host it must use MessageBox.Show(). Calling onbeforeunload is difficult, it is an IDispatch pointer whose default member (dispid 0) returns the string. Add a reference to
c:\windows\system32\stdole2.tlb
and paste this code:You'll use it like this:
我刚刚处理了类似的问题。这是一个迟到的答案,但希望它可以帮助某人。该解决方案基于这篇 MSKB 文章。它也适用于网页处理 onbeforeunload 事件通过
attachEvent
或addEventListener
实现。上面的代码适用于 WinForms 版本的
WebBrowser
控件。对于WPF版本,应首先通过反射获取ActiveXInstance
:I just dealt with a similar problem. This is a late answer, but hopefully it may help someone. The solution is based on this MSKB article. It also works for cases when the web page handles onbeforeunload event via
attachEvent
oraddEventListener
.The above code is for WinForms version of
WebBrowser
control. For WPF version,ActiveXInstance
should be first obtained via reflection:如果您愿意提前执行事件代码(可以是任何内容),则以下内容会在您的
Window.Load
中为我捕获字符串;If your happy to prematurely execute the event code (which could be anything) the following captures the string for me in your
Window.Load
;