diff options
Diffstat (limited to 'src/libs/dutil/WixToolset.DUtil/logutil.cpp')
-rw-r--r-- | src/libs/dutil/WixToolset.DUtil/logutil.cpp | 961 |
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 | ||
21 | static HMODULE LogUtil_hModule = NULL; | ||
22 | static BOOL LogUtil_fDisabled = FALSE; | ||
23 | static HANDLE LogUtil_hLog = INVALID_HANDLE_VALUE; | ||
24 | static LPWSTR LogUtil_sczLogPath = NULL; | ||
25 | static LPSTR LogUtil_sczPreInitBuffer = NULL; | ||
26 | static REPORT_LEVEL LogUtil_rlCurrent = REPORT_STANDARD; | ||
27 | static CRITICAL_SECTION LogUtil_csLog = { }; | ||
28 | static BOOL LogUtil_fInitializedCriticalSection = FALSE; | ||
29 | |||
30 | // Customization of certain parts of the string, within a line | ||
31 | static LPWSTR LogUtil_sczSpecialBeginLine = NULL; | ||
32 | static LPWSTR LogUtil_sczSpecialEndLine = NULL; | ||
33 | static LPWSTR LogUtil_sczSpecialAfterTimeStamp = NULL; | ||
34 | |||
35 | static LPCSTR LOGUTIL_UNKNOWN = "unknown"; | ||
36 | static LPCSTR LOGUTIL_WARNING = "warning"; | ||
37 | static LPCSTR LOGUTIL_STANDARD = "standard"; | ||
38 | static LPCSTR LOGUTIL_VERBOSE = "verbose"; | ||
39 | static LPCSTR LOGUTIL_DEBUG = "debug"; | ||
40 | static LPCSTR LOGUTIL_NONE = "none"; | ||
41 | |||
42 | // prototypes | ||
43 | static 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 | ); | ||
50 | static 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 | ); | ||
56 | static 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 | ||
64 | static PFN_LOGSTRINGWORKRAW s_vpfLogStringWorkRaw = NULL; | ||
65 | static LPVOID s_vpvLogStringWorkRawContext = NULL; | ||
66 | |||
67 | |||
68 | /******************************************************************** | ||
69 | IsLogInitialized - Checks if log is currently initialized. | ||
70 | ********************************************************************/ | ||
71 | extern "C" BOOL DAPI IsLogInitialized() | ||
72 | { | ||
73 | return LogUtil_fInitializedCriticalSection; | ||
74 | } | ||
75 | |||
76 | /******************************************************************** | ||
77 | IsLogOpen - Checks if log is currently initialized and open. | ||
78 | ********************************************************************/ | ||
79 | extern "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 | ********************************************************************/ | ||
89 | extern "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 | ********************************************************************/ | ||
108 | extern "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 | |||
173 | LExit: | ||
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 | ********************************************************************/ | ||
189 | void 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 | ********************************************************************/ | ||
207 | void 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 | ********************************************************************/ | ||
222 | HRESULT 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 | |||
249 | LExit: | ||
250 | if (fEnteredCriticalSection) | ||
251 | { | ||
252 | ::LeaveCriticalSection(&LogUtil_csLog); | ||
253 | } | ||
254 | |||
255 | return hr; | ||
256 | } | ||
257 | |||
258 | |||
259 | extern "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 | |||
274 | extern "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 | ********************************************************************/ | ||
299 | extern "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 | ********************************************************************/ | ||
309 | HRESULT 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 | |||
350 | LExit: | ||
351 | return hr; | ||
352 | } | ||
353 | |||
354 | /******************************************************************** | ||
355 | LogSetLevel - sets the logging level | ||
356 | |||
357 | NOTE: returns previous logging level | ||
358 | ********************************************************************/ | ||
359 | extern "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 | ********************************************************************/ | ||
406 | extern "C" REPORT_LEVEL DAPI LogGetLevel() | ||
407 | { | ||
408 | return LogUtil_rlCurrent; | ||
409 | } | ||
410 | |||
411 | |||
412 | /******************************************************************** | ||
413 | LogGetPath - gets the current log path | ||
414 | |||
415 | ********************************************************************/ | ||
416 | extern "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 | |||
432 | LExit: | ||
433 | return hr; | ||
434 | } | ||
435 | |||
436 | |||
437 | /******************************************************************** | ||
438 | LogGetHandle - gets the current log file handle | ||
439 | |||
440 | ********************************************************************/ | ||
441 | extern "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 | ********************************************************************/ | ||
452 | extern "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 | |||
468 | extern "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 | |||
484 | LExit: | ||
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 | ********************************************************************/ | ||
493 | extern "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 | |||
509 | extern "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 | |||
525 | LExit: | ||
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 | |||
535 | extern "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 | |||
552 | LExit: | ||
553 | return hr; | ||
554 | } | ||
555 | |||
556 | extern "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 | |||
576 | LExit: | ||
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 | ********************************************************************/ | ||
588 | extern "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 | |||
604 | extern "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 | |||
626 | LExit: | ||
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 | ********************************************************************/ | ||
640 | extern "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 | |||
663 | LExit: | ||
664 | return hr; | ||
665 | } | ||
666 | |||
667 | /******************************************************************** | ||
668 | LogHeader - write a standard header to the log | ||
669 | |||
670 | ********************************************************************/ | ||
671 | extern "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 | |||
743 | static 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 | |||
758 | extern "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 | ********************************************************************/ | ||
772 | extern "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 | |||
809 | LExit: | ||
810 | return hr; | ||
811 | } | ||
812 | |||
813 | // | ||
814 | // private worker functions | ||
815 | // | ||
816 | static 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 | |||
848 | LExit: | ||
849 | if (pwz) | ||
850 | { | ||
851 | ::LocalFree(pwz); | ||
852 | } | ||
853 | |||
854 | return hr; | ||
855 | } | ||
856 | |||
857 | |||
858 | static 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 | |||
881 | LExit: | ||
882 | ReleaseStr(sczFormat); | ||
883 | ReleaseStr(sczMessage); | ||
884 | |||
885 | return hr; | ||
886 | } | ||
887 | |||
888 | |||
889 | static 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 | |||
951 | LExit: | ||
952 | if (fEnteredCriticalSection) | ||
953 | { | ||
954 | ::LeaveCriticalSection(&LogUtil_csLog); | ||
955 | } | ||
956 | |||
957 | ReleaseStr(scz); | ||
958 | ReleaseStr(sczMultiByte); | ||
959 | |||
960 | return hr; | ||
961 | } | ||