How to create an application package (C++)

Posted by mannyee on Mon, 06 May 2019 01:00:04 +0200

Notes

If you want to create a UWP application package, see Creating an application package using the MakeAppx.exe tool.

 

Learn how to use the Packaging API to create application packages for Windows App Store applications.

If you want to manually create a desktop application package, you can also use the MakeAppx.exe tool, which uses the packaged API.For more information, see App packager (MakeAppx.exe).

If you are using Visual Studio, it is recommended that you package your application using the Visual Studio Packaging Wizard.For more details, see Packaging UWP applications.

Explain

Step 1: Create a package writer

To create a package writer, call the IAppxFactory:: CreatePackageWriter method.The first parameter is the output stream that will be written to the package.The second parameter is a pointer to the APPX_PACKAGE_SETTINGS structure, which specifies the package settings.The third parameter is an output parameter that receives a pointer to the IAppxPackageWriter pointer.

C++ Replication
#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;
}

Step 2: Add a valid content file for your application to the package

Call IAppxPackageWriter:: The AddPayloadFile method adds the file to the package.The first parameter is the relative path of the file.The second parameter represents the content type of the file.The third parameter specifies the options in the APPX_COMPRESSION_OPTION enumeration.The fourth parameter is the input stream of the file.The following effects are shown

 Brass float valve, Stainless steel flanged floating ball valve, Stainless steel water tank float valve, Q941F-16P Stainless Steel Electric Ball Valve, Button floating ball valve, Brass small hole floating ball valve, Z45X Dark Rod Soft Sealed Gate Valve, GLY Storage Tank Volume Hole, FZT explosion-proof fire-proof breathing manhole, CWX Micro Electric Ball Valve, Q941F Electric Switch Ball Valve, Q941F Electric Flanged Ball Valve, Q911F Electric Internal Threaded Ball Valve, Q671F Pneumatic Clamp Ball Valve, Q611N Pneumatic High Pressure Ball Valve, Q641F Pneumatic Manual Ball Valve, Q611F Pneumatic Three-piece Ball Valve, Q611F Pneumatic Two-piece Ball Valve, Q641F Pneumatic Adjusting Ball Valve, Q971F Electric Spherical Valve, Q641F stainless steel pneumatic balloon valve, Stainless steel high pressure ball valve, Super High Pressure Ball Valve, Q41Y High Pressure Hard Sealed Ball Valve, QJ41M High Temperature Resistant Ball Valve, QJ41M Stainless Steel High Temperature Ball Valve, Flanged high temperature ball valve, Q41H High Temperature Hard Sealed Ball Valve, Q41H Metal Hard Sealed High Temperature Ball Valve, (Q45F) T-type three-way ball valve, (Q44F) L-type three-way ball valve, KHB3K High Pressure Triple-pass Ball Valve, BQ71F thin insulation ball valve, Q11F Threaded Ball Valve, Q11F Internal Threaded Ball Valve, Q21F Outer Threaded Ball Valve, Q41F Flanged Ball Valve, Q41F Flanged Ball Valve, Q61F Welded Ball Valve, Q61F Fully Welded Ball Valve, Q61F Welded Ball Valve, Q71F ball-clip valve, Q71F pair Clip Ball Valve, Q61N butt welded ball valve, Q91F card sleeve ball valve, Q91F Stainless Steel Sleeve Ball Valve, Q41N Forged Steel Ball Valve, Q61N Welded Forged Steel Ball Valve, Q41N High Pressure Forged Steel Ball Valve, KQ41F Stainless Steel Sulfur-resistant Ball Valve, Cut off ball valve, Pneumatic Cut-off Ball Valve-Pneumatic O-type Cut-off Ball Valve, Electric Cut-off Ball Valve-Electric O-type Cut-off Ball Valve, Q46F stainless steel four-way ball valve, Q46F four-way ball valve, Q41F Gas Ball Valve, Q41F Gas Ball Valve, Q81F Ball Valve, Q81F Fast Loading Ball Valve, Q81F Hygienic Quick Loading Ball Valve, Q81F Hygienic Ball Valve, Q681F Hygienic Pneumatic Ball Valve, Canton ball valve, Q11F Wide Internal Threaded Ball Valve, Q21F External Threaded Ball Valve, Q41F wide flange ball valve, Q11F spout ball valve, Q11F spike ball valve, V-type ball valve, Electric V-type ball valve, Pneumatic V-type ball valve, YJZQ High Pressure Hydraulic Ball Valve, KHBF-KHMF Flanged High Pressure Ball Valve, CJZQ High Pressure Ball Valve, VH3V 2-position 3-way high pressure ball valve, BKH-MKH High Pressure Ball Valve, VH2V Direct High Pressure Ball Valve, BME High Pressure Ball Valve, KHB High Pressure Ball Valve, KHP-PKH High Pressure Plate Ball Valve, (Pulverized Coal Injection) Ash Ball Valve, Q647MF Pneumatic Ash-unloading Ball Valve (Pulverized Coal Injection Ball Valve)

C++ Replication
// 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;
        }
    }
}

The previous code uses these variables to define and get FileStream auxiliary functions.

C++ Replication
#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;
}

Step 3: Add the package manifest to the package

Each package must have a package list.To add a package manifest to the package, create an input stream for the file, then call IAppxPackageWriter:: The Close method writes the manifest at the end of the package and closes the output stream of the package writer.

This code uses the helper function shown in the previous step of GetFileStream to create a stream for the package manifest.

C++ Replication
// 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;
}

Step 4: Clean up the package writer

Before returning from the wmain function, call the Release method to clean up the package writer and call the CoUninitialize function.

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

Topics: C++ Windows less xml