STASTRIP.C
上传用户:bangxh
上传日期:2007-01-31
资源大小:42235k
文件大小:21k
源码类别:

Windows编程

开发平台:

Visual C++

  1. /*
  2.  * STASTRIP.C
  3.  * StatStrip Control
  4.  *
  5.  * Window procedure and other functions that are frequently used in
  6.  * the life of a StatStrip.
  7.  *
  8.  * Copyright (c)1993-1995 Microsoft Corporation, All Rights Reserved
  9.  *
  10.  * Kraig Brockschmidt, Microsoft
  11.  * Internet  :  kraigb@microsoft.com
  12.  * Compuserve:  >INTERNET:kraigb@microsoft.com
  13.  */
  14. #include "inoledll.h"
  15. /*
  16.  * StatStripWndProc
  17.  *
  18.  * Purpose:
  19.  *  Window procedure for the StatStrip control.
  20.  */
  21. LRESULT APIENTRY StatStripWndProc(HWND hWnd, UINT iMsg
  22.     , WPARAM wParam, LPARAM lParam)
  23.     {
  24.     PSTATSTRIP      pST;
  25.     int             cyFont;
  26.     HDC             hDC;
  27.     LRESULT         lRet;
  28.     pST=(PSTATSTRIP)GetWindowLong(hWnd, STATWL_STRUCTURE);
  29.     switch (iMsg)
  30.         {
  31.         case WM_NCCREATE:
  32.             pST=(PSTATSTRIP)(void *)LocalAlloc(LPTR, CBSTATSTRIP);
  33.             if (NULL==pST)
  34.                 return -1L;
  35.             //Calc size of 0 point font, which we'll use as default.
  36.             hDC=GetDC(NULL);
  37.             cyFont=-MulDiv(10, GetDeviceCaps(hDC, LOGPIXELSY), 72);
  38.             ReleaseDC(NULL, hDC);
  39.             pST->hFont=CreateFont(cyFont, 0, 0, 0, FW_NORMAL, FALSE
  40.                 , FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS
  41.                 , CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY
  42.                 , VARIABLE_PITCH | FF_SWISS, TEXT("MS Sans Serif"));
  43.             /*
  44.              * If we could create the font, remember that we own it.
  45.              * If font creation failed, then we'll just do with the
  46.              * system font.
  47.              */
  48.             pST->fMyFont=(NULL!=pST->hFont);
  49.             SetWindowLong(hWnd, STATWL_STRUCTURE, (LONG)pST);
  50.             return DefWindowProc(hWnd, iMsg, wParam, lParam);
  51.         case WM_DESTROY:
  52.             StatStripClean(pST, TRUE);
  53.             LocalFree((HLOCAL)(UINT)(LONG)pST);
  54.             break;
  55.         case WM_ERASEBKGND:
  56.             /*
  57.              * Eat this message to avoid erasing portions that we are
  58.              * going to repaint in WM_PAINT.  Part of a change-state-
  59.              * and-repaint strategy is to rely on WM_PAINT to do
  60.              * anything visual, which includes erasing invalid
  61.              * portions.  Letting WM_ERASEBKGND erase the background
  62.              * is redundant.
  63.              */
  64.             return TRUE;
  65.         case WM_PAINT:
  66.             StatStripPaint(hWnd, pST);
  67.             break;
  68.         case WM_SETFONT:
  69.             if (!IsWindowEnabled(hWnd))
  70.                 break;
  71.             /*
  72.              * wParam has the new font.  Always repaint immediately.
  73.              * First, delete the old font only if we own it.
  74.              */
  75.             if (NULL!=pST->hFont && pST->fMyFont)
  76.                 DeleteObject(pST->hFont);
  77.             //Save the new font but mark that we don't own it.
  78.             pST->hFont=(HFONT)wParam;
  79.             pST->fMyFont=FALSE;
  80.             InvalidateRect(hWnd, NULL, FALSE);
  81.             UpdateWindow(hWnd);
  82.             break;
  83.         case WM_GETFONT:
  84.             return (LRESULT)(UINT)pST->hFont;
  85.         case WM_SETTEXT:
  86.             if (!IsWindowEnabled(hWnd))
  87.                 break;
  88.             //This saves the text for us, so we only have to repaint.
  89.             lRet=DefWindowProc(hWnd, iMsg, wParam, lParam);
  90.             InvalidateRect(hWnd, NULL, FALSE);
  91.             UpdateWindow(hWnd);
  92.             break;
  93.         case WM_ENABLE:
  94.             //Repaint on enabling or disabling either way.
  95.             InvalidateRect(hWnd, NULL, FALSE);
  96.             UpdateWindow(hWnd);
  97.             break;
  98.         //Control-specific messages
  99.         case STATM_MESSAGEMAP:
  100.             //lParam is an LPSTATMESSAGEMAPINIT
  101.             if (0L!=lParam)
  102.                 {
  103.                 LPSTATMESSAGEMAPINIT  pMI=(LPSTATMESSAGEMAPINIT)lParam;
  104.                 return (LRESULT)StatStripMessageMap(hWnd
  105.                     , pMI->hWndOwner, pMI->hInst, pMI->uIDRMap
  106.                     , pMI->idsMin, pMI->idsMax, pMI->cchMax
  107.                     , pMI->uIDPopupMin, pMI->uIDPopupMax
  108.                     , pMI->uIDStatic, pMI->uIDBlank
  109.                     , pMI->uIDSysMenu);
  110.                 }
  111.             break;
  112.         case STATM_MENUSELECT:
  113.             //wParam and lParam from caller's WM_MENUSELECT message
  114.             StatStripMenuSelect(hWnd, wParam, lParam);
  115.             break;
  116.         case STATM_MESSAGEDISPLAY:
  117.             //wParam is ID to display
  118.             StatStripMessageDisplay(hWnd, (USHORT)wParam);
  119.             break;
  120.         default:
  121.             return DefWindowProc(hWnd, iMsg, wParam, lParam);
  122.         }
  123.     return 0L;
  124.     }
  125. /*
  126.  * StatStripPaint
  127.  * Internal
  128.  *
  129.  * Purpose:
  130.  *  Provides drawing the StatStrip window with the 3-D effect and the
  131.  *  current message and the current font.
  132.  *
  133.  * Parameters:
  134.  *  hWnd            HWND of the window;
  135.  *  pST             PSTATSTRIP containing control information.
  136.  *
  137.  * Return Value:
  138.  *  None
  139.  */
  140. void StatStripPaint(HWND hWnd, PSTATSTRIP pST)
  141.     {
  142.     int             y;
  143.     HDC             hDC;
  144.     RECT            rc;
  145.     UINT            cch;
  146.     TCHAR           szMsg[512];
  147.     HPEN            hPenFrame, hPenHigh;
  148.     HFONT           hFontT;
  149.     HBRUSH          hBr;
  150.     COLORREF        crHighlight;
  151.     TEXTMETRIC      tm;
  152.     PAINTSTRUCT     ps;
  153.     hDC=BeginPaint(hWnd, &ps);
  154.     GetClientRect(hWnd, &rc);
  155.     //Draw the top line using the frame color
  156.     hPenFrame=CreatePen(PS_SOLID, 1, GetSysColor(COLOR_WINDOWFRAME));
  157.     if (NULL!=hPenFrame)
  158.         {
  159.         SelectObject(hDC, hPenFrame);
  160.         MoveToEx(hDC, rc.left, rc.top, NULL);
  161.         LineTo(hDC, rc.right, rc.top);
  162.         }
  163.     //Draw the two highlight lines
  164.     crHighlight=GetSysColor(COLOR_BTNHIGHLIGHT);
  165.     hPenHigh=CreatePen(PS_SOLID, 1, crHighlight);
  166.     if (NULL!=hPenHigh)
  167.         {
  168.         SelectObject(hDC, hPenHigh);
  169.         MoveToEx(hDC, rc.left, rc.bottom, NULL);
  170.         LineTo(hDC, rc.left, rc.top+1);
  171.         LineTo(hDC, rc.right, rc.top+1);
  172.         }
  173.     //Draw the face color avoiding the frame and highlight
  174.     rc.top +=2;
  175.     rc.left+=1;
  176.     hBr=CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
  177.     FillRect(hDC, &rc, hBr);
  178.     DeleteObject(hBr);
  179.     //Now write the text, color sensitive to the enabled state
  180.     SetBkMode(hDC, TRANSPARENT);
  181.     SetTextColor(hDC, GetSysColor(IsWindowEnabled(hWnd)
  182.         ? COLOR_BTNTEXT : COLOR_BTNSHADOW));
  183.     //Center the font vertically, accounting for borders on the rect.
  184.     hFontT=SelectObject(hDC, pST->hFont);
  185.     GetTextMetrics(hDC, &tm);
  186.     y=rc.top+(((rc.bottom-rc.top)-tm.tmHeight) >> 1);
  187.     y=max(y, rc.top);
  188.     cch=GetWindowText(hWnd, szMsg, sizeof(szMsg));
  189.     TextOut(hDC, 4, y, szMsg, cch);
  190.     SelectObject(hDC, hFontT);
  191.     //Clean up
  192.     EndPaint(hWnd, &ps);
  193.     DeleteObject(hPenHigh);
  194.     DeleteObject(hPenFrame);
  195.     return;
  196.     }
  197. /*
  198.  * StatStripMenuSelect
  199.  * External
  200.  *
  201.  * Purpose:
  202.  *  Displays the appropriate message for whatever is in the
  203.  *  parameters of a WM_MENUSELECT message.  This can only be called
  204.  *  if StatStripMessageMap has already been called and must be used
  205.  *  with the same menu the owner window had at the time of that call.
  206.  *
  207.  *  Since we're unpacking an application's messages for it, the app
  208.  *  and this DLL must match 16-16 and 32-32 bits.
  209.  *
  210.  * Parameters:
  211.  *  hWnd            HWND of the StatStrip
  212.  *  wParam          WPARAM of the WM_MENUSELECT message.
  213.  *  lParam          LPARAM of the WM_MENUSELECT message.
  214.  *
  215.  * Return Value:
  216.  *  None
  217.  */
  218. void WINAPI StatStripMenuSelect(HWND hWnd, WPARAM wParam
  219.     , LPARAM lParam)
  220.     {
  221.     PSTATSTRIP      pST;
  222.     USHORT          uID;
  223.     MENUSELECTPARAMS(wItem, wMenuFlags, hMenu);
  224.     if (!IsWindow(hWnd) || !IsWindowEnabled(hWnd))
  225.         return;
  226.     pST=(PSTATSTRIP)GetWindowLong(hWnd, STATWL_STRUCTURE);
  227.     //Case 1:  Menu was cancelled, display static string
  228.     if (0==wItem && 0xFFFF==wMenuFlags)
  229.         uID=pST->uIDStatic;
  230.     else
  231.         //Case 2:  System menu selected by itself.
  232.         if (MF_POPUP & wMenuFlags && MF_SYSMENU & wMenuFlags)
  233.             uID=pST->uIDSysMenu;
  234.         else
  235.             /*
  236.              * Case 3:  A popup menu was chosen:  Find the ID for
  237.              * hMenu (in wItem)
  238.              */
  239.             if (MF_POPUP & wMenuFlags)
  240.                 uID=IDFromHMenu(pST, (HMENU)wItem);
  241.             else
  242.                 //Case 4:  A menu item is selected
  243.                 if (0!=wItem)
  244.                     uID=(UINT)wItem;
  245.                 else
  246.                     //Case 5:  Nothing is selected (like a separator)
  247.                     uID=pST->uIDBlank;
  248.     /*
  249.      * Go display the message.  Since all this function does extra
  250.      * that we may not have to do here is a GetWindowLong, which
  251.      * is quick, so by doing this we keep the code in one place and
  252.      * only have to test this API to test the other.
  253.      */
  254.     StatStripMessageDisplay(hWnd, uID);
  255.     return;
  256.     }
  257. /*
  258.  * StatStripMessageDisplay
  259.  * External
  260.  *
  261.  * Purpose:
  262.  *  Displays the appropriate message for a given ID value.   This can
  263.  *  only be called if StatStripMessageMap has already been called.
  264.  *
  265.  * Parameters:
  266.  *  hWnd            HWND of the StatStrip
  267.  *  uID             USHORT of the message to display.
  268.  *
  269.  * Return Value:
  270.  *  None
  271.  */
  272. void WINAPI StatStripMessageDisplay(HWND hWnd, USHORT uID)
  273.     {
  274.     PSTATSTRIP      pST;
  275.     UINT            idsMsg;
  276.     if (!IsWindow(hWnd) || !IsWindowEnabled(hWnd))
  277.         return;
  278.     pST=(PSTATSTRIP)GetWindowLong(hWnd, STATWL_STRUCTURE);
  279.     //Go look up the string ID to display.
  280.     idsMsg=IStringFromID(pST->pSMM, pST->cMessages, uID);
  281.     SetWindowText(hWnd, pST->ppsz[idsMsg-pST->idsMin]);
  282.     return;
  283.     }
  284. /*
  285.  * IDFromHMenu
  286.  *
  287.  * Purpose:
  288.  *  Given a specific menu handle, searches through pST->pPMM for a
  289.  *  match and returns the ID associated with that menu.
  290.  *
  291.  * Parameters:
  292.  *  pST             PSTATSTRIP of the control
  293.  *  hMenu           HMENU to search for
  294.  *
  295.  * Return Value:
  296.  *  USHORT          ID associated with the menu handle.
  297.  */
  298. USHORT IDFromHMenu(PSTATSTRIP pST, HMENU hMenu)
  299.     {
  300.     USHORT      uID=pST->uIDBlank;      //Default is empty
  301.     UINT        i;
  302.    #ifdef WIN32
  303.     /*
  304.      * Under Win32 the hMenu passed here from the WM_MENUSELECT
  305.      * message will only be the index to the actual menu.  We
  306.      * have to use GetSubMenu from the top-level menu using this
  307.      * index to actually get the menu handle.
  308.      */
  309.     hMenu=GetSubMenu(GetMenu(pST->hWndOwner), (UINT)hMenu);
  310.    #endif
  311.     for (i=0; i < pST->cPopups; i++)
  312.         {
  313.         if (pST->pPMM[i].hMenu==hMenu)
  314.             {
  315.             uID=pST->pPMM[i].uID;
  316.             break;
  317.             }
  318.         }
  319.     return uID;
  320.     }
  321. /*
  322.  * IStringFromID
  323.  *
  324.  * Purpose:
  325.  *  Performs a binary search in a STATMESSAGEMAP array looking for
  326.  *  a specific item ID returning the string ID for that item.
  327.  *
  328.  * Parameters:
  329.  *  pSMM            PSTATMESSAGEMAP to search
  330.  *  cItems          USHORT size of the map in elements
  331.  *  uID             USHORT item ID to locate.
  332.  *
  333.  * Return Value:
  334.  *  UINT            String ID associated with wItem.
  335.  */
  336. UINT IStringFromID(PSTATMESSAGEMAP pSMM, USHORT cItems, USHORT uID)
  337.     {
  338.     UINT        iLow =0;
  339.     UINT        iHigh=cItems-1;
  340.     UINT        iMid;
  341.     while (TRUE)
  342.         {
  343.         iMid=(iLow+iHigh) >> 1;
  344.         if (uID < pSMM[iMid].uID)
  345.             iHigh=iMid-1;
  346.         else
  347.             {
  348.             if (uID > pSMM[iMid].uID)
  349.                 iLow=iMid+1;
  350.             else
  351.                 break;    //Equality
  352.             }
  353.         if (iHigh < iLow)
  354.             break;
  355.         }
  356.     return pSMM[iMid].idsMsg;
  357.     }
  358. /*
  359.  * StatStripMessageMap
  360.  *
  361.  * Purpose:
  362.  *  Initializes the message mappings in the StatStrip for subsequent
  363.  *  use with StatMessageMenuSelect.  If this function is called more
  364.  *  than once then any previous initialization is cleaned up so
  365.  *  previous message IDs will then be invalid.
  366.  *
  367.  *  The total number of messages is inferred by (idsMax-idsMin+1).
  368.  *  The maximum number of popup menu items we'll hold is inferred by
  369.  *  (uIDPopupMax-uIDPopupMin+1).
  370.  *
  371.  * Parameters:
  372.  *  hWnd            HWND of the StatStrip control.
  373.  *  hWndOwner       HWND of the window owning menus.
  374.  *  hInst           HINSTANCE of the app from which to load resources
  375.  *  uIDRMap         UINT identifying a resource mapping ID values
  376.  *                  to string ID values.
  377.  *  idsMin          UINT specifying the lowest string ID to load.
  378.  *  idsMax          UINT specifying the hightest string ID to load.
  379.  *  cchMax          UINT maximum string length.
  380.  *  uIDPopupMin     USHORT lowest ID to assign to popup menus.
  381.  *  uIDPopupMax     USHORT highest ID to assign to popup menus.
  382.  *  uIDStatic       USHORT ID for the quiescent message.
  383.  *  uIDBlank        USHORT ID for a blank message.
  384.  *  uIDSysMenu      USHORT ID for the system menu.
  385.  *
  386.  * Return Value:
  387.  *  BOOL            TRUE if the function was successful, FALSE
  388.  *                  otherwise.
  389.  */
  390. BOOL WINAPI StatStripMessageMap(HWND hWnd, HWND hWndOwner
  391.     , HINSTANCE hInst , UINT uIDRMap, UINT idsMin, UINT idsMax
  392.     , UINT cchMax, USHORT uIDPopupMin, USHORT uIDPopupMax
  393.     , USHORT uIDStatic, USHORT uIDBlank, USHORT uIDSysMenu)
  394.     {
  395.     PSTATSTRIP      pST;
  396.     HMENU           hMenu;
  397.     HRSRC           hRes;
  398.     UINT            i;
  399.     USHORT          uID;
  400.    #ifdef WIN32
  401.     DWORD           cbRes;
  402.     DWORD           dwPrevProt;
  403.    #endif
  404.     if (!IsWindow(hWnd))
  405.         return FALSE;
  406.     pST=(PSTATSTRIP)GetWindowLong(hWnd, STATWL_STRUCTURE);
  407.     if (NULL==pST)
  408.         return FALSE;
  409.     //Parameter validation
  410.     if (NULL==hInst || idsMax < idsMin || uIDPopupMax < uIDPopupMin)
  411.         return FALSE;
  412.     //Clean ourselves out if we've already initialized.
  413.     if (pST->fMapped)
  414.         StatStripClean(pST, FALSE);
  415.     pST->hWndOwner  =hWndOwner;
  416.     pST->idsMin     =idsMin;
  417.     pST->idsMax     =idsMax;
  418.     pST->cMessages  =(USHORT)(idsMax-idsMin+1);
  419.     pST->uIDPopupMin=uIDPopupMin;
  420.     pST->uIDPopupMax=uIDPopupMax;
  421.     pST->cPopups    =(USHORT)(uIDPopupMax-uIDPopupMin+1);
  422.     pST->uIDStatic  =uIDStatic;
  423.     pST->uIDBlank   =uIDBlank;
  424.     pST->uIDSysMenu =uIDSysMenu;
  425.     //Load the STATMESSAGEMAP array from our resources.
  426.     hRes=FindResource(hInst, MAKEINTRESOURCE(uIDRMap), RT_RCDATA);
  427.     if (NULL==hRes)
  428.         return FALSE;
  429.     pST->hMemSMM=LoadResource(hInst, hRes);
  430.     if (NULL==pST->hMemSMM)
  431.         return FALSE;
  432.     pST->pSMM=(PSTATMESSAGEMAP)LockResource(pST->hMemSMM);
  433.     if (NULL==pST->pSMM)
  434.         {
  435.         StatStripClean(pST, FALSE);
  436.         return FALSE;
  437.         }
  438.    #ifdef WIN32
  439.     /*
  440.      * In Win32 resource pages are read-only when loaded.
  441.      * Change to read-write for the purposes of sorting
  442.      * initially.
  443.      */
  444.     cbRes=SizeofResource(hInst, hRes);
  445.     VirtualProtect(pST->pSMM, cbRes, PAGE_READWRITE, &dwPrevProt);
  446.    #endif
  447.     //Sort these for binary search lookup.
  448.     StatMessageMapSort(pST->pSMM, pST->cMessages);
  449.    #ifdef WIN32
  450.     VirtualProtect(pST->pSMM, cbRes, dwPrevProt, &dwPrevProt);
  451.    #endif
  452.     //Allocate space for string pointers
  453.     pST->ppsz=(LPTSTR *)LocalAlloc(LPTR
  454.         , sizeof(LPTSTR)*pST->cMessages);
  455.     if (NULL==pST->ppsz)
  456.         {
  457.         StatStripClean(pST, FALSE);
  458.         return FALSE;
  459.         }
  460.     //Load the stringtable for messages.
  461.     pST->hMemSzStat=HStringCache(hInst, idsMin, idsMax, cchMax
  462.         , pST->ppsz);
  463.     if (NULL==pST->hMemSzStat)
  464.         {
  465.         StatStripClean(pST, FALSE);
  466.         return FALSE;
  467.         }
  468.     //Allocate an array of POPUPMENUMAP structures
  469.     pST->pPMM=(PPOPUPMENUMAP)(void *)LocalAlloc(LPTR
  470.         , sizeof(POPUPMENUMAP)*pST->cPopups);
  471.     if (NULL==pST->pPMM)
  472.         {
  473.         StatStripClean(pST, FALSE);
  474.         return FALSE;
  475.         }
  476.     //Initialize the array mapping popup menus to specific IDs.
  477.     uID=uIDPopupMin;
  478.     hMenu=GetMenu(hWndOwner);
  479.     for (i=0; i < pST->cPopups; i++)
  480.         {
  481.         pST->pPMM[i].hMenu=GetSubMenu(hMenu, i);
  482.         pST->pPMM[i].uID  =uID++;
  483.         }
  484.     pST->fMapped=TRUE;
  485.     return TRUE;
  486.     }
  487. /*
  488.  * StatStripClean
  489.  *
  490.  * Purpose:
  491.  *  Cleans out any allocations in a STATSTRIP.
  492.  *
  493.  * Parameters:
  494.  *  pST             PSTATSTRIP to clean
  495.  *  fIncludeFont    BOOL indicates if we're to also clean the font.
  496.  *                  This is FALSE from StatMessageMap, TRUE from
  497.  *                  WM_DESTROY.
  498.  *
  499.  * Return Value:
  500.  *  None
  501.  */
  502. void StatStripClean(PSTATSTRIP pST, BOOL fIncludeFont)
  503.     {
  504.     //Free up anything from StatMessageMap
  505.     if (NULL!=pST->pPMM)
  506.         {
  507.         LocalFree((HLOCAL)(UINT)(LONG)pST->pPMM);
  508.         pST->pPMM=NULL;
  509.         }
  510.     if (NULL!=pST->ppsz)
  511.         {
  512.         LocalFree((HLOCAL)(UINT)(LONG)pST->ppsz);
  513.         pST->ppsz=NULL;
  514.         }
  515.     if (NULL!=pST->hMemSzStat)
  516.         {
  517.         HStringCacheFree(pST->hMemSzStat);
  518.         pST->hMemSzStat=NULL;
  519.         }
  520.     if (NULL!=pST->pSMM)
  521.         {
  522.        #ifndef WIN32
  523.         UnlockResource(pST->hMemSMM);
  524.        #endif
  525.         pST->pSMM=NULL;
  526.         }
  527.     if (NULL!=pST->hMemSMM)
  528.         {
  529.         FreeResource(pST->hMemSMM);
  530.         pST->hMemSMM=NULL;
  531.         }
  532.     //Delete the old font only if we own it.
  533.     if (fIncludeFont)
  534.         {
  535.         if (NULL!=pST->hFont && pST->fMyFont)
  536.             {
  537.             DeleteObject(pST->hFont);
  538.             pST->hFont=NULL;
  539.             }
  540.         }
  541.     return;
  542.     }
  543. /*
  544.  * HStringCache
  545.  *
  546.  * Purpose:
  547.  *  Allocates memory and reads a stringtable into that memory.
  548.  *  Pointers to the strings in the memory are then stored at ppsz
  549.  *  which is assumed to be (idsMax-idsMin+1) strings long.
  550.  *
  551.  * Parameters:
  552.  *  hInst           HINSTANCE of the application
  553.  *  idsMin          UINT string index to start loading
  554.  *  idsMax          UINT maximum string index in this table.
  555.  *  cchMax          UINT length of the longest string in TCHARs.
  556.  *  ppsz            LPTSTR * to an array in which to store pointers.
  557.  *
  558.  * Return Value:
  559.  *  HGLOBAL         Handle to the memory.  NULL if memory could
  560.  *                  not be allocated.
  561.  */
  562. HGLOBAL HStringCache(HINSTANCE hInst, UINT idsMin, UINT idsMax
  563.     , UINT cchMax, LPTSTR *ppsz)
  564.     {
  565.     HANDLE      hMem;
  566.     LPTSTR      pch;
  567.     UINT        cchUsed=0;
  568.     UINT        cch;
  569.     UINT        i, cStrings;
  570.     cStrings=idsMax-idsMin+1;
  571.     hMem=GlobalAlloc(GHND, cStrings * cchMax * sizeof(TCHAR));
  572.     if (NULL!=hMem)
  573.         {
  574.         pch=GlobalLock(hMem);
  575.         /*
  576.          * Load the strings into the memory and retain the specific
  577.          * pointer to that string.
  578.          */
  579.         for (i=0; i < cStrings; i++)
  580.             {
  581.             cch=LoadString(hInst, i+idsMin, (LPTSTR)(pch+cchUsed)
  582.                 , cchMax-1);
  583.             ppsz[i]=(LPTSTR)(pch+cchUsed);
  584.             /*
  585.              * Add one char to cch to include a NULL.  The memory
  586.              * was ZEROINITed on allocation so by skipping a TCHAR
  587.              * we get the NULL.
  588.              */
  589.             cchUsed+=cch+sizeof(TCHAR);
  590.             }
  591.         /*
  592.          * Memory is locked for the duration of the app.  Don't
  593.          * bother reallocating since we might have to recalc
  594.          * all the pointers to the strings again.
  595.          */
  596.         }
  597.     return hMem;
  598.     }
  599. /*
  600.  * HStringCacheFree
  601.  *
  602.  * Purpose:
  603.  *  Frees up any memory associated with the string cache from
  604.  *  HStringCache.
  605.  *
  606.  * Parameters:
  607.  *  hMem            HGLOBAL to the memory containing the cahce.
  608.  *
  609.  * Return Value:
  610.  *  None
  611.  */
  612. void HStringCacheFree(HGLOBAL hMem)
  613.     {
  614.     if (NULL!=hMem)
  615.         {
  616.         GlobalUnlock(hMem);
  617.         GlobalFree(hMem);
  618.         }
  619.     return;
  620.     }
  621. /*
  622.  * StatMessageMapSort
  623.  *
  624.  * Purpose:
  625.  *  Performs a selection sort on the STATMESSAGEMAP array that we
  626.  *  load from our resource.  Since we expect that the data is
  627.  *  partially sorted (we tend to place things in resources in groups
  628.  *  of seqential values), since the number of messages is usually
  629.  *  < 200, and since we're in startup (which takes a long time
  630.  *  anyway), selection sort is a better choice to implement over
  631.  *  qsort saving much code.
  632.  *
  633.  * Parameters:
  634.  *  pSMM            PSTATMESSAGEMAP to sort
  635.  *  n               USHORT number of elements in the array.
  636.  *
  637.  * Return Value:
  638.  *  None
  639.  */
  640. void StatMessageMapSort(PSTATMESSAGEMAP pSMM, USHORT n)
  641.     {
  642.     USHORT          i, j, k;
  643.     STATMESSAGEMAP  smm;
  644.     for (j=0; j < (UINT)(n-1); j++)
  645.         {
  646.         k=j;
  647.         smm.uID   =pSMM[j].uID;
  648.         smm.idsMsg=pSMM[j].idsMsg;
  649.         for (i=j+1; i < (UINT)n; i++)
  650.             {
  651.             if (pSMM[i].uID < smm.uID)
  652.                 {
  653.                 smm.uID   =pSMM[i].uID;
  654.                 smm.idsMsg=pSMM[i].idsMsg;
  655.                 k=i;
  656.                 }
  657.             }
  658.         smm.uID       =pSMM[j].uID;
  659.         smm.idsMsg    =pSMM[j].idsMsg;
  660.         pSMM[j].uID   =pSMM[k].uID;
  661.         pSMM[j].idsMsg=pSMM[k].idsMsg;
  662.         pSMM[k].uID   =smm.uID;
  663.         pSMM[k].idsMsg=smm.idsMsg;
  664.         }
  665.     return;
  666.     }