WebBrowser の「戻る」「進む」の履歴を取得する

WebBrowser の GoBack メソッドや GoForward メソッドを呼び出すと過去に見たページに移動します。この「過去に見たページ」の情報はトラベルログとして保存されています。
トラベルログの取得方法を調べたのでメモ。

COM のインタフェース等を定義します

[ComImport]
[Guid("6d5140c1-7436-11ce-8034-00aa006009fa")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IServiceProvider
{
    int QueryService(ref Guid guidService, ref Guid riid, [MarshalAs(UnmanagedType.Interface)]out object ppvObject);
}

[ComImport]
[Guid("7EBFDD87-AD18-11d3-A4C5-00C04F72D6B8")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface ITravelLogEntry
{
    int GetTitle(ref StringBuilder ppszTitle);
    int GetURL(ref StringBuilder ppszUrl);
}

[ComImport]
[Guid("7EBFDD85-AD18-11d3-A4C5-00C04F72D6B8")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IEnumTravelLogEntry
{
    int Next([MarshalAs(UnmanagedType.U4)]int cElt, [MarshalAs(UnmanagedType.Interface)]ref ITravelLogEntry rgElt, [MarshalAs(UnmanagedType.U4)]ref int pcEltFetched);
    int Skip([MarshalAs(UnmanagedType.U4)]int cElt);
    int Reset();
    int Clone([MarshalAs(UnmanagedType.Interface)]ref IEnumTravelLogEntry ppenum);
}

[ComImport]
[Guid("7EBFDD80-AD18-11d3-A4C5-00C04F72D6B8")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface ITravelLogStg
{
    int CreateEntry(string pszUrl, string pszTitle, ITravelLogEntry ptleRelativeTo, bool fPrepend, ref ITravelLogEntry pptle);
    int TravelTo(ITravelLogEntry ptle);
    int EnumEntries(TLENUMF ptle, ref IEnumTravelLogEntry ppenum);
    int FindEntries(TLENUMF ptle, string pszUrl, ref IEnumTravelLogEntry ppenum);
    int GetCount(TLENUMF flags, ref int pcEntries);
    int RemoveEntry([MarshalAs(UnmanagedType.Interface)]ITravelLogEntry ptle);
    int GetRelativeEntry(int iOffset, out ITravelLogEntry ptle);
}

[Flags]
internal enum TLENUMF : int
{
    TLEF_RELATIVE_INCLUDE_CURRENT = 0x0001,
    TLEF_RELATIVE_BACK = 0x0010,
    TLEF_RELATIVE_FORE = 0x0020,
    TLEF_INCLUDE_UNINVOKEABLE = 0x0040,
    TLEF_ABSOLUTE = 0x0031,
}

定数も定義しておきます

private const int S_OK = unchecked((int)0x00000000);
private const int S_FALSE = unchecked((int)0x00000001);
private static Guid SID_STravelLogCursor = new Guid("7EBFDD80-AD18-11d3-A4C5-00C04F72D6B8");
private static Guid IID_ITravelLogStg = new Guid("7EBFDD80-AD18-11d3-A4C5-00C04F72D6B8");

「戻る」エントリを取得します

// WinForms の WebBrowser から IServiceProvider を取得
IServiceProvider serviceProvider = webBrowser.ActiveXInstance as IServiceProvider;

object ppvObject = new object();
try
{
    // ITravelLogStg を取得
    serviceProvider.QueryService(ref SID_STravelLogCursor, ref IID_ITravelLogStg, out ppvObject);
    ITravelLogStg travelLogStg = ppvObject as ITravelLogStg;

    // IEnumTravelLogEntry を取得
    IEnumTravelLogEntry enumTravelLogEntry = null;
    try
    {
        // 現在のページよりも前に見たページの情報を取得する
        travelLogStg.EnumEntries(TLENUMF.TLEF_RELATIVE_BACK, ref enumTravelLogEntry);

        // トラベルログのエントリを9件取得する
        for (int i = 0; i < 9; i++)
        {
            ITravelLogEntry regElt = null;
            try
            {
                // トラベルログの次のエントリを取得
                int pcEltFetched = 0;
                int result = enumTravelLogEntry.Next(1, ref regElt, ref pcEltFetched);

                if (result == S_FALSE)
                {
                    break;
                }
                else if (regElt != null)
                {
                    // エントリのタイトルを取得
                    StringBuilder title = new StringBuilder();
                    regElt.GetTitle(ref title);

                    // タイトルを出力
                    Console.WriteLine(title);
                }
            }
            finally
            {
                if (regElt != null)
                {
                    Marshal.ReleaseComObject(regElt);
                }
            }
        }
    }
    finally
    {
        Marshal.ReleaseComObject(enumTravelLogEntry);
    }
}
finally
{
    Marshal.ReleaseComObject(ppvObject);
}

「進む」エントリを取得します

「戻る」の時とほぼ同じです。

// WinForms の WebBrowser から IServiceProvider を取得
IServiceProvider serviceProvider = browser.ActiveXInstance as IServiceProvider;

object ppvObject = new object();
try
{
    // ITravelLogStg を取得
    serviceProvider.QueryService(ref SID_STravelLogCursor, ref IID_ITravelLogStg, out ppvObject);
    ITravelLogStg travelLogStg = ppvObject as ITravelLogStg;

    // IEnumTravelLogEntry を取得
    IEnumTravelLogEntry enumTravelLogEntry = null;
    try
    {
        // 現在のページよりも後に見たページの情報を取得
        travelLogStg.EnumEntries(TLENUMF.TLEF_RELATIVE_FORE, ref enumTravelLogEntry);

        // トラベルログのエントリを9件取得する
        for (int i = 0; i < 9; i++)
        {
            ITravelLogEntry regElt = null;
            try
            {
                // トラベルログの次のエントリを取得
                int pcEltFetched = 0;
                int result = enumTravelLogEntry.Next(1, ref regElt, ref pcEltFetched);

                if (result == S_FALSE)
                {
                    break;
                }
                else if (regElt != null)
                {
                    // エントリのタイトルを取得
                    StringBuilder title = new StringBuilder();
                    regElt.GetTitle(ref title);

                    // タイトルを出力
                    Console.WriteLine(title);
                }
            }
            finally
            {
                if (regElt != null)
                {
                    Marshal.ReleaseComObject(regElt);
                }
            }
        }
    }
    finally
    {
        Marshal.ReleaseComObject(enumTravelLogEntry);
    }
}
finally
{
    Marshal.ReleaseComObject(ppvObject);
}

トラベルログを使ってページを移動します

// WinForm の WebBrowser から IServiceProvider を取得
IServiceProvider serviceProvider = webBrowser.ActiveXInstance as IServiceProvider;

object ppvObject = new object();
try
{
    // ITravelLogStg を取得
    serviceProvider.QueryService(ref SID_STravelLogCursor, ref IID_ITravelLogStg, out ppvObject);
    ITravelLogStg travelLogStg = ppvObject as ITravelLogStg;

    ITravelLogEntry entry = null;
    try
    {
        // トラベルログのエントリを取得します。
        // 現在のページよりも前のエントリを取得する場合は第1引数に負の値を指定します。
        // 現在のページよりも後のエントリを場合は第1引数に正の値を指定します。
        // 下の例では、現在ページより3つ後に見たページのエントリを取得します。
        travelLogStg.GetRelativeEntry(3, out entry);

        // ページを移動
        travelLogStg.TravelTo(entry);
    }
    finally
    {
        Marshal.ReleaseComObject(entry);
    }
}
finally
{
    Marshal.ReleaseComObject(ppvObject);
}