commit 75803c312eb442e03184ce0dacf197ee8f5bb432
parent 38aac878ab34d0176b34faf6946d7a0b55d4ce97
Author: JohnMcPMS <johnmcp@microsoft.com>
Date: Thu, 20 Jun 2024 17:13:05 -0700
Launch COM server for elevated use via packaged path (#4567)
## Change
Leverage the feature that allows launching a packaged process via its
packaged executable location as long as there is a matching alias. This
better ensures that we are launching the correct process.
Diffstat:
3 files changed, 119 insertions(+), 26 deletions(-)
diff --git a/src/PowerShell/Microsoft.WinGet.Client.Engine/Common/ErrorCode.cs b/src/PowerShell/Microsoft.WinGet.Client.Engine/Common/ErrorCode.cs
@@ -1,4 +1,4 @@
-// -----------------------------------------------------------------------------
+// -----------------------------------------------------------------------------
// <copyright file="ErrorCode.cs" company="Microsoft Corporation">
// Copyright (c) Microsoft Corporation. Licensed under the MIT License.
// </copyright>
@@ -25,5 +25,10 @@ namespace Microsoft.WinGet.Client.Engine.Common
/// Error code for RPC_S_CALL_FAILED.
/// </summary>
public const int RpcCallFailed = unchecked((int)0x800706BE);
+
+ /// <summary>
+ /// Error code for ERROR_PACKAGE_NOT_REGISTERED_FOR_USER.
+ /// </summary>
+ public const int PackageNotRegisteredForUser = unchecked((int)0x80073D35);
}
}
diff --git a/src/PowerShell/Microsoft.WinGet.Client.Engine/Helpers/ManagementDeploymentFactory.cs b/src/PowerShell/Microsoft.WinGet.Client.Engine/Helpers/ManagementDeploymentFactory.cs
@@ -1,4 +1,4 @@
-// -----------------------------------------------------------------------------
+// -----------------------------------------------------------------------------
// <copyright file="ManagementDeploymentFactory.cs" company="Microsoft Corporation">
// Copyright (c) Microsoft Corporation. Licensed under the MIT License.
// </copyright>
@@ -179,7 +179,7 @@ namespace Microsoft.WinGet.Client.Engine.Helpers
if (hr < 0)
{
- if (hr == ErrorCode.FileNotFound)
+ if (hr == ErrorCode.FileNotFound || hr == ErrorCode.PackageNotRegisteredForUser)
{
throw new WinGetIntegrityException(IntegrityCategory.AppInstallerNotInstalled);
}
diff --git a/src/WinGetServer/WinGetServerManualActivation_Client.cpp b/src/WinGetServer/WinGetServerManualActivation_Client.cpp
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#include "WinGetServer.h"
+#include "appmodel.h"
#include "Utils.h"
#include <wil/com.h>
@@ -15,9 +16,12 @@
#include <shlobj_core.h>
#ifdef USE_PROD_WINGET_SERVER
-const std::wstring_view s_ServerExePath = L"Microsoft\\WindowsApps\\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe\\WindowsPackageManagerServer.exe";
+const std::wstring_view s_ServerPackageFamilyName = L"Microsoft.DesktopAppInstaller_8wekyb3d8bbwe";
+const std::wstring_view s_ServerFileName = L"WindowsPackageManagerServer.exe";
#else
-const std::wstring_view s_ServerExePath = L"Microsoft\\WindowsApps\\WinGetDevCLI_8wekyb3d8bbwe\\WindowsPackageManagerServerDev.exe";
+const std::wstring_view s_LocalAppDataRelativeServerExePath = L"Microsoft\\WindowsApps\\WinGetDevCLI_8wekyb3d8bbwe\\WindowsPackageManagerServerDev.exe";
+const std::wstring_view s_ServerPackageFamilyName = L"WinGetDevCLI_8wekyb3d8bbwe";
+const std::wstring_view s_ServerFileName = L"WinGetServer\\WindowsPackageManagerServer.exe";
#endif
_Must_inspect_result_
@@ -35,13 +39,6 @@ void __RPC_USER MIDL_user_free(_Pre_maybenull_ _Post_invalid_ void* ptr)
}
}
-std::filesystem::path GetKnownFolderPath(const KNOWNFOLDERID& id)
-{
- wil::unique_cotaskmem_string knownFolder = nullptr;
- THROW_IF_FAILED(SHGetKnownFolderPath(id, KF_FLAG_NO_ALIAS | KF_FLAG_DONT_VERIFY | KF_FLAG_NO_PACKAGE_REDIRECTION, NULL, &knownFolder));
- return knownFolder.get();
-}
-
struct FreeWithRpcStringFree { void operator()(RPC_CSTR* in) { RpcStringFreeA(in); } };
using UniqueRpcString = std::unique_ptr<RPC_CSTR, FreeWithRpcStringFree>;
@@ -64,23 +61,112 @@ void InitializeRpcBinding()
THROW_HR_IF(HRESULT_FROM_WIN32(status), status != RPC_S_OK);
}
-HRESULT LaunchWinGetServerWithManualActivation()
+struct ServerProcessLauncher
{
- const std::filesystem::path& localAppDataPath = GetKnownFolderPath(FOLDERID_LocalAppData);
- const std::filesystem::path& serverExePath = localAppDataPath / s_ServerExePath;
- std::wstring commandLineInput = std::wstring{ serverExePath } + L" --manualActivation";
+ ServerProcessLauncher()
+ {
+ try
+ {
+ m_serverExePath = GetPackageLocation(s_ServerPackageFamilyName, s_ServerFileName) / s_ServerFileName;
+
+#ifndef USE_PROD_WINGET_SERVER
+ // The feature that allows directly launching a packaged process as long as it has a matching alias
+ // requires a failure to trigger, and the dev package is not ACL'd to force this to happen. Attempting
+ // to use the other code path results in an unpackaged server, causing other issues.
+ // We run the product code above to ensure that it is functioning properly, but then replace it with
+ // the path of the alias.
+ m_serverExePath = GetKnownFolderPath(FOLDERID_LocalAppData) / s_LocalAppDataRelativeServerExePath;
+#endif
+ }
+ catch (wil::ResultException& re)
+ {
+ m_hr = re.GetErrorCode();
+ }
+ }
+
+ HRESULT LaunchWinGetServerWithManualActivation()
+ {
+ RETURN_IF_FAILED(m_hr);
- STARTUPINFO info = { sizeof(info) };
- wil::unique_process_information process;
+ std::wstring commandLineInput = std::wstring{ m_serverExePath } + L" --manualActivation";
- RETURN_LAST_ERROR_IF(!CreateProcessW(NULL, &commandLineInput[0], NULL, NULL, FALSE, 0, NULL, NULL, &info, &process));
+ STARTUPINFO info = { sizeof(info) };
+ wil::unique_process_information process;
- // Wait for manual reset event from server before proceeding with COM activation.
- wil::unique_event manualResetEvent = CreateOrOpenServerStartEvent();
- manualResetEvent.wait(10000);
+ RETURN_LAST_ERROR_IF(!CreateProcessW(NULL, &commandLineInput[0], NULL, NULL, FALSE, 0, NULL, NULL, &info, &process));
- return S_OK;
-}
+ // Wait for manual reset event from server before proceeding with COM activation.
+ wil::unique_event manualResetEvent = CreateOrOpenServerStartEvent();
+ manualResetEvent.wait(10000);
+
+ return S_OK;
+ }
+
+private:
+ std::filesystem::path GetPackageLocation(std::wstring_view packageFamilyName, std::wstring_view fileName)
+ {
+ std::wstring pfn{ packageFamilyName };
+ UINT32 count = 0;
+ std::unique_ptr<PWSTR[]> fullNames;
+ UINT32 bufferLength = 0;
+ std::unique_ptr<WCHAR[]> buffer;
+ std::unique_ptr<UINT32[]> properties;
+
+ LONG result = FindPackagesByPackageFamily(pfn.c_str(), PACKAGE_FILTER_HEAD, &count, nullptr, &bufferLength, nullptr, nullptr);
+ THROW_WIN32_IF(result, result != ERROR_INSUFFICIENT_BUFFER);
+
+ for (size_t i = 0; i < 10 && result == ERROR_INSUFFICIENT_BUFFER; ++i)
+ {
+ fullNames = std::make_unique<PWSTR[]>(count);
+ buffer = std::make_unique<WCHAR[]>(bufferLength);
+ properties = std::make_unique<UINT32[]>(count);
+
+ result = FindPackagesByPackageFamily(pfn.c_str(), PACKAGE_FILTER_HEAD, &count, fullNames.get(), &bufferLength, buffer.get(), properties.get());
+ }
+
+ THROW_IF_WIN32_ERROR(result);
+
+ for (UINT32 i = 0; i < count; ++i)
+ {
+ // Includes null terminator
+ UINT32 pathLength = 0;
+ result = GetPackagePathByFullName(fullNames[i], &pathLength, nullptr);
+ if (result != ERROR_INSUFFICIENT_BUFFER)
+ {
+ continue;
+ }
+
+ std::wstring packagePath;
+ packagePath.resize(static_cast<size_t>(pathLength));
+
+ if (FAILED_WIN32(GetPackagePathByFullName(fullNames[i], &pathLength, &packagePath[0])))
+ {
+ continue;
+ }
+ packagePath.resize(static_cast<size_t>(pathLength - 1), L'\0');
+
+ std::filesystem::path resultPath = std::move(packagePath);
+ std::filesystem::path exePath = resultPath / fileName;
+
+ if (GetFileAttributesW(exePath.c_str()) != INVALID_FILE_ATTRIBUTES)
+ {
+ return resultPath;
+ }
+ }
+
+ THROW_WIN32(ERROR_PACKAGE_NOT_REGISTERED_FOR_USER);
+ }
+
+ std::filesystem::path GetKnownFolderPath(const KNOWNFOLDERID& id)
+ {
+ wil::unique_cotaskmem_string knownFolder = nullptr;
+ THROW_IF_FAILED(SHGetKnownFolderPath(id, KF_FLAG_NO_ALIAS | KF_FLAG_DONT_VERIFY | KF_FLAG_NO_PACKAGE_REDIRECTION, NULL, &knownFolder));
+ return knownFolder.get();
+ }
+
+ std::filesystem::path m_serverExePath;
+ HRESULT m_hr = S_OK;
+};
HRESULT CallCreateInstance(REFCLSID rclsid, REFIID riid, UINT32 flags, UINT32* bufferByteCount, BYTE** buffer)
{
@@ -132,10 +218,12 @@ extern "C" HRESULT WinGetServerManualActivation_CreateInstance(REFCLSID rclsid,
HRESULT result = CreateComInstance(rclsid, riid, flags, out);
if (FAILED(result))
{
+ ServerProcessLauncher launcher;
+
for (int i = 0; i < 3; i++)
{
- result = LaunchWinGetServerWithManualActivation();
- if (result == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
+ result = launcher.LaunchWinGetServerWithManualActivation();
+ if (result == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) || result == HRESULT_FROM_WIN32(ERROR_PACKAGE_NOT_REGISTERED_FOR_USER))
{
break;
}