65.9K
CodeProject 正在变化。 阅读更多。
Home

从一个或多个文件创建 .cab 压缩包

starIconstarIconstarIconstarIconstarIcon

5.00/5 (11投票s)

2018年8月24日

CPOL

2分钟阅读

viewsIcon

24421

MakeCab 工具内置于 Windows 中,但使用它创建 .cab 压缩包有点棘手。为什么不编写一个小程序来为你完成这项任务呢?

引言

微软要求希望将其驱动程序认证为 Windows 10 的开发者,需要将驱动程序文件打包成单个 cab 并进行代码签名。我正在寻找一种以编程方式实现的方法。我找到了 MakeCab 工具,但初步看来,它只允许传递一个文件参数,所以我正在寻找将多个文件打包的最简单方法。

背景

.Cab 格式 似乎有点过时了。它是由微软在需要将属于安装应用程序的文件打包到磁盘时创建的。

我阅读了 Cabinet 文件压缩和提取 这篇文章。

我希望找到一种更简单的方法来创建 .cab 文件,这促使我写这篇文章。

即使在今天,当创建一个 .cab 文件时,它也会在名为“Disk1”、“Disk2”等文件夹中创建。我的代码也通过允许简单的函数调用来简化这一点。

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

例如

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

目标 cab 文件将放置在文件的旁边。

关于 MakeCab 的另一个有趣的事实是,将多个文件添加到新的 .cab 的唯一方法是创建一个包含所有要添加的文件列表的文件。我的函数为你执行此操作。然后它会清理,你只会找到创建的 .cab

构建块

我将首先向你展示我们在 Secured Globe, Inc. 中使用的一些构建块。首先,一个用于执行命令的函数,就像它已在 CMD 中键入并执行一样,收集结果并将其显示给你。如果发生错误,则编写友好的错误描述。

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

基本上,我们将生成文件列表,然后使用适当的参数调用 MakeCab 并返回结果。

CreateCabFromFiles 函数

有几个 const 需要定义

#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"

我们假设对于函数的范围,将创建一个名为“disk1”的单个“磁盘”。

这是 CreateCabFromFiles() 函数

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

//

清理

为了避免打开“磁盘”并查找创建的 .cab,以及删除为正确“馈送”MakeCab 而创建的文件,此函数清理包括以下步骤

  1. .cab 文件从“disk1”复制到当前路径。
  2. disk1”文件夹被清空并删除。
  3. 用于托管要添加的文件列表的文件被删除。
  4. 用于我们的 DoRun() 函数的“results”文件也被删除。

测试代码

我使用了以下函数调用,发现它在 3 个驱动程序文件旁边创建了“drivers.cab”。

CreateCab(L"drivers.cab", 3,L"drv.sys", L"drv.inf", L"drv.cat");
© . All rights reserved.