Ho un'applicazione WinForms in cui ho ospitato una pagina Web all'interno di un controllo WebBrowser.Come intercettare l'evento onbeforeunload in un controllo WebBrowser?
Il contenuto della pagina web è il seguente:
<!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>
Come potete vedere ho sottoscritto l'evento onbeforeunload
che permette di mostrare una finestra di conferma prima di uscire da questa pagina. Funziona bene quando clicco sull'ancoraggio che ricarica la pagina. Viene visualizzata la finestra di conferma e l'utente può annullare il ricaricamento della pagina. Funziona bene nel controllo hosted di WinForms.
Ora, ciò che sto incontrando è l'intercettazione e l'esecuzione di questo evento quando l'utente chiude l'applicazione WinForms (facendo clic sul pulsante X per esempio).
Sono in grado di recuperare il contenuto di questa funzione nell'applicazione WinForms ma non importa quello che ho provato, non sono stato in grado di ottenere il contenuto della stringa restituita da questa funzione in modo da poterla utilizzare in seguito per simulare un MessageBox quando l'utente tenta di chiudere l'applicazione:
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");
ho provato il metodo window.execScript
a non disponibile:
// returns null
var script = string.Format("({0})();", bu);
var result = window.execScript(script, "javascript");
ho anche provato quanto segue ma anche restituito nullo:
01.235.Come risorsa finale potrei usare un parser javascript di terze parti a cui posso alimentare il corpo di questa funzione e lo eseguirà e mi darà il valore di ritorno ma sarebbe davvero l'ultima risorsa. Sarei felice se esistesse un modo più nativo per farlo utilizzando la libreria MSHTML di Microsoft.
UPDATE:
Questo viene ora risolto grazie alla excellent answer che @Hans disponibile. Per qualche motivo non sono riuscito a far funzionare la sua soluzione sulla mia macchina di prova (Win7 x64, profilo client .NET 4.0, IE9, locale en-US) e ricevevo sempre hr = -2147024809
dopo la chiamata IDispatch.Invoke
. Così ho modificato la firma IDispatch P/Invoke al seguente (questa firma non richiede un riferimento a c:\windows\system32\stdole2.tlb
da aggiungere al progetto):
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
);
}
e poi ho sottoscritto l'evento di chiusura del formare ed è stato in grado di recuperare il messaggio restituito dall'evento onbeforeunload
e richiedere all'utente:
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);
}
mai fare affidamento su 'window.onbeforeunload()', non potranno mai mai lavorare in Opera Browser. Solo un consiglio. –
@ sh4nx0r, non mi interessa di Opera. Sto usando un controllo WebBrowser ospitato in un'applicazione WinForms. Ciò implica Internet Explorer. –
Apparentemente 'execScript' restituirà sempre null: S Utile lo so. Tutto quello che posso pensare è che si rotola il proprio parser molto molto basilare che divide il risultato di 'bu' da virgolette singole e afferra il primo elemento nella serie di stringhe reture; come ho detto molto molto di base;) –