From 3d60cece22103f3e9bfff1ca67afd5d4a7009421 Mon Sep 17 00:00:00 2001 From: Ryan <69221034+ryfu-msft@users.noreply.github.com> Date: Wed, 11 Oct 2023 16:51:52 -0400 Subject: [PATCH] Invoke ShellExecute on dism.exe for enabling Windows Features (#3659) --- .github/actions/spelling/allow.txt | 1 + src/AppInstallerCLICore/Resources.h | 3 +- .../Workflows/DependenciesFlow.cpp | 132 +++++---- .../Workflows/MsiInstallFlow.cpp | 2 +- .../ShellExecuteInstallerHandler.cpp | 114 +++++++- .../Workflows/ShellExecuteInstallerHandler.h | 14 + src/AppInstallerCLIE2ETests/InstallCommand.cs | 6 +- .../Shared/Strings/en-us/winget.resw | 13 +- src/AppInstallerCLITests/TestHooks.h | 106 ++------ src/AppInstallerCLITests/WindowsFeature.cpp | 47 +--- .../AppInstallerCommonCore.vcxproj | 2 - .../AppInstallerCommonCore.vcxproj.filters | 6 - .../Public/winget/WindowsFeature.h | 210 -------------- src/AppInstallerCommonCore/WindowsFeature.cpp | 256 ------------------ 14 files changed, 236 insertions(+), 676 deletions(-) delete mode 100644 src/AppInstallerCommonCore/Public/winget/WindowsFeature.h delete mode 100644 src/AppInstallerCommonCore/WindowsFeature.cpp diff --git a/.github/actions/spelling/allow.txt b/.github/actions/spelling/allow.txt index 678275cdb4..620d4eb9c7 100644 --- a/.github/actions/spelling/allow.txt +++ b/.github/actions/spelling/allow.txt @@ -129,6 +129,7 @@ deserializing dest devblogs differentpath +DISMAPI DIRECTONLY distro dll diff --git a/src/AppInstallerCLICore/Resources.h b/src/AppInstallerCLICore/Resources.h index 8beb0be175..a55a5ef52b 100644 --- a/src/AppInstallerCLICore/Resources.h +++ b/src/AppInstallerCLICore/Resources.h @@ -157,6 +157,7 @@ namespace AppInstaller::CLI::Resource WINGET_DEFINE_RESOURCE_STRINGID(DownloadDirectoryArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(Downloading); WINGET_DEFINE_RESOURCE_STRINGID(EnableAdminSettingFailed); + WINGET_DEFINE_RESOURCE_STRINGID(EnableWindowsFeaturesSuccess); WINGET_DEFINE_RESOURCE_STRINGID(EnablingWindowsFeature); WINGET_DEFINE_RESOURCE_STRINGID(ErrorCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(ErrorCommandShortDescription); @@ -226,7 +227,7 @@ namespace AppInstaller::CLI::Resource WINGET_DEFINE_RESOURCE_STRINGID(IncludeUnknownArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(IncludeUnknownInListArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(IncompatibleArgumentsProvided); - WINGET_DEFINE_RESOURCE_STRINGID(InstallationAbandoned); + WINGET_DEFINE_RESOURCE_STRINGID(InstallAbandoned); WINGET_DEFINE_RESOURCE_STRINGID(InstallationDisclaimer1); WINGET_DEFINE_RESOURCE_STRINGID(InstallationDisclaimer2); WINGET_DEFINE_RESOURCE_STRINGID(InstallationDisclaimerMSStore); diff --git a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp index 2b629a4d78..cdfc1bd7a3 100644 --- a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp @@ -6,12 +6,11 @@ #include "ManifestComparator.h" #include "InstallFlow.h" #include "winget\DependenciesGraph.h" -#include "winget\WindowsFeature.h" #include "DependencyNodeProcessor.h" +#include "ShellExecuteInstallerHandler.h" using namespace AppInstaller::Repository; using namespace AppInstaller::Manifest; -using namespace AppInstaller::WindowsFeature; namespace AppInstaller::CLI::Workflow { @@ -136,82 +135,91 @@ namespace AppInstaller::CLI::Workflow const auto& rootDependencies = context.Get()->Dependencies; - if (rootDependencies.Empty()) + if (rootDependencies.Empty() || !rootDependencies.HasAnyOf(DependencyType::WindowsFeature)) { return; } - if (rootDependencies.HasAnyOf(DependencyType::WindowsFeature)) + context << Workflow::EnsureRunningAsAdmin; + + if (context.IsTerminated()) { - context << Workflow::EnsureRunningAsAdmin; - if (context.IsTerminated()) + return; + } + + bool isCancelled = false; + bool enableFeaturesFailed = false; + bool rebootRequired = false; + bool force = context.Args.Contains(Execution::Args::Type::Force); + + rootDependencies.ApplyToType(DependencyType::WindowsFeature, [&context, &isCancelled, &enableFeaturesFailed, &force, &rebootRequired](Dependency dependency) { - return; - } + if (enableFeaturesFailed && !force || isCancelled) + { + return; + } - HRESULT hr = S_OK; - std::shared_ptr dismHelper = std::make_shared(); + auto featureName = dependency.Id(); - bool force = context.Args.Contains(Execution::Args::Type::Force); - bool rebootRequired = false; + auto featureContextPtr = context.CreateSubContext(); + Execution::Context& featureContext = *featureContextPtr; + auto previousThreadGlobals = featureContext.SetForCurrentThread(); - rootDependencies.ApplyToType(DependencyType::WindowsFeature, [&context, &hr, &dismHelper, &force, &rebootRequired](Dependency dependency) + featureContext << Workflow::ShellExecuteEnableWindowsFeature(featureName); + + if (featureContext.IsTerminated()) { - if (SUCCEEDED(hr) || force) - { - auto featureName = dependency.Id(); - AICLI_LOG(Core, Verbose, << "Processing Windows Feature dependency [" << featureName << "]"); - WindowsFeature::WindowsFeature windowsFeature = dismHelper->GetWindowsFeature(featureName); + isCancelled = true; + return; + } - if (windowsFeature.DoesExist()) - { - if (!windowsFeature.IsEnabled()) - { - Utility::LocIndString featureDisplayName = windowsFeature.GetDisplayName(); - Utility::LocIndView locIndFeatureName{ featureName }; - - context.Reporter.Info() << Resource::String::EnablingWindowsFeature(featureDisplayName, locIndFeatureName) << std::endl; - - AICLI_LOG(Core, Info, << "Enabling Windows Feature [" << featureName << "] returned with HRESULT: " << hr); - auto enableFeatureFunction = [&](IProgressCallback& progress)->HRESULT { return windowsFeature.Enable(progress); }; - hr = context.Reporter.ExecuteWithProgress(enableFeatureFunction, true); - - if (FAILED(hr)) - { - AICLI_LOG(Core, Error, << "Failed to enable Windows Feature " << featureDisplayName << " [" << locIndFeatureName << "] with exit code: " << hr); - context.Reporter.Warn() << Resource::String::FailedToEnableWindowsFeature(featureDisplayName, locIndFeatureName) << std::endl - << GetUserPresentableMessage(hr) << std::endl; - } - - if (hr == ERROR_SUCCESS_REBOOT_REQUIRED || windowsFeature.GetRestartRequiredStatus() == DismRestartType::DismRestartRequired) - { - rebootRequired = true; - } - } - } - else - { - // Note: If a feature is not found, continue enabling the rest of the dependencies but block immediately after unless force arg is present. - AICLI_LOG(Core, Info, << "Windows Feature [" << featureName << "] does not exist"); - hr = APPINSTALLER_CLI_ERROR_INSTALL_MISSING_DEPENDENCY; - context.Reporter.Warn() << Resource::String::WindowsFeatureNotFound(Utility::LocIndView{ featureName }) << std::endl; - } - } - }); + Utility::LocIndView locIndFeatureName{ featureName }; + DWORD result = featureContext.Get(); - if (FAILED(hr)) - { - if (force) + if (result == ERROR_SUCCESS) + { + AICLI_LOG(Core, Info, << "Successfully enabled [" << featureName << "]"); + } + else if (result == 0x800f080c) // DISMAPI_E_UNKNOWN_FEATURE + { + AICLI_LOG(Core, Warning, << "Windows Feature [" << featureName << "] does not exist"); + enableFeaturesFailed = true; + featureContext.Reporter.Warn() << Resource::String::WindowsFeatureNotFound(locIndFeatureName) << std::endl; + } + else if (result == ERROR_SUCCESS_REBOOT_REQUIRED) { - context.Reporter.Warn() << Resource::String::FailedToEnableWindowsFeatureOverridden << std::endl; + AICLI_LOG(Core, Info, << "Reboot required for [" << featureName << "]"); + rebootRequired = true; } else { - context.Reporter.Error() << Resource::String::FailedToEnableWindowsFeatureOverrideRequired << std::endl; - AICLI_TERMINATE_CONTEXT(hr); + AICLI_LOG(Core, Error, << "Failed to enable Windows Feature [" << featureName << "] with exit code: " << result); + enableFeaturesFailed = true; + featureContext.Reporter.Warn() << Resource::String::FailedToEnableWindowsFeature(locIndFeatureName, result) << std::endl; } + }); + + if (isCancelled) + { + context.Reporter.Warn() << Resource::String::InstallAbandoned << std::endl; + AICLI_TERMINATE_CONTEXT(E_ABORT); + } + + if (enableFeaturesFailed) + { + if (force) + { + context.Reporter.Warn() << Resource::String::FailedToEnableWindowsFeatureOverridden << std::endl; } - else if (rebootRequired) + else + { + context.Reporter.Error() << Resource::String::FailedToEnableWindowsFeatureOverrideRequired << std::endl; + AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INSTALL_DEPENDENCIES); + } + } + else + { + if (rebootRequired) { if (force) { @@ -223,6 +231,10 @@ namespace AppInstaller::CLI::Workflow AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INSTALL_REBOOT_REQUIRED_TO_INSTALL); } } + else + { + context.Reporter.Info() << Resource::String::EnableWindowsFeaturesSuccess << std::endl; + } } } diff --git a/src/AppInstallerCLICore/Workflows/MsiInstallFlow.cpp b/src/AppInstallerCLICore/Workflows/MsiInstallFlow.cpp index a0f0553b96..a17b8657e7 100644 --- a/src/AppInstallerCLICore/Workflows/MsiInstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/MsiInstallFlow.cpp @@ -52,7 +52,7 @@ namespace AppInstaller::CLI::Workflow if (!installResult) { - context.Reporter.Warn() << Resource::String::InstallationAbandoned << std::endl; + context.Reporter.Warn() << Resource::String::InstallAbandoned << std::endl; AICLI_TERMINATE_CONTEXT(E_ABORT); } else diff --git a/src/AppInstallerCLICore/Workflows/ShellExecuteInstallerHandler.cpp b/src/AppInstallerCLICore/Workflows/ShellExecuteInstallerHandler.cpp index 6e9c00f439..f5614c9c42 100644 --- a/src/AppInstallerCLICore/Workflows/ShellExecuteInstallerHandler.cpp +++ b/src/AppInstallerCLICore/Workflows/ShellExecuteInstallerHandler.cpp @@ -4,6 +4,7 @@ #include "ShellExecuteInstallerHandler.h" #include #include +#include using namespace AppInstaller::CLI; using namespace AppInstaller::Utility; @@ -15,7 +16,7 @@ namespace AppInstaller::CLI::Workflow namespace { // ShellExecutes the given path. - std::optional InvokeShellExecuteEx(const std::filesystem::path& filePath, const std::string& args, bool useRunAs, IProgressCallback& progress) + std::optional InvokeShellExecuteEx(const std::filesystem::path& filePath, const std::string& args, bool useRunAs, int show, IProgressCallback& progress) { AICLI_LOG(CLI, Info, << "Starting: '" << filePath.u8string() << "' with arguments '" << args << '\''); @@ -25,9 +26,7 @@ namespace AppInstaller::CLI::Workflow execInfo.lpFile = filePath.c_str(); std::wstring argsUtf16 = Utility::ConvertToUTF16(args); execInfo.lpParameters = argsUtf16.c_str(); - // Some installers force UI. Setting to SW_HIDE will hide installer UI and installation will never complete. - // Verified setting to SW_SHOW does not hurt silent mode since no UI will be shown. - execInfo.nShow = SW_SHOW; + execInfo.nShow = show; // This installer must be run elevated, but we are not currently. // Have ShellExecute elevate the installer since it won't do so itself. @@ -68,7 +67,9 @@ namespace AppInstaller::CLI::Workflow std::optional InvokeShellExecute(const std::filesystem::path& filePath, const std::string& args, IProgressCallback& progress) { - return InvokeShellExecuteEx(filePath, args, false, progress); + // Some installers force UI. Setting to SW_HIDE will hide installer UI and installation will never complete. + // Verified setting to SW_SHOW does not hurt silent mode since no UI will be shown. + return InvokeShellExecuteEx(filePath, args, false, SW_SHOW, progress); } // Gets the escaped installer args. @@ -201,6 +202,45 @@ namespace AppInstaller::CLI::Workflow return args; } + + std::filesystem::path GetDismExecutablePath() + { + return AppInstaller::Filesystem::GetExpandedPath("%windir%\\system32\\dism.exe"); + } + + std::optional DoesWindowsFeatureExist(Execution::Context& context, std::string_view featureName) + { + std::string args = "/Online /Get-FeatureInfo /FeatureName:" + std::string{ featureName }; + auto dismExecPath = GetDismExecutablePath(); + + auto getFeatureInfoResult = context.Reporter.ExecuteWithProgress( + std::bind(InvokeShellExecuteEx, + dismExecPath, + args, + false, + SW_HIDE, + std::placeholders::_1)); + + return getFeatureInfoResult; + } + + std::optional EnableWindowsFeature(Execution::Context& context, std::string_view featureName) + { + std::string args = "/Online /Enable-Feature /NoRestart /FeatureName:" + std::string{ featureName }; + auto dismExecPath = GetDismExecutablePath(); + + AICLI_LOG(Core, Info, << "Enabling Windows Feature [" << featureName << "]"); + + auto enableFeatureResult = context.Reporter.ExecuteWithProgress( + std::bind(InvokeShellExecuteEx, + dismExecPath, + args, + false, + SW_HIDE, + std::placeholders::_1)); + + return enableFeatureResult; + } } void ShellExecuteInstallImpl(Execution::Context& context) @@ -221,16 +261,19 @@ namespace AppInstaller::CLI::Workflow context.Reporter.Warn() << Resource::String::InstallerElevationExpected << std::endl; } + // Some installers force UI. Setting to SW_HIDE will hide installer UI and installation will never complete. + // Verified setting to SW_SHOW does not hurt silent mode since no UI will be shown. auto installResult = context.Reporter.ExecuteWithProgress( std::bind(InvokeShellExecuteEx, context.Get(), installerArgs, installer->ElevationRequirement == ElevationRequirementEnum::ElevationRequired && !isElevated, + SW_SHOW, std::placeholders::_1)); if (!installResult) { - context.Reporter.Warn() << Resource::String::InstallationAbandoned << std::endl; + context.Reporter.Warn() << Resource::String::InstallAbandoned << std::endl; AICLI_TERMINATE_CONTEXT(E_ABORT); } else @@ -307,8 +350,65 @@ namespace AppInstaller::CLI::Workflow else { context.Add(uninstallResult.value()); - } } } + +#ifndef AICLI_DISABLE_TEST_HOOKS + std::optional s_EnableWindowsFeatureResult_Override{}; + + void TestHook_SetEnableWindowsFeatureResult_Override(std::optional&& result) + { + s_EnableWindowsFeatureResult_Override = std::move(result); + } + + std::optional s_DoesWindowsFeatureExistResult_Override{}; + + void TestHook_SetDoesWindowsFeatureExistResult_Override(std::optional&& result) + { + s_DoesWindowsFeatureExistResult_Override = std::move(result); + } +#endif + + void ShellExecuteEnableWindowsFeature::operator()(Execution::Context& context) const + { + Utility::LocIndView locIndFeatureName{ m_featureName }; + +#ifndef AICLI_DISABLE_TEST_HOOKS + auto doesFeatureExistResult = s_DoesWindowsFeatureExistResult_Override.has_value() ? + s_DoesWindowsFeatureExistResult_Override.value() : + DoesWindowsFeatureExist(context, m_featureName); +#else + auto doesFeatureExistResult = DoesWindowsFeatureExist(context, featureName); +#endif + + if (!doesFeatureExistResult) + { + AICLI_TERMINATE_CONTEXT(E_ABORT); + } + else if (doesFeatureExistResult.value() != ERROR_SUCCESS) + { + context.Add(doesFeatureExistResult.value()); + return; + } + + context.Reporter.Info() << Resource::String::EnablingWindowsFeature(locIndFeatureName) << std::endl; + +#ifndef AICLI_DISABLE_TEST_HOOKS + auto enableFeatureResult = s_EnableWindowsFeatureResult_Override.has_value() ? + s_EnableWindowsFeatureResult_Override.value() : + EnableWindowsFeature(context, m_featureName); +#else + auto enableFeatureResult = EnableWindowsFeature(context, featureName); +#endif + + if (!enableFeatureResult) + { + AICLI_TERMINATE_CONTEXT(E_ABORT); + } + else + { + context.Add(enableFeatureResult.value()); + } + } } \ No newline at end of file diff --git a/src/AppInstallerCLICore/Workflows/ShellExecuteInstallerHandler.h b/src/AppInstallerCLICore/Workflows/ShellExecuteInstallerHandler.h index 18c007dfe7..a93d55908a 100644 --- a/src/AppInstallerCLICore/Workflows/ShellExecuteInstallerHandler.h +++ b/src/AppInstallerCLICore/Workflows/ShellExecuteInstallerHandler.h @@ -34,4 +34,18 @@ namespace AppInstaller::CLI::Workflow // Inputs: Manifest?, Installer, InstallerPath // Outputs: InstallerArgs void GetInstallerArgs(Execution::Context& context); + + // Enables the Windows Feature dependency by invoking ShellExecute on the DISM executable. + // Required Args: None + // Inputs: Windows Feature dependency + // Outputs: None + struct ShellExecuteEnableWindowsFeature : public WorkflowTask + { + ShellExecuteEnableWindowsFeature(std::string_view featureName) : WorkflowTask("ShellExecuteEnableWindowsFeature"), m_featureName(featureName) {} + + void operator()(Execution::Context& context) const override; + + private: + std::string_view m_featureName; + }; } \ No newline at end of file diff --git a/src/AppInstallerCLIE2ETests/InstallCommand.cs b/src/AppInstallerCLIE2ETests/InstallCommand.cs index c9198905c0..8890dffe93 100644 --- a/src/AppInstallerCLIE2ETests/InstallCommand.cs +++ b/src/AppInstallerCLIE2ETests/InstallCommand.cs @@ -629,25 +629,25 @@ public void InstallExeWithLatestInstalledWithForce() /// Test install a package with an invalid Windows Feature dependency. /// [Test] - [Ignore("Need change to implementation of Windows Feature dependencies.")] public void InstallWithWindowsFeatureDependency_FeatureNotFound() { var testDir = TestCommon.GetRandomTestDir(); var installResult = TestCommon.RunAICLICommand("install", $"AppInstallerTest.WindowsFeature -l {testDir}"); - Assert.AreEqual(Constants.ErrorCode.ERROR_INSTALL_MISSING_DEPENDENCY, installResult.ExitCode); + Assert.AreEqual(Constants.ErrorCode.ERROR_INSTALL_DEPENDENCIES, installResult.ExitCode); Assert.True(installResult.StdOut.Contains("The feature [invalidFeature] was not found.")); + Assert.True(installResult.StdOut.Contains("Failed to enable Windows Feature dependencies. To proceed with installation, use '--force'.")); } /// /// Test install a package with a Windows Feature dependency using the force argument. /// [Test] - [Ignore("Need change to implementation of Windows Feature dependencies.")] public void InstallWithWindowsFeatureDependency_Force() { var testDir = TestCommon.GetRandomTestDir(); var installResult = TestCommon.RunAICLICommand("install", $"AppInstallerTest.WindowsFeature --silent --force -l {testDir}"); Assert.AreEqual(Constants.ErrorCode.S_OK, installResult.ExitCode); + Assert.True(installResult.StdOut.Contains("Failed to enable Windows Feature dependencies; proceeding due to --force")); Assert.True(installResult.StdOut.Contains("Successfully installed")); Assert.True(TestCommon.VerifyTestExeInstalledAndCleanup(testDir)); } diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw index 3803346ab8..78e1b0daac 100644 --- a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw +++ b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw @@ -1838,13 +1838,13 @@ Please specify one of them using the --source option to proceed. {Locked="--force"} - Enabling {0} [{1}]... - {Locked="{0}","{1}"} Message displayed to the user regarding which Windows Feature is being enabled. + Enabling [{0}]... + {Locked="{0}"} Message displayed to the user regarding which Windows Feature is being enabled. - Failed to enable {0} [{1}] feature. - {Locked="{0}"} Windows Feature display name -{Locked="{1}"} Windows Feature name + Failed to enable [{0}] feature: {1} + {Locked="{0}","{1}"} An error when enabling a Windows Feature. {0} is a placeholder for the name of the Windows Feature. +{1} is a placeholder for the unrecognized error code. Reboot required to fully enable the Windows Feature(s); proceeding due to --force @@ -2584,4 +2584,7 @@ Please specify one of them using the --source option to proceed. Unavailable + + Successfully enabled Windows Features dependencies + \ No newline at end of file diff --git a/src/AppInstallerCLITests/TestHooks.h b/src/AppInstallerCLITests/TestHooks.h index b66aa7a507..b10fce8a55 100644 --- a/src/AppInstallerCLITests/TestHooks.h +++ b/src/AppInstallerCLITests/TestHooks.h @@ -11,7 +11,6 @@ #include #include #include -#include #include #ifdef AICLI_DISABLE_TEST_HOOKS @@ -62,14 +61,10 @@ namespace AppInstaller void TestHook_SetScanArchiveResult_Override(bool* status); } - namespace WindowsFeature + namespace CLI::Workflow { - void TestHook_MockDismHelper_Override(bool status); - void TestHook_SetEnableWindowsFeatureResult_Override(HRESULT* result); - void TestHook_SetIsWindowsFeatureEnabledResult_Override(bool* status); - void TestHook_SetDoesWindowsFeatureExistResult_Override(bool* status); - void TestHook_SetWindowsFeatureGetDisplayNameResult_Override(Utility::LocIndString* displayName); - void TestHook_SetWindowsFeatureGetRestartStatusResult_Override(AppInstaller::WindowsFeature::DismRestartType* restartType); + void TestHook_SetEnableWindowsFeatureResult_Override(std::optional&& result); + void TestHook_SetDoesWindowsFeatureExistResult_Override(std::optional&& result); } } @@ -120,112 +115,45 @@ namespace TestHook } }; - struct MockDismHelper_Override - { - MockDismHelper_Override() - { - AppInstaller::WindowsFeature::TestHook_MockDismHelper_Override(true); - } - - ~MockDismHelper_Override() - { - AppInstaller::WindowsFeature::TestHook_MockDismHelper_Override(false); - } - }; - - struct SetEnableWindowsFeatureResult_Override + struct SetExtractIconFromArpEntryResult_Override { - SetEnableWindowsFeatureResult_Override(HRESULT result) : m_result(result) + SetExtractIconFromArpEntryResult_Override(std::vector extractedIcons) : m_extractedIcons(std::move(extractedIcons)) { - AppInstaller::WindowsFeature::TestHook_SetEnableWindowsFeatureResult_Override(&m_result); + AppInstaller::Repository::TestHook_SetExtractIconFromArpEntryResult_Override(&m_extractedIcons); } - ~SetEnableWindowsFeatureResult_Override() + ~SetExtractIconFromArpEntryResult_Override() { - AppInstaller::WindowsFeature::TestHook_SetEnableWindowsFeatureResult_Override(nullptr); + AppInstaller::Repository::TestHook_SetExtractIconFromArpEntryResult_Override(nullptr); } private: - HRESULT m_result; + std::vector m_extractedIcons; }; - struct SetIsWindowsFeatureEnabledResult_Override + struct SetEnableWindowsFeatureResult_Override { - SetIsWindowsFeatureEnabledResult_Override(bool status) : m_status(status) + SetEnableWindowsFeatureResult_Override(DWORD result) { - AppInstaller::WindowsFeature::TestHook_SetIsWindowsFeatureEnabledResult_Override(&m_status); + AppInstaller::CLI::Workflow::TestHook_SetEnableWindowsFeatureResult_Override(result); } - ~SetIsWindowsFeatureEnabledResult_Override() + ~SetEnableWindowsFeatureResult_Override() { - AppInstaller::WindowsFeature::TestHook_SetIsWindowsFeatureEnabledResult_Override(nullptr); + AppInstaller::CLI::Workflow::TestHook_SetEnableWindowsFeatureResult_Override({}); } - - private: - bool m_status; }; struct SetDoesWindowsFeatureExistResult_Override { - SetDoesWindowsFeatureExistResult_Override(bool status) : m_status(status) + SetDoesWindowsFeatureExistResult_Override(DWORD result) { - AppInstaller::WindowsFeature::TestHook_SetDoesWindowsFeatureExistResult_Override(&m_status); + AppInstaller::CLI::Workflow::TestHook_SetDoesWindowsFeatureExistResult_Override(result); } ~SetDoesWindowsFeatureExistResult_Override() { - AppInstaller::WindowsFeature::TestHook_SetDoesWindowsFeatureExistResult_Override(nullptr); - } - - private: - bool m_status; - }; - - struct SetWindowsFeatureGetDisplayNameResult_Override - { - SetWindowsFeatureGetDisplayNameResult_Override(AppInstaller::Utility::LocIndString displayName) : m_displayName(displayName) - { - AppInstaller::WindowsFeature::TestHook_SetWindowsFeatureGetDisplayNameResult_Override(&m_displayName); - } - - ~SetWindowsFeatureGetDisplayNameResult_Override() - { - AppInstaller::WindowsFeature::TestHook_SetWindowsFeatureGetDisplayNameResult_Override(nullptr); - } - - private: - AppInstaller::Utility::LocIndString m_displayName; - }; - - struct SetWindowsFeatureGetRestartStatusResult_Override - { - SetWindowsFeatureGetRestartStatusResult_Override(AppInstaller::WindowsFeature::DismRestartType restartType) : m_restartType(restartType) - { - AppInstaller::WindowsFeature::TestHook_SetWindowsFeatureGetRestartStatusResult_Override(&m_restartType); - } - - ~SetWindowsFeatureGetRestartStatusResult_Override() - { - AppInstaller::WindowsFeature::TestHook_SetWindowsFeatureGetRestartStatusResult_Override(nullptr); - } - - private: - AppInstaller::WindowsFeature::DismRestartType m_restartType; - }; - - struct SetExtractIconFromArpEntryResult_Override - { - SetExtractIconFromArpEntryResult_Override(std::vector extractedIcons) : m_extractedIcons(std::move(extractedIcons)) - { - AppInstaller::Repository::TestHook_SetExtractIconFromArpEntryResult_Override(&m_extractedIcons); + AppInstaller::CLI::Workflow::TestHook_SetDoesWindowsFeatureExistResult_Override({}); } - - ~SetExtractIconFromArpEntryResult_Override() - { - AppInstaller::Repository::TestHook_SetExtractIconFromArpEntryResult_Override(nullptr); - } - - private: - std::vector m_extractedIcons; }; } \ No newline at end of file diff --git a/src/AppInstallerCLITests/WindowsFeature.cpp b/src/AppInstallerCLITests/WindowsFeature.cpp index df84e23207..4307985d7d 100644 --- a/src/AppInstallerCLITests/WindowsFeature.cpp +++ b/src/AppInstallerCLITests/WindowsFeature.cpp @@ -5,13 +5,11 @@ #include "WorkflowCommon.h" #include #include -#include #include "TestHooks.h" using namespace AppInstaller::CLI; using namespace AppInstaller::Settings; using namespace AppInstaller::Utility; -using namespace AppInstaller::WindowsFeature; using namespace TestCommon; TEST_CASE("InstallFlow_WindowsFeatureDoesNotExist", "[windowsFeature]") @@ -34,14 +32,11 @@ TEST_CASE("InstallFlow_WindowsFeatureDoesNotExist", "[windowsFeature]") context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_WindowsFeatures.yaml").GetPath().u8string()); - auto mockDismHelperOverride = TestHook::MockDismHelper_Override(); - auto doesFeatureExistOverride = TestHook::SetDoesWindowsFeatureExistResult_Override(false); - InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); - REQUIRE(context.GetTerminationHR() == APPINSTALLER_CLI_ERROR_INSTALL_MISSING_DEPENDENCY); + REQUIRE(context.GetTerminationHR() == APPINSTALLER_CLI_ERROR_INSTALL_DEPENDENCIES); REQUIRE(!std::filesystem::exists(installResultPath.GetPath())); REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::WindowsFeatureNotFound(LocIndView{ "testFeature1" })).get()) != std::string::npos); @@ -70,20 +65,14 @@ TEST_CASE("InstallFlow_FailedToEnableWindowsFeature", "[windowsFeature]") context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_WindowsFeatures.yaml").GetPath().u8string()); - // Override with arbitrary DISM api error (DISMAPI_E_DISMAPI_NOT_INITIALIZED) and make windows feature discoverable. - HRESULT dismErrorResult = 0xc0040001; - auto mockDismHelperOverride = TestHook::MockDismHelper_Override(); - auto doesFeatureExistOverride = TestHook::SetDoesWindowsFeatureExistResult_Override(true); - auto setIsFeatureEnabledOverride = TestHook::SetIsWindowsFeatureEnabledResult_Override(false); - auto setEnableFeatureOverride = TestHook::SetEnableWindowsFeatureResult_Override(dismErrorResult); - auto getDisplayNameOverride = TestHook::SetWindowsFeatureGetDisplayNameResult_Override(LocIndString{ "Test Windows Feature"_liv }); - auto getRestartStatusOverride = TestHook::SetWindowsFeatureGetRestartStatusResult_Override(DismRestartNo); + auto setDoesFeatureExistOverride = TestHook::SetDoesWindowsFeatureExistResult_Override(ERROR_SUCCESS); + auto setEnableFeatureOverride = TestHook::SetEnableWindowsFeatureResult_Override(0xc0040001); // DISMAPI_E_DISMAPI_NOT_INITIALIZED InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); - REQUIRE(context.GetTerminationHR() == dismErrorResult); + REQUIRE(context.GetTerminationHR() == APPINSTALLER_CLI_ERROR_INSTALL_DEPENDENCIES); REQUIRE(!std::filesystem::exists(installResultPath.GetPath())); REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::FailedToEnableWindowsFeatureOverrideRequired).get()) != std::string::npos); } @@ -101,15 +90,9 @@ TEST_CASE("InstallFlow_FailedToEnableWindowsFeature_Force", "[windowsFeature]") TestCommon::TestUserSettings testSettings; testSettings.Set(true); - // Override with arbitrary DISM api error (DISMAPI_E_DISMAPI_NOT_INITIALIZED) and make windows feature discoverable. - HRESULT dismErrorResult = 0xc0040001; - LocIndString testFeatureDisplayName = LocIndString{ "Test Windows Feature"_liv }; - auto mockDismHelperOverride = TestHook::MockDismHelper_Override(); - auto setEnableFeatureOverride = TestHook::SetEnableWindowsFeatureResult_Override(dismErrorResult); - auto doesFeatureExistOverride = TestHook::SetDoesWindowsFeatureExistResult_Override(true); - auto setIsFeatureEnabledOverride = TestHook::SetIsWindowsFeatureEnabledResult_Override(false); - auto getDisplayNameOverride = TestHook::SetWindowsFeatureGetDisplayNameResult_Override(testFeatureDisplayName); - auto getRestartStatusOverride = TestHook::SetWindowsFeatureGetRestartStatusResult_Override(DismRestartNo); + auto doesFeatureExistOverride = TestHook::SetDoesWindowsFeatureExistResult_Override(ERROR_SUCCESS); + auto expectedErrorCode = 0xc0040001; // DISMAPI_E_DISMAPI_NOT_INITIALIZED + auto setEnableFeatureOverride = TestHook::SetEnableWindowsFeatureResult_Override(expectedErrorCode); std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; @@ -126,8 +109,8 @@ TEST_CASE("InstallFlow_FailedToEnableWindowsFeature_Force", "[windowsFeature]") // Verify Installer is called and parameters are passed in. REQUIRE(context.GetTerminationHR() == ERROR_SUCCESS); - REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::FailedToEnableWindowsFeature(testFeatureDisplayName, LocIndView{ "testFeature1" })).get()) != std::string::npos); - REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::FailedToEnableWindowsFeature(testFeatureDisplayName, LocIndView{ "testFeature2" })).get()) != std::string::npos); + REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::FailedToEnableWindowsFeature(LocIndView{ "testFeature1" }, expectedErrorCode)).get()) != std::string::npos); + REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::FailedToEnableWindowsFeature(LocIndView{ "testFeature2" }, expectedErrorCode)).get()) != std::string::npos); REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::FailedToEnableWindowsFeatureOverridden).get()) != std::string::npos); REQUIRE(std::filesystem::exists(installResultPath.GetPath())); std::ifstream installResultFile(installResultPath.GetPath()); @@ -152,12 +135,8 @@ TEST_CASE("InstallFlow_RebootRequired", "[windowsFeature]") testSettings.Set(true); // Override with reboot required HRESULT. - auto mockDismHelperOverride = TestHook::MockDismHelper_Override(); + auto doesFeatureExistOverride = TestHook::SetDoesWindowsFeatureExistResult_Override(ERROR_SUCCESS); auto setEnableFeatureOverride = TestHook::SetEnableWindowsFeatureResult_Override(ERROR_SUCCESS_REBOOT_REQUIRED); - auto setIsFeatureEnabledOverride = TestHook::SetIsWindowsFeatureEnabledResult_Override (false); - auto doesFeatureExistOverride = TestHook::SetDoesWindowsFeatureExistResult_Override(true); - auto getDisplayNameOverride = TestHook::SetWindowsFeatureGetDisplayNameResult_Override(LocIndString{ "Test Windows Feature"_liv }); - auto getRestartStatusOverride = TestHook::SetWindowsFeatureGetRestartStatusResult_Override(DismRestartRequired); std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; @@ -189,12 +168,8 @@ TEST_CASE("InstallFlow_RebootRequired_Force", "[windowsFeature]") testSettings.Set(true); // Override with reboot required HRESULT. - auto mockDismHelperOverride = TestHook::MockDismHelper_Override(); + auto doesFeatureExistOverride = TestHook::SetDoesWindowsFeatureExistResult_Override(ERROR_SUCCESS); auto setEnableFeatureOverride = TestHook::SetEnableWindowsFeatureResult_Override(ERROR_SUCCESS_REBOOT_REQUIRED); - auto setIsFeatureEnabledOverride = TestHook::SetIsWindowsFeatureEnabledResult_Override(false); - auto doesFeatureExistOverride = TestHook::SetDoesWindowsFeatureExistResult_Override(true); - auto getDisplayNameOverride = TestHook::SetWindowsFeatureGetDisplayNameResult_Override(LocIndString{ "Test Windows Feature"_liv }); - auto getRestartStatusOverride = TestHook::SetWindowsFeatureGetRestartStatusResult_Override(DismRestartRequired); std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; diff --git a/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj b/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj index c89bacfcb8..38828978ee 100644 --- a/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj +++ b/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj @@ -438,7 +438,6 @@ - @@ -503,7 +502,6 @@ - diff --git a/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj.filters b/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj.filters index d2ffa568fc..fcc3bd5a70 100644 --- a/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj.filters +++ b/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj.filters @@ -174,9 +174,6 @@ Public\winget - - Public\winget - Public\winget @@ -320,9 +317,6 @@ Source Files - - Source Files - Source Files diff --git a/src/AppInstallerCommonCore/Public/winget/WindowsFeature.h b/src/AppInstallerCommonCore/Public/winget/WindowsFeature.h deleted file mode 100644 index a2ec6c09dc..0000000000 --- a/src/AppInstallerCommonCore/Public/winget/WindowsFeature.h +++ /dev/null @@ -1,210 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#pragma once -#include -#include - -namespace AppInstaller::WindowsFeature -{ - /****************************************************************************\ - - Declaration copied from DismApi.h to support enabling Windows Features. - - Copyright (c) Microsoft Corporation. - All rights reserved. - - \****************************************************************************/ - -#ifndef _DISMAPI_H_ -#define _DISMAPI_H_ - -#include - -#pragma region Desktop Family or DISM Package -#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_PKG_DISM) - -#ifdef __cplusplus - extern "C" - { -#endif - - typedef UINT DismSession; - typedef void(CALLBACK* DISM_PROGRESS_CALLBACK)(_In_ UINT Current, _In_ UINT Total, _In_opt_ PVOID UserData); - -#define DISM_ONLINE_IMAGE L"DISM_{53BFAE52-B167-4E2F-A258-0A37B57FF845}" -#define DISM_SESSION_DEFAULT 0 - - typedef enum _DismLogLevel - { - DismLogErrors = 0, - DismLogErrorsWarnings, - DismLogErrorsWarningsInfo, - DismLogErrorsWarningsInfoDebug - } DismLogLevel; - - typedef enum _DismPackageIdentifier - { - DismPackageNone = 0, - DismPackageName, - DismPackagePath - } DismPackageIdentifier; - - typedef enum _DismPackageFeatureState - { - DismStateNotPresent = 0, - DismStateUninstallPending, - DismStateStaged, - DismStateResolved, // For internal use only - DismStateRemoved = DismStateResolved, - DismStateInstalled, - DismStateInstallPending, - DismStateSuperseded, - DismStatePartiallyInstalled - } DismPackageFeatureState; - - typedef enum _DismRestartType - { - DismRestartNo = 0, - DismRestartPossible, - DismRestartRequired - } DismRestartType; - -#pragma pack(push, 1) - - typedef struct _DismCustomProperty - { - PCWSTR Name; - PCWSTR Value; - PCWSTR Path; - } DismCustomProperty; - - typedef struct _DismFeature - { - PCWSTR FeatureName; - DismPackageFeatureState State; - } DismFeature; - - typedef struct _DismFeatureInfo - { - PCWSTR FeatureName; - DismPackageFeatureState FeatureState; - PCWSTR DisplayName; - PCWSTR Description; - DismRestartType RestartRequired; - DismCustomProperty* CustomProperty; - UINT CustomPropertyCount; - } DismFeatureInfo; - -#pragma pack(pop) - -#ifdef __cplusplus - } -#endif - -#endif /* WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_PKG_DISM) */ -#pragma endregion - -#endif // _DISMAPI_H_ - - using DismInitializePtr = HRESULT(WINAPI*)(int, PCWSTR, PCWSTR); - using DismOpenSessionPtr = HRESULT(WINAPI*)(PCWSTR, PCWSTR, PCWSTR, DismSession*); - using DismCloseSessionPtr = HRESULT(WINAPI*)(DismSession); - using DismShutdownPtr = HRESULT(WINAPI*)(); - using DismGetFeatureInfoPtr = HRESULT(WINAPI*)(UINT, PCWSTR, PCWSTR, DismPackageIdentifier, DismFeatureInfo**); - using DismEnableFeaturePtr = HRESULT(WINAPI*)(UINT, PCWSTR, PCWSTR, DismPackageIdentifier, BOOL, PCWSTR*, UINT, BOOL, HANDLE, DISM_PROGRESS_CALLBACK, PVOID); - using DismDisableFeaturePtr = HRESULT(WINAPI*)(UINT, PCWSTR, PCWSTR, BOOL, HANDLE, DISM_PROGRESS_CALLBACK, PVOID); - using DismDeletePtr = HRESULT(WINAPI*)(VOID*); - - // Forward declaration - struct DismHelper; - - /// - /// Struct representation of a single Windows Feature. - /// - struct WindowsFeature - { - friend DismHelper; - - ~WindowsFeature(); - - // TODO: Implement progress via DismProgressFunction - HRESULT Enable(IProgressCallback& progress); - HRESULT Disable(); - bool DoesExist(); - bool IsEnabled(); - Utility::LocIndString GetDisplayName(); - DismRestartType GetRestartRequiredStatus(); - - DismPackageFeatureState GetState() - { - return m_featureInfo->FeatureState; - } - - protected: - WindowsFeature(std::shared_ptr dismHelper, const std::string& name); - - private: - void GetFeatureInfo(); - - std::string m_featureName; - std::shared_ptr m_dismHelper; - DismFeatureInfo* m_featureInfo = nullptr; - }; - - struct DismHelper : public std::enable_shared_from_this - { - DismHelper(); - ~DismHelper(); - - WindowsFeature GetWindowsFeature(const std::string& featureName) - { - return WindowsFeature(std::move(GetPtr()), featureName); - } - - HRESULT EnableFeature( - PCWSTR featureName, - PCWSTR identifier, - DismPackageIdentifier packageIdentifier, - BOOL limitAccess, - PCWSTR* sourcePaths, - UINT sourcePathCount, - BOOL enableAll, - HANDLE cancelEvent, - DISM_PROGRESS_CALLBACK progress, - PVOID userData); - - HRESULT DisableFeature(PCWSTR featureName, PCWSTR packageName, BOOL removePayload, HANDLE cancelEvent, DISM_PROGRESS_CALLBACK progress, PVOID userData); - - HRESULT GetFeatureInfo(PCWSTR featureName, PCWSTR identifier, DismPackageIdentifier packageIdentifier, DismFeatureInfo** featureInfo); - - HRESULT Delete(VOID* dismStructure); - - private: - typedef UINT DismSession; - - wil::unique_hmodule m_module; - DismSession m_dismSession = DISM_SESSION_DEFAULT; - - DismInitializePtr m_dismInitialize = nullptr; - DismOpenSessionPtr m_dismOpenSession = nullptr; - DismGetFeatureInfoPtr m_dismGetFeatureInfo = nullptr; - DismEnableFeaturePtr m_dismEnableFeature = nullptr; - DismDisableFeaturePtr m_dismDisableFeature = nullptr; - DismCloseSessionPtr m_dismCloseSession = nullptr; - DismShutdownPtr m_dismShutdown = nullptr; - DismDeletePtr m_dismDelete = nullptr; - - std::shared_ptr GetPtr() - { - return shared_from_this(); - } - - void Initialize(); - void OpenSession(); - void CloseSession(); - void Shutdown(); - - template - FuncType GetProcAddressHelper(HMODULE module, LPCSTR functionName); - }; -} \ No newline at end of file diff --git a/src/AppInstallerCommonCore/WindowsFeature.cpp b/src/AppInstallerCommonCore/WindowsFeature.cpp deleted file mode 100644 index d21875c60f..0000000000 --- a/src/AppInstallerCommonCore/WindowsFeature.cpp +++ /dev/null @@ -1,256 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#include "pch.h" -#include "winget/WindowsFeature.h" -#include "Public/AppInstallerLogging.h" -#include "Public/AppInstallerStrings.h" - -namespace AppInstaller::WindowsFeature -{ -#ifndef AICLI_DISABLE_TEST_HOOKS - static bool s_MockDismHelper_Override = false; - - void TestHook_MockDismHelper_Override(bool status) - { - s_MockDismHelper_Override = status; - } -#endif - - DismHelper::DismHelper() - { -#ifndef AICLI_DISABLE_TEST_HOOKS - // The entire DismHelper class and its functions needs to be mocked since DismHost.exe inherits log file handles. - // Without this, the unit tests will fail to complete waiting for DismHost.exe to release the log file handles. - if (s_MockDismHelper_Override) - { - return; - } -#endif - - m_module.reset(LoadLibraryEx(L"dismapi.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32)); - - if (!m_module) - { - AICLI_LOG(Core, Error, << "Could not load dismapi.dll"); - THROW_LAST_ERROR(); - } - - m_dismInitialize = GetProcAddressHelper(m_module.get(), "DismInitialize"); - m_dismOpenSession = GetProcAddressHelper(m_module.get(), "DismOpenSession"); - m_dismGetFeatureInfo = GetProcAddressHelper(m_module.get(), "DismGetFeatureInfo"); - m_dismEnableFeature = GetProcAddressHelper(m_module.get(), "DismEnableFeature"); - m_dismDisableFeature = GetProcAddressHelper(m_module.get(), "DismDisableFeature"); - m_dismDelete = GetProcAddressHelper(m_module.get(), "DismDelete"); - m_dismCloseSession = GetProcAddressHelper(m_module.get(), "DismCloseSession"); - m_dismShutdown = GetProcAddressHelper(m_module.get(), "DismShutdown"); - - Initialize(); - OpenSession(); - } - - DismHelper::~DismHelper() - { -#ifndef AICLI_DISABLE_TEST_HOOKS - if (s_MockDismHelper_Override) - { - return; - } -#endif - CloseSession(); - Shutdown(); - }; - - template - FuncType DismHelper::GetProcAddressHelper(HMODULE module, LPCSTR functionName) - { - FuncType result = reinterpret_cast(GetProcAddress(module, functionName)); - if (!result) - { - AICLI_LOG(Core, Error, << "Could not get proc address of " << functionName); - THROW_LAST_ERROR(); - } - return result; - } - - void DismHelper::Initialize() - { - LOG_IF_FAILED(m_dismInitialize(DismLogErrorsWarningsInfo, NULL, NULL)); - } - - void DismHelper::OpenSession() - { - LOG_IF_FAILED(m_dismOpenSession(DISM_ONLINE_IMAGE, NULL, NULL, &m_dismSession)); - } - - void DismHelper::CloseSession() - { - LOG_IF_FAILED(m_dismCloseSession(m_dismSession)); - } - - HRESULT DismHelper::EnableFeature( - PCWSTR featureName, - PCWSTR identifier, - DismPackageIdentifier packageIdentifier, - BOOL limitAccess, - PCWSTR* sourcePaths, - UINT sourcePathCount, - BOOL enableAll, - HANDLE cancelEvent, - DISM_PROGRESS_CALLBACK progress, - PVOID userData) - { - return m_dismEnableFeature(m_dismSession, featureName, identifier, packageIdentifier, limitAccess, sourcePaths, sourcePathCount, enableAll, cancelEvent, progress, userData); - } - - HRESULT DismHelper::DisableFeature(PCWSTR featureName, PCWSTR packageName, BOOL removePayload, HANDLE cancelEvent, DISM_PROGRESS_CALLBACK progress, PVOID userData) - { - return m_dismDisableFeature(m_dismSession, featureName, packageName, removePayload, cancelEvent, progress, userData); - } - - HRESULT DismHelper::GetFeatureInfo(PCWSTR featureName, PCWSTR identifier, DismPackageIdentifier packageIdentifier, DismFeatureInfo** featureInfo) - { - return m_dismGetFeatureInfo(m_dismSession, featureName, identifier, packageIdentifier, featureInfo); - } - - HRESULT DismHelper::Delete(VOID* dismStructure) - { - return m_dismDelete(dismStructure); - } - - void DismHelper::Shutdown() - { - LOG_IF_FAILED(m_dismShutdown()); - } - - WindowsFeature::WindowsFeature(std::shared_ptr dismHelper, const std::string& name) - : m_dismHelper(dismHelper), m_featureName(name) - { -#ifndef AICLI_DISABLE_TEST_HOOKS - if (s_MockDismHelper_Override) - { - return; - } -#endif - GetFeatureInfo(); - } - - WindowsFeature::~WindowsFeature() - { - if (m_featureInfo) - { - LOG_IF_FAILED(m_dismHelper->Delete(m_featureInfo)); - } - } - -#ifndef AICLI_DISABLE_TEST_HOOKS - static HRESULT* s_EnableWindowsFeatureResult_TestHook_Override = nullptr; - - void TestHook_SetEnableWindowsFeatureResult_Override(HRESULT* result) - { - s_EnableWindowsFeatureResult_TestHook_Override = result; - } - - static bool* s_DoesWindowsFeatureExistResult_TestHook_Override = nullptr; - - void TestHook_SetDoesWindowsFeatureExistResult_Override(bool* result) - { - s_DoesWindowsFeatureExistResult_TestHook_Override = result; - } - - static bool* s_IsWindowsFeatureEnabledResult_TestHook_Override = nullptr; - - void TestHook_SetIsWindowsFeatureEnabledResult_Override(bool* status) - { - s_IsWindowsFeatureEnabledResult_TestHook_Override = status; - } - - static Utility::LocIndString* s_WindowsFeatureGetDisplayNameResult_TestHook_Override = nullptr; - - void TestHook_SetWindowsFeatureGetDisplayNameResult_Override(Utility::LocIndString* displayName) - { - s_WindowsFeatureGetDisplayNameResult_TestHook_Override = displayName; - } - - static DismRestartType* s_WindowsFeatureGetRestartRequiredStatusResult_TestHook_Override = nullptr; - - void TestHook_SetWindowsFeatureGetRestartStatusResult_Override(DismRestartType* restartType) - { - s_WindowsFeatureGetRestartRequiredStatusResult_TestHook_Override = restartType; - } -#endif - - HRESULT WindowsFeature::Enable(AppInstaller::IProgressCallback& progress) - { - UNREFERENCED_PARAMETER(progress); - -#ifndef AICLI_DISABLE_TEST_HOOKS - if (s_EnableWindowsFeatureResult_TestHook_Override) - { - return *s_EnableWindowsFeatureResult_TestHook_Override; - } -#endif - HRESULT hr = m_dismHelper->EnableFeature(Utility::ConvertToUTF16(m_featureName).c_str(), NULL, DismPackageNone, FALSE, NULL, NULL, FALSE, NULL, NULL, NULL); - LOG_IF_FAILED(hr); - return hr; - } - - HRESULT WindowsFeature::Disable() - { - HRESULT hr = m_dismHelper->DisableFeature(Utility::ConvertToUTF16(m_featureName).c_str(), NULL, FALSE, NULL, NULL, NULL); - LOG_IF_FAILED(hr); - return hr; - } - - bool WindowsFeature::DoesExist() - { -#ifndef AICLI_DISABLE_TEST_HOOKS - if (s_DoesWindowsFeatureExistResult_TestHook_Override) - { - return *s_DoesWindowsFeatureExistResult_TestHook_Override; - } -#endif - return m_featureInfo; - } - - bool WindowsFeature::IsEnabled() - { -#ifndef AICLI_DISABLE_TEST_HOOKS - if (s_IsWindowsFeatureEnabledResult_TestHook_Override) - { - return *s_IsWindowsFeatureEnabledResult_TestHook_Override; - } -#endif - // Refresh feature info state prior to retrieving state info. - GetFeatureInfo(); - DismPackageFeatureState featureState = GetState(); - AICLI_LOG(Core, Info, << "Feature state of " << m_featureName << " is " << featureState); - return featureState == DismStateInstalled; - } - - Utility::LocIndString WindowsFeature::GetDisplayName() - { -#ifndef AICLI_DISABLE_TEST_HOOKS - if (s_WindowsFeatureGetDisplayNameResult_TestHook_Override) - { - return *s_WindowsFeatureGetDisplayNameResult_TestHook_Override; - } -#endif - return Utility::LocIndString{ Utility::ConvertToUTF8(std::wstring{ m_featureInfo->DisplayName }) }; - } - - DismRestartType WindowsFeature::GetRestartRequiredStatus() - { -#ifndef AICLI_DISABLE_TEST_HOOKS - if (s_WindowsFeatureGetRestartRequiredStatusResult_TestHook_Override) - { - return *s_WindowsFeatureGetRestartRequiredStatusResult_TestHook_Override; - } -#endif - return m_featureInfo->RestartRequired; - } - - void WindowsFeature::GetFeatureInfo() - { - LOG_IF_FAILED(m_dismHelper->GetFeatureInfo(Utility::ConvertToUTF16(m_featureName).c_str(), NULL, DismPackageNone, &m_featureInfo)); - } -} \ No newline at end of file