7-zip 动态库编译与简单 创建/解压 压缩文件

又开一个新坑了,压缩解压。。。

编译

windos系统

先讲讲windows下怎么编译7-zip模块吧(我已经放弃了直接使用7zip的代码)
有兴趣可以继续研究一下,最后调用一大堆接口是调用7zHandlerOut.cpp里面的CHandler::UpdateItems

最后为了可以跨平台减少工作量,我决定使用7z的动态库,不使用动态库的在CPP/7zip/UI/Console/Main.cpp里面的Main2函数(Alone7z)

首先下载7-zip的源码(如果你只需要解压常见的7z文档只[需要7zr.dll或7zxr.dll],可以考虑只下载LZMA SDK)。

 

http://www.7-zip.org/download.html

选择7-Zip Source code 或 LZMA SDK: (C, C++, C#, Java)

文件 源码所在目录 说明
7za.dll CPP/7zip/Bundles/Format7z 支持7z格式文件的压缩与解压
7zxa.dll CPP/7zip/Bundles/Format7zExtract 支持7z格式文件的解压
7zra.dll CPP/7zip/Bundles/Format7zR* 支持7z格式文件的压缩与解压
(仅限算法7z/LZMA/BCJ/BCJ2)
7zxr.dll CPP/7zip/Bundles/Format7zExtractR* 支持7z格式文件的解压
(仅限算法7z/LZMA/BCJ/BCJ2)
7z.dll CPP/7zip/Bundles/Format7zF 支持所有格式文件的压缩和解压
(7-zip这个软件支持多少就支持多少)

以上表格的内容在7z-src的readme.txt和lzma sdk里的lzma-sdk.txt 有详细描述
*标注的文件在LZMA SDK中包含,并且实测可以解压LZMA2的7z文件

我只需要用7zra.dll,我下载的是LZMA SDK 1801

然后打开VS2015 x64 本机工具命令提示符

进入要编译的dll的源码目录,输入

nmake NEW_COMPILER=1 MY_STATIC_LINK=1 CPU=AMD64

如果不需要静态编译(就是你想带上msvc的运行库)可以去掉MY_STATIC_LINK

如果编译的是32位的dll 打开VS2015 x64  x86兼容工具命令提示符,然后去掉AMD64

然后在O或AMD64目录内就能看到你要的dll了

unix类系统

unix类系统可以下载后面的p7zip,不过似乎还没更新18.01版本

然后make Client7z

mac OS编译示例:

安装xcode

重命名makefile.macosx_llvm_64bitsmakefile.machine(覆盖原文件)

执行make Client7z

 

在bin目录下可以找到7z.so,虽然这是.so后缀名的文件,实际上是一个bundle类型的macho对象。为了统一标识,我们可以将7z.so重命名为7z.bundle(但是在p7zip编译的7zClient内仍然使用7z.so这一文件名)

如果需要生成动态库,可以在最后链接时将参数-bundle 改成-shared 名字从bin/7z.so改成bin/7z.dylib

测试编译的文件能否正常使用

cd ~/Downloads/p7zip_16.02/bin/
./Client7z a 1.7z 7z.so

如果看到有1.7z生成,说明生成的7z.so可以使用

7zra.dll/7z.so/7z.dylib创建7z压缩包示例

主要使用到的函数以及我不太靠谱的翻译
(以下的代码我经过修改以便理解,但可能不能跨平台编译)

extern "C" CreateObject(const GUID *clsID, const GUID *iid, void **outObject);

创建 一个特定类型的 COM对象(我们需要的是7z的IOutArchive),其中有一些常量我们需要知道

const GUID CLSID_CFormat7z = { 0x23170F69, 0x40C1, 0x278A,{ 0x10, 0x00, 0x00, 0x01, 0x10,  0x07,  0x00,  0x00 } };//7z格式
const GUID IID_IOutArchive = { 0x23170F69 ,0x40C1 ,0x278A,{ 0x00, 0x00, 0x00, 0x06, 0x00,  0xA0,  0x00,  0x00} };//IOutArchive

然后还有7z格式的IOutArchive类,这个类可以创建7z格式压缩包

class IOutArchive
{
public :

	//...
	virtual  __declspec(nothrow) long __stdcall UpdateItems(ISequentialOutStream *outStream, UInt32 numItems, IArchiveUpdateCallback *updateCallback) = 0;
	//STDMETHOD(UpdateItems)(ISequentialOutStream *outStream, UInt32 numItems, IArchiveUpdateCallback *updateCallback);
	//...
        //release()
};

这个类中我们会用到方法有两个 UpdateItems 和 release,显然release是当我们使用完毕这个类要调用的一个释放函数。
UpdateItems的几个参数我稍微做一下说明(详细说明在iArchive.h内)

outStream: 压缩包文件的输出流,这个输出流必须要具有位置查找功能(创建7z格式的压缩文件),就是要有Write,Seek和SetSize这几个方法。
numItems: 要更新的文件个数
updateCallback: 回调函数,用来传递更新进度与获取每个要被更新文件的详细信息

然后是IArchiveUpdateCallback类

 

class UpdateCallbackExample_7z :
public IArchiveUpdateCallback,
public ICryptoGetTextPassword2,
public CMyUnknownImp
{
public:
MY_UNKNOWN_IMP2(IArchiveUpdateCallback, ICryptoGetTextPassword2)
//让7z库知道这个类实现了IArchiveUpdateCallback与ICryptoGetTextPassword2的接口

//INTERFACE_IProgress(x);
STDMETHOD(SetTotal)(UInt64 total);
//总进度
STDMETHOD(SetCompleted)(const UInt64 *completeValue);
//已完成进度

//INTERFACE_IArchiveUpdateCallback(PURE);
STDMETHOD(GetUpdateItemInfo)(UInt32 index, Int32 *newData, Int32 *newProps, UInt32 *indexInArchive);
//获取这个Item要更新的数据(newData是更新数据,newProps是更新属性)
STDMETHOD(GetProperty)(UInt32 index, PROPID propID, PROPVARIANT *value);
//获取文件的属性
STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **inStream);
//获取文件的输入流
STDMETHOD(SetOperationResult)(Int32 operationResult);
//当每一个被更新的item处理完会调用

//INTERFACE_IArchiveUpdateCallback2 !!! 7z.dll 不支持分卷功能,如果需要分卷,要自己在传入UpdateItems的outStream中处理
//STDMETHOD(GetVolumeSize)(UInt32 index, UInt64 *size);//每一个分卷大小  
//STDMETHOD(GetVolumeStream)(UInt32 index, ISequentialOutStream **volumeStream);//第index个文件的输出流

//ICryptoGetTextPassword2
STDMETHOD(CryptoGetTextPassword2)(Int32 *passwordIsDefined, BSTR *password);//加密设置(在默认情况下编译的7zra.dll中是不支持加密功能的)
//...
};

 

未完待续

 

致谢

本文使用到的7zip源码是遵循LGPL许可发布的,可以在http://www.7-zip.org下载

还有许多搜索到资料,由于太多太零散了,没在此列出