如何创建应用程序包(C ++)

时间:2023-02-21 08:10:06

 备注

如果您要创建UWP应用程序包,请参阅使用MakeAppx.exe工具创建应用程序包。

 

了解如何使用打包API为Windows应用商店应用创建应用包。

如果要手动创建桌面应用程序包,还可以使用使用打包API的MakeAppx.exe工具。有关详细信息,请参阅App packager(MakeAppx.exe)。

如果您使用的是Visual Studio,建议您使用Visual Studio打包向导打包您的应用程序。有关更多详细信息,请参阅打包UWP应用程序。

说明

第1步:创建包编写器

要创建包编写器,请调用IAppxFactory :: CreatePackageWriter方法。第一个参数是将写入包的输出流。第二个参数是指向APPX_PACKAGE_SETTINGS结构的指针,该结构指定包设置。第三个参数是一个输出参数,它接收指向IAppxPackageWriter指针的指针。

C ++
#include <windows.h>
#include <shlwapi.h>
#include <AppxPackaging.h>
// We store the produced package under this file name.
const LPCWSTR OutputPackagePath = L"HelloWorld.appx";
int wmain() {
    HRESULT hr = S_OK;
    // Specify the appropriate COM threading model
    hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
    if (SUCCEEDED(hr))
    {
        // Create a package writer
        IAppxPackageWriter* packageWriter = NULL;
        hr = GetPackageWriter(OutputPackagePath, &packageWriter);
    }
}
//
// Creates an app package writer with default settings.
//
// Parameters:
// outputFileName 
// Fully qualified name of the app package (.appx file) to be created.
// writer 
// On success, receives the created instance of IAppxPackageWriter.
//
HRESULT GetPackageWriter( _In_ LPCWSTR outputFileName, _Outptr_ IAppxPackageWriter** writer) {
    const LPCWSTR Sha256AlgorithmUri = L"https://www.w3.org/2001/04/xmlenc#sha256"; 
    HRESULT hr = S_OK;
    IStream* outputStream = NULL;
    IUri* hashMethod = NULL;
    APPX_PACKAGE_SETTINGS packageSettings = {0};
    IAppxFactory* appxFactory = NULL;
    // Create a stream over the output file for the package 
    hr = SHCreateStreamOnFileEx(
            outputFileName,
            STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE,
            0,     // default file attributes
            TRUE,  // create file if it does not exist
            NULL,  // no template
            &outputStream);
    // Create default package writer settings, including hash algorithm URI
    // and Zip format.
    if (SUCCEEDED(hr))
    {        hr = CreateUri(
                Sha256AlgorithmUri,
                Uri_CREATE_CANONICALIZE,
                0, // reserved parameter
                &hashMethod);
    }
    if (SUCCEEDED(hr))
    {
        packageSettings.forceZip32 = TRUE;
        packageSettings.hashMethod = hashMethod;
    }
    // Create a new Appx factory
    if (SUCCEEDED(hr))
    {
        hr = CoCreateInstance(
                __uuidof(AppxFactory),
                NULL,
                CLSCTX_INPROC_SERVER,
                __uuidof(IAppxFactory),
                (LPVOID*)(&appxFactory));
    }
    // Create a new package writer using the factory
    if (SUCCEEDED(hr))
    {
        hr = appxFactory->CreatePackageWriter(
                outputStream,
                &packageSettings,
                writer);
    }
    // Clean up allocated resources
    if (appxFactory != NULL)
    {
        appxFactory->Release();
        appxFactory = NULL;
    }
    if (hashMethod != NULL)
    {
        hashMethod->Release();
        hashMethod = NULL;
    }
    if (outputStream != NULL)
    {
        outputStream->Release();
        outputStream = NULL;
    }
    return hr;
}

第2步:将应用程序的有效内容文件添加到包中

调用IAppxPackageWriter :: AddPayloadFile方法将文件添加到包中。第一个参数是文件的相对路径。第二个参数表示文件的内容类型。第三个参数指定APPX_COMPRESSION_OPTION枚举中的选项。第四个参数是文件的输入流。以下效果展示

 黄铜浮球阀 不锈钢法兰浮球阀 不锈钢水箱浮球阀 Q941F-16P不锈钢电动球阀 丝扣浮球阀 黄铜小孔浮球阀 Z45X暗杆软密封闸阀 GLY储油罐量油孔 FZT防爆阻火呼吸人孔 CWX微型电动球阀 Q941F电动开关球阀 Q941F电动法兰球阀 Q911F电动内螺纹球阀 Q671F气动对夹式球阀 Q611N气动高压球阀 Q641F气动带手动球阀 Q611F气动三片式球阀 Q611F气动两片式球阀 Q641F气动调节球阀 Q971F电动对夹球阀 Q641F不锈钢气动球阀 不锈钢高压球阀 超高压球阀 Q41Y高压硬密封球阀 QJ41M耐高温球阀 QJ41M不锈钢高温球阀 法兰高温球阀 Q41H高温硬密封球阀 Q41H金属硬密封高温球阀 (Q45F)T型三通球阀 (Q44F)L型三通球阀 KHB3K高压三通球阀 BQ71F薄型保温球阀 Q11F螺纹球阀 Q11F内螺纹球阀 Q21F外螺纹球阀 Q41F法兰球阀 Q41F法兰式球阀 Q61F焊接球阀 Q61F全焊接球阀 Q61F焊接式球阀 Q71F对夹球阀 Q71F对夹式球阀 Q61N对焊球阀 Q91F卡套球阀 Q91F不锈钢卡套球阀 Q41N锻钢球阀 Q61N焊接锻钢球阀 Q41N高压锻钢球阀 KQ41F不锈钢抗硫球阀 切断球阀 气动切断球阀-气动O型切断球阀 电动切断球阀-电动O型切断球阀 Q46F不锈钢四通球阀 Q46F四通球阀 Q41F燃气球阀 Q41F燃气专用球阀 Q81F卫生球阀 Q81F快装球阀 Q81F卫生级快装球阀 Q81F卫生级球阀 Q681F卫生级气动球阀 广式球阀 Q11F广式内螺纹球阀 Q21F广式外螺纹球阀 Q41F广式法兰球阀 Q11F丝口球阀 Q11F丝扣球阀 V型球阀 电动V型球阀 气动V型球阀 YJZQ高压液压球阀 KHBF-KHMF法兰式高压球阀 CJZQ高压球阀 VH3V二位三通高压球阀 BKH-MKH高压球阀 VH2V直通高压球阀 BME高压球阀 KHB高压球阀 KHP-PKH高压板式球阀 (喷煤粉)卸灰球阀 Q647MF气动卸灰球阀(喷煤粉专用球阀)

C ++
// Path where all input files are stored
const LPCWSTR DataPath = L"Data\\";
// Add all payload files to the package writer
for (int i = 0; SUCCEEDED(hr) && (i < PayloadFilesCount); i++)
{
    IStream* fileStream = NULL;
    hr = GetFileStream(DataPath, PayloadFilesName[i], &fileStream);
    if (SUCCEEDED(hr))
    {
        packageWriter->AddPayloadFile(
            PayloadFilesName[i],
            PayloadFilesContentType[i],
            PayloadFilesCompression[i],
            fileStream);
        }
        if (fileStream != NULL)
        {
            fileStream->Release();
            fileStream = NULL;
        }
    }
}

前面的代码使用这些变量定义和GetFileStream辅助函数。

C ++
#include <strsafe.h>
#include <shlwapi.h>
// The produced app package's content consists of these files, with
// corresponding content types and compression options.
const int PayloadFilesCount = 4;
const LPCWSTR PayloadFilesName[PayloadFilesCount] = {
    L"AppTile.png",
    L"Default.html",
    L"images\\smiley.jpg",
    L"Error.html",
};
const LPCWSTR PayloadFilesContentType[PayloadFilesCount] = {
    L"image/png",
    L"text/html",
    L"image/jpeg",
    L"text/html",
};
const APPX_COMPRESSION_OPTION PayloadFilesCompression[PayloadFilesCount] = {
    APPX_COMPRESSION_OPTION_NONE,
    APPX_COMPRESSION_OPTION_NORMAL,
    APPX_COMPRESSION_OPTION_NONE,
    APPX_COMPRESSION_OPTION_NORMAL,
};
//
// Creates a readable IStream over the specified file. For simplicity, we assume that the fully 
// qualified file name is 100 characters or less. Your code should 
// handle longer names, and allocate the buffer dynamically.
//
// Parameters:
// path 
// Path of the folder that contains the file to be opened; must end with a '\'
// fileName 
// Name, of the file to be opened, not including the path
// stream 
// On success, receives the created instance of IStream
//
HRESULT GetFileStream( _In_ LPCWSTR path, _In_ LPCWSTR fileName, _Outptr_ IStream** stream) {
    HRESULT hr = S_OK;
    const int MaxFileNameLength = 100;
    WCHAR fullFileName[MaxFileNameLength + 1];
    // Create full file name by concatenating path and fileName
    hr = StringCchCopyW(fullFileName, MaxFileNameLength, path);
    if (SUCCEEDED(hr))
    {
        hr = StringCchCat(fullFileName, MaxFileNameLength, fileName);
    }
    // Create stream for reading the file
    if (SUCCEEDED(hr))
    {
        hr = SHCreateStreamOnFileEx(
                fullFileName,
                STGM_READ | STGM_SHARE_EXCLUSIVE,
                0,      // default file attributes
                FALSE,  // don't create new file
                NULL,   // no template
                stream);
    }
    return hr;
}

第3步:将包清单添加到包中

每个包都必须有一个包清单。要将包清单添加到包中,请为该文件创建输入流,然后调用IAppxPackageWriter :: Close方法在包的末尾写入清单并关闭包编写器的输出流。

此代码使用GetFileStream上一步中显示的帮助程序函数为程序包清单创建流。

C ++
// We read the app package's manifest from this file
const LPCWSTR ManifestFileName = L"AppxManifest.xml";
IStream* manifestStream = NULL;
hr = GetFileStream(DataPath, ManifestFileName, &manifestStream);
if (SUCCEEDED(hr))
{
    hr = packageWriter->Close(manifestStream);
}
if (manifestStream != NULL)
{
    manifestStream->Release();
    manifestStream = NULL;
}

第4步:清理包编写器

在从wmain函数返回之前,调用Release方法来清理包编写器并调用CoUninitialize函数。

C ++
if (packageWriter != NULL)
{
    packageWriter->Release();
    packageWriter = NULL;
}
CoUninitialize();