首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C# 讀取 IE 快取 (Temporary Internet Files) 完整實作:WinInet API 與索引解析

     編輯:關於C語言

背景

IE 快取資料夾 (Temporary Internet Files) 不是一般檔案系統結構,Explorer 透過 index.dat 索引呈現虛擬檢視。直接用 Directory.GetFiles 只能看到 desktop.ini。必須呼叫 WinInet API 遍歷快取條目。

核心 API (wininet.dll)

函數用途
FindFirstUrlCacheEntry開始列舉,回傳枚舉控制碼
FindNextUrlCacheEntry取得下一個快取條目
FindCloseUrlCache關閉枚舉控制碼
FileTimeToSystemTime(kernel32) FILETIME 轉 SYSTEMTIME

結構定義


using System;
using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct INTERNET_CACHE_ENTRY_INFO
{
    public int dwStructSize;
    public IntPtr lpszSourceUrlName;      // 來源 URL
    public IntPtr lpszLocalFileName;      // 本地檔案路徑
    public int CacheEntryType;            // 快取類型
    public int dwUseCount;                // 使用計數
    public int dwHitRate;                 // 命中率
    public int dwSizeLow;                 // 檔案大小低 32 位
    public int dwSizeHigh;                // 檔案大小高 32 位
    public FILETIME LastModifiedTime;     // 最後修改時間
    public FILETIME ExpireTime;           // 過期時間
    public FILETIME LastAccessTime;       // 最後存取時間
    public FILETIME LastSyncTime;         // 最後同步時間
    public IntPtr lpHeaderInfo;           // 標頭資訊
    public int dwHeaderInfoSize;
    public IntPtr lpszFileExtension;      // 副檔名
    public int dwExemptDelta;
}

[StructLayout(LayoutKind.Sequential)]
public struct FILETIME
{
    public uint dwLowDateTime;
    public uint dwHighDateTime;
}

[StructLayout(LayoutKind.Sequential)]
public struct SYSTEMTIME
{
    public short wYear, wMonth, wDayOfWeek, wDay, wHour, wMinute, wSecond, wMilliseconds;
}

P/Invoke 宣告


public static class WinInet
{
    [DllImport("wininet.dll", SetLastError = true, CharSet = CharSet.Auto)]
    public static extern IntPtr FindFirstUrlCacheEntry(
        string lpszUrlSearchPattern,
        IntPtr lpFirstCacheEntryInfo,
        ref int lpdwFirstCacheEntryInfoBufferSize);

    [DllImport("wininet.dll", SetLastError = true, CharSet = CharSet.Auto)]
    public static extern bool FindNextUrlCacheEntry(
        IntPtr hEnumHandle,
        IntPtr lpNextCacheEntryInfo,
        ref int lpdwNextCacheEntryInfoBufferSize);

    [DllImport("wininet.dll")]
    public static extern bool FindCloseUrlCache(IntPtr hEnumHandle);

    [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    public static extern bool FileTimeToSystemTime(
        ref FILETIME lpFileTime,
        out SYSTEMTIME lpSystemTime);
}

完整列舉程式碼


public class IeCacheReader
{
    private const int ERROR_NO_MORE_ITEMS = 259;

    public void EnumerateCache(Action onEntry)
    {
        int bufferSize = 0;
        // 第一次呼叫取得所需緩衝區大小
        IntPtr enumHandle = WinInet.FindFirstUrlCacheEntry(null, IntPtr.Zero, ref bufferSize);
        int err = Marshal.GetLastWin32Error();
        if (err == ERROR_NO_MORE_ITEMS) return; // 快取為空

        IntPtr buffer = Marshal.AllocHGlobal(bufferSize);
        try
        {
            enumHandle = WinInet.FindFirstUrlCacheEntry(null, buffer, ref bufferSize);
            if (enumHandle == IntPtr.Zero) throw new System.ComponentModel.Win32Exception();

            while (true)
            {
                var entry = (INTERNET_CACHE_ENTRY_INFO)Marshal.PtrToStructure(buffer, typeof(INTERNET_CACHE_ENTRY_INFO));
                string url = Marshal.PtrToStringAuto(entry.lpszSourceUrlName);
                string localFile = Marshal.PtrToStringAuto(entry.lpszLocalFileName);
                long size = ((long)entry.dwSizeHigh << 32) | (uint)entry.dwSizeLow;
                DateTime lastAccess = FileTimeToDateTime(entry.LastAccessTime);

                onEntry(url, localFile, lastAccess, size);

                bufferSize = bufferSize; // 重置
                bool hasNext = WinInet.FindNextUrlCacheEntry(enumHandle, buffer, ref bufferSize);
                if (!hasNext)
                {
                    err = Marshal.GetLastWin32Error();
                    if (err == ERROR_NO_MORE_ITEMS) break;
                    if (err == 122) // ERROR_INSUFFICIENT_BUFFER
                    {
                        buffer = Marshal.ReAllocHGlobal(buffer, (IntPtr)bufferSize);
                        continue;
                    }
                    throw new System.ComponentModel.Win32Exception(err);
                }
            }
        }
        finally
        {
            WinInet.FindCloseUrlCache(enumHandle);
            Marshal.FreeHGlobal(buffer);
        }
    }

    private DateTime FileTimeToDateTime(FILETIME ft)
    {
        WinInet.FileTimeToSystemTime(ref ft, out SYSTEMTIME st);
        return new DateTime(st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
    }
}

// 使用範例
var reader = new IeCacheReader();
reader.EnumerateCache((url, file, time, size) =>
{
    Console.WriteLine($"URL: {url}\nFile: {file}\nTime: {time}\nSize: {size}\n---");
});

注意事項

  • 需參考 wininet.dll,僅限 Windows 平台。
  • IE 10+ / Edge 使用新式快取機制 (WebCacheV01.dat),此 API 可能不完整支援。
  • 現代應用建議使用 HttpClient + 自訂快取,而非依賴 IE 快取。
  • 執行需足夠權限存取使用者設定檔資料夾。

替代方案:Managed 封裝庫

NuGet 套件 WinInet.Interop 或 Microsoft.Win32.Interop 提供更完整的 managed 封裝,減少手寫 P/Invoke 錯誤。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved