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.

505 lines
15 KiB

  1. //#pragma title( "Err.cpp - Basic error handling/message/logging" )
  2. /*
  3. Copyright (c) 1995-1998, Mission Critical Software, Inc. All rights reserved.
  4. ===============================================================================
  5. Module - Err.cpp
  6. System - Common
  7. Author - Tom Bernhardt, Rich Denham
  8. Created - 1994-08-22
  9. Description - Implements the TError class that handles basic exception
  10. handling, message generation, and logging functions.
  11. Updates - 1997-09-12 RED replace TTime class
  12. ===============================================================================
  13. */
  14. #ifdef USE_STDAFX
  15. # include "stdafx.h"
  16. #else
  17. # include <windows.h>
  18. #endif
  19. #ifndef WIN16_VERSION
  20. #include <lm.h>
  21. #endif
  22. #include <stdlib.h>
  23. #include <stdio.h>
  24. #include <fcntl.h>
  25. #include <io.h>
  26. #include <string.h>
  27. #include <stdarg.h>
  28. #include <share.h>
  29. #include <time.h>
  30. #include <rpc.h>
  31. #include <rpcdce.h>
  32. #include <sys\types.h>
  33. #include <sys\stat.h>
  34. #include "Common.hpp"
  35. #include "Err.hpp"
  36. #include "UString.hpp"
  37. #include <ResStr.h>
  38. #include "TReg.hpp"
  39. #define TERR_MAX_MSG_LEN (2000)
  40. #define BYTE_ORDER_MARK (0xFEFF)
  41. TError::TError(
  42. int displevel ,// in -mimimum severity level to display
  43. int loglevel ,// in -mimimum severity level to log
  44. WCHAR const * filename ,// in -file name of log (NULL if none)
  45. int logmode ,// in -0=replace, 1=append
  46. int beeplevel // in -min error level for beeping
  47. )
  48. {
  49. lastError = 0;
  50. maxError = 0;
  51. logLevel = loglevel;
  52. dispLevel = displevel;
  53. logFile = NULL;
  54. beepLevel = beeplevel;
  55. LogOpen(filename, logmode, loglevel);
  56. }
  57. TError::~TError()
  58. {
  59. LogClose();
  60. }
  61. // Closes any existing open logFile and opens a new log file if the fileName is
  62. // not null. If it is a null string, then a default fileName of "Temp.log" is
  63. // used.
  64. BOOL
  65. TError::LogOpen(
  66. WCHAR const * fileName ,// in -name of file including any path
  67. int mode ,// in -0=overwrite, 1=append
  68. int level ,// in -minimum level to log
  69. bool bBeginNew // in -begin a new log file
  70. )
  71. {
  72. BOOL retval=TRUE;
  73. if ( logFile )
  74. {
  75. fclose(logFile);
  76. logFile = NULL;
  77. }
  78. if ( fileName && fileName[0] )
  79. {
  80. // Check to see if the file already exists
  81. WIN32_FIND_DATA fDat;
  82. HANDLE hFind;
  83. BOOL bExisted = FALSE;
  84. hFind = FindFirstFile(fileName,&fDat);
  85. if ( hFind != INVALID_HANDLE_VALUE )
  86. {
  87. FindClose(hFind);
  88. if (bBeginNew)
  89. {
  90. // rename existing log file
  91. // get next sequence number from registry
  92. DWORD dwSequence = 1;
  93. static WCHAR c_szValueName[] = L"LogSeqNum";
  94. TRegKey key(GET_STRING(IDS_HKLM_DomainAdmin_Key));
  95. key.ValueGetDWORD(c_szValueName, &dwSequence);
  96. // split path components
  97. WCHAR szPath[_MAX_PATH];
  98. WCHAR szDrive[_MAX_DRIVE];
  99. WCHAR szDir[_MAX_DIR];
  100. WCHAR szFName[_MAX_FNAME];
  101. WCHAR szExt[_MAX_EXT];
  102. _wsplitpath(fileName, szDrive, szDir, szFName, szExt);
  103. // find name for backup that isn't already used...
  104. for (bool bFoundName = false; bFoundName == false; dwSequence++)
  105. {
  106. // generate backup name using the sequence number
  107. WCHAR szFNameSeq[_MAX_FNAME];
  108. wsprintf(szFNameSeq, L"%s %04lu", szFName, dwSequence);
  109. // make path from path components
  110. _wmakepath(szPath, szDrive, szDir, szFNameSeq, szExt);
  111. // check if file exists
  112. WIN32_FIND_DATA fd;
  113. HANDLE hFind = FindFirstFile(szPath, &fd);
  114. if (hFind == INVALID_HANDLE_VALUE)
  115. {
  116. DWORD dwError = GetLastError();
  117. if (dwError == ERROR_FILE_NOT_FOUND)
  118. {
  119. bFoundName = true;
  120. }
  121. }
  122. else
  123. {
  124. FindClose(hFind);
  125. }
  126. }
  127. if (bFoundName)
  128. {
  129. // attempt to rename file
  130. if (MoveFile(fileName, szPath))
  131. {
  132. // save next sequence number in registry
  133. key.ValueSetDWORD(c_szValueName, dwSequence);
  134. }
  135. else
  136. {
  137. bExisted = TRUE;
  138. }
  139. }
  140. if (!bExisted)
  141. {
  142. // get log history value from registry
  143. TRegKey keyHistory(GET_STRING(IDS_HKLM_DomainAdmin_Key));
  144. DWORD dwHistory = 20;
  145. static WCHAR c_szValueName[] = L"LogHistory";
  146. if (keyHistory.ValueGetDWORD(c_szValueName, &dwHistory) == ERROR_FILE_NOT_FOUND)
  147. {
  148. keyHistory.ValueSetDWORD(c_szValueName, dwHistory);
  149. }
  150. keyHistory.Close();
  151. if (dwSequence > dwHistory)
  152. {
  153. DWORD dwMinimum = dwSequence - dwHistory;
  154. // generate migration log path specification
  155. WCHAR szFNameSpec[_MAX_FNAME];
  156. wsprintf(szFNameSpec, L"%s *", szFName);
  157. _wmakepath(szPath, szDrive, szDir, szFNameSpec, szExt);
  158. // for each migration older than minimum
  159. WIN32_FIND_DATA fd;
  160. HANDLE hFind = FindFirstFile(szPath, &fd);
  161. if (hFind != INVALID_HANDLE_VALUE)
  162. {
  163. do
  164. {
  165. DWORD dwFileSequence;
  166. if (swscanf(fd.cFileName, L"%*s %lu", &dwFileSequence) == 1)
  167. {
  168. // if file sequence less than minimum to keep...
  169. if (dwFileSequence < dwMinimum)
  170. {
  171. // delete file
  172. WCHAR szDeleteName[_MAX_FNAME];
  173. _wsplitpath(fd.cFileName, 0, 0, szDeleteName, 0);
  174. WCHAR szDeletePath[_MAX_PATH];
  175. _wmakepath(szDeletePath, szDrive, szDir, szDeleteName, szExt);
  176. DeleteFile(szDeletePath);
  177. }
  178. }
  179. }
  180. while (FindNextFile(hFind, &fd));
  181. FindClose(hFind);
  182. }
  183. }
  184. }
  185. key.Close();
  186. }
  187. else
  188. {
  189. // overwrite or append to existing log file
  190. bExisted = TRUE;
  191. }
  192. }
  193. logFile = _wfsopen( fileName, mode == 0 ? L"wb" : L"ab", _SH_DENYNO );
  194. if ( !logFile )
  195. {
  196. MsgWrite( 4101, L"Log Open(%s) failed", fileName );
  197. retval = FALSE;
  198. }
  199. else
  200. {
  201. if (! bExisted )
  202. {
  203. // this is a new file we've just created
  204. // we need to write the byte order mark to the beginning of the file
  205. WCHAR x = BYTE_ORDER_MARK;
  206. fwprintf(logFile,L"%lc",x);
  207. }
  208. }
  209. }
  210. logLevel = level;
  211. return retval;
  212. }
  213. //-----------------------------------------------------------------------------
  214. // Writes formatted message to log file and flushes buffers
  215. //-----------------------------------------------------------------------------
  216. void TError::LogWrite(WCHAR const * msg)
  217. {
  218. WCHAR sTime[32];
  219. WCHAR sTemp[TERR_MAX_MSG_LEN];
  220. // Get rid of the <CR> from the end of the message because it causes things
  221. // to run together in the logs
  222. wcscpy(sTemp, msg);
  223. DWORD dwLen = wcslen(sTemp);
  224. if ( sTemp[dwLen-1] == 0x0d )
  225. sTemp[dwLen-1] = 0x00;
  226. if ( logFile )
  227. {
  228. fseek(logFile, 0L, SEEK_END);
  229. fwprintf(
  230. logFile,
  231. L"%s %s\r\n",
  232. gTTime.FormatIsoLcl( gTTime.Now( NULL ), sTime ),
  233. sTemp );
  234. fflush( logFile );
  235. }
  236. }
  237. //-----------------------------------------------------------------------------
  238. // Error message with format and arguments
  239. //-----------------------------------------------------------------------------
  240. void __cdecl
  241. TError::MsgWrite(
  242. int num ,// in -error number/level code
  243. WCHAR const msg[] ,// in -error message to display
  244. ... // in -printf args to msg pattern
  245. )
  246. {
  247. WCHAR suffix[TERR_MAX_MSG_LEN];
  248. va_list argPtr;
  249. va_start(argPtr, msg);
  250. _vsnwprintf(suffix, DIM(suffix) - 1, msg, argPtr);
  251. suffix[DIM(suffix) - 1] = L'\0';
  252. va_end(argPtr);
  253. MsgProcess(num, suffix);
  254. }
  255. #ifndef WIN16_VERSION
  256. //-----------------------------------------------------------------------------
  257. // System Error message with format and arguments
  258. //-----------------------------------------------------------------------------
  259. void __cdecl
  260. TError::SysMsgWrite(
  261. int num ,// in -error number/level code
  262. DWORD lastRc ,// in -error return code
  263. WCHAR const msg[] ,// in -error message/pattern to display
  264. ... // in -printf args to msg pattern
  265. )
  266. {
  267. WCHAR suffix[TERR_MAX_MSG_LEN];
  268. va_list argPtr;
  269. int len;
  270. // When an error occurs while in a constructor for a global object,
  271. // the TError object may not yet exist. In this case, "this" is zero
  272. // and we gotta get out of here before we generate a protection exception.
  273. if ( !this )
  274. return;
  275. va_start(argPtr, msg);
  276. len = _vsnwprintf(suffix, DIM(suffix) - 1, msg, argPtr);
  277. // append the system message for the lastRc at the end.
  278. if ( len < DIM(suffix) - 1 )
  279. {
  280. ErrorCodeToText(lastRc, DIM(suffix) - len - 1, suffix + len);
  281. }
  282. suffix[DIM(suffix) - 1] = L'\0';
  283. va_end(argPtr);
  284. MsgProcess(num, suffix);
  285. }
  286. //-----------------------------------------------------------------------------
  287. // System Error message with format and arguments
  288. //-----------------------------------------------------------------------------
  289. void __cdecl
  290. TError::SysMsgWrite(
  291. int num ,// in -error number/level code
  292. WCHAR const msg[] ,// in -error message/pattern to display
  293. ... // in -printf args to msg pattern
  294. )
  295. {
  296. WCHAR suffix[TERR_MAX_MSG_LEN];
  297. va_list argPtr;
  298. int len;
  299. DWORD lastRc = GetLastError();
  300. // When an error occurs while in a constructor for a global object,
  301. // the TError object may not yet exist. In this case, "this" is zero
  302. // and we gotta get out of here before we generate a protection exception.
  303. if ( !this )
  304. return;
  305. va_start( argPtr, msg );
  306. len = _vsnwprintf( suffix, DIM(suffix) - 1, msg, argPtr );
  307. // append the system message for the lastRc at the end.
  308. if ( len < DIM(suffix) - 1 )
  309. {
  310. ErrorCodeToText(lastRc, DIM(suffix) - len - 1, suffix + len);
  311. }
  312. suffix[DIM(suffix) - 1] = L'\0';
  313. va_end(argPtr);
  314. MsgProcess(num, suffix);
  315. }
  316. #endif
  317. //-----------------------------------------------------------------------------
  318. // Error message format, display and exception processing function
  319. //-----------------------------------------------------------------------------
  320. void __stdcall
  321. TError::MsgProcess(
  322. int num ,// in -error number/level code
  323. WCHAR const * str // in -error string to display
  324. )
  325. {
  326. static WCHAR const prefLetter[] = L"TIWEEEEXXXXX"; // These form the status code that appears at the start of each error message
  327. WCHAR fullmsg[TERR_MAX_MSG_LEN];
  328. struct
  329. {
  330. USHORT frequency; // audio frequency
  331. USHORT duration; // duration in mSec
  332. } audio[] = {{ 300, 20},{ 500, 50},{ 700, 100},
  333. { 800, 200},{1000, 300},{1500, 400},
  334. {2500, 750},{2500,1000},{2500,1000}};
  335. if ( num >= 0 )
  336. level = num / 10000; // 10000's position of error number
  337. else
  338. level = -1;
  339. if ( level <= 0 )
  340. {
  341. wcsncpy(fullmsg, str, DIM(fullmsg));
  342. fullmsg[DIM(fullmsg) - 1] = L'\0'; // ensure null termination
  343. }
  344. else
  345. {
  346. if ( num > maxError )
  347. maxError = num;
  348. _snwprintf(fullmsg, DIM(fullmsg), L"%c%1d:%04d %-s", prefLetter[level+1], level, num % 10000, str);
  349. fullmsg[DIM(fullmsg) - 1] = L'\0'; // ensure null termination
  350. }
  351. lastError = num;
  352. if ( level >= beepLevel )
  353. Beep(audio[level].frequency, audio[level].duration);
  354. if ( level >= logLevel )
  355. LogWrite(fullmsg);
  356. if ( level > 4 )
  357. {
  358. exit(level);
  359. }
  360. }
  361. //-----------------------------------------------------------------------------
  362. // Return text for error code
  363. //-----------------------------------------------------------------------------
  364. WCHAR *
  365. TError::ErrorCodeToText(
  366. DWORD code ,// in -message code
  367. DWORD lenMsg ,// in -length of message text area
  368. WCHAR * msg // out-returned message text
  369. )
  370. {
  371. static HMODULE hNetMsg = NULL;
  372. DWORD rc;
  373. WCHAR * pMsg;
  374. msg[0] = '\0'; // force to null
  375. if ( code >= NERR_BASE && code < MAX_NERR )
  376. {
  377. if ( !hNetMsg )
  378. hNetMsg = LoadLibrary(L"netmsg.dll");
  379. rc = 1;
  380. }
  381. else
  382. {
  383. rc = DceErrorInqText( code, msg );
  384. // Change any imbedded CR or LF to blank.
  385. for ( pMsg = msg;
  386. *pMsg;
  387. pMsg++ )
  388. {
  389. if ( (*pMsg == L'\x0D') || (*pMsg == L'\x0A') )
  390. *pMsg = L' ';
  391. }
  392. // Remove trailing blanks
  393. for ( pMsg--;
  394. pMsg >= msg;
  395. pMsg-- )
  396. {
  397. if ( *pMsg == L' ' )
  398. *pMsg = L'\0';
  399. else
  400. break;
  401. }
  402. }
  403. if ( rc )
  404. {
  405. if ( code >= NERR_BASE && code < MAX_NERR && hNetMsg )
  406. {
  407. FormatMessage(FORMAT_MESSAGE_FROM_HMODULE
  408. | FORMAT_MESSAGE_MAX_WIDTH_MASK
  409. | FORMAT_MESSAGE_IGNORE_INSERTS
  410. | 80,
  411. hNetMsg,
  412. code,
  413. 0,
  414. msg,
  415. lenMsg,
  416. NULL );
  417. }
  418. else
  419. {
  420. FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM
  421. | FORMAT_MESSAGE_MAX_WIDTH_MASK
  422. | FORMAT_MESSAGE_IGNORE_INSERTS
  423. | 80,
  424. NULL,
  425. code,
  426. 0,
  427. msg,
  428. lenMsg,
  429. NULL );
  430. }
  431. }
  432. return msg;
  433. }
  434. // Err.cpp - end of file