| 1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. |
| 2 | |
| 3 | #include "precomp.h" |
| 4 | |
| 5 | static VOID DestroyNetFxChainer( |
| 6 | __in NetFxChainer* pChainer |
| 7 | ) |
| 8 | { |
| 9 | if (pChainer) |
| 10 | { |
| 11 | ReleaseHandle(pChainer->hSection); |
| 12 | ReleaseHandle(pChainer->hEventChaineeSend); |
| 13 | ReleaseHandle(pChainer->hEventChainerSend); |
| 14 | ReleaseHandle(pChainer->hMutex); |
| 15 | |
| 16 | if (pChainer->pData) |
| 17 | { |
| 18 | ::UnmapViewOfFile(pChainer->pData); |
| 19 | } |
| 20 | |
| 21 | MemFree(pChainer); |
| 22 | } |
| 23 | } |
| 24 | |
| 25 | static HRESULT CreateNetFxChainer( |
| 26 | __in LPCWSTR wzSectionName, |
| 27 | __in LPCWSTR wzEventName, |
| 28 | __out NetFxChainer** ppChainer |
| 29 | ) |
| 30 | { |
| 31 | HRESULT hr = S_OK; |
| 32 | LPWSTR sczName = NULL; |
| 33 | NetFxChainer* pChainer = NULL; |
| 34 | |
| 35 | pChainer = (NetFxChainer*)MemAlloc(sizeof(NetFxChainer), TRUE); |
| 36 | ExitOnNull(pChainer, hr, E_OUTOFMEMORY, "Failed to allocate memory for NetFxChainer struct."); |
| 37 | |
| 38 | pChainer->hEventChaineeSend = ::CreateEvent(NULL, FALSE, FALSE, wzEventName); |
| 39 | ExitOnNullWithLastError(pChainer->hEventChaineeSend, hr, "Failed to create event: %ls", wzEventName); |
| 40 | |
| 41 | hr = StrAllocFormatted(&sczName, L"%ls_send", wzEventName); |
| 42 | ExitOnFailure(hr, "failed to allocate memory for event name"); |
| 43 | |
| 44 | pChainer->hEventChainerSend = ::CreateEvent(NULL, FALSE, FALSE, sczName); |
| 45 | ExitOnNullWithLastError(pChainer->hEventChainerSend, hr, "Failed to create event: %ls", sczName); |
| 46 | |
| 47 | hr = StrAllocFormatted(&sczName, L"%ls_mutex", wzEventName); |
| 48 | ExitOnFailure(hr, "failed to allocate memory for mutex name"); |
| 49 | |
| 50 | // Create the mutex, we initially own |
| 51 | pChainer->hMutex = ::CreateMutex(NULL, TRUE, sczName); |
| 52 | ExitOnNullWithLastError(pChainer->hMutex, hr, "Failed to create mutex: %ls", sczName); |
| 53 | |
| 54 | pChainer->hSection = ::CreateFileMapping(INVALID_HANDLE_VALUE, |
| 55 | NULL, // security attributes |
| 56 | PAGE_READWRITE, |
| 57 | 0, // high-order DWORD of maximum size |
| 58 | NETFXDATA_SIZE, // low-order DWORD of maximum size |
| 59 | wzSectionName); |
| 60 | ExitOnNullWithLastError(pChainer->hSection, hr, "Failed to memory map cabinet file: %ls", wzSectionName); |
| 61 | |
| 62 | pChainer->pData = reinterpret_cast<NetFxDataStructure*>(::MapViewOfFile(pChainer->hSection, |
| 63 | FILE_MAP_WRITE, |
| 64 | 0, 0, // offsets |
| 65 | 0 // map entire file |
| 66 | )); |
| 67 | ExitOnNullWithLastError(pChainer->pData, hr, "Failed to MapViewOfFile for %ls.", wzSectionName); |
| 68 | |
| 69 | // Initialize the shared memory |
| 70 | hr = ::StringCchCopyW(pChainer->pData->szEventName, countof(pChainer->pData->szEventName), wzEventName); |
| 71 | ExitOnFailure(hr, "failed to copy event name to shared memory structure."); |
| 72 | pChainer->pData->downloadFinished = false; |
| 73 | pChainer->pData->downloadSoFar = 0; |
| 74 | pChainer->pData->hrDownloadFinished = E_PENDING; |
| 75 | pChainer->pData->downloadAbort = false; |
| 76 | pChainer->pData->installFinished = false; |
| 77 | pChainer->pData->installSoFar = 0; |
| 78 | pChainer->pData->hrInstallFinished = E_PENDING; |
| 79 | pChainer->pData->installAbort = false; |
| 80 | pChainer->pData->hrInternalError = S_OK; |
| 81 | pChainer->pData->version = NETFXDATA_VERSION; |
| 82 | pChainer->pData->messageCode = 0; |
| 83 | pChainer->pData->messageResponse = 0; |
| 84 | pChainer->pData->messageDataLength = 0; |
| 85 | |
| 86 | // Done with initialization, allow others to access. |
| 87 | ::ReleaseMutex(pChainer->hMutex); |
| 88 | |
| 89 | *ppChainer = pChainer; |
| 90 | pChainer = NULL; |
| 91 | |
| 92 | LExit: |
| 93 | ReleaseStr(sczName); |
| 94 | |
| 95 | if (pChainer) |
| 96 | { |
| 97 | // Something failed, release the mutex and destroy the object |
| 98 | if (pChainer->hMutex) |
| 99 | { |
| 100 | ::ReleaseMutex(pChainer->hMutex); |
| 101 | } |
| 102 | |
| 103 | DestroyNetFxChainer(pChainer); |
| 104 | } |
| 105 | |
| 106 | return hr; |
| 107 | } |
| 108 | |
| 109 | |
| 110 | static VOID NetFxAbort( |
| 111 | __in NetFxChainer* pChainer |
| 112 | ) |
| 113 | { |
| 114 | ::WaitForSingleObject(pChainer->hMutex, INFINITE); |
| 115 | |
| 116 | pChainer->pData->downloadAbort = true; |
| 117 | pChainer->pData->installAbort = true; |
| 118 | |
| 119 | ::ReleaseMutex(pChainer->hMutex); |
| 120 | |
| 121 | ::SetEvent(pChainer->hEventChainerSend); |
| 122 | } |
| 123 | |
| 124 | static BYTE NetFxGetProgress( |
| 125 | __in NetFxChainer* pChainer |
| 126 | ) |
| 127 | { |
| 128 | BYTE bProgress = 0; |
| 129 | ::WaitForSingleObject(pChainer->hMutex, INFINITE); |
| 130 | |
| 131 | bProgress = (pChainer->pData->installSoFar + pChainer->pData->downloadSoFar) / 2; |
| 132 | |
| 133 | ::ReleaseMutex(pChainer->hMutex); |
| 134 | |
| 135 | return bProgress; |
| 136 | } |
| 137 | |
| 138 | static HRESULT NetFxGetMessage( |
| 139 | __in NetFxChainer* pChainer, |
| 140 | __out DWORD* pdwMessage, |
| 141 | __out LPVOID* ppBuffer, |
| 142 | __out DWORD* pdwBufferSize |
| 143 | ) |
| 144 | { |
| 145 | HRESULT hr = S_OK; |
| 146 | ::WaitForSingleObject(pChainer->hMutex, INFINITE); |
| 147 | |
| 148 | *pdwMessage = pChainer->pData->messageCode; |
| 149 | *ppBuffer = NULL; |
| 150 | *pdwBufferSize = 0; |
| 151 | |
| 152 | if (NETFX_NO_MESSAGE != *pdwMessage) |
| 153 | { |
| 154 | *ppBuffer = MemAlloc(pChainer->pData->messageDataLength, TRUE); |
| 155 | ExitOnNull(*ppBuffer, hr, E_OUTOFMEMORY, "Failed to allocate memory for message data"); |
| 156 | |
| 157 | memcpy(*ppBuffer, pChainer->pData->messageData, pChainer->pData->messageDataLength); |
| 158 | *pdwBufferSize = pChainer->pData->messageDataLength; |
| 159 | } |
| 160 | |
| 161 | LExit: |
| 162 | ::ReleaseMutex(pChainer->hMutex); |
| 163 | |
| 164 | return hr; |
| 165 | } |
| 166 | |
| 167 | static void NetFxRespond( |
| 168 | __in NetFxChainer* pChainer, |
| 169 | __in DWORD dwResponse |
| 170 | ) |
| 171 | { |
| 172 | ::WaitForSingleObject(pChainer->hMutex, INFINITE); |
| 173 | |
| 174 | pChainer->pData->messageCode = NETFX_NO_MESSAGE; |
| 175 | pChainer->pData->messageResponse = dwResponse; |
| 176 | if (IDCANCEL == dwResponse) |
| 177 | { |
| 178 | pChainer->pData->downloadAbort = true; |
| 179 | pChainer->pData->installAbort = true; |
| 180 | } |
| 181 | |
| 182 | ::ReleaseMutex(pChainer->hMutex); |
| 183 | |
| 184 | ::SetEvent(pChainer->hEventChainerSend); |
| 185 | } |
| 186 | |
| 187 | static HRESULT NetFxGetResult( |
| 188 | __in NetFxChainer* pChainer, |
| 189 | __out HRESULT* phrInternalError |
| 190 | ) |
| 191 | { |
| 192 | HRESULT hr = S_OK; |
| 193 | ::WaitForSingleObject(pChainer->hMutex, INFINITE); |
| 194 | |
| 195 | hr = pChainer->pData->hrInstallFinished; |
| 196 | |
| 197 | if (FAILED(pChainer->pData->hrDownloadFinished) && // Download failed |
| 198 | (S_OK == hr || E_ABORT == hr)) // Install succeeded or was aborted |
| 199 | { |
| 200 | hr = pChainer->pData->hrDownloadFinished; |
| 201 | } |
| 202 | |
| 203 | if (phrInternalError) |
| 204 | { |
| 205 | *phrInternalError = pChainer->pData->hrInternalError; |
| 206 | } |
| 207 | |
| 208 | ::ReleaseMutex(pChainer->hMutex); |
| 209 | |
| 210 | return hr; |
| 211 | } |
| 212 | |
| 213 | static HRESULT OnNetFxFilesInUse( |
| 214 | __in NetFxChainer* pNetfxChainer, |
| 215 | __in NetFxCloseApplications* pCloseApps, |
| 216 | __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler, |
| 217 | __in LPVOID pvContext |
| 218 | ) |
| 219 | { |
| 220 | HRESULT hr = S_OK; |
| 221 | DWORD cFiles = 0; |
| 222 | LPWSTR* rgwzFiles = NULL; |
| 223 | GENERIC_EXECUTE_MESSAGE message = { }; |
| 224 | DWORD dwResponse = 0; |
| 225 | |
| 226 | cFiles = pCloseApps->dwApplicationsSize; |
| 227 | rgwzFiles = (LPWSTR*)MemAlloc(sizeof(LPWSTR*) * cFiles, TRUE); |
| 228 | ExitOnNull(rgwzFiles, hr, E_OUTOFMEMORY, "Failed to allocate buffer."); |
| 229 | |
| 230 | for (DWORD i = 0; i < pCloseApps->dwApplicationsSize; ++i) |
| 231 | { |
| 232 | rgwzFiles[i] = pCloseApps->applications[i].szName; |
| 233 | } |
| 234 | |
| 235 | // send message |
| 236 | message.type = GENERIC_EXECUTE_MESSAGE_NETFX_FILES_IN_USE; |
| 237 | message.dwUIHint = MB_ABORTRETRYIGNORE; |
| 238 | message.filesInUse.cFiles = cFiles; |
| 239 | message.filesInUse.rgwzFiles = (LPCWSTR*)rgwzFiles; |
| 240 | dwResponse = (DWORD)pfnMessageHandler(&message, pvContext); |
| 241 | |
| 242 | NetFxRespond(pNetfxChainer, dwResponse); |
| 243 | |
| 244 | LExit: |
| 245 | ReleaseMem(rgwzFiles); |
| 246 | |
| 247 | return hr; |
| 248 | } |
| 249 | |
| 250 | static HRESULT OnNetFxProgress( |
| 251 | __in NetFxChainer* pNetfxChainer, |
| 252 | __in BYTE bProgress, |
| 253 | __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler, |
| 254 | __in LPVOID pvContext |
| 255 | ) |
| 256 | { |
| 257 | GENERIC_EXECUTE_MESSAGE message = { }; |
| 258 | DWORD dwResponse = 0; |
| 259 | |
| 260 | // send message |
| 261 | message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; |
| 262 | message.dwUIHint = MB_OKCANCEL; |
| 263 | message.progress.dwPercentage = 100 * (DWORD)bProgress / BYTE_MAX; |
| 264 | dwResponse = (DWORD)pfnMessageHandler(&message, pvContext); |
| 265 | |
| 266 | if (IDCANCEL == dwResponse) |
| 267 | { |
| 268 | NetFxAbort(pNetfxChainer); |
| 269 | } |
| 270 | |
| 271 | return S_OK; |
| 272 | } |
| 273 | |
| 274 | static HRESULT OnNetFxError( |
| 275 | __in NetFxChainer* /*pNetfxChainer*/, |
| 276 | __in HRESULT hrError, |
| 277 | __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler, |
| 278 | __in LPVOID pvContext |
| 279 | ) |
| 280 | { |
| 281 | GENERIC_EXECUTE_MESSAGE message = { }; |
| 282 | DWORD dwResponse = 0; |
| 283 | |
| 284 | // send message |
| 285 | message.type = GENERIC_EXECUTE_MESSAGE_ERROR; |
| 286 | message.dwUIHint = MB_OK; |
| 287 | message.error.dwErrorCode = hrError; |
| 288 | message.error.wzMessage = NULL; |
| 289 | dwResponse = (DWORD)pfnMessageHandler(&message, pvContext); |
| 290 | |
| 291 | return S_OK; |
| 292 | } |
| 293 | |
| 294 | static HRESULT ProcessNetFxMessage( |
| 295 | __in NetFxChainer* pNetfxChainer, |
| 296 | __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, |
| 297 | __in LPVOID pvContext |
| 298 | ) |
| 299 | { |
| 300 | HRESULT hr = S_OK; |
| 301 | DWORD dwMessage = NETFX_NO_MESSAGE; |
| 302 | DWORD dwBufferSize = 0; |
| 303 | LPVOID pBuffer = NULL; |
| 304 | |
| 305 | // send progress |
| 306 | hr = OnNetFxProgress(pNetfxChainer, NetFxGetProgress(pNetfxChainer), pfnGenericMessageHandler, pvContext); |
| 307 | ExitOnFailure(hr, "Failed to send progress from netfx chainer."); |
| 308 | |
| 309 | // Check for message |
| 310 | hr = NetFxGetMessage(pNetfxChainer, &dwMessage, &pBuffer, &dwBufferSize); |
| 311 | ExitOnFailure(hr, "Failed to get message from netfx chainer."); |
| 312 | |
| 313 | switch(dwMessage) |
| 314 | { |
| 315 | case NETFX_CLOSE_APPS: |
| 316 | hr = OnNetFxFilesInUse(pNetfxChainer, (NetFxCloseApplications*)pBuffer, pfnGenericMessageHandler, pvContext); |
| 317 | ExitOnFailure(hr, "Failed to send files in use message from netfx chainer."); |
| 318 | break; |
| 319 | |
| 320 | default: |
| 321 | // No message we understand. |
| 322 | break; |
| 323 | } |
| 324 | |
| 325 | LExit: |
| 326 | ReleaseMem(pBuffer); |
| 327 | |
| 328 | return hr; |
| 329 | } |
| 330 | |
| 331 | extern "C" HRESULT NetFxRunChainer( |
| 332 | __in_z LPCWSTR wzExecutablePath, |
| 333 | __in_z LPWSTR sczBaseCommand, |
| 334 | __in_z_opt LPCWSTR wzUserArgs, |
| 335 | __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, |
| 336 | __in LPVOID pvContext, |
| 337 | __out DWORD* pdwExitCode |
| 338 | ) |
| 339 | { |
| 340 | HRESULT hr = S_OK; |
| 341 | DWORD dwSignaledIndex = 0; |
| 342 | BOOL fTimedOut = 0; |
| 343 | WCHAR wzGuid[GUID_STRING_LENGTH]; |
| 344 | LPWSTR sczEventName = NULL; |
| 345 | LPWSTR sczSectionName = NULL; |
| 346 | LPWSTR sczCommand = NULL; |
| 347 | NetFxChainer* pNetfxChainer = NULL; |
| 348 | PROCESS_INFORMATION pi = { }; |
| 349 | HRESULT hrInternalError = 0; |
| 350 | |
| 351 | // Create the unique name suffix. |
| 352 | hr = GuidFixedCreate(wzGuid); |
| 353 | ExitOnRootFailure(hr, "Failed to create netfx chainer guid."); |
| 354 | |
| 355 | hr = StrAllocFormatted(&sczSectionName, L"NetFxSection.%ls", wzGuid); |
| 356 | ExitOnFailure(hr, "Failed to allocate section name."); |
| 357 | |
| 358 | hr = StrAllocFormatted(&sczEventName, L"NetFxEvent.%ls", wzGuid); |
| 359 | ExitOnFailure(hr, "Failed to allocate event name."); |
| 360 | |
| 361 | hr = CreateNetFxChainer(sczSectionName, sczEventName, &pNetfxChainer); |
| 362 | ExitOnFailure(hr, "Failed to create netfx chainer."); |
| 363 | |
| 364 | hr = StrAllocFormatted(&sczCommand, L"%ls /pipe %ls", sczBaseCommand, sczSectionName); |
| 365 | ExitOnFailure(hr, "Failed to append netfx chainer args."); |
| 366 | |
| 367 | // Always add user supplied arguments last. |
| 368 | if (wzUserArgs) |
| 369 | { |
| 370 | hr = StrAllocConcatFormattedSecure(&sczCommand, L" %ls", wzUserArgs); |
| 371 | ExitOnFailure(hr, "Failed to append user args."); |
| 372 | } |
| 373 | |
| 374 | hr = CoreCreateProcess(wzExecutablePath, sczCommand, FALSE, CREATE_NO_WINDOW, NULL, 0, &pi); |
| 375 | ExitOnFailure(hr, "Failed to CreateProcess on path: %ls", wzExecutablePath); |
| 376 | |
| 377 | HANDLE handles[2] = { pi.hProcess, pNetfxChainer->hEventChaineeSend }; |
| 378 | |
| 379 | for (;;) |
| 380 | { |
| 381 | hr = AppWaitForMultipleObjects(2, handles, FALSE, 100, &dwSignaledIndex); |
| 382 | ExitOnWaitObjectFailure(hr, fTimedOut, "Failed to wait for netfx chainer process to complete"); |
| 383 | |
| 384 | if (fTimedOut) |
| 385 | { |
| 386 | continue; |
| 387 | } |
| 388 | |
| 389 | switch (dwSignaledIndex) |
| 390 | { |
| 391 | case 0: |
| 392 | // Process has exited |
| 393 | *pdwExitCode = NetFxGetResult(pNetfxChainer, &hrInternalError); |
| 394 | if (E_PENDING == *pdwExitCode) |
| 395 | { |
| 396 | if (!::GetExitCodeProcess(pi.hProcess, pdwExitCode)) |
| 397 | { |
| 398 | ExitWithLastError(hr, "Failed to get netfx return code."); |
| 399 | } |
| 400 | } |
| 401 | else if (FAILED(hrInternalError)) |
| 402 | { |
| 403 | // push internal error message |
| 404 | hr = OnNetFxError(pNetfxChainer, hrInternalError, pfnGenericMessageHandler, pvContext); |
| 405 | ExitOnFailure(hr, "Failed to send internal error message from netfx chainer."); |
| 406 | } |
| 407 | |
| 408 | ExitFunction(); |
| 409 | case 1: |
| 410 | // Chainee has notified us of a change. |
| 411 | hr = ProcessNetFxMessage(pNetfxChainer, pfnGenericMessageHandler, pvContext); |
| 412 | ExitOnFailure(hr, "Failed to process netfx chainer message."); |
| 413 | |
| 414 | break; |
| 415 | } |
| 416 | } |
| 417 | |
| 418 | LExit: |
| 419 | ReleaseStr(sczSectionName); |
| 420 | ReleaseStr(sczEventName); |
| 421 | StrSecureZeroFreeString(sczCommand); |
| 422 | DestroyNetFxChainer(pNetfxChainer); |
| 423 | ReleaseHandle(pi.hThread); |
| 424 | ReleaseHandle(pi.hProcess); |
| 425 | |
| 426 | return hr; |
| 427 | } |