Dar*_*rov 8 c# browser onbeforeunload winforms
我有一个WinForms应用程序,其中我在WebBrowser控件中托管了一个Web页面.
网页的内容如下:
<!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>
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,我已经订阅了该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");
Run Code Online (Sandbox Code Playgroud)
我试过这个window.execScript
方法没有可用:
// returns null
var script = string.Format("({0})();", bu);
var result = window.execScript(script, "javascript");
Run Code Online (Sandbox Code Playgroud)
我也尝试了以下但它也返回null:
var result = window.execScript("(function() { return 'foo'; })();", "javascript");
Run Code Online (Sandbox Code Playgroud)
作为最后的手段,我可以使用第三方javascript解析器,我可以提供此函数的主体,它将执行它并给我返回值,但这将是最后的手段.如果使用Microsoft的MSHTML库有更原生的方法,我会很高兴.
更新:
由于@Hans提供的出色答案,现在解决了这个问题.出于某种原因,我无法使他的解决方案在我的测试机器上运行(Win7 x64,.NET 4.0客户端配置文件,IE9,en-US语言环境),并且我总是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
);
}
Run Code Online (Sandbox Code Playgroud)
然后我订阅了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);
}
Run Code Online (Sandbox Code Playgroud)
Han*_*ant 10
IHtmlWindow2.onbeforeunload本身不显示对话框.它只返回一个字符串,主机必须在消息框中使用该字符串.由于您的Winforms应用程序是主机,因此它必须使用MessageBox.Show().调用onbeforeunload很困难,它是一个IDispatch指针,其默认成员(dispid 0)返回字符串.添加c:\windows\system32\stdole2.tlb
对此代码的引用并粘贴:
using System.Runtime.InteropServices;
...
[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("00020400-0000-0000-C000-000000000046")]
public interface IDispatch {
int dummy1();
int dummy2();
int dummy3();
[PreserveSig]
int Invoke(int dispIdMember, ref Guid riid, int lcid, int dwFlags,
[In, Out] stdole.DISPPARAMS pDispParams,
[Out, MarshalAs(UnmanagedType.LPArray)] object[] pVarResult,
[In, Out] stdole.EXCEPINFO pExcepInfo,
[Out, MarshalAs(UnmanagedType.LPArray)] IntPtr[] pArgErr);
}
Run Code Online (Sandbox Code Playgroud)
你会像这样使用它:
protected override void OnFormClosing(FormClosingEventArgs e) {
var window = (IHTMLWindow2)webBrowser1.Document.Window.DomWindow;
var args = new stdole.DISPPARAMS();
var result = new object[1];
var except = new stdole.EXCEPINFO();
var idisp = (IDispatch)window.onbeforeunload;
var iid = Guid.Empty;
int hr = idisp.Invoke(0, ref iid, 1033, 1, args, result, except, null);
if (hr == 0) {
if (MessageBox.Show(this, (string)result[0], "Confirm",
MessageBoxButtons.OKCancel) == DialogResult.Cancel) e.Cancel = true;
}
base.OnFormClosing(e);
}
Run Code Online (Sandbox Code Playgroud)