aboutsummaryrefslogtreecommitdiff
path: root/src/libs/dutil/WixToolset.DUtil/logutil.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/dutil/WixToolset.DUtil/logutil.cpp')
-rw-r--r--src/libs/dutil/WixToolset.DUtil/logutil.cpp961
1 files changed, 961 insertions, 0 deletions
diff --git a/src/libs/dutil/WixToolset.DUtil/logutil.cpp b/src/libs/dutil/WixToolset.DUtil/logutil.cpp
new file mode 100644
index 00000000..ac68036a
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/logutil.cpp
@@ -0,0 +1,961 @@
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
6// Exit macros
7#define LoguExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_LOGUTIL, x, s, __VA_ARGS__)
8#define LoguExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_LOGUTIL, x, s, __VA_ARGS__)
9#define LoguExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_LOGUTIL, x, s, __VA_ARGS__)
10#define LoguExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_LOGUTIL, x, s, __VA_ARGS__)
11#define LoguExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_LOGUTIL, x, s, __VA_ARGS__)
12#define LoguExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_LOGUTIL, x, s, __VA_ARGS__)
13#define LoguExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_LOGUTIL, p, x, e, s, __VA_ARGS__)
14#define LoguExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_LOGUTIL, p, x, s, __VA_ARGS__)
15#define LoguExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_LOGUTIL, p, x, e, s, __VA_ARGS__)
16#define LoguExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_LOGUTIL, p, x, s, __VA_ARGS__)
17#define LoguExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_LOGUTIL, e, x, s, __VA_ARGS__)
18#define LoguExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_LOGUTIL, g, x, s, __VA_ARGS__)
19
20// globals
21static HMODULE LogUtil_hModule = NULL;
22static BOOL LogUtil_fDisabled = FALSE;
23static HANDLE LogUtil_hLog = INVALID_HANDLE_VALUE;
24static LPWSTR LogUtil_sczLogPath = NULL;
25static LPSTR LogUtil_sczPreInitBuffer = NULL;
26static REPORT_LEVEL LogUtil_rlCurrent = REPORT_STANDARD;
27static CRITICAL_SECTION LogUtil_csLog = { };
28static BOOL LogUtil_fInitializedCriticalSection = FALSE;
29
30// Customization of certain parts of the string, within a line
31static LPWSTR LogUtil_sczSpecialBeginLine = NULL;
32static LPWSTR LogUtil_sczSpecialEndLine = NULL;
33static LPWSTR LogUtil_sczSpecialAfterTimeStamp = NULL;
34
35static LPCSTR LOGUTIL_UNKNOWN = "unknown";
36static LPCSTR LOGUTIL_WARNING = "warning";
37static LPCSTR LOGUTIL_STANDARD = "standard";
38static LPCSTR LOGUTIL_VERBOSE = "verbose";
39static LPCSTR LOGUTIL_DEBUG = "debug";
40static LPCSTR LOGUTIL_NONE = "none";
41
42// prototypes
43static HRESULT LogIdWork(
44 __in REPORT_LEVEL rl,
45 __in_opt HMODULE hModule,
46 __in DWORD dwLogId,
47 __in va_list args,
48 __in BOOL fLOGUTIL_NEWLINE
49 );
50static HRESULT LogStringWorkArgs(
51 __in REPORT_LEVEL rl,
52 __in_z __format_string LPCSTR szFormat,
53 __in va_list args,
54 __in BOOL fLOGUTIL_NEWLINE
55 );
56static HRESULT LogStringWork(
57 __in REPORT_LEVEL rl,
58 __in DWORD dwLogId,
59 __in_z LPCWSTR sczString,
60 __in BOOL fLOGUTIL_NEWLINE
61 );
62
63// Hook to allow redirecting LogStringWorkRaw function calls
64static PFN_LOGSTRINGWORKRAW s_vpfLogStringWorkRaw = NULL;
65static LPVOID s_vpvLogStringWorkRawContext = NULL;
66
67
68/********************************************************************
69 IsLogInitialized - Checks if log is currently initialized.
70********************************************************************/
71extern "C" BOOL DAPI IsLogInitialized()
72{
73 return LogUtil_fInitializedCriticalSection;
74}
75
76/********************************************************************
77 IsLogOpen - Checks if log is currently initialized and open.
78********************************************************************/
79extern "C" BOOL DAPI IsLogOpen()
80{
81 return (INVALID_HANDLE_VALUE != LogUtil_hLog && NULL != LogUtil_sczLogPath);
82}
83
84
85/********************************************************************
86 LogInitialize - initializes the logutil API
87
88********************************************************************/
89extern "C" void DAPI LogInitialize(
90 __in HMODULE hModule
91 )
92{
93 AssertSz(INVALID_HANDLE_VALUE == LogUtil_hLog && !LogUtil_sczLogPath, "LogInitialize() or LogOpen() - already called.");
94
95 LogUtil_hModule = hModule;
96 LogUtil_fDisabled = FALSE;
97
98 ::InitializeCriticalSection(&LogUtil_csLog);
99 LogUtil_fInitializedCriticalSection = TRUE;
100}
101
102
103/********************************************************************
104 LogOpen - creates an application log file
105
106 NOTE: if wzExt is null then wzLog is path to desired log else wzLog and wzExt are used to generate log name
107********************************************************************/
108extern "C" HRESULT DAPI LogOpen(
109 __in_z_opt LPCWSTR wzDirectory,
110 __in_z LPCWSTR wzLog,
111 __in_z_opt LPCWSTR wzPostfix,
112 __in_z_opt LPCWSTR wzExt,
113 __in BOOL fAppend,
114 __in BOOL fHeader,
115 __out_z_opt LPWSTR* psczLogPath
116 )
117{
118 HRESULT hr = S_OK;
119 BOOL fEnteredCriticalSection = FALSE;
120 LPWSTR sczLogDirectory = NULL;
121
122 ::EnterCriticalSection(&LogUtil_csLog);
123 fEnteredCriticalSection = TRUE;
124
125 if (wzExt && *wzExt)
126 {
127 hr = PathCreateTimeBasedTempFile(wzDirectory, wzLog, wzPostfix, wzExt, &LogUtil_sczLogPath, &LogUtil_hLog);
128 LoguExitOnFailure(hr, "Failed to create log based on current system time.");
129 }
130 else
131 {
132 hr = PathConcat(wzDirectory, wzLog, &LogUtil_sczLogPath);
133 LoguExitOnFailure(hr, "Failed to combine the log path.");
134
135 hr = PathGetDirectory(LogUtil_sczLogPath, &sczLogDirectory);
136 LoguExitOnFailure(hr, "Failed to get log directory.");
137
138 hr = DirEnsureExists(sczLogDirectory, NULL);
139 LoguExitOnFailure(hr, "Failed to ensure log file directory exists: %ls", sczLogDirectory);
140
141 LogUtil_hLog = ::CreateFileW(LogUtil_sczLogPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, (fAppend) ? OPEN_ALWAYS : CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
142 if (INVALID_HANDLE_VALUE == LogUtil_hLog)
143 {
144 LoguExitOnLastError(hr, "failed to create log file: %ls", LogUtil_sczLogPath);
145 }
146
147 if (fAppend)
148 {
149 ::SetFilePointer(LogUtil_hLog, 0, 0, FILE_END);
150 }
151 }
152
153 LogUtil_fDisabled = FALSE;
154
155 if (fHeader)
156 {
157 LogHeader();
158 }
159
160 if (NULL != LogUtil_sczPreInitBuffer)
161 {
162 // Log anything that was logged before LogOpen() was called.
163 LogStringWorkRaw(LogUtil_sczPreInitBuffer);
164 ReleaseNullStr(LogUtil_sczPreInitBuffer);
165 }
166
167 if (psczLogPath)
168 {
169 hr = StrAllocString(psczLogPath, LogUtil_sczLogPath, 0);
170 LoguExitOnFailure(hr, "Failed to copy log path.");
171 }
172
173LExit:
174 if (fEnteredCriticalSection)
175 {
176 ::LeaveCriticalSection(&LogUtil_csLog);
177 }
178
179 ReleaseStr(sczLogDirectory);
180
181 return hr;
182}
183
184
185/********************************************************************
186 LogDisable - closes any open files and disables in memory logging.
187
188********************************************************************/
189void DAPI LogDisable()
190{
191 ::EnterCriticalSection(&LogUtil_csLog);
192
193 LogUtil_fDisabled = TRUE;
194
195 ReleaseFileHandle(LogUtil_hLog);
196 ReleaseNullStr(LogUtil_sczLogPath);
197 ReleaseNullStr(LogUtil_sczPreInitBuffer);
198
199 ::LeaveCriticalSection(&LogUtil_csLog);
200}
201
202
203/********************************************************************
204 LogRedirect - Redirects all logging strings to the specified
205 function - or set NULL to disable the hook
206********************************************************************/
207void DAPI LogRedirect(
208 __in_opt PFN_LOGSTRINGWORKRAW vpfLogStringWorkRaw,
209 __in_opt LPVOID pvContext
210 )
211{
212 s_vpfLogStringWorkRaw = vpfLogStringWorkRaw;
213 s_vpvLogStringWorkRawContext = pvContext;
214}
215
216
217/********************************************************************
218 LogRename - Renames a logfile, moving its contents to a new path,
219 and re-opening the file for appending at the new
220 location
221********************************************************************/
222HRESULT DAPI LogRename(
223 __in_z LPCWSTR wzNewPath
224 )
225{
226 HRESULT hr = S_OK;
227 BOOL fEnteredCriticalSection = FALSE;
228
229 ::EnterCriticalSection(&LogUtil_csLog);
230 fEnteredCriticalSection = TRUE;
231
232 ReleaseFileHandle(LogUtil_hLog);
233
234 hr = FileEnsureMove(LogUtil_sczLogPath, wzNewPath, TRUE, TRUE);
235 LoguExitOnFailure(hr, "Failed to move logfile to new location: %ls", wzNewPath);
236
237 hr = StrAllocString(&LogUtil_sczLogPath, wzNewPath, 0);
238 LoguExitOnFailure(hr, "Failed to store new logfile path: %ls", wzNewPath);
239
240 LogUtil_hLog = ::CreateFileW(LogUtil_sczLogPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
241 if (INVALID_HANDLE_VALUE == LogUtil_hLog)
242 {
243 LoguExitOnLastError(hr, "failed to create log file: %ls", LogUtil_sczLogPath);
244 }
245
246 // Enable "append" mode by moving file pointer to the end
247 ::SetFilePointer(LogUtil_hLog, 0, 0, FILE_END);
248
249LExit:
250 if (fEnteredCriticalSection)
251 {
252 ::LeaveCriticalSection(&LogUtil_csLog);
253 }
254
255 return hr;
256}
257
258
259extern "C" void DAPI LogClose(
260 __in BOOL fFooter
261 )
262{
263 if (INVALID_HANDLE_VALUE != LogUtil_hLog && fFooter)
264 {
265 LogFooter();
266 }
267
268 ReleaseFileHandle(LogUtil_hLog);
269 ReleaseNullStr(LogUtil_sczLogPath);
270 ReleaseNullStr(LogUtil_sczPreInitBuffer);
271}
272
273
274extern "C" void DAPI LogUninitialize(
275 __in BOOL fFooter
276 )
277{
278 LogClose(fFooter);
279
280 if (LogUtil_fInitializedCriticalSection)
281 {
282 ::DeleteCriticalSection(&LogUtil_csLog);
283 LogUtil_fInitializedCriticalSection = FALSE;
284 }
285
286 LogUtil_hModule = NULL;
287 LogUtil_fDisabled = FALSE;
288
289 ReleaseNullStr(LogUtil_sczSpecialBeginLine);
290 ReleaseNullStr(LogUtil_sczSpecialAfterTimeStamp);
291 ReleaseNullStr(LogUtil_sczSpecialEndLine);
292}
293
294
295/********************************************************************
296 LogIsOpen - returns whether log file is open or note
297
298********************************************************************/
299extern "C" BOOL DAPI LogIsOpen()
300{
301 return INVALID_HANDLE_VALUE != LogUtil_hLog;
302}
303
304
305/********************************************************************
306 LogSetSpecialParams - sets a special beginline string, endline
307 string, post-timestamp string, etc.
308********************************************************************/
309HRESULT DAPI LogSetSpecialParams(
310 __in_z_opt LPCWSTR wzSpecialBeginLine,
311 __in_z_opt LPCWSTR wzSpecialAfterTimeStamp,
312 __in_z_opt LPCWSTR wzSpecialEndLine
313 )
314{
315 HRESULT hr = S_OK;
316
317 // Handle special string to be prepended before every full line
318 if (NULL == wzSpecialBeginLine)
319 {
320 ReleaseNullStr(LogUtil_sczSpecialBeginLine);
321 }
322 else
323 {
324 hr = StrAllocConcat(&LogUtil_sczSpecialBeginLine, wzSpecialBeginLine, 0);
325 LoguExitOnFailure(hr, "Failed to allocate copy of special beginline string");
326 }
327
328 // Handle special string to be appended to every time stamp
329 if (NULL == wzSpecialAfterTimeStamp)
330 {
331 ReleaseNullStr(LogUtil_sczSpecialAfterTimeStamp);
332 }
333 else
334 {
335 hr = StrAllocConcat(&LogUtil_sczSpecialAfterTimeStamp, wzSpecialAfterTimeStamp, 0);
336 LoguExitOnFailure(hr, "Failed to allocate copy of special post-timestamp string");
337 }
338
339 // Handle special string to be appended before every full line
340 if (NULL == wzSpecialEndLine)
341 {
342 ReleaseNullStr(LogUtil_sczSpecialEndLine);
343 }
344 else
345 {
346 hr = StrAllocConcat(&LogUtil_sczSpecialEndLine, wzSpecialEndLine, 0);
347 LoguExitOnFailure(hr, "Failed to allocate copy of special endline string");
348 }
349
350LExit:
351 return hr;
352}
353
354/********************************************************************
355 LogSetLevel - sets the logging level
356
357 NOTE: returns previous logging level
358********************************************************************/
359extern "C" REPORT_LEVEL DAPI LogSetLevel(
360 __in REPORT_LEVEL rl,
361 __in BOOL fLogChange
362 )
363{
364 AssertSz(REPORT_ERROR != rl, "REPORT_ERROR is not a valid logging level to set");
365
366 REPORT_LEVEL rlPrev = LogUtil_rlCurrent;
367
368 if (LogUtil_rlCurrent != rl)
369 {
370 LogUtil_rlCurrent = rl;
371
372 if (fLogChange)
373 {
374 LPCSTR szLevel = LOGUTIL_UNKNOWN;
375 switch (LogUtil_rlCurrent)
376 {
377 case REPORT_WARNING:
378 szLevel = LOGUTIL_WARNING;
379 break;
380 case REPORT_STANDARD:
381 szLevel = LOGUTIL_STANDARD;
382 break;
383 case REPORT_VERBOSE:
384 szLevel = LOGUTIL_VERBOSE;
385 break;
386 case REPORT_DEBUG:
387 szLevel = LOGUTIL_DEBUG;
388 break;
389 case REPORT_NONE:
390 szLevel = LOGUTIL_NONE;
391 break;
392 }
393
394 LogStringLine(REPORT_STANDARD, "--- logging level: %hs ---", szLevel);
395 }
396 }
397
398 return rlPrev;
399}
400
401
402/********************************************************************
403 LogGetLevel - gets the current logging level
404
405********************************************************************/
406extern "C" REPORT_LEVEL DAPI LogGetLevel()
407{
408 return LogUtil_rlCurrent;
409}
410
411
412/********************************************************************
413 LogGetPath - gets the current log path
414
415********************************************************************/
416extern "C" HRESULT DAPI LogGetPath(
417 __out_ecount_z(cchLogPath) LPWSTR pwzLogPath,
418 __in DWORD cchLogPath
419 )
420{
421 Assert(pwzLogPath);
422
423 HRESULT hr = S_OK;
424
425 if (NULL == LogUtil_sczLogPath) // they can't have a path if there isn't one!
426 {
427 ExitFunction1(hr = E_UNEXPECTED);
428 }
429
430 hr = ::StringCchCopyW(pwzLogPath, cchLogPath, LogUtil_sczLogPath);
431
432LExit:
433 return hr;
434}
435
436
437/********************************************************************
438 LogGetHandle - gets the current log file handle
439
440********************************************************************/
441extern "C" HANDLE DAPI LogGetHandle()
442{
443 return LogUtil_hLog;
444}
445
446
447/********************************************************************
448 LogString - write a string to the log
449
450 NOTE: use printf formatting ("%ls", "%d", etc.)
451********************************************************************/
452extern "C" HRESULT DAPIV LogString(
453 __in REPORT_LEVEL rl,
454 __in_z __format_string LPCSTR szFormat,
455 ...
456 )
457{
458 HRESULT hr = S_OK;
459 va_list args;
460
461 va_start(args, szFormat);
462 hr = LogStringArgs(rl, szFormat, args);
463 va_end(args);
464
465 return hr;
466}
467
468extern "C" HRESULT DAPI LogStringArgs(
469 __in REPORT_LEVEL rl,
470 __in_z __format_string LPCSTR szFormat,
471 __in va_list args
472 )
473{
474 AssertSz(REPORT_NONE != rl, "REPORT_NONE is not a valid logging level");
475 HRESULT hr = S_OK;
476
477 if (REPORT_ERROR != rl && LogUtil_rlCurrent < rl)
478 {
479 ExitFunction1(hr = S_FALSE);
480 }
481
482 hr = LogStringWorkArgs(rl, szFormat, args, FALSE);
483
484LExit:
485 return hr;
486}
487
488/********************************************************************
489 LogStringLine - write a string plus LOGUTIL_NEWLINE to the log
490
491 NOTE: use printf formatting ("%ls", "%d", etc.)
492********************************************************************/
493extern "C" HRESULT DAPIV LogStringLine(
494 __in REPORT_LEVEL rl,
495 __in_z __format_string LPCSTR szFormat,
496 ...
497 )
498{
499 HRESULT hr = S_OK;
500 va_list args;
501
502 va_start(args, szFormat);
503 hr = LogStringLineArgs(rl, szFormat, args);
504 va_end(args);
505
506 return hr;
507}
508
509extern "C" HRESULT DAPI LogStringLineArgs(
510 __in REPORT_LEVEL rl,
511 __in_z __format_string LPCSTR szFormat,
512 __in va_list args
513 )
514{
515 AssertSz(REPORT_NONE != rl, "REPORT_NONE is not a valid logging level");
516 HRESULT hr = S_OK;
517
518 if (REPORT_ERROR != rl && LogUtil_rlCurrent < rl)
519 {
520 ExitFunction1(hr = S_FALSE);
521 }
522
523 hr = LogStringWorkArgs(rl, szFormat, args, TRUE);
524
525LExit:
526 return hr;
527}
528
529/********************************************************************
530 LogIdModuleArgs - write a string embedded in a MESSAGETABLE to the log
531
532 NOTE: uses format string from MESSAGETABLE resource
533********************************************************************/
534
535extern "C" HRESULT DAPI LogIdModuleArgs(
536 __in REPORT_LEVEL rl,
537 __in DWORD dwLogId,
538 __in_opt HMODULE hModule,
539 __in va_list args
540 )
541{
542 AssertSz(REPORT_NONE != rl, "REPORT_NONE is not a valid logging level");
543 HRESULT hr = S_OK;
544
545 if (REPORT_ERROR != rl && LogUtil_rlCurrent < rl)
546 {
547 ExitFunction1(hr = S_FALSE);
548 }
549
550 hr = LogIdWork(rl, (hModule) ? hModule : LogUtil_hModule, dwLogId, args, TRUE);
551
552LExit:
553 return hr;
554}
555
556extern "C" HRESULT DAPI LogIdModule(
557 __in REPORT_LEVEL rl,
558 __in DWORD dwLogId,
559 __in_opt HMODULE hModule,
560 ...
561 )
562{
563 AssertSz(REPORT_NONE != rl, "REPORT_NONE is not a valid logging level");
564 HRESULT hr = S_OK;
565 va_list args;
566
567 if (REPORT_ERROR != rl && LogUtil_rlCurrent < rl)
568 {
569 ExitFunction1(hr = S_FALSE);
570 }
571
572 va_start(args, hModule);
573 hr = LogIdWork(rl, (hModule) ? hModule : LogUtil_hModule, dwLogId, args, TRUE);
574 va_end(args);
575
576LExit:
577 return hr;
578}
579
580
581
582
583/********************************************************************
584 LogError - write an error to the log
585
586 NOTE: use printf formatting ("%ls", "%d", etc.)
587********************************************************************/
588extern "C" HRESULT DAPIV LogErrorString(
589 __in HRESULT hrError,
590 __in_z __format_string LPCSTR szFormat,
591 ...
592 )
593{
594 HRESULT hr = S_OK;
595
596 va_list args;
597 va_start(args, szFormat);
598 hr = LogErrorStringArgs(hrError, szFormat, args);
599 va_end(args);
600
601 return hr;
602}
603
604extern "C" HRESULT DAPI LogErrorStringArgs(
605 __in HRESULT hrError,
606 __in_z __format_string LPCSTR szFormat,
607 __in va_list args
608 )
609{
610 HRESULT hr = S_OK;
611 LPWSTR sczFormat = NULL;
612 LPWSTR sczMessage = NULL;
613
614 hr = StrAllocStringAnsi(&sczFormat, szFormat, 0, CP_ACP);
615 LoguExitOnFailure(hr, "Failed to convert format string to wide character string");
616
617 // format the string as a unicode string - this is necessary to be able to include
618 // international characters in our output string. This does have the counterintuitive effect
619 // that the caller's "%s" is interpreted differently
620 // (so callers should use %hs for LPSTR and %ls for LPWSTR)
621 hr = StrAllocFormattedArgs(&sczMessage, sczFormat, args);
622 LoguExitOnFailure(hr, "Failed to format error message: \"%ls\"", sczFormat);
623
624 hr = LogStringLine(REPORT_ERROR, "Error 0x%x: %ls", hrError, sczMessage);
625
626LExit:
627 ReleaseStr(sczFormat);
628 ReleaseStr(sczMessage);
629
630 return hr;
631}
632
633
634/********************************************************************
635 LogErrorIdModule - write an error string embedded in a MESSAGETABLE to the log
636
637 NOTE: uses format string from MESSAGETABLE resource
638 can log no more than three strings in the error message
639********************************************************************/
640extern "C" HRESULT DAPI LogErrorIdModule(
641 __in HRESULT hrError,
642 __in DWORD dwLogId,
643 __in_opt HMODULE hModule,
644 __in_z_opt LPCWSTR wzString1 = NULL,
645 __in_z_opt LPCWSTR wzString2 = NULL,
646 __in_z_opt LPCWSTR wzString3 = NULL
647 )
648{
649 HRESULT hr = S_OK;
650 WCHAR wzError[11];
651 WORD cStrings = 1; // guaranteed wzError is in the list
652
653 hr = ::StringCchPrintfW(wzError, countof(wzError), L"0x%08x", hrError);
654 LoguExitOnFailure(hr, "failed to format error code: \"0%08x\"", hrError);
655
656 cStrings += wzString1 ? 1 : 0;
657 cStrings += wzString2 ? 1 : 0;
658 cStrings += wzString3 ? 1 : 0;
659
660 hr = LogIdModule(REPORT_ERROR, dwLogId, hModule, wzError, wzString1, wzString2, wzString3);
661 LoguExitOnFailure(hr, "Failed to log id module.");
662
663LExit:
664 return hr;
665}
666
667/********************************************************************
668 LogHeader - write a standard header to the log
669
670********************************************************************/
671extern "C" HRESULT DAPI LogHeader()
672{
673 HRESULT hr = S_OK;
674 WCHAR wzComputerName[MAX_PATH];
675 DWORD cchComputerName = countof(wzComputerName);
676 WCHAR wzPath[MAX_PATH];
677 DWORD dwMajorVersion = 0;
678 DWORD dwMinorVersion = 0;
679 LPCSTR szLevel = LOGUTIL_UNKNOWN;
680 LPWSTR sczCurrentDateTime = NULL;
681
682 //
683 // get the interesting data
684 //
685 if (!::GetModuleFileNameW(NULL, wzPath, countof(wzPath)))
686 {
687 memset(wzPath, 0, sizeof(wzPath));
688 }
689
690 hr = FileVersion(wzPath, &dwMajorVersion, &dwMinorVersion);
691 if (FAILED(hr))
692 {
693 dwMajorVersion = 0;
694 dwMinorVersion = 0;
695 }
696
697 if (!::GetComputerNameW(wzComputerName, &cchComputerName))
698 {
699 ::SecureZeroMemory(wzComputerName, sizeof(wzComputerName));
700 }
701
702 TimeCurrentDateTime(&sczCurrentDateTime, FALSE);
703
704 //
705 // write data to the log
706 //
707 LogStringLine(REPORT_STANDARD, "=== Logging started: %ls ===", sczCurrentDateTime);
708 LogStringLine(REPORT_STANDARD, "Executable: %ls v%d.%d.%d.%d", wzPath, dwMajorVersion >> 16, dwMajorVersion & 0xFFFF, dwMinorVersion >> 16, dwMinorVersion & 0xFFFF);
709 LogStringLine(REPORT_STANDARD, "Computer : %ls", wzComputerName);
710 switch (LogUtil_rlCurrent)
711 {
712 case REPORT_WARNING:
713 szLevel = LOGUTIL_WARNING;
714 break;
715 case REPORT_STANDARD:
716 szLevel = LOGUTIL_STANDARD;
717 break;
718 case REPORT_VERBOSE:
719 szLevel = LOGUTIL_VERBOSE;
720 break;
721 case REPORT_DEBUG:
722 szLevel = LOGUTIL_DEBUG;
723 break;
724 case REPORT_NONE:
725 szLevel = LOGUTIL_NONE;
726 break;
727 }
728 LogStringLine(REPORT_STANDARD, "--- logging level: %hs ---", szLevel);
729
730 hr = S_OK;
731
732 ReleaseStr(sczCurrentDateTime);
733
734 return hr;
735}
736
737
738/********************************************************************
739 LogFooterWork - write a standard footer to the log
740
741********************************************************************/
742
743static HRESULT LogFooterWork(
744 __in_z __format_string LPCSTR szFormat,
745 ...
746 )
747{
748 HRESULT hr = S_OK;
749
750 va_list args;
751 va_start(args, szFormat);
752 hr = LogStringWorkArgs(REPORT_STANDARD, szFormat, args, TRUE);
753 va_end(args);
754
755 return hr;
756}
757
758extern "C" HRESULT DAPI LogFooter()
759{
760 HRESULT hr = S_OK;
761 LPWSTR sczCurrentDateTime = NULL;
762 TimeCurrentDateTime(&sczCurrentDateTime, FALSE);
763 hr = LogFooterWork("=== Logging stopped: %ls ===", sczCurrentDateTime);
764 ReleaseStr(sczCurrentDateTime);
765 return hr;
766}
767
768/********************************************************************
769 LogStringWorkRaw - Write a raw, unformatted string to the log
770
771********************************************************************/
772extern "C" HRESULT LogStringWorkRaw(
773 __in_z LPCSTR szLogData
774 )
775{
776 Assert(szLogData && *szLogData);
777
778 HRESULT hr = S_OK;
779 size_t cchLogData = 0;
780 DWORD cbLogData = 0;
781 DWORD cbTotal = 0;
782 DWORD cbWrote = 0;
783
784 hr = ::StringCchLengthA(szLogData, STRSAFE_MAX_CCH, &cchLogData);
785 LoguExitOnRootFailure(hr, "Failed to get length of raw string");
786
787 cbLogData = (DWORD)cchLogData;
788
789 // If the log hasn't been initialized yet, store it in a buffer
790 if (INVALID_HANDLE_VALUE == LogUtil_hLog)
791 {
792 hr = StrAnsiAllocConcat(&LogUtil_sczPreInitBuffer, szLogData, 0);
793 LoguExitOnFailure(hr, "Failed to concatenate string to pre-init buffer");
794
795 ExitFunction1(hr = S_OK);
796 }
797
798 // write the string
799 while (cbTotal < cbLogData)
800 {
801 if (!::WriteFile(LogUtil_hLog, reinterpret_cast<const BYTE*>(szLogData) + cbTotal, cbLogData - cbTotal, &cbWrote, NULL))
802 {
803 LoguExitOnLastError(hr, "Failed to write output to log: %ls - %hs", LogUtil_sczLogPath, szLogData);
804 }
805
806 cbTotal += cbWrote;
807 }
808
809LExit:
810 return hr;
811}
812
813//
814// private worker functions
815//
816static HRESULT LogIdWork(
817 __in REPORT_LEVEL rl,
818 __in_opt HMODULE hModule,
819 __in DWORD dwLogId,
820 __in va_list args,
821 __in BOOL fLOGUTIL_NEWLINE
822 )
823{
824 HRESULT hr = S_OK;
825 LPWSTR pwz = NULL;
826 DWORD cch = 0;
827
828 // get the string for the id
829#pragma prefast(push)
830#pragma prefast(disable:25028)
831#pragma prefast(disable:25068)
832 cch = ::FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE,
833 static_cast<LPCVOID>(hModule), dwLogId, 0, reinterpret_cast<LPWSTR>(&pwz), 0, &args);
834#pragma prefast(pop)
835
836 if (0 == cch)
837 {
838 LoguExitOnLastError(hr, "failed to log id: %d", dwLogId);
839 }
840
841 if (2 <= cch && L'\r' == pwz[cch-2] && L'\n' == pwz[cch-1])
842 {
843 pwz[cch-2] = L'\0'; // remove newline from message table
844 }
845
846 LogStringWork(rl, dwLogId, pwz, fLOGUTIL_NEWLINE);
847
848LExit:
849 if (pwz)
850 {
851 ::LocalFree(pwz);
852 }
853
854 return hr;
855}
856
857
858static HRESULT LogStringWorkArgs(
859 __in REPORT_LEVEL rl,
860 __in_z __format_string LPCSTR szFormat,
861 __in va_list args,
862 __in BOOL fLOGUTIL_NEWLINE
863 )
864{
865 Assert(szFormat && *szFormat);
866
867 HRESULT hr = S_OK;
868 LPWSTR sczFormat = NULL;
869 LPWSTR sczMessage = NULL;
870
871 hr = StrAllocStringAnsi(&sczFormat, szFormat, 0, CP_ACP);
872 LoguExitOnFailure(hr, "Failed to convert format string to wide character string");
873
874 // format the string as a unicode string
875 hr = StrAllocFormattedArgs(&sczMessage, sczFormat, args);
876 LoguExitOnFailure(hr, "Failed to format message: \"%ls\"", sczFormat);
877
878 hr = LogStringWork(rl, 0, sczMessage, fLOGUTIL_NEWLINE);
879 LoguExitOnFailure(hr, "Failed to write formatted string to log:%ls", sczMessage);
880
881LExit:
882 ReleaseStr(sczFormat);
883 ReleaseStr(sczMessage);
884
885 return hr;
886}
887
888
889static HRESULT LogStringWork(
890 __in REPORT_LEVEL rl,
891 __in DWORD dwLogId,
892 __in_z LPCWSTR sczString,
893 __in BOOL fLOGUTIL_NEWLINE
894 )
895{
896 Assert(sczString && *sczString);
897
898 HRESULT hr = S_OK;
899 BOOL fEnteredCriticalSection = FALSE;
900 LPWSTR scz = NULL;
901 LPCWSTR wzLogData = NULL;
902 LPSTR sczMultiByte = NULL;
903
904 // If logging is disabled, just bail.
905 if (LogUtil_fDisabled)
906 {
907 ExitFunction();
908 }
909
910 ::EnterCriticalSection(&LogUtil_csLog);
911 fEnteredCriticalSection = TRUE;
912
913 if (fLOGUTIL_NEWLINE)
914 {
915 // get the process and thread id.
916 DWORD dwProcessId = ::GetCurrentProcessId();
917 DWORD dwThreadId = ::GetCurrentThreadId();
918
919 // get the time relative to GMT.
920 SYSTEMTIME st = { };
921 ::GetLocalTime(&st);
922
923 DWORD dwId = dwLogId & 0xFFFFFFF;
924 DWORD dwType = dwLogId & 0xF0000000;
925 LPSTR szType = (0xE0000000 == dwType || REPORT_ERROR == rl) ? "e" : (0xA0000000 == dwType || REPORT_WARNING == rl) ? "w" : "i";
926
927 // add line prefix and trailing newline
928 hr = StrAllocFormatted(&scz, L"%ls[%04X:%04X][%04hu-%02hu-%02huT%02hu:%02hu:%02hu]%hs%03d:%ls %ls%ls", LogUtil_sczSpecialBeginLine ? LogUtil_sczSpecialBeginLine : L"",
929 dwProcessId, dwThreadId, st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, szType, dwId,
930 LogUtil_sczSpecialAfterTimeStamp ? LogUtil_sczSpecialAfterTimeStamp : L"", sczString, LogUtil_sczSpecialEndLine ? LogUtil_sczSpecialEndLine : L"\r\n");
931 LoguExitOnFailure(hr, "Failed to format line prefix.");
932 }
933
934 wzLogData = scz ? scz : sczString;
935
936 // Convert to UTF-8 before writing out to the log file
937 hr = StrAnsiAllocString(&sczMultiByte, wzLogData, 0, CP_UTF8);
938 LoguExitOnFailure(hr, "Failed to convert log string to UTF-8");
939
940 if (s_vpfLogStringWorkRaw)
941 {
942 hr = s_vpfLogStringWorkRaw(sczMultiByte, s_vpvLogStringWorkRawContext);
943 LoguExitOnFailure(hr, "Failed to write string to log using redirected function: %ls", sczString);
944 }
945 else
946 {
947 hr = LogStringWorkRaw(sczMultiByte);
948 LoguExitOnFailure(hr, "Failed to write string to log using default function: %ls", sczString);
949 }
950
951LExit:
952 if (fEnteredCriticalSection)
953 {
954 ::LeaveCriticalSection(&LogUtil_csLog);
955 }
956
957 ReleaseStr(scz);
958 ReleaseStr(sczMultiByte);
959
960 return hr;
961}