home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 10 / 10.iso / l / l430 / 1.ddi / CHAP6.ZIP / USERWALK.C < prev    next >
Encoding:
C/C++ Source or Header  |  1992-06-08  |  28.6 KB  |  1,174 lines

  1. /*
  2.     USERWALK.C -- Walks the USER heap segments, and allows browsing
  3.     of recognized structures.
  4.  
  5.     Copyright (c) Dave Maxey 1992
  6.     
  7.     From Chapter 6 of "Undocumented Windows" (Addison-Wesley 1992)
  8.     by Andrew Schulman, Dave Maxey and Matt Pietrek
  9.  
  10.     Build using: WINIOBC USERWALK (for Borland C++ v3.00)
  11.                  WINIOMS USERWALK (for Microsoft C/SDK)
  12. */
  13.  
  14. #include <windows.h>
  15. #include <ctype.h>
  16. #include <stdarg.h>
  17. #include <stdio.h>
  18. #include <string.h>
  19. #include <dos.h>
  20. #include "toolhelp.h"
  21. #include "winio.h"
  22. #include "userobj.h"
  23.  
  24. #ifndef __BORLANDC__ 
  25. #define MK_FP(a,b)  ((void far *)(((unsigned long)(a) << 16) | (b))) 
  26. //#else 
  27. //#define asm _asm 
  28. #endif 
  29.  
  30. typedef struct _LINESTOHEAP
  31.     {
  32.     WORD wHeap;
  33.     int    nFirstLine, nLastLine;
  34.     } LINESTOHEAP;
  35.  
  36. void WalkOneUserHeap(WORD seg);
  37. void UserHeapWalk(void);
  38. BOOL ContainsLocalHeap(WORD seg);
  39. void MainWndLine(HWND hwnd, LPSTR line, int lineNum);
  40. void OtherWndLine(HWND hwnd, LPSTR line, int lineNum);
  41.  
  42. /* The following are structure 'viewer' functions. Since only a
  43.    subset of the structures referred to are known, many of these call
  44.    DumpBlock, which is the hex dump default */
  45. void DumpBlock(WORD wSel, WORD wOfs, WORD wBytes);
  46. void DumpClass(WORD wSel, WORD wOfs, WORD wBytes);
  47. void DumpWindow(WORD wSel, WORD wOfs, WORD wBytes);
  48. void DumpString(WORD wSel, WORD wOfs, WORD wBytes);
  49. void DumpMenu(WORD wSel, WORD wOfs, WORD wBytes);
  50. void DumpClip(WORD wSel, WORD wOfs, WORD wBytes);
  51. void DumpCombo(WORD wSel, WORD wOfs, WORD wBytes);
  52. void DumpPalette(WORD wSel, WORD wOfs, WORD wBytes);
  53. void DumpEditCtl(WORD wSel, WORD wOfs, WORD wBytes);
  54. void DumpBWL(WORD wSel, WORD wOfs, WORD wBytes);
  55. void DumpOwnerDraw(WORD wSel, WORD wOfs, WORD wBytes);
  56. void DumpSPB(WORD wSel, WORD wOfs, WORD wBytes);
  57. void DumpChkPnt(WORD wSel, WORD wOfs, WORD wBytes);
  58. void DumpDCE(WORD wSel, WORD wOfs, WORD wBytes);
  59. void DumpMWP(WORD wSel, WORD wOfs, WORD wBytes);
  60. void DumpProp(WORD wSel, WORD wOfs, WORD wBytes);
  61. void DumpListbox(WORD wSel, WORD wOfs, WORD wBytes);
  62. void DumpMisc(WORD wSel, WORD wOfs, WORD wBytes);
  63. void DumpAtom(WORD wSel, WORD wOfs, WORD wBytes);
  64. void DumpLockInpSt(WORD wSel, WORD wOfs, WORD wBytes);
  65. void DumpHooklist(WORD wSel, WORD wOfs, WORD wBytes);
  66. void DumpUSUDAlloc(WORD wSel, WORD wOfs, WORD wBytes);
  67. void DumpHotKeyList(WORD wSel, WORD wOfs, WORD wBytes);
  68. void DumpPopupMenu(WORD wSel, WORD wOfs, WORD wBytes);
  69.  
  70. BOOL FarLocalSize(WORD wSel, WORD wH, WORD *pOfs, WORD *pSize);
  71. BOOL MyIsMenu(WORD wSel, WORD wOfs);
  72. void GetAnyAtomName(WORD wSeg, ATOM nAtom, LPSTR lpBuffer, int nSize);
  73. void buildlist(void);
  74. BOOL IsMenuHeap(WORD wSel);
  75. void DoRefresh(HWND hwnd, WORD wID);
  76.  
  77.     
  78. #define ID_REFRESH        1
  79. #define ID_HEAPS        2
  80. #define ID_WINDOWS        3
  81. #define ID_ANALYSIS        5
  82. #define MAXHEAPS        8
  83.  
  84. WORD wCurrView;
  85. HMENU hMenuView;
  86. char szTitle[] = "USER Heap Walker";
  87. char UnknownString[] = "<UNKNOWN>";
  88. int nLineNum = 0;
  89. int nHeaps;
  90. BOOL bDebug, b31;
  91. WORD wDefHeap, wMenuHeap, wAtomHeap;
  92. char *strIndent = "                                ";
  93. SYSHEAPINFO shi;
  94. HWND hwndAnal = NULL;
  95.  
  96. // These are the TOOLHELP defined object types
  97. char *UserBlockType[] =
  98. {
  99. "NORMAL",             // 0
  100. "Class",              // 1
  101. "Window",             // 2
  102. "STRING",             // 3
  103. "Menu",               // 4
  104. "CLIP",               // 5
  105. "Combo box",          // 6
  106. "PALETTE",            // 7
  107. "Edit control",       // 8
  108. "BWL",                // 9
  109. "OWNERDRAW",          // 10
  110. "SPB",                // 11
  111. "CHECKPOINT",         // 12
  112. "DCE",                // 13
  113. "MWP",                // 14
  114. "Property",           // 15
  115. "Listbox",            // 16
  116. "MISC",               // 17
  117. "atom",               // 18
  118. "LOCKINPUTSTATE",     // 19
  119. "HOOKLIST",           // 20
  120. "USERSEEUSERDOALLOC", // 21
  121. "HOTKEYLIST",         // 22
  122. "POPUPMENU"           // 23
  123. };
  124.  
  125. #define BLOCKTYPES (sizeof(UserBlockType) / sizeof(char *))
  126.  
  127. POINT pntSize[] = {
  128.     { 76, -1 },
  129.     { 55, 16 },
  130.     { 50, 28 },
  131.     { 76, -1 },
  132.     { 55, 15 },
  133.     { 76, -1 },
  134.     { 76, -1 },
  135.     { 76, -1 },
  136.     { 76, -1 },
  137.     { 76, -1 },
  138.     { 76, -1 },
  139.     { 76, -1 },
  140.     { 76, -1 },
  141.     { 76, -1 },
  142.     { 76, -1 },
  143.     { 60, 20 },
  144.     { 76, -1 },
  145.     { 76, -1 },
  146.     { 76, -1 },
  147.     { 76, -1 },
  148.     { 76, -1 },
  149.     { 76, -1 },
  150.     { 76, -1 },
  151.     { 76, -1 },
  152.     { 76, -1 }
  153.     };
  154.     
  155. typedef long HEAPAMOUNTS[BLOCKTYPES];
  156.  
  157. LINESTOHEAP ltoh[MAXHEAPS] = {0};
  158. HEAPAMOUNTS heapamnts[MAXHEAPS] = {0};
  159. DWORD heaptots[MAXHEAPS] = {0};
  160.  
  161.  
  162. typedef void (* fnDUMP)(WORD wSel, WORD wOfs, WORD wBytes);
  163.  
  164. fnDUMP fnDump[] =
  165.     { DumpBlock, DumpClass, DumpWindow, DumpString, DumpMenu, DumpClip,
  166.         DumpCombo, DumpPalette, DumpEditCtl, DumpBWL, DumpOwnerDraw,
  167.         DumpSPB, DumpChkPnt, DumpDCE, DumpMWP, DumpProp, DumpListbox,
  168.         DumpMisc, DumpAtom, DumpLockInpSt, DumpHooklist, DumpUSUDAlloc,
  169.         DumpHotKeyList, DumpPopupMenu, DumpBlock };
  170.  
  171. #define    WINDOWSIZE(y, x) \
  172.     ((DWORD) ((DWORD) ((unsigned)(y)) << 16) | ((unsigned)(x)))
  173.  
  174. typedef struct {
  175.     char *szField;
  176.     int iType;
  177.     } HFIELD;
  178.  
  179. HFIELD hfld[] = {
  180.     { "hwndNext", 2 }, { "hwndChild", 2 }, { "hClass", 1 },
  181.     { "hwndLastActive", 2 }, { "hmenuSystem", 4 }, { "hwndOwner", 2 },
  182.     { "wID_Menu", 4 }, { "hwndParent", 2 }, { "hmenuNext", 4},
  183.     { "hIDorPopup", 4 }, { "hProp", 15 }, { "hDCE", 13 } };
  184.  
  185. #define MAXHFIELDS        (sizeof(hfld) / sizeof(HFIELD))
  186.     
  187. WORD verr(WORD sel)
  188.     {
  189.     _asm mov ax, 1
  190.     _asm verr word ptr sel
  191.     _asm je short ok
  192.     _asm dec ax
  193.     ok:;
  194.     }
  195.  
  196. WORD lsl(WORD wSel)
  197.     {
  198.     _asm sub ax, ax
  199.     _asm lsl ax, wSel
  200.     }
  201.  
  202.  
  203. #define WIDTH 16
  204.  
  205. void DumpBlock(WORD wSel, WORD wOfs, WORD wBytes)
  206.     {
  207.     LPSTR fp, p;
  208.     WORD i, j, c;
  209.     
  210.     fp = MK_FP(wSel, wOfs);
  211.     
  212.     for (i=0; i<wBytes; i += WIDTH)
  213.         {
  214.         c = ((wBytes-i) > WIDTH) ? WIDTH : wBytes-i;
  215.         printf("%04X | ", i);
  216.         for (j=c, p=fp+i; j--; p++)
  217.             printf("%02X ", (unsigned char) *p);
  218.         for (j=WIDTH-c; j--; )  // pad out on last line
  219.             printf("   ");
  220.         putchar('|'); putchar(' ');
  221.         for (j=c, p=fp+i; j--; p++)
  222.             putchar( isprint(*p) ? *p : '.' );
  223.         putchar('\n');
  224.         }
  225.     }
  226.  
  227.  
  228. void DumpClass(WORD wSel, WORD wOfs, WORD wBytes)
  229.     {
  230.     CLASS far *pCls;
  231.     char szClsName[64];
  232.     pCls = (CLASS FAR *) MK_FP(wSel, wOfs);
  233.  
  234.     GetAnyAtomName(shi.hUserSegment, pCls->atomCls, (LPSTR) &szClsName, 64);
  235.     
  236.     printf("Lines marked '->' may be double clicked\nfor expansion\n\n");
  237.     printf(    "CLASS:\n"
  238.             "\thcNext\t\t: %04X\n"
  239.             "\twSig\t\t: %04X (\"%c%c\")\n"
  240.             "\tatomCls\t\t: %04X (\"%s\")\n"
  241.             "->\thDCE\t: %04X\n"
  242.             "\tcClsWnds\t: %04X\n"
  243.             "\tstyle\t\t: %04X\n"
  244.             "\tlpfnWndProc\t: %Fp\n"
  245.             "\tcbClsExtra\t: %04X\n"
  246.             "\tcbWndExtra\t: %04X\n"
  247.             "\thInstance\t: %04X\n"
  248.             "\thIcon\t\t: %04X\n"
  249.             "\thCursor\t\t: %04X\n"
  250.             "\thbrBackground\t: %04X\n"
  251.             "\tlpszMenuName\t: %Fp\n"
  252.             "\tlpszClassName\t: %Fp\n",
  253.             pCls->hcNext, pCls->wSig, pCls->wSig & 0xff, pCls->wSig >> 8,
  254.             pCls->atomCls, szClsName, pCls->hDCE,
  255.             pCls->cClsWnds, pCls->wc.style,
  256.             pCls->wc.lpfnWndProc, pCls->wc.cbClsExtra,
  257.             pCls->wc.cbWndExtra, pCls->wc.hInstance,
  258.             pCls->wc.hIcon, pCls->wc.hCursor,
  259.             pCls->wc.hbrBackground, pCls->wc.lpszMenuName,
  260.             pCls->wc.lpszClassName
  261.         );
  262.     }
  263.  
  264.  
  265. void DumpWindow(WORD wSel, WORD wOfs, WORD wBytes)
  266.     {
  267.     WND_3_0 far *pwnd_3_0;
  268.     WND_3_1 far *pwnd_3_1;
  269.     CLASS far *pcls;
  270.     WORD clsofs;
  271.     char strClass[20];
  272.     char strTitle[80];
  273.  
  274.     printf("Lines marked '->' may be double clicked\nfor expansion\n\n");
  275.     pwnd_3_0 = (void far *) pwnd_3_1 = (void far *) MK_FP(wSel, wOfs);
  276.     clsofs = (WORD) (b31 ? pwnd_3_1->hClass : pwnd_3_0->hClass);
  277.     (void far *) pcls = MK_FP(wSel, clsofs);
  278.     GetAnyAtomName(wDefHeap, pcls->atomCls,
  279.         (LPSTR) &strClass, sizeof(strClass));
  280.     GetWindowText(wOfs, (LPSTR) &strTitle, sizeof(strTitle));
  281.     printf("Window Title: %s\nWindow Class: %s\n\n", strTitle, strClass); 
  282.     if (! b31)
  283.         printf( "WND:\n"
  284.                 "->\thwndNext\t: %04X\n"
  285.                 "->\thwndChild\t: %04X\n"
  286.                 "->\thClass\t\t: %04X\n"
  287.                 "->\thProp\t\t: %04X\n"
  288.                 "->\thwndLastActive\t: %04X\n"
  289.                 "\thScroll\t\t: %04X\n"
  290.                 "\thmemTaskQ\t: %04X\n"
  291.                 "\thrgnUpdate\t: %04X\n"
  292.                 "->\thDCE\t\t: %04X\n"
  293.                 "->\thmenuSystem\t: %04X\n"
  294.                 "->\thwndOwner\t: %04X\n"
  295.                 "\trectWindow\t: (%d, %d, %d, %d)\n"
  296.                 "\trectClient\t: (%d, %d, %d, %d)\n"
  297.                 "\thPalette\t: %04X\n"
  298.                 "\twFlags\t\t: %04X\n"
  299.                 "\trgfExStyle\t: %08lX\n"
  300.                 "\trgfStyle\t: %08lX\n"
  301.                 "->\twID_Menu\t: %04X\n"
  302.                 "\thText\t\t: %04X\n"
  303.                 "->\thwndParent\t: %04X\n"
  304.                 "\thInstance\t: %04X\n"
  305.                 "\tlpfnWndProc   : %Fp\n",
  306.                 pwnd_3_0->hwndNext, pwnd_3_0->hwndChild,
  307.                 pwnd_3_0->hClass, pwnd_3_0->hProp,
  308.                 pwnd_3_0->hwndLastActive, pwnd_3_0->hScroll,
  309.                 pwnd_3_0->hmemTaskQ,
  310.                 pwnd_3_0->hrgnUpdate, pwnd_3_0->hDCE,
  311.                 pwnd_3_0->hmenuSystem, pwnd_3_0->hwndOwner,
  312.                 pwnd_3_0->rectWindow.left, pwnd_3_0->rectWindow.top,
  313.                 pwnd_3_0->rectWindow.right, pwnd_3_0->rectWindow.bottom,
  314.                 pwnd_3_0->rectClient.left, pwnd_3_0->rectClient.top,
  315.                 pwnd_3_0->rectClient.right, pwnd_3_0->rectClient.bottom,
  316.                 pwnd_3_0->hPalette, pwnd_3_0->wFlags,
  317.                 pwnd_3_0->rgfExStyle, pwnd_3_0->rgfStyle,
  318.                 pwnd_3_0->wID_Menu, pwnd_3_0->hText, pwnd_3_0->hwndParent,
  319.                 pwnd_3_0->hInstance, pwnd_3_0->lpfnWndProc
  320.                 );
  321.     else
  322.         printf( "WND:\n"
  323.                 "->\thwndNext\t: %04X\n"
  324.                 "->\thwndChild\t: %04X\n"
  325.                 "->\thwndParent\t: %04X\n"
  326.                 "->\thwndOwner\t: %04X\n"
  327.                 "\trectWindow\t: (%d, %d, %d, %d)\n"
  328.                 "\trectClient\t: (%d, %d, %d, %d)\n"
  329.                 "\thTaskQ\t\t: %04X\n"
  330.                 "\thrgnUpdate\t: %04X\n"
  331.                 "->\thClass\t\t: %04X\n"
  332.                 "\thInstance\t: %04X\n"
  333.                 "\tlpfnWndProc\t: %Fp\n"
  334.                 "\tdwFlags\t\t: %08lX\n"
  335.                 "\trgfStyle\t: %08lX\n"
  336.                 "\trgfExStyle\t: %08lX\n"
  337.                 "->\twID_Menu\t: %04X\n"
  338.                 "\thText\t\t: %04X\n"
  339.                 "\thScroll\t\t: %04X\n"
  340.                 "->\thProp\t\t: %04X\n"
  341.                 "->\thwndLastActive\t: %04X\n"
  342.                 "->\thmenuSystem\t: %04X\n",
  343.                 pwnd_3_1->hwndNext, pwnd_3_1->hwndChild,
  344.                 pwnd_3_1->hwndParent, pwnd_3_1->hwndOwner,
  345.                 pwnd_3_1->rectWindow.left, pwnd_3_1->rectWindow.top,
  346.                 pwnd_3_1->rectWindow.right, pwnd_3_1->rectWindow.bottom,
  347.                 pwnd_3_1->rectClient.left, pwnd_3_1->rectClient.top,
  348.                 pwnd_3_1->rectClient.right, pwnd_3_1->rectClient.bottom,
  349.                 pwnd_3_1->hmemTaskQ, pwnd_3_1->hrgnUpdate,
  350.                 pwnd_3_1->hClass,
  351.                 pwnd_3_1->hInstance, pwnd_3_1->lpfnWndProc,
  352.                 pwnd_3_1->dwFlags,
  353.                 pwnd_3_1->rgfStyle, pwnd_3_1->rgfExStyle,
  354.                 pwnd_3_1->wID_Menu, pwnd_3_1->hText,
  355.                 pwnd_3_1->hScroll, pwnd_3_1->hProp,
  356.                 pwnd_3_1->hwndLastActive, pwnd_3_1->hmenuSystem
  357.                 );
  358.     }
  359.  
  360.  
  361. void DumpMenu(WORD wSel, WORD wOfs, WORD wBytes)
  362.     {
  363.     int i;
  364.     MENU_3_0 FAR *lpMenu30;
  365.     MENU_3_1 FAR *lpMenu31;
  366.     LPITEM lpItem;
  367.     char buf[30];
  368.  
  369.     lpMenu30 = (void far *) lpMenu31 = (void far *) MK_FP(wSel, wOfs);
  370.     if (! MyIsMenu(wSel, wOfs))
  371.         {
  372.         winio_warn(FALSE, szTitle, "%04X is either no longer valid\n"
  373.             "as an HMENU, or is an ID.", wOfs);
  374.         return;
  375.         }
  376.     if (! b31)
  377.         printf("MENU:\n"
  378.             "\twFlags\t\t: %04X\n"
  379.             "\tiCurrSel\t: %d\n"
  380.             "\tiCurrPopup\t: %d\n"
  381.             "\tcbMenu\t\t: %d\n"
  382.             "\tcxWidth\t\t: %d\n"
  383.             "\tcyHeight\t: %d\n"
  384.             "\tcItems\t\t: %d\n"
  385.             "->\thwndOwner\t: %04X\n",
  386.             lpMenu30->wFlags,
  387.             lpMenu30->iCurrSel,
  388.             lpMenu30->iCurrPopup,
  389.             lpMenu30->cbMenu,
  390.             lpMenu30->cxWidth,
  391.             lpMenu30->cyHeight,
  392.             lpMenu30->cItems,
  393.             lpMenu30->hwndOwner
  394.             );
  395.     else
  396.         printf("MENU:\n"
  397.             "->\thmenuNext\t: %04X\n"
  398.             "\twFlags\t\t: %04X\n"
  399.             "\twMagic\t\t: %04X (\"%c%c\")\n"
  400.             "\thTaskQ\t\t: %04X\n"
  401.             "\tcxWidth\t\t: %d\n"
  402.             "\tcyHeight\t: %d\n"
  403.             "\tcItems\t\t: %d\n"
  404.             "->\thwndOwner\t: %04X\n"
  405.             "\thItems\t\t: %04X\n"
  406.             "\tw12\t\t: %04X\n",
  407.             lpMenu31->hmenuNext,
  408.             lpMenu31->wFlags,
  409.             lpMenu31->wMagic, lpMenu31->wMagic & 0xff, lpMenu31->wMagic >> 8,
  410.             lpMenu31->hTaskQ,
  411.             lpMenu31->cxWidth,
  412.             lpMenu31->cyHeight,
  413.             lpMenu31->cItems,
  414.             lpMenu31->hwndOwner,
  415.             lpMenu31->hItems,
  416.             lpMenu31->w12
  417.             );
  418.     
  419.     lpItem = b31 ? (void far *) MK_FP(wSel, lpMenu31->hItems)
  420.                 : (void far *) MK_FP(wSel, wOfs + sizeof(MENU_3_0));
  421.                 
  422.     // cItems is at the same offset in both versions
  423.     // and the ITEM structure didn't change.
  424.     for (i = 0; i < lpMenu30->cItems; i++)
  425.         {
  426.         GlobalGetAtomName(lpItem->hStrOrBmp, (LPSTR) &buf, sizeof(buf));
  427.         printf("ITEM %d:\n"
  428.             "\twFlags\t\t: %04X\n"
  429.             "->\thIDorPopup\t: %04X\n"
  430.             "\trectCapture\t: (%d, %d, %d, %d)\n"
  431.             "\txTab\t\t: %d\n"
  432.             "\thCheckedBmp\t: %04X\n"
  433.             "\thUncheckedBmp\t: %04X\n"
  434.             "\thStrOrBmp\t: %04X (\"%s\")\n"
  435.             "\txULStart\t: %d\n"
  436.             "\tcxULLen\t\t: %d\n"
  437.             "\tcbItemLen\t: %d\n",
  438.             i + 1,
  439.             lpItem->wFlags,
  440.             lpItem->hIDorPopup,
  441.             lpItem->rectCapture.left, lpItem->rectCapture.top,
  442.             lpItem->rectCapture.right, lpItem->rectCapture.bottom,
  443.             lpItem->xTab,
  444.             lpItem->hCheckedBmp,
  445.             lpItem->hUncheckedBmp,
  446.             lpItem->hStrOrBmp, buf,
  447.             lpItem->xULStart,
  448.             lpItem->cxULLen,
  449.             lpItem->cbItemLen
  450.             );
  451.         lpItem++;
  452.         }
  453.     }
  454.  
  455.  
  456. void DumpString(WORD wSel, WORD wOfs, WORD wBytes)
  457.     {
  458.     DumpBlock(wSel, wOfs, wBytes);
  459.     }
  460.  
  461.  
  462. void DumpClip(WORD wSel, WORD wOfs, WORD wBytes)
  463.     {
  464.     DumpBlock(wSel, wOfs, wBytes);
  465.     }
  466.  
  467.  
  468. void DumpCombo(WORD wSel, WORD wOfs, WORD wBytes)
  469.     {
  470.     DumpBlock(wSel, wOfs, wBytes);
  471.     }
  472.  
  473.  
  474. void DumpPalette(WORD wSel, WORD wOfs, WORD wBytes)
  475.     {
  476.     DumpBlock(wSel, wOfs, wBytes);
  477.     }
  478.  
  479.  
  480. void DumpEditCtl(WORD wSel, WORD wOfs, WORD wBytes)
  481.     {
  482.     DumpBlock(wSel, wOfs, wBytes);
  483.     }
  484.  
  485.  
  486. void DumpBWL(WORD wSel, WORD wOfs, WORD wBytes)
  487.     {
  488.     DumpBlock(wSel, wOfs, wBytes);
  489.     }
  490.  
  491.  
  492. void DumpOwnerDraw(WORD wSel, WORD wOfs, WORD wBytes)
  493.     {
  494.     DumpBlock(wSel, wOfs, wBytes);
  495.     }
  496.  
  497.  
  498. void DumpSPB(WORD wSel, WORD wOfs, WORD wBytes)
  499.     {
  500.     DumpBlock(wSel, wOfs, wBytes);
  501.     }
  502.  
  503.  
  504. void DumpChkPnt(WORD wSel, WORD wOfs, WORD wBytes)
  505.     {
  506.     DumpBlock(wSel, wOfs, wBytes);
  507.     }
  508.  
  509.  
  510. void DumpDCE(WORD wSel, WORD wOfs, WORD wBytes)
  511.     {
  512.     CLASS far *pCls;
  513.     char szClsName[64];
  514.     pCls = (CLASS FAR *) MK_FP(wSel, wOfs);
  515.  
  516.     GetAnyAtomName(shi.hUserSegment, pCls->atomCls, (LPSTR) &szClsName, 64);
  517.     
  518.     printf("Lines marked '->' may be double clicked\nfor expansion\n\n");
  519.     printf(    "CLASS:\n"
  520.             "\thcNext\t\t: %04X\n"
  521.             "\twSig\t\t: %04X (\"%c%c\")\n"
  522.             "\tatomCls\t\t: %04X (\"%s\")\n"
  523.             "->\thDCE\t\t: %04X\n"
  524.             "\tcClsWnds\t: %04X\n"
  525.             "\tstyle\t\t: %04X\n"
  526.             "\tlpfnWndProc\t: %Fp\n"
  527.             "\tcbClsExtra\t: %04X\n"
  528.             "\tcbWndExtra\t: %04X\n"
  529.             "\thInstance\t: %04X\n"
  530.             "\thIcon\t\t: %04X\n"
  531.             "\thCursor\t\t: %04X\n"
  532.             "\thbrBackground\t: %04X\n"
  533.             "\tlpszMenuName\t: %Fp\n"
  534.             "\tlpszClassName\t: %Fp\n",
  535.             pCls->hcNext, pCls->wSig, pCls->wSig & 0xff, pCls->wSig >> 8,
  536.             pCls->atomCls, szClsName, pCls->hDCE,
  537.             pCls->cClsWnds, pCls->wc.style,
  538.             pCls->wc.lpfnWndProc, pCls->wc.cbClsExtra,
  539.             pCls->wc.cbWndExtra, pCls->wc.hInstance,
  540.             pCls->wc.hIcon, pCls->wc.hCursor,
  541.             pCls->wc.hbrBackground, pCls->wc.lpszMenuName,
  542.             pCls->wc.lpszClassName
  543.         );
  544.     }
  545.  
  546.  
  547. void DumpMWP(WORD wSel, WORD wOfs, WORD wBytes)
  548.     {
  549.     DumpBlock(wSel, wOfs, wBytes);
  550.     }
  551.  
  552.  
  553. void DumpProp(WORD wSel, WORD wOfs, WORD wBytes)
  554.     {
  555.     char strAtom[50];
  556.     int i, iMax;
  557.     PROPERTY FAR *pProp = (PROPERTY FAR *) MK_FP(wSel, wOfs);
  558.  
  559.     printf(    "PROPLIST:\n"
  560.             "\tcProps\t\t: %d\n\n",
  561.             iMax = *((int far *) pProp));
  562.     
  563.     (DWORD) pProp += 2;
  564.     
  565.     for (i = 0; i < iMax; i++)
  566.         {
  567.         GetAnyAtomName(wDefHeap, pProp->atomID, (LPSTR) &strAtom,
  568.             sizeof(strAtom));
  569.         
  570.         printf(
  571.             "PROPERTY %d:\n"
  572.             "\tatomID\t\t: %04X (\"%s\")\n"
  573.             "\thData\t\t: %04X\n"
  574.             "\twFlags\t\t: %04X\n",
  575.             i + 1,
  576.             pProp->atomID, strAtom,
  577.             pProp->hData,
  578.             pProp->wFlags);
  579.         }
  580.     }
  581.  
  582.  
  583. void DumpListbox(WORD wSel, WORD wOfs, WORD wBytes)
  584.     {
  585.     DumpBlock(wSel, wOfs, wBytes);
  586.     }
  587.  
  588.  
  589. void DumpMisc(WORD wSel, WORD wOfs, WORD wBytes)
  590.     {
  591.     DumpBlock(wSel, wOfs, wBytes);
  592.     }
  593.  
  594.  
  595. void DumpAtom(WORD wSel, WORD wOfs, WORD wBytes)
  596.     {
  597.     DumpBlock(wSel, wOfs, wBytes);
  598.     }
  599.  
  600.  
  601. void DumpLockInpSt(WORD wSel, WORD wOfs, WORD wBytes)
  602.     {
  603.     DumpBlock(wSel, wOfs, wBytes);
  604.     }
  605.  
  606.  
  607. void DumpHooklist(WORD wSel, WORD wOfs, WORD wBytes)
  608.     {
  609.     DumpBlock(wSel, wOfs, wBytes);
  610.     }
  611.  
  612.  
  613. void DumpUSUDAlloc(WORD wSel, WORD wOfs, WORD wBytes)
  614.     {
  615.     DumpBlock(wSel, wOfs, wBytes);
  616.     }
  617.  
  618.  
  619. void DumpHotKeyList(WORD wSel, WORD wOfs, WORD wBytes)
  620.     {
  621.     DumpBlock(wSel, wOfs, wBytes);
  622.     }
  623.  
  624.  
  625. void DumpPopupMenu(WORD wSel, WORD wOfs, WORD wBytes)
  626.     {
  627.     DumpBlock(wSel, wOfs, wBytes);
  628.     }
  629.  
  630.  
  631.  
  632.  
  633. void DispatchDump(char *typ, WORD wH, WORD wSel, WORD wOfs, WORD wBytes)
  634.     {
  635.     WORD wLimit;
  636.     char buf[80];
  637.     HWND hwndNew, hwndSav;
  638.     int i;
  639.     DWORD dwSave;
  640.  
  641.     wSel &= ~3;
  642.     wSel |= 1;
  643.     
  644.     if (! verr(wSel))
  645.         {
  646.         winio_warn(FALSE, szTitle, "%04x no longer valid\n", wSel);
  647.         return;
  648.         }
  649.     if ((wLimit = lsl(wSel)) < wOfs)
  650.         {
  651.         winio_warn(FALSE, szTitle,
  652.             "Start offs %04X is outside selector %04X limit of %04X\n",
  653.             wOfs, wSel, wLimit);
  654.         return;
  655.         }
  656.     if ((wLimit + 1) < (wBytes + wOfs))
  657.         {
  658.         winio_warn(FALSE, szTitle,
  659.             "Selector %04X limit is %04X, length of dump adjusted\n",
  660.             wSel, wLimit);
  661.         wBytes = wLimit - wOfs + 1;
  662.         }
  663.     
  664.     for (i = 0; i < BLOCKTYPES; i++)
  665.         if (strcmp(typ, UserBlockType[i]) == 0) break;
  666.  
  667.     sprintf(buf, "%s handle %04X @ %04X:%04X for %d bytes",
  668.         typ, wH, wSel, wOfs, wBytes);
  669.     
  670.     if ((hwndNew = FindWindow(NULL, buf)) != NULL)
  671.         {
  672.         ShowWindow(hwndNew, SW_RESTORE);
  673.         BringWindowToTop(hwndNew);
  674.         return;
  675.         }
  676.  
  677.     // Use the table entry for window size. If the entry has a
  678.     // y size of -1, it means it is a dump, and we use the amount
  679.     // of data to display to determine the y size.
  680.     dwSave = winio_defwindowsize(
  681.         WINDOWSIZE( ((pntSize[i].y == -1)
  682.             ? min(20, (wBytes + 15) / 16) : pntSize[i].y),
  683.             pntSize[i].x));
  684.     
  685.     if (! (hwndNew = winio_window(buf, min(wBytes * 20, 1024), WW_HASMENU)))
  686.         {
  687.         winio_warn(FALSE, "Dump", "Insufficient resources!");
  688.         return;
  689.         }
  690.     
  691.     winio_defwindowsize(dwSave);
  692.     
  693.     hwndSav = winio_setcurrent(hwndNew);
  694.     
  695.     winio_setpaint(hwndNew, FALSE);
  696.     
  697.     // there is an extra entry in the fnDump table to allow for when
  698.     // the type was not in the UserBlockType table
  699.     (* (fnDump[i]))(wSel, wOfs, wBytes);
  700.     
  701.     winio_setlinefn(hwndNew, OtherWndLine);
  702.     winio_setpaint(hwndNew, TRUE);
  703.     winio_home(hwndNew);
  704.     winio_setcurrent(hwndSav);
  705.  
  706.     }
  707.  
  708.     
  709. void WindowViewLine(HWND hwnd, LPSTR lpLine, int iLine)
  710.     {
  711.     int i;
  712.     WORD wndofs;
  713.     char strText[80];
  714.     
  715.     lstrcpy((LPSTR) &strText, lpLine);
  716.     for (i = 0; strText[i] == ' '; i++);
  717.     if (! strText[i]) return;
  718.     sscanf((char *) &strText[i], "%04X", &wndofs);
  719.     if (! IsWindow(wndofs))
  720.         {
  721.         winio_warn(FALSE, szTitle,
  722.             "HWND %04X is no longer valid", wndofs);
  723.         buildlist();
  724.         return;
  725.         }
  726.     
  727.     DispatchDump(UserBlockType[2], wndofs, wDefHeap, wndofs,
  728.         b31 ? sizeof(WND_3_1) : sizeof(WND_3_0));
  729.     }
  730.     
  731.  
  732. void HeapViewLine(HWND hwnd, LPSTR line, int lineNum)
  733.     {
  734.     WORD wSel, wH, wOfs, wBytes;
  735.     char typ[20];
  736.     char buf[80];
  737.     int i;
  738.     
  739.     for (i = 0; i < nHeaps; i++)
  740.         if (lineNum < ltoh[i].nFirstLine)
  741.             return;
  742.         else
  743.         if (lineNum < ltoh[i].nLastLine)
  744.             break;
  745.     
  746.     wSel = ltoh[i].wHeap;
  747.     
  748.     lstrcpy(buf, line);
  749.     
  750.     if (sscanf(buf, "%04X    %04X  %04X  %s", &wH, &wOfs, &wBytes, &typ) < 1)
  751.         return;
  752.     
  753.     DispatchDump(typ, wH, wSel, wOfs, wBytes);
  754.     }
  755.  
  756.  
  757.  
  758. void OtherWndLine(HWND hwnd, LPSTR line, int lineNum)
  759.     {
  760.     WORD wSel, wH, wOfs, wBytes;
  761.     char typ[20];
  762.     char buf[80];
  763.     int i;
  764.     
  765.     if ((*line++ != '-') || (*line++ != '>')) return;
  766.     
  767.     GetWindowText(hwnd, (LPSTR) &buf, sizeof(buf));
  768.     sscanf(buf, "%s handle %04X @ %04X:%04X for %d bytes",
  769.         &typ, &wH, &wSel, &wOfs, &wBytes);
  770.  
  771.     lstrcpy(buf, line);
  772.     
  773.     if (sscanf(buf, "      %s : %04X", &typ, &wH) < 1)
  774.         return;
  775.     
  776.     if (! wH)
  777.         {
  778.         winio_warn(FALSE, szTitle, "NULL handle!");
  779.         return;
  780.         }
  781.  
  782.     for (i = 0; i < MAXHFIELDS; i++)
  783.         if (strcmp(hfld[i].szField, typ) == 0)
  784.             break;
  785.     if (i == MAXHFIELDS) return;
  786.     
  787.     if (! FarLocalSize(wSel, wH - (bDebug ? 4 : 0),
  788.         &wOfs, &wBytes))
  789.         {
  790.         winio_warn(FALSE, szTitle, "Handle %04X in USER heap %04X\n"
  791.             "is no longer valid.", wH, wSel);
  792.         DoRefresh(__hMainWnd, ID_REFRESH);
  793.         }
  794.     else
  795.         {
  796.         if (bDebug) wOfs += 4;
  797.         DispatchDump(UserBlockType[hfld[i].iType], wH, wSel, wOfs, wBytes);
  798.         }
  799.     }
  800.  
  801.  
  802.  
  803. char *GetUserBlockType(WORD type)
  804.     {
  805.     // only default USER local heap gets ToolHelp USER subtypes;
  806.     // use signatures to show for the rest?
  807.     
  808.     if ( type <= LT_USER_MAX )
  809.         return UserBlockType[type];
  810.  
  811.     if ( type == 0xFF )
  812.         return "FREE";
  813.  
  814.     return UnknownString;
  815.     }
  816.     
  817. void WalkOneUserHeap(WORD seg)
  818.     {
  819.     LOCALENTRY le;
  820.     BOOL ok;
  821.     int typ;
  822.     
  823.     if (! seg) return;
  824.  
  825.     printf(
  826.         "USER heap in segment %04xh:\n"
  827.         "(Double-click to view a block)\n"
  828.         "HANDLE  ADDR  SIZE  TYPE\n",
  829.         seg);
  830.     nLineNum += 4;
  831.     
  832.     ltoh[nHeaps].wHeap = seg;
  833.     ltoh[nHeaps].nFirstLine = nLineNum;
  834.     
  835.     le.dwSize = sizeof(le);
  836.     ok = LocalFirst(&le, seg);
  837.  
  838.     while ( ok )
  839.         {
  840.         // In debug versions, each block has a 4 byte overhead
  841.         if (bDebug)
  842.             {
  843.             le.hHandle += 4;
  844.             le.wAddress += 4;
  845.             }
  846.     
  847.         nLineNum++;
  848.         typ = le.wType ? le.wType
  849.                 : (IsWindow(le.hHandle) && (seg == wDefHeap)) ? 2
  850.                 : *((WORD FAR *) MK_FP(seg, le.wAddress + 2)) ==
  851.                     CLASS_MAGIC ? 1
  852.                 : MyIsMenu(seg, le.wAddress) ? 4
  853.                 : 0;
  854.         printf
  855.             (
  856.             "%04X    %04X  %04X  %s\n",
  857.             le.hHandle, le.wAddress, le.wSize,
  858.             GetUserBlockType(typ)
  859.             );
  860.         heaptots[nHeaps] += le.wSize;
  861.         if (typ < BLOCKTYPES)
  862.             ((DWORD) (heapamnts[nHeaps])[typ]) += le.wSize;
  863.  
  864.         ok = LocalNext(&le);
  865.         }
  866.     ltoh[nHeaps].nLastLine = nLineNum;
  867.     printf("\n");
  868.     nLineNum++;
  869.     }
  870.  
  871.  
  872. void UserHeapWalk(void)
  873.     {
  874.     GLOBALENTRY ge;
  875.     BOOL ok;
  876.     HANDLE hUSER = GetModuleHandle("USER");
  877.  
  878.     winio_clear(__hMainWnd);
  879.  
  880.     // Turn off repaints to minimize Heisenberg effects on heap
  881.         
  882.     winio_setbusy();
  883.     winio_setpaint(__hMainWnd, FALSE);
  884.  
  885.     memset(heapamnts, 0, sizeof(heapamnts));
  886.     memset(heaptots, 0, sizeof(heaptots));
  887.     nHeaps = 0;
  888.     nLineNum = 0;
  889.     
  890.     WalkOneUserHeap(wDefHeap);
  891.     printf("\n");
  892.  
  893.     nHeaps = 1;
  894.     ge.dwSize = sizeof(ge);
  895.     ok = GlobalFirst(&ge, GLOBAL_ALL);
  896.     
  897.     while ( ok )
  898.         {
  899.         if (ge.hOwner == hUSER)
  900.             {
  901.             ge.hBlock &= 0xfffc;
  902.             ge.hBlock |= 1;
  903.     
  904.             if ((ContainsLocalHeap(ge.hBlock)) &&
  905.                 (ge.hBlock != wDefHeap))
  906.                 {
  907.                 if ((b31) && IsMenuHeap(ge.hBlock))
  908.                     wMenuHeap = ge.hBlock;
  909.                 WalkOneUserHeap(ge.hBlock);
  910.                 nHeaps++;
  911.                 printf("\n");
  912.                 }
  913.             }
  914.         ok = GlobalNext(&ge, GLOBAL_ALL);
  915.         }
  916.  
  917.     // Turn repaints back on, and position at the top of the info
  918.     winio_setpaint(__hMainWnd, TRUE);
  919.     winio_resetbusy();
  920.     winio_home(__hMainWnd);
  921.     }
  922.  
  923.  
  924. void AnalyzeHeaps(HWND hwnd, WORD wID)
  925.     {
  926.     int i, j;
  927.     long amnt, total, totfree, allheaps = 0;
  928.     HWND hwndSave;
  929.  
  930.     if (IsWindow(hwndAnal))
  931.         {
  932.         ShowWindow(hwndAnal, SW_RESTORE);
  933.         BringWindowToTop(hwndAnal);
  934.         return;
  935.         }
  936.     
  937.     winio_defwindowsize(WINDOWSIZE(20, 60));
  938.     hwndAnal = winio_window("User Heap Analysis", 1024, WW_HASMENU);
  939.  
  940.     hwndSave = winio_setcurrent(hwndAnal);
  941.     winio_setpaint(hwndAnal, FALSE);
  942.     winio_setbusy();
  943.     
  944.     for (i = 0; i < nHeaps; i++)
  945.         {
  946.         printf("Heap at %04X is %ld bytes:\n", ltoh[i].wHeap,
  947.             totfree = total = heaptots[i]);
  948.         for (j = 0; j < BLOCKTYPES; j++)
  949.             {
  950.             if (! (amnt = heapamnts[i][j])) continue;
  951.             printf("\t%-20.20sTotal: %ld bytes (%ld%c)\n",
  952.                 UserBlockType[j], amnt, (amnt * 100) / total, '%');
  953.             totfree -= amnt;
  954.             }
  955.         printf("\tFREE\t\t\t : %ld bytes (%ld%c)\n",
  956.             totfree, ((totfree * 100) / total) + 1 /* Kludge for rounding */,
  957.             '%');
  958.         allheaps += total;
  959.         printf("\n");
  960.         }
  961.     
  962.     printf("Total USER memory in heaps is %ld bytes", allheaps);
  963.     
  964.     winio_resetbusy();
  965.     winio_setpaint(hwndAnal, TRUE);
  966.     winio_home(hwndAnal);
  967.     winio_setcurrent(hwndSave);
  968.     }
  969.  
  970.  
  971.  
  972. void printtree(WORD wndofs)
  973.     {
  974.     char strClass[20];
  975.     char strText[80];
  976.     WND_3_0 far *hwnd30;
  977.     WND_3_1 far *hwnd31;
  978.     CLASS far *pcls;
  979.     WORD clsofs;
  980.  
  981.     for (;;)
  982.         {
  983.         (void far *) hwnd31 = (void far *) hwnd30 = MK_FP(wDefHeap, wndofs);
  984.         GetWindowText((HWND) wndofs, (LPSTR) &strText, sizeof(strText));
  985.         clsofs = (WORD) (b31 ? hwnd31->hClass : hwnd30->hClass);
  986.         (void far *) pcls = MK_FP(wDefHeap, clsofs);
  987.         if (pcls->wSig != 0x4b4e)
  988.             winio_warn(FALSE, __szModule, "Bad class pointer");
  989.         GetAnyAtomName(wDefHeap, pcls->atomCls,
  990.             (LPSTR) &strClass, sizeof(strClass));
  991.         printf("%s%04X %s [%s]\n", strIndent, wndofs, strText, strClass);
  992.         if (hwnd30->hwndChild != NULL)
  993.             {
  994.             strIndent -= 4;
  995.             printtree(hwnd30->hwndChild);
  996.             strIndent += 4;
  997.             }
  998.  
  999.         if ((wndofs = hwnd30->hwndNext) == NULL)
  1000.             break;
  1001.         }
  1002.     }
  1003.  
  1004.  
  1005. void buildlist(void)
  1006.     {
  1007.     winio_clear(__hMainWnd);
  1008.     winio_setbusy();
  1009.     winio_setpaint(__hMainWnd, FALSE);
  1010.     
  1011.     strIndent += strlen(strIndent);
  1012.     
  1013.     printtree(GetDesktopWindow());
  1014.  
  1015.     winio_setpaint(__hMainWnd, TRUE);
  1016.     winio_resetbusy();
  1017.     winio_home(__hMainWnd);
  1018.     }
  1019.  
  1020.  
  1021.  
  1022. void SwitchToHeaps(HWND hwnd, WORD wID)
  1023.     {
  1024.     char buf[50];
  1025.     
  1026.     strcpy(buf, szTitle);
  1027.     strcat(buf, ": Heap Segments");
  1028.     winio_settitle(__hMainWnd, buf);
  1029.     CheckMenuItem(hMenuView, ID_HEAPS, MF_CHECKED);
  1030.     CheckMenuItem(hMenuView, ID_WINDOWS, MF_UNCHECKED);
  1031.     winio_setlinefn(hwnd, HeapViewLine);
  1032.     wCurrView = ID_HEAPS;
  1033.     
  1034.     UserHeapWalk();
  1035.     }
  1036.  
  1037.  
  1038. void SwitchToWindows(HWND hwnd, WORD wID)
  1039.     {
  1040.     char buf[50];
  1041.     
  1042.     strcpy(buf, szTitle);
  1043.     strcat(buf, ": Window Hierarchy");
  1044.     winio_settitle(__hMainWnd, buf);
  1045.     CheckMenuItem(hMenuView, ID_HEAPS, MF_UNCHECKED);
  1046.     CheckMenuItem(hMenuView, ID_WINDOWS, MF_CHECKED);
  1047.     winio_setlinefn(hwnd, WindowViewLine);
  1048.     wCurrView = ID_WINDOWS;
  1049.     
  1050.     buildlist();
  1051.     }
  1052.  
  1053.  
  1054. void DoRefresh(HWND hwnd, WORD wID)
  1055.     {
  1056.     switch (wCurrView)
  1057.         {
  1058.         case ID_HEAPS :
  1059.             UserHeapWalk();
  1060.             if (IsWindow(hwndAnal))
  1061.                 AnalyzeHeaps(__hMainWnd, ID_ANALYSIS);
  1062.             break;
  1063.         case ID_WINDOWS : buildlist(); break;
  1064.         }
  1065.     }
  1066.  
  1067.  
  1068. int main(int argc, char *argv[] )
  1069.     {
  1070.     b31 = HIBYTE(GetVersion());
  1071.     bDebug = GetSystemMetrics(SM_DEBUG);
  1072.     winio_setbufsize(__hMainWnd, (WORD) 65530, FALSE);
  1073.  
  1074.     winio_about("USERWALK"
  1075.         "\nWalks the USER heap segments, and allows browsing"
  1076.         "\nof recognized structures."
  1077.         "\n\nFrom Chapter 6 of"
  1078.         "\n\"Undocumented Windows\" (Addison-Wesley, 1992)"
  1079.         "\nby Andrew Schulman, David Maxey and Matt Pietrek"
  1080.         );
  1081.     
  1082.     winio_setmenufunc(__hMainWnd, ID_REFRESH, (MENU_FUNC) DoRefresh);
  1083.     winio_setmenufunc(__hMainWnd, ID_HEAPS, (MENU_FUNC) SwitchToHeaps);
  1084.     winio_setmenufunc(__hMainWnd, ID_ANALYSIS, (MENU_FUNC) AnalyzeHeaps);
  1085.     winio_setmenufunc(__hMainWnd, ID_WINDOWS, (MENU_FUNC) SwitchToWindows);
  1086.  
  1087.     hMenuView = CreateMenu();
  1088.     AppendMenu(hMenuView, MF_ENABLED | MF_CHECKED | MF_STRING,
  1089.         ID_HEAPS, "&Heap Segments");
  1090.     AppendMenu(hMenuView, MF_ENABLED | MF_STRING,
  1091.         ID_WINDOWS, "&Window Hierarchy");
  1092.     InsertMenu(winio_hmenumain(__hMainWnd), 1,
  1093.         MF_ENABLED | MF_POPUP | MF_BYPOSITION,
  1094.         hMenuView, "&View");
  1095.     InsertMenu(winio_hmenumain(__hMainWnd), 2,
  1096.         MF_ENABLED | MF_STRING | MF_BYPOSITION,
  1097.         ID_ANALYSIS, "&Analyze Heaps");
  1098.     InsertMenu(winio_hmenumain(__hMainWnd), 3,
  1099.         MF_ENABLED | MF_STRING | MF_BYPOSITION,
  1100.         ID_REFRESH, "&Refresh!");
  1101.     DrawMenuBar(__hMainWnd);
  1102.  
  1103.     shi.dwSize = sizeof(shi);
  1104.     SystemHeapInfo(&shi);
  1105.     wDefHeap = shi.hUserSegment;
  1106.     wDefHeap &= 0xfffc;
  1107.     wDefHeap |= 1;
  1108.  
  1109.     if (! b31) wMenuHeap = wDefHeap;
  1110.  
  1111.     // This must be the first View type to initialize the heap types
  1112.     SwitchToHeaps(__hMainWnd, ID_HEAPS);
  1113.  
  1114.     wMenuHeap &= 0xfffc;
  1115.     wMenuHeap |= 1;
  1116.  
  1117.     return 0;
  1118.     }
  1119.  
  1120. // We can rely on ToolHelp to tell us whether a block contains a
  1121. // valid local heap; all the homebrew validity checks are not needed here
  1122. BOOL ContainsLocalHeap(WORD seg)
  1123.     {
  1124.     LOCALENTRY le;
  1125.     le.dwSize = sizeof(le);
  1126.     return LocalFirst(&le, seg);
  1127.     }
  1128.  
  1129.  
  1130. BOOL FarLocalSize(WORD wSel, WORD wH, WORD *pOfs, WORD *pSize)
  1131.     {
  1132.     LOCALENTRY le;
  1133.     BOOL ok;
  1134.     
  1135.     le.dwSize = sizeof(le);
  1136.     ok = LocalFirst(&le, wSel);
  1137.  
  1138.     while (ok && (le.hHandle != wH))
  1139.         ok = LocalNext(&le);
  1140.     if (ok)
  1141.         {
  1142.         *pOfs = le.wAddress;
  1143.         *pSize = le.wSize;
  1144.         }
  1145.     return ok;
  1146.     }
  1147.  
  1148.  
  1149. BOOL MyIsMenu(WORD wSel, WORD wOfs)
  1150.     {
  1151.     MENU_3_0 FAR *lp30 = MK_FP(wSel, wOfs);
  1152.     MENU_3_1 FAR *lp31 = MK_FP(wSel, wOfs);
  1153.     
  1154.     return b31 ? lp31->wMagic == MENU_MAGIC
  1155.         : (lp30->cbMenu ==
  1156.                 (((lp30->cItems + 1) * sizeof(ITEM)) + sizeof(MENU_3_0)));
  1157.     }
  1158.  
  1159. void GetAnyAtomName(WORD wSeg, ATOM nAtom, LPSTR lpBuffer, int nSize)
  1160.     {
  1161.     _asm push ds;
  1162.     _asm mov ds, word ptr wSeg;
  1163.     GetAtomName(nAtom, lpBuffer, nSize);
  1164.     _asm pop ds;
  1165.     }
  1166.  
  1167.  
  1168. BOOL IsMenuHeap(WORD wSel)
  1169.     {
  1170.     // This is a terrible hack, but it appears to work!
  1171.     return ((MENU_3_1 FAR *) MK_FP(wSel, 0x54))->wMagic == MENU_MAGIC;
  1172.     }
  1173.  
  1174.