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.

787 lines
20 KiB

  1. /*++
  2. Copyright (c) 1997 Microsoft Corporation
  3. Module Name:
  4. stilog.cpp
  5. Abstract:
  6. Class to handle file based logging
  7. Author:
  8. Vlad Sadovsky (VladS) 01-Sep-1997
  9. History:
  10. --*/
  11. //
  12. // Include Headers
  13. //
  14. #include "cplusinc.h"
  15. #include "sticomm.h"
  16. //
  17. // Static definitions and variables
  18. //
  19. static const TCHAR szDefaultName[] = TEXT("Sti_Trace.log");
  20. static const TCHAR szDefaultTracerName[] = TEXT("STI");
  21. static const TCHAR szColumns[] = TEXT("Severity TracerName [Process::ThreadId] Time MessageText\r\n\r\n");
  22. static const TCHAR szOpenedLog[] = TEXT("\n****** Opened file log at %s %s .Tracer (%s) , called from [%#s::%#lx]\n");
  23. static const TCHAR szClosedLog[] = TEXT("\n******Closed trace log on %s %s Tracer (%s) , called from [%#s::%#lx]\n");
  24. #define STI_LOG_NUM_BYTES_TO_LOCK 4096
  25. //
  26. // Functions
  27. //
  28. //
  29. inline BOOL
  30. FormatStdTime(
  31. IN const SYSTEMTIME * pstNow,
  32. IN OUT TCHAR * pchBuffer,
  33. IN int cbBuffer
  34. )
  35. {
  36. return ( GetTimeFormat( LOCALE_SYSTEM_DEFAULT,
  37. ( LOCALE_NOUSEROVERRIDE | TIME_FORCE24HOURFORMAT|
  38. TIME_NOTIMEMARKER),
  39. pstNow, NULL, pchBuffer, cbBuffer)
  40. != 0);
  41. } // FormatStdTime()
  42. inline BOOL
  43. FormatStdDate( IN const SYSTEMTIME * pstNow,
  44. IN OUT TCHAR * pchBuffer,
  45. IN int cbBuffer)
  46. {
  47. return ( GetDateFormat( LOCALE_SYSTEM_DEFAULT, LOCALE_NOUSEROVERRIDE,
  48. pstNow, NULL, pchBuffer, cbBuffer)
  49. != 0);
  50. } // FormatStdDate()
  51. inline TCHAR
  52. TraceFlagToLetter(
  53. IN DWORD dwType
  54. )
  55. {
  56. if (dwType & STI_TRACE_ERROR) {
  57. return TEXT('e');
  58. }
  59. else if (dwType & STI_TRACE_WARNING) {
  60. return TEXT('w');
  61. }
  62. else if (dwType & STI_TRACE_INFORMATION) {
  63. return TEXT('i');
  64. }
  65. else {
  66. return TEXT('t');
  67. }
  68. }
  69. STI_FILE_LOG::STI_FILE_LOG(
  70. IN LPCTSTR lpszTracerName,
  71. IN LPCTSTR lpszLogName,
  72. IN DWORD dwFlags, // = 0
  73. IN HMODULE hMessageModule // =NULL
  74. )
  75. /*++
  76. Description
  77. Constructor function for given log object.
  78. Arguments:
  79. lpszSource: file name for log. Relative to Windows directory
  80. Note:
  81. --*/
  82. {
  83. LPCTSTR lpActualLogName;
  84. TCHAR szTempName[MAX_PATH];
  85. LPTSTR pszFilePart;
  86. DWORD dwError;
  87. DWORD cbName;
  88. DWORD dwLevel;
  89. DWORD dwMode;
  90. BOOL fTruncate = FALSE;
  91. m_dwSignature = SIGNATURE_FILE_LOG;
  92. lpActualLogName = szDefaultName;
  93. m_lWrittenHeader = FALSE;
  94. m_hDefaultMessageModule = hMessageModule ? hMessageModule : GetModuleHandle(NULL);
  95. dwLevel = STI_TRACE_ERROR;
  96. dwMode = STI_TRACE_ADD_TIME | STI_TRACE_ADD_THREAD;
  97. m_hLogFile = INVALID_HANDLE_VALUE;
  98. *m_szLogFilePath = TEXT('\0');
  99. m_dwMaxSize = STI_MAX_LOG_SIZE;
  100. ReportError(NO_ERROR);
  101. //
  102. // Read global settings from the registry
  103. //
  104. RegEntry re(REGSTR_PATH_STICONTROL REGSTR_PATH_LOGGING,HKEY_LOCAL_MACHINE);
  105. if (re.IsValid()) {
  106. m_dwMaxSize = re.GetNumber(REGSTR_VAL_LOG_MAXSIZE,STI_MAX_LOG_SIZE);
  107. //
  108. // If we need to check append flag, read it from the registry
  109. //
  110. if (dwFlags & STIFILELOG_CHECK_TRUNCATE_ON_BOOT) {
  111. fTruncate = re.GetNumber(REGSTR_VAL_LOG_TRUNCATE_ON_BOOT,FALSE);
  112. }
  113. }
  114. //
  115. // Open file for logging
  116. //
  117. if (lpszLogName && *lpszLogName ) {
  118. lpActualLogName = lpszLogName;
  119. }
  120. //
  121. // lpszTracerName is ANSI.
  122. //
  123. if (lpszTracerName && *lpszTracerName ) {
  124. lstrcpyn(m_szTracerName,lpszTracerName,sizeof(m_szTracerName) / sizeof(m_szTracerName[0]));
  125. m_szTracerName[sizeof(m_szTracerName) / sizeof(m_szTracerName[0]) -1] = TEXT('\0');
  126. }
  127. else {
  128. lstrcpy(m_szTracerName,szDefaultTracerName);
  129. }
  130. //
  131. // Approximate process name with name of the executable binary used to create process
  132. //
  133. *szTempName = TEXT('\0');
  134. ::GetModuleFileName(NULL,szTempName,sizeof(szTempName) / sizeof(szTempName[0]));
  135. szTempName[(sizeof(szTempName) / sizeof(szTempName[0])) - 1] = TEXT('\0');
  136. pszFilePart = _tcsrchr(szTempName,TEXT('\\'));
  137. pszFilePart = (pszFilePart && *pszFilePart) ? ::CharNext(pszFilePart) : szTempName;
  138. lstrcpyn(m_szProcessName,pszFilePart,sizeof(m_szProcessName)/sizeof(TCHAR));
  139. m_szProcessName[sizeof(m_szProcessName)/sizeof(TCHAR) - 1] = TEXT('\0');
  140. //
  141. // Read flags for this logger
  142. //
  143. re.MoveToSubKey(lpszTracerName);
  144. if (re.IsValid()) {
  145. dwLevel = re.GetNumber(REGSTR_VAL_LOG_LEVEL,STI_TRACE_ERROR)
  146. & STI_TRACE_MESSAGE_TYPE_MASK;
  147. dwMode = re.GetNumber(REGSTR_VAL_LOG_MODE,STI_TRACE_ADD_THREAD)
  148. & STI_TRACE_MESSAGE_FLAGS_MASK;
  149. }
  150. m_dwReportMode = dwLevel | dwMode;
  151. //
  152. // Open log file
  153. //
  154. DWORD dwCharsAvailable = sizeof(m_szLogFilePath) / sizeof(m_szLogFilePath[0]) - lstrlen(TEXT("\\")) - lstrlen(lpActualLogName);
  155. cbName = ::ExpandEnvironmentStrings(TEXT("%USERPROFILE%"),
  156. m_szLogFilePath,
  157. dwCharsAvailable);
  158. if (( cbName == 0) || !*m_szLogFilePath ) {
  159. ReportError(::GetLastError());
  160. return;
  161. }
  162. m_szLogFilePath[sizeof(m_szLogFilePath) / sizeof(m_szLogFilePath[0]) - 1] = TEXT('\0');
  163. lstrcat(lstrcat(m_szLogFilePath,TEXT("\\")),lpActualLogName);
  164. m_hLogFile = ::CreateFile(m_szLogFilePath,
  165. GENERIC_WRITE,
  166. FILE_SHARE_WRITE | FILE_SHARE_READ,
  167. NULL, // security attributes
  168. OPEN_ALWAYS,
  169. FILE_ATTRIBUTE_NORMAL,
  170. NULL); // template file handle
  171. // if ( m_hLogFile!= INVALID_HANDLE_VALUE) {
  172. if (IS_VALID_HANDLE(m_hLogFile)) {
  173. if(fTruncate) {
  174. ::SetFilePointer( m_hLogFile, 0, NULL, FILE_BEGIN );
  175. ::SetEndOfFile( m_hLogFile );
  176. }
  177. ::SetFilePointer( m_hLogFile, 0, NULL, FILE_END);
  178. }
  179. if (!IS_VALID_HANDLE(m_hLogFile)) {
  180. ReportError(::GetLastError());
  181. }
  182. else {
  183. //
  184. // Success
  185. //
  186. }
  187. } /* STI_FILE_LOG::STI_FILE_LOG() */
  188. STI_FILE_LOG::~STI_FILE_LOG( VOID)
  189. /*++
  190. Description:
  191. Destructor function for given STI_FILE_LOG object.
  192. --*/
  193. {
  194. SYSTEMTIME stCurrentTime;
  195. TCHAR szFmtDate[20];
  196. TCHAR szFmtTime[32];
  197. TCHAR szTextBuffer[128];
  198. if(m_lWrittenHeader) {
  199. GetLocalTime(&stCurrentTime);
  200. FormatStdDate( &stCurrentTime, szFmtDate, 15);
  201. FormatStdTime( &stCurrentTime, szFmtTime, sizeof(szFmtTime) / sizeof(szFmtTime[0]));
  202. ::_sntprintf(szTextBuffer,
  203. sizeof(szTextBuffer)/sizeof(szTextBuffer[0]),
  204. szClosedLog,
  205. szFmtDate,
  206. szFmtTime,
  207. m_szTracerName,
  208. m_szProcessName,
  209. ::GetCurrentThreadId()
  210. );
  211. szTextBuffer[sizeof(szTextBuffer)/sizeof(szTextBuffer[0]) - 1] = TEXT('\0');
  212. WriteStringToLog(szTextBuffer);
  213. }
  214. if (IS_VALID_HANDLE(m_hLogFile)) {
  215. ::FlushFileBuffers( m_hLogFile);
  216. ::CloseHandle(m_hLogFile);
  217. m_hLogFile = INVALID_HANDLE_VALUE;
  218. }
  219. m_dwSignature = SIGNATURE_FILE_LOG_FREE;
  220. } /* STI_FILE_LOG::~STI_FILE_LOG() */
  221. //
  222. // IUnknown methods. Used only for reference counting
  223. //
  224. STDMETHODIMP
  225. STI_FILE_LOG::QueryInterface( REFIID riid, LPVOID * ppvObj)
  226. {
  227. return E_FAIL;
  228. }
  229. STDMETHODIMP_(ULONG)
  230. STI_FILE_LOG::AddRef( void)
  231. {
  232. ::InterlockedIncrement(&m_cRef);
  233. return m_cRef;
  234. }
  235. STDMETHODIMP_(ULONG)
  236. STI_FILE_LOG::Release( void)
  237. {
  238. LONG cNew;
  239. if(!(cNew = ::InterlockedDecrement(&m_cRef))) {
  240. delete this;
  241. }
  242. return cNew;
  243. }
  244. void
  245. STI_FILE_LOG::
  246. WriteLogSessionHeader(
  247. VOID
  248. )
  249. /*++
  250. Description
  251. Arguments:
  252. Note:
  253. --*/
  254. {
  255. SYSTEMTIME stCurrentTime;
  256. TCHAR szFmtDate[32];
  257. TCHAR szFmtTime[32];
  258. TCHAR szTextBuffer[128];
  259. GetLocalTime(&stCurrentTime);
  260. FormatStdDate( &stCurrentTime, szFmtDate, sizeof(szFmtDate) / sizeof(szFmtDate[0]));
  261. FormatStdTime( &stCurrentTime, szFmtTime, sizeof(szFmtTime) / sizeof(szFmtTime[0]));
  262. ::_sntprintf(szTextBuffer,
  263. sizeof(szTextBuffer)/sizeof(szTextBuffer[0]),
  264. szOpenedLog,
  265. szFmtDate,
  266. szFmtTime,
  267. m_szTracerName,
  268. m_szProcessName,
  269. ::GetCurrentThreadId());
  270. szTextBuffer[sizeof(szTextBuffer)/sizeof(szTextBuffer[0]) -1] = TEXT('\0');
  271. WriteStringToLog(szTextBuffer);
  272. WriteStringToLog(szColumns);
  273. }
  274. void
  275. STI_FILE_LOG::
  276. ReportMessage(
  277. DWORD dwType,
  278. LPCTSTR pszMsg,
  279. ...
  280. )
  281. /*++
  282. Description
  283. Arguments:
  284. Note:
  285. --*/
  286. {
  287. va_list list;
  288. TCHAR *pchBuff = NULL;
  289. DWORD cch;
  290. va_start (list, pszMsg);
  291. pchBuff = NULL;
  292. cch = ::FormatMessage(FORMAT_MESSAGE_MAX_WIDTH_MASK |
  293. FORMAT_MESSAGE_ALLOCATE_BUFFER |
  294. FORMAT_MESSAGE_FROM_STRING,
  295. pszMsg,
  296. 0,
  297. 0,
  298. (LPTSTR) &pchBuff,
  299. 1024,
  300. &list
  301. );
  302. if (pchBuff && cch) {
  303. vReportMessage(dwType,pchBuff,list);
  304. LocalFree(pchBuff);
  305. }
  306. va_end(list);
  307. }
  308. void
  309. STI_FILE_LOG::
  310. ReportMessage(
  311. DWORD dwType,
  312. DWORD idMessage,
  313. ...
  314. )
  315. /*++
  316. Description
  317. Arguments:
  318. Note:
  319. --*/
  320. {
  321. va_list list;
  322. TCHAR *pchBuff = NULL;
  323. DWORD cch;
  324. va_start (list, idMessage);
  325. //
  326. // Read message and add inserts
  327. //
  328. pchBuff = NULL;
  329. cch = ::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
  330. FORMAT_MESSAGE_MAX_WIDTH_MASK |
  331. FORMAT_MESSAGE_FROM_HMODULE,
  332. m_hDefaultMessageModule ,
  333. idMessage,
  334. 0,
  335. (LPTSTR) &pchBuff,
  336. 1024,
  337. &list
  338. );
  339. if (pchBuff && cch) {
  340. vReportMessage(dwType,pchBuff,list);
  341. LocalFree(pchBuff);
  342. }
  343. va_end(list);
  344. }
  345. void
  346. STI_FILE_LOG::
  347. vReportMessage(
  348. DWORD dwType,
  349. LPCTSTR pszMsg,
  350. va_list arglist
  351. )
  352. /*++
  353. Description
  354. Arguments:
  355. Note:
  356. --*/
  357. {
  358. TCHAR achTextBuffer[1024];
  359. CSimpleString csTextBuffer;
  360. DWORD dwReportMode;
  361. if ((QueryError() != NOERROR) ||
  362. (!m_hLogFile || m_hLogFile == INVALID_HANDLE_VALUE)) {
  363. return;
  364. }
  365. // Do we need to display this message ?
  366. if (!((dwType & m_dwReportMode) & STI_TRACE_MESSAGE_TYPE_MASK)) {
  367. return;
  368. }
  369. // Start message with type
  370. csTextBuffer = TraceFlagToLetter(dwType);
  371. csTextBuffer += TEXT(" ");
  372. // Add name of tracer source
  373. csTextBuffer += m_szTracerName;
  374. csTextBuffer += TEXT(" ");
  375. dwReportMode = m_dwReportMode | (dwType & STI_TRACE_MESSAGE_FLAGS_MASK);
  376. //
  377. // Prepare header if needed
  378. //
  379. if (dwReportMode & STI_TRACE_MESSAGE_FLAGS_MASK ) {
  380. if (dwReportMode & STI_TRACE_ADD_THREAD ) {
  381. //
  382. // Add process::thread ID
  383. //
  384. CSimpleString csThreadId;
  385. csThreadId.Format(TEXT("[%#s::%#lx] "), m_szProcessName, ::GetCurrentThreadId());
  386. csTextBuffer += csThreadId;
  387. csTextBuffer += TEXT(" ");
  388. }
  389. if (dwReportMode & STI_TRACE_ADD_TIME ) {
  390. // Add current time
  391. SYSTEMTIME stCurrentTime;
  392. TCHAR szFmtTime[32];
  393. *szFmtTime = TEXT('\0');
  394. ::GetLocalTime(&stCurrentTime);
  395. FormatStdTime( &stCurrentTime, szFmtTime, 15);
  396. csTextBuffer += szFmtTime;
  397. csTextBuffer += TEXT(" ");
  398. }
  399. }
  400. csTextBuffer += TEXT("\t");
  401. //
  402. // Now add message text itself
  403. //
  404. _vsntprintf(achTextBuffer, sizeof(achTextBuffer)/sizeof(achTextBuffer[0]), pszMsg, arglist);
  405. achTextBuffer[sizeof(achTextBuffer)/sizeof(achTextBuffer[0]) - 1] = TEXT('\0');
  406. csTextBuffer += achTextBuffer;
  407. csTextBuffer += TEXT("\r\n");
  408. //
  409. // Write to the file, flushing buffers if message type is ERROR
  410. //
  411. WriteStringToLog(csTextBuffer.String(),(dwType & STI_TRACE_MESSAGE_TYPE_MASK) & STI_TRACE_ERROR);
  412. }
  413. VOID
  414. STI_FILE_LOG::
  415. WriteStringToLog(
  416. LPCTSTR pszTextBuffer,
  417. BOOL fFlush // = FALSE
  418. )
  419. {
  420. DWORD dwcbWritten;
  421. LONG lHeaderWrittenFlag;
  422. //
  423. // Integrity check. Make sure the file handle is valid - if not,
  424. // then bail.
  425. //
  426. if (!(IS_VALID_HANDLE(m_hLogFile))) {
  427. #ifdef DEBUG
  428. //OutputDebugString(TEXT("STILOG File or Mutex handle is invalid. "));
  429. #endif
  430. return ;
  431. }
  432. //
  433. // If had not done yet, write session header here. Note this is recursive call
  434. // and flag marking header should be set first.
  435. //
  436. lHeaderWrittenFlag = InterlockedExchange(&m_lWrittenHeader,1L);
  437. if (!lHeaderWrittenFlag) {
  438. WriteLogSessionHeader();
  439. }
  440. //
  441. // Check that log file size is not exceeding the limit. Assuming log file size is set to fit
  442. // into 32bit field, so we don't bother looking at high dword.
  443. //
  444. // Nb: We are not saving backup copy of the log, expecting that it never grows that large
  445. //
  446. BY_HANDLE_FILE_INFORMATION fi;
  447. if (!GetFileInformationByHandle(m_hLogFile,&fi)) {
  448. #ifdef DEBUG
  449. OutputDebugString(TEXT("STILOG could not get file size for log file. "));
  450. #endif
  451. return ;
  452. }
  453. if ( fi.nFileSizeHigh !=0 || (fi.nFileSizeLow > m_dwMaxSize) ){
  454. ::SetFilePointer( m_hLogFile, 0, NULL, FILE_BEGIN );
  455. ::SetEndOfFile( m_hLogFile );
  456. ::GetFileInformationByHandle(m_hLogFile,&fi);
  457. }
  458. OVERLAPPED overlappedStruct = {0}; // Initialize all members to 0
  459. overlappedStruct.Offset = fi.nFileSizeLow;
  460. overlappedStruct.OffsetHigh = fi.nFileSizeHigh;
  461. //
  462. // Wait till lock is granted. We can call LockfileEx to do this since the file was not opened for async i/o
  463. //
  464. BOOL fLockedFile = ::LockFileEx(m_hLogFile, // log file handle
  465. LOCKFILE_EXCLUSIVE_LOCK, // we want to be the only writer
  466. 0, // reserved - must be 0
  467. STI_LOG_NUM_BYTES_TO_LOCK, // low-order word of length
  468. 0, // high-order word of length
  469. &overlappedStruct); // contains starting offset
  470. if (fLockedFile)
  471. {
  472. ::SetFilePointer( m_hLogFile, 0, NULL, FILE_END);
  473. #ifdef _UNICODE
  474. UINT len = lstrlen(pszTextBuffer);
  475. CHAR *ansiBuffer = (CHAR *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (len + 2));
  476. if(ansiBuffer) {
  477. ::WideCharToMultiByte(CP_ACP,
  478. 0,
  479. pszTextBuffer,
  480. -1,
  481. ansiBuffer,
  482. len + 1,
  483. NULL,
  484. NULL);
  485. ::WriteFile(m_hLogFile,
  486. ansiBuffer,
  487. len,
  488. &dwcbWritten,
  489. NULL);
  490. HeapFree(GetProcessHeap(), 0, ansiBuffer);
  491. ansiBuffer = NULL;
  492. }
  493. #else
  494. ::WriteFile(m_hLogFile,
  495. pszTextBuffer,
  496. lstrlen(pszTextBuffer),
  497. &dwcbWritten,
  498. NULL);
  499. #endif
  500. BOOL fUnlockedFile = ::UnlockFileEx(m_hLogFile, // handle to file
  501. 0, // reserved
  502. STI_LOG_NUM_BYTES_TO_LOCK, // low-order part of length
  503. 0, // high-order part of length
  504. &overlappedStruct); // unlock region start
  505. if (!fUnlockedFile)
  506. {
  507. #ifdef DEBUG
  508. ::OutputDebugString(TEXT("Failed to unlock STI log file!"));
  509. #endif
  510. }
  511. if (fFlush) {
  512. // Flush buffers to disk
  513. FlushFileBuffers(m_hLogFile);
  514. }
  515. }
  516. else
  517. {
  518. #ifdef DEBUG
  519. ::OutputDebugString(TEXT("Failed to lock STI log file!"));
  520. #endif
  521. }
  522. #ifdef DEBUG
  523. ::OutputDebugString(pszTextBuffer);
  524. #endif
  525. }
  526. //
  527. // C-APIs
  528. //
  529. HANDLE
  530. WINAPI
  531. CreateStiFileLog(
  532. IN LPCTSTR lpszTracerName,
  533. IN LPCTSTR lpszLogName,
  534. IN DWORD dwReportMode
  535. )
  536. /*++
  537. Description
  538. Arguments:
  539. Note:
  540. --*/
  541. {
  542. HANDLE hRet = INVALID_HANDLE_VALUE;
  543. STI_FILE_LOG* pStiFileLog = NULL;
  544. pStiFileLog = new STI_FILE_LOG(lpszTracerName,lpszLogName);
  545. if(pStiFileLog){
  546. if (pStiFileLog->IsValid()) {
  547. hRet = static_cast<HANDLE>(pStiFileLog);
  548. pStiFileLog->SetReportMode(pStiFileLog->QueryReportMode() | dwReportMode);
  549. } else {
  550. //
  551. // Notice that we delete this object rather than calling
  552. // CloseStiFileLog. We do this because it the filelog is
  553. // not valid (maybe it had an internal creation error),
  554. // CloseStiFileLog won't try to delete it, hence we delete
  555. // it here.
  556. //
  557. delete pStiFileLog;
  558. pStiFileLog = NULL;
  559. hRet = INVALID_HANDLE_VALUE;
  560. }
  561. } else {
  562. ::SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  563. }
  564. return hRet;
  565. }
  566. DWORD
  567. WINAPI
  568. CloseStiFileLog(
  569. IN HANDLE hFileLog
  570. )
  571. {
  572. STI_FILE_LOG* pStiFileLog = NULL;
  573. pStiFileLog = static_cast<STI_FILE_LOG*>(hFileLog);
  574. if (IsBadWritePtr(pStiFileLog,sizeof(STI_FILE_LOG)) ||
  575. !pStiFileLog->IsValid()) {
  576. ::SetLastError(ERROR_INVALID_HANDLE);
  577. return ERROR_INVALID_HANDLE;
  578. }
  579. delete pStiFileLog;
  580. return NOERROR;
  581. }
  582. DWORD
  583. WINAPI
  584. ReportStiLogMessage(
  585. IN HANDLE hFileLog,
  586. IN DWORD dwType,
  587. IN LPCTSTR psz,
  588. ...
  589. )
  590. {
  591. STI_FILE_LOG* pStiFileLog = NULL;
  592. pStiFileLog = static_cast<STI_FILE_LOG*>(hFileLog);
  593. if (IsBadWritePtr(pStiFileLog,sizeof(STI_FILE_LOG)) ||
  594. !pStiFileLog->IsValid()) {
  595. ::SetLastError(ERROR_INVALID_HANDLE);
  596. return ERROR_INVALID_HANDLE;
  597. }
  598. va_list list;
  599. va_start (list, psz);
  600. pStiFileLog->vReportMessage(dwType,psz,list);
  601. va_end(list);
  602. return NOERROR;
  603. }
  604. /********************************* End of File ***************************/