Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add uninstall functionality to Com api #1909

Merged
merged 15 commits into from
Mar 2, 2022
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