winget-cli

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

WinGetServerManualActivation_Client.cpp (8556B)


      1 // Copyright (c) Microsoft Corporation.
      2 // Licensed under the MIT License.
      3 #include "WinGetServer.h"
      4 #include "appmodel.h"
      5 #include "Utils.h"
      6 
      7 #include <wil/com.h>
      8 #include <wil/result.h>
      9 #include <wil/safecast.h>
     10 
     11 #include <memory>
     12 #include <mutex>
     13 #include <string>
     14 #include <shtypes.h>
     15 #include <filesystem>
     16 #include <shlobj_core.h>
     17 
     18 #ifdef USE_PROD_WINGET_SERVER
     19 const std::wstring_view s_ServerPackageFamilyName = L"Microsoft.DesktopAppInstaller_8wekyb3d8bbwe";
     20 const std::wstring_view s_ServerFileName = L"WindowsPackageManagerServer.exe";
     21 #else
     22 const std::wstring_view s_LocalAppDataRelativeServerExePath = L"Microsoft\\WindowsApps\\WinGetDevCLI_8wekyb3d8bbwe\\WindowsPackageManagerServerDev.exe";
     23 const std::wstring_view s_ServerPackageFamilyName = L"WinGetDevCLI_8wekyb3d8bbwe";
     24 const std::wstring_view s_ServerFileName = L"WinGetServer\\WindowsPackageManagerServer.exe";
     25 #endif
     26 
     27 _Must_inspect_result_
     28 _Ret_maybenull_ _Post_writable_byte_size_(size)
     29 void* __RPC_USER MIDL_user_allocate(_In_ size_t size)
     30 {
     31     return malloc(size);
     32 }
     33 
     34 void __RPC_USER MIDL_user_free(_Pre_maybenull_ _Post_invalid_ void* ptr)
     35 {
     36     if (ptr)
     37     {
     38         free(ptr);
     39     }
     40 }
     41 
     42 struct FreeWithRpcStringFree { void operator()(RPC_CSTR* in) { RpcStringFreeA(in); } };
     43 using UniqueRpcString = std::unique_ptr<RPC_CSTR, FreeWithRpcStringFree>;
     44 
     45 struct DeleteWithMidlFree { void operator()(void* m) { MIDL_user_free(m); } };
     46 using UniqueMidl = std::unique_ptr<BYTE, DeleteWithMidlFree>;
     47 
     48 void InitializeRpcBinding()
     49 {
     50     std::string protocol = "ncacn_np";
     51     std::string endpoint = "\\pipe\\WinGetServerManualActivation_" + GetUserSID();
     52 
     53     unsigned char* binding = nullptr;
     54     UniqueRpcString bindingPtr;
     55 
     56     RPC_STATUS status = RpcStringBindingComposeA(nullptr, GetUCharString(protocol), nullptr, GetUCharString(endpoint), nullptr, &binding);
     57     THROW_HR_IF(HRESULT_FROM_WIN32(status), status != RPC_S_OK);
     58     bindingPtr.reset(&binding);
     59 
     60     status = RpcBindingFromStringBindingA(binding, &WinGetServerManualActivation_IfHandle);
     61     THROW_HR_IF(HRESULT_FROM_WIN32(status), status != RPC_S_OK);
     62 }
     63 
     64 struct ServerProcessLauncher
     65 {
     66     ServerProcessLauncher()
     67     {
     68         try
     69         {
     70             m_serverExePath = GetPackageLocation(s_ServerPackageFamilyName, s_ServerFileName) / s_ServerFileName;
     71 
     72 #ifndef USE_PROD_WINGET_SERVER
     73             // The feature that allows directly launching a packaged process as long as it has a matching alias
     74             // requires a failure to trigger, and the dev package is not ACL'd to force this to happen. Attempting
     75             // to use the other code path results in an unpackaged server, causing other issues.
     76             // We run the product code above to ensure that it is functioning properly, but then replace it with
     77             // the path of the alias.
     78             m_serverExePath = GetKnownFolderPath(FOLDERID_LocalAppData) / s_LocalAppDataRelativeServerExePath;
     79 #endif
     80         }
     81         catch (wil::ResultException& re)
     82         {
     83             m_hr = re.GetErrorCode();
     84         }
     85     }
     86 
     87     HRESULT LaunchWinGetServerWithManualActivation()
     88     {
     89         RETURN_IF_FAILED(m_hr);
     90 
     91         std::wstring commandLineInput = std::wstring{ m_serverExePath } + L" --manualActivation";
     92 
     93         STARTUPINFO info = { sizeof(info) };
     94         wil::unique_process_information process;
     95 
     96         RETURN_LAST_ERROR_IF(!CreateProcessW(NULL, &commandLineInput[0], NULL, NULL, FALSE, 0, NULL, NULL, &info, &process));
     97 
     98         // Wait for manual reset event from server before proceeding with COM activation.
     99         wil::unique_event manualResetEvent = CreateOrOpenServerStartEvent();
    100         manualResetEvent.wait(10000);
    101 
    102         return S_OK;
    103     }
    104 
    105 private:
    106     std::filesystem::path GetPackageLocation(std::wstring_view packageFamilyName, std::wstring_view fileName)
    107     {
    108         std::wstring pfn{ packageFamilyName };
    109         UINT32 count = 0;
    110         std::unique_ptr<PWSTR[]> fullNames;
    111         UINT32 bufferLength = 0;
    112         std::unique_ptr<WCHAR[]> buffer;
    113         std::unique_ptr<UINT32[]> properties;
    114 
    115         LONG result = FindPackagesByPackageFamily(pfn.c_str(), PACKAGE_FILTER_HEAD, &count, nullptr, &bufferLength, nullptr, nullptr);
    116         THROW_WIN32_IF(result, result != ERROR_INSUFFICIENT_BUFFER);
    117 
    118         for (size_t i = 0; i < 10 && result == ERROR_INSUFFICIENT_BUFFER; ++i)
    119         {
    120             fullNames = std::make_unique<PWSTR[]>(count);
    121             buffer = std::make_unique<WCHAR[]>(bufferLength);
    122             properties = std::make_unique<UINT32[]>(count);
    123 
    124             result = FindPackagesByPackageFamily(pfn.c_str(), PACKAGE_FILTER_HEAD, &count, fullNames.get(), &bufferLength, buffer.get(), properties.get());
    125         }
    126 
    127         THROW_IF_WIN32_ERROR(result);
    128 
    129         for (UINT32 i = 0; i < count; ++i)
    130         {
    131             // Includes null terminator
    132             UINT32 pathLength = 0;
    133             result = GetPackagePathByFullName(fullNames[i], &pathLength, nullptr);
    134             if (result != ERROR_INSUFFICIENT_BUFFER)
    135             {
    136                 continue;
    137             }
    138 
    139             std::wstring packagePath;
    140             packagePath.resize(static_cast<size_t>(pathLength));
    141 
    142             if (FAILED_WIN32(GetPackagePathByFullName(fullNames[i], &pathLength, &packagePath[0])))
    143             {
    144                 continue;
    145             }
    146             packagePath.resize(static_cast<size_t>(pathLength - 1), L'\0');
    147 
    148             std::filesystem::path resultPath = std::move(packagePath);
    149             std::filesystem::path exePath = resultPath / fileName;
    150 
    151             if (GetFileAttributesW(exePath.c_str()) != INVALID_FILE_ATTRIBUTES)
    152             {
    153                 return resultPath;
    154             }
    155         }
    156 
    157         THROW_WIN32(ERROR_PACKAGE_NOT_REGISTERED_FOR_USER);
    158     }
    159 
    160     std::filesystem::path GetKnownFolderPath(const KNOWNFOLDERID& id)
    161     {
    162         wil::unique_cotaskmem_string knownFolder = nullptr;
    163         THROW_IF_FAILED(SHGetKnownFolderPath(id, KF_FLAG_NO_ALIAS | KF_FLAG_DONT_VERIFY | KF_FLAG_NO_PACKAGE_REDIRECTION, NULL, &knownFolder));
    164         return knownFolder.get();
    165     }
    166 
    167     std::filesystem::path m_serverExePath;
    168     HRESULT m_hr = S_OK;
    169 };
    170 
    171 HRESULT CallCreateInstance(REFCLSID rclsid, REFIID riid, UINT32 flags, UINT32* bufferByteCount, BYTE** buffer)
    172 {
    173     RpcTryExcept
    174     {
    175         RETURN_IF_FAILED(CreateInstance(rclsid, riid, flags, bufferByteCount, buffer));
    176     }
    177     RpcExcept(1)
    178     {
    179         return HRESULT_FROM_WIN32(RpcExceptionCode());
    180     }
    181     RpcEndExcept;
    182 
    183     return S_OK;
    184 }
    185 
    186 HRESULT CreateComInstance(REFCLSID rclsid, REFIID riid, UINT32 flags, void** out)
    187 {
    188     UINT32 bufferByteCount = 0;
    189     BYTE* buffer = nullptr;
    190     UniqueMidl bufferPtr;
    191 
    192     RETURN_IF_FAILED(CallCreateInstance(rclsid, riid, flags, &bufferByteCount, &buffer));
    193 
    194     bufferPtr.reset(buffer);
    195 
    196     wil::com_ptr<IStream> stream;
    197     RETURN_IF_FAILED(CreateStreamOnHGlobal(nullptr, TRUE, &stream));
    198     RETURN_IF_FAILED(stream->Write(buffer, bufferByteCount, nullptr));
    199     RETURN_IF_FAILED(stream->Seek({}, STREAM_SEEK_SET, nullptr));
    200 
    201     wil::com_ptr<IUnknown> output;
    202     RETURN_IF_FAILED(CoUnmarshalInterface(stream.get(), riid, reinterpret_cast<void**>(&output)));
    203     *out = output.detach();
    204     return S_OK;
    205 }
    206 
    207 extern "C" HRESULT WinGetServerManualActivation_CreateInstance(REFCLSID rclsid, REFIID riid, UINT32 flags, void** out)
    208 {
    209     RETURN_HR_IF_NULL(E_POINTER, out);
    210 
    211     static std::once_flag rpcBindingOnce;
    212     try
    213     {
    214         std::call_once(rpcBindingOnce, InitializeRpcBinding);
    215     }
    216     CATCH_RETURN();
    217 
    218     HRESULT result = CreateComInstance(rclsid, riid, flags, out);
    219     if (FAILED(result))
    220     {
    221         ServerProcessLauncher launcher;
    222 
    223         for (int i = 0; i < 3; i++)
    224         {
    225             result = launcher.LaunchWinGetServerWithManualActivation();
    226             if (result == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) || result == HRESULT_FROM_WIN32(ERROR_PACKAGE_NOT_REGISTERED_FOR_USER))
    227             {
    228                 break;
    229             }
    230 
    231             result = CreateComInstance(rclsid, riid, flags, out);
    232             if (SUCCEEDED(result))
    233             {
    234                 break;
    235             }
    236 
    237             Sleep(200);
    238         }
    239     }
    240 
    241     return result;
    242 }
    243 
    244 extern "C" HRESULT WinGetServerManualActivation_Terminate()
    245 {
    246     RpcBindingFree(&WinGetServerManualActivation_IfHandle);
    247     return S_OK;
    248 }