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.

846 lines
22 KiB

  1. //============================================================================
  2. // Copyright (c) 1995, Microsoft Corporation
  3. //
  4. // File: client.c
  5. //
  6. // History:
  7. // Abolade Gbadegesin July-25-1995 Created
  8. //
  9. // Client struct routines and I/O routines for tracing dll
  10. //============================================================================
  11. #include <nt.h>
  12. #include <ntrtl.h>
  13. #include <nturtl.h>
  14. #include <windows.h>
  15. #include <stdlib.h>
  16. #include <rtutils.h>
  17. #include "trace.h"
  18. //#define STRSAFE_LIB
  19. #include <strsafe.h>
  20. //
  21. // assumes server is locked for writing
  22. //
  23. DWORD
  24. TraceCreateClient(
  25. LPTRACE_CLIENT *lplpclient
  26. ) {
  27. DWORD dwErr;
  28. LPTRACE_CLIENT lpclient;
  29. lpclient = HeapAlloc(GetProcessHeap(), 0, sizeof(TRACE_CLIENT));
  30. if (lpclient == NULL) {
  31. return ERROR_NOT_ENOUGH_MEMORY;
  32. }
  33. //
  34. // initialize fields in the client structure
  35. //
  36. lpclient->TC_ClientID = MAX_CLIENT_COUNT;
  37. lpclient->TC_Flags = 0;
  38. lpclient->TC_File = NULL;
  39. lpclient->TC_Console = NULL;
  40. lpclient->TC_ConfigKey = NULL;
  41. lpclient->TC_ConfigEvent = NULL;
  42. lpclient->TC_MaxFileSize = DEF_MAXFILESIZE;
  43. ZeroMemory(lpclient->TC_ClientNameA, MAX_CLIENTNAME_LENGTH * sizeof(CHAR));
  44. ZeroMemory(lpclient->TC_ClientNameW, MAX_CLIENTNAME_LENGTH * sizeof(WCHAR));
  45. if (ExpandEnvironmentStrings(
  46. DEF_FILEDIRECTORY, lpclient->TC_FileDir,
  47. MAX_PATH)==0)
  48. {
  49. return GetLastError();
  50. }
  51. #ifdef UNICODE
  52. // below is strsafe
  53. wcstombs(
  54. lpclient->TC_FileDirA, lpclient->TC_FileDirW,
  55. lstrlenW(lpclient->TC_FileDirW) + 1
  56. );
  57. #else
  58. //below is strsafe
  59. mbstowcs(
  60. lpclient->TC_FileDirW, lpclient->TC_FileDirA,
  61. lstrlenA(lpclient->TC_FileDirA) + 1
  62. );
  63. #endif
  64. dwErr = TRACE_STARTUP_LOCKING(lpclient);
  65. if (dwErr != NO_ERROR) {
  66. HeapFree(GetProcessHeap(), 0, lpclient);
  67. lpclient = NULL;
  68. }
  69. // why interlocked..
  70. InterlockedExchangePointer(lplpclient, lpclient);
  71. return dwErr;
  72. }
  73. //
  74. // assumes server is locked for writing and client is locked for writing
  75. //
  76. DWORD
  77. TraceDeleteClient(
  78. LPTRACE_SERVER lpserver,
  79. LPTRACE_CLIENT *lplpclient
  80. ) {
  81. LPTRACE_CLIENT lpclient;
  82. if (lplpclient == NULL || *lplpclient == NULL) {
  83. return ERROR_INVALID_PARAMETER;
  84. }
  85. lpclient = *lplpclient;
  86. InterlockedExchangePointer(lplpclient, NULL);
  87. InterlockedExchange(lpserver->TS_FlagsCache + lpclient->TC_ClientID, 0);
  88. TRACE_CLEANUP_LOCKING(lpclient);
  89. //
  90. // closing this key will cause the event to be signalled
  91. // however, we hold the lock on the table so the server thread
  92. // will be blocked until the cleanup completes
  93. //
  94. if (lpclient->TC_ConfigKey != NULL) {
  95. RegCloseKey(lpclient->TC_ConfigKey);
  96. }
  97. //
  98. // if server thread created, then leave it to server to close the handle,
  99. // else close the handle here
  100. //
  101. if (lpclient->TC_ConfigEvent != NULL) {
  102. if (lpserver->TS_Flags & TRACEFLAGS_SERVERTHREAD)
  103. {
  104. PLIST_ENTRY ple = (PLIST_ENTRY) HeapAlloc(GetProcessHeap(), 0,
  105. sizeof(LIST_ENTRY)
  106. +sizeof(HANDLE));
  107. if (ple)
  108. {
  109. HANDLE *hEvent = (HANDLE *)(ple + 1);
  110. *hEvent = lpclient->TC_ConfigEvent;
  111. InsertHeadList(&lpserver->TS_ClientEventsToClose,
  112. ple);
  113. }
  114. }
  115. else
  116. {
  117. CloseHandle(lpclient->TC_ConfigEvent);
  118. }
  119. }
  120. if (TRACE_CLIENT_USES_CONSOLE(lpclient)) {
  121. TraceCloseClientConsole(lpserver, lpclient);
  122. }
  123. if (TRACE_CLIENT_USES_FILE(lpclient)) {
  124. TraceCloseClientFile(lpclient);
  125. }
  126. HeapFree(GetProcessHeap(), 0, lpclient);
  127. return 0;
  128. }
  129. //
  130. // assumes server is locked for reading or for writing
  131. //
  132. LPTRACE_CLIENT
  133. TraceFindClient(
  134. LPTRACE_SERVER lpserver,
  135. LPCTSTR lpszClient
  136. ) {
  137. DWORD dwClient;
  138. LPTRACE_CLIENT *lplpc, *lplpcstart, *lplpcend;
  139. lplpcstart = lpserver->TS_ClientTable;
  140. lplpcend = lplpcstart + MAX_CLIENT_COUNT;
  141. for (lplpc = lplpcstart; lplpc < lplpcend; lplpc++) {
  142. if (*lplpc != NULL &&
  143. lstrcmp((*lplpc)->TC_ClientName, lpszClient) == 0) {
  144. break;
  145. }
  146. }
  147. return (lplpc < lplpcend) ? *lplpc : NULL;
  148. }
  149. //
  150. // assumes that the server is locked for writing,
  151. // and that the client is locked for writing
  152. // also assumes the client is not already a console client
  153. //
  154. DWORD TraceOpenClientConsole(LPTRACE_SERVER lpserver,
  155. LPTRACE_CLIENT lpclient) {
  156. DWORD dwErr;
  157. COORD screen;
  158. HANDLE hConsole;
  159. //
  160. // if all console tracing is disabled, do nothing
  161. //
  162. if ((lpserver->TS_Flags & TRACEFLAGS_USECONSOLE) == 0) {
  163. return 0;
  164. }
  165. //
  166. // create the console if it isn't already created
  167. //
  168. if (lpserver->TS_Console==NULL || lpserver->TS_Console==INVALID_HANDLE_VALUE) {
  169. //
  170. // allocate a console and set the buffer size
  171. //
  172. if (AllocConsole() == 0)
  173. {
  174. dwErr = GetLastError();
  175. if (dwErr != ERROR_ACCESS_DENIED)
  176. {
  177. lpserver->TS_Console = INVALID_HANDLE_VALUE;
  178. return dwErr;
  179. }
  180. }
  181. else
  182. {
  183. lpserver->TS_ConsoleCreated = TRUE;
  184. }
  185. lpserver->TS_Console = GetStdHandle(STD_INPUT_HANDLE);
  186. if (lpserver->TS_Console == INVALID_HANDLE_VALUE )
  187. return GetLastError();
  188. }
  189. //
  190. // allocate a console for this client
  191. //
  192. hConsole = CreateConsoleScreenBuffer(
  193. GENERIC_READ | GENERIC_WRITE, 0, NULL,
  194. CONSOLE_TEXTMODE_BUFFER, NULL
  195. );
  196. if (hConsole == INVALID_HANDLE_VALUE) { return GetLastError(); }
  197. //
  198. // set the buffer to the standard size
  199. // and save the console buffer handle
  200. //
  201. screen.X = DEF_SCREENBUF_WIDTH;
  202. screen.Y = DEF_SCREENBUF_HEIGHT;
  203. SetConsoleScreenBufferSize(hConsole, screen);
  204. lpclient->TC_Console = hConsole;
  205. //
  206. // see if there was a previous console client;
  207. // if not, set this new one's screen buffer to be
  208. // the active screen buffer
  209. //
  210. if (lpserver->TS_ConsoleOwner == MAX_CLIENT_COUNT) {
  211. TraceUpdateConsoleOwner(lpserver, 1);
  212. }
  213. return 0;
  214. }
  215. //
  216. // assumes that the server is locked for writing,
  217. // and that the client is locked for writing
  218. // also assumes the client is already a console client
  219. //
  220. DWORD
  221. TraceCloseClientConsole(
  222. LPTRACE_SERVER lpserver,
  223. LPTRACE_CLIENT lpclient
  224. ) {
  225. HANDLE hConsole;
  226. //
  227. // if all console tracing is disabled, do nothing
  228. //
  229. if ((lpserver->TS_Flags & TRACEFLAGS_USECONSOLE) == 0) {
  230. return 0;
  231. }
  232. //
  233. // close the client's screen buffer and associated handles
  234. //
  235. if (lpclient->TC_Console!=NULL && lpclient->TC_Console!=INVALID_HANDLE_VALUE) {
  236. CloseHandle(lpclient->TC_Console);
  237. }
  238. lpclient->TC_Console = NULL;
  239. //
  240. // if the client owned the screen, find another owner
  241. //
  242. if (lpserver->TS_ConsoleOwner == lpclient->TC_ClientID) {
  243. TraceUpdateConsoleOwner(lpserver, 1);
  244. }
  245. //
  246. // if no owner was found, free the server's console
  247. //
  248. if (lpserver->TS_ConsoleOwner == MAX_CLIENT_COUNT ||
  249. lpserver->TS_ConsoleOwner == lpclient->TC_ClientID) {
  250. lpserver->TS_ConsoleOwner = MAX_CLIENT_COUNT;
  251. if (lpserver->TS_Console!=NULL && lpserver->TS_Console!=INVALID_HANDLE_VALUE) {
  252. CloseHandle(lpserver->TS_Console);
  253. }
  254. if (lpserver->TS_ConsoleCreated == TRUE)
  255. {
  256. FreeConsole();
  257. lpserver->TS_ConsoleCreated = FALSE;
  258. }
  259. lpserver->TS_Console = NULL;
  260. }
  261. return 0;
  262. }
  263. //
  264. // assumes that the server is locked for reading or writing
  265. // and that the client is locked for writing
  266. //
  267. DWORD
  268. TraceCreateClientFile(
  269. LPTRACE_CLIENT lpclient
  270. ) {
  271. DWORD dwErr;
  272. HANDLE hFile;
  273. LPOVERLAPPED lpovl;
  274. TCHAR szFilename[MAX_PATH];
  275. HRESULT hrResult;
  276. //
  277. // create the directory in case it doesn't exist
  278. //
  279. if (CreateDirectory(lpclient->TC_FileDir, NULL) != NO_ERROR) {
  280. return GetLastError();
  281. }
  282. //
  283. // figure out the file name
  284. //
  285. hrResult = StringCchCopy(szFilename, MAX_PATH, lpclient->TC_FileDir);
  286. if (FAILED(hrResult))
  287. return HRESULT_CODE(hrResult);
  288. hrResult = StringCchCat(szFilename, MAX_PATH, STR_DIRSEP);
  289. if (FAILED(hrResult))
  290. return HRESULT_CODE(hrResult);
  291. hrResult = StringCchCat(szFilename, MAX_PATH, lpclient->TC_ClientName);
  292. if (FAILED(hrResult))
  293. return HRESULT_CODE(hrResult);
  294. hrResult = StringCchCat(szFilename, MAX_PATH, STR_LOGEXT);
  295. if (FAILED(hrResult))
  296. return HRESULT_CODE(hrResult);
  297. //
  298. // open the file, disabling write sharing
  299. //
  300. hFile = CreateFile(
  301. szFilename, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ,
  302. NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL
  303. );
  304. if (hFile == INVALID_HANDLE_VALUE) {
  305. return GetLastError();
  306. }
  307. SetFilePointer(hFile, 0, NULL, FILE_END);
  308. lpclient->TC_File = hFile;
  309. return 0;
  310. }
  311. //
  312. // assumes that the server is locked for reading or writing
  313. // and that the client is locked for writing
  314. //
  315. DWORD
  316. TraceMoveClientFile(
  317. LPTRACE_CLIENT lpclient
  318. ) {
  319. TCHAR szDestname[MAX_PATH], szSrcname[MAX_PATH];
  320. HRESULT hrResult;
  321. hrResult = StringCchCopy(szSrcname, MAX_PATH, lpclient->TC_FileDir);
  322. if (FAILED(hrResult))
  323. return HRESULT_CODE(hrResult);
  324. hrResult = StringCchCat(szSrcname, MAX_PATH, STR_DIRSEP);
  325. if (FAILED(hrResult))
  326. return HRESULT_CODE(hrResult);
  327. hrResult = StringCchCat(szSrcname, MAX_PATH, lpclient->TC_ClientName);
  328. if (FAILED(hrResult))
  329. return HRESULT_CODE(hrResult);
  330. hrResult = StringCchCopy(szDestname, MAX_PATH, szSrcname);
  331. if (FAILED(hrResult))
  332. return HRESULT_CODE(hrResult);
  333. hrResult = StringCchCat(szSrcname, MAX_PATH, STR_LOGEXT);
  334. if (FAILED(hrResult))
  335. return HRESULT_CODE(hrResult);
  336. hrResult = StringCchCat(szDestname, MAX_PATH, STR_OLDEXT);
  337. if (FAILED(hrResult))
  338. return HRESULT_CODE(hrResult);
  339. //
  340. // close the file handle if it is open
  341. //
  342. TraceCloseClientFile(lpclient);
  343. //
  344. // do the move
  345. //
  346. if (MoveFileEx(
  347. szSrcname, szDestname,
  348. MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED
  349. ) == 0)
  350. {
  351. DeleteFile(szSrcname);
  352. }
  353. //
  354. // re-open the log file
  355. //
  356. return TraceCreateClientFile(lpclient);
  357. }
  358. //
  359. // assumes that the server is locked for reading or writing
  360. // and that the client is locked for writing
  361. //
  362. DWORD
  363. TraceCloseClientFile(
  364. LPTRACE_CLIENT lpclient
  365. ) {
  366. if (lpclient->TC_File != NULL) {
  367. CloseHandle(lpclient->TC_File);
  368. lpclient->TC_File = NULL;
  369. }
  370. return 0;
  371. }
  372. //
  373. // assumes that the server is locked for reading or writing
  374. // and that the client is locked for reading
  375. // Return: 0 if error
  376. //
  377. DWORD
  378. TraceWriteOutput(
  379. LPTRACE_SERVER lpserver,
  380. LPTRACE_CLIENT lpclient,
  381. DWORD dwFlags,
  382. LPCTSTR lpszOutput
  383. ) {
  384. BOOL bSuccess=TRUE;
  385. DWORD dwFileMask, dwConsoleMask;
  386. DWORD dwErr, dwFileSize, dwBytesToWrite, dwBytesWritten, dwChars;
  387. dwBytesWritten = 0;
  388. dwBytesToWrite = lstrlen(lpszOutput) * sizeof(TCHAR);//size in bytes
  389. dwFileMask = dwConsoleMask = 1;
  390. //
  391. // if the client uses output masking, compute the mask for this message
  392. //
  393. if (dwFlags & TRACE_USE_MASK) {
  394. dwFileMask = (dwFlags & lpclient->TC_FileMask);
  395. dwConsoleMask = (dwFlags & lpclient->TC_ConsoleMask);
  396. }
  397. if (TRACE_CLIENT_USES_FILE(lpclient) &&
  398. (dwFileMask != 0) && (lpclient->TC_File != NULL)
  399. && lpclient->TC_File !=INVALID_HANDLE_VALUE) {
  400. //
  401. // check the size of the file to see if it needs renaming
  402. //
  403. dwFileSize = GetFileSize(lpclient->TC_File, NULL);
  404. if (dwFileSize > (lpclient->TC_MaxFileSize - dwBytesToWrite)) {
  405. TRACE_READ_TO_WRITELOCK(lpclient);
  406. dwFileSize = GetFileSize(*((HANDLE volatile *)&lpclient->TC_File), NULL);
  407. if (dwFileSize == INVALID_FILE_SIZE)
  408. {
  409. TRACE_WRITE_TO_READLOCK(lpclient);
  410. return 0;
  411. }
  412. if (dwFileSize > (lpclient->TC_MaxFileSize - dwBytesToWrite)) {
  413. //
  414. // move the existing file over and start with an empty one
  415. //
  416. dwErr = TraceMoveClientFile(lpclient);
  417. if (dwErr!=NO_ERROR) {
  418. TRACE_WRITE_TO_READLOCK(lpclient);
  419. return 0;
  420. }
  421. dwFileSize = 0;
  422. }
  423. else {
  424. if (lpclient->TC_File == NULL
  425. || lpclient->TC_File == INVALID_HANDLE_VALUE)
  426. {
  427. TRACE_WRITE_TO_READLOCK(lpclient);
  428. return 0;
  429. }
  430. // do nothing
  431. }
  432. TRACE_WRITE_TO_READLOCK(lpclient);
  433. }
  434. //
  435. // perform the write operation
  436. //
  437. if ((*((HANDLE volatile *)&lpclient->TC_File) != NULL)
  438. && (*((HANDLE volatile *)&lpclient->TC_File) !=
  439. INVALID_HANDLE_VALUE))
  440. {
  441. bSuccess =
  442. WriteFile(
  443. lpclient->TC_File, lpszOutput, dwBytesToWrite,
  444. &dwBytesWritten, NULL
  445. );
  446. }
  447. }
  448. if (TRACE_CLIENT_USES_CONSOLE(lpclient) &&
  449. dwConsoleMask != 0 && lpclient->TC_Console != NULL) {
  450. //
  451. // write to the console directly; this is less costly
  452. // than writing to a file, which is fortunate since we
  453. // cannot use completion ports with console handles
  454. //
  455. dwChars = dwBytesToWrite / sizeof(TCHAR);
  456. bSuccess =
  457. WriteConsole(
  458. lpclient->TC_Console, lpszOutput, dwChars, &dwChars, NULL
  459. );
  460. }
  461. return bSuccess? dwBytesWritten : 0;
  462. }
  463. //----------------------------------------------------------------------------
  464. // Function: TraceDumpLine
  465. //
  466. // Parameters:
  467. // LPTRACE_CLIENT lpclient pointer to client struct for caller
  468. // LPBYTE lpbBytes address of buffer to dump
  469. // DWORD dwLine length of line in bytes
  470. // DWORD dwGroup size of byte groupings
  471. // BOOL bPrefixAddr if TRUE, prefix lines with addresses
  472. // LPBYTE lpbPrefix address with which to prefix lines
  473. // LPTSTR lpszPrefix optional string with which to prefix lines
  474. // Returns:
  475. // count of bytes written. 0 if error.
  476. //----------------------------------------------------------------------------
  477. DWORD
  478. TraceDumpLine(
  479. LPTRACE_SERVER lpserver,
  480. LPTRACE_CLIENT lpclient,
  481. DWORD dwFlags,
  482. LPBYTE lpbBytes,
  483. DWORD dwLine,
  484. DWORD dwGroup,
  485. BOOL bPrefixAddr,
  486. LPBYTE lpbPrefix,
  487. LPCTSTR lpszPrefix
  488. ) {
  489. #define TRACE_DUMP_LINE_BUF_SIZE 256
  490. INT offset;
  491. LPTSTR lpszHex, lpszAscii;
  492. TCHAR szBuffer[TRACE_DUMP_LINE_BUF_SIZE] = TEXT("\r\n");
  493. TCHAR szAscii[BYTES_PER_DUMPLINE + 2] = TEXT("");
  494. TCHAR szHex[(3 * BYTES_PER_DUMPLINE) + 1] = TEXT("");
  495. TCHAR szDigits[] = TEXT("0123456789ABCDEF");
  496. HRESULT hrResult;
  497. //
  498. // prepend prefix string if necessary
  499. //
  500. if (lpszPrefix != NULL) {
  501. hrResult = StringCchCat(szBuffer,
  502. TRACE_DUMP_LINE_BUF_SIZE, lpszPrefix);
  503. if (FAILED(hrResult))
  504. return 0;
  505. }
  506. //
  507. // make sure that dwLine is not too big to overflow buffers later on
  508. //
  509. if (dwLine > BYTES_PER_DUMPLINE)
  510. return 0;
  511. //
  512. // prepend address if needed
  513. //
  514. if (bPrefixAddr) {
  515. LPTSTR lpsz;
  516. ULONG_PTR ulpAddress = (ULONG_PTR) lpbPrefix;
  517. ULONG i, ulCurLen;
  518. //
  519. // each line prints out a hex-digit
  520. // with the most-significant digit leftmost in the string
  521. // prepend address to lpsz[1]..lpsz[2*sizeof(ULONG_PTR)]
  522. //
  523. ulCurLen = lstrlen(szBuffer);
  524. if (ulCurLen + 2*sizeof(ULONG_PTR) + 3 > TRACE_DUMP_LINE_BUF_SIZE-1)
  525. return 0;
  526. lpsz = szBuffer + ulCurLen;
  527. for (i=0; i<2*sizeof(ULONG_PTR); i++) {
  528. lpsz[2*sizeof(ULONG_PTR)-i] = szDigits[ulpAddress & 0x0F];
  529. ulpAddress >>= 4;
  530. }
  531. lpsz[2*sizeof(ULONG_PTR) + 1] = TEXT(':');
  532. lpsz[2*sizeof(ULONG_PTR) + 2] = TEXT(' ');
  533. lpsz[2*sizeof(ULONG_PTR) + 3] = TEXT('\0');
  534. }
  535. lpszHex = szHex;
  536. lpszAscii = szAscii;
  537. //
  538. // rather than test the size of the grouping every time through
  539. // a loop, have a loop for each group size
  540. //
  541. switch(dwGroup) {
  542. //
  543. // single byte groupings
  544. //
  545. case 1: {
  546. while (dwLine >= sizeof(BYTE)) {
  547. //
  548. // print hex digits
  549. //
  550. *lpszHex++ = szDigits[*lpbBytes / 16];
  551. *lpszHex++ = szDigits[*lpbBytes % 16];
  552. *lpszHex++ = TEXT(' ');
  553. //
  554. // print ascii characters
  555. //
  556. *lpszAscii++ =
  557. (*lpbBytes >= 0x20 && *lpbBytes < 0x80) ? *lpbBytes
  558. : TEXT('.');
  559. ++lpbBytes;
  560. --dwLine;
  561. }
  562. break;
  563. }
  564. //
  565. // word-sized groupings
  566. //
  567. case 2: {
  568. WORD wBytes;
  569. BYTE loByte, hiByte;
  570. //
  571. // should already be aligned on a word boundary
  572. //
  573. while (dwLine >= sizeof(WORD)) {
  574. wBytes = *(LPWORD)lpbBytes;
  575. loByte = LOBYTE(wBytes);
  576. hiByte = HIBYTE(wBytes);
  577. // print hex digits
  578. *lpszHex++ = szDigits[hiByte / 16];
  579. *lpszHex++ = szDigits[hiByte % 16];
  580. *lpszHex++ = szDigits[loByte / 16];
  581. *lpszHex++ = szDigits[loByte % 16];
  582. *lpszHex++ = TEXT(' ');
  583. // print ascii characters
  584. *lpszAscii++ =
  585. (hiByte >= 0x20 && hiByte < 0x80) ? hiByte : TEXT('.');
  586. *lpszAscii++ =
  587. (loByte >= 0x20 && loByte < 0x80) ? loByte : TEXT('.');
  588. dwLine -= sizeof(WORD);
  589. lpbBytes += sizeof(WORD);
  590. }
  591. break;
  592. }
  593. //
  594. // double-word sized groupings
  595. //
  596. case 4: {
  597. DWORD dwBytes;
  598. BYTE loloByte, lohiByte, hiloByte, hihiByte;
  599. //
  600. // should already be aligned on a double-word boundary
  601. //
  602. while (dwLine >= sizeof(DWORD)) {
  603. dwBytes = *(LPDWORD)lpbBytes;
  604. hihiByte = HIBYTE(HIWORD(dwBytes));
  605. lohiByte = LOBYTE(HIWORD(dwBytes));
  606. hiloByte = HIBYTE(LOWORD(dwBytes));
  607. loloByte = LOBYTE(LOWORD(dwBytes));
  608. // print hex digits
  609. *lpszHex++ = szDigits[hihiByte / 16];
  610. *lpszHex++ = szDigits[hihiByte % 16];
  611. *lpszHex++ = szDigits[lohiByte / 16];
  612. *lpszHex++ = szDigits[lohiByte % 16];
  613. *lpszHex++ = szDigits[hiloByte / 16];
  614. *lpszHex++ = szDigits[hiloByte % 16];
  615. *lpszHex++ = szDigits[loloByte / 16];
  616. *lpszHex++ = szDigits[loloByte % 16];
  617. *lpszHex++ = TEXT(' ');
  618. // print ascii characters
  619. *lpszAscii++ =
  620. (hihiByte >= 0x20 && hihiByte < 0x80) ? hihiByte
  621. : TEXT('.');
  622. *lpszAscii++ =
  623. (lohiByte >= 0x20 && lohiByte < 0x80) ? lohiByte
  624. : TEXT('.');
  625. *lpszAscii++ =
  626. (hiloByte >= 0x20 && hiloByte < 0x80) ? hiloByte
  627. : TEXT('.');
  628. *lpszAscii++ =
  629. (loloByte >= 0x20 && loloByte < 0x80) ? loloByte
  630. : TEXT('.');
  631. // on to the next double-word
  632. dwLine -= sizeof(DWORD);
  633. lpbBytes += sizeof(DWORD);
  634. }
  635. break;
  636. }
  637. default:
  638. break;
  639. }
  640. *lpszHex = *lpszAscii = TEXT('\0');
  641. hrResult = StringCchCat(szBuffer, TRACE_DUMP_LINE_BUF_SIZE, szHex);
  642. if (FAILED(hrResult))
  643. return 0;
  644. hrResult = StringCchCat(szBuffer, TRACE_DUMP_LINE_BUF_SIZE, TEXT("|")); //ss not req
  645. if (FAILED(hrResult))
  646. return 0;
  647. hrResult = StringCchCat(szBuffer, TRACE_DUMP_LINE_BUF_SIZE, szAscii);
  648. if (FAILED(hrResult))
  649. return 0;
  650. hrResult = StringCchCat(szBuffer, TRACE_DUMP_LINE_BUF_SIZE, TEXT("|")); //ss not req
  651. if (FAILED(hrResult))
  652. return 0;
  653. return TraceWriteOutput(lpserver, lpclient, dwFlags, szBuffer);
  654. }