Source code of Windows XP (NT5)
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.

2856 lines
61 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. Ovidiu Temereanca (ovidiut) 23-Oct-1998
  11. Implemented a new log mechanism and added new logging capabilities
  12. --*/
  13. #include "pch.h"
  14. /*++
  15. Macro Expansion List Description:
  16. TYPE_DEFAULTS specify the default destination for the frequently used types,
  17. such as LOG_ERROR, LOG_FATAL_ERROR, and so on.
  18. Line Syntax:
  19. DEFMAC(TypeString, Flags)
  20. Arguments:
  21. TypeString - Specifies the LOG_ constant as defined in log.h
  22. Flags - One or more of:
  23. DEFAULT_ERROR_FLAGS - Specifies debug log, setup log, debugger,
  24. popup, and the value of GetLastError.
  25. OD_DEBUGLOG - Specifies the debug log
  26. OD_ERROR - Specifies type is an error (gets value of
  27. GetLastError)
  28. OD_SUPPRESS - Suppresses all output for the type
  29. OD_LOGFILE - Specifies the setup log
  30. OD_DEBUGGER - Specifies the debugger (i.e., VC or remote debugger)
  31. OD_CONSOLE - Specifies the console (via printf)
  32. OD_POPUP - Specifies a message box
  33. OD_FORCE_POPUP - Specifies a message box, even if debug message
  34. was turned off via a click on Cancel
  35. OD_MUST_BE_LOCALIZED - Indicates the type must originate from a
  36. localized message; used for LOG() calls that
  37. generate popups. (So English messages
  38. don't sneak into the project.)
  39. OD_UNATTEND_POPUP - Causes popup even in unattend mode
  40. OD_ASSERT - Give DebugBreak option in popup
  41. Variables Generated From List:
  42. g_DefaultDest
  43. --*/
  44. #ifndef DEBUG
  45. #define TYPE_DEFAULTS \
  46. DEFMAC(LOG_FATAL_ERROR, DEFAULT_ERROR_FLAGS|USER_POPUP_FLAGS) \
  47. DEFMAC(LOG_ERROR, DEFAULT_ERROR_FLAGS) \
  48. DEFMAC(LOG_INFORMATION, OD_LOGFILE) \
  49. DEFMAC(LOG_ACCOUNTS, OD_LOGFILE) \
  50. DEFMAC(LOG_CONFIG, OD_CONFIG|OD_NOFORMAT) \
  51. #else
  52. #define TYPE_DEFAULTS \
  53. DEFMAC(LOG_FATAL_ERROR, DEFAULT_ERROR_FLAGS|USER_POPUP_FLAGS) \
  54. DEFMAC(LOG_ERROR, DEFAULT_ERROR_FLAGS) \
  55. DEFMAC(DBG_WHOOPS, DEFAULT_ERROR_FLAGS) \
  56. DEFMAC(DBG_WARNING, OD_DEBUGLOG|OD_LOGFILE|OD_DEBUGGER) \
  57. DEFMAC(DBG_VERBOSE, OD_DEBUGLOG|OD_DEBUGGER) \
  58. DEFMAC(DBG_NAUSEA, OD_DEBUGLOG) \
  59. DEFMAC(DBG_ASSERT, DEFAULT_ERROR_FLAGS) \
  60. DEFMAC(LOG_INFORMATION, OD_LOGFILE) \
  61. DEFMAC(LOG_ACCOUNTS, OD_LOGFILE) \
  62. DEFMAC(LOG_CONFIG, OD_CONFIG|OD_NOFORMAT) \
  63. DEFMAC("PoolMem", OD_SUPPRESS) \
  64. #endif
  65. //
  66. // This constant sets the default output
  67. //
  68. #ifndef DEBUG
  69. #define NORMAL_DEFAULT OD_LOGFILE
  70. #else
  71. #define NORMAL_DEFAULT OD_DEBUGLOG
  72. #endif
  73. //
  74. // Constants and types
  75. //
  76. #define OUTPUT_BUFSIZE_LARGE 8192
  77. #define OUTPUT_BUFSIZE_SMALL 128
  78. #define MAX_MSGTITLE_LEN 14
  79. #define MSGBODY_INDENT 12
  80. #define SCREEN_WIDTH 80
  81. #define MAX_TYPE 64
  82. #define S_COLUMNDOUBLELINEA ":\r\n\r\n"
  83. #define S_COLUMNDOUBLELINEW L":\r\n\r\n"
  84. #define S_NEWLINEA "\r\n"
  85. #define S_NEWLINEW L"\r\n"
  86. #define NEWLINE_CHAR_COUNT (sizeof (S_NEWLINEA) - 1)
  87. #define OUT_UNDEFINED(OutDest) (OutDest == OD_UNDEFINED)
  88. #define OUT_DEBUGLOG(OutDest) ((OutDest & OD_DEBUGLOG) != 0)
  89. #define OUT_SUPPRESSED(OutDest) ((OutDest & OD_SUPPRESS) != 0)
  90. #define OUT_NO_OUTPUT(OutDest) (OUT_UNDEFINED(OutDest) || OUT_SUPPRESSED(OutDest))
  91. #define OUT_ERROR(OutDest) ((OutDest & OD_ERROR) != 0)
  92. #define OUT_LOGFILE(OutDest) ((OutDest & OD_LOGFILE) != 0)
  93. #define OUT_DEBUGGER(OutDest) ((OutDest & OD_DEBUGGER) != 0)
  94. #define OUT_CONSOLE(OutDest) ((OutDest & OD_CONSOLE) != 0)
  95. #define OUT_POPUP(OutDest) ((OutDest & (OD_POPUP|OD_FORCE_POPUP|OD_UNATTEND_POPUP)) != 0)
  96. #define OUT_POPUP_CANCEL(OutDest) ((OutDest & (OD_POPUP_CANCEL|OD_FORCE_POPUP)) == OD_POPUP_CANCEL)
  97. #define OUT_FORCED_POPUP(OutDest) ((OutDest & (OD_FORCE_POPUP|OD_UNATTEND_POPUP)) != 0)
  98. #define MUST_BE_LOCALIZED(OutDest) ((OutDest & OD_MUST_BE_LOCALIZED) == OD_MUST_BE_LOCALIZED)
  99. #define OUT_ASSERT(OutDest) ((OutDest & OD_ASSERT) != 0)
  100. #define OUT_CONFIG(OutDest) ((OutDest & OD_CONFIG) != 0)
  101. #define OUT_NOFORMAT(OutDest) ((OutDest & OD_NOFORMAT) != 0)
  102. #ifdef DEBUG
  103. #define DEFAULT_ERROR_FLAGS (OD_DEBUGLOG | OD_LOGFILE | OD_POPUP | OD_ERROR | OD_UNATTEND_POPUP | OD_ASSERT)
  104. #define USER_POPUP_FLAGS (OD_FORCE_POPUP | OD_MUST_BE_LOCALIZED)
  105. #else
  106. #define DEFAULT_ERROR_FLAGS (OD_LOGFILE | OD_POPUP | OD_ERROR | OD_MUST_BE_LOCALIZED)
  107. #define USER_POPUP_FLAGS (OD_FORCE_POPUP | OD_MUST_BE_LOCALIZED)
  108. #endif
  109. #define END_OF_BUFFER(buf) ((buf) + (sizeof(buf) / sizeof(buf[0])) - 1)
  110. #define DEBUG_SECTION "Debug"
  111. #define ENTRY_ALL "All"
  112. #define ENTRY_DEFAULTOVERRIDE "DefaultOverride"
  113. #define LOGSEVERITY LogSeverity
  114. #define LOGSEV_FATAL_ERROR LogSevFatalError
  115. #define LOGSEV_ERROR LogSevError
  116. #define LOGSEV_WARNING LogSevWarning
  117. #define LOGSEV_INFORMATION LogSevInformation
  118. typedef enum {
  119. OD_UNDEFINED = 0x00, // undefined output dest
  120. OD_DEBUGLOG = 0x01, // debuglog used
  121. OD_SUPPRESS = 0x02, // don't log to any device
  122. OD_ERROR = 0x04, // automatically append GetLastError() to the message
  123. OD_LOGFILE = 0x08, // messages go to logfile
  124. OD_DEBUGGER = 0x10, // messages go to debugger
  125. OD_CONSOLE = 0x20, // messages go to console
  126. OD_POPUP = 0x40, // display a popup dialog
  127. OD_POPUP_CANCEL = 0x80, // do not display a popup dialog (cancelled by user)
  128. OD_FORCE_POPUP = 0x100, // force the popup to be displayed always
  129. OD_MUST_BE_LOCALIZED = 0x200, // used for LOG() that will generate a popup
  130. OD_UNATTEND_POPUP = 0x400, // force the popup to be displayed in unattend mode
  131. OD_ASSERT = 0x800, // give DebugBreak option in popup
  132. OD_CONFIG = 0x1000, // output to config.dmp
  133. OD_NOFORMAT = 0x2000 // no format on output string
  134. } OUTPUT_DESTINATION;
  135. #define OUTPUTDEST DWORD
  136. typedef struct {
  137. PCSTR Value; // string value entered by the user (LOG,POPUP,SUPPRESS etc.)
  138. OUTPUTDEST OutDest; // any combination of OutDest flags
  139. } STRING2BINARY, *PSTRING2BINARY;
  140. const STRING2BINARY g_String2Binary[] = {
  141. "SUPPRESS", OD_SUPPRESS,
  142. "LOG", OD_LOGFILE,
  143. "POPUP", OD_POPUP,
  144. "DEBUGGER", OD_DEBUGGER,
  145. "CONSOLE", OD_CONSOLE,
  146. "ERROR", OD_ERROR,
  147. "NOCANCEL", OD_FORCE_POPUP,
  148. "ASSERT", OD_ASSERT
  149. };
  150. const PCSTR g_IgnoreKeys[] = {
  151. "Debug",
  152. "KeepTempFiles"
  153. };
  154. //
  155. // a window handle for popup parent
  156. //
  157. HWND g_LogPopupParentWnd = NULL;
  158. //
  159. // thread id that set this window handle
  160. //
  161. DWORD g_InitThreadId = 0;
  162. static OUTPUTDEST g_OutDestAll = OD_UNDEFINED;
  163. static OUTPUTDEST g_OutDestDefault = NORMAL_DEFAULT;
  164. static PVOID g_TypeSt = NULL;
  165. static BOOL g_HasTitle = FALSE;
  166. static CHAR g_LastType [MAX_TYPE];
  167. static BOOL g_SuppressAllPopups = FALSE;
  168. CHAR g_ConfigDmpPathBufA[MAX_MBCHAR_PATH];
  169. BOOL g_ResetLog = FALSE;
  170. #ifdef PROGRESS_BAR
  171. HANDLE g_ProgressBarLog = INVALID_HANDLE_VALUE;
  172. #endif //PROGRESS_BAR
  173. #ifdef DEBUG
  174. CHAR g_DebugInfPathBufA[] = "C:\\debug.inf";
  175. WCHAR g_DebugInfPathBufW[] = L"C:\\debug.inf";
  176. CHAR g_Debug9xLogPathBufA[] = "C:\\debug9x.log";
  177. CHAR g_DebugNtLogPathBufA[] = "C:\\debugnt.log";
  178. PCSTR g_DebugLogPathA = NULL;
  179. //
  180. // If g_DoLog is TRUE, then, debug logging is enabled in the
  181. // checked build even if there is no debug.inf.
  182. // This variable can be enabled via the /#U:DOLOG command line directive...
  183. BOOL g_DoLog = FALSE;
  184. #define PRIVATE_ASSERT(expr) pPrivateAssert(expr,#expr,__LINE__);
  185. #else
  186. #define PRIVATE_ASSERT(expr)
  187. #endif // DEBUG
  188. #define DEFMAC(typestr, flags) {typestr, flags},
  189. typedef struct {
  190. PCSTR Type;
  191. DWORD Flags;
  192. } DEFAULT_DESTINATION, *PDEFAULT_DESTINATION;
  193. DEFAULT_DESTINATION g_DefaultDest[] = {TYPE_DEFAULTS /* , */ {NULL, 0}};
  194. #undef DEFMAC
  195. #ifdef DEBUG
  196. VOID
  197. pPrivateAssert (
  198. IN BOOL Expr,
  199. IN PCSTR StringExpr,
  200. IN UINT Line
  201. )
  202. {
  203. CHAR Buffer[256];
  204. if (Expr) {
  205. return;
  206. }
  207. wsprintfA (Buffer, "LOG FAILURE: %s (log.c line %u)", StringExpr, Line);
  208. MessageBox (NULL, Buffer, NULL, MB_OK);
  209. }
  210. #endif
  211. BOOL
  212. pIgnoreKey (
  213. IN PCSTR Key
  214. )
  215. /*++
  216. Routine Description:
  217. pIgnoreKey decides if a key from [debug] section of DEBUG.INF
  218. should be ignored for our purposes (we are only looking for
  219. <All>, <DefaultOverride> and log/debug types).
  220. Specifically, we ignore all keywords in <g_IgnoreKeys> table.
  221. Arguments:
  222. Key - Specifies the key from [debug] section of DEBUG.INF
  223. Return Value:
  224. TRUE if the key should be ignored, or FALSE if it will be taken into consideration.
  225. --*/
  226. {
  227. INT i;
  228. for(i = 0; i < sizeof (g_IgnoreKeys) / sizeof (PCSTR); i++) {
  229. if (StringIMatchA (Key, g_IgnoreKeys[i])) {
  230. return TRUE;
  231. }
  232. }
  233. return FALSE;
  234. }
  235. OUTPUTDEST
  236. pConvertToOutputType (
  237. IN PCSTR Value
  238. )
  239. /*++
  240. Routine Description:
  241. pConvertToOutputType converts a text value entered by the user in
  242. DEBUG.INF file, associated with a type (e.g. "LOG", "POPUP" etc.).
  243. Arguments:
  244. Value - Specifies the text value
  245. Return Value:
  246. The OUTPUT_DESTINATION value associated with the given value or
  247. OD_UNDEFINED if the value is not valid.
  248. --*/
  249. {
  250. INT i;
  251. for(i = 0; i < sizeof (g_String2Binary) / sizeof (STRING2BINARY); i++) {
  252. if (StringIMatchA (Value, g_String2Binary[i].Value)) {
  253. return g_String2Binary[i].OutDest;
  254. }
  255. }
  256. return OD_UNDEFINED;
  257. }
  258. OUTPUTDEST
  259. pGetTypeOutputDestFromTable (
  260. IN PCSTR Type
  261. )
  262. /*++
  263. Routine Description:
  264. pGetTypeOutputDestFromTable returns the output destination associated
  265. with the specified type in the global table
  266. Arguments:
  267. Type - Specifies the type
  268. Return Value:
  269. Any combination of enum OUTPUT_DESTINATION values associated with
  270. the given type.
  271. --*/
  272. {
  273. OUTPUTDEST OutDest;
  274. if (g_TypeSt == NULL) {
  275. //
  276. // sorry, log is closed
  277. //
  278. return OD_UNDEFINED;
  279. }
  280. if (-1 != pSetupStringTableLookUpStringEx (
  281. g_TypeSt,
  282. (PSTR)Type, // remove const, however string will not be modified
  283. STRTAB_CASE_INSENSITIVE,
  284. &OutDest,
  285. sizeof (OutDest)
  286. )) {
  287. #ifdef DEBUG
  288. if (g_DoLog) {
  289. OutDest |= OD_DEBUGLOG;
  290. }
  291. #endif
  292. return OutDest;
  293. }
  294. return OD_UNDEFINED;
  295. }
  296. OUTPUTDEST
  297. pGetTypeOutputDest (
  298. IN PCSTR Type
  299. )
  300. /*++
  301. Routine Description:
  302. pGetTypeOutputDest returns the default output
  303. destination for the specified type.
  304. Arguments:
  305. Type - Specifies the type
  306. Return Value:
  307. Any combination of enum OUTPUT_DESTINATION values associated with
  308. the given type.
  309. --*/
  310. {
  311. OUTPUTDEST OutDest;
  312. //
  313. // first check for ALL
  314. //
  315. if (!OUT_UNDEFINED (g_OutDestAll)) {
  316. OutDest = g_OutDestAll;
  317. } else {
  318. //
  319. // otherwise try to get it from the table
  320. //
  321. OutDest = pGetTypeOutputDestFromTable (Type);
  322. if (OUT_UNDEFINED (OutDest)) {
  323. //
  324. // just return the default
  325. //
  326. OutDest = g_OutDestDefault;
  327. }
  328. }
  329. #ifdef DEBUG
  330. if (g_DoLog) {
  331. OutDest |= OD_DEBUGLOG;
  332. }
  333. #endif
  334. return OutDest;
  335. }
  336. BOOL
  337. pIsPopupEnabled (
  338. IN PCSTR Type
  339. )
  340. /*++
  341. Routine Description:
  342. pIsPopupEnabled decides if the type should produce a popup output. The user may
  343. disable popup display for a type.
  344. Arguments:
  345. Type - Specifies the type
  346. Return Value:
  347. TRUE if the type should display a popup message.
  348. --*/
  349. {
  350. OUTPUTDEST OutDest;
  351. //
  352. // first check if any specific output is available for this type,
  353. // and if so, check if the OUT_POPUP_CANCEL flag is not set
  354. //
  355. if (g_SuppressAllPopups) {
  356. return FALSE;
  357. }
  358. OutDest = pGetTypeOutputDestFromTable (Type);
  359. if (OUT_POPUP_CANCEL (OutDest)) {
  360. return FALSE;
  361. }
  362. // just return the popup type of ALL of DefaultOverride
  363. return OUT_POPUP (pGetTypeOutputDest (Type));
  364. }
  365. LOGSEVERITY
  366. pGetSeverityFromType (
  367. IN PCSTR Type
  368. )
  369. /*++
  370. Routine Description:
  371. pGetSeverityFromType converts a type to a default severity
  372. that will be used by the debug log system.
  373. Arguments:
  374. Type - Specifies the type
  375. Return Value:
  376. The default log severity associated with the given type; if the specified
  377. type is not found, it returns LogSevInformation.
  378. --*/
  379. {
  380. if (OUT_ERROR (pGetTypeOutputDest (Type))) {
  381. return LogSevError;
  382. }
  383. return LogSevInformation;
  384. }
  385. BOOL
  386. pTableAddType (
  387. IN PCSTR Type,
  388. IN OUTPUTDEST OutDest
  389. )
  390. /*++
  391. Routine Description:
  392. pTableAddType adds a <Type, OutDest> association
  393. to the table g_TypeSt. If an association of Type already exists,
  394. it is modified to reflect the new association.
  395. Arguments:
  396. Type - Specifies the log/debug type string
  397. OutDest - Specifies what new destination(s) are associated with the type
  398. Return Value:
  399. TRUE if the association was successful and the Type is now in the table
  400. --*/
  401. {
  402. PRIVATE_ASSERT (g_TypeSt != NULL);
  403. return -1 != pSetupStringTableAddStringEx(
  404. g_TypeSt,
  405. (PSTR)Type, // remove const, however string will not be modified
  406. STRTAB_CASE_INSENSITIVE | STRTAB_NEW_EXTRADATA,
  407. &OutDest,
  408. sizeof(OutDest)
  409. );
  410. }
  411. OUTPUTDEST
  412. pGetAttributes (
  413. IN OUT PINFCONTEXT InfContext
  414. )
  415. /*++
  416. Routine Description:
  417. pGetAttributes converts the text values associated with the key on
  418. the line specified by the given context. If multiple values are
  419. specified, the corresponding OUTPUT_DESTINATION values are ORed together
  420. in the return value.
  421. Arguments:
  422. InfContext - Specifies the DEBUG.INF context of the key whose values
  423. are being converted and receives the updated context
  424. after this processing is done
  425. Return Value:
  426. Any combination of enum OUTPUT_DESTINATION values associated with
  427. the given key.
  428. --*/
  429. {
  430. OUTPUTDEST OutDest = OD_UNDEFINED;
  431. CHAR Value[OUTPUT_BUFSIZE_SMALL];
  432. INT Field;
  433. for(Field = SetupGetFieldCount (InfContext); Field > 0; Field--) {
  434. if (SetupGetStringFieldA (
  435. InfContext,
  436. Field,
  437. Value,
  438. OUTPUT_BUFSIZE_SMALL,
  439. NULL
  440. )) {
  441. OutDest |= pConvertToOutputType(Value);
  442. }
  443. }
  444. return OutDest;
  445. }
  446. BOOL
  447. pGetUserPreferences (
  448. IN HINF Inf
  449. )
  450. /*++
  451. Routine Description:
  452. pGetUserPreferences converts user's options specified in the given Inf file
  453. (usually DEBUG.INF) and stores them in g_TypeSt table. If <All> and
  454. <DefaultOverride> entries are found, their values are stored in OutputTypeAll
  455. and OutputTypeDefault, respectivelly, if not NULL.
  456. Arguments:
  457. Inf - Specifies the open inf file hanlde to process
  458. OutputTypeAll - Receives the Output Dest for the special <All> entry
  459. OutputTypeDefault - Receives the Output Dest for the special <DefaultOverride> entry
  460. Return Value:
  461. TRUE if the processing of the INF file was OK.
  462. --*/
  463. {
  464. INFCONTEXT InfContext;
  465. OUTPUTDEST OutDest;
  466. CHAR Key[OUTPUT_BUFSIZE_SMALL];
  467. if (SetupFindFirstLineA (Inf, DEBUG_SECTION, NULL, &InfContext)) {
  468. do {
  469. // check to see if this key is not interesting
  470. if (!SetupGetStringFieldA (
  471. &InfContext,
  472. 0,
  473. Key,
  474. OUTPUT_BUFSIZE_SMALL,
  475. NULL
  476. )) {
  477. continue;
  478. }
  479. if (pIgnoreKey (Key)) {
  480. continue;
  481. }
  482. // check for special cases
  483. if (StringIMatchA (Key, ENTRY_ALL)) {
  484. g_OutDestAll = pGetAttributes (&InfContext);
  485. // no reason to continue since ALL types will take this setting...
  486. break;
  487. } else {
  488. if (StringIMatchA (Key, ENTRY_DEFAULTOVERRIDE)) {
  489. g_OutDestDefault = pGetAttributes(&InfContext);
  490. } else {
  491. OutDest = pGetAttributes(&InfContext);
  492. // lines like <Type>= or like <Type>=<not a keyword(s)> are ignored
  493. if (!OUT_UNDEFINED (OutDest)) {
  494. if (!pTableAddType (Key, OutDest)) {
  495. return FALSE;
  496. }
  497. }
  498. }
  499. }
  500. } while (SetupFindNextLine (&InfContext, &InfContext));
  501. }
  502. return TRUE;
  503. }
  504. /*++
  505. Routine Description:
  506. pPadTitleA and pPadTitleW append to Title a specified number of spaces.
  507. Arguments:
  508. Title - Specifies the title (it will appear on the left column).
  509. The buffer must be large enough to hold the additional spaces
  510. Indent - Specifies the indent of the message body. If necessary,
  511. spaces will be appended to the Title to get to Indent column.
  512. Return Value:
  513. none
  514. --*/
  515. VOID
  516. pPadTitleA (
  517. IN OUT PSTR Title,
  518. IN INT Indent
  519. )
  520. {
  521. INT i;
  522. PSTR p;
  523. for (i = ByteCountA (Title), p = GetEndOfStringA (Title); i < Indent; i++) {
  524. *p++ = ' ';
  525. }
  526. *p = 0;
  527. }
  528. VOID
  529. pPadTitleW (
  530. IN OUT PWSTR Title,
  531. IN INT Indent
  532. )
  533. {
  534. INT i;
  535. PWSTR p;
  536. for (i = TcharCountW (Title), p = GetEndOfStringW (Title); i < Indent; i++) {
  537. *p++ = L' ';
  538. }
  539. *p = 0;
  540. }
  541. /*++
  542. Routine Description:
  543. pFindNextLineA and pFindNextLineW return the position where
  544. the next line begins
  545. Arguments:
  546. Line - Specifies the current line
  547. Indent - Specifies the indent of the message body. The next line
  548. will start preferably after a newline or a white space,
  549. but no further than the last column, which is
  550. SCREEN_WIDTH - Indent.
  551. Return Value:
  552. The position of the first character on the next line.
  553. --*/
  554. PCSTR
  555. pFindNextLineA (
  556. IN PCSTR Line,
  557. IN INT Indent,
  558. IN PBOOL TrimLeadingSpace
  559. )
  560. {
  561. INT Col = 0;
  562. INT MaxCol = SCREEN_WIDTH - 1 - Indent;
  563. PCSTR LastSpace = NULL;
  564. PCSTR PrevLine = Line;
  565. UINT ch;
  566. *TrimLeadingSpace = FALSE;
  567. while ( (ch = _mbsnextc (Line)) != 0 && Col < MaxCol) {
  568. if (ch == '\n') {
  569. LastSpace = Line;
  570. break;
  571. }
  572. if (ch > 255) {
  573. LastSpace = Line;
  574. Col++;
  575. } else {
  576. if (_ismbcspace (ch)) {
  577. LastSpace = Line;
  578. }
  579. }
  580. Col++;
  581. PrevLine = Line;
  582. Line = _mbsinc (Line);
  583. }
  584. if (ch == 0) {
  585. return Line;
  586. }
  587. if (LastSpace == NULL) {
  588. // we must cut this even if no white space or 2-byte char was found
  589. LastSpace = PrevLine;
  590. }
  591. if (ch != '\n') {
  592. *TrimLeadingSpace = TRUE;
  593. }
  594. return _mbsinc (LastSpace);
  595. }
  596. PCWSTR
  597. pFindNextLineW (
  598. IN PCWSTR Line,
  599. IN INT Indent,
  600. IN PBOOL TrimLeadingSpace
  601. )
  602. {
  603. INT Col = 0;
  604. INT MaxCol = SCREEN_WIDTH - 1 - Indent;
  605. PCWSTR LastSpace = NULL;
  606. PCWSTR PrevLine = Line;
  607. WCHAR ch;
  608. *TrimLeadingSpace = FALSE;
  609. while ( (ch = *Line) != 0 && Col < MaxCol) {
  610. if (ch == L'\n') {
  611. LastSpace = Line;
  612. break;
  613. }
  614. if (ch > 255) {
  615. LastSpace = Line;
  616. } else {
  617. if (iswspace (ch)) {
  618. LastSpace = Line;
  619. }
  620. }
  621. Col++;
  622. PrevLine = Line;
  623. Line++;
  624. }
  625. if (ch == 0) {
  626. return Line;
  627. }
  628. if (LastSpace == NULL) {
  629. // we must cut this even if no white space was found
  630. LastSpace = PrevLine;
  631. }
  632. if (ch != L'\n') {
  633. *TrimLeadingSpace = TRUE;
  634. }
  635. return LastSpace + 1;
  636. }
  637. /*++
  638. Routine Description:
  639. pHangingIndentA and pHangingIndentW break in lines and indent
  640. the text in Buffer, which is no larger than Size.
  641. Arguments:
  642. Buffer - Specifies the buffer containing text to format. The resulting
  643. text will be put in the same buffer
  644. Size - Specifies the size of this buffer, in bytes
  645. Indent - Specifies the indent to be used by all new generated lines.
  646. Return Value:
  647. none
  648. --*/
  649. VOID
  650. pHangingIndentA (
  651. IN OUT PSTR Buffer,
  652. IN DWORD Size,
  653. IN INT Indent
  654. )
  655. {
  656. CHAR IndentBuffer[OUTPUT_BUFSIZE_LARGE];
  657. PCSTR NextLine;
  658. PCSTR s;
  659. PSTR d;
  660. INT i;
  661. BOOL TrimLeadingSpace;
  662. PCSTR EndOfBuf;
  663. BOOL AppendNewLine = FALSE;
  664. NextLine = Buffer;
  665. s = Buffer;
  666. d = IndentBuffer;
  667. EndOfBuf = END_OF_BUFFER(IndentBuffer) - 3;
  668. while (*s && d < EndOfBuf) {
  669. //
  670. // Find end of next line
  671. //
  672. NextLine = (PSTR)pFindNextLineA (s, Indent, &TrimLeadingSpace);
  673. //
  674. // Copy one line from source to dest
  675. //
  676. while (s < NextLine && d < EndOfBuf) {
  677. switch (*s) {
  678. case '\r':
  679. s++;
  680. if (*s == '\r') {
  681. continue;
  682. } else if (*s != '\n') {
  683. s--;
  684. }
  685. // fall through
  686. case '\n':
  687. *d++ = '\r';
  688. *d++ = '\n';
  689. s++;
  690. break;
  691. default:
  692. if (isleadbyte (*s)) {
  693. *d++ = *s++;
  694. }
  695. *d++ = *s++;
  696. break;
  697. }
  698. }
  699. //
  700. // Trim leading space if necessary
  701. //
  702. if (TrimLeadingSpace) {
  703. while (*s == ' ') {
  704. s++;
  705. }
  706. }
  707. if (*s) {
  708. //
  709. // If another line, prepare an indent and insert a new line
  710. // after this multiline message
  711. //
  712. AppendNewLine = TRUE;
  713. if (d < EndOfBuf && TrimLeadingSpace) {
  714. *d++ = L'\r';
  715. *d++ = L'\n';
  716. }
  717. for (i = 0 ; i < Indent && d < EndOfBuf ; i++) {
  718. *d++ = ' ';
  719. }
  720. }
  721. }
  722. if (AppendNewLine && d < EndOfBuf) {
  723. *d++ = L'\r';
  724. *d++ = L'\n';
  725. }
  726. // make sure the string is zero-terminated
  727. PRIVATE_ASSERT (d <= END_OF_BUFFER(IndentBuffer));
  728. *d = 0;
  729. // copy the result to output buffer
  730. StringCopyByteCountA (Buffer, IndentBuffer, Size);
  731. }
  732. VOID
  733. pHangingIndentW (
  734. IN OUT PWSTR Buffer,
  735. IN DWORD Size,
  736. IN INT Indent
  737. )
  738. {
  739. WCHAR IndentBuffer[OUTPUT_BUFSIZE_LARGE];
  740. PCWSTR NextLine;
  741. PCWSTR s;
  742. PWSTR d;
  743. INT i;
  744. BOOL TrimLeadingSpace;
  745. PCWSTR EndOfBuf;
  746. BOOL AppendNewLine = FALSE;
  747. NextLine = Buffer;
  748. s = Buffer;
  749. d = IndentBuffer;
  750. EndOfBuf = END_OF_BUFFER(IndentBuffer) - 1;
  751. while (*s && d < EndOfBuf) {
  752. //
  753. // Find end of next line
  754. //
  755. NextLine = (PWSTR)pFindNextLineW (s, Indent, &TrimLeadingSpace);
  756. //
  757. // Copy one line from source to dest
  758. //
  759. while (s < NextLine && d < EndOfBuf) {
  760. switch (*s) {
  761. case L'\r':
  762. s++;
  763. if (*s == L'\r') {
  764. continue;
  765. } else if (*s != L'\n') {
  766. s--;
  767. }
  768. // fall through
  769. case L'\n':
  770. *d++ = L'\r';
  771. *d++ = L'\n';
  772. s++;
  773. break;
  774. default:
  775. *d++ = *s++;
  776. break;
  777. }
  778. }
  779. //
  780. // Trim leading space if necessary
  781. //
  782. if (TrimLeadingSpace) {
  783. while (*s == L' ') {
  784. s++;
  785. }
  786. }
  787. if (*s) {
  788. //
  789. // If another line, prepare an indent and insert a new line
  790. // after this multiline message
  791. //
  792. AppendNewLine = TRUE;
  793. if (d < EndOfBuf && TrimLeadingSpace) {
  794. *d++ = L'\r';
  795. *d++ = L'\n';
  796. }
  797. for (i = 0 ; i < Indent && d < EndOfBuf ; i++) {
  798. *d++ = L' ';
  799. }
  800. }
  801. }
  802. if (AppendNewLine && d < EndOfBuf) {
  803. *d++ = L'\r';
  804. *d++ = L'\n';
  805. }
  806. // make sure the string is zero-terminated
  807. PRIVATE_ASSERT (d <= END_OF_BUFFER(IndentBuffer));
  808. *d = 0;
  809. // copy the result to output buffer
  810. StringCopyTcharCountW (Buffer, IndentBuffer, Size);
  811. }
  812. /*++
  813. Routine Description:
  814. pAppendLastErrorA and pAppendLastErrorW append the specified error code
  815. to the Message and writes the output to the MsgWithErr buffer.
  816. Arguments:
  817. MsgWithErr - Receives the formatted message. This buffer
  818. is supplied by caller
  819. BufferSize - Specifies the size of the buffer, in bytes
  820. Message - Specifies the body of the message
  821. LastError - Specifies the error code that will be appended
  822. Return Value:
  823. none
  824. --*/
  825. VOID
  826. pAppendLastErrorA (
  827. OUT PSTR MsgWithErr,
  828. IN DWORD BufferSize,
  829. IN PCSTR Message,
  830. IN LONG LastError
  831. )
  832. {
  833. PSTR Append;
  834. DWORD ErrMsgLen;
  835. StringCopyByteCountA (MsgWithErr, Message, BufferSize);
  836. Append = GetEndOfStringA (MsgWithErr);
  837. ErrMsgLen = MsgWithErr + BufferSize - Append;
  838. if (ErrMsgLen > 0) {
  839. if (LastError < 10) {
  840. _snprintf (Append, ErrMsgLen, " [ERROR=%lu]", LastError);
  841. } else {
  842. _snprintf (Append, ErrMsgLen, " [ERROR=%lu (%lXh)]", LastError, LastError);
  843. }
  844. }
  845. }
  846. VOID
  847. pAppendLastErrorW (
  848. OUT PWSTR MsgWithErr,
  849. IN DWORD BufferSize,
  850. IN PCWSTR Message,
  851. IN LONG LastError
  852. )
  853. {
  854. PWSTR Append;
  855. DWORD ErrMsgLen;
  856. StringCopyTcharCountW (MsgWithErr, Message, BufferSize / sizeof(WCHAR));
  857. Append = GetEndOfStringW (MsgWithErr);
  858. ErrMsgLen = MsgWithErr + (BufferSize / sizeof(WCHAR)) - Append;
  859. if (ErrMsgLen > 0) {
  860. if (LastError < 10) {
  861. _snwprintf (Append, ErrMsgLen, L" [ERROR=%lu]", LastError);
  862. } else {
  863. _snwprintf (Append, ErrMsgLen, L" [ERROR=%lu (%lXh)]", LastError, LastError);
  864. }
  865. }
  866. }
  867. /*++
  868. Routine Description:
  869. pIndentMessageA and pIndentMessageW format the specified message
  870. with the type in the left column and body of the message in the right.
  871. Arguments:
  872. FormattedMsg - Receives the formatted message. This buffer
  873. is supplied by caller
  874. BufferSize - Specifies the size of the buffer
  875. Type - Specifies the type of the message
  876. Body - Specifies the body of the message
  877. Indent - Specifies the column to indent to
  878. LastError - Specifies the last error code if different than ERROR_SUCCESS;
  879. in this case it will be appended to the message
  880. Return Value:
  881. none
  882. --*/
  883. VOID
  884. pIndentMessageA (
  885. OUT PSTR FormattedMsg,
  886. IN DWORD BufferSize,
  887. IN PCSTR Type,
  888. IN PCSTR Body,
  889. IN INT Indent,
  890. IN LONG LastError
  891. )
  892. {
  893. CHAR BodyWithErr[OUTPUT_BUFSIZE_LARGE];
  894. PCSTR MyMsgBody;
  895. PSTR Current;
  896. DWORD Remaining;
  897. MyMsgBody = Body;
  898. Remaining = BufferSize - Indent;
  899. if (LastError != ERROR_SUCCESS) {
  900. MyMsgBody = BodyWithErr;
  901. pAppendLastErrorA (BodyWithErr, sizeof (BodyWithErr), Body, LastError);
  902. }
  903. StringCopyByteCountA (FormattedMsg, Type, MAX_MSGTITLE_LEN);
  904. pPadTitleA (FormattedMsg, Indent);
  905. Current = FormattedMsg + Indent;
  906. StringCopyByteCountA (Current, MyMsgBody, Remaining);
  907. pHangingIndentA (Current, Remaining, Indent);
  908. // append a new line if space left
  909. Current = GetEndOfStringA (Current);
  910. if (Current + NEWLINE_CHAR_COUNT + 1 < FormattedMsg + BufferSize) {
  911. *Current++ = '\r';
  912. *Current++ = '\n';
  913. *Current = 0;
  914. }
  915. }
  916. VOID
  917. pIndentMessageW (
  918. OUT PWSTR FormattedMsg,
  919. IN DWORD BufferSize,
  920. IN PCSTR Type,
  921. IN PCWSTR Body,
  922. IN INT Indent,
  923. IN LONG LastError
  924. )
  925. {
  926. WCHAR TypeW[OUTPUT_BUFSIZE_SMALL];
  927. WCHAR BodyWithErr[OUTPUT_BUFSIZE_LARGE];
  928. PCWSTR MyMsgBody;
  929. PWSTR Current;
  930. DWORD Remaining;
  931. MyMsgBody = Body;
  932. Remaining = BufferSize - Indent;
  933. if (LastError != ERROR_SUCCESS) {
  934. MyMsgBody = BodyWithErr;
  935. pAppendLastErrorW (BodyWithErr, sizeof (BodyWithErr), Body, LastError);
  936. }
  937. KnownSizeAtoW (TypeW, Type);
  938. StringCopyTcharCountW (FormattedMsg, TypeW, MAX_MSGTITLE_LEN);
  939. pPadTitleW (FormattedMsg, Indent);
  940. Current = FormattedMsg + Indent;
  941. StringCopyTcharCountW (Current, MyMsgBody, Remaining);
  942. pHangingIndentW (Current, Remaining, Indent);
  943. // append a new line if space left
  944. Current = GetEndOfStringW (Current);
  945. if (Current + NEWLINE_CHAR_COUNT + 1 < FormattedMsg + BufferSize) {
  946. *Current++ = L'\r';
  947. *Current++ = L'\n';
  948. *Current = 0;
  949. }
  950. }
  951. /*++
  952. Routine Description:
  953. pWriteToSetupLogA and pWriteToSetupLogW log the specified message
  954. to the setup log using Setup API functions.
  955. Arguments:
  956. Severity - Specifies the severity of the message, as defined by the Setup API
  957. FormattedMsg - Specifies the message
  958. Return Value:
  959. none
  960. --*/
  961. VOID
  962. pWriteToSetupLogA (
  963. IN LOGSEVERITY Severity,
  964. IN PCSTR FormattedMsg
  965. )
  966. {
  967. if (!SetupOpenLog (FALSE)) {
  968. PRIVATE_ASSERT (FALSE);
  969. return;
  970. }
  971. if (!SetupLogErrorA (FormattedMsg, Severity)) {
  972. PRIVATE_ASSERT (FALSE);
  973. }
  974. SetupCloseLog();
  975. }
  976. VOID
  977. pWriteToSetupLogW (
  978. IN LOGSEVERITY Severity,
  979. IN PCWSTR FormattedMsg
  980. )
  981. {
  982. if (!SetupOpenLog (FALSE)) {
  983. PRIVATE_ASSERT (FALSE);
  984. return;
  985. }
  986. if (!SetupLogErrorW (FormattedMsg, Severity)) {
  987. PRIVATE_ASSERT (FALSE);
  988. }
  989. SetupCloseLog();
  990. }
  991. /*++
  992. Routine Description:
  993. pDisplayPopupA and pDisplayPopupW displays the specified message to
  994. a popup window, if <g_LogPopupParentWnd> is not NULL (attended mode).
  995. Arguments:
  996. Type - Specifies the type of the message, displayed as the popup's title
  997. Msg - Specifies the message
  998. LastError - Specifies the last error; it will be printed if != ERROR_SUCCESS
  999. Forced - Specifies TRUE to force the popup, even in unattended mode
  1000. Return Value:
  1001. none
  1002. --*/
  1003. VOID
  1004. pDisplayPopupA (
  1005. IN PCSTR Type,
  1006. IN PCSTR Msg,
  1007. IN LONG LastError,
  1008. IN BOOL Forced
  1009. )
  1010. {
  1011. #ifdef DEBUG
  1012. CHAR FormattedMsg[OUTPUT_BUFSIZE_LARGE];
  1013. CHAR Buffer[OUTPUT_BUFSIZE_SMALL];
  1014. PSTR Current = Buffer;
  1015. #endif
  1016. UINT MBStyle;
  1017. LONG rc;
  1018. OUTPUTDEST OutDest;
  1019. HWND ParentWnd;
  1020. PCSTR DisplayMessage = Msg;
  1021. LOGSEVERITY Severity = pGetSeverityFromType (Type);
  1022. OutDest = pGetTypeOutputDest (Type);
  1023. if (g_LogPopupParentWnd || Forced) {
  1024. #ifdef DEBUG
  1025. if (LastError != ERROR_SUCCESS) {
  1026. if (LastError < 10) {
  1027. Current += wsprintfA (Buffer, " [ERROR=%u]", LastError);
  1028. } else {
  1029. Current += wsprintfA (Buffer, " [ERROR=%u (%Xh)]", LastError, LastError);
  1030. }
  1031. }
  1032. if (OUT_ASSERT (OutDest)) {
  1033. Current += wsprintfA (
  1034. Current,
  1035. "\n\nBreak now? (Hit Yes to break, No to continue, or Cancel to disable '%s' message boxes)",
  1036. Type
  1037. );
  1038. } else {
  1039. Current += wsprintfA (
  1040. Current,
  1041. "\n\n(Hit Cancel to disable '%s' message boxes)",
  1042. Type
  1043. );
  1044. }
  1045. if (Current > Buffer) {
  1046. //
  1047. // the displayed message should be modified to include additional info
  1048. //
  1049. DisplayMessage = FormattedMsg;
  1050. StringCopyByteCountA (
  1051. FormattedMsg,
  1052. Msg,
  1053. sizeof (FormattedMsg) / sizeof (CHAR) - (Current - Buffer)
  1054. );
  1055. StringCatA (FormattedMsg, Buffer);
  1056. }
  1057. #endif
  1058. switch (Severity) {
  1059. case LOGSEV_FATAL_ERROR:
  1060. MBStyle = MB_ICONSTOP;
  1061. break;
  1062. case LOGSEV_ERROR:
  1063. MBStyle = MB_ICONERROR;
  1064. break;
  1065. case LOGSEV_WARNING:
  1066. MBStyle = MB_ICONEXCLAMATION;
  1067. break;
  1068. default:
  1069. MBStyle = MB_ICONINFORMATION;
  1070. }
  1071. MBStyle |= MB_SETFOREGROUND;
  1072. #ifdef DEBUG
  1073. if (OUT_ASSERT (OutDest)) {
  1074. MBStyle |= MB_YESNOCANCEL|MB_DEFBUTTON2;
  1075. } else {
  1076. MBStyle |= MB_OKCANCEL;
  1077. }
  1078. #else
  1079. MBStyle |= MB_OK;
  1080. #endif
  1081. //
  1082. // check current thread id; if different than thread that initialized
  1083. // parent window handle, set parent to NULL
  1084. //
  1085. if (GetCurrentThreadId () == g_InitThreadId) {
  1086. ParentWnd = g_LogPopupParentWnd;
  1087. } else {
  1088. ParentWnd = NULL;
  1089. }
  1090. rc = MessageBoxA (ParentWnd, DisplayMessage, Type, MBStyle);
  1091. #ifdef DEBUG
  1092. if (rc == IDCANCEL) {
  1093. //
  1094. // cancel this type of messages
  1095. //
  1096. pTableAddType (Type, OutDest | OD_POPUP_CANCEL);
  1097. } else if (rc == IDYES) {
  1098. //
  1099. // If Yes was clicked, call DebugBreak to get assert behavoir
  1100. //
  1101. DebugBreak();
  1102. }
  1103. #endif
  1104. }
  1105. }
  1106. VOID
  1107. pDisplayPopupW (
  1108. IN PCSTR Type,
  1109. IN PWSTR Msg,
  1110. IN LONG LastError,
  1111. IN BOOL Forced
  1112. )
  1113. {
  1114. PCSTR MsgA;
  1115. //
  1116. // call the ANSI version because wsprintfW is not properly implemented on Win9x
  1117. //
  1118. MsgA = ConvertWtoA (Msg);
  1119. pDisplayPopupA (Type, MsgA, LastError, Forced);
  1120. FreeConvertedStr (MsgA);
  1121. }
  1122. /*++
  1123. Routine Description:
  1124. pRawWriteLogOutputA and pRawWriteLogOutputW output specified message
  1125. to all character devices implied by the type. The message is not
  1126. formatted in any way
  1127. Arguments:
  1128. Type - Specifies the type of the message, displayed as the popup's title
  1129. Msg - Specifies the message
  1130. Return Value:
  1131. none
  1132. --*/
  1133. VOID
  1134. pRawWriteLogOutputA (
  1135. IN PCSTR Type,
  1136. IN PCSTR Message,
  1137. IN PCSTR FormattedMsg
  1138. )
  1139. {
  1140. OUTPUTDEST OutDest;
  1141. HANDLE Handle;
  1142. LONG LastError;
  1143. CHAR BodyWithErr[OUTPUT_BUFSIZE_LARGE];
  1144. PCSTR LogMessage;
  1145. OutDest = pGetTypeOutputDest (Type);
  1146. if (OUT_NO_OUTPUT (OutDest)) {
  1147. return;
  1148. }
  1149. if (OUT_LOGFILE (OutDest)) {
  1150. //
  1151. // determine the severity of the message
  1152. //
  1153. if (OUT_ERROR (OutDest)) {
  1154. if (Message) {
  1155. LogMessage = Message;
  1156. LastError = GetLastError ();
  1157. if (LastError != ERROR_SUCCESS) {
  1158. pAppendLastErrorA (BodyWithErr, sizeof (BodyWithErr), Message, LastError);
  1159. LogMessage = BodyWithErr;
  1160. }
  1161. pWriteToSetupLogA (LOGSEV_INFORMATION, "Error:\r\n");
  1162. pWriteToSetupLogA (LOGSEV_ERROR, LogMessage);
  1163. pWriteToSetupLogA (LOGSEV_INFORMATION, "\r\n\r\n");
  1164. } else {
  1165. PRIVATE_ASSERT (FALSE);
  1166. }
  1167. } else {
  1168. pWriteToSetupLogA (LOGSEV_INFORMATION, FormattedMsg);
  1169. }
  1170. }
  1171. //
  1172. // log to each specified device
  1173. //
  1174. if (OUT_DEBUGGER(OutDest)) {
  1175. OutputDebugStringA (FormattedMsg);
  1176. }
  1177. if (OUT_CONSOLE(OutDest)) {
  1178. fprintf (stderr, "%s", FormattedMsg);
  1179. }
  1180. #ifdef DEBUG
  1181. if (OUT_DEBUGLOG (OutDest)) {
  1182. Handle = CreateFileA (
  1183. g_DebugLogPathA,
  1184. GENERIC_WRITE,
  1185. FILE_SHARE_READ,
  1186. NULL,
  1187. OPEN_ALWAYS,
  1188. FILE_ATTRIBUTE_NORMAL,
  1189. NULL
  1190. );
  1191. if (Handle != INVALID_HANDLE_VALUE) {
  1192. SetFilePointer (Handle, 0, NULL, FILE_END);
  1193. WriteFileStringA (Handle, FormattedMsg);
  1194. CloseHandle (Handle);
  1195. }
  1196. }
  1197. #endif
  1198. if (OUT_CONFIG (OutDest)) {
  1199. Handle = CreateFileA (
  1200. g_ConfigDmpPathBufA,
  1201. GENERIC_WRITE,
  1202. FILE_SHARE_READ,
  1203. NULL,
  1204. OPEN_ALWAYS,
  1205. FILE_ATTRIBUTE_NORMAL,
  1206. NULL
  1207. );
  1208. if (Handle != INVALID_HANDLE_VALUE) {
  1209. SetFilePointer (Handle, 0, NULL, FILE_END);
  1210. WriteFileStringA (Handle, FormattedMsg);
  1211. CloseHandle (Handle);
  1212. }
  1213. }
  1214. }
  1215. VOID
  1216. pRawWriteLogOutputW (
  1217. IN PCSTR Type,
  1218. IN PCWSTR Message,
  1219. IN PCWSTR FormattedMsg
  1220. )
  1221. {
  1222. OUTPUTDEST OutDest;
  1223. HANDLE Handle;
  1224. LONG LastError;
  1225. WCHAR BodyWithErr[OUTPUT_BUFSIZE_LARGE];
  1226. PCWSTR LogMessage;
  1227. OutDest = pGetTypeOutputDest (Type);
  1228. if (OUT_NO_OUTPUT (OutDest)) {
  1229. return;
  1230. }
  1231. if (OUT_LOGFILE (OutDest)) {
  1232. //
  1233. // determine the severity of the message
  1234. //
  1235. if (OUT_ERROR (OutDest)) {
  1236. if (Message) {
  1237. LogMessage = Message;
  1238. LastError = GetLastError ();
  1239. if (LastError != ERROR_SUCCESS) {
  1240. pAppendLastErrorW (BodyWithErr, sizeof (BodyWithErr), Message, LastError);
  1241. LogMessage = BodyWithErr;
  1242. }
  1243. pWriteToSetupLogW (LOGSEV_INFORMATION, L"Error:\r\n");
  1244. pWriteToSetupLogW (LOGSEV_ERROR, LogMessage);
  1245. pWriteToSetupLogW (LOGSEV_INFORMATION, L"\r\n\r\n");
  1246. } else {
  1247. PRIVATE_ASSERT (FALSE);
  1248. }
  1249. } else {
  1250. pWriteToSetupLogW (LOGSEV_INFORMATION, FormattedMsg);
  1251. }
  1252. }
  1253. //
  1254. // log to each specified device
  1255. //
  1256. if (OUT_DEBUGGER(OutDest)) {
  1257. OutputDebugStringW (FormattedMsg);
  1258. }
  1259. if (OUT_CONSOLE(OutDest)) {
  1260. fwprintf (stderr, L"%s", FormattedMsg);
  1261. }
  1262. #ifdef DEBUG
  1263. if (OUT_DEBUGLOG (OutDest)) {
  1264. Handle = CreateFileA (
  1265. g_DebugLogPathA,
  1266. GENERIC_WRITE,
  1267. FILE_SHARE_READ,
  1268. NULL,
  1269. OPEN_ALWAYS,
  1270. FILE_ATTRIBUTE_NORMAL,
  1271. NULL
  1272. );
  1273. if (Handle != INVALID_HANDLE_VALUE) {
  1274. SetFilePointer (Handle, 0, NULL, FILE_END);
  1275. WriteFileStringW (Handle, FormattedMsg);
  1276. CloseHandle (Handle);
  1277. }
  1278. }
  1279. #endif
  1280. if (OUT_CONFIG (OutDest)) {
  1281. Handle = CreateFileA (
  1282. g_ConfigDmpPathBufA,
  1283. GENERIC_WRITE,
  1284. FILE_SHARE_READ,
  1285. NULL,
  1286. OPEN_ALWAYS,
  1287. FILE_ATTRIBUTE_NORMAL,
  1288. NULL
  1289. );
  1290. if (Handle != INVALID_HANDLE_VALUE) {
  1291. SetFilePointer (Handle, 0, NULL, FILE_END);
  1292. WriteFileStringW (Handle, FormattedMsg);
  1293. CloseHandle (Handle);
  1294. }
  1295. }
  1296. }
  1297. /*++
  1298. Routine Description:
  1299. pFormatAndWriteMsgA and pFormatAndWriteMsgW format the message
  1300. specified by the Format argument and outputs it to all destinations
  1301. specified in OutDest. If no destination for the message,
  1302. no action is performed.
  1303. Arguments:
  1304. Type - Specifies the type (category) of the message
  1305. Format - Specifies either the message in ASCII format or
  1306. a message ID (if HIWORD(Format) == 0). The message
  1307. will be formatted using args.
  1308. args - Specifies a list of arguments to be used when formatting
  1309. the message. If a message ID is used for Format, args
  1310. is supposed to be an array of pointers to strings
  1311. Return Value:
  1312. none
  1313. --*/
  1314. VOID
  1315. pFormatAndWriteMsgA (
  1316. IN PCSTR Type,
  1317. IN PCSTR Format,
  1318. IN va_list args
  1319. )
  1320. {
  1321. CHAR Output[OUTPUT_BUFSIZE_LARGE];
  1322. CHAR FormattedMsg[OUTPUT_BUFSIZE_LARGE];
  1323. OUTPUTDEST OutDest;
  1324. LONG LastError;
  1325. // clear LOGTITLE flag on each regular LOG
  1326. g_HasTitle = FALSE;
  1327. OutDest = pGetTypeOutputDest (Type);
  1328. if (OUT_NO_OUTPUT(OutDest)) {
  1329. return;
  1330. }
  1331. if (OUT_ERROR (OutDest)) {
  1332. LastError = GetLastError();
  1333. } else {
  1334. LastError = ERROR_SUCCESS;
  1335. }
  1336. // format output string
  1337. if (HIWORD(Format) == 0) {
  1338. //
  1339. // this is actually a Resource String ID
  1340. //
  1341. if (!FormatMessageA (
  1342. FORMAT_MESSAGE_FROM_HMODULE,
  1343. (LPVOID) g_hInst,
  1344. (DWORD) Format,
  1345. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  1346. (LPVOID) Output,
  1347. OUTPUT_BUFSIZE_LARGE,
  1348. &args
  1349. )) {
  1350. // the string is missing from Resources
  1351. DEBUGMSG ((DBG_WHOOPS, "Log() called with invalid MsgID"));
  1352. return;
  1353. }
  1354. } else {
  1355. //
  1356. // format given string using printf style
  1357. //
  1358. _vsnprintf(Output, OUTPUT_BUFSIZE_LARGE, Format, args);
  1359. }
  1360. if (OUT_NOFORMAT (OutDest)) {
  1361. _tcssafecpy (FormattedMsg, Output, sizeof(FormattedMsg) - (NEWLINE_CHAR_COUNT + 1) * sizeof (CHAR));
  1362. StringCatA (FormattedMsg, S_NEWLINEA);
  1363. } else {
  1364. pIndentMessageA (
  1365. FormattedMsg,
  1366. OUTPUT_BUFSIZE_LARGE,
  1367. Type,
  1368. Output,
  1369. MSGBODY_INDENT,
  1370. LastError
  1371. );
  1372. }
  1373. pRawWriteLogOutputA (Type, Output, FormattedMsg);
  1374. if (pIsPopupEnabled (Type)) {
  1375. #ifdef DEBUG
  1376. if (MUST_BE_LOCALIZED (OutDest)) {
  1377. PRIVATE_ASSERT (HIWORD (Format) == 0);
  1378. }
  1379. pDisplayPopupA (Type, Output, LastError, OUT_FORCED_POPUP(OutDest));
  1380. #else
  1381. if (HIWORD (Format) == 0) {
  1382. pDisplayPopupA (Type, Output, LastError, OUT_FORCED_POPUP(OutDest));
  1383. }
  1384. #endif
  1385. }
  1386. }
  1387. VOID
  1388. pFormatAndWriteMsgW (
  1389. IN PCSTR Type,
  1390. IN PCSTR Format,
  1391. IN va_list args
  1392. )
  1393. {
  1394. WCHAR FormatW[OUTPUT_BUFSIZE_LARGE];
  1395. WCHAR Output[OUTPUT_BUFSIZE_LARGE];
  1396. WCHAR FormattedMsg[OUTPUT_BUFSIZE_LARGE];
  1397. OUTPUTDEST OutDest;
  1398. LONG LastError;
  1399. // clear LOGTITLE flag on each regular LOG
  1400. g_HasTitle = FALSE;
  1401. OutDest = pGetTypeOutputDest (Type);
  1402. if (OUT_NO_OUTPUT(OutDest)) {
  1403. return;
  1404. }
  1405. if (OUT_ERROR (OutDest)) {
  1406. LastError = GetLastError();
  1407. } else {
  1408. LastError = ERROR_SUCCESS;
  1409. }
  1410. // format output string
  1411. if (HIWORD(Format) == 0) {
  1412. //
  1413. // this is actually a Resource String ID
  1414. //
  1415. if (!FormatMessageW (
  1416. FORMAT_MESSAGE_FROM_HMODULE,
  1417. (LPVOID) g_hInst,
  1418. (DWORD) Format,
  1419. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  1420. (LPVOID) Output,
  1421. OUTPUT_BUFSIZE_LARGE,
  1422. &args
  1423. )) {
  1424. // the string is missing from Resources
  1425. DEBUGMSG ((DBG_WHOOPS, "Log() called with invalid MsgID"));
  1426. return;
  1427. }
  1428. } else {
  1429. KnownSizeAtoW (FormatW, Format);
  1430. //
  1431. // format given string using printf style
  1432. //
  1433. _vsnwprintf(Output, OUTPUT_BUFSIZE_LARGE, FormatW, args);
  1434. }
  1435. if (OUT_NOFORMAT (OutDest)) {
  1436. _wcssafecpy (FormattedMsg, Output, sizeof(FormattedMsg) - (NEWLINE_CHAR_COUNT + 1) * sizeof (WCHAR));
  1437. StringCatW (FormattedMsg, S_NEWLINEW);
  1438. } else {
  1439. pIndentMessageW (
  1440. FormattedMsg,
  1441. OUTPUT_BUFSIZE_LARGE,
  1442. Type,
  1443. Output,
  1444. MSGBODY_INDENT,
  1445. LastError
  1446. );
  1447. }
  1448. pRawWriteLogOutputW (Type, Output, FormattedMsg);
  1449. if (pIsPopupEnabled (Type)) {
  1450. #ifdef DEBUG
  1451. if (MUST_BE_LOCALIZED (OutDest)) {
  1452. PRIVATE_ASSERT (HIWORD (Format) == 0);
  1453. }
  1454. pDisplayPopupW (Type, Output, LastError, OUT_FORCED_POPUP(OutDest));
  1455. #else
  1456. if (HIWORD (Format) == 0) {
  1457. pDisplayPopupW (Type, Output, LastError, OUT_FORCED_POPUP(OutDest));
  1458. }
  1459. #endif
  1460. }
  1461. }
  1462. BOOL
  1463. pLogInit (
  1464. IN HWND *LogPopupParentWnd, OPTIONAL
  1465. OUT HWND *OrgPopupParentWnd, OPTIONAL
  1466. IN BOOL FirstTimeInit
  1467. )
  1468. /*++
  1469. Routine Description:
  1470. pLogInit actually initializes the log system.
  1471. Arguments:
  1472. LogPopupParentWnd - Specifies the parent window to be used by the
  1473. popups, or NULL if popups are to be suppressed.
  1474. This value is optional only if FirstTimeInit
  1475. is FALSE.
  1476. OrgPopupParentWnd - Receives the original parent window.
  1477. FirstTimeInit - Specifies TRUE for the first log initialization,
  1478. or FALSE for reinitialization
  1479. Return Value:
  1480. TRUE if log system successfully initialized
  1481. --*/
  1482. {
  1483. HINF Inf = INVALID_HANDLE_VALUE;
  1484. BOOL Result = FALSE;
  1485. PDEFAULT_DESTINATION Dest;
  1486. #ifdef DEBUG
  1487. CHAR TempPath[MAX_MBCHAR_PATH];
  1488. #endif
  1489. PRIVATE_ASSERT (!FirstTimeInit || LogPopupParentWnd);
  1490. __try {
  1491. if (FirstTimeInit) {
  1492. PRIVATE_ASSERT (!g_TypeSt);
  1493. g_TypeSt = pSetupStringTableInitializeEx(sizeof (OUTPUTDEST), 0);
  1494. if (!g_TypeSt) {
  1495. __leave;
  1496. }
  1497. Dest = g_DefaultDest;
  1498. while (Dest->Type) {
  1499. pTableAddType (Dest->Type, Dest->Flags);
  1500. Dest++;
  1501. }
  1502. if (!GetWindowsDirectoryA (g_ConfigDmpPathBufA, MAX_MBCHAR_PATH)) {
  1503. __leave;
  1504. }
  1505. StringCopyA (AppendWackA (g_ConfigDmpPathBufA), TEXT("config.dmp"));
  1506. #ifdef PROGRESS_BAR
  1507. PRIVATE_ASSERT (g_ProgressBarLog == INVALID_HANDLE_VALUE);
  1508. g_ProgressBarLog = CreateFile (
  1509. ISNT() ? TEXT("C:\\pbnt.txt") : TEXT("C:\\pb9x.txt"),
  1510. GENERIC_WRITE,
  1511. 0,
  1512. NULL,
  1513. OPEN_ALWAYS,
  1514. FILE_ATTRIBUTE_NORMAL,
  1515. NULL
  1516. );
  1517. if (g_ProgressBarLog != INVALID_HANDLE_VALUE) {
  1518. SetFilePointer (g_ProgressBarLog, 0, NULL, FILE_END);
  1519. }
  1520. #endif
  1521. }
  1522. if (g_ResetLog) {
  1523. SetFileAttributesA (g_ConfigDmpPathBufA, FILE_ATTRIBUTE_NORMAL);
  1524. DeleteFileA (g_ConfigDmpPathBufA);
  1525. }
  1526. #ifdef DEBUG
  1527. if (FirstTimeInit) {
  1528. if (ISPC98()) {
  1529. GetSystemDirectoryA (TempPath, ARRAYSIZE (TempPath));
  1530. // replace C with the actual sys drive letter
  1531. g_DebugNtLogPathBufA[0] = g_Debug9xLogPathBufA[0] = TempPath[0];
  1532. g_DebugInfPathBufA[0] = TempPath[0];
  1533. //
  1534. // only the first byte is important because drive letters are not double-byte chars
  1535. //
  1536. g_DebugInfPathBufW[0] = (WCHAR)TempPath[0];
  1537. }
  1538. //
  1539. // now get user's preferences
  1540. //
  1541. Inf = SetupOpenInfFileA (g_DebugInfPathBufA, NULL, INF_STYLE_WIN4 | INF_STYLE_OLDNT, NULL);
  1542. if (INVALID_HANDLE_VALUE != Inf && pGetUserPreferences(Inf)) {
  1543. g_DoLog = TRUE;
  1544. }
  1545. }
  1546. if (g_DebugLogPathA == NULL) {
  1547. g_DebugLogPathA = ISNT() ? g_DebugNtLogPathBufA : g_Debug9xLogPathBufA;
  1548. }
  1549. if (g_ResetLog) {
  1550. SetFileAttributesA (g_DebugLogPathA, FILE_ATTRIBUTE_NORMAL);
  1551. DeleteFileA (g_DebugLogPathA);
  1552. }
  1553. #endif
  1554. if (OrgPopupParentWnd) {
  1555. *OrgPopupParentWnd = g_LogPopupParentWnd;
  1556. }
  1557. if (LogPopupParentWnd) {
  1558. g_LogPopupParentWnd = *LogPopupParentWnd;
  1559. g_InitThreadId = GetCurrentThreadId ();
  1560. }
  1561. Result = TRUE;
  1562. }
  1563. __finally {
  1564. if (Inf != INVALID_HANDLE_VALUE) {
  1565. SetupCloseInfFile (Inf);
  1566. }
  1567. if (!Result) {
  1568. if (g_TypeSt) {
  1569. pSetupStringTableDestroy(g_TypeSt);
  1570. g_TypeSt = NULL;
  1571. }
  1572. g_OutDestAll = OD_UNDEFINED;
  1573. g_OutDestDefault = OD_UNDEFINED;
  1574. #ifdef DEBUG
  1575. g_DoLog = FALSE;
  1576. #endif
  1577. #ifdef PROGRESS_BAR
  1578. if (g_ProgressBarLog != INVALID_HANDLE_VALUE) {
  1579. CloseHandle (g_ProgressBarLog);
  1580. g_ProgressBarLog = INVALID_HANDLE_VALUE;
  1581. }
  1582. #endif
  1583. }
  1584. }
  1585. return Result;
  1586. }
  1587. BOOL
  1588. LogInit (
  1589. HWND Parent
  1590. )
  1591. /*++
  1592. Routine Description:
  1593. LogInit initializes the log system calling the worker pLogInit. This function
  1594. should be only called once
  1595. Arguments:
  1596. Parent - Specifies the initial parent window for all popups. If NULL,
  1597. the popups are suppressed. Callers can use LogReInit to change
  1598. the parent window handle at any time.
  1599. Return Value:
  1600. TRUE if log system successfully initialized
  1601. --*/
  1602. {
  1603. return pLogInit (&Parent, NULL, TRUE);
  1604. }
  1605. BOOL
  1606. LogReInit (
  1607. IN HWND *NewParent, OPTIONAL
  1608. OUT HWND *OrgParent OPTIONAL
  1609. )
  1610. /*++
  1611. Routine Description:
  1612. LogReInit re-initializes the log system calling the worker pLogInit.
  1613. This function may be called any number of times, but only after LogInit
  1614. Arguments:
  1615. NewParent - Specifies the new parent handle.
  1616. OrgParent - Receives the old parent handle.
  1617. Return Value:
  1618. TRUE if log system was successfully re-initialized
  1619. --*/
  1620. {
  1621. return pLogInit (NewParent, OrgParent, FALSE);
  1622. }
  1623. VOID
  1624. LogExit (
  1625. VOID
  1626. )
  1627. /*++
  1628. Routine Description:
  1629. LogExit cleans up any resources used by the log system
  1630. Arguments:
  1631. none
  1632. Return Value:
  1633. none
  1634. --*/
  1635. {
  1636. #ifdef DEBUG
  1637. if (g_DebugLogPathA) {
  1638. g_DebugLogPathA = NULL;
  1639. }
  1640. #endif
  1641. #ifdef PROGRESS_BAR
  1642. if (g_ProgressBarLog != INVALID_HANDLE_VALUE) {
  1643. CloseHandle (g_ProgressBarLog);
  1644. g_ProgressBarLog = INVALID_HANDLE_VALUE;
  1645. }
  1646. #endif
  1647. if (g_TypeSt) {
  1648. pSetupStringTableDestroy(g_TypeSt);
  1649. g_TypeSt = NULL;
  1650. }
  1651. g_OutDestAll = OD_UNDEFINED;
  1652. g_OutDestDefault = OD_UNDEFINED;
  1653. }
  1654. /*++
  1655. Routine Description:
  1656. LogA and LogW preserve the last error code; they call the helpers
  1657. pFormatAndWriteMsgA and pFormatAndWriteMsgW respectivelly.
  1658. Arguments:
  1659. Type - Specifies the type (category) of the message
  1660. Format - Specifies either the message in ASCII format or
  1661. a message ID (if HIWORD(Format) == 0). The message
  1662. will be formatted using args.
  1663. ... - Specifies a list of arguments to be used when formatting
  1664. the message. If a message ID is used for Format, args
  1665. is supposed to be an array of pointers to strings
  1666. Return Value:
  1667. none
  1668. --*/
  1669. VOID
  1670. _cdecl
  1671. LogA (
  1672. IN PCSTR Type,
  1673. IN PCSTR Format,
  1674. ...
  1675. )
  1676. {
  1677. va_list args;
  1678. PushError();
  1679. va_start (args, Format);
  1680. pFormatAndWriteMsgA (
  1681. Type,
  1682. Format,
  1683. args
  1684. );
  1685. va_end (args);
  1686. PopError();
  1687. }
  1688. VOID
  1689. _cdecl
  1690. LogW (
  1691. IN PCSTR Type,
  1692. IN PCSTR Format,
  1693. ...
  1694. )
  1695. {
  1696. va_list args;
  1697. PushError();
  1698. va_start (args, Format);
  1699. pFormatAndWriteMsgW (
  1700. Type,
  1701. Format,
  1702. args
  1703. );
  1704. va_end (args);
  1705. PopError();
  1706. }
  1707. VOID
  1708. _cdecl
  1709. LogIfA (
  1710. IN BOOL Condition,
  1711. IN PCSTR Type,
  1712. IN PCSTR Format,
  1713. ...
  1714. )
  1715. {
  1716. va_list args;
  1717. if (!Condition) {
  1718. return;
  1719. }
  1720. PushError();
  1721. va_start (args, Format);
  1722. pFormatAndWriteMsgA (
  1723. Type,
  1724. Format,
  1725. args
  1726. );
  1727. va_end (args);
  1728. PopError();
  1729. }
  1730. VOID
  1731. _cdecl
  1732. LogIfW (
  1733. IN BOOL Condition,
  1734. IN PCSTR Type,
  1735. IN PCSTR Format,
  1736. ...
  1737. )
  1738. {
  1739. va_list args;
  1740. if (!Condition) {
  1741. return;
  1742. }
  1743. PushError();
  1744. va_start (args, Format);
  1745. pFormatAndWriteMsgW (
  1746. Type,
  1747. Format,
  1748. args
  1749. );
  1750. va_end (args);
  1751. PopError();
  1752. }
  1753. VOID
  1754. LogTitleA (
  1755. IN PCSTR Type,
  1756. IN PCSTR Title OPTIONAL
  1757. )
  1758. {
  1759. CHAR FormattedMsg[OUTPUT_BUFSIZE_LARGE];
  1760. StringCopyByteCountA (g_LastType, Type, sizeof (g_LastType));
  1761. if (!Title) {
  1762. Title = Type;
  1763. }
  1764. StringCopyByteCountA (FormattedMsg, Title, sizeof (FormattedMsg) - sizeof (S_COLUMNDOUBLELINEA));
  1765. StringCatA (FormattedMsg, S_COLUMNDOUBLELINEA);
  1766. pRawWriteLogOutputA (Type, NULL, FormattedMsg);
  1767. //
  1768. // set LOGTITLE flag
  1769. //
  1770. g_HasTitle = TRUE;
  1771. }
  1772. VOID
  1773. LogTitleW (
  1774. IN PCSTR Type,
  1775. IN PCWSTR Title OPTIONAL
  1776. )
  1777. {
  1778. WCHAR FormattedMsg[OUTPUT_BUFSIZE_LARGE];
  1779. WCHAR TypeW[OUTPUT_BUFSIZE_SMALL];
  1780. StringCopyByteCountA (g_LastType, Type, sizeof (g_LastType));
  1781. if (!Title) {
  1782. KnownSizeAtoW (TypeW, Type);
  1783. Title = TypeW;
  1784. }
  1785. StringCopyByteCountW (FormattedMsg, Title, sizeof (FormattedMsg) - sizeof (S_COLUMNDOUBLELINEW));
  1786. StringCatW (FormattedMsg, S_COLUMNDOUBLELINEW);
  1787. pRawWriteLogOutputW (Type, NULL, FormattedMsg);
  1788. //
  1789. // set LOGTITLE flag
  1790. //
  1791. g_HasTitle = TRUE;
  1792. }
  1793. VOID
  1794. LogLineA (
  1795. IN PCSTR Line
  1796. )
  1797. {
  1798. CHAR Output[OUTPUT_BUFSIZE_LARGE];
  1799. BOOL HasNewLine = FALSE;
  1800. PCSTR p;
  1801. if (!Line) {
  1802. return;
  1803. }
  1804. if (!g_HasTitle) {
  1805. DEBUGMSG ((DBG_WHOOPS, "LOGTITLE missing before LOGLINE"));
  1806. return;
  1807. }
  1808. StringCopyByteCountA (Output, Line, sizeof (Output) - 4);
  1809. //
  1810. // find out if the line terminates with newline
  1811. //
  1812. for (p = _mbsstr (Output, S_NEWLINEA); p; p = _mbsstr (p + NEWLINE_CHAR_COUNT, S_NEWLINEA)) {
  1813. if (p[NEWLINE_CHAR_COUNT] == 0) {
  1814. //
  1815. // the line ends with a newline
  1816. //
  1817. HasNewLine = TRUE;
  1818. break;
  1819. }
  1820. }
  1821. if (!HasNewLine) {
  1822. StringCatA (Output, S_NEWLINEA);
  1823. }
  1824. pRawWriteLogOutputA (g_LastType, NULL, Output);
  1825. }
  1826. VOID
  1827. LogLineW (
  1828. IN PCWSTR Line
  1829. )
  1830. {
  1831. WCHAR Output[OUTPUT_BUFSIZE_LARGE];
  1832. BOOL HasNewLine = FALSE;
  1833. PCWSTR p;
  1834. if (!Line) {
  1835. return;
  1836. }
  1837. if (!g_HasTitle) {
  1838. DEBUGMSG ((DBG_WHOOPS, "LOGTITLE missing before LOGLINE"));
  1839. return;
  1840. }
  1841. StringCopyTcharCountW (Output, Line, sizeof (Output) / sizeof (WCHAR) - 4);
  1842. //
  1843. // find out if the line terminates with newline
  1844. //
  1845. for (p = wcsstr (Output, S_NEWLINEW); p; p = wcsstr (p + NEWLINE_CHAR_COUNT, S_NEWLINEW)) {
  1846. if (p[NEWLINE_CHAR_COUNT] == 0) {
  1847. //
  1848. // the line ends with a newline
  1849. //
  1850. HasNewLine = TRUE;
  1851. break;
  1852. }
  1853. }
  1854. if (!HasNewLine) {
  1855. StringCatW (Output, S_NEWLINEW);
  1856. }
  1857. pRawWriteLogOutputW (g_LastType, NULL, Output);
  1858. }
  1859. VOID
  1860. LogDirectA (
  1861. IN PCSTR Type,
  1862. IN PCSTR Text
  1863. )
  1864. {
  1865. g_HasTitle = FALSE;
  1866. pRawWriteLogOutputA (Type, NULL, Text);
  1867. }
  1868. VOID
  1869. LogDirectW (
  1870. IN PCSTR Type,
  1871. IN PCWSTR Text
  1872. )
  1873. {
  1874. g_HasTitle = FALSE;
  1875. pRawWriteLogOutputW (Type, NULL, Text);
  1876. }
  1877. VOID
  1878. SuppressAllLogPopups (
  1879. IN BOOL SuppressOn
  1880. )
  1881. {
  1882. g_SuppressAllPopups = SuppressOn;
  1883. }
  1884. #ifdef PROGRESS_BAR
  1885. VOID
  1886. _cdecl
  1887. LogTime (
  1888. IN PCSTR Format,
  1889. ...
  1890. )
  1891. {
  1892. static DWORD FirstTickCount = 0;
  1893. static DWORD LastTickCount = 0;
  1894. DWORD CurrentTickCount;
  1895. CHAR Msg[OUTPUT_BUFSIZE_LARGE];
  1896. PSTR AppendPos;
  1897. va_list args;
  1898. PushError();
  1899. CurrentTickCount = GetTickCount();
  1900. //
  1901. // If this is the first call save the tick count.
  1902. //
  1903. if (!FirstTickCount) {
  1904. FirstTickCount = CurrentTickCount;
  1905. LastTickCount = CurrentTickCount;
  1906. }
  1907. //
  1908. // Now, build the passed in string.
  1909. //
  1910. va_start (args, Format);
  1911. AppendPos = Msg + vsprintf (Msg, Format, args);
  1912. va_end (args);
  1913. sprintf (
  1914. AppendPos,
  1915. "\t%lu\t%lu\r\n",
  1916. CurrentTickCount - LastTickCount,
  1917. CurrentTickCount - FirstTickCount
  1918. );
  1919. if (g_ProgressBarLog != INVALID_HANDLE_VALUE) {
  1920. WriteFileStringA (g_ProgressBarLog, Msg);
  1921. }
  1922. LastTickCount = CurrentTickCount;
  1923. PopError();
  1924. }
  1925. #else // !PROGRESS_BAR
  1926. #ifdef DEBUG
  1927. /*++
  1928. Routine Description:
  1929. DebugLogTimeA and DebugLogTimeW preserve the last error code;
  1930. they append the current date and time to the formatted message,
  1931. then call LogA and LogW to actually process the message.
  1932. Arguments:
  1933. Format - Specifies either the message in ASCII format or
  1934. a message ID (if HIWORD(Format) == 0). The message
  1935. will be formatted using args.
  1936. ... - Specifies a list of arguments to be used when formatting
  1937. the message. If a message ID is used for Format, args
  1938. is supposed to be an array of pointers to strings
  1939. Return Value:
  1940. none
  1941. --*/
  1942. VOID
  1943. _cdecl
  1944. DebugLogTimeA (
  1945. IN PCSTR Format,
  1946. ...
  1947. )
  1948. {
  1949. static DWORD FirstTickCountA = 0;
  1950. static DWORD LastTickCountA = 0;
  1951. CHAR Msg[OUTPUT_BUFSIZE_LARGE];
  1952. CHAR Date[OUTPUT_BUFSIZE_SMALL];
  1953. CHAR Time[OUTPUT_BUFSIZE_SMALL];
  1954. PSTR AppendPos, End;
  1955. DWORD CurrentTickCount;
  1956. va_list args;
  1957. PushError();
  1958. //
  1959. // first, get the current date and time into the string.
  1960. //
  1961. if (!GetDateFormatA (
  1962. LOCALE_SYSTEM_DEFAULT,
  1963. LOCALE_NOUSEROVERRIDE,
  1964. NULL,
  1965. NULL,
  1966. Date,
  1967. OUTPUT_BUFSIZE_SMALL)) {
  1968. StringCopyA (Date,"** Error retrieving date. **");
  1969. }
  1970. if (!GetTimeFormatA (
  1971. LOCALE_SYSTEM_DEFAULT,
  1972. LOCALE_NOUSEROVERRIDE,
  1973. NULL,
  1974. NULL,
  1975. Time,
  1976. OUTPUT_BUFSIZE_SMALL)) {
  1977. StringCopyA (Time,"** Error retrieving time. **");
  1978. }
  1979. //
  1980. // Now, get the current tick count.
  1981. //
  1982. CurrentTickCount = GetTickCount();
  1983. //
  1984. // If this is the first call save the tick count.
  1985. //
  1986. if (!FirstTickCountA) {
  1987. FirstTickCountA = CurrentTickCount;
  1988. LastTickCountA = CurrentTickCount;
  1989. }
  1990. //
  1991. // Now, build the passed in string.
  1992. //
  1993. va_start (args, Format);
  1994. AppendPos = Msg + _vsnprintf (Msg, OUTPUT_BUFSIZE_LARGE, Format, args);
  1995. va_end (args);
  1996. //
  1997. // Append the time statistics to the end of the string.
  1998. //
  1999. End = Msg + OUTPUT_BUFSIZE_LARGE;
  2000. _snprintf(
  2001. AppendPos,
  2002. End - AppendPos,
  2003. "\nCurrent Date and Time: %s %s\n"
  2004. "Milliseconds since last DEBUGLOGTIME call : %u\n"
  2005. "Milliseconds since first DEBUGLOGTIME call: %u\n",
  2006. Date,
  2007. Time,
  2008. CurrentTickCount - LastTickCountA,
  2009. CurrentTickCount - FirstTickCountA
  2010. );
  2011. LastTickCountA = CurrentTickCount;
  2012. //
  2013. // Now, pass the results onto debugoutput.
  2014. //
  2015. LogA (DBG_TIME, "%s", Msg);
  2016. PopError();
  2017. }
  2018. VOID
  2019. _cdecl
  2020. DebugLogTimeW (
  2021. IN PCSTR Format,
  2022. ...
  2023. )
  2024. {
  2025. static DWORD FirstTickCountW = 0;
  2026. static DWORD LastTickCountW = 0;
  2027. WCHAR MsgW[OUTPUT_BUFSIZE_LARGE];
  2028. WCHAR DateW[OUTPUT_BUFSIZE_SMALL];
  2029. WCHAR TimeW[OUTPUT_BUFSIZE_SMALL];
  2030. PCWSTR FormatW;
  2031. PWSTR AppendPosW, EndW;
  2032. DWORD CurrentTickCount;
  2033. va_list args;
  2034. PushError();
  2035. //
  2036. // first, get the current date and time into the string.
  2037. //
  2038. if (!GetDateFormatW (
  2039. LOCALE_SYSTEM_DEFAULT,
  2040. LOCALE_NOUSEROVERRIDE,
  2041. NULL,
  2042. NULL,
  2043. DateW,
  2044. OUTPUT_BUFSIZE_SMALL)) {
  2045. StringCopyW (DateW, L"** Error retrieving date. **");
  2046. }
  2047. if (!GetTimeFormatW (
  2048. LOCALE_SYSTEM_DEFAULT,
  2049. LOCALE_NOUSEROVERRIDE,
  2050. NULL,
  2051. NULL,
  2052. TimeW,
  2053. OUTPUT_BUFSIZE_SMALL)) {
  2054. StringCopyW (TimeW, L"** Error retrieving time. **");
  2055. }
  2056. //
  2057. // Now, get the current tick count.
  2058. //
  2059. CurrentTickCount = GetTickCount();
  2060. //
  2061. // If this is the first call save the tick count.
  2062. //
  2063. if (!FirstTickCountW) {
  2064. FirstTickCountW = CurrentTickCount;
  2065. LastTickCountW = CurrentTickCount;
  2066. }
  2067. //
  2068. // Now, build the passed in string.
  2069. //
  2070. va_start (args, Format);
  2071. FormatW = ConvertAtoW (Format);
  2072. AppendPosW = MsgW + _vsnwprintf (MsgW, OUTPUT_BUFSIZE_LARGE, FormatW, args);
  2073. FreeConvertedStr (FormatW);
  2074. va_end (args);
  2075. //
  2076. // Append the time statistics to the end of the string.
  2077. //
  2078. EndW = MsgW + OUTPUT_BUFSIZE_LARGE;
  2079. _snwprintf(
  2080. AppendPosW,
  2081. EndW - AppendPosW,
  2082. L"\nCurrent Date and Time: %s %s\n"
  2083. L"Milliseconds since last DEBUGLOGTIME call : %u\n"
  2084. L"Milliseconds since first DEBUGLOGTIME call: %u\n",
  2085. DateW,
  2086. TimeW,
  2087. CurrentTickCount - LastTickCountW,
  2088. CurrentTickCount - FirstTickCountW
  2089. );
  2090. LastTickCountW = CurrentTickCount;
  2091. //
  2092. // Now, pass the results onto debugoutput.
  2093. //
  2094. LogW (DBG_TIME, "%s", MsgW);
  2095. PopError();
  2096. }
  2097. #endif // DEBUG
  2098. #endif // PROGRESS_BAR