diff options
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 | }; | ||