netfldr.cpp
Upload User: xhy777
Upload Date: 2007-02-14
Package Size: 24088k
Code Size: 129k
Category:

Windows Kernel

Development Platform:

Visual C++

  1. BOOL CNetFolder::_GetPathForShare(LPCIDNETRESOURCE pidn, LPTSTR pszPath)
  2. {
  3.     NETRESOURCE nr;
  4.     DWORD err, dwRedir, dwResult;
  5.     TCHAR szAccessName[MAX_PATH], szRemoteName[MAX_PATH], szProviderName[MAX_PATH];
  6.     UINT cbAccessName;
  7.     NET_CopyResName(pidn, szRemoteName, ARRAYSIZE(szRemoteName));
  8.     if (NULL != _pszResName)
  9.     {
  10.         //
  11.         // Combine the folder name with the share name
  12.         // to create a UNC path.
  13.         //
  14.         // Borrow the szProviderName[] buffer for a bit.
  15.         //
  16.         PathCombine(szProviderName, _pszResName, szRemoteName);
  17.         //
  18.         // To be safe: UNC prefix implies that name is available using FS access
  19.         // Theoretically it also should be routed to MPR, but it is late to do this
  20.         //
  21.         if (PathIsUNC(szProviderName))
  22.         {
  23.             lstrcpy(pszPath, szProviderName);
  24.             return FALSE;
  25.         }
  26.         szProviderName[0] = TEXT('');
  27.     }
  28.     // Check cache
  29.     ENTERCRITICAL;
  30.     if (lstrcmpi(g_szLastAttemptedJunctionName, szRemoteName) == 0)
  31.     {
  32.         // cache hit
  33.         lstrcpy(pszPath, g_szLastResolvedJunctionName);
  34.         LEAVECRITICAL;
  35.         return TRUE;
  36.     }
  37.     LEAVECRITICAL;
  38.     memset(&nr, 0, SIZEOF(NETRESOURCE));
  39.     nr.lpRemoteName = szRemoteName;
  40.     nr.lpProvider = (LPTSTR) _GetProvider(pidn, szProviderName, ARRAYSIZE(szProviderName));
  41.     nr.dwType = NET_GetType(pidn);
  42.     nr.dwUsage = NET_GetUsage(pidn);
  43.     nr.dwDisplayType = NET_GetDisplayType(pidn);
  44.     dwRedir = CONNECT_TEMPORARY;
  45.     // Prepare access name buffer and net resource request buffer
  46.     cbAccessName = SIZEOF(szAccessName);        // BUGBUG verify this is cb, not cch
  47.     szAccessName[0] = 0;
  48.     err = WNetUseConnection(NULL, &nr, NULL, NULL, dwRedir, szAccessName, (LPDWORD) &cbAccessName, &dwResult);
  49.     if ((WN_SUCCESS != err) || !szAccessName[0])
  50.     {
  51.         lstrcpy(pszPath, szRemoteName);
  52.         return FALSE;
  53.     }
  54.     // Get the return name
  55.     lstrcpy(pszPath, szAccessName);
  56.     // Update cache entry
  57.     // BUGBUG We also want to record insuccessful resolution, although
  58.     // it is not really important, as we come to resolving code often
  59.     // only if it succeeded at least once.
  60.     {
  61.         ENTERCRITICAL;
  62.         lstrcpy(g_szLastAttemptedJunctionName, szRemoteName);
  63.         lstrcpy(g_szLastResolvedJunctionName, szAccessName);
  64.         LEAVECRITICAL;
  65.     }
  66.     return TRUE;
  67. }
  68. // in:
  69. //      pidn    may be multi-level net resource pidl like
  70. //              [entire net] [provider] [server] [share] [... file sys]
  71. //           or [server] [share] [... file sys]
  72. HRESULT CNetFolder::_GetPathForItem(LPCIDNETRESOURCE pidn, LPTSTR pszPath)
  73. {
  74.     *pszPath = 0;
  75.     // loop down
  76.     for (; !ILIsEmpty((LPCITEMIDLIST)pidn) ; pidn = (LPCIDNETRESOURCE)_ILNext((LPCITEMIDLIST)pidn))
  77.     {
  78.         if (NET_GetFlags(pidn) & SHID_JUNCTION)     // \servershare or strike/sys
  79.         {
  80.             _GetPathForShare(pidn, pszPath);
  81.             break;  // below this we don't know about any of the PIDLs
  82.         }
  83.         else
  84.         {
  85.             NET_CopyResName(pidn, pszPath, MAX_PATH);
  86.         }
  87.     }
  88.     return *pszPath ? S_OK : E_NOTIMPL;
  89. }
  90. HRESULT CNetFolder::_GetPathForItemW(LPCIDNETRESOURCE pidn, LPWSTR pszPath)
  91. {
  92. #ifdef UNICODE
  93.     return _GetPathForItem(pidn, pszPath);
  94. #else // UNICODE
  95.     TCHAR szPath[MAX_PATH];
  96.     HRESULT hres = _GetPathForItem(pidn, szPath);
  97.     if (SUCCEEDED(hres))
  98.         SHTCharToUnicode(szPath, pszPath, MAX_PATH);
  99.     else
  100.         *pszPath = 0;
  101.     return hres;
  102. #endif // UNICODE
  103. }
  104. // in:
  105. //  pidl
  106. //
  107. // takes the last items and create a folder for it, assuming the first section is the 
  108. // used to initialze.  the riid and ppv are used to return an object.
  109. //
  110. HRESULT CNetFolder::_CreateFolderForItem(LPCITEMIDLIST pidl, LPCITEMIDLIST pidlTarget, LPCIDNETRESOURCE pidnForProvider, REFIID riid, void** ppv)
  111. {
  112.     LPCITEMIDLIST pidlLast = ILFindLastID(pidl);
  113.     LPCIDNETRESOURCE pidn = NET_IsValidID(pidlLast);
  114.     if ( !pidn )
  115.         return E_INVALIDARG;
  116.     if (NET_IsRemoteFld(pidn))
  117.     {
  118.         // note: I think this is dead functionality. it was used in NT4 but we can't find
  119.         // the impl of this CLSID_Remote anymore...
  120.         IPersistFolder * ppf;
  121.         HRESULT hres = SHCoCreateInstance(NULL, &CLSID_Remote, NULL, IID_IPersistFolder, (void **)&ppf);
  122.         if (SUCCEEDED(hres))
  123.         {
  124.             hres = ppf->Initialize(pidl);
  125.             if (SUCCEEDED(hres))
  126.                 hres = ppf->QueryInterface(riid, ppv);
  127.             ppf->Release();
  128.         }
  129.         return hres;
  130.     }
  131.     else if (NET_GetFlags(pidn) & SHID_JUNCTION)     // \servershare or strike/sys
  132.     {
  133.         PERSIST_FOLDER_TARGET_INFO pfti = {0};
  134.         pfti.pidlTargetFolder = (LPITEMIDLIST)pidlTarget;    
  135.         _GetPathForItemW(pidn, pfti.szTargetParsingName);
  136.         pfti.csidl = -1;
  137.         pfti.dwAttributes = FILE_ATTRIBUTE_DIRECTORY; // maybe add system?
  138.         return CFSFolder_CreateFolder(NULL, pidl, &pfti, riid, ppv);
  139.     }
  140.     else
  141.     {
  142.         TCHAR szPath[MAX_PATH];
  143.         NET_CopyResName(pidn, szPath, ARRAYSIZE(szPath));
  144.         return CNetFldr_CreateInstance(pidl, pidlTarget, NET_GetDisplayType(pidn), pidnForProvider, szPath, riid, ppv);
  145.     }
  146. }
  147. // find the share part of a UNC
  148. //  \servershare
  149. //  return pointer to "share" or pointer to empty string if none
  150. LPCTSTR PathFindShareName(LPCTSTR pszUNC)
  151. {
  152.     LPCTSTR psz = SkipServerSlashes(pszUNC);
  153.     if (*psz)
  154.     {
  155.         psz = StrChr(psz + 1, TEXT('\'));
  156.         if (psz)
  157.             psz++;
  158.         else
  159.             psz = TEXT("");
  160.     }
  161.     return psz;
  162. }
  163. //
  164. // To be called back from within SHCreateEnumObjects
  165. //
  166. // lParam       - LPDEFENUM
  167. // pvData       - pointer to ENUMNETWORK constructed by CNetRoot_EnumObjects
  168. // ecid         - enumeration command (event)
  169. // index        - LPDEFENUM->iCur (unused)
  170. HRESULT CALLBACK CNetFolder::EnumCallBack(LPARAM lParam, void *pvData, UINT ecid, UINT index)
  171. {
  172.     HRESULT hres = S_OK;
  173.     ENUM_DATA *penet = (ENUM_DATA *)pvData;
  174.     if (ecid == ECID_SETNEXTID)
  175.     {
  176.         // Time to stop enumeration?
  177.         if (penet->dwRemote & RMF_STOP_ENUM)
  178.             return S_FALSE;       // Yes
  179.         //
  180.         // should we try and get the links enumerator?
  181.         //
  182.         if ( penet->dwRemote & RMF_GETLINKENUM )
  183.         {
  184.             CreateNetHoodShortcuts();
  185.             IShellFolder2* psfNetHood;                                                                                             
  186.             if (SUCCEEDED(penet->pnf->v_GetFileFolder(&psfNetHood)))
  187.                 psfNetHood->EnumObjects(NULL, penet->grfFlags, &penet->peunk);
  188.             if (penet->peunk)
  189.                 penet->dwRemote |= RMF_SHOWLINKS;
  190.             penet->dwRemote &= ~RMF_GETLINKENUM;
  191.         }
  192.         //
  193.         // should we be showing the links?
  194.         //
  195.         if (penet->dwRemote & RMF_SHOWLINKS)
  196.         {
  197.             if (penet->peunk)
  198.             {
  199.                 ULONG celtFetched;
  200.                 LPITEMIDLIST pidl;
  201.                 hres = penet->peunk->Next(1, &pidl, &celtFetched);
  202.                 if (hres == S_OK && celtFetched == 1)
  203.                 {
  204.                     ASSERT(pidl);
  205.                     CDefEnum_SetReturn(lParam, pidl);
  206.                     return S_OK;       // Added link
  207.                 }
  208.             }
  209.             penet->dwRemote &= ~RMF_SHOWLINKS; // Done enumerating links
  210.         }
  211.         hres = S_OK;
  212.         // Do we add the remote folder?
  213.         // (Note: as a hack to ensure that the remote folder is added
  214.         // to the 'hood despite what MPR says, RMF_SHOWREMOTE can be
  215.         // set without RMF_CONTEXT set.)
  216.         if ((penet->dwRemote & RMF_SHOWREMOTE) && !(penet->dwRemote & RMF_REMOTESHOWN))
  217.         {
  218.             // Yes
  219.             // Only try to put the remote entry in once.
  220.             penet->dwRemote |= RMF_REMOTESHOWN;
  221.             // Is this not the Context container?
  222.             // (See note above as to why we are asking this question.)
  223.             if ( !(penet->dwRemote & RMF_CONTEXT) ) 
  224.             {
  225.                 // Yes; stop after the next time
  226.                 penet->dwRemote |= RMF_STOP_ENUM;
  227.             }
  228.             // We have fallen thru because the remote services is not
  229.             // installed.
  230.             // Is this not the Context container AND the remote folder
  231.             // is not installed?
  232.             if ( !(penet->dwRemote & RMF_CONTEXT) ) 
  233.             {
  234.                 // Yes; nothing else to enumerate
  235.                 return S_FALSE;
  236.             }
  237.         }
  238.         if ( penet->dwRemote & RMF_FAKENETROOT )
  239.         {
  240.             if ( !(penet->dwRemote & RMF_ENTIRENETSHOWN) )
  241.             {                           
  242.                 _CreateEntireNetwork(NULL, lParam);         // fake entire net
  243.                 penet->dwRemote |= RMF_ENTIRENETSHOWN;
  244.             }
  245.             else
  246.             {
  247.                 return S_FALSE;         // no more to enumerate
  248.             }
  249.         }
  250.         else
  251.         {
  252.             while (TRUE)
  253.             {
  254.                 ULONG err = WN_SUCCESS;
  255.                 LPNETRESOURCE pnr;
  256.                 if (penet->iItem >= penet->cItems)
  257.                 {
  258.                     DWORD dwSize = SIZEOF(penet->szBuffer);
  259.                     // Figure that on average no item over 128 bytes...
  260.                     //penet->cItems = sizeof(penet->szBuffer) >> 7;
  261.                     penet->cItems = -1;
  262.                     penet->iItem = 0;
  263.                     err = WNetEnumResource(penet->hEnum, (DWORD*)&penet->cItems, penet->szBuffer, &dwSize);
  264.                     DebugMsg(DM_TRACE, TEXT("Net EnumCallback: err=%d Count=%d"),
  265.                         err, penet->cItems);
  266.                 }
  267.                 pnr = &penet->anr[penet->iItem++];
  268.                 // Output some debug messages to help us track
  269. #ifdef NET_TRACE
  270.                 DebugMsg(DM_TRACE, TEXT("Net EnumCallback: err=%d s=%d, t=%d, dt=%d, u=%d, %s"),
  271.                     err, pnr->dwScope, pnr->dwType, pnr->dwDisplayType,
  272.                     pnr->dwUsage, pnr->lpRemoteName ? pnr->lpRemoteName : TEXT("[NULL]"));
  273. #endif //NET_TRACE
  274.                 // Note: the <= below is correct as we already incremented the index...
  275.                 if (err == WN_SUCCESS && (penet->iItem <= penet->cItems))
  276.                 {
  277.                     // decide if the thing is a folder or not
  278.                     ULONG grfFlagsItem = ((pnr->dwUsage & RESOURCEUSAGE_CONTAINER) || 
  279.                                           (pnr->dwType == RESOURCETYPE_DISK) ||
  280.                                           (pnr->dwType == RESOURCETYPE_ANY)) ?
  281.                                             SHCONTF_FOLDERS : SHCONTF_NONFOLDERS;
  282.                     // If this is the context enumeration, we want to insert the
  283.                     // Remote Services after the first container.
  284.                     // Remember that we need to return the Remote Services
  285.                     // in the next iteration.
  286.                     //
  287.                     if ((pnr->dwUsage & RESOURCEUSAGE_CONTAINER) &&
  288.                         (penet->dwRemote & RMF_CONTEXT))
  289.                     {
  290.                         penet->dwRemote |= RMF_SHOWREMOTE;
  291.                     }
  292.                     if ((penet->pnf->_uDisplayType == RESOURCEDISPLAYTYPE_SERVER) &&
  293.                         (penet->grfFlags & SHCONTF_SHAREABLE))
  294.                     {
  295.                         // filter out ADMIN$ and IPC$, lame, based on str len
  296.                         if (lstrlen(PathFindShareName(pnr->lpRemoteName)) > 2)
  297.                             grfFlagsItem = 0;
  298.                     }
  299.                     // Check if we found requested type of net resource.
  300.                     if (penet->grfFlags & grfFlagsItem)
  301.                     {
  302.                         // Yes.
  303.                         ASSERT(lParam);     // else we leak here
  304.                         if ( SUCCEEDED(_NetResToIDList(pnr, FALSE, TRUE, (penet->grfFlags & SHCONTF_NONFOLDERS), NULL, lParam)) )
  305.                         {
  306.                             break;
  307.                         }
  308.                     }
  309.                 }
  310.                 else if (err == WN_NO_MORE_ENTRIES) 
  311.                 {
  312.                     hres = S_FALSE; // no more element
  313.                     break;
  314.                 }
  315.                 else 
  316.                 {
  317.                     DebugMsg(DM_ERROR, TEXT("sh ER - WNetEnumResource failed (%lx)"), err);
  318.                     hres = E_FAIL;
  319.                     break;
  320.                 }
  321.             }
  322.         }
  323.     }
  324.     else if (ecid == ECID_RELEASE)
  325.     {
  326.         penet->pnf->Release();              // release the "this" ptr we have
  327.         if (penet->peunk)
  328.             penet->peunk->Release();
  329.         if ( penet->hEnum )
  330.             WNetCloseEnum(penet->hEnum);
  331.         LocalFree((HLOCAL)penet);
  332.     }
  333.     return hres;
  334. }
  335. // get the provider for an item or the folder itself. since some items don't have the
  336. // provider stored we fall back to the folder to get the provider in that case
  337. //
  338. //  in:
  339. //      pidn    item to get provider for. if NULL get provider for the folder
  340. //
  341. // returns:
  342. //      NULL        no provider in the item or the folder
  343. //      non NULL    address of passed in buffer
  344. LPCTSTR CNetFolder::_GetProvider(LPCIDNETRESOURCE pidn, LPTSTR pszProvider, UINT cchProvider)
  345. {
  346.     if (pidn && NET_CopyProviderName(pidn, pszProvider, cchProvider))
  347.         return pszProvider;
  348.     if ( _pidnForProvider )
  349.     {
  350.         NET_CopyProviderName( _pidnForProvider, pszProvider, cchProvider );
  351.         return pszProvider;
  352.     }
  353.     return NULL;
  354. }
  355. // construct a net idlist either copying the existing data from a pidl or 
  356. // from a NETRESOURCE structure
  357. HRESULT CNetFolder::_CreateNetIDList(LPIDNETRESOURCE pidnIn, 
  358.                                      LPCTSTR pszName, LPCTSTR pszProvider, LPCTSTR pszComment, 
  359.                                      LPITEMIDLIST *ppidl)
  360. {
  361.     LPBYTE pb;
  362.     UINT cbmkid = SIZEOF(IDNETRESOURCE)-SIZEOF(CHAR);
  363.     UINT cchName, cchProvider, cchComment, cbProviderType = 0;
  364.     LPIDNETRESOURCE pidn;
  365.     WORD wNetType = 0;
  366.     BOOL fUnicode = FALSE;
  367.     UINT  cchAnsiName, cchAnsiProvider, cchAnsiComment;
  368.     CHAR szAnsiName[MAX_PATH], szAnsiProvider[MAX_PATH], szAnsiComment[MAX_PATH];
  369.     ASSERT(ppidl != NULL);
  370.     *ppidl = NULL;
  371.     if (!pszName)
  372.         pszName = c_szNULL;     // For now put in an empty string...
  373.     if ( pszProvider )
  374.         cbProviderType += SIZEOF(WORD);
  375.     
  376. #ifdef WINNT
  377.     //
  378.     // Win9x shipped with one set of provider name which are 
  379.     // different on NT.  Therefore lets convert the NT one to
  380.     // something that Win9x can understand.
  381.     //
  382.     if (pszProvider)
  383.     {
  384.         DWORD dwRes, dwType;
  385.         INT i;
  386.         cbProviderType = SIZEOF(WORD);
  387.         dwRes = WNetGetProviderType(pszProvider, &dwType);
  388.         if (dwRes == WN_SUCCESS)
  389.         {
  390.             wNetType = HIWORD(dwType);
  391.             for (i=0; i < c_cProviders; i++)
  392.             {
  393.                 if (c_rgProviderMap[i].wNetType == wNetType)
  394.                 {
  395.                     pszProvider = c_rgProviderMap[i].lpName;
  396.                     break;
  397.                 }
  398.             }
  399.         }
  400.     }
  401. #endif
  402.     // compute the string lengths ready to build an IDLIST
  403.     cchName = lstrlen(pszName)+1;
  404.     cchProvider = pszProvider ? lstrlen(pszProvider)+1 : 0;
  405.     cchComment = pszComment ? lstrlen(pszComment)+1 : 0;
  406.     cchAnsiName = 0;
  407.     cchAnsiProvider = 0;
  408.     cchAnsiComment = 0;
  409.     fUnicode  = !DoesStringRoundTrip(pszName, szAnsiName, ARRAYSIZE(szAnsiProvider));
  410.     cchAnsiName = lstrlenA(szAnsiName)+1;
  411.     if ( pszProvider )
  412.     {
  413.         fUnicode |= !DoesStringRoundTrip(pszProvider, szAnsiProvider, ARRAYSIZE(szAnsiProvider));
  414.         cchAnsiProvider = lstrlenA(szAnsiProvider)+1;
  415.     }
  416.     if ( pszComment )
  417.     {
  418.         fUnicode |= !DoesStringRoundTrip(pszComment, szAnsiComment, ARRAYSIZE(szAnsiComment));
  419.         cchAnsiComment = lstrlenA(szAnsiComment)+1;
  420.     }
  421.     // allocate and fill the IDLIST header
  422.     cbmkid += cbProviderType+cchAnsiName + cchAnsiProvider + cchAnsiComment;
  423.     if ( fUnicode )
  424.         cbmkid += (SIZEOF(WCHAR)*(cchName+cchProvider+cchComment));
  425.     pidn = (LPIDNETRESOURCE)_ILCreate(cbmkid + SIZEOF(USHORT));
  426.     if (!pidn)
  427.         return E_OUTOFMEMORY;
  428.     pidn->cb = (WORD)cbmkid;
  429.     pidn->bFlags = pidnIn->bFlags;
  430.     pidn->uType = pidnIn->uType;
  431.     pidn->uUsage = pidnIn->uUsage;
  432.     if (pszProvider)
  433.         pidn->uUsage |= NET_HASPROVIDER;
  434.     if (pszComment)
  435.         pidn->uUsage |= NET_HASCOMMENT;
  436.     pb = (LPBYTE) pidn->szNetResName;
  437.     //
  438.     // write the ANSI strings into the IDLIST
  439.     //
  440.     StrCpyA((PSTR) pb, szAnsiName);
  441.     pb += cchAnsiName;
  442.     if ( pszProvider )
  443.     {
  444.         StrCpyA((PSTR) pb, szAnsiProvider);
  445.         pb += cchAnsiProvider;
  446.     }
  447.     if ( pszComment )
  448.     {
  449.         StrCpyA((PSTR) pb, szAnsiComment);
  450.         pb += cchAnsiComment;
  451.     }
  452.     // if we are going to be UNICODE then lets write those strings also.
  453.     // Note that we must use unaligned string copies since the is no
  454.     // promse that the ANSI strings will have an even number of characters
  455.     // in them.
  456. #ifdef UNICODE
  457.     if ( fUnicode )
  458.     {
  459.         pidn->uUsage |= NET_UNICODE;
  460.       
  461.         ualstrcpyW((UNALIGNED WCHAR *)pb, pszName);
  462.         pb += cchName*SIZEOF(WCHAR);
  463.         if ( pszProvider )
  464.         {
  465.             ualstrcpyW((UNALIGNED WCHAR *)pb, pszProvider);
  466.             pb += cchProvider*SIZEOF(WCHAR);
  467.         }
  468.         if ( pszComment )
  469.         {
  470.             ualstrcpyW((UNALIGNED WCHAR *)pb, pszComment);
  471.             pb += cchComment*SIZEOF(WCHAR);
  472.         }
  473.     }
  474. #endif
  475.     //
  476.     // and the trailing provider type
  477.     //
  478.     if (cbProviderType)
  479.     {
  480.         // Store the provider type
  481.         pb = (LPBYTE)pidn + pidn->cb - SIZEOF(WORD);
  482.         *((UNALIGNED WORD *)pb) = wNetType;
  483.     }
  484.     *ppidl = (LPITEMIDLIST)pidn;
  485.     return S_OK;
  486. }
  487. // wrapper for converting a NETRESOURCE into an IDLIST via _CreateNetPidl
  488. HRESULT CNetFolder::_NetResToIDList(NETRESOURCE *pnr, 
  489.                                     BOOL fAllowNull, BOOL fKeepProviderName, BOOL fKeepComment, 
  490.                                     LPITEMIDLIST *ppidl, LPARAM lParam)
  491. {
  492.     NETRESOURCE nr = *pnr;
  493.     LPITEMIDLIST pidl;
  494.     LPTSTR pszName, pszProvider, pszComment;
  495.     IDNETRESOURCE idn;
  496.     LPTSTR psz;
  497.     if ( ppidl )
  498.         *ppidl = NULL;
  499.     switch (pnr->dwDisplayType) 
  500.     {
  501.         case RESOURCEDISPLAYTYPE_NETWORK:
  502.             pszName = pnr->lpProvider;
  503.             break;
  504.         case RESOURCEDISPLAYTYPE_ROOT:
  505.             pszName =pnr->lpComment;
  506.             break;
  507.         default:
  508.         {
  509.             // check the name for a NULL string (returned sometimes in NT4 domains)
  510.             pszName = pnr->lpRemoteName;
  511.             if ( !fAllowNull && !*pszName )
  512.                 return E_FAIL;
  513.             // pretty stuff after the "\"
  514.             psz = (LPTSTR)SkipServerSlashes(pnr->lpRemoteName);
  515.             if ( *psz )
  516.                 PathMakePretty(psz);
  517.             break;
  518.         }
  519.     }
  520.     pszProvider = fKeepProviderName ? nr.lpProvider:NULL;
  521.     pszComment = fKeepComment ? nr.lpComment:NULL;
  522.        
  523.     idn.bFlags = (BYTE)(SHID_NET | (pnr->dwDisplayType & 0x0f));
  524.     idn.uType  = (BYTE)(pnr->dwType & 0x0f);
  525.     idn.uUsage = (BYTE)(pnr->dwUsage & 0x0f);
  526.     // Is the current resource a share of some kind and not a container
  527.     if ((pnr->dwDisplayType == RESOURCEDISPLAYTYPE_SHARE || pnr->dwDisplayType == RESOURCEDISPLAYTYPE_SHAREADMIN) &&
  528.         !(pnr->dwUsage & RESOURCEUSAGE_CONTAINER))
  529.     {
  530.         // If so, remember to delegate children of this folder to FSFolder
  531.         idn.bFlags |= (BYTE)SHID_JUNCTION;    // \servershare type thing
  532.     }
  533.     HRESULT hres = _CreateNetIDList(&idn, pszName, pszProvider, pszComment, &pidl);
  534.     if ( SUCCEEDED(hres) )
  535.     {
  536.         if ( lParam )
  537.             CDefEnum_SetReturn(lParam, (LPITEMIDLIST)pidl);
  538.         if ( ppidl )
  539.             *ppidl = pidl;
  540.     }
  541.     return hres;
  542. }
  543. HRESULT CNetFolder::_CreateEntireNetwork(LPITEMIDLIST *ppidl, LPARAM lParam)
  544. {
  545.     TCHAR szPath[MAX_PATH];
  546.     NETRESOURCE nr = {0};
  547.     // We need to add the Rest of network entry.  This is psuedo
  548.     // bogus, as we should either always do it ourself or have
  549.     // MPR always do it, but here it goes...
  550.     LoadString(HINST_THISDLL, IDS_RESTOFNET, szPath, ARRAYSIZE(szPath));
  551.     nr.dwDisplayType = RESOURCEDISPLAYTYPE_ROOT;
  552.     nr.dwType = RESOURCETYPE_ANY;
  553.     nr.dwUsage = RESOURCEUSAGE_CONTAINER;
  554.     nr.lpComment = szPath;
  555.     return _NetResToIDList(&nr, TRUE, FALSE, FALSE, ppidl, lParam);     // allows NULL namess
  556. }
  557. //===========================================================================
  558. //
  559. // To be called back from within CDefFolderMenu
  560. //
  561. STDAPI CNetwork_DFMCallBackBG(IShellFolder *psf, HWND hwnd,
  562.                               IDataObject *pdtobj, UINT uMsg, 
  563.                               WPARAM wParam, LPARAM lParam)
  564. {
  565.     HRESULT hres = S_OK;
  566.     CNetFolder* pThis;
  567.     if (FAILED(psf->QueryInterface(CLSID_CNetFldr, (void**) &pThis)))
  568.         return E_UNEXPECTED;
  569.     switch(uMsg)
  570.     {
  571.     case DFM_MERGECONTEXTMENU:
  572.         if (!(wParam & (CMF_VERBSONLY | CMF_DVFILE)))
  573.         {
  574.             CDefFolderMenu_MergeMenu(HINST_THISDLL, POPUP_NETWORK_BACKGROUND,
  575.                     POPUP_NETWORK_POPUPMERGE, (LPQCMINFO)lParam);
  576.         }
  577.         break;
  578.     case DFM_GETHELPTEXT:
  579.         LoadStringA(HINST_THISDLL, LOWORD(wParam) + IDS_MH_FSIDM_FIRST, (LPSTR)lParam, HIWORD(wParam));
  580.         break;
  581.     case DFM_GETHELPTEXTW:
  582.         LoadStringW(HINST_THISDLL, LOWORD(wParam) + IDS_MH_FSIDM_FIRST, (LPWSTR)lParam, HIWORD(wParam));
  583.         break;
  584.     case DFM_INVOKECOMMAND:
  585.         switch(wParam)
  586.         {
  587.         case FSIDM_SORTBYNAME:
  588.         case FSIDM_SORTBYCOMMENT:
  589.             ShellFolderView_ReArrange(hwnd, (wParam == FSIDM_SORTBYNAME) ? 0 : 1);
  590.             break;
  591.         case FSIDM_PROPERTIESBG:
  592.             hres = SHPropertiesForPidl(hwnd, pThis->_pidl, (LPCTSTR)lParam);
  593.             break;
  594.         default:
  595.             // This is one of view menu items, use the default code.
  596.                 hres = S_FALSE;
  597.             break;
  598.         }
  599.         break;
  600.     default:
  601.         hres = E_NOTIMPL;
  602.         break;
  603.     }
  604.     return hres;
  605. }
  606. //===========================================================================
  607. //
  608. // To be called back from within CDefFolderMenu
  609. //
  610. STDAPI CNetFolder::DFMCallBack(IShellFolder* psf, HWND hwnd,
  611.                                   IDataObject* pdtobj, UINT uMsg, 
  612.                                   WPARAM wParam, LPARAM lParam)
  613. {
  614.     HRESULT hres = S_OK;
  615.     switch(uMsg)
  616.     {
  617.     case DFM_MERGECONTEXTMENU:
  618.         if (pdtobj)
  619.         {
  620.             STGMEDIUM medium;
  621.             LPIDA pida;
  622.             LPQCMINFO pqcm = (LPQCMINFO)lParam;
  623.             UINT idCmdBase = pqcm->idCmdFirst; // must be called before merge
  624.             CDefFolderMenu_MergeMenu(HINST_THISDLL, POPUP_NETWORK_ITEM, 0, pqcm);
  625.             pida = DataObj_GetHIDA(pdtobj, &medium);
  626.             if (pida)
  627.             {
  628.                 if (pida->cidl > 0)
  629.                 {
  630.                     LPIDNETRESOURCE pidn = (LPIDNETRESOURCE)IDA_GetIDListPtr(pida, 0);
  631.                     // Only enable "connect" command if the first one is a share.
  632.                     if (pidn)
  633.                     {
  634.                         ULONG rgf = 0;
  635.                         if( NET_GetFlags(pidn) & SHID_JUNCTION &&
  636.                             !SHRestricted( REST_NONETCONNECTDISCONNECT ) )
  637.                         {
  638.                             EnableMenuItem(pqcm->hmenu, idCmdBase + FSIDM_CONNECT,
  639.                                 MF_CHECKED | MF_BYCOMMAND);
  640.                         }
  641.                     }
  642.                 }
  643.                 HIDA_ReleaseStgMedium(pida, &medium);
  644.             }
  645.         }
  646.         break;
  647.     case DFM_GETHELPTEXT:
  648.         LoadStringA(HINST_THISDLL, LOWORD(wParam) + IDS_MH_FSIDM_FIRST, (LPSTR)lParam, HIWORD(wParam));
  649.         break;
  650.     case DFM_GETHELPTEXTW:
  651.         LoadStringW(HINST_THISDLL, LOWORD(wParam) + IDS_MH_FSIDM_FIRST, (LPWSTR)lParam, HIWORD(wParam));
  652.         break;
  653.     case DFM_INVOKECOMMAND:
  654.         switch(wParam)
  655.         {
  656.         case DFM_CMD_PROPERTIES:
  657.             SHLaunchPropSheet(_PropertiesThreadProc, pdtobj, (LPCTSTR)lParam, psf, NULL);
  658.             break;
  659.         case DFM_CMD_LINK:
  660.             {
  661.                 hres = S_FALSE; // do the default shortcut stuff
  662.                 CNetFolder* pThis;
  663.                 if (SUCCEEDED(psf->QueryInterface(CLSID_CNetFldr, (void**) &pThis)))
  664.                 {
  665.                     // net hood special case.  in this case we want to create the shortuct
  666.                     // in the net hood, not offer to put this on the desktop
  667.                     IShellFolder2* psfFiles;
  668.                     if (SUCCEEDED(pThis->v_GetFileFolder(&psfFiles)))
  669.                     {
  670.                         FS_CreateLinks(hwnd, psfFiles, pdtobj, (LPCTSTR)lParam, CMIC_MASK_FLAG_NO_UI);
  671.                         hres = S_OK;    // we created the links
  672.                     }
  673.                 }
  674.             }
  675.             break;
  676.         case FSIDM_CONNECT:
  677.             if (pdtobj)
  678.             {
  679.                 STGMEDIUM medium;
  680.                 LPIDA pida = DataObj_GetHIDA(pdtobj, &medium);
  681.                 if (pida)
  682.                 {
  683.                     UINT iidl;
  684.                     for (iidl = 0; iidl < pida->cidl; iidl++)
  685.                     {
  686.                         LPIDNETRESOURCE pidn = (LPIDNETRESOURCE)IDA_GetIDListPtr(pida, iidl);
  687.                         // Only execute "connect" on shares.
  688.                         if (NET_GetFlags(pidn) & SHID_JUNCTION)
  689.                         {
  690.                             TCHAR szName[MAX_PATH];
  691.                             LPTSTR pszName = NET_CopyResName(pidn, szName, ARRAYSIZE(szName));
  692.                             DWORD err = SHStartNetConnectionDialog(hwnd, pszName, RESOURCETYPE_DISK);
  693.                             DebugMsg(DM_TRACE, TEXT("CNet FSIDM_CONNECT (%s, %x)"), szName, err);
  694.                             // events will get generated automatically
  695.                         }
  696.                     }
  697.                     HIDA_ReleaseStgMedium(pida, &medium);
  698.                 }
  699.             }
  700.             break;
  701.         default:
  702.             // This is one of view menu items, use the default code.
  703.             hres = S_FALSE;
  704.             break;
  705.         }
  706.         break;
  707.     default:
  708.         hres = E_NOTIMPL;
  709.         break;
  710.     }
  711.     return hres;
  712. }
  713. STDAPI CNetFolder::PrinterDFMCallBack(IShellFolder* psf, HWND hwnd,
  714.                                       IDataObject* pdtobj, UINT uMsg, 
  715.                                       WPARAM wParam, LPARAM lParam)
  716. {
  717.     HRESULT hres = S_OK;
  718.     switch(uMsg)
  719.     {
  720.     case DFM_MERGECONTEXTMENU:
  721.         //
  722.         //  Returning S_FALSE indicates no need to get verbs from
  723.         // extensions.
  724.         //
  725.         hres = S_FALSE;
  726.         break;
  727.     // if anyone hooks our context menu, we want to be on top (Open)
  728.     case DFM_MERGECONTEXTMENU_TOP:
  729.         if (pdtobj)
  730.         {
  731.             LPQCMINFO pqcm = (LPQCMINFO)lParam;
  732.             // insert verbs
  733.             CDefFolderMenu_MergeMenu(HINST_THISDLL, POPUP_NETWORK_PRINTER, 0, pqcm);
  734. #ifndef WINNT
  735.             //
  736.             // WINNT does not support Capturing print ports, so no
  737.             // need to check.
  738.             //
  739.             if (!(GetSystemMetrics(SM_NETWORK) & RNC_NETWORKS))
  740.             {
  741.                 // remove "map" if no net
  742.                 DeleteMenu(pqcm->hmenu, pqcm->idCmdFirst + FSIDM_CONNECT_PRN, MF_BYCOMMAND);
  743.             }
  744. #endif
  745.             SetMenuDefaultItem(pqcm->hmenu, 0, MF_BYPOSITION);
  746.         }
  747.         break;
  748.     case DFM_GETHELPTEXT:
  749.         LoadStringA(HINST_THISDLL, LOWORD(wParam) + IDS_MH_FSIDM_FIRST, (LPSTR)lParam, HIWORD(wParam));
  750.         break;
  751.     case DFM_GETHELPTEXTW:
  752.         LoadStringW(HINST_THISDLL, LOWORD(wParam) + IDS_MH_FSIDM_FIRST, (LPWSTR)lParam, HIWORD(wParam));
  753.         break;
  754.     case DFM_INVOKECOMMAND:
  755.         switch(wParam)
  756.         {
  757.         case DFM_CMD_PROPERTIES:
  758.             SHLaunchPropSheet(_PropertiesThreadProc, pdtobj, (LPCTSTR)lParam, psf, NULL);
  759.             break;
  760.         case DFM_CMD_LINK:
  761.             // do the default create shortcut crap
  762.             return S_FALSE;
  763.         case FSIDM_OPENPRN:
  764.         case FSIDM_NETPRN_INSTALL:
  765. #ifndef WINNT
  766.         case FSIDM_CONNECT_PRN:
  767. #endif
  768.         {
  769.             STGMEDIUM medium;
  770.             LPIDA pida = DataObj_GetHIDA(pdtobj, &medium);
  771.             if (pida)
  772.             {
  773.                 UINT action, i;
  774.                 // set up the operation we are going to perform
  775.                 switch (wParam) {
  776.                 case FSIDM_OPENPRN:
  777.                     action = PRINTACTION_OPENNETPRN;
  778.                     break;
  779.                 case FSIDM_NETPRN_INSTALL:
  780.                     action = PRINTACTION_NETINSTALL;
  781.                     break;
  782.                 default: // FSIDM_CONNECT_PRN
  783.                     action = (UINT)-1;
  784.                     break;
  785.                 }
  786.                 for (i = 0; i < pida->cidl; i++)
  787.                 {
  788.                     LPIDNETRESOURCE pidn = (LPIDNETRESOURCE)IDA_GetIDListPtr(pida, i);
  789.                     // Only execute command for a net print share
  790.                     if (_IsPrintShare(pidn))
  791.                     {
  792.                         TCHAR szName[MAX_PATH];
  793.                         NET_CopyResName(pidn,szName,ARRAYSIZE(szName));
  794. #ifndef WINNT // PRINTQ
  795.                         if (action == (UINT)-1)
  796.                         {
  797.                             SHNetConnectionDialog(hwnd, szName, RESOURCETYPE_PRINT);
  798.                         }
  799.                         else
  800. #endif
  801.                         {
  802.                             SHInvokePrinterCommand(hwnd, action, szName, NULL, FALSE);
  803.                         }
  804.                     }
  805.                 } // for (i...
  806.                 HIDA_ReleaseStgMedium(pida, &medium);
  807.             } // if (medium.hGlobal)
  808.             break;
  809.         } // case ID_NETWORK_PRINTER_INSTALL, FSIDM_CONNECT_PRN
  810.         } // switch(wparam)
  811.         break;
  812.     default:
  813.         hres = E_NOTIMPL;
  814.         break;
  815.     }
  816.     return hres;
  817. }
  818. //
  819. // REVIEW: Almost identical code in fstreex.c
  820. //
  821. DWORD CALLBACK CNetFolder::_PropertiesThreadProc(void *pv)
  822. {
  823.     PROPSTUFF* pps = (PROPSTUFF *)pv;
  824.     CNetFolder* pThis;
  825.     if (SUCCEEDED(pps->psf->QueryInterface(CLSID_CNetFldr, (void**) &pThis)))
  826.     {
  827.         STGMEDIUM medium;
  828.         LPIDA pida = DataObj_GetHIDA(pps->pdtobj, &medium);
  829.         if (pida)
  830.         {
  831.             // Yes, do context menu.
  832.             HKEY ahkeys[NKID_COUNT];
  833.             HRESULT hres = pThis->_OpenKeys((LPCIDNETRESOURCE)IDA_GetIDListPtr(pida, 0), ahkeys);
  834.             if (SUCCEEDED(hres))
  835.             {
  836.                 LPTSTR pszCaption = SHGetCaption(medium.hGlobal);
  837.                 SHOpenPropSheet(pszCaption, ahkeys, ARRAYSIZE(ahkeys),
  838.                                 &CLSID_ShellNetDefExt,
  839.                                 pps->pdtobj, NULL, pps->pStartPage);
  840.                 if (pszCaption)
  841.                     SHFree(pszCaption);
  842.                 SHRegCloseKeys(ahkeys, ARRAYSIZE(ahkeys));
  843.             }
  844.             HIDA_ReleaseStgMedium(pida, &medium);
  845.         }
  846.     }
  847.     return S_OK;
  848. }
  849. STDAPI CNetFolder::GAOCallbackNet(IShellFolder2* psf, LPCITEMIDLIST pidl, ULONG* prgfInOut)
  850. {
  851.     LPCIDNETRESOURCE pidn = (LPCIDNETRESOURCE)pidl;
  852.     ULONG rgfOut = SFGAO_CANLINK | SFGAO_HASPROPSHEET | SFGAO_HASSUBFOLDER |
  853.                    SFGAO_FOLDER | SFGAO_FILESYSANCESTOR | SFGAO_CANMONIKER;
  854.     if (NET_GetFlags(pidn) & SHID_JUNCTION)
  855.     {
  856.         if ((NET_GetType(pidn) == RESOURCETYPE_DISK) || 
  857.             (NET_GetType(pidn) == RESOURCETYPE_ANY))
  858.             rgfOut |= SFGAO_FILESYSTEM | SFGAO_DROPTARGET;
  859.         else
  860.             rgfOut &= ~SFGAO_FILESYSANCESTOR;
  861.     }
  862.     if (_IsPrintShare(pidn))
  863.     {
  864.         rgfOut |= SFGAO_DROPTARGET; // for drag and drop printing
  865.         rgfOut &= ~(SFGAO_FILESYSANCESTOR | SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_CANMONIKER | SFGAO_HASSUBFOLDER);
  866.     }
  867.     if (NET_IsRemoteFld(pidn))
  868.     {
  869.         rgfOut &= ~(SFGAO_FILESYSANCESTOR | SFGAO_FILESYSTEM | SFGAO_CANMONIKER);
  870.     }
  871.     *prgfInOut = rgfOut;
  872.     return S_OK;
  873. }
  874. // This is only used by the CNetRootFolder subclass, but because we can only QI for
  875. // CLSID_NetFldr, and we can't access protected members of any CNetFolder instance 
  876. // from a member function of CNetRootFolder, we'll make it belong to CNetFolder
  877. HRESULT CALLBACK CNetFolder::GAOCallbackNetRoot(IShellFolder2* psf, LPCITEMIDLIST pidl, ULONG* prgfInOut)
  878. {
  879.     CNetFolder* pNetF;
  880.     HRESULT hres = psf->QueryInterface(CLSID_CNetFldr, (void**) &pNetF);
  881.     if (SUCCEEDED(hres))
  882.     {
  883.         if (NET_IsValidID(pidl))
  884.         {
  885.             hres = pNetF->CNetFolder::GetAttributesOf(1, &pidl, prgfInOut);
  886.         }
  887.         else 
  888.         {
  889.             IShellFolder2* psfFiles;
  890.             hres = pNetF->v_GetFileFolder(&psfFiles);
  891.             if (SUCCEEDED(hres))
  892.                 hres = psfFiles->GetAttributesOf(1, &pidl, prgfInOut);
  893.         }
  894.     }
  895.     return hres;
  896. }
  897. // this is called by netfind.c
  898. STDMETHODIMP CNetwork_EnumSearches(IShellFolder2* psf2, LPENUMEXTRASEARCH *ppenum)
  899. {
  900.     HRESULT hres;
  901.     if (NULL != psf2)
  902.     {
  903.         CNetFolder* pNetF;
  904.         hres = psf2->QueryInterface(CLSID_CNetFldr, (void**) &pNetF);
  905.         if (SUCCEEDED(hres))
  906.             hres = pNetF->EnumSearches(ppenum);
  907.     }
  908.     else
  909.     {
  910.         hres = E_INVALIDARG;
  911.     }
  912.     return hres;
  913. }
  914. // given the resulting ppidl and a pszRest continue to parse through and add in the remainder
  915. // of the file system path.
  916. HRESULT CNetFolder::_ParseRest(LPBC pbc, LPCWSTR pszRest, LPITEMIDLIST* ppidl, DWORD* pdwAttributes)
  917. {
  918.     HRESULT hres = S_OK;
  919.     if ( pszRest && pszRest[0] )
  920.     {
  921.         IShellFolder* psfBind;
  922.         // need to QI to get the agregated case
  923.         hres = QueryInterface(IID_IShellFolder, (void**) &psfBind);
  924.         if (SUCCEEDED(hres))
  925.         {
  926.             IShellFolder* psfSub;
  927.             // pass down to pick off stuff below including regitems and file names
  928.             hres = psfBind->BindToObject(*ppidl, NULL, IID_IShellFolder, (void**) &psfSub);
  929.             if (SUCCEEDED(hres))
  930.             {
  931.                 LPITEMIDLIST pidlSubDir;
  932.                 // skip leading  if there is one present
  933.                 if ( pszRest[0] == L'\' )
  934.                     pszRest++;
  935.                 hres = psfSub->ParseDisplayName(NULL, pbc, (LPWSTR)pszRest, NULL, &pidlSubDir, pdwAttributes);
  936.                 if (SUCCEEDED(hres))
  937.                 {
  938.                     hres = SHILAppend(pidlSubDir, ppidl);
  939.                 }
  940.                 psfSub->Release();
  941.             }
  942.             psfBind->Release();
  943.         }
  944.     }
  945.     else
  946.     {
  947.         if ( pdwAttributes )
  948.         {
  949.             LPCITEMIDLIST apidlLast[1] = { ILFindLastID(*ppidl) };
  950.             hres = GetAttributesOf(1, apidlLast, pdwAttributes);
  951.         }
  952.     }
  953.     return hres;
  954. }
  955. // handle parsing a net name into a IDLIST structure that represents that NETRESOURCE
  956. // up to the root.  we loop using each NETRESOURCE and calling NetResourceGetParent,
  957. // we assume that if we receive a ERROR_BAD_NET_NAME we are done.
  958. HRESULT CNetFolder::_NetResToIDLists(NETRESOURCE *pnr, DWORD dwbuf, LPITEMIDLIST *ppidl)
  959. {
  960.     HRESULT hres = S_OK;
  961.     do
  962.     {
  963.         LPITEMIDLIST pidlT;
  964.         hres = _NetResToIDList(pnr, FALSE, TRUE, TRUE, &pidlT, 0);
  965.         if ( SUCCEEDED(hres) )
  966.         {
  967.             hres = SHILPrepend(pidlT, ppidl);
  968.             if ( FAILED(hres) )
  969.             {
  970.                 ILFree(pidlT);
  971.             }
  972.             else
  973.             {
  974.                 // lets get the resource parent, if the display type is ROOT
  975.                 // then there is no point as we are already at the top of the
  976.                 // chain, calling for root will just fail (or on Win9x cause 
  977.                 // us to loop forever, eat all the stack and die!).
  978.                 if ( (pnr->dwDisplayType == RESOURCEDISPLAYTYPE_NETWORK) ||
  979.                       (pnr->dwDisplayType == RESOURCEDISPLAYTYPE_ROOT) ||
  980.                        (WNetGetResourceParent(pnr, pnr, &dwbuf) != WN_SUCCESS) )
  981.                 {
  982.                     break;
  983.                 }
  984.             }
  985.         }
  986.     }
  987.     while ( SUCCEEDED(hres) );
  988.     return hres;
  989. }
  990. // get the parsable network name from the object
  991. LPTSTR CNetFolder::_GetNameForParsing(LPCWSTR pwszName, LPTSTR pszBuffer, INT cchBuffer, LPTSTR *ppszRegItem)
  992. {
  993.     LPTSTR pszRegItem = NULL;
  994.     INT cSlashes = 0;
  995.     *ppszRegItem = NULL;
  996.     
  997.     SHUnicodeToTChar(pwszName, pszBuffer, cchBuffer);    
  998.     // remove the trailing  if there is one, NTLanMan barfs if we pass a string containing it
  999.     INT cchPath = lstrlen(pszBuffer)-1;
  1000.     if ( (cchPath > 2) && (pszBuffer[cchPath] == TEXT('\')) )
  1001.         pszBuffer[cchPath] = TEXT('');
  1002.     // lets walk the name, look for :: squence to signify the start of a regitem name,
  1003.     // and if the number of slashes is > 2 then we should bail
  1004.     
  1005.     LPTSTR pszUNC = pszBuffer+2;    
  1006.     while ( pszUNC && *pszUNC && (cSlashes < 2) )
  1007.     {
  1008.         if ( (pszUNC[0] == TEXT('\')) && 
  1009.                 (pszUNC[1] == TEXT(':')) && (pszUNC[2] == TEXT(':')) )
  1010.         {
  1011.             *ppszRegItem = pszUNC;
  1012.             break;
  1013.         }
  1014.         pszUNC = StrChr(pszUNC+1, TEXT('\'));
  1015.         cSlashes++;
  1016.     }
  1017.     return pszUNC;
  1018. }
  1019. HRESULT CNetFolder::_ParseNetName(HWND hwnd, LPBC pbc, 
  1020.                                   LPCWSTR pwszName, ULONG* pchEaten,
  1021.                                   LPITEMIDLIST *ppidl, DWORD *pdwAttrib)
  1022. {
  1023.     HRESULT hres;
  1024.     NETRESOURCE nr = { 0 };
  1025.     struct _NRTEMP 
  1026.     {
  1027.         NETRESOURCE nr;
  1028.         TCHAR szBuffer[1024];
  1029.     } nrOut = { 0 };
  1030.     TCHAR szPath[MAX_PATH];
  1031.     TCHAR szTemp[MAX_PATH];
  1032.     DWORD dwres, dwbuf = SIZEOF(nrOut.szBuffer);
  1033.     LPTSTR pszServerShare = NULL;
  1034.     LPTSTR pszRestOfName = NULL;
  1035.     LPTSTR pszFakeRestOfName = NULL;
  1036.     LPTSTR pszRegItem = NULL;
  1037.     // validate the name before we start cracking it...
  1038.     if ( !PathIsUNCW(pwszName) )
  1039.         return HRESULT_FROM_WIN32(ERROR_BAD_NET_NAME);
  1040.     pszFakeRestOfName = _GetNameForParsing(pwszName, szPath, ARRAYSIZE(szPath), &pszRegItem);
  1041.     nr.lpRemoteName = szPath;
  1042.     nr.lpProvider = (LPTSTR)_GetProvider(NULL, szTemp, ARRAYSIZE(szTemp));
  1043.     nr.dwType = RESOURCETYPE_ANY;
  1044.     // if there is a regitem string then we must truncate at it, otherwise MPR will
  1045.     // try and parse it as part of the name
  1046.     if ( pszRegItem )
  1047.         *pszRegItem = TEXT('');
  1048. #ifdef WINNT
  1049.     dwres = WNetGetResourceInformation(&nr, &nrOut.nr, &dwbuf, &pszRestOfName);    
  1050.     if ( WN_SUCCESS != dwres )
  1051. #endif
  1052.     {
  1053.         TCHAR cT;
  1054.         LPTSTR pszTemp;
  1055.         // truncate the string at the \servershare to try and parse the name,
  1056.         // note at this point if MPR resolves the alias on a Novel server this could
  1057.         // get very confusing (eg. \strikefoobah may resolve to \stringbla,
  1058.         // yet our concept of what pszRestOfName will be wrong!
  1059.     
  1060.         if ( pszFakeRestOfName )
  1061.         {
  1062.             cT = *pszFakeRestOfName;
  1063.             *pszFakeRestOfName = TEXT('');
  1064.         }
  1065.         dwres = WNetGetResourceInformation(&nr, &nrOut.nr, &dwbuf, &pszTemp);    
  1066.         if ( dwres != WN_SUCCESS )
  1067.         {
  1068.             // we failed to get the net connection information using the truncated
  1069.             // string, therefore lets try and add a connection.  if that works
  1070.             // then we can assume that the connection exists, and we should
  1071.             // just fake up a NR output structure.
  1072.             dwres = WNetAddConnection3(NULL, &nr, NULL, NULL, 0);
  1073.             if ( dwres == WN_SUCCESS )
  1074.             {
  1075.                 nrOut.nr = nr;
  1076.                 nrOut.nr.dwDisplayType = RESOURCEDISPLAYTYPE_SHARE;
  1077.                 nrOut.nr.dwUsage = RESOURCEUSAGE_CONNECTABLE;
  1078.                 
  1079.                 StrCpy(szTemp, nrOut.nr.lpRemoteName);        // copy away the \servershare string
  1080.                 nrOut.nr.lpRemoteName = szTemp;
  1081.             }
  1082.         }
  1083.         if ( pszFakeRestOfName )
  1084.             *pszFakeRestOfName = cT;
  1085.         pszRestOfName = pszFakeRestOfName;
  1086.     }
  1087.     if ( WN_SUCCESS == dwres )
  1088.     {
  1089.         WCHAR wszRestOfName[MAX_PATH] = { 0 };
  1090.         if ( pszRestOfName )
  1091.             SHTCharToUnicode(pszRestOfName, wszRestOfName, ARRAYSIZE(wszRestOfName));
  1092.         // assume we are truncating at the regitem and parsing through
  1093.         if ( pszRegItem )
  1094.             pszRestOfName = pszRegItem;
  1095.         // attempt to convert the NETRESOURCE to a string to IDLISTS by walking the
  1096.         // parents, then add in Entire Network
  1097.         hres = _NetResToIDLists(&nrOut.nr, dwbuf, ppidl);
  1098.         if ( SUCCEEDED(hres) )
  1099.         {
  1100.             LPITEMIDLIST pidlT;
  1101.             hres = _CreateEntireNetwork(&pidlT, 0);     
  1102.             if ( SUCCEEDED(hres) )
  1103.             {
  1104.                 hres = SHILPrepend(pidlT, ppidl);
  1105.                 if ( FAILED(hres) )
  1106.                     ILFree(pidlT);
  1107.             }
  1108.         }
  1109.         // if we have a local string then lets continue to parse it by binding to 
  1110.         // its parent folder, otherwise we just want to return the attributes
  1111.         if ( SUCCEEDED(hres) )
  1112.             hres = _ParseRest(pbc, wszRestOfName, ppidl, pdwAttrib);
  1113.     }
  1114.     else
  1115.     {
  1116.         hres = HRESULT_FROM_WIN32(dwres);
  1117.     }
  1118.     if ( FAILED(hres) )
  1119.     {
  1120.         ILFree(*ppidl);
  1121.         *ppidl = NULL;
  1122.     }
  1123.     return hres;
  1124. }
  1125. //
  1126. // simple name parsing for the network paths.  this makes big assumptions about the
  1127. // \servershare format we are given, and the type of IDLISTs to return.
  1128. //
  1129. HRESULT CNetFolder::_AddUnknownIDList(DWORD dwDisplayType, LPITEMIDLIST *ppidl)
  1130. {
  1131.     HRESULT hres = E_OUTOFMEMORY;
  1132.     NETRESOURCE nr = { 0 };
  1133.     LPITEMIDLIST pidlT;
  1134.     nr.dwScope = RESOURCE_GLOBALNET;
  1135.     nr.dwDisplayType = dwDisplayType;
  1136.     nr.dwUsage = RESOURCEUSAGE_CONTAINER;
  1137.     nr.lpRemoteName = TEXT("");               // null name means fake item
  1138.     hres = _NetResToIDList(&nr, TRUE, FALSE, FALSE, &pidlT, 0);
  1139.     if ( SUCCEEDED(hres) )
  1140.     {
  1141.         hres = SHILAppend(pidlT, ppidl);
  1142.         if ( FAILED(hres) )
  1143.             ILFree(pidlT);
  1144.     }
  1145.     return hres;
  1146. }
  1147. HRESULT CNetFolder::_ParseSimple(LPBC pbc, LPWSTR pszName, LPITEMIDLIST* ppidl, DWORD* pdwAttributes)
  1148. {
  1149.     HRESULT hres = S_OK;
  1150.     NETRESOURCE nr = {0};
  1151.     TCHAR szName[MAX_PATH];
  1152.     LPTSTR psz;
  1153.     LPWSTR pszSlash;
  1154.     LPCITEMIDLIST pidlMapped;
  1155.     LPITEMIDLIST pidlT;
  1156.     USES_CONVERSION;
  1157.     *ppidl = NULL;
  1158.     if ( !PathIsUNCW(pszName) )
  1159.         return E_INVALIDARG;
  1160.     // see if there is a UNC root mapping for this, if so we are golden
  1161.     SHUnicodeToTChar(pszName, szName, ARRAYSIZE(szName));
  1162.     psz = (LPTSTR)NPTMapNameToPidl(szName, &pidlMapped);
  1163.     if (pidlMapped)
  1164.     {
  1165.         hres = SHILClone(_ILNext(pidlMapped), ppidl); // skip the MyNetPlaces part
  1166.         pszSlash = (LPWSTR)pszName + (psz - szName);
  1167.     }
  1168.     else
  1169.     {
  1170.         // create the entire network IDLIST, provider and domain elements
  1171.         hres = _CreateEntireNetwork(ppidl, 0);
  1172. #ifdef WINNT
  1173.         if ( SUCCEEDED(hres) )
  1174.             hres = _AddUnknownIDList(RESOURCEDISPLAYTYPE_NETWORK, ppidl);
  1175. #endif
  1176.         if ( SUCCEEDED(hres) )
  1177.             hres = _AddUnknownIDList(RESOURCEDISPLAYTYPE_DOMAIN, ppidl);
  1178.         // create the server IDLIST
  1179.         if ( SUCCEEDED(hres) )
  1180.         {
  1181.             pszSlash = StrChrW(pszName+2, L'\');
  1182.             if ( pszSlash )
  1183.                 *pszSlash = L'';
  1184.             nr.dwScope = RESOURCE_GLOBALNET;
  1185.             nr.dwDisplayType = RESOURCEDISPLAYTYPE_SERVER;
  1186.             nr.dwType = RESOURCETYPE_DISK;
  1187.             nr.dwUsage = RESOURCEUSAGE_CONTAINER;
  1188.             nr.lpRemoteName = W2T(pszName);
  1189.             hres = _NetResToIDList(&nr, FALSE, FALSE, FALSE, &pidlT, 0);
  1190.             if ( SUCCEEDED(hres) )
  1191.                 hres = SHILAppend(pidlT, ppidl);
  1192.             if ( pszSlash )
  1193.                 *pszSlash = L'\';
  1194.             // if we have a trailing  then lets add in the share part of the IDLIST
  1195.             if ( SUCCEEDED(hres) && pszSlash )
  1196.             {
  1197.                 pszSlash = StrChrW(pszSlash+1, L'\');
  1198.                 if (pszSlash)
  1199.                     *pszSlash = L'';
  1200.                 nr.dwDisplayType = RESOURCEDISPLAYTYPE_SHARE;
  1201.                 nr.dwUsage = RESOURCEUSAGE_CONNECTABLE;
  1202.                 nr.lpRemoteName = W2T(pszName);
  1203.                 hres = _NetResToIDList(&nr, FALSE, FALSE, FALSE, &pidlT, 0);
  1204.                 if ( SUCCEEDED(hres) )
  1205.                     hres = SHILAppend(pidlT, ppidl);
  1206.                 if (pszSlash)
  1207.                     *pszSlash = L'\';
  1208.             }
  1209.         }
  1210.     }
  1211.     if ( FAILED(hres) )
  1212.     {
  1213.         ILFree(*ppidl);
  1214.         *ppidl = NULL;
  1215.     }
  1216.     else
  1217.     {
  1218.         hres = _ParseRest(pbc, pszSlash, ppidl, pdwAttributes);
  1219.     }
  1220.     
  1221.     return hres;
  1222. }
  1223. // try parsing out the EntireNet or localised version.  if we find that object then try and
  1224. // parse through that to the regitems or other objects which live below.   this inturn
  1225. // will cause an instance of CNetFolder to be created to generate the other parsing names.
  1226. //
  1227. // returns:
  1228. //      S_FALSE         - not rest of net, try something else
  1229. //      S_OK            - was rest of net, use this
  1230. //      FAILED(hres)    - error result, return
  1231. HRESULT CNetRootFolder::_TryParseEntireNet(HWND hwnd, LPBC pbc, WCHAR *pwszName, LPITEMIDLIST *ppidl, DWORD *pdwAttributes)
  1232. {
  1233.     HRESULT hres = S_FALSE; // skip, not rest of net
  1234.  
  1235.     *ppidl = NULL;
  1236.     if ( !PathIsUNCW(pwszName) )
  1237.     {
  1238.         const WCHAR szEntireNetwork[] = L"EntireNetwork";
  1239.         WCHAR szRestOfNet[128];
  1240.         INT cchRestOfNet = LoadStringW(HINST_THISDLL, IDS_RESTOFNET, szRestOfNet, ARRAYSIZE(szRestOfNet));
  1241.        
  1242.         BOOL fRestOfNet = !StrCmpNIW(szRestOfNet, pwszName, cchRestOfNet);
  1243.         if ( !fRestOfNet && !StrCmpNIW(szEntireNetwork, pwszName, ARRAYSIZE(szEntireNetwork)-1) ) 
  1244.         {
  1245.             fRestOfNet = TRUE;
  1246.             cchRestOfNet = ARRAYSIZE(szEntireNetwork)-1;
  1247.         }
  1248.         
  1249.         if ( fRestOfNet )
  1250.         {
  1251.             hres = _CreateEntireNetwork(ppidl, 0);
  1252.             if (SUCCEEDED(hres))
  1253.             {
  1254.                 if (pdwAttributes)
  1255.                     GetAttributesOf(1, (LPCITEMIDLIST *)ppidl, pdwAttributes);
  1256.                 hres = S_OK;
  1257.             }
  1258.             // 
  1259.             // if we find extra stuff after the name then lets bind and continue the parsing
  1260.             // from there on.  this is needed so the we can access regitems burried inside
  1261.             // entire net.
  1262.             //
  1263.             // eg:  EntireNetwork\::{clsid}
  1264.             //
  1265.             if ( SUCCEEDED(hres) && 
  1266.                     (pwszName[cchRestOfNet] == L'\') && pwszName[cchRestOfNet+1] )
  1267.             {
  1268.                 IShellFolder *psfRestOfNet;
  1269.                 hres = BindToObject(*ppidl, NULL, IID_IShellFolder, (void **)&psfRestOfNet);
  1270.                 if ( SUCCEEDED(hres) )
  1271.                 {
  1272.                     LPITEMIDLIST pidl;
  1273.                     hres = psfRestOfNet->ParseDisplayName(hwnd, pbc, 
  1274.                                                           pwszName+cchRestOfNet+1, 
  1275.                                                           NULL,  
  1276.                                                           &pidl, 
  1277.                                                           pdwAttributes);
  1278.                     if  ( SUCCEEDED(hres) )
  1279.                         hres = SHILAppend(pidl, ppidl);                        
  1280.                     psfRestOfNet->Release();
  1281.                 }
  1282.             }
  1283.         }
  1284.     }
  1285.     return hres;
  1286. }
  1287. // CNetRootFolder::ParseDisplayname
  1288. //  - swtich based on the file system context to see if we need to do a simple parse or not,
  1289. //  - check for "EntireNet" and delegate parsing as required.
  1290. STDMETHODIMP CNetRootFolder::ParseDisplayName(HWND hwnd, LPBC pbc, WCHAR* pszName, ULONG* pchEaten,
  1291.                                               LPITEMIDLIST* ppidl, DWORD* pdwAttributes)
  1292. {
  1293.     HRESULT hres = _TryParseEntireNet(hwnd, pbc, pszName, ppidl, pdwAttributes);
  1294.     if (hres == S_OK)
  1295.         return hres;
  1296.     hres = SHIsFileSysBindCtx(pbc, NULL);
  1297.     if (S_OK == hres)
  1298.     {
  1299.         hres = _ParseSimple(pbc, pszName, ppidl, pdwAttributes);
  1300.     }
  1301.     else
  1302.     {
  1303.         hres = _ParseNetName(hwnd, pbc, pszName, pchEaten, ppidl, pdwAttributes);
  1304.         if ((HRESULT_FROM_WIN32(ERROR_BAD_NET_NAME) == hres))
  1305.         {
  1306.             IShellFolder2 *psfFiles;
  1307.             if (SUCCEEDED(v_GetFileFolder(&psfFiles)))
  1308.                 hres = psfFiles->ParseDisplayName(hwnd, pbc, pszName, pchEaten, ppidl, pdwAttributes);
  1309.         }
  1310.     }
  1311.     return hres;
  1312. }
  1313. STDMETHODIMP CNetRootFolder::EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList** ppenum)
  1314. {
  1315.     HRESULT hres = E_OUTOFMEMORY;
  1316.     ENUM_DATA *penet = (ENUM_DATA *)LocalAlloc(LPTR, SIZEOF(ENUM_DATA));
  1317.     if (penet)
  1318.     {
  1319.         // 
  1320.         // get the link enumerator on the first call into the net enumerator
  1321.         //
  1322.         AddRef();
  1323.         penet->pnf = this;
  1324.         penet->dwRemote |= RMF_GETLINKENUM;
  1325.         //
  1326.         // Do we enumerate the workgroup?
  1327.         //
  1328.     
  1329.         if ( !SHRestricted(REST_ENUMWORKGROUP) )
  1330.         {
  1331.             DWORD dwValue, dwSize = SIZEOF(dwValue);
  1332.             // Don't enumerate the workgroup, if the restriction says so
  1333.             penet->dwRemote |= RMF_FAKENETROOT;
  1334.             // Check the WNet policy to see if we should be showing the entire net object, if not 
  1335.             // mark it as shown so that the enumerator doesn't return it.
  1336.             if ( ERROR_SUCCESS == SHGetValue(HKEY_CURRENT_USER, WNET_POLICY_KEY, TEXT("NoEntireNetwork"), 
  1337.                                                                             NULL, (void *)&dwValue, &dwSize) )
  1338.             {
  1339.                 if ( dwValue )
  1340.                     penet->dwRemote |= RMF_ENTIRENETSHOWN;
  1341.             }
  1342.         }
  1343.         //
  1344.         // if we are not faking the net root then lets call _OpenEnum, otherwise lets ignore
  1345.         //
  1346.         if (!(penet->dwRemote & RMF_FAKENETROOT))
  1347.         {
  1348.             DWORD err = _OpenEnum(hwnd, grfFlags, NULL, &penet->hEnum);
  1349.             // Always add the remote folder to the 'hood
  1350.             if (WN_SUCCESS != err)
  1351.             {
  1352.                 // Yes; still show remote anyway (only)
  1353.                 penet->dwRemote |= RMF_SHOWREMOTE;
  1354.             }
  1355.             else
  1356.             {
  1357.                 // No; allow everything to be enumerated in the 'hood.
  1358.                 penet->dwRemote |= RMF_CONTEXT;
  1359.             }
  1360.         }
  1361.         penet->grfFlags = grfFlags;
  1362.         hres = SHCreateEnumObjects(hwnd, penet, EnumCallBack, ppenum);
  1363.     }
  1364.     return hres;
  1365. }
  1366. STDMETHODIMP CNetRootFolder::BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void** ppvOut)
  1367. {
  1368.     HRESULT hres;
  1369.     if (NET_IsValidID(pidl))
  1370.         hres = CNetFolder::BindToObject(pidl, pbc, riid, ppvOut);
  1371.     else
  1372.     {
  1373.         IShellFolder2* psfFiles;
  1374.         hres = v_GetFileFolder(&psfFiles);
  1375.         if (SUCCEEDED(hres))
  1376.             hres = psfFiles->BindToObject(pidl, pbc, riid, ppvOut);
  1377.     }
  1378.     return hres;
  1379. }
  1380. STDMETHODIMP CNetRootFolder::CompareIDs(LPARAM iCol, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  1381. {
  1382.     HRESULT hres = E_INVALIDARG;
  1383.     // First obtain the collate type of the pidls and their respective
  1384.     // collate order.
  1385.     LONG iColateType1 = _GetFilePIDLType(pidl1);
  1386.     LONG iColateType2 = _GetFilePIDLType(pidl2);
  1387.     if (iColateType1 == iColateType2) 
  1388.     {
  1389.         // pidls are of same type.
  1390.         if (iColateType1 == _HOOD_COL_FILE)  // two file system pidls
  1391.         {
  1392.             SHCOLUMNID     scid;
  1393.             IShellFolder2* psfFiles;
  1394.             
  1395.             if (SUCCEEDED(v_GetFileFolder(&psfFiles)) &&
  1396.                 SUCCEEDED(MapColumnToSCID((UINT)iCol & SHCIDS_COLUMNMASK, &scid)))
  1397.             {
  1398.                 if (IsEqualSCID(scid, SCID_NAME))
  1399.                 {
  1400.                     //  Name is a no-brainer; delegate directly to the fs folder
  1401.                     return psfFiles->CompareIDs(iCol, pidl1, pidl2);
  1402.                 }
  1403.                 else
  1404.                 {
  1405.                     //  Other columns are extended columns.   Let FS folder
  1406.                     //  acquire the extended column data abstractly from handlers,
  1407.                     //  then we'll compare the values ourselves.
  1408.                     //  BUGBUG [scotthan]: We're limited to VT_BSTR values.
  1409.                     VARIANT var1 = {0}, var2 = {0};
  1410.                     if (FAILED(psfFiles->GetDetailsEx(pidl1, &scid, &var1)) || var1.vt != VT_BSTR)
  1411.                         VariantClear(&var1);
  1412.                     if (FAILED(psfFiles->GetDetailsEx(pidl2, &scid, &var2)) || var2.vt != VT_BSTR)
  1413.                         VariantClear(&var2);
  1414.                     if (var1.bstrVal && var2.bstrVal) 
  1415.                         hres = ResultFromShort(StrCmpW(var1.bstrVal, var2.bstrVal));
  1416.                     else if (var1.bstrVal)
  1417.                         hres = ResultFromShort(1);
  1418.                     else if (var2.bstrVal)
  1419.                         hres = ResultFromShort(-1);
  1420.                     else
  1421.                         hres = ResultFromShort(0);
  1422.                     VariantClear(&var1);
  1423.                     VariantClear(&var2);
  1424.                 }
  1425.             }
  1426.         }
  1427.         else 
  1428.         {
  1429.             // pidls same and are not of type file,
  1430.             // so both must be a type understood
  1431.             // by the CNetwork class - pass on to compare.
  1432.             return CNetFolder::CompareIDs(iCol, pidl1, pidl2);
  1433.         }
  1434.     }
  1435.     else 
  1436.     {
  1437.         // ensure that entire network ends up at the head of the list
  1438.         LPCIDNETRESOURCE pidn1 = NET_IsValidID(pidl1);
  1439.         LPCIDNETRESOURCE pidn2 = NET_IsValidID(pidl2);
  1440.         if ( (pidn1 && (NET_GetDisplayType(pidn1) == RESOURCEDISPLAYTYPE_ROOT)) ||
  1441.               (pidn2 && (NET_GetDisplayType(pidn2) == RESOURCEDISPLAYTYPE_ROOT)) )
  1442.         {
  1443.             if ( iColateType1 == _HOOD_COL_FILE )
  1444.                 return ResultFromShort(1);
  1445.             else
  1446.                 return ResultFromShort(-1);
  1447.         }
  1448.         // pidls are not of same type, so have already been correctly
  1449.         // collated (consequently, sorting is first by type and
  1450.         // then by subfield).
  1451.         return ResultFromShort(((iColateType2 - iColateType1) > 0) ? 1 : -1);
  1452.     }
  1453.     return hres;
  1454. }
  1455. STDMETHODIMP CNetRootFolder::CreateViewObject(HWND hwnd, REFIID riid, void** ppvOut)
  1456. {
  1457.     ASSERT(ILIsEqual(_pidl, (LPCITEMIDLIST)&c_idlNet));
  1458.     if (IsEqualIID(riid, IID_IDropTarget))
  1459.     {
  1460.         return CIDLDropTarget_Create(hwnd, &c_CNetRootTargetVtbl, _pidl, (IDropTarget**) ppvOut);
  1461.     }
  1462.     return CNetFolder::CreateViewObject(hwnd, riid, ppvOut);
  1463. }
  1464. STDMETHODIMP CNetRootFolder::GetAttributesOf(UINT cidl, LPCITEMIDLIST* apidl, ULONG* prgfInOut)
  1465. {
  1466.     HRESULT hres;
  1467.     if (cidl == 0)
  1468.     {
  1469.         //
  1470.         // The user can rename links in the hood.
  1471.         //
  1472.         hres = CNetFolder::GetAttributesOf(cidl, apidl, prgfInOut);
  1473.         *prgfInOut |= SFGAO_CANRENAME;
  1474.     }
  1475.     else
  1476.     {
  1477.         hres = Multi_GetAttributesOf((IShellFolder2*) this, cidl, apidl, prgfInOut, GAOCallbackNetRoot);
  1478.     }
  1479.     return hres;
  1480. }
  1481. STDMETHODIMP CNetRootFolder::GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD dwFlags, STRRET* pStrRet)
  1482. {
  1483.     HRESULT hres;
  1484.     if (NET_IsValidID(pidl) || IsSelf(1, &pidl))
  1485.     {
  1486.         hres = CNetFolder::GetDisplayNameOf(pidl, dwFlags, pStrRet);
  1487.     }
  1488.     else
  1489.     {
  1490.         IShellFolder2* psfFiles;
  1491.         hres = v_GetFileFolder(&psfFiles);
  1492.         if (SUCCEEDED(hres))
  1493.             hres = psfFiles->GetDisplayNameOf(pidl, dwFlags, pStrRet);
  1494.     }
  1495.     return hres;
  1496. }
  1497. STDMETHODIMP CNetRootFolder::SetNameOf(HWND hwnd, LPCITEMIDLIST pidl, LPCOLESTR lpszName,
  1498.                                        DWORD dwRes, LPITEMIDLIST* ppidl)
  1499. {
  1500.     HRESULT hres;
  1501.     if (NET_IsValidID(pidl))
  1502.     {
  1503.         hres = CNetFolder::SetNameOf(hwnd, pidl, lpszName, dwRes, ppidl);
  1504.     }
  1505.     else
  1506.     {
  1507.         IShellFolder2* psfFiles;
  1508.         hres = v_GetFileFolder(&psfFiles);
  1509.         if (SUCCEEDED(hres))
  1510.             hres = psfFiles->SetNameOf(hwnd, pidl, lpszName, dwRes, ppidl);
  1511.     }
  1512.     return hres;
  1513. }
  1514. STDMETHODIMP CNetRootFolder::GetUIObjectOf(HWND hwnd, UINT cidl, LPCITEMIDLIST* apidl,
  1515.                                            REFIID riid, UINT* prgfInOut, void** ppvOut)
  1516. {
  1517.     HRESULT hres = E_INVALIDARG;
  1518.     LPCIDNETRESOURCE pidn = cidl ? NET_IsValidID(apidl[0]) : NULL;
  1519.     BOOL fStriped = FALSE;
  1520.     ASSERT(ILIsEqual(_pidl, (LPCITEMIDLIST)&c_idlNet));
  1521.     *ppvOut = NULL;
  1522.     if (pidn)
  1523.     {
  1524.         fStriped = _MakeStripToLikeKinds(&cidl, &apidl, TRUE);
  1525.         if (IsEqualIID(riid, IID_IContextMenu))
  1526.         {
  1527.             HKEY ahkeys[NKID_COUNT];
  1528.             hres = _OpenKeys(pidn, ahkeys);
  1529.             if (SUCCEEDED(hres))
  1530.             {
  1531.                 IShellFolder* psfOuter;
  1532.                 hres = QueryInterface(IID_IShellFolder, (void**) &psfOuter);
  1533.                 if (SUCCEEDED(hres))
  1534.                 {
  1535.                     hres = CDefFolderMenu_Create2(_pidl, hwnd, cidl, apidl, 
  1536.                                                   psfOuter, _GetCallbackType(pidn),
  1537.                                                   ARRAYSIZE(ahkeys), ahkeys, (IContextMenu**) ppvOut);
  1538.                     psfOuter->Release();
  1539.                 }
  1540.                 SHRegCloseKeys(ahkeys, ARRAYSIZE(ahkeys));
  1541.             }
  1542.         }
  1543.         else
  1544.             hres = CNetFolder::GetUIObjectOf(hwnd, cidl, apidl, riid, prgfInOut, ppvOut);
  1545.     }
  1546.     else
  1547.     {
  1548.         fStriped = _MakeStripToLikeKinds(&cidl, &apidl, FALSE);
  1549.         IShellFolder2* psfFiles;
  1550.         hres = v_GetFileFolder(&psfFiles);
  1551.         if (SUCCEEDED(hres))
  1552.             hres = psfFiles->GetUIObjectOf(hwnd, cidl, apidl, riid, prgfInOut, ppvOut);
  1553.     }
  1554.     if (fStriped)
  1555.         LocalFree((HLOCAL)apidl);
  1556.     return hres;
  1557. }
  1558. STDMETHODIMP CNetRootFolder::GetClassID(CLSID* pCLSID)
  1559. {
  1560.     *pCLSID = CLSID_NetworkPlaces;
  1561.     return S_OK;
  1562. }
  1563. STDMETHODIMP CNetRootFolder::Initialize(LPCITEMIDLIST pidl)
  1564. {
  1565.     ASSERT(ILIsEqual(pidl, (LPCITEMIDLIST)&c_idlNet));
  1566.     // Only allow the Net root on the desktop
  1567.     HRESULT hres = !IsIDListInNameSpace(pidl, &CLSID_NetworkPlaces) || !ILIsEmpty(_ILNext(pidl)) ? E_INVALIDARG : S_OK;
  1568.     if (SUCCEEDED(hres))
  1569.     {
  1570.         hres = SHILClone(pidl, &_pidl);
  1571.     }
  1572.     return hres;
  1573. }
  1574. LONG CNetFolder::_GetFilePIDLType(LPCITEMIDLIST pidl)
  1575. {
  1576.     if (NET_IsValidID(pidl)) 
  1577.     {
  1578.         if (NET_IsRemoteFld((LPIDNETRESOURCE)pidl)) 
  1579.         {
  1580.             return _HOOD_COL_REMOTE;
  1581.         }
  1582.         if (NET_GetDisplayType((LPIDNETRESOURCE)pidl) == RESOURCEDISPLAYTYPE_ROOT) 
  1583.         {
  1584.             return _HOOD_COL_RON;
  1585.         }
  1586.         return _HOOD_COL_NET;
  1587.     }
  1588.     return _HOOD_COL_FILE;
  1589. }
  1590. /* This function adds a provider name to an IDLIST that doesn't already have one. */
  1591. /* A new IDLIST pointer is returned; the old pointer is no longer valid. */
  1592. LPITEMIDLIST CNetFolder::_AddProviderToPidl(LPITEMIDLIST pidl, LPCTSTR lpProvider)
  1593. {
  1594.     LPIDNETRESOURCE pidn = (LPIDNETRESOURCE)pidl;
  1595.     if ( !NET_FHasProvider(pidn) )
  1596.     {
  1597.         LPITEMIDLIST pidlres;
  1598.         TCHAR szName[MAX_PATH], szComment[MAX_PATH];
  1599.         // construct a new IDLIST preserving the name, comment and other information
  1600.         NET_CopyResName(pidn, szName, ARRAYSIZE(szName));
  1601.         NET_CopyComment(pidn, szComment, ARRAYSIZE(szComment));
  1602.         HRESULT hres = _CreateNetIDList(pidn, szName, lpProvider, szComment[0] ? szComment:NULL, &pidlres);
  1603.         if ( SUCCEEDED(hres) && !ILIsEmpty(_ILNext(pidl)) )
  1604.         {
  1605.             LPITEMIDLIST pidlT;
  1606.             hres = SHILCombine(pidlres, _ILNext(pidl), &pidlT);
  1607.             if ( SUCCEEDED(hres) )
  1608.             {
  1609.                 ILFree(pidlres);
  1610.                 pidlres = pidlT;
  1611.             }
  1612.         }
  1613.         // if we have a result, free the old PIDL and return the new
  1614.         if ( SUCCEEDED(hres) )
  1615.         {
  1616.             ILFree(pidl);
  1617.             pidl = pidlres;
  1618.         }
  1619.     }
  1620.     return pidl;
  1621. }
  1622. BOOL CNetFolder::_MakeStripToLikeKinds(UINT *pcidl, LPCITEMIDLIST **papidl, BOOL fNetObjects)
  1623. {
  1624.     LPITEMIDLIST *apidl = (LPITEMIDLIST*)*papidl;
  1625.     int iidl, cidl = *pcidl;
  1626.     for (iidl = 0; iidl < cidl; iidl++)
  1627.     {
  1628.         if ((NET_IsValidID(apidl[iidl]) != NULL) != fNetObjects)
  1629.         {
  1630.             int cpidlHomo;
  1631.             LPCITEMIDLIST *apidlHomo = (LPCITEMIDLIST *)LocalAlloc(LPTR, SIZEOF(LPCITEMIDLIST) * cidl);
  1632.             if (!apidlHomo)
  1633.                 return FALSE;
  1634.             cpidlHomo = 0;
  1635.             for (iidl = 0; iidl < cidl; iidl++)
  1636.             {
  1637.                 if ((NET_IsValidID(apidl[iidl]) != NULL) == fNetObjects)
  1638.                     apidlHomo[cpidlHomo++] = apidl[iidl];
  1639.             }
  1640.             // Setup to use the stripped version of the pidl array...
  1641.             *pcidl = cpidlHomo;
  1642.             *papidl = apidlHomo;
  1643.             return TRUE;
  1644.         }
  1645.     }
  1646.     return FALSE;
  1647. }
  1648. HRESULT CNetRootFolder::v_GetFileFolder(IShellFolder2 **psf)
  1649. {
  1650.     HRESULT hres;
  1651.     hres = SHCacheTrackingFolder((LPCITEMIDLIST)&c_idlNet, CSIDL_NETHOOD, &_psfFiles);
  1652.     *psf = _psfFiles;
  1653.     return hres;
  1654. }