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.

1100 lines
27 KiB

  1. /*++
  2. Copyright (c) 2001 Microsoft Corporation
  3. Module Name:
  4. VerifLog.cpp
  5. Abstract:
  6. This module implements the code for manipulating the AppVerifier log file.
  7. Author:
  8. dmunsil created 04/26/2001
  9. Revision History:
  10. 08/14/2001 robkenny Moved code inside the ShimLib namespace.
  11. 09/21/2001 rparsons Logging code now uses NT calls.
  12. 09/25/2001 rparsons Added critical section.
  13. --*/
  14. #include "ShimHook.h"
  15. #include "VerifLog.h"
  16. #include "avrfutil.h"
  17. namespace ShimLib
  18. {
  19. typedef struct _VLOG_GLOBAL_DATA {
  20. BOOL bLoggingDisabled; // was logging disabled?
  21. WCHAR szSessionLog[MAX_PATH];
  22. WCHAR szProcessLog[MAX_PATH];
  23. RTL_CRITICAL_SECTION csLogging;
  24. } VLOG_GLOBAL_DATA, *PVLOG_GLOBAL_DATA;
  25. PVLOG_GLOBAL_DATA g_pData = NULL;
  26. HANDLE g_hMap = NULL; // mapping handle for global data
  27. BOOL g_bVerifierLogInited = FALSE; // have we been through the init sequence?
  28. BOOL g_bLoggingDisabled = TRUE; // have we been through the init sequence?
  29. BOOL g_bLogBreakIn = FALSE;
  30. CString g_strSessionLog;
  31. CString g_strProcessLog;
  32. RTL_CRITICAL_SECTION g_csLogging;
  33. LPVOID g_pDllBase; // our own DLL base
  34. LPVOID g_pDllEnd; // one past the DLL's last byte
  35. DWORD g_dwSizeOfImage; // our own DLL image size
  36. void
  37. CheckForDebuggerBreakIn(
  38. void
  39. )
  40. {
  41. /*
  42. UNICODE_STRING ustrKey;
  43. UNICODE_STRING ustrBreakIn;
  44. NTSTATUS Status;
  45. OBJECT_ATTRIBUTES ObjectAttributes;
  46. HANDLE KeyHandle;
  47. PKEY_VALUE_PARTIAL_INFORMATION KeyValueInformation;
  48. ULONG KeyValueBuffer[256];
  49. ULONG KeyValueLength;
  50. RtlInitUnicodeString(&ustrKey, AV_KEY);
  51. RtlInitUnicodeString(&ustrBreakIn, AV_BREAKIN);
  52. InitializeObjectAttributes(&ObjectAttributes,
  53. &ustrKey,
  54. OBJ_CASE_INSENSITIVE,
  55. NULL,
  56. NULL);
  57. Status = NtOpenKey(&KeyHandle,
  58. GENERIC_READ,
  59. &ObjectAttributes);
  60. if (!NT_SUCCESS(Status)) {
  61. return;
  62. }
  63. KeyValueInformation = (PKEY_VALUE_PARTIAL_INFORMATION)&KeyValueBuffer;
  64. Status = NtQueryValueKey(KeyHandle,
  65. &ustrBreakIn,
  66. KeyValuePartialInformation,
  67. KeyValueInformation,
  68. sizeof(KeyValueBuffer),
  69. &KeyValueLength);
  70. NtClose(KeyHandle);
  71. if (!NT_SUCCESS(Status)) {
  72. return;
  73. }
  74. //
  75. // Check for the value type.
  76. //
  77. if (KeyValueInformation->Type != REG_DWORD) {
  78. return;
  79. }
  80. g_bLogBreakIn = (*(DWORD*)(&KeyValueInformation->Data) != 0);
  81. */
  82. WCHAR szExe[100];
  83. GetCurrentExeName(szExe, 100);
  84. g_bLogBreakIn = GetShimSettingDWORD(L"General", szExe, AV_BREAKIN, FALSE);
  85. }
  86. BOOL
  87. GetModuleNameAndOffset(
  88. LPVOID lpAddress, // IN return address to search for
  89. LPWSTR lpwszModuleName, // OUT name of module that contains address
  90. DWORD dwBufferChars, // IN size in chars of module name buffer
  91. PDWORD pdwOffset // OUT offset within module
  92. )
  93. {
  94. PPEB Peb = NtCurrentPeb();
  95. PLIST_ENTRY LdrHead;
  96. PLIST_ENTRY LdrNext;
  97. DWORD i;
  98. BOOL bRet = FALSE;
  99. if (!lpAddress || !lpwszModuleName || !pdwOffset) {
  100. return FALSE;
  101. }
  102. //
  103. // search for the module
  104. //
  105. LdrHead = &Peb->Ldr->InMemoryOrderModuleList;
  106. LdrNext = LdrHead->Flink;
  107. while (LdrNext != LdrHead) {
  108. PLDR_DATA_TABLE_ENTRY LdrEntry;
  109. LdrEntry = CONTAINING_RECORD(LdrNext, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
  110. //
  111. // Is this it?
  112. //
  113. if (lpAddress >= LdrEntry->DllBase && lpAddress < ((PBYTE)(LdrEntry->DllBase) + LdrEntry->SizeOfImage)) {
  114. wcsncpy(lpwszModuleName, LdrEntry->BaseDllName.Buffer, dwBufferChars);
  115. lpwszModuleName[dwBufferChars - 1] = 0;
  116. *pdwOffset = (DWORD)((PBYTE)lpAddress - (PBYTE)(LdrEntry->DllBase));
  117. bRet = TRUE;
  118. break;
  119. }
  120. LdrNext = LdrEntry->InMemoryOrderLinks.Flink;
  121. }
  122. return bRet;
  123. }
  124. void
  125. GetCallingModule(
  126. LPWSTR szModule,
  127. DWORD dwChars,
  128. PDWORD pdwOffset
  129. )
  130. {
  131. PVOID apRetAddresses[10];
  132. USHORT unAddresses, i;
  133. BOOL bFound = FALSE;
  134. ULONG ulHash;
  135. //
  136. // On W2K, RtlCaptureStackBackTrace tries to dereference the fourth
  137. // argument (the returned hash) without ensuring that it's valid.
  138. // This causes on an access violation. On XP, the problem has been
  139. // fixed. We get a hash value back, but we'll never use it.
  140. //
  141. unAddresses = RtlCaptureStackBackTrace(3, 10, apRetAddresses, &ulHash);
  142. for (i = 0; i != unAddresses; i++) {
  143. PVOID pAddress = apRetAddresses[i];
  144. if (pAddress < g_pDllBase || pAddress >= g_pDllEnd) {
  145. bFound = GetModuleNameAndOffset(pAddress, szModule, dwChars, pdwOffset);
  146. if (bFound) {
  147. break;
  148. }
  149. }
  150. }
  151. if (!bFound) {
  152. if (pdwOffset) {
  153. *pdwOffset = 0;
  154. }
  155. if (szModule && dwChars > 10) {
  156. wcscpy(szModule, L"<unknown>");
  157. }
  158. }
  159. return;
  160. }
  161. /*++
  162. Function Description:
  163. Initializes the globals holding this module's base address and size
  164. Return Value:
  165. none.
  166. History:
  167. 09/26/2001 dmunsil Created
  168. --*/
  169. void
  170. GetCurrentModuleInfo(void)
  171. {
  172. PPEB Peb = NtCurrentPeb();
  173. PLIST_ENTRY LdrHead;
  174. PLIST_ENTRY LdrNext;
  175. DWORD i;
  176. //
  177. // the base address is just the hInst
  178. //
  179. g_pDllBase = (LPVOID)g_hinstDll;
  180. //
  181. // now go find the size of the image by looking through the
  182. // loader's module list
  183. //
  184. LdrHead = &Peb->Ldr->InMemoryOrderModuleList;
  185. LdrNext = LdrHead->Flink;
  186. while (LdrNext != LdrHead) {
  187. PLDR_DATA_TABLE_ENTRY LdrEntry;
  188. LdrEntry = CONTAINING_RECORD(LdrNext, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
  189. //
  190. // Is this it?
  191. //
  192. if (LdrEntry->DllBase == g_pDllBase) {
  193. g_dwSizeOfImage = LdrEntry->SizeOfImage;
  194. g_pDllEnd = (PVOID)((PBYTE)g_pDllBase + g_dwSizeOfImage);
  195. break;
  196. }
  197. LdrNext = LdrEntry->InMemoryOrderLinks.Flink;
  198. }
  199. }
  200. /*++
  201. Function Description:
  202. Initializes the support for file logging.
  203. Return Value:
  204. TRUE if successful, FALSE if failed
  205. History:
  206. 04/26/2001 dmunsil Created
  207. 09/27/2001 rparsons Converted to use NT calls
  208. --*/
  209. BOOL
  210. InitVerifierLogSupport(void)
  211. {
  212. CString strVLogPath;
  213. CString strProcessPath;
  214. CString strProcessName;
  215. CString strTemp;
  216. SYSTEMTIME LocalTime;
  217. CString strTime;
  218. CString strShared;
  219. char *szTemp;
  220. int nTemp;
  221. BOOL bAlreadyInited;
  222. BOOL bSuccess = FALSE;
  223. DWORD dwID;
  224. DWORD dwErr;
  225. NTSTATUS status;
  226. UNICODE_STRING strLogFile = {0};
  227. OBJECT_ATTRIBUTES ObjectAttributes;
  228. IO_STATUS_BLOCK IoStatusBlock;
  229. HANDLE hFile = INVALID_HANDLE_VALUE;
  230. //
  231. // if we've already been inited, get out
  232. //
  233. if (g_bVerifierLogInited) {
  234. return FALSE;
  235. }
  236. g_bVerifierLogInited = TRUE;
  237. CheckForDebuggerBreakIn();
  238. //
  239. // get the current module's base address and size
  240. //
  241. GetCurrentModuleInfo();
  242. //
  243. // first check for a shared memory block
  244. //
  245. dwID = GetCurrentProcessId();
  246. strShared.Format(L"VeriferLog_%08X", dwID);
  247. g_hMap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, strShared.GetAnsi());
  248. if (g_hMap) {
  249. bAlreadyInited = TRUE;
  250. } else {
  251. bAlreadyInited = FALSE;
  252. g_hMap = CreateFileMapping(INVALID_HANDLE_VALUE,
  253. NULL,
  254. PAGE_READWRITE,
  255. 0,
  256. sizeof(VLOG_GLOBAL_DATA),
  257. strShared.GetAnsi());
  258. }
  259. if (!g_hMap) {
  260. DPF("VerifierLog", eDbgLevelError, "Cannot get shared global data.");
  261. g_bLoggingDisabled = TRUE;
  262. return FALSE;
  263. }
  264. g_pData = (PVLOG_GLOBAL_DATA)MapViewOfFile(g_hMap, FILE_MAP_ALL_ACCESS, 0, 0, 0);
  265. if (!g_pData) {
  266. DPF("VerifierLog", eDbgLevelError, "Cannot map shared global data.");
  267. g_bLoggingDisabled = TRUE;
  268. return FALSE;
  269. }
  270. if (bAlreadyInited) {
  271. if (g_pData->szProcessLog[0] == 0 || g_pData->szSessionLog[0] == 0) {
  272. g_bLoggingDisabled = TRUE;
  273. g_pData->bLoggingDisabled = TRUE;
  274. return FALSE;
  275. }
  276. g_bLoggingDisabled = g_pData->bLoggingDisabled;
  277. g_strSessionLog = g_pData->szSessionLog;
  278. g_strProcessLog = g_pData->szProcessLog;
  279. g_csLogging = g_pData->csLogging;
  280. return TRUE;
  281. } else {
  282. //
  283. // just in case -- make sure these are NULL
  284. //
  285. ZeroMemory(g_pData, sizeof(VLOG_GLOBAL_DATA));
  286. }
  287. //
  288. // we need to init the file mapping, so temporarily disable logging, just in case.
  289. //
  290. g_pData->bLoggingDisabled = TRUE;
  291. //
  292. // init the CString objects and critical section
  293. //
  294. RtlInitializeCriticalSection(&g_csLogging);
  295. //
  296. // the verifier log will be located in %ALLUSERSPROFILE%\\Documents\\AppVerifierLogs
  297. //
  298. //
  299. // First, check that VLog exists; if not, we're not logging
  300. //
  301. strVLogPath = L"%ALLUSERSPROFILE%\\Documents\\AppVerifierLogs";
  302. strVLogPath.ExpandEnvironmentStringsW();
  303. if (GetFileAttributesW(strVLogPath.Get()) == -1) {
  304. DPF("VerifierLog", eDbgLevelInfo, "No log directory %ls. Logging disabled.", strVLogPath.Get());
  305. g_bLoggingDisabled = TRUE;
  306. return FALSE;
  307. }
  308. //
  309. // Next, check for the existence of session.log. If it's not there,
  310. // we're not logging
  311. //
  312. g_strSessionLog = strVLogPath;
  313. g_strSessionLog += L"\\session.log";
  314. if (GetFileAttributesW(g_strSessionLog.Get()) == -1) {
  315. DPF("VerifierLog", eDbgLevelInfo, "No session log file '%ls'. Logging disabled.", g_strSessionLog.Get());
  316. g_bLoggingDisabled = TRUE;
  317. return FALSE;
  318. }
  319. //
  320. // get the process log file name
  321. //
  322. if (strProcessPath.GetModuleFileNameW(NULL) == 0) {
  323. DPF("VerifierLog", eDbgLevelError, "Cannot get module file name.");
  324. g_bLoggingDisabled = TRUE;
  325. return FALSE;
  326. }
  327. //
  328. // strip out just the name minus path and extension
  329. //
  330. strProcessPath.SplitPath(NULL, NULL, &strProcessName, NULL);
  331. //
  332. // combine into log name, find first available
  333. //
  334. nTemp = 0;
  335. do {
  336. g_strProcessLog.Format(L"%ls\\%ls%d.%ls", strVLogPath.Get(), strProcessName.Get(), nTemp, L"log");
  337. nTemp++;
  338. } while (GetFileAttributesW(g_strProcessLog.Get()) != -1);
  339. //
  340. // Convert the path to the log file from DOS to NT.
  341. //
  342. bSuccess = RtlDosPathNameToNtPathName_U(g_strProcessLog.Get(), &strLogFile, NULL, NULL);
  343. if (!bSuccess) {
  344. DPF("VerifierLog",
  345. eDbgLevelError,
  346. "Failed to convert log file '%ls' to NT path",
  347. g_strProcessLog.Get());
  348. return FALSE;
  349. }
  350. //
  351. // Attempt to get a handle to our log file.
  352. // Truncate the file if it already exists.
  353. //
  354. InitializeObjectAttributes(&ObjectAttributes,
  355. &strLogFile,
  356. OBJ_CASE_INSENSITIVE,
  357. NULL,
  358. NULL);
  359. status = NtCreateFile(&hFile,
  360. GENERIC_ALL | SYNCHRONIZE,
  361. &ObjectAttributes,
  362. &IoStatusBlock,
  363. NULL,
  364. FILE_ATTRIBUTE_NORMAL,
  365. 0,
  366. FILE_OPEN_IF,
  367. FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
  368. NULL,
  369. 0);
  370. RtlFreeUnicodeString(&strLogFile);
  371. if (!NT_SUCCESS(status)) {
  372. DPF("VerifierLog", eDbgLevelError, "0x%X Failed to open log file %ls",
  373. status, g_strProcessLog.Get());
  374. g_bLoggingDisabled = TRUE;
  375. return FALSE;
  376. }
  377. NtClose(hFile);
  378. //
  379. // put the info in the session log and the process log
  380. //
  381. g_pData->bLoggingDisabled = FALSE;
  382. g_bLoggingDisabled = FALSE;
  383. //
  384. // I realize these pointers point to process-specific memory, but since
  385. // this mapping is only shared by this process, it seems safe.
  386. //
  387. wcscpy(g_pData->szProcessLog, g_strProcessLog);
  388. wcscpy(g_pData->szSessionLog, g_strSessionLog);
  389. GetLocalTime(&LocalTime);
  390. strTime.Format(L"%d/%d/%d %d:%02d:%02d",
  391. LocalTime.wMonth,
  392. LocalTime.wDay,
  393. LocalTime.wYear,
  394. LocalTime.wHour,
  395. LocalTime.wMinute,
  396. LocalTime.wSecond
  397. );
  398. strTemp.Format(L"# LOG_BEGIN %ls '%ls' '%ls'", strTime.Get(),
  399. strProcessPath.Get(), g_strProcessLog.Get());
  400. szTemp = strTemp.GetAnsi();
  401. WriteToProcessLog(szTemp);
  402. WriteToSessionLog(szTemp);
  403. return TRUE;
  404. }
  405. /*++
  406. Function Description:
  407. clean up all our shared file resources
  408. History:
  409. 04/26/2001 dmunsil Created
  410. --*/
  411. void
  412. ReleaseVerifierLogSupport(void)
  413. {
  414. g_bLoggingDisabled = TRUE;
  415. if (g_pData) {
  416. UnmapViewOfFile(g_pData);
  417. g_pData = NULL;
  418. if (g_hMap) {
  419. CloseHandle(g_hMap);
  420. g_hMap = NULL;
  421. }
  422. }
  423. }
  424. /*++
  425. Function Description:
  426. Logs a problem that the verifier has found
  427. History:
  428. 04/26/2001 dmunsil Created
  429. --*/
  430. void
  431. CVerifierLog::VLog(
  432. VLOG_LEVEL eLevel,
  433. DWORD dwLogNum,
  434. LPCSTR pszFmt,
  435. ...
  436. )
  437. {
  438. char szT[1024];
  439. char *szTemp;
  440. int nLen;
  441. int nRemain;
  442. va_list arglist;
  443. DWORD dwOffset = 0;
  444. WCHAR szModule[256];
  445. if (g_bLoggingDisabled) {
  446. return;
  447. }
  448. GetCallingModule(szModule, 256, &dwOffset);
  449. _snprintf(szT, 1023, "| %ls %d | %d %ls %X'", m_strShimName.Get(), dwLogNum, eLevel,
  450. szModule, dwOffset);
  451. nLen = lstrlen(szT);
  452. szTemp = szT + nLen;
  453. nRemain = 1023 - nLen;
  454. if (nRemain > 0) {
  455. va_start(arglist, pszFmt);
  456. _vsnprintf(szTemp, nRemain, pszFmt, arglist);
  457. va_end(arglist);
  458. }
  459. szT[1023] = 0;
  460. WriteToProcessLog(szT);
  461. if (g_bLogBreakIn) {
  462. OutputDebugString(szT);
  463. DbgBreakPoint();
  464. }
  465. }
  466. /*++
  467. Function Description:
  468. Dumps the header for a shim that tells how many log entries it has.
  469. History:
  470. 04/26/2001 dmunsil Created
  471. --*/
  472. void
  473. CVerifierLog::DumpShimHeader(void)
  474. {
  475. char szT[1024];
  476. if (m_bHeaderDumped) {
  477. return;
  478. }
  479. _snprintf(szT, 1023, "# SHIM_BEGIN %ls %d", m_strShimName.Get(), m_dwEntries);
  480. WriteToProcessLog(szT);
  481. m_bHeaderDumped = TRUE;
  482. }
  483. /*++
  484. Function Description:
  485. Dumps into the log the text string associated with
  486. each log entry. These are dumped before logging begins, just to
  487. provide the strings necessary for the verifier UI to display them
  488. Return Value:
  489. History:
  490. 04/26/2001 dmunsil Created
  491. --*/
  492. void
  493. CVerifierLog::DumpLogEntry(
  494. DWORD dwLogNum,
  495. UINT unResTitle,
  496. UINT unResDescription,
  497. UINT unResURL
  498. )
  499. {
  500. WCHAR szRes[1024];
  501. char szLine[4096];
  502. if (g_bLoggingDisabled) {
  503. return;
  504. }
  505. //
  506. // dump the header, if necessary
  507. //
  508. DumpShimHeader();
  509. if (!VLogLoadString(g_hinstDll, unResTitle, szRes, 1024)) {
  510. DPF("VerifierLog", eDbgLevelError, "No string resource found for title.");
  511. szRes[0] = 0;
  512. }
  513. sprintf(szLine, "# LOGENTRY %ls %d '%ls", m_strShimName.Get(), dwLogNum, szRes);
  514. WriteToProcessLog(szLine);
  515. if (!VLogLoadString(g_hinstDll, unResDescription, szRes, 1024)) {
  516. DPF("VerifierLog", eDbgLevelWarning, "No string resource found for description.");
  517. szRes[0] = 0;
  518. }
  519. if (szRes[0]) {
  520. WriteToProcessLog("# DESCRIPTION BEGIN");
  521. sprintf(szLine, "%ls", szRes);
  522. WriteToProcessLog(szLine);
  523. WriteToProcessLog("# DESCRIPTION END");
  524. }
  525. if (!VLogLoadString(g_hinstDll, unResURL, szRes, 1024)) {
  526. DPF("VerifierLog", eDbgLevelWarning, "No string resource found for URL.");
  527. szRes[0] = 0;
  528. }
  529. if (szRes[0]) {
  530. sprintf(szLine, "# URL '%ls", szRes);
  531. WriteToProcessLog(szLine);
  532. }
  533. }
  534. /*++
  535. Function Description:
  536. Writes a line of text to the process log file
  537. Return Value:
  538. History:
  539. 04/26/2001 dmunsil Created
  540. 09/21/2001 rparsons Converted to NT calls
  541. --*/
  542. void
  543. WriteToProcessLog(
  544. LPCSTR szLine
  545. )
  546. {
  547. int nLen = 0;
  548. OBJECT_ATTRIBUTES ObjectAttributes;
  549. IO_STATUS_BLOCK IoStatusBlock;
  550. LARGE_INTEGER liOffset;
  551. UNICODE_STRING strLogFile = {0};
  552. NTSTATUS status;
  553. char szNewLine[] = "\r\n";
  554. HANDLE hFile = INVALID_HANDLE_VALUE;
  555. BOOL bSuccess = FALSE;
  556. if (g_bLoggingDisabled) {
  557. return;
  558. }
  559. //
  560. // Convert the path to the log file from DOS to NT.
  561. //
  562. bSuccess = RtlDosPathNameToNtPathName_U(g_strProcessLog.Get(), &strLogFile, NULL, NULL);
  563. if (!bSuccess) {
  564. DPF("VerifierLog",
  565. eDbgLevelError,
  566. "[WriteToProcessLog] Failed to convert log file '%ls' to NT path",
  567. g_strProcessLog.Get());
  568. return;
  569. }
  570. //
  571. // Attempt to get a handle to our log file.
  572. //
  573. InitializeObjectAttributes(&ObjectAttributes,
  574. &strLogFile,
  575. OBJ_CASE_INSENSITIVE,
  576. NULL,
  577. NULL);
  578. status = NtCreateFile(&hFile,
  579. FILE_APPEND_DATA | SYNCHRONIZE,
  580. &ObjectAttributes,
  581. &IoStatusBlock,
  582. NULL,
  583. FILE_ATTRIBUTE_NORMAL,
  584. 0,
  585. FILE_OPEN,
  586. FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
  587. NULL,
  588. 0);
  589. RtlFreeUnicodeString(&strLogFile);
  590. if (!NT_SUCCESS(status)) {
  591. DPF("VerifierLog", eDbgLevelError, "[WriteToProcessLog] 0x%X Failed to open log file %ls",
  592. status, g_strProcessLog.Get());
  593. return;
  594. }
  595. //
  596. // Make sure we have no '\n' or '\r' at the end of the string.
  597. //
  598. nLen = lstrlen(szLine);
  599. while (nLen && (szLine[nLen - 1] == '\n' || szLine[nLen - 1] == '\r')) {
  600. nLen--;
  601. }
  602. //
  603. // Write the data out to the file.
  604. //
  605. IoStatusBlock.Status = 0;
  606. IoStatusBlock.Information = 0;
  607. liOffset.LowPart = 0;
  608. liOffset.HighPart = 0;
  609. //
  610. // Enter a critical section to ensure that log entries are in the proper order.
  611. //
  612. RtlEnterCriticalSection(&g_csLogging);
  613. status = NtWriteFile(hFile,
  614. NULL,
  615. NULL,
  616. NULL,
  617. &IoStatusBlock,
  618. (PVOID)szLine,
  619. (ULONG)nLen,
  620. &liOffset,
  621. NULL);
  622. if (!NT_SUCCESS(status)) {
  623. DPF("VerifierLog", eDbgLevelError, "[WriteToProcessLog] 0x%X Failed to make entry in log file",
  624. status);
  625. goto exit;
  626. }
  627. //
  628. // Now write a new line to the log file.
  629. //
  630. IoStatusBlock.Status = 0;
  631. IoStatusBlock.Information = 0;
  632. liOffset.LowPart = 0;
  633. liOffset.HighPart = 0;
  634. nLen = lstrlen(szNewLine);
  635. status = NtWriteFile(hFile,
  636. NULL,
  637. NULL,
  638. NULL,
  639. &IoStatusBlock,
  640. (PVOID)szNewLine,
  641. (ULONG)nLen,
  642. &liOffset,
  643. NULL);
  644. if (!NT_SUCCESS(status)) {
  645. DPF("VerifierLog", eDbgLevelError, "[WriteToProcessLog] 0x%X Failed to write new line to log file",
  646. status);
  647. goto exit;
  648. }
  649. //
  650. // Dump it out to the debugger on checked builds.
  651. //
  652. #if DBG
  653. DebugPrintf("VerifierLog", eDbgLevelInfo, szLine);
  654. DebugPrintf("VerifierLog", eDbgLevelInfo, szNewLine);
  655. #endif // DBG
  656. exit:
  657. if (INVALID_HANDLE_VALUE != hFile) {
  658. NtClose(hFile);
  659. hFile = INVALID_HANDLE_VALUE;
  660. }
  661. RtlLeaveCriticalSection(&g_csLogging);
  662. }
  663. /*++
  664. Function Description:
  665. Writes a line of text to the session log file
  666. Return Value:
  667. History:
  668. 04/26/2001 dmunsil Created
  669. 09/21/2001 rparsons Converted to NT calls
  670. --*/
  671. void
  672. WriteToSessionLog(
  673. LPCSTR szLine
  674. )
  675. {
  676. int nLen = 0;
  677. OBJECT_ATTRIBUTES ObjectAttributes;
  678. IO_STATUS_BLOCK IoStatusBlock;
  679. LARGE_INTEGER liOffset;
  680. UNICODE_STRING strLogFile = {0};
  681. NTSTATUS status;
  682. char szNewLine[] = "\r\n";
  683. HANDLE hFile = INVALID_HANDLE_VALUE;
  684. BOOL bSuccess = FALSE;
  685. if (g_bLoggingDisabled) {
  686. return;
  687. }
  688. //
  689. // Convert the path to the log file from DOS to NT.
  690. //
  691. bSuccess = RtlDosPathNameToNtPathName_U(g_strSessionLog.Get(), &strLogFile, NULL, NULL);
  692. if (!bSuccess) {
  693. DPF("VerifierLog",
  694. eDbgLevelError,
  695. "[WriteToSessionLog] Failed to convert log file '%ls' to NT path",
  696. g_strSessionLog.Get());
  697. return;
  698. }
  699. //
  700. // Attempt to get a handle to our log file.
  701. //
  702. InitializeObjectAttributes(&ObjectAttributes,
  703. &strLogFile,
  704. OBJ_CASE_INSENSITIVE,
  705. NULL,
  706. NULL);
  707. status = NtCreateFile(&hFile,
  708. FILE_APPEND_DATA | SYNCHRONIZE,
  709. &ObjectAttributes,
  710. &IoStatusBlock,
  711. NULL,
  712. FILE_ATTRIBUTE_NORMAL,
  713. 0,
  714. FILE_OPEN,
  715. FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
  716. NULL,
  717. 0);
  718. RtlFreeUnicodeString(&strLogFile);
  719. if (!NT_SUCCESS(status)) {
  720. DPF("VerifierLog", eDbgLevelError, "[WriteToSessionLog] 0x%X Failed to open log file %ls",
  721. status, g_strProcessLog.Get());
  722. return;
  723. }
  724. //
  725. // Make sure we have no '\n' or '\r' at the end of the string.
  726. //
  727. nLen = lstrlen(szLine);
  728. while (nLen && (szLine[nLen - 1] == '\n' || szLine[nLen - 1] == '\r')) {
  729. nLen--;
  730. }
  731. //
  732. // Write the data out to the file.
  733. //
  734. IoStatusBlock.Status = 0;
  735. IoStatusBlock.Information = 0;
  736. liOffset.LowPart = 0;
  737. liOffset.HighPart = 0;
  738. //
  739. // Enter a critical section to ensure that log entries are in the proper order.
  740. //
  741. RtlEnterCriticalSection(&g_csLogging);
  742. status = NtWriteFile(hFile,
  743. NULL,
  744. NULL,
  745. NULL,
  746. &IoStatusBlock,
  747. (PVOID)szLine,
  748. (ULONG)nLen,
  749. &liOffset,
  750. NULL);
  751. if (!NT_SUCCESS(status)) {
  752. DPF("VerifierLog", eDbgLevelError, "[WriteToSessionLog] 0x%X Failed to make entry in log file",
  753. status);
  754. goto exit;
  755. }
  756. //
  757. // Now write a new line to the log file.
  758. //
  759. IoStatusBlock.Status = 0;
  760. IoStatusBlock.Information = 0;
  761. liOffset.LowPart = 0;
  762. liOffset.HighPart = 0;
  763. nLen = lstrlen(szNewLine);
  764. status = NtWriteFile(hFile,
  765. NULL,
  766. NULL,
  767. NULL,
  768. &IoStatusBlock,
  769. (PVOID)szNewLine,
  770. (ULONG)nLen,
  771. &liOffset,
  772. NULL);
  773. if (!NT_SUCCESS(status)) {
  774. DPF("VerifierLog", eDbgLevelError, "[WriteToSessionLog] 0x%X Failed to write new line to log file",
  775. status);
  776. goto exit;
  777. }
  778. //
  779. // Dump it out to the debugger on checked builds.
  780. //
  781. #if DBG
  782. DebugPrintf("VerifierLog", eDbgLevelInfo, szLine);
  783. DebugPrintf("VerifierLog", eDbgLevelInfo, szNewLine);
  784. #endif // DBG
  785. exit:
  786. if (INVALID_HANDLE_VALUE != hFile) {
  787. NtClose(hFile);
  788. hFile = INVALID_HANDLE_VALUE;
  789. }
  790. RtlLeaveCriticalSection(&g_csLogging);
  791. }
  792. int VLogLoadString(
  793. HMODULE hModule,
  794. UINT wID,
  795. LPWSTR lpBuffer, // Unicode buffer
  796. int cchBufferMax)
  797. {
  798. HRSRC hResInfo;
  799. HANDLE hStringSeg;
  800. LPWSTR lpsz;
  801. int cch;
  802. /*
  803. * Make sure the parms are valid.
  804. */
  805. if (lpBuffer == NULL) {
  806. DPF("VLogLoadString", eDbgLevelWarning, "LoadStringOrError: lpBuffer == NULL");
  807. return 0;
  808. }
  809. cch = 0;
  810. /*
  811. * String Tables are broken up into 16 string segments. Find the segment
  812. * containing the string we are interested in.
  813. */
  814. if (hResInfo = FindResourceW(hModule, (LPWSTR)ULongToPtr( ((LONG)(((USHORT)wID >> 4) + 1)) ), (LPWSTR)RT_STRING)) {
  815. /*
  816. * Load that segment.
  817. */
  818. hStringSeg = LoadResource(hModule, hResInfo);
  819. /*
  820. * Lock the resource.
  821. */
  822. if (lpsz = (LPWSTR)LockResource(hStringSeg)) {
  823. /*
  824. * Move past the other strings in this segment.
  825. * (16 strings in a segment -> & 0x0F)
  826. */
  827. wID &= 0x0F;
  828. while (TRUE) {
  829. cch = *((WCHAR *)lpsz++); // PASCAL like string count
  830. // first WCHAR is count of WCHARs
  831. if (wID-- == 0) break;
  832. lpsz += cch; // Step to start if next string
  833. }
  834. /*
  835. * chhBufferMax == 0 means return a pointer to the read-only resource buffer.
  836. */
  837. if (cchBufferMax == 0) {
  838. *(LPWSTR *)lpBuffer = lpsz;
  839. } else {
  840. /*
  841. * Account for the NULL
  842. */
  843. cchBufferMax--;
  844. /*
  845. * Don't copy more than the max allowed.
  846. */
  847. if (cch > cchBufferMax)
  848. cch = cchBufferMax;
  849. /*
  850. * Copy the string into the buffer.
  851. */
  852. RtlCopyMemory(lpBuffer, lpsz, cch*sizeof(WCHAR));
  853. }
  854. /*
  855. * Unlock resource, but don't free it - better performance this
  856. * way.
  857. */
  858. UnlockResource(hStringSeg);
  859. }
  860. }
  861. /*
  862. * Append a NULL.
  863. */
  864. if (cchBufferMax != 0) {
  865. lpBuffer[cch] = 0;
  866. }
  867. return cch;
  868. }
  869. }; // end of namespace ShimLib