MiNiWebブラウザのコアテクノロジの詳細

19738 ワード

MiNiWebブラウザのコアテクノロジの詳細
 
 
目次
1、  概要
2、  目標、課題、およびソリューション
  スナップスクリプトエラー
  フィルタポップアップ
 マルチタブページまたはマルチウィンドウブラウズ機能の追加
ウィンドウがスクリプトによって閉じられている場合は、確認する必要があります.
3、  Webbrowser拡張コントロールの作成
  iwebbrowser 2インタフェースの実装
  dwebbrowserevents 2インタフェースの実装
4、  この拡張コントロールの使用
 スナップスクリプトエラー
 ポップアップ・ウィンドウをフィルタし、マルチ・ラベル・ページまたはマルチ・ウィンドウ・ブラウズ機能を追加
終了イベントの使用
 
 1、概要
        .net 2.0のsystem.windows.formsネーミングスペースにwebbrowserコントロールが追加されました.このコントロール自体は非常に役に立ちますが、場合によっては必要なイベントは提供されていません.この記事では、Webbrowserコントロールを拡張し、ポップアップウィンドウのマスク、スクリプトエラーのスナップ、新しいウィンドウのスナップ、マルチラベルブラウズウィンドウ環境に表示するなどの機能を追加する方法について説明します.       Webbrowserコントロールを拡張する場合、一部の機能は.net frameworkのヘルプファイルに書き込まれていません.「この方法は.netインフラストラクチャをサポートするために使用され、コードに直接使用することは推奨されません」というヒント情報を無視して、iwebbrowser 2の接続を実現するオブジェクトを作成し、ブラウザオブジェクトのすべての機能を使用することができます.また、dwebbrowserevents 2インタフェースを使用して、コントロールにイベントを追加します.
          iwebbrowser 2インタフェースとdwebbrowserevents 2インタフェースを理解していると仮定し、comの相互運用と関連するインタフェースの知識も理解する必要があります.
2、目標、課題、解決策
このコンポーネントが達成する目標は、次のとおりです.
スクリプトエラーを簡潔にキャプチャ
 フィルタポップアップ
 マルチタブページブラウズまたはマルチウィンドウブラウズ機能の追加
 ウィンドウがスクリプトで閉じられたときに確認する必要があります
       このセクションでは、これらの目標を達成するために発生する問題と関連する解決策について簡単に説明します.次のセクションでは、より多くのコードの詳細を説明します.
スナップスクリプトエラー
       Webbrowserコントロールにはscripterrorsuppressedプロパティがあります.このプロパティをtrueに設定すると、スクリプトエラーのダイアログボックスが無効になるだけでなく、ユーザー証明書が必要なセキュリティサイトにログインするときに表示されるログインダイアログボックスも無効になります.しかし、もし私たちがまだこの機能が必要であれば、スクリプトのエラーの通知を取得したい場合、またはすべてのスクリプトのエラーの詳細を知りたい場合はどうすればいいですか?
       スクリプトエラーはhtmlwindow.errorイベントでキャプチャできます.このイベントはスクリプトにエラーが発生したときにトリガーされ、すべてのエラー詳細情報が含まれますが、難点はhtmlwindowがhtmldocumentオブジェクトを介してアクセスする必要があることです.このオブジェクトはいつでも有効ではありません.htmldocumentオブジェクトはnavigatedイベントがトリガーされたときにのみ有効です.ユーザーがf 5キーを押してブラウザをリフレッシュしている場合、申し訳ありませんが、navigatedイベントはトリガーされません.多くの試みを経て、デフォルトのwebbrowserコントロールの一部ではないdownloadcompleteイベントを使用する唯一の方法があることに気づきました.
 ソリューション:
1.       dwebbrowsereventsインタフェースの実装
2.       downloadcompleteイベントの作成
3.       downloadcompleteイベントがトリガーされるとhtmlwindow.errorイベントを購読します
4.       このerrorイベントを利用してスクリプトエラーの具体的な情報を取得します
5.       スクリプトエラーを阻止するためにhandledプロパティをtrueに設定
 フィルタポップアップ
       ポップアップウィンドウのほとんどはあまり人気がないか、適切ではありません.これらのポップアップウィンドウを遮断するには追加の情報が必要です.ユーザーがwindows xp sp 2またはwindows 2003 sp 1以降を使用する場合、newwindow 3イベントはこれらの補助情報を提供することができ、このイベントがトリガーされていない場合、newwindows 2イベントがそのイベントに代わる.newwindow 3イベントがトリガーされると、次の内容を確認できます.
l  ユーザーの操作によって新しいウィンドウが開いたかどうか
l  ユーザーが上書きキーを押しているかどうか(ctrlキー)
l  ウィンドウが閉じているためにポップアップウィンドウが表示されるかどうか
l  ウィンドウを開くurlアドレスを取得
l  もっと...
         newwindows 3イベントを使用すると、この目的が明らかになります.このイベントを使用するには、dwebbrowserevents 2インタフェースを実装する必要があります.
 ソリューション:
1.       dwebbrowserevents 2インタフェースの実装
2.       新しいイベントと新しいイベントパラメータクラスの作成
3.       このイベントを実行し、適切な情報を添付します.
4.       このイベントがトリガーされたら、今回のナビゲーションをキャンセルする必要があるかどうかを確認します.
 マルチタブページまたはマルチウィンドウブラウズ機能の追加
       マルチタグページ方式のブラウズは、ie 7など、現在ますます流行しているようです.これが新しい機能です.マルチラベル・ページの閲覧を実現する難点は、スクリプトまたはハイパーリンクが新しいウィンドウを作成するときに、対応する新しいラベル・ページまたはサブウィンドウを作成する必要があることです.また、マルチウィンドウまたはマルチラベル・ページのウィンドウ名を解析する必要があります.(例:using System; using System.Security; using System.Runtime.InteropServices; using System.Windows.Forms; using System.Security.Permissions; namespace MiniBrowser { ///<summary> /// An extended version of the <see cref="WebBrowser"/> control. ///</summary> class ExtendedWebBrowser : System.Windows.Forms.WebBrowser { private UnsafeNativeMethods.IWebBrowser2 axIWebBrowser2; ///<summary> /// This method supports the .NET Framework infrastructure and is not intended to be used directly from your code. /// Called by the control when the underlying ActiveX control is created. ///</summary> ///<param name="nativeActiveXObject"></param> [PermissionSet(SecurityAction.LinkDemand, Name = "FullTrust")] protected override void AttachInterfaces(object nativeActiveXObject) { this.axIWebBrowser2 = (UnsafeNativeMethods.IWebBrowser2)nativeActiveXObject; base.AttachInterfaces(nativeActiveXObject); } ///<summary> /// This method supports the .NET Framework infrastructure and is not intended to be used directly from your code. /// Called by the control when the underlying ActiveX control is discarded. ///</summary> [PermissionSet(SecurityAction.LinkDemand, Name = "FullTrust")] protected override void DetachInterfaces() { this.axIWebBrowser2 = null; base.DetachInterfaces(); } ///<summary> /// Returns the automation object for the web browser ///</summary> public object Application { get { return axIWebBrowser2.Application; } } System.Windows.Forms.AxHost.ConnectionPointCookie cookie; WebBrowserExtendedEvents events; ///<summary> /// This method will be called to give you a chance to create your own event sink ///</summary> [PermissionSet(SecurityAction.LinkDemand, Name = "FullTrust")] protected override void CreateSink() { // Make sure to call the base class or the normal events won't fire base.CreateSink(); events = new WebBrowserExtendedEvents(this); cookie = new AxHost.ConnectionPointCookie(this.ActiveXInstance, events, typeof(UnsafeNativeMethods.DWebBrowserEvents2)); } ///<summary> /// Detaches the event sink ///</summary> [PermissionSet(SecurityAction.LinkDemand, Name = "FullTrust")] protected override void DetachSink() { if (null != cookie) { cookie.Disconnect(); cookie = null; } } ///<summary> /// Fires when downloading of a document begins ///</summary> public event EventHandler Downloading; ///<summary> /// Raises the <see cref="Downloading"/> event ///</summary> ///<param name="e">Empty <see cref="EventArgs"/></param> ///<remarks> /// You could start an animation or a notification that downloading is starting ///</remarks> protected void OnDownloading(EventArgs e) { if (Downloading != null) Downloading(this, e); } ///<summary> /// Fires when downloading is completed ///</summary> ///<remarks> /// Here you could start monitoring for script errors. ///</remarks> public event EventHandler DownloadComplete; ///<summary> /// Raises the <see cref="DownloadComplete"/> event ///</summary> ///<param name="e">Empty <see cref="EventArgs"/></param> protected virtual void OnDownloadComplete(EventArgs e) { if (DownloadComplete != null) DownloadComplete(this, e); } …… } }
 
Webbrowserコントロールには、attachinterfaces()とdetachinterfaces()の2つの未公開インタフェースがあります.これらの方法はiwebbrowser 2インタフェースの参照を得るために使用される. 
次に、アプリケーション属性を追加できます.
  
         ///<summary>

        /// Returns the automation object for the web browser

        ///</summary>

        public object Application

        {

            get { return axIWebBrowser2.Application; }

        }


 
       このプロパティを使用して、新しいウィンドウを作成し、新しいウィンドウイベントがトリガーされたときにブラウザを新しいウィンドウにリダイレクトできます. 
dwebbrowserevents 2インタフェースの実装 
この例では、次のイベントが実装されています.
l  Newwindow 2とnewwindow 3(ポップアップウィンドウをマスクして新しいウィンドウを作成するためのもの)
l  downloadbeginとdownloadcomplete(スクリプトエラーをキャプチャするために使用)
l  beforenavigate 2(ページにナビゲートする前にナビゲートするアドレスを表示するために使用されます) 
         dwebbrowsereventsインタフェースを簡潔に実現するためには、コンポーネントにプライベートな埋め込みクラスを構築することが望ましい.これにより、必要なすべてのイベントが1つの場所にあり、簡単に検索できます.このクラスをインスタンス化すると、呼び出し元に参照を提供し、この参照を使用してメソッドを呼び出して必要なイベントをトリガーすることができます.
        コンポーネントの構築中にこれらのイベントは付属していませんが、少し遅れています.ここには2つの方法があり、それを再ロードすることができます.これらはcreatesink()とdetachsink()です.これらをすべて追加すると、私たちのコードは次のようになります(一部のコードは読みやすいように削除されています) 
/// <summary>

/// An extended version of the <see cref="WebBrowser"/> control.

/// </summary>    

public class extendedwebbrowser : system.windows.forms.webbrowser

{

  //   (MORE CODE HERE)

 

 SYSTEM.WINDOWS.FORMS.AXHOST.CONNECTIONPOINTCOOKIE COOKIE;

 WEBBROWSEREXTENDEDEVENTS EVENTS;

 

  /// <SUMMARY>

 /// THIS METHOD WILL BE CALLED TO GIVE

 /// YOU A CHANCE TO CREATE YOUR OWN EVENT SINK

 /// </SUMMARY>

 [PERMISSIONSET(SECURITYACTION.LINKDEMAND, NAME = "FULLTRUST")]

 PROTECTED OVERRIDE VOID CREATESINK()

 {

   // MAKE SURE TO CALL THE BASE CLASS OR THE NORMAL EVENTS WON'T FIRE

   BASE.CREATESINK();

   EVENTS = NEW WEBBROWSEREXTENDEDEVENTS(THIS);

   COOKIE = NEW AXHOST.CONNECTIONPOINTCOOKIE(THIS.ACTIVEXINSTANCE, 

                     EVENTS, TYPEOF(UNSAFENATIVEMETHODS.DWEBBROWSEREVENTS2));

 }

 

  /// <SUMMARY>

 /// DETACHES THE EVENT SINK

 /// </SUMMARY>

 [PERMISSIONSET(SECURITYACTION.LINKDEMAND, NAME = "FULLTRUST")]

 PROTECTED OVERRIDE VOID DETACHSINK()

  {

   IF (NULL != COOKIE)

       {

        COOKIE.DISCONNECT();

        COOKIE = NULL;

   }

 }

 

  /// <SUMMARY>

 /// FIRES WHEN DOWNLOADING OF A DOCUMENT BEGINS

 /// </SUMMARY>

 PUBLIC EVENT EVENTHANDLER DOWNLOADING;

 

  /// <SUMMARY>

 /// RAISES THE <SEE CREF="DOWNLOADING"/> EVENT

 /// </SUMMARY>

 /// <PARAM NAME="E">EMPTY <SEE CREF="EVENTARGS"/></PARAM>

 /// <REMARKS>

 /// YOU COULD START AN ANIMATION

 /// OR A NOTIFICATION THAT DOWNLOADING IS STARTING

 /// </REMARKS>

 PROTECTED VOID ONDOWNLOADING(EVENTARGS E)

  {

   IF (DOWNLOADING != NULL)

        DOWNLOADING(THIS, E);

 }

 

 //   (MORE CODE HERE)

 

  THE IMPLEMENTATION OF DWEBBROWSEREVENTS2 FOR FIRING EXTRA EVENTS
}

 
 
4、このコンポーネントを使う
       前節では、新しいコンポーネントを作成しました.では、これらの新しいイベントを使用して、できるだけ多くのブラウザの機能をマイニングします.各目標について、具体的には以下のように説明します.
 スナップスクリプトエラー
       サンプル・プログラムでは、エラーが発生したリストとエラーの具体的な内容が簡単に表示されるツール・ウィンドウがあります.単一のインスタンスクラスは、スクリプトエラーの情報を把握し、この情報が変更されたときにすべてのサブスクライバに通知します.これらのスクリプトエラーをキャプチャするために、browsercontrolはまずdownloadcompleteイベントに添付され、次にhtmlwindow.errorイベントにサブスクリプションされます.このイベントがトリガーされると、このスクリプトエラーを登録し、handledプロパティをtrueに設定します.
    
public partial class browsercontrol : usercontrol
    {

     public browsercontrol()

      {

       initializecomponent();

       _browser = new extendedwebbrowser();

       _browser.dock = dockstyle.fill;

 

       // here's the new downloadcomplete event

       _browser.downloadcomplete += 

         new eventhandler(_browser_downloadcomplete);

       // some more code here 

       this.containerpanel.controls.add(_browser);

       // some more code here 

     }

 

 

     void _browser_downloadcomplete(object sender, eventargs e)

      {

       // check wheter the document is available (it should be)

       if (this.webbrowser.document != null)

         // subscribe to the error event

         this.webbrowser.document.window.error += 

           new htmlelementerroreventhandler(window_error);

     }

 

     void window_error(object sender, htmlelementerroreventargs e)

      {

       // we got a script error, record it

       scripterrormanager.instance.registerscripterror(e.url, 

                                e.description, e.linenumber);

       // let the browser know we handled this error.

       e.handled = true;

     }

 

     // some more code here

   }


 ポップアップ・ウィンドウをフィルタし、マルチ・ラベル・ページまたはマルチ・ウィンドウ・ブラウズ機能を追加
        スナップポップアップウィンドウは、ユーザーによって構成できる必要があります.例示の目的のために、私は4つのレベルを実現しました.新しいウィンドウをすべて遮蔽するまで、ウィンドウを遮蔽しません.次のコードはbrowsercontorlの一部であり、これをどのように実現するかを示します.新しいウィンドウが承諾されると、サンプル・プログラムは、新しいウィンドウにウィンドウ名を実装する方法を示します.  
void _browser_startnewwindow(object sender, 

 browserextendednavigatingeventargs e)

  {

   // here we do the pop-up blocker work

 

   // note that in windows 2000 or lower this event will fire, but the

   // event arguments will not contain any useful information

   // for blocking pop-ups.

 

   // there are 4 filter levels.

   // none: allow all pop-ups

   // low: allow pop-ups from secure sites

   // medium: block most pop-ups

   // high: block all pop-ups (use ctrl to override)

 

   // we need the instance of the main form,

   // because this holds the instance

   // to the windowmanager.

   mainform mf = getmainformfromcontrol(sender as control);

   if (mf == null)

     return;

 

   // allow a popup when there is no information

   // available or when the ctrl key is pressed

   bool allowpopup = (e.navigationcontext == urlcontext.none) 

        || ((e.navigationcontext & 

        urlcontext.overridekey) == urlcontext.overridekey);

 

   if (!allowpopup)

    {

     // give none, low & medium still a chance.

     switch (settingshelper.current.filterlevel)

      {

       case popupblockerfilterlevel.none:

         allowpopup = true;

         break;

       case popupblockerfilterlevel.low:

         // see if this is a secure site

         if (this.webbrowser.encryptionlevel != 

                  webbrowserencryptionlevel.insecure)

           allowpopup = true;

         else

           // not a secure site, handle this like the medium filter

           goto case popupblockerfilterlevel.medium;

         break;

       case popupblockerfilterlevel.medium:

         // this is the most dificult one.

         // only when the user first inited

         // and the new window is user inited

         if ((e.navigationcontext & urlcontext.userfirstinited) 

              == urlcontext.userfirstinited && 

              (e.navigationcontext & urlcontext.userinited) 

              == urlcontext.userinited)

           allowpopup = true;

         break;

     }

   }

 

   if (allowpopup)

    {

     // check wheter it's a html dialog box.

     // if so, allow the popup but do not open a new tab

     if (!((e.navigationcontext & 

            urlcontext.htmldialog) == urlcontext.htmldialog))

      {

       extendedwebbrowser ewb = mf.windowmanager.new(false);

       // the (in)famous application object

       e.automationobject = ewb.application;

     }

   }

   else

     // here you could notify the user that the pop-up was blocked

     e.cancel = true;

 } 

 
このイベントをstartnewwindowと呼ぶのは、コード設計規則がイベントの名前を「before」または「after」で始まることを承諾しないためである.「newwindown」イベントはこの範囲内ではありません.
 quitイベントの使用
       quitイベントがトリガーされると、正しいウィンドウまたはラベルページを見つけて閉じるだけで、「dispose」という例でいいです.