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包含一个HandlerHandler里包含处理每一种Archive的接口。

每一种Archive包含一个Register,用于向全局对象注册,只有注册后的Handler才会被调用。

Handler必须继承IInArchive接口,表示可读,用于解压。(必选)

Handler可以继承IOutArchive接口,表示可写,用于压缩文档。(可选)

根据官方说明:
Packing / unpacking: 7z, XZ, BZIP2, GZIP, TAR, ZIP and WIM

仅这几种Archive是可以被压缩的,因此这些ArchiveHandler要继承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 获取解码器属性。传入codecIndexPROPID,传出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 使用新文件夹操作接口:IFolderOperationsIFolderSetFlatMode
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 格式相关推荐

  1. 对python源码进行编译,加密python脚本

    对python源码进行编译 1.生成.pyc文件 import py_compile py_compile.compile('hello.py') 2.优化源码文件 python -O -m py_c ...

  2. 初步了解mac下C源码的编译过程

    初步了解mac下C源码的编译过程 参考: 请问Mac OS X (10.9.1)下创建和使用动态链接库的方法 cc.gcc.g++.CC的区别概括 - 今晚打酱油_ - 博客园 Linux 下 的 c ...

  3. Jar包反编译后修改源码再编译

    Jar包反编译后修改源码再编译 文章目录 Jar包反编译后修改源码再编译 1. 场景 2. 详细步骤 2.1 查看源码 2.2 生成源文件 2.3 构建项目 2.4 编译成jar包 3. 参考链接 1 ...

  4. 源码下编译APK,却是总是提示,找不到符号:SystemProperties 。。。

    >>>>>在DeskClock下任意一个java文件中,import android.os.SystemProperties;然后添加user库,将layoutlib.j ...

  5. 从源码开始编译一个带有WEB服务器功能的小型LINUX(下)

    上接:从源码开始编译一个带有WEB服务器功能的小型LINUX(上) 七.为新构建的ToyLinux启用虚拟控制台 这个可以通过宿主机来实现,也可以直接启动刚构建成功的小Linux进行配置.我们这里采用 ...

  6. MySQL源码包编译安装

    +++++++++++++++++++++++++++++++++++++++++++ 标题:MySQL数据库实例部署 时间:2019年5月2日 内容:MySQL源码包进行编译,然后部署MySQL单实 ...

  7. Android FrameWork学习(一)Android 7 0系统源码下载 编译

    最近计划着研究下 Android 7.0 的系统源码,之前也没做过什么记录,这次正好将学习的内容记录下来,方便以后复习巩固. 既然要学习我们的系统源码,那我们第一步要做的就是下载源码并进行编译了. # ...

  8. 编译 php mysql 依赖包_MySQL 5.5.15源码包编译安装

    mysql果然是不愧是目前最火的数据库,自从mysql5.5.8之后,mysql的源码包编译安装都要用到cmake来进行编译了,编译的过程没有本质 mysql果然是不愧是目前最火的数据库,自从mysq ...

  9. 如何获取ubuntu源码包里面的源码进行编译

    如何获取ubuntu源码包里面的源码进行编译 1.在获取源码包之前,确保在软件源配置文件 /etc/apt/sources.list 中添加了deb-src项 2.使用如下命令获取xxx源码包的详细信 ...

  10. Nginx实战基础篇一 源码包编译安装部署web服务器

    Nginx实战基础篇一 源码包编译安装部署web服务器 版权声明: 本文遵循"署名非商业性使用相同方式共享 2.5 中国大陆"协议 您可以自由复制.发行.展览.表演.放映.广播或通 ...

最新文章

  1. 基于深度学习的口罩规范佩戴检测【树莓派+PC训练、测试】
  2. 机器学习基础(一)——人工神经网络与简单的感知器
  3. 全局事件-广播(Broadcast)
  4. 排序算法汇总(转载收藏)
  5. linux克隆的虚拟,linux(CentOS7)下克隆虚拟机并配置网络(固定ip)
  6. word List 17
  7. (摘录)sockaddr与sockaddr_in,sockaddr_un结构体详细讲解
  8. hihocoder1033交错和
  9. css 设置表格右边有图片_我写CSS常用的方法
  10. [转载] python int 幂函数_Python中对数和幂函数的不精确结果
  11. Ubuntu-vim 命令
  12. Qt开源作品41-网络调试助手增强版V2022
  13. win10系统如何启动sql服务器,win10系统打开SQL Server数据库服务的方法
  14. 梦殇 chapter three
  15. python下拉框二级联动_Excel数据规范输入技巧 | 二级联动下拉菜单
  16. MC33063电源啸叫
  17. SimpleDateFormat多线程天坑
  18. linux 添加系统启动,怎样把这个linux系统添加到启动选项?
  19. Linux下安装新世纪五笔输入法(附表)(for 小白)
  20. Win32汇编学习笔记之基础篇

热门文章

  1. restlet Framework2.2和2.3版本的对比
  2. 西门子PLC控制器家族产品汇总
  3. java代码c3p0连接池配置,c3p0连接池acquireincrement属性配置详解
  4. Eap无线加密服务器,使用hostapd做radius服务器进行EAP认证,巩固无线安全
  5. chrome安装JSONview插件,即可在浏览中查看json文件
  6. 刷魔趣系统与安装google相机
  7. 实对称矩阵的特征值求法_理解矩阵得相似对角化
  8. Windows系统查看svg缩略图插件
  9. C# Newtonsoft JArray排序
  10. vm14 Ubuntu16不能复制粘贴