如何拦截WebBrowser控件onbeforeunload事件? [英] How to intercept the onbeforeunload event in a WebBrowser control?

查看:512
本文介绍了如何拦截WebBrowser控件onbeforeunload事件?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在我所主持的内部网页中的WinForms应用程序web浏览器控制。

网页的内容如下:

 <!DOCTYPE HTML>
< HTML LANG =ENDIR =升>
< HEAD>
  <标题> onbeforeunload测试< /标题>
  <间的charset =UTF-8>
< /头>
<身体GT;&所述; A HREF =#的onclick =window.location.reload();>试验&下; / A><脚本类型=文/ JavaScript的>
    window.onbeforeunload =功能(){
        返回'你确定要离开这个页面吗?;
    };
< / SCRIPT>
< /身体GT;
< / HTML>

正如你可以看到我已经订阅了<一个href=\"http://msdn.microsoft.com/en-us/library/ms536907%28v=vs.85%29.aspx\"><$c$c>onbeforeunload事件这使得导航离开该页面之前,显示一个确认对话框。当我点击该重新加载页面锚这工作得很好。在确认框示出,用户可以取消在网页的重新加载。这个托管的WinForms控件内正常工作。

现在,我有被拦截和执行本次活动的困难,当用户关闭WinForms应用程序(通过点击例如X按钮)。

我能够在WinForms应用程序获取此功能的内容,但不管是什么我想我是不是能够得到字符串的内容,这个函数返回,这样我以后可以用它来伪造一个MessageBox当用户试图关闭该应用程序:

  webBrowser1.Navigated + =(发件人,E)=&GT;
{
    webBrowser1.Document.Window.Load + =(S,EE)=&GT;
    {
        //为了得到IHTMLWindow2界面我已经引用
        // Microsoft HTML对象库(MSHTML)COM控件
        VAR窗口=(IHTMLWindow2)webBrowser1.Document.Window.DomWindow;        //卜变量包含onbeforeunload事件的脚本
        变种BU = window.onbeforeunload();        //如何获得由该函数返回的字符串
        //这样我可以订阅WinForms应用程序的关闭事件
        //并显示具有相同的文本假的MessageBox?
    };
};
WebBrowser1.Navigate时(文件:/// C:将/index.htm);

我曾尝试<一个href=\"http://msdn.microsoft.com/en-us/library/aa741364%28v=vs.85%29.aspx\"><$c$c>window.execScript方法没有可用的:

  //返回空
VAR脚本=的String.Format(({0})();,BU);
VAR的结果= window.execScript(剧本的javascript);

我也试过以下,但它也返回null:

  VAR的结果= window.execScript((函数(){返回'富';})();,JavaScript的);

作为最后的手段,我可​​以使用第三方的JavaScript解析器来,我可以养活这个函数体,它会执行它,并给我的返回值但真的是最后的手段。我会很高兴,如果有做到这一点使用微软的MSHTML库更原生的方式。


更新:

这是目前解决得益于出色答卷的那@Hans提供。出于某种原因,我不能让我的测试机上他的解决方案的工作(Win7的X64,.NET 4.0客户端配置文件,IE9,EN-US语言),我总是让 HR = -2147024809 IDispatch.Invoke 调用之后。所以,我已经修改了IDispatch的P / Invoke签名如下(此签名不要求 C参:\\ WINDOWS \\ SYSTEM32 \\ STDOLE2.TLB 要添加到项目):

 使用系统;
使用System.Runtime.InteropServices;
使用System.Runtime.InteropServices.ComTypes;[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)
[的Guid(00020400-0000-0000-C000-000000000046)]
公共接口的IDispatch
{
    [preserveSig]
    INT GetTypeInfoCount(出诠释计数);
    [preserveSig]
    INT GetTypeInfo的(
        [的MarshalAs(UnmanagedType.U4)INT iTInfo,
        [的MarshalAs(UnmanagedType.U4)INT LCID,
        出的ITypeInfo所属类别
    );    [preserveSig]
    INT的GetIDsOfNames(
        裁判的Guid RIID,
        [的MarshalAs(UnmanagedType.LPArray,ArraySubType = UnmanagedType.LPWStr)的String [] rgsNames,
        INT的CNAME,
        INT LCID,
        [的MarshalAs(UnmanagedType.LPArray)INT [] rgDispId
    );    [preserveSig]
    INT调用(
        INT dispIdMember,
        裁判的Guid RIID,
        UINT LCID,
        USHORT wFlags,
        REF System.Runtime.InteropServices.ComTypes.DISPPARAMS pDispParams,
        out对象pVarResult,
        REF System.Runtime.InteropServices.ComTypes.EXCEPINFO pExcepInfo,
        IntPtr的[] pArgErr
    );
}

然后我已订阅形式的Closing事件,并能够获取由 onbeforeunload 事件返回的消息,并提示用户:

 保护覆盖无效OnFormClosing(FormClosingEventArgs E)
{
    VAR窗口=(IHTMLWindow2)webBrowser1.Document.Window.DomWindow;
    变参=新System.Runtime.InteropServices.ComTypes.DISPPARAMS();
    VAR的结果=新的对象();
    除了=新System.Runtime.InteropServices.ComTypes.EXCEPINFO()VAR;
    VAR idisp = window.onbeforeunload为IDispatch接口;
    如果(idisp!= NULL)
    {
        VAR IID = Guid.Empty;
        VAR LCID =(UINT)CultureInfo.CurrentCulture.LCID;
        INT HR = idisp.Invoke(0,参考IID,LCID,1,标记指定参数时,出来的结果,除了裁判,NULL);
        如果(HR == 0)
        {
            VAR MSGBOX = MessageBox.Show(
                这个,
                (串)的结果,
                确认,
                MessageBoxButtons.OKCancel
            );
            e.Cancel = MSGBOX == DialogResult.Cancel;
        }
    }
    base.OnFormClosing(E);
}


解决方案

IHtmlWindow2.onbeforeunload本身不显示该对话框。它只是返回一个字符串,该主机必须然后在消息框中使用。由于您的WinForms应用程序是主机必须使用MessageBox.Show()。调用onbeforeunload是困难的,这是一个IDispatch指针,其默认成员(DISPID 0)返回字符串。添加一个引用到 C:\\ WINDOWS \\ SYSTEM32 \\ STDOLE2.TLB 并粘贴此code:

 使用System.Runtime.InteropServices;
...
        [ComImport,InterfaceType(ComInterfaceType.InterfaceIsIUnknown)的Guid(00020400-0000-0000-C000-000000000046)]
        公共接口的IDispatch {
            INT dummy1();
            INT dummy2();
            INT dummy3();
            [preserveSig]
            INT调用(INT dispIdMember,裁判的Guid RIID,诠释LCID,INT dwFlags中,
                [输入,输出] stdole.DISPPARAMS pDispParams,
                [手续,的MarshalAs(UnmanagedType.LPArray)] [对象] pVarResult,
                [输入,输出] stdole.EXCEPINFO pExcepInfo,
                [手续,的MarshalAs(UnmanagedType.LPArray)] IntPtr的[] pArgErr);
        }

您会使用这样的:

 保护覆盖无效OnFormClosing(FormClosingEventArgs E){
        VAR窗口=(IHTMLWindow2)webBrowser1.Document.Window.DomWindow;
        变参=新stdole.DISPPARAMS();
        VAR的结果=新对象[1];
        除了=新stdole.EXCEPINFO()VAR;
        VAR idisp =(IDispatch接口)window.onbeforeunload;
        VAR IID = Guid.Empty;
        INT HR = idisp.Invoke(0,参考IID,1033,1,指定参数时,结果,除,NULL);
        如果(HR == 0){
            如果(MessageBox.Show(这一点,(串)结果[0],确认,
                MessageBoxButtons.OKCancel)== DialogResult.Cancel)e.Cancel = TRUE;
        }
        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);
}

解决方案

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:

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);
        }

You'll use it like this:

    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);                                                       
    }

这篇关于如何拦截WebBrowser控件onbeforeunload事件?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆