How to use 7z SDK to compress files in VC

Posted by Fruct0se on Tue, 13 Aug 2019 15:23:31 +0200

First login 7z official organization website https://www.7-zip.org/ Friends with poor English can switch to Chinese first.

Open the LZMA SDK page on the left and download the latest SDK and instructions as shown below.

After downloading, SDK packages support a variety of programming languages, where only C++ (CPP) is studied, and others are similar.

Firstly, it supports various decompression modules, corresponding to the following directories:

For these modules, in the lzma-sdk.txt file in the LZMA 1900 DOC directory, there is the following paragraph:

  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

 

These modules are mentioned here because later we will use them to compile dll to call VC.

For example, here we use D: 7z compression lzma1900 CPP 7zip Bundles Format7zR. The corresponding makefile has been provided for us in the SDK library. We just need to use the nmake tool provided in different versions of VS to compile.

Using the command tool of VS as shown above, you can directly use the cd command to enter the corresponding directory and compile nmake.

The compilation is completed as follows:

In the catalog shown below, the newly compiled 7zra.dll is a dynamic library for vc calls. Although there are many command implementations using ShellExcute to operate 7z.exe on the Internet, I have not used this method to ensure that the main program has only one execution file.

With the dynamic library 7zra.dll, we need to refer to the example project under D: lzma1900 CPP 7zip UI Client7z to trim our vc compression library and open the following workspace with VS:

The original project can be compiled:

However, the main function provided involves a lot of analysis and operation of the command line, which is a bit tedious. I have made some simplification and adjustment in many places, and sorted out a general function as follows. In addition, we need to understand the basic architecture of 7z sdk about compression. First, we need a Vector Vector Group CObjectVector < CDirItem > & dirItems. Each item of the Vector Vector Vector Group is actually used to store the information of a file that we need to compress. It will be used to transmit when Init (&dirItems) of the CArchiveUpdateCallback class is used. Enter. In addition, we need a path to the final compressed *.7z file, which archiveName is used to pass in Create(archiveName, true) when the CArchiveUpdateCallback class is built.

Understanding the compression process, decompression process has been relatively simple, left to the reader to achieve it.

In order to realize the commissioning of this project, we need to adjust the position of the project as follows.

In addition, don't forget to copy the previous library 7zra.dll into the generated execute file Client.exe directory.

 

As follows, I made some adjustments to Client7z.cpp in the following places, and the unused parts were commented and deleted directly.

In addition, the following functions are compiled to realize the compression of recursive subdirectories and simplify the main function.

//Encapsulated File Function for Getting Compressed Folder List
bool GetArchiveItemFromPath(const char * strDirPath,const char * parentDirPath,CObjectVector<CDirItem> &dirItems)
{
	//Note that there is a function CmdStringToFString that converts from const char * to FString, but I'm not using it. Instead, I use the macros provided by the library directly.

	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											//Recursively traverses all the directories and files that contain them and records them
	{
		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;
		//Be sure to assign the full path under the compressed root path, otherwise you can't compress the contents of subdirectories and recursively compress the contents of subdirectories, and their contents will be empty.
		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);	//Recursion of the contents of all subdirectories
		}

	}while(findFile.FindNext(fileInfo));

	findFile.Close();

	return true;
}

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

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

	Func_CreateObject createObjectFunc = (Func_CreateObject)lib.GetProc("CreateObject");	//Export CreateObject function pointer
	if (!createObjectFunc)
	{
		PrintError("Can not get CreateObject");
		return 1;
	}

	FString archiveName = fas2fs(ArchivePackPath);			//Compressed file path

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

	if (!outFileStreamSpec->Create(archiveName, false))			//Create a compressed file
	{
		PrintError("can't create archive file");
		return 1;
	}

	CMyComPtr<IOutArchive> outArchive;
	if (createObjectFunc(&CLSID_Format, &IID_IOutArchive, (void **)&outArchive) != S_OK)	//Component Compression Object
	{
		PrintError("Can not get class object");
		return 1;
	}

	CArchiveUpdateCallback *updateCallbackSpec = new CArchiveUpdateCallback;
	CMyComPtr<IArchiveUpdateCallback2> updateCallback(updateCallbackSpec);
	updateCallbackSpec->Init(&dirItems);										//Initialization of Compression Action Execution Class with Project Group to be Compressed

	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)			//Determine several file compression failures
		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;	//Purely eliminating compilation warnings makes no sense
	args = args;
	CObjectVector<CDirItem> ItemList;
	GetArchiveItemFromPath("D:\\Mycomputer\\abc",NULL,ItemList);
	ArchiveFile(ItemList,"D:\\Mycomputer\\abc\\CPP.7Z");
  
	return 0;
}

During the test, I used the following three-level subdirectory structure, which can compress it normally.

 

If you need the following source package research, you can download the source code in the link.

Topics: SDK Programming Makefile Windows