引言
關於IE緩存管理方面的資料非常少,網上雖然有部分文章介紹,也有工具下載(如搜索緩存或清除緩存等),但都不夠全面和深入。
另外,IE緩存管理主要依賴幾個index.dat文件和wininet庫,而MSDN對wininet庫的幫助非常簡單,而且沒有示例代碼。
網上能夠找到的資料大部分都是對wininet的http協議處理接口方面的介紹,對於緩存處理部分介紹的很少,加上這部分接口定義得有點晦澀,所以讓人覺得有點困繞。
本文結合自己的項目經驗,對IE的緩存機制,特別是wininet庫中緩存管理的接口使用,提出全面深入的說明。
弄懂了wininet緩存管理接口,以後寫緩存監控、緩存搜索和緩存清理等工具就非常簡單了。
2. 術語定義
無。
3. IE緩存文件
3.1. IE緩存分類
IE緩存主要分爲3大類:cookie、臨時文件(包括未過期的資源和脫機文件)、歷史記錄。
這3類緩存分別都有一個索引文件,文件名爲index.dat。
存放路徑如下(以我的電腦爲例):
緩存內容索引:C:/Documents and Settings/liyafeng/Local Settings/Temporary Internet Files/Content.IE5/index.dat
緩存cookie索引:C:/Documents and Settings/liyafeng/Cookies/index.dat
歷史記錄索引:C:/Documents and Settings/liyafeng/Local Settings/History/History.IE5/index.dat。
這3個index.dat文件格式被加密處理過,微軟沒有公開也不打算公開文件格式,這種做法被很多人指責,但是微軟依然我行我素。
通過IE的internet選項清除緩存時不能刪除index.dat文件,但是在磁盤清除臨時文件時會刪除這3個index.dat文件。
通過IE清除緩存的方法如下:
目前訪問這些索引文件的唯一方法是通過wininet庫的相關接口,這也是本文重點介紹的地方。
注意:緩存的臨時文件被文件系統做了特殊處理,只能通過完整目錄路徑訪問,否則無法看到,即使你顯示所有文件和系統文件。
3.2. IE緩存管理的流程
流程如下:
1、 啓動IE。
2、 如果不存在就創建緩存內容索引:C:/Documents and Settings/liyafeng/Local Settings/Temporary Internet Files/Content.IE5/index.dat。
3、 如果不存在就創建cookie索引:C:/Documents and Settings/liyafeng/Cookies/index.dat。
4、 如果不存在歷史記錄就創建歷史索引:C:/Documents and Settings/liyafeng/Local Settings/History/History.IE5/index.dat。
5、 調用InternetOpen初始化一個IE應用。
6、 設置回調函數,通過InternetSetStatusCallback。
7、 枚舉cookie列表。
8、 枚舉臨時文件列表。
9、 枚舉歷史記錄列表。
10、 枚舉當前用戶的訪問列表。
11、 查詢當前訪問的url是否被緩存。
12、 如果被緩存,則從緩存中讀取相應的信息,包括最後修改時間、最後訪問時間和過期時間。
13、 發送http請求。
14、 http服務器會檢查訪問的資源是否過期,如果未過期,則會返回“304 Not Modified”。
15、 根據緩存的索引信息,從本地臨時文件中提取內容。
16、 如果第11步中沒有被緩存,則直接從http服務器下載資源。
4. Wininet庫的緩存管理接口
4.1. 簡介
Wininet庫內置有簡單卻靈活的緩衝支持。從網絡接收到的任何數據都會緩存到硬盤中,然後在後續請求中被獲取。應用程序可以控制每個請求的緩存。對於來自服務器的HTTP請求,大多數收到的頭部也被緩存。當從緩存中爲HTTP請求獲取響應時,也會把緩存的頭部數據返回給調用者。這使得數據下載對於用戶是透明的,無論數據是來自緩存還是來自網絡。
接口分爲枚舉緩存、創建緩存、查詢緩存和刪除緩存,以及對緩存組的操作。
4.2. 枚舉緩存
FindFirstUrlCacheEntry 開始對緩存的枚舉
FindFirstUrlCacheEntryEx 開始對緩存的過濾型枚舉
FindNextUrlCacheEntry 返回緩存中的下一個條目
FindNextUrlCacheEntryEx 在過濾型枚舉中返回下一個緩存條目
FindCloseUrlCache 關閉指定的枚舉句柄
FindFirstUrlCacheEntry和FindNextUrlCacheEntry函數可枚舉緩存中存儲的信息。FindFirstUrlCacheEntry使用傳入的搜索模式、緩衝區及其尺寸來創建枚舉句柄,並返回第一個緩存條目。FindNextUrlCacheEntry使用FindFirstUrlCacheEntry創建的句柄,一個緩衝區及其尺寸,返回下一個緩存條目。
這兩個函數都在緩衝區中存入一個INTERNET_CACHE_ENTRY_INFO條目。對於各個條目,結構體的大小是不同的。如果傳入的緩衝區尺寸不夠大,則函數調用失敗,GetLastError返回ERROR_INSUFFICIENT_BUFFER。此時緩衝區尺寸參數值會指示緩存條目所需的緩衝區尺寸,應該分配一個這麼大的緩衝區,然後重新調用函數。
INTERNET_CACHE_ENTRY_INFO結構體包含結構體尺寸、緩存信息的URL、本地文件名、緩存條目類型、使用計數、命中率、尺寸、最後修改時間、失效時間、最後訪問時間、最後同步時間、頭部信息、頭部信息尺寸和文件擴展名信息。
FindFirstUrlCacheEntry函數要求傳入搜索模式和用於存儲INTERNET_CACHE_ENTRY_INFO結構體的緩衝區及其尺寸。當前僅實現了默認搜索模式,它返回所有緩存條目。
緩存枚舉完成後,應該用FindCloseUrlCache關閉緩存枚舉句柄。
注意:FindNextUrlCacheEntry返回FALSE時,有兩種可能。一種是INTERNET_CACHE_ENTRY_INFO條目分配的緩衝區不夠,另一種可能是枚舉結束。
關於緩存區不夠如何處理,還有嵌入式指針問題,在後面查詢緩存中會給出示例代碼。
4.3. 查詢緩存條目信息
GetUrlCacheEntryInfo 獲取某緩存條目的信息
GetUrlCacheEntryInfoEx 轉換任何會被HttpSendRequest應用於離線模式的緩存重定向,然後搜索指定的URL。
GetUrlCacheEntryInfo函數獲取指定URL的INTERNET_CACHE_ENTRY_INFO結構體。結構體包含結構體尺寸、緩存信息的URL、本地文件名、緩存條目類型、使用計數、命中率、尺寸、最後修改時間、失效時間、最後訪問時間、最後同步時間、頭部信息、頭部信息尺寸和文件擴展名信息。
GetUrlCacheEntryInfo接受一個URL、一個用於保存INTERNET_CACHE_ENTRY_INFO結構體的緩衝區及其尺寸。如果找到了給定的URL,其信息會複製到緩衝區中。否則,函數調用失敗,GetLastError返回ERROR_FILE_NOT_FOUND。如果緩衝區大小不足以保存緩存條目信息,則函數調用失敗,GetLastError返回ERROR_INSUFFICIENT_BUFFER。此時緩衝區尺寸參數會指示所需要的緩衝區尺寸。
GetUrlCacheEntry不會做URL解析,所以對於含有錨定(#)的URL,即使請求的資源在緩存中,函數也會找不到。比如說,如果傳入 http://example.com/example.htm#sample,即使它在緩存中,函數也會返回ERROR_FILE_NOT_FOUND。
typedef struct _INTERNET_CACHE_ENTRY_INFOA {
DWORD dwStructSize; // version of cache system.
LPSTR lpszSourceUrlName; // embedded pointer to the URL name string.
LPSTR lpszLocalFileName; // embedded pointer to the local file name.
DWORD CacheEntryType; // cache type bit mask.
DWORD dwUseCount; // current users count of the cache entry.
DWORD dwHitRate; // num of times the cache entry was retrieved.
DWORD dwSizeLow; // low DWORD of the file size.
DWORD dwSizeHigh; // high DWORD of the file size.
FILETIME LastModifiedTime; // last modified time of the file in GMT format.
FILETIME ExpireTime; // expire time of the file in GMT format
FILETIME LastAccessTime; // last accessed time in GMT format
FILETIME LastSyncTime; // last time the URL was synchronized
// with the source
LPSTR lpHeaderInfo; // embedded pointer to the header info.
DWORD dwHeaderInfoSize; // size of the above header.
LPSTR lpszFileExtension; // File extension used to retrive the urldata as a file.
union { // Exemption delta from last access time.
DWORD dwReserved;
DWORD dwExemptDelta;
}; // Exemption delta from last access
} INTERNET_CACHE_ENTRY_INFOA, * LPINTERNET_CACHE_ENTRY_INFOA;
注意:embedded pointer即嵌入式指針,是一個比較晦澀的點,真實意義是在結構體後分配一塊連續內存,然後讓這個指針指向它,就好象是嵌入在結構體內,因此得名。
不知道是否解釋清楚,後面以示例代碼來說明,呵呵。
示例代碼:
INTERNET_CACHE_ENTRY_INFOA *lpCacheEntryInfo = new INTERNET_CACHE_ENTRY_INFOA;
DWORD cbCacheEntryInfo = sizeof(INTERNET_CACHE_ENTRY_INFOA);
BOOL fOk = GetUrlCacheEntryInfoA(lpszUrl,lpCacheEntryInfo,&cbCacheEntryInfo);
if( !fOk && cbCacheEntryInfo > sizeof(INTERNET_CACHE_ENTRY_INFOA) )
{
lpCacheEntryInfo = (LPINTERNET_CACHE_ENTRY_INFOA) new char[cbCacheEntryInfo];//強制轉換,保證指針可以指向結構體內部。
fOk = GetUrlCacheEntryInfoA(lpszUrl,lpCacheEntryInfo,&cbCacheEntryInfo);
PR_DEBUG("do again:fOk:%d,cbCacheEntryInfo:%d",fOk,cbCacheEntryInfo);
}
4.4. 創建緩存條目
CreateUrlCacheEntry 分配請求的緩存存儲器,創建本地文件名用於保存對應源名稱的緩存條目
CommitUrlCacheEntry 緩存存儲器中某特定文件中的數據,將它與給定的URL關聯起來
使用CreateUrlCacheEntry和CommitUrlCacheEntry可以創建緩存條目。
CreateUrlCacheEntry接受URL、預期的文件尺寸和文件擴展名,創建用於保存相應緩存條目的本地文件名。可以使用這個文件名向本地文件中寫入數據。數據寫入完成後應該調用CommitUrlCacheEntry。
CommitUrlCacheEntry接受URL、本地文件名、失效時間、最後修改時間、緩存條目類型、頭部信息及其尺寸和文件擴展名,在緩存存儲器中保存文件數據,並與給定的URL關聯起來。
注意:CreateUrlCacheEntry只是在內存中創建緩存條目,必須調用CommitUrlCacheEntry才寫入index.dat文件中。另外,CreateUrlCacheEntry會創建一個空的臨時文件。CommitUrlCacheEntry會校驗文件名與url的關係,這是IE防止用戶把臨時文件挪到其他地方的一種手段。
4.5. 刪除緩存條目
DeleteUrlCacheEntry 如果緩存中存在與源名稱相關的文件,刪除它。
DeleteUrlCacheEntry刪除與給定URL相關的緩存文件。如果沒有找到相應的緩存文件,則函數調用失敗,GetLastError返回ERROR_FILE_NOT_FOUND。如果緩存文件當前被鎖定或者正在使用中,則函數調用失敗,GetLastError返回ERROR_ACCESS_DENIED。文件解鎖後會被刪除。
4.6. 獲取緩存文件
RetrieveUrlCacheEntryFile 以文件的形式從緩存中獲取一個緩存條目
UnlockUrlCacheEntryFile 解鎖因爲使用RetrieveUrlCacheEntryFile從緩存中獲取其文件用於使用而被鎖定的緩存條目。
對於要求某資源的文件名才能的啓動的應用程序,可使用RetrieveUrlCacheEntryFile和UnlockUrlCacheEntryFile函數。
不要求文件名的程序應該使用RetrieveUrlCacheEntryStream、ReadUrlCacheEntryStream和UnlockUrlCacheEntryStream獲取緩存信息。
RetrieveUrlCacheEntryFile接受一個URL和用於保存INTERNET_CACHE_ENTRY_INFO結構體的緩衝區及其尺寸,爲調用者獲取並鎖定緩存文件。
使用完緩存文件後,應該調用UnlockUrlCacheEntryFile解鎖文件。
注意:雖然你也可以直接使用readfile來讀取緩存條目,但是此時對文件沒有加鎖,因此在讀取的過程中可能會被修改或刪除,因此推薦使用RetrieveUrlCacheEntryFile。
使用方法:先調用RetrieveUrlCacheEntryFile獲取條目信息並對文件加鎖,條目信息中有緩存文件的本地文件名,然後調用readfile讀取文件內容,讀取完成後調用UnlockUrlCacheEntryFile解鎖。
4.7. 獲取緩存流
RetrieveUrlCacheEntryStream 提供最高效的、實現無關的訪問緩存數據的方法。
ReadUrlCacheEntryStream 從RetrieveUrlCacheEntryStream打開的流中讀取緩存數據。
RetrieveUrlCacheEntryStream、ReadUrlCacheEntryStream和UnlockUrlCacheEntryStream用於獲取緩存中的資源。
RetrieveUrlCacheEntryStream接受一個URL,一個用於存儲INTERNET_CACHE_ENTRY_INFO結構體的緩衝區及其尺寸和一個表明是否可以進行隨機讀取的布爾值。如果找到了緩存文件,函數會創建到文件的句柄。函數不做URL解析,所以對於含有錨定(#)的URL,即使資源在緩存中,它也找不到。比如說,如果傳入 http://example.com/example.htm#sample,即使它在緩存中,函數也會返回ERROR_FILE_NOT_FOUND。
ReadUrlCacheEntryStream要求傳入RetrieveUrlCacheEntryStream創建的句柄、文件偏移量、緩衝區及其尺寸。如果緩衝區不足以容納可用數據,函數調用會失敗,GetLastError返回ERROR_INSUFFICIENT_BUFFER,緩衝區尺寸參數會被設置爲下載資源所需緩衝區大小。
獲取緩存文件後,應該調用UnlockUrlCacheEntryStream關閉RetrieveUrlCacheEntryStream創建的句柄。
注意:流操作主要針對不關心本地文件名的應用,所有讀取和關閉流的操作都必須使用RetrieveUrlCacheEntryStream返回的句柄。
4.8. 緩存組操作
CreateUrlCacheGroup 生成一個緩存組標識
SetUrlCacheEntryGroup 向緩存組添加條目,或者從中刪除條目
DeleteUrlCacheGroup 釋放GROUPID以及緩存索引文件中任何與之相關的狀態
要創建緩存組,必須調用CreateUrlCacheGroup爲其生成一個GROUPID。使用SetUrlCacheEntryGroup函數,提供緩存條目的URL和INTERNET_CACHE_GROUP_ADD標誌就可以把條目加入到緩存組中。要從緩存組中刪除條目,則傳入條目的URL,使用INTERNET_CACHE_GROUP_REMOVE標誌。
FindFirstUrlCacheEntryEx和FindNextUrlCacheEntryEx函數可以枚舉指定緩存組中的條目。完成枚舉後,應該用FindCloseUrlCache關閉枚舉句柄。