.NET MSIE OnBeforeNavigate2 修复





5.00/5 (14投票s)
2002年10月28日
3分钟阅读

233134

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 日 - 首次发布
许可证
本文未附加明确的许可证,但可能在文章文本或下载文件本身中包含使用条款。如有疑问,请通过下面的讨论区联系作者。
作者可能使用的许可证列表可以在此处找到。