zoukankan      html  css  js  c++  java
  • 强制所有网页链接在同一页面打开或者在TabControl中弹出新窗口

    IEwebbrowser中老生常谈的话题。

    一般的解决都是通过

        // webBrowser.Navigating += WebBrowser_Navigating; 注册转跳前事件
        private  void WebBrowser_Navigating(object sender, System.Windows.Forms.WebBrowserNavigatingEventArgs e)
            {
                webBrowser.Navigate("新的网页地址");
            }

    但是并不是特别的好用,比如网页中设置是弹出窗口来跳转网页

    下面我会将为什么不好使用,已经正确的用法

    好在是C# 4.72开源了。不用反编译了。 有些东西也好解释了开源地址

    搜索Webbrowser查看源代码,你会发现。很多功能都是由一个叫做AxWebbrowser的类是实现的。很明显,webbrowser大部分都是Com控件的包装。

    找到Navigate,看看是具体代码

     

     我们继续深挖

      private void PerformNavigateHelper(string urlString, bool newWindow, string targetFrameName, byte[] postData, string headers)
            {
                object objUrlString = (object)urlString;
                object objFlags = (object) (newWindow ? 1 : 0);
                object objTargetFrameName = (object)targetFrameName;
                object objPostData = (object)postData;
                object objHeaders = (object)headers;
                PerformNavigate2(ref objUrlString, ref objFlags, ref objTargetFrameName, ref objPostData, ref objHeaders);
            }
     
            private void PerformNavigate2(ref object URL, ref object flags, ref object targetFrameName, ref object postData, ref object headers) 
            {
                try {
                    this.AxIWebBrowser2.Navigate2(ref URL, ref flags, ref targetFrameName, ref postData, ref headers);
                }
                catch (COMException ce) {
                    if ((uint)unchecked(ce.ErrorCode) != (uint)unchecked(0x800704c7)) {
                        // "the operation was canceled by the user" - navigation failed
                        // ignore this error, IE has already alerted the user. 
                        throw;
                    }
                }
            }

    跳转都是用一个方法。

    最终实现的是一个叫做PerformNaviagate2内的AxIWebbrowser2所实现的

    继续深挖

    最后发现在一个名为UnsafeNativeMethods的类中

    这个类是用来做什么呢?

    是实现win32API和COM的。(说句心里话写桌面软件,微软心里面还是C++是亲儿子。多少懂一些C++没有错。)

      [DispId(500)] void Navigate2([In] ref object URL, [In] ref object flags,
                                [In] ref object targetFrameName, [In] ref object postData,
                                [In] ref object headers);

    嗯,看起来似乎就是普通的导航连接啊。

    到这里就是很明显了,Navigate就是负责普通的导航,如果是遇到弹出窗口等 基本不好用的

    那我们该如何正确的处理呢?

    准确的说,我们是想在网页弹出新窗口或者跳转新网页的时候,将其强制的定位到一个网页,让其不弹出新的窗口。

    所以我们重新回到了Webbrowser 了

    我们发现Webbrowser继承了WebbroweserBase

    我们来看看父类中的函数

    果不其然发现了重点

        /// <include file='docWebBrowserBase.uex' path='docs/doc[@for="WebBrowserBase.CreateSink"]/*' />
            /// <devdoc>
            ///     <para>
            /// This will be called when we are ready to start listening to events.
            /// Inheritors can override this method to hook their own connection points.
            ///     </para>
            /// </devdoc>
            protected virtual void CreateSink() {
            }
     

    百度翻译了一下

    哈,找到了我们该如何触发事件的地方了。这意思就是事件发生时,Webbrowser会做的一些事情。

    很明显子类肯定要重写这个方法。重新到Webbrowser寻找这个方法

    protected override void CreateSink() {
                object ax = this.activeXInstance;
                if (ax != null) {
                    webBrowserEvent = new WebBrowserEvent(this);
                    webBrowserEvent.AllowNavigation = AllowNavigation;
                    this.cookie = new AxHost.ConnectionPointCookie(ax, webBrowserEvent,
                            typeof(UnsafeNativeMethods.DWebBrowserEvents2));
                }
            }

    来看一下啊所有的参数都是些什么

     this.activeXInstance;
     //这是Webbrowser的父类一个参数,意思是获取基础 ActiveX WebBrowser 控件。
     
      webBrowserEvent = new WebBrowserEvent(this);    
    webBrowserEvent.AllowNavigation = AllowNavigation; //这两个都是一个实例,只不过在设置参数。 //WebBrowserEvent是什么? //他是实现了 //StandardOleMarshalObject,UnsafeNativeMethods.DWebBrowserEvents2的类 this.cookie = new AxHost.ConnectionPointCookie(ax, webBrowserEvent, typeof(UnsafeNativeMethods.DWebBrowserEvents2)); //创建给定接口类型的连接点。 //将调用实现该接口的托管代码接收器。

    到现在也说了很多 我们来理一下思路

    Navigate可以排除掉了。不是我们想要的。

    那是什么地方呢?

    我们来看看这个WebBrowserEvent类都是实现了方法

     public void BeforeNavigate2(object pDisp, ref object urlObject, ref object flags, ref object targetFrameName, ref object postData, ref object headers, ref bool cancel) {
                    Debug.Assert(parent != null, "Parent should have been set");
                    //Note: we want to allow navigation if we haven't already navigated.
                    if (AllowNavigation || !haveNavigated)
                    {
                        Debug.Assert(urlObject == null || urlObject is string, "invalid url type");
                        Debug.Assert(targetFrameName == null || targetFrameName is string, "invalid targetFrameName type");
                        Debug.Assert(headers == null || headers is string, "invalid headers type");
                        //
                        // Due to a bug in the interop code where the variant.bstr value gets set
                        // to -1 on return back to native code, if the original value was null, we
                        // have to set targetFrameName and headers to "".
                        if (targetFrameName == null) {
                            targetFrameName = "";
                        }
                        if (headers == null) {
                            headers = "";
                        }
     
                        string urlString = urlObject == null ? "" : (string)urlObject;
                        WebBrowserNavigatingEventArgs e = new WebBrowserNavigatingEventArgs(
                            new Uri(urlString), targetFrameName == null ? "" : (string)targetFrameName);
                        this.parent.OnNavigating(e);
                        cancel = e.Cancel;
                    }
                    else 
                    {
                        cancel = true;
                    }
                }
     public void NewWindow2(ref object ppDisp, ref bool cancel) {
                    CancelEventArgs e = new CancelEventArgs();
                    this.parent.OnNewWindow(e);
                    cancel = e.Cancel;
                }

    到这里大致过程就明了。

    深层的跳转,新开窗口都这里。

    我们现在只有能重写以上这两个就可以了。

    最后 我们再来整理一下全部的思路

    理解了大致的思路,我们就编写代码了。

    思路就是

    手写编写DWwebbrowserEvent2的接口,编写两个方法。

    手写WebbrowserEvent类,实现DW接口。还需要继承StandardOleMarshalObject类

    剩下就重写CreateSinK方法了。这个只需要继承Webbrowser就好了。

    为了重写定位或者跳转网页,很明显我们还需要一个类来实现webbrowser的url。

    而且还需要实现CancelEventArgs类来设置是否取消事件。

     public class WebBrowserUrl : CancelEventArgs
        {
            public string Url { get; }
    
            public string Frame { get; }
    
            public WebBrowserUrl(String url, String frame) : base()
            {
                this.Url = url;
                this.Frame = frame;
            }
    
        }
        public class NewWebBrwser : System.Windows.Forms.WebBrowser
        {
            System.Windows.Forms.AxHost.ConnectionPointCookie cookie;
            NewWebBrowserEvent events;
    
            public event EventHandler BeforeNavigate;
    
            public event EventHandler BeforeNewWindow;
    
            protected override void CreateSink()
            {
                base.CreateSink();//还是需要源
                events = new NewWebBrowserEvent(this);
                cookie = new AxHost.ConnectionPointCookie(this.ActiveXInstance, events, typeof(DWebBrowserEvents2));
            }
            protected override void DetachSink()
            {
                if (null != cookie)
                {
                    cookie.Disconnect();
                    cookie = null;
                }
                base.DetachSink();
            }
         public void OnBeforeNavigate(string url, string frame, out bool cancel)
            {
              
                WebBrowserUrl webBrowserUrl = new WebBrowserUrl(url, frame);
                BeforeNavigate?.Invoke(this, webBrowserUrl);
                cancel = webBrowserUrl.Cancel;
            }
          public void OnBeforeNewWindow(string url, out bool cancel)
            {
               
                 WebBrowserUrl webBrowserUrl = new WebBrowserUrl(url, null);
                 BeforeNewWindow?.Invoke(this, webBrowserUrl);
                 cancel = webBrowserUrl.Cancel;
    
            }
           
    
        }
        public class NewWebBrowserEvent : System.Runtime.InteropServices.StandardOleMarshalObject, DWebBrowserEvents2
        {
            private NewWebBrwser webBrowser;
    
            public NewWebBrowserEvent(NewWebBrwser newWebBrowser) => webBrowser = newWebBrowser;
    
            public void BeforeNavigate2(object pDisp, ref object urlObject, ref object flags, ref object targetFrameName, ref object postData, ref object headers, ref bool cancel) => webBrowser.OnBeforeNavigate((string)urlObject, (string)targetFrameName, out cancel);
    
            //当高于IE6时使用
            public void NewWindow3(object pDisp, ref bool cancel, ref object flags, ref object URLContext, ref object URL) => webBrowser.OnBeforeNewWindow((string)URL, out cancel);
        }
    
        //下面这些特性都是古老的COM要用的
        [System.Runtime.InteropServices.ComImport(), System.Runtime.InteropServices.Guid("34A715A0-6587-11D0-924A-0020AFC7AC4D"),
            System.Runtime.InteropServices.InterfaceTypeAttribute(System.Runtime.InteropServices.ComInterfaceType.InterfaceIsIDispatch),
            System.Runtime.InteropServices.TypeLibType(System.Runtime.InteropServices.TypeLibTypeFlags.FHidden)]
        public interface DWebBrowserEvents2
        {
            [System.Runtime.InteropServices.DispId(250)]
            void BeforeNavigate2(object pDisp, ref object urlObject, ref object flags, ref object targetFrameName, ref object postData, ref object headers, ref bool cancel);
    
    
            //当高于IE6时使用 //本来应该还有一个NewWindow2 太古老 根本用不上了
            [System.Runtime.InteropServices.DispId(273)]
            void NewWindow3([System.Runtime.InteropServices.In,System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.IDispatch)]
                    object pDisp,
                          [System.Runtime.InteropServices.In, System.Runtime.InteropServices.Out]
                    ref bool cancel,
                          [System.Runtime.InteropServices.In]
                    ref object flags,
                          [System.Runtime.InteropServices.In]
                    ref object URLContext,
                          [System.Runtime.InteropServices.In]
                    ref object URL);
        }

    使用方式

           NewWebBrwser Brwser = new NewWebBrwser();
            public Form1()
            {
                InitializeComponent();
    
                Brwser.Url = new Uri("http://www.baidu.com");
                Brwser.BeforeNewWindow += Brwser_BeforeNewWindow;
                Brwser.BeforeNavigate += Brwser_BeforeNavigate;
                this.Controls.Add(Brwser);
            }
    
    
            private void Brwser_BeforeNewWindow(object sender, EventArgs e)
            {
                WebBrowserUrl newWeb = e as WebBrowserUrl;
    
                Brwser.Navigate(newWeb.Url);
    
                newWeb.Cancel = true;//取消转跳事件
            }
    
            private void Brwser_BeforeNavigate(object sender, EventArgs e)
            {
                
    
            }
  • 相关阅读:
    jQuery 源码解析(二十四) DOM操作模块 包裹元素 详解
    jQuery 源码解析(二十三) DOM操作模块 替换元素 详解
    jQuery 源码解析(二十二) DOM操作模块 复制元素 详解
    jQuery 源码分析(二十一) DOM操作模块 删除元素 详解
    jQuery 源码分析(二十) DOM操作模块 插入元素 详解
    jQuery 源码分析(十九) DOM遍历模块详解
    python 简单工厂模式
    python 爬虫-协程 采集博客园
    vue 自定义image组件
    微信小程序 image组件坑
  • 原文地址:https://www.cnblogs.com/T-ARF/p/9428121.html
Copyright © 2011-2022 走看看