| 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 | |
| 6 | // struct |
| 7 | |
| 8 | struct BURN_EMBEDDED_CALLBACK_CONTEXT |
| 9 | { |
| 10 | PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler; |
| 11 | LPVOID pvContext; |
| 12 | }; |
| 13 | |
| 14 | // internal function declarations |
| 15 | |
| 16 | static HRESULT ProcessEmbeddedMessages( |
| 17 | __in PIPE_MESSAGE* pMsg, |
| 18 | __in_opt LPVOID pvContext, |
| 19 | __out DWORD* pdwResult |
| 20 | ); |
| 21 | static HRESULT OnEmbeddedErrorMessage( |
| 22 | __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler, |
| 23 | __in LPVOID pvContext, |
| 24 | __in_bcount(cbData) BYTE* pbData, |
| 25 | __in SIZE_T cbData, |
| 26 | __out DWORD* pdwResult |
| 27 | ); |
| 28 | static HRESULT OnEmbeddedProgress( |
| 29 | __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler, |
| 30 | __in LPVOID pvContext, |
| 31 | __in_bcount(cbData) BYTE* pbData, |
| 32 | __in SIZE_T cbData, |
| 33 | __out DWORD* pdwResult |
| 34 | ); |
| 35 | |
| 36 | // function definitions |
| 37 | |
| 38 | /******************************************************************* |
| 39 | EmbeddedRunBundle - |
| 40 | |
| 41 | *******************************************************************/ |
| 42 | extern "C" HRESULT EmbeddedRunBundle( |
| 43 | __in BURN_PIPE_CONNECTION* pConnection, |
| 44 | __in_z LPCWSTR wzExecutablePath, |
| 45 | __in_z LPWSTR sczBaseCommand, |
| 46 | __in_z_opt LPCWSTR wzUserArgs, |
| 47 | __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, |
| 48 | __in LPVOID pvContext, |
| 49 | __out DWORD* pdwExitCode |
| 50 | ) |
| 51 | { |
| 52 | HRESULT hr = S_OK; |
| 53 | DWORD dwCurrentProcessId = ::GetCurrentProcessId(); |
| 54 | LPWSTR sczCommand = NULL; |
| 55 | PROCESS_INFORMATION pi = { }; |
| 56 | BURN_PIPE_RESULT result = { }; |
| 57 | |
| 58 | BurnPipeConnectionInitialize(pConnection); |
| 59 | |
| 60 | BURN_EMBEDDED_CALLBACK_CONTEXT context = { }; |
| 61 | context.pfnGenericMessageHandler = pfnGenericMessageHandler; |
| 62 | context.pvContext = pvContext; |
| 63 | |
| 64 | hr = BurnPipeCreateNameAndSecret(&pConnection->sczName, &pConnection->sczSecret); |
| 65 | ExitOnFailure(hr, "Failed to create embedded pipe name and client token."); |
| 66 | |
| 67 | hr = BurnPipeCreatePipes(pConnection, FALSE); |
| 68 | ExitOnFailure(hr, "Failed to create embedded pipe."); |
| 69 | |
| 70 | hr = StrAllocFormatted(&sczCommand, L"%ls -%ls %ls %ls %u", sczBaseCommand, BURN_COMMANDLINE_SWITCH_EMBEDDED, pConnection->sczName, pConnection->sczSecret, dwCurrentProcessId); |
| 71 | ExitOnFailure(hr, "Failed to append embedded args."); |
| 72 | |
| 73 | // Always add user supplied arguments last. |
| 74 | if (wzUserArgs) |
| 75 | { |
| 76 | hr = StrAllocConcatFormattedSecure(&sczCommand, L" %ls", wzUserArgs); |
| 77 | ExitOnFailure(hr, "Failed to append user args."); |
| 78 | } |
| 79 | |
| 80 | hr = CoreCreateProcess(wzExecutablePath, sczCommand, TRUE, CREATE_NO_WINDOW, NULL, 0, &pi); |
| 81 | ExitOnFailure(hr, "Failed to create embedded process at path: %ls", wzExecutablePath); |
| 82 | |
| 83 | pConnection->dwProcessId = ::GetProcessId(pi.hProcess); |
| 84 | pConnection->hProcess = pi.hProcess; |
| 85 | pi.hProcess = NULL; |
| 86 | |
| 87 | hr = BurnPipeWaitForChildConnect(pConnection); |
| 88 | ExitOnFailure(hr, "Failed to wait for embedded process to connect to pipe."); |
| 89 | |
| 90 | hr = BurnPipePumpMessages(pConnection->hPipe, ProcessEmbeddedMessages, &context, &result); |
| 91 | ExitOnFailure(hr, "Failed to process messages from embedded message."); |
| 92 | |
| 93 | // Get the return code from the embedded process. |
| 94 | hr = CoreWaitForProcCompletion(pConnection->hProcess, INFINITE, pdwExitCode); |
| 95 | ExitOnFailure(hr, "Failed to wait for embedded executable: %ls", wzExecutablePath); |
| 96 | |
| 97 | LExit: |
| 98 | ReleaseHandle(pi.hThread); |
| 99 | ReleaseHandle(pi.hProcess); |
| 100 | |
| 101 | StrSecureZeroFreeString(sczCommand); |
| 102 | BurnPipeConnectionUninitialize(pConnection); |
| 103 | |
| 104 | return hr; |
| 105 | } |
| 106 | |
| 107 | |
| 108 | // internal function definitions |
| 109 | |
| 110 | static HRESULT ProcessEmbeddedMessages( |
| 111 | __in PIPE_MESSAGE* pMsg, |
| 112 | __in_opt LPVOID pvContext, |
| 113 | __out DWORD* pdwResult |
| 114 | ) |
| 115 | { |
| 116 | HRESULT hr = S_OK; |
| 117 | BURN_EMBEDDED_CALLBACK_CONTEXT* pContext = static_cast<BURN_EMBEDDED_CALLBACK_CONTEXT*>(pvContext); |
| 118 | DWORD dwResult = 0; |
| 119 | |
| 120 | // Process the message. |
| 121 | switch (pMsg->dwMessageType) |
| 122 | { |
| 123 | case BURN_EMBEDDED_MESSAGE_TYPE_ERROR: |
| 124 | hr = OnEmbeddedErrorMessage(pContext->pfnGenericMessageHandler, pContext->pvContext, static_cast<BYTE*>(pMsg->pvData), pMsg->cbData, &dwResult); |
| 125 | ExitOnFailure(hr, "Failed to process embedded error message."); |
| 126 | break; |
| 127 | |
| 128 | case BURN_EMBEDDED_MESSAGE_TYPE_PROGRESS: |
| 129 | hr = OnEmbeddedProgress(pContext->pfnGenericMessageHandler, pContext->pvContext, static_cast<BYTE*>(pMsg->pvData), pMsg->cbData, &dwResult); |
| 130 | ExitOnFailure(hr, "Failed to process embedded progress message."); |
| 131 | break; |
| 132 | |
| 133 | default: |
| 134 | LogStringLine(REPORT_DEBUG, "Unexpected embedded message received from child process, msg: %u", pMsg->dwMessageType); |
| 135 | dwResult = (DWORD)E_NOTIMPL; |
| 136 | } |
| 137 | |
| 138 | *pdwResult = dwResult; |
| 139 | |
| 140 | LExit: |
| 141 | return hr; |
| 142 | } |
| 143 | |
| 144 | static HRESULT OnEmbeddedErrorMessage( |
| 145 | __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler, |
| 146 | __in LPVOID pvContext, |
| 147 | __in_bcount(cbData) BYTE* pbData, |
| 148 | __in SIZE_T cbData, |
| 149 | __out DWORD* pdwResult |
| 150 | ) |
| 151 | { |
| 152 | HRESULT hr = S_OK; |
| 153 | SIZE_T iData = 0; |
| 154 | GENERIC_EXECUTE_MESSAGE message = { }; |
| 155 | LPWSTR sczMessage = NULL; |
| 156 | |
| 157 | message.type = GENERIC_EXECUTE_MESSAGE_ERROR; |
| 158 | |
| 159 | hr = BuffReadNumber(pbData, cbData, &iData, &message.error.dwErrorCode); |
| 160 | ExitOnFailure(hr, "Failed to read error code from buffer."); |
| 161 | |
| 162 | hr = BuffReadString(pbData, cbData, &iData, &sczMessage); |
| 163 | ExitOnFailure(hr, "Failed to read error message from buffer."); |
| 164 | |
| 165 | message.error.wzMessage = sczMessage; |
| 166 | |
| 167 | hr = BuffReadNumber(pbData, cbData, &iData, &message.dwUIHint); |
| 168 | ExitOnFailure(hr, "Failed to read UI hint from buffer."); |
| 169 | |
| 170 | *pdwResult = (DWORD)pfnMessageHandler(&message, pvContext); |
| 171 | |
| 172 | LExit: |
| 173 | ReleaseStr(sczMessage); |
| 174 | |
| 175 | return hr; |
| 176 | } |
| 177 | |
| 178 | static HRESULT OnEmbeddedProgress( |
| 179 | __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler, |
| 180 | __in LPVOID pvContext, |
| 181 | __in_bcount(cbData) BYTE* pbData, |
| 182 | __in SIZE_T cbData, |
| 183 | __out DWORD* pdwResult |
| 184 | ) |
| 185 | { |
| 186 | HRESULT hr = S_OK; |
| 187 | SIZE_T iData = 0; |
| 188 | GENERIC_EXECUTE_MESSAGE message = { }; |
| 189 | |
| 190 | message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; |
| 191 | message.dwUIHint = MB_OKCANCEL; |
| 192 | |
| 193 | hr = BuffReadNumber(pbData, cbData, &iData, &message.progress.dwPercentage); |
| 194 | ExitOnFailure(hr, "Failed to read progress from buffer."); |
| 195 | |
| 196 | *pdwResult = (DWORD)pfnMessageHandler(&message, pvContext); |
| 197 | |
| 198 | LExit: |
| 199 | return hr; |
| 200 | } |