summaryrefslogtreecommitdiff
path: root/src/dtf/SfxCA/RemoteMsiSession.h
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2022-03-31 11:56:14 -0700
committerRob Mensching <rob@firegiant.com>2022-03-31 18:01:06 -0700
commit47582b162368e8edf7a3b11c13b8e9dabc5f0a26 (patch)
tree2c4063eff325684bed39de0edacd7866a257ae02 /src/dtf/SfxCA/RemoteMsiSession.h
parent167296c42497c4e95f0d5d71168542d747655981 (diff)
downloadwix-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.h898
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//
12class RemoteMsiSession
13{
14public:
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
91public:
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
300private:
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
858private:
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};