Skip to content

Commit

Permalink
Add uninstall functionality to Com api (microsoft#1909)
Browse files Browse the repository at this point in the history
  • Loading branch information
yao-msft authored Mar 2, 2022
1 parent 739aefe commit 79c8c11
Show file tree
Hide file tree
Showing 36 changed files with 888 additions and 209 deletions.
5 changes: 5 additions & 0 deletions .github/actions/spelling/expect.txt
Original file line number Diff line number Diff line change
Expand Up @@ -363,9 +363,14 @@ thiscouldbeapc
threehundred
Tlg
tombstoned
TOptions
tpl
TProgress
transitioning
TResult
trimstart
TState
TStatus
UCase
ucasemap
UChars
Expand Down
4 changes: 2 additions & 2 deletions src/AppInstallerCLICore/AppInstallerCLICore.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -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" />
Expand Down Expand Up @@ -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" />
Expand Down
4 changes: 2 additions & 2 deletions src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -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">
Expand Down Expand Up @@ -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">
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
}
11 changes: 1 addition & 10 deletions src/AppInstallerCLICore/Commands/UninstallCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
}
54 changes: 44 additions & 10 deletions src/AppInstallerCLICore/ContextOrchestrator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,17 @@
#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>

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)
Expand All @@ -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()
Expand All @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;
}
}
21 changes: 19 additions & 2 deletions src/AppInstallerCLICore/ContextOrchestrator.h
Original file line number Diff line number Diff line change
Expand Up @@ -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; }
Expand All @@ -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;
Expand All @@ -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
Expand Down
4 changes: 2 additions & 2 deletions src/AppInstallerCLICore/ExecutionContextData.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ namespace AppInstaller::CLI::Execution
InstallerPath,
LogPath,
InstallerArgs,
InstallerReturnCode,
OperationReturnCode,
CompletionData,
InstalledPackageVersion,
UninstallString,
Expand Down Expand Up @@ -130,7 +130,7 @@ namespace AppInstaller::CLI::Execution
};

template <>
struct DataMapping<Data::InstallerReturnCode>
struct DataMapping<Data::OperationReturnCode>
{
using value_t = DWORD;
};
Expand Down
4 changes: 2 additions & 2 deletions src/AppInstallerCLICore/Workflows/InstallFlow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -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()))
{
Expand Down
2 changes: 1 addition & 1 deletion src/AppInstallerCLICore/Workflows/MsiInstallFlow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ namespace AppInstaller::CLI::Workflow
}
else
{
context.Add<Execution::Data::InstallerReturnCode>(installResult.value());
context.Add<Execution::Data::OperationReturnCode>(installResult.value());
}
}
}
2 changes: 1 addition & 1 deletion src/AppInstallerCLICore/Workflows/MsiInstallFlow.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ namespace AppInstaller::CLI::Workflow
}
else
{
context.Add<Execution::Data::InstallerReturnCode>(installResult.value());
context.Add<Execution::Data::OperationReturnCode>(installResult.value());
}
}

Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading

0 comments on commit 79c8c11

Please sign in to comment.