引言
Windows磁碟清理工具(Disk CleanUp)是一個實用快捷並擁有簡單易用介面的系統清理軟體,更值得系統開發管理員注意的是,此系統清理軟體是建立在以COM技術為基礎發展的,支援第三方外掛程式,並且可以根據需要自製定義功能二次開發的平台。在這裡,我們對於Windows磁碟清理工具的開發介面做深入地研究,在此基礎上舉例示範添加一個尋找“*.tmp”臨時檔案的功能。
技術討論
微軟的COM技術廣泛地運用在Windows的模組化設計中,致使支援二次開發。關於COM技術基礎與應用,可參考。在此,我們只為Windows磁碟清理工具,簡稱清理工具的擴充介面加以分解。清理工具首次出現在Windows 98作業系統中,並在後來推出的Windows版本中予以改進,添加了新的功能。比如說,在NTFS的檔案系統下,自動壓縮不經常訪問的檔案。這些新功能通過COM模組實現,在清理工具中作為外掛程式調用。早期的版本是通過IEmptyVolumeCache介面調用,在Windows 2000以後的版本中,還加入了IEmptyVolumeCache2介面,加入了較小的更新。
IEmptyVolumeCache介面由五個函數組成,根據呼叫的順序,分別是:
virtual /* [local] */ HRESULT STDMETHODCALLTYPE Initialize( /* [in] */ HKEY hkRegKey, /* [in] */ LPCWSTR pcwszVolume, /* [out] */ LPWSTR *ppwszDisplayName, /* [out] */ LPWSTR *ppwszDescription, /* [out] */ DWORD *pdwFlags) = 0;
virtual HRESULT STDMETHODCALLTYPE GetSpaceUsed( /* [out] */ DWORDLONG *pdwlSpaceUsed, /* [in] */ IEmptyVolumeCacheCallBack *picb) = 0;
virtual HRESULT STDMETHODCALLTYPE ShowProperties( /* [in] */ HWND hwnd) = 0;
virtual HRESULT STDMETHODCALLTYPE Purge( /* [in] */ DWORDLONG dwlSpaceToFree, /* [in] */ IEmptyVolumeCacheCallBack *picb) = 0;
virtual HRESULT STDMETHODCALLTYPE Deactivate( /* [out] */ DWORD *pdwFlags) = 0; |
清理工具在正常執行時,首先調用Initialize初始化外掛程式,隨後執行GetSpaceUsed來掃描可清除的檔案大小。掃描完畢後,清理工具的主介面就出現了如圖1所示,在此,我們加入了清理TMP檔案的功能可以瀏覽不同的清理檔案種類。列表中的每一個檔案種類均由一個COM外掛程式實現。除了閱覽可清理檔案大小以外,使用者在可以點擊一個可自訂的按鈕,調用外掛程式的ShowProperties功能,以顯示更詳細的資料。如使用者選擇OK,清理工具就調用Purge函數,清理掃描出來的檔案。最後,Deactivate函數被調用,終止外掛程式的應用。
運用於Windows 2000以後的清理工具的外掛程式也應該支援IEmptyVolumeCache的介面。IEmptyVolumeCache只由一個函數組成:
virtual /* [local] */ HRESULT STDMETHODCALLTYPE InitializeEx(
/* [in] */ HKEY hkRegKey, /* [in] */ LPCWSTR pcwszVolume, /* [in] */ LPCWSTR pcwszKeyName, /* [out] */ LPWSTR *ppwszDisplayName, /* [out] */ LPWSTR *ppwszDescription, /* [out] */ LPWSTR *ppwszBtnText, /* [out] */ DWORD *pdwFlags) = 0; |
InitializeEx增加了更嚴格的本地化語言要求,加強了國際化的支援,並且允許自訂按鈕的顯示文字。pdwFlags變數用於在工具與外掛程式間傳遞資訊,支援下列旗標:
EVCF_OUTOFDISKSPACE EVCF_SETTINGSMODE EVCF_DONTSHOWIFZERO EVCF_ENABLEBYDEFAULT EVCF_ENABLEBYDEFAULT_AUTO EVCF_HASSETTINGS EVCF_REMOVEFROMLIST |
EVCF_OUTOFDISKSPACE與EVCF_SETTINGSMODE用於工具傳遞給外掛程式的設定。EVCF_OUTOFDISKSPACE表示當前硬碟的空餘空間非常有限,需要儘可能多地清理,即使是系統的效能會受到影響。EVCF_SETTINGSMODE表示可定期執行的無人控制模式。在此模式下,GetSpaceUsed,Purge,及ShowProperties都將不予調用,所有清理任務應予InitializeEx時執行。其它旗標用於外掛程式傳遞給工具的不同運行模式。EVCF_DONTSHOWIFZERO表示在沒有找到可刪除檔案時不顯示此類型,EVCF_ENABLEBYDEFAULT表示此類型檔案可以安全刪除,EVCF_ENABLEBYDEFAULT_AUTO表示此類型檔案可以非常安全的刪除,EVCF_HASSETTINGS表示此外掛程式支援ShowProperties功能,可以顯示詳細資料。EVCF_REMOVEFROMLIST表示是一次性清理任務,清理工具在執行後自動將外掛程式關閉,以後不再執行。
圖1 清理工具的主介面
實現方法
我們開發一個新的清理工具外掛程式,掃描並清理*.TMP檔案。COM的編程有多種方法,我們選擇了ATL庫。關於ATL庫的運用。
我們在Visual Studio .Net 2003中產生新的ATL的DLL Server項目,並使用Add Class加入新的ATL Simple Object控制項類CCleanSimpleHandler。在定義中,我們讓CCleanSimpleHandler從IEmptyVolumeCache2繼承。並且,我們添加了下列變數:
// 儲存掃描出檔案的大小
DWORDLONG m_dwlFileSize;
// 儲存根目錄
WCHAR m_strRootDir[MAX_PATH];
// 儲存掃描出檔案清單
std::vector<WCHAR *> m_lstFilesToDel; |
然後,我們一一實現IEmptyVolumeCache及IEmptyVolumeCache2介面的函數。在下面的代碼清單中,沒有包括嚴格的檢查錯誤傳回值。這是為了簡短代碼的長度,提高可讀性。在實際應用中,檢查錯誤傳回值是不可少的。為了不同版本Windows相容,我們在InitializeEx中調用Initialize。
HRESULT CCleanSimpleHandler::InitializeEx (HKEY hKey, LPCWSTR pcwszVolume, LPCWSTR pcwszKeyName, LPWSTR *ppwszDisplayName, LPWSTR *ppwszDescription, LPWSTR *ppwszBtnText, DWORD *pdwFlags) { HRESULT hr = Initialize (hKey, pcwszVolume, ppwszDisplayName, ppwszDescription, pdwFlags); *ppwszBtnText = (LPWSTR) CoTaskMemAlloc (64 * sizeof (WCHAR)); StrCpyW(*ppwszBtnText, L"View files"); return hr; }
HRESULT CCleanSimpleHandler::Initialize (HKEY hKey, LPCWSTR pcwszVolume, LPWSTR *ppwszDisplayName, LPWSTR *ppwszDescription, DWORD *pdwFlags) { StrCpyW(m_strRootDir, pcwszVolume); *ppwszDisplayName = (LPWSTR) CoTaskMemAlloc(256 * sizeof (WCHAR)); StrCpyW(*ppwszDisplayName, L"*.TMP files"); *ppwszDescription = (LPWSTR) CoTaskMemAlloc (256 * sizeof (WCHAR)); StrCpyW(*ppwszDescription, L"Temporary files - *.TMP"); *pdwFlags = EVCF_HASSETTINGS | EVCF_ENABLEBYDEFAULT; m_dwlFileSize = 0; return S_OK; } |
在GetSpaceUsed中,我們調用ScanDir來掃描*.TMP檔案,儲存在m_lstFilesToDel中。GetSpaceUsed的第二個參數是IEmptyVolumeCacheCallBack介面的指標,用於調用其ScanProgress函數以報告掃描的進展情況。ScanProgress函數定義是:
HRESULT ScanProgress(DWORDLONG dwlSpaceUsed, DWORD dwFlags, LPCWSTR pwszReserved);
其中dwFlags正常應設為零,在結束時改為EVCCBF_LASTNOTIFICATION。ScanProgress函數的傳回值很重要,因為使用者可以在任何時候中斷在進行中的清理任務。如ScanProgress返回E_ABORT,GetSpaceUsed應最快終端掃描,函數返回。因此,我們在遞迴的目錄掃描函數ScanDir中,加入了如中斷立即退出的功能。
HRESULT CCleanSimpleHandler::GetSpaceUsed (DWORDLONG *pdwSpaceUsed, IEmptyVolumeCacheCallBack *picb) { m_dwlFileSize = 0; ScanDir(m_strRootDir, picb); picb->ScanProgress(m_dwlFileSize, EVCCBF_LASTNOTIFICATION ,NULL); *pdwSpaceUsed = m_dwlFileSize; return S_OK; }
bool CCleanSimpleHandler::ScanDir(WCHAR * szDir, IEmptyVolumeCacheCallBack *pcib) { WCHAR strPath[MAX_PATH]; WCHAR* pchPathFileName; bool cancelled = false; WIN32_FIND_DATAW fd; HANDLE hFind;
if (cancelled = FAILED(pcib->ScanProgress(m_dwlFileSize, NULL, NULL))) return false; StrCpyW(strPath,szDir); PathAppendW(strPath, L"*"); pchPathFileName = strPath+lstrlenW(strPath)-1; hFind = FindFirstFileW(strPath, &fd); if (hFind == INVALID_HANDLE_VALUE) // E.g. Due to security issues return true; do { StrCpyW(pchPathFileName, fd.cFileName); if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { if (fd.cFileName[0] != '.') { if (cancelled = !ScanDir(strPath, pcib)) break; } } else { WCHAR* pchExt = PathFindExtensionW(strPath); if ( StrCmpIW(pchExt, L".tmp") == 0 ) { m_dwlFileSize += ((DWORDLONG)fd.nFileSizeHigh)*4294967295+fd.nFileSizeLow; WCHAR* filename = (WCHAR *)CoTaskMemAlloc((lstrlenW(strPath)+1)*sizeof(WCHAR)); StrCpyW(filename, strPath); m_lstFilesToDel.push_back(filename); } }
} while (FindNextFileW(hFind, &fd) != NULL); FindClose(hFind); return !cancelled; } |
其他的函數很簡單。Purge函數將掃描出的檔案清單m_lstFilesToDel中的檔案一一刪除。ShowProperties中,我們顯示掃描出來的檔案。最後,Deactivate將分配的記憶體釋放。
HRESULT CCleanSimpleHandler::Purge (DWORDLONG dwSpaceToFree, IEmptyVolumeCacheCallBack *picb) { for (unsigned int i=0; i < m_lstFilesToDel.size(); ++i) DeleteFileW(m_lstFilesToDel[i]); return S_OK; }
HRESULT CCleanSimpleHandler::ShowProperties (HWND hWnd) { for (unsigned int i=0; i < m_lstFilesToDel.size(); ++i) if (MessageBoxW(hWnd, m_lstFilesToDel[i], L"View files", MB_OKCANCEL|MB_ICONINFORMATION)==IDCANCEL) break; return S_OK; }
HRESULT CCleanSimpleHandler::Deactivate (LPDWORD pdwFlags) { for (unsigned int i=0; i < m_lstFilesToDel.size(); ++i) CoTaskMemFree(m_lstFilesToDel[i]); m_lstFilesToDel.clear(); *pdwFlags = 0; return S_OK; |
結論和建議
通過執行個體分解,我們對Windows磁碟清理工具的基於COM技術的開發介面做了深入地研究。Windows外殼中有較多的開發介面,本文介紹的開發思想也可以運用在其它擴充外掛程式中。