65.9K
CodeProject 正在变化。 阅读更多。
Home

.NET MSIE OnBeforeNavigate2 修复

starIconstarIconstarIconstarIconstarIcon

5.00/5 (14投票s)

2002年10月28日

3分钟阅读

viewsIcon

233134

downloadIcon

903

提供了一个修复程序,用于捕获 MS Internet Explorer 中否则隐藏的事件

代码重用

也就是说,如果你想在一个 .NET 应用程序中使用 Internet Explorer 控件,通常的做法是在工具箱窗口中,点击“自定义”,搜索 Microsoft Web Browser Control (shdocvw.dll),将其拖放到窗体上,然后开始探索对象模型。

这很简单,但效果不如预期。你永远不会收到一些事件的通知。

如果你对编程细节不感兴趣,只需重用源代码。不要害怕文件大小(54KB),它有点大,因为有两个互操作程序集,但真正添加的代码只有 10 行。该项目是通过以下步骤获得的:

  • 将 Web 浏览器控件拖放到名为 Form1 的窗体上
  • 添加代码片段以修复 Web 浏览器控件(见下文)
  • 添加一个文本框来管理 URL 输入
  • TextBox 上添加一个事件处理程序,以捕获键盘回车并要求 IE 在其上执行简单的 Navigate(url)

解释

在 .NET 环境中使用 IE 时存在一个问题。由于 .NET 1.0 的限制,封送无法处理复杂的变体类型,而变体类型是 DWebBrowserEvents2 调度接口触发的大多数事件的核心。

不幸的是,OnBeforeNavigate2 事件是通过此接口触发的。程序员经常使用此事件来在用户点击链接或提交表单时收到通知,从而为他们提供非常有价值的信息,包括 URL、发布的数据、标头,甚至可以根据应用程序逻辑取消导航。

现在我们知道我们不能按原样使用此事件。

但是,通过仔细观察核心 Internet Explorer 接口(通过在 shdocvw.dll 上使用 OLEView,或者查看位于 Platform SDK\Include\ExDisp.idl 中的重新分发的 IDL 接口),我们可以看到 DWebBrowserEvents 接口(较旧但向后支持)提供了诸如 OnBeforeNavigate 之类的事件(注意缺少2)。

这是 IDL 中这些接口的摘录

[
  uuid(8856F961-340A-11D0-A96B-00C04FD705A2),
  helpstring("WebBrowser Control"),
  control
]
coclass WebBrowser 
{
    [default] interface IWebBrowser2; // default COM interface
    interface IWebBrowser;

    // default event source
    [default, source] dispinterface DWebBrowserEvents2; 
    [source] dispinterface DWebBrowserEvents;
};

[
  uuid(34A715A0-6587-11D0-924A-0020AFC7AC4D),
  helpstring("Web Browser Control events interface"),
  hidden
]
dispinterface DWebBrowserEvents2 
{
    properties:
    methods:

  // note the VARIANT* everywhere
  // (the VARIANT* is the heart of the issue we have)
  [id(0x000000fa)]
  void BeforeNavigate2(
                        [in] IDispatch* pDisp, 
                        [in] VARIANT* URL, 
                        [in] VARIANT* Flags, 
                        [in] VARIANT* TargetFrameName, 
                        [in] VARIANT* PostData, 
                        [in] VARIANT* Headers, 
                        [in, out] VARIANT_BOOL* Cancel);

   ...
}

[
  uuid(EAB22AC2-30C1-11CF-A7EB-0000C05BAE0B),
  helpstring("Web Browser Control Events (old)"),
  hidden
]
dispinterface DWebBrowserEvents {
    properties:
    methods:

  [id(0x00000064)]
  void BeforeNavigate(
                        [in] BSTR URL, 
                        long Flags, 
                        BSTR TargetFrameName, 
                        VARIANT* PostData, 
                        BSTR Headers, 
                        [in, out] VARIANT_BOOL* Cancel);
  ...
}

需要注意的重要一点是,IDL 将 DWebBrowserEvents2 定义为默认事件源,而不是 DWebBrowserEvents。 因此,互操作包装器生成器 (tlbimp.exe) 将为我们提供仅反映这一点的封送代码,即 AxInterop.SHDocVw.dll(ActiveX 层)和 Interop.SHDocVw.dll (shdocvw.dll 包装器)。 因此,如果你键入 axWebBrowser1. (注意点),那么智能感知将向你显示此接口的方法,而不是 DWebBrowserEvents 的方法。 强制转换在这里没有帮助:编译器可以正常工作,但它会在运行时失败。 看起来我们有点卡住了。

为了继续,我们实际上将要求互操作封送拆收器在运行时生成 DWebBrowserEvents 接口的包装器。 让我们展示一些代码

/// <summary>
/// Summary description for Form1.
/// </summary>
public class Form1 : System.Windows.Forms.Form
{
    private AxSHDocVw.AxWebBrowser axWebBrowser1;
    private SHDocVw.WebBrowserClass ie_events;
    private System.Windows.Forms.TextBox textBox1;

    public Form1()
    {
        //
        // Required for Windows Form Designer support
        //
        InitializeComponent();

        // -- begin code snippet --

        ie_events = (SHDocVw.WebBrowserClass) 
                    Marshal.CreateWrapperOfType(
                        axWebBrowser1.GetOcx(),
                        typeof(SHDocVw.WebBrowserClass)
                    );

        // -- end code snippet --

        ...
    }
}

CreateWrapperOfType 调用执行为我们创建 RCW(执行 COM 接口和方法的层)的魔术。 我们没有传递我们想要的 SHDocVw.DWebBrowserEvents 接口类型,而是传递了 SHDocVw.WebBrowserClass。 为什么? 这又是一个技巧,封送拆收器期望一个 coclass 类型来构建 RCW,而不是一个简单的接口。 WebBrowserClass 是 IDL 中声明的 coclass WebBrowser 的 .NET 名称。

生成的 RCW 存储在我们 Form 的一个成员中。 现在我们有了正确的接口来使用。 凭借 IDL COM 声明,如果我们在 ie_events 上使用智能感知,我们将看到两个接口的方法和事件。 在那里我们有 BeforeNavigate

我们完成了,让我们展示如何使用此事件来获取实际的通知。 在 .NET 中,我们只需创建一个委托,并将一个事件处理程序附加到它

public Form1()
{
    //
    // Required for Windows Form Designer support
    //
    InitializeComponent();

    // -- begin code snippet --

    ie_events = (SHDocVw.WebBrowserClass) Marshal.CreateWrapperOfType(
        axWebBrowser1.GetOcx(),
        typeof(SHDocVw.WebBrowserClass)
    );

    SHDocVw.DWebBrowserEvents_BeforeNavigateEventHandler BeforeNavigateE = 
        new SHDocVw.DWebBrowserEvents_BeforeNavigateEventHandler( 
            OnBeforeNavigate 
        );

    ie_events.BeforeNavigate += BeforeNavigateE;

    // -- end code snippet --

    ...
}

public void OnBeforeNavigate(string url, 
                             int flags, 
                             string targetFrame, 
                             ref object postData, 
                             string headers, 
                             ref bool Cancel)
{
    int c = 0; // PUT A BREAKPOINT HERE
}

一个演示应用程序

为了让屏幕上发生一些事情,我们立即要求 Web 浏览器显示 CodeProject(面部放松...)

textBox1.Text = "https://codeproject.org.cn";
OnNewUrl(null,null);

// KeyUp handler (used to trap VK_RETURN from the text box)
private void OnNewUrl(object sender, KeyEventArgs e)
{
    object o = null;

    if (e==null || e.KeyCode==Keys.Enter)
        axWebBrowser1.Navigate(textBox1.Text, ref o, ref o, 
            ref o, ref o);
}

好了。

历史

  • 2002 年 10 月 28 日 - 首次发布

许可证

本文未附加明确的许可证,但可能在文章文本或下载文件本身中包含使用条款。如有疑问,请通过下面的讨论区联系作者。

作者可能使用的许可证列表可以在此处找到。

© . All rights reserved.