Add ability to unpack and apply acls to package folders in msixmgr.exe (#201)
* Add ability to unpack packages and apply ACLs to the resulting package folders to msixmgr.exe * Add command to apply acls to already unpacked package. For Unpack command, add logging for invalid package path. Wrap hmodule in smart pointer. Gate ACL logic behind IsApplyACLs * Reformated logging for invalid package path * Bug fix for wrapping applyaclsdll module with unique_ptr * Refactor: move unpack/acl logic out of msixmgr.cpp and into separate files/classes * Remove classes from UnpackProvider and ApplyACLsProvider * Bug Fix: ApplyACLs command could not accept package path parameter; Bug fix: use Text<WCHAR> instead of PWSTR * Use the optional package's publisher, not its entire package family name, when no publisher specified for main package dependency * Updated Api_AppxManifestReader_OptionalPackage test to reflect bug fix * Removed logging from ApplyACLsProvider * Test bug fix: missing command in expectedPublisherValues vector * Test fix: wrong publisher specified in expectedPublisherValues * Updated help text; Output info to user on failures * Revert "Test fix: wrong publisher specified in expectedPublisherValues" This reverts commit 8c4954c6c167e6c7446055dcbb395a1be80f83f8. * Checking back in test changes * Checking back in msixmgr.vcxproj* files * Output errors in hex; Output error message specific to missing cert * Added msixmgr.exe dependency ApplyACLs.dll
This commit is contained in:
Родитель
e6f55f088e
Коммит
3c48599fc2
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
|
@ -0,0 +1,51 @@
|
|||
#include "MSIXWindows.hpp"
|
||||
#include "ApplyACLsProvider.hpp"
|
||||
#include "msixmgrLogger.hpp"
|
||||
#include "..\msixmgrLib\GeneralUtil.hpp"
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <TraceLoggingProvider.h>
|
||||
#include "InstallUI.hpp"
|
||||
|
||||
using namespace MsixCoreLib;
|
||||
using namespace std;
|
||||
|
||||
namespace MsixCoreLib
|
||||
{
|
||||
HRESULT ApplyACLs(
|
||||
_In_ std::vector<std::wstring> packageFolders)
|
||||
{
|
||||
auto autoFreeLibrary = [](HMODULE* module)
|
||||
{
|
||||
if (module != nullptr)
|
||||
{
|
||||
FreeLibrary(*module);
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<HMODULE, decltype(autoFreeLibrary)>
|
||||
applyACLsDll(nullptr, autoFreeLibrary);
|
||||
|
||||
HMODULE applyACLsLocal = LoadLibrary(L"applyacls.dll");
|
||||
applyACLsDll.reset(&applyACLsLocal);
|
||||
|
||||
if (applyACLsDll == nullptr)
|
||||
{
|
||||
std::wcout << "Failed to load applyacls.dll. Please confirm the dll is next to this exe" << std::endl;
|
||||
}
|
||||
|
||||
typedef HRESULT(STDMETHODCALLTYPE *APPLYACLSTOPACKAGEFOLDER)(PCWSTR folderPath);
|
||||
|
||||
APPLYACLSTOPACKAGEFOLDER ApplyACLsToPackageFolder =
|
||||
reinterpret_cast<APPLYACLSTOPACKAGEFOLDER>
|
||||
(GetProcAddress(*applyACLsDll, "ApplyACLsToPackageFolder"));
|
||||
|
||||
for (auto folder : packageFolders)
|
||||
{
|
||||
RETURN_IF_FAILED(ApplyACLsToPackageFolder(folder.c_str()));
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
#pragma once
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace MsixCoreLib
|
||||
{
|
||||
HRESULT ApplyACLs(
|
||||
_In_ std::vector<std::wstring> packageFolders);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -78,7 +78,7 @@ std::map<std::wstring, Options, CaseInsensitiveLess> CommandLineInterface::s_opt
|
|||
{
|
||||
L"-Unpack",
|
||||
Options(false, IDS_STRING_HELP_OPTION_UNPACK,
|
||||
[&](CommandLineInterface* commandLineInterface, const std::string& packagePath)
|
||||
[&](CommandLineInterface* commandLineInterface, const std::string&)
|
||||
{
|
||||
if (commandLineInterface->m_operationType != OperationType::Undefined)
|
||||
{
|
||||
|
@ -91,7 +91,7 @@ std::map<std::wstring, Options, CaseInsensitiveLess> CommandLineInterface::s_opt
|
|||
{
|
||||
{
|
||||
L"-packagePath",
|
||||
Option(true, IDS_STRING_HELP_OPTION_UNPACK_PATH,
|
||||
Option(true, IDS_STRING_HELP_OPTION_UNPACK_PACKAGEPATH,
|
||||
[&](CommandLineInterface* commandLineInterface, const std::string& packagePath)
|
||||
{
|
||||
if (commandLineInterface->m_operationType != OperationType::Unpack)
|
||||
|
@ -130,6 +130,34 @@ std::map<std::wstring, Options, CaseInsensitiveLess> CommandLineInterface::s_opt
|
|||
}
|
||||
})
|
||||
},
|
||||
{
|
||||
L"-ApplyACLs",
|
||||
Options(false, IDS_STRING_HELP_OPTION_APPLYACLS,
|
||||
[&](CommandLineInterface* commandLineInterface, const std::string&)
|
||||
{
|
||||
if (commandLineInterface->m_operationType != OperationType::Undefined)
|
||||
{
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
commandLineInterface->m_operationType = OperationType::ApplyACLs;
|
||||
return S_OK;
|
||||
},
|
||||
{
|
||||
{
|
||||
L"-packagePath",
|
||||
Option(true, IDS_STRING_HELP_OPTION_APPLYACLS_PACKAGEPATH,
|
||||
[&](CommandLineInterface* commandLineInterface, const std::string& packagePath)
|
||||
{
|
||||
if (commandLineInterface->m_operationType != OperationType::ApplyACLs)
|
||||
{
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
commandLineInterface->m_packageFilePath = utf8_to_utf16(packagePath);
|
||||
return S_OK;
|
||||
}),
|
||||
}
|
||||
})
|
||||
},
|
||||
{
|
||||
L"-?",
|
||||
Options(false, IDS_STRING_HELP_OPTION_HELP,
|
||||
|
@ -148,12 +176,23 @@ std::map<std::wstring, std::wstring> CommandLineInterface::s_optionAliases =
|
|||
|
||||
void CommandLineInterface::DisplayHelp()
|
||||
{
|
||||
std::wcout << GetStringResource(IDS_STRING_HELPTEXT) << std::endl;
|
||||
std::wcout << GetStringResource(IDS_STRING_HELPTEXT_USAGE) << std::endl;
|
||||
std::wcout << GetStringResource(IDS_STRING_HELPTEXT_DESCRIPTION) << std::endl;
|
||||
std::wcout << GetStringResource(IDS_STRING_HELPTEXT_OPTIONS) << std::endl;
|
||||
|
||||
for (const auto& option : CommandLineInterface::s_options)
|
||||
{
|
||||
std::wcout << L"\t" << std::left << std::setfill(L' ') << std::setw(5) <<
|
||||
option.first << L": " << GetStringResource(option.second.Help) << std::endl;
|
||||
|
||||
if (option.second.HasSuboptions)
|
||||
{
|
||||
for (const auto& suboption : option.second.Suboptions)
|
||||
{
|
||||
std::wcout << L"\t\t" << std::left << std::setfill(L' ') << std::setw(5) <<
|
||||
suboption.first << L": " << GetStringResource(suboption.second.Help) << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -209,8 +248,8 @@ HRESULT CommandLineInterface::Init()
|
|||
auto suboptions = option->second.Suboptions;
|
||||
while (index < m_argc)
|
||||
{
|
||||
std::wstring optionString = utf8_to_utf16(m_argv[index]);
|
||||
auto suboption = suboptions.find(optionString);
|
||||
std::wstring suboptionString = utf8_to_utf16(m_argv[index]);
|
||||
auto suboption = suboptions.find(suboptionString);
|
||||
if (suboption == suboptions.end())
|
||||
{
|
||||
TraceLoggingWrite(g_MsixUITraceLoggingProvider,
|
||||
|
@ -226,9 +265,9 @@ HRESULT CommandLineInterface::Init()
|
|||
{
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
parameter = m_argv[index];
|
||||
suboptionParameter = m_argv[index];
|
||||
}
|
||||
RETURN_IF_FAILED(option->second.DefaultCallback(this, parameter));
|
||||
RETURN_IF_FAILED(suboption->second.Callback(this, suboptionParameter));
|
||||
|
||||
++index;
|
||||
}
|
||||
|
|
|
@ -30,24 +30,6 @@ struct Option
|
|||
CallbackFunction Callback;
|
||||
};
|
||||
|
||||
struct Options
|
||||
{
|
||||
using CallbackFunction = std::function<HRESULT(CommandLineInterface* commandLineInterface, const std::string& value)>;
|
||||
|
||||
Options(bool takesParam, const UINT help, CallbackFunction defaultCallback) : Help(help), DefaultCallback(defaultCallback), TakesParameter(takesParam), HasSuboptions(false) {}
|
||||
Options(bool takesParam, const UINT help, CallbackFunction defaultCallback, std::map<std::wstring, Option> suboptions) : Help(help), DefaultCallback(defaultCallback), TakesParameter(takesParam), Suboptions(suboptions)
|
||||
{
|
||||
HasSuboptions = !Suboptions.empty();
|
||||
}
|
||||
|
||||
bool HasSuboptions;
|
||||
bool TakesParameter;
|
||||
std::wstring Name;
|
||||
UINT Help;
|
||||
CallbackFunction DefaultCallback;
|
||||
std::map<std::wstring, Option> Suboptions;
|
||||
};
|
||||
|
||||
struct CaseInsensitiveLess
|
||||
{
|
||||
struct CaseInsensitiveCompare
|
||||
|
@ -66,6 +48,24 @@ struct CaseInsensitiveLess
|
|||
}
|
||||
};
|
||||
|
||||
struct Options
|
||||
{
|
||||
using CallbackFunction = std::function<HRESULT(CommandLineInterface* commandLineInterface, const std::string& value)>;
|
||||
|
||||
Options(bool takesParam, const UINT help, CallbackFunction defaultCallback) : Help(help), DefaultCallback(defaultCallback), TakesParameter(takesParam), HasSuboptions(false) {}
|
||||
Options(bool takesParam, const UINT help, CallbackFunction defaultCallback, std::map<std::wstring, Option, CaseInsensitiveLess> suboptions) : Help(help), DefaultCallback(defaultCallback), TakesParameter(takesParam), Suboptions(suboptions)
|
||||
{
|
||||
HasSuboptions = !Suboptions.empty();
|
||||
}
|
||||
|
||||
bool HasSuboptions;
|
||||
bool TakesParameter;
|
||||
std::wstring Name;
|
||||
UINT Help;
|
||||
CallbackFunction DefaultCallback;
|
||||
std::map<std::wstring, Option, CaseInsensitiveLess> Suboptions;
|
||||
};
|
||||
|
||||
/// Parses the command line specified and creates a request.
|
||||
class CommandLineInterface
|
||||
{
|
||||
|
@ -81,8 +81,10 @@ public:
|
|||
void DisplayHelp();
|
||||
HRESULT Init();
|
||||
bool IsQuietMode() { return m_quietMode; }
|
||||
bool IsApplyACLs() { return m_applyACLs; }
|
||||
std::wstring GetPackageFilePathToInstall() { return m_packageFilePath; }
|
||||
std::wstring GetPackageFullName() { return m_packageFullName; }
|
||||
std::wstring GetUnpackDestination() { return m_unpackDestination; }
|
||||
OperationType GetOperationType() { return m_operationType; }
|
||||
private:
|
||||
int m_argc = 0;
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <CryptoProvider.hpp>
|
||||
#include <TraceLoggingProvider.h>
|
||||
#include "MsixTraceLoggingProvider.hpp"
|
||||
#include <memory>
|
||||
|
||||
namespace MsixCoreLib
|
||||
{
|
||||
|
|
|
@ -0,0 +1,170 @@
|
|||
#include "MSIXWindows.hpp"
|
||||
#include "UnpackProvider.hpp"
|
||||
#include "ApplyACLsProvider.hpp"
|
||||
|
||||
#include <TraceLoggingProvider.h>
|
||||
#include "msixmgrLogger.hpp"
|
||||
#include "..\msixmgrLib\GeneralUtil.hpp"
|
||||
#include "MSIXWindows.hpp"
|
||||
#include <shlobj_core.h>
|
||||
#include <CommCtrl.h>
|
||||
#include <map>
|
||||
#include <iostream>
|
||||
|
||||
using namespace MsixCoreLib;
|
||||
using namespace std;
|
||||
|
||||
namespace MsixCoreLib
|
||||
{
|
||||
|
||||
HRESULT UnpackPackage(
|
||||
_In_ std::wstring packageFilePath,
|
||||
_In_ std::wstring destination,
|
||||
_In_ bool isApplyACLs)
|
||||
{
|
||||
MSIX_PACKUNPACK_OPTION unpackOption = MSIX_PACKUNPACK_OPTION_UNPACKWITHFLATSTRUCTURE;
|
||||
MSIX_VALIDATION_OPTION validationOption = MSIX_VALIDATION_OPTION_FULL;
|
||||
|
||||
auto unpackDestinationUTF8 = utf16_to_utf8(destination);
|
||||
std::vector<char> unpackDestination(unpackDestinationUTF8.c_str(), unpackDestinationUTF8.c_str() + unpackDestinationUTF8.size() + 1);
|
||||
|
||||
ComPtr<IAppxFactory> factory;
|
||||
RETURN_IF_FAILED(CoCreateAppxFactoryWithHeap(MyAllocate, MyFree, validationOption, &factory));
|
||||
|
||||
ComPtr<IStream> stream;
|
||||
RETURN_IF_FAILED(CreateStreamOnFileUTF16(packageFilePath.c_str(), true, &stream));
|
||||
|
||||
ComPtr<IAppxPackageReader> reader;
|
||||
RETURN_IF_FAILED(factory->CreatePackageReader(stream.Get(), &reader));
|
||||
|
||||
RETURN_IF_FAILED(UnpackPackageFromPackageReader(
|
||||
unpackOption,
|
||||
reader.Get(),
|
||||
&unpackDestination[0]));
|
||||
|
||||
if (isApplyACLs)
|
||||
{
|
||||
std::vector<std::wstring> packageFolders;
|
||||
|
||||
Text<WCHAR> packageFullName;
|
||||
|
||||
ComPtr<IAppxManifestReader> manifestReader;
|
||||
RETURN_IF_FAILED(reader->GetManifest(&manifestReader));
|
||||
|
||||
ComPtr<IAppxManifestPackageId> packageId;
|
||||
RETURN_IF_FAILED(manifestReader->GetPackageId(&packageId));
|
||||
|
||||
RETURN_IF_FAILED(packageId->GetPackageFullName(&packageFullName));
|
||||
|
||||
std::wstring packageFolderName = destination + L"\\" + packageFullName.Get();
|
||||
packageFolders.push_back(packageFolderName);
|
||||
|
||||
RETURN_IF_FAILED(ApplyACLs(packageFolders));
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT UnpackBundle(
|
||||
_In_ std::wstring packageFilePath,
|
||||
_In_ std::wstring destination,
|
||||
_In_ bool isApplyACLs)
|
||||
{
|
||||
MSIX_APPLICABILITY_OPTIONS applicabilityOption = static_cast<MSIX_APPLICABILITY_OPTIONS>(MSIX_APPLICABILITY_NONE);
|
||||
MSIX_PACKUNPACK_OPTION unpackOption = MSIX_PACKUNPACK_OPTION_UNPACKWITHFLATSTRUCTURE;
|
||||
MSIX_VALIDATION_OPTION validationOption = MSIX_VALIDATION_OPTION_FULL;
|
||||
|
||||
auto unpackDestinationUTF8 = utf16_to_utf8(destination);
|
||||
std::vector<char> unpackDestination(unpackDestinationUTF8.c_str(), unpackDestinationUTF8.c_str() + unpackDestinationUTF8.size() + 1);
|
||||
|
||||
ComPtr<IAppxBundleFactory> factory;
|
||||
RETURN_IF_FAILED(CoCreateAppxBundleFactoryWithHeap(MyAllocate, MyFree, validationOption, applicabilityOption, &factory));
|
||||
|
||||
ComPtr<IStream> stream;
|
||||
RETURN_IF_FAILED(CreateStreamOnFileUTF16(packageFilePath.c_str(), true, &stream));
|
||||
|
||||
ComPtr<IAppxBundleReader> reader;
|
||||
RETURN_IF_FAILED(factory->CreateBundleReader(stream.Get(), &reader));
|
||||
|
||||
RETURN_IF_FAILED(UnpackBundleFromBundleReader(
|
||||
unpackOption,
|
||||
reader.Get(),
|
||||
&unpackDestination[0]));
|
||||
|
||||
std::vector<std::wstring> packageFolders;
|
||||
|
||||
if (isApplyACLs)
|
||||
{
|
||||
ComPtr<IAppxBundleManifestReader> bundleManifestReader;
|
||||
RETURN_IF_FAILED(reader->GetManifest(&bundleManifestReader));
|
||||
|
||||
// Determine the name of the folder created for the unpacked bundle, and add this name to our list of package folders
|
||||
Text<WCHAR> bundleFullName;
|
||||
ComPtr<IAppxManifestPackageId> bundleId;
|
||||
RETURN_IF_FAILED(bundleManifestReader->GetPackageId(&bundleId));
|
||||
RETURN_IF_FAILED(bundleId->GetPackageFullName(&bundleFullName));
|
||||
|
||||
std::wstring bundleFolderName = destination + L"\\" + bundleFullName.Get();
|
||||
packageFolders.push_back(bundleFolderName);
|
||||
|
||||
// Now we must determine the names of the folders created for each package in the bundle
|
||||
// To do so, we can use the bundle reader's GetPayloadPackages API, which will tell us the names of all the package FILES
|
||||
// that were unpacked. While we could then create a stream for each of these files to determine their package full names, this could
|
||||
// be potentially costly. Instead, we will use the bundle manifest reader's GetPackageInfoItems API, which will give us easy access
|
||||
// to <package file name, package full name> pairs for ALL packages in the bundle (not necessarily the ones that were unpacked). Once
|
||||
// we have built up a map of these file name -> full name pairs, we can quickly determine the package full name for each file name
|
||||
// returned by GetPayloadPackages. Append the package full name to the destination to get the folder name for the unpacked package, then
|
||||
// add this folder name to our list of package folder names.
|
||||
std::map <std::wstring, std::wstring> packagesMap;
|
||||
|
||||
ComPtr<IAppxBundleManifestPackageInfoEnumerator> packages;
|
||||
ComPtr<IAppxBundleManifestPackageInfo> currentPackage;
|
||||
RETURN_IF_FAILED(bundleManifestReader->GetPackageInfoItems(&packages));
|
||||
BOOL hasCurrent = FALSE;
|
||||
// Populate the packagesMap with package file name, package full name pairs
|
||||
for (packages->GetHasCurrent(&hasCurrent); hasCurrent; packages->MoveNext(&hasCurrent))
|
||||
{
|
||||
RETURN_IF_FAILED(packages->GetCurrent(¤tPackage));
|
||||
ComPtr<IAppxManifestPackageId> manifestPackageID;
|
||||
RETURN_IF_FAILED(currentPackage->GetPackageId(&manifestPackageID));
|
||||
|
||||
Text<WCHAR> packageFullName;
|
||||
RETURN_IF_FAILED(manifestPackageID->GetPackageFullName(&packageFullName));
|
||||
|
||||
Text<WCHAR> packageFileName;
|
||||
RETURN_IF_FAILED(currentPackage->GetFileName(&packageFileName));
|
||||
|
||||
std::wstring stdPackageFileName = packageFileName.Get();
|
||||
std::wstring stdPackageFullName = packageFullName.Get();
|
||||
|
||||
packagesMap[stdPackageFileName] = stdPackageFullName;
|
||||
}
|
||||
|
||||
// Use GetPayloadPackages to enumerate over the package files that actually got unpacked
|
||||
// after applicability logic was applied.
|
||||
hasCurrent = false;
|
||||
ComPtr<IAppxFilesEnumerator> packageFilesEnumerator;
|
||||
RETURN_IF_FAILED(reader->GetPayloadPackages(&packageFilesEnumerator));
|
||||
RETURN_IF_FAILED(packageFilesEnumerator->GetHasCurrent(&hasCurrent));
|
||||
while (hasCurrent)
|
||||
{
|
||||
ComPtr<IAppxFile> file;
|
||||
RETURN_IF_FAILED(packageFilesEnumerator->GetCurrent(&file));
|
||||
|
||||
Text<WCHAR> packageFileName;
|
||||
RETURN_IF_FAILED(file->GetName(&packageFileName));
|
||||
|
||||
std::wstring stdPackageFileName = packageFileName.Get();
|
||||
auto packageFullName = packagesMap[packageFileName.Get()];
|
||||
std::wstring packageFolderName = destination + L"\\" + packageFullName;
|
||||
packageFolders.push_back(packageFolderName);
|
||||
|
||||
RETURN_IF_FAILED(packageFilesEnumerator->MoveNext(&hasCurrent));
|
||||
}
|
||||
|
||||
RETURN_IF_FAILED(ApplyACLs(packageFolders));
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
#pragma once
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace MsixCoreLib
|
||||
{
|
||||
HRESULT UnpackPackage(
|
||||
_In_ std::wstring packageFilePath,
|
||||
_In_ std::wstring destination,
|
||||
_In_ bool isApplyACLs);
|
||||
|
||||
HRESULT UnpackBundle(
|
||||
_In_ std::wstring packageFilePath,
|
||||
_In_ std::wstring destination,
|
||||
_In_ bool isApplyACLs);
|
||||
}
|
|
@ -16,6 +16,9 @@
|
|||
#include "..\msixmgrLib\GeneralUtil.hpp"
|
||||
#include "resource.h"
|
||||
#include <VersionHelpers.h>
|
||||
#include "UnpackProvider.hpp"
|
||||
#include "ApplyACLsProvider.hpp"
|
||||
#include "MsixErrors.hpp"
|
||||
|
||||
#include <msixmgrActions.hpp>
|
||||
using namespace std;
|
||||
|
@ -137,6 +140,49 @@ int main(int argc, char * argv[])
|
|||
std::cout << numPackages << " Package(s) found" << std::endl;
|
||||
return S_OK;
|
||||
}
|
||||
case OperationType::Unpack:
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
auto packageFilePath = cli.GetPackageFilePathToInstall();
|
||||
auto unpackDestination = cli.GetUnpackDestination();
|
||||
|
||||
if (IsPackageFile(packageFilePath))
|
||||
{
|
||||
hr = MsixCoreLib::UnpackPackage(packageFilePath, unpackDestination, cli.IsApplyACLs());
|
||||
}
|
||||
else if (IsBundleFile(packageFilePath))
|
||||
{
|
||||
hr = MsixCoreLib::UnpackBundle(packageFilePath, unpackDestination, cli.IsApplyACLs());
|
||||
}
|
||||
else
|
||||
{
|
||||
std::wcout << std::endl;
|
||||
std::wcout << "Invalid package path: " << packageFilePath << std::endl;
|
||||
std::wcout << "Please confirm the given package path is an .appx, .appxbundle, .msix, or .msixbundle file" << std::endl;
|
||||
std::wcout << std::endl;
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
if (FAILED(hr))
|
||||
{
|
||||
std::wcout << std::endl;
|
||||
std::wcout << L"Failed with HRESULT 0x" << std::hex << hr << L" when trying to unpack " << packageFilePath << std::endl;
|
||||
if (hr == static_cast<HRESULT>(MSIX::Error::CertNotTrusted))
|
||||
{
|
||||
std::wcout << L"Please confirm that the certificate has been installed for this package" << std::endl;
|
||||
}
|
||||
std::wcout << std::endl;
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
case OperationType::ApplyACLs:
|
||||
{
|
||||
std::vector<std::wstring> packageFolders;
|
||||
packageFolders.push_back(cli.GetPackageFilePathToInstall()); // we're not actually installing anything. The API just returns the file path name we need.
|
||||
RETURN_IF_FAILED(MsixCoreLib::ApplyACLs(packageFolders));
|
||||
return S_OK;
|
||||
}
|
||||
default:
|
||||
return E_NOT_SET;
|
||||
}
|
||||
|
|
|
@ -75,7 +75,7 @@ BEGIN
|
|||
IDS_STRING_HELP_OPTION_FINDPACKAGE
|
||||
"Find package with the specific package full name."
|
||||
IDS_STRING_HELP_OPTION_HELP "Display this help text."
|
||||
IDS_STRING_HELPTEXT " Usage:\n ------\n \tmsixmgr.exe [options]\n\n Description:\n-----------\n \tInstalls an msix package, removes an msix package or queries for msix packages\n\n Options:\n -------\n"
|
||||
IDS_STRING_HELPTEXT_USAGE " Usage:\n ------\n \tmsixmgr.exe [options]\n \tmsixmgr.exe -Unpack -packagePath <path to package> -destination <output folder> [-applyacls]\n \tmsixmgr.exe -ApplyACLs -packagePath <package folder path>\n\n"
|
||||
IDS_STRING_UI_CANCEL "Cancel"
|
||||
END
|
||||
|
||||
|
@ -96,11 +96,16 @@ BEGIN
|
|||
IDS_STRING_REINSTALLAPP "Reinstall"
|
||||
IDS_RUNFULLTRUST_CAPABILITY "Uses all system resources"
|
||||
IDS_STRING_ERROR_MSG "App installation failed with error message: An internal error occurred with error 0x"
|
||||
IDS_STRING_HELP_OPTION_UNPACK "Unpack package - Extract package contents to folder"
|
||||
IDS_STRING_HELP_OPTION_UNPACK_PATH "Path to package to unpack"
|
||||
IDS_STRING_HELP_OPTION_UNPACK_DESTINATION "Path to location where package should be unpacked to"
|
||||
IDS_STRING_HELP_OPTION_UNPACK_APPLYACLS "Apply ACLs - apply ACLs to the folder created by unpack"
|
||||
END
|
||||
IDS_STRING_HELPTEXT_DESCRIPTION " Description:\n -----------\n \tInstalls an msix package, removes an msix package or queries for msix packages.\n \tMay also unpack msix packages and apply ACLs to the resulting package folders\n\n"
|
||||
IDS_STRING_HELPTEXT_OPTIONS " Options:\n -------\n"
|
||||
IDS_STRING_HELP_OPTION_UNPACK "Unpack a package (.appx, .msix, .appxbundle, .msixbundle) and extract its contents to a folder."
|
||||
IDS_STRING_HELP_OPTION_UNPACK_PACKAGEPATH "the path to the package to unpack"
|
||||
IDS_STRING_HELP_OPTION_UNPACK_DESTINATION "the directory to place the resulting package folder(s) in"
|
||||
IDS_STRING_HELP_OPTION_UNPACK_APPLYACLS "optional parameter that applies ACLs to the resulting package folder(s) and their parent folder"
|
||||
IDS_STRING_HELP_OPTION_APPLYACLS "Applies ACLs to a package folder (an unpacked package)"
|
||||
IDS_STRING_HELP_OPTION_APPLYACLS_PACKAGEPATH "the path to the folder to apply acls to"
|
||||
|
||||
END
|
||||
|
||||
#endif // English (United States) resources
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -200,15 +200,19 @@
|
|||
</Manifest>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="ApplyACLsProvider.hpp" />
|
||||
<ClInclude Include="CommandLineInterface.hpp" />
|
||||
<ClInclude Include="InstallUI.hpp" />
|
||||
<ClInclude Include="resource.h" />
|
||||
<ClInclude Include="UnpackProvider.hpp" />
|
||||
<ClInclude Include="Util.hpp" />
|
||||
<ClInclude Include="msixmgrLogger.hpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="ApplyACLsProvider.cpp" />
|
||||
<ClCompile Include="InstallUI.cpp" />
|
||||
<ClCompile Include="CommandLineInterface.cpp" />
|
||||
<ClCompile Include="UnpackProvider.cpp" />
|
||||
<ClCompile Include="Util.cpp" />
|
||||
<ClCompile Include="msixmgr.cpp" />
|
||||
<ClCompile Include="msixmgrLogger.cpp" />
|
||||
|
|
|
@ -30,6 +30,12 @@
|
|||
<ClInclude Include="Util.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="UnpackProvider.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ApplyACLsProvider.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="InstallUI.cpp">
|
||||
|
@ -47,6 +53,12 @@
|
|||
<ClCompile Include="Util.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ApplyACLsProvider.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="UnpackProvider.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="msixmgr.rc">
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
#define IDS_STRING_HELP_OPTION_FINDALLPACKAGES 107
|
||||
#define IDS_STRING_HELP_OPTION_FINDPACKAGE 108
|
||||
#define IDS_STRING_HELP_OPTION_HELP 109
|
||||
#define IDS_STRING_HELPTEXT 110
|
||||
#define IDS_STRING_HELPTEXT_USAGE 110
|
||||
#define IDS_STRING_UI_CANCEL 111
|
||||
#define IDS_STRING_UI_CLOSE 112
|
||||
#define IDS_STRING_UI_TITLE 113
|
||||
|
@ -28,10 +28,14 @@
|
|||
#define IDS_STRING_REINSTALLAPP 122
|
||||
#define IDS_RUNFULLTRUST_CAPABILITY 123
|
||||
#define IDS_STRING_ERROR_MSG 124
|
||||
#define IDS_STRING_HELP_OPTION_UNPACK 125
|
||||
#define IDS_STRING_HELP_OPTION_UNPACK_PATH 126
|
||||
#define IDS_STRING_HELP_OPTION_UNPACK_DESTINATION 127
|
||||
#define IDS_STRING_HELP_OPTION_UNPACK_APPLYACLS 128
|
||||
#define IDS_STRING_HELPTEXT_DESCRIPTION 125
|
||||
#define IDS_STRING_HELPTEXT_OPTIONS 126
|
||||
#define IDS_STRING_HELP_OPTION_UNPACK 127
|
||||
#define IDS_STRING_HELP_OPTION_UNPACK_PACKAGEPATH 128
|
||||
#define IDS_STRING_HELP_OPTION_UNPACK_DESTINATION 129
|
||||
#define IDS_STRING_HELP_OPTION_UNPACK_APPLYACLS 130
|
||||
#define IDS_STRING_HELP_OPTION_APPLYACLS 131
|
||||
#define IDS_STRING_HELP_OPTION_APPLYACLS_PACKAGEPATH 132
|
||||
|
||||
// Next default values for new objects
|
||||
//
|
||||
|
|
|
@ -196,6 +196,24 @@ namespace MsixCoreLib
|
|||
return VerifyVersionInfo(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_BUILDNUMBER, dwlConditionMask);
|
||||
}
|
||||
|
||||
bool EndsWith(std::wstring const &fullString, std::wstring const &ending) {
|
||||
if (fullString.length() >= ending.length()) {
|
||||
return (0 == fullString.compare(fullString.length() - ending.length(), ending.length(), ending));
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool IsPackageFile(std::wstring const& path)
|
||||
{
|
||||
return EndsWith(path, L".appx") || EndsWith(path, L".msix");
|
||||
}
|
||||
|
||||
bool IsBundleFile(std::wstring const& path)
|
||||
{
|
||||
return EndsWith(path, L".appxbundle") || EndsWith(path, L".msixbundle");
|
||||
}
|
||||
|
||||
/// Replaces all oldchars in input with newchar
|
||||
///
|
||||
|
|
|
@ -55,7 +55,7 @@ namespace MsixCoreLib
|
|||
|
||||
/// Returns the current user sid as a string.
|
||||
///
|
||||
/// @param userSidString - current user sid stirng
|
||||
/// @param userSidString - current user sid string
|
||||
HRESULT GetCurrentUserSidString(std::wstring & userSidString);
|
||||
|
||||
/// Determines if the currently running OS is Windows 10 RS3 or later.
|
||||
|
@ -63,6 +63,23 @@ namespace MsixCoreLib
|
|||
/// https://docs.microsoft.com/en-us/windows/desktop/SysInfo/targeting-your-application-at-windows-8-1
|
||||
BOOL IsWindows10RS3OrLater();
|
||||
|
||||
/// Determines whether a string ends with a given substring
|
||||
///
|
||||
/// @return true if "fullString" ends with the specified "ending"; otherwise false
|
||||
bool EndsWith(std::wstring const &fullString, std::wstring const &ending);
|
||||
|
||||
/// Determines whether a given file path corresponds to a package file (.appx, .msix)
|
||||
///
|
||||
/// @path -path for some file
|
||||
/// @return true if "path" is a package file path ending with ".appx" or "msix"; otherwise false
|
||||
bool IsPackageFile(std::wstring const& path);
|
||||
|
||||
/// Determines whether a given file path corresponds to a bundle file (.appxbundle, .msixbundle)
|
||||
///
|
||||
/// @path -path for some file
|
||||
/// @return true if "path" is a bundle file path ending with ".appxbundle" or "msixbundle"; otherwise false
|
||||
bool IsBundleFile(std::wstring const& path);
|
||||
|
||||
/// Replaces all oldchars in input with newchar
|
||||
///
|
||||
/// @param input - The input string that contains the characters to be changed
|
||||
|
|
|
@ -425,20 +425,18 @@ namespace MSIX {
|
|||
auto name = dependencyNode->GetAttributeValue(XmlAttributeName::Name);
|
||||
auto publisher = dependencyNode->GetAttributeValue(XmlAttributeName::Publisher);
|
||||
std::string packageFamilyName;
|
||||
std::string publisherHash;
|
||||
|
||||
// if no publisher for the main package dependency is specified, we default to the publisher of the optional package itself
|
||||
if (publisher.empty())
|
||||
{
|
||||
auto packageIdInternal = context->self->m_packageId.As<IAppxManifestPackageIdInternal>();
|
||||
packageFamilyName = packageIdInternal->GetPackageFamilyName();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Convert publisher to publisher hash so we can build package family name
|
||||
std::string publisherHash = ComputePublisherId(publisher);
|
||||
packageFamilyName = name + "_" + publisherHash;
|
||||
publisher = packageIdInternal->GetPublisher();
|
||||
}
|
||||
|
||||
publisherHash = ComputePublisherId(publisher);
|
||||
packageFamilyName = name + "_" + publisherHash;
|
||||
|
||||
auto dependency = ComPtr<IAppxManifestMainPackageDependency>::Make<AppxManifestMainPackageDependency>(context->self->m_factory.Get(), name, publisher, packageFamilyName);
|
||||
context->packageDependencies->push_back(std::move(dependency));
|
||||
return true;
|
||||
|
|
|
@ -623,13 +623,13 @@ TEST_CASE("Api_AppxManifestReader_OptionalPackage", "[api]")
|
|||
|
||||
std::vector<std::string> expectedPublisherValues =
|
||||
{
|
||||
"", // We expect the value to be NULL. Handle this special case separately
|
||||
"CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US",
|
||||
"CN=Microsoft Corporation2, O=Microsoft Corporation2, L=Redmond2, S=Washington, C=US2",
|
||||
};
|
||||
|
||||
std::vector<std::string> expectedPackageFamilyNameValues =
|
||||
{
|
||||
"SampleAppManifest_8wekyb3d8bbwe",
|
||||
"Demo.MyMainApp_8wekyb3d8bbwe",
|
||||
"Demo.MyMainApp_4395hyxtvknyy",
|
||||
};
|
||||
|
||||
|
@ -653,26 +653,13 @@ TEST_CASE("Api_AppxManifestReader_OptionalPackage", "[api]")
|
|||
REQUIRE_SUCCEEDED(dependencyUtf8->GetName(&nameUtf8));
|
||||
REQUIRE(expectedName == nameUtf8.ToString());
|
||||
|
||||
if (numDep == 0)
|
||||
{
|
||||
MsixTest::Wrappers::Buffer<wchar_t> publisher;
|
||||
REQUIRE_SUCCEEDED(dependency->GetPublisher(&publisher));
|
||||
REQUIRE(publisher.Get() == NULL);
|
||||
MsixTest::Wrappers::Buffer<wchar_t> publisher;
|
||||
REQUIRE_SUCCEEDED(dependency->GetPublisher(&publisher));
|
||||
REQUIRE(expectedPublisherValues[numDep] == publisher.ToString());
|
||||
|
||||
MsixTest::Wrappers::Buffer<char> publisherUtf8;
|
||||
REQUIRE_SUCCEEDED(dependencyUtf8->GetPublisher(&publisherUtf8));
|
||||
REQUIRE(publisherUtf8.Get() == NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
MsixTest::Wrappers::Buffer<wchar_t> publisher;
|
||||
REQUIRE_SUCCEEDED(dependency->GetPublisher(&publisher));
|
||||
REQUIRE(expectedPublisherValues[numDep] == publisher.ToString());
|
||||
|
||||
MsixTest::Wrappers::Buffer<char> publisherUtf8;
|
||||
REQUIRE_SUCCEEDED(dependencyUtf8->GetPublisher(&publisherUtf8));
|
||||
REQUIRE(expectedPublisherValues[numDep] == publisherUtf8.ToString());
|
||||
}
|
||||
MsixTest::Wrappers::Buffer<char> publisherUtf8;
|
||||
REQUIRE_SUCCEEDED(dependencyUtf8->GetPublisher(&publisherUtf8));
|
||||
REQUIRE(expectedPublisherValues[numDep] == publisherUtf8.ToString());
|
||||
|
||||
MsixTest::Wrappers::Buffer<wchar_t> packageFamilyName;
|
||||
REQUIRE_SUCCEEDED(dependency->GetPackageFamilyName(&packageFamilyName));
|
||||
|
|
Загрузка…
Ссылка в новой задаче