Creating a .cab archive from one or more files

August 25, 2018

Introduction

Microsoft requires drivers developers 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 right 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. 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:

 

CreateCabFromFiles(TargetCabName, NoFIles, File1,File2,File3);

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.

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:

#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

//

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;
}
 

//

 

Remember to set the Language of your code snippet using the Language dropdown.

Use the "var" button to to wrap Variable or class names in <code> tags like this.


Points of Interest

Did you learn anything interesting/fun/annoying while writing the code? Did you do anything particularly clever or wild or zany?


History

Keep a running update of any changes or improvements you've made here.

 

Please reload

Featured Posts

Integrating a Desktop application with Pay Pal in c++

July 3, 2018

1/4
Please reload

Recent Posts

March 14, 2017

Please reload

Archive