677 строки
29 KiB
C++
677 строки
29 KiB
C++
// msixmgr.cpp :
|
|
// The main entry point for msixmgr.exe. This application manages various facets of msix packages, including
|
|
// a working preview for the MSIX/APPX installer for Windows 7 SP1 and higher OS versions
|
|
#include "MSIXWindows.hpp"
|
|
#include <shlobj_core.h>
|
|
#include <CommCtrl.h>
|
|
|
|
#include <string>
|
|
#include <sstream>
|
|
#include <iostream>
|
|
#include <vector>
|
|
#include <TraceLoggingProvider.h>
|
|
#include "InstallUI.hpp"
|
|
#include "CommandLineInterface.hpp"
|
|
#include "msixmgrLogger.hpp"
|
|
#include "Util.hpp"
|
|
#include "..\msixmgrLib\GeneralUtil.hpp"
|
|
#include "resource.h"
|
|
#include <VersionHelpers.h>
|
|
#include "UnpackProvider.hpp"
|
|
#include "ApplyACLsProvider.hpp"
|
|
#include "VHDProvider.hpp"
|
|
#include "CIMProvider.hpp"
|
|
#include "MsixErrors.hpp"
|
|
#include <filesystem>
|
|
|
|
#include <msixmgrActions.hpp>
|
|
using namespace std;
|
|
using namespace MsixCoreLib;
|
|
|
|
TRACELOGGING_DECLARE_PROVIDER(g_MsixTraceLoggingProvider);
|
|
|
|
HRESULT LogFileInfo()
|
|
{
|
|
WCHAR filePath[MAX_PATH];
|
|
DWORD lengthCopied = GetModuleFileNameW(nullptr, filePath, MAX_PATH);
|
|
if (lengthCopied == 0)
|
|
{
|
|
RETURN_IF_FAILED(HRESULT_FROM_WIN32(GetLastError()));
|
|
}
|
|
|
|
UINT64 version = 0;
|
|
bool isUnversioned = false;
|
|
RETURN_IF_FAILED(GetFileVersion(filePath, version, isUnversioned));
|
|
|
|
std::wstring versionString =
|
|
std::to_wstring((version & 0xFFFF000000000000) >> 48) + L"." +
|
|
std::to_wstring((version & 0x0000FFFF00000000) >> 32) + L"." +
|
|
std::to_wstring((version & 0x00000000FFFF0000) >> 16) + L"." +
|
|
std::to_wstring(version & 0x000000000000FFFF);
|
|
TraceLoggingWrite(g_MsixUITraceLoggingProvider,
|
|
"msixmgr.exe file version",
|
|
TraceLoggingValue(versionString.c_str(), "Version"));
|
|
return S_OK;
|
|
}
|
|
|
|
bool IsAdmin()
|
|
{
|
|
HANDLE token = nullptr;
|
|
if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token))
|
|
{
|
|
TOKEN_ELEVATION_TYPE type;
|
|
DWORD length = sizeof(TOKEN_ELEVATION_TYPE);
|
|
if (GetTokenInformation(token, TokenElevationType, &type, sizeof(TOKEN_ELEVATION_TYPE), &length))
|
|
{
|
|
PWSTR elevationTypeString = nullptr;
|
|
switch (type)
|
|
{
|
|
case TokenElevationTypeDefault:
|
|
elevationTypeString = L"DefaultType";
|
|
break;
|
|
case TokenElevationTypeFull:
|
|
elevationTypeString = L"Full";
|
|
break;
|
|
case TokenElevationTypeLimited:
|
|
elevationTypeString = L"Limited";
|
|
break;
|
|
default:
|
|
elevationTypeString = L"unknown";
|
|
break;
|
|
}
|
|
|
|
BOOL isUserAdmin = IsUserAnAdmin();
|
|
TraceLoggingWrite(g_MsixUITraceLoggingProvider, "ElevationType",
|
|
TraceLoggingValue(elevationTypeString, "ElevationTypeString"),
|
|
TraceLoggingValue((DWORD)type, "elevationTypeEnum"),
|
|
TraceLoggingValue(isUserAdmin, "isUserAdmin"));
|
|
if (type == TokenElevationTypeFull || (type == TokenElevationTypeDefault && isUserAdmin))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void RelaunchAsAdmin(int argc, char * argv[])
|
|
{
|
|
// free the console window so it's not showing when UAC prompt shows.
|
|
FreeConsole();
|
|
|
|
SHELLEXECUTEINFOW shellExecuteInfo = {};
|
|
shellExecuteInfo.cbSize = sizeof(SHELLEXECUTEINFOW);
|
|
shellExecuteInfo.fMask = SEE_MASK_DEFAULT;
|
|
shellExecuteInfo.lpVerb = L"runas";
|
|
shellExecuteInfo.lpFile = L"msixmgr.exe";
|
|
shellExecuteInfo.nShow = SW_SHOW;
|
|
|
|
std::wstringstream argumentstream;
|
|
for (int i = 1; i < argc; i++)
|
|
{
|
|
argumentstream << argv[i] << L" ";
|
|
}
|
|
std::wstring args = argumentstream.str();
|
|
|
|
shellExecuteInfo.lpParameters = args.c_str();
|
|
|
|
TraceLoggingWrite(g_MsixUITraceLoggingProvider, "Relaunching as admin",
|
|
TraceLoggingValue(args.c_str(), "args"));
|
|
|
|
ShellExecuteExW(&shellExecuteInfo);
|
|
}
|
|
|
|
void OutputUnpackFailures(
|
|
_In_ std::wstring packageSource,
|
|
_In_ std::vector<std::wstring> skippedFiles,
|
|
_In_ std::vector<std::wstring> failedPackages,
|
|
_In_ std::vector<HRESULT> failedPackagesErrors)
|
|
{
|
|
if (!skippedFiles.empty())
|
|
{
|
|
std::wcout << std::endl;
|
|
std::wcout << "[WARNING] The following items from " << packageSource << " were ignored because they are not packages or bundles " << std::endl;
|
|
std::wcout << std::endl;
|
|
|
|
for (int i = 0; i < skippedFiles.size(); i++)
|
|
{
|
|
std::wcout << skippedFiles.at(i) << std::endl;
|
|
}
|
|
|
|
std::wcout << std::endl;
|
|
}
|
|
|
|
if (!failedPackages.empty())
|
|
{
|
|
std::wcout << std::endl;
|
|
std::wcout << "[WARNING] The following packages from " << packageSource << " failed to get unpacked. Please try again: " << std::endl;
|
|
std::wcout << std::endl;
|
|
|
|
for (int i = 0; i < failedPackages.size(); i++)
|
|
{
|
|
HRESULT hr = failedPackagesErrors.at(i);
|
|
|
|
std::wcout << L"Failed with HRESULT 0x" << std::hex << hr << L" when trying to unpack " << failedPackages.at(i) << 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;
|
|
}
|
|
else if (hr == static_cast<HRESULT>(MSIX::Error::FileWrite))
|
|
{
|
|
std::wcout << L"The tool encountered a file write error. If you are unpacking to a VHD, please try again with a larger VHD, as file write errors may be caused by insufficient disk space." << std::endl;
|
|
}
|
|
else if (hr == E_INVALIDARG)
|
|
{
|
|
std::wcout << "Please confirm the given package path is an .appx, .appxbundle, .msix, or .msixbundle file" << std::endl;
|
|
}
|
|
|
|
std::wcout << std::endl;
|
|
}
|
|
}
|
|
}
|
|
|
|
int main(int argc, char * argv[])
|
|
{
|
|
// Register the providers
|
|
TraceLoggingRegister(g_MsixUITraceLoggingProvider);
|
|
TraceLoggingRegister(g_MsixTraceLoggingProvider);
|
|
|
|
// Determine if running as admin up front to log the result in all codepaths
|
|
bool isAdmin = IsAdmin();
|
|
|
|
HRESULT hrCoInitialize = CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
|
if (FAILED(hrCoInitialize))
|
|
{
|
|
std::wcout << GetStringResource(IDS_STRING_FAILED_COM_INITIALIZATION) << " " << std::hex << hrCoInitialize << std::endl;
|
|
return 1;
|
|
}
|
|
|
|
(void)(LogFileInfo());
|
|
|
|
CommandLineInterface cli(argc, argv);
|
|
|
|
const HRESULT hrCreateRequest = cli.Init();
|
|
if (SUCCEEDED(hrCreateRequest))
|
|
{
|
|
switch (cli.GetOperationType())
|
|
{
|
|
case OperationType::Add:
|
|
{
|
|
AutoPtr<IPackageManager> packageManager;
|
|
RETURN_IF_FAILED(MsixCoreLib_CreatePackageManager(&packageManager));
|
|
|
|
if (cli.IsQuietMode())
|
|
{
|
|
if (!isAdmin)
|
|
{
|
|
RelaunchAsAdmin(argc, argv);
|
|
return 0;
|
|
}
|
|
HRESULT hrAddPackage = packageManager->AddPackage(cli.GetPackageFilePathToInstall(), DeploymentOptions::None);
|
|
if (FAILED(hrAddPackage))
|
|
{
|
|
std::wcout << GetStringResource(IDS_STRING_FAILED_REQUEST) << " " << std::hex << hrAddPackage << std::endl;
|
|
return hrAddPackage;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (IsWindows10RS3OrLater())
|
|
{
|
|
TraceLoggingWrite(g_MsixUITraceLoggingProvider, "Windows10RS3 or later: redirecting to appInstaller");
|
|
|
|
std::wstring protocol = std::wstring(L"ms-appinstaller:?source=");
|
|
|
|
std::wstring httpPrefix(L"http");
|
|
bool isPathHttp = cli.GetPackageFilePathToInstall().compare(0, httpPrefix.length(), httpPrefix) == 0;
|
|
if (isPathHttp)
|
|
{
|
|
protocol.append(cli.GetPackageFilePathToInstall());
|
|
}
|
|
else
|
|
{
|
|
const int bufSize = 1024;
|
|
wchar_t path[bufSize];
|
|
if (!GetFullPathNameW(cli.GetPackageFilePathToInstall().c_str(), bufSize, path, nullptr))
|
|
{
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
protocol.append(path);
|
|
}
|
|
ShellExecuteW(nullptr, L"Open", protocol.c_str(), nullptr, nullptr, SW_SHOWNORMAL);
|
|
}
|
|
else
|
|
{
|
|
if (!isAdmin)
|
|
{
|
|
RelaunchAsAdmin(argc, argv);
|
|
return 0;
|
|
}
|
|
auto ui = new UI(packageManager, cli.GetPackageFilePathToInstall(), UIType::InstallUIAdd);
|
|
ui->ShowUI();
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case OperationType::Remove:
|
|
{
|
|
if (!isAdmin)
|
|
{
|
|
RelaunchAsAdmin(argc, argv);
|
|
return 0;
|
|
}
|
|
FreeConsole();
|
|
AutoPtr<IPackageManager> packageManager;
|
|
RETURN_IF_FAILED(MsixCoreLib_CreatePackageManager(&packageManager));
|
|
|
|
auto packageFullName = cli.GetPackageFullName();
|
|
HRESULT hrRemovePackage = packageManager->RemovePackage(packageFullName);
|
|
if (FAILED(hrRemovePackage))
|
|
{
|
|
std::wcout << GetStringResource(IDS_STRING_FAILED_REQUEST) << " " << std::hex << hrRemovePackage << std::endl;
|
|
return hrRemovePackage;
|
|
}
|
|
break;
|
|
}
|
|
case OperationType::FindPackage:
|
|
{
|
|
AutoPtr<IPackageManager> packageManager;
|
|
RETURN_IF_FAILED(MsixCoreLib_CreatePackageManager(&packageManager));
|
|
|
|
std::unique_ptr<std::vector<std::shared_ptr<IInstalledPackage>>> packages;
|
|
RETURN_IF_FAILED(packageManager->FindPackages(cli.GetPackageFullName(), packages));
|
|
|
|
if (packages != nullptr)
|
|
{
|
|
unsigned int numPackages = 0;
|
|
for (auto& package : *packages)
|
|
{
|
|
std::wcout << std::endl;
|
|
std::wcout << L"PackageFullName: " << package->GetPackageFullName().c_str() << std::endl;
|
|
std::wcout << L"DisplayName: " << package->GetDisplayName().c_str() << std::endl;
|
|
|
|
std::wcout << L"DirectoryPath: " << package->GetInstalledLocation().c_str() << std::endl;
|
|
std::wcout << std::endl;
|
|
numPackages++;
|
|
}
|
|
|
|
std::cout << numPackages << " Package(s) found" << std::endl;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
case OperationType::Unpack:
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
auto packageSourcePath = cli.GetPackageFilePathToInstall();
|
|
auto unpackDestination = cli.GetUnpackDestination();
|
|
auto rootDirectory = cli.GetRootDirectory();
|
|
WVDFileType fileType = cli.GetFileType();
|
|
bool createFile = cli.IsCreate();
|
|
|
|
std::vector<std::wstring> skippedFiles;
|
|
std::vector<std::wstring> failedPackages;
|
|
std::vector<HRESULT> failedPackagesErrors;
|
|
|
|
if (fileType == WVDFileType::CIM)
|
|
{
|
|
if (rootDirectory.empty() || fileType == WVDFileType::NotSpecified)
|
|
{
|
|
std::wcout << std::endl;
|
|
std::wcout << "Creating a file with the -create option requires both a -rootDirectory and -fileType" << std::endl;
|
|
std::wcout << std::endl;
|
|
return E_INVALIDARG;
|
|
}
|
|
if (!EndsWith(unpackDestination, L".cim"))
|
|
{
|
|
std::wcout << std::endl;
|
|
std::wcout << "Invalid CIM file name. File name must have .cim file extension" << std::endl;
|
|
std::wcout << std::endl;
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
// Create a temporary directory to unpack package(s) since we cannot unpack to the CIM directly.
|
|
// Append long path prefix to temporary directory path to handle paths that exceed the maximum path length limit
|
|
std::wstring currentDirectory = std::filesystem::current_path();
|
|
std::wstring uniqueIdString;
|
|
RETURN_IF_FAILED(CreateGUIDString(&uniqueIdString));
|
|
std::wstring tempDirPathString = L"\\\\?\\" + currentDirectory + L"\\" + uniqueIdString;
|
|
std::filesystem::path tempDirPath(tempDirPathString);
|
|
|
|
std::error_code createDirectoryErrorCode;
|
|
bool createTempDirResult = std::filesystem::create_directory(tempDirPath, createDirectoryErrorCode);
|
|
|
|
// Since we're using a GUID, this should almost never happen
|
|
if (!createTempDirResult)
|
|
{
|
|
std::wcout << std::endl;
|
|
std::wcout << "Failed to create temp directory " << tempDirPathString << std::endl;
|
|
std::wcout << "This may occur when the directory path already exists. Please try again." << std::endl;
|
|
std::wcout << std::endl;
|
|
return E_UNEXPECTED;
|
|
}
|
|
if (createDirectoryErrorCode.value() != 0)
|
|
{
|
|
// Again, we expect that the creation of the temp directory will fail very rarely. Output the exception
|
|
// and have the user try again.
|
|
std::wcout << std::endl;
|
|
std::wcout << "Creation of temp directory " << tempDirPathString << " failed with error: " << createDirectoryErrorCode.value() << std::endl;
|
|
std::cout << "Error message: " << createDirectoryErrorCode.message() << std::endl;
|
|
std::wcout << "Please try again." << std::endl;
|
|
std::wcout << std::endl;
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
RETURN_IF_FAILED(MsixCoreLib::Unpack(
|
|
packageSourcePath,
|
|
tempDirPathString,
|
|
cli.IsApplyACLs(),
|
|
cli.IsValidateSignature(),
|
|
skippedFiles,
|
|
failedPackages,
|
|
failedPackagesErrors));
|
|
|
|
HRESULT hrCreateCIM = MsixCoreLib::CreateAndAddToCIM(unpackDestination, tempDirPathString, rootDirectory);
|
|
|
|
// Best-effort attempt to remove temp directory
|
|
std::error_code removeTempDirErrorCode;
|
|
bool removeTemprDirResult = std::filesystem::remove_all(tempDirPath, removeTempDirErrorCode);
|
|
if (!removeTemprDirResult || removeTempDirErrorCode.value() != 0)
|
|
{
|
|
std::wcout << std::endl;
|
|
std::wcout << "Failed to remove the temp dir " << tempDirPath << std::endl;
|
|
std::wcout << "Ignoring this non-fatal error and moving on" << std::endl;
|
|
std::wcout << std::endl;
|
|
}
|
|
|
|
if (FAILED(hrCreateCIM))
|
|
{
|
|
std::wcout << std::endl;
|
|
std::wcout << "Creating the CIM file " << unpackDestination << " failed with HRESULT 0x" << std::hex << hrCreateCIM << std::endl;
|
|
std::wcout << std::endl;
|
|
return hrCreateCIM;
|
|
}
|
|
else
|
|
{
|
|
std::wcout << std::endl;
|
|
std::wcout << "Successfully created the CIM file: " << unpackDestination << std::endl;
|
|
std::wcout << std::endl;
|
|
|
|
OutputUnpackFailures(packageSourcePath, skippedFiles, failedPackages, failedPackagesErrors);
|
|
}
|
|
|
|
}
|
|
// UnpackDestinationFileType::NotSpecified is only valid if unpacking to an existing VHD
|
|
else if (fileType == WVDFileType::NotSpecified || fileType == WVDFileType::VHD || fileType == WVDFileType::VHDX)
|
|
{
|
|
if (createFile)
|
|
{
|
|
if (!(EndsWith(unpackDestination, L".vhd") || (EndsWith(unpackDestination, L".vhdx"))))
|
|
{
|
|
std::wcout << std::endl;
|
|
std::wcout << "Invalid VHD file name. File name must have .vhd or .vhdx file extension" << std::endl;
|
|
std::wcout << std::endl;
|
|
return E_INVALIDARG;
|
|
}
|
|
else
|
|
{
|
|
if (cli.GetVHDSize() == 0)
|
|
{
|
|
std::wcout << std::endl;
|
|
std::wcout << "VHD size was not specified. Please provide a vhd size in MB using the -vhdSize option" << std::endl;
|
|
std::wcout << std::endl;
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
std::wstring driveLetter;
|
|
HRESULT hrCreateVHD = MsixCoreLib::CreateAndMountVHD(unpackDestination, cli.GetVHDSize(), fileType == WVDFileType::VHD, driveLetter);
|
|
if (FAILED(hrCreateVHD))
|
|
{
|
|
std::wcout << std::endl;
|
|
std::wcout << "Creating the VHD(X) file " << unpackDestination << " failed with HRESULT 0x" << std::hex << hrCreateVHD << std::endl;
|
|
|
|
if (hrCreateVHD != HRESULT_FROM_WIN32(ERROR_FILE_EXISTS))
|
|
{
|
|
// Best effort to unmount and delete the VHD file
|
|
if (std::filesystem::exists(std::filesystem::path(unpackDestination.c_str())))
|
|
{
|
|
MsixCoreLib::UnmountVHD(unpackDestination);
|
|
if (_wremove(unpackDestination.c_str()) != 0)
|
|
{
|
|
std::wcout << "Failed best-effort attempt to delete the incomplete VHD(X) file: " << unpackDestination << " Please do not use this file." << std::endl;
|
|
}
|
|
else
|
|
{
|
|
std::wcout << "Best-effort attempt to delete the incomplete VHD(X) file " << unpackDestination << " succeeded." << std::endl;
|
|
}
|
|
}
|
|
}
|
|
|
|
std::wcout << std::endl;
|
|
|
|
return hrCreateVHD;
|
|
}
|
|
|
|
// Unpack to the mounted VHD
|
|
std::wstring mountedUnpackDest = driveLetter + L":\\" + cli.GetRootDirectory();
|
|
RETURN_IF_FAILED(MsixCoreLib::Unpack(
|
|
packageSourcePath,
|
|
mountedUnpackDest,
|
|
cli.IsApplyACLs(),
|
|
cli.IsValidateSignature(),
|
|
skippedFiles,
|
|
failedPackages,
|
|
failedPackagesErrors
|
|
));
|
|
|
|
HRESULT hrUnmount = MsixCoreLib::UnmountVHD(unpackDestination);
|
|
if (FAILED(hrUnmount))
|
|
{
|
|
std::wcout << std::endl;
|
|
std::wcout << "Unmounting the VHD " << unpackDestination << " failed with HRESULT 0x" << std::hex << hrCreateVHD << std::endl;
|
|
std::wcout << "Ignoring as non-fatal error.." << std::endl;
|
|
std::wcout << std::endl;
|
|
}
|
|
|
|
OutputUnpackFailures(packageSourcePath, skippedFiles, failedPackages, failedPackagesErrors);
|
|
|
|
std::wcout << std::endl;
|
|
std::wcout << "Finished unpacking packages to: " << unpackDestination << std::endl;
|
|
std::wcout << std::endl;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RETURN_IF_FAILED(MsixCoreLib::Unpack(
|
|
packageSourcePath,
|
|
unpackDestination,
|
|
cli.IsApplyACLs(),
|
|
cli.IsValidateSignature(),
|
|
skippedFiles,
|
|
failedPackages,
|
|
failedPackagesErrors));
|
|
|
|
std::wcout << std::endl;
|
|
std::wcout << "Finished unpacking packages to: " << unpackDestination << std::endl;
|
|
std::wcout << std::endl;
|
|
|
|
OutputUnpackFailures(packageSourcePath, skippedFiles, failedPackages, failedPackagesErrors);
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
case OperationType::MountImage:
|
|
{
|
|
WVDFileType fileType = cli.GetFileType();
|
|
|
|
if (cli.GetMountImagePath().empty())
|
|
{
|
|
std::wcout << std::endl;
|
|
std::wcout << "Please provide the path to the image you would like to mount." << std::endl;
|
|
std::wcout << std::endl;
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (fileType == WVDFileType::CIM)
|
|
{
|
|
std::wstring volumeId;
|
|
HRESULT hrMountCIM = MsixCoreLib::MountCIM(cli.GetMountImagePath(), volumeId);
|
|
if (FAILED(hrMountCIM))
|
|
{
|
|
std::wcout << std::endl;
|
|
std::wcout << "Mounting the CIM file " << cli.GetMountImagePath() << " failed with HRESULT 0x" << std::hex << hrMountCIM << std::endl;
|
|
std::wcout << std::endl;
|
|
return hrMountCIM;
|
|
}
|
|
else
|
|
{
|
|
std::wcout << std::endl;
|
|
std::wcout << "Image successfully mounted!" << std::endl;
|
|
std::wcout << "To examine contents in File Explorer, press Win + R and enter the following: " << std::endl;
|
|
std::wcout << "\\\\?\\Volume{" << volumeId << "}" << std::endl;
|
|
std::wcout << std::endl;
|
|
std::wcout << "To unmount, run one of the followings commands: " << std::endl;
|
|
std::wcout << "msixmgr.exe -unmountimage -imagePath " << cli.GetMountImagePath() << " -filetype CIM" << std::endl;
|
|
std::wcout << "msixmgr.exe -unmountimage -volumeid " << volumeId << " -filetype CIM" << std::endl;
|
|
std::wcout << std::endl;
|
|
}
|
|
}
|
|
else if (fileType == WVDFileType::VHD || fileType == WVDFileType::VHDX)
|
|
{
|
|
std::wstring driveLetter;
|
|
HRESULT hrMountVHD = MsixCoreLib::MountVHD(cli.GetMountImagePath(), cli.isMountReadOnly(), driveLetter);
|
|
if (FAILED(hrMountVHD))
|
|
{
|
|
std::wcout << std::endl;
|
|
std::wcout << "Mounting the VHD(X) file " << cli.GetMountImagePath() << " failed with HRESULT 0x" << std::hex << hrMountVHD << std::endl;
|
|
std::wcout << std::endl;
|
|
return hrMountVHD;
|
|
}
|
|
else
|
|
{
|
|
bool isVHD = cli.GetFileType() == WVDFileType::VHD;
|
|
std::wcout << std::endl;
|
|
std::wcout << "Image " << cli.GetMountImagePath() << " successfully mounted to " << driveLetter << ":\\" << std::endl;
|
|
std::wcout << "To unmount, run the following command: " << std::endl;
|
|
std::wcout << "msixmgr.exe -unmountimage -imagePath " << cli.GetMountImagePath() << " -filetype VHD" << (isVHD ? "" : "X") << std::endl;
|
|
std::wcout << std::endl;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
std::wcout << std::endl;
|
|
std::wcout << "Please specify one of the following supported file types for the -MountImage command: {VHD, VHDX, CIM}" << std::endl;
|
|
std::wcout << std::endl;
|
|
return ERROR_NOT_SUPPORTED;
|
|
}
|
|
return S_OK;
|
|
}
|
|
case OperationType::UnmountImage:
|
|
{
|
|
WVDFileType fileType = cli.GetFileType();
|
|
if (fileType == WVDFileType::CIM)
|
|
{
|
|
if (cli.GetVolumeId().empty() && cli.GetMountImagePath().empty())
|
|
{
|
|
std::wcout << std::endl;
|
|
std::wcout << "To unmount an CIM image, please provide either the CIM file path or the volume the image was mounted to." << std::endl;
|
|
std::wcout << "The CIM file path can be specified using the -imagepath option." << std::endl;
|
|
std::wcout << "The volume can be specified using the -volumeId option." << std::endl;
|
|
std::wcout << std::endl;
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
HRESULT hrUnmountCIM = MsixCoreLib::UnmountCIM(cli.GetMountImagePath(), cli.GetVolumeId());
|
|
|
|
if (FAILED(hrUnmountCIM))
|
|
{
|
|
std::wcout << std::endl;
|
|
std::wcout << "Unmounting the CIM " << " failed with HRESULT 0x" << std::hex << hrUnmountCIM << std::endl;
|
|
|
|
// ERROR_NOT_FOUND may be returned if only the mount image path but not the volume id was provided
|
|
// and msixmgr was unable to find the volume id associated with a given image path.
|
|
if (hrUnmountCIM == HRESULT_FROM_WIN32(ERROR_NOT_FOUND) && cli.GetVolumeId().empty())
|
|
{
|
|
std::wcout << "The error ERROR_NOT_FOUND may indicate a failure to find the volume id associated with a given image path."<< std::endl;
|
|
std::wcout << "Please try unmounting using the -volumeId option." << std::endl;
|
|
}
|
|
|
|
std::wcout << std::endl;
|
|
return hrUnmountCIM;
|
|
}
|
|
else
|
|
{
|
|
std::wcout << std::endl;
|
|
if (!cli.GetMountImagePath().empty())
|
|
{
|
|
std::wcout << "Successfully unmounted the CIM file: " << cli.GetMountImagePath() << std::endl;
|
|
}
|
|
else
|
|
{
|
|
std::wcout << "Successfully unmounted the CIM with volume id: " << cli.GetVolumeId() << std::endl;
|
|
}
|
|
|
|
std::wcout << std::endl;
|
|
}
|
|
}
|
|
else if (fileType == WVDFileType::VHD || fileType == WVDFileType::VHDX)
|
|
{
|
|
if (cli.GetMountImagePath().empty())
|
|
{
|
|
std::wcout << std::endl;
|
|
std::wcout << "Please provide the path to the image you would like to unmount." << std::endl;
|
|
std::wcout << std::endl;
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
HRESULT hrUnmountVHD = MsixCoreLib::UnmountVHD(cli.GetMountImagePath());
|
|
|
|
if (FAILED(hrUnmountVHD))
|
|
{
|
|
std::wcout << std::endl;
|
|
std::wcout << "Unmounting the VHD " << cli.GetMountImagePath() << " failed with HRESULT 0x" << std::hex << hrUnmountVHD << std::endl;
|
|
std::wcout << std::endl;
|
|
return hrUnmountVHD;
|
|
}
|
|
else
|
|
{
|
|
std::wcout << std::endl;
|
|
std::wcout << "Successfully unmounted the VHD " << cli.GetMountImagePath() << std::endl;
|
|
std::wcout << std::endl;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
std::wcout << std::endl;
|
|
std::wcout << "Please specify one of the following supported file types for the -UnmountImage command: {VHD, VHDX, CIM}" << std::endl;
|
|
std::wcout << std::endl;
|
|
return ERROR_NOT_SUPPORTED;
|
|
}
|
|
return S_OK;
|
|
}
|
|
default:
|
|
return E_NOT_SET;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
cli.DisplayHelp();
|
|
}
|
|
|
|
// Stop TraceLogging and unregister the providers
|
|
TraceLoggingUnregister(g_MsixUITraceLoggingProvider);
|
|
TraceLoggingUnregister(g_MsixTraceLoggingProvider);
|
|
|
|
return 0;
|
|
}
|