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
中,我将代码贴出来。
#include "StdAfx.h"#include "../../Common/RegisterArc.h"#include "ZipHandler.h"namespace NArchive {namespace NZip {static const Byte k_Signature[] = {4, 0x50, 0x4B, 0x03, 0x04, // Local4, 0x50, 0x4B, 0x05, 0x06, // Ecd4, 0x50, 0x4B, 0x06, 0x06, // Ecd646, 0x50, 0x4B, 0x07, 0x08, 0x50, 0x4B, // Span / Descriptor6, 0x50, 0x4B, 0x30, 0x30, 0x50, 0x4B }; // NoSpanREGISTER_ARC_IO("zip", "zip z01 zipx jar xpi odt ods docx xlsx epub ipa apk appx", 0, 1,k_Signature,0,NArcInfoFlags::kFindSignature |NArcInfoFlags::kMultiSignature |NArcInfoFlags::kUseGlobalOffset,IsArc_Zip)}}
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()
释放
static inline HRESULT SetPropStrFromBin(const char *s, unsigned size, PROPVARIANT *value)
{if ((value->bstrVal = ::SysAllocStringByteLen(s, size)) != 0)value->vt = VT_BSTR;return S_OK;
}static inline HRESULT SetPropGUID(const GUID &guid, PROPVARIANT *value)
{return SetPropStrFromBin((const char *)&guid, sizeof(guid), value);
}
注:文档CPP\7zip\Archive\Guid.txt
中有一段描述,是关于格式classid
的。
返回的classid都应当符合这种格式{23170F69-40C1-278A-1000-000110xx0000}
中间两位xx在下表中可以找到对应关系,如果只是希望打开指定格式的文档,直接指定classid即可,不需要通过GetHandlerProperty2
来获取。
Handler GUIDs:{23170F69-40C1-278A-1000-000110xx0000}01 Zip02 BZip203 Rar04 Arj05 Z06 Lzh07 7z08 Cab09 Nsis0A lzma0B lzma860C xz0D ppmd......
4. 创建IInArchive
对象
CMyComPtr<IInArchive> parc;
HRESULt hr = CreateObject(&clsid, &IID_IInArchive, (void**)&parc));
注:我在Linux
版本调试时,遇到了崩溃的问题,调试之后发现,7z中的IUnknown
接口在Linux
中使用了虚析构函数。
这与我代码中已有的IUnknown
定义不一致,创建的对象会有虚表地址,因此调用的函数地址错位导致崩溃。
DEFINE_GUID(IID_IUnknown,
0x00000000, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46);
struct IUnknown
{STDMETHOD(QueryInterface) (REFIID iid, void **outObject) PURE;STDMETHOD_(ULONG, AddRef)() PURE;STDMETHOD_(ULONG, Release)() PURE;#ifndef _WIN32virtual ~IUnknown() {}#endif
};
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 格式相关推荐
- 对python源码进行编译,加密python脚本
对python源码进行编译 1.生成.pyc文件 import py_compile py_compile.compile('hello.py') 2.优化源码文件 python -O -m py_c ...
- 初步了解mac下C源码的编译过程
初步了解mac下C源码的编译过程 参考: 请问Mac OS X (10.9.1)下创建和使用动态链接库的方法 cc.gcc.g++.CC的区别概括 - 今晚打酱油_ - 博客园 Linux 下 的 c ...
- Jar包反编译后修改源码再编译
Jar包反编译后修改源码再编译 文章目录 Jar包反编译后修改源码再编译 1. 场景 2. 详细步骤 2.1 查看源码 2.2 生成源文件 2.3 构建项目 2.4 编译成jar包 3. 参考链接 1 ...
- 源码下编译APK,却是总是提示,找不到符号:SystemProperties 。。。
>>>>>在DeskClock下任意一个java文件中,import android.os.SystemProperties;然后添加user库,将layoutlib.j ...
- 从源码开始编译一个带有WEB服务器功能的小型LINUX(下)
上接:从源码开始编译一个带有WEB服务器功能的小型LINUX(上) 七.为新构建的ToyLinux启用虚拟控制台 这个可以通过宿主机来实现,也可以直接启动刚构建成功的小Linux进行配置.我们这里采用 ...
- MySQL源码包编译安装
+++++++++++++++++++++++++++++++++++++++++++ 标题:MySQL数据库实例部署 时间:2019年5月2日 内容:MySQL源码包进行编译,然后部署MySQL单实 ...
- Android FrameWork学习(一)Android 7 0系统源码下载 编译
最近计划着研究下 Android 7.0 的系统源码,之前也没做过什么记录,这次正好将学习的内容记录下来,方便以后复习巩固. 既然要学习我们的系统源码,那我们第一步要做的就是下载源码并进行编译了. # ...
- 编译 php mysql 依赖包_MySQL 5.5.15源码包编译安装
mysql果然是不愧是目前最火的数据库,自从mysql5.5.8之后,mysql的源码包编译安装都要用到cmake来进行编译了,编译的过程没有本质 mysql果然是不愧是目前最火的数据库,自从mysq ...
- 如何获取ubuntu源码包里面的源码进行编译
如何获取ubuntu源码包里面的源码进行编译 1.在获取源码包之前,确保在软件源配置文件 /etc/apt/sources.list 中添加了deb-src项 2.使用如下命令获取xxx源码包的详细信 ...
- Nginx实战基础篇一 源码包编译安装部署web服务器
Nginx实战基础篇一 源码包编译安装部署web服务器 版权声明: 本文遵循"署名非商业性使用相同方式共享 2.5 中国大陆"协议 您可以自由复制.发行.展览.表演.放映.广播或通 ...
最新文章
- 基于深度学习的口罩规范佩戴检测【树莓派+PC训练、测试】
- 机器学习基础(一)——人工神经网络与简单的感知器
- 全局事件-广播(Broadcast)
- 排序算法汇总(转载收藏)
- linux克隆的虚拟,linux(CentOS7)下克隆虚拟机并配置网络(固定ip)
- word List 17
- (摘录)sockaddr与sockaddr_in,sockaddr_un结构体详细讲解
- hihocoder1033交错和
- css 设置表格右边有图片_我写CSS常用的方法
- [转载] python int 幂函数_Python中对数和幂函数的不精确结果
- Ubuntu-vim 命令
- Qt开源作品41-网络调试助手增强版V2022
- win10系统如何启动sql服务器,win10系统打开SQL Server数据库服务的方法
- 梦殇 chapter three
- python下拉框二级联动_Excel数据规范输入技巧 | 二级联动下拉菜单
- MC33063电源啸叫
- SimpleDateFormat多线程天坑
- linux 添加系统启动,怎样把这个linux系统添加到启动选项?
- Linux下安装新世纪五笔输入法(附表)(for 小白)
- Win32汇编学习笔记之基础篇
热门文章
- restlet Framework2.2和2.3版本的对比
- 西门子PLC控制器家族产品汇总
- java代码c3p0连接池配置,c3p0连接池acquireincrement属性配置详解
- Eap无线加密服务器,使用hostapd做radius服务器进行EAP认证,巩固无线安全
- chrome安装JSONview插件,即可在浏览中查看json文件
- 刷魔趣系统与安装google相机
- 实对称矩阵的特征值求法_理解矩阵得相似对角化
- Windows系统查看svg缩略图插件
- C# Newtonsoft JArray排序
- vm14 Ubuntu16不能复制粘贴