以前只知道 7z 的做者是 Windows 用戶,7z 是在 Windows 上開發出來的。如今看了源碼才知道,做者深受 Windows 影響,代碼中大量依賴 COM 接口,但並無使用 ATL 庫,實際上是做者本身修改簡化後的 COM 接口實現。若是你沒有 COM 組件開發經驗,那麼這裏會比較難受了,推薦趕忙先去補一補 COM 技術細節。數組
7z 中的這套 COM 接口簡化了大部分細節,甚至引用計數都沒加鎖,這個操做其實有點迷。。估計做者本身在代碼中能保證線程同步吧。因爲有大量輔助宏,實際寫接口實現類時仍是比較容易的。測試
仍是有針對性的讀源碼,咱們要增長新存檔格式,重點就要看 CPP/7zip/Archive/IArchive.h
文件,這個文件中包含了十幾個接口定義,和一些用於註冊存檔格式的宏,下面我把重要的幾個接口和宏摘出來,如今能夠不用懂它們的具體含義,以後遇到時回來查就行。線程
如下全部接口方法的返回值均爲 HRESULT
,且不能拋出異常。code
如下全部註釋均以存檔包實現者的角度來寫,而不是庫使用者。對象
進度回調接口,是多個接口的父類。排序
// 若是 SetTotal 被調用,則: // 對於 xz、gz、bz二、lzma、z、ppmd 格式,SetCompleted 爲打包大小 // 對於其餘格式,SetCompleted 爲解包大小 // 若是 SetTotal 沒有被調用,則 SetCompleted 爲打包大小 SetTotal(UInt64 total); SetCompleted(const UInt64 *completeValue);
打開存檔的進度回調,在存檔包處理對象的 Open()
方法中做爲參數傳入,其自己還用做其餘用途,如能夠經過該接口 QueryInterface
到 IArchiveOpenVolumeCallback
接口,從而獲知當前打開的存檔文件的相關信息。繼承
SetTotal(const UInt64 *files, const UInt64 *bytes); SetCompleted(const UInt64 *files, const UInt64 *bytes);
繼承自 IProgress
。解壓回調,在存檔包處理對象的 Extract()
方法中做爲參數傳入,除了用做報告解壓進度外,其 GetStream()
方法仍是重要的用來獲取輸入流的方法。索引
GetStream( UInt32 index, // 要解壓的項目索引 ISequentialOutStream **outStream, Int32 askExtractMode // 可用值爲 NExtract::NAskMode 枚舉,若是不爲 kExtract,則不能實際解壓 ); PrepareOperation(Int32 askExtractMode); // opRes 的可用值爲 NExtract::NOperationResult 枚舉 SetOperationResult(Int32 opRes);
用來獲取當前打開卷的相關信息。對於分卷打包格式,還可用來打開後續的其餘卷。接口
GetProperty(PROPID propID, PROPVARIANT *value); GetStream(const wchar_t *name, IInStream **inStream);
用來對外提供一個指定項目的順序輸入流,可能用於流式操做,無縫打開包內的其餘存檔包。ip
GetStream(UInt32 index, ISequentialInStream **stream);
支持讀取的存檔包的核心接口,存檔包對象必須實現的接口之一。
// 若是 kUseGlobalOffset,stream 當前位置能夠爲非零 // 若是 !kUseGlobalOffset,stream 當前位置爲 0 // 若是 maxCheckStartPosition == NULL,則 handler 可以在 stream 中尋找存檔起點 // 若是 *maxCheckStartPosition == 0,則 handler 必須只把當前位置做爲存檔起點 Open( IInStream *stream, const UInt64 *maxCheckStartPosition, IArchiveOpenCallback *openCallback ); Close(); GetNumberOfItems(UInt32 *numItems); // 寫出步驟: // 1. 調用 extractCallback->GetStream(index, &outStream, askMode) 來獲取寫出流 // 2. 調用 extractCallback->PrepareOperation(askMode) 來讓 7z 作好準備 // 3. 建立須要的 Coder 來寫入 outStream // 4. 寫入完成後,釋放 outStream,調用 extractCallback->SetOperationResult() 設置解壓結果 Extract( const UInt32* indices, // 要解壓的全部索引數組,必須是已排序的 UInt32 numItems, // -1 爲解壓全部文件,注意這裏類型爲 Uint32 Int32 testMode, // 非零時,僅測試不寫出文件 IArchiveExtractCallback *extractCallback ); GetNumberOfProperties(UInt32 *numProps); GetPropertyInfo(UInt32 index, BSTR *name, PROPID *propID, VARTYPE *varType); GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value); GetNumberOfArchiveProperties(UInt32 *numProps); GetArchivePropertyInfo(UInt32 index, BSTR *name, PROPID *propID, VARTYPE *varType); // kpidOffset:存檔數據的起點,VT_EMPTY 表示起點爲 0,可爲 VT_UI四、VT_UI八、VT_I8 類型 // kpidPhySize:存檔數據的實際大小,VT_EMPTY 表示未知,可以大於文件大小 // kpidIsDeleted、kpidIsAltStream、kpidIsAux、kpidINode:若是 GetProperty 支持這些屬性,則必須返回 VARIANT_TRUE (VT_BOOL) GetArchiveProperty(PROPID propID, PROPVARIANT *value);
若是存檔包支持從順序輸入流中打開,能夠實現該接口。
OpenSeq(ISequentialInStream *stream);