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 }