aboutsummaryrefslogtreecommitdiff
path: root/src/samples/Dtf/Tools/SfxCA/RemoteMsiSession.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/samples/Dtf/Tools/SfxCA/RemoteMsiSession.h')
-rw-r--r--src/samples/Dtf/Tools/SfxCA/RemoteMsiSession.h898
1 files changed, 898 insertions, 0 deletions
diff --git a/src/samples/Dtf/Tools/SfxCA/RemoteMsiSession.h b/src/samples/Dtf/Tools/SfxCA/RemoteMsiSession.h
new file mode 100644
index 00000000..90c7c01f
--- /dev/null
+++ b/src/samples/Dtf/Tools/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};