winget-cli

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit 79c8c11bd2145d91b3472c0b30f7ec3441693ce2
parent 739aefe60aedd437dd53cccf7de45bec29532a8d
Author: yao-msft <50888816+yao-msft@users.noreply.github.com>
Date:   Wed,  2 Mar 2022 12:00:51 -0800

Add uninstall functionality to Com api (#1909)


Diffstat:
M.github/actions/spelling/expect.txt | 5+++++
Msrc/AppInstallerCLICore/AppInstallerCLICore.vcxproj | 4++--
Msrc/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters | 4++--
Asrc/AppInstallerCLICore/Commands/COMCommand.cpp | 41+++++++++++++++++++++++++++++++++++++++++
Asrc/AppInstallerCLICore/Commands/COMCommand.h | 37+++++++++++++++++++++++++++++++++++++
Dsrc/AppInstallerCLICore/Commands/COMInstallCommand.cpp | 33---------------------------------
Dsrc/AppInstallerCLICore/Commands/COMInstallCommand.h | 27---------------------------
Msrc/AppInstallerCLICore/Commands/UninstallCommand.cpp | 11+----------
Msrc/AppInstallerCLICore/ContextOrchestrator.cpp | 54++++++++++++++++++++++++++++++++++++++++++++----------
Msrc/AppInstallerCLICore/ContextOrchestrator.h | 21+++++++++++++++++++--
Msrc/AppInstallerCLICore/ExecutionContextData.h | 4++--
Msrc/AppInstallerCLICore/Workflows/InstallFlow.cpp | 4++--
Msrc/AppInstallerCLICore/Workflows/MsiInstallFlow.cpp | 2+-
Msrc/AppInstallerCLICore/Workflows/MsiInstallFlow.h | 2+-
Msrc/AppInstallerCLICore/Workflows/ShellExecuteInstallerHandler.cpp | 4+++-
Msrc/AppInstallerCLICore/Workflows/ShellExecuteInstallerHandler.h | 4++--
Msrc/AppInstallerCLICore/Workflows/UninstallFlow.cpp | 25++++++++++++++++++++++++-
Msrc/AppInstallerCLICore/Workflows/UninstallFlow.h | 7+++++++
Msrc/AppInstallerCLIPackage/Package.appxmanifest | 2++
Msrc/AppInstallerCLITests/WorkFlow.cpp | 2+-
Asrc/Microsoft.Management.Deployment.Client/Client.UninstallOptions.h | 15+++++++++++++++
Msrc/Microsoft.Management.Deployment.Client/Microsoft.Management.Deployment.Client.vcxproj | 2++
Msrc/Microsoft.Management.Deployment.Client/PackageManager.cpp | 8++++++++
Asrc/Microsoft.Management.Deployment.Client/UninstallOptions.cpp | 49+++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/Microsoft.Management.Deployment/CatalogPackage.cpp | 4++++
Msrc/Microsoft.Management.Deployment/CatalogPackage.h | 1+
Msrc/Microsoft.Management.Deployment/Converters.cpp | 77-----------------------------------------------------------------------------
Msrc/Microsoft.Management.Deployment/Converters.h | 93++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Msrc/Microsoft.Management.Deployment/InstallResult.h | 2+-
Msrc/Microsoft.Management.Deployment/Microsoft.Management.Deployment.vcxproj | 4++++
Msrc/Microsoft.Management.Deployment/PackageManager.cpp | 380++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
Msrc/Microsoft.Management.Deployment/PackageManager.h | 4++++
Msrc/Microsoft.Management.Deployment/PackageManager.idl | 105++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Asrc/Microsoft.Management.Deployment/UninstallOptions.cpp | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Microsoft.Management.Deployment/UninstallOptions.h | 43+++++++++++++++++++++++++++++++++++++++++++
Asrc/Microsoft.Management.Deployment/UninstallResult.cpp | 44++++++++++++++++++++++++++++++++++++++++++++
Asrc/Microsoft.Management.Deployment/UninstallResult.h | 36++++++++++++++++++++++++++++++++++++
Msrc/WindowsPackageManager/main.cpp | 1+
38 files changed, 947 insertions(+), 268 deletions(-)

diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt @@ -363,9 +363,14 @@ thiscouldbeapc threehundred Tlg tombstoned +TOptions tpl +TProgress transitioning +TResult trimstart +TState +TStatus UCase ucasemap UChars diff --git a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj @@ -241,7 +241,7 @@ <ClInclude Include="Argument.h" /> <ClInclude Include="ChannelStreams.h" /> <ClInclude Include="Command.h" /> - <ClInclude Include="Commands\COMInstallCommand.h" /> + <ClInclude Include="Commands\COMCommand.h" /> <ClInclude Include="Commands\CompleteCommand.h" /> <ClInclude Include="Commands\ExperimentalCommand.h" /> <ClInclude Include="Commands\ExportCommand.h" /> @@ -293,7 +293,7 @@ </ItemGroup> <ItemGroup> <ClCompile Include="COMContext.cpp" /> - <ClCompile Include="Commands\COMInstallCommand.cpp" /> + <ClCompile Include="Commands\COMCommand.cpp" /> <ClCompile Include="Commands\ImportCommand.cpp" /> <ClCompile Include="ContextOrchestrator.cpp" /> <ClCompile Include="Workflows\DependenciesFlow.cpp" /> diff --git a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters @@ -158,7 +158,7 @@ <ClInclude Include="Workflows\DependenciesFlow.h"> <Filter>Workflows</Filter> </ClInclude> - <ClInclude Include="Commands\COMInstallCommand.h"> + <ClInclude Include="Commands\COMCommand.h"> <Filter>Commands</Filter> </ClInclude> <ClInclude Include="Workflows\MsiInstallFlow.h"> @@ -301,7 +301,7 @@ <ClCompile Include="ContextOrchestrator.cpp"> <Filter>Source Files</Filter> </ClCompile> - <ClCompile Include="Commands\COMInstallCommand.cpp"> + <ClCompile Include="Commands\COMCommand.cpp"> <Filter>Commands</Filter> </ClCompile> <ClCompile Include="Workflows\MsiInstallFlow.cpp"> diff --git a/src/AppInstallerCLICore/Commands/COMCommand.cpp b/src/AppInstallerCLICore/Commands/COMCommand.cpp @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "COMCommand.h" +#include "Workflows/DownloadFlow.h" +#include "Workflows/InstallFlow.h" +#include "Workflows/UninstallFlow.h" +#include "Workflows/WorkflowBase.h" + +using namespace AppInstaller::CLI::Execution; +using namespace AppInstaller::CLI::Workflow; +using namespace AppInstaller::Manifest; +using namespace AppInstaller::Utility::literals; + +namespace AppInstaller::CLI +{ + // IMPORTANT: To use this command, the caller should have already retrieved the package manifest (GetManifest()) and added it to the Context Data + void COMDownloadCommand::ExecuteInternal(Context& context) const + { + context << + Workflow::ReportExecutionStage(ExecutionStage::Discovery) << + Workflow::SelectInstaller << + Workflow::EnsureApplicableInstaller << + Workflow::DownloadSinglePackage; + } + + // IMPORTANT: To use this command, the caller should have already executed the COMDownloadCommand + void COMInstallCommand::ExecuteInternal(Context& context) const + { + context << + Workflow::ReverifyInstallerHash << + Workflow::InstallPackageInstaller; + } + + // IMPORTANT: To use this command, the caller should have already retrieved the InstalledPackageVersion and added it to the Context Data + void COMUninstallCommand::ExecuteInternal(Execution::Context& context) const + { + context << + Workflow::UninstallSinglePackage; + } +} diff --git a/src/AppInstallerCLICore/Commands/COMCommand.h b/src/AppInstallerCLICore/Commands/COMCommand.h @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "Command.h" + +namespace AppInstaller::CLI +{ + // IMPORTANT: To use this command, the caller should have already retrieved the package manifest (GetManifest()) and added it to the Context Data + struct COMDownloadCommand final : public Command + { + constexpr static std::string_view CommandName = "download"sv; + COMDownloadCommand(std::string_view parent) : Command(CommandName, parent) {} + + protected: + void ExecuteInternal(Execution::Context& context) const override; + }; + + // IMPORTANT: To use this command, the caller should have already retrieved the package manifest (GetManifest()) and added it to the Context Data + struct COMInstallCommand final : public Command + { + constexpr static std::string_view CommandName = "install"sv; + COMInstallCommand(std::string_view parent) : Command(CommandName, parent) {} + + protected: + void ExecuteInternal(Execution::Context& context) const override; + }; + + // IMPORTANT: To use this command, the caller should have already retrieved the InstalledPackageVersion and added it to the Context Data + struct COMUninstallCommand final : public Command + { + constexpr static std::string_view CommandName = "uninstall"sv; + COMUninstallCommand(std::string_view parent) : Command(CommandName, parent) {} + + protected: + void ExecuteInternal(Execution::Context& context) const override; + }; +} diff --git a/src/AppInstallerCLICore/Commands/COMInstallCommand.cpp b/src/AppInstallerCLICore/Commands/COMInstallCommand.cpp @@ -1,33 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#include "pch.h" -#include "COMInstallCommand.h" -#include "Workflows/DownloadFlow.h" -#include "Workflows/InstallFlow.h" -#include "Workflows/WorkflowBase.h" - -using namespace AppInstaller::CLI::Execution; -using namespace AppInstaller::CLI::Workflow; -using namespace AppInstaller::Manifest; -using namespace AppInstaller::Utility::literals; - -namespace AppInstaller::CLI -{ - // IMPORTANT: To use this command, the caller should have already retrieved the package manifest (GetManifest()) and added it to the Context Data - void COMDownloadCommand::ExecuteInternal(Context& context) const - { - context << - Workflow::ReportExecutionStage(ExecutionStage::Discovery) << - Workflow::SelectInstaller << - Workflow::EnsureApplicableInstaller << - Workflow::DownloadSinglePackage; - } - - // IMPORTANT: To use this command, the caller should have already executed the COMDownloadCommand - void COMInstallCommand::ExecuteInternal(Context& context) const - { - context << - Workflow::ReverifyInstallerHash << - Workflow::InstallPackageInstaller; - } -} diff --git a/src/AppInstallerCLICore/Commands/COMInstallCommand.h b/src/AppInstallerCLICore/Commands/COMInstallCommand.h @@ -1,27 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#pragma once -#include "Command.h" - -namespace AppInstaller::CLI -{ - // IMPORTANT: To use this command, the caller should have already retrieved the package manifest (GetManifest()) and added it to the Context Data - struct COMDownloadCommand final : public Command - { - constexpr static std::string_view CommandName = "download"sv; - COMDownloadCommand(std::string_view parent) : Command(CommandName, parent) {} - - protected: - void ExecuteInternal(Execution::Context& context) const override; - }; - - // IMPORTANT: To use this command, the caller should have already retrieved the package manifest (GetManifest()) and added it to the Context Data - struct COMInstallCommand final : public Command - { - constexpr static std::string_view CommandName = "install"sv; - COMInstallCommand(std::string_view parent) : Command(CommandName, parent) {} - - protected: - void ExecuteInternal(Execution::Context& context) const override; - }; -} diff --git a/src/AppInstallerCLICore/Commands/UninstallCommand.cpp b/src/AppInstallerCLICore/Commands/UninstallCommand.cpp @@ -3,10 +3,8 @@ #include "pch.h" #include "UninstallCommand.h" #include "Workflows/UninstallFlow.h" -#include "Workflows/InstallFlow.h" #include "Workflows/CompletionFlow.h" #include "Workflows/WorkflowBase.h" -#include "Workflows/DependenciesFlow.h" #include "Resources.h" using AppInstaller::CLI::Execution::Args; @@ -130,13 +128,6 @@ namespace AppInstaller::CLI } context << - Workflow::GetInstalledPackageVersion << - Workflow::GetUninstallInfo << - Workflow::GetDependenciesInfoForUninstall << - Workflow::ReportDependencies(Resource::String::UninstallCommandReportDependencies) << - Workflow::ReportExecutionStage(ExecutionStage::Execution) << - Workflow::ExecuteUninstaller << - Workflow::ReportExecutionStage(ExecutionStage::PostExecution) << - Workflow::RecordUninstall; + Workflow::UninstallSinglePackage; } } \ No newline at end of file diff --git a/src/AppInstallerCLICore/ContextOrchestrator.cpp b/src/AppInstallerCLICore/ContextOrchestrator.cpp @@ -4,7 +4,7 @@ #include "ExecutionContext.h" #include "ContextOrchestrator.h" #include "COMContext.h" -#include "Commands/COMInstallCommand.h" +#include "Commands/COMCommand.h" #include "winget/UserSettings.h" #include <Commands/RootCommand.h> @@ -12,6 +12,9 @@ namespace AppInstaller::CLI::Execution { namespace { + // Operation command queue used by install and uninstall commands. + constexpr static std::string_view OperationCommandQueueName = "operation"sv; + // Callback function used by worker threads in the queue. // context must be a pointer to a queue item. void CALLBACK OrchestratorQueueWorkCallback(PTP_CALLBACK_INSTANCE, PVOID context, PTP_WORK) @@ -23,6 +26,17 @@ namespace AppInstaller::CLI::Execution queue->RunItem(queueItem->GetId()); } } + + // Get command queue name based on command name. + std::string_view GetCommandQueueName(std::string_view commandName) + { + if (commandName == COMInstallCommand::CommandName || commandName == COMUninstallCommand::CommandName) + { + return OperationCommandQueueName; + } + + return commandName; + } } ContextOrchestrator& ContextOrchestrator::Instance() @@ -43,11 +57,11 @@ namespace AppInstaller::CLI::Execution // use that as the maximum (up to 3); otherwise use a single thread. const auto supportedConcurrentThreads = std::thread::hardware_concurrency(); const UINT32 maxDownloadThreads = 3; - const UINT32 installThreads = 1; + const UINT32 operationThreads = 1; const UINT32 downloadThreads = std::min(supportedConcurrentThreads ? supportedConcurrentThreads - 1 : 1, maxDownloadThreads); AddCommandQueue(COMDownloadCommand::CommandName, downloadThreads); - AddCommandQueue(COMInstallCommand::CommandName, installThreads); + AddCommandQueue(OperationCommandQueueName, operationThreads); } void ContextOrchestrator::AddCommandQueue(std::string_view commandName, UINT32 allowedThreads) @@ -79,7 +93,8 @@ namespace AppInstaller::CLI::Execution THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INSTALL_ALREADY_RUNNING), FindById(item->GetId())); } - m_commandQueues.at(std::string(item->GetNextCommand().Name()))->EnqueueAndRunItem(item); + std::string commandQueueName{ GetCommandQueueName(item->GetNextCommand().Name()) }; + m_commandQueues.at(commandQueueName)->EnqueueAndRunItem(item); } void ContextOrchestrator::RemoveItemInState(const OrchestratorQueueItem& item, OrchestratorQueueItemState state) @@ -111,14 +126,20 @@ namespace AppInstaller::CLI::Execution void ContextOrchestrator::AddItemManifestToInstallingSource(const OrchestratorQueueItem& queueItem) { - const auto& manifest = queueItem.GetContext().Get<Execution::Data::Manifest>(); - m_installingWriteableSource.AddPackageVersion(manifest, std::filesystem::path{ manifest.Id + '.' + manifest.Version }); + if (queueItem.IsApplicableForInstallingSource()) + { + const auto& manifest = queueItem.GetContext().Get<Execution::Data::Manifest>(); + m_installingWriteableSource.AddPackageVersion(manifest, std::filesystem::path{ manifest.Id + '.' + manifest.Version }); + } } void ContextOrchestrator::RemoveItemManifestFromInstallingSource(const OrchestratorQueueItem& queueItem) { - const auto& manifest = queueItem.GetContext().Get<Execution::Data::Manifest>(); - m_installingWriteableSource.RemovePackageVersion(manifest, std::filesystem::path{ manifest.Id + '.' + manifest.Version }); + if (queueItem.IsApplicableForInstallingSource()) + { + const auto& manifest = queueItem.GetContext().Get<Execution::Data::Manifest>(); + m_installingWriteableSource.RemovePackageVersion(manifest, std::filesystem::path{ manifest.Id + '.' + manifest.Version }); + } } _Requires_lock_held_(m_queueLock) @@ -313,11 +334,24 @@ namespace AppInstaller::CLI::Execution (GetSourceId() == comparedId.GetSourceId())); } - std::unique_ptr<OrchestratorQueueItem> OrchestratorQueueItemFactory::CreateItemForInstall(std::wstring packageId, std::wstring sourceId, std::unique_ptr<COMContext> context) + std::unique_ptr<OrchestratorQueueItem> OrchestratorQueueItemFactory::CreateItemForInstall(std::wstring packageId, std::wstring sourceId, std::unique_ptr<COMContext> context, bool isUpgrade) { - std::unique_ptr<OrchestratorQueueItem> item = std::make_unique<OrchestratorQueueItem>(OrchestratorQueueItemId(std::move(packageId), std::move(sourceId)), std::move(context)); + std::unique_ptr<OrchestratorQueueItem> item = std::make_unique<OrchestratorQueueItem>(OrchestratorQueueItemId(std::move(packageId), std::move(sourceId)), std::move(context), isUpgrade ? PackageOperationType::Upgrade : PackageOperationType::Install); item->AddCommand(std::make_unique<::AppInstaller::CLI::COMDownloadCommand>(RootCommand::CommandName)); item->AddCommand(std::make_unique<::AppInstaller::CLI::COMInstallCommand>(RootCommand::CommandName)); return item; } + + std::unique_ptr<OrchestratorQueueItem> OrchestratorQueueItemFactory::CreateItemForUninstall(std::wstring packageId, std::wstring sourceId, std::unique_ptr<COMContext> context) + { + std::unique_ptr<OrchestratorQueueItem> item = std::make_unique<OrchestratorQueueItem>(OrchestratorQueueItemId(std::move(packageId), std::move(sourceId)), std::move(context), PackageOperationType::Uninstall); + item->AddCommand(std::make_unique<::AppInstaller::CLI::COMUninstallCommand>(RootCommand::CommandName)); + return item; + } + + std::unique_ptr<OrchestratorQueueItem> OrchestratorQueueItemFactory::CreateItemForSearch(std::wstring packageId, std::wstring sourceId, std::unique_ptr<COMContext> context) + { + std::unique_ptr<OrchestratorQueueItem> item = std::make_unique<OrchestratorQueueItem>(OrchestratorQueueItemId(std::move(packageId), std::move(sourceId)), std::move(context), PackageOperationType::None); + return item; + } } diff --git a/src/AppInstallerCLICore/ContextOrchestrator.h b/src/AppInstallerCLICore/ContextOrchestrator.h @@ -40,9 +40,18 @@ namespace AppInstaller::CLI::Execution struct OrchestratorQueue; + enum class PackageOperationType + { + None, + Install, + Upgrade, + Uninstall, + }; + struct OrchestratorQueueItem { - OrchestratorQueueItem(OrchestratorQueueItemId id, std::unique_ptr<COMContext> context) : m_id(std::move(id)), m_context(std::move(context)) {} + OrchestratorQueueItem(OrchestratorQueueItemId id, std::unique_ptr<COMContext> context, PackageOperationType operationType) : + m_id(std::move(id)), m_context(std::move(context)), m_operationType(operationType) {} OrchestratorQueueItemState GetState() const { return m_state; } void SetState(OrchestratorQueueItemState state) { m_state = state; } @@ -66,6 +75,8 @@ namespace AppInstaller::CLI::Execution bool IsOnFirstCommand() const { return m_isOnFirstCommand; } bool IsComplete() const { return m_commands.empty(); } + bool IsApplicableForInstallingSource() const { return m_operationType == PackageOperationType::Install || m_operationType == PackageOperationType::Upgrade; } + PackageOperationType GetPackageOperationType() const { return m_operationType; } private: OrchestratorQueueItemState m_state = OrchestratorQueueItemState::NotQueued; @@ -75,11 +86,17 @@ namespace AppInstaller::CLI::Execution std::deque<std::unique_ptr<Command>> m_commands; bool m_isOnFirstCommand = true; OrchestratorQueue* m_currentQueue = nullptr; + PackageOperationType m_operationType = PackageOperationType::None; }; struct OrchestratorQueueItemFactory { - static std::unique_ptr<OrchestratorQueueItem> CreateItemForInstall(std::wstring packageId, std::wstring sourceId, std::unique_ptr<COMContext> context); + // Create queue item for install/upgrade + static std::unique_ptr<OrchestratorQueueItem> CreateItemForInstall(std::wstring packageId, std::wstring sourceId, std::unique_ptr<COMContext> context, bool isUpgrade); + // Create queue item for uninstall + static std::unique_ptr<OrchestratorQueueItem> CreateItemForUninstall(std::wstring packageId, std::wstring sourceId, std::unique_ptr<COMContext> context); + // Create queue item for finding existing entry from the orchestrator queue + static std::unique_ptr<OrchestratorQueueItem> CreateItemForSearch(std::wstring packageId, std::wstring sourceId, std::unique_ptr<COMContext> context); }; struct ContextOrchestrator diff --git a/src/AppInstallerCLICore/ExecutionContextData.h b/src/AppInstallerCLICore/ExecutionContextData.h @@ -33,7 +33,7 @@ namespace AppInstaller::CLI::Execution InstallerPath, LogPath, InstallerArgs, - InstallerReturnCode, + OperationReturnCode, CompletionData, InstalledPackageVersion, UninstallString, @@ -130,7 +130,7 @@ namespace AppInstaller::CLI::Execution }; template <> - struct DataMapping<Data::InstallerReturnCode> + struct DataMapping<Data::OperationReturnCode> { using value_t = DWORD; }; diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp @@ -302,7 +302,7 @@ namespace AppInstaller::CLI::Workflow } catch (const wil::ResultException& re) { - context.Add<Execution::Data::InstallerReturnCode>(re.GetErrorCode()); + context.Add<Execution::Data::OperationReturnCode>(re.GetErrorCode()); context << ReportInstallerResult("MSIX"sv, re.GetErrorCode(), /* isHResult */ true); return; } @@ -319,7 +319,7 @@ namespace AppInstaller::CLI::Workflow void ReportInstallerResult::operator()(Execution::Context& context) const { - DWORD installResult = context.Get<Execution::Data::InstallerReturnCode>(); + DWORD installResult = context.Get<Execution::Data::OperationReturnCode>(); const auto& additionalSuccessCodes = context.Get<Execution::Data::Installer>()->InstallerSuccessCodes; if (installResult != 0 && (std::find(additionalSuccessCodes.begin(), additionalSuccessCodes.end(), installResult) == additionalSuccessCodes.end())) { diff --git a/src/AppInstallerCLICore/Workflows/MsiInstallFlow.cpp b/src/AppInstallerCLICore/Workflows/MsiInstallFlow.cpp @@ -49,7 +49,7 @@ namespace AppInstaller::CLI::Workflow } else { - context.Add<Execution::Data::InstallerReturnCode>(installResult.value()); + context.Add<Execution::Data::OperationReturnCode>(installResult.value()); } } } diff --git a/src/AppInstallerCLICore/Workflows/MsiInstallFlow.h b/src/AppInstallerCLICore/Workflows/MsiInstallFlow.h @@ -8,6 +8,6 @@ namespace AppInstaller::CLI::Workflow // Ensures that there is an applicable installer. // Required Args: None // Inputs: InstallerArgs, Installer, InstallerPath, Manifest - // Outputs: InstallerReturnCode + // Outputs: OperationReturnCode void DirectMSIInstallImpl(Execution::Context& context); } diff --git a/src/AppInstallerCLICore/Workflows/ShellExecuteInstallerHandler.cpp b/src/AppInstallerCLICore/Workflows/ShellExecuteInstallerHandler.cpp @@ -202,7 +202,7 @@ namespace AppInstaller::CLI::Workflow } else { - context.Add<Execution::Data::InstallerReturnCode>(installResult.value()); + context.Add<Execution::Data::OperationReturnCode>(installResult.value()); } } @@ -253,6 +253,7 @@ namespace AppInstaller::CLI::Workflow "UninstallString", uninstallResult.value()); + context.Add<Execution::Data::OperationReturnCode>(uninstallResult.value()); context.Reporter.Error() << Resource::String::UninstallFailedWithCode << ' ' << uninstallResult.value() << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_EXEC_UNINSTALL_COMMAND_FAILED); } @@ -293,6 +294,7 @@ namespace AppInstaller::CLI::Workflow "MsiExec", uninstallResult.value()); + context.Add<Execution::Data::OperationReturnCode>(uninstallResult.value()); context.Reporter.Error() << Resource::String::UninstallFailedWithCode << ' ' << uninstallResult.value() << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_EXEC_UNINSTALL_COMMAND_FAILED); } diff --git a/src/AppInstallerCLICore/Workflows/ShellExecuteInstallerHandler.h b/src/AppInstallerCLICore/Workflows/ShellExecuteInstallerHandler.h @@ -14,13 +14,13 @@ namespace AppInstaller::CLI::Workflow // Install is done through invoking ShellExecute on downloaded installer. // Required Args: None // Inputs: Manifest?, InstallerPath, InstallerArgs - // Outputs: InstallerReturnCode + // Outputs: OperationReturnCode void ShellExecuteInstallImpl(Execution::Context& context); // Uninstall is done through invoking ShellExecute on uninstall string. // Required Args: None // Inputs: UninstallString - // Outputs: None + // Outputs: OperationReturnCode void ShellExecuteUninstallImpl(Execution::Context& context); // Removes the MSI diff --git a/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp b/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp @@ -3,6 +3,7 @@ #include "pch.h" #include "UninstallFlow.h" #include "WorkflowBase.h" +#include "DependenciesFlow.h" #include "ShellExecuteInstallerHandler.h" #include "AppInstallerMsixInfo.h" @@ -51,6 +52,19 @@ namespace AppInstaller::CLI::Workflow }; } + void UninstallSinglePackage(Execution::Context& context) + { + context << + Workflow::GetInstalledPackageVersion << + Workflow::GetUninstallInfo << + Workflow::GetDependenciesInfoForUninstall << + Workflow::ReportDependencies(Resource::String::UninstallCommandReportDependencies) << + Workflow::ReportExecutionStage(ExecutionStage::Execution) << + Workflow::ExecuteUninstaller << + Workflow::ReportExecutionStage(ExecutionStage::PostExecution) << + Workflow::RecordUninstall; + } + void GetUninstallInfo(Execution::Context& context) { auto installedPackageVersion = context.Get<Execution::Data::InstalledPackageVersion>(); @@ -156,7 +170,16 @@ namespace AppInstaller::CLI::Workflow } AICLI_LOG(CLI, Info, << "Removing MSIX package: " << packageFullName.value()); - context.Reporter.ExecuteWithProgress(std::bind(Deployment::RemovePackage, packageFullName.value(), std::placeholders::_1)); + try + { + context.Reporter.ExecuteWithProgress(std::bind(Deployment::RemovePackage, packageFullName.value(), std::placeholders::_1)); + } + catch (const wil::ResultException& re) + { + context.Add<Execution::Data::OperationReturnCode>(re.GetErrorCode()); + context.Reporter.Error() << Resource::String::UninstallFailedWithCode << ' ' << re.GetErrorCode() << std::endl; + AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_EXEC_UNINSTALL_COMMAND_FAILED); + } } context.Reporter.Info() << Resource::String::UninstallFlowUninstallSuccess << std::endl; diff --git a/src/AppInstallerCLICore/Workflows/UninstallFlow.h b/src/AppInstallerCLICore/Workflows/UninstallFlow.h @@ -5,6 +5,13 @@ namespace AppInstaller::CLI::Workflow { + // Uninstalls a single package. This also does the reporting, user interaction, and recording + // for single-package uninstallation. + // RequiredArgs: None + // Inputs: InstalledPackageVersion + // Outputs: None + void UninstallSinglePackage(Execution::Context& context); + // Gets the command string or package family names used to uninstall the package. // Required Args: None // Inputs: InstalledPackageVersion diff --git a/src/AppInstallerCLIPackage/Package.appxmanifest b/src/AppInstallerCLIPackage/Package.appxmanifest @@ -48,6 +48,8 @@ </com:Class> <com:Class Id ="3F85B9F4-487A-4C48-9035-2903F8A6D9E8" DisplayName="PackageMatchFilter Server"> </com:Class> + <com:Class Id ="AA2A5C04-1AD9-46C4-B74F-6B334AD7EB8C" DisplayName="UninstallOptions Server"> + </com:Class> </com:ExeServer> </com:ComServer> </com:Extension> diff --git a/src/AppInstallerCLITests/WorkFlow.cpp b/src/AppInstallerCLITests/WorkFlow.cpp @@ -551,7 +551,7 @@ void OverrideForDirectMsi(TestContext& context) file << context.Get<Execution::Data::InstallerArgs>(); file.close(); - context.Add<Execution::Data::InstallerReturnCode>(0); + context.Add<Execution::Data::OperationReturnCode>(0); } }); } diff --git a/src/Microsoft.Management.Deployment.Client/Client.UninstallOptions.h b/src/Microsoft.Management.Deployment.Client/Client.UninstallOptions.h @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "UninstallOptions.g.h" + +namespace winrt::Microsoft::Management::Deployment::factory_implementation +{ + struct UninstallOptions : UninstallOptionsT<UninstallOptions, implementation::UninstallOptions> + { + auto ActivateInstance() const + { + return winrt::create_instance<winrt::Microsoft::Management::Deployment::UninstallOptions>(__uuidof(implementation::UninstallOptions), CLSCTX_ALL); + } + }; +} diff --git a/src/Microsoft.Management.Deployment.Client/Microsoft.Management.Deployment.Client.vcxproj b/src/Microsoft.Management.Deployment.Client/Microsoft.Management.Deployment.Client.vcxproj @@ -129,6 +129,7 @@ </Link> </ItemDefinitionGroup> <ItemGroup> + <ClInclude Include="Client.UninstallOptions.h" /> <ClInclude Include="pch.h" /> <ClInclude Include="Client.CreateCompositePackageCatalogOptions.h" /> <ClInclude Include="Client.FindPackagesOptions.h" /> @@ -146,6 +147,7 @@ </ClCompile> <ClCompile Include="PackageManager.cpp" /> <ClCompile Include="$(GeneratedFilesDir)module.g.cpp" /> + <ClCompile Include="UninstallOptions.cpp" /> </ItemGroup> <ItemGroup> <Midl Include="..\Microsoft.Management.Deployment\PackageManager.idl" /> diff --git a/src/Microsoft.Management.Deployment.Client/PackageManager.cpp b/src/Microsoft.Management.Deployment.Client/PackageManager.cpp @@ -42,4 +42,12 @@ namespace winrt::Microsoft::Management::Deployment::implementation { throw hresult_not_implemented(); } + winrt::Windows::Foundation::IAsyncOperationWithProgress<winrt::Microsoft::Management::Deployment::UninstallResult, winrt::Microsoft::Management::Deployment::UninstallProgress> PackageManager::UninstallPackageAsync(winrt::Microsoft::Management::Deployment::CatalogPackage package, winrt::Microsoft::Management::Deployment::UninstallOptions) + { + throw hresult_not_implemented(); + } + winrt::Windows::Foundation::IAsyncOperationWithProgress<winrt::Microsoft::Management::Deployment::UninstallResult, winrt::Microsoft::Management::Deployment::UninstallProgress> PackageManager::GetUninstallProgress(winrt::Microsoft::Management::Deployment::CatalogPackage package, winrt::Microsoft::Management::Deployment::PackageCatalogInfo catalogInfo) + { + throw hresult_not_implemented(); + } } diff --git a/src/Microsoft.Management.Deployment.Client/UninstallOptions.cpp b/src/Microsoft.Management.Deployment.Client/UninstallOptions.cpp @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#pragma warning( push ) +#pragma warning ( disable : 4467 6388) +#include <UninstallOptions.h> +#include <Client.UninstallOptions.h> +#pragma warning( pop ) +#include "UninstallOptions.g.cpp" + +namespace winrt::Microsoft::Management::Deployment::implementation +{ + UninstallOptions::UninstallOptions() + { + throw hresult_not_implemented(); + } + winrt::Microsoft::Management::Deployment::PackageVersionId UninstallOptions::PackageVersionId() + { + throw hresult_not_implemented(); + } + void UninstallOptions::PackageVersionId(winrt::Microsoft::Management::Deployment::PackageVersionId const&) + { + throw hresult_not_implemented(); + } + winrt::Microsoft::Management::Deployment::PackageUninstallMode UninstallOptions::PackageUninstallMode() + { + throw hresult_not_implemented(); + } + void UninstallOptions::PackageUninstallMode(winrt::Microsoft::Management::Deployment::PackageUninstallMode const&) + { + throw hresult_not_implemented(); + } + hstring UninstallOptions::LogOutputPath() + { + throw hresult_not_implemented(); + } + void UninstallOptions::LogOutputPath(hstring const&) + { + throw hresult_not_implemented(); + } + hstring UninstallOptions::CorrelationData() + { + throw hresult_not_implemented(); + } + void UninstallOptions::CorrelationData(hstring const&) + { + throw hresult_not_implemented(); + } +} diff --git a/src/Microsoft.Management.Deployment/CatalogPackage.cpp b/src/Microsoft.Management.Deployment/CatalogPackage.cpp @@ -96,4 +96,8 @@ namespace winrt::Microsoft::Management::Deployment::implementation { return m_package->IsUpdateAvailable(); } + std::shared_ptr<::AppInstaller::Repository::IPackage> CatalogPackage::GetRepositoryPackage() + { + return m_package; + } } diff --git a/src/Microsoft.Management.Deployment/CatalogPackage.h b/src/Microsoft.Management.Deployment/CatalogPackage.h @@ -13,6 +13,7 @@ namespace winrt::Microsoft::Management::Deployment::implementation void Initialize( ::AppInstaller::Repository::Source source, std::shared_ptr<::AppInstaller::Repository::IPackage> package); + std::shared_ptr<::AppInstaller::Repository::IPackage> GetRepositoryPackage(); #endif hstring Id(); diff --git a/src/Microsoft.Management.Deployment/Converters.cpp b/src/Microsoft.Management.Deployment/Converters.cpp @@ -172,83 +172,6 @@ namespace winrt::Microsoft::Management::Deployment::implementation return metadataKey; } - winrt::Microsoft::Management::Deployment::InstallResultStatus GetInstallResultStatus(::AppInstaller::CLI::Workflow::ExecutionStage executionStage, winrt::hresult hresult) - { - winrt::Microsoft::Management::Deployment::InstallResultStatus resultStatus = winrt::Microsoft::Management::Deployment::InstallResultStatus::Ok; - - // Map some known hresults to specific statuses, otherwise use the execution stage to determine the status. - switch (hresult) - { - case S_OK: - resultStatus = winrt::Microsoft::Management::Deployment::InstallResultStatus::Ok; - break; - case APPINSTALLER_CLI_ERROR_MSSTORE_BLOCKED_BY_POLICY: - case APPINSTALLER_CLI_ERROR_MSSTORE_APP_BLOCKED_BY_POLICY: - case APPINSTALLER_CLI_ERROR_EXPERIMENTAL_FEATURE_DISABLED: - case APPINSTALLER_CLI_ERROR_BLOCKED_BY_POLICY: - resultStatus = winrt::Microsoft::Management::Deployment::InstallResultStatus::BlockedByPolicy; - break; - case APPINSTALLER_CLI_ERROR_INVALID_MANIFEST: - resultStatus = winrt::Microsoft::Management::Deployment::InstallResultStatus::ManifestError; - break; - case E_INVALIDARG: - case APPINSTALLER_CLI_ERROR_INVALID_CL_ARGUMENTS: - resultStatus = winrt::Microsoft::Management::Deployment::InstallResultStatus::InvalidOptions; - break; - case APPINSTALLER_CLI_ERROR_NO_APPLICABLE_INSTALLER: - resultStatus = winrt::Microsoft::Management::Deployment::InstallResultStatus::NoApplicableInstallers; - break; - case APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE: - case APPINSTALLER_CLI_ERROR_UPGRADE_VERSION_UNKNOWN: - case APPINSTALLER_CLI_ERROR_UPGRADE_VERSION_NOT_NEWER: - resultStatus = winrt::Microsoft::Management::Deployment::InstallResultStatus::NoApplicableUpgrade; - break; - case APPINSTALLER_CLI_ERROR_CANNOT_WRITE_TO_UPLEVEL_INDEX: - case APPINSTALLER_CLI_ERROR_INDEX_INTEGRITY_COMPROMISED: - case APPINSTALLER_CLI_ERROR_YAML_INIT_FAILED: - case APPINSTALLER_CLI_ERROR_YAML_INVALID_MAPPING_KEY: - case APPINSTALLER_CLI_ERROR_YAML_DUPLICATE_MAPPING_KEY: - case APPINSTALLER_CLI_ERROR_YAML_INVALID_OPERATION: - case APPINSTALLER_CLI_ERROR_YAML_DOC_BUILD_FAILED: - case APPINSTALLER_CLI_ERROR_YAML_INVALID_EMITTER_STATE: - case APPINSTALLER_CLI_ERROR_YAML_INVALID_DATA: - case APPINSTALLER_CLI_ERROR_LIBYAML_ERROR: - case APPINSTALLER_CLI_ERROR_INTERNAL_ERROR: - resultStatus = winrt::Microsoft::Management::Deployment::InstallResultStatus::InternalError; - break; - default: - switch (executionStage) - { - case ::AppInstaller::CLI::Workflow::ExecutionStage::Initial: - resultStatus = winrt::Microsoft::Management::Deployment::InstallResultStatus::InternalError; - break; - case ::AppInstaller::CLI::Workflow::ExecutionStage::ParseArgs: - resultStatus = winrt::Microsoft::Management::Deployment::InstallResultStatus::InvalidOptions; - break; - case ::AppInstaller::CLI::Workflow::ExecutionStage::Discovery: - resultStatus = winrt::Microsoft::Management::Deployment::InstallResultStatus::CatalogError; - break; - case ::AppInstaller::CLI::Workflow::ExecutionStage::Download: - resultStatus = winrt::Microsoft::Management::Deployment::InstallResultStatus::DownloadError; - break; - case ::AppInstaller::CLI::Workflow::ExecutionStage::PreExecution: - resultStatus = winrt::Microsoft::Management::Deployment::InstallResultStatus::InternalError; - break; - case ::AppInstaller::CLI::Workflow::ExecutionStage::Execution: - resultStatus = winrt::Microsoft::Management::Deployment::InstallResultStatus::InstallError; - break; - case ::AppInstaller::CLI::Workflow::ExecutionStage::PostExecution: - resultStatus = winrt::Microsoft::Management::Deployment::InstallResultStatus::InternalError; - break; - default: - resultStatus = winrt::Microsoft::Management::Deployment::InstallResultStatus::InternalError; - break; - } - } - - return resultStatus; - } - winrt::Microsoft::Management::Deployment::FindPackagesResultStatus FindPackagesResultStatus(winrt::hresult hresult) { winrt::Microsoft::Management::Deployment::FindPackagesResultStatus resultStatus = winrt::Microsoft::Management::Deployment::FindPackagesResultStatus::Ok; diff --git a/src/Microsoft.Management.Deployment/Converters.h b/src/Microsoft.Management.Deployment/Converters.h @@ -14,8 +14,99 @@ namespace winrt::Microsoft::Management::Deployment::implementation ::AppInstaller::Repository::MatchType GetRepositoryMatchType(winrt::Microsoft::Management::Deployment::PackageFieldMatchOption option); ::AppInstaller::Repository::CompositeSearchBehavior GetRepositoryCompositeSearchBehavior(winrt::Microsoft::Management::Deployment::CompositeSearchBehavior searchBehavior); ::AppInstaller::Repository::PackageVersionMetadata GetRepositoryPackageVersionMetadata(winrt::Microsoft::Management::Deployment::PackageVersionMetadataField packageVersionMetadataField); - winrt::Microsoft::Management::Deployment::InstallResultStatus GetInstallResultStatus(::AppInstaller::CLI::Workflow::ExecutionStage executionStage, winrt::hresult hresult); winrt::Microsoft::Management::Deployment::FindPackagesResultStatus FindPackagesResultStatus(winrt::hresult hresult); std::optional<::AppInstaller::Utility::Architecture> GetUtilityArchitecture(winrt::Windows::System::ProcessorArchitecture architecture); std::optional<winrt::Windows::System::ProcessorArchitecture> GetWindowsSystemProcessorArchitecture(::AppInstaller::Utility::Architecture architecture); + +#define WINGET_GET_OPERATION_RESULT_STATUS(_installResultStatus_, _uninstallResultStatus_) \ + if constexpr (std::is_same_v<TStatus, winrt::Microsoft::Management::Deployment::InstallResultStatus>) \ + { \ + resultStatus = TStatus::_installResultStatus_; \ + } \ + else if constexpr (std::is_same_v<TStatus, winrt::Microsoft::Management::Deployment::UninstallResultStatus>) \ + { \ + resultStatus = TStatus::_uninstallResultStatus_; \ + } \ + + template <typename TStatus> + TStatus GetOperationResultStatus(::AppInstaller::CLI::Workflow::ExecutionStage executionStage, winrt::hresult hresult) + { + TStatus resultStatus = TStatus::Ok; + + // Map some known hresults to specific statuses, otherwise use the execution stage to determine the status. + switch (hresult) + { + case S_OK: + resultStatus = TStatus::Ok; + break; + case APPINSTALLER_CLI_ERROR_MSSTORE_BLOCKED_BY_POLICY: + case APPINSTALLER_CLI_ERROR_MSSTORE_APP_BLOCKED_BY_POLICY: + case APPINSTALLER_CLI_ERROR_EXPERIMENTAL_FEATURE_DISABLED: + case APPINSTALLER_CLI_ERROR_BLOCKED_BY_POLICY: + resultStatus = TStatus::BlockedByPolicy; + break; + case APPINSTALLER_CLI_ERROR_INVALID_MANIFEST: + resultStatus = TStatus::ManifestError; + break; + case E_INVALIDARG: + case APPINSTALLER_CLI_ERROR_INVALID_CL_ARGUMENTS: + resultStatus = TStatus::InvalidOptions; + break; + case APPINSTALLER_CLI_ERROR_NO_APPLICABLE_INSTALLER: + WINGET_GET_OPERATION_RESULT_STATUS(NoApplicableInstallers, InternalError); + break; + case APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE: + case APPINSTALLER_CLI_ERROR_UPGRADE_VERSION_UNKNOWN: + case APPINSTALLER_CLI_ERROR_UPGRADE_VERSION_NOT_NEWER: + WINGET_GET_OPERATION_RESULT_STATUS(NoApplicableUpgrade, InternalError); + break; + case APPINSTALLER_CLI_ERROR_NO_UNINSTALL_INFO_FOUND: + case APPINSTALLER_CLI_ERROR_EXEC_UNINSTALL_COMMAND_FAILED: + WINGET_GET_OPERATION_RESULT_STATUS(InstallError, UninstallError); + break; + case APPINSTALLER_CLI_ERROR_CANNOT_WRITE_TO_UPLEVEL_INDEX: + case APPINSTALLER_CLI_ERROR_INDEX_INTEGRITY_COMPROMISED: + case APPINSTALLER_CLI_ERROR_YAML_INIT_FAILED: + case APPINSTALLER_CLI_ERROR_YAML_INVALID_MAPPING_KEY: + case APPINSTALLER_CLI_ERROR_YAML_DUPLICATE_MAPPING_KEY: + case APPINSTALLER_CLI_ERROR_YAML_INVALID_OPERATION: + case APPINSTALLER_CLI_ERROR_YAML_DOC_BUILD_FAILED: + case APPINSTALLER_CLI_ERROR_YAML_INVALID_EMITTER_STATE: + case APPINSTALLER_CLI_ERROR_YAML_INVALID_DATA: + case APPINSTALLER_CLI_ERROR_LIBYAML_ERROR: + case APPINSTALLER_CLI_ERROR_INTERNAL_ERROR: + resultStatus = TStatus::InternalError; + break; + default: + switch (executionStage) + { + case ::AppInstaller::CLI::Workflow::ExecutionStage::Initial: + resultStatus = TStatus::InternalError; + break; + case ::AppInstaller::CLI::Workflow::ExecutionStage::ParseArgs: + resultStatus = TStatus::InvalidOptions; + break; + case ::AppInstaller::CLI::Workflow::ExecutionStage::Discovery: + resultStatus = TStatus::CatalogError; + break; + case ::AppInstaller::CLI::Workflow::ExecutionStage::Download: + WINGET_GET_OPERATION_RESULT_STATUS(DownloadError, InternalError); + break; + case ::AppInstaller::CLI::Workflow::ExecutionStage::PreExecution: + resultStatus = TStatus::InternalError; + break; + case ::AppInstaller::CLI::Workflow::ExecutionStage::Execution: + WINGET_GET_OPERATION_RESULT_STATUS(InstallError, UninstallError); + break; + case ::AppInstaller::CLI::Workflow::ExecutionStage::PostExecution: + resultStatus = TStatus::InternalError; + break; + default: + resultStatus = TStatus::InternalError; + break; + } + } + + return resultStatus; + } } diff --git a/src/Microsoft.Management.Deployment/InstallResult.h b/src/Microsoft.Management.Deployment/InstallResult.h @@ -8,7 +8,7 @@ namespace winrt::Microsoft::Management::Deployment::implementation struct InstallResult : InstallResultT<InstallResult> { InstallResult() = default; - + #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) void Initialize( winrt::Microsoft::Management::Deployment::InstallResultStatus status, diff --git a/src/Microsoft.Management.Deployment/Microsoft.Management.Deployment.vcxproj b/src/Microsoft.Management.Deployment/Microsoft.Management.Deployment.vcxproj @@ -154,6 +154,8 @@ <ClInclude Include="PackageVersionId.h" /> <ClInclude Include="PackageVersionInfo.h" /> <ClInclude Include="pch.h" /> + <ClInclude Include="UninstallOptions.h" /> + <ClInclude Include="UninstallResult.h" /> </ItemGroup> <ItemGroup> <ClCompile Include="CatalogPackage.cpp" /> @@ -177,6 +179,8 @@ <PrecompiledHeader>Create</PrecompiledHeader> </ClCompile> <ClCompile Include="$(GeneratedFilesDir)module.g.cpp" /> + <ClCompile Include="UninstallOptions.cpp" /> + <ClCompile Include="UninstallResult.cpp" /> </ItemGroup> <ItemGroup> <None Include="Microsoft_Management_Deployment.def" /> diff --git a/src/Microsoft.Management.Deployment/PackageManager.cpp b/src/Microsoft.Management.Deployment/PackageManager.cpp @@ -9,7 +9,7 @@ #include "Workflows/WorkflowBase.h" #include <winget/UserSettings.h> #include <winget/Manifest.h> -#include "Commands/COMInstallCommand.h" +#include "Commands/COMCommand.h" #include <AppInstallerTelemetry.h> #include <AppInstallerErrors.h> #pragma warning( push ) @@ -20,7 +20,9 @@ #include "PackageManager.h" #pragma warning( pop ) #include "PackageManager.g.cpp" +#include "CatalogPackage.h" #include "InstallResult.h" +#include "UninstallResult.h" #include "PackageCatalogInfo.h" #include "PackageCatalogReference.h" #include "PackageVersionInfo.h" @@ -135,6 +137,12 @@ namespace winrt::Microsoft::Management::Deployment::implementation context->Add<::AppInstaller::CLI::Execution::Data::Manifest>(std::move(manifest)); context->Add<::AppInstaller::CLI::Execution::Data::PackageVersion>(std::move(internalPackageVersion)); } + void AddInstalledVersionToContext(winrt::Microsoft::Management::Deployment::PackageVersionInfo installedVersionInfo, ::AppInstaller::CLI::Execution::Context* context) + { + winrt::Microsoft::Management::Deployment::implementation::PackageVersionInfo* installedVersionInfoImpl = get_self<winrt::Microsoft::Management::Deployment::implementation::PackageVersionInfo>(installedVersionInfo); + std::shared_ptr<::AppInstaller::Repository::IPackageVersion> internalInstalledVersion = installedVersionInfoImpl->GetRepositoryPackageVersion(); + context->Add<AppInstaller::CLI::Execution::Data::InstalledPackageVersion>(internalInstalledVersion); + } winrt::Microsoft::Management::Deployment::PackageCatalogReference PackageManager::CreateCompositePackageCatalog(winrt::Microsoft::Management::Deployment::CreateCompositePackageCatalogOptions const& options) { if (!options) @@ -159,13 +167,45 @@ namespace winrt::Microsoft::Management::Deployment::implementation winrt::Microsoft::Management::Deployment::InstallResult GetInstallResult(::Workflow::ExecutionStage executionStage, winrt::hresult terminationHR, uint32_t installerError, winrt::hstring correlationData, bool rebootRequired) { - winrt::Microsoft::Management::Deployment::InstallResultStatus installResultStatus = GetInstallResultStatus(executionStage, terminationHR); + winrt::Microsoft::Management::Deployment::InstallResultStatus installResultStatus = GetOperationResultStatus<InstallResultStatus>(executionStage, terminationHR); auto installResult = winrt::make_self<wil::details::module_count_wrapper<winrt::Microsoft::Management::Deployment::implementation::InstallResult>>(); installResult->Initialize(installResultStatus, terminationHR, installerError, correlationData, rebootRequired); return *installResult; } - std::optional<winrt::Microsoft::Management::Deployment::InstallProgress> GetProgress( + winrt::Microsoft::Management::Deployment::UninstallResult GetUninstallResult(::Workflow::ExecutionStage executionStage, winrt::hresult terminationHR, uint32_t uninstallerError, winrt::hstring correlationData, bool rebootRequired) + { + winrt::Microsoft::Management::Deployment::UninstallResultStatus uninstallResultStatus = GetOperationResultStatus<UninstallResultStatus>(executionStage, terminationHR); + auto uninstallResult = winrt::make_self<wil::details::module_count_wrapper<winrt::Microsoft::Management::Deployment::implementation::UninstallResult>>(); + uninstallResult->Initialize(uninstallResultStatus, terminationHR, uninstallerError, correlationData, rebootRequired); + return *uninstallResult; + } + + template <typename TResult> + TResult GetOperationResult(::Workflow::ExecutionStage executionStage, winrt::hresult terminationHR, uint32_t operationError, winrt::hstring correlationData, bool rebootRequired) + { + if constexpr (std::is_same_v<TResult, winrt::Microsoft::Management::Deployment::InstallResult>) + { + return GetInstallResult(executionStage, terminationHR, operationError, correlationData, rebootRequired); + } + else if constexpr (std::is_same_v<TResult, winrt::Microsoft::Management::Deployment::UninstallResult>) + { + return GetUninstallResult(executionStage, terminationHR, operationError, correlationData, rebootRequired); + } + } + +#define WINGET_GET_PROGRESS_STATE(_installState_, _uninstallState_) \ + if constexpr (std::is_same_v<TState, winrt::Microsoft::Management::Deployment::PackageInstallProgressState>) \ + { \ + progressState = TState::_installState_; \ + } \ + else if constexpr (std::is_same_v<TState, winrt::Microsoft::Management::Deployment::PackageUninstallProgressState>) \ + { \ + progressState = TState::_uninstallState_; \ + } + + template <typename TProgress, typename TState> + std::optional<TProgress> GetProgress( ReportType reportType, uint64_t current, uint64_t maximum, @@ -173,9 +213,9 @@ namespace winrt::Microsoft::Management::Deployment::implementation ::Workflow::ExecutionStage executionPhase) { bool reportProgress = false; - PackageInstallProgressState progressState = PackageInstallProgressState::Queued; + TState progressState = TState::Queued; double downloadProgress = 0; - double installProgress = 0; + double operationProgress = 0; uint64_t downloadBytesDownloaded = 0; uint64_t downloadBytesRequired = 0; switch (executionPhase) @@ -186,46 +226,49 @@ namespace winrt::Microsoft::Management::Deployment::implementation // We already reported queued progress up front. break; case ::Workflow::ExecutionStage::Download: - progressState = PackageInstallProgressState::Downloading; - if (reportType == ReportType::BeginProgress) + if constexpr (std::is_same_v<TProgress, winrt::Microsoft::Management::Deployment::InstallProgress>) { - reportProgress = true; - } - else if (progressType == ::AppInstaller::ProgressType::Bytes) - { - downloadBytesDownloaded = current; - downloadBytesRequired = maximum; - if (maximum > 0 && maximum >= current) + progressState = PackageInstallProgressState::Downloading; + if (reportType == ReportType::BeginProgress) { reportProgress = true; - downloadProgress = static_cast<double>(current) / static_cast<double>(maximum); + } + else if (progressType == ::AppInstaller::ProgressType::Bytes) + { + downloadBytesDownloaded = current; + downloadBytesRequired = maximum; + if (maximum > 0 && maximum >= current) + { + reportProgress = true; + downloadProgress = static_cast<double>(current) / static_cast<double>(maximum); + } } } break; case ::Workflow::ExecutionStage::PreExecution: - // Wait until installer starts to report Installing. + // Wait until installer starts to report operation. break; case ::Workflow::ExecutionStage::Execution: - progressState = PackageInstallProgressState::Installing; + WINGET_GET_PROGRESS_STATE(Installing, Uninstalling); downloadProgress = 1; if (reportType == ReportType::ExecutionPhaseUpdate) { - // Install is starting. Send progress so callers know the AsyncOperation can't be cancelled. + // Operation is starting. Send progress so callers know the AsyncOperation can't be cancelled. reportProgress = true; } else if (reportType == ReportType::EndProgress) { - // Install is "finished". May not have succeeded. + // Operation is "finished". May not have succeeded. reportProgress = true; - installProgress = 1; + operationProgress = 1; } else if (progressType == ::AppInstaller::ProgressType::Percent) { if (maximum > 0 && maximum >= current) { - // Install is progressing + // Operation is progressing reportProgress = true; - installProgress = static_cast<double>(current) / static_cast<double>(maximum); + operationProgress = static_cast<double>(current) / static_cast<double>(maximum); } } break; @@ -234,16 +277,24 @@ namespace winrt::Microsoft::Management::Deployment::implementation { // Send PostInstall progress when it switches to PostExecution phase. reportProgress = true; - progressState = PackageInstallProgressState::PostInstall; + WINGET_GET_PROGRESS_STATE(PostInstall, PostUninstall); downloadProgress = 1; - installProgress = 1; + operationProgress = 1; } break; } if (reportProgress) { - winrt::Microsoft::Management::Deployment::InstallProgress contextProgress{ progressState, downloadBytesDownloaded, downloadBytesRequired, downloadProgress, installProgress }; - return contextProgress; + if constexpr (std::is_same_v<TProgress, winrt::Microsoft::Management::Deployment::InstallProgress>) + { + TProgress progress{ progressState, downloadBytesDownloaded, downloadBytesRequired, downloadProgress, operationProgress }; + return progress; + } + else if constexpr (std::is_same_v<TProgress, winrt::Microsoft::Management::Deployment::UninstallProgress>) + { + TProgress progress{ progressState, operationProgress }; + return progress; + } } else { @@ -271,16 +322,10 @@ namespace winrt::Microsoft::Management::Deployment::implementation return packageVersionInfo; } - std::unique_ptr<COMContext> CreateContextFromInstallOptions( - winrt::Microsoft::Management::Deployment::CatalogPackage package, - winrt::Microsoft::Management::Deployment::InstallOptions options, - std::wstring callerProcessInfoString) + void PopulateContextFromInstallOptions( + ::AppInstaller::CLI::Execution::Context* context, + winrt::Microsoft::Management::Deployment::InstallOptions options) { - std::unique_ptr<COMContext> context = std::make_unique<COMContext>(); - hstring correlationData = (options) ? options.CorrelationData() : L""; - context->SetContextLoggers(correlationData, ::AppInstaller::Utility::ConvertToUTF8(callerProcessInfoString)); - - // Convert the options to arguments for the installer. if (options) { if (!options.LogOutputPath().empty()) @@ -335,13 +380,53 @@ namespace winrt::Microsoft::Management::Deployment::implementation } context->Add<Data::AllowedArchitectures>(std::move(allowedArchitectures)); } + + // Note: AdditionalPackageCatalogArguments is not needed during install since the manifest is already known so no additional calls to the source are needed. The property is deprecated. } + } - // If the version of the package is specified use that, otherwise use the default. - Microsoft::Management::Deployment::PackageVersionInfo packageVersionInfo = GetPackageVersionInfo(package, options); - AddPackageManifestToContext(packageVersionInfo, context.get()); + void PopulateContextFromUninstallOptions( + ::AppInstaller::CLI::Execution::Context* context, + winrt::Microsoft::Management::Deployment::UninstallOptions options) + { + if (options) + { + if (!options.LogOutputPath().empty()) + { + context->Args.AddArg(Execution::Args::Type::Log, ::AppInstaller::Utility::ConvertToUTF8(options.LogOutputPath())); + context->Args.AddArg(Execution::Args::Type::VerboseLogs); + } + + if (options.PackageUninstallMode() == PackageUninstallMode::Interactive) + { + context->Args.AddArg(Execution::Args::Type::Interactive); + } + else if (options.PackageUninstallMode() == PackageUninstallMode::Silent) + { + context->Args.AddArg(Execution::Args::Type::Silent); + } + } + } + + template <typename TOptions> + std::unique_ptr<COMContext> CreateContextFromOperationOptions( + TOptions options, + std::wstring callerProcessInfoString) + { + std::unique_ptr<COMContext> context = std::make_unique<COMContext>(); + hstring correlationData = (options) ? options.CorrelationData() : L""; + context->SetContextLoggers(correlationData, ::AppInstaller::Utility::ConvertToUTF8(callerProcessInfoString)); + + // Convert the options to arguments for the installer. + if constexpr (std::is_same_v<TOptions, winrt::Microsoft::Management::Deployment::InstallOptions>) + { + PopulateContextFromInstallOptions(context.get(), options); + } + else if constexpr (std::is_same_v<TOptions, winrt::Microsoft::Management::Deployment::UninstallOptions>) + { + PopulateContextFromUninstallOptions(context.get(), options); + } - // Note: AdditionalPackageCatalogArguments is not needed during install since the manifest is already known so no additional calls to the source are needed. The property is deprecated. return context; } @@ -354,7 +439,7 @@ namespace winrt::Microsoft::Management::Deployment::implementation // If the caller has passed in the catalog they expect the package to have come from, then only look for an install from that catalog. // Fail if they've used a catalog that doesn't have an Id. This can currently happen for Info objects that come from PackageCatalogReference objects for REST catalogs. THROW_HR_IF(APPINSTALLER_CLI_ERROR_INVALID_CL_ARGUMENTS, catalogInfo.Id().empty()); - auto searchItem = Execution::OrchestratorQueueItemFactory::CreateItemForInstall(std::wstring{ package.Id() }, std::wstring{ catalogInfo.Id() }, std::move(context)); + auto searchItem = Execution::OrchestratorQueueItemFactory::CreateItemForSearch(std::wstring{ package.Id() }, std::wstring{ catalogInfo.Id() }, std::move(context)); queueItem = Execution::ContextOrchestrator::Instance().GetQueueItem(searchItem->GetId()); return queueItem; } @@ -365,7 +450,7 @@ namespace winrt::Microsoft::Management::Deployment::implementation Microsoft::Management::Deployment::PackageVersionInfo installedVersionInfo = package.InstalledVersion(); if (installedVersionInfo) { - auto searchItem = Execution::OrchestratorQueueItemFactory::CreateItemForInstall(std::wstring{ package.Id() }, std::wstring{ installedVersionInfo.PackageCatalog().Info().Id() }, std::move(context)); + auto searchItem = Execution::OrchestratorQueueItemFactory::CreateItemForSearch(std::wstring{ package.Id() }, std::wstring{ installedVersionInfo.PackageCatalog().Info().Id() }, std::move(context)); queueItem = Execution::ContextOrchestrator::Instance().GetQueueItem(searchItem->GetId()); if (queueItem) { @@ -377,7 +462,7 @@ namespace winrt::Microsoft::Management::Deployment::implementation Microsoft::Management::Deployment::PackageVersionInfo defaultInstallVersionInfo = package.DefaultInstallVersion(); if (defaultInstallVersionInfo) { - auto searchItem = Execution::OrchestratorQueueItemFactory::CreateItemForInstall(std::wstring{ package.Id() }, std::wstring{ defaultInstallVersionInfo.PackageCatalog().Info().Id() }, std::move(context)); + auto searchItem = Execution::OrchestratorQueueItemFactory::CreateItemForSearch(std::wstring{ package.Id() }, std::wstring{ defaultInstallVersionInfo.PackageCatalog().Info().Id() }, std::move(context)); queueItem = Execution::ContextOrchestrator::Instance().GetQueueItem(searchItem->GetId()); if (queueItem) { @@ -388,7 +473,7 @@ namespace winrt::Microsoft::Management::Deployment::implementation // Finally check all catalogs in AvailableVersions. for (Microsoft::Management::Deployment::PackageVersionId versionId : package.AvailableVersions()) { - auto searchItem = Execution::OrchestratorQueueItemFactory::CreateItemForInstall(std::wstring{ package.Id() }, std::wstring{ package.GetPackageVersionInfo(versionId).PackageCatalog().Info().Id() }, std::move(context)); + auto searchItem = Execution::OrchestratorQueueItemFactory::CreateItemForSearch(std::wstring{ package.Id() }, std::wstring{ package.GetPackageVersionInfo(versionId).PackageCatalog().Info().Id() }, std::move(context)); queueItem = Execution::ContextOrchestrator::Instance().GetQueueItem(searchItem->GetId()); if (queueItem) { @@ -397,16 +482,71 @@ namespace winrt::Microsoft::Management::Deployment::implementation } return nullptr; } - winrt::Windows::Foundation::IAsyncOperationWithProgress<winrt::Microsoft::Management::Deployment::InstallResult, winrt::Microsoft::Management::Deployment::InstallProgress> GetInstallOperation( + + std::unique_ptr<Execution::OrchestratorQueueItem> CreateQueueItemForInstall( + std::unique_ptr<::AppInstaller::CLI::Execution::COMContext> comContext, + winrt::Microsoft::Management::Deployment::CatalogPackage package, + winrt::Microsoft::Management::Deployment::InstallOptions options, + bool isUpgrade) + { + // Add manifest and PackageVersion to context for install/upgrade. + // If the version of the package is specified use that, otherwise use the default. + Microsoft::Management::Deployment::PackageVersionInfo packageVersionInfo = GetPackageVersionInfo(package, options); + AddPackageManifestToContext(packageVersionInfo, comContext.get()); + + if (isUpgrade) + { + AppInstaller::Utility::VersionAndChannel installedVersion{ winrt::to_string(package.InstalledVersion().Version()), winrt::to_string(package.InstalledVersion().Channel()) }; + AppInstaller::Utility::VersionAndChannel upgradeVersion{ winrt::to_string(packageVersionInfo.Version()), winrt::to_string(packageVersionInfo.Channel()) }; + + // Perform upgrade version check + if (upgradeVersion.GetVersion().IsUnknown()) + { + if (!(options.AllowUpgradeToUnknownVersion() && + AppInstaller::Utility::ICUCaseInsensitiveEquals(installedVersion.GetChannel().ToString(), upgradeVersion.GetChannel().ToString()))) + { + THROW_HR(APPINSTALLER_CLI_ERROR_UPGRADE_VERSION_UNKNOWN); + } + } + else if (!installedVersion.IsUpdatedBy(upgradeVersion)) + { + THROW_HR(APPINSTALLER_CLI_ERROR_UPGRADE_VERSION_NOT_NEWER); + } + + // Set upgrade flag + comContext->SetFlags(AppInstaller::CLI::Execution::ContextFlag::InstallerExecutionUseUpdate); + // Add installed version + AddInstalledVersionToContext(package.InstalledVersion(), comContext.get()); + } + + return Execution::OrchestratorQueueItemFactory::CreateItemForInstall(std::wstring{ package.Id() }, std::wstring{ packageVersionInfo.PackageCatalog().Info().Id() }, std::move(comContext), isUpgrade); + } + + std::unique_ptr<Execution::OrchestratorQueueItem> CreateQueueItemForUninstall( + std::unique_ptr<::AppInstaller::CLI::Execution::COMContext> comContext, + winrt::Microsoft::Management::Deployment::CatalogPackage package) + { + // Add installed version + AddInstalledVersionToContext(package.InstalledVersion(), comContext.get()); + // Add Package which is used by RecordUninstall later for removing from tracking catalog of correlated available sources as best effort + winrt::Microsoft::Management::Deployment::implementation::CatalogPackage* catalogPackageImpl = get_self<winrt::Microsoft::Management::Deployment::implementation::CatalogPackage>(package); + std::shared_ptr<::AppInstaller::Repository::IPackage> internalPackage = catalogPackageImpl->GetRepositoryPackage(); + comContext->Add<AppInstaller::CLI::Execution::Data::Package>(internalPackage); + + return Execution::OrchestratorQueueItemFactory::CreateItemForUninstall(std::wstring{ package.Id() }, std::wstring{ package.InstalledVersion().PackageCatalog().Info().Id() }, std::move(comContext)); + } + + template <typename TResult, typename TProgress, typename TOptions, typename TProgressState> + winrt::Windows::Foundation::IAsyncOperationWithProgress<TResult, TProgress> GetPackageOperation( bool canCancelQueueItem, std::shared_ptr<Execution::OrchestratorQueueItem> queueItemParam, winrt::Microsoft::Management::Deployment::CatalogPackage package = nullptr, - winrt::Microsoft::Management::Deployment::InstallOptions options = nullptr, + TOptions options = nullptr, std::wstring callerProcessInfoString = {}, bool isUpgrade = false) { winrt::hresult terminationHR = S_OK; - uint32_t installerError = 0; + uint32_t operationError = 0; hstring correlationData = (options) ? options.CorrelationData() : L""; ::Workflow::ExecutionStage executionStage = ::Workflow::ExecutionStage::Initial; @@ -422,41 +562,29 @@ namespace winrt::Microsoft::Management::Deployment::implementation if (queueItem == nullptr) { - Microsoft::Management::Deployment::PackageVersionInfo packageVersionInfo = GetPackageVersionInfo(package, options); - std::unique_ptr<COMContext> comContext = CreateContextFromInstallOptions(package, options, callerProcessInfoString); + std::unique_ptr<COMContext> comContext = CreateContextFromOperationOptions<TOptions>(options, callerProcessInfoString); - if (isUpgrade) + if constexpr (std::is_same_v<TOptions, winrt::Microsoft::Management::Deployment::InstallOptions>) { - AppInstaller::Utility::VersionAndChannel installedVersion{ winrt::to_string(package.InstalledVersion().Version()), winrt::to_string(package.InstalledVersion().Channel()) }; - AppInstaller::Utility::VersionAndChannel upgradeVersion{ winrt::to_string(packageVersionInfo.Version()), winrt::to_string(packageVersionInfo.Channel()) }; - - // Perform upgrade version check - if (upgradeVersion.GetVersion().IsUnknown()) - { - if (!(options.AllowUpgradeToUnknownVersion() && - AppInstaller::Utility::ICUCaseInsensitiveEquals(installedVersion.GetChannel().ToString(), upgradeVersion.GetChannel().ToString()))) - { - co_return GetInstallResult(executionStage, APPINSTALLER_CLI_ERROR_UPGRADE_VERSION_UNKNOWN, 0, correlationData, false); - } - } - else if (!installedVersion.IsUpdatedBy(upgradeVersion)) - { - co_return GetInstallResult(executionStage, APPINSTALLER_CLI_ERROR_UPGRADE_VERSION_NOT_NEWER, 0, correlationData, false); - } - - // Set upgrade flag - comContext->SetFlags(AppInstaller::CLI::Execution::ContextFlag::InstallerExecutionUseUpdate); - // Add installed version - winrt::Microsoft::Management::Deployment::implementation::PackageVersionInfo* installedVersionInfoImpl = get_self<winrt::Microsoft::Management::Deployment::implementation::PackageVersionInfo>(package.InstalledVersion()); - std::shared_ptr<::AppInstaller::Repository::IPackageVersion> internalInstalledVersion = installedVersionInfoImpl->GetRepositoryPackageVersion(); - comContext->Add<AppInstaller::CLI::Execution::Data::InstalledPackageVersion>(internalInstalledVersion); + queueItem = CreateQueueItemForInstall(std::move(comContext), package, options, isUpgrade); + } + else if constexpr (std::is_same_v<TOptions, winrt::Microsoft::Management::Deployment::UninstallOptions>) + { + queueItem = CreateQueueItemForUninstall(std::move(comContext), package); } - queueItem = Execution::OrchestratorQueueItemFactory::CreateItemForInstall(std::wstring{ package.Id() }, std::wstring{ packageVersionInfo.PackageCatalog().Info().Id() }, std::move(comContext)); Execution::ContextOrchestrator::Instance().EnqueueAndRunItem(queueItem); - InstallProgress queuedProgress{ PackageInstallProgressState::Queued, 0, 0, 0 }; - report_progress(queuedProgress); + if constexpr (std::is_same_v<TProgress, winrt::Microsoft::Management::Deployment::PackageInstallProgressState>) + { + TProgress queuedProgress{ TProgressState::Queued, 0, 0, 0 }; + report_progress(queuedProgress); + } + else if constexpr (std::is_same_v<TProgress, winrt::Microsoft::Management::Deployment::PackageUninstallProgressState>) + { + TProgress queuedProgress{ TProgressState::Queued, 0}; + report_progress(queuedProgress); + } } { // correlation data is not passed in when retrieving an existing queue item, so get it from the existing context. @@ -465,18 +593,18 @@ namespace winrt::Microsoft::Management::Deployment::implementation wil::unique_event progressEvent{ wil::EventOptions::None }; - std::atomic<winrt::Microsoft::Management::Deployment::InstallProgress> installProgress; - queueItem->GetContext().AddProgressCallbackFunction([&installProgress, &progressEvent]( + std::atomic<TProgress> operationProgress; + queueItem->GetContext().AddProgressCallbackFunction([&operationProgress, &progressEvent]( ReportType reportType, uint64_t current, uint64_t maximum, ::AppInstaller::ProgressType progressType, ::Workflow::ExecutionStage executionPhase) { - std::optional<winrt::Microsoft::Management::Deployment::InstallProgress> installProgressOptional = GetProgress(reportType, current, maximum, progressType, executionPhase); - if (installProgressOptional.has_value()) + std::optional<TProgress> operationProgressOptional = GetProgress<TProgress, TProgressState>(reportType, current, maximum, progressType, executionPhase); + if (operationProgressOptional.has_value()) { - installProgress = installProgressOptional.value(); + operationProgress = operationProgressOptional.value(); progressEvent.SetEvent(); } return; @@ -519,7 +647,7 @@ namespace winrt::Microsoft::Management::Deployment::implementation // while the report_progress call is hung\in progress. // Duplicate progress events can be fired if another progress event comes from the ComContext to the callback after the listener // has been awaked, but before it has gotten the installProgress. - report_progress(installProgress); + report_progress(operationProgress); break; // operationEvents[1] was signaled, operation completed @@ -538,28 +666,29 @@ namespace winrt::Microsoft::Management::Deployment::implementation // The install command has finished, check for success/failure and how far it got. terminationHR = queueItem->GetContext().GetTerminationHR(); executionStage = queueItem->GetContext().GetExecutionStage(); - if (queueItem->GetContext().Contains(Data::InstallerReturnCode)) + if (queueItem->GetContext().Contains(Data::OperationReturnCode)) { - installerError = static_cast<uint32_t>(queueItem->GetContext().Get<Data::InstallerReturnCode>()); + operationError = static_cast<uint32_t>(queueItem->GetContext().Get<Data::OperationReturnCode>()); } } } WINGET_CATCH_STORE(terminationHR, APPINSTALLER_CLI_ERROR_COMMAND_FAILED); // TODO - RebootRequired not yet populated, msi arguments not returned from Execute. - co_return GetInstallResult(executionStage, terminationHR, installerError, correlationData, false); + co_return GetOperationResult<TResult>(executionStage, terminationHR, operationError, correlationData, false); } - winrt::Windows::Foundation::IAsyncOperationWithProgress<winrt::Microsoft::Management::Deployment::InstallResult, winrt::Microsoft::Management::Deployment::InstallProgress> GetEmptyAsynchronousResultForInstallOperation( + template <typename TResult, typename TProgress> + winrt::Windows::Foundation::IAsyncOperationWithProgress<TResult, TProgress> GetEmptyAsynchronousResultForOperation( HRESULT hr, hstring correlationData) { // If a function uses co_await or co_return (i.e. if it is a co_routine), it cannot use return directly. // This helper helps a function that is not a coroutine itself to return errors asynchronously. - co_return GetInstallResult(::Workflow::ExecutionStage::Initial, hr, 0, correlationData, false); + co_return GetOperationResult<TResult>(::Workflow::ExecutionStage::Initial, hr, 0, correlationData, false); } -#define WINGET_RETURN_INSTALL_RESULT_HR_IF(hr, boolVal) { if(boolVal) { return GetEmptyAsynchronousResultForInstallOperation(hr, correlationData); }} +#define WINGET_RETURN_INSTALL_RESULT_HR_IF(hr, boolVal) { if(boolVal) { return GetEmptyAsynchronousResultForOperation<Deployment::InstallResult, Deployment::InstallProgress>(hr, correlationData); }} #define WINGET_RETURN_INSTALL_RESULT_HR_IF_FAILED(hr) { WINGET_RETURN_INSTALL_RESULT_HR_IF(hr, FAILED(hr)) } winrt::Windows::Foundation::IAsyncOperationWithProgress<winrt::Microsoft::Management::Deployment::InstallResult, winrt::Microsoft::Management::Deployment::InstallProgress> PackageManager::InstallPackageAsync(winrt::Microsoft::Management::Deployment::CatalogPackage package, winrt::Microsoft::Management::Deployment::InstallOptions options) @@ -583,7 +712,8 @@ namespace winrt::Microsoft::Management::Deployment::implementation WINGET_CATCH_STORE(hr, APPINSTALLER_CLI_ERROR_COMMAND_FAILED); WINGET_RETURN_INSTALL_RESULT_HR_IF_FAILED(hr); - return GetInstallOperation(true /*canCancelQueueItem*/, nullptr /*queueItem*/, package, options, std::move(callerProcessInfoString)); + return GetPackageOperation<Deployment::InstallResult, Deployment::InstallProgress, Deployment::InstallOptions, Deployment::PackageInstallProgressState>( + true /*canCancelQueueItem*/, nullptr /*queueItem*/, package, options, std::move(callerProcessInfoString)); } winrt::Windows::Foundation::IAsyncOperationWithProgress<winrt::Microsoft::Management::Deployment::InstallResult, winrt::Microsoft::Management::Deployment::InstallProgress> PackageManager::UpgradePackageAsync(winrt::Microsoft::Management::Deployment::CatalogPackage package, winrt::Microsoft::Management::Deployment::InstallOptions options) @@ -609,7 +739,8 @@ namespace winrt::Microsoft::Management::Deployment::implementation WINGET_CATCH_STORE(hr, APPINSTALLER_CLI_ERROR_COMMAND_FAILED); WINGET_RETURN_INSTALL_RESULT_HR_IF_FAILED(hr); - return GetInstallOperation(true /*canCancelQueueItem*/, nullptr /*queueItem*/, package, options, std::move(callerProcessInfoString), true /* isUpgrade */); + return GetPackageOperation<Deployment::InstallResult, Deployment::InstallProgress, Deployment::InstallOptions, Deployment::PackageInstallProgressState>( + true /*canCancelQueueItem*/, nullptr /*queueItem*/, package, options, std::move(callerProcessInfoString), true /* isUpgrade */); } winrt::Windows::Foundation::IAsyncOperationWithProgress<winrt::Microsoft::Management::Deployment::InstallResult, winrt::Microsoft::Management::Deployment::InstallProgress> PackageManager::GetInstallProgress(winrt::Microsoft::Management::Deployment::CatalogPackage package, winrt::Microsoft::Management::Deployment::PackageCatalogInfo catalogInfo) @@ -634,7 +765,8 @@ namespace winrt::Microsoft::Management::Deployment::implementation // Get the queueItem synchronously. queueItem = GetExistingQueueItemForPackage(package, catalogInfo); - if (queueItem == nullptr) + if (queueItem == nullptr || + (queueItem->GetPackageOperationType() != PackageOperationType::Install && queueItem->GetPackageOperationType() != PackageOperationType::Upgrade)) { return nullptr; } @@ -642,7 +774,73 @@ namespace winrt::Microsoft::Management::Deployment::implementation WINGET_CATCH_STORE(hr, APPINSTALLER_CLI_ERROR_COMMAND_FAILED); WINGET_RETURN_INSTALL_RESULT_HR_IF_FAILED(hr); - return GetInstallOperation(canCancelQueueItem, std::move(queueItem)); + return GetPackageOperation<Deployment::InstallResult, Deployment::InstallProgress, Deployment::InstallOptions, Deployment::PackageInstallProgressState>( + canCancelQueueItem, std::move(queueItem)); + } + +#define WINGET_RETURN_UNINSTALL_RESULT_HR_IF(hr, boolVal) { if(boolVal) { return GetEmptyAsynchronousResultForOperation<Deployment::UninstallResult, Deployment::UninstallProgress>(hr, correlationData); }} +#define WINGET_RETURN_UNINSTALL_RESULT_HR_IF_FAILED(hr) { WINGET_RETURN_UNINSTALL_RESULT_HR_IF(hr, FAILED(hr)) } + + winrt::Windows::Foundation::IAsyncOperationWithProgress<winrt::Microsoft::Management::Deployment::UninstallResult, winrt::Microsoft::Management::Deployment::UninstallProgress> PackageManager::UninstallPackageAsync(winrt::Microsoft::Management::Deployment::CatalogPackage package, winrt::Microsoft::Management::Deployment::UninstallOptions options) + { + hstring correlationData = (options) ? options.CorrelationData() : L""; + + // options and catalog can both be null, package must be set. + WINGET_RETURN_UNINSTALL_RESULT_HR_IF(APPINSTALLER_CLI_ERROR_INVALID_CL_ARGUMENTS, !package); + // the package should have an installed version to be uninstalled. + WINGET_RETURN_UNINSTALL_RESULT_HR_IF(APPINSTALLER_CLI_ERROR_INVALID_CL_ARGUMENTS, !package.InstalledVersion()); + + HRESULT hr = S_OK; + std::wstring callerProcessInfoString; + try + { + // Check for permissions and get caller info for telemetry. + // This must be done before any co_awaits since it requires info from the rpc caller thread. + auto [hrGetCallerId, callerProcessId] = GetCallerProcessId(); + WINGET_RETURN_UNINSTALL_RESULT_HR_IF_FAILED(hrGetCallerId); + WINGET_RETURN_UNINSTALL_RESULT_HR_IF_FAILED(EnsureProcessHasCapability(Capability::PackageManagement, callerProcessId)); + callerProcessInfoString = TryGetCallerProcessInfo(callerProcessId); + } + WINGET_CATCH_STORE(hr, APPINSTALLER_CLI_ERROR_COMMAND_FAILED); + WINGET_RETURN_UNINSTALL_RESULT_HR_IF_FAILED(hr); + + return GetPackageOperation<Deployment::UninstallResult, Deployment::UninstallProgress, Deployment::UninstallOptions, Deployment::PackageUninstallProgressState>( + true /*canCancelQueueItem*/, nullptr /*queueItem*/, package, options, std::move(callerProcessInfoString)); + } + + winrt::Windows::Foundation::IAsyncOperationWithProgress<winrt::Microsoft::Management::Deployment::UninstallResult, winrt::Microsoft::Management::Deployment::UninstallProgress> PackageManager::GetUninstallProgress(winrt::Microsoft::Management::Deployment::CatalogPackage package, winrt::Microsoft::Management::Deployment::PackageCatalogInfo catalogInfo) + { + hstring correlationData; + WINGET_RETURN_UNINSTALL_RESULT_HR_IF(APPINSTALLER_CLI_ERROR_INVALID_CL_ARGUMENTS, !package); + + HRESULT hr = S_OK; + std::shared_ptr<Execution::OrchestratorQueueItem> queueItem = nullptr; + bool canCancelQueueItem = false; + try + { + // Check for permissions + // This must be done before any co_awaits since it requires info from the rpc caller thread. + auto [hrGetCallerId, callerProcessId] = GetCallerProcessId(); + WINGET_RETURN_UNINSTALL_RESULT_HR_IF_FAILED(hrGetCallerId); + canCancelQueueItem = SUCCEEDED(EnsureProcessHasCapability(Capability::PackageManagement, callerProcessId)); + if (!canCancelQueueItem) + { + WINGET_RETURN_UNINSTALL_RESULT_HR_IF_FAILED(EnsureProcessHasCapability(Capability::PackageQuery, callerProcessId)); + } + + // Get the queueItem synchronously. + queueItem = GetExistingQueueItemForPackage(package, catalogInfo); + if (queueItem == nullptr || + queueItem->GetPackageOperationType() != PackageOperationType::Uninstall) + { + return nullptr; + } + } + WINGET_CATCH_STORE(hr, APPINSTALLER_CLI_ERROR_COMMAND_FAILED); + WINGET_RETURN_UNINSTALL_RESULT_HR_IF_FAILED(hr); + + return GetPackageOperation<Deployment::UninstallResult, Deployment::UninstallProgress, Deployment::UninstallOptions, Deployment::PackageUninstallProgressState>( + canCancelQueueItem, std::move(queueItem)); } CoCreatableMicrosoftManagementDeploymentClass(PackageManager); diff --git a/src/Microsoft.Management.Deployment/PackageManager.h b/src/Microsoft.Management.Deployment/PackageManager.h @@ -27,6 +27,10 @@ namespace winrt::Microsoft::Management::Deployment::implementation // Contract 4.0 winrt::Windows::Foundation::IAsyncOperationWithProgress<winrt::Microsoft::Management::Deployment::InstallResult, winrt::Microsoft::Management::Deployment::InstallProgress> UpgradePackageAsync(winrt::Microsoft::Management::Deployment::CatalogPackage package, winrt::Microsoft::Management::Deployment::InstallOptions options); + winrt::Windows::Foundation::IAsyncOperationWithProgress<winrt::Microsoft::Management::Deployment::UninstallResult, winrt::Microsoft::Management::Deployment::UninstallProgress> + UninstallPackageAsync(winrt::Microsoft::Management::Deployment::CatalogPackage package, winrt::Microsoft::Management::Deployment::UninstallOptions options); + winrt::Windows::Foundation::IAsyncOperationWithProgress<winrt::Microsoft::Management::Deployment::UninstallResult, winrt::Microsoft::Management::Deployment::UninstallProgress> + GetUninstallProgress(winrt::Microsoft::Management::Deployment::CatalogPackage package, winrt::Microsoft::Management::Deployment::PackageCatalogInfo catalogInfo); }; } diff --git a/src/Microsoft.Management.Deployment/PackageManager.idl b/src/Microsoft.Management.Deployment/PackageManager.idl @@ -5,7 +5,7 @@ namespace Microsoft.Management.Deployment [contractversion(4)] apicontract WindowsPackageManagerContract{}; - /// State of the install. + /// State of the install [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] enum PackageInstallProgressState { @@ -87,6 +87,66 @@ namespace Microsoft.Management.Deployment } } + /// State of the uninstall + [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 4)] + enum PackageUninstallProgressState + { + /// The uninstall is queued but not yet active. Cancellation of the IAsyncOperationWithProgress in this + /// state will prevent the package from uninstalling. + Queued, + /// The uninstall is in progress. Cancellation of the IAsyncOperationWithProgress in this state will not + /// stop the installation or the post uninstall steps. + Uninstalling, + /// The uninstaller has completed and cleanup actions are in progress. Cancellation of the + /// IAsyncOperationWithProgress in this state will not stop cleanup or roll back the uninstall. + PostUninstall, + /// The operation has completed. + Finished, + }; + + /// Progress object for the uninstall + [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 4)] + struct UninstallProgress + { + /// State of the uninstall + PackageUninstallProgressState State; + /// Uninstall percentage if known. + Double UninstallationProgress; + }; + + /// Status of the uninstall call + /// Implementation Note: Errors mapped from AppInstallerErrors.h + [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 4)] + enum UninstallResultStatus + { + Ok, + BlockedByPolicy, + CatalogError, + InternalError, + InvalidOptions, + UninstallError, + ManifestError, + }; + + /// Result of the uninstall + [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 4)] + runtimeclass UninstallResult + { + /// Used by a caller to correlate the install with a caller's data. + String CorrelationData{ get; }; + /// Whether a restart is required to complete the install. + Boolean RebootRequired{ get; }; + + /// Batched error code, example APPINSTALLER_CLI_ERROR_SHELLEXEC_INSTALL_FAILED + UninstallResultStatus Status{ get; }; + /// The error code of the overall operation. + HRESULT ExtendedErrorCode{ get; }; + + /// The error code from the uninstall attempt. Only valid if the Status is UninstallError. + /// This value's meaning will require knowledge of the specific uninstaller or install technology. + UInt32 UninstallerErrorCode{ get; }; + } + /// IMPLEMENTATION NOTE: SourceOrigin from winget/RepositorySource.h /// Defines the origin of the package catalog details. [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] @@ -550,6 +610,39 @@ namespace Microsoft.Management.Deployment } } + [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 4)] + enum PackageUninstallMode + { + /// The default experience for the installer. Installer may show some UI. + Default, + /// Runs the installer in silent mode. This suppresses the installer's UI to the extent + /// possible (installer may still show some required UI). + Silent, + /// Runs the installer in interactive mode. + Interactive, + }; + + /// Options when uninstalling a package. + /// Intended to allow full compatibility with the "winget uninstall" command line interface. + [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 4)] + runtimeclass UninstallOptions + { + UninstallOptions(); + + /// Optionally specifies the version from the package to uninstall. If unspecified the version matching + /// CatalogPackage.GetLatestVersion() is used. + PackageVersionId PackageVersionId; + + /// Silent, Interactive, or Default + PackageUninstallMode PackageUninstallMode; + /// Directs the logging to a log file. If provided, the installer must have have write access to the file + String LogOutputPath; + + /// Used by a caller to correlate the install with a caller's data. + /// The string must be JSON encoded. + String CorrelationData; + } + [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] runtimeclass PackageManager { @@ -582,6 +675,12 @@ namespace Microsoft.Management.Deployment { /// Upgrade the specified package Windows.Foundation.IAsyncOperationWithProgress<InstallResult, InstallProgress> UpgradePackageAsync(CatalogPackage package, InstallOptions options); + + /// Uninstall the specified package + Windows.Foundation.IAsyncOperationWithProgress<UninstallResult, UninstallProgress> UninstallPackageAsync(CatalogPackage package, UninstallOptions options); + + /// Get uninstall progress + Windows.Foundation.IAsyncOperationWithProgress<UninstallResult, UninstallProgress> GetUninstallProgress(CatalogPackage package, PackageCatalogInfo catalogInfo); } } @@ -606,6 +705,10 @@ namespace Microsoft.Management.Deployment interface Windows.Foundation.Collections.IVectorView<InstallOptions>; interface Windows.Foundation.Collections.IVector<InstallResult>; interface Windows.Foundation.Collections.IVectorView<InstallResult>; + interface Windows.Foundation.Collections.IVector<UninstallOptions>; + interface Windows.Foundation.Collections.IVectorView<UninstallOptions>; + interface Windows.Foundation.Collections.IVector<UninstallResult>; + interface Windows.Foundation.Collections.IVectorView<UninstallResult>; interface Windows.Foundation.Collections.IVector<MatchResult>; interface Windows.Foundation.Collections.IVectorView<MatchResult>; interface Windows.Foundation.Collections.IVector<PackageMatchFilter>; diff --git a/src/Microsoft.Management.Deployment/UninstallOptions.cpp b/src/Microsoft.Management.Deployment/UninstallOptions.cpp @@ -0,0 +1,54 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#pragma warning( push ) +#pragma warning ( disable : 4467 6388) +// 6388 Allow CreateInstance. +#include <wil\cppwinrt_wrl.h> +// 4467 Allow use of uuid attribute for com object creation. +#include "UninstallOptions.h" +#pragma warning( pop ) +#include "UninstallOptions.g.cpp" +#include "Converters.h" +#include "Helpers.h" + +namespace winrt::Microsoft::Management::Deployment::implementation +{ + UninstallOptions::UninstallOptions() + { + } + winrt::Microsoft::Management::Deployment::PackageVersionId UninstallOptions::PackageVersionId() + { + return m_packageVersionId; + } + void UninstallOptions::PackageVersionId(winrt::Microsoft::Management::Deployment::PackageVersionId const& value) + { + m_packageVersionId = value; + } + winrt::Microsoft::Management::Deployment::PackageUninstallMode UninstallOptions::PackageUninstallMode() + { + return m_packageUninstallMode; + } + void UninstallOptions::PackageUninstallMode(winrt::Microsoft::Management::Deployment::PackageUninstallMode const& value) + { + m_packageUninstallMode = value; + } + hstring UninstallOptions::LogOutputPath() + { + return hstring(m_logOutputPath); + } + void UninstallOptions::LogOutputPath(hstring const& value) + { + m_logOutputPath = value; + } + hstring UninstallOptions::CorrelationData() + { + return hstring(m_correlationData); + } + void UninstallOptions::CorrelationData(hstring const& value) + { + m_correlationData = value; + } + + CoCreatableMicrosoftManagementDeploymentClass(UninstallOptions); +} diff --git a/src/Microsoft.Management.Deployment/UninstallOptions.h b/src/Microsoft.Management.Deployment/UninstallOptions.h @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "UninstallOptions.g.h" + +namespace winrt::Microsoft::Management::Deployment::implementation +{ +#if USE_PROD_CLSIDS + [uuid("E1D9A11E-9F85-4D87-9C17-2B93143ADB8D")] +#else + [uuid("AA2A5C04-1AD9-46C4-B74F-6B334AD7EB8C")] +#endif + struct UninstallOptions : UninstallOptionsT<UninstallOptions> + { + UninstallOptions(); + + winrt::Microsoft::Management::Deployment::PackageVersionId PackageVersionId(); + void PackageVersionId(winrt::Microsoft::Management::Deployment::PackageVersionId const& value); + winrt::Microsoft::Management::Deployment::PackageUninstallMode PackageUninstallMode(); + void PackageUninstallMode(winrt::Microsoft::Management::Deployment::PackageUninstallMode const& value); + hstring LogOutputPath(); + void LogOutputPath(hstring const& value); + hstring CorrelationData(); + void CorrelationData(hstring const& value); + +#if !defined(INCLUDE_ONLY_INTERFACE_METHODS) + private: + winrt::Microsoft::Management::Deployment::PackageVersionId m_packageVersionId{ nullptr }; + winrt::Microsoft::Management::Deployment::PackageUninstallMode m_packageUninstallMode = winrt::Microsoft::Management::Deployment::PackageUninstallMode::Default; + std::wstring m_logOutputPath = L""; + std::wstring m_correlationData = L""; +#endif + }; +} + +#if !defined(INCLUDE_ONLY_INTERFACE_METHODS) +namespace winrt::Microsoft::Management::Deployment::factory_implementation +{ + struct UninstallOptions : UninstallOptionsT<UninstallOptions, implementation::UninstallOptions> + { + }; +} +#endif diff --git a/src/Microsoft.Management.Deployment/UninstallResult.cpp b/src/Microsoft.Management.Deployment/UninstallResult.cpp @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "UninstallResult.h" +#include "UninstallResult.g.cpp" +#include <wil\cppwinrt_wrl.h> + +namespace winrt::Microsoft::Management::Deployment::implementation +{ + void UninstallResult::Initialize( + winrt::Microsoft::Management::Deployment::UninstallResultStatus status, + winrt::hresult extendedErrorCode, + uint32_t uninstallerErrorCode, + hstring const& correlationData, + bool rebootRequired) + { + m_status = status; + m_extendedErrorCode = extendedErrorCode; + m_uninstallerErrorCode = uninstallerErrorCode; + m_correlationData = correlationData; + m_rebootRequired = rebootRequired; + } + hstring UninstallResult::CorrelationData() + { + return hstring(m_correlationData); + } + bool UninstallResult::RebootRequired() + { + return m_rebootRequired; + } + winrt::Microsoft::Management::Deployment::UninstallResultStatus UninstallResult::Status() + { + return m_status; + } + winrt::hresult UninstallResult::ExtendedErrorCode() + { + return m_extendedErrorCode; + } + + uint32_t UninstallResult::UninstallerErrorCode() + { + return m_uninstallerErrorCode; + } +} diff --git a/src/Microsoft.Management.Deployment/UninstallResult.h b/src/Microsoft.Management.Deployment/UninstallResult.h @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "UninstallResult.g.h" + +namespace winrt::Microsoft::Management::Deployment::implementation +{ + struct UninstallResult : UninstallResultT<UninstallResult> + { + UninstallResult() = default; + +#if !defined(INCLUDE_ONLY_INTERFACE_METHODS) + void Initialize( + winrt::Microsoft::Management::Deployment::UninstallResultStatus status, + winrt::hresult extendedErrorCode, + uint32_t uninstallerErrorCode, + hstring const& correlationData, + bool rebootRequired); +#endif + + hstring CorrelationData(); + bool RebootRequired(); + winrt::Microsoft::Management::Deployment::UninstallResultStatus Status(); + winrt::hresult ExtendedErrorCode(); + uint32_t UninstallerErrorCode(); + +#if !defined(INCLUDE_ONLY_INTERFACE_METHODS) + private: + std::wstring m_correlationData = L""; + bool m_rebootRequired = false; + winrt::Microsoft::Management::Deployment::UninstallResultStatus m_status = winrt::Microsoft::Management::Deployment::UninstallResultStatus::Ok; + winrt::hresult m_extendedErrorCode = S_OK; + uint32_t m_uninstallerErrorCode = 0; +#endif + }; +} diff --git a/src/WindowsPackageManager/main.cpp b/src/WindowsPackageManager/main.cpp @@ -17,6 +17,7 @@ CoCreatableClassWrlCreatorMapInclude(PackageManager); CoCreatableClassWrlCreatorMapInclude(FindPackagesOptions); CoCreatableClassWrlCreatorMapInclude(CreateCompositePackageCatalogOptions); CoCreatableClassWrlCreatorMapInclude(InstallOptions); +CoCreatableClassWrlCreatorMapInclude(UninstallOptions); CoCreatableClassWrlCreatorMapInclude(PackageMatchFilter); extern "C"