我有一個WinForms應用程序,我在其中託管了一個WebBrowser控件中的網頁。如何攔截WebBrowser控件中的onbeforeunload事件?
網頁的內容如下:
<!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應用程序中獲取此函數的內容,但無論我嘗試了什麼,我都無法獲取此函數返回的字符串的內容,以便稍後可以使用它來僞造消息框,當用戶試圖關閉應用程序:
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庫有更原始的方式來做到這一點,我會很高興。
UPDATE:
現在這是解決這得益於@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
);
}
,然後我已訂閱的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瀏覽器的工作。只是一個小費。 – 2012-01-16 16:37:04
@ sh4nx0r,我不在乎Opera。我正在使用WinForms應用程序中託管的WebBrowser控件。這意味着Internet Explorer。 – 2012-01-16 16:39:17
顯然'execScript'將總是返回null:S有幫助,我知道。我所能想到的是,你可以推出自己非常基本的解析器,它將'bu'的結果用單引號分開,並抓住retured字符串數組中的第一項;就像我說的非常基本;) – 2012-01-16 17:27:50