InternetGetCookie

.NETはCookieContrainerクラスをHttpWebRequestとHttpWebResponseにバインドすることで、比較的簡単にWebサーバとやりとりするCookieの処理を実装できるのだが、それは自身で生成するか、サーバから受け取ったCookieだけが対象であり、Webブラウザにより生成、保存されたCookieを共有して利用することはできない。しかし、この処理は、過去にサイトにログインした際に生成、保存したCookieを初回のリクエスト時にWebサーバに送る等、アプリケーションではよくつかう手だ。
InternetExploler等で設定されたCookieを取得するためには、標題のAPIが用意されているのだが、残念ながらこのメソッドはwininetのAPIであり、.NET Framework 2.0のマネジドコードでは提供されていない。従って、マネジドコードから使うにはP/Invoke経由で使う必要がある。(私がメソッドの存在に気が付いていないだけかもしれない)

[DllImport("wininet.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool InternetGetCookie(
        string lpszUrlName,
        string lpszCookieName,
        StringBuilder lpszCookieData,
        [MarshalAs(UnmanagedType.U4)]
                ref int lpdwSize
        );

//P/Invoke.NETでは以下のようにマッピングされていた
[DllImport("wininet.dll", CharSet = CharSet.Auto, SetLastError=true)]
public static extern bool InternetGetCookie(
        string lpszUrlName,
        string lpszCookieName,
        [Out] string lpszCookieData,
        [MarshalAs(UnmanagedType.U4)]
                out int lpdwSize
        );

同じ第3パラメタをStringBuilderと[Out] stringマップしているのは、実際にどちらで使っても書ける。stringでマップした場合は取得した文字列が"\0\0"でターミネートされるので、トリムする必要があるだけだ。(これって、今後のことを考えるとどちらを使うのが良いのだろう。)

P/Invokeの記述が出来たら、後はマネジドコードから使うだけ。(第3パラメタに[Out]stringを使う例は省略)

public static string RetrieveIECookies(string url)
{
    StringBuilder cookieHeader = new StringBuilder(new String(' ', 256), 256);
    int datasize = cookieHeader.Length;
    if (!InternetGetCookie(url, null, cookieHeader, ref datasize))
    {
        if (datasize < 0) return String.Empty;
        new StringBuilder(datasize); 
        InternetGetCookie(url, null, cookieHeader, ref datasize);
    }
    return cookieHeader.ToString(); 
}

このメソッドで取得したCookieを今日の最初のエントリで紹介したSetCookiesメソッドでCookieContainerにセットして、HttpWebRequestにパインドしてやれば、既に存在しているCookieを初回のGETなりPOSTなりでWebサーバに送信できる。

Uri uri = new Uri("http://hoge.com/hogehoge");
HttpWebRequest httpRequest = (HttpWebRequest)HttpWebRequest.Create(uri);
CookieContainer cookieContainer = new CookieContainer();
cookieContainer.SetCookies(uri, RetrieveIECookies(uri.AbsoluteUri));
httpRequest.CookieContainer = cookieContainer;