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