diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt index 580cb0be4b..e34f3bc6de 100644 --- 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 index c2445f5371..4774f19379 100644 --- a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj +++ b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj @@ -241,7 +241,7 @@ - + @@ -293,7 +293,7 @@ - + diff --git a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters index 7201125793..bf7879a49e 100644 --- a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters +++ b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters @@ -158,7 +158,7 @@ Workflows - + Commands @@ -301,7 +301,7 @@ Source Files - + Commands diff --git a/src/AppInstallerCLICore/Commands/COMInstallCommand.cpp b/src/AppInstallerCLICore/Commands/COMCommand.cpp similarity index 76% rename from src/AppInstallerCLICore/Commands/COMInstallCommand.cpp rename to src/AppInstallerCLICore/Commands/COMCommand.cpp index 57a4fc9bc4..8eabe18dca 100644 --- a/src/AppInstallerCLICore/Commands/COMInstallCommand.cpp +++ b/src/AppInstallerCLICore/Commands/COMCommand.cpp @@ -1,9 +1,10 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" -#include "COMInstallCommand.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; @@ -30,4 +31,11 @@ namespace AppInstaller::CLI 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/COMInstallCommand.h b/src/AppInstallerCLICore/Commands/COMCommand.h similarity index 69% rename from src/AppInstallerCLICore/Commands/COMInstallCommand.h rename to src/AppInstallerCLICore/Commands/COMCommand.h index b2d50c410b..4ecedae490 100644 --- a/src/AppInstallerCLICore/Commands/COMInstallCommand.h +++ b/src/AppInstallerCLICore/Commands/COMCommand.h @@ -24,4 +24,14 @@ namespace AppInstaller::CLI 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/UninstallCommand.cpp b/src/AppInstallerCLICore/Commands/UninstallCommand.cpp index 18eddd30e7..1f6b3264f6 100644 --- 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 index 2cc38a8b42..8ac5baef8c 100644 --- 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 @@ -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(); - m_installingWriteableSource.AddPackageVersion(manifest, std::filesystem::path{ manifest.Id + '.' + manifest.Version }); + if (queueItem.IsApplicableForInstallingSource()) + { + const auto& manifest = queueItem.GetContext().Get(); + m_installingWriteableSource.AddPackageVersion(manifest, std::filesystem::path{ manifest.Id + '.' + manifest.Version }); + } } void ContextOrchestrator::RemoveItemManifestFromInstallingSource(const OrchestratorQueueItem& queueItem) { - const auto& manifest = queueItem.GetContext().Get(); - m_installingWriteableSource.RemovePackageVersion(manifest, std::filesystem::path{ manifest.Id + '.' + manifest.Version }); + if (queueItem.IsApplicableForInstallingSource()) + { + const auto& manifest = queueItem.GetContext().Get(); + 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 OrchestratorQueueItemFactory::CreateItemForInstall(std::wstring packageId, std::wstring sourceId, std::unique_ptr context) + std::unique_ptr OrchestratorQueueItemFactory::CreateItemForInstall(std::wstring packageId, std::wstring sourceId, std::unique_ptr context, bool isUpgrade) { - std::unique_ptr item = std::make_unique(OrchestratorQueueItemId(std::move(packageId), std::move(sourceId)), std::move(context)); + std::unique_ptr item = std::make_unique(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 OrchestratorQueueItemFactory::CreateItemForUninstall(std::wstring packageId, std::wstring sourceId, std::unique_ptr context) + { + std::unique_ptr item = std::make_unique(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 OrchestratorQueueItemFactory::CreateItemForSearch(std::wstring packageId, std::wstring sourceId, std::unique_ptr context) + { + std::unique_ptr item = std::make_unique(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 index 0aaadc0491..f4a2357a9c 100644 --- 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 context) : m_id(std::move(id)), m_context(std::move(context)) {} + OrchestratorQueueItem(OrchestratorQueueItemId id, std::unique_ptr 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> m_commands; bool m_isOnFirstCommand = true; OrchestratorQueue* m_currentQueue = nullptr; + PackageOperationType m_operationType = PackageOperationType::None; }; struct OrchestratorQueueItemFactory { - static std::unique_ptr CreateItemForInstall(std::wstring packageId, std::wstring sourceId, std::unique_ptr context); + // Create queue item for install/upgrade + static std::unique_ptr CreateItemForInstall(std::wstring packageId, std::wstring sourceId, std::unique_ptr context, bool isUpgrade); + // Create queue item for uninstall + static std::unique_ptr CreateItemForUninstall(std::wstring packageId, std::wstring sourceId, std::unique_ptr context); + // Create queue item for finding existing entry from the orchestrator queue + static std::unique_ptr CreateItemForSearch(std::wstring packageId, std::wstring sourceId, std::unique_ptr context); }; struct ContextOrchestrator diff --git a/src/AppInstallerCLICore/ExecutionContextData.h b/src/AppInstallerCLICore/ExecutionContextData.h index d37abc7b12..9e26d2b191 100644 --- 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 + struct DataMapping { using value_t = DWORD; }; diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp index ebd5154f42..093d58e485 100644 --- 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(re.GetErrorCode()); + context.Add(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(); + DWORD installResult = context.Get(); const auto& additionalSuccessCodes = context.Get()->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 index be0fd4e608..38c4021cff 100644 --- a/src/AppInstallerCLICore/Workflows/MsiInstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/MsiInstallFlow.cpp @@ -49,7 +49,7 @@ namespace AppInstaller::CLI::Workflow } else { - context.Add(installResult.value()); + context.Add(installResult.value()); } } } diff --git a/src/AppInstallerCLICore/Workflows/MsiInstallFlow.h b/src/AppInstallerCLICore/Workflows/MsiInstallFlow.h index 7aac116148..4516c8b00a 100644 --- 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 index 83ca60a863..c0849a6825 100644 --- a/src/AppInstallerCLICore/Workflows/ShellExecuteInstallerHandler.cpp +++ b/src/AppInstallerCLICore/Workflows/ShellExecuteInstallerHandler.cpp @@ -202,7 +202,7 @@ namespace AppInstaller::CLI::Workflow } else { - context.Add(installResult.value()); + context.Add(installResult.value()); } } @@ -253,6 +253,7 @@ namespace AppInstaller::CLI::Workflow "UninstallString", uninstallResult.value()); + context.Add(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(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 index ff5721e3d4..18c007dfe7 100644 --- 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 index e1bdca3140..708753cd50 100644 --- 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(); @@ -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(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 index 6db0954cf9..21a12650f0 100644 --- 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 index b0a3134c61..58a2ff098d 100644 --- a/src/AppInstallerCLIPackage/Package.appxmanifest +++ b/src/AppInstallerCLIPackage/Package.appxmanifest @@ -48,6 +48,8 @@ + + diff --git a/src/AppInstallerCLITests/WorkFlow.cpp b/src/AppInstallerCLITests/WorkFlow.cpp index 8edc977151..b2525934ad 100644 --- a/src/AppInstallerCLITests/WorkFlow.cpp +++ b/src/AppInstallerCLITests/WorkFlow.cpp @@ -551,7 +551,7 @@ void OverrideForDirectMsi(TestContext& context) file << context.Get(); file.close(); - context.Add(0); + context.Add(0); } }); } diff --git a/src/Microsoft.Management.Deployment.Client/Client.UninstallOptions.h b/src/Microsoft.Management.Deployment.Client/Client.UninstallOptions.h new file mode 100644 index 0000000000..f65e03ed3a --- /dev/null +++ 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 + { + auto ActivateInstance() const + { + return winrt::create_instance(__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 index fc9db158eb..1994278418 100644 --- 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 @@ + @@ -146,6 +147,7 @@ + diff --git a/src/Microsoft.Management.Deployment.Client/PackageManager.cpp b/src/Microsoft.Management.Deployment.Client/PackageManager.cpp index 53a1a21ff0..7200a88eef 100644 --- 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 PackageManager::UninstallPackageAsync(winrt::Microsoft::Management::Deployment::CatalogPackage package, winrt::Microsoft::Management::Deployment::UninstallOptions) + { + throw hresult_not_implemented(); + } + winrt::Windows::Foundation::IAsyncOperationWithProgress 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 new file mode 100644 index 0000000000..61b99e3e76 --- /dev/null +++ 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 +#include +#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 index 69f5aa38cf..56b1c4404e 100644 --- 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 index a245b58e84..87a51fe254 100644 --- 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 index 29478b22f8..c45fbd3a1e 100644 --- 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 index bda1a927a6..1d81c32ee2 100644 --- 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 GetWindowsSystemProcessorArchitecture(::AppInstaller::Utility::Architecture architecture); + +#define WINGET_GET_OPERATION_RESULT_STATUS(_installResultStatus_, _uninstallResultStatus_) \ + if constexpr (std::is_same_v) \ + { \ + resultStatus = TStatus::_installResultStatus_; \ + } \ + else if constexpr (std::is_same_v) \ + { \ + resultStatus = TStatus::_uninstallResultStatus_; \ + } \ + + template + 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 index 4010cbcbf0..ee09e6045f 100644 --- 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() = 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 index 675325591d..1345c5a64f 100644 --- a/src/Microsoft.Management.Deployment/Microsoft.Management.Deployment.vcxproj +++ b/src/Microsoft.Management.Deployment/Microsoft.Management.Deployment.vcxproj @@ -154,6 +154,8 @@ + + @@ -177,6 +179,8 @@ Create + + diff --git a/src/Microsoft.Management.Deployment/PackageManager.cpp b/src/Microsoft.Management.Deployment/PackageManager.cpp index 7bb07ac71e..a4ec730dea 100644 --- a/src/Microsoft.Management.Deployment/PackageManager.cpp +++ b/src/Microsoft.Management.Deployment/PackageManager.cpp @@ -9,7 +9,7 @@ #include "Workflows/WorkflowBase.h" #include #include -#include "Commands/COMInstallCommand.h" +#include "Commands/COMCommand.h" #include #include #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(installedVersionInfo); + std::shared_ptr<::AppInstaller::Repository::IPackageVersion> internalInstalledVersion = installedVersionInfoImpl->GetRepositoryPackageVersion(); + context->Add(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(executionStage, terminationHR); auto installResult = winrt::make_self>(); installResult->Initialize(installResultStatus, terminationHR, installerError, correlationData, rebootRequired); return *installResult; } - std::optional 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(executionStage, terminationHR); + auto uninstallResult = winrt::make_self>(); + uninstallResult->Initialize(uninstallResultStatus, terminationHR, uninstallerError, correlationData, rebootRequired); + return *uninstallResult; + } + + template + TResult GetOperationResult(::Workflow::ExecutionStage executionStage, winrt::hresult terminationHR, uint32_t operationError, winrt::hstring correlationData, bool rebootRequired) + { + if constexpr (std::is_same_v) + { + return GetInstallResult(executionStage, terminationHR, operationError, correlationData, rebootRequired); + } + else if constexpr (std::is_same_v) + { + return GetUninstallResult(executionStage, terminationHR, operationError, correlationData, rebootRequired); + } + } + +#define WINGET_GET_PROGRESS_STATE(_installState_, _uninstallState_) \ + if constexpr (std::is_same_v) \ + { \ + progressState = TState::_installState_; \ + } \ + else if constexpr (std::is_same_v) \ + { \ + progressState = TState::_uninstallState_; \ + } + + template + std::optional 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) { - 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(current) / static_cast(maximum); + } + else if (progressType == ::AppInstaller::ProgressType::Bytes) + { + downloadBytesDownloaded = current; + downloadBytesRequired = maximum; + if (maximum > 0 && maximum >= current) + { + reportProgress = true; + downloadProgress = static_cast(current) / static_cast(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(current) / static_cast(maximum); + operationProgress = static_cast(current) / static_cast(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 progress{ progressState, downloadBytesDownloaded, downloadBytesRequired, downloadProgress, operationProgress }; + return progress; + } + else if constexpr (std::is_same_v) + { + TProgress progress{ progressState, operationProgress }; + return progress; + } } else { @@ -271,16 +322,10 @@ namespace winrt::Microsoft::Management::Deployment::implementation return packageVersionInfo; } - std::unique_ptr 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 context = std::make_unique(); - 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(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 + std::unique_ptr CreateContextFromOperationOptions( + TOptions options, + std::wstring callerProcessInfoString) + { + std::unique_ptr context = std::make_unique(); + 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) + { + PopulateContextFromInstallOptions(context.get(), options); + } + else if constexpr (std::is_same_v) + { + 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 GetInstallOperation( + + std::unique_ptr 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 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(package); + std::shared_ptr<::AppInstaller::Repository::IPackage> internalPackage = catalogPackageImpl->GetRepositoryPackage(); + comContext->Add(internalPackage); + + return Execution::OrchestratorQueueItemFactory::CreateItemForUninstall(std::wstring{ package.Id() }, std::wstring{ package.InstalledVersion().PackageCatalog().Info().Id() }, std::move(comContext)); + } + + template + winrt::Windows::Foundation::IAsyncOperationWithProgress GetPackageOperation( bool canCancelQueueItem, std::shared_ptr 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 = CreateContextFromInstallOptions(package, options, callerProcessInfoString); + std::unique_ptr comContext = CreateContextFromOperationOptions(options, callerProcessInfoString); - if (isUpgrade) + if constexpr (std::is_same_v) { - 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(package.InstalledVersion()); - std::shared_ptr<::AppInstaller::Repository::IPackageVersion> internalInstalledVersion = installedVersionInfoImpl->GetRepositoryPackageVersion(); - comContext->Add(internalInstalledVersion); + queueItem = CreateQueueItemForInstall(std::move(comContext), package, options, isUpgrade); + } + else if constexpr (std::is_same_v) + { + 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 queuedProgress{ TProgressState::Queued, 0, 0, 0 }; + report_progress(queuedProgress); + } + else if constexpr (std::is_same_v) + { + 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 installProgress; - queueItem->GetContext().AddProgressCallbackFunction([&installProgress, &progressEvent]( + std::atomic operationProgress; + queueItem->GetContext().AddProgressCallbackFunction([&operationProgress, &progressEvent]( ReportType reportType, uint64_t current, uint64_t maximum, ::AppInstaller::ProgressType progressType, ::Workflow::ExecutionStage executionPhase) { - std::optional installProgressOptional = GetProgress(reportType, current, maximum, progressType, executionPhase); - if (installProgressOptional.has_value()) + std::optional operationProgressOptional = GetProgress(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(queueItem->GetContext().Get()); + operationError = static_cast(queueItem->GetContext().Get()); } } } 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(executionStage, terminationHR, operationError, correlationData, false); } - winrt::Windows::Foundation::IAsyncOperationWithProgress GetEmptyAsynchronousResultForInstallOperation( + template + winrt::Windows::Foundation::IAsyncOperationWithProgress 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(::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(hr, correlationData); }} #define WINGET_RETURN_INSTALL_RESULT_HR_IF_FAILED(hr) { WINGET_RETURN_INSTALL_RESULT_HR_IF(hr, FAILED(hr)) } winrt::Windows::Foundation::IAsyncOperationWithProgress 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( + true /*canCancelQueueItem*/, nullptr /*queueItem*/, package, options, std::move(callerProcessInfoString)); } winrt::Windows::Foundation::IAsyncOperationWithProgress 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( + true /*canCancelQueueItem*/, nullptr /*queueItem*/, package, options, std::move(callerProcessInfoString), true /* isUpgrade */); } winrt::Windows::Foundation::IAsyncOperationWithProgress 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( + canCancelQueueItem, std::move(queueItem)); + } + +#define WINGET_RETURN_UNINSTALL_RESULT_HR_IF(hr, boolVal) { if(boolVal) { return GetEmptyAsynchronousResultForOperation(hr, correlationData); }} +#define WINGET_RETURN_UNINSTALL_RESULT_HR_IF_FAILED(hr) { WINGET_RETURN_UNINSTALL_RESULT_HR_IF(hr, FAILED(hr)) } + + winrt::Windows::Foundation::IAsyncOperationWithProgress 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( + true /*canCancelQueueItem*/, nullptr /*queueItem*/, package, options, std::move(callerProcessInfoString)); + } + + winrt::Windows::Foundation::IAsyncOperationWithProgress 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 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( + canCancelQueueItem, std::move(queueItem)); } CoCreatableMicrosoftManagementDeploymentClass(PackageManager); diff --git a/src/Microsoft.Management.Deployment/PackageManager.h b/src/Microsoft.Management.Deployment/PackageManager.h index 879fb0a7a5..2d3086c7c9 100644 --- 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 UpgradePackageAsync(winrt::Microsoft::Management::Deployment::CatalogPackage package, winrt::Microsoft::Management::Deployment::InstallOptions options); + winrt::Windows::Foundation::IAsyncOperationWithProgress + UninstallPackageAsync(winrt::Microsoft::Management::Deployment::CatalogPackage package, winrt::Microsoft::Management::Deployment::UninstallOptions options); + winrt::Windows::Foundation::IAsyncOperationWithProgress + 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 index c9888a020e..f39b28accd 100644 --- 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 UpgradePackageAsync(CatalogPackage package, InstallOptions options); + + /// Uninstall the specified package + Windows.Foundation.IAsyncOperationWithProgress UninstallPackageAsync(CatalogPackage package, UninstallOptions options); + + /// Get uninstall progress + Windows.Foundation.IAsyncOperationWithProgress GetUninstallProgress(CatalogPackage package, PackageCatalogInfo catalogInfo); } } @@ -606,6 +705,10 @@ namespace Microsoft.Management.Deployment interface Windows.Foundation.Collections.IVectorView; interface Windows.Foundation.Collections.IVector; interface Windows.Foundation.Collections.IVectorView; + interface Windows.Foundation.Collections.IVector; + interface Windows.Foundation.Collections.IVectorView; + interface Windows.Foundation.Collections.IVector; + interface Windows.Foundation.Collections.IVectorView; interface Windows.Foundation.Collections.IVector; interface Windows.Foundation.Collections.IVectorView; interface Windows.Foundation.Collections.IVector; diff --git a/src/Microsoft.Management.Deployment/UninstallOptions.cpp b/src/Microsoft.Management.Deployment/UninstallOptions.cpp new file mode 100644 index 0000000000..6bf3912265 --- /dev/null +++ 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 +// 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 new file mode 100644 index 0000000000..d56ecae072 --- /dev/null +++ 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(); + + 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 + { + }; +} +#endif diff --git a/src/Microsoft.Management.Deployment/UninstallResult.cpp b/src/Microsoft.Management.Deployment/UninstallResult.cpp new file mode 100644 index 0000000000..cc0830646e --- /dev/null +++ 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 + +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 new file mode 100644 index 0000000000..2b3ac1a780 --- /dev/null +++ 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() = 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 index 791b0d829c..972176038a 100644 --- 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"