WebSurfer's Home

トップ > Blog 1   |   Login
Filter by APML

WebBrowser の拡張

by WebSurfer 1. July 2012 18:04

先の記事 SHDocVw.dll と AxSHDocVw.dll の作り方と使い方 では、ActiveX の WebBrowser コントロール (shdocvw.dll) をホストする、AxHost クラス から派生するラッパーコントロールを Visual Studio で生成し、それ使って NewWindow2 イベントを利用する例を書きました。

それと比較するために、.NET Framework の WebBrowser(これも shdocvw.dll のマネージラッパー)を拡張して同様なことを行うコードを書いてみました。

かなり面倒で、最初は COM の相互運用の知識がなかったのでお手上げ状態でした。あちこちググって調べて、動くようになるまで 3 日ぐらいかかりました。(笑)

詳しくは以下のコードとそのコメントを参照してください。参考にしたページの一覧も書いておきます。(手抜きですみません)

WebBrowser.CreateSink メソッド

Extended .NET 2.0 WebBrowser Control

COM相互運用機能の利用

COM相互運用機能の利用 - パート2

Microsoft .NET/COM の移行と相互運用性

COM ラッパー

ランタイム呼び出し可能ラッパー

COM 相互運用性 - 第 1 部 : C# クライアント チュートリアル

方法: COM ソースによって発生したイベントを処理する

.NET の WebBrowser を拡張したクラス

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Permissions;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Reflection;

namespace WebBrowserExtended
{
  public class MyWebBrowser : WebBrowser
  {
    // WebBrowser の AxtiveX への参照
    private IWebBrowser2 axIWebBrowser2;

    // WebBrowser の AxtiveX が作成されたとき呼び出される
    [PermissionSet(SecurityAction.LinkDemand, 
      Name = "FullTrust")]
    protected override void 
      AttachInterfaces(object nativeActiveXObject)
    {
      this.axIWebBrowser2 = 
        (IWebBrowser2)nativeActiveXObject;
      base.AttachInterfaces(nativeActiveXObject);
    }

    [PermissionSet(SecurityAction.LinkDemand, 
      Name = "FullTrust")]
    protected override void DetachInterfaces()
    {
      this.axIWebBrowser2 = null;
      base.DetachInterfaces();
    }

    public object Application
    {
      get
      {
        if ((this.axIWebBrowser2 == null))
        {
          throw new 
            AxHost.InvalidActiveXStateException(
              "Application",
              AxHost.ActiveXInvokeKind.PropertyGet);
        }

        // この Application プロパティは COM の
        // マネージラッパー
        return this.axIWebBrowser2.Application;
      }
    }

    public bool RegisterAsBrowser
    {
      get
      {
        if ((this.axIWebBrowser2 == null))
        {
          throw new 
            AxHost.InvalidActiveXStateException(
              "RegisterAsBrowser",
              AxHost.ActiveXInvokeKind.PropertyGet);
        }

        // この RegisterAsBrowser プロパティは
        // COM のマネージラッパー
        return this.axIWebBrowser2.RegisterAsBrowser;
      }
      set
      {
        if ((this.axIWebBrowser2 == null))
        {
          throw new 
            AxHost.InvalidActiveXStateException(
              "RegisterAsBrowser",
              AxHost.ActiveXInvokeKind.PropertySet);
        }

        // この RegisterAsBrowser プロパティは
        // COM のマネージラッパー
        this.axIWebBrowser2.RegisterAsBrowser = value;
      }
    }

    // シンクオブジェクトへの参照
    private MyWebBrowserEventSink sink;

    // HTTP 通信の cookie とは関係ないので注意
    private AxHost.ConnectionPointCookie cookie;

    // シンクをサブスクライバ・リストに追加
    [PermissionSetAttribute(SecurityAction.LinkDemand, 
      Name="FullTrust")]
    protected override void CreateSink() 
    {
      base.CreateSink();

      if ((this.axIWebBrowser2 == null)) 
      {
        throw new AxHost.InvalidActiveXStateException(
            "CreateSink",
            AxHost.ActiveXInvokeKind.MethodInvoke);
      }

      this.sink = new MyWebBrowserEventSink(this);
      this.cookie = new AxHost.ConnectionPointCookie(
                          this.axIWebBrowser2,
                          this.sink, 
                          typeof(DWebBrowserEvents2));
    }

    // シンクのサブスクライブを解除
    [PermissionSetAttribute(SecurityAction.LinkDemand, 
      Name="FullTrust")]
    protected override void DetachSink() 
    {
      if (cookie != null) 
      {
        this.cookie.Disconnect();
        this.cookie = null;
      }
      base.DetachSink();
    }

    // NewWindow2 イベントの定義
    public event NewWindow2EventHandler NewWindow2;

    // .NET 側の NewWindow2 イベントを発動するメソッド
    protected virtual void 
      OnNewWindow2(NewWindow2EventArgs e) 
    {
      if ((this.NewWindow2 != null)) 
      {
        this.NewWindow2(this, e);
      }
    }

    // コネクションポイントからの呼び出しを受け取る
    // クライアント・シンクのクラス定義
    [ClassInterface(ClassInterfaceType.None)]
    public class MyWebBrowserEventSink :
      StandardOleMarshalObject, DWebBrowserEvents2
    {
      private MyWebBrowser browser;

      public MyWebBrowserEventSink(MyWebBrowser browser)
      {
        this.browser = browser;
      }

      // COM ソースから発生したイベントから呼び出される
      // メソッド
      public void 
        NewWindow2(ref object ppDisp, ref bool cancel)
      {
        NewWindow2EventArgs e = 
          new NewWindow2EventArgs(ppDisp, cancel);

        // .NET 側の NewWindow2 イベントを発動
        this.browser.OnNewWindow2(e);

        ppDisp = e.PpDisp;
        cancel = e.Cancel;
      }
    }
  }

  // NewWindow2 イベントのハンドラのデリゲート
  public delegate void NewWindow2EventHandler(object sender, 
    NewWindow2EventArgs e);

  // NewWindow2 イベントハンドラ引数のクラス定義
  public class NewWindow2EventArgs : EventArgs
  {        
    public object PpDisp { get; set; }
    public bool Cancel { get; set; }
        
    public NewWindow2EventArgs(object ppDisp, bool cancel)
    {
      this.PpDisp = ppDisp;
      this.Cancel = cancel;
    }
  }

  // DWebBrowserEvents2 インターフェイスの NewWindow2 メ
  // ソッド、IWebBrowser2 インターフェイスの Application
  // プロパティと RegisterAsBrowser プロパティをインポー
  // ト(つまり、マネージラッパーをコンパイル時に生成)。
  // ComImport, InterfaceType, Guid 指定は必須らしい。
  [ComImport, 
  InterfaceType(ComInterfaceType.InterfaceIsIDispatch), 
  Guid("34A715A0-6587-11D0-924A-0020AFC7AC4D")]
  public interface DWebBrowserEvents2
  {
    [DispId(0xfb)]
    void NewWindow2(
      [In, Out, MarshalAs(UnmanagedType.IDispatch)]
      ref object ppDisp, 
      [In, Out, MarshalAs(UnmanagedType.VariantBool)] 
      ref bool Cancel);
  }

  [ComImport, 
  Guid("D30C1661-CDAF-11D0-8A3E-00C04FC9E26E"),
  InterfaceType(ComInterfaceType.InterfaceIsIDispatch), ]
  public interface IWebBrowser2
  {
    [DispId(200)]
    object Application
    {
      [return: MarshalAs(UnmanagedType.IDispatch)]
      get;
    }

    [DispId(0x228)]
    bool RegisterAsBrowser
    {
      get;
      set;
    }
  }
}

Form1.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace WebBrowserExtended
{
  public partial class Form1 : Form
  {
    private MyWebBrowser browser;
        
    public Form1()
    {
      browser = new MyWebBrowser();
            
      InitializeComponent();
                        
      browser.Dock = DockStyle.Fill;
      this.Controls.Add(browser);
      browser.NewWindow2 += 
        new NewWindow2EventHandler(browser_NewWindow2);
    }

    private void button1_Click(object sender, EventArgs e)
    {
      browser.Navigate(
        "http://msdntestnew/159-HyperLinkToPdf.aspx");
    }

    private void browser_NewWindow2(object sender, 
      NewWindow2EventArgs e)
    {
      Form1 frmWB = new Form1();

      // WebBrowser.AttachInterfaces メソッドは Visible
      // プロパティを true にすると呼び出される。なので、
      // ここで設定しないと RegisterAsBrowser プロパティ、
      // Application プロパティで例外がスローされてしまう。
      frmWB.Visible = true;

      frmWB.browser.RegisterAsBrowser = true;
      e.PpDisp = frmWB.browser.Application;
    }
  }
}

Tags:

.NET Framework

About this blog

2010年5月にこのブログを立ち上げました。その後 ブログ2 を追加し、ここは ASP.NET 関係のトピックス、ブログ2はそれ以外のトピックスに分けました。

Calendar

<<  December 2019  >>
MoTuWeThFrSaSu
2526272829301
2345678
9101112131415
16171819202122
23242526272829
303112345

View posts in large calendar