Implement flat bundle creation (#389)

* Attempt to create bundle method

* ProcessCommonOptions method

* Pass flags

* Update

* Create appxbundlefactory and bundlewriter

* Added interfaces to implement

* edit bundle options

* Remove comments

* Create vector of packages to write to bundle manifest

* Create bundle manifest bundle element

* Write Bundle Manifest Elements

* Add AppxBundleManifest, BlockMap, contenttype.xml to zip

* Validations to store fields into manifest

* Write Resources and Dependencies to Bundle Manifest

* Fetches correct value for resources and tdfs

* Created AppxBundleWriter and BundleWriterHelper files

* Writer verion to manifest, address PR comments

* process common and input options

* Use stdstrin instead of wchar

* Address PR comments

* Removed closeinternal from AppxBundleWriter.cpp

* Use exceptions instead of hresult

* Implement AppxManifestReader->GetQualifiedResources

* Write resources to bundle manifest

* Write correct namespaces to manifest file

* Code review comments

* Resolve build on other platforms

* Add packbundle to if MSIX_PACK

* Build should pass now

* Remove ectra qualification for build to pass

* add to msix_pack

* STATFLAG_NONAME error

* Resolve compiler error

* UINT64 and std::uint64 compiler error

* Use std::time instead of systemtime

* Build break

* resolve UINT64 ios compiler error

* Comment out arm64 mac jobs- known config issue

* Mac pipeline failure

* Remove Text data structure as per PR feedback

* gitignore, move zlib file, update filename to versionhelpers

* Resolve build

* Remove thumbs.db file
This commit is contained in:
jyvenugo 2020-11-06 11:20:41 -08:00 коммит произвёл GitHub
Родитель 96f2ffc9ac
Коммит eb2a53ca25
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
26 изменённых файлов: 1474 добавлений и 65 удалений

2
.gitignore поставляемый
Просмотреть файл

@ -406,3 +406,5 @@ src/msix/common/MSIXResource.cpp
src/test/MacOS-Linux/testApiResults.txt
src/test/mobile/iOSBVT/iOSBVT.xcodeproj/project.xcworkspace/
src/test/mobile/TEST-MsixSDK-AOSP.xml
Thumbs.db

Просмотреть файл

@ -50,21 +50,21 @@ jobs:
_arguments: -b Debug --pack
_artifact: MACOSchk-pack
# arm64
debug_nopack_arm64:
_arguments: -b Debug -arch arm64 --skip-tests
_artifact: MACOSarm64chk
release_nopack_arm64:
_arguments: -b MinSizeRel -arch arm64 --skip-tests
_artifact: MACOSarm64
release_nobundle_arm64:
_arguments: -b MinSizeRel -sb -arch arm64 --skip-tests
_artifact: MACOSarm64-nobundle
release_pack_arm64:
_arguments: -b MinSizeRel --pack -arch arm64 --skip-tests
_artifact: MACOSarm64-pack
debug_pack_arm64:
_arguments: -b Debug --pack -arch arm64 --skip-tests
_artifact: MACOSarm64chk-pack
#debug_nopack_arm64:
# _arguments: -b Debug -arch arm64 --skip-tests
# _artifact: MACOSarm64chk
#release_nopack_arm64:
# _arguments: -b MinSizeRel -arch arm64 --skip-tests
# _artifact: MACOSarm64
#release_nobundle_arm64:
# _arguments: -b MinSizeRel -sb -arch arm64 --skip-tests
# _artifact: MACOSarm64-nobundle
#release_pack_arm64:
# _arguments: -b MinSizeRel --pack -arch arm64 --skip-tests
# _artifact: MACOSarm64-pack
#debug_pack_arm64:
# _arguments: -b Debug --pack -arch arm64 --skip-tests
# _artifact: MACOSarm64chk-pack
steps:
# Az Pipelines has Xcode 11.6 as default. For arm64, change to supported Xcode.
@ -119,41 +119,41 @@ jobs:
ArtifactName: $(_artifact)
condition: succeededOrFailed()
- job: macOS_universal_nopack
dependsOn:
- 'macOS'
pool:
name: Azure Pipelines
vmImage: macOS-latest
steps:
- template: templates/macos-universal.yml
parameters:
artifact_output: MACOS-Universal
artifact_x86: MACOS
artifact_arm64: MACOSarm64
#- job: macOS_universal_nopack
# dependsOn:
# - 'macOS'
# pool:
# name: Azure Pipelines
# vmImage: macOS-latest
# steps:
# - template: templates/macos-universal.yml
# parameters:
# artifact_output: MACOS-Universal
# artifact_x86: MACOS
# artifact_arm64: MACOSarm64
- job: macOS_universal_nobundle
dependsOn:
- 'macOS'
pool:
name: Azure Pipelines
vmImage: macOS-latest
steps:
- template: templates/macos-universal.yml
parameters:
artifact_output: MACOS-nobundle-Universal
artifact_x86: MACOS-nobundle
artifact_arm64: MACOSarm64-nobundle
#- job: macOS_universal_nobundle
# dependsOn:
# - 'macOS'
# pool:
# name: Azure Pipelines
# vmImage: macOS-latest
# steps:
# - template: templates/macos-universal.yml
# parameters:
# artifact_output: MACOS-nobundle-Universal
# artifact_x86: MACOS-nobundle
# artifact_arm64: MACOSarm64-nobundle
- job: macOS_universal_pack
dependsOn:
- 'macOS'
pool:
name: Azure Pipelines
vmImage: macOS-latest
steps:
- template: templates/macos-universal.yml
parameters:
artifact_output: MACOS-pack-Universal
artifact_x86: MACOS-pack
artifact_arm64: MACOSarm64-pack
#- job: macOS_universal_pack
# dependsOn:
# - 'macOS'
# pool:
# name: Azure Pipelines
# vmImage: macOS-latest
# steps:
# - template: templates/macos-universal.yml
# parameters:
# artifact_output: MACOS-pack-Universal
# artifact_x86: MACOS-pack
# artifact_arm64: MACOSarm64-pack

Просмотреть файл

@ -0,0 +1,78 @@
//
// Copyright (C) 2019 Microsoft. All rights reserved.
// See LICENSE file in the project root for full license information.
//
#pragma once
#include "AppxPackaging.hpp"
#include "ComHelper.hpp"
#include "DirectoryObject.hpp"
#include "AppxBlockMapWriter.hpp"
#include "ContentTypeWriter.hpp"
#include "ZipObjectWriter.hpp"
#include "BundleWriterHelper.hpp"
#include "BundleManifestWriter.hpp"
#include "AppxPackageInfo.hpp"
// internal interface
// {ca90bcd9-78a2-4773-820c-0b687de49f99}
#ifndef WIN32
interface IBundleWriter : public IUnknown
#else
#include "Unknwn.h"
#include "Objidl.h"
class IBundleWriter : public IUnknown
#endif
{
public:
virtual void ProcessBundlePayload(const MSIX::ComPtr<IDirectoryObject>& from, bool flatBundle) = 0;
};
MSIX_INTERFACE(IBundleWriter, 0xca90bcd9,0x78a2,0x4773,0x82,0x0c,0x0b,0x68,0x7d,0xe4,0x9f,0x99);
namespace MSIX {
class AppxBundleWriter final : public ComClass<AppxBundleWriter, IBundleWriter, IAppxBundleWriter, IAppxBundleWriter4>
{
public:
AppxBundleWriter(IMsixFactory* factory, const ComPtr<IZipWriter>& zip, std::uint64_t bundleVersion);
~AppxBundleWriter() {};
// IBundleWriter
void ProcessBundlePayload(const ComPtr<IDirectoryObject>& from, bool flatBundle) override;
// IAppxBundleWriter
HRESULT STDMETHODCALLTYPE AddPayloadPackage(LPCWSTR fileName, IStream* packageStream) noexcept override;
HRESULT STDMETHODCALLTYPE Close() noexcept override;
// IAppxBundleWriter4
HRESULT STDMETHODCALLTYPE AddPackageReference(LPCWSTR fileName, IStream* inputStream,
BOOL isDefaultApplicablePackage) noexcept override;
HRESULT STDMETHODCALLTYPE AddPayloadPackage(LPCWSTR fileName, IStream* packageStream,
BOOL isDefaultApplicablePackage) noexcept override;
HRESULT STDMETHODCALLTYPE AddExternalPackageReference(LPCWSTR fileName, IStream* inputStream,
BOOL isDefaultApplicablePackage) noexcept override;
protected:
typedef enum
{
Open = 1,
Closed = 2,
Failed = 3
}
WriterState;
void AddFileToPackage(const std::string& name, IStream* stream, bool toCompress,
bool addToBlockMap, const char* contentType, bool forceContentTypeOverride = false);
void AddPackageReferenceInternal(std::string fileName, IStream* packageStream,
bool isDefaultApplicablePackage);
WriterState m_state;
ComPtr<IMsixFactory> m_factory;
ComPtr<IZipWriter> m_zipWriter;
BlockMapWriter m_blockMapWriter;
ContentTypeWriter m_contentTypeWriter;
BundleWriterHelper m_bundleWriterHelper;
};
}

Просмотреть файл

@ -32,9 +32,37 @@ public:
};
MSIX_INTERFACE(IAppxManifestObject, 0xeff6d561,0xa236,0x4058,0x9f,0x1d,0x8f,0x93,0x63,0x3f,0xba,0x4b);
// {daf72e2b-6252-4ed3-a476-5fb656aa0e2c}
#ifndef WIN32
interface IAppxManifestTargetDeviceFamilyInternal : public IUnknown
#else
#include "Unknwn.h"
#include "Objidl.h"
class IAppxManifestTargetDeviceFamilyInternal : public IUnknown
#endif
{
public:
virtual const std::string& GetName() = 0;
};
MSIX_INTERFACE(IAppxManifestTargetDeviceFamilyInternal, 0xdaf72e2b,0x6252,0x4ed3,0xa4,0x76,0x5f,0xb6,0x56,0xaa,0x0e,0x2c);
// {9e2fb304-cec6-4ef0-8df3-10bb2ce714a3}
#ifndef WIN32
interface IAppxManifestQualifiedResourceInternal : public IUnknown
#else
#include "Unknwn.h"
#include "Objidl.h"
class IAppxManifestQualifiedResourceInternal : public IUnknown
#endif
{
public:
virtual const std::string& GetLanguage() = 0;
};
MSIX_INTERFACE(IAppxManifestQualifiedResourceInternal, 0x9e2fb304,0xcec6,0x4ef0,0x8d,0xf3,0x10,0xbb,0x2c,0xe7,0x14,0xa3);
namespace MSIX {
class AppxManifestTargetDeviceFamily final : public ComClass<AppxManifestTargetDeviceFamily, IAppxManifestTargetDeviceFamily, IAppxManifestTargetDeviceFamilyUtf8>
class AppxManifestTargetDeviceFamily final : public ComClass<AppxManifestTargetDeviceFamily, IAppxManifestTargetDeviceFamily, IAppxManifestTargetDeviceFamilyUtf8, IAppxManifestTargetDeviceFamilyInternal>
{
public:
AppxManifestTargetDeviceFamily(IMsixFactory* factory, std::string& name, std::string& minVersion, std::string& maxVersion) :
@ -70,6 +98,12 @@ namespace MSIX {
return m_factory->MarshalOutStringUtf8(m_name, name);
} CATCH_RETURN();
//IAppxManifestTargetDeviceFamilyInternal
const std::string& GetName() override
{
return m_name;
}
protected:
ComPtr<IMsixFactory> m_factory;
std::string m_name;
@ -299,6 +333,51 @@ namespace MSIX {
std::string m_packageFamilyName;
};
class AppxManifestQualifiedResource final : public ComClass<AppxManifestQualifiedResource, IAppxManifestQualifiedResource, IAppxManifestQualifiedResourceUtf8, IAppxManifestQualifiedResourceInternal>
{
public:
AppxManifestQualifiedResource(IMsixFactory* factory, std::string& language, std::string& scale, std::string& DXFeatureLevel) :
m_factory(factory), m_language(language)
{
//TODO: Process and assign scale and DXFeatureLevel
}
// IAppxManifestQualifiedResource
HRESULT STDMETHODCALLTYPE GetLanguage(LPWSTR *language) noexcept override try
{
ThrowErrorIf(Error::InvalidParameter, (language == nullptr || *language != nullptr), "bad pointer");
return m_factory->MarshalOutString(m_language, language);
} CATCH_RETURN();
HRESULT STDMETHODCALLTYPE GetScale(UINT32 *scale) noexcept override try
{
return static_cast<HRESULT>(Error::NotImplemented);
} CATCH_RETURN();
HRESULT STDMETHODCALLTYPE GetDXFeatureLevel(DX_FEATURE_LEVEL *dxFeatureLevel) noexcept override try
{
return static_cast<HRESULT>(Error::NotImplemented);
} CATCH_RETURN();
// IAppxManifestQualifiedResourceUtf8
HRESULT STDMETHODCALLTYPE GetLanguage(LPSTR *language) noexcept override try
{
return m_factory->MarshalOutStringUtf8(m_language, language);
} CATCH_RETURN();
// IAppxManifestQualifiedResourceInternal
const std::string& GetLanguage() override
{
return m_language;
}
protected:
ComPtr<IMsixFactory> m_factory;
std::string m_language;
std::uint32_t m_scale;
DX_FEATURE_LEVEL m_DXFeatureLevel;
};
// Object backed by AppxManifest.xml
class AppxManifestObject final : public ComClass<AppxManifestObject, ChainInterfaces<IAppxManifestReader4, IAppxManifestReader3, IAppxManifestReader2, IAppxManifestReader>,
IAppxManifestReader5, IVerifierObject, IAppxManifestObject, IMsixDocumentElement>

Просмотреть файл

@ -0,0 +1,64 @@
//
// Copyright (C) 2019 Microsoft. All rights reserved.
// See LICENSE file in the project root for full license information.
//
#pragma once
#include "XmlWriter.hpp"
#include "ComHelper.hpp"
#include "Exceptions.hpp"
#include "UnicodeConversion.hpp"
#include "VersionHelpers.hpp"
#include <vector>
namespace MSIX {
struct PackageInfo
{
APPX_BUNDLE_PAYLOAD_PACKAGE_TYPE type;
std::uint64_t version;
std::string architecture;
std::string resourceId;
std::string fileName;
std::uint64_t size;
std::uint64_t offset;
ComPtr<IAppxManifestQualifiedResourcesEnumerator> resources;
bool isDefaultApplicablePackage;
ComPtr<IAppxManifestTargetDeviceFamiliesEnumerator> tdfs;
};
struct OptionalBundleInfo
{
std::string name;
std::string publisher;
std::uint64_t version;
std::string fileName;
std::vector<PackageInfo> optionalPackages;
};
class BundleManifestWriter final
{
public:
BundleManifestWriter();
void StartBundleManifest(std::string targetXmlNamespace,
std::string name, std::string publisher, std::uint64_t version);
void StartBundleElement();
void WriteIdentityElement(std::string name, std::string publisher, std::uint64_t version);
void StartPackagesElement();
void WritePackageElement(PackageInfo packageInfo);
void WriteResourcesElement(IAppxManifestQualifiedResourcesEnumerator* resources);
void WriteDependenciesElement(IAppxManifestTargetDeviceFamiliesEnumerator* tdfs);
void EndPackagesElement();
void Close();
ComPtr<IStream> GetStream() { return m_xmlWriter.GetStream(); }
std::string GetQualifiedName(std::string namespaceAlias, std::string name);
std::string GetElementName(std::string targetNamespace, std::string targetNamespaceAlias, std::string name);
protected:
XmlWriter m_xmlWriter;
std::string targetXmlNamespace;
};
}

Просмотреть файл

@ -0,0 +1,79 @@
#pragma once
#include "AppxPackaging.hpp"
#include "ComHelper.hpp"
#include "DirectoryObject.hpp"
#include "AppxBlockMapWriter.hpp"
#include "ContentTypeWriter.hpp"
#include "ZipObjectWriter.hpp"
#include "AppxPackageInfo.hpp"
#include "BundleManifestWriter.hpp"
#include <map>
namespace MSIX {
class BundleWriterHelper
{
public:
BundleWriterHelper();
std::uint64_t GetStreamSize(IStream* stream);
void AddPackage(std::string fileName, IAppxPackageReader* packageReader, std::uint64_t bundleOffset,
std::uint64_t packageSize, bool isDefaultApplicableResource);
void GetValidatedPackageData(
std::string fileName,
IAppxPackageReader* packageReader,
APPX_BUNDLE_PAYLOAD_PACKAGE_TYPE* packageType,
IAppxManifestPackageId** packageId,
IAppxManifestQualifiedResourcesEnumerator** resources,
IAppxManifestTargetDeviceFamiliesEnumerator** tdfs);
void AddValidatedPackageData(
std::string fileName,
std::uint64_t bundleOffset,
std::uint64_t packageSize,
APPX_BUNDLE_PAYLOAD_PACKAGE_TYPE packageType,
ComPtr<IAppxManifestPackageId> packageId,
bool isDefaultApplicablePackage,
IAppxManifestQualifiedResourcesEnumerator* resources,
IAppxManifestTargetDeviceFamiliesEnumerator* tdfs);
APPX_BUNDLE_PAYLOAD_PACKAGE_TYPE GetPayloadPackageType(
IAppxManifestReader* packageManifestReader,
std::string fileName);
void ValidateNameAndPublisher(
IAppxManifestPackageIdInternal* packageId,
std::string filename);
void ValidateApplicationElement(
IAppxManifestReader* packageManifestReader,
std::string fileName);
void AddPackageInfoToVector(std::vector<PackageInfo>& packagesVector, PackageInfo packageInfo);
void EndBundleManifest();
ComPtr<IStream> GetBundleManifestStream() { return m_bundleManifestWriter.GetStream(); }
void SetBundleVersion(std::uint64_t bundleVersion) { this->bundleVersion = bundleVersion; }
std::uint64_t GetBundleVersion() { return this->bundleVersion; }
std::vector<PackageInfo> GetPayloadPackages() { return payloadPackages; }
private:
std::vector<PackageInfo> payloadPackages;
std::map<std::string, OptionalBundleInfo> optionalBundles;
bool hasExternalPackages;
bool hasDefaultOrNeutralResources;
std::string mainPackageName;
std::string mainPackagePublisher;
std::uint64_t bundleVersion;
BundleManifestWriter m_bundleManifestWriter;
};
}

Просмотреть файл

@ -23,6 +23,7 @@ namespace MSIX {
static const ContentType& GetContentTypeByExtension(std::string& ext);
static const std::string GetPayloadFileContentType(APPX_FOOTPRINT_FILE_TYPE footprintFile);
static const std::string GetBundlePayloadFileContentType(APPX_BUNDLE_FOOTPRINT_FILE_TYPE footprintFile);
private:
APPX_COMPRESSION_OPTION m_compressionOpt;

Просмотреть файл

@ -13,7 +13,7 @@ namespace MSIX {
{
static bool IsFileNameValid(const std::string& name);
static bool IsIdentifierValid(const std::string& name);
static bool IsFootPrintFile(const std::string& fileName);
static bool IsFootPrintFile(const std::string& fileName, bool isBundle);
static bool IsReservedFolder(const std::string& fileName);
};
}

Просмотреть файл

@ -77,6 +77,7 @@ enum class XmlAttributeName : std::uint8_t
Package_Applications_Application_Id,
Category,
MaxMajorVersionTested,
DXFeatureLevel,
};
// {ac94449e-442d-4bed-8fca-83770c0f7ee9}

Просмотреть файл

@ -0,0 +1,12 @@
#pragma once
#include "MSIXWindows.hpp"
#include "UnicodeConversion.hpp"
namespace MSIX {
std::uint64_t ConvertVersionStringToUint64(const std::string& versionString);
std::string ConvertVersionToString(std::uint64_t version);
}

Просмотреть файл

@ -14,6 +14,7 @@ namespace MSIX {
// common attribute names
static const char* xmlnsAttribute = "xmlns";
static const char* xmlNamespaceDelimiter = ":";
// This is a super light xml writer that doesn't use any xml libraries and
// just writes to a stream the basics of an xml file.

Просмотреть файл

@ -14,6 +14,8 @@
#include <memory>
#include <utility>
#include <zlib.h>
// {350dd671-0c40-4cd7-9a5b-27456d604bd0}
#ifndef WIN32
interface IZipWriter : public IUnknown

Просмотреть файл

@ -78,6 +78,7 @@ SpecializeUuidOfImpl(IAppxManifestQualifiedResourcesEnumerator);
SpecializeUuidOfImpl(IAppxManifestQualifiedResource);
SpecializeUuidOfImpl(IAppxBundleFactory);
SpecializeUuidOfImpl(IAppxBundleWriter);
SpecializeUuidOfImpl(IAppxBundleWriter4);
SpecializeUuidOfImpl(IAppxBundleReader);
SpecializeUuidOfImpl(IAppxBundleManifestReader);
SpecializeUuidOfImpl(IAppxBundleManifestPackageInfoEnumerator);
@ -124,6 +125,7 @@ interface IAppxManifestQualifiedResourcesEnumerator;
interface IAppxManifestQualifiedResource;
interface IAppxBundleFactory;
interface IAppxBundleWriter;
interface IAppxBundleWriter4;
interface IAppxBundleReader;
interface IAppxBundleManifestReader;
interface IAppxBundleManifestPackageInfoEnumerator;
@ -970,6 +972,31 @@ enum tagLOCKTYPE
};
#endif /* __IAppxBundleWriter_INTERFACE_DEFINED__ */
#ifndef __IAppxBundleWriter4_INTERFACE_DEFINED__
#define __IAppxBundleWriter4_INTERFACE_DEFINED__
// {9CD9D523-5009-4C01-9882-DC029FBD47A3}
MSIX_INTERFACE(IAppxBundleWriter4,0x9cd9d523,0x5009,0x4c01,0x98,0x82,0xdc,0x02,0x9f,0xbd,0x47,0xa3);
interface IAppxBundleWriter4 : public IUnknown
{
public:
virtual HRESULT STDMETHODCALLTYPE AddPayloadPackage(
/* [string][in] */ LPCWSTR fileName,
/* [in] */ IStream* packageStream,
/* [in] */ BOOL isDefaultApplicablePackage) noexcept = 0;
virtual HRESULT AddPackageReference(
/* [string][in] */ LPCWSTR fileName,
/* [in] */ IStream* inputStream,
/* [in] */ BOOL isDefaultApplicablePackage) noexcept = 0;
virtual HRESULT AddExternalPackageReference(
/* [string][in] */ LPCWSTR fileName,
/* [in] */ IStream* inputStream,
/* [in] */ BOOL isDefaultApplicablePackage) noexcept = 0;
};
#endif /* __IAppxBundleWriter4_INTERFACE_DEFINED__ */
#ifndef __IAppxBundleReader_INTERFACE_DEFINED__
#define __IAppxBundleReader_INTERFACE_DEFINED__
@ -1665,6 +1692,17 @@ enum MSIX_APPLICABILITY_OPTIONS
MSIX_APPLICABILITY_OPTION_SKIPLANGUAGE = 0x2,
} MSIX_APPLICABILITY_OPTIONS;
typedef /* [v1_enum] */
enum MSIX_BUNDLE_OPTIONS
{
MSIX_OPTION_NONE = 0x0,
MSIX_OPTION_VERBOSE = 0x1,
MSIX_OPTION_OVERWRITE = 0x2,
MSIX_OPTION_NOOVERWRITE = 0x4,
MSIX_OPTION_VERSION = 0x8,
MSIX_BUNDLE_OPTION_FLATBUNDLE = 0x10,
} MSIX_BUNDLE_OPTIONS;
#define MSIX_PLATFORM_ALL MSIX_PLATFORM_WINDOWS10 | \
MSIX_PLATFORM_WINDOWS10 | \
MSIX_PLATFORM_WINDOWS8 | \
@ -1731,6 +1769,14 @@ MSIX_API HRESULT STDMETHODCALLTYPE PackPackage(
char* outputPackage
) noexcept;
MSIX_API HRESULT STDMETHODCALLTYPE PackBundle(
MSIX_BUNDLE_OPTIONS bundleOptions,
char* directoryPath,
char* outputBundle,
char* mappingFile,
char* version
) noexcept;
#endif // MSIX_PACK
// A call to called CoCreateAppxFactory is required before start using the factory on non-windows platforms specifying

Просмотреть файл

@ -418,6 +418,38 @@ MSIX_APPLICABILITY_OPTIONS GetApplicabilityOption(const Invocation& invocation)
return applicability;
}
MSIX_BUNDLE_OPTIONS GetBundleOptions(const Invocation& invocation)
{
MSIX_BUNDLE_OPTIONS bundleOptions = MSIX_BUNDLE_OPTIONS::MSIX_OPTION_NONE;
if (invocation.IsOptionPresent("-v"))
{
bundleOptions |= MSIX_BUNDLE_OPTIONS::MSIX_OPTION_VERBOSE;
}
if (invocation.IsOptionPresent("-o"))
{
bundleOptions |= MSIX_BUNDLE_OPTIONS::MSIX_OPTION_OVERWRITE;
}
if (invocation.IsOptionPresent("-no"))
{
bundleOptions |= MSIX_BUNDLE_OPTIONS::MSIX_OPTION_NOOVERWRITE;
}
if (invocation.IsOptionPresent("-bv"))
{
bundleOptions |= MSIX_BUNDLE_OPTIONS::MSIX_OPTION_VERSION;
}
if (invocation.IsOptionPresent("-fb"))
{
bundleOptions |= MSIX_BUNDLE_OPTIONS::MSIX_BUNDLE_OPTION_FLATBUNDLE;
}
return bundleOptions;
}
#pragma region Commands
Command CreateHelpCommand(const std::vector<Command>& commands)
@ -551,6 +583,66 @@ Command CreatePackCommand()
return result;
}
Command CreateBundleCommand()
{
Command result{ "bundle", "Create a new app bundle from files on disk",
{
Option{ "-d", "Input directory path.", false, 1, "inputDirectory" },
Option{ "-p", "Output bundle file path.", true, 1, "outputBundle" },
Option{ "-f", "Mapping file path.", false, 1, "mappingFile" },
Option{ "-bv", "Specifies the version number of the bundle being created. The version"
"must be in dotted - quad notation of four integers"
"<Major>.<Minor>.<Build>.<Revision> ranging from 0 to 65535 each.If the"
"/ bv option is not specified or is set to 0.0.0.0, the bundle is created"
"using the current date - time formatted as the version :"
"<Year>.<Month - Day>.<Hour - Minute>.<Second - Millisecond>.", false, 1, "version" },
Option{ "-fb", "Generates a fully sparse bundle where all packages are references to"
"packages that exist outside of the bundle file." },
Option{ "-o", "Forces the output to overwrite any existing files with the"
"same name.By default, the user is asked whether to overwrite existing"
"files with the same name.You can't use this option with /no." },
Option{ "-no","Prevents the output from overwriting any existing files"
"with the same name.By default, the user is asked whether to overwrite"
"existing files with the same name.You can't use this option with /o." },
Option{ "-v", "Enables verbose output of messages to the console."},
Option{ TOOL_HELP_COMMAND_STRING, "Displays this help text." },
}
};
result.SetDescription({
"Creates an app bundle at <output bundle name> by adding all files from",
"either <content directory>(including subfolders) or a list of files within"
"<mapping file>.If either source contains a bundle manifest, it will be"
"ignored."
"Using / p will result in the bundle being unencrypted, while using / ep will"
"result in the bundle being encrypted.If you use / ep you must specify"
"either / kt or /kf.",
});
result.SetInvocationFunc([](const Invocation& invocation)
{
char* directoryPath = (invocation.IsOptionPresent("-d")) ?
const_cast<char*>(invocation.GetOptionValue("-d").c_str()) : nullptr;
char* mappingFile = (invocation.IsOptionPresent("-f")) ?
const_cast<char*>(invocation.GetOptionValue("-f").c_str()) : nullptr;
char* version = (invocation.IsOptionPresent("-bv")) ?
const_cast<char*>(invocation.GetOptionValue("-bv").c_str()) : nullptr;
return PackBundle(
GetBundleOptions(invocation),
directoryPath,
const_cast<char*>(invocation.GetOptionValue("-p").c_str()),
mappingFile,
version);
});
return result;
}
#endif
#pragma endregion
@ -566,6 +658,7 @@ int main(int argc, char* argv[])
CreateUnbundleCommand(),
#ifdef MSIX_PACK
CreatePackCommand(),
CreateBundleCommand(),
#endif
};

Просмотреть файл

@ -19,6 +19,7 @@ list(APPEND MSIX_UNPACK_EXPORTS
if(MSIX_PACK)
list(APPEND MSIX_PACK_EXPORTS
"PackPackage"
"PackBundle"
)
endif()
@ -130,6 +131,10 @@ if(MSIX_PACK)
pack/ContentType.cpp
pack/DeflateStream.cpp
pack/ZipObjectWriter.cpp
pack/BundleManifestWriter.cpp
pack/BundleWriterHelper.cpp
pack/AppxBundleWriter.cpp
pack/VersionHelpers.cpp
)
endif()
@ -224,7 +229,7 @@ endforeach()
add_library(${PROJECT_NAME} SHARED
msix.cpp
${MsixSrc}
)
)
# Adding dependency to the third party libs directory
add_dependencies(${PROJECT_NAME} LIBS)

Просмотреть файл

@ -11,6 +11,7 @@
#include "VectorStream.hpp"
#include "MsixFeatureSelector.hpp"
#include "AppxPackageWriter.hpp"
#include "AppxBundleWriter.hpp"
#include "ZipObjectWriter.hpp"
#ifdef BUNDLE_SUPPORT
@ -88,7 +89,15 @@ namespace MSIX {
HRESULT STDMETHODCALLTYPE AppxFactory::CreateBundleWriter(IStream *outputStream, UINT64 bundleVersion, IAppxBundleWriter **bundleWriter) noexcept try
{
THROW_IF_BUNDLE_NOT_ENABLED
NOTIMPLEMENTED;
ThrowErrorIf(Error::InvalidParameter, (outputStream == nullptr || bundleWriter == nullptr || *bundleWriter != nullptr), "Invalid parameter");
#ifdef MSIX_PACK
ComPtr<IMsixFactory> self;
ThrowHrIfFailed(QueryInterface(UuidOfImpl<IMsixFactory>::iid, reinterpret_cast<void**>(&self)));
auto zip = ComPtr<IZipWriter>::Make<ZipObjectWriter>(outputStream);
auto result = ComPtr<IAppxBundleWriter>::Make<AppxBundleWriter>(self.Get(), zip, bundleVersion);
*bundleWriter = result.Detach();
#endif
return static_cast<HRESULT>(Error::OK);
} CATCH_RETURN();
HRESULT STDMETHODCALLTYPE AppxFactory::CreateBundleReader(IStream *inputStream, IAppxBundleReader **bundleReader) noexcept try

Просмотреть файл

@ -357,7 +357,31 @@ namespace MSIX {
// IAppxManifestReader2
HRESULT STDMETHODCALLTYPE AppxManifestObject::GetQualifiedResources(IAppxManifestQualifiedResourcesEnumerator **resources) noexcept
{
return static_cast<HRESULT>(Error::NotImplemented);
ThrowErrorIf(Error::InvalidParameter, (resources == nullptr || *resources != nullptr), "bad pointer");
std::vector<ComPtr<IAppxManifestQualifiedResource>> qualifiedResources;
struct _context
{
AppxManifestObject* self;
std::vector<ComPtr<IAppxManifestQualifiedResource>>* qualifiedResources;
};
_context context = { this, &qualifiedResources};
// Parse Resource elements
XmlVisitor visitorResource(static_cast<void*>(&context), [](void* c, const ComPtr<IXmlElement>& resourceNode)->bool
{
_context* context = reinterpret_cast<_context*>(c);
auto language = resourceNode->GetAttributeValue(XmlAttributeName::Language);
auto scale = resourceNode->GetAttributeValue(XmlAttributeName::Scale);
auto dxFeatureLevel = resourceNode->GetAttributeValue(XmlAttributeName::DXFeatureLevel);
auto resource = ComPtr<IAppxManifestQualifiedResource>::Make<AppxManifestQualifiedResource>(context->self->m_factory.Get(), language, scale, dxFeatureLevel);
context->qualifiedResources->push_back(std::move(resource));
return true;
});
m_dom->ForEachElementIn(m_dom->GetDocument(), XmlQueryName::Package_Resources_Resource, visitorResource);
*resources = ComPtr<IAppxManifestQualifiedResourcesEnumerator>::
Make<EnumeratorCom<IAppxManifestQualifiedResourcesEnumerator,IAppxManifestQualifiedResource>>(qualifiedResources).Detach();
return static_cast<HRESULT>(Error::OK);
}
// IAppxManifestReader3

Просмотреть файл

@ -288,10 +288,20 @@ namespace MSIX {
return !IsProhibitedFileName(lowSegment) && !HasProhibitedPrefix(lowSegment) && !HasProhibitedSuffix(lowSegment);
}
bool FileNameValidation::IsFootPrintFile(const std::string& fileName)
bool FileNameValidation::IsFootPrintFile(const std::string& fileName, bool isBundle)
{
bool result = false;
std::string lowIdent = Helper::tolower(fileName);
return ((lowIdent == "appxmanifest.xml") ||
if (isBundle)
{
result = lowIdent == "appxmetadata/appxbundlemanifest.xml";
}
else
{
result = lowIdent == "appxmanifest.xml";
}
return (result ||
(lowIdent == "appxsignature.p7x") ||
(lowIdent == "appxblockmap.xml") ||
(lowIdent == "[content_types].xml"));

Просмотреть файл

@ -28,6 +28,7 @@ static const char* attributeNames[] = {
/* Package_Applications_Application_Id */"Id",
/* Category */"Category",
/* MaxMajorVersionTested */"MaxMajorVersionTested",
/* DXFeatureLevel */"DXFeatureLevel",
};
#ifdef USING_MSXML

Просмотреть файл

@ -6,6 +6,7 @@
#include <memory>
#include <cstdlib>
#include <functional>
#include <map>
#include "Exceptions.hpp"
#include "FileStream.hpp"
@ -17,7 +18,9 @@
#include "AppxPackageObject.hpp"
#include "MsixFeatureSelector.hpp"
#include "AppxPackageWriter.hpp"
#include "AppxBundleWriter.hpp"
#include "ScopeExit.hpp"
#include "VersionHelpers.hpp"
#ifndef WIN32
// on non-win32 platforms, compile with -fvisibility=hidden
@ -288,4 +291,101 @@ MSIX_API HRESULT STDMETHODCALLTYPE PackPackage(
return static_cast<HRESULT>(MSIX::Error::OK);
} CATCH_RETURN();
MSIX_API HRESULT STDMETHODCALLTYPE PackBundle(
MSIX_BUNDLE_OPTIONS bundleOptions,
char* directoryPath,
char* outputBundle,
char* mappingFile,
char* version
) noexcept try
{
std::unique_ptr<std::map<std::string, std::string>> externalPackagesList;
std::uint64_t bundleVersion = 0;
bool flatBundle = false;
bool overWrite = false;
//Process Common Options
if (bundleOptions & MSIX_BUNDLE_OPTIONS::MSIX_OPTION_VERSION)
{
bundleVersion = MSIX::ConvertVersionStringToUint64(version);
}
if ((bundleOptions & MSIX_BUNDLE_OPTIONS::MSIX_OPTION_OVERWRITE) && (bundleOptions & MSIX_BUNDLE_OPTIONS::MSIX_OPTION_NOOVERWRITE))
{
ThrowErrorAndLog(MSIX::Error::InvalidParameter, "You can't specify options -o and -no at the same time.");
}
if (bundleOptions & MSIX_BUNDLE_OPTIONS::MSIX_OPTION_OVERWRITE)
{
overWrite = true;
}
if (bundleOptions & MSIX_BUNDLE_OPTIONS::MSIX_OPTION_NOOVERWRITE)
{
overWrite = false;
}
if (0 == (bundleOptions & MSIX_BUNDLE_OPTIONS::MSIX_BUNDLE_OPTION_FLATBUNDLE))
{
flatBundle = true;
}
if (bundleOptions & MSIX_BUNDLE_OPTIONS::MSIX_OPTION_VERBOSE)
{
//TODO: Process option for verbose
}
//TODO: Error if outputBundle is an existing directory
//Process Input options
if(directoryPath == nullptr && mappingFile == nullptr)
{
ThrowErrorAndLog(MSIX::Error::InvalidParameter, "You must specify either a content directory (-d) or a mapping file (-f).");
}
else if(directoryPath != nullptr && mappingFile != nullptr)
{
ThrowErrorAndLog(MSIX::Error::InvalidParameter, "You can't specify both a content directory (-d) and a mapping file (-f).");
}
//TODO:: Error if directoryPath is a file
MSIX::ComPtr<IDirectoryObject> from;
if(directoryPath != nullptr && outputBundle != nullptr)
{
from = MSIX::ComPtr<IDirectoryObject>::Make<MSIX::DirectoryObject>(directoryPath);
}
else if(mappingFile != nullptr && outputBundle != nullptr)
{
//Create from list from mapping file(Currently keeping it same as above, have to
//parse from mapping file into externalPackagesList)
from = MSIX::ComPtr<IDirectoryObject>::Make<MSIX::DirectoryObject>(directoryPath);
}
auto deleteFile = MSIX::scope_exit([&outputBundle]
{
remove(outputBundle);
});
MSIX::ComPtr<IStream> stream;
ThrowHrIfFailed(CreateStreamOnFile(outputBundle, false, &stream));
MSIX::ComPtr<IAppxBundleFactory> factory;
ThrowHrIfFailed(CoCreateAppxBundleFactoryWithHeap(InternalAllocate, InternalFree,
MSIX_VALIDATION_OPTION::MSIX_VALIDATION_OPTION_FULL,
MSIX_APPLICABILITY_OPTIONS::MSIX_APPLICABILITY_OPTION_FULL,
&factory));
MSIX::ComPtr<IAppxBundleWriter> bundleWriter;
MSIX::ComPtr<IAppxBundleWriter4> bundleWriter4;
ThrowHrIfFailed(factory->CreateBundleWriter(stream.Get(), bundleVersion, &bundleWriter));
bundleWriter4 = bundleWriter.As<IAppxBundleWriter4>();
bundleWriter4.As<IBundleWriter>()->ProcessBundlePayload(from, flatBundle);
ThrowHrIfFailed(bundleWriter->Close());
deleteFile.release();
return static_cast<HRESULT>(MSIX::Error::OK);
} CATCH_RETURN();
#endif // MSIX_PACK

Просмотреть файл

@ -0,0 +1,233 @@
//
// Copyright (C) 2019 Microsoft. All rights reserved.
// See LICENSE file in the project root for full license information.
//
#include "AppxPackaging.hpp"
#include "AppxBundleWriter.hpp"
#include "MsixErrors.hpp"
#include "Exceptions.hpp"
#include "ContentType.hpp"
#include "Encoding.hpp"
#include "ZipObjectWriter.hpp"
#include "AppxManifestObject.hpp"
#include "ScopeExit.hpp"
#include "FileNameValidation.hpp"
#include "StringHelper.hpp"
#include <ctime>
#include <iomanip>
namespace MSIX {
AppxBundleWriter::AppxBundleWriter(IMsixFactory* factory, const ComPtr<IZipWriter>& zip, std::uint64_t bundleVersion)
: m_factory(factory), m_zipWriter(zip)
{
m_state = WriterState::Open;
if(bundleVersion == 0)
{
// The generated version number has the format: YYYY.MMDD.hhmm.0
std::time_t t = std::time(nullptr);
std::tm tm = *std::gmtime(&t);
std::stringstream ss;
ss << std::put_time(&tm, "%Y.%m%d.%H%M.0");
this->m_bundleWriterHelper.SetBundleVersion(ConvertVersionStringToUint64(ss.str()));
}
else
{
this->m_bundleWriterHelper.SetBundleVersion(bundleVersion);
}
}
// IBundleWriter
void AppxBundleWriter::ProcessBundlePayload(const ComPtr<IDirectoryObject>& from, bool flatBundle)
{
ThrowErrorIf(Error::InvalidState, m_state != WriterState::Open, "Invalid package writer state");
auto failState = MSIX::scope_exit([this]
{
this->m_state = WriterState::Failed;
});
auto fileMap = from->GetFilesByLastModDate();
for (const auto& file : fileMap)
{
if (!(FileNameValidation::IsFootPrintFile(file.second, true)))
{
std::string ext = Helper::tolower(file.second.substr(file.second.find_last_of(".") + 1));
auto contentType = ContentType::GetContentTypeByExtension(ext);
auto stream = from.As<IStorageObject>()->GetFile(file.second);
if (flatBundle)
{
ThrowHrIfFailed(AddPackageReference(utf8_to_wstring(file.second).c_str(), stream.Get(), false));
}
}
}
//Process external packages passed as input created from mapping file
/*if (externalPackagesList != nullptr)
{
}*/
failState.release();
}
// IAppxBundleWriter
HRESULT STDMETHODCALLTYPE AppxBundleWriter::AddPayloadPackage(LPCWSTR fileName, IStream* packageStream) noexcept try
{
// TODO: implement
NOTIMPLEMENTED;
} CATCH_RETURN();
HRESULT STDMETHODCALLTYPE AppxBundleWriter::Close() noexcept try
{
ThrowErrorIf(Error::InvalidState, m_state != WriterState::Open, "Invalid package writer state");
auto failState = MSIX::scope_exit([this]
{
this->m_state = WriterState::Failed;
});
//Process AppxBundleManifest.xml and add it to the bundle
m_bundleWriterHelper.EndBundleManifest();
auto bundleManifestStream = m_bundleWriterHelper.GetBundleManifestStream();
auto bundleManifestContentType = ContentType::GetBundlePayloadFileContentType(APPX_BUNDLE_FOOTPRINT_FILE_TYPE_MANIFEST);
AddFileToPackage(APPXBUNDLEMANIFEST_XML, bundleManifestStream.Get(), true, true, bundleManifestContentType.c_str());
// Close blockmap and add it to the bundle
m_blockMapWriter.Close();
auto blockMapStream = m_blockMapWriter.GetStream();
auto blockMapContentType = ContentType::GetPayloadFileContentType(APPX_FOOTPRINT_FILE_TYPE_BLOCKMAP);
AddFileToPackage(APPXBLOCKMAP_XML, blockMapStream.Get(), true, false, blockMapContentType.c_str());
// Close content types and add it to the bundle
m_contentTypeWriter.Close();
auto contentTypeStream = m_contentTypeWriter.GetStream();
AddFileToPackage(CONTENT_TYPES_XML, contentTypeStream.Get(), true, false, nullptr);
m_zipWriter->Close();
failState.release();
m_state = WriterState::Closed;
return static_cast<HRESULT>(Error::OK);
} CATCH_RETURN();
// IAppxBundleWriter4
HRESULT STDMETHODCALLTYPE AppxBundleWriter::AddPackageReference(LPCWSTR fileName,
IStream* inputStream, BOOL isDefaultApplicablePackage) noexcept try
{
this->AddPackageReferenceInternal(wstring_to_utf8(fileName), inputStream, !!isDefaultApplicablePackage);
return static_cast<HRESULT>(Error::OK);
} CATCH_RETURN();
void AppxBundleWriter::AddPackageReferenceInternal(std::string fileName, IStream* packageStream,
bool isDefaultApplicablePackage)
{
auto appxFactory = m_factory.As<IAppxFactory>();
ComPtr<IAppxPackageReader> reader;
ThrowHrIfFailed(appxFactory->CreatePackageReader(packageStream, &reader));
std::uint64_t packageStreamSize = this->m_bundleWriterHelper.GetStreamSize(packageStream);
this->m_bundleWriterHelper.AddPackage(fileName, reader.Get(), 0, packageStreamSize, isDefaultApplicablePackage);
}
HRESULT STDMETHODCALLTYPE AppxBundleWriter::AddPayloadPackage(LPCWSTR fileName, IStream* packageStream,
BOOL isDefaultApplicablePackage) noexcept try
{
// TODO: implement
NOTIMPLEMENTED;
} CATCH_RETURN();
HRESULT STDMETHODCALLTYPE AppxBundleWriter::AddExternalPackageReference(LPCWSTR fileName,
IStream* inputStream, BOOL isDefaultApplicablePackage) noexcept try
{
// TODO: implement
NOTIMPLEMENTED;
} CATCH_RETURN();
void AppxBundleWriter::AddFileToPackage(const std::string& name, IStream* stream, bool toCompress,
bool addToBlockMap, const char* contentType, bool forceContentTypeOverride)
{
std::string opcFileName;
// Don't encode [Content Type].xml
if (contentType != nullptr)
{
opcFileName = Encoding::EncodeFileName(name);
}
else
{
opcFileName = name;
}
auto fileInfo = m_zipWriter->PrepareToAddFile(opcFileName, toCompress);
// Add content type to [Content Types].xml
if (contentType != nullptr)
{
m_contentTypeWriter.AddContentType(name, contentType, forceContentTypeOverride);
}
// This might be called with external IStream implementations. Don't rely on internal implementation of FileStream
LARGE_INTEGER start = { 0 };
ULARGE_INTEGER end = { 0 };
ThrowHrIfFailed(stream->Seek(start, StreamBase::Reference::END, &end));
ThrowHrIfFailed(stream->Seek(start, StreamBase::Reference::START, nullptr));
std::uint64_t uncompressedSize = static_cast<std::uint64_t>(end.QuadPart);
// Add file to block map.
if (addToBlockMap)
{
m_blockMapWriter.AddFile(name, uncompressedSize, fileInfo.first);
}
auto& zipFileStream = fileInfo.second;
std::uint64_t bytesToRead = uncompressedSize;
std::uint32_t crc = 0;
while (bytesToRead > 0)
{
// Calculate the size of the next block to add
std::uint32_t blockSize = (bytesToRead > DefaultBlockSize) ? DefaultBlockSize : static_cast<std::uint32_t>(bytesToRead);
bytesToRead -= blockSize;
// read block from stream
std::vector<std::uint8_t> block;
block.resize(blockSize);
ULONG bytesRead;
ThrowHrIfFailed(stream->Read(static_cast<void*>(block.data()), static_cast<ULONG>(blockSize), &bytesRead));
ThrowErrorIfNot(Error::FileRead, (static_cast<ULONG>(blockSize) == bytesRead), "Read stream file failed");
crc = crc32(crc, block.data(), static_cast<uInt>(block.size()));
// Write block and compress if needed
ULONG bytesWritten = 0;
ThrowHrIfFailed(zipFileStream->Write(block.data(), static_cast<ULONG>(block.size()), &bytesWritten));
// Add block to blockmap
if (addToBlockMap)
{
m_blockMapWriter.AddBlock(block, bytesWritten, toCompress);
}
}
if (toCompress)
{
// Put the stream termination on
std::vector<std::uint8_t> buffer;
ULONG bytesWritten = 0;
ThrowHrIfFailed(zipFileStream->Write(buffer.data(), static_cast<ULONG>(buffer.size()), &bytesWritten));
}
// Close File element
if (addToBlockMap)
{
m_blockMapWriter.CloseFile();
}
// This could be the compressed or uncompressed size
auto streamSize = zipFileStream.As<IStreamInternal>()->GetSize();
m_zipWriter->EndFile(crc, streamSize, uncompressedSize, true);
}
}

Просмотреть файл

@ -21,8 +21,6 @@
#include <algorithm>
#include <functional>
#include <zlib.h>
namespace MSIX {
AppxPackageWriter::AppxPackageWriter(IMsixFactory* factory, const ComPtr<IZipWriter>& zip) : m_factory(factory), m_zipWriter(zip)
@ -44,7 +42,7 @@ namespace MSIX {
{
// If any footprint file is present, ignore it. We only require the AppxManifest.xml
// and any other will be ignored and a new one will be created for the package.
if(!(FileNameValidation::IsFootPrintFile(file.second) || FileNameValidation::IsReservedFolder(file.second)))
if(!(FileNameValidation::IsFootPrintFile(file.second, false) || FileNameValidation::IsReservedFolder(file.second)))
{
std::string ext = Helper::tolower(file.second.substr(file.second.find_last_of(".") + 1));
auto contentType = ContentType::GetContentTypeByExtension(ext);
@ -155,7 +153,7 @@ namespace MSIX {
APPX_COMPRESSION_OPTION compressionOpt, const char* contentType)
{
ThrowErrorIfNot(Error::InvalidParameter, FileNameValidation::IsFileNameValid(name), "Invalid file name");
ThrowErrorIf(Error::InvalidParameter, FileNameValidation::IsFootPrintFile(name), "Trying to add footprint file to package");
ThrowErrorIf(Error::InvalidParameter, FileNameValidation::IsFootPrintFile(name, false), "Trying to add footprint file to package");
ThrowErrorIf(Error::InvalidParameter, FileNameValidation::IsReservedFolder(name), "Trying to add file in reserved folder");
ValidateCompressionOption(compressionOpt);
AddFileToPackage(name, stream, compressionOpt != APPX_COMPRESSION_OPTION_NONE, true, contentType);

Просмотреть файл

@ -0,0 +1,275 @@
//
// Copyright (C) 2019 Microsoft. All rights reserved.
// See LICENSE file in the project root for full license information.
//
#include "XmlWriter.hpp"
#include "BundleManifestWriter.hpp"
#include "Crypto.hpp"
#include "StringHelper.hpp"
#include "AppxManifestObject.hpp"
#include <vector>
namespace MSIX {
static const char* bundleManifestElement = "Bundle";
static const char* schemaVersionAttribute = "SchemaVersion";
static const char* Win2019SchemaVersion = "5.0";
static const char* identityManifestElement = "Identity";
static const char* identityNameAttribute = "Name";
static const char* identityPublisherAttribute = "Publisher";
static const char* identityVersionAttribute = "Version";
static const char* packagesManifestElement = "Packages";
static const char* packageManifestElement = "Package";
static const char* packageTypeAttribute = "Type";
static const char* packageVersionAttribute = "Version";
static const char* packageArchitectureAttribute = "Architecture";
static const char* packageResourceIdAttribute = "ResourceId";
static const char* packageFileNameAttribute = "FileName";
static const char* resourcesManifestElement = "Resources";
static const char* resourceManifestElement = "Resource";
static const char* resourceLanguageAttribute = "Language";
static const char* dependenciesManifestElementWithoutPrefix = "Dependencies";
static const char* targetDeviceFamilyManifestElementWithoutPrefix = "TargetDeviceFamily";
static const char* tdfNameAttribute = "Name";
static const char* tdfMinVersionAttribute = "MinVersion";
static const char* tdfMaxVersionTestedAttribute = "MaxVersionTested";
static const char* ApplicationPackageType = "application";
static const char* ResourcePackageType = "resource";
static const char* NamespaceAlias = "b";
static const char* Namespace = "http://schemas.microsoft.com/appx/2013/bundle";
static const char* Namespace2016Alias = "b2";
static const char* Namespace2016 = "http://schemas.microsoft.com/appx/2016/bundle";
static const char* Namespace2017Alias = "b3";
static const char* Namespace2017 = "http://schemas.microsoft.com/appx/2017/bundle";
static const char* Namespace2018Alias = "b4";
static const char* Namespace2018 = "http://schemas.microsoft.com/appx/2018/bundle";
static const char* Namespace2019Alias = "b5";
static const char* Namespace2019 = "http://schemas.microsoft.com/appx/2019/bundle";
BundleManifestWriter::BundleManifestWriter() : m_xmlWriter(XmlWriter(bundleManifestElement)) {}
void BundleManifestWriter::StartBundleManifest(std::string targetXmlNamespace, std::string name,
std::string publisher, std::uint64_t version)
{
this->targetXmlNamespace = targetXmlNamespace;
StartBundleElement();
WriteIdentityElement(name, publisher, version);
StartPackagesElement();
}
void BundleManifestWriter::StartBundleElement()
{
m_xmlWriter.AddAttribute(xmlnsAttribute, this->targetXmlNamespace);
m_xmlWriter.AddAttribute(schemaVersionAttribute, Win2019SchemaVersion);
std::string bundle2018QName = GetQualifiedName(xmlnsAttribute, Namespace2018Alias);
m_xmlWriter.AddAttribute(bundle2018QName, Namespace2018);
std::string bundle2019QName = GetQualifiedName(xmlnsAttribute, Namespace2019Alias);
m_xmlWriter.AddAttribute(bundle2019QName, Namespace2019);
std::string ignorableNamespaces;
ignorableNamespaces.append(Namespace2018Alias);
ignorableNamespaces.append(" ");
ignorableNamespaces.append(Namespace2019Alias);
m_xmlWriter.AddAttribute("IgnorableNamespaces", ignorableNamespaces);
}
void BundleManifestWriter::WriteIdentityElement(std::string name, std::string publisher, std::uint64_t version)
{
m_xmlWriter.StartElement(identityManifestElement);
m_xmlWriter.AddAttribute(identityNameAttribute, name);
m_xmlWriter.AddAttribute(identityPublisherAttribute, publisher);
std::string versionString = MSIX::ConvertVersionToString(version);
m_xmlWriter.AddAttribute(identityVersionAttribute, versionString);
m_xmlWriter.CloseElement();
}
void BundleManifestWriter::StartPackagesElement()
{
m_xmlWriter.StartElement(packagesManifestElement);
}
void BundleManifestWriter::WritePackageElement(PackageInfo packageInfo)
{
m_xmlWriter.StartElement(packageManifestElement);
std::string packageTypeString;
if(packageInfo.type == APPX_BUNDLE_PAYLOAD_PACKAGE_TYPE::APPX_BUNDLE_PAYLOAD_PACKAGE_TYPE_APPLICATION)
{
packageTypeString = ApplicationPackageType;
}
else if (packageInfo.type == APPX_BUNDLE_PAYLOAD_PACKAGE_TYPE::APPX_BUNDLE_PAYLOAD_PACKAGE_TYPE_RESOURCE)
{
packageTypeString = ResourcePackageType;
}
m_xmlWriter.AddAttribute(packageTypeAttribute, packageTypeString);
std::string versionString = MSIX::ConvertVersionToString(packageInfo.version);
m_xmlWriter.AddAttribute(packageVersionAttribute, versionString);
if(packageInfo.type == APPX_BUNDLE_PAYLOAD_PACKAGE_TYPE::APPX_BUNDLE_PAYLOAD_PACKAGE_TYPE_APPLICATION)
{
m_xmlWriter.AddAttribute(packageArchitectureAttribute, packageInfo.architecture);
}
if (!packageInfo.resourceId.empty() && (packageInfo.resourceId.size() > 0))
{
m_xmlWriter.AddAttribute(packageResourceIdAttribute, packageInfo.resourceId);
}
if(!packageInfo.fileName.empty())
{
m_xmlWriter.AddAttribute(packageFileNameAttribute, packageInfo.fileName);
}
if(packageInfo.offset > 0)
{
//TODO: not applicable for flat bundle
}
if (packageInfo.size > 0 && packageInfo.offset > 0)
{
//TODO: not applicable for flat bundles
}
//WriteResourcesElement
WriteResourcesElement(packageInfo.resources.Get());
//WriteDependenciesElement
WriteDependenciesElement(packageInfo.tdfs.Get());
//End Package Tag
m_xmlWriter.CloseElement();
}
void BundleManifestWriter::WriteResourcesElement(IAppxManifestQualifiedResourcesEnumerator* resources)
{
BOOL hasResources = FALSE;
ThrowHrIfFailed(resources->GetHasCurrent(&hasResources));
if (hasResources)
{
//Start Resources element
m_xmlWriter.StartElement(resourcesManifestElement);
BOOL hasNext = FALSE;
ThrowHrIfFailed(resources->GetHasCurrent(&hasNext));
while (hasNext)
{
ComPtr<IAppxManifestQualifiedResource> resource;
ThrowHrIfFailed(resources->GetCurrent(&resource));
//Start Resource element
m_xmlWriter.StartElement(resourceManifestElement);
auto qualifiedResourceInternal = resource.As<IAppxManifestQualifiedResourceInternal>();
std::string languageString = qualifiedResourceInternal->GetLanguage();
if (!languageString.empty())
{
m_xmlWriter.AddAttribute(resourceLanguageAttribute, languageString);
}
//TODO:: Write scale and dxfeaturelevel attributes
//End Resource element
m_xmlWriter.CloseElement();
ThrowHrIfFailed(resources->MoveNext(&hasNext));
}
//End Resources element
m_xmlWriter.CloseElement();
}
}
void BundleManifestWriter::WriteDependenciesElement(IAppxManifestTargetDeviceFamiliesEnumerator* tdfs)
{
BOOL hasNext = FALSE;
ThrowHrIfFailed(tdfs->GetHasCurrent(&hasNext));
if (hasNext)
{
std::string dependencyQName = GetElementName(Namespace2018, Namespace2018Alias, dependenciesManifestElementWithoutPrefix);
m_xmlWriter.StartElement(dependencyQName);
while (hasNext)
{
ComPtr<IAppxManifestTargetDeviceFamily> tdf;
ThrowHrIfFailed(tdfs->GetCurrent(&tdf));
//Start TargetDeviceFamily manifest element
std::string tdfQName = GetElementName(Namespace2018, Namespace2018Alias, targetDeviceFamilyManifestElementWithoutPrefix);
m_xmlWriter.StartElement(tdfQName);
auto targetDeviceFamilyInternal = tdf.As<IAppxManifestTargetDeviceFamilyInternal>();
std::string name = targetDeviceFamilyInternal->GetName();
m_xmlWriter.AddAttribute(tdfNameAttribute, name);
//Get minversion
UINT64 minVersion;
ThrowHrIfFailed(tdf->GetMinVersion(&minVersion));
std::string minVerionString = MSIX::ConvertVersionToString(minVersion);
m_xmlWriter.AddAttribute(tdfMinVersionAttribute, minVerionString);
//Get maxversiontested
UINT64 maxVersionTested;
ThrowHrIfFailed(tdf->GetMaxVersionTested(&maxVersionTested));
std::string maxVersionTestedString = MSIX::ConvertVersionToString(maxVersionTested);
m_xmlWriter.AddAttribute(tdfMaxVersionTestedAttribute, maxVersionTestedString);
//End TargetDeviceFamily manifest element
m_xmlWriter.CloseElement();
ThrowHrIfFailed(tdfs->MoveNext(&hasNext));
}
//End Dependencies Tag
m_xmlWriter.CloseElement();
}
}
void BundleManifestWriter::EndPackagesElement()
{
//TODO:: Main state to check if package is added and close only if
m_xmlWriter.CloseElement();
}
void BundleManifestWriter::Close()
{
//Ends Bundle Element
m_xmlWriter.CloseElement();
}
std::string BundleManifestWriter::GetElementName(std::string targetNamespace, std::string targetNamespaceAlias, std::string name)
{
std::string qualifiedName;
if ((this->targetXmlNamespace.compare(targetNamespace) != 0) && (!targetNamespaceAlias.empty()))
{
qualifiedName = GetQualifiedName(targetNamespaceAlias, name);
}
else
{
qualifiedName = name;
}
return qualifiedName;
}
std::string BundleManifestWriter::GetQualifiedName(std::string namespaceAlias, std::string name)
{
std::string output;
output.append(namespaceAlias);
output.append(xmlNamespaceDelimiter);
output.append(name);
return output;
}
}

Просмотреть файл

@ -0,0 +1,244 @@
#include "BundleWriterHelper.hpp"
namespace MSIX {
BundleWriterHelper::BundleWriterHelper()
{
this->bundleVersion = 0;
this->hasExternalPackages = false;
this->hasDefaultOrNeutralResources = false;
}
std::uint64_t BundleWriterHelper::GetStreamSize(IStream* stream)
{
STATSTG stat;
ThrowHrIfFailed(stream->Stat(&stat, 1));
return stat.cbSize.QuadPart;
}
void BundleWriterHelper::AddPackage(std::string fileName, IAppxPackageReader* packageReader,
std::uint64_t bundleOffset, std::uint64_t packageSize, bool isDefaultApplicableResource)
{
ComPtr<IAppxManifestPackageId> packageId;
APPX_BUNDLE_PAYLOAD_PACKAGE_TYPE packageType = APPX_BUNDLE_PAYLOAD_PACKAGE_TYPE::APPX_BUNDLE_PAYLOAD_PACKAGE_TYPE_APPLICATION;
ComPtr<IAppxManifestQualifiedResourcesEnumerator> resources;
ComPtr<IAppxManifestTargetDeviceFamiliesEnumerator> tdfs;
GetValidatedPackageData(fileName, packageReader, &packageType, &packageId, &resources, &tdfs);
AddValidatedPackageData(fileName, bundleOffset, packageSize, packageType, packageId,
isDefaultApplicableResource, resources.Get(), tdfs.Get());
}
void BundleWriterHelper::GetValidatedPackageData(
std::string fileName,
IAppxPackageReader* packageReader,
APPX_BUNDLE_PAYLOAD_PACKAGE_TYPE* packageType,
IAppxManifestPackageId** packageId,
IAppxManifestQualifiedResourcesEnumerator** resources,
IAppxManifestTargetDeviceFamiliesEnumerator** tdfs)
{
*packageId = nullptr;
*resources = nullptr;
*tdfs = nullptr;
ComPtr<IAppxManifestPackageId> loadedPackageId;
APPX_BUNDLE_PAYLOAD_PACKAGE_TYPE loadedPackageType = APPX_BUNDLE_PAYLOAD_PACKAGE_TYPE::APPX_BUNDLE_PAYLOAD_PACKAGE_TYPE_APPLICATION;
ComPtr<IAppxManifestQualifiedResourcesEnumerator> loadedResources;
ComPtr<IAppxManifestTargetDeviceFamiliesEnumerator> loadedTdfs;
ComPtr<IAppxManifestReader> manifestReader;
ThrowHrIfFailed(packageReader->GetManifest(&manifestReader));
ThrowHrIfFailed(manifestReader->GetPackageId(&loadedPackageId));
ComPtr<IAppxManifestReader3> manifestReader3;
ThrowHrIfFailed(manifestReader->QueryInterface(UuidOfImpl<IAppxManifestReader3>::iid, reinterpret_cast<void**>(&manifestReader3)));
ThrowHrIfFailed(manifestReader3->GetQualifiedResources(&loadedResources));
ThrowHrIfFailed(manifestReader3->GetTargetDeviceFamilies(&loadedTdfs));
loadedPackageType = GetPayloadPackageType(manifestReader.Get(), fileName);
//TODO:: Validate Package matches SHA256 hash method
auto packageIdInternal = loadedPackageId.As<IAppxManifestPackageIdInternal>();
ValidateNameAndPublisher(packageIdInternal.Get(), fileName);
//TODO: TDF checks
if (loadedPackageType == APPX_BUNDLE_PAYLOAD_PACKAGE_TYPE_APPLICATION)
{
ValidateApplicationElement(manifestReader.Get(), fileName);
}
*packageType = loadedPackageType;
*packageId = loadedPackageId.Detach();
*resources = loadedResources.Detach();
if (loadedTdfs.Get() != nullptr)
{
*tdfs = loadedTdfs.Detach();
}
}
void BundleWriterHelper::ValidateApplicationElement(
IAppxManifestReader* packageManifestReader,
std::string fileName)
{
ComPtr<IAppxManifestReader4> manifestReader4;
ThrowHrIfFailed(packageManifestReader->QueryInterface(UuidOfImpl<IAppxManifestReader4>::iid, reinterpret_cast<void**>(&manifestReader4)));
ComPtr<IAppxManifestOptionalPackageInfo> optionalPackageInfo;
ThrowHrIfFailed(manifestReader4->GetOptionalPackageInfo(&optionalPackageInfo));
BOOL packageIsOptional = FALSE;
ThrowHrIfFailed(optionalPackageInfo->GetIsOptionalPackage(&packageIsOptional));
if (!packageIsOptional) // optional payload packages are not required to declare any <Application> elements
{
ComPtr<IAppxManifestApplicationsEnumerator> applications;
ThrowHrIfFailed(packageManifestReader->GetApplications(&applications));
BOOL hasApplication = FALSE;
ThrowHrIfFailed(applications->GetHasCurrent(&hasApplication));
if (!hasApplication)
{
ThrowErrorAndLog(Error::AppxManifestSemanticError, "The package is not valid in the bundle because its manifest does not declare any Application elements.");
}
}
}
void BundleWriterHelper::ValidateNameAndPublisher(IAppxManifestPackageIdInternal* packageId,
std::string filename)
{
if(this->mainPackageName.empty())
{
this->mainPackageName = packageId->GetName();
this->mainPackagePublisher = packageId->GetPublisher();
}
else
{
std::string packageName = packageId->GetName();
if ((this->mainPackageName.compare(packageName)) != 0)
{
std::string packageFullName = packageId->GetPackageFullName();
ThrowErrorAndLog(Error::AppxManifestSemanticError, "The package is not valid in the bundle because it has a different package family name than other packages in the bundle.");
}
std::string publisherName = packageId->GetPublisher();
if ((this->mainPackagePublisher.compare(publisherName)) != 0)
{
std::string packageFullName = packageId->GetPackageFullName();
ThrowErrorAndLog(Error::AppxManifestSemanticError, "The package is not valid in the bundle because it has a different package family name than other packages in the bundle.");
}
}
}
APPX_BUNDLE_PAYLOAD_PACKAGE_TYPE BundleWriterHelper::GetPayloadPackageType(IAppxManifestReader* packageManifestReader,
std::string fileName)
{
ComPtr<IAppxManifestProperties> packageProperties;
ThrowHrIfFailed(packageManifestReader->GetProperties(&packageProperties));
BOOL isFrameworkPackage = FALSE;
ThrowHrIfFailed(packageProperties->GetBoolValue(L"Framework", &isFrameworkPackage));
if (isFrameworkPackage)
{
ThrowErrorAndLog(Error::AppxManifestSemanticError, "The package is not valid in the bundle because it is a framework package.");
}
BOOL isResourcePackage = FALSE;
ThrowHrIfFailed(packageProperties->GetBoolValue(L"ResourcePackage", &isResourcePackage));
APPX_BUNDLE_PAYLOAD_PACKAGE_TYPE packageType = (isResourcePackage ? APPX_BUNDLE_PAYLOAD_PACKAGE_TYPE_RESOURCE : APPX_BUNDLE_PAYLOAD_PACKAGE_TYPE_APPLICATION);
return packageType;
}
void BundleWriterHelper::AddValidatedPackageData(
std::string fileName,
std::uint64_t bundleOffset,
std::uint64_t packageSize,
APPX_BUNDLE_PAYLOAD_PACKAGE_TYPE packageType,
ComPtr<IAppxManifestPackageId> packageId,
bool isDefaultApplicablePackage,
IAppxManifestQualifiedResourcesEnumerator* resources,
IAppxManifestTargetDeviceFamiliesEnumerator* tdfs)
{
//TODO: validate package payload extension
auto innerPackageIdInternal = packageId.As<IAppxManifestPackageIdInternal>();
PackageInfo packageInfo;
packageInfo.type = packageType;
packageInfo.architecture = innerPackageIdInternal->GetArchitecture();
UINT64 version;
ThrowHrIfFailed(packageId->GetVersion(&version));
packageInfo.version = version;
packageInfo.resourceId = innerPackageIdInternal->GetResourceId();
packageInfo.isDefaultApplicablePackage = isDefaultApplicablePackage;
packageInfo.resources = resources;
packageInfo.fileName = fileName;
packageInfo.size = packageSize;
packageInfo.offset = bundleOffset;
packageInfo.tdfs = tdfs;
AddPackageInfoToVector(this->payloadPackages, packageInfo);
}
void BundleWriterHelper::AddPackageInfoToVector(std::vector<PackageInfo>& packagesVector,
PackageInfo packageInfo)
{
packagesVector.push_back(packageInfo);
if (packageInfo.offset == 0)
{
this->hasExternalPackages = true;
}
if (packageInfo.isDefaultApplicablePackage)
{
this->hasDefaultOrNeutralResources = true;
}
BOOL hasResources = FALSE;
ThrowHrIfFailed(packageInfo.resources->GetHasCurrent(&hasResources));
if (!hasResources)
{
this->hasDefaultOrNeutralResources = true;
}
}
void BundleWriterHelper::EndBundleManifest()
{
std::string targetXmlNamespace = "http://schemas.microsoft.com/appx/2013/bundle";
bool isPre2018BundleManifest = true;
//TODO: Only use new 2018 bundle schema if the bundle contains more than 1 neutral app packages
if (this->hasDefaultOrNeutralResources)
{
targetXmlNamespace = "http://schemas.microsoft.com/appx/2017/bundle";
}
else if ((this->optionalBundles.size() > 0) || this->hasExternalPackages)
{
targetXmlNamespace = "http://schemas.microsoft.com/appx/2016/bundle";
}
m_bundleManifestWriter.StartBundleManifest(targetXmlNamespace, this->mainPackageName,
this->mainPackagePublisher, this->bundleVersion);
for(std::size_t i = 0; i < this->payloadPackages.size(); i++)
{
m_bundleManifestWriter.WritePackageElement(payloadPackages[i]);
}
//TODO: this->OptionalBundles
//Ends Packages and bundle Element
m_bundleManifestWriter.EndPackagesElement();
m_bundleManifestWriter.Close();
}
}

Просмотреть файл

@ -135,4 +135,22 @@ namespace MSIX {
// TODO: add other ones if needed, otherwise throw
ThrowErrorAndLog(Error::NotSupported, "Payload file content type not found");
}
const std::string ContentType::GetBundlePayloadFileContentType(APPX_BUNDLE_FOOTPRINT_FILE_TYPE footprintFile)
{
if (footprintFile == APPX_BUNDLE_FOOTPRINT_FILE_TYPE_MANIFEST)
{
return "application/vnd.ms-appx.bundlemanifest+xml";
}
if (footprintFile == APPX_BUNDLE_FOOTPRINT_FILE_TYPE_BLOCKMAP)
{
return "application/vnd.ms-appx.blockmap+xml";
}
if (footprintFile == APPX_BUNDLE_FOOTPRINT_FILE_TYPE_SIGNATURE)
{
return "application/vnd.ms-appx.signature";
}
// TODO: add other ones if needed, otherwise throw
ThrowErrorAndLog(Error::NotSupported, "Bundle Payload file content type not found");
}
}

Просмотреть файл

@ -0,0 +1,34 @@
#include "VersionHelpers.hpp"
namespace MSIX{
std::uint64_t ConvertVersionStringToUint64(const std::string& versionString)
{
std::uint64_t version = 0;
size_t position = 0;
auto nextPeriod = versionString.find('.', position);
version = (std::uint64_t)std::stoi(versionString.substr(position, nextPeriod)) << 0x30;
position = nextPeriod + 1;
nextPeriod = versionString.find('.', position);
version += (std::uint64_t)std::stoi(versionString.substr(position, nextPeriod)) << 0x20;
position = nextPeriod + 1;
nextPeriod = versionString.find('.', position);
version += (std::uint64_t)std::stoi(versionString.substr(position, nextPeriod)) << 0x10;
position = nextPeriod + 1;
nextPeriod = versionString.find('.', position);
version += (std::uint64_t)std::stoi(versionString.substr(position, nextPeriod));
return version;
}
std::string ConvertVersionToString(std::uint64_t version)
{
return std::to_string((version >> 0x30) & 0xFFFF) + "."
+ std::to_string((version >> 0x20) & 0xFFFF) + "."
+ std::to_string((version >> 0x10) & 0xFFFF) + "."
+ std::to_string((version) & 0xFFFF);
}
}