【发布时间】:2012-02-11 13:48:15
【问题描述】:
我有一个 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 提供的excellent answer,这个问题现在得到了解决。由于某种原因,我无法让他的解决方案在我的测试机器(Win7 x64、.NET 4.0 客户端配置文件、IE9、en-US 语言环境)上运行,并且在IDispatch.Invoke 调用之后我总是得到hr = -2147024809。所以我将 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
);
}
然后我订阅了 Form 的 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);
}
【问题讨论】:
-
永远不要依赖
window.onbeforeunload(),它们永远不会在 Opera 浏览器下工作。只是一个提示。 -
@sh4nx0r,我不关心 Opera。我正在使用托管在 WinForms 应用程序中的 WebBrowser 控件。这意味着 Internet Explorer。
-
显然 'execScript' 将始终返回 null :S 我知道很有帮助。我能想到的是,您使用自己的非常基本的解析器,用单引号将 'bu' 的结果拆分并获取返回的字符串数组中的第一项;就像我说的非常非常基本;)
-
@KevRitchie,不,我会never roll my own parser。那将是疯狂的。正如我所说,如果没有本机解决方案,我只会使用第三方 javascript 解析器作为最后的手段。
-
@DarinDimitrov 好文章。可惜没有原生支持:(
标签: c# winforms browser onbeforeunload