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:
CreateCabFromFiles(TargetCabName, n, File1,File2,File3, ...);
For example:
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.
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 const
s 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;
}
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:
- The .cab file is copied from "disk1" to the current path.
- The "disk1" folder is emptied and deleted.
- The file used to host the list of files to be added, is deleted.
- 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.
CreateCab(L"drivers.cab", 3,L"drv.sys", L"drv.inf", L"drv.cat");