Click here to Skip to main content
15,498,859 members
Articles / General Programming / Compression
Article
Posted 24 Aug 2018

Stats

14.5K views
5 bookmarked

Creating a .cab Archive from One or More Files

Rate me:
Please Sign up or sign in to vote.
5.00/5 (11 votes)
24 Aug 2018CPOL2 min read
The MakeCab tool is built-in in Windows but using it to create a .cab archive is a bit tricky. Why not write a small program that will do that for you?

Introduction

Microsoft requires Drivers developers who wish to qualify them for Windows 10, to pack the drivers files into a single cab and code sign it. I was looking for a way to do so programmatically. I found the MakeCab tool but from first look, it allows passing one parameter for the file, so I was looking for the easiest way to pack several files.

Background

The .Cab format seems to be a bit outdated. It was created by Microsoft when files which were part of a Setup application needed to be packed into disks.

I read the article Cabinet File Compression and Extraction.

I was hoping to find a simpler way to create .cab files, which is what brought me to write this article.

Even today, when a .cab file is created, it will be created in folders named "Disk1", "Disk2", etc. My code also simplifies that by allowing a simple function call:

C++
CreateCabFromFiles(TargetCabName, n, File1,File2,File3, ...);

For example:

C++
CreateCabFromFiles(L"test.cab",5,L"aaa.txt",L"bbb.txt",L"ccc.txt",L"ddd.txt",L"eee.txt");

The target cab will be placed next to the files.

Another interesting fact related to MakeCab is that the only way to add several files into a new .cab would be creating a file containing a list of all files to be added. My function does that for you. It then cleans up and you will only find the .cab created.

The Building Blocks

I will start by showing you a few building blocks we use in Secured Globe, Inc. First, a function that is used to execute a command, as if it has been typed and executed via CMD, collecting the result and displaying it back to you. In case of an error, composing a friendly error description.

C++
bool DoRun(WCHAR *command)
{
    DWORD retSize;
    LPTSTR pTemp = NULL;
    TCHAR Command[BUFSIZE] = L"";
    DeleteFile(RESULTS_FILE);
    _tcscpy_s(Command, L"/C ");
    _tcscat_s(Command, command);
    _tcscat_s(Command, L" >");
    _tcscat_s(Command, RESULTS_FILE);
    wprintf(L"Calling:\n%s\n", Command);
    bool result = ShellExecute(GetActiveWindow(), L"OPEN", L"cmd", Command, NULL, 0L);
    Sleep(1000);
    if (result)
    {
        std::FILE *fp = _wfopen(RESULTS_FILE, L"rb");
        if (fp)
        {
            std::string contents;
            std::fseek(fp, 0, SEEK_END);
            contents.resize(std::ftell(fp));
            std::rewind(fp);
            std::fread(&contents[0], 1, contents.size(), fp);
            std::fclose(fp);
            CString temp1 = (CString)(CStringA)(contents.c_str());
            wprintf(L"Result:\n%s\n", temp1.GetBuffer());
        }
    }
    else
    {
        retSize = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
            FORMAT_MESSAGE_FROM_SYSTEM |
            FORMAT_MESSAGE_ARGUMENT_ARRAY,
            NULL,
            GetLastError(),
            LANG_NEUTRAL,
            (LPTSTR)&pTemp,
            0,
            NULL);
        return(L"Error: %s\n", pTemp);
    }
}

So basically, we will be generating the list of files and then calling MakeCab with the proper parameters and bring back the results.

The CreateCabFromFiles Function

There are several consts to be defined:

C++
#define RESULTS_FILE L"result.txt"
#define FILELIST_FILE L"files.txt"
#define MAKECAB_COMMAND L"makecab /d CabinetName1=%s  /f %s"
#define CAB_DEF_FOLDER L"disk1"

We assume that for the scope of our function, there will be a single "disk" created which is named by default as "disk1".

Here is the CreateCabFromFiles() function:

C++
//
bool CreateCabFromFiles(LPWSTR TargetCab, int argc, ...)
{
    va_list ptr;
    va_start(ptr, argc);

    FILE *fp = _wfopen(FILELIST_FILE, L"w");
    if (fp)
    {
        for (int i = 0; i < argc; i++)
        {
            LPWSTR *filetowrite = va_arg(ptr, LPWSTR *);
            fwprintf(fp, L"%s\n", filetowrite);
        }
        fclose(fp);
        CString command;
        command.Format(MAKECAB_COMMAND, TargetCab, FILELIST_FILE);
        if (DoRun(command.GetBuffer()))
        {
            if (CopyFile(CAB_DEF_FOLDER + (CString)L"\\" + TargetCab, TargetCab, FALSE))
            {
                wprintf(L"Created cab file: %s\n", TargetCab);
                DeleteFile(CAB_DEF_FOLDER + (CString)L"\\" + TargetCab);
                RemoveDirectory(CAB_DEF_FOLDER);
                DeleteFile(FILELIST_FILE);
                return true;
            }
        }
    }
    return true;
}

//

The Clean Up

To save the need to open "disks" and look for the created .cab, and also to delete files created for the purpose of properly "feeding" MakeCab, the cleanup of this function includes the following steps:

  1. The .cab file is copied from "disk1" to the current path.
  2. The "disk1" folder is emptied and deleted.
  3. The file used to host the list of files to be added, is deleted.
  4. The "results" file used for our DoRun() function is also deleted.

Testing the Code

I used the following function call and found that it created "drivers.cab" next to the 3 Driver files.

C++
CreateCab(L"drivers.cab", 3,L"drv.sys", L"drv.inf", L"drv.cat");

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
CEO Secured Globe, Inc.
United States United States
Michael Haephrati is a music composer, an inventor and an expert specializes in software development and information security, who has built a unique perspective which combines technology and the end user experience. He is the author of a the book Learning C++ , which teaches C++ 20, and was published in August 2022.

He is the CEO of Secured Globe, Inc., and also active at Stack Overflow.

Read our Corporate blog or read my Personal blog.





Comments and Discussions

 
GeneralMy vote of 5 Pin
Ahmad Mahmoud [candseeme]25-Aug-18 5:41
MemberAhmad Mahmoud [candseeme]25-Aug-18 5:41 
GeneralRe: My vote of 5 Pin
Michael Haephrati25-Aug-18 5:59
mvaMichael Haephrati25-Aug-18 5:59 
GeneralRe: My vote of 5 Pin
Ahmad Mahmoud [candseeme]25-Aug-18 7:28
MemberAhmad Mahmoud [candseeme]25-Aug-18 7:28 
AnswerRe: My vote of 5 Pin
Michael Haephrati25-Aug-18 8:25
mvaMichael Haephrati25-Aug-18 8:25 
AnswerMy vote of 5 Pin
Hans Richter25-Aug-18 1:47
MemberHans Richter25-Aug-18 1:47 
GeneralMy vote of 5 Pin
Jim Forst24-Aug-18 17:42
MemberJim Forst24-Aug-18 17:42 
Suggestionpass an array of files instead Pin
Ahmad Mahmoud [candseeme]24-Aug-18 15:04
MemberAhmad Mahmoud [candseeme]24-Aug-18 15:04 
SuggestionRe: pass an array of files instead Pin
John Klinner24-Aug-18 17:27
MemberJohn Klinner24-Aug-18 17:27 
GeneralRe: pass an array of files instead Pin
Jim Forst24-Aug-18 17:56
MemberJim Forst24-Aug-18 17:56 
GeneralRe: pass an array of files instead Pin
Ahmad Mahmoud [candseeme]25-Aug-18 7:56
MemberAhmad Mahmoud [candseeme]25-Aug-18 7:56 
GeneralRe: pass an array of files instead Pin
Michael Haephrati26-Aug-18 0:17
mvaMichael Haephrati26-Aug-18 0:17 
Ahmed, no problem Smile | :) but please see the screenshot I sent you, somehow your comment was logged as a vote of "1", but then you voted "5" (thanks!), so kindly delete your comment just for the sake of removing this insulting "1".
- Michael Haephrati מיכאל האפרתי

GeneralRe: pass an array of files instead Pin
Ahmad Mahmoud [candseeme]26-Aug-18 0:34
MemberAhmad Mahmoud [candseeme]26-Aug-18 0:34 
GeneralRe: pass an array of files instead Pin
Michael Haephrati26-Aug-18 0:55
mvaMichael Haephrati26-Aug-18 0:55 
GeneralRe: pass an array of files instead Pin
Michael Haephrati28-Aug-18 5:21
mvaMichael Haephrati28-Aug-18 5:21 
GeneralRe: pass an array of files instead Pin
Ahmad Mahmoud [candseeme]28-Aug-18 10:29
MemberAhmad Mahmoud [candseeme]28-Aug-18 10:29 
GeneralRe: pass an array of files instead Pin
Michael Haephrati29-Aug-18 5:36
mvaMichael Haephrati29-Aug-18 5:36 
GeneralRe: pass an array of files instead Pin
Ahmad Mahmoud [candseeme]1-Sep-18 23:21
MemberAhmad Mahmoud [candseeme]1-Sep-18 23:21 
AnswerRe: pass an array of files instead Pin
Michael Haephrati3-Sep-18 6:23
mvaMichael Haephrati3-Sep-18 6:23 
GeneralRe: pass an array of files instead Pin
Michael Haephrati25-Aug-18 1:21
mvaMichael Haephrati25-Aug-18 1:21 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.