home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: WPS_PM / WPS_PM.zip / xfld085s.zip / helpers / wphandle.c < prev    next >
C/C++ Source or Header  |  1999-02-23  |  22KB  |  621 lines

  1.  
  2. /*
  3.  *@@sourcefile wphandle.c:
  4.  *      This file contains the logic for dealing with
  5.  *      those annoying WPS object handles in OS2SYS.INI.
  6.  *
  7.  *      Function prefixes (new with V0.81):
  8.  *      --  wph*   WPS object helper functions
  9.  *
  10.  *@@include #define INCL_WINSHELLDATA
  11.  *@@include #define INCL_WINWORKPLACE
  12.  *@@include #include <os2.h>
  13.  *@@include #include "wphandle.h"
  14.  */
  15.  
  16. /*
  17.  *      This code is mostly written by Henk Kelder and published
  18.  *      with his kind permission.
  19.  *      This file Copyright (C) 1997-99 Ulrich Möller, Henk Kelder.
  20.  *      This file is part of the XFolder source package.
  21.  *      XFolder is free software; you can redistribute it and/or modify
  22.  *      it under the terms of the GNU General Public License as published
  23.  *      by the Free Software Foundation, in version 2 as it comes in the
  24.  *      "COPYING" file of the XFolder main distribution.
  25.  *      This program is distributed in the hope that it will be useful,
  26.  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
  27.  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  28.  *      GNU General Public License for more details.
  29.  */
  30.  
  31. // uncomment this to debug WPS handles
  32. // #define _PMPRINTF_
  33.  
  34. #define INCL_DOS
  35. #define INCL_WINSHELLDATA
  36. #include <os2.h>
  37.  
  38. #define OPTIONS_SIZE 32767
  39.  
  40. #include <stdio.h>
  41. #include <string.h>
  42. #include <stdlib.h>
  43. #include <io.h>
  44.  
  45. #include "wphandle.h"
  46. #include "pmprintf.h"
  47.  
  48. /* static ULONG ulHandlesSize = 0L;
  49. static BYTE  szActiveHandles[100];
  50. static BYTE  szCurHandles[100];
  51.  
  52. static BOOL  fNewFormat; */
  53.  
  54. /****************************************************
  55.  *                                                  *
  56.  *  helper functions                                *
  57.  *                                                  *
  58.  ****************************************************/
  59.  
  60. static USHORT _System wphSearchBufferForHandle(PBYTE pHandlesBuffer, ULONG ulBufSize, USHORT usParent, PSZ pszFname);
  61. PNODE _System wphFindPartName(PBYTE pHandlesBuffer, ULONG ulBufSize, USHORT usID, PSZ pszFname, USHORT usMax);
  62.  
  63. #define MakeDiskHandle(usObjID) (usObjID | 0x30000)
  64.  
  65. #define IsObjectDisk(hObject) ((hObject & 0x30000) == 0x30000)
  66.  
  67. /*
  68.  *@@ wphQueryProfileData:
  69.  *      like PrfQueryProfileData, but allocates sufficient
  70.  *      memory and returns a pointer to that buffer.
  71.  *      pulSize must point to a ULONG which will then
  72.  *      contain the no. of copied bytes.
  73.  */
  74.  
  75. PBYTE wphQueryProfileData(HINI hIniSystem,    // in: can be HINI_USER or HINI_SYSTEM
  76.                           PSZ pApp, PSZ pKey, // in: load what?
  77.                           PULONG pulSize)     // out: bytes loaded
  78. {
  79.     PBYTE pData = NULL;
  80.     if (PrfQueryProfileSize(hIniSystem, pApp, pKey, pulSize)) {
  81.         pData = malloc(*pulSize);
  82.         PrfQueryProfileData(hIniSystem, pApp, pKey, pData, pulSize);
  83.     }
  84.     // _Pmpf(("  wphQueryProfileData(%lX, %s, %s, %d)", hIniSystem, pApp, pKey, *pulSize));
  85.     return (pData);
  86. }
  87.  
  88. /*
  89.  *@@ wphEnumProfileKeys:
  90.  *      allocates memory for a buffer and copies the keys
  91.  *      for pApp into it.
  92.  *      Returns the pointer to the buffer.
  93.  *      pulKeysSize must point to a ULONG which will then
  94.  *      contain the size of the returned buffer.
  95.  */
  96.  
  97. PBYTE wphEnumProfileKeys(HINI hIniSystem,       // in: can be HINI_USER or HINI_SYSTEM
  98.                          PSZ pApp,              // in: app to query
  99.                          PULONG pulKeysSize)    // out: sizeof(return buffer)
  100. {
  101.     PBYTE pszKeys = NULL;
  102.     if (PrfQueryProfileSize(hIniSystem, pApp, NULL, pulKeysSize)) {
  103.         pszKeys = malloc(*pulKeysSize);
  104.         if (pszKeys) {
  105.             PrfQueryProfileData(hIniSystem, pApp, NULL, pszKeys, pulKeysSize);
  106.         }
  107.     }
  108.     return (pszKeys);
  109. }
  110.  
  111. /*
  112.  * wphResetBlockBuffer:
  113.  *      Reset the block buffer, make sure the buffer is re-read.
  114.  */
  115.  
  116. /* VOID _System wphResetBlockBuffer(VOID)
  117. {
  118.    if (pHandlesBuffer)
  119.    {
  120.        free(pHandlesBuffer);
  121.        pHandlesBuffer = NULL;
  122.    }
  123. } */
  124.  
  125. /*
  126.  *@@ wphQueryActiveHandles:
  127.  *      this copies the contents of PM_Workplace:ActiveHandles
  128.  *      in OS2SYS.INI into a given buffer. There are always two
  129.  *      buffers in OS2SYS.INI for object handles, called
  130.  *      "PM_Workplace:HandlesX" with "X" either being "0" or "1".
  131.  *      It seems that every time the WPS does something in the
  132.  *      handles section, it writes the data to the inactive
  133.  *      buffer first and then makes it the active buffer by
  134.  *      changing the "active handles" key. You can test this
  135.  *      by creating a shadow on your Desktop.
  136.  *
  137.  *      This function copies the key only, but not the actual
  138.  *      handles blocks (use wphReadAllBlocks for that).
  139.  *
  140.  *      This gets called by the one-shot function
  141.  *      wphQueryHandleFromPath.
  142.  */
  143.  
  144. BOOL _System wphQueryActiveHandles(HINI hIniSystem,       // in: can be HINI_USER or HINI_SYSTEM
  145.                                  PSZ pszHandlesAppName, // out: active handles buffer.
  146.                                  USHORT usMax)          // in:  sizeof(pszHandlesAppName)
  147. {
  148.     PBYTE pszHandles;
  149.     ULONG ulProfileSize;
  150.  
  151.     pszHandles = wphQueryProfileData(hIniSystem,
  152.                         ACTIVEHANDLES, HANDLESAPP,
  153.                         &ulProfileSize);
  154.     if (!pszHandles)
  155.     {
  156.         strncpy(pszHandlesAppName, HANDLES, usMax-1);
  157.         return TRUE;
  158.     }
  159.     // fNewFormat = TRUE;
  160.     strncpy(pszHandlesAppName, pszHandles, usMax-1);
  161.     free(pszHandles);
  162.     return TRUE;
  163. }
  164.  
  165. /*
  166.  *@@ wphReadAllBlocks:
  167.  *      this reads all the BLOCK's in the active handles
  168.  *      section in OS2SYS.INI into one common buffer, for
  169.  *      which sufficient memory is allocated.
  170.  *
  171.  *      Each of these blocks is a maximum of 64 KB, hell
  172.  *      knows why, maybe the INIs can't handle more. This
  173.  *      func combines all the BLOCK's into one buffer anyways,
  174.  *      which can then be searched.
  175.  *
  176.  *      Use wphQueryActiveHandles to pass the active handles
  177.  *      section to this function.
  178.  *
  179.  *      This gets called by the one-shot function
  180.  *      wphQueryHandleFromPath.
  181.  */
  182.  
  183. BOOL _System wphReadAllBlocks(HINI hiniSystem,       // in: can be HINI_USER or HINI_SYSTEM
  184.                                PSZ pszActiveHandles,  // in: active handles section
  185.                                PBYTE* ppBlock,        // in/out: pointer to buffer, which
  186.                                                       //    will point to the allocated
  187.                                                       //    memory afterwards
  188.                                PULONG pulSize)        // out: size of the allocated buffer
  189. {
  190.     PBYTE   pbAllBlocks,
  191.             pszBlockName,
  192.             p;
  193.     ULONG   ulBlockSize;
  194.     ULONG   ulTotalSize;
  195.     BYTE    rgfBlock[100];
  196.     BYTE    szAppName[10];
  197.     USHORT  usBlockNo;
  198.  
  199.     pbAllBlocks = wphEnumProfileKeys(hiniSystem, pszActiveHandles, &ulBlockSize);
  200.     if (!pbAllBlocks)
  201.        return FALSE;
  202.  
  203.     // query size of all individual blocks and calculate total
  204.     memset(rgfBlock, 0, sizeof rgfBlock);
  205.     ulTotalSize = 0L;
  206.     pszBlockName = pbAllBlocks;
  207.     while (*pszBlockName)
  208.     {
  209.         if (!memicmp(pszBlockName, "BLOCK", 5))
  210.         {
  211.            usBlockNo = atoi(pszBlockName + 5);
  212.            if (usBlockNo < 100)
  213.            {
  214.                rgfBlock[usBlockNo] = TRUE;
  215.  
  216.                if (!PrfQueryProfileSize(hiniSystem,
  217.                          pszActiveHandles,
  218.                          pszBlockName,
  219.                          &ulBlockSize))
  220.                {
  221.                    free(pbAllBlocks);
  222.                    return FALSE;
  223.                }
  224.                ulTotalSize += ulBlockSize;
  225.            }
  226.         }
  227.         pszBlockName += strlen(pszBlockName) + 1;
  228.     }
  229.  
  230.     // *pulSize now contains the total size of all blocks
  231.     *ppBlock = malloc(ulTotalSize);
  232.     if (!(*ppBlock))
  233.     {
  234.        /* MessageBox("wphReadAllBlocks", "Not enough memory for profile data!"); */
  235.          free(pbAllBlocks);
  236.          return FALSE;
  237.     }
  238.  
  239.     // now get all blocks into the memory we allocated
  240.     p = *ppBlock;
  241.     (*pulSize) = 0;
  242.     for (usBlockNo = 1; usBlockNo < 100; usBlockNo++)
  243.     {
  244.         if (!rgfBlock[usBlockNo])
  245.             break;
  246.  
  247.         sprintf(szAppName, "BLOCK%u", (UINT)usBlockNo);
  248.         ulBlockSize = ulTotalSize;
  249.         if (!PrfQueryProfileData(hiniSystem,
  250.                   pszActiveHandles,
  251.                   szAppName,
  252.                   p,
  253.                   &ulBlockSize))
  254.         {
  255.             free(pbAllBlocks);
  256.             free(*ppBlock);
  257.             return FALSE;
  258.         }
  259.         p += ulBlockSize;
  260.         (*pulSize) += ulBlockSize;
  261.         ulTotalSize -= ulBlockSize;
  262.     }
  263.  
  264.     free(pbAllBlocks);
  265.     return TRUE;
  266. }
  267.  
  268. /****************************************************
  269.  *                                                  *
  270.  *  get hObject from filename                       *
  271.  *                                                  *
  272.  ****************************************************/
  273.  
  274. /*
  275.  *@@ wphSearchBufferForHandle:
  276.  *      returns the four-digit object handle which corresponds
  277.  *      to pszFname, searching pHandlesBuffer. Note that you
  278.  *      must OR the return value with 0x30000 to make this
  279.  *      a valid WPS file-system handle.
  280.  *
  281.  *      You must pass a handles buffer to this function which
  282.  *      has been filled using wphReadAllBlocks above.
  283.  *
  284.  *      This gets called by the one-shot function
  285.  *      wphQueryHandleFromPath.
  286.  */
  287.  
  288. USHORT _System wphSearchBufferForHandle(PBYTE pHandlesBuffer, // in: handles buffer (all BLOCK's)
  289.                                 ULONG ulBufSize,      // in: sizeof(pHandlesBuffer)
  290.                                 USHORT usParent,      // in: parent NODE ID;
  291.                                                       //     must be 0 initially
  292.                                 PSZ pszFname) // in: fully qlf'd filename to search for
  293. {
  294.     PDRIV pDriv;
  295.     PNODE pNode;
  296.     PBYTE pCur;                 // current
  297.     PBYTE p,
  298.           pEnd;                 // *end of buffer
  299.     USHORT usPartSize;
  300.  
  301.     // _Pmpf(("Entering wphSearchBufferForHandle for %s", pszFname));
  302.  
  303.     // The composed BLOCKs in the handles buffer make up a tree of
  304.     // DRIVE and NODE structures (see wphandle.h). Each NODE stands
  305.     // for either a directory or a file. (We don't care about the
  306.     // DRIVE structures because the root directory gets a NODE also.)
  307.     // Each NODE contains the non-qualified file name, an object ID,
  308.     // and the object ID of its parent NODE.
  309.     // We can thus work our way through the buffer by splitting the
  310.     // fully qualified filename that we're searching for into the
  311.     // different directory names and, each time, searching for the
  312.     // corresponding NODE. If we have found that, we go for the next.
  313.     // Example for C:\OS2\E.EXE:
  314.     //   1) first search for the "C:" NODE
  315.     //   2) then find the "OS2" node which has "C" as its parent NODE
  316.     //      (we do this by comparing the parent object handles)
  317.     //   3) then find the "E.EXE" NODE the same way
  318.     // The "E.EXE" NODE then has the object handle we're looking for.
  319.  
  320.     // So first find the length of the first filename part (which
  321.     // should be 2 for the drive letter, "C:")
  322.     p = strchr(pszFname, '\\');
  323.     if (p)
  324.         // backslash found:
  325.         usPartSize = p - pszFname;      // extract first part
  326.     else
  327.         usPartSize = strlen(pszFname);
  328.  
  329.     // now set the pointer for the end of the BLOCKs buffer
  330.     pEnd = pHandlesBuffer + ulBufSize;
  331.  
  332.     // pCur is our variable pointer where we're at now; there
  333.     // is some offset of 4 bytes at the beginning (duh)
  334.     pCur = pHandlesBuffer + 4;
  335.  
  336.     // _Pmpf(("  Searching for: %s, usPartSize: %d", pszFname, usPartSize));
  337.  
  338.     // go!
  339.     while (pCur < pEnd)
  340.     {
  341.         // the first four chars tell us whether it's
  342.         // a DRIVE or a NODE structure
  343.         if (!memicmp(pCur, "DRIV", 4))
  344.         {
  345.             ULONG ul;
  346.             // pCur points to a DRIVE node:
  347.             // we don't care about these, because the root
  348.             // directory has a real NODE too, so we just
  349.             // skip this
  350.             pDriv = (PDRIV)pCur;
  351.             pCur += sizeof(DRIV) + strlen(pDriv->szName);
  352.         }
  353.         else if (!memicmp(pCur, "NODE", 4))
  354.         {
  355.             // pCur points to a regular NODE: offset pointer first
  356.             pNode = (PNODE)pCur;
  357.             pCur += sizeof (NODE) + pNode->usNameSize;
  358.  
  359.             // does the NODE have the same parent that we
  360.             // are currently searching for? This is "0"
  361.             // for root directories (and initially for us
  362.             // too)
  363.             if (usParent == pNode->usParentHandle)
  364.             {
  365.                 // yes:
  366.                 // _Pmpf(("  found matching parents (%lX): %s", usParent, pNode->szName));
  367.  
  368.                 // does the NODE have the same partname length?
  369.                 if (pNode->usNameSize == usPartSize)
  370.                 {
  371.                     // yes:
  372.                     // _Pmpf(("    found matching partnames sizes: %d", pNode->usNameSize));
  373.  
  374.                     // do the partnames match too?
  375.                     if (memicmp(pszFname, pNode->szName, usPartSize) == 0)
  376.                     {
  377.                         // OK!! proper NODE found!
  378.                         // _Pmpf(("      FOUND %s!!", pNode->szName));
  379.  
  380.                         // now check if this was the last NODE
  381.                         // we were looking for
  382.                         if (strlen(pszFname) == usPartSize)
  383.                            // yes: return ID
  384.                            return (pNode->usHandle);
  385.  
  386.                         // else: update our status;
  387.                         // get next partname
  388.                         pszFname += usPartSize + 1;
  389.                         // calc next partname length
  390.                         p = strchr(pszFname, '\\');
  391.                         if (p)
  392.                            usPartSize = p - pszFname;
  393.                         else
  394.                            usPartSize = strlen(pszFname);
  395.  
  396.                         // get next parent to search for
  397.                         // (which is the current handle)
  398.                         usParent = pNode->usHandle;
  399.                     }
  400.                 }
  401.             }
  402.         }
  403.         else
  404.             // neither DRIVE nor NODE: error
  405.             return (0);
  406.  
  407.     } // end while
  408.  
  409.     // not found: end of buffer reached
  410.     return (0);
  411. }
  412.  
  413. /*
  414.  *@@ wphQueryHandleFromPath:
  415.  *      find the object handle for pszName; this
  416.  *      can either be an object ID ("<WP_DESKTOP>")
  417.  *      or a fully qualified filename.
  418.  *      This is a one-shot function, using wphQueryActiveHandles,
  419.  *      wphReadAllBlocks, and wphSearchBufferForHandle.
  420.  *
  421.  *      Returns:
  422.  *      --  -1:     file does not exist
  423.  *      --  -2:     error querying handles buffer
  424.  *      --  0:      object handle not found; might not exist
  425.  *      --  other:  found object handle
  426.  *
  427.  *      NOTE: This function uses C runtime library
  428.  *      string comparison functions. These only work
  429.  *      properly if you have set the locale for the
  430.  *      C runtime properly. This is, for example, a
  431.  *      problem with file names containing German umlauts,
  432.  *      which are not found properly.
  433.  *      You should put some
  434.  +          setlocale(LC_ALL, "");
  435.  *      statement somewhere, which reacts to the LANG
  436.  *      variable which OS/2 puts into CONFIG.SYS per
  437.  *      default.
  438.  */
  439.  
  440. HOBJECT _System wphQueryHandleFromPath(HINI hIniUser,   // in: user ini file
  441.                                  HINI hIniSystem,       // in: system ini file
  442.                                  PSZ pszName)           // in: fully qlf'd filename
  443. {
  444.     PBYTE       pHandlesBuffer = NULL;
  445.     ULONG       cbHandlesBuffer = 0,
  446.                 cbLocation = 0;
  447.     USHORT      usObjID;
  448.     PVOID       pvData;
  449.     HOBJECT     hObject = 0L;
  450.     BYTE        szFullPath[300];
  451.     CHAR        szActiveHandles[100];
  452.  
  453.     // _Pmpf(("wphQueryHandleFromPath: %s", pszName));
  454.  
  455.     // try to get the objectID via PM_Workplace:Location, since
  456.     // pszName might also be a "<WP_DESKTOP>" like thingy
  457.     pvData = wphQueryProfileData(hIniUser,
  458.                         LOCATION,   // "PM_Workplace:Location" (<WP_DESKTOP> etc.)
  459.                         pszName,
  460.                         &cbLocation);
  461.     if (pvData) {
  462.         // found there:
  463.         if (cbLocation >= sizeof(HOBJECT))
  464.             hObject = *(PULONG)pvData;
  465.         free(pvData);
  466.         // _Pmpf(("  object ID found, hObject: %lX", hObject));
  467.     }
  468.     if (hObject)
  469.         return (hObject);
  470.  
  471.     // not found there: is pszName an existing pathname?
  472.     if (access(pszName, 0)) {    // POSIX C for checking existence
  473.         // == -1: file does not exist
  474.         // _Pmpf(("  path not found, returning -1"));
  475.         return (-1);
  476.     }
  477.  
  478.     // else: make full path for pszName
  479.     _fullpath(szFullPath, pszName, sizeof(szFullPath));
  480.  
  481.     // check if the HandlesBlock is valid
  482.     wphQueryActiveHandles(hIniSystem, szActiveHandles, sizeof(szActiveHandles));
  483.     // _Pmpf(("  szActiveHandles: %s", szActiveHandles));
  484.  
  485.     // now load all the BLOCKs into a common buffer
  486.     if (!wphReadAllBlocks(hIniSystem,
  487.                            szActiveHandles,
  488.                            &pHandlesBuffer, &cbHandlesBuffer))
  489.         // error:
  490.         return (-2);
  491.  
  492.     // and search that buffer
  493.     usObjID = wphSearchBufferForHandle(pHandlesBuffer,
  494.                                 cbHandlesBuffer,
  495.                                 0,                  // usParent
  496.                                 szFullPath);
  497.  
  498.     if (usObjID)
  499.         // found: OR 0x30000
  500.         hObject = MakeDiskHandle(usObjID);
  501.  
  502.     free(pHandlesBuffer);
  503.  
  504.     return (hObject);
  505. }
  506.  
  507. /****************************************************
  508.  *                                                  *
  509.  *  get filename from hObject                       *
  510.  *                                                  *
  511.  ****************************************************/
  512.  
  513. /*
  514.  *@@ wphFindPartName:
  515.  *      this searches pHandlesBuffer for usHandle and, if found,
  516.  *      appends the object partname to pszFname.
  517.  *      This function recurses, if neccessary.
  518.  *
  519.  *      This gets called by the one-shot function
  520.  *      wphQueryPathFromHandle.
  521.  */
  522.  
  523. PNODE _System wphFindPartName(PBYTE pHandlesBuffer, // in: handles buffer
  524.                              ULONG ulBufSize,       // in: buffer size
  525.                              USHORT usHandle,       // in: handle to search for
  526.                              PSZ pszFname,          // out: object partname
  527.                              USHORT usMax)          // in: sizeof(pszFname)
  528. {
  529.     PDRIV pDriv;
  530.     PNODE pNode;
  531.     PBYTE p, pEnd;
  532.     USHORT usSize;
  533.  
  534.     pEnd = pHandlesBuffer + ulBufSize;
  535.     p = pHandlesBuffer + 4;
  536.     while (p < pEnd)
  537.     {
  538.         if (!memicmp(p, "DRIV", 4))
  539.         {
  540.             pDriv = (PDRIV)p;
  541.             p += sizeof(DRIV) + strlen(pDriv->szName);
  542.         }
  543.         else if (!memicmp(p, "NODE", 4))
  544.         {
  545.             pNode = (PNODE)p;
  546.             p += sizeof (NODE) + pNode->usNameSize;
  547.             if (pNode->usHandle == usHandle)
  548.             {
  549.                 usSize = usMax - strlen(pszFname);
  550.                 if (usSize > pNode->usNameSize)
  551.                     usSize = pNode->usNameSize;
  552.                 if (pNode->usParentHandle)
  553.                 {
  554.                     if (!wphFindPartName(pHandlesBuffer, ulBufSize,
  555.                                     pNode->usParentHandle,
  556.                                     pszFname,
  557.                                     usMax))
  558.                        return (NULL);
  559.                     strcat(pszFname, "\\");
  560.                     strncat(pszFname, pNode->szName, usSize);
  561.                     return pNode;
  562.                 } else {
  563.                     strncpy(pszFname, pNode->szName, usSize);
  564.                     return pNode;
  565.                 }
  566.             }
  567.         }
  568.         else
  569.            return (NULL);
  570.     }
  571.     return (NULL);
  572. }
  573.  
  574. /*
  575.  *@@ wphQueryPathFromHandle:
  576.  *      reverse to wphQueryHandleFromPath, this gets the
  577.  *      filename for hObject.
  578.  *      This is a one-shot function, using wphQueryActiveHandles,
  579.  *      wphReadAllBlocks, and wphFindPartName.
  580.  */
  581.  
  582. PNODE _System wphQueryPathFromHandle(HINI hIniSystem,
  583.                                 HOBJECT hObject,    // in: five-digit object handle
  584.                                 PSZ pszFname,       // out: filename, if found
  585.                                 USHORT usMax)       // in: sizeof(*pszFname)
  586. {
  587.     USHORT      usObjID;
  588.     PBYTE       pHandlesBuffer = NULL;
  589.     ULONG       cbHandlesBuffer = 0;
  590.     CHAR        szActiveHandles[100];
  591.  
  592.     wphQueryActiveHandles(hIniSystem, szActiveHandles, sizeof(szActiveHandles));
  593.     /* if (strcmp(szCurHandles, szActiveHandles))
  594.     {
  595.         // active handles changed? => adjust
  596.         if (pHandlesBuffer)
  597.            free(pHandlesBuffer);
  598.         pHandlesBuffer = NULL;
  599.         strcpy(szActiveHandles, szCurHandles);
  600.     } */
  601.  
  602.     // use lower byte only
  603.     usObjID = LOUSHORT(hObject);
  604.  
  605.     if (!IsObjectDisk(hObject))
  606.        return NULL;
  607.  
  608.     if (!pHandlesBuffer)
  609.        if (!wphReadAllBlocks(hIniSystem, szActiveHandles,
  610.                 &pHandlesBuffer, &cbHandlesBuffer))
  611.           return NULL;
  612.  
  613.     memset(pszFname, 0, usMax);
  614.  
  615.     free(pHandlesBuffer);
  616.  
  617.     return (wphFindPartName(pHandlesBuffer, cbHandlesBuffer, usObjID, pszFname, usMax));
  618. }
  619.  
  620.  
  621.