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.

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