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.

1124 lines
21 KiB

  1. /*++
  2. Copyright (c) 1995-1997 Microsoft Corporation
  3. Module Name:
  4. fcache.cxx
  5. Abstract:
  6. This module contains the cache-related ntsd debugger extensions for
  7. Internet Information Server
  8. Author:
  9. Keith Moore (keithmo) 12-Aug-1997
  10. Revision History:
  11. Michael Courage (mcourage) 06-Feb-1998 Updated for rewritten cache
  12. --*/
  13. #include "inetdbgp.h"
  14. //
  15. // Worker routines.
  16. //
  17. PSTR
  18. DemuxToString(
  19. IN ULONG Demux
  20. )
  21. /*++
  22. Routine Description:
  23. Converts the specified demux value to a printable string.
  24. Arguments:
  25. Demux - The demux value to map.
  26. Return Value:
  27. PSTR - The printable string.
  28. --*/
  29. {
  30. switch( Demux ) {
  31. case RESERVED_DEMUX_START :
  32. return "RESERVED_DEMUX_START";
  33. case RESERVED_DEMUX_DIRECTORY_LISTING :
  34. return "RESERVED_DEMUX_DIRECTORY_LISTING";
  35. case RESERVED_DEMUX_ATOMIC_DIRECTORY_GUARD :
  36. return "RESERVED_DEMUX_ATOMIC_DIRECTORY_GUARD";
  37. case RESERVED_DEMUX_OPEN_FILE :
  38. return "RESERVED_DEMUX_OPEN_FILE";
  39. case RESERVED_DEMUX_URI_INFO :
  40. return "RESERVED_DEMUX_URI_INFO";
  41. case RESERVED_DEMUX_PHYSICAL_OPEN_FILE :
  42. return "RESERVED_DEMUX_PHYSICAL_OPEN_FILE";
  43. default :
  44. return "Unknown";
  45. }
  46. } // DemuxToString
  47. PSTR
  48. SignatureToString(
  49. IN DWORD CurrentSignature,
  50. IN DWORD ValidSignature,
  51. IN DWORD FreedSignature
  52. )
  53. /*++
  54. Routine Description:
  55. Determines an appropriate display string for the given signature.
  56. If the current signature matches the expected valid signature,
  57. then the string "OK" is returned. If the current signature matches
  58. the freed signature, then "FREED" is returned. Otherwise, "INVALID"
  59. is returned.
  60. Arguments:
  61. CurrentSignature - The current signature as retrieved from the
  62. object/structure.
  63. ValidSignature - The expected signature for a valid, in-use
  64. object/structure.
  65. FreedSignature - The signature assigned to the object/structure
  66. just before it is freed.
  67. Return Value:
  68. PSTR - "OK", "FREED", or "INVALID" as appropriate.
  69. --*/
  70. {
  71. if( CurrentSignature == ValidSignature ) {
  72. return "OK";
  73. }
  74. if( CurrentSignature == FreedSignature ) {
  75. return "FREED";
  76. }
  77. return "INVALID";
  78. } // SignatureToString
  79. PSTR
  80. BoolToString(
  81. IN BOOL Flag
  82. )
  83. /*++
  84. Routine Description:
  85. Maps the given BOOL to a displayable string.
  86. Arguments:
  87. Flag - The BOOL flag to map.
  88. Return Value:
  89. PSTR - "TRUE", "FALSE", or "INVALID" as appropriate.
  90. --*/
  91. {
  92. //
  93. // In general, explicit testing for TRUE is a Bad Thing, but in
  94. // this case, it's useful for catching invalid data.
  95. //
  96. if( Flag == TRUE ) {
  97. return "TRUE";
  98. }
  99. if( Flag == FALSE ) {
  100. return "FALSE";
  101. }
  102. return "INVALID";
  103. } // BoolToString
  104. PSTR
  105. FIStateToString(
  106. FI_STATE state
  107. )
  108. /*++
  109. Routine Description:
  110. Maps the given BOOL to a displayable string.
  111. Arguments:
  112. Flag - The BOOL flag to map.
  113. Return Value:
  114. PSTR - "TRUE", "FALSE", or "INVALID" as appropriate.
  115. --*/
  116. {
  117. switch (state) {
  118. case FI_UNINITIALIZED:
  119. return "FI_UNINITIALIZED";
  120. break;
  121. case FI_OPEN:
  122. return "FI_OPEN";
  123. break;
  124. case FI_FLUSHED:
  125. return "FI_FLUSHED";
  126. break;
  127. case FI_FLUSHED_UNINIT:
  128. return "FI_FLUSHED_UNINIT";
  129. break;
  130. case FI_CLOSED:
  131. return "FI_CLOSED";
  132. break;
  133. default:
  134. return "INVALID";
  135. }
  136. } // FIStateToString
  137. VOID
  138. DumpBlobHeader(
  139. IN PSTR Prefix,
  140. IN PBLOB_HEADER BlobHeader,
  141. IN ULONG_PTR ActualAddress
  142. )
  143. /*++
  144. Routine Description:
  145. Formats and dumps a specific BLOB_HEADER structure.
  146. Arguments:
  147. Prefix - The prefix to use before each printed line. This makes
  148. hierarchical object displays much prettier.
  149. BlobHeader - Points to a local copy of the structure.
  150. ActualAddress - The virtual address of the object in the debugee.
  151. Return Value:
  152. None.
  153. --*/
  154. {
  155. ULONG_PTR offset;
  156. UCHAR symbol[MAX_SYMBOL_LEN];
  157. CHAR renderedSymbol[MAX_SYMBOL_LEN+30];
  158. GetSymbol(
  159. (ULONG_PTR) BlobHeader->pfnFreeRoutine,
  160. symbol,
  161. &offset
  162. );
  163. if( symbol[0] == '\0' ) {
  164. renderedSymbol[0] = '\0';
  165. } else if( offset == 0 ) {
  166. sprintf(
  167. renderedSymbol,
  168. " (%s)",
  169. symbol
  170. );
  171. } else {
  172. sprintf(
  173. renderedSymbol,
  174. " (%s+0x%lx)",
  175. symbol,
  176. offset
  177. );
  178. }
  179. dprintf(
  180. "%sBLOB_HEADER @ %p\n"
  181. "%s IsCached = %s\n"
  182. "%s pfnFreeRoutine = %08lp\n"
  183. "%s blobKey @ %08lp\n"
  184. "%s dwRefCount = %08lx\n"
  185. "%s TTL = %08lx\n"
  186. "%s pSecDesc = %08lp\n"
  187. "%s hLastSuccessAccessToken = %08lp%s\n"
  188. "\n",
  189. Prefix,
  190. ActualAddress,
  191. Prefix,
  192. BoolToString( BlobHeader->IsCached ),
  193. Prefix,
  194. BlobHeader->pfnFreeRoutine,
  195. Prefix,
  196. BlobHeader->pBlobKey,
  197. Prefix,
  198. BlobHeader->lRefCount,
  199. Prefix,
  200. BlobHeader->TTL,
  201. Prefix,
  202. BlobHeader->pSecDesc,
  203. Prefix,
  204. BlobHeader->hLastSuccessAccessToken,
  205. renderedSymbol
  206. );
  207. } // DumpBlobHeader
  208. VOID
  209. DumpOpenFileInfo(
  210. IN PSTR Prefix,
  211. IN LPTS_OPEN_FILE_INFO OpenFileInfo,
  212. IN ULONG_PTR ActualAddress
  213. )
  214. /*++
  215. Routine Description:
  216. Formats and dumps a specific TS_OPEN_FILE_INFO structure.
  217. Arguments:
  218. Prefix - The prefix to use before each printed line. This makes
  219. hierarchical object displays much prettier.
  220. OpenFileInfo - Points to a local copy of the structure.
  221. ActualAddress - The virtual address of the object in the debugee.
  222. Return Value:
  223. None.
  224. --*/
  225. {
  226. dprintf(
  227. "%sTS_OPEN_FILE_INFO @ %p %s\n"
  228. "%s m_hFile = %08lp\n"
  229. "%s m_hUser = %08lp\n"
  230. "%s m_CastratedLastWriteTime @ %p\n",
  231. Prefix,
  232. ActualAddress,
  233. SignatureToString(OpenFileInfo->m_Signature,
  234. TS_FILE_INFO_SIGNATURE,
  235. TS_FREE_FILE_INFO_SIGNATURE),
  236. Prefix,
  237. OpenFileInfo->m_hFile,
  238. Prefix,
  239. OpenFileInfo->m_hUser,
  240. Prefix,
  241. ActualAddress + FIELD_OFFSET( TS_OPEN_FILE_INFO, m_CastratedLastWriteTime )
  242. );
  243. dprintf(
  244. "%s m_cbSecDescMaxSize = %08lx\n"
  245. "%s m_pSecurityDescriptor = %08lp\n"
  246. "%s m_fSecurityDescriptor = %s\n",
  247. Prefix,
  248. OpenFileInfo->m_cbSecDescMaxSize,
  249. Prefix,
  250. OpenFileInfo->m_pSecurityDescriptor,
  251. Prefix,
  252. BoolToString( OpenFileInfo->m_fSecurityDescriptor ) );
  253. dprintf(
  254. "%s m_achHttpInfo @ %p (%s)\n"
  255. "%s m_cchHttpInfo = %lu\n"
  256. "%s m_achETag @ %p (%s)\n"
  257. "%s m_cchETag = %lu\n"
  258. "%s m_ETagIsWeak = %s\n"
  259. "\n",
  260. Prefix,
  261. ActualAddress + FIELD_OFFSET( TS_OPEN_FILE_INFO, m_achHttpInfo ),
  262. OpenFileInfo->m_achHttpInfo,
  263. Prefix,
  264. OpenFileInfo->m_cchHttpInfo,
  265. Prefix,
  266. ActualAddress + FIELD_OFFSET( TS_OPEN_FILE_INFO, m_achETag ),
  267. OpenFileInfo->m_achETag,
  268. Prefix,
  269. OpenFileInfo->m_cchETag,
  270. Prefix,
  271. BoolToString( OpenFileInfo->m_ETagIsWeak )
  272. );
  273. if ( (ULONG_PTR) OpenFileInfo->m_FileKey.m_pszFileName == ActualAddress
  274. + FIELD_OFFSET(TS_OPEN_FILE_INFO, m_FileKey)
  275. + FIELD_OFFSET(CFileKey, m_achFileNameBuf) ) {
  276. //
  277. // Just print the internal buffer
  278. //
  279. dprintf(
  280. "%s m_FileKey.m_achFileNameBuf = %s\n",
  281. Prefix,
  282. OpenFileInfo->m_FileKey.m_achFileNameBuf
  283. );
  284. } else {
  285. //
  286. // print address of the real string
  287. //
  288. dprintf(
  289. "%s m_FileKey.m_pszFileName = %p\n",
  290. Prefix,
  291. OpenFileInfo->m_FileKey.m_pszFileName
  292. );
  293. }
  294. dprintf(
  295. "%s m_FileKey.m_cbFileName = %d\n"
  296. "%s m_bIsCached = %s\n"
  297. "%s m_bState = %s\n"
  298. "%s m_dwIORefCount = %08lx\n"
  299. "%s m_lRefCount = %08lx\n",
  300. Prefix,
  301. OpenFileInfo->m_FileKey.m_cbFileName,
  302. Prefix,
  303. BoolToString( OpenFileInfo->m_bIsCached ),
  304. Prefix,
  305. FIStateToString( OpenFileInfo->m_state ),
  306. Prefix,
  307. OpenFileInfo->m_dwIORefCount,
  308. Prefix,
  309. OpenFileInfo->m_lRefCount );
  310. } // DumpOpenFileInfo
  311. VOID
  312. DumpUriInfo(
  313. IN PSTR Prefix,
  314. IN PW3_URI_INFO UriInfo,
  315. IN ULONG_PTR ActualAddress
  316. )
  317. /*++
  318. Routine Description:
  319. Formats and dumps a specific W3_URI_INFO structure.
  320. Arguments:
  321. Prefix - The prefix to use before each printed line. This makes
  322. hierarchical object displays much prettier.
  323. UriInfo - Points to a local copy of the structure.
  324. ActualAddress - The virtual address of the object in the debugee.
  325. Return Value:
  326. None.
  327. --*/
  328. {
  329. CHAR name[MAX_PATH];
  330. CHAR unmappedName[MAX_PATH];
  331. if( UriInfo->pszName == NULL ) {
  332. strcpy( name, "<null>" );
  333. } else if( !ReadMemory(
  334. (ULONG_PTR)UriInfo->pszName,
  335. name,
  336. sizeof(name),
  337. NULL
  338. ) ) {
  339. strcpy( name, "<unreadable>" );
  340. }
  341. if( UriInfo->pszUnmappedName == NULL ) {
  342. strcpy( unmappedName, "<null>" );
  343. } else if( !ReadMemory(
  344. (ULONG_PTR)UriInfo->pszUnmappedName,
  345. unmappedName,
  346. sizeof(unmappedName),
  347. NULL
  348. ) ) {
  349. strcpy( unmappedName, "<unreadable>" );
  350. }
  351. dprintf(
  352. "%sW3_URI_INFO @ %p\n"
  353. "%s bIsCached = %s\n"
  354. "%s pOpenFileInfo = %08lp\n",
  355. Prefix,
  356. ActualAddress,
  357. Prefix,
  358. BoolToString( UriInfo->bIsCached ),
  359. Prefix,
  360. UriInfo->pOpenFileInfo
  361. );
  362. dprintf(
  363. "%s dwFileOpenError = %lu\n"
  364. "%s cchName = %lu\n"
  365. "%s pszName = %08lp (%s)\n"
  366. "%s pszUnmappedName = %08lp (%s)\n"
  367. "%s pMetaData = %08lp\n"
  368. "\n",
  369. Prefix,
  370. UriInfo->dwFileOpenError,
  371. Prefix,
  372. UriInfo->cchName,
  373. Prefix,
  374. UriInfo->pszName,
  375. name,
  376. Prefix,
  377. UriInfo->pszUnmappedName,
  378. unmappedName,
  379. Prefix,
  380. UriInfo->pMetaData
  381. );
  382. } // DumpUriInfo
  383. VOID
  384. DumpUriInfoBlob(
  385. IN PSTR Prefix,
  386. IN PBLOB_HEADER BlobHeader,
  387. IN ULONG_PTR ActualAddress
  388. )
  389. /*++
  390. Routine Description:
  391. Formats and dumps a W3_URI_INFO blob.
  392. Arguments:
  393. Prefix - The prefix to use before each printed line. This makes
  394. hierarchical object displays much prettier.
  395. BlobHeader - Points to a local copy of the blob.
  396. ActualAddress - The virtual address of the object in the debugee.
  397. Return Value:
  398. None.
  399. --*/
  400. {
  401. W3_URI_INFO localUriInfo;
  402. if( !ReadMemory(
  403. ActualAddress + sizeof(*BlobHeader),
  404. &localUriInfo,
  405. sizeof(localUriInfo),
  406. NULL
  407. ) ) {
  408. dprintf(
  409. "inetdbg: cannot read W3_URI_INFO @ %p\n",
  410. ActualAddress + sizeof(*BlobHeader)
  411. );
  412. return;
  413. }
  414. DumpBlobHeader(
  415. Prefix,
  416. BlobHeader,
  417. ActualAddress
  418. );
  419. DumpUriInfo(
  420. Prefix,
  421. &localUriInfo,
  422. ActualAddress + sizeof(*BlobHeader)
  423. );
  424. } // DumpUriInfoBlob
  425. VOID
  426. DumpFileCacheTable(
  427. IN CFileHashTable * fht,
  428. IN ULONG_PTR ActualAddress,
  429. IN CFileCacheStats * fcs,
  430. IN BOOLEAN DumpEntries,
  431. IN BOOLEAN Verbose
  432. )
  433. /*++
  434. Routine Description:
  435. Formats and dumps the entire cache table.
  436. Arguments:
  437. fht - Points to a local copy of the cache table.
  438. fcs - Points to the place where the cache statistics live
  439. ActualAddress - The virtual address of the cache table in the debugee.
  440. DumpEntries - Dump all the file info entries.
  441. Verbose - If TRUE, then be verbose.
  442. Return Value:
  443. None.
  444. --*/
  445. {
  446. LONG i;
  447. //
  448. // Dump simple data.
  449. //
  450. dprintf(
  451. "CFileHashTable @ %p\n",
  452. ActualAddress
  453. );
  454. //
  455. // If requested, dump the individual entries
  456. //
  457. if (DumpEntries) {
  458. //
  459. // this will be GeorgeRe's problem
  460. //
  461. }
  462. } // DumpFileCacheTable
  463. //
  464. // NTSD extension entrypoints.
  465. //
  466. DECLARE_API( fcache )
  467. /*++
  468. Routine Description:
  469. This function is called as an NTSD extension to format and dump
  470. the entire open file cache or a single cache object.
  471. Arguments:
  472. hCurrentProcess - Supplies a handle to the current process (at the
  473. time the extension was called).
  474. hCurrentThread - Supplies a handle to the current thread (at the
  475. time the extension was called).
  476. CurrentPc - Supplies the current pc at the time the extension is
  477. called.
  478. lpExtensionApis - Supplies the address of the functions callable
  479. by this extension.
  480. lpArgumentString - Supplies the asciiz string that describes the
  481. ansi string to be dumped.
  482. Return Value:
  483. None.
  484. --*/
  485. {
  486. ULONG_PTR address;
  487. ULONG_PTR address2;
  488. CFileHashTable * cacheTable;
  489. BOOLEAN dumpBin;
  490. BOOLEAN dumpMru;
  491. BOOLEAN verbose;
  492. INIT_API();
  493. //
  494. // Establish defaults.
  495. //
  496. dumpBin = FALSE;
  497. dumpMru = FALSE;
  498. verbose = FALSE;
  499. //
  500. // Skip any leading blanks.
  501. //
  502. while( *lpArgumentString == ' ' ||
  503. *lpArgumentString == '\t' ) {
  504. lpArgumentString++;
  505. }
  506. //
  507. // Process switches.
  508. //
  509. while( *lpArgumentString == '-' ) {
  510. lpArgumentString++;
  511. while( *lpArgumentString != ' ' &&
  512. *lpArgumentString != '\t' &&
  513. *lpArgumentString != '\0' ) {
  514. switch( *lpArgumentString ) {
  515. case 'v' :
  516. case 'V' :
  517. verbose = !verbose;
  518. break;
  519. case 'b' :
  520. case 'B' :
  521. dumpBin = !dumpBin;
  522. break;
  523. case 'm' :
  524. case 'M' :
  525. dumpMru = !dumpMru;
  526. break;
  527. case '-' : // Set the switches the way I like them. --keithmo
  528. verbose = TRUE;
  529. dumpBin = TRUE;
  530. dumpMru = FALSE;
  531. break;
  532. default :
  533. PrintUsage( "fcache" );
  534. return;
  535. }
  536. lpArgumentString++;
  537. }
  538. while( *lpArgumentString == ' ' ||
  539. *lpArgumentString == '\t' ) {
  540. lpArgumentString++;
  541. }
  542. }
  543. if( *lpArgumentString != '\0' ) {
  544. //
  545. // Dump a single object.
  546. //
  547. address = GetExpression( lpArgumentString );
  548. if( address == 0 ) {
  549. dprintf(
  550. "inetdbg: cannot evaluate \"%s\"\n",
  551. lpArgumentString
  552. );
  553. return;
  554. }
  555. return;
  556. }
  557. //
  558. // Dump the entire cache table.
  559. //
  560. address = GetExpression( "infocomm!g_pFileInfoTable" );
  561. if( address == 0 ) {
  562. dprintf(
  563. "inetdbg: cannot find infocomm!g_pFileInfoTable\n"
  564. );
  565. return;
  566. }
  567. address2 = GetExpression( "infocomm!g_pFileCacheStats" );
  568. if( address2 == 0 ) {
  569. dprintf(
  570. "inetdbg: cannot find infocomm!g_pFileCacheStats\n"
  571. );
  572. return;
  573. }
  574. if( !ReadMemory(
  575. address,
  576. &cacheTable,
  577. sizeof(cacheTable),
  578. NULL
  579. ) ) {
  580. dprintf(
  581. "inetdbg: cannot read CACHE_TABLE @ 0x%p\n",
  582. address
  583. );
  584. return;
  585. }
  586. } // DECLARE_API( fcache )
  587. DECLARE_API( open )
  588. /*++
  589. Routine Description:
  590. This function is called as an NTSD extension to format and dump
  591. a specific TS_OPEN_FILE_INFO structure.
  592. Arguments:
  593. hCurrentProcess - Supplies a handle to the current process (at the
  594. time the extension was called).
  595. hCurrentThread - Supplies a handle to the current thread (at the
  596. time the extension was called).
  597. CurrentPc - Supplies the current pc at the time the extension is
  598. called.
  599. lpExtensionApis - Supplies the address of the functions callable
  600. by this extension.
  601. lpArgumentString - Supplies the asciiz string that describes the
  602. ansi string to be dumped.
  603. Return Value:
  604. None.
  605. --*/
  606. {
  607. ULONG_PTR address;
  608. BYTE openFileInfo[sizeof(TS_OPEN_FILE_INFO)];
  609. INIT_API();
  610. //
  611. // Skip any leading blanks.
  612. //
  613. while( *lpArgumentString == ' ' ||
  614. *lpArgumentString == '\t' ) {
  615. lpArgumentString++;
  616. }
  617. if( *lpArgumentString == '\0' ) {
  618. PrintUsage( "open" );
  619. return;
  620. }
  621. address = GetExpression( lpArgumentString );
  622. if( address == 0 ) {
  623. dprintf(
  624. "inetdbg: cannot evaluate \"%s\"\n",
  625. lpArgumentString
  626. );
  627. return;
  628. }
  629. //
  630. // Read the object.
  631. //
  632. if( !ReadMemory(
  633. (ULONG_PTR)address,
  634. &openFileInfo,
  635. sizeof(openFileInfo),
  636. NULL
  637. ) ) {
  638. dprintf(
  639. "inetdbg: cannot read TS_OPEN_FILE_INFO @ %lp\n",
  640. address
  641. );
  642. return;
  643. }
  644. DumpOpenFileInfo(
  645. "",
  646. (TS_OPEN_FILE_INFO *) &openFileInfo,
  647. address
  648. );
  649. } // DECLARE_API( open )
  650. DECLARE_API( uri )
  651. /*++
  652. Routine Description:
  653. This function is called as an NTSD extension to format and dump
  654. a specific W3_URI_INFO structure.
  655. Arguments:
  656. hCurrentProcess - Supplies a handle to the current process (at the
  657. time the extension was called).
  658. hCurrentThread - Supplies a handle to the current thread (at the
  659. time the extension was called).
  660. CurrentPc - Supplies the current pc at the time the extension is
  661. called.
  662. lpExtensionApis - Supplies the address of the functions callable
  663. by this extension.
  664. lpArgumentString - Supplies the asciiz string that describes the
  665. ansi string to be dumped.
  666. Return Value:
  667. None.
  668. --*/
  669. {
  670. ULONG_PTR address;
  671. W3_URI_INFO uriInfo;
  672. INIT_API();
  673. //
  674. // Skip any leading blanks.
  675. //
  676. while( *lpArgumentString == ' ' ||
  677. *lpArgumentString == '\t' ) {
  678. lpArgumentString++;
  679. }
  680. if( *lpArgumentString == '\0' ) {
  681. PrintUsage( "uri" );
  682. return;
  683. }
  684. address = GetExpression( lpArgumentString );
  685. if( address == 0 ) {
  686. dprintf(
  687. "inetdbg: cannot evaluate \"%s\"\n",
  688. lpArgumentString
  689. );
  690. return;
  691. }
  692. //
  693. // Read the object.
  694. //
  695. if( !ReadMemory(
  696. address,
  697. &uriInfo,
  698. sizeof(uriInfo),
  699. NULL
  700. ) ) {
  701. dprintf(
  702. "inetdbg: cannot read W3_URI_INFO @ %lp\n",
  703. address
  704. );
  705. return;
  706. }
  707. DumpUriInfo(
  708. "",
  709. &uriInfo,
  710. address
  711. );
  712. } // DECLARE_API( uri )
  713. DECLARE_API( blob )
  714. /*++
  715. Routine Description:
  716. This function is called as an NTSD extension to format and dump
  717. a specific BLOB_HEADER structure.
  718. Arguments:
  719. hCurrentProcess - Supplies a handle to the current process (at the
  720. time the extension was called).
  721. hCurrentThread - Supplies a handle to the current thread (at the
  722. time the extension was called).
  723. CurrentPc - Supplies the current pc at the time the extension is
  724. called.
  725. lpExtensionApis - Supplies the address of the functions callable
  726. by this extension.
  727. lpArgumentString - Supplies the asciiz string that describes the
  728. ansi string to be dumped.
  729. Return Value:
  730. None.
  731. --*/
  732. {
  733. ULONG_PTR address;
  734. BLOB_HEADER blobHeader;
  735. INIT_API();
  736. //
  737. // Skip any leading blanks.
  738. //
  739. while( *lpArgumentString == ' ' ||
  740. *lpArgumentString == '\t' ) {
  741. lpArgumentString++;
  742. }
  743. if( *lpArgumentString == '\0' ) {
  744. PrintUsage( "blob" );
  745. return;
  746. }
  747. address = GetExpression( lpArgumentString );
  748. if( address == 0 ) {
  749. dprintf(
  750. "inetdbg: cannot evaluate \"%s\"\n",
  751. lpArgumentString
  752. );
  753. return;
  754. }
  755. //
  756. // Read the object.
  757. //
  758. if( !ReadMemory(
  759. address,
  760. &blobHeader,
  761. sizeof(blobHeader),
  762. NULL
  763. ) ) {
  764. dprintf(
  765. "inetdbg: cannot read BLOB_HEADER @ %lp\n",
  766. address
  767. );
  768. return;
  769. }
  770. DumpBlobHeader(
  771. "",
  772. &blobHeader,
  773. address
  774. );
  775. } // DECLARE_API( blob )