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

3334 lines
72 KiB

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