WebSurfer's Home

トップ > Blog 1   |   ログイン
APMLフィルター

WebBrowser で HttpOnly 属性の Cookie 取得

by WebSurfer 2012年8月13日 15:35

.NET Framework の WebBrowser を利用した Windows アプリで、ドキュメントに関連付けられている HTTP Cookie を取得するには HtmlDocument.Cookie プロパティ が利用できます。

ただし、HttpOnly 属性を持つ HTTP Cookie は例外です。その理由は、The Code Project のページ Retrieve HttpOnly Session Cookie in WebBrowser に述べられていますが、IE6 以降でクロスサイトスクリプティング対応のため HttpOnly 属性が追加され、その属性を持つ HTTP Cookie にはクライアントスクリプトからアクセスできなくなっているからだそうです。

HttpOnly 属性を持つ HTTP Cookie には、例えば、ASP.NET のセッションクッキー、匿名ユーザー識別用クッキーが該当します(下の Fiddler による応答ヘッダーの画像で、名前が ASP.NET_SessionId と .ASPXANONYMOUS のもの)。

HTTP Cookies(Fiddler による応答ヘッダー)

上の画像の HTTP Cookie の内、HtmlDocument.Cookie プロパティで取得できるのは RandomNumber と DateTimeCookie のみです。この例では、HtmlDocument.Cookie プロパティから取得できる文字列は以下のようになります。

DateTimeCookie=2012/08/13 14:22:48; RandomNumber=1123940529

すべての HTTP Cookie を取得するには、WININET.dll の InternetGetCookieExA 関数 (wininet.h) を利用できます(mshtml.dll ではない点に注意)。以下のような感じです。

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;
using System.Runtime.InteropServices;

namespace WebBrowserHttpOnlyCookie
{
  public partial class Form1 : Form
  {
    public Form1()
    {
      InitializeComponent();

      textBox1.Text = 
          "http://msdntestnew/171-SendCookies.aspx";
    }

    private void button1_Click(object sender, EventArgs e)
    {
      webBrowser1.Navigate(textBox1.Text);
    }

    [DllImport("wininet.dll", CharSet = CharSet.Auto, 
      SetLastError = true)]
    static extern bool InternetGetCookieEx(
        string pchURL, 
        string pchCookieName, 
        StringBuilder pchCookieData, 
        ref uint pcchCookieData, 
        int dwFlags, 
        IntPtr lpReserved);

    const int INTERNET_COOKIE_HTTPONLY = 0x00002000;

    public static string GetCookies(string uri)
    {
      uint datasize = 1024;           
      StringBuilder cookieData = 
        new StringBuilder((int)datasize);
      bool result = InternetGetCookieEx(
                         uri,
                         null,
                         cookieData,
                         ref datasize,
                         INTERNET_COOKIE_HTTPONLY,
                         IntPtr.Zero);

      if (result && cookieData.Length > 0)
      {
        return cookieData.ToString();
      }
      else
      {
        return null;
      }
    }

    private void webBrowser1_DocumentCompleted(
      object sender, 
      WebBrowserDocumentCompletedEventArgs e)
    {
      label1.Text = webBrowser1.Document.Cookie;
      label2.Text = GetCookies(textBox1.Text);
    }
  }
}

Tags: , ,

.NET Framework

WebBrowser で iframe の中の要素を取得

by WebSurfer 2012年8月10日 21:45
注意:
iframe に表示するページは親ページと同じドメインのものでないと、以下に紹介する方法では DOM は取得できないので注意してください(クロスサイトスクリプト対策だそうです)。

.NET Framework の WebBrowser に表示したページに iframe が含まれる場合、iframe の中の 要素 (HtmlElement) を取得するにはどうしたらいいかという話です。

WebBrowser に iframe を含むページを表示

ページが単一のドキュメントで構成されている場合は、WebBrowser.Document プロパティ で取得できる HtmlDocument オブジェクト から、HtmlDocument.GetElementById メソッドなどを使って、ドキュメントを構成する要素 (HtmlElement) を取得できます。

しかしながら、ページに iframe が含まれている場合、HtmlDocument オブジェクトや、その All プロパティで取得できる HtmlElementCollection オブジェクトから iframe の中の要素を直接取得することはできません。(iframe は取得できますが、その子要素は取得できません)

iframe の中の子要素 (HtmlElement) を取得するには一工夫必要です。では、どうしたらいいでしょう?

例として、以下のように iframe を持ち、iframe の src 属性にテキストボックス 2 つとボタン 1 つを持つログインページページを指定してあるページで、iframe の中の要素 (HtmlElement) を取得して操作することを考えます。

親ページ(iframe を持つ)

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
</head>
<body>
    <h1>iframe 中の Login ページ</h1>
    <iframe id="iframe1" src="168-Login.aspx" />
</body>
</html>

ログインページ(iframe に表示)

<%@ Page Language="C#" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">

</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        ID:<input id="Text1" 
            name="Text1" 
            type="text" />
        <br />
        PW:<input id="Text2" 
            name="Text2" 
            type="text" />
        <br />
        <input id="Submit1" 
            name="Submit1" 
            type="submit" 
            value="Login" />
    </div>
    </form>
</body>
</html>

上の「親ページ」を WebBrowser に表示したとします。

このとき、以下のコードでコメントアウトしたコードのように webBrowser1.Document.All で取得できる HtmlElementCollection には iframe の子要素は含まれないので id や pw は null になってしまいます。

iframe は WebBrowser コントロール内の別のウィンドウです。HtmlDocument.Window プロパティでドキュメントに関連付けられている HtmlWindow オブジェクトを取得し、HtmlWindow.Frames プロパティで Web ページ内で定義されている各 iframe 要素への参照を取得できます。(frame 要素の場合も同様です)

以下のようにして、iframe 内の要素 (HtmlElement) を取得して、操作することができます。

// これでは取得できない
//HtmlElement id = 
//    webBrowser1.Document.All.GetElementsByName("Text1")[0];
//HtmlElement pw = 
//    webBrowser1.Document.All.GetElementsByName("Text2")[0];
//id.InnerText = "loginName";
//pw.InnerText = "password";
//HtmlElement login = 
//    webBrowser1.Document.All.GetElementsByName("Submit1")[0];
//login.InvokeMember("click");


HtmlWindow iframe = webBrowser1.Document.Window.Frames[0];
HtmlElementCollection elements = iframe.Document.All;
            
HtmlElement id = elements.GetElementsByName("Text1")[0];
HtmlElement pw = elements.GetElementsByName("Text2")[0];
id.InnerText = "loginName";
pw.InnerText = "password";

HtmlElement login = elements.GetElementsByName("Submit1")[0];
login.InvokeMember("click");

Tags: ,

.NET Framework

イベントハンドラ

by WebSurfer 2012年7月2日 22:28

C# を使った .NET Framework アプリでのイベントとハンドラの宣言、定義の方法を忘れかけていて余計な時間がかかったので、整理して、備忘録として簡単に書いておきます。

まず、イベントを宣言するには、その前にデリゲート型の宣言が必要です(EventHandler デリゲートのようにクラスライブラリに用意されている場合は不要です)。

C# のデリゲートは、C や C++ の関数ポインタと同じと考えればよいと思います。以下の例は、Object 型と EventArgs 型の引数を持つデリゲート型 ChangedEventHandler の宣言です。

public delegate void 
  ChangedEventHandler(object sender, EventArgs e);

上の構文は、メソッド宣言の構文と同様ですが、delegate キーワードによって、ChangedEventHandler がデリゲート型であることをコンパイラに通知しています。

規約により、.NET Framework のイベントデリゲートは、そのイベントの発生元(上の例では sender)と、そのイベントのデータ(上の例では e)という 2 つのパラメータを持ちます。

デリゲートを定義した後、イベントの実装は以下のように行います。細かい説明はコメントを参照してください。

public class MyClass : ArrayList
{
  // デリゲートを用いてイベントを宣言。クラスの外からは
  // 公開フィールドのように見えるが、アクセスには制限が
  // あり、実行できるのは以下の処理のみ。
  //  ・新しいデリゲートの結合
  //  ・結合されたデリゲートの削除
  public event ChangedEventHandler Changed;

  // イベントを起動するメソッド OnChanged を作成し
  // ておき、必要なタイミングで呼び出す。イベントは、イ
  // ベントを宣言したクラスの中からしか起動できない。そ
  // れゆえ、protected として作成し、派生クラスがイベン
  // トを起動できるようにする。
  protected virtual void OnChanged(EventArgs e)
  {
    // ハンドラがイベントにアタッチされてない場合 null
    // になるので、起動する前に null でない事を確認。
    if (this.Changed != null)
    {
      this.Changed(this, e);
    }
  }

  // 例えば、何かを追加したタイミングでこのイベントを発
  // 生させたいときは、以下のようにする。
  public override int Add(object value)
  {
    int i = 0;
    // 何かの処置
    OnChanged(EventArgs.Empty);
    return i;
  }
}

ここまでの宣言、定義で、MyClass.Add メソッドが起動された時、MyClass.Changed イベントが発生します。

ただし、ハンドラの定義、ハンドラのイベントへのアタッチがないので、それ以上は何も起こりません。

ハンドラの定義とイベントへのアタッチは以下の例のようにします。

public class EventListener
{
  private MyClass _myClass;

  public EventListener(MyClass myClass)
  {
    this._myClass = myClass;
    // ハンドラをイベントにアタッチ
    this._myClass.Changed += 
      new ChangedEventHandler(MyClass_Changed);
  }

  // ハンドラの定義
  private void MyClass_Changed(object sender, EventArgs e)
  {
    // 何かの処理
  }
}

Tags:

.NET Framework

About this blog

2010年5月にこのブログを立ち上げました。主に ASP.NET Web アプリ関係の記事です。

Calendar

<<  2024年4月  >>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

View posts in large calendar