diff options
author | Rob Mensching <rob@firegiant.com> | 2022-03-31 11:56:14 -0700 |
---|---|---|
committer | Rob Mensching <rob@firegiant.com> | 2022-03-31 18:01:06 -0700 |
commit | 47582b162368e8edf7a3b11c13b8e9dabc5f0a26 (patch) | |
tree | 2c4063eff325684bed39de0edacd7866a257ae02 /src/dtf/SfxCA/RemoteMsiSession.h | |
parent | 167296c42497c4e95f0d5d71168542d747655981 (diff) | |
download | wix-47582b162368e8edf7a3b11c13b8e9dabc5f0a26.tar.gz wix-47582b162368e8edf7a3b11c13b8e9dabc5f0a26.tar.bz2 wix-47582b162368e8edf7a3b11c13b8e9dabc5f0a26.zip |
Provide managed CA and Embedded UI DTF libraries via NuGet
Lots of refactoring to bring the SFX tooling back into the 'dtf'
layer since they are (in the end) tightly coupled to some DTF
assemblies. Also refactored the DTF tests into their own folder
and added a couple integration tests to build using the new CA/UI
NuGet package.
Closes wixtoolset/issues#6080
Diffstat (limited to 'src/dtf/SfxCA/RemoteMsiSession.h')
-rw-r--r-- | src/dtf/SfxCA/RemoteMsiSession.h | 898 |
1 files changed, 898 insertions, 0 deletions
diff --git a/src/dtf/SfxCA/RemoteMsiSession.h b/src/dtf/SfxCA/RemoteMsiSession.h new file mode 100644 index 00000000..90c7c01f --- /dev/null +++ b/src/dtf/SfxCA/RemoteMsiSession.h | |||
@@ -0,0 +1,898 @@ | |||
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 | #define LARGE_BUFFER_THRESHOLD 65536 // bytes | ||
4 | #define MIN_BUFFER_STRING_SIZE 1024 // wchar_ts | ||
5 | |||
6 | /////////////////////////////////////////////////////////////////////////////////////// | ||
7 | // RemoteMsiSession // | ||
8 | ////////////////////// | ||
9 | // | ||
10 | // Allows accessing MSI APIs from another process using named pipes. | ||
11 | // | ||
12 | class RemoteMsiSession | ||
13 | { | ||
14 | public: | ||
15 | |||
16 | // This enumeration MUST stay in sync with the | ||
17 | // managed equivalent in RemotableNativeMethods.cs! | ||
18 | enum RequestId | ||
19 | { | ||
20 | EndSession = 0, | ||
21 | MsiCloseHandle, | ||
22 | MsiCreateRecord, | ||
23 | MsiDatabaseGetPrimaryKeys, | ||
24 | MsiDatabaseIsTablePersistent, | ||
25 | MsiDatabaseOpenView, | ||
26 | MsiDoAction, | ||
27 | MsiEnumComponentCosts, | ||
28 | MsiEvaluateCondition, | ||
29 | MsiFormatRecord, | ||
30 | MsiGetActiveDatabase, | ||
31 | MsiGetComponentState, | ||
32 | MsiGetFeatureCost, | ||
33 | MsiGetFeatureState, | ||
34 | MsiGetFeatureValidStates, | ||
35 | MsiGetLanguage, | ||
36 | MsiGetLastErrorRecord, | ||
37 | MsiGetMode, | ||
38 | MsiGetProperty, | ||
39 | MsiGetSourcePath, | ||
40 | MsiGetSummaryInformation, | ||
41 | MsiGetTargetPath, | ||
42 | MsiProcessMessage, | ||
43 | MsiRecordClearData, | ||
44 | MsiRecordDataSize, | ||
45 | MsiRecordGetFieldCount, | ||
46 | MsiRecordGetInteger, | ||
47 | MsiRecordGetString, | ||
48 | MsiRecordIsNull, | ||
49 | MsiRecordReadStream, | ||
50 | MsiRecordSetInteger, | ||
51 | MsiRecordSetStream, | ||
52 | MsiRecordSetString, | ||
53 | MsiSequence, | ||
54 | MsiSetComponentState, | ||
55 | MsiSetFeatureAttributes, | ||
56 | MsiSetFeatureState, | ||
57 | MsiSetInstallLevel, | ||
58 | MsiSetMode, | ||
59 | MsiSetProperty, | ||
60 | MsiSetTargetPath, | ||
61 | MsiSummaryInfoGetProperty, | ||
62 | MsiVerifyDiskSpace, | ||
63 | MsiViewExecute, | ||
64 | MsiViewFetch, | ||
65 | MsiViewGetError, | ||
66 | MsiViewGetColumnInfo, | ||
67 | MsiViewModify, | ||
68 | }; | ||
69 | |||
70 | static const int MAX_REQUEST_FIELDS = 4; | ||
71 | |||
72 | // Used to pass data back and forth for remote API calls, | ||
73 | // including in & out params & return values. | ||
74 | // Only strings and ints are supported. | ||
75 | struct RequestData | ||
76 | { | ||
77 | struct | ||
78 | { | ||
79 | VARENUM vt; | ||
80 | union { | ||
81 | int iValue; | ||
82 | UINT uiValue; | ||
83 | DWORD cchValue; | ||
84 | LPWSTR szValue; | ||
85 | BYTE* sValue; | ||
86 | DWORD cbValue; | ||
87 | }; | ||
88 | } fields[MAX_REQUEST_FIELDS]; | ||
89 | }; | ||
90 | |||
91 | public: | ||
92 | |||
93 | // This value is set from the single data parameter in the EndSession request. | ||
94 | // It saves the exit code of the out-of-proc custom action. | ||
95 | int ExitCode; | ||
96 | |||
97 | ///////////////////////////////////////////////////////////////////////////////////// | ||
98 | // RemoteMsiSession constructor | ||
99 | // | ||
100 | // Creates a new remote session instance, for use either by the server | ||
101 | // or client process. | ||
102 | // | ||
103 | // szName - Identifies the session instance being remoted. The server and | ||
104 | // the client must use the same name. The name should be unique | ||
105 | // enough to avoid conflicting with other instances on the system. | ||
106 | // | ||
107 | // fServer - True if the calling process is the server process, false if the | ||
108 | // calling process is the client process. | ||
109 | // | ||
110 | RemoteMsiSession(const wchar_t* szName, bool fServer=true) | ||
111 | : m_fServer(fServer), | ||
112 | m_szName(szName != NULL && szName[0] != L'\0' ? szName : L"RemoteMsiSession"), | ||
113 | m_szPipeName(NULL), | ||
114 | m_hPipe(NULL), | ||
115 | m_fConnecting(false), | ||
116 | m_fConnected(false), | ||
117 | m_hReceiveThread(NULL), | ||
118 | m_hReceiveStopEvent(NULL), | ||
119 | m_pBufReceive(NULL), | ||
120 | m_cbBufReceive(0), | ||
121 | m_pBufSend(NULL), | ||
122 | m_cbBufSend(0), | ||
123 | ExitCode(ERROR_INSTALL_FAILURE) | ||
124 | { | ||
125 | SecureZeroMemory(&m_overlapped, sizeof(OVERLAPPED)); | ||
126 | m_overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); | ||
127 | } | ||
128 | |||
129 | ///////////////////////////////////////////////////////////////////////////////////// | ||
130 | // RemoteMsiSession destructor | ||
131 | // | ||
132 | // Closes any open handles and frees any allocated memory. | ||
133 | // | ||
134 | ~RemoteMsiSession() | ||
135 | { | ||
136 | WaitExitCode(); | ||
137 | if (m_hPipe != NULL) | ||
138 | { | ||
139 | CloseHandle(m_hPipe); | ||
140 | m_hPipe = NULL; | ||
141 | } | ||
142 | if (m_overlapped.hEvent != NULL) | ||
143 | { | ||
144 | CloseHandle(m_overlapped.hEvent); | ||
145 | m_overlapped.hEvent = NULL; | ||
146 | } | ||
147 | if (m_szPipeName != NULL) | ||
148 | { | ||
149 | delete[] m_szPipeName; | ||
150 | m_szPipeName = NULL; | ||
151 | } | ||
152 | if (m_pBufReceive != NULL) | ||
153 | { | ||
154 | SecureZeroMemory(m_pBufReceive, m_cbBufReceive); | ||
155 | delete[] m_pBufReceive; | ||
156 | m_pBufReceive = NULL; | ||
157 | } | ||
158 | if (m_pBufSend != NULL) | ||
159 | { | ||
160 | SecureZeroMemory(m_pBufSend, m_cbBufSend); | ||
161 | delete[] m_pBufSend; | ||
162 | m_pBufSend = NULL; | ||
163 | } | ||
164 | m_fConnecting = false; | ||
165 | m_fConnected = false; | ||
166 | } | ||
167 | |||
168 | ///////////////////////////////////////////////////////////////////////////////////// | ||
169 | // RemoteMsiSession::WaitExitCode() | ||
170 | // | ||
171 | // Waits for the server processing thread to complete. | ||
172 | // | ||
173 | void WaitExitCode() | ||
174 | { | ||
175 | if (m_hReceiveThread != NULL) | ||
176 | { | ||
177 | SetEvent(m_hReceiveStopEvent); | ||
178 | WaitForSingleObject(m_hReceiveThread, INFINITE); | ||
179 | CloseHandle(m_hReceiveThread); | ||
180 | m_hReceiveThread = NULL; | ||
181 | } | ||
182 | } | ||
183 | |||
184 | ///////////////////////////////////////////////////////////////////////////////////// | ||
185 | // RemoteMsiSession::Connect() | ||
186 | // | ||
187 | // Connects the inter-process communication channel. | ||
188 | // (Currently implemented as a named pipe.) | ||
189 | // | ||
190 | // This method must be called first by the server process, then by the client | ||
191 | // process. The method does not block; the server will asynchronously wait | ||
192 | // for the client process to make the connection. | ||
193 | // | ||
194 | // Returns: 0 on success, Win32 error code on failure. | ||
195 | // | ||
196 | virtual DWORD Connect() | ||
197 | { | ||
198 | const wchar_t* szPipePrefix = L"\\\\.\\pipe\\"; | ||
199 | size_t cchPipeNameBuf = wcslen(szPipePrefix) + wcslen(m_szName) + 1; | ||
200 | m_szPipeName = new wchar_t[cchPipeNameBuf]; | ||
201 | |||
202 | if (m_szPipeName == NULL) | ||
203 | { | ||
204 | return ERROR_OUTOFMEMORY; | ||
205 | } | ||
206 | else | ||
207 | { | ||
208 | wcscpy_s(m_szPipeName, cchPipeNameBuf, szPipePrefix); | ||
209 | wcscat_s(m_szPipeName, cchPipeNameBuf, m_szName); | ||
210 | |||
211 | if (m_fServer) | ||
212 | { | ||
213 | return this->ConnectPipeServer(); | ||
214 | } | ||
215 | else | ||
216 | { | ||
217 | return this->ConnectPipeClient(); | ||
218 | } | ||
219 | } | ||
220 | } | ||
221 | |||
222 | ///////////////////////////////////////////////////////////////////////////////////// | ||
223 | // RemoteMsiSession::IsConnected() | ||
224 | // | ||
225 | // Checks if the server process and client process are currently connected. | ||
226 | // | ||
227 | virtual bool IsConnected() const | ||
228 | { | ||
229 | return m_fConnected; | ||
230 | } | ||
231 | |||
232 | ///////////////////////////////////////////////////////////////////////////////////// | ||
233 | // RemoteMsiSession::ProcessRequests() | ||
234 | // | ||
235 | // For use by the service process. Watches for requests in the input buffer and calls | ||
236 | // the callback for each one. | ||
237 | // | ||
238 | // This method does not block; it spawns a separate thread to do the work. | ||
239 | // | ||
240 | // Returns: 0 on success, Win32 error code on failure. | ||
241 | // | ||
242 | virtual DWORD ProcessRequests() | ||
243 | { | ||
244 | return this->StartProcessingReqests(); | ||
245 | } | ||
246 | |||
247 | ///////////////////////////////////////////////////////////////////////////////////// | ||
248 | // RemoteMsiSession::SendRequest() | ||
249 | // | ||
250 | // For use by the client process. Sends a request to the server and | ||
251 | // synchronously waits on a response, up to the timeout value. | ||
252 | // | ||
253 | // id - ID code of the MSI API call being requested. | ||
254 | // | ||
255 | // pRequest - Pointer to a data structure containing request parameters. | ||
256 | // | ||
257 | // ppResponse - [OUT] Pointer to a location that receives the response parameters. | ||
258 | // | ||
259 | // Returns: 0 on success, Win32 error code on failure. | ||
260 | // Returns WAIT_TIMEOUT if no response was received in time. | ||
261 | // | ||
262 | virtual DWORD SendRequest(RequestId id, const RequestData* pRequest, RequestData** ppResponse) | ||
263 | { | ||
264 | if (m_fServer) | ||
265 | { | ||
266 | return ERROR_INVALID_OPERATION; | ||
267 | } | ||
268 | |||
269 | if (!m_fConnected) | ||
270 | { | ||
271 | *ppResponse = NULL; | ||
272 | return 0; | ||
273 | } | ||
274 | |||
275 | DWORD dwRet = this->SendRequest(id, pRequest); | ||
276 | if (dwRet != 0) | ||
277 | { | ||
278 | return dwRet; | ||
279 | } | ||
280 | |||
281 | if (id != EndSession) | ||
282 | { | ||
283 | static RequestData response; | ||
284 | if (ppResponse != NULL) | ||
285 | { | ||
286 | *ppResponse = &response; | ||
287 | } | ||
288 | |||
289 | return this->ReceiveResponse(id, &response); | ||
290 | } | ||
291 | else | ||
292 | { | ||
293 | CloseHandle(m_hPipe); | ||
294 | m_hPipe = NULL; | ||
295 | m_fConnected = false; | ||
296 | return 0; | ||
297 | } | ||
298 | } | ||
299 | |||
300 | private: | ||
301 | |||
302 | // | ||
303 | // Do not allow assignment. | ||
304 | // | ||
305 | RemoteMsiSession& operator=(const RemoteMsiSession&); | ||
306 | |||
307 | // | ||
308 | // Called only by the server process. | ||
309 | // Create a new thread to handle receiving requests. | ||
310 | // | ||
311 | DWORD StartProcessingReqests() | ||
312 | { | ||
313 | if (!m_fServer || m_hReceiveStopEvent != NULL) | ||
314 | { | ||
315 | return ERROR_INVALID_OPERATION; | ||
316 | } | ||
317 | |||
318 | DWORD dwRet = 0; | ||
319 | |||
320 | m_hReceiveStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL); | ||
321 | |||
322 | if (m_hReceiveStopEvent == NULL) | ||
323 | { | ||
324 | dwRet = GetLastError(); | ||
325 | } | ||
326 | else | ||
327 | { | ||
328 | if (m_hReceiveThread != NULL) | ||
329 | { | ||
330 | CloseHandle(m_hReceiveThread); | ||
331 | } | ||
332 | |||
333 | m_hReceiveThread = CreateThread(NULL, 0, | ||
334 | RemoteMsiSession::ProcessRequestsThreadStatic, this, 0, NULL); | ||
335 | |||
336 | if (m_hReceiveThread == NULL) | ||
337 | { | ||
338 | dwRet = GetLastError(); | ||
339 | CloseHandle(m_hReceiveStopEvent); | ||
340 | m_hReceiveStopEvent = NULL; | ||
341 | } | ||
342 | } | ||
343 | |||
344 | return dwRet; | ||
345 | } | ||
346 | |||
347 | // | ||
348 | // Called only by the watcher process. | ||
349 | // First verify the connection is complete. Then continually read and parse messages, | ||
350 | // invoke the callback, and send the replies. | ||
351 | // | ||
352 | static DWORD WINAPI ProcessRequestsThreadStatic(void* pv) | ||
353 | { | ||
354 | return reinterpret_cast<RemoteMsiSession*>(pv)->ProcessRequestsThread(); | ||
355 | } | ||
356 | |||
357 | DWORD ProcessRequestsThread() | ||
358 | { | ||
359 | DWORD dwRet; | ||
360 | |||
361 | dwRet = CompleteConnection(); | ||
362 | if (dwRet != 0) | ||
363 | { | ||
364 | if (dwRet == ERROR_OPERATION_ABORTED) dwRet = 0; | ||
365 | } | ||
366 | |||
367 | while (m_fConnected) | ||
368 | { | ||
369 | RequestId id; | ||
370 | RequestData req; | ||
371 | dwRet = ReceiveRequest(&id, &req); | ||
372 | if (dwRet != 0) | ||
373 | { | ||
374 | if (dwRet == ERROR_OPERATION_ABORTED || | ||
375 | dwRet == ERROR_BROKEN_PIPE || dwRet == ERROR_NO_DATA) | ||
376 | { | ||
377 | dwRet = 0; | ||
378 | } | ||
379 | } | ||
380 | else | ||
381 | { | ||
382 | RequestData resp; | ||
383 | ProcessRequest(id, &req, &resp); | ||
384 | |||
385 | if (id == EndSession) | ||
386 | { | ||
387 | break; | ||
388 | } | ||
389 | |||
390 | dwRet = SendResponse(id, &resp); | ||
391 | if (dwRet != 0 && dwRet != ERROR_BROKEN_PIPE && dwRet != ERROR_NO_DATA) | ||
392 | { | ||
393 | dwRet = 0; | ||
394 | } | ||
395 | } | ||
396 | } | ||
397 | |||
398 | CloseHandle(m_hReceiveStopEvent); | ||
399 | m_hReceiveStopEvent = NULL; | ||
400 | return dwRet; | ||
401 | } | ||
402 | |||
403 | // | ||
404 | // Called only by the server process's receive thread. | ||
405 | // Read one request into a RequestData object. | ||
406 | // | ||
407 | DWORD ReceiveRequest(RequestId* pId, RequestData* pReq) | ||
408 | { | ||
409 | DWORD dwRet = this->ReadPipe((BYTE*) pId, sizeof(RequestId)); | ||
410 | |||
411 | if (dwRet == 0) | ||
412 | { | ||
413 | dwRet = this->ReadRequestData(pReq); | ||
414 | } | ||
415 | |||
416 | return dwRet; | ||
417 | } | ||
418 | |||
419 | // | ||
420 | // Called by the server process's receive thread or the client's request call | ||
421 | // to read the response. Read data from the pipe, allowing interruption by the | ||
422 | // stop event if on the server. | ||
423 | // | ||
424 | DWORD ReadPipe(__out_bcount(cbRead) BYTE* pBuf, DWORD cbRead) | ||
425 | { | ||
426 | DWORD dwRet = 0; | ||
427 | DWORD dwTotalBytesRead = 0; | ||
428 | |||
429 | while (dwRet == 0 && dwTotalBytesRead < cbRead) | ||
430 | { | ||
431 | DWORD dwBytesReadThisTime; | ||
432 | ResetEvent(m_overlapped.hEvent); | ||
433 | if (!ReadFile(m_hPipe, pBuf + dwTotalBytesRead, cbRead - dwTotalBytesRead, &dwBytesReadThisTime, &m_overlapped)) | ||
434 | { | ||
435 | dwRet = GetLastError(); | ||
436 | if (dwRet == ERROR_IO_PENDING) | ||
437 | { | ||
438 | if (m_fServer) | ||
439 | { | ||
440 | HANDLE hWaitHandles[] = { m_overlapped.hEvent, m_hReceiveStopEvent }; | ||
441 | dwRet = WaitForMultipleObjects(2, hWaitHandles, FALSE, INFINITE); | ||
442 | } | ||
443 | else | ||
444 | { | ||
445 | dwRet = WaitForSingleObject(m_overlapped.hEvent, INFINITE); | ||
446 | } | ||
447 | |||
448 | if (dwRet == WAIT_OBJECT_0) | ||
449 | { | ||
450 | if (!GetOverlappedResult(m_hPipe, &m_overlapped, &dwBytesReadThisTime, FALSE)) | ||
451 | { | ||
452 | dwRet = GetLastError(); | ||
453 | } | ||
454 | } | ||
455 | else if (dwRet == WAIT_FAILED) | ||
456 | { | ||
457 | dwRet = GetLastError(); | ||
458 | } | ||
459 | else | ||
460 | { | ||
461 | dwRet = ERROR_OPERATION_ABORTED; | ||
462 | } | ||
463 | } | ||
464 | } | ||
465 | |||
466 | dwTotalBytesRead += dwBytesReadThisTime; | ||
467 | } | ||
468 | |||
469 | if (dwRet != 0) | ||
470 | { | ||
471 | if (m_fServer) | ||
472 | { | ||
473 | CancelIo(m_hPipe); | ||
474 | DisconnectNamedPipe(m_hPipe); | ||
475 | } | ||
476 | else | ||
477 | { | ||
478 | CloseHandle(m_hPipe); | ||
479 | m_hPipe = NULL; | ||
480 | } | ||
481 | m_fConnected = false; | ||
482 | } | ||
483 | |||
484 | return dwRet; | ||
485 | } | ||
486 | |||
487 | // | ||
488 | // Called only by the server process. | ||
489 | // Given a request, invoke the MSI API and return the response. | ||
490 | // This is implemented in RemoteMsi.cpp. | ||
491 | // | ||
492 | void ProcessRequest(RequestId id, const RequestData* pReq, RequestData* pResp); | ||
493 | |||
494 | // | ||
495 | // Called only by the client process. | ||
496 | // Send request data over the pipe. | ||
497 | // | ||
498 | DWORD SendRequest(RequestId id, const RequestData* pRequest) | ||
499 | { | ||
500 | DWORD dwRet = WriteRequestData(id, pRequest); | ||
501 | |||
502 | if (dwRet != 0) | ||
503 | { | ||
504 | m_fConnected = false; | ||
505 | CloseHandle(m_hPipe); | ||
506 | m_hPipe = NULL; | ||
507 | } | ||
508 | |||
509 | return dwRet; | ||
510 | } | ||
511 | |||
512 | // | ||
513 | // Called only by the server process. | ||
514 | // Just send a response over the pipe. | ||
515 | // | ||
516 | DWORD SendResponse(RequestId id, const RequestData* pResp) | ||
517 | { | ||
518 | DWORD dwRet = WriteRequestData(id, pResp); | ||
519 | |||
520 | if (dwRet != 0) | ||
521 | { | ||
522 | DisconnectNamedPipe(m_hPipe); | ||
523 | m_fConnected = false; | ||
524 | } | ||
525 | |||
526 | return dwRet; | ||
527 | } | ||
528 | |||
529 | // | ||
530 | // Called either by the client or server process. | ||
531 | // Writes data to the pipe for a request or response. | ||
532 | // | ||
533 | DWORD WriteRequestData(RequestId id, const RequestData* pReq) | ||
534 | { | ||
535 | DWORD dwRet = 0; | ||
536 | |||
537 | RequestData req = *pReq; // Make a copy because the const data can't be changed. | ||
538 | |||
539 | dwRet = this->WritePipe((const BYTE *)&id, sizeof(RequestId)); | ||
540 | if (dwRet != 0) | ||
541 | { | ||
542 | return dwRet; | ||
543 | } | ||
544 | |||
545 | BYTE* sValues[MAX_REQUEST_FIELDS] = {0}; | ||
546 | for (int i = 0; i < MAX_REQUEST_FIELDS; i++) | ||
547 | { | ||
548 | if (req.fields[i].vt == VT_LPWSTR) | ||
549 | { | ||
550 | sValues[i] = (BYTE*) req.fields[i].szValue; | ||
551 | req.fields[i].cchValue = (DWORD) wcslen(req.fields[i].szValue); | ||
552 | } | ||
553 | else if (req.fields[i].vt == VT_STREAM) | ||
554 | { | ||
555 | sValues[i] = req.fields[i].sValue; | ||
556 | req.fields[i].cbValue = (DWORD) req.fields[i + 1].uiValue; | ||
557 | } | ||
558 | } | ||
559 | |||
560 | dwRet = this->WritePipe((const BYTE *)&req, sizeof(RequestData)); | ||
561 | if (dwRet != 0) | ||
562 | { | ||
563 | return dwRet; | ||
564 | } | ||
565 | |||
566 | for (int i = 0; i < MAX_REQUEST_FIELDS; i++) | ||
567 | { | ||
568 | if (sValues[i] != NULL) | ||
569 | { | ||
570 | DWORD cbValue; | ||
571 | if (req.fields[i].vt == VT_LPWSTR) | ||
572 | { | ||
573 | cbValue = (req.fields[i].cchValue + 1) * sizeof(WCHAR); | ||
574 | } | ||
575 | else | ||
576 | { | ||
577 | cbValue = req.fields[i].cbValue; | ||
578 | } | ||
579 | |||
580 | dwRet = this->WritePipe(const_cast<BYTE*> (sValues[i]), cbValue); | ||
581 | if (dwRet != 0) | ||
582 | { | ||
583 | break; | ||
584 | } | ||
585 | } | ||
586 | } | ||
587 | |||
588 | return dwRet; | ||
589 | } | ||
590 | |||
591 | // | ||
592 | // Called when writing a request or response. Writes data to | ||
593 | // the pipe, allowing interruption by the stop event if on the server. | ||
594 | // | ||
595 | DWORD WritePipe(const BYTE* pBuf, DWORD cbWrite) | ||
596 | { | ||
597 | DWORD dwRet = 0; | ||
598 | DWORD dwTotalBytesWritten = 0; | ||
599 | |||
600 | while (dwRet == 0 && dwTotalBytesWritten < cbWrite) | ||
601 | { | ||
602 | DWORD dwBytesWrittenThisTime; | ||
603 | ResetEvent(m_overlapped.hEvent); | ||
604 | if (!WriteFile(m_hPipe, pBuf + dwTotalBytesWritten, cbWrite - dwTotalBytesWritten, &dwBytesWrittenThisTime, &m_overlapped)) | ||
605 | { | ||
606 | dwRet = GetLastError(); | ||
607 | if (dwRet == ERROR_IO_PENDING) | ||
608 | { | ||
609 | if (m_fServer) | ||
610 | { | ||
611 | HANDLE hWaitHandles[] = { m_overlapped.hEvent, m_hReceiveStopEvent }; | ||
612 | dwRet = WaitForMultipleObjects(2, hWaitHandles, FALSE, INFINITE); | ||
613 | } | ||
614 | else | ||
615 | { | ||
616 | dwRet = WaitForSingleObject(m_overlapped.hEvent, INFINITE); | ||
617 | } | ||
618 | |||
619 | if (dwRet == WAIT_OBJECT_0) | ||
620 | { | ||
621 | if (!GetOverlappedResult(m_hPipe, &m_overlapped, &dwBytesWrittenThisTime, FALSE)) | ||
622 | { | ||
623 | dwRet = GetLastError(); | ||
624 | } | ||
625 | } | ||
626 | else if (dwRet == WAIT_FAILED) | ||
627 | { | ||
628 | dwRet = GetLastError(); | ||
629 | } | ||
630 | else | ||
631 | { | ||
632 | dwRet = ERROR_OPERATION_ABORTED; | ||
633 | } | ||
634 | } | ||
635 | } | ||
636 | |||
637 | dwTotalBytesWritten += dwBytesWrittenThisTime; | ||
638 | } | ||
639 | |||
640 | return dwRet; | ||
641 | } | ||
642 | |||
643 | // | ||
644 | // Called either by the client or server process. | ||
645 | // Reads data from the pipe for a request or response. | ||
646 | // | ||
647 | DWORD ReadRequestData(RequestData* pReq) | ||
648 | { | ||
649 | DWORD dwRet = ReadPipe((BYTE*) pReq, sizeof(RequestData)); | ||
650 | |||
651 | if (dwRet == 0) | ||
652 | { | ||
653 | DWORD cbData = 0; | ||
654 | for (int i = 0; i < MAX_REQUEST_FIELDS; i++) | ||
655 | { | ||
656 | if (pReq->fields[i].vt == VT_LPWSTR) | ||
657 | { | ||
658 | cbData += (pReq->fields[i].cchValue + 1) * sizeof(WCHAR); | ||
659 | } | ||
660 | else if (pReq->fields[i].vt == VT_STREAM) | ||
661 | { | ||
662 | cbData += pReq->fields[i].cbValue; | ||
663 | } | ||
664 | } | ||
665 | |||
666 | if (cbData > 0) | ||
667 | { | ||
668 | if (!CheckRequestDataBuf(cbData)) | ||
669 | { | ||
670 | return ERROR_OUTOFMEMORY; | ||
671 | } | ||
672 | |||
673 | dwRet = this->ReadPipe((BYTE*) m_pBufReceive, cbData); | ||
674 | if (dwRet == 0) | ||
675 | { | ||
676 | DWORD dwOffset = 0; | ||
677 | for (int i = 0; i < MAX_REQUEST_FIELDS; i++) | ||
678 | { | ||
679 | if (pReq->fields[i].vt == VT_LPWSTR) | ||
680 | { | ||
681 | LPWSTR szTemp = (LPWSTR) (m_pBufReceive + dwOffset); | ||
682 | dwOffset += (pReq->fields[i].cchValue + 1) * sizeof(WCHAR); | ||
683 | pReq->fields[i].szValue = szTemp; | ||
684 | } | ||
685 | else if (pReq->fields[i].vt == VT_STREAM) | ||
686 | { | ||
687 | BYTE* sTemp = m_pBufReceive + dwOffset; | ||
688 | dwOffset += pReq->fields[i].cbValue; | ||
689 | pReq->fields[i].sValue = sTemp; | ||
690 | } | ||
691 | } | ||
692 | } | ||
693 | } | ||
694 | } | ||
695 | |||
696 | return dwRet; | ||
697 | } | ||
698 | |||
699 | // | ||
700 | // Called only by the client process. | ||
701 | // Wait for a response on the pipe. If no response is received before the timeout, | ||
702 | // then give up and close the connection. | ||
703 | // | ||
704 | DWORD ReceiveResponse(RequestId id, RequestData* pResp) | ||
705 | { | ||
706 | RequestId responseId; | ||
707 | DWORD dwRet = ReadPipe((BYTE*) &responseId, sizeof(RequestId)); | ||
708 | if (dwRet == 0 && responseId != id) | ||
709 | { | ||
710 | dwRet = ERROR_OPERATION_ABORTED; | ||
711 | } | ||
712 | |||
713 | if (dwRet == 0) | ||
714 | { | ||
715 | dwRet = this->ReadRequestData(pResp); | ||
716 | } | ||
717 | |||
718 | return dwRet; | ||
719 | } | ||
720 | |||
721 | // | ||
722 | // Called only by the server process's receive thread. | ||
723 | // Try to complete and verify an asynchronous connection operation. | ||
724 | // | ||
725 | DWORD CompleteConnection() | ||
726 | { | ||
727 | DWORD dwRet = 0; | ||
728 | if (m_fConnecting) | ||
729 | { | ||
730 | HANDLE hWaitHandles[] = { m_overlapped.hEvent, m_hReceiveStopEvent }; | ||
731 | DWORD dwWaitRes = WaitForMultipleObjects(2, hWaitHandles, FALSE, INFINITE); | ||
732 | |||
733 | if (dwWaitRes == WAIT_OBJECT_0) | ||
734 | { | ||
735 | m_fConnecting = false; | ||
736 | |||
737 | DWORD dwUnused; | ||
738 | if (GetOverlappedResult(m_hPipe, &m_overlapped, &dwUnused, FALSE)) | ||
739 | { | ||
740 | m_fConnected = true; | ||
741 | } | ||
742 | else | ||
743 | { | ||
744 | dwRet = GetLastError(); | ||
745 | } | ||
746 | } | ||
747 | else if (dwWaitRes == WAIT_FAILED) | ||
748 | { | ||
749 | CancelIo(m_hPipe); | ||
750 | dwRet = GetLastError(); | ||
751 | } | ||
752 | else | ||
753 | { | ||
754 | CancelIo(m_hPipe); | ||
755 | dwRet = ERROR_OPERATION_ABORTED; | ||
756 | } | ||
757 | } | ||
758 | return dwRet; | ||
759 | } | ||
760 | |||
761 | // | ||
762 | // Called only by the server process. | ||
763 | // Creates a named pipe instance and begins asynchronously waiting | ||
764 | // for a connection from the client process. | ||
765 | // | ||
766 | DWORD ConnectPipeServer() | ||
767 | { | ||
768 | DWORD dwRet = 0; | ||
769 | const int BUFSIZE = 1024; // Suggested pipe I/O buffer sizes | ||
770 | m_hPipe = CreateNamedPipe( | ||
771 | m_szPipeName, | ||
772 | PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | FILE_FLAG_FIRST_PIPE_INSTANCE, | ||
773 | PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, | ||
774 | 1, BUFSIZE, BUFSIZE, 0, NULL); | ||
775 | if (m_hPipe == INVALID_HANDLE_VALUE) | ||
776 | { | ||
777 | m_hPipe = NULL; | ||
778 | dwRet = GetLastError(); | ||
779 | } | ||
780 | else if (ConnectNamedPipe(m_hPipe, &m_overlapped)) | ||
781 | { | ||
782 | m_fConnected = true; | ||
783 | } | ||
784 | else | ||
785 | { | ||
786 | dwRet = GetLastError(); | ||
787 | |||
788 | if (dwRet == ERROR_PIPE_BUSY) | ||
789 | { | ||
790 | // All pipe instances are busy, so wait for a maximum of 20 seconds | ||
791 | dwRet = 0; | ||
792 | if (WaitNamedPipe(m_szPipeName, 20000)) | ||
793 | { | ||
794 | m_fConnected = true; | ||
795 | } | ||
796 | else | ||
797 | { | ||
798 | dwRet = GetLastError(); | ||
799 | } | ||
800 | } | ||
801 | |||
802 | if (dwRet == ERROR_IO_PENDING) | ||
803 | { | ||
804 | dwRet = 0; | ||
805 | m_fConnecting = true; | ||
806 | } | ||
807 | } | ||
808 | return dwRet; | ||
809 | } | ||
810 | |||
811 | // | ||
812 | // Called only by the client process. | ||
813 | // Attemps to open a connection to an existing named pipe instance | ||
814 | // which should have already been created by the server process. | ||
815 | // | ||
816 | DWORD ConnectPipeClient() | ||
817 | { | ||
818 | DWORD dwRet = 0; | ||
819 | m_hPipe = CreateFile( | ||
820 | m_szPipeName, GENERIC_READ | GENERIC_WRITE, | ||
821 | 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); | ||
822 | if (m_hPipe != INVALID_HANDLE_VALUE) | ||
823 | { | ||
824 | m_fConnected = true; | ||
825 | } | ||
826 | else | ||
827 | { | ||
828 | m_hPipe = NULL; | ||
829 | dwRet = GetLastError(); | ||
830 | } | ||
831 | return dwRet; | ||
832 | } | ||
833 | |||
834 | // | ||
835 | // Ensures that the request buffer is large enough to hold a request, | ||
836 | // reallocating the buffer if necessary. | ||
837 | // It will also reduce the buffer size if the previous allocation was very large. | ||
838 | // | ||
839 | BOOL CheckRequestDataBuf(DWORD cbBuf) | ||
840 | { | ||
841 | if (m_cbBufReceive < cbBuf || (LARGE_BUFFER_THRESHOLD < m_cbBufReceive && cbBuf < m_cbBufReceive)) | ||
842 | { | ||
843 | if (m_pBufReceive != NULL) | ||
844 | { | ||
845 | SecureZeroMemory(m_pBufReceive, m_cbBufReceive); | ||
846 | delete[] m_pBufReceive; | ||
847 | } | ||
848 | m_cbBufReceive = max(MIN_BUFFER_STRING_SIZE*2, cbBuf); | ||
849 | m_pBufReceive = new BYTE[m_cbBufReceive]; | ||
850 | if (m_pBufReceive == NULL) | ||
851 | { | ||
852 | m_cbBufReceive = 0; | ||
853 | } | ||
854 | } | ||
855 | return m_pBufReceive != NULL; | ||
856 | } | ||
857 | |||
858 | private: | ||
859 | |||
860 | // Name of this instance. | ||
861 | const wchar_t* m_szName; | ||
862 | |||
863 | // "\\.\pipe\name" | ||
864 | wchar_t* m_szPipeName; | ||
865 | |||
866 | // Handle to the pipe instance. | ||
867 | HANDLE m_hPipe; | ||
868 | |||
869 | // Handle to the thread that receives requests. | ||
870 | HANDLE m_hReceiveThread; | ||
871 | |||
872 | // Handle to the event used to signal the receive thread to exit. | ||
873 | HANDLE m_hReceiveStopEvent; | ||
874 | |||
875 | // All pipe I/O is done in overlapped mode to avoid unintentional blocking. | ||
876 | OVERLAPPED m_overlapped; | ||
877 | |||
878 | // Dynamically-resized buffer for receiving requests. | ||
879 | BYTE* m_pBufReceive; | ||
880 | |||
881 | // Current size of the receive request buffer. | ||
882 | DWORD m_cbBufReceive; | ||
883 | |||
884 | // Dynamically-resized buffer for sending requests. | ||
885 | wchar_t* m_pBufSend; | ||
886 | |||
887 | // Current size of the send request buffer. | ||
888 | DWORD m_cbBufSend; | ||
889 | |||
890 | // True if this is the server process, false if this is the client process. | ||
891 | const bool m_fServer; | ||
892 | |||
893 | // True if an asynchronous connection operation is currently in progress. | ||
894 | bool m_fConnecting; | ||
895 | |||
896 | // True if the pipe is currently connected. | ||
897 | bool m_fConnected; | ||
898 | }; | ||