main
cpp 945 lines 23 KB
Raw
1 // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3 #include "precomp.h"
4
5 const DWORD VARIABLE_GROW_FACTOR = 80;
6 static DWORD vdwDebuggerCheck = 0;
7 static IBootstrapperEngine* vpEngine = NULL;
8
9 static HRESULT ParseCommandLine(
10 __inout_z LPWSTR *psczPipeBaseName,
11 __inout_z LPWSTR *psczPipeSecret,
12 __out DWORD64 *pqwEngineAPIVersion
13 );
14 static HRESULT ConnectToEngine(
15 __in_z LPCWSTR wzPipeBaseName,
16 __in_z LPCWSTR wzPipeSecret,
17 __out HANDLE *phBAPipe,
18 __out HANDLE *phEnginePipe
19 );
20 static HRESULT ConnectAndVerify(
21 __in_z LPCWSTR wzPipeName,
22 __in_z LPCWSTR wzPipeSecret,
23 __in DWORD cbPipeSecret,
24 __out HANDLE *phPipe
25 );
26 static HRESULT PumpMessages(
27 __in HANDLE hPipe,
28 __in IBootstrapperApplication* pApplication,
29 __in IBootstrapperEngine* pEngine
30 );
31 static void MsgProc(
32 __in BOOTSTRAPPER_APPLICATION_MESSAGE messageType,
33 __in_bcount(cbData) LPVOID pvData,
34 __in DWORD cbData,
35 __in IBootstrapperApplication* pApplication,
36 __in IBootstrapperEngine* pEngine
37 );
38
39 // prototypes
40
41 EXTERN_C HRESULT __stdcall BootstrapperApplicationRun(
42 __in IBootstrapperApplication* pApplication
43 )
44 {
45 HRESULT hr = S_OK;
46 BOOL fComInitialized = FALSE;
47 DWORD64 qwEngineAPIVersion = 0;
48 LPWSTR sczPipeBaseName = NULL;
49 LPWSTR sczPipeSecret = NULL;
50 HANDLE hBAPipe = INVALID_HANDLE_VALUE;
51 HANDLE hEnginePipe = INVALID_HANDLE_VALUE;
52 IBootstrapperEngine* pEngine = NULL;
53 BOOL fInitializedBal = FALSE;
54
55 // initialize COM
56 hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED);
57 ExitOnFailure(hr, "Failed to initialize COM.");
58 fComInitialized = TRUE;
59
60 hr = ParseCommandLine(&sczPipeBaseName, &sczPipeSecret, &qwEngineAPIVersion);
61 BalExitOnFailure(hr, "Failed to parse command line.");
62
63 // TODO: Validate the engine API version.
64
65 hr = ConnectToEngine(sczPipeBaseName, sczPipeSecret, &hBAPipe, &hEnginePipe);
66 BalExitOnFailure(hr, "Failed to connect to engine.");
67
68 hr = BalBootstrapperEngineCreate(hEnginePipe, &pEngine);
69 BalExitOnFailure(hr, "Failed to create bootstrapper engine.");
70
71 BalInitialize(pEngine);
72 fInitializedBal = TRUE;
73
74 BalDebuggerCheck();
75
76 hr = MsgPump(hBAPipe, pApplication, pEngine);
77 BalExitOnFailure(hr, "Failed while pumping messages.");
78
79 LExit:
80 if (fInitializedBal)
81 {
82 BalUninitialize();
83 }
84
85 ReleaseNullObject(pEngine);
86 ReleasePipeHandle(hEnginePipe);
87 ReleasePipeHandle(hBAPipe);
88 ReleaseStr(sczPipeSecret);
89 ReleaseStr(sczPipeBaseName);
90
91 if (fComInitialized)
92 {
93 ::CoUninitialize();
94 }
95
96 return hr;
97 }
98
99 DAPI_(void) BalInitialize(
100 __in IBootstrapperEngine* pEngine
101 )
102 {
103 pEngine->AddRef();
104
105 ReleaseObject(vpEngine);
106 vpEngine = pEngine;
107 }
108
109 DAPI_(void) BalUninitialize()
110 {
111 ReleaseNullObject(vpEngine);
112 }
113
114 DAPI_(VOID) BalDebuggerCheck()
115 {
116 HRESULT hr = S_OK;
117 HKEY hk = NULL;
118 BOOL fDebug = FALSE;
119 LPWSTR sczDebugBootstrapperApplications = NULL;
120 LPWSTR sczDebugBootstrapperApplication = NULL;
121 LPWSTR sczModulePath = NULL;
122 LPCWSTR wzModuleFilename = NULL;
123 WCHAR wzMessage[1024] = { };
124
125 if (0 == vdwDebuggerCheck)
126 {
127 ++vdwDebuggerCheck;
128
129 hr = RegOpen(HKEY_LOCAL_MACHINE, L"System\\CurrentControlSet\\Control\\Session Manager\\Environment", KEY_QUERY_VALUE, &hk);
130 if (SUCCEEDED(hr))
131 {
132 hr = RegReadString(hk, L"WixDebugBootstrapperApplications", &sczDebugBootstrapperApplications);
133 if (SUCCEEDED(hr) && sczDebugBootstrapperApplications && *sczDebugBootstrapperApplications &&
134 sczDebugBootstrapperApplications[0] != L'0' && !sczDebugBootstrapperApplications[1])
135 {
136 hr = PathForCurrentProcess(&sczModulePath, NULL);
137 if (SUCCEEDED(hr) && sczModulePath && *sczModulePath)
138 {
139 wzModuleFilename = PathFile(sczModulePath);
140 if (wzModuleFilename)
141 {
142 fDebug = TRUE;
143 }
144 }
145 }
146 else
147 {
148 hr = RegReadString(hk, L"WixDebugBootstrapperApplication", &sczDebugBootstrapperApplication);
149 if (SUCCEEDED(hr) && sczDebugBootstrapperApplication && *sczDebugBootstrapperApplication)
150 {
151 hr = PathForCurrentProcess(&sczModulePath, NULL);
152 if (SUCCEEDED(hr) && sczModulePath && *sczModulePath)
153 {
154 wzModuleFilename = PathFile(sczModulePath);
155 if (wzModuleFilename && CSTR_EQUAL == ::CompareStringOrdinal(sczDebugBootstrapperApplication, -1, wzModuleFilename, -1, TRUE))
156 {
157 fDebug = TRUE;
158 }
159 }
160 }
161 }
162
163 if (fDebug)
164 {
165 hr = ::StringCchPrintfW(wzMessage, countof(wzMessage), L"To debug the bootstrapper application process %ls\n\nSet breakpoints and attach a debugger to process id: %d (0x%x)", wzModuleFilename, ::GetCurrentProcessId(), ::GetCurrentProcessId());
166
167 if (SUCCEEDED(hr))
168 {
169 ::MessageBoxW(NULL, wzMessage, L"WiX Bootstrapper Application", MB_SERVICE_NOTIFICATION | MB_TOPMOST | MB_ICONQUESTION | MB_OK | MB_SYSTEMMODAL);
170 }
171 }
172 }
173 }
174
175 ReleaseRegKey(hk);
176 ReleaseStr(sczModulePath);
177 ReleaseStr(sczDebugBootstrapperApplication);
178 ReleaseStr(sczDebugBootstrapperApplications);
179 }
180
181 DAPI_(HRESULT) BalManifestLoad(
182 __in HMODULE hBootstrapperApplicationModule,
183 __out IXMLDOMDocument** ppixdManifest
184 )
185 {
186 HRESULT hr = S_OK;
187 LPWSTR sczPath = NULL;
188
189 hr = PathRelativeToModule(&sczPath, BAL_MANIFEST_FILENAME, hBootstrapperApplicationModule);
190 ExitOnFailure(hr, "Failed to get path to bootstrapper application manifest: %ls", BAL_MANIFEST_FILENAME);
191
192 hr = XmlLoadDocumentFromFile(sczPath, ppixdManifest);
193 ExitOnFailure(hr, "Failed to load bootstrapper application manifest '%ls' from path: %ls", BAL_MANIFEST_FILENAME, sczPath);
194
195 LExit:
196 ReleaseStr(sczPath);
197 return hr;
198 }
199
200
201 DAPI_(HRESULT) BalEvaluateCondition(
202 __in_z LPCWSTR wzCondition,
203 __out BOOL* pf
204 )
205 {
206 HRESULT hr = S_OK;
207
208 if (!vpEngine)
209 {
210 hr = E_POINTER;
211 ExitOnRootFailure(hr, "BalInitialize() must be called first.");
212 }
213
214 hr = vpEngine->EvaluateCondition(wzCondition, pf);
215
216 LExit:
217 return hr;
218 }
219
220
221 DAPI_(HRESULT) BalEscapeString(
222 __in_z LPCWSTR wzIn,
223 __inout LPWSTR* psczOut
224 )
225 {
226 HRESULT hr = S_OK;
227
228 if (!vpEngine)
229 {
230 hr = E_POINTER;
231 ExitOnRootFailure(hr, "BalInitialize() must be called first.");
232 }
233
234 hr = BalEscapeStringFromEngine(vpEngine, wzIn, psczOut);
235
236 LExit:
237 return hr;
238 }
239
240
241 DAPI_(HRESULT) BalEscapeStringFromEngine(
242 __in IBootstrapperEngine* pEngine,
243 __in_z LPCWSTR wzIn,
244 __inout LPWSTR* psczOut
245 )
246 {
247 HRESULT hr = S_OK;
248 SIZE_T cch = 0;
249
250 if (*psczOut)
251 {
252 hr = StrMaxLength(*psczOut, &cch);
253 ExitOnFailure(hr, "Failed to determine length of value.");
254 }
255 else
256 {
257 hr = ::StringCchLengthW(wzIn, STRSAFE_MAX_LENGTH, reinterpret_cast<size_t*>(&cch));
258 ExitOnFailure(hr, "Failed to determine length of source.");
259
260 cch = min(STRSAFE_MAX_LENGTH, cch + VARIABLE_GROW_FACTOR);
261 hr = StrAlloc(psczOut, cch);
262 ExitOnFailure(hr, "Failed to pre-allocate value.");
263 }
264
265 hr = pEngine->EscapeString(wzIn, *psczOut, &cch);
266 if (E_MOREDATA == hr)
267 {
268 ++cch;
269
270 hr = StrAllocSecure(psczOut, cch);
271 ExitOnFailure(hr, "Failed to allocate value.");
272
273 hr = pEngine->EscapeString(wzIn, *psczOut, &cch);
274 }
275
276 LExit:
277 return hr;
278 }
279
280
281 // The contents of psczOut may be sensitive, should keep encrypted and SecureZeroFree.
282 DAPI_(HRESULT) BalFormatString(
283 __in_z LPCWSTR wzFormat,
284 __inout LPWSTR* psczOut
285 )
286 {
287 HRESULT hr = S_OK;
288
289 if (!vpEngine)
290 {
291 hr = E_POINTER;
292 ExitOnRootFailure(hr, "BalInitialize() must be called first.");
293 }
294
295 hr = BalFormatStringFromEngine(vpEngine, wzFormat, psczOut);
296
297 LExit:
298 return hr;
299 }
300
301
302 // The contents of psczOut may be sensitive, should keep encrypted and SecureZeroFree.
303 DAPI_(HRESULT) BalFormatStringFromEngine(
304 __in IBootstrapperEngine* pEngine,
305 __in_z LPCWSTR wzFormat,
306 __inout LPWSTR* psczOut
307 )
308 {
309 HRESULT hr = S_OK;
310 SIZE_T cch = 0;
311
312 if (*psczOut)
313 {
314 hr = StrMaxLength(*psczOut, &cch);
315 ExitOnFailure(hr, "Failed to determine length of value.");
316 }
317 else
318 {
319 hr = ::StringCchLengthW(wzFormat, STRSAFE_MAX_LENGTH, reinterpret_cast<size_t*>(&cch));
320 ExitOnFailure(hr, "Failed to determine length of source.");
321
322 cch = min(STRSAFE_MAX_LENGTH, cch + VARIABLE_GROW_FACTOR);
323 hr = StrAlloc(psczOut, cch);
324 ExitOnFailure(hr, "Failed to pre-allocate value.");
325 }
326
327 hr = pEngine->FormatString(wzFormat, *psczOut, &cch);
328 if (E_MOREDATA == hr)
329 {
330 ++cch;
331
332 hr = StrAllocSecure(psczOut, cch);
333 ExitOnFailure(hr, "Failed to allocate value.");
334
335 hr = pEngine->FormatString(wzFormat, *psczOut, &cch);
336 }
337
338 LExit:
339 return hr;
340 }
341
342
343 // The contents of pllValue may be sensitive, if variable is hidden should keep value encrypted and SecureZeroMemory.
344 DAPI_(HRESULT) BalGetNumericVariable(
345 __in_z LPCWSTR wzVariable,
346 __out LONGLONG* pllValue
347 )
348 {
349 HRESULT hr = S_OK;
350
351 if (!vpEngine)
352 {
353 hr = E_POINTER;
354 ExitOnRootFailure(hr, "BalInitialize() must be called first.");
355 }
356
357 hr = vpEngine->GetVariableNumeric(wzVariable, pllValue);
358
359 LExit:
360 return hr;
361 }
362
363
364 DAPI_(HRESULT) BalSetNumericVariable(
365 __in_z LPCWSTR wzVariable,
366 __in LONGLONG llValue
367 )
368 {
369 HRESULT hr = S_OK;
370
371 if (!vpEngine)
372 {
373 hr = E_POINTER;
374 ExitOnRootFailure(hr, "BalInitialize() must be called first.");
375 }
376
377 hr = vpEngine->SetVariableNumeric(wzVariable, llValue);
378
379 LExit:
380 return hr;
381 }
382
383
384 DAPI_(BOOL) BalVariableExists(
385 __in_z LPCWSTR wzVariable
386 )
387 {
388 HRESULT hr = S_OK;
389 BOOL fExists = FALSE;
390
391 if (!vpEngine)
392 {
393 hr = E_POINTER;
394 ExitOnRootFailure(hr, "BalInitialize() must be called first.");
395 }
396
397 fExists = BalVariableExistsFromEngine(vpEngine, wzVariable);
398
399 LExit:
400 return fExists;
401 }
402
403
404 DAPI_(BOOL) BalVariableExistsFromEngine(
405 __in IBootstrapperEngine* pEngine,
406 __in_z LPCWSTR wzVariable
407 )
408 {
409 HRESULT hr = S_OK;
410 SIZE_T cch = 0;
411
412 hr = pEngine->GetVariableString(wzVariable, NULL, &cch);
413
414 return E_NOTFOUND != hr;
415 }
416
417
418 // The contents of psczValue may be sensitive, if variable is hidden should keep value encrypted and SecureZeroFree.
419 DAPI_(HRESULT) BalGetStringVariable(
420 __in_z LPCWSTR wzVariable,
421 __inout LPWSTR* psczValue
422 )
423 {
424 HRESULT hr = S_OK;
425
426 if (!vpEngine)
427 {
428 hr = E_POINTER;
429 ExitOnRootFailure(hr, "BalInitialize() must be called first.");
430 }
431
432 hr = BalGetStringVariableFromEngine(vpEngine, wzVariable, psczValue);
433
434 LExit:
435 return hr;
436 }
437
438
439 // The contents of psczValue may be sensitive, if variable is hidden should keep value encrypted and SecureZeroFree.
440 DAPI_(HRESULT) BalGetStringVariableFromEngine(
441 __in IBootstrapperEngine* pEngine,
442 __in_z LPCWSTR wzVariable,
443 __inout LPWSTR* psczValue
444 )
445 {
446 HRESULT hr = S_OK;
447 SIZE_T cch = 0;
448
449 if (*psczValue)
450 {
451 hr = StrMaxLength(*psczValue, &cch);
452 ExitOnFailure(hr, "Failed to determine length of value.");
453 }
454 else
455 {
456 cch = VARIABLE_GROW_FACTOR;
457 hr = StrAlloc(psczValue, cch);
458 ExitOnFailure(hr, "Failed to pre-allocate value.");
459 }
460
461 hr = pEngine->GetVariableString(wzVariable, *psczValue, &cch);
462 if (E_MOREDATA == hr)
463 {
464 ++cch;
465
466 hr = StrAllocSecure(psczValue, cch);
467 ExitOnFailure(hr, "Failed to allocate value.");
468
469 hr = pEngine->GetVariableString(wzVariable, *psczValue, &cch);
470 }
471
472 LExit:
473 return hr;
474 }
475
476 DAPI_(HRESULT) BalSetStringVariable(
477 __in_z LPCWSTR wzVariable,
478 __in_z_opt LPCWSTR wzValue,
479 __in BOOL fFormatted
480 )
481 {
482 HRESULT hr = S_OK;
483
484 if (!vpEngine)
485 {
486 hr = E_POINTER;
487 ExitOnRootFailure(hr, "BalInitialize() must be called first.");
488 }
489
490 hr = vpEngine->SetVariableString(wzVariable, wzValue, fFormatted);
491
492 LExit:
493 return hr;
494 }
495
496
497 DAPI_(HRESULT) BalGetVersionVariable(
498 __in_z LPCWSTR wzVariable,
499 __inout LPWSTR* psczValue
500 )
501 {
502 HRESULT hr = S_OK;
503
504 if (!vpEngine)
505 {
506 hr = E_POINTER;
507 ExitOnRootFailure(hr, "BalInitialize() must be called first.");
508 }
509
510 hr = BalGetVersionVariableFromEngine(vpEngine, wzVariable, psczValue);
511
512 LExit:
513 return hr;
514 }
515
516
517 DAPI_(HRESULT) BalGetVersionVariableFromEngine(
518 __in IBootstrapperEngine* pEngine,
519 __in_z LPCWSTR wzVariable,
520 __inout LPWSTR* psczValue
521 )
522 {
523 HRESULT hr = S_OK;
524 SIZE_T cch = 0;
525
526 if (*psczValue)
527 {
528 hr = StrMaxLength(*psczValue, &cch);
529 ExitOnFailure(hr, "Failed to determine length of value.");
530 }
531 else
532 {
533 cch = VARIABLE_GROW_FACTOR;
534 hr = StrAlloc(psczValue, cch);
535 ExitOnFailure(hr, "Failed to pre-allocate value.");
536 }
537
538 hr = pEngine->GetVariableVersion(wzVariable, *psczValue, &cch);
539 if (E_MOREDATA == hr)
540 {
541 ++cch;
542
543 hr = StrAllocSecure(psczValue, cch);
544 ExitOnFailure(hr, "Failed to allocate value.");
545
546 hr = pEngine->GetVariableVersion(wzVariable, *psczValue, &cch);
547 }
548
549 LExit:
550 return hr;
551 }
552
553 DAPI_(HRESULT) BalGetRelatedBundleVariable(
554 __in_z LPCWSTR wzBundleId,
555 __in_z LPCWSTR wzVariable,
556 __inout LPWSTR* psczValue
557 )
558 {
559 HRESULT hr = S_OK;
560
561 if (!vpEngine)
562 {
563 hr = E_POINTER;
564 ExitOnRootFailure(hr, "BalInitialize() must be called first.");
565 }
566
567 hr = BalGetRelatedBundleVariableFromEngine(vpEngine, wzBundleId, wzVariable, psczValue);
568
569 LExit:
570 return hr;
571 }
572
573 DAPI_(HRESULT) BalGetRelatedBundleVariableFromEngine(
574 __in IBootstrapperEngine* pEngine,
575 __in_z LPCWSTR wzBundleId,
576 __in_z LPCWSTR wzVariable,
577 __inout LPWSTR* psczValue
578 )
579 {
580 HRESULT hr = S_OK;
581 SIZE_T cch = 0;
582
583 if (*psczValue)
584 {
585 hr = StrMaxLength(*psczValue, reinterpret_cast<DWORD_PTR*>(&cch));
586 ExitOnFailure(hr, "Failed to determine length of value.");
587 }
588
589 hr = pEngine->GetRelatedBundleVariable(wzBundleId, wzVariable, *psczValue, &cch);
590 if (E_MOREDATA == hr)
591 {
592 ++cch;
593
594 hr = StrAllocSecure(psczValue, cch);
595 ExitOnFailure(hr, "Failed to allocate value.");
596
597 hr = pEngine->GetRelatedBundleVariable(wzBundleId, wzVariable, *psczValue, &cch);
598 }
599
600 LExit:
601 return hr;
602 }
603
604 DAPI_(HRESULT) BalSetVersionVariable(
605 __in_z LPCWSTR wzVariable,
606 __in_z_opt LPCWSTR wzValue
607 )
608 {
609 HRESULT hr = S_OK;
610
611 if (!vpEngine)
612 {
613 hr = E_POINTER;
614 ExitOnRootFailure(hr, "BalInitialize() must be called first.");
615 }
616
617 hr = vpEngine->SetVariableVersion(wzVariable, wzValue);
618
619 LExit:
620 return hr;
621 }
622
623
624 DAPIV_(HRESULT) BalLog(
625 __in BOOTSTRAPPER_LOG_LEVEL level,
626 __in_z __format_string LPCSTR szFormat,
627 ...
628 )
629 {
630 HRESULT hr = S_OK;
631 va_list args;
632
633 if (!vpEngine)
634 {
635 hr = E_POINTER;
636 ExitOnRootFailure(hr, "BalInitialize() must be called first.");
637 }
638
639 va_start(args, szFormat);
640 hr = BalLogArgs(level, szFormat, args);
641 va_end(args);
642
643 LExit:
644 return hr;
645 }
646
647
648 DAPI_(HRESULT) BalLogArgs(
649 __in BOOTSTRAPPER_LOG_LEVEL level,
650 __in_z __format_string LPCSTR szFormat,
651 __in va_list args
652 )
653 {
654 HRESULT hr = S_OK;
655 LPSTR sczFormattedAnsi = NULL;
656 LPWSTR sczMessage = NULL;
657
658 if (!vpEngine)
659 {
660 hr = E_POINTER;
661 ExitOnRootFailure(hr, "BalInitialize() must be called first.");
662 }
663
664 hr = StrAnsiAllocFormattedArgs(&sczFormattedAnsi, szFormat, args);
665 ExitOnFailure(hr, "Failed to format log string.");
666
667 hr = StrAllocStringAnsi(&sczMessage, sczFormattedAnsi, 0, CP_UTF8);
668 ExitOnFailure(hr, "Failed to convert log string to Unicode.");
669
670 hr = vpEngine->Log(level, sczMessage);
671
672 LExit:
673 ReleaseStr(sczMessage);
674 ReleaseStr(sczFormattedAnsi);
675 return hr;
676 }
677
678
679 DAPIV_(HRESULT) BalLogError(
680 __in HRESULT hrError,
681 __in_z __format_string LPCSTR szFormat,
682 ...
683 )
684 {
685 HRESULT hr = S_OK;
686 va_list args;
687
688 if (!vpEngine)
689 {
690 hr = E_POINTER;
691 ExitOnRootFailure(hr, "BalInitialize() must be called first.");
692 }
693
694 va_start(args, szFormat);
695 hr = BalLogErrorArgs(hrError, szFormat, args);
696 va_end(args);
697
698 LExit:
699 return hr;
700 }
701
702
703 DAPI_(HRESULT) BalLogErrorArgs(
704 __in HRESULT hrError,
705 __in_z __format_string LPCSTR szFormat,
706 __in va_list args
707 )
708 {
709 HRESULT hr = S_OK;
710 LPSTR sczFormattedAnsi = NULL;
711 LPWSTR sczMessage = NULL;
712
713 if (!vpEngine)
714 {
715 hr = E_POINTER;
716 ExitOnRootFailure(hr, "BalInitialize() must be called first.");
717 }
718
719 hr = StrAnsiAllocFormattedArgs(&sczFormattedAnsi, szFormat, args);
720 ExitOnFailure(hr, "Failed to format error log string.");
721
722 hr = StrAllocFormatted(&sczMessage, L"Error 0x%08x: %S", hrError, sczFormattedAnsi);
723 ExitOnFailure(hr, "Failed to prepend error number to error log string.");
724
725 hr = vpEngine->Log(BOOTSTRAPPER_LOG_LEVEL_ERROR, sczMessage);
726
727 LExit:
728 ReleaseStr(sczMessage);
729 ReleaseStr(sczFormattedAnsi);
730 return hr;
731 }
732
733 DAPIV_(HRESULT) BalLogId(
734 __in BOOTSTRAPPER_LOG_LEVEL level,
735 __in DWORD dwLogId,
736 __in HMODULE hModule,
737 ...
738 )
739 {
740 HRESULT hr = S_OK;
741 va_list args;
742
743 if (!vpEngine)
744 {
745 hr = E_POINTER;
746 ExitOnRootFailure(hr, "BalInitialize() must be called first.");
747 }
748
749 va_start(args, hModule);
750 hr = BalLogIdArgs(level, dwLogId, hModule, args);
751 va_end(args);
752
753 LExit:
754 return hr;
755 }
756
757 DAPI_(HRESULT) BalLogIdArgs(
758 __in BOOTSTRAPPER_LOG_LEVEL level,
759 __in DWORD dwLogId,
760 __in HMODULE hModule,
761 __in va_list args
762 )
763 {
764
765 HRESULT hr = S_OK;
766 LPWSTR pwz = NULL;
767 DWORD cch = 0;
768
769 if (!vpEngine)
770 {
771 hr = E_POINTER;
772 ExitOnRootFailure(hr, "BalInitialize() must be called first.");
773 }
774
775 // Get the string for the id.
776 #pragma prefast(push)
777 #pragma prefast(disable:25028)
778 #pragma prefast(disable:25068)
779 cch = ::FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE,
780 static_cast<LPCVOID>(hModule), dwLogId, 0, reinterpret_cast<LPWSTR>(&pwz), 0, &args);
781 #pragma prefast(pop)
782
783 if (0 == cch)
784 {
785 ExitOnLastError(hr, "Failed to log id: %d", dwLogId);
786 }
787
788 if (2 <= cch && L'\r' == pwz[cch - 2] && L'\n' == pwz[cch - 1])
789 {
790 pwz[cch - 2] = L'\0'; // remove newline from message table.
791 }
792
793 hr = vpEngine->Log(level, pwz);
794
795 LExit:
796 if (pwz)
797 {
798 ::LocalFree(pwz);
799 }
800
801 return hr;
802 }
803
804
805 static HRESULT ParseCommandLine(
806 __inout_z LPWSTR *psczPipeBaseName,
807 __inout_z LPWSTR *psczPipeSecret,
808 __out DWORD64 *pqwEngineAPIVersion
809 )
810 {
811 HRESULT hr = S_OK;
812 LPWSTR wzCommandLine = ::GetCommandLineW();
813 int argc = 0;
814 LPWSTR* argv = NULL;
815
816 *pqwEngineAPIVersion = 0;
817
818 hr = AppParseCommandLine(wzCommandLine, &argc, &argv);
819 ExitOnFailure(hr, "Failed to parse command line.");
820
821 // Skip the executable full path in argv[0].
822 for (int i = 1; i < argc; ++i)
823 {
824 if (argv[i][0] == L'-')
825 {
826 if (CSTR_EQUAL == ::CompareStringOrdinal(&argv[i][1], -1, BOOTSTRAPPER_APPLICATION_COMMANDLINE_SWITCH_API_VERSION, -1, TRUE))
827 {
828 if (i + 1 >= argc)
829 {
830 BalExitOnRootFailure(hr = E_INVALIDARG, "Must specify an api version.");
831 }
832
833 ++i;
834
835 hr = StrStringToUInt64(argv[i], 0, pqwEngineAPIVersion);
836 BalExitOnFailure(hr, "Failed to parse api version: %ls", argv[i]);
837 }
838 else if (CSTR_EQUAL == ::CompareStringOrdinal(&argv[i][1], -1, BOOTSTRAPPER_APPLICATION_COMMANDLINE_SWITCH_PIPE_NAME, -1, TRUE))
839 {
840 if (i + 2 >= argc)
841 {
842 BalExitOnRootFailure(hr = E_INVALIDARG, "Must specify a pipe name and pipe secret.");
843 }
844
845 ++i;
846
847 hr = StrAllocString(psczPipeBaseName, argv[i], 0);
848 BalExitOnFailure(hr, "Failed to copy pipe name.");
849
850 ++i;
851
852 hr = StrAllocString(psczPipeSecret, argv[i], 0);
853 BalExitOnFailure(hr, "Failed to copy pipe secret.");
854 }
855 }
856 else
857 {
858 BalExitWithRootFailure(hr, E_INVALIDARG, "Invalid argument: %ls", argv[i]);
859 }
860 }
861
862 LExit:
863 if (argv)
864 {
865 AppFreeCommandLineArgs(argv);
866 }
867
868 return hr;
869 }
870
871 static HRESULT ConnectToEngine(
872 __in_z LPCWSTR wzPipeBaseName,
873 __in_z LPCWSTR wzPipeSecret,
874 __out HANDLE *phBAPipe,
875 __out HANDLE *phEnginePipe
876 )
877 {
878 HRESULT hr = S_OK;
879 LPWSTR sczBAPipeName = NULL;
880 LPWSTR sczEnginePipeName = NULL;
881 HANDLE hBAPipe = INVALID_HANDLE_VALUE;
882 HANDLE hEnginePipe = INVALID_HANDLE_VALUE;
883
884 DWORD cbPipeSecret = lstrlenW(wzPipeSecret) * sizeof(WCHAR);
885
886 hr = StrAllocFormatted(&sczBAPipeName, L"%ls%ls", wzPipeBaseName, L".BA");
887 ExitOnFailure(hr, "Failed to allocate BA pipe name.");
888
889 hr = StrAllocFormatted(&sczEnginePipeName, L"%ls%ls", wzPipeBaseName, L".BAEngine");
890 ExitOnFailure(hr, "Failed to allocate BA engine pipe name.");
891
892 hr = ConnectAndVerify(sczBAPipeName, wzPipeSecret, cbPipeSecret, &hBAPipe);
893 BalExitOnFailure(hr, "Failed to connect to bootstrapper application pipe.");
894
895 hr = ConnectAndVerify(sczEnginePipeName, wzPipeSecret, cbPipeSecret, &hEnginePipe);
896 BalExitOnFailure(hr, "Failed to connect to engine pipe.");
897
898 *phBAPipe = hBAPipe;
899 hBAPipe = INVALID_HANDLE_VALUE;
900
901 *phEnginePipe = hEnginePipe;
902 hEnginePipe = INVALID_HANDLE_VALUE;
903
904 LExit:
905 ReleasePipeHandle(hEnginePipe);
906 ReleasePipeHandle(hBAPipe);
907 ReleaseStr(sczEnginePipeName);
908 ReleaseStr(sczBAPipeName);
909
910 return hr;
911 }
912
913 static HRESULT ConnectAndVerify(
914 __in_z LPCWSTR wzPipeName,
915 __in_z LPCWSTR wzPipeSecret,
916 __in DWORD cbPipeSecret,
917 __out HANDLE *phPipe
918 )
919 {
920 HRESULT hr = S_OK;
921 HRESULT hrConnect = S_OK;
922 HANDLE hPipe = INVALID_HANDLE_VALUE;
923
924 hr = PipeClientConnect(wzPipeName, &hPipe);
925 BalExitOnFailure(hr, "Failed to connect to pipe.");
926
927 hr = FileWriteHandle(hPipe, reinterpret_cast<LPCBYTE>(&cbPipeSecret), sizeof(cbPipeSecret));
928 BalExitOnFailure(hr, "Failed to write secret size to pipe.");
929
930 hr = FileWriteHandle(hPipe, reinterpret_cast<LPCBYTE>(wzPipeSecret), cbPipeSecret);
931 BalExitOnFailure(hr, "Failed to write secret size to pipe.");
932
933 FileReadHandle(hPipe, reinterpret_cast<LPBYTE>(&hrConnect), sizeof(hrConnect));
934 BalExitOnFailure(hr, "Failed to read connect result from pipe.");
935
936 BalExitOnFailure(hrConnect, "Failed connect result from pipe.");
937
938 *phPipe = hPipe;
939 hPipe = INVALID_HANDLE_VALUE;
940
941 LExit:
942 ReleasePipeHandle(hPipe);
943
944 return hr;
945 }