如何在VC中使用7z SDK压缩文件

▼魔方 西西 提交于 2019-11-27 05:55:43

首先登陆7z官方组织网站https://www.7-zip.org/,英文不好的朋友可先切换至中文。

打开左边LZMA SDK页面,下载如下图最新SDK和说明文档。

下载好后,SDK包内容对多种编程语言进行了支持,这里只研究C++(即CPP),其他类似。

首先是对各种加解压缩模块的支持,对应如下目录:

关于这些模块,在lzma1900\DOC目录下的lzma-sdk.txt文件中,有如下一段说明:

  Bundles  - Modules that are bundles of other modules (files)

      Alone7z       - 7zr.exe: Standalone 7-Zip console program (reduced version)

      Format7zExtractR  - 7zxr.dll: Reduced version of 7z DLL: extracting from 7z/LZMA/BCJ/BCJ2.

      Format7zR         - 7zr.dll:  Reduced version of 7z DLL: extracting/compressing to 7z/LZMA/BCJ/BCJ2

      LzmaCon       - lzma.exe: LZMA compression/decompression

      LzmaSpec      - example code for LZMA Specification

      SFXCon        - 7zCon.sfx: Console 7z SFX module

      SFXSetup      - 7zS.sfx: 7z SFX module for installers

      SFXWin        - 7z.sfx: GUI 7z SFX module

 

这里之所以提到这些模块,是因为后面我们要用到他们编译dll,来给VC调用。

如这里我们用到的是D:\7z压缩\lzma1900\CPP\7zip\Bundles\Format7zR,SDK库中已经为我们提供了对应的makefile文件。我们只需要使用不同版本的VS中提供的nmake工具,来进行编译即可。

使用如上图VS的命令工具,直接使用cd命令进到对应目录下,nmake编译即可。

编译完成后如下:

如下图目录中,新编译出的如下7zra.dll,即为我们需要的供vc调用的动态库,虽然网上也提供了很多用ShellExcute去操作7z.exe等的命令实现方式,但是为了保证主程序只有一个执行文件,这里我没有采用这种方式。

有了动态库7zra.dll,接下来我们需要参阅D:\lzma1900\CPP\7zip\UI\Client7z下的示例工程,修整出我们的vc压缩库,用VS打开如下工作空间:

原始工程即可通过编译:

但提供的main函数中涉及了很多对命令行的解析和操作,有点冗长,我对很多地方做了一些简化和调整,整理出如下的一个通用函数。此外,我们需要明白7z sdk 关于压缩这一块的基本架构,首先我们需要一个Vector向量组CObjectVector<CDirItem> &dirItems,它的每个项目其实就是用来存放我们需要压缩的某个文件的信息,它将被用于在CArchiveUpdateCallbackInit(&dirItems)时传入。此外,我们还需要一个最终压缩得到的*.7z的最终压缩文件的路径,该路径archiveName被用于在CArchiveUpdateCallback类构建时传入Create(archiveName, true)。

了解了压缩的过程,解压缩过程已经比较简单了,留给读者自行实现即可。

这里为了实现调试该项目,我们需要对该工程做如下位置的调整。

此外,别忘了把之前的库7zra.dll拷贝到生成的执行文件Client.exe目录下。

 

如下我对Client7z.cpp做了以下位置的一些调整,没有用到的部分直接注释和删除了。

另编写了如下的几个函数,来实现递归子级目录的压缩,简化了main函数的内容。

//封装的获取压缩文件夹列表文件函数
bool GetArchiveItemFromPath(const char * strDirPath,const char * parentDirPath,CObjectVector<CDirItem> &dirItems)
{
	//注意这里提供了一个从const char * 转换到FString的函数CmdStringToFString,
    //但我没有用,而是直接用的库提供的宏来做转换

	NWindows::NFile::NFind::CFindFile findFile;
	NWindows::NFile::NFind::CFileInfo fileInfo;
	
	FString dirPath = fas2fs(strDirPath);

	bool bRet = findFile.FindFirst(dirPath + fas2fs("\\*.*"),fileInfo);
	if (bRet == false)return bRet;

	do				//递归遍历出所有的包含目录和文件并记录
	{
		if (fileInfo.IsDots())
		{
			continue;
		}

		CDirItem di;
		di.Attrib = fileInfo.Attrib;
		di.Size = fileInfo.Size;
		di.CTime = fileInfo.CTime;
		di.ATime = fileInfo.ATime;
		di.MTime = fileInfo.MTime;

	    //一定要赋值为压缩根路径下的全路径,否则不能压缩子目录内容及递归压缩子
         //目录的子目录下的内容,他们的内容将是空的

		if (parentDirPath != NULL)		
		{
			di.Name = fas2fs(parentDirPath);
			di.Name += fas2fs("\\");
			di.Name += fileInfo.Name;
		}
		else
		{
			di.Name = fileInfo.Name;
		}
		di.FullPath = dirPath;
		di.FullPath+= fas2fs("\\");
		di.FullPath+=fileInfo.Name;
		dirItems.Add(di);

		if (fileInfo.IsDir())
		{
			FString parentPath;
			if (parentDirPath != NULL)
			{
				parentPath = fas2fs(parentDirPath);
				parentPath += fas2fs("\\");
				parentPath += fileInfo.Name;
			}
			else
			{
				parentPath = fileInfo.Name;
			}
			FString fFullPath = dirPath;
			fFullPath+= fas2fs("\\");
			fFullPath+=fileInfo.Name;

            //递归所有的子级目录的内容
			GetArchiveItemFromPath(fs2fas(fFullPath),fs2fas(parentPath),dirItems);	
		}

	}while(findFile.FindNext(fileInfo));

	findFile.Close();

	return true;
}

DWORD ArchiveFile(CObjectVector<CDirItem> &dirItems,const char * ArchivePackPath)
{
	NT_CHECK

	NDLL::CLibrary lib;

    //加载7zra.dll库
	if (!lib.Load(NDLL::GetModuleDirPrefix() + FTEXT(kDllName)))
	{
		PrintError("Can not load 7-zip library");
		return 1;
	}

    //导出CreateObject函数指针
	Func_CreateObject createObjectFunc = (Func_CreateObject)lib.GetProc("CreateObject");	
	if (!createObjectFunc)
	{
		PrintError("Can not get CreateObject");
		return 1;
	}

	FString archiveName = fas2fs(ArchivePackPath);		//压缩文件路径

	COutFileStream *outFileStreamSpec = new COutFileStream;
	CMyComPtr<IOutStream> outFileStream = outFileStreamSpec;

	if (!outFileStreamSpec->Create(archiveName, false))	  //创建压缩文件
	{
		PrintError("can't create archive file");
		return 1;
	}

	CMyComPtr<IOutArchive> outArchive;
    //构件压缩对象
	if (createObjectFunc(&CLSID_Format, &IID_IOutArchive, (void **)&outArchive) != S_OK)	
	{
		PrintError("Can not get class object");
		return 1;
	}

	CArchiveUpdateCallback *updateCallbackSpec = new CArchiveUpdateCallback;
	CMyComPtr<IArchiveUpdateCallback2> updateCallback(updateCallbackSpec);
	updateCallbackSpec->Init(&dirItems);		//用待压缩项目组初始化压缩动作执行类

	HRESULT result = outArchive->UpdateItems(outFileStream, dirItems.Size(), updateCallback);

	updateCallbackSpec->Finilize();

	if (result != S_OK)
	{
		PrintError("Update Error");
		return 1;
	}

	FOR_VECTOR (i, updateCallbackSpec->FailedFiles)
	{
		PrintNewLine();
		PrintError("Error for file", updateCallbackSpec->FailedFiles[i]);
	}

	if (updateCallbackSpec->FailedFiles.Size() != 0)    //判断有几个文件压缩失败
		return 1;

	return 0;
}
// Main function
#define NT_CHECK_FAIL_ACTION PrintError("Unsupported Windows version"); return 1;

int MY_CDECL main(int numArgs, const char *args[])
{
	numArgs = numArgs;	//纯粹消除编译警告,无任何意义
	args = args;
	CObjectVector<CDirItem> ItemList;
	GetArchiveItemFromPath("D:\\Mycomputer\\abc",NULL,ItemList);
	ArchiveFile(ItemList,"D:\\Mycomputer\\abc\\CPP.7Z");
  
	return 0;
}

测试时我使用了如下的三级子目录结构,可以正常实现对其的压缩。

 

如需如下源码包研究的,可在链接中下载源码。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!