• 强制所有网页链接在同一页面打开或者在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)
            {
                
    
            }
  • 相关阅读:
    个人推荐网上商店
    vs 安装程序制作
    this linker was not configured to use sysroots和C compiler cannot create executables的解决办法
    将asihttprequest编译后的目标文件打包
    cygwin下的gcc4.7.1编译心得
    给ubuntu12.04换3.4.6的内核
    boost::asio::streambuf相关的操作方法
    应用boost库serialize标准库里的map
    cygwin下gdb7.4编译
    sql server存储过程分页,支持cte
  • 原文地址:https://www.cnblogs.com/T-ARF/p/9428121.html
Copyright © 2020-2023  润新知