Merged PR 2461986: Add interface for applicability override

Added interface to override language applicability in bundle reader.
Added sample to illustrate usage.

Related work items: #19015501
This commit is contained in:
Letao Wang 2018-10-16 01:13:32 +00:00 коммит произвёл Adrian Mascarenhas
Родитель 35e69e4ec5
Коммит 49e5f4c1c9
12 изменённых файлов: 447 добавлений и 2 удалений

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

@ -6,7 +6,9 @@ cmake_minimum_required(VERSION 3.8.0 FATAL_ERROR)
add_subdirectory(ExtractContentsSample)
add_subdirectory(BundleSample)
add_subdirectory(OverrideStreamSample)
add_subdirectory(OverrideLanguageSample)
add_dependencies(ExtractContentsSample msix)
add_dependencies(BundleSample msix)
add_dependencies(OverrideStreamSample msix)
add_dependencies(OverrideLanguageSample msix)

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

@ -0,0 +1,27 @@
# Copyright (C) 2017 Microsoft. All rights reserved.
# See LICENSE file in the project root for full license information.
cmake_minimum_required(VERSION 3.8.0 FATAL_ERROR)
project (OverrideLanguageSample)
# Define two variables in order not to repeat ourselves.
set(BINARY_NAME OverrideLanguageSample)
if(WIN32)
set(DESCRIPTION "OverrideLanguageSample manifest")
configure_file(${CMAKE_PROJECT_ROOT}/manifest.cmakein ${CMAKE_CURRENT_BINARY_DIR}/${BINARY_NAME}.exe.manifest CRLF)
set(MANIFEST ${CMAKE_CURRENT_BINARY_DIR}/${BINARY_NAME}.exe.manifest)
endif()
add_executable(${BINARY_NAME}
OverrideLanguageSample.cpp
${MANIFEST}
)
target_include_directories(${BINARY_NAME} PRIVATE ${CMAKE_BINARY_DIR}/src/msix)
add_dependencies(${BINARY_NAME} msix)
if (LINUX OR AOSP)
target_link_libraries(${BINARY_NAME} PRIVATE -latomic)
endif()
target_link_libraries(${BINARY_NAME} PRIVATE msix)

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

@ -0,0 +1,329 @@
//
// Copyright (C) 2017 Microsoft. All rights reserved.
// See LICENSE file in the project root for full license information.
//
#include "AppxPackaging.hpp"
#include "MSIXWindows.hpp"
#include <iostream>
#include <atomic>
#define RETURN_IF_FAILED(a) \
{ HRESULT __hr = a; \
if (FAILED(__hr)) \
{ return __hr; } \
}
// Stripped down ComPtr provided for those platforms that do not already have a ComPtr class.
template <class T>
class ComPtr
{
public:
// default ctor
ComPtr() = default;
ComPtr(T* ptr) : m_ptr(ptr) { InternalAddRef(); }
// For use instead of ComPtr<T> t(new Foo(...)); given that the class has an Initialize function
template<class U, class... Args>
static HRESULT MakeAndInitialize(T** result, Args&&... args)
{
ComPtr<U> inner(new U());
RETURN_IF_FAILED(inner->Initialize(std::forward<Args>(args)...));
RETURN_IF_FAILED(inner->QueryInterface(UuidOfImpl<T>::iid, reinterpret_cast<void**>(result)));
return S_OK;
}
// For use instead of ComPtr<T> t(new Foo(...));
template<class U, class... Args>
static ComPtr<T> Make(Args&&... args)
{
ComPtr<T> result;
result.m_ptr = new U(std::forward<Args>(args)...);
return result;
}
~ComPtr() { InternalRelease(); }
inline T* operator->() const { return m_ptr; }
inline T* Get() const { return m_ptr; }
inline T** operator&()
{
InternalRelease();
return &m_ptr;
}
T* Detach()
{
T* temp = m_ptr;
m_ptr = nullptr;
return temp;
}
protected:
T* m_ptr = nullptr;
inline void InternalAddRef() { if (m_ptr) { m_ptr->AddRef(); } }
inline void InternalRelease()
{
T* temp = m_ptr;
if (temp)
{
m_ptr = nullptr;
temp->Release();
}
}
};
// Or you can use what-ever allocator/deallocator is best for your platform...
LPVOID STDMETHODCALLTYPE MyAllocate(SIZE_T cb) { return std::malloc(cb); }
void STDMETHODCALLTYPE MyFree(LPVOID pv) { std::free(pv); }
// Helper class to free string buffers obtained from the packaging APIs.
template<typename T>
class Text
{
public:
T** operator&() { return &content; }
~Text() { Cleanup(); }
T* Get() { return content; }
T* content = nullptr;
protected:
void Cleanup() { if (content) { MyFree(content); content = nullptr; } }
};
int Help()
{
std::cout << std::endl;
std::cout << "Usage:" << std::endl;
std::cout << "------" << std::endl;
std::cout << "\t" << "OverrideLanguageSample <bundle> <languages ...>" << std::endl;
std::cout << std::endl;
std::cout << "Description:" << std::endl;
std::cout << "------------" << std::endl;
std::cout << "\tSample to show the usage of IMsixFactoryOverrides for MSIX_FACTORY_EXTENSION_APPLICABILITY_LANGUAGES " << std::endl;
std::cout << "\tusing language BCP47 tags from the command line." << std::endl;
std::cout << std::endl;
return 0;
}
class OverrideLanguages final : public IMsixApplicabilityLanguagesEnumerator
{
public:
OverrideLanguages(char** languages, int numLanguages) : m_languages(languages), m_numLanguages(numLanguages) {}
// IUnknown
virtual ULONG STDMETHODCALLTYPE AddRef() override
{
return ++m_ref;
}
virtual ULONG STDMETHODCALLTYPE Release() override
{
if (--m_ref == 0)
{
delete this;
return 0;
}
return m_ref;
}
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) noexcept override
{
if (ppvObject == nullptr || *ppvObject != nullptr)
{
return E_INVALIDARG;
}
*ppvObject = nullptr;
if (riid == UuidOfImpl<IUnknown>::iid)
{
*ppvObject = static_cast<void*>(reinterpret_cast<IUnknown*>(this));
AddRef();
return S_OK;
}
if (riid == UuidOfImpl<IMsixApplicabilityLanguagesEnumerator>::iid)
{
*ppvObject = static_cast<void*>(reinterpret_cast<IMsixApplicabilityLanguagesEnumerator*>(this));
AddRef();
return S_OK;
}
return E_NOINTERFACE;
}
// IMsixApplicabilityLanguagesEnumerator
virtual HRESULT STDMETHODCALLTYPE GetCurrent(LPCSTR *bcp47Language) noexcept override
{
if (m_currentIndex < m_numLanguages)
{
*bcp47Language = m_languages[m_currentIndex];
return S_OK;
}
else
{
return E_BOUNDS;
}
}
virtual HRESULT STDMETHODCALLTYPE GetHasCurrent(BOOL *hasCurrent) noexcept override
{
*hasCurrent = (m_currentIndex < m_numLanguages);
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE MoveNext(BOOL *hasNext) noexcept override
{
if (m_currentIndex < m_numLanguages)
{
m_currentIndex++;
*hasNext = (m_currentIndex < m_numLanguages);
return S_OK;
}
else
{
return E_BOUNDS;
}
}
protected:
std::atomic<std::uint32_t> m_ref;
char** m_languages = nullptr;
int m_numLanguages = 0;
int m_currentIndex = 0;
};
HRESULT ShowInformationOfPackage(IAppxFile* packageFile)
{
Text<WCHAR> packageName;
RETURN_IF_FAILED(packageFile->GetName(&packageName));
std::wcout << L"\tName: " << packageName.Get() << std::endl;
ComPtr<IAppxFactory> factory;
RETURN_IF_FAILED(CoCreateAppxFactoryWithHeap(
MyAllocate,
MyFree,
MSIX_VALIDATION_OPTION::MSIX_VALIDATION_OPTION_SKIPSIGNATURE,
&factory));
// Get stream of the package and package reader
ComPtr<IStream> packageStream;
RETURN_IF_FAILED(packageFile->GetStream(&packageStream));
ComPtr<IAppxPackageReader> packageReader;
RETURN_IF_FAILED(factory->CreatePackageReader(packageStream.Get(), &packageReader));
// Get information about the package from the manifest
ComPtr<IAppxManifestReader> manifest;
RETURN_IF_FAILED(packageReader->GetManifest(&manifest));
ComPtr<IAppxManifestPackageId> bundlePackageId;
RETURN_IF_FAILED(manifest->GetPackageId(&bundlePackageId));
// Get full name of the bundle
Text<WCHAR> fullName;
RETURN_IF_FAILED(bundlePackageId->GetPackageFullName(&fullName));
std::wcout << L"\tFull Name: " << fullName.Get() << std::endl;
// Show what are the resources of the package.
ComPtr<IAppxManifestResourcesEnumerator> resourcesEnum;
RETURN_IF_FAILED(manifest->GetResources(&resourcesEnum));
BOOL hasCurrent = FALSE;
RETURN_IF_FAILED(resourcesEnum->GetHasCurrent(&hasCurrent));
while (hasCurrent)
{
Text<WCHAR> resource;
RETURN_IF_FAILED(resourcesEnum->GetCurrent(&resource));
std::wcout << L"\tResource: " << resource.Get() << std::endl;
RETURN_IF_FAILED(resourcesEnum->MoveNext(&hasCurrent));
}
return S_OK;
}
HRESULT ShowInformationOfBundle(char* bundleName, char** languages, int numLanguages)
{
std::cout << "File: " << bundleName << std::endl;
// Initialize the factory with full validation and applicability options.
ComPtr<IAppxBundleFactory> bundleFactory;
RETURN_IF_FAILED(CoCreateAppxBundleFactoryWithHeap(
MyAllocate,
MyFree,
MSIX_VALIDATION_OPTION::MSIX_VALIDATION_OPTION_SKIPSIGNATURE,
MSIX_APPLICABILITY_OPTIONS::MSIX_APPLICABILITY_OPTION_FULL,
&bundleFactory));
// Create our object that implements IMsixApplicabilityLanguagesEnumerator
auto overrideLanguages = ComPtr<IMsixApplicabilityLanguagesEnumerator>::Make<OverrideLanguages>(languages, numLanguages);
// Get the IMsixFactoryOverrides and set the override.
// Do this before using the factory to create the bundle reader.
ComPtr<IMsixFactoryOverrides> factoryOverrides;
RETURN_IF_FAILED(bundleFactory->QueryInterface(UuidOfImpl<IMsixFactoryOverrides>::iid, reinterpret_cast<void**>(&factoryOverrides)));
RETURN_IF_FAILED(factoryOverrides->SpecifyExtension(MSIX_FACTORY_EXTENSION_APPLICABILITY_LANGUAGES, overrideLanguages.Get()));
// Create stream on the file provided.
ComPtr<IStream> inputStream;
RETURN_IF_FAILED(CreateStreamOnFile(const_cast<char*>(bundleName), true, &inputStream));
// Now get the bundle reader
ComPtr<IAppxBundleReader> bundleReader;
RETURN_IF_FAILED(bundleFactory->CreateBundleReader(inputStream.Get(), &bundleReader));
// Get basic information about this bundle from the bundle manifest
ComPtr<IAppxBundleManifestReader> manifestReader;
RETURN_IF_FAILED(bundleReader->GetManifest(&manifestReader));
ComPtr<IAppxManifestPackageId> bundlePackageId;
RETURN_IF_FAILED(manifestReader->GetPackageId(&bundlePackageId));
// Get full name of the bundle
Text<WCHAR> fullName;
RETURN_IF_FAILED(bundlePackageId->GetPackageFullName(&fullName));
std::wcout << L"Full Name: " << fullName.Get() << std::endl;
// A bundle with the MSIX_FACTORY_EXTENSION_APPLICABILITY_LANGUAGES override
// will only show the packages that are applicable to the list of languages
// specified. Get the applicable packages by calling GetPayloadPackages.
ComPtr<IAppxFilesEnumerator> applicablePackages;
RETURN_IF_FAILED(bundleReader->GetPayloadPackages(&applicablePackages));
BOOL hasCurrent = FALSE;
RETURN_IF_FAILED(applicablePackages->GetHasCurrent(&hasCurrent));
std::wcout << L"Applicable Packages: " << std::endl;
while (hasCurrent)
{
ComPtr<IAppxFile> applicablePackage;
RETURN_IF_FAILED(applicablePackages->GetCurrent(&applicablePackage));
RETURN_IF_FAILED(ShowInformationOfPackage(applicablePackage.Get()));
std::wcout << std::endl;
RETURN_IF_FAILED(applicablePackages->MoveNext(&hasCurrent));
}
return S_OK;
}
int main(int argc, char* argv[])
{
if (argc < 3)
{
return Help();
}
HRESULT hr = ShowInformationOfBundle(argv[1], argv + 2, argc - 2);
if (FAILED(hr))
{
std::cout << "Error: " << std::hex << hr << " while reading the bundle" << std::endl;
Text<char> text;
auto logResult = GetLogTextUTF8(MyAllocate, &text);
if (0 == logResult)
{
std::cout << "LOG:" << std::endl << text.content << std::endl;
}
else
{
std::cout << "UNABLE TO GET LOG WITH HR=" << std::hex << logResult << std::endl;
}
}
return 0;
}

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

@ -50,6 +50,9 @@ namespace MSIX {
Applicability(MSIX_APPLICABILITY_OPTIONS applicabilityFlags) : m_applicabilityFlags(applicabilityFlags)
{}
void InitializeLanguages();
void InitializeLanguages(IMsixApplicabilityLanguagesEnumerator* languagesEnumerator);
void AddPackageIfApplicable(ComPtr<IAppxPackageReader>& reader, std::string& packageName,
const std::vector<Bcp47Tag>& packageLanguages, APPX_BUNDLE_PAYLOAD_PACKAGE_TYPE packageType, bool hasQualifiedResources);
@ -65,5 +68,6 @@ namespace MSIX {
std::vector<std::pair<std::string, ComPtr<IAppxPackageReader>>> m_variantFormPackages;
std::vector<std::pair<std::string, ComPtr<IAppxPackageReader>>> m_extraApplicationPackages;
MSIX_APPLICABILITY_OPTIONS m_applicabilityFlags = MSIX_APPLICABILITY_OPTIONS::MSIX_APPLICABILITY_OPTION_FULL;
std::vector<Bcp47Tag> m_languages;
};
}

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

@ -71,5 +71,6 @@ namespace MSIX {
std::vector<std::uint8_t> m_resourcesVector;
MSIX_APPLICABILITY_OPTIONS m_applicabilityFlags;
ComPtr<IMsixStreamFactory> m_streamFactory;
ComPtr<IMsixApplicabilityLanguagesEnumerator> m_applicabilityLanguagesEnumerator;
};
}

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

@ -1121,6 +1121,7 @@ interface IMsixElement;
interface IMsixElementEnumerator;
interface IMsixFactoryOverrides;
interface IMsixStreamFactory;
interface IMsixApplicabilityLanguagesEnumerator;
#ifndef __IMsixDocumentElement_INTERFACE_DEFINED__
#define __IMsixDocumentElement_INTERFACE_DEFINED__
@ -1177,6 +1178,7 @@ EXTERN_C DECLSPEC_SELECTANY const IID IID_IMsixFactoryOverrides;
enum MSIX_FACTORY_EXTENSION
{
MSIX_FACTORY_EXTENSION_STREAM_FACTORY = 0x1,
MSIX_FACTORY_EXTENSION_APPLICABILITY_LANGUAGES = 0x2,
} MSIX_FACTORY_EXTENSION;
// {0acedbdb-57cd-4aca-8cee-33fa52394316}
@ -1203,6 +1205,25 @@ EXTERN_C DECLSPEC_SELECTANY const IID IID_IMsixStreamFactory;
#endif /* __IMsixStreamFactory_INTERFACE_DEFINED__ */
#ifndef __IMsixApplicabilityLanguagesEnumerator_INTERFACE_DEFINED__
#define __IMsixApplicabilityLanguagesEnumerator_INTERFACE_DEFINED__
EXTERN_C DECLSPEC_SELECTANY const IID IID_IMsixApplicabilityLanguagesEnumerator;
// {BFC4655A-BE7A-456A-BC4E-2AF9481E8432}
interface IMsixApplicabilityLanguagesEnumerator : public IUnknown
{
virtual HRESULT STDMETHODCALLTYPE GetCurrent(
/* [retval][string][out] */ LPCSTR *bcp47Language) = 0;
virtual HRESULT STDMETHODCALLTYPE GetHasCurrent(
/* [retval][out] */ BOOL *hasCurrent) = 0;
virtual HRESULT STDMETHODCALLTYPE MoveNext(
/* [retval][out] */ BOOL *hasNext) = 0;
};
#endif /* __IMsixApplicabilityLanguagesEnumerator_INTERFACE_DEFINED__ */
extern "C++" {
typedef /* [v1_enum] */
@ -1386,5 +1407,6 @@ SpecializeUuidOfImpl(IMsixElement);
SpecializeUuidOfImpl(IMsixElementEnumerator);
SpecializeUuidOfImpl(IMsixFactoryOverrides);
SpecializeUuidOfImpl(IMsixStreamFactory);
SpecializeUuidOfImpl(IMsixApplicabilityLanguagesEnumerator);
#endif //__appxpackaging_hpp__

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

@ -112,6 +112,16 @@
typedef const WCHAR* LPCWSTR;
#endif
#ifndef _LPSTR_DEFINED
#define _LPSTR_DEFINED
typedef char* LPSTR;
#endif
#ifndef _LPCSTR_DEFINED
#define _LPCSTR_DEFINED
typedef const char* LPCSTR;
#endif
#ifndef _BYTE_DEFINED
#define _BYTE_DEFINED
typedef unsigned char BYTE;
@ -254,6 +264,10 @@
#define E_NOTIMPL 0x80004001
#endif
#ifndef E_BOUNDS
#define E_BOUNDS 0x8000000b
#endif
#if !defined (_SYS_GUID_OPERATORS_)
#define _SYS_GUID_OPERATORS_

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

@ -136,6 +136,25 @@ namespace MSIX {
return result;
}
void Applicability::InitializeLanguages()
{
m_languages = GetLanguages();
}
void Applicability::InitializeLanguages(IMsixApplicabilityLanguagesEnumerator* languagesEnumerator)
{
BOOL hasNext = FALSE;
ThrowHrIfFailed(languagesEnumerator->GetHasCurrent(&hasNext));
while (hasNext)
{
LPCSTR language = nullptr;
ThrowHrIfFailed(languagesEnumerator->GetCurrent(&language));
m_languages.push_back(std::string(language));
ThrowHrIfFailed(languagesEnumerator->MoveNext(&hasNext));
}
}
void Applicability::AddPackageIfApplicable(ComPtr<IAppxPackageReader>& reader, std::string& packageName,
const std::vector<Bcp47Tag>& packageLanguages, APPX_BUNDLE_PAYLOAD_PACKAGE_TYPE packageType, bool hasQualifiedResources)
{
@ -158,7 +177,7 @@ namespace MSIX {
bool hasMatch = false;
bool hasVariantMatch = false;
for (auto& systemLanguage : GetLanguages())
for (auto& systemLanguage : m_languages)
{
for (auto& packageLanguage : packageLanguages)
{

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

@ -186,6 +186,10 @@ namespace MSIX {
{
ThrowHrIfFailed(extension->QueryInterface(UuidOfImpl<IMsixStreamFactory>::iid, reinterpret_cast<void**>(&m_streamFactory)));
}
else if (name == MSIX_FACTORY_EXTENSION_APPLICABILITY_LANGUAGES)
{
ThrowHrIfFailed(extension->QueryInterface(UuidOfImpl<IMsixApplicabilityLanguagesEnumerator>::iid, reinterpret_cast<void**>(&m_applicabilityLanguagesEnumerator)));
}
else
{
return static_cast<HRESULT>(Error::InvalidParameter);
@ -205,6 +209,13 @@ namespace MSIX {
*extension = m_streamFactory.As<IUnknown>().Detach();
}
}
else if (name == MSIX_FACTORY_EXTENSION_APPLICABILITY_LANGUAGES)
{
if (m_applicabilityLanguagesEnumerator.Get() != nullptr)
{
*extension = m_applicabilityLanguagesEnumerator.As<IUnknown>().Detach();
}
}
else
{
return static_cast<HRESULT>(Error::InvalidParameter);

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

@ -216,6 +216,21 @@ namespace MSIX {
auto appxFactory = m_factory.As<IAppxFactory>();
Applicability applicability(applicabilityFlags);
auto factoryOverrides = m_factory.As<IMsixFactoryOverrides>();
ComPtr<IUnknown> applicabilityLanguagesUnk;
ThrowHrIfFailed(factoryOverrides->GetCurrentSpecifiedExtension(MSIX_FACTORY_EXTENSION_APPLICABILITY_LANGUAGES, &applicabilityLanguagesUnk));
if (applicabilityLanguagesUnk.Get() != nullptr)
{
auto applicabilityLanguagesEnumerator = applicabilityLanguagesUnk.As<IMsixApplicabilityLanguagesEnumerator>();
applicability.InitializeLanguages(applicabilityLanguagesEnumerator.Get());
}
else
{
applicability.InitializeLanguages();
}
for (const auto& package : bundleInfo->GetPackages())
{
auto bundleInfoInternal = package.As<IAppxBundleManifestPackageInfoInternal>();
@ -232,7 +247,6 @@ namespace MSIX {
// We should only do this for flat bundles. If we do it for normal bundles and the user specify a
// stream factory we will basically unpack any package the user wants with the same name as the package
// we are looking, which sounds dangerous.
auto factoryOverrides = m_factory.As<IMsixFactoryOverrides>();
ComPtr<IUnknown> streamFactoryUnk;
ThrowHrIfFailed(factoryOverrides->GetCurrentSpecifiedExtension(MSIX_FACTORY_EXTENSION_STREAM_FACTORY, &streamFactoryUnk));

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

@ -78,6 +78,7 @@ MIDL_PUBLIC_GUID(IID, IID_IMsixElement,0x5B6786FF,0x6145,0x4F0E,0xB8,0xC9,0x8E,0
MIDL_PUBLIC_GUID(IID, IID_IMsixElementEnumerator,0x7E7EA105,0xA4F9,0x4C12,0x9E,0xFA,0x98,0xEF,0x18,0x95,0x41,0x8A);
MIDL_PUBLIC_GUID(IID, IID_IMsixFactoryOverrides,0x0ACEDBDB,0x57CD,0x4ACA,0X8C,0XEE,0X33,0XFA,0X52,0X39,0X43,0X16);
MIDL_PUBLIC_GUID(IID, IID_IMsixStreamFactory,0XC74F4821,0X3B82,0X4AD5,0X98,0XEA,0X3D,0X52,0X68,0X1A,0XFF,0X56);
MIDL_PUBLIC_GUID(IID, IID_IMsixApplicabilityLanguagesEnumerator,0xbfc4655a,0xbe7a,0x456a,0xbc,0x4e,0x2a,0xf9,0x48,0x1e,0x84,0x32);
// internal interfaces.
MIDL_DEFINE_GUID(IID, IID_IPackage, 0x51B2C456,0xAAA9,0x46D6,0x8E,0xC9,0x29,0x82,0x20,0x55,0x91,0x89);

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

@ -98,6 +98,7 @@ else()
"IID_IMsixElementEnumerator"
"IID_IMsixFactoryOverrides"
"IID_IMsixStreamFactory"
"IID_IMsixApplicabilityLanguagesEnumerator"
)
if((IOS) OR (MACOS))
# on Apple platforms you can explicitly define which symbols are exported