home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / sdktools / winnt / netwatch / net.c < prev    next >
C/C++ Source or Header  |  1995-06-13  |  31KB  |  1,272 lines

  1. /*
  2.  *  net.c
  3.  *  
  4.  *  Purpose:
  5.  *      net functions
  6.  *  
  7.  *  Owner:
  8.  *      MikeSart
  9.  */
  10. #define UNICODE 1
  11.  
  12. #include <windows.h>
  13. #include <windowsx.h>
  14. #include <commctrl.h>
  15. #include <string.h>
  16. #include <time.h>
  17. #include <lm.h>
  18. #include "netwatch.h"
  19. #include "rcids.h"
  20.  
  21. ASSERTDATA
  22.  
  23. // From lmcons.h:
  24. //  NNLEN: max share name
  25. //  UNLEN: max user name
  26. //  MAXCOMMENTSZ: max comment length
  27. //  PATHLEN: max path
  28.  
  29. // From datetime.c
  30. TCHAR    *PutTime(time_t tm_t, TCHAR *szStr, UINT cch);
  31. TCHAR    *PutDate(time_t tm_t, TCHAR *szStr, UINT cch);
  32. TCHAR    *PutCounterTime(DWORD dw, TCHAR *szStr, UINT cch);
  33.  
  34. /*
  35.  *    Useful macros
  36.  */
  37. // Should we consider szShare hidden?
  38. #define FIsShareHidden(_szShare) \
  39.     ((_szShare)[lstrlen(_szShare) - 1] == TEXT('$'))
  40. // Is the lParam in the TV_ITEM a pointer to TVDATA or just a type?
  41. #define FIsLParamTVData(_lp) \
  42.     (((_lp) + 1) > 1)
  43.  
  44. /*
  45.  *    Globules
  46.  */
  47. SCONST TCHAR    szFmtNum[]      = TEXT("%lu");
  48.  
  49. /*
  50.  *    Typedefs
  51.  */
  52. typedef struct tagTVDATA
  53. {
  54.     DWORD    dwType;
  55. } TVDATA;
  56.  
  57. typedef struct tagTVDATASHARE
  58. {
  59.     DWORD    dwType;                    // TYPE_SHARE, TYPE_USER, or TYPE_FILE
  60.     DWORD    dwShareType;            // STYPE_IPC, etc.
  61.     TCHAR    szShareName[NNLEN + 1];    // e$, etc.
  62.  
  63.     TCHAR    szServerName[UNCLEN + 1];
  64. } TVDATASHARE;
  65.  
  66. typedef struct tagTVDATAUSER
  67. {
  68.     DWORD    dwType;                    // TYPE_SHARE, TYPE_USER, or TYPE_FILE
  69.     TCHAR    szUserName[UNLEN + 1];    // BillG, etc.
  70.     TCHAR    szNetName[UNCLEN + 1];    // \\BILLG1, etc.
  71.  
  72.     TVDATASHARE    *pTVDataShare;        // Parent data
  73. } TVDATAUSER;
  74.  
  75. typedef struct tagTVDATAFILE
  76. {
  77.     DWORD    dwType;                    // TYPE_SHARE, TYPE_USER, or TYPE_FILE
  78.     DWORD    dwFileId;                // fi3_id
  79.  
  80.     TVDATAUSER    *pTVDataUser;        // Parent data
  81. } TVDATAFILE;
  82.  
  83. /*
  84.  *    Treeview nonsense
  85.  */
  86.  
  87. /*
  88.  *    TreeView_FindExactString
  89.  *
  90.  *    Purpose:
  91.  *        Find a szText starting from hItemStart
  92.  *
  93.  *    Arguments:
  94.  *        hParent, hItemStart, szText
  95.  *
  96.  *    Returns:
  97.  *        hItem or NULL
  98.  */
  99. HTREEITEM
  100. TreeView_FindExactString(HWND hwndTV, HTREEITEM hParent, HTREEITEM hItemStart,
  101.     TCHAR *szText)
  102. {
  103.     TCHAR    szT[cchTVSzMax];
  104.  
  105.     // Start looking one node after us
  106.     hItemStart = hItemStart ? TreeView_GetNextSibling(hwndTV, hItemStart) :
  107.         TreeView_GetChild(hwndTV, hParent);
  108.  
  109.     // Loop through looking for that baby
  110.     while(hItemStart)
  111.     {
  112.         // Get the text
  113.         if(FTreeView_GetString(hwndTV, hItemStart, szT, cchTVSzMax))
  114.         {
  115.             UINT nRet = lstrcmp(szText, szT);
  116.  
  117.             // Strings should be in the treeview control like:
  118.             //   aaaa$
  119.             //   bbbb$
  120.             //   zzzz$
  121.             //   bbbb
  122.             //   ffff
  123.  
  124.             // So, if the strings match, return em
  125.             // Else szText is larger than szT and we're past the hidden shares
  126.             // so the item shouldn't be here
  127.             if(!nRet)
  128.                 return hItemStart;
  129.             else if((nRet < 0) && !FIsShareHidden(szT))
  130.                 return NULL;
  131.         }
  132.  
  133.         hItemStart = TreeView_GetNextSibling(hwndTV, hItemStart);
  134.     }
  135.  
  136.     return hItemStart;
  137. }
  138.  
  139. /*
  140.  *    FTreeView_DeleteRange
  141.  *
  142.  *    Purpose: Delete hItemStart+1 to hItemEnd-1
  143.  *        hItemStart
  144.  *        hItemStart+1
  145.  *        ...
  146.  *        HItemEnd-1
  147.  *        HItemEnd
  148.  *
  149.  *    Arguments:
  150.  *        hwndTV, hItemStart, hItemEnd
  151.  *
  152.  *    Returns:
  153.  *        TRUE - success, FALSE - failure
  154.  */
  155. BOOL
  156. FTreeView_DeleteRange(HWND hwndTV, HTREEITEM hParent, HTREEITEM hItemStart,
  157.     HTREEITEM hItemEnd)
  158. {
  159.     // If NULL, we want the first item, else we want hItemStart + 1
  160.     hItemStart = hItemStart ? TreeView_GetNextSibling(hwndTV, hItemStart) :
  161.         TreeView_GetChild(hwndTV, hParent);
  162.  
  163.     while(hItemStart && (hItemStart != hItemEnd))
  164.     {
  165.         HTREEITEM    hItemT;
  166.  
  167.         hItemT = TreeView_GetNextSibling(hwndTV, hItemStart);
  168.         TreeView_DeleteItem(hwndTV, hItemStart);
  169.         hItemStart = hItemT;
  170.     }
  171.  
  172.     return TRUE;
  173. }
  174.  
  175. /*
  176.  *    TreeView_TVReplacelParam
  177.  *
  178.  *    Purpose:
  179.  *        Replace an lparam and free any associated memory
  180.  *
  181.  *    Arguments:
  182.  *        hItem to replace and lParamNew
  183.  *
  184.  *    Returns:
  185.  *        TRUE - success, FALSE - failed
  186.  */
  187. BOOL
  188. TreeView_TVReplacelParam(HWND hwndTV, HTREEITEM hItem, LPARAM lParamNew)
  189. {
  190.     TV_ITEM        tvItem;
  191.     LPARAM        lParamOld;
  192.  
  193.     tvItem.mask = TVIF_PARAM;
  194.     tvItem.hItem = hItem;
  195.     TreeView_GetItem(hwndTV, &tvItem);
  196.     lParamOld = tvItem.lParam;
  197.  
  198.     tvItem.lParam = lParamNew;
  199.     TreeView_SetItem(hwndTV, &tvItem);
  200.  
  201.     if(FIsLParamTVData(lParamOld))
  202.         GlobalFreePtr((TVDATA *)lParamOld);
  203.     return TRUE;
  204. }
  205.  
  206. /*
  207.  *    TreeView_TVDataInsert
  208.  *
  209.  *    Purpose:
  210.  *        Try to be smart when inserting TV items
  211.  *
  212.  *    Arguments:
  213.  *        ptvis to insert
  214.  *
  215.  *    Returns:
  216.  *        hItem of newly inserted item
  217.  */
  218. HTREEITEM
  219. TreeView_TVDataInsert(HWND hwndTV, TV_INSERTSTRUCT *ptvis)
  220. {
  221.     HTREEITEM    hItem;
  222.  
  223.     // See if this item exists anywhere already
  224.     hItem = TreeView_FindExactString(hwndTV, ptvis->hParent,
  225.         ptvis->hInsertAfter, ptvis->item.pszText);
  226.  
  227.     // It does: del everything from the last inserted node to right before us
  228.     if(hItem)
  229.     {
  230.         OutputDbgStr("TreeView_TVDataInsert found '%ls'", ptvis->item.pszText);
  231.         FTreeView_DeleteRange(hwndTV, ptvis->hParent, ptvis->hInsertAfter,
  232.             hItem);
  233.         TreeView_TVReplacelParam(hwndTV, hItem, ptvis->item.lParam);
  234.         ptvis->hInsertAfter = hItem;
  235.         return hItem;
  236.     }
  237.  
  238.     // ok, it's a new string, insert it
  239.     // If this is the first one, make sure it's the first one
  240.     OutputDbgStr("TreeView_TVDataInsert inserting '%ls'", ptvis->item.pszText);
  241.     if(!ptvis->hInsertAfter)
  242.         ptvis->hInsertAfter = TVI_FIRST;
  243.     hItem = TreeView_InsertItem(hwndTV, ptvis);
  244.     // Store away the last item we've inserted
  245.     ptvis->hInsertAfter = hItem;
  246.  
  247.     return hItem;
  248. }
  249.  
  250. /*
  251.  *    TreeView_FreeItemData
  252.  *
  253.  *    Purpose:
  254.  *        Free our alloc'd lParam (called via DELETEITEM)
  255.  *
  256.  *    Arguments:
  257.  *        ptvItem of item being deleted
  258.  *
  259.  *    Returns:
  260.  *        nada
  261.  */
  262. VOID
  263. TreeView_FreeItemData(TV_ITEM *ptvItem)
  264. {
  265.     if(FIsLParamTVData(ptvItem->lParam))
  266.     {
  267.         OutputDbgStr("TreeView_FreeItemData '%d'",
  268.             ((TVDATA *)ptvItem->lParam)->dwType);
  269.         GlobalFreePtr((TVDATA *)ptvItem->lParam);
  270.         ptvItem->lParam = 0;
  271.     }
  272. }
  273.  
  274. /*
  275.  *    TreeView_GetSelectedItemData
  276.  *
  277.  *    Purpose:
  278.  *        Get type and lParam of selected item
  279.  *
  280.  *    Arguments:
  281.  *        pdwType
  282.  *
  283.  *    Returns:
  284.  *        lParam of selected item
  285.  */
  286. LPARAM
  287. TreeView_GetSelectedItemData(HWND hwndTV, DWORD *pdwType)
  288. {
  289.     HTREEITEM    hItem;
  290.     TV_ITEM        tvItem;
  291.  
  292.     *pdwType = TYPE_ERROR;
  293.     if(!(hItem = TreeView_GetSelection(hwndTV)))
  294.         return 0;
  295.  
  296.     tvItem.mask = TVIF_PARAM;
  297.     tvItem.hItem = hItem;
  298.     tvItem.lParam = TYPE_ERROR;
  299.     TreeView_GetItem(hwndTV, &tvItem);
  300.  
  301.     *pdwType = FIsLParamTVData(tvItem.lParam) ?
  302.         ((TVDATA *)tvItem.lParam)->dwType : tvItem.lParam;
  303.     return tvItem.lParam;
  304. }
  305.  
  306. /*
  307.  *    Sorting routines
  308.  */
  309.  
  310. /*
  311.  *    CompareShareNames
  312.  *
  313.  *    Purpose:
  314.  *        compare two share names being 'smart' about hidden shares
  315.  *
  316.  *    Arguments:
  317.  *        share1, share2
  318.  *
  319.  *    Returns:
  320.  *        -1, 0, 1
  321.  */
  322. int
  323. CompareShareNames(TCHAR *szShareName1, TCHAR *szShareName2)
  324. {
  325.     BOOL    fShare1Hidden;
  326.     BOOL    fShare2Hidden;
  327.  
  328.     fShare1Hidden = FIsShareHidden(szShareName1);
  329.     fShare2Hidden = FIsShareHidden(szShareName2);
  330.  
  331.     // If they're the same type, just string compare them
  332.     if(fShare1Hidden == fShare2Hidden)
  333.         return lstrcmpi(szShareName1, szShareName2);
  334.  
  335.     // Otherwise we want hidden shares to go first
  336.     return fShare1Hidden ? -1 : 1;
  337. }
  338.  
  339. /*
  340.  *    QSortCallbacks
  341.  *
  342.  *    Purpose:
  343.  *        called from qsort to figure what goes where
  344.  *
  345.  *    Arguments:
  346.  *        elements to compare
  347.  *
  348.  *    Returns:
  349.  *        < 0: elem1 less than elem2
  350.  *        = 0: elem1 equivalent to elem2
  351.  *        > 0: elem1 greater than elem2
  352.  */
  353. int __cdecl
  354. CompareShareNamesQSortCallback(const void *elem1, const void *elem2)
  355. {
  356.     return CompareShareNames(((SHARE_INFO_2 *)elem1)->shi2_netname,
  357.         ((SHARE_INFO_2 *)elem2)->shi2_netname);
  358. }
  359.  
  360. int __cdecl
  361. CompareUserNamesQSortCallback(const void *elem1, const void *elem2)
  362. {
  363.     return lstrcmp(((CONNECTION_INFO_1 *)elem1)->coni1_username,
  364.         ((CONNECTION_INFO_1 *)elem2)->coni1_username);
  365. }
  366.  
  367. int __cdecl
  368. CompareFileNamesQSortCallback(const void *elem1, const void *elem2)
  369. {
  370.     return lstrcmp(((FILE_INFO_3 *)elem1)->fi3_pathname,
  371.         ((FILE_INFO_3 *)elem2)->fi3_pathname);
  372. }
  373.  
  374. /*
  375.  *    Enumeration routines
  376.  */
  377.  
  378. /*
  379.  *    FilesEnum
  380.  *
  381.  *    Purpose:
  382.  *        Enumerate the open files on a sharepath, username, and server
  383.  *
  384.  *    Arguments:
  385.  *        all kinds of stuff
  386.  *
  387.  *    Returns:
  388.  *        nas
  389.  */
  390. NET_API_STATUS
  391. FilesEnum(HWND hwndTV, HTREEITEM hParent, LPTSTR szServerName, TCHAR *username,
  392.     TCHAR *sharepath, TVDATAUSER *pTVDataUser)
  393. {
  394.     NET_API_STATUS    nas;
  395.     TV_INSERTSTRUCT    tvis;
  396.     FILE_INFO_3        *pfi3;
  397.     FILE_INFO_3        *fi3 = NULL;
  398.     DWORD            dwentriesread;
  399.     DWORD            dwtotalentries;
  400.     TCHAR            *psharepathend;
  401.  
  402.     // Some stuff doesn't change
  403.     tvis.hInsertAfter = NULL;
  404.     tvis.hParent = hParent;
  405.     tvis.item.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM;
  406.  
  407.     // Reasoning: if you are passed c:\ as a sharepath, sometimes c: is opened
  408.     // as execute. But since we would be qualifying paths on c:\, it won't show
  409.     // c: as being open.  So - remove the last \ if it's there.
  410.     if(sharepath && sharepath[0])
  411.     {
  412.         psharepathend = sharepath + lstrlen(sharepath) - 1;
  413.         if(*psharepathend == TEXT('\\'))
  414.             *psharepathend = '\0';
  415.     }
  416.  
  417.     nas = NetFileEnum(szServerName, sharepath, username, 3,
  418.         (LPBYTE *)&fi3, MAX_PREFERRED_LENGTH, 
  419.         &dwentriesread, &dwtotalentries, NULL);
  420.     if(nas || !dwentriesread)
  421.         goto err;
  422.  
  423.     qsort(fi3, dwentriesread, sizeof(fi3[0]), CompareFileNamesQSortCallback);
  424.     for(pfi3 = fi3; dwentriesread; pfi3++, dwentriesread--)
  425.     {
  426.         TVDATAFILE    *pTVDataFile;
  427.         UINT        nBitmap = BMP_READ;
  428.  
  429.         // And our bmp is...
  430.         if((pfi3->fi3_permissions & PERM_FILE_WRITE) ||
  431.             (pfi3->fi3_permissions & PERM_FILE_CREATE))
  432.                 nBitmap = BMP_WRITE;
  433.  
  434.         // Things to remember for properties, delete, etc.
  435.         if(pTVDataFile = (TVDATAFILE *)GlobalAllocPtr(GHND, sizeof(TVDATAFILE)))
  436.         {
  437.             pTVDataFile->dwType = TYPE_FILE;
  438.             pTVDataFile->dwFileId = pfi3->fi3_id;
  439.             pTVDataFile->pTVDataUser = pTVDataUser;
  440.         }
  441.  
  442.         // Insert that baby
  443.         tvis.item.lParam = (LPARAM)pTVDataFile;
  444.         tvis.item.pszText = pfi3->fi3_pathname;
  445.         tvis.item.iImage = tvis.item.iSelectedImage = nBitmap;
  446.         TreeView_TVDataInsert(hwndTV, &tvis);
  447.     }
  448.  
  449. err:
  450.     // Whack any stragglers hanging at the end
  451.     FTreeView_DeleteRange(hwndTV, hParent, tvis.hInsertAfter, NULL);
  452.  
  453.     NetApiBufferFree(fi3);
  454.     if(nas)
  455.         AddErrorStringToTV(hwndTV, hParent, IDS_ERRENUMFILES, nas);
  456.     return nas;
  457. }
  458.  
  459. /*
  460.  *    UsersEnum
  461.  *
  462.  *    Purpose:
  463.  *        Enumerate the users on a sharepath and server
  464.  *
  465.  *    Arguments:
  466.  *        lots o garbage
  467.  *
  468.  *    Returns:
  469.  *        nas
  470.  */
  471. NET_API_STATUS
  472. UsersEnum(HWND hwndTV, HTREEITEM hParent, LPTSTR szServerName, TCHAR *sharename,
  473.     TCHAR *sharepath, TVDATASHARE *pTVDataShare)
  474. {
  475.     NET_API_STATUS        nas;
  476.     TV_INSERTSTRUCT        tvis;
  477.     HTREEITEM            hItem;
  478.     CONNECTION_INFO_1    *pci1;
  479.     CONNECTION_INFO_1    *ci1 = NULL;
  480.     DWORD                dwentriesread;
  481.     DWORD                dwtotalentries;
  482.     static TCHAR        szFmtUser[cchSzMax + 1];
  483.     TCHAR                szUserT[NNLEN + UNLEN + cchSzMax + 1];
  484.  
  485.     // Some stuff doesn't change
  486.     tvis.hInsertAfter = NULL;
  487.     tvis.hParent = hParent;
  488.     tvis.item.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM;
  489.  
  490.     if(!szFmtUser[0])
  491.         lstrcpy(szFmtUser, szFromIDS1(IDS_FMTUSER));
  492.  
  493.     nas = NetConnectionEnum(szServerName, sharename, 1, (LPBYTE *)&ci1,
  494.         MAX_PREFERRED_LENGTH, &dwentriesread, &dwtotalentries, NULL);
  495.     if(nas || !dwentriesread)
  496.         goto err;
  497.  
  498.     qsort(ci1, dwentriesread, sizeof(ci1[0]), CompareUserNamesQSortCallback);
  499.     for(pci1 = ci1; dwentriesread; pci1++, dwentriesread--)
  500.     {
  501.         TCHAR        *netname;
  502.         TCHAR        *username;
  503.         TVDATAUSER    *pTVDataUser;
  504.  
  505.         username = pci1->coni1_username;
  506.         netname = pci1->coni1_netname;
  507.         if(!username || !username[0])
  508.             username = szNil;
  509.         else
  510.             CharLower(username);
  511.         if(!netname || !netname[0])
  512.             netname = szNil;
  513.         else
  514.             CharUpper(netname);
  515.  
  516.         if(lstrlen(netname) > CNLEN)
  517.             netname[CNLEN] = 0;
  518.         if(lstrlen(username) > UNLEN)
  519.             username[UNLEN] = 0;
  520.  
  521.         wsprintf(szUserT, szFmtUser, username, netname);
  522.  
  523.         // Things to remember for properties, delete, etc.
  524.         if(pTVDataUser = (TVDATAUSER *)GlobalAllocPtr(GHND, sizeof(TVDATAUSER)))
  525.         {
  526.             pTVDataUser->dwType = TYPE_USER;
  527.             lstrcpy(pTVDataUser->szUserName, username);
  528.             pTVDataUser->szNetName[0] = pTVDataUser->szNetName[1] = '\\';
  529.             lstrcpy(&pTVDataUser->szNetName[2], netname);
  530.             pTVDataUser->pTVDataShare = pTVDataShare;
  531.         }
  532.  
  533.         tvis.item.lParam = (LPARAM)pTVDataUser;
  534.         tvis.item.pszText = szUserT;
  535.         tvis.item.iImage = tvis.item.iSelectedImage = BMP_USER;
  536.         hItem = TreeView_TVDataInsert(hwndTV, &tvis);
  537.  
  538.         // If we haven't run into any errors and the user wants to see files
  539.         if(pci1->coni1_num_opens &&
  540.             unMenuFlags[IDM_SHOWFILES & 0xff] == MF_CHECKED && !nas)
  541.         {
  542.             // Enum the files
  543.             nas = FilesEnum(hwndTV, hItem, szServerName, username,
  544.                 sharepath, pTVDataUser);
  545.             // make sure we're expanded
  546.             TreeView_Expand(hwndTV, hItem, TVE_EXPAND);
  547.         }
  548.         else
  549.         {
  550.             // Delete all our children
  551.             FTreeView_DeleteRange(hwndTV, hItem, NULL, NULL);
  552.         }
  553.     }
  554.  
  555.     // reset any filesenum errors we may have encountered
  556.     nas = 0;
  557.  
  558. err:
  559.     // Whack any stragglers hanging at the end
  560.     FTreeView_DeleteRange(hwndTV, hParent, tvis.hInsertAfter, NULL);
  561.  
  562.     NetApiBufferFree(ci1);
  563.     if(nas)
  564.         AddErrorStringToTV(hwndTV, hParent, IDS_ERRENUMUSERS, nas);
  565.     return nas;
  566. }
  567.  
  568. /*
  569.  *    SharesEnum
  570.  *
  571.  *    Purpose:
  572.  *        Enumerate the shares on a server
  573.  *
  574.  *    Arguments:
  575.  *        server to enum
  576.  *
  577.  *    Returns:
  578.  *        nas
  579.  */
  580. #define offsetof(s, m)  (size_t)&(((s *)0)->m)
  581. #define shoffset(l, m)  (offsetof(SHARE_INFO_##l, m))
  582. NET_API_STATUS
  583. SharesEnum(HWND hwndTV, HTREEITEM hParent, LPTSTR szServerName, UINT *pcUsers,
  584.     INT nLevel)
  585. {
  586.     NET_API_STATUS    nas;
  587.     TV_INSERTSTRUCT    tvis;
  588.     HTREEITEM        hItem;
  589.     SHARE_INFO_2    *pshi2;
  590.     UINT            cbStruct;
  591.     SHARE_INFO_2    *shi2 = NULL;
  592.     DWORD            dwentriesread;
  593.     DWORD            dwtotalentries;
  594.     SCONST TCHAR    szEndSep[] = TEXT(")");
  595.     SCONST TCHAR    szBegSep[] = TEXT(" (");
  596.     // +3 for the begsep and endsep
  597.     TCHAR            szShareT[NNLEN + MAXCOMMENTSZ + 3 + 1];
  598.  
  599.     Assert(nLevel == 1 || nLevel == 2);
  600.     // We assume the first three parts of these structs are the same. Make sure.
  601.     // Using cbStruct here cause the compiler loses the entire line if I don't?
  602.     Assert((cbStruct = shoffset(1, shi1_netname)) == shoffset(2, shi2_netname));
  603.     Assert((cbStruct = shoffset(1, shi1_type)) == shoffset(2, shi2_type));
  604.     Assert((cbStruct = shoffset(1, shi1_remark)) == shoffset(2, shi2_remark));
  605.  
  606.     cbStruct = nLevel == 2 ? sizeof(SHARE_INFO_2) : sizeof(SHARE_INFO_1);
  607.     // WARNING: don't use the following members of shi2 without checking nLevel!
  608.     //    shi2_permissions;
  609.     //    shi2_max_uses;
  610.     //    shi2_current_uses
  611.     //    shi2_path;
  612.     //    shi2_passwd;
  613.  
  614.     // Some stuff doesn't change
  615.     tvis.hInsertAfter = NULL;
  616.     tvis.hParent = hParent;
  617.     tvis.item.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM;
  618.  
  619.     nas = NetShareEnum(szServerName, nLevel, (LPBYTE *)&shi2,
  620.         MAX_PREFERRED_LENGTH, &dwentriesread, &dwtotalentries, NULL);
  621.     if(nas || !dwentriesread)
  622.         goto err;
  623.  
  624.     // Sort the shares and then cruise through adding em all to the TV
  625.     qsort(shi2, dwentriesread, cbStruct, CompareShareNamesQSortCallback);
  626.     for(pshi2 = shi2; dwentriesread; ((LPBYTE)pshi2) += cbStruct, dwentriesread--)
  627.     {
  628.         TCHAR            *sharename;
  629.         TCHAR            *shareremark;
  630.         UINT            nCurrentUses;
  631.         TVDATASHARE        *pTVDataShare;
  632.  
  633.         sharename = pshi2->shi2_netname;
  634.         shareremark = pshi2->shi2_remark;
  635.         nCurrentUses = (nLevel == 2) ? pshi2->shi2_current_uses : 0;
  636.  
  637.         if(!sharename)
  638.             continue;
  639.         if(!shareremark)
  640.             shareremark = szNil;
  641.  
  642.         if(lstrlen(sharename) > NNLEN)
  643.             sharename[NNLEN] = 0;
  644.         if(lstrlen(shareremark) > MAXCOMMENTSZ)
  645.             shareremark[MAXCOMMENTSZ] = 0;
  646.  
  647.         // skip hidden shares
  648.         if((unMenuFlags[IDM_SHOWHIDDEN & 0xff] == MF_UNCHECKED) &&
  649.             FIsShareHidden(sharename))
  650.                 continue;
  651.  
  652.         // Keep a running total
  653.         *pcUsers += nCurrentUses;
  654.  
  655.         // If we have any users connected or the onlyshowinuse isn't checked
  656.         if(nCurrentUses || (unMenuFlags[IDM_SHOWINUSE & 0xff] != MF_CHECKED))
  657.         {
  658.             int nBitmap = ((pshi2->shi2_type & 0xf) == STYPE_IPC) ?
  659.                 BMP_IPC : BMP_SHARE;
  660.  
  661.             // Copy over the share name and comment
  662.             lstrcpy(szShareT, sharename);
  663.             if(shareremark[0])
  664.             {
  665.                 lstrcat(szShareT, szBegSep);
  666.                 lstrcat(szShareT, shareremark);
  667.                 lstrcat(szShareT, szEndSep);
  668.             }
  669.  
  670.             // Things to remember for properties, delete, etc.
  671.             if(pTVDataShare = (TVDATASHARE *)GlobalAllocPtr(GHND,
  672.                 sizeof(TVDATASHARE)))
  673.             {
  674.                 pTVDataShare->dwType = TYPE_SHARE;
  675.                 pTVDataShare->dwShareType = pshi2->shi2_type;
  676.                 lstrcpy(pTVDataShare->szShareName, sharename);
  677.                 lstrcpy(pTVDataShare->szServerName, szServerName);
  678.             }
  679.  
  680.             tvis.item.lParam = (LPARAM)pTVDataShare;
  681.             tvis.item.pszText = szShareT;
  682.             tvis.item.iImage = tvis.item.iSelectedImage = nBitmap;
  683.             hItem = TreeView_TVDataInsert(hwndTV, &tvis);
  684.  
  685.             // If we previously got an error enuming users, don't waste our time
  686.             // trying again.
  687.             if(!nas && nCurrentUses)
  688.             {
  689.                 Assert(nLevel == 2);
  690.                 nas = UsersEnum(hwndTV, hItem, szServerName,
  691.                     sharename, pshi2->shi2_path, pTVDataShare);
  692.                 // Make sure we're showing it all
  693.                 TreeView_Expand(hwndTV, hItem, TVE_EXPAND);
  694.             }
  695.             else
  696.             {
  697.                 // Delete all our children
  698.                 FTreeView_DeleteRange(hwndTV, hItem, NULL, NULL);
  699.             }
  700.         }
  701.     }
  702.  
  703.     // reset any errors UsersEnum may have trickled up
  704.     nas = 0;
  705.  
  706. err:
  707.     // Whack any stragglers hanging at the end
  708.     FTreeView_DeleteRange(hwndTV, hParent, tvis.hInsertAfter, NULL);
  709.  
  710.     if(nas)
  711.         AddErrorStringToTV(hwndTV, hParent, IDS_ERRENUMSHARES, nas);
  712.     NetApiBufferFree(shi2);
  713.     return nas;
  714. }
  715.  
  716. /*
  717.  *    RefreshDisplay
  718.  *
  719.  *    Purpose:
  720.  *        Update the treeview with piping hot info
  721.  *
  722.  *    Arguments:
  723.  *        main hwnd
  724.  *
  725.  *    Returns:
  726.  *        Count of uses connected to shares
  727.  */
  728. UINT
  729. RefreshDisplay(HWND hwnd, HWND hwndTV)
  730. {
  731.     HTREEITEM        hItem;
  732.     HCURSOR            hCursor;
  733.     UINT            cUsers = 0;
  734.     static UINT        cRefreshes = 0;
  735.  
  736.     // Turn off the timer
  737.     PunchTimer(hwnd, FALSE);
  738.     OutputDbgStr("RefreshDisplay (%d): 0x%08lx", cRefreshes, GetTickCount());
  739.  
  740.     // This may take a while
  741.     hCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
  742.  
  743.     // Enumerate all the shares
  744.     hItem = TreeView_GetRoot(hwndTV);
  745.     while(hItem)
  746.     {
  747.         TV_ITEM        tvItem;
  748.         HTREEITEM    hItemChild;
  749.         TCHAR        szT[UNCLEN + 1];
  750.  
  751.         // Check to see if the first child is an error
  752.         if((cRefreshes % 4) && (hItemChild = TreeView_GetChild(hwndTV, hItem)))
  753.         {
  754.             // If it is, only update it 1 out of 4 times
  755.             tvItem.mask = TVIF_PARAM;
  756.             tvItem.hItem = hItemChild;
  757.             if(TreeView_GetItem(hwndTV, &tvItem) && tvItem.lParam == TYPE_ERROR)
  758.             {
  759.                 OutputDbgStr("Skipping errored out server");
  760.                 goto nextserver;
  761.             }
  762.         }
  763.  
  764.         // Get the server name and server image
  765.         tvItem.mask = TVIF_TEXT | TVIF_IMAGE;
  766.         tvItem.hItem = hItem;
  767.         tvItem.pszText = szT;
  768.         tvItem.cchTextMax = UNCLEN + 1;
  769.         if(TreeView_GetItem(hwndTV, &tvItem) && (szT[0] == '\\'))
  770.         {
  771.             // Enumerate all the shares under here
  772.             SharesEnum(hwndTV, hItem, szT, &cUsers,
  773.                 tvItem.iImage == BMP_COMPUTER ? 2 : 1);
  774.             // Make sure we're showing it all
  775.             TreeView_Expand(hwndTV, hItem, TVE_EXPAND);
  776.         }
  777.  
  778.         // Next server
  779. nextserver:
  780.         hItem = TreeView_GetNextSibling(hwndTV, hItem);
  781.     }
  782.  
  783.     // Bring back our arrow
  784.     SetCursor(hCursor);
  785.  
  786.     // Swing the selection back into view
  787.     if(hItem = TreeView_GetSelection(hwndTV))
  788.         TreeView_EnsureVisible(hwndTV, hItem);
  789.  
  790.     // Resume the timer again
  791.     PunchTimer(hwnd, TRUE);
  792.  
  793.     // Update our number of refreshes and return the count of users
  794.     cRefreshes++;
  795.     return cUsers;
  796. }
  797.  
  798. /*
  799.  *    Properties
  800.  */
  801.  
  802. /*
  803.  *    DisplayComputerDetails
  804.  *
  805.  *    Purpose:
  806.  *        Delete/Show property page for computer
  807.  *
  808.  *    Arguments:
  809.  *        hwnd, wAction
  810.  *
  811.  *    Returns:
  812.  *        nas
  813.  */
  814. NET_API_STATUS
  815. DisplayComputerDetails(HWND hwnd, TVDATA *pTVData, WORD wAction)
  816. {
  817.     HTREEITEM        hItem;
  818.     HWND            hwndTV;
  819.     NET_API_STATUS    nas = 0;
  820.     SERVER_INFO_102    *si102 = NULL;
  821.  
  822.     if(!(hwndTV = GetDlgItem(hwnd, IDD_tvwSHARES)))
  823.         return 0;
  824.  
  825.     if(!(hItem = TreeView_GetSelection(hwndTV)))
  826.         return 0;
  827.  
  828.     if(wAction == VK_DELETE)
  829.     {
  830.         TreeView_DeleteItem(hwndTV, hItem);
  831.     }
  832.     else
  833.     {
  834.         TV_ITEM    tvItem;
  835.         HCURSOR    hCursor;
  836.         TCHAR    szT[UNCLEN + 1];
  837.         TCHAR    szVer[cchSzMax + 1];
  838.  
  839.         szT[0] = 0;
  840.         tvItem.mask = TVIF_TEXT;
  841.         tvItem.hItem = hItem;
  842.         tvItem.pszText = szT;
  843.         tvItem.cchTextMax = UNCLEN + 1;
  844.         TreeView_GetItem(hwndTV, &tvItem);
  845.  
  846.         hCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
  847.         nas = NetServerGetInfo(szT, 102, (LPBYTE *)&si102);
  848.         SetCursor(hCursor);
  849.         if(nas || !si102)
  850.             goto err;
  851.  
  852.         wsprintf(szVer, TEXT("%d.%d"),
  853.             si102->sv102_version_major & MAJOR_VERSION_MASK,
  854.             si102->sv102_version_minor);
  855.  
  856.         PropertyDlg(hwnd,
  857.             IDS_SERVERPROPS,
  858.             0xfffffff1,
  859.             si102->sv102_name,
  860.             si102->sv102_comment,
  861.             si102->sv102_userpath,
  862.             szVer,
  863.             szFromIDS1(IDS_HIDDEN + !!si102->sv102_hidden),
  864.             NULL);
  865.     }
  866.  
  867. err:
  868.     NetApiBufferFree(si102);
  869.     return nas;
  870. }
  871.  
  872. /*
  873.  *    DisplayShareDetails
  874.  *
  875.  *    Purpose:
  876.  *        Delete/Show property page for shares
  877.  *
  878.  *    Arguments:
  879.  *        hwnd, pTVDataShare, wAction
  880.  *
  881.  *    Returns:
  882.  *        nas
  883.  */
  884. NET_API_STATUS
  885. DisplayShareDetails(HWND hwnd, TVDATA *pTVData, WORD wAction)
  886. {
  887.     NET_API_STATUS    nas;
  888.     HCURSOR            hCursor;
  889.     SHARE_INFO_2    *shi2 = NULL;
  890.     TVDATASHARE        *pTVDataShare = (TVDATASHARE *)pTVData;
  891.  
  892.     hCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
  893.     nas = NetShareGetInfo(pTVDataShare->szServerName, pTVDataShare->szShareName,
  894.         2, (LPBYTE *)&shi2);
  895.     SetCursor(hCursor);
  896.     if(!nas && shi2)
  897.     {
  898.         if(wAction == VK_DELETE)
  899.         {
  900.             // if no one is connected, nuke it, else confirm with user
  901.             if(!(shi2->shi2_current_uses) ||
  902.                 (MessageBox(hwnd, szFromIDS1(IDS_AREYOUSURE + TYPE_SHARE), 
  903.                     szAppName, MB_ICONEXCLAMATION | MB_YESNO | MB_DEFBUTTON2) == 
  904.                     IDYES))
  905.             {
  906.                 nas = NetShareDel(pTVDataShare->szServerName,
  907.                     pTVDataShare->szShareName, 0);
  908.             }
  909.         }
  910.         else
  911.         {
  912.             TCHAR    szMaxUses[11];
  913.             TCHAR    szMaxCurrent[11];
  914.  
  915.             wsprintf(szMaxUses, szFmtNum, shi2->shi2_max_uses);
  916.             wsprintf(szMaxCurrent, szFmtNum, shi2->shi2_current_uses);
  917.  
  918.             PropertyDlg(hwnd,
  919.                 IDS_SHAREPROPS,
  920.                 ((shi2->shi2_type & 0xf) == STYPE_IPC) ? 0xfffffff8 : 0xfffffff0,
  921.                 shi2->shi2_netname,
  922.                 shi2->shi2_path,
  923.                 shi2->shi2_remark,
  924.                 (shi2->shi2_max_uses == SHI_USES_UNLIMITED) ? 
  925.                     szFromIDS1(IDS_NOLIMIT) : szMaxUses,
  926.                 szMaxCurrent,
  927.                 NULL);
  928.         }
  929.     }
  930.  
  931.     NetApiBufferFree(shi2);
  932.     return nas;
  933. }
  934.  
  935. /*
  936.  *    DisplayFileDetails
  937.  *
  938.  *    Purpose:
  939.  *        Delete/Show property page for files
  940.  *
  941.  *    Arguments:
  942.  *        hwnd, pTVDataFile, wAction
  943.  *
  944.  *    Returns:
  945.  *        nas
  946.  */
  947. NET_API_STATUS
  948. DisplayFileDetails(HWND hwnd, TVDATA *pTVData, WORD wAction)
  949. {
  950.     NET_API_STATUS    nas;
  951.     HCURSOR            hCursor;
  952.     FILE_INFO_3        *fi3 = NULL;
  953.     TCHAR            *szServerName;  
  954.     TVDATAFILE        *pTVDataFile = (TVDATAFILE *)pTVData;
  955.  
  956.     hCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
  957.     szServerName = pTVDataFile->pTVDataUser->pTVDataShare->szServerName;
  958.     nas = NetFileGetInfo(szServerName, pTVDataFile->dwFileId, 3, (LPBYTE *)&fi3);
  959.     SetCursor(hCursor);
  960.     if(!nas && fi3)
  961.     {
  962.         if(wAction == VK_DELETE)
  963.         {
  964.             TCHAR    szMsg[cchMsgMax];
  965.  
  966.             wsprintf(szMsg, szFromIDS1(IDS_AREYOUSURE + TYPE_FILE),
  967.                 pTVDataFile->pTVDataUser->szUserName, fi3->fi3_pathname);
  968.             if(MessageBox(hwnd, szMsg, szAppName,
  969.                 MB_ICONEXCLAMATION | MB_YESNO | MB_DEFBUTTON2) == IDYES)
  970.             {
  971.                 nas = NetFileClose(szServerName, pTVDataFile->dwFileId);
  972.             }
  973.         }
  974.         else
  975.         {
  976.             TCHAR    szNumLocks[11];
  977.             UINT    nBitmap = BMP_READ;
  978.  
  979.             if((fi3->fi3_permissions & PERM_FILE_WRITE) ||
  980.                 (fi3->fi3_permissions & PERM_FILE_CREATE))
  981.                     nBitmap = BMP_WRITE;
  982.  
  983.             wsprintf(szNumLocks, szFmtNum, fi3->fi3_num_locks);
  984.  
  985.             PropertyDlg(hwnd,
  986.                 IDS_FILEPROPS,
  987.                 0xfffff072 | (nBitmap << 8),
  988.                 fi3->fi3_pathname,
  989.                 fi3->fi3_username,
  990.                 szFromIDS1(IDS_FILEPROPS + 10 + nBitmap),
  991.                 szNumLocks,
  992.                 NULL,
  993.                 NULL);
  994.         }
  995.     }
  996.  
  997.     NetApiBufferFree(fi3);
  998.     return nas;
  999. }
  1000.  
  1001. /*
  1002.  *    DisplayUserDetails
  1003.  *
  1004.  *    Purpose:
  1005.  *        Delete/Show property page for users
  1006.  *
  1007.  *    Arguments:
  1008.  *        hwnd, pTVDataUser, wAction
  1009.  *
  1010.  *    Returns:
  1011.  *        nas
  1012.  */
  1013. NET_API_STATUS
  1014. DisplayUserDetails(HWND hwnd, TVDATA *pTVData, WORD wAction)
  1015. {
  1016.     NET_API_STATUS    nas;
  1017.     time_t            tm_t;
  1018.     TCHAR            *szServerName;
  1019.     SESSION_INFO_2    *sesi2 = NULL;
  1020.     TCHAR            szUserName[UNLEN + cchSzMax + 1];
  1021.     TVDATAUSER        *pTVDataUser = (TVDATAUSER *)pTVData;
  1022.  
  1023.     szServerName = pTVDataUser->pTVDataShare->szServerName;
  1024.     nas = NetSessionGetInfo(szServerName, pTVDataUser->szNetName,
  1025.         pTVDataUser->szUserName, 2, (LPBYTE *)&sesi2);
  1026.     if(!nas && sesi2)
  1027.     {
  1028.         if(wAction == VK_DELETE)
  1029.         {
  1030.             UINT    ids;
  1031.             TCHAR    szMsg[cchMsgMax];
  1032.  
  1033.             // if user has open files, tell the del happy person this
  1034.             // otherwise just reaffirm that they wanna hack em.
  1035.             ids = sesi2->sesi2_num_opens ?
  1036.                 IDS_AREYOUSURE + TYPE_USER : IDS_AREYOUSUREDISUSER;
  1037.             wsprintf(szMsg, szFromIDS1(ids), pTVDataUser->szUserName);
  1038.             if(MessageBox(hwnd, szMsg, szAppName,
  1039.                 MB_ICONEXCLAMATION | MB_YESNO | MB_DEFBUTTON2) == IDYES)
  1040.             {
  1041.                 nas = NetSessionDel(szServerName, pTVDataUser->szNetName,
  1042.                     pTVDataUser->szUserName);
  1043.             }
  1044.         }
  1045.         else
  1046.         {
  1047.             TCHAR    szNumOpens[11];
  1048.             TCHAR    szIdleTime[cchSzMax];
  1049.             TCHAR    szConnectTime[cchSzMax];
  1050.             SCONST TCHAR szSpace[] = TEXT(" ");
  1051.  
  1052.             lstrcpy(szUserName, pTVDataUser->szUserName);
  1053.             if(sesi2->sesi2_user_flags & SESS_GUEST)
  1054.                 lstrcat(szUserName, szFromIDS1(IDS_GUEST));
  1055.  
  1056.             time(&tm_t);
  1057.             PutTime(tm_t - sesi2->sesi2_time, szIdleTime, cchSzMax);
  1058.             PutDate(tm_t - sesi2->sesi2_time, szConnectTime, cchSzMax);
  1059.             lstrcat(szConnectTime, szSpace);
  1060.             lstrcat(szConnectTime, szIdleTime);
  1061.  
  1062.             wsprintf(szNumOpens, szFmtNum, sesi2->sesi2_num_opens);
  1063.  
  1064.             PropertyDlg(hwnd,
  1065.                 IDS_USERPROPS,
  1066.                 pTVDataUser->pTVDataShare->dwShareType == STYPE_IPC ?
  1067.                     0xfffff817 : 0xfffff017,
  1068.                 szUserName,
  1069.                 pTVDataUser->szNetName, 
  1070.                 pTVDataUser->pTVDataShare->szShareName,
  1071.                 szConnectTime,
  1072.                 PutCounterTime(sesi2->sesi2_idle_time, szIdleTime, cchSzMax),
  1073.                 szNumOpens);
  1074.         }
  1075.     }
  1076.  
  1077.     if(!nas && !sesi2)
  1078.         nas = ERROR_NOT_CONNECTED;
  1079.     NetApiBufferFree(sesi2);
  1080.     return nas;
  1081. }
  1082.  
  1083. /*
  1084.  *    Nifty little array of property functions
  1085.  */
  1086. typedef NET_API_STATUS (*PFNDISPLAYDETAILS)(HWND hwnd, TVDATA *pTVData, WORD wAction);
  1087. SCONST PFNDISPLAYDETAILS pfnDisplayDetails[TYPE_MAX] =
  1088. {
  1089.     DisplayComputerDetails,
  1090.     DisplayShareDetails,
  1091.     DisplayUserDetails,
  1092.     DisplayFileDetails
  1093. };
  1094.  
  1095. /*
  1096.  *    HandleWM_VKEY
  1097.  *
  1098.  *    Purpose:
  1099.  *        handle WM_VKEYS: VK_DELETE and VK_RETURN
  1100.  *
  1101.  *    Arguments:
  1102.  *        main hwnd and wAction
  1103.  *
  1104.  *    Returns:
  1105.  *        TRUE - success, FALSE - error
  1106.  */
  1107. BOOL
  1108. HandleWM_VKEY(HWND hwnd, HWND hwndTV, WORD wAction)
  1109. {
  1110.     NET_API_STATUS    nas = 0;
  1111.     TVDATA            *pTVData;
  1112.     DWORD            dwType = TYPE_COMPUTER;
  1113.  
  1114.     OutputDbgStr("HandleWM_VKEY");
  1115.     // Only handle return and delete keys
  1116.     if((wAction != VK_RETURN) && (wAction != VK_DELETE))
  1117.         return FALSE;
  1118.  
  1119.     // Get the Selected item data
  1120.     pTVData = (TVDATA *)TreeView_GetSelectedItemData(hwndTV, &dwType);
  1121.  
  1122.     // Ok, do the order
  1123.     PunchTimer(hwnd, FALSE);
  1124.  
  1125.     // jump to the display page
  1126.     if(dwType < TYPE_MAX)
  1127.         nas = (pfnDisplayDetails[dwType])(hwnd, pTVData, wAction);
  1128.  
  1129.     // Report any errors
  1130.     if(nas)
  1131.     {
  1132.         TCHAR    szMsg[cchErrMax];
  1133.         TCHAR    *szErrMessage = NULL;
  1134.  
  1135.         lstrcpy(szMsg, szFromIDS1(IDS_ERRACTION));
  1136.         if(szErrMessage = GetSystemErrMessage(nas))
  1137.             lstrcat(szMsg, szErrMessage);
  1138.  
  1139.         MessageBox(hwnd, szMsg, szAppName, MB_ICONEXCLAMATION);
  1140.         GlobalFreeNullPtr(szErrMessage);
  1141.     }
  1142.  
  1143.     // If we hit an error or they whacked something, update now
  1144.     if(nas || (wAction == VK_DELETE))
  1145.         PostMessage(hwnd, WM_TIMER, 0, 0L);
  1146.     else
  1147.         PunchTimer(hwnd, TRUE);
  1148.  
  1149.     return TRUE;
  1150. }
  1151.  
  1152. /*
  1153.  *    AddErrorStringToTV
  1154.  *
  1155.  *    Purpose:
  1156.  *        Slap an error string in the TV
  1157.  *
  1158.  *    Arguments:
  1159.  *        hParent, ids of error, nas of error
  1160.  *
  1161.  *    Returns:
  1162.  *        nada
  1163.  */
  1164. VOID
  1165. AddErrorStringToTV(HWND hwndTV, HTREEITEM hParent, UINT ids, NET_API_STATUS nas)
  1166. {
  1167.     TV_INSERTSTRUCT    tvis;
  1168.     TCHAR            *szErrMessage;
  1169.     SCONST TCHAR    szSep[] = TEXT(": ");
  1170.     TCHAR            szErrBuf[cchErrMax];
  1171.  
  1172.     // Get the text
  1173.     lstrcpy(szErrBuf, szFromIDS1(ids));
  1174.     if(szErrMessage = GetSystemErrMessage(nas))
  1175.     {
  1176.         lstrcat(szErrBuf, szSep);
  1177.         lstrcat(szErrBuf, szErrMessage);
  1178.         LocalFree((HLOCAL)szErrMessage);
  1179.     }
  1180.  
  1181.     // Add the error string
  1182.     tvis.hParent = hParent;
  1183.     tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
  1184.     tvis.hInsertAfter = NULL;
  1185.     tvis.item.pszText = szErrBuf;
  1186.     tvis.item.iImage = tvis.item.iSelectedImage = BMP_DENIED;
  1187.     tvis.item.lParam = (LPARAM)TYPE_ERROR;
  1188.     TreeView_TVDataInsert(hwndTV, &tvis);
  1189.  
  1190.     // Delete all other children
  1191.     FTreeView_DeleteRange(hwndTV, hParent, tvis.hInsertAfter, NULL);
  1192. }
  1193.  
  1194. /*
  1195.  *    FAnyRemoteDrives
  1196.  *
  1197.  *    Purpose:
  1198.  *        Checks if there are any remote drives connected
  1199.  *
  1200.  *    Arguments:
  1201.  *        zip
  1202.  *
  1203.  *    Returns:
  1204.  *        Returns TRUE if there are
  1205.  */
  1206. BOOL
  1207. FAnyRemoteDrives()
  1208. {
  1209.     INT        ch;
  1210.     UINT    errmode;
  1211.     TCHAR    szRoot[4];
  1212.     BOOL    fRet = FALSE;
  1213.  
  1214.     errmode = SetErrorMode(SEM_FAILCRITICALERRORS);
  1215.  
  1216.     szRoot[1] = ':';
  1217.     szRoot[2] = '\\';
  1218.     szRoot[3] = 0;
  1219.     for (ch = 'C'; ch <= 'Z'; ch++)
  1220.     {
  1221.         szRoot[0] = ch;
  1222.         if(GetDriveType(szRoot) == DRIVE_REMOTE)
  1223.         {
  1224.             fRet = TRUE;
  1225.             break;
  1226.         }
  1227.     }
  1228.  
  1229.     SetErrorMode(errmode);
  1230.     return fRet;
  1231. }
  1232.  
  1233. /*
  1234.  *    HandleMenu
  1235.  *
  1236.  *    Purpose:
  1237.  *        Disable\Enable\Change menu according to what is selected
  1238.  *
  1239.  *    Arguments:
  1240.  *        hwnd
  1241.  *
  1242.  *    Returns:
  1243.  *        zip
  1244.  */
  1245. VOID
  1246. HandleMenu(HWND hwnd, HWND hwndTV, HMENU hMenu)
  1247. {
  1248.     DWORD    dwType = TYPE_COMPUTER;
  1249.  
  1250.     TreeView_GetSelectedItemData(hwndTV, &dwType);
  1251.     OutputDbgStr("Item type %d selected", dwType);
  1252.  
  1253.     // Enable/disable the disconnect network drive menu item
  1254.     if(hMenu == ghMenu)
  1255.     {
  1256.         EnableMenuItem(hMenu, IDM_DISCONNECTDRIVE, FAnyRemoteDrives() ?
  1257.             MF_BYCOMMAND | MF_ENABLED : MF_BYCOMMAND | MF_GRAYED);
  1258.     }
  1259.  
  1260.     if(dwType == TYPE_ERROR)
  1261.     {
  1262.         EnableMenuItem(hMenu, IDM_DELETERESOURCE, MF_BYCOMMAND | MF_GRAYED);
  1263.         EnableMenuItem(hMenu, IDM_PROPERTIES, MF_BYCOMMAND | MF_GRAYED);
  1264.     }
  1265.     else
  1266.     {
  1267.         EnableMenuItem(hMenu, IDM_PROPERTIES, MF_BYCOMMAND | MF_ENABLED);
  1268.         ModifyMenu(hMenu, IDM_DELETERESOURCE, MF_BYCOMMAND | MF_STRING,
  1269.             IDM_DELETERESOURCE, szFromIDS1(IDS_DELETERESOURCE + dwType));
  1270.     }
  1271. }
  1272.