Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3251 lines
71 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. log.c
  5. Abstract:
  6. Tools for logging problems for the user.
  7. Author:
  8. Jim Schmidt (jimschm) 23-Jan-1997
  9. Revisions:
  10. ovidiut 08-Oct-1999 Updated for new coding conventions and Win64 compliance
  11. ovidiut 23-Oct-1998 Implemented a new log mechanism and added new logging capabilities
  12. marcw 2-Sep-1999 Moved over from Win9xUpg project.
  13. ovidiut 15-Mar-2000 Eliminate dependencies on HashTable/PoolMemory
  14. --*/
  15. #include "pch.h"
  16. //
  17. // Includes
  18. //
  19. // None
  20. //
  21. // Strings
  22. //
  23. #define S_COLUMNDOUBLELINEA ":\r\n\r\n"
  24. #define S_COLUMNDOUBLELINEW L":\r\n\r\n"
  25. #define S_NEWLINEA "\r\n"
  26. #define S_NEWLINEW L"\r\n"
  27. #define DEBUG_SECTION "Debug"
  28. #define ENTRY_ALL "All"
  29. #define ENTRY_DEFAULTOVERRIDE "DefaultOverride"
  30. //
  31. // Constants
  32. //
  33. #define OUTPUT_BUFSIZE_LARGE 8192
  34. #define OUTPUT_BUFSIZE_SMALL 1024
  35. #define MAX_MSGTITLE_LEN 13
  36. #define MSGBODY_INDENT 14
  37. #define SCREEN_WIDTH 80
  38. #define MAX_TYPE 32
  39. #define TYPE_ARRAY_SIZE 10
  40. //
  41. // Macros
  42. //
  43. #define OUT_UNDEFINED(OutDest) (OutDest == OD_UNDEFINED)
  44. #define OUT_DEBUGLOG(OutDest) ((OutDest & OD_DEBUGLOG) != 0)
  45. #define OUT_SUPPRESSED(OutDest) ((OutDest & OD_SUPPRESS) != 0)
  46. #define OUT_NO_OUTPUT(OutDest) (OUT_UNDEFINED(OutDest) || OUT_SUPPRESSED(OutDest))
  47. #define OUT_ERROR(OutDest) ((OutDest & OD_ERROR) != 0)
  48. #define OUT_LOGFILE(OutDest) ((OutDest & OD_LOGFILE) != 0)
  49. #define OUT_DEBUGGER(OutDest) ((OutDest & OD_DEBUGGER) != 0)
  50. #define OUT_CONSOLE(OutDest) ((OutDest & OD_CONSOLE) != 0)
  51. #define OUT_POPUP(OutDest) ((OutDest & (OD_POPUP|OD_FORCE_POPUP|OD_UNATTEND_POPUP)) != 0)
  52. #define OUT_POPUP_CANCEL(OutDest) ((OutDest & (OD_POPUP_CANCEL|OD_FORCE_POPUP)) == OD_POPUP_CANCEL)
  53. #define OUT_FORCED_POPUP(OutDest) ((OutDest & (OD_FORCE_POPUP|OD_UNATTEND_POPUP)) != 0)
  54. #define MUST_BE_LOCALIZED(OutDest) ((OutDest & OD_MUST_BE_LOCALIZED) == OD_MUST_BE_LOCALIZED)
  55. #define OUT_ASSERT(OutDest) ((OutDest & OD_ASSERT) != 0)
  56. #ifdef DEBUG
  57. #define DEFAULT_ERROR_FLAGS (OD_DEBUGLOG | OD_LOGFILE | OD_POPUP | OD_ERROR | OD_UNATTEND_POPUP | OD_ASSERT)
  58. #define USER_POPUP_FLAGS (OD_FORCE_POPUP)
  59. #else
  60. #define DEFAULT_ERROR_FLAGS (OD_LOGFILE | OD_POPUP | OD_ERROR | OD_MUST_BE_LOCALIZED)
  61. #define USER_POPUP_FLAGS (OD_FORCE_POPUP | OD_MUST_BE_LOCALIZED)
  62. #endif
  63. #define END_OF_BUFFER(buf) ((buf) + (DWSIZEOF(buf) / DWSIZEOF(buf[0])) - 1)
  64. // This constant sets the default output
  65. #ifndef DEBUG
  66. #define NORMAL_DEFAULT OD_LOGFILE
  67. #else
  68. #define NORMAL_DEFAULT OD_DEBUGLOG
  69. #endif
  70. #ifdef DEBUG
  71. #define PRIVATE_ASSERT(expr) pPrivateAssert(expr,#expr,__LINE__);
  72. #else
  73. #define PRIVATE_ASSERT(expr)
  74. #endif // DEBUG
  75. #define NEWLINE_CHAR_COUNTA (DWSIZEOF (S_NEWLINEA) / DWSIZEOF (CHAR) - 1)
  76. #define NEWLINE_CHAR_COUNTW (DWSIZEOF (S_NEWLINEW) / DWSIZEOF (WCHAR) - 1)
  77. //
  78. // Types
  79. //
  80. typedef DWORD OUTPUTDEST;
  81. typedef struct {
  82. PCSTR Value; // string value entered by the user (LOG,POPUP,SUPPRESS etc.)
  83. OUTPUTDEST OutDest; // any combination of OutDest flags
  84. } STRING2BINARY, *PSTRING2BINARY;
  85. typedef struct {
  86. PCSTR Type;
  87. DWORD Flags;
  88. } DEFAULT_DESTINATION, *PDEFAULT_DESTINATION;
  89. typedef struct {
  90. CHAR Type[MAX_TYPE];
  91. DWORD OutputDest;
  92. } MAPTYPE2OUTDEST, *PMAPTYPE2OUTDEST;
  93. //
  94. // Globals
  95. //
  96. const STRING2BINARY g_String2Binary[] = {
  97. "SUPPRESS", OD_SUPPRESS,
  98. "LOG", OD_LOGFILE,
  99. "POPUP", OD_POPUP,
  100. "DEBUGGER", OD_DEBUGGER,
  101. "CONSOLE", OD_CONSOLE,
  102. "ERROR", OD_ERROR,
  103. "NOCANCEL", OD_FORCE_POPUP,
  104. "ASSERT", OD_ASSERT
  105. };
  106. const PCSTR g_IgnoreKeys[] = {
  107. "Debug",
  108. "KeepTempFiles"
  109. };
  110. BOOL g_LogInit;
  111. HMODULE g_LibHandle;
  112. CHAR g_MainLogFile [MAX_PATH] = "";
  113. HANDLE g_LogMutex;
  114. INT g_LoggingNow;
  115. // a window handle for popup parent
  116. HWND g_LogPopupParentWnd = NULL;
  117. // thread id that set this window handle
  118. DWORD g_InitThreadId = 0;
  119. DWORD g_LogError;
  120. //
  121. // type table elements
  122. //
  123. PMAPTYPE2OUTDEST g_FirstTypePtr = NULL;
  124. DWORD g_TypeTableCount = 0;
  125. DWORD g_TypeTableFreeCount = 0;
  126. OUTPUTDEST g_OutDestAll = OD_UNDEFINED;
  127. OUTPUTDEST g_OutDestDefault = NORMAL_DEFAULT;
  128. BOOL g_HasTitle = FALSE;
  129. CHAR g_LastType [MAX_TYPE];
  130. BOOL g_SuppressAllPopups = FALSE;
  131. BOOL g_ResetLog = FALSE;
  132. PLOGCALLBACKA g_LogCallbackA;
  133. PLOGCALLBACKW g_LogCallbackW;
  134. #ifdef DEBUG
  135. CHAR g_DebugInfPathBufA[] = "C:\\debug.inf";
  136. CHAR g_DebugLogFile[MAX_PATH];
  137. // If g_DoLog is TRUE, then, debug logging is enabled in the
  138. // checked build even if there is no debug.inf.
  139. BOOL g_DoLog = FALSE;
  140. DWORD g_FirstTickCount = 0;
  141. DWORD g_LastTickCount = 0;
  142. #endif
  143. //
  144. // Macro expansion list
  145. //
  146. #ifndef DEBUG
  147. #define TYPE_DEFAULTS \
  148. DEFMAC(LOG_FATAL_ERROR, DEFAULT_ERROR_FLAGS|USER_POPUP_FLAGS) \
  149. DEFMAC(LOG_MODULE_ERROR, DEFAULT_ERROR_FLAGS|USER_POPUP_FLAGS) \
  150. DEFMAC(LOG_ERROR, DEFAULT_ERROR_FLAGS) \
  151. DEFMAC(LOG_INFORMATION, OD_LOGFILE) \
  152. DEFMAC(LOG_STATUS, OD_SUPPRESS) \
  153. #else
  154. #define TYPE_DEFAULTS \
  155. DEFMAC(LOG_FATAL_ERROR, DEFAULT_ERROR_FLAGS|USER_POPUP_FLAGS) \
  156. DEFMAC(LOG_MODULE_ERROR, DEFAULT_ERROR_FLAGS|USER_POPUP_FLAGS) \
  157. DEFMAC(LOG_ERROR, DEFAULT_ERROR_FLAGS) \
  158. DEFMAC(DBG_WHOOPS, DEFAULT_ERROR_FLAGS) \
  159. DEFMAC(DBG_WARNING, OD_LOGFILE|OD_DEBUGGER) \
  160. DEFMAC(DBG_ASSERT,DEFAULT_ERROR_FLAGS|OD_UNATTEND_POPUP) \
  161. DEFMAC(LOG_INFORMATION, OD_LOGFILE) \
  162. DEFMAC(LOG_STATUS, OD_SUPPRESS) \
  163. #endif
  164. //
  165. // Private function prototypes
  166. //
  167. VOID
  168. InitializeLog (
  169. VOID
  170. );
  171. //
  172. // Macro expansion definition
  173. //
  174. /*++
  175. Macro Expansion List Description:
  176. TYPE_DEFAULTS specify the default destination for the frequently used types,
  177. such as LOG_ERROR, LOG_FATAL_ERROR, and so on.
  178. Line Syntax:
  179. DEFMAC(TypeString, Flags)
  180. Arguments:
  181. TypeString - Specifies the LOG_ constant as defined in log.h
  182. Flags - One or more of:
  183. DEFAULT_ERROR_FLAGS - Specifies debug log, setup log, debugger,
  184. popup, and the value of GetLastError.
  185. OD_DEBUGLOG - Specifies the debug log
  186. OD_ERROR - Specifies type is an error (gets value of
  187. GetLastError)
  188. OD_SUPPRESS - Suppresses all output for the type
  189. OD_LOGFILE - Specifies the setup log
  190. OD_DEBUGGER - Specifies the debugger (i.e., VC or remote debugger)
  191. OD_CONSOLE - Specifies the console (via printf)
  192. OD_POPUP - Specifies a message box
  193. OD_FORCE_POPUP - Specifies a message box, even if debug message
  194. was turned off via a click on Cancel
  195. OD_MUST_BE_LOCALIZED - Indicates the type must originate from a
  196. localized message; used for LOG() calls that
  197. generate popups. (So English messages
  198. don't sneak into the project.)
  199. OD_UNATTEND_POPUP - Causes popup even in unattend mode
  200. OD_ASSERT - Give DebugBreak option in popup
  201. Variables Generated From List:
  202. g_DefaultDest
  203. --*/
  204. #define DEFMAC(typestr, flags) {typestr, (flags)},
  205. DEFAULT_DESTINATION g_DefaultDest[] = {
  206. TYPE_DEFAULTS /* , */
  207. {NULL, 0}
  208. };
  209. #undef DEFMAC
  210. //
  211. // Code
  212. //
  213. #ifdef DEBUG
  214. VOID
  215. pPrivateAssert (
  216. IN BOOL Expr,
  217. IN PCSTR StringExpr,
  218. IN UINT Line
  219. )
  220. {
  221. CHAR buffer[256];
  222. if (Expr) {
  223. return;
  224. }
  225. wsprintfA (buffer, "LOG FAILURE: %s (log.c line %u)", StringExpr, Line);
  226. MessageBoxA (NULL, buffer, NULL, MB_OK);
  227. }
  228. #endif
  229. BOOL
  230. pIgnoreKey (
  231. IN PCSTR Key
  232. )
  233. /*++
  234. Routine Description:
  235. pIgnoreKey decides if a key from [debug] section of DEBUG.INF
  236. should be ignored for our purposes (we are only looking for
  237. <All>, <DefaultOverride> and log/debug types).
  238. Specifically, we ignore all keywords in <g_IgnoreKeys> table.
  239. Arguments:
  240. Key - Specifies the key from [debug] section of DEBUG.INF
  241. Return Value:
  242. TRUE if the key should be ignored, or FALSE if it will be taken into consideration.
  243. --*/
  244. {
  245. UINT i;
  246. for (i = 0; i < DWSIZEOF (g_IgnoreKeys) / DWSIZEOF (PCSTR); i++) {
  247. if (StringIMatchA (Key, g_IgnoreKeys[i])) {
  248. return TRUE;
  249. }
  250. }
  251. return FALSE;
  252. }
  253. OUTPUTDEST
  254. pConvertToOutputType (
  255. IN PCSTR Value
  256. )
  257. /*++
  258. Routine Description:
  259. pConvertToOutputType converts a text value entered by the user in
  260. DEBUG.INF file, associated with a type (e.g. "LOG", "POPUP" etc.).
  261. Arguments:
  262. Value - Specifies the text value
  263. Return Value:
  264. The OUTPUT_DESTINATION value associated with the given value or
  265. OD_UNDEFINED if the value is not valid.
  266. --*/
  267. {
  268. UINT i;
  269. for (i = 0; i < DWSIZEOF (g_String2Binary) / DWSIZEOF (STRING2BINARY); i++) {
  270. if (StringIMatchA (Value, g_String2Binary[i].Value)) {
  271. return g_String2Binary[i].OutDest;
  272. }
  273. }
  274. return OD_UNDEFINED;
  275. }
  276. OUTPUTDEST
  277. pGetTypeOutputDestFromTable (
  278. IN PCSTR Type
  279. )
  280. /*++
  281. Routine Description:
  282. pGetTypeOutputDestFromTable returns the output destination associated
  283. with the specified type in the global table
  284. Arguments:
  285. Type - Specifies the type
  286. Return Value:
  287. Any combination of enum OUTPUT_DESTINATION values associated with
  288. the given type.
  289. --*/
  290. {
  291. PMAPTYPE2OUTDEST typePtr;
  292. PMAPTYPE2OUTDEST last;
  293. OUTPUTDEST outDest = OD_UNDEFINED;
  294. if (g_FirstTypePtr) {
  295. typePtr = g_FirstTypePtr;
  296. last = g_FirstTypePtr + g_TypeTableCount;
  297. while (typePtr < last) {
  298. if (StringIMatchA (typePtr->Type, Type)) {
  299. outDest = typePtr->OutputDest;
  300. #ifdef DEBUG
  301. if (g_DoLog) {
  302. outDest |= OD_DEBUGLOG;
  303. }
  304. #endif
  305. break;
  306. }
  307. typePtr++;
  308. }
  309. }
  310. return outDest;
  311. }
  312. OUTPUTDEST
  313. pGetTypeOutputDest (
  314. IN PCSTR Type
  315. )
  316. /*++
  317. Routine Description:
  318. pGetTypeOutputDest returns the default output
  319. destination for the specified type.
  320. Arguments:
  321. Type - Specifies the type
  322. Return Value:
  323. Any combination of enum OUTPUT_DESTINATION values associated with
  324. the given type.
  325. --*/
  326. {
  327. OUTPUTDEST outDest;
  328. //
  329. // first check for ALL
  330. //
  331. if (!OUT_UNDEFINED (g_OutDestAll)) {
  332. outDest = g_OutDestAll;
  333. } else {
  334. //
  335. // otherwise try to get it from the table
  336. //
  337. outDest = pGetTypeOutputDestFromTable (Type);
  338. if (OUT_UNDEFINED (outDest)) {
  339. //
  340. // just return the default
  341. //
  342. outDest = g_OutDestDefault;
  343. }
  344. }
  345. #ifdef DEBUG
  346. if (g_DoLog) {
  347. outDest |= OD_DEBUGLOG;
  348. }
  349. #endif
  350. return outDest;
  351. }
  352. BOOL
  353. pIsPopupEnabled (
  354. IN PCSTR Type
  355. )
  356. /*++
  357. Routine Description:
  358. pIsPopupEnabled decides if the type should produce a popup output. The user may
  359. disable popup display for a type.
  360. Arguments:
  361. Type - Specifies the type
  362. Return Value:
  363. TRUE if the type should display a popup message.
  364. --*/
  365. {
  366. OUTPUTDEST outDest;
  367. //
  368. // first check if any specific output is available for this type,
  369. // and if so, check if the OUT_POPUP_CANCEL flag is not set
  370. //
  371. if (g_SuppressAllPopups) {
  372. return FALSE;
  373. }
  374. outDest = pGetTypeOutputDestFromTable (Type);
  375. if (OUT_POPUP_CANCEL (outDest)) {
  376. return FALSE;
  377. }
  378. // just return the popup type of ALL of DefaultOverride
  379. return OUT_POPUP (pGetTypeOutputDest (Type));
  380. }
  381. LOGSEVERITY
  382. pGetSeverityFromType (
  383. IN PCSTR Type
  384. )
  385. /*++
  386. Routine Description:
  387. pGetSeverityFromType converts a type to a default severity
  388. that will be used by the debug log system.
  389. Arguments:
  390. Type - Specifies the type
  391. Return Value:
  392. The default log severity associated with the given type; if the specified
  393. type is not found, it returns LOGSEV_INFORMATION.
  394. --*/
  395. {
  396. if (OUT_ERROR (pGetTypeOutputDest (Type))) {
  397. return LOGSEV_ERROR;
  398. }
  399. return LOGSEV_INFORMATION;
  400. }
  401. BOOL
  402. LogSetErrorDest (
  403. IN PCSTR Type,
  404. IN OUTPUT_DESTINATION OutDest
  405. )
  406. /*++
  407. Routine Description:
  408. LogSetErrorDest adds a <Type, OutDest> association
  409. to the table g_FirstTypePtr. If an association of Type already exists,
  410. it is modified to reflect the new association.
  411. Arguments:
  412. Type - Specifies the log/debug type string
  413. OutDest - Specifies what new destination(s) are associated with the type
  414. Return Value:
  415. TRUE if the association was successful and the Type is now in the table
  416. --*/
  417. {
  418. PMAPTYPE2OUTDEST typePtr;
  419. UINT u;
  420. //
  421. // Try to locate the existing type
  422. //
  423. for (u = 0 ; u < g_TypeTableCount ; u++) {
  424. typePtr = g_FirstTypePtr + u;
  425. if (StringIMatchA (typePtr->Type, Type)) {
  426. typePtr->OutputDest = OutDest;
  427. return TRUE;
  428. }
  429. }
  430. //
  431. // look if any free slots are available first
  432. //
  433. if (!g_TypeTableFreeCount) {
  434. PRIVATE_ASSERT (g_hHeap != NULL);
  435. if (!g_FirstTypePtr) {
  436. typePtr = HeapAlloc (
  437. g_hHeap,
  438. 0,
  439. DWSIZEOF (MAPTYPE2OUTDEST) * TYPE_ARRAY_SIZE
  440. );
  441. } else {
  442. typePtr = HeapReAlloc (
  443. g_hHeap,
  444. 0,
  445. g_FirstTypePtr,
  446. DWSIZEOF (MAPTYPE2OUTDEST) * (TYPE_ARRAY_SIZE + g_TypeTableCount)
  447. );
  448. }
  449. if (!typePtr) {
  450. return FALSE;
  451. }
  452. g_FirstTypePtr = typePtr;
  453. g_TypeTableFreeCount = TYPE_ARRAY_SIZE;
  454. }
  455. typePtr = g_FirstTypePtr + g_TypeTableCount;
  456. StringCopyByteCountA (typePtr->Type, Type, DWSIZEOF (typePtr->Type));
  457. typePtr->OutputDest = OutDest;
  458. g_TypeTableCount++;
  459. g_TypeTableFreeCount--;
  460. return TRUE;
  461. }
  462. OUTPUTDEST
  463. pGetAttributes (
  464. IN OUT PINFCONTEXT InfContext
  465. )
  466. /*++
  467. Routine Description:
  468. pGetAttributes converts the text values associated with the key on
  469. the line specified by the given context. If multiple values are
  470. specified, the corresponding OUTPUT_DESTINATION values are ORed together
  471. in the return value.
  472. Arguments:
  473. InfContext - Specifies the DEBUG.INF context of the key whose values
  474. are being converted and receives the updated context
  475. after this processing is done
  476. Return Value:
  477. Any combination of enum OUTPUT_DESTINATION values associated with
  478. the given key.
  479. --*/
  480. {
  481. OUTPUTDEST outDest = OD_UNDEFINED;
  482. CHAR value[OUTPUT_BUFSIZE_SMALL];
  483. UINT field;
  484. for (field = SetupGetFieldCount (InfContext); field > 0; field--) {
  485. if (SetupGetStringFieldA (
  486. InfContext,
  487. field,
  488. value,
  489. OUTPUT_BUFSIZE_SMALL,
  490. NULL
  491. )) {
  492. outDest |= pConvertToOutputType(value);
  493. }
  494. }
  495. return outDest;
  496. }
  497. BOOL
  498. pGetUserPreferences (
  499. IN HINF Inf
  500. )
  501. /*++
  502. Routine Description:
  503. pGetUserPreferences converts user's options specified in the given Inf file
  504. (usually DEBUG.INF) and stores them in g_FirstTypePtr table. If <All> and
  505. <DefaultOverride> entries are found, their values are stored in OutputTypeAll
  506. and OutputTypeDefault, respectivelly, if not NULL.
  507. Arguments:
  508. Inf - Specifies the open inf file hanlde to process
  509. OutputTypeAll - Receives the Output Dest for the special <All> entry
  510. OutputTypeDefault - Receives the Output Dest for the special <DefaultOverride> entry
  511. Return Value:
  512. TRUE if the processing of the INF file was OK.
  513. --*/
  514. {
  515. INFCONTEXT infContext;
  516. OUTPUTDEST outDest;
  517. CHAR key[OUTPUT_BUFSIZE_SMALL];
  518. if (SetupFindFirstLineA (Inf, DEBUG_SECTION, NULL, &infContext)) {
  519. do {
  520. // check to see if this key is not interesting
  521. if (!SetupGetStringFieldA (
  522. &infContext,
  523. 0,
  524. key,
  525. OUTPUT_BUFSIZE_SMALL,
  526. NULL
  527. )) {
  528. continue;
  529. }
  530. if (pIgnoreKey (key)) {
  531. continue;
  532. }
  533. // check for special cases
  534. if (StringIMatchA (key, ENTRY_ALL)) {
  535. g_OutDestAll = pGetAttributes (&infContext);
  536. // no reason to continue since ALL types will take this setting...
  537. break;
  538. } else {
  539. if (StringIMatchA (key, ENTRY_DEFAULTOVERRIDE)) {
  540. g_OutDestDefault = pGetAttributes(&infContext);
  541. } else {
  542. outDest = pGetAttributes(&infContext);
  543. // lines like <Type>= or like <Type>=<not a keyword(s)> are ignored
  544. if (!OUT_UNDEFINED (outDest)) {
  545. if (!LogSetErrorDest (key, outDest)) {
  546. return FALSE;
  547. }
  548. }
  549. }
  550. }
  551. } while (SetupFindNextLine (&infContext, &infContext));
  552. }
  553. return TRUE;
  554. }
  555. /*++
  556. Routine Description:
  557. pPadTitleA and pPadTitleW append to Title a specified number of spaces.
  558. Arguments:
  559. Title - Specifies the title (it will appear on the left column).
  560. The buffer must be large enough to hold the additional spaces
  561. Indent - Specifies the indent of the message body. If necessary,
  562. spaces will be appended to the Title to get to Indent column.
  563. Return Value:
  564. none
  565. --*/
  566. VOID
  567. pPadTitleA (
  568. IN OUT PSTR Title,
  569. IN UINT Indent
  570. )
  571. {
  572. UINT i;
  573. PSTR p;
  574. if (Title == NULL) {
  575. return;
  576. }
  577. for (i = ByteCountA (Title), p = GetEndOfStringA (Title); i < Indent; i++) {
  578. *p++ = ' '; //lint !e613 !e794
  579. }
  580. *p = 0; //lint !e613 !e794
  581. }
  582. VOID
  583. pPadTitleW (
  584. IN OUT PWSTR Title,
  585. IN UINT Indent
  586. )
  587. {
  588. UINT i;
  589. PWSTR p;
  590. if (Title == NULL) {
  591. return;
  592. }
  593. for (i = CharCountW (Title), p = GetEndOfStringW (Title); i < Indent; i++) {
  594. *p++ = L' '; //lint !e613
  595. }
  596. *p = 0; //lint !e613
  597. }
  598. /*++
  599. Routine Description:
  600. pFindNextLineA and pFindNextLineW return the position where
  601. the next line begins
  602. Arguments:
  603. Line - Specifies the current line
  604. Indent - Specifies the indent of the message body. The next line
  605. will start preferably after a newline or a white space,
  606. but no further than the last column, which is
  607. SCREEN_WIDTH - Indent.
  608. Return Value:
  609. The position of the first character on the next line.
  610. --*/
  611. PCSTR
  612. pFindNextLineA (
  613. IN PCSTR Line,
  614. IN UINT Indent,
  615. OUT PBOOL TrimLeadingSpace
  616. )
  617. {
  618. UINT column = 0;
  619. UINT columnMax = SCREEN_WIDTH - 1 - Indent;
  620. PCSTR lastSpace = NULL;
  621. PCSTR prevLine = Line;
  622. UINT ch;
  623. *TrimLeadingSpace = FALSE;
  624. while ( (ch = _mbsnextc (Line)) != 0 && column < columnMax) {
  625. if (ch == '\n') {
  626. lastSpace = Line;
  627. break;
  628. }
  629. if (ch > 255) {
  630. lastSpace = Line;
  631. column++;
  632. } else {
  633. if (_ismbcspace (ch)) {
  634. lastSpace = Line;
  635. }
  636. }
  637. column++;
  638. prevLine = Line;
  639. Line = _mbsinc (Line);
  640. }
  641. if (ch == 0) {
  642. return Line;
  643. }
  644. if (lastSpace == NULL) {
  645. // we must cut this even if no white space or 2-byte char was found
  646. lastSpace = prevLine;
  647. }
  648. if (ch != '\n') {
  649. *TrimLeadingSpace = TRUE;
  650. }
  651. return _mbsinc (lastSpace);
  652. }
  653. PCWSTR
  654. pFindNextLineW (
  655. IN PCWSTR Line,
  656. IN UINT Indent,
  657. OUT PBOOL TrimLeadingSpace
  658. )
  659. {
  660. UINT column = 0;
  661. UINT columnMax = SCREEN_WIDTH - 1 - Indent;
  662. PCWSTR lastSpace = NULL;
  663. PCWSTR prevLine = Line;
  664. WCHAR ch;
  665. *TrimLeadingSpace = FALSE;
  666. while ( (ch = *Line) != 0 && column < columnMax) {
  667. if (ch == L'\n') {
  668. lastSpace = Line;
  669. break;
  670. }
  671. if (ch > 255) {
  672. lastSpace = Line;
  673. } else {
  674. if (iswspace (ch)) {
  675. lastSpace = Line;
  676. }
  677. }
  678. column++;
  679. prevLine = Line;
  680. Line++;
  681. }
  682. if (ch == 0) {
  683. return Line;
  684. }
  685. if (lastSpace == NULL) {
  686. // we must cut this even if no white space was found
  687. lastSpace = prevLine;
  688. }
  689. if (ch != L'\n') {
  690. *TrimLeadingSpace = TRUE;
  691. }
  692. return lastSpace + 1;
  693. }
  694. /*++
  695. Routine Description:
  696. pHangingIndentA and pHangingIndentW break in lines and indent
  697. the text in buffer, which is no larger than Size.
  698. Arguments:
  699. buffer - Specifies the buffer containing text to format. The resulting
  700. text will be put in the same buffer
  701. Size - Specifies the size of this buffer, in bytes
  702. Indent - Specifies the indent to be used by all new generated lines.
  703. Return Value:
  704. none
  705. --*/
  706. VOID
  707. pHangingIndentA (
  708. IN OUT PSTR buffer,
  709. IN DWORD Size,
  710. IN UINT Indent
  711. )
  712. {
  713. CHAR indentBuffer[OUTPUT_BUFSIZE_LARGE];
  714. PCSTR nextLine;
  715. PCSTR s;
  716. PSTR d;
  717. UINT i;
  718. BOOL trimLeadingSpace;
  719. PCSTR endOfBuf;
  720. BOOL appendNewLine = FALSE;
  721. nextLine = buffer;
  722. s = buffer;
  723. d = indentBuffer;
  724. endOfBuf = END_OF_BUFFER(indentBuffer) - 3;
  725. while (*s && d < endOfBuf) {
  726. //
  727. // Find end of next line
  728. //
  729. nextLine = (PSTR)pFindNextLineA (s, Indent, &trimLeadingSpace);
  730. //
  731. // Copy one line from source to dest
  732. //
  733. while (s < nextLine && d < endOfBuf) {
  734. switch (*s) {
  735. case '\r':
  736. s++;
  737. if (*s == '\r') {
  738. continue;
  739. } else if (*s != '\n') {
  740. s--;
  741. }
  742. // fall through
  743. case '\n':
  744. *d++ = '\r';
  745. *d++ = '\n';
  746. s++;
  747. break;
  748. default:
  749. if (IsLeadByte (s)) {
  750. *d++ = *s++;
  751. }
  752. *d++ = *s++;
  753. break;
  754. }
  755. }
  756. //
  757. // Trim leading space if necessary
  758. //
  759. if (trimLeadingSpace) {
  760. while (*s == ' ') {
  761. s++;
  762. }
  763. }
  764. if (*s) {
  765. //
  766. // If another line, prepare an indent and insert a new line
  767. // after this multiline message
  768. //
  769. appendNewLine = TRUE;
  770. if (d < endOfBuf && trimLeadingSpace) {
  771. *d++ = L'\r';
  772. *d++ = L'\n';
  773. }
  774. for (i = 0 ; i < Indent && d < endOfBuf ; i++) {
  775. *d++ = ' ';
  776. }
  777. }
  778. }
  779. if (appendNewLine && d < endOfBuf) {
  780. *d++ = L'\r';
  781. *d++ = L'\n';
  782. }
  783. // make sure the string is zero-terminated
  784. PRIVATE_ASSERT (d <= END_OF_BUFFER(indentBuffer));
  785. *d = 0;
  786. // copy the result to output buffer
  787. StringCopyByteCountA (buffer, indentBuffer, Size);
  788. }
  789. VOID
  790. pHangingIndentW (
  791. IN OUT PWSTR buffer,
  792. IN DWORD Size,
  793. IN UINT Indent
  794. )
  795. {
  796. WCHAR indentBuffer[OUTPUT_BUFSIZE_LARGE];
  797. PCWSTR nextLine;
  798. PCWSTR s;
  799. PWSTR d;
  800. UINT i;
  801. BOOL trimLeadingSpace;
  802. PCWSTR endOfBuf;
  803. BOOL appendNewLine = FALSE;
  804. nextLine = buffer;
  805. s = buffer;
  806. d = indentBuffer;
  807. endOfBuf = END_OF_BUFFER(indentBuffer) - 1;
  808. while (*s && d < endOfBuf) {
  809. //
  810. // Find end of next line
  811. //
  812. nextLine = (PWSTR)pFindNextLineW (s, Indent, &trimLeadingSpace);
  813. //
  814. // Copy one line from source to dest
  815. //
  816. while (s < nextLine && d < endOfBuf) {
  817. switch (*s) {
  818. case L'\r':
  819. s++;
  820. if (*s == L'\r') {
  821. continue;
  822. } else if (*s != L'\n') {
  823. s--;
  824. }
  825. // fall through
  826. case L'\n':
  827. *d++ = L'\r';
  828. *d++ = L'\n';
  829. s++;
  830. break;
  831. default:
  832. *d++ = *s++;
  833. break;
  834. }
  835. }
  836. //
  837. // Trim leading space if necessary
  838. //
  839. if (trimLeadingSpace) {
  840. while (*s == L' ') {
  841. s++;
  842. }
  843. }
  844. if (*s) {
  845. //
  846. // If another line, prepare an indent and insert a new line
  847. // after this multiline message
  848. //
  849. appendNewLine = TRUE;
  850. if (d < endOfBuf && trimLeadingSpace) {
  851. *d++ = L'\r';
  852. *d++ = L'\n';
  853. }
  854. for (i = 0 ; i < Indent && d < endOfBuf ; i++) {
  855. *d++ = L' ';
  856. }
  857. }
  858. }
  859. if (appendNewLine && d < endOfBuf) {
  860. *d++ = L'\r';
  861. *d++ = L'\n';
  862. }
  863. // make sure the string is zero-terminated
  864. PRIVATE_ASSERT (d <= END_OF_BUFFER(indentBuffer));
  865. *d = 0;
  866. // copy the result to output buffer
  867. StringCopyCharCountW (buffer, indentBuffer, Size);
  868. }
  869. /*++
  870. Routine Description:
  871. pAppendLastErrorA and pAppendLastErrorW append the specified error code
  872. to the Message and writes the output to the MsgWithErr buffer.
  873. Arguments:
  874. MsgWithErr - Receives the formatted message. This buffer
  875. is supplied by caller
  876. BufferSize - Specifies the size of the buffer, in bytes
  877. Message - Specifies the body of the message
  878. LastError - Specifies the error code that will be appended
  879. Return Value:
  880. none
  881. --*/
  882. VOID
  883. pAppendLastErrorA (
  884. OUT PSTR MsgWithErr,
  885. IN DWORD BufferSize,
  886. IN PCSTR Message,
  887. IN DWORD LastError
  888. )
  889. {
  890. PSTR append;
  891. DWORD errMsgLen;
  892. StringCopyByteCountA (MsgWithErr, Message, BufferSize);
  893. append = GetEndOfStringA (MsgWithErr);
  894. errMsgLen = (DWORD)(MsgWithErr + BufferSize - append); //lint !e613
  895. if (errMsgLen > 0) {
  896. if (LastError < 10) {
  897. _snprintf (append, errMsgLen, " [ERROR=%lu]", LastError);
  898. } else {
  899. _snprintf (append, errMsgLen, " [ERROR=%lu (%lXh)]", LastError, LastError);
  900. }
  901. }
  902. }
  903. VOID
  904. pAppendLastErrorW (
  905. OUT PWSTR MsgWithErr,
  906. IN DWORD BufferSize,
  907. IN PCWSTR Message,
  908. IN DWORD LastError
  909. )
  910. {
  911. PWSTR append;
  912. DWORD errMsgLen;
  913. StringCopyCharCountW (MsgWithErr, Message, BufferSize / DWSIZEOF(WCHAR));
  914. append = GetEndOfStringW (MsgWithErr);
  915. errMsgLen = (DWORD)(MsgWithErr + (BufferSize / DWSIZEOF(WCHAR)) - append);
  916. if (errMsgLen > 0) {
  917. if (LastError < 10) {
  918. _snwprintf (append, errMsgLen, L" [ERROR=%lu]", LastError);
  919. } else {
  920. _snwprintf (append, errMsgLen, L" [ERROR=%lu (%lXh)]", LastError, LastError);
  921. }
  922. }
  923. }
  924. /*++
  925. Routine Description:
  926. pIndentMessageA and pIndentMessageW format the specified message
  927. with the type in the left column and body of the message in the right.
  928. Arguments:
  929. formattedMsg - Receives the formatted message. This buffer
  930. is supplied by caller
  931. BufferSize - Specifies the size of the buffer
  932. Type - Specifies the type of the message
  933. Body - Specifies the body of the message
  934. Indent - Specifies the column to indent to
  935. LastError - Specifies the last error code if different than ERROR_SUCCESS;
  936. in this case it will be appended to the message
  937. Return Value:
  938. none
  939. --*/
  940. VOID
  941. pIndentMessageA (
  942. OUT PSTR formattedMsg,
  943. IN DWORD BufferSize,
  944. IN PCSTR Type,
  945. IN PCSTR Body,
  946. IN UINT Indent,
  947. IN DWORD LastError
  948. )
  949. {
  950. CHAR bodyWithErr[OUTPUT_BUFSIZE_LARGE];
  951. PCSTR myMsgBody;
  952. PSTR currentPos;
  953. DWORD remaining;
  954. myMsgBody = Body;
  955. remaining = BufferSize - Indent;
  956. if (LastError != ERROR_SUCCESS) {
  957. myMsgBody = bodyWithErr;
  958. pAppendLastErrorA (bodyWithErr, DWSIZEOF (bodyWithErr), Body, LastError);
  959. }
  960. StringCopyByteCountA (formattedMsg, Type, MAX_MSGTITLE_LEN);
  961. pPadTitleA (formattedMsg, Indent);
  962. currentPos = formattedMsg + Indent;
  963. StringCopyByteCountA (currentPos, myMsgBody, remaining);
  964. pHangingIndentA (currentPos, remaining, Indent);
  965. // append a new line if space left
  966. currentPos = GetEndOfStringA (currentPos);
  967. if (currentPos + NEWLINE_CHAR_COUNTA + 1 < formattedMsg + BufferSize) { //lint !e613
  968. *currentPos++ = '\r'; //lint !e613
  969. *currentPos++ = '\n'; //lint !e613
  970. *currentPos = 0; //lint !e613
  971. }
  972. }
  973. VOID
  974. pIndentMessageW (
  975. OUT PWSTR formattedMsg,
  976. IN DWORD BufferSize,
  977. IN PCSTR Type,
  978. IN PCWSTR Body,
  979. IN UINT Indent,
  980. IN DWORD LastError
  981. )
  982. {
  983. WCHAR typeW[OUTPUT_BUFSIZE_SMALL];
  984. WCHAR bodyWithErr[OUTPUT_BUFSIZE_LARGE];
  985. PCWSTR myMsgBody;
  986. PWSTR currentPos;
  987. DWORD remaining;
  988. myMsgBody = Body;
  989. remaining = BufferSize - Indent;
  990. if (LastError != ERROR_SUCCESS) {
  991. myMsgBody = bodyWithErr;
  992. pAppendLastErrorW (bodyWithErr, DWSIZEOF (bodyWithErr), Body, LastError);
  993. }
  994. KnownSizeAtoW (typeW, Type);
  995. StringCopyCharCountW (formattedMsg, typeW, MAX_MSGTITLE_LEN);
  996. pPadTitleW (formattedMsg, Indent);
  997. currentPos = formattedMsg + Indent;
  998. StringCopyCharCountW (currentPos, myMsgBody, remaining);
  999. pHangingIndentW (currentPos, remaining, Indent);
  1000. // append a new line if space left
  1001. currentPos = GetEndOfStringW (currentPos);
  1002. if (currentPos + NEWLINE_CHAR_COUNTW + 1 < formattedMsg + BufferSize) {
  1003. *currentPos++ = L'\r';
  1004. *currentPos++ = L'\n';
  1005. *currentPos = 0;
  1006. }
  1007. }
  1008. PCSTR
  1009. pGetSeverityStr (
  1010. IN LOGSEVERITY Severity,
  1011. IN BOOL Begin
  1012. )
  1013. {
  1014. switch (Severity) {
  1015. case LogSevFatalError:
  1016. return Begin?"":"\r\n***";
  1017. case LogSevError:
  1018. return Begin?"":"\r\n***";
  1019. case LogSevWarning:
  1020. return "";
  1021. }
  1022. return "";
  1023. }
  1024. /*++
  1025. Routine Description:
  1026. pWriteToMainLogA and pWriteToMainLogW log the specified message to the main
  1027. end-user log.
  1028. Arguments:
  1029. Severity - Specifies the severity of the message, as defined by the Setup API
  1030. formattedMsg - Specifies the message
  1031. Return Value:
  1032. none
  1033. --*/
  1034. VOID
  1035. pWriteToMainLogA (
  1036. IN PCSTR Type,
  1037. IN LOGSEVERITY Severity,
  1038. IN PCSTR FormattedMsg
  1039. )
  1040. {
  1041. HANDLE logHandle = NULL;
  1042. logHandle = CreateFileA (
  1043. g_MainLogFile,
  1044. GENERIC_WRITE,
  1045. FILE_SHARE_READ,
  1046. NULL,
  1047. OPEN_ALWAYS,
  1048. FILE_ATTRIBUTE_NORMAL,
  1049. NULL
  1050. );
  1051. if (logHandle != INVALID_HANDLE_VALUE) {
  1052. SetFilePointer (logHandle, 0, NULL, FILE_END);
  1053. WriteFileStringA (logHandle, pGetSeverityStr (Severity, TRUE));
  1054. WriteFileStringA (logHandle, FormattedMsg);
  1055. WriteFileStringA (logHandle, pGetSeverityStr (Severity, FALSE));
  1056. CloseHandle (logHandle);
  1057. }
  1058. }
  1059. VOID
  1060. pWriteToMainLogW (
  1061. IN PCSTR Type,
  1062. IN LOGSEVERITY Severity,
  1063. IN PCWSTR FormattedMsg
  1064. )
  1065. {
  1066. HANDLE logHandle = NULL;
  1067. logHandle = CreateFileA (
  1068. g_MainLogFile,
  1069. GENERIC_WRITE,
  1070. FILE_SHARE_READ,
  1071. NULL,
  1072. OPEN_ALWAYS,
  1073. FILE_ATTRIBUTE_NORMAL,
  1074. NULL
  1075. );
  1076. if (logHandle != INVALID_HANDLE_VALUE) {
  1077. SetFilePointer (logHandle, 0, NULL, FILE_END);
  1078. WriteFileStringA (logHandle, pGetSeverityStr (Severity, TRUE));
  1079. WriteFileStringW (logHandle, FormattedMsg);
  1080. WriteFileStringA (logHandle, pGetSeverityStr (Severity, FALSE));
  1081. CloseHandle (logHandle);
  1082. }
  1083. }
  1084. /*++
  1085. Routine Description:
  1086. pDisplayPopupA and pDisplayPopupW displays the specified message to
  1087. a popup window, if <g_LogPopupParentWnd> is not NULL (attended mode).
  1088. Arguments:
  1089. Type - Specifies the type of the message, displayed as the popup's title
  1090. Msg - Specifies the message
  1091. LastError - Specifies the last error; it will be printed if != ERROR_SUCCESS
  1092. Forced - Specifies TRUE to force the popup, even in unattended mode
  1093. Return Value:
  1094. none
  1095. --*/
  1096. VOID
  1097. pDisplayPopupA (
  1098. IN PCSTR Type,
  1099. IN PCSTR Msg,
  1100. IN DWORD LastError,
  1101. IN BOOL Forced
  1102. )
  1103. {
  1104. #ifdef DEBUG
  1105. CHAR formattedMsg[OUTPUT_BUFSIZE_LARGE];
  1106. CHAR buffer[OUTPUT_BUFSIZE_SMALL];
  1107. PSTR currentPos = buffer;
  1108. #endif
  1109. UINT mbStyle;
  1110. LONG rc;
  1111. OUTPUTDEST outDest;
  1112. HWND parentWnd;
  1113. PCSTR displayMessage = Msg;
  1114. LOGSEVERITY severity = pGetSeverityFromType (Type);
  1115. outDest = pGetTypeOutputDest (Type);
  1116. if (g_LogPopupParentWnd || Forced) {
  1117. #ifdef DEBUG
  1118. if (LastError != ERROR_SUCCESS) {
  1119. if (LastError < 10) {
  1120. currentPos += wsprintfA (buffer, " [ERROR=%u]", LastError);
  1121. } else {
  1122. currentPos += wsprintfA (buffer, " [ERROR=%u (%Xh)]", LastError, LastError);
  1123. }
  1124. }
  1125. if (OUT_ASSERT (outDest)) {
  1126. currentPos += wsprintfA (
  1127. currentPos,
  1128. "\n\nBreak now? (Hit Yes to break, No to continue, or Cancel to disable '%s' message boxes)",
  1129. Type
  1130. );
  1131. } else {
  1132. currentPos += wsprintfA (
  1133. currentPos,
  1134. "\n\n(Hit Cancel to disable '%s' message boxes)",
  1135. Type
  1136. );
  1137. }
  1138. if (currentPos > buffer) {
  1139. //
  1140. // the displayed message should be modified to include additional info
  1141. //
  1142. displayMessage = formattedMsg;
  1143. StringCopyByteCountA (
  1144. formattedMsg,
  1145. Msg,
  1146. ARRAYSIZE(formattedMsg) - (HALF_PTR) (currentPos - buffer)
  1147. );
  1148. StringCatA (formattedMsg, buffer);
  1149. }
  1150. #endif
  1151. switch (severity) {
  1152. case LOGSEV_FATAL_ERROR:
  1153. mbStyle = MB_ICONSTOP;
  1154. break;
  1155. case LOGSEV_ERROR:
  1156. mbStyle = MB_ICONERROR;
  1157. break;
  1158. case LOGSEV_WARNING:
  1159. mbStyle = MB_ICONEXCLAMATION;
  1160. break;
  1161. default:
  1162. mbStyle = MB_ICONINFORMATION;
  1163. }
  1164. mbStyle |= MB_SETFOREGROUND;
  1165. #ifdef DEBUG
  1166. if (OUT_ASSERT (outDest)) {
  1167. mbStyle |= MB_YESNOCANCEL|MB_DEFBUTTON2;
  1168. } else {
  1169. mbStyle |= MB_OKCANCEL;
  1170. }
  1171. #else
  1172. mbStyle |= MB_OK;
  1173. #endif
  1174. //
  1175. // check current thread id; if different than thread that initialized
  1176. // parent window handle, set parent to NULL
  1177. //
  1178. if (GetCurrentThreadId () == g_InitThreadId) {
  1179. parentWnd = g_LogPopupParentWnd;
  1180. } else {
  1181. parentWnd = NULL;
  1182. }
  1183. rc = MessageBoxA (parentWnd, displayMessage, Type, mbStyle);
  1184. #ifdef DEBUG
  1185. if (rc == IDCANCEL) {
  1186. //
  1187. // cancel this type of messages
  1188. //
  1189. LogSetErrorDest (Type, outDest | OD_POPUP_CANCEL);
  1190. } else if (rc == IDYES) {
  1191. //
  1192. // If Yes was clicked, call DebugBreak to get assert behavoir
  1193. //
  1194. DebugBreak();
  1195. }
  1196. #endif
  1197. }
  1198. }
  1199. VOID
  1200. pDisplayPopupW (
  1201. IN PCSTR Type,
  1202. IN PWSTR Msg,
  1203. IN DWORD LastError,
  1204. IN BOOL Forced
  1205. )
  1206. {
  1207. PCSTR msgA;
  1208. //
  1209. // call the ANSI version because wsprintfW is not properly implemented on Win9x
  1210. //
  1211. msgA = ConvertWtoA (Msg);
  1212. pDisplayPopupA (Type, msgA, LastError, Forced);
  1213. FreeConvertedStr (msgA);
  1214. }
  1215. /*++
  1216. Routine Description:
  1217. pRawWriteLogOutputA and pRawWriteLogOutputW output specified message
  1218. to all character devices implied by the type. The message is not
  1219. formatted in any way
  1220. Arguments:
  1221. Type - Specifies the type of the message, displayed as the popup's title
  1222. Msg - Specifies the message
  1223. Return Value:
  1224. none
  1225. --*/
  1226. VOID
  1227. pRawWriteLogOutputA (
  1228. IN PCSTR Type,
  1229. IN PCSTR Message,
  1230. IN PCSTR formattedMsg,
  1231. IN BOOL NoMainLog
  1232. )
  1233. {
  1234. OUTPUTDEST outDest;
  1235. LOGARGA callbackArgA;
  1236. LOGARGW callbackArgW;
  1237. static BOOL inCallback = FALSE;
  1238. #ifdef DEBUG
  1239. HANDLE handle;
  1240. #endif
  1241. outDest = pGetTypeOutputDest (Type);
  1242. if (OUT_NO_OUTPUT (outDest)) {
  1243. return;
  1244. }
  1245. if (!inCallback && (g_LogCallbackA || g_LogCallbackW)) {
  1246. inCallback = TRUE;
  1247. if (g_LogCallbackA) {
  1248. ZeroMemory (&callbackArgA, sizeof (callbackArgA));
  1249. callbackArgA.Type = Type;
  1250. callbackArgA.ModuleInstance = g_LibHandle;
  1251. callbackArgA.Message = Message;
  1252. callbackArgA.FormattedMessage = formattedMsg;
  1253. callbackArgA.Debug = NoMainLog;
  1254. g_LogCallbackA (&callbackArgA);
  1255. } else {
  1256. ZeroMemory (&callbackArgW, sizeof (callbackArgW));
  1257. callbackArgW.Type = Type;
  1258. callbackArgW.ModuleInstance = g_LibHandle;
  1259. callbackArgW.Message = ConvertAtoW (Message);
  1260. callbackArgW.FormattedMessage = ConvertAtoW (formattedMsg);
  1261. callbackArgW.Debug = NoMainLog;
  1262. g_LogCallbackW (&callbackArgW);
  1263. if (callbackArgW.Message) {
  1264. FreeConvertedStr (callbackArgW.Message);
  1265. }
  1266. if (callbackArgW.FormattedMessage) {
  1267. FreeConvertedStr (callbackArgW.FormattedMessage);
  1268. }
  1269. }
  1270. inCallback = FALSE;
  1271. return;
  1272. }
  1273. if (!NoMainLog && OUT_LOGFILE (outDest)) {
  1274. pWriteToMainLogA (Type, LOGSEV_INFORMATION, formattedMsg);
  1275. }
  1276. //
  1277. // log to each specified device
  1278. //
  1279. if (OUT_DEBUGGER(outDest)) {
  1280. OutputDebugStringA (formattedMsg);
  1281. }
  1282. if (OUT_CONSOLE(outDest)) {
  1283. fprintf (stderr, "%s", formattedMsg);
  1284. }
  1285. #ifdef DEBUG
  1286. if (OUT_DEBUGLOG (outDest)) {
  1287. handle = CreateFileA (
  1288. g_DebugLogFile,
  1289. GENERIC_WRITE,
  1290. FILE_SHARE_READ,
  1291. NULL,
  1292. OPEN_ALWAYS,
  1293. FILE_ATTRIBUTE_NORMAL,
  1294. NULL
  1295. );
  1296. if (handle != INVALID_HANDLE_VALUE) {
  1297. SetFilePointer (handle, 0, NULL, FILE_END);
  1298. WriteFileStringA (handle, formattedMsg);
  1299. CloseHandle (handle);
  1300. }
  1301. }
  1302. #endif
  1303. }
  1304. VOID
  1305. pRawWriteLogOutputW (
  1306. IN PCSTR Type,
  1307. IN PCWSTR Message,
  1308. IN PCWSTR formattedMsg,
  1309. IN BOOL NoMainLog
  1310. )
  1311. {
  1312. OUTPUTDEST outDest;
  1313. LOGARGA callbackArgA;
  1314. LOGARGW callbackArgW;
  1315. static BOOL inCallback = FALSE;
  1316. #ifdef DEBUG
  1317. HANDLE handle;
  1318. #endif
  1319. outDest = pGetTypeOutputDest (Type);
  1320. if (OUT_NO_OUTPUT (outDest)) {
  1321. return;
  1322. }
  1323. if (!inCallback && (g_LogCallbackA || g_LogCallbackW)) {
  1324. inCallback = TRUE;
  1325. if (g_LogCallbackW) {
  1326. ZeroMemory (&callbackArgW, sizeof (callbackArgW));
  1327. callbackArgW.Type = Type;
  1328. callbackArgW.ModuleInstance = g_LibHandle;
  1329. callbackArgW.Message = Message;
  1330. callbackArgW.FormattedMessage = formattedMsg;
  1331. callbackArgW.Debug = NoMainLog;
  1332. g_LogCallbackW (&callbackArgW);
  1333. } else {
  1334. ZeroMemory (&callbackArgA, sizeof (callbackArgA));
  1335. callbackArgA.Type = Type;
  1336. callbackArgA.ModuleInstance = g_LibHandle;
  1337. callbackArgA.Message = ConvertWtoA (Message);
  1338. callbackArgA.FormattedMessage = ConvertWtoA (formattedMsg);
  1339. callbackArgA.Debug = NoMainLog;
  1340. g_LogCallbackA (&callbackArgA);
  1341. if (callbackArgA.Message) {
  1342. FreeConvertedStr (callbackArgA.Message);
  1343. }
  1344. if (callbackArgA.FormattedMessage) {
  1345. FreeConvertedStr (callbackArgA.FormattedMessage);
  1346. }
  1347. }
  1348. inCallback = FALSE;
  1349. return;
  1350. }
  1351. if (!NoMainLog && OUT_LOGFILE (outDest)) {
  1352. pWriteToMainLogW (Type, LOGSEV_INFORMATION, formattedMsg);
  1353. }
  1354. //
  1355. // log to each specified device
  1356. //
  1357. if (OUT_DEBUGGER(outDest)) {
  1358. OutputDebugStringW (formattedMsg);
  1359. }
  1360. if (OUT_CONSOLE(outDest)) {
  1361. fwprintf (stderr, L"%s", formattedMsg);
  1362. }
  1363. #ifdef DEBUG
  1364. if (OUT_DEBUGLOG (outDest)) {
  1365. handle = CreateFileA (
  1366. g_DebugLogFile,
  1367. GENERIC_WRITE,
  1368. FILE_SHARE_READ,
  1369. NULL,
  1370. OPEN_ALWAYS,
  1371. FILE_ATTRIBUTE_NORMAL,
  1372. NULL
  1373. );
  1374. if (handle != INVALID_HANDLE_VALUE) {
  1375. SetFilePointer (handle, 0, NULL, FILE_END);
  1376. WriteFileStringW (handle, formattedMsg);
  1377. CloseHandle (handle);
  1378. }
  1379. }
  1380. #endif
  1381. }
  1382. /*++
  1383. Routine Description:
  1384. pFormatAndWriteMsgA and pFormatAndWriteMsgW format the message
  1385. specified by the Format argument and outputs it to all destinations
  1386. specified in OutDest. If no destination for the message,
  1387. no action is performed.
  1388. Arguments:
  1389. Type - Specifies the type (category) of the message
  1390. Format - Specifies either the message in ASCII format or
  1391. a message ID (if SHIFTRIGHT16(Format) == 0). The message
  1392. will be formatted using args.
  1393. args - Specifies a list of arguments to be used when formatting
  1394. the message. If a message ID is used for Format, args
  1395. is supposed to be an array of pointers to strings
  1396. Return Value:
  1397. none
  1398. --*/
  1399. VOID
  1400. pFormatAndWriteMsgA (
  1401. IN BOOL NoMainLog,
  1402. IN PCSTR Type,
  1403. IN PCSTR Format,
  1404. IN va_list args
  1405. )
  1406. {
  1407. CHAR output[OUTPUT_BUFSIZE_LARGE];
  1408. CHAR formattedMsg[OUTPUT_BUFSIZE_LARGE];
  1409. OUTPUTDEST outDest;
  1410. DWORD lastError;
  1411. PRIVATE_ASSERT (g_LoggingNow > 0);
  1412. // clear LOGTITLE flag on each regular LOG
  1413. g_HasTitle = FALSE;
  1414. outDest = pGetTypeOutputDest (Type);
  1415. if (OUT_NO_OUTPUT(outDest)) {
  1416. return;
  1417. }
  1418. if (OUT_ERROR (outDest)) {
  1419. lastError = GetLastError();
  1420. } else {
  1421. lastError = ERROR_SUCCESS;
  1422. }
  1423. // format output string
  1424. if (SHIFTRIGHT16((UBINT)Format) == 0) {
  1425. //
  1426. // this is actually a Resource String ID
  1427. //
  1428. if (!FormatMessageA (
  1429. FORMAT_MESSAGE_FROM_HMODULE,
  1430. (LPVOID) g_LibHandle,
  1431. (DWORD)(UBINT) Format,
  1432. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  1433. (LPVOID) output,
  1434. OUTPUT_BUFSIZE_LARGE,
  1435. &args
  1436. )) {
  1437. // the string is missing from Resources
  1438. DEBUGMSG ((DBG_WHOOPS, "Log() called with invalid MsgID, Instance=0x%08X", g_LibHandle));
  1439. return;
  1440. }
  1441. } else {
  1442. //
  1443. // format given string using printf style
  1444. //
  1445. _vsnprintf(output, OUTPUT_BUFSIZE_LARGE, Format, args);
  1446. output [OUTPUT_BUFSIZE_LARGE - 1] = 0;
  1447. }
  1448. pIndentMessageA (
  1449. formattedMsg,
  1450. OUTPUT_BUFSIZE_LARGE,
  1451. Type,
  1452. output,
  1453. MSGBODY_INDENT,
  1454. lastError
  1455. );
  1456. pRawWriteLogOutputA (Type, output, formattedMsg, NoMainLog);
  1457. if (pIsPopupEnabled (Type)) {
  1458. #ifdef DEBUG
  1459. if (MUST_BE_LOCALIZED (outDest)) {
  1460. PRIVATE_ASSERT (
  1461. !MUST_BE_LOCALIZED (outDest) ||
  1462. (SHIFTRIGHT16((UBINT)Format) == 0)
  1463. );
  1464. }
  1465. pDisplayPopupA (Type, output, lastError, OUT_FORCED_POPUP(outDest));
  1466. #else
  1467. if (SHIFTRIGHT16 ((UBINT)Format) == 0) {
  1468. pDisplayPopupA (Type, output, lastError, OUT_FORCED_POPUP(outDest));
  1469. }
  1470. #endif
  1471. }
  1472. }
  1473. VOID
  1474. pFormatAndWriteMsgW (
  1475. IN BOOL NoMainLog,
  1476. IN PCSTR Type,
  1477. IN PCSTR Format,
  1478. IN va_list args
  1479. )
  1480. {
  1481. WCHAR formatW[OUTPUT_BUFSIZE_LARGE];
  1482. WCHAR output[OUTPUT_BUFSIZE_LARGE];
  1483. WCHAR formattedMsg[OUTPUT_BUFSIZE_LARGE];
  1484. OUTPUTDEST outDest;
  1485. DWORD lastError;
  1486. PRIVATE_ASSERT (g_LoggingNow > 0);
  1487. // clear LOGTITLE flag on each regular LOG
  1488. g_HasTitle = FALSE;
  1489. outDest = pGetTypeOutputDest (Type);
  1490. if (OUT_NO_OUTPUT(outDest)) {
  1491. return;
  1492. }
  1493. if (OUT_ERROR (outDest)) {
  1494. lastError = GetLastError();
  1495. } else {
  1496. lastError = ERROR_SUCCESS;
  1497. }
  1498. // format output string
  1499. if (SHIFTRIGHT16((UBINT)Format) == 0) {
  1500. //
  1501. // this is actually a Resource String ID
  1502. //
  1503. if (!FormatMessageW (
  1504. FORMAT_MESSAGE_FROM_HMODULE,
  1505. (LPVOID) g_LibHandle,
  1506. (DWORD)(UBINT) Format,
  1507. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  1508. (LPVOID) output,
  1509. OUTPUT_BUFSIZE_LARGE,
  1510. &args
  1511. )) {
  1512. // the string is missing from Resources
  1513. DEBUGMSG ((DBG_WHOOPS, "Log() called with invalid MsgID, Instance=0x%08X", g_LibHandle));
  1514. return;
  1515. }
  1516. } else {
  1517. KnownSizeAtoW (formatW, Format);
  1518. //
  1519. // format given string using printf style
  1520. //
  1521. _vsnwprintf(output, OUTPUT_BUFSIZE_LARGE, formatW, args);
  1522. output [OUTPUT_BUFSIZE_LARGE - 1] = 0;
  1523. }
  1524. pIndentMessageW (
  1525. formattedMsg,
  1526. OUTPUT_BUFSIZE_LARGE,
  1527. Type,
  1528. output,
  1529. MSGBODY_INDENT,
  1530. lastError
  1531. );
  1532. pRawWriteLogOutputW (Type, output, formattedMsg, NoMainLog);
  1533. if (pIsPopupEnabled (Type)) {
  1534. #ifdef DEBUG
  1535. if (MUST_BE_LOCALIZED (outDest)) {
  1536. PRIVATE_ASSERT (SHIFTRIGHT16((UBINT)Format) == 0);
  1537. }
  1538. pDisplayPopupW (Type, output, lastError, OUT_FORCED_POPUP(outDest));
  1539. #else
  1540. if (SHIFTRIGHT16 ((UBINT)Format) == 0) {
  1541. pDisplayPopupW (Type, output, lastError, OUT_FORCED_POPUP(outDest));
  1542. }
  1543. #endif
  1544. }
  1545. }
  1546. BOOL
  1547. pInitLog (
  1548. IN BOOL FirstTimeInit,
  1549. IN HWND LogPopupParentWnd, OPTIONAL
  1550. OUT HWND *OrgPopupParentWnd, OPTIONAL
  1551. IN PCSTR LogFile, OPTIONAL
  1552. IN PLOGCALLBACKA LogCallbackA, OPTIONAL
  1553. IN PLOGCALLBACKW LogCallbackW OPTIONAL
  1554. )
  1555. /*++
  1556. Routine Description:
  1557. pInitLog actually initializes the log system.
  1558. Arguments:
  1559. LogPopupParentWnd - Specifies the parent window to be used by the
  1560. popups, or NULL if popups are to be suppressed.
  1561. This value is not optional on the first call
  1562. to this function.
  1563. OrgPopupParentWnd - Receives the original parent window.
  1564. LogFile - Specifies the name of the log file. If not specified,
  1565. logging goes to a default file (%windir%\cobra.log).
  1566. LogCallback - Specifies a function to call instead of the internal
  1567. logging functions.
  1568. Return Value:
  1569. TRUE if log system successfully initialized
  1570. --*/
  1571. {
  1572. HINF hInf = INVALID_HANDLE_VALUE;
  1573. BOOL result = FALSE;
  1574. PDEFAULT_DESTINATION dest;
  1575. #ifdef DEBUG
  1576. PSTR p;
  1577. #endif
  1578. __try {
  1579. g_LogInit = FALSE;
  1580. if (FirstTimeInit) {
  1581. PRIVATE_ASSERT (!g_FirstTypePtr);
  1582. dest = g_DefaultDest;
  1583. while (dest->Type) {
  1584. LogSetErrorDest (dest->Type, dest->Flags);
  1585. dest++;
  1586. }
  1587. GetWindowsDirectoryA (g_MainLogFile, ARRAYSIZE(g_MainLogFile));
  1588. StringCatA (g_MainLogFile, "\\cobra");
  1589. #ifdef DEBUG
  1590. StringCopyA (g_DebugLogFile, g_MainLogFile);
  1591. StringCatA (g_DebugLogFile, ".dbg");
  1592. g_DebugInfPathBufA[0] = g_DebugLogFile[0];
  1593. #endif
  1594. StringCatA (g_MainLogFile, ".log");
  1595. }
  1596. if (LogFile) {
  1597. StackStringCopyA (g_MainLogFile, LogFile);
  1598. #ifdef DEBUG
  1599. StringCopyA (g_DebugLogFile, g_MainLogFile);
  1600. p = _mbsrchr (g_DebugLogFile, '.');
  1601. if (p) {
  1602. if (_mbschr (p, TEXT('\\'))) {
  1603. p = NULL;
  1604. }
  1605. }
  1606. if (p) {
  1607. StringCopyA (p, ".dbg");
  1608. } else {
  1609. StringCatA (g_DebugLogFile, ".dbg");
  1610. }
  1611. #endif
  1612. }
  1613. if (g_ResetLog) {
  1614. SetFileAttributesA (g_MainLogFile, FILE_ATTRIBUTE_NORMAL);
  1615. DeleteFileA (g_MainLogFile);
  1616. }
  1617. #ifdef DEBUG
  1618. if (g_ResetLog) {
  1619. SetFileAttributesA (g_DebugLogFile, FILE_ATTRIBUTE_NORMAL);
  1620. DeleteFileA (g_DebugLogFile);
  1621. }
  1622. #endif
  1623. if (LogCallbackA) {
  1624. g_LogCallbackA = LogCallbackA;
  1625. }
  1626. if (LogCallbackW) {
  1627. g_LogCallbackW = LogCallbackW;
  1628. }
  1629. #ifdef DEBUG
  1630. if (FirstTimeInit) {
  1631. //
  1632. // get user's preferences
  1633. //
  1634. hInf = SetupOpenInfFileA (g_DebugInfPathBufA, NULL, INF_STYLE_WIN4 | INF_STYLE_OLDNT, NULL);
  1635. if (INVALID_HANDLE_VALUE != hInf && pGetUserPreferences(hInf)) {
  1636. g_DoLog = TRUE;
  1637. }
  1638. }
  1639. if (g_ResetLog) {
  1640. SetFileAttributesA (g_DebugLogFile, FILE_ATTRIBUTE_NORMAL);
  1641. DeleteFileA (g_DebugLogFile);
  1642. }
  1643. #endif
  1644. if (OrgPopupParentWnd) {
  1645. *OrgPopupParentWnd = g_LogPopupParentWnd;
  1646. }
  1647. if (LogPopupParentWnd) {
  1648. g_LogPopupParentWnd = LogPopupParentWnd;
  1649. g_InitThreadId = GetCurrentThreadId ();
  1650. }
  1651. result = TRUE;
  1652. }
  1653. __finally {
  1654. if (hInf != INVALID_HANDLE_VALUE) {
  1655. SetupCloseInfFile (hInf);
  1656. }
  1657. if (!result) { //lint !e774
  1658. if (g_FirstTypePtr) {
  1659. HeapFree (g_hHeap, 0, g_FirstTypePtr);
  1660. g_FirstTypePtr = NULL;
  1661. g_TypeTableCount = 0;
  1662. g_TypeTableFreeCount = 0;
  1663. }
  1664. g_OutDestAll = OD_UNDEFINED;
  1665. g_OutDestDefault = OD_UNDEFINED;
  1666. #ifdef DEBUG
  1667. g_DoLog = FALSE;
  1668. #endif
  1669. }
  1670. g_LogInit = TRUE;
  1671. g_ResetLog = FALSE;
  1672. }
  1673. return result;
  1674. }
  1675. VOID
  1676. LogSetVerboseLevel (
  1677. IN OUTPUT_DESTINATION Level
  1678. )
  1679. {
  1680. OUTPUT_DESTINATION Debugger = 0;
  1681. if (Level > 3) {
  1682. Debugger = OD_DEBUGGER|OD_ASSERT;
  1683. }
  1684. LogSetErrorDest (LOG_FATAL_ERROR, Level > 0 ? OD_POPUP_CANCEL|OD_LOGFILE|OD_ERROR|OD_CONSOLE|Debugger : OD_SUPPRESS);
  1685. LogSetErrorDest (LOG_ERROR, Level > 0 ? OD_POPUP_CANCEL|OD_LOGFILE|OD_ERROR|OD_CONSOLE|Debugger : OD_SUPPRESS);
  1686. LogSetErrorDest (LOG_MODULE_ERROR, Level > 0 ? OD_POPUP_CANCEL|OD_LOGFILE|OD_ERROR|OD_CONSOLE|Debugger : OD_SUPPRESS);
  1687. LogSetErrorDest (LOG_WARNING, Level > 1 ? OD_LOGFILE|Debugger : OD_SUPPRESS);
  1688. LogSetErrorDest (LOG_INFORMATION, Level > 2 ? OD_LOGFILE|Debugger : OD_SUPPRESS);
  1689. LogSetErrorDest ("Assert", OD_POPUP|OD_ERROR|Debugger);
  1690. LogSetErrorDest ("Verbose", Level > 2 ? OD_LOGFILE|Debugger : OD_SUPPRESS);
  1691. }
  1692. VOID
  1693. LogSetVerboseBitmap (
  1694. IN LOG_LEVEL Bitmap,
  1695. IN LOG_LEVEL BitsToAdjustMask,
  1696. IN BOOL EnableDebugger
  1697. )
  1698. {
  1699. OUTPUT_DESTINATION Debugger = 0;
  1700. if (EnableDebugger) {
  1701. Debugger = OD_DEBUGGER|OD_ASSERT;
  1702. }
  1703. if (BitsToAdjustMask & LL_FATAL_ERROR) {
  1704. LogSetErrorDest (LOG_FATAL_ERROR, (Bitmap & LL_FATAL_ERROR) ? OD_LOGFILE|OD_ERROR|OD_CONSOLE|Debugger : OD_SUPPRESS);
  1705. }
  1706. if (BitsToAdjustMask & LL_MODULE_ERROR) {
  1707. LogSetErrorDest (LOG_MODULE_ERROR, (Bitmap & LL_MODULE_ERROR) ? OD_LOGFILE|OD_ERROR|OD_CONSOLE|Debugger : OD_SUPPRESS);
  1708. }
  1709. if (BitsToAdjustMask & LL_ERROR) {
  1710. LogSetErrorDest (LOG_ERROR, (Bitmap & LL_ERROR) ? OD_LOGFILE|OD_ERROR|OD_CONSOLE|Debugger : OD_SUPPRESS);
  1711. }
  1712. if (BitsToAdjustMask & LL_WARNING) {
  1713. LogSetErrorDest (LOG_WARNING, (Bitmap & LL_WARNING) ? OD_LOGFILE|Debugger : OD_SUPPRESS);
  1714. }
  1715. if (BitsToAdjustMask & LL_INFORMATION) {
  1716. LogSetErrorDest (LOG_INFORMATION, (Bitmap & LL_INFORMATION) ? OD_LOGFILE|Debugger : OD_SUPPRESS);
  1717. }
  1718. if (BitsToAdjustMask & LL_STATUS) {
  1719. LogSetErrorDest (LOG_STATUS, (Bitmap & LL_STATUS) ? OD_LOGFILE|Debugger : OD_SUPPRESS);
  1720. }
  1721. if (BitsToAdjustMask & LL_UPDATE) {
  1722. LogSetErrorDest (LOG_UPDATE, (Bitmap & LL_UPDATE) ? OD_CONSOLE : OD_SUPPRESS);
  1723. }
  1724. }
  1725. /*++
  1726. Routine Description:
  1727. pInitialize initializes the log system calling the worker pInitLog. This function
  1728. should be only called once
  1729. Arguments:
  1730. None
  1731. Return Value:
  1732. TRUE if log system successfully initialized
  1733. --*/
  1734. BOOL
  1735. pInitialize (
  1736. VOID
  1737. )
  1738. {
  1739. return pInitLog (TRUE, NULL, NULL, NULL, NULL, NULL);
  1740. }
  1741. /*++
  1742. Routine Description:
  1743. LogReInit re-initializes the log system calling the worker pInitLog.
  1744. This function may be called any number of times, but only after pInitialize()
  1745. Arguments:
  1746. NewParent - Specifies the new parent handle.
  1747. OrgParent - Receives the old parent handle.
  1748. LogFile - Specifies a new log file name
  1749. LogCallback - Specifies a callback function that handles the log message (so
  1750. one module can pass log messages to another)
  1751. ResourceImage - Specifies the module path to use in FormatMessage when the
  1752. message is resource-based
  1753. Return Value:
  1754. TRUE if log system was successfully re-initialized
  1755. --*/
  1756. BOOL
  1757. LogReInitA (
  1758. IN HWND NewParent, OPTIONAL
  1759. OUT HWND *OrgParent, OPTIONAL
  1760. IN PCSTR LogFile, OPTIONAL
  1761. IN PLOGCALLBACKA LogCallback OPTIONAL
  1762. )
  1763. {
  1764. return pInitLog (FALSE, NewParent, OrgParent, LogFile, LogCallback, NULL);
  1765. }
  1766. BOOL
  1767. LogReInitW (
  1768. IN HWND NewParent, OPTIONAL
  1769. OUT HWND *OrgParent, OPTIONAL
  1770. IN PCWSTR LogFile, OPTIONAL
  1771. IN PLOGCALLBACKW LogCallback OPTIONAL
  1772. )
  1773. {
  1774. CHAR ansiLogFile[MAX_MBCHAR_PATH];
  1775. if (LogFile) {
  1776. KnownSizeWtoA (ansiLogFile, LogFile);
  1777. LogFile = (PWSTR) ansiLogFile;
  1778. }
  1779. return pInitLog (FALSE, NewParent, OrgParent, (PCSTR) LogFile, NULL, LogCallback);
  1780. }
  1781. VOID
  1782. LogBegin (
  1783. IN HMODULE ModuleInstance
  1784. )
  1785. {
  1786. DWORD threadError;
  1787. DWORD rc;
  1788. threadError = GetLastError ();
  1789. if (!g_LogMutex) {
  1790. InitializeLog();
  1791. }
  1792. rc = WaitForSingleObject (g_LogMutex, INFINITE);
  1793. PRIVATE_ASSERT (rc == WAIT_OBJECT_0 || rc == WAIT_ABANDONED);
  1794. if (rc == WAIT_ABANDONED) {
  1795. g_LoggingNow = 0;
  1796. }
  1797. if (!g_LoggingNow) {
  1798. g_LibHandle = ModuleInstance;
  1799. SetLastError (threadError);
  1800. g_LogError = threadError;
  1801. }
  1802. g_LoggingNow++;
  1803. }
  1804. VOID
  1805. LogEnd (
  1806. VOID
  1807. )
  1808. {
  1809. g_LoggingNow--;
  1810. if (!g_LoggingNow) {
  1811. g_LibHandle = g_hInst;
  1812. SetLastError (g_LogError);
  1813. }
  1814. ReleaseMutex (g_LogMutex);
  1815. }
  1816. VOID
  1817. pDisableLog (
  1818. VOID
  1819. )
  1820. {
  1821. g_LogInit = FALSE;
  1822. }
  1823. VOID
  1824. pExitLog (
  1825. VOID
  1826. )
  1827. /*++
  1828. Routine Description:
  1829. pExitLog cleans up any resources used by the log system
  1830. Arguments:
  1831. none
  1832. Return Value:
  1833. none
  1834. --*/
  1835. {
  1836. g_LogInit = FALSE;
  1837. WaitForSingleObject (g_LogMutex, 60000);
  1838. CloseHandle (g_LogMutex);
  1839. g_LogMutex = NULL;
  1840. if (g_FirstTypePtr) {
  1841. HeapFree (g_hHeap, 0, g_FirstTypePtr);
  1842. g_FirstTypePtr = NULL;
  1843. g_TypeTableCount = 0;
  1844. g_TypeTableFreeCount = 0;
  1845. }
  1846. g_OutDestAll = OD_UNDEFINED;
  1847. g_OutDestDefault = OD_UNDEFINED;
  1848. }
  1849. /*++
  1850. Routine Description:
  1851. LogA and LogW preserve the last error code; they call the helpers
  1852. pFormatAndWriteMsgA and pFormatAndWriteMsgW respectivelly.
  1853. Arguments:
  1854. Type - Specifies the type (category) of the message
  1855. Format - Specifies either the message in ASCII format or
  1856. a message ID (if SHIFTRIGHT16(Format) == 0). The message
  1857. will be formatted using args.
  1858. ... - Specifies a list of arguments to be used when formatting
  1859. the message. If a message ID is used for Format, args
  1860. is supposed to be an array of pointers to strings
  1861. Return Value:
  1862. none
  1863. --*/
  1864. VOID
  1865. _cdecl
  1866. LogA (
  1867. IN PCSTR Type,
  1868. IN PCSTR Format,
  1869. ...
  1870. )
  1871. {
  1872. va_list args;
  1873. if (!g_LogInit) {
  1874. return;
  1875. }
  1876. va_start (args, Format);
  1877. pFormatAndWriteMsgA (
  1878. FALSE,
  1879. Type,
  1880. Format,
  1881. args
  1882. );
  1883. va_end (args);
  1884. }
  1885. VOID
  1886. _cdecl
  1887. LogW (
  1888. IN PCSTR Type,
  1889. IN PCSTR Format,
  1890. ...
  1891. )
  1892. {
  1893. va_list args;
  1894. if (!g_LogInit) {
  1895. return;
  1896. }
  1897. va_start (args, Format);
  1898. pFormatAndWriteMsgW (
  1899. FALSE,
  1900. Type,
  1901. Format,
  1902. args
  1903. );
  1904. va_end (args);
  1905. }
  1906. VOID
  1907. _cdecl
  1908. LogIfA (
  1909. IN BOOL Condition,
  1910. IN PCSTR Type,
  1911. IN PCSTR Format,
  1912. ...
  1913. )
  1914. {
  1915. va_list args;
  1916. if (!g_LogInit) {
  1917. return;
  1918. }
  1919. if (!Condition) {
  1920. return;
  1921. }
  1922. va_start (args, Format);
  1923. pFormatAndWriteMsgA (
  1924. FALSE,
  1925. Type,
  1926. Format,
  1927. args
  1928. );
  1929. va_end (args);
  1930. }
  1931. VOID
  1932. _cdecl
  1933. LogIfW (
  1934. IN BOOL Condition,
  1935. IN PCSTR Type,
  1936. IN PCSTR Format,
  1937. ...
  1938. )
  1939. {
  1940. va_list args;
  1941. if (!g_LogInit) {
  1942. return;
  1943. }
  1944. if (!Condition) {
  1945. return;
  1946. }
  1947. va_start (args, Format);
  1948. pFormatAndWriteMsgW (
  1949. FALSE,
  1950. Type,
  1951. Format,
  1952. args
  1953. );
  1954. va_end (args);
  1955. }
  1956. #ifdef DEBUG
  1957. VOID
  1958. _cdecl
  1959. DbgLogA (
  1960. IN PCSTR Type,
  1961. IN PCSTR Format,
  1962. ...
  1963. )
  1964. {
  1965. va_list args;
  1966. if (!g_LogInit) {
  1967. return;
  1968. }
  1969. va_start (args, Format);
  1970. pFormatAndWriteMsgA (
  1971. TRUE,
  1972. Type,
  1973. Format,
  1974. args
  1975. );
  1976. va_end (args);
  1977. }
  1978. VOID
  1979. _cdecl
  1980. DbgLogW (
  1981. IN PCSTR Type,
  1982. IN PCSTR Format,
  1983. ...
  1984. )
  1985. {
  1986. va_list args;
  1987. if (!g_LogInit) {
  1988. return;
  1989. }
  1990. va_start (args, Format);
  1991. pFormatAndWriteMsgW (
  1992. TRUE,
  1993. Type,
  1994. Format,
  1995. args
  1996. );
  1997. va_end (args);
  1998. }
  1999. VOID
  2000. _cdecl
  2001. DbgLogIfA (
  2002. IN BOOL Condition,
  2003. IN PCSTR Type,
  2004. IN PCSTR Format,
  2005. ...
  2006. )
  2007. {
  2008. va_list args;
  2009. if (!g_LogInit) {
  2010. return;
  2011. }
  2012. if (!Condition) {
  2013. return;
  2014. }
  2015. va_start (args, Format);
  2016. pFormatAndWriteMsgA (
  2017. TRUE,
  2018. Type,
  2019. Format,
  2020. args
  2021. );
  2022. va_end (args);
  2023. }
  2024. VOID
  2025. _cdecl
  2026. DbgLogIfW (
  2027. IN BOOL Condition,
  2028. IN PCSTR Type,
  2029. IN PCSTR Format,
  2030. ...
  2031. )
  2032. {
  2033. va_list args;
  2034. if (!g_LogInit) {
  2035. return;
  2036. }
  2037. if (!Condition) {
  2038. return;
  2039. }
  2040. va_start (args, Format);
  2041. pFormatAndWriteMsgW (
  2042. TRUE,
  2043. Type,
  2044. Format,
  2045. args
  2046. );
  2047. va_end (args);
  2048. }
  2049. #endif
  2050. VOID
  2051. LogTitleA (
  2052. IN PCSTR Type,
  2053. IN PCSTR Title OPTIONAL
  2054. )
  2055. {
  2056. CHAR formattedMsg[OUTPUT_BUFSIZE_LARGE];
  2057. if (!g_LogInit) {
  2058. return;
  2059. }
  2060. StringCopyByteCountA (g_LastType, Type, DWSIZEOF (g_LastType));
  2061. if (!Title) {
  2062. Title = Type;
  2063. }
  2064. StringCopyByteCountA (formattedMsg, Title, DWSIZEOF (formattedMsg) - DWSIZEOF (S_COLUMNDOUBLELINEA));
  2065. StringCatA (formattedMsg, S_COLUMNDOUBLELINEA);
  2066. pRawWriteLogOutputA (Type, NULL, formattedMsg, FALSE);
  2067. //
  2068. // set LOGTITLE flag
  2069. //
  2070. g_HasTitle = TRUE;
  2071. }
  2072. VOID
  2073. LogTitleW (
  2074. IN PCSTR Type,
  2075. IN PCWSTR Title OPTIONAL
  2076. )
  2077. {
  2078. WCHAR formattedMsg[OUTPUT_BUFSIZE_LARGE];
  2079. WCHAR typeW[OUTPUT_BUFSIZE_SMALL];
  2080. if (!g_LogInit) {
  2081. return;
  2082. }
  2083. StringCopyCharCountA (g_LastType, Type, DWSIZEOF (g_LastType));
  2084. if (!Title) {
  2085. KnownSizeAtoW (typeW, Type);
  2086. Title = typeW;
  2087. }
  2088. StringCopyCharCountW (formattedMsg, Title, DWSIZEOF (formattedMsg) - DWSIZEOF (S_COLUMNDOUBLELINEW));
  2089. StringCatW (formattedMsg, S_COLUMNDOUBLELINEW);
  2090. pRawWriteLogOutputW (Type, NULL, formattedMsg, FALSE);
  2091. //
  2092. // set LOGTITLE flag
  2093. //
  2094. g_HasTitle = TRUE;
  2095. }
  2096. VOID
  2097. LogLineA (
  2098. IN PCSTR Line
  2099. )
  2100. {
  2101. CHAR output[OUTPUT_BUFSIZE_LARGE];
  2102. BOOL hasNewLine = FALSE;
  2103. PCSTR p;
  2104. if (!g_LogInit) {
  2105. return;
  2106. }
  2107. if (!Line) {
  2108. return;
  2109. }
  2110. if (!g_HasTitle) {
  2111. DEBUGMSG ((DBG_WHOOPS, "LOGTITLE missing before LOGLINE"));
  2112. return;
  2113. }
  2114. StringCopyByteCountA (output, Line, DWSIZEOF (output) - 4);
  2115. //
  2116. // find out if the line terminates with newline
  2117. //
  2118. for (p = _mbsstr (output, S_NEWLINEA); p; p = _mbsstr (p + NEWLINE_CHAR_COUNTA, S_NEWLINEA)) {
  2119. if (p[NEWLINE_CHAR_COUNTA] == 0) {
  2120. //
  2121. // the line ends with a newline
  2122. //
  2123. hasNewLine = TRUE;
  2124. break;
  2125. }
  2126. }
  2127. if (!hasNewLine) {
  2128. StringCatA (output, S_NEWLINEA);
  2129. }
  2130. pRawWriteLogOutputA (g_LastType, NULL, output, FALSE);
  2131. }
  2132. VOID
  2133. LogLineW (
  2134. IN PCWSTR Line
  2135. )
  2136. {
  2137. WCHAR output[OUTPUT_BUFSIZE_LARGE];
  2138. BOOL hasNewLine = FALSE;
  2139. PCWSTR p;
  2140. if (!g_LogInit) {
  2141. return;
  2142. }
  2143. if (!Line) {
  2144. return;
  2145. }
  2146. if (!g_HasTitle) {
  2147. DEBUGMSG ((DBG_WHOOPS, "LOGTITLE missing before LOGLINE"));
  2148. return;
  2149. }
  2150. StringCopyCharCountW (output, Line, DWSIZEOF (output) / DWSIZEOF (WCHAR) - 4);
  2151. //
  2152. // find out if the line terminates with newline
  2153. //
  2154. for (p = wcsstr (output, S_NEWLINEW); p; p = wcsstr (p + NEWLINE_CHAR_COUNTW, S_NEWLINEW)) {
  2155. if (p[NEWLINE_CHAR_COUNTW] == 0) {
  2156. //
  2157. // the line ends with a newline
  2158. //
  2159. hasNewLine = TRUE;
  2160. break;
  2161. }
  2162. }
  2163. if (!hasNewLine) {
  2164. StringCatW (output, S_NEWLINEW);
  2165. }
  2166. pRawWriteLogOutputW (g_LastType, NULL, output, FALSE);
  2167. }
  2168. VOID
  2169. LogDirectA (
  2170. IN PCSTR Type,
  2171. IN PCSTR Text
  2172. )
  2173. {
  2174. if (!g_LogInit) {
  2175. return;
  2176. }
  2177. g_HasTitle = FALSE;
  2178. pRawWriteLogOutputA (Type, NULL, Text, FALSE);
  2179. }
  2180. VOID
  2181. LogDirectW (
  2182. IN PCSTR Type,
  2183. IN PCWSTR Text
  2184. )
  2185. {
  2186. if (!g_LogInit) {
  2187. return;
  2188. }
  2189. g_HasTitle = FALSE;
  2190. pRawWriteLogOutputW (Type, NULL, Text, FALSE);
  2191. }
  2192. #ifdef DEBUG
  2193. VOID
  2194. DbgDirectA (
  2195. IN PCSTR Type,
  2196. IN PCSTR Text
  2197. )
  2198. {
  2199. if (!g_LogInit) {
  2200. return;
  2201. }
  2202. g_HasTitle = FALSE;
  2203. pRawWriteLogOutputA (Type, NULL, Text, TRUE);
  2204. }
  2205. VOID
  2206. DbgDirectW (
  2207. IN PCSTR Type,
  2208. IN PCWSTR Text
  2209. )
  2210. {
  2211. if (!g_LogInit) {
  2212. return;
  2213. }
  2214. g_HasTitle = FALSE;
  2215. pRawWriteLogOutputW (Type, NULL, Text, TRUE);
  2216. }
  2217. #endif
  2218. VOID
  2219. SuppressAllLogPopups (
  2220. IN BOOL SuppressOn
  2221. )
  2222. {
  2223. g_SuppressAllPopups = SuppressOn;
  2224. }
  2225. #ifdef DEBUG
  2226. /*++
  2227. Routine Description:
  2228. DebugLogTimeA and DebugLogTimeW preserve the last error code;
  2229. they append the current date and time to the formatted message,
  2230. then call LogA and LogW to actually process the message.
  2231. Arguments:
  2232. Format - Specifies either the message in ASCII format or
  2233. a message ID (if SHIFTRIGHT16(Format) == 0). The message
  2234. will be formatted using args.
  2235. ... - Specifies a list of arguments to be used when formatting
  2236. the message. If a message ID is used for Format, args
  2237. is supposed to be an array of pointers to strings
  2238. Return Value:
  2239. none
  2240. --*/
  2241. VOID
  2242. _cdecl
  2243. DebugLogTimeA (
  2244. IN PCSTR Format,
  2245. ...
  2246. )
  2247. {
  2248. CHAR msg[OUTPUT_BUFSIZE_LARGE];
  2249. CHAR date[OUTPUT_BUFSIZE_SMALL];
  2250. CHAR ttime[OUTPUT_BUFSIZE_SMALL];
  2251. PSTR appendPos, end;
  2252. DWORD currentTickCount;
  2253. va_list args;
  2254. if (!g_LogInit) {
  2255. return;
  2256. }
  2257. if (!g_DoLog) {
  2258. return;
  2259. }
  2260. //
  2261. // first, get the current date and time into the string.
  2262. //
  2263. if (!GetDateFormatA (
  2264. LOCALE_SYSTEM_DEFAULT,
  2265. LOCALE_NOUSEROVERRIDE,
  2266. NULL,
  2267. NULL,
  2268. date,
  2269. OUTPUT_BUFSIZE_SMALL)) {
  2270. StringCopyA (date,"** Error retrieving date. **");
  2271. }
  2272. if (!GetTimeFormatA (
  2273. LOCALE_SYSTEM_DEFAULT,
  2274. LOCALE_NOUSEROVERRIDE,
  2275. NULL,
  2276. NULL,
  2277. ttime,
  2278. OUTPUT_BUFSIZE_SMALL)) {
  2279. StringCopyA (ttime,"** Error retrieving time. **");
  2280. }
  2281. //
  2282. // Now, get the current tick count.
  2283. //
  2284. currentTickCount = GetTickCount();
  2285. //
  2286. // If this is the first call save the tick count.
  2287. //
  2288. if (!g_FirstTickCount) {
  2289. g_FirstTickCount = currentTickCount;
  2290. g_LastTickCount = currentTickCount;
  2291. }
  2292. //
  2293. // Now, build the passed in string.
  2294. //
  2295. va_start (args, Format);
  2296. appendPos = msg + _vsnprintf (msg, OUTPUT_BUFSIZE_LARGE, Format, args);
  2297. va_end (args);
  2298. //
  2299. // Append the time statistics to the end of the string.
  2300. //
  2301. end = msg + OUTPUT_BUFSIZE_LARGE;
  2302. _snprintf(
  2303. appendPos,
  2304. ((UBINT)end - (UBINT)appendPos) / (DWSIZEOF (CHAR)),
  2305. "\nCurrent Date and Time: %s %s\n"
  2306. "Milliseconds since last DEBUGLOGTIME call : %u\n"
  2307. "Milliseconds since first DEBUGLOGTIME call: %u\n",
  2308. date,
  2309. ttime,
  2310. currentTickCount - g_LastTickCount,
  2311. currentTickCount - g_FirstTickCount
  2312. );
  2313. g_LastTickCount = currentTickCount;
  2314. //
  2315. // Now, pass the results onto debugoutput.
  2316. //
  2317. LogA (DBG_TIME, "%s", msg);
  2318. }
  2319. VOID
  2320. _cdecl
  2321. DebugLogTimeW (
  2322. IN PCSTR Format,
  2323. ...
  2324. )
  2325. {
  2326. WCHAR msgW[OUTPUT_BUFSIZE_LARGE];
  2327. WCHAR dateW[OUTPUT_BUFSIZE_SMALL];
  2328. WCHAR timeW[OUTPUT_BUFSIZE_SMALL];
  2329. PCWSTR formatW;
  2330. PWSTR appendPosW, endW;
  2331. DWORD currentTickCount;
  2332. va_list args;
  2333. if (!g_LogInit) {
  2334. return;
  2335. }
  2336. if (!g_DoLog) {
  2337. return;
  2338. }
  2339. //
  2340. // first, get the current date and time into the string.
  2341. //
  2342. if (!GetDateFormatW (
  2343. LOCALE_SYSTEM_DEFAULT,
  2344. LOCALE_NOUSEROVERRIDE,
  2345. NULL,
  2346. NULL,
  2347. dateW,
  2348. OUTPUT_BUFSIZE_SMALL)) {
  2349. StringCopyW (dateW, L"** Error retrieving date. **");
  2350. }
  2351. if (!GetTimeFormatW (
  2352. LOCALE_SYSTEM_DEFAULT,
  2353. LOCALE_NOUSEROVERRIDE,
  2354. NULL,
  2355. NULL,
  2356. timeW,
  2357. OUTPUT_BUFSIZE_SMALL)) {
  2358. StringCopyW (timeW, L"** Error retrieving time. **");
  2359. }
  2360. //
  2361. // Now, get the current tick count.
  2362. //
  2363. currentTickCount = GetTickCount();
  2364. //
  2365. // If this is the first call save the tick count.
  2366. //
  2367. if (!g_FirstTickCount) {
  2368. g_FirstTickCount = currentTickCount;
  2369. g_LastTickCount = currentTickCount;
  2370. }
  2371. //
  2372. // Now, build the passed in string.
  2373. //
  2374. va_start (args, Format);
  2375. formatW = ConvertAtoW (Format);
  2376. appendPosW = msgW + _vsnwprintf (msgW, OUTPUT_BUFSIZE_LARGE, formatW, args);
  2377. FreeConvertedStr (formatW);
  2378. va_end (args);
  2379. //
  2380. // Append the time statistics to the end of the string.
  2381. //
  2382. endW = msgW + OUTPUT_BUFSIZE_LARGE;
  2383. _snwprintf(
  2384. appendPosW,
  2385. ((UBINT)endW - (UBINT)appendPosW) / (DWSIZEOF (WCHAR)),
  2386. L"\nCurrent Date and Time: %s %s\n"
  2387. L"Milliseconds since last DEBUGLOGTIME call : %u\n"
  2388. L"Milliseconds since first DEBUGLOGTIME call: %u\n",
  2389. dateW,
  2390. timeW,
  2391. currentTickCount - g_LastTickCount,
  2392. currentTickCount - g_FirstTickCount
  2393. );
  2394. g_LastTickCount = currentTickCount;
  2395. //
  2396. // Now, pass the results onto debugoutput.
  2397. //
  2398. LogW (DBG_TIME, "%s", msgW);
  2399. }
  2400. #endif // DEBUG
  2401. VOID
  2402. InitializeLog (
  2403. VOID
  2404. )
  2405. {
  2406. g_LogMutex = CreateMutex (NULL, FALSE, TEXT("cobra_log_mutex"));
  2407. UtInitialize (NULL);
  2408. pInitialize ();
  2409. }
  2410. EXPORT
  2411. BOOL
  2412. WINAPI
  2413. DllMain (
  2414. IN HINSTANCE hInstance,
  2415. IN DWORD dwReason,
  2416. IN LPVOID lpReserved
  2417. )
  2418. {
  2419. if (dwReason == DLL_PROCESS_ATTACH) {
  2420. g_hInst = hInstance;
  2421. g_LibHandle = hInstance;
  2422. InitializeLog ();
  2423. PRIVATE_ASSERT (g_LogMutex != NULL);
  2424. }
  2425. return TRUE;
  2426. }
  2427. VOID
  2428. LogDeleteOnNextInit(
  2429. VOID
  2430. )
  2431. {
  2432. g_ResetLog = TRUE;
  2433. }
  2434. #ifdef DEBUG
  2435. VOID
  2436. LogCopyDebugInfPathA(
  2437. OUT PSTR MaxPathBuffer
  2438. )
  2439. {
  2440. StringCopyByteCountA (MaxPathBuffer, g_DebugInfPathBufA, MAX_PATH);
  2441. }
  2442. VOID
  2443. LogCopyDebugInfPathW(
  2444. OUT PWSTR MaxPathBuffer
  2445. )
  2446. {
  2447. KnownSizeAtoW (MaxPathBuffer, g_DebugInfPathBufA);
  2448. }
  2449. #endif