7z源码的编译与使用_markdown 格式
7z作為開源的解壓縮項目,支持多種格式的解壓縮,由 Igor Pavlov 開發,最新的版本為 19.00 版。
源碼下載位置:https://www.7-zip.org/a/7z1900-src.7z
1、源碼結構
源碼解壓之后,是這樣的結構:
| Asm | 包含主要算法的匯編實現,直接使用匯編可以提高執行效率。但是卻對跨平臺移植造成了一些困難 |
| C | 主要是算法的代碼,由C語言實現 |
| CPP | 相關COM接口的實現,界面,工程文件等 |
| DOC | 相關文檔 |
對于我們編譯項目來說,最主要的就是 CPP 文件夾,編譯的項目文件在 CPP\7zip\Bundles\ 中可以找到。
| Alone | 獨立的可執行程序,支持的解壓格式僅包括7z, cab, tar, zip這幾種。 |
| Alone7z | 獨立的可執行程序,僅支持7z格式。FM文件管理器(File Manager),通過加載7z.dll的導出函數進行解壓。 |
| Format7z | 7za.dll 7z Standalone Plugin,7z 獨立插件(僅7z格式) |
| Format7zF | 7z.dll 7z Plugin, 7z插件,包含各種格式。 |
| SFXCon | 自解壓(控制臺程序)。 |
| SFXSetup | 自解壓程序(安裝包)。 |
| SFXWin | 自解壓程序(Windows界面)。 |
2、代碼的編譯
打開CPP\7zip\Bundles\Format7zF\Format7z.dsw,即可打開7z.dll工程。
我使用的編譯器是vs2008,打開dsw文件提示升級,轉換后可生成sln文件和對應的vcproj文件。
直接編譯。第一次編譯,報錯:1>LINK : 無法創建 .ILK 文件的映射;正在非增量鏈接
1>LINK : fatal error LNK1104: 無法打開文件“C:\Program Files\7-Zip\7z.dll”解決方法:文件占用,編譯器權限不夠,不能對此文件進行修改,修改生成目標地址,生成到其他地方就可以了。
第二次編譯,報錯:
1>正在鏈接... 1>.\Debug\7zCrcOpt.obj : fatal error LNK1107: 文件無效或損壞: 無法在 0x276 處讀取解決方法:刪除工程中的asm文件,改由對應的c文件實現,記得將這些c文件的預編譯頭選項改為“不使用預編譯頭”。
第三次編譯,成功。
3、內部接口
7z.dll 中的每一種支持的格式被稱作 Archive,代碼位于CPP\7zip\Archive中。
每一種Archive包含一個Handler,Handler里包含處理每一種Archive的接口。
每一種Archive包含一個Register,用于向全局對象注冊,只有注冊后的Handler才會被調用。
Handler必須繼承IInArchive接口,表示可讀,用于解壓。(必選)
Handler可以繼承IOutArchive接口,表示可寫,用于壓縮文檔。(可選)
根據官方說明:
Packing / unpacking: 7z, XZ, BZIP2, GZIP, TAR, ZIP and WIM
僅這幾種Archive是可以被壓縮的,因此這些Archive的Handler要繼承IOutArchive接口。
如果代碼只用于解壓,而不用于壓縮,可定義EXTRACT_ONLY宏,可不生成IOutArchive,可以減少文件體積。
| ICoder.h | ICompressProgressInfo | 設置進度,用于向外部展示進度條 |
| ICompressCoder | 解碼 | |
| ICompressCoder2 | 同上,傳出多個Stream對象 | |
| ICompressSetCoderPropertiesOpt | 設置屬性 | |
| ICompressSetCoderProperties | 設置屬性 | |
| ICompressSetDecoderProperties2 | 設置屬性 | |
| ICompressWriteCoderProperties | 將屬性寫入到Stream | |
| ICompressGetInStreamProcessedSize | 獲取已經處理的大小 | |
| ICompressSetCoderMt | 設置進程數 | |
| ICompressSetFinishMode | 設置結束標志 | |
| ICompressGetInStreamProcessedSize2 | 獲取已經處理的大小 | |
| ICompressSetMemLimit | 設置內存限制 | |
| ICompressGetSubStreamSize | 獲取內部流文件大小 | |
| ICompressSetInStream | 設置壓縮傳入的InStream流對象 | |
| ICompressSetInStreamSize | 設置壓縮傳入的InStream流對象大小 | |
| ICompressSetOutStreamSize | 設置壓縮傳出的InStream流對象 | |
| ICompressSetBufSize | 設置緩沖區大小 | |
| ICompressInitEncoder | 初始化編碼器 | |
| ICompressSetInStream2 | 設置輸入流 | |
| ICompressSetOutStream2 | 設置輸出流 | |
| ICompressSetInStreamSize2 | 設置輸入流大小 | |
| ICompressFilter | 設置過濾器,只處理小于等于size的文檔 | |
| ICompressCodecsInfo | 獲取壓縮解碼器信息 | |
| ISetCompressCodecsInfo | 設置壓縮編碼器信息 | |
| ICryptoProperties | 加密屬性 | |
| ICryptoResetInitVector | 加密,重置InitVector | |
| ICryptoSetPassword | 設置密碼,用戶處理加密文檔。 | |
| ICryptoSetCRC | 設置CRC,用于處理加密文檔的。 | |
| IHasher | 計算哈希接口 | |
| IHashers | 哈希管理器 | |
| IStream.h | ISequentialInStream | 順序可讀文件流 |
| ISequentialOutStream | 順序可寫文件流 | |
| IInStream | 隨機可讀文件流(在ISequentialInStream基礎上增加Seek函數) | |
| IOutStream | 隨機可寫文件流(在ISequentialOutStream基礎上增加Seek/SetSize函數) | |
| IStreamGetSize | 獲取文件流大小 | |
| IOutStreamFinish | 為可寫文件流設置結束狀態 | |
| IStreamGetProps | 獲取文件流的屬性 | |
| IStreamGetProps2 | 獲取文件流的屬性 | |
| IArchive.h | IInArchive | 可讀文檔(用于輸入) |
| IArchiveGetRawProps | 文檔屬性 | |
| IArchiveGetRootProps | 根文檔屬性 | |
| IArchiveOpenSeq | 將順序流打開為文檔 | |
| IArchiveUpdateCallback | 設置文檔更新回調函數 | |
| IArchiveUpdateCallback2 | 設置文檔更新回調函數 | |
| IArchiveUpdateCallbackFile | 設置文檔更新回調函數(到輸出流) | |
| IOutArchive | 可寫文檔(用于輸出) | |
| ISetProperties | 設置屬性 | |
| IArchiveKeepModeForNextOpen | 下次打開時保持相同模式 | |
| IArchiveAllowTail | 允許尾部數據 |
4、外部接口
調用通過IDA打開7z.dll可發現其導出函數。
| CreateDecoder | 創建解碼器 |
| CreateEncoder | 創建編碼器 |
| CreateObject | 創建對象 |
| GetHandlerProperty2 | 獲取Handler屬性 |
| GetHandlerProperty | 獲取Handler屬性 |
| GetHashers | 獲取IHasher對象 |
| GetIsArc | 獲取IsArc函數地址 |
| GetMethodProperty | 獲取解碼器屬性。傳入codecIndex和PROPID,傳出PROPVARIANT* |
| GetNumberOfFormats | 獲取文件格式的數量。(指:7z,zip,rar等文件格式) |
| GetNumberOfMethods | 獲取解碼器的數量。(指:BCJ2,LZMA,Deflate等格式編碼) |
| SetCaseSensitive | 設置當前文件系統是否大小寫敏感,WINDOWS默認不敏感,其他系統默認敏感。 |
| SetCodecs | 傳入ICompressCodecsInfo對象,設置外部解碼器。 |
| SetLargePageMode | 設置大內存頁模式,這種模式可申請更多的內存。 |
5、解碼器
解碼器通過【注冊】的方式,注冊到全局變量g_Arcs中。
@ CPP\7zip\UI\Common\LoadCodecs.cpp
static const unsigned kNumArcsMax = 64; static unsigned g_NumArcs = 0; static const CArcInfo *g_Arcs[kNumArcsMax];根據定義g_Arcs最多可以容納64種不同的解碼器。
CArcInfo的定義如下:
struct CArcInfo {UInt16 Flags;Byte Id;Byte SignatureSize;UInt16 SignatureOffset;const Byte *Signature;const char *Name;const char *Ext;const char *AddExt;Func_CreateInArchive CreateInArchive;Func_CreateOutArchive CreateOutArchive;Func_IsArc IsArc;bool IsMultiSignature() const{ return (Flags & NArcInfoFlags::kMultiSignature) != 0; } };CArcInfo各主要成員的含義:
| CArcInfo::Flags | 定義在@CPP\7zip\Archive\IArchive.h中,NArcInfoFlags有詳細說明。 |
| CArcInfo::Id | Archive的ID標識符,例如:7z=7, Rar=3。 |
| CArcInfo::SignatureSize | 解碼器標識的長度。 |
| CArcInfo::Signature | 解碼器標識符,例如:zip={0x50, 0x4B, 0x03, 0x04},7z={'7' + 1, 'z', 0xBC, 0xAF, 0x27, 0x1C}。等。 |
| CArcInfo::Name | 解碼器名稱。 |
| CArcInfo::Ext | 解碼器擴展名。 |
| CArcInfo::CreateInArchive | 函數指針,創建解碼器InArchive對象,用于打開文件用于解壓。 |
| CArcInfo::CreateOutArchive | 函數指針,創建解碼器OutArchive對象,用于創建文件用于壓縮。 |
| CArcInfo::IsArc | 函數指針,判斷文件格式是否合法。 |
| CArcInfo::IsMultiSignature | 判斷是否有多個Signature。 |
各解碼器通過RegisterArc.h中封裝的宏進行注冊。
如:ZIP 的注冊代碼位于CPP\7zip\Archive\Zip\ZipRegister.cpp中,我將代碼貼出來。
6、相關宏開關
| _7ZIP_ST | Single-Thread單線程,默認未定義,開啟后將不編譯多線程相關邏輯 |
| _7ZIP_LARGE_PAGES | 開啟大內存頁,默認未開啟 |
| _SZ_ALLOC_DEBUG | 默認不開啟,開啟后可輸出內存申請與釋放的Log |
| USE_MIXER_MT | Multiple_Thread 多線程解碼器,不可與USE_MIXER_ST同時開啟。 |
| USE_MIXER_ST | Single_Thread 單線程解碼器,不可與USE_MIXER_MT同時開啟。 |
| EXTRACT_ONLY | 開啟后只包含解壓邏輯,不包含壓縮邏輯。 |
| NSIS_SCRIPT | 是否將NSIS腳本解壓,默認關閉,需要手動修改CPP\7zip\Archive\Nsis\Nsis.h開啟 |
| EXTERNAL_CODECS | 是否使用外部解碼器,全局變量g_ExternalCodecs負責加載外部解碼器。CPP\7zip\UI\Agent\Agent.cpp@LoadGlobalCodecs()中包含g_ExternalCodecs相關初始化邏輯。 |
| NEW_FOLDER_INTERFACE | 使用新文件夾操作接口:IFolderOperations和IFolderSetFlatMode |
| NO_READ_FROM_CODER | 禁止從解碼器讀取數據,默認未定義 |
| USE_WIN_FILE | 默認開啟,開啟后使用Windows API處理文件(CreateFile/CloseHandle等),否則使用C函數處理文件(如open,close等)。 |
7、接口的調用
1. 模塊加載
使用LoadLibrary/GetProcAddress(Windows)或dlopen/dlsym(Linux)獲取函數地址。
2. 獲取文件格式數量
DWORD dwFormat; HRESULT hr = GetNumberOfFormats(&dwFormat);3. 獲取每種格式的GUID
for(DWORD i=0;i<dwFormat;++i) {PROPVARIANT propvar;propvar.vt = VT_EMPTY;HRESULT hr = GetHandlerProperty2(i, NArchive::NHandlerPropID::kClassID, &propvar)GUID clsid = *(const GUID *)propvar.bstrVal;SysFreeString(propvar.bstrVal);// todo...... }在GetHandlerProperty2內部,是通過SetPropGUID()函數將classid的值傳遞給PROPVARIANT的。
內部調用了SysAllocStringByteLen(),為避免內存泄漏,獲取成功后應當調用SysFreeString()釋放
注:文檔CPP\7zip\Archive\Guid.txt中有一段描述,是關于格式classid的。
返回的classid都應當符合這種格式{23170F69-40C1-278A-1000-000110xx0000}
中間兩位xx在下表中可以找到對應關系,如果只是希望打開指定格式的文檔,直接指定classid即可,不需要通過GetHandlerProperty2來獲取。
4. 創建IInArchive對象
CMyComPtr<IInArchive> parc; HRESULt hr = CreateObject(&clsid, &IID_IInArchive, (void**)&parc));注:我在Linux版本調試時,遇到了崩潰的問題,調試之后發現,7z中的IUnknown接口在Linux中使用了虛析構函數。
這與我代碼中已有的IUnknown定義不一致,創建的對象會有虛表地址,因此調用的函數地址錯位導致崩潰。
5. 獲取包中文件數量
在IInArchive->Open返回成功之后。可通過調用IInArchive->GetNumberOfItems()獲取文檔中包含的文件數量。
UInt32 dwItems; HRESULT hr = parc->GetNumberOfItems(&dwItems);6. 解壓
IInArchive->Extract負責文檔的解壓。
DWORD dw; // dw 為要解壓的文檔ID,從0開始 C7zArchiveOpenCB opencb(); // 自定義一個CallBack類,繼承自IArchiveExtractCallBack接口 CMyComPtr<IArchiveExtractCallback> pcb = &opencb(); HRESULT hr = parc->Extract(&dw, 1, 0/*TestMode*/, pcb)); CMyComPtr<ISequentialOutStream> pSeqOutStm; hr = pcb->GetStream(0, &pSeqOutStm, 0); // pSeqOutStm 為解壓文件流,只支持順序寫入 // 需要調用者對pSeqOutStm進行包裝,使其支持Read/Seek/Tell等操作7. 關閉IInArchive
解壓完畢后,調用IInArchive->Close關閉IInArchive對象,以便回收內存防止泄漏。
parc->Close();------先寫這么多,后續更------
總結
以上是生活随笔為你收集整理的7z源码的编译与使用_markdown 格式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 函数式编程工具:filter和reduc
- 下一篇: log4j简介及应用