#include "shellprv.h" #include "caggunk.h" #include "views.h" #include "ids.h" #include "shitemid.h" #include "fstreex.h" #include "clsobj.h" #include "datautil.h" #include "winnetp.h" // RESOURCE_SHAREABLE #include "prop.h" #include "infotip.h" #include "basefvcb.h" #include "netview.h" #include "printer.h" #include "fsdata.h" #include "idldrop.h" #include "enumidlist.h" #include "util.h" #include #define WNNC_NET_LARGEST WNNC_NET_SYMFONET HRESULT CNetRootDropTarget_CreateInstance(HWND hwnd, LPCITEMIDLIST pidl, IDropTarget **ppdropt); class CNetData : public CFSIDLData { public: CNetData(LPCITEMIDLIST pidlFolder, UINT cidl, LPCITEMIDLIST apidl[]): CFSIDLData(pidlFolder, cidl, apidl, NULL) { }; // IDataObject methods overwrite STDMETHODIMP GetData(FORMATETC *pFmtEtc, STGMEDIUM *pstm); STDMETHODIMP QueryGetData(FORMATETC *pFmtEtc); protected: STDMETHODIMP GetHDrop(FORMATETC *pformatetcIn, STGMEDIUM *pmedium); }; // {22BEB58B-0794-11d2-A4AA-00C04F8EEB3E} const GUID CLSID_CNetFldr = { 0x22beb58b, 0x794, 0x11d2, 0xa4, 0xaa, 0x0, 0xc0, 0x4f, 0x8e, 0xeb, 0x3e }; // idlist.c STDAPI_(void) StrRetFormat(STRRET *pStrRet, LPCITEMIDLIST pidlRel, LPCTSTR pszTemplate, LPCTSTR pszAppend); // in stdenum.cpp STDAPI_(void *) CStandardEnum_CreateInstance(REFIID riid, BOOL bInterfaces, int cElement, int cbElement, void *rgElements, void (WINAPI * pfnCopyElement)(void *, const void *, DWORD)); // is a \\server\printer object BOOL _IsPrintShare(LPCIDNETRESOURCE pidn) { return NET_GetDisplayType(pidn) == RESOURCEDISPLAYTYPE_SHARE && NET_GetType(pidn) == RESOURCETYPE_PRINT; } // column information enum { ICOL_NAME = 0, ICOL_COMMENT, ICOL_COMPUTERNAME, ICOL_NETWORKLOCATION }; const COLUMN_INFO s_net_cols[] = { DEFINE_COL_STR_ENTRY(SCID_NAME, 30, IDS_NAME_COL), DEFINE_COL_STR_ENTRY(SCID_Comment, 30, IDS_EXCOL_COMMENT), DEFINE_COL_STR_ENTRY(SCID_COMPUTERNAME, 30, IDS_EXCOL_COMPUTER), DEFINE_COL_STR_ENTRY(SCID_NETWORKLOCATION, 30, IDS_NETWORKLOCATION), }; #define MAX_ICOL_NETFOLDER (ICOL_COMMENT+1) #define MAX_ICOL_NETROOT (ICOL_NETWORKLOCATION+1) STDAPI CNetwork_DFMCallBackBG(IShellFolder *psf, HWND hwnd, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam); class CNetFolderViewCB; class CNetFolderEnum; class CNetFolder : public CAggregatedUnknown, public IShellFolder2, public IPersistFolder3, public IShellIconOverlay { friend CNetFolderViewCB; friend CNetFolderEnum; public: // IUnknown STDMETHODIMP QueryInterface(REFIID riid, void ** ppvObj) { return CAggregatedUnknown::QueryInterface(riid, ppvObj); }; STDMETHODIMP_(ULONG) AddRef(void) { return CAggregatedUnknown::AddRef(); }; STDMETHODIMP_(ULONG) Release(void) { return CAggregatedUnknown::Release(); }; // IShellFolder STDMETHODIMP ParseDisplayName(HWND hwnd, LPBC pbc, LPOLESTR lpszDisplayName, ULONG* pchEaten, LPITEMIDLIST* ppidl, ULONG* pdwAttributes); STDMETHODIMP EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList ** ppenumIDList); STDMETHODIMP BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv); STDMETHODIMP BindToStorage(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppvObj); STDMETHODIMP CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2); STDMETHODIMP CreateViewObject(HWND hwndOwner, REFIID riid, void **ppv); STDMETHODIMP GetAttributesOf(UINT cidl, LPCITEMIDLIST* apidl, ULONG* rgfInOut); STDMETHODIMP GetUIObjectOf(HWND hwndOwner, UINT cidl, LPCITEMIDLIST* apidl, REFIID riid, UINT* prgfInOut, void **ppv); STDMETHODIMP GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD uFlags, LPSTRRET lpName); STDMETHODIMP SetNameOf(HWND hwnd, LPCITEMIDLIST pidl, LPCOLESTR lpszName, DWORD uFlags, LPITEMIDLIST* ppidlOut); // IShellFolder2 STDMETHODIMP GetDefaultSearchGUID(GUID *pGuid); STDMETHODIMP EnumSearches(IEnumExtraSearch **ppenum); STDMETHODIMP GetDefaultColumn(DWORD dwRes, ULONG* pSort, ULONG* pDisplay); STDMETHODIMP GetDefaultColumnState(UINT iColumn, DWORD* pbState) { return _GetDefaultColumnState(MAX_ICOL_NETFOLDER, iColumn, pbState); } STDMETHODIMP GetDetailsEx(LPCITEMIDLIST pidl, const SHCOLUMNID* pscid, VARIANT* pv); STDMETHODIMP GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS* pDetails) { return _GetDetailsOf(MAX_ICOL_NETFOLDER, pidl, iColumn, pDetails); } STDMETHODIMP MapColumnToSCID(UINT iColumn, SHCOLUMNID* pscid) { return _MapColumnToSCID(MAX_ICOL_NETFOLDER, iColumn, pscid); } // IPersist STDMETHODIMP GetClassID(CLSID* pClassID); // IPersistFolder STDMETHODIMP Initialize(LPCITEMIDLIST pidl); // IPersistFolder2 STDMETHODIMP GetCurFolder(LPITEMIDLIST* ppidl); // IPersistFolder3 STDMETHOD(InitializeEx)(IBindCtx *pbc, LPCITEMIDLIST pidlRoot, const PERSIST_FOLDER_TARGET_INFO *ppfai); STDMETHOD(GetFolderTargetInfo)(PERSIST_FOLDER_TARGET_INFO *ppfai); // *** IShellIconOverlay methods*** STDMETHOD(GetOverlayIndex)(LPCITEMIDLIST pidl, int * pIndex); STDMETHOD(GetOverlayIconIndex)(LPCITEMIDLIST pidl, int * pIconIndex); protected: CNetFolder(IUnknown* punkOuter); ~CNetFolder(); virtual HRESULT v_GetFileFolder(IShellFolder2 **ppsfFiles) { *ppsfFiles = NULL; return E_NOTIMPL; }; // used by the CAggregatedUnknown stuff HRESULT v_InternalQueryInterface(REFIID riid, void **ppvObj); HRESULT _OpenKeys(LPCIDNETRESOURCE pidn, HKEY ahkeys[]); LPCTSTR _GetProvider(LPCIDNETRESOURCE pidn, IBindCtx *pbc, LPTSTR pszProvider, UINT cchProvider); DWORD _OpenEnum(HWND hwnd, DWORD grfFlags, LPNETRESOURCE pnr, HANDLE *phEnum); static HRESULT _CreateNetIDList(LPIDNETRESOURCE pidnIn, LPCTSTR pszName, LPCTSTR pszProvider, LPCTSTR pszComment, LPITEMIDLIST *ppidl); static HRESULT _NetResToIDList(NETRESOURCE *pnr, BOOL fKeepNullRemoteName, BOOL fKeepProviderName, BOOL fKeepComment, LPITEMIDLIST *ppidl); static HRESULT _CreateEntireNetwork(LPITEMIDLIST *ppidl); static HRESULT _CreateEntireNetworkFullIDList(LPITEMIDLIST *ppidl); LPTSTR _GetNameForParsing(LPCWSTR pwszName, LPTSTR pszBuffer, INT cchBuffer, LPTSTR *ppszRegItem); HRESULT _ParseRest(LPBC pbc, LPCWSTR pszRest, LPITEMIDLIST* ppidl, DWORD* pdwAttributes); HRESULT _AddUnknownIDList(DWORD dwDisplayType, LPITEMIDLIST *ppidl); HRESULT _ParseSimple(LPBC pbc, LPWSTR pszName, LPITEMIDLIST* ppidl, DWORD* pdwAttributes); HRESULT _NetResToIDLists(NETRESOURCE *pnr, DWORD dwbuf, LPITEMIDLIST *ppidl); HRESULT _ParseNetName(HWND hwnd, LPBC pbc, LPCWSTR pwszName, ULONG* pchEaten, LPITEMIDLIST* ppidl, DWORD* pdwAttributes); LONG _GetFilePIDLType(LPCITEMIDLIST pidl); LPITEMIDLIST _AddProviderToPidl(LPITEMIDLIST pidl, LPCTSTR lpProvider); BOOL _MakeStripToLikeKinds(UINT *pcidl, LPCITEMIDLIST **papidl, BOOL fNetObjects); HRESULT _GetDefaultColumnState(UINT cColumns, UINT iColumn, DWORD* pdwState); HRESULT _GetDetailsOf(UINT cColumns, LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *pDetails); HRESULT _MapColumnToSCID(UINT cColumns, UINT iColumn, SHCOLUMNID* pscid); LPFNDFMCALLBACK _GetCallbackType(LPCIDNETRESOURCE pidn) { return _IsPrintShare(pidn) ? &PrinterDFMCallBack : &DFMCallBack; }; static HRESULT CALLBACK _AttributesCallbackRoot(IShellFolder2* psf, LPCITEMIDLIST pidl, ULONG* prgfInOut); LPITEMIDLIST _pidl; LPITEMIDLIST _pidlTarget; // pidl of where the folder is in the namespace LPCIDNETRESOURCE _pidnForProvider; // optional provider for this container... LPTSTR _pszResName; // optional resource name of this container UINT _uDisplayType; // display type of the folder IShellFolder2* _psfFiles; IUnknown* _punkReg; private: HRESULT _CreateInstance(LPCITEMIDLIST pidlAbs, LPCITEMIDLIST pidlTarget, UINT uDisplayType, LPCIDNETRESOURCE pidnForProvider, LPCTSTR pszResName, REFIID riid, void **ppv); friend HRESULT CNetwork_DFMCallBackBG(IShellFolder *psf, HWND hwnd, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam); static DWORD CALLBACK _PropertiesThreadProc(void *pv); static HRESULT DFMCallBack(IShellFolder* psf, HWND hwnd, IDataObject* pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam); static HRESULT PrinterDFMCallBack(IShellFolder* psf, HWND hwnd, IDataObject* pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam); static HRESULT CALLBACK _AttributesCallback(IShellFolder2* psf, LPCITEMIDLIST pidl, ULONG* prgfInOut); BOOL _GetPathForShare(LPCIDNETRESOURCE pidn, LPTSTR pszPath); HRESULT _GetPathForItem(LPCIDNETRESOURCE pidn, LPTSTR pszPath); HRESULT _GetPathForItemW(LPCIDNETRESOURCE pidn, LPWSTR pszPath); HRESULT _CreateFolderForItem(LPBC pbc, LPCITEMIDLIST pidl, LPCITEMIDLIST pidlTarget, LPCIDNETRESOURCE pidnForProvider, REFIID riid, void **ppv); HRESULT _GetFormatName(LPCIDNETRESOURCE pidn, STRRET* pStrRet); HRESULT _GetIconOverlayInfo(LPCIDNETRESOURCE pidn, int *pIndex, DWORD dwFlags); HKEY _OpenProviderTypeKey(LPCIDNETRESOURCE pidn); HKEY _OpenProviderKey(LPCIDNETRESOURCE pidn); static void WINAPI _CopyEnumElement(void* pDest, const void* pSource, DWORD dwSize); HRESULT _GetNetResource(LPCIDNETRESOURCE pidn, NETRESOURCEW* pnr, int cb); }; class CNetRootFolder : public CNetFolder { public: // IUnknown STDMETHODIMP QueryInterface(REFIID riid, void ** ppvObj) { return CNetFolder::QueryInterface(riid, ppvObj); }; STDMETHODIMP_(ULONG) AddRef(void) { return CNetFolder::AddRef(); }; STDMETHODIMP_(ULONG) Release(void) { return CNetFolder::Release(); }; // IShellFolder STDMETHODIMP ParseDisplayName(HWND hwnd, LPBC pbc, LPOLESTR lpszDisplayName, ULONG* pchEaten, LPITEMIDLIST* ppidl, ULONG* pdwAttributes); STDMETHODIMP EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList ** ppenumIDList); STDMETHODIMP BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv); STDMETHODIMP BindToStorage(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppvObj) { return CNetFolder::BindToStorage(pidl, pbc, riid, ppvObj); }; STDMETHODIMP CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2); STDMETHODIMP CreateViewObject(HWND hwndOwner, REFIID riid, void **ppv); STDMETHODIMP GetAttributesOf(UINT cidl, LPCITEMIDLIST* apidl, ULONG* rgfInOut); STDMETHODIMP GetUIObjectOf(HWND hwndOwner, UINT cidl, LPCITEMIDLIST* apidl, REFIID riid, UINT* prgfInOut, void **ppv); STDMETHODIMP GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD uFlags, LPSTRRET lpName); STDMETHODIMP SetNameOf(HWND hwnd, LPCITEMIDLIST pidl, LPCOLESTR lpszName, DWORD uFlags, LPITEMIDLIST* ppidlOut); // IShellFolder2 STDMETHODIMP GetDefaultSearchGUID(GUID *pGuid) { return CNetFolder::GetDefaultSearchGUID(pGuid); }; STDMETHODIMP EnumSearches(IEnumExtraSearch **ppenum) { return CNetFolder::EnumSearches(ppenum); }; STDMETHODIMP GetDefaultColumn(DWORD dwRes, ULONG* pSort, ULONG* pDisplay) { return CNetFolder::GetDefaultColumn(dwRes, pSort, pDisplay); }; STDMETHODIMP GetDefaultColumnState(UINT iColumn, DWORD* pbState) { return _GetDefaultColumnState(MAX_ICOL_NETROOT, iColumn, pbState); } // +1 for <= check STDMETHODIMP GetDetailsEx(LPCITEMIDLIST pidl, const SHCOLUMNID* pscid, VARIANT* pv) { return CNetFolder::GetDetailsEx(pidl, pscid, pv); }; STDMETHODIMP GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS* pDetails) { return _GetDetailsOf(MAX_ICOL_NETROOT, pidl, iColumn, pDetails); }; STDMETHODIMP MapColumnToSCID(UINT iColumn, SHCOLUMNID* pscid) { return _MapColumnToSCID(MAX_ICOL_NETROOT, iColumn, pscid); } // IPersist STDMETHODIMP GetClassID(CLSID* pClassID); // IPersistFolder STDMETHODIMP Initialize(LPCITEMIDLIST pidl); // IPersistFolder2 STDMETHODIMP GetCurFolder(LPITEMIDLIST* ppidl) { return CNetFolder::GetCurFolder(ppidl); }; // IPersistFolder3 STDMETHOD(InitializeEx)(IBindCtx *pbc, LPCITEMIDLIST pidlRoot, const PERSIST_FOLDER_TARGET_INFO *ppfai) { return CNetFolder::InitializeEx(pbc, pidlRoot, ppfai); }; STDMETHOD(GetFolderTargetInfo)(PERSIST_FOLDER_TARGET_INFO *ppfai) { return CNetFolder::GetFolderTargetInfo(ppfai); }; protected: CNetRootFolder(IUnknown* punkOuter) : CNetFolder(punkOuter) { }; ~CNetRootFolder() { ASSERT(NULL != _spThis); _spThis = NULL; }; BOOL v_HandleDelete(PLONG pcRef); HRESULT v_GetFileFolder(IShellFolder2 **ppsfFiles); private: HRESULT _TryParseEntireNet(HWND hwnd, LPBC pbc, WCHAR *pwszName, LPITEMIDLIST *ppidl, DWORD *pdwAttributes); friend HRESULT CNetwork_CreateInstance(IUnknown* punkOuter, REFIID riid, void **ppv); static CNetRootFolder* _spThis; }; class CNetFolderViewCB : public CBaseShellFolderViewCB { public: CNetFolderViewCB(CNetFolder *pFolder); // IShellFolderViewCB STDMETHODIMP RealMessage(UINT uMsg, WPARAM wParam, LPARAM lParam); private: ~CNetFolderViewCB(); HRESULT OnINVOKECOMMAND(DWORD pv, UINT wP); HRESULT OnGETHELPTEXT(DWORD pv, UINT wPl, UINT wPh, LPTSTR lP); HRESULT OnREFRESH(DWORD pv, BOOL fPreRefresh); HRESULT OnDELAYWINDOWCREATE(DWORD pv, HWND hwnd); HRESULT OnGETCOLSAVESTREAM(DWORD pv, WPARAM wP, IStream **pps); HRESULT OnDEFITEMCOUNT(DWORD pv, UINT *pnItems); HRESULT OnGetZone(DWORD pv, DWORD * pdwZone); HRESULT OnEnumeratedItems(DWORD pv, UINT celt, LPCITEMIDLIST *rgpidl); HRESULT OnDefViewMode(DWORD pv, FOLDERVIEWMODE* pvm); HRESULT OnGetDeferredViewSettings(DWORD pv, SFVM_DEFERRED_VIEW_SETTINGS* pSettings); BOOL _GetProviderKeyName(LPTSTR pszName, UINT uNameLen); BOOL _EntireNetworkAvailable(); CNetFolder *_pFolder; UINT _cItems; // Web View implementation HRESULT OnGetWebViewLayout(DWORD pv, UINT uViewMode, SFVM_WEBVIEW_LAYOUT_DATA* pData); HRESULT OnGetWebViewContent(DWORD pv, SFVM_WEBVIEW_CONTENT_DATA* pData); HRESULT OnGetWebViewTasks(DWORD pv, SFVM_WEBVIEW_TASKSECTION_DATA* pTasks); public: static HRESULT _CanShowHNW(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState); static HRESULT _CanViewComputersNearMe(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState); static HRESULT _CanSearchActiveDirectory(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState); static HRESULT _DoRunDll32(LPTSTR pszParameters); // helper to do a ShellExecute of RunDll32. static HRESULT _OnViewNetConnections(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc); static HRESULT _OnAddNetworkPlace(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc) { return _DoRunDll32(TEXT("netplwiz.dll,AddNetPlaceRunDll")); } static HRESULT _OnHomeNetworkWizard(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc) { return _DoRunDll32(TEXT("hnetwiz.dll,HomeNetWizardRunDll")); } static HRESULT _OnViewComputersNearMe(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc); static HRESULT _OnSearchActiveDirectory(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc) { return _DoRunDll32(TEXT("dsquery.dll,OpenQueryWindow")); } }; #define NETFLDR_EVENTS \ SHCNE_RENAMEITEM | SHCNE_RENAMEFOLDER | \ SHCNE_CREATE | SHCNE_DELETE | SHCNE_UPDATEDIR | SHCNE_UPDATEITEM | \ SHCNE_MKDIR | SHCNE_RMDIR CNetFolderViewCB::CNetFolderViewCB(CNetFolder *pFolder) : CBaseShellFolderViewCB(pFolder->_pidl, NETFLDR_EVENTS), _pFolder(pFolder) { _pFolder->AddRef(); } CNetFolderViewCB::~CNetFolderViewCB() { _pFolder->Release(); } HRESULT CNetFolderViewCB::OnINVOKECOMMAND(DWORD pv, UINT wP) { return CNetwork_DFMCallBackBG(_pFolder, _hwndMain, NULL, DFM_INVOKECOMMAND, wP, 0); } HRESULT CNetFolderViewCB::OnGETHELPTEXT(DWORD pv, UINT wPl, UINT wPh, LPTSTR lP) { return CNetwork_DFMCallBackBG(_pFolder, _hwndMain, NULL, DFM_GETHELPTEXTW, MAKEWPARAM(wPl, wPh), (LPARAM)lP); } HRESULT CNetFolderViewCB::OnREFRESH(DWORD pv, BOOL fPreRefresh) { if (fPreRefresh) { RefreshNetCrawler(); } return S_OK; } HRESULT CNetFolderViewCB::OnDELAYWINDOWCREATE(DWORD pv, HWND hwnd) { // only do delay window processing in the net root. if (RESOURCEDISPLAYTYPE_GENERIC == _pFolder->_uDisplayType) // MyNetPlaces { RefreshNetCrawler(); } return S_OK; } HRESULT CNetFolderViewCB::OnGETCOLSAVESTREAM(DWORD pv, WPARAM wP, IStream **pps) { LPCTSTR pszValName; switch (_pFolder->_uDisplayType) { case RESOURCEDISPLAYTYPE_DOMAIN: pszValName = TEXT("NetDomainColsX"); break; case RESOURCEDISPLAYTYPE_SERVER: pszValName = TEXT("NetServerColsX"); break; default: return E_FAIL; } *pps = OpenRegStream(HKEY_CURRENT_USER, REGSTR_PATH_EXPLORER, pszValName, (DWORD) wP); return *pps ? S_OK : E_FAIL; } // HRESULT CNetFolderViewCB::OnGetZone(DWORD pv, DWORD * pdwZone); HRESULT CNetFolderViewCB::OnEnumeratedItems(DWORD pv, UINT celt, LPCITEMIDLIST *rgpidl) { _cItems = celt; return S_OK; } HRESULT CNetFolderViewCB::OnDefViewMode(DWORD pv, FOLDERVIEWMODE* pvm) { if (_cItems < DEFVIEW_FVM_MANY_CUTOFF) *pvm = FVM_TILE; else *pvm = FVM_ICON; // used to pick icon only for My Net Places ((_pFolder->_uDisplayType == RESOURCEDISPLAYTYPE_GENERIC)) return S_OK; } HRESULT CNetFolderViewCB::OnGetDeferredViewSettings(DWORD pv, SFVM_DEFERRED_VIEW_SETTINGS* pSettings) { OnDefViewMode(pv, &pSettings->fvm); // if this is the root folder then lets sort accordingly if (_pFolder->_uDisplayType == RESOURCEDISPLAYTYPE_GENERIC) { pSettings->fGroupView = TRUE; pSettings->uSortCol = ICOL_NETWORKLOCATION; pSettings->iSortDirection = 1; } return S_OK; } HRESULT CNetFolderViewCB::OnGetZone(DWORD pv, DWORD * pdwZone) { if (pdwZone) *pdwZone = URLZONE_INTRANET; // default is "Local Intranet" return S_OK; } HRESULT CNetFolderViewCB::OnGetWebViewLayout(DWORD pv, UINT uViewMode, SFVM_WEBVIEW_LAYOUT_DATA* pData) { ZeroMemory(pData, sizeof(*pData)); pData->dwLayout = SFVMWVL_NORMAL; return S_OK; } // HNW is shown on X86 pro or personal workgroup only HRESULT CNetFolderViewCB::_CanShowHNW(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState) { #ifdef _WIN64 *puisState = UIS_DISABLED; return S_OK; #else if (IsOS(OS_ANYSERVER)) *puisState = UIS_DISABLED; // Server-type OS else *puisState = !IsOS(OS_DOMAINMEMBER) ? UIS_ENABLED : UIS_DISABLED; return S_OK; #endif } HRESULT CNetFolderViewCB::_CanViewComputersNearMe(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState) { if (!SHRestricted(REST_NOCOMPUTERSNEARME)) *puisState = !IsOS(OS_DOMAINMEMBER) ? UIS_ENABLED : UIS_DISABLED; else *puisState = UIS_DISABLED; return S_OK; } HRESULT CNetFolderViewCB::_CanSearchActiveDirectory(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState) { if (IsOS(OS_DOMAINMEMBER) && (GetEnvironmentVariable(TEXT("USERDNSDOMAIN"), NULL, 0) > 0)) *puisState = UIS_ENABLED; else *puisState = UIS_DISABLED; return S_OK; } HRESULT CNetFolderViewCB::_OnViewNetConnections(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc) { LPITEMIDLIST pidl; HRESULT hr = SHGetFolderLocation(NULL, CSIDL_CONNECTIONS, NULL, 0, &pidl); if (SUCCEEDED(hr)) { hr = ((CNetFolderViewCB*)(void*)pv)->_BrowseObject(pidl); ILFree(pidl); } return hr; } HRESULT CNetFolderViewCB::_DoRunDll32(LPTSTR pszParameters) { SHELLEXECUTEINFO sei = {0}; sei.cbSize = sizeof(sei); sei.lpFile = TEXT("rundll32.exe"); sei.lpParameters = pszParameters; sei.nShow = SW_SHOWNORMAL; return ShellExecuteEx(&sei) ? S_OK : E_FAIL; } HRESULT CNetFolderViewCB::_OnViewComputersNearMe(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc) { LPITEMIDLIST pidl; HRESULT hr = SHGetFolderLocation(NULL, CSIDL_COMPUTERSNEARME, NULL, 0, &pidl); if (SUCCEEDED(hr)) { hr = ((CNetFolderViewCB*)(void*)pv)->_BrowseObject(pidl); ILFree(pidl); } return hr; } const WVTASKITEM c_MyNetPlacesTaskHeader = WVTI_HEADER(L"shell32.dll", IDS_HEADER_MYNETPLACES, IDS_HEADER_MYNETPLACES_TT); const WVTASKITEM c_MyNetPlacesTaskList[] = { WVTI_ENTRY_ALL(CLSID_NULL, L"shell32.dll", IDS_TASK_ADDNETWORKPLACE, IDS_TASK_ADDNETWORKPLACE_TT, IDI_TASK_ADDNETWORKPLACE, NULL, CNetFolderViewCB::_OnAddNetworkPlace), WVTI_ENTRY_ALL(CLSID_NULL, L"shell32.dll", IDS_TASK_VIEWNETCONNECTIONS, IDS_TASK_VIEWNETCONNECTIONS_TT, IDI_TASK_VIEWNETCONNECTIONS, NULL, CNetFolderViewCB::_OnViewNetConnections), WVTI_ENTRY_ALL(CLSID_NULL, L"shell32.dll", IDS_TASK_HOMENETWORKWIZARD, IDS_TASK_HOMENETWORKWIZARD_TT, IDI_TASK_HOMENETWORKWIZARD, CNetFolderViewCB::_CanShowHNW, CNetFolderViewCB::_OnHomeNetworkWizard), WVTI_ENTRY_ALL(CLSID_NULL, L"shell32.dll", IDS_TASK_COMPUTERSNEARME, IDS_TASK_COMPUTERSNEARME_TT, IDI_GROUP, CNetFolderViewCB::_CanViewComputersNearMe, CNetFolderViewCB::_OnViewComputersNearMe), WVTI_ENTRY_ALL(CLSID_NULL, L"shell32.dll", IDS_TASK_SEARCHDS, IDS_TASK_SEARCHDS_TT, IDI_TASK_SEARCHDS, CNetFolderViewCB::_CanSearchActiveDirectory, CNetFolderViewCB::_OnSearchActiveDirectory), }; BOOL CNetFolderViewCB::_EntireNetworkAvailable() { BOOL fRet = FALSE; // Only enable if we're in a Domain if (IsOS(OS_DOMAINMEMBER) && !SHRestricted(REST_NOENTIRENETWORK)) { LPITEMIDLIST pidl; if (SUCCEEDED(CNetFolder::_CreateEntireNetworkFullIDList(&pidl))) { // ... and we're not already in the "Entire Network" folder. if (!ILIsEqual(_pidl, pidl)) { fRet = TRUE; } ILFree(pidl); } } return fRet; } HRESULT CNetFolderViewCB::OnGetWebViewContent(DWORD pv, SFVM_WEBVIEW_CONTENT_DATA* pData) { ZeroMemory(pData, sizeof(*pData)); Create_IUIElement(&c_MyNetPlacesTaskHeader, &(pData->pFolderTaskHeader)); LPCTSTR rgCsidls[] = { MAKEINTRESOURCE(CSIDL_DRIVES), MAKEINTRESOURCE(CSIDL_PERSONAL), MAKEINTRESOURCE(CSIDL_COMMON_DOCUMENTS), MAKEINTRESOURCE(CSIDL_PRINTERS) }; if (_EntireNetworkAvailable()) { LPITEMIDLIST pidlEntireNetwork = NULL; CNetFolder::_CreateEntireNetworkFullIDList(&pidlEntireNetwork); CreateIEnumIDListOnCSIDLs2(_pidl, pidlEntireNetwork, rgCsidls, ARRAYSIZE(rgCsidls), &(pData->penumOtherPlaces)); ILFree(pidlEntireNetwork); } else { CreateIEnumIDListOnCSIDLs(_pidl, rgCsidls, ARRAYSIZE(rgCsidls), &(pData->penumOtherPlaces)); } return S_OK; } HRESULT CNetFolderViewCB::OnGetWebViewTasks(DWORD pv, SFVM_WEBVIEW_TASKSECTION_DATA* pTasks) { ZeroMemory(pTasks, sizeof(*pTasks)); Create_IEnumUICommand((IUnknown*)(void*)this, c_MyNetPlacesTaskList, ARRAYSIZE(c_MyNetPlacesTaskList), &pTasks->penumFolderTasks); return S_OK; } STDMETHODIMP CNetFolderViewCB::RealMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { HANDLE_MSG(0, SFVM_INVOKECOMMAND, OnINVOKECOMMAND); HANDLE_MSG(0, SFVM_GETHELPTEXT, OnGETHELPTEXT); HANDLE_MSG(0, SFVM_DELAYWINDOWCREATE, OnDELAYWINDOWCREATE); HANDLE_MSG(0, SFVM_GETCOLSAVESTREAM, OnGETCOLSAVESTREAM); HANDLE_MSG(0, SFVM_GETZONE, OnGetZone); HANDLE_MSG(0, SFVM_ENUMERATEDITEMS, OnEnumeratedItems); HANDLE_MSG(0, SFVM_DEFVIEWMODE, OnDefViewMode); HANDLE_MSG(0, SFVM_GETDEFERREDVIEWSETTINGS, OnGetDeferredViewSettings); HANDLE_MSG(0, SFVM_REFRESH, OnREFRESH); HANDLE_MSG(0, SFVM_GETWEBVIEWLAYOUT, OnGetWebViewLayout); HANDLE_MSG(0, SFVM_GETWEBVIEWCONTENT, OnGetWebViewContent); HANDLE_MSG(0, SFVM_GETWEBVIEWTASKS, OnGetWebViewTasks); default: return E_FAIL; } return S_OK; } // Replace all the space characters in the provider name with '_'. void ReplaceSpacesWithUnderscore(LPTSTR psz) { while (psz = StrChr(psz, TEXT(' '))) { *psz = TEXT('_'); psz++; // DBCS safe } } BOOL CNetFolderViewCB::_GetProviderKeyName(LPTSTR pszName, UINT uNameLen) { if (_pFolder->_GetProvider(NULL, NULL, pszName, uNameLen)) { ReplaceSpacesWithUnderscore(pszName); } return (BOOL)*pszName; } // Define a collate order for the hood object types #define _HOOD_COL_RON 0 #define _HOOD_COL_REMOTE 1 #define _HOOD_COL_FILE 2 #define _HOOD_COL_NET 3 const static ICONMAP c_aicmpNet[] = { { SHID_NET_NETWORK , II_NETWORK }, { SHID_NET_DOMAIN , II_GROUP }, { SHID_NET_SERVER , II_SERVER }, { SHID_NET_SHARE , (UINT)EIRESID(IDI_SERVERSHARE) }, { SHID_NET_DIRECTORY , II_FOLDER }, { SHID_NET_PRINTER , II_PRINTER }, { SHID_NET_RESTOFNET , II_WORLD }, { SHID_NET_SHAREADMIN , II_DRIVEFIXED }, { SHID_NET_TREE , II_TREE }, { SHID_NET_NDSCONTAINER, (UINT)EIRESID(IDI_NDSCONTAINER) }, }; enum { NKID_PROVIDERTYPE = 0, NKID_PROVIDER, NKID_NETCLASS, NKID_NETWORK, NKID_DIRECTORY, NKID_FOLDER }; #define NKID_COUNT 6 // This is one-entry cache for remote junctions resolution TCHAR g_szLastAttemptedJunctionName[MAX_PATH] = {0}; TCHAR g_szLastResolvedJunctionName[MAX_PATH] = {0}; REGITEMSINFO g_riiNetRoot = { REGSTR_PATH_EXPLORER TEXT("\\NetworkNeighborhood\\NameSpace"), NULL, TEXT(':'), SHID_NET_REGITEM, 1, SFGAO_CANLINK, 0, NULL, RIISA_ORIGINAL, NULL, 0, 0, }; CNetRootFolder* CNetRootFolder::_spThis = NULL; HRESULT CNetFolder::_CreateInstance(LPCITEMIDLIST pidlAbs, LPCITEMIDLIST pidlTarget, UINT uDisplayType, LPCIDNETRESOURCE pidnForProvider, LPCTSTR pszResName, REFIID riid, void **ppv) { HRESULT hr = E_OUTOFMEMORY; *ppv = NULL; if (!ILIsEmpty(pidlAbs)) { CNetFolder* pNetF = new CNetFolder(NULL); if (NULL != pNetF) { pNetF->_uDisplayType = uDisplayType; if (pidnForProvider) { //Make sure that the pidnProvider has provider information. ASSERT(NET_FHasProvider(pidnForProvider)) //We are interested only in the provider informarion which is contained in the first entry. //Its enough if we clone only the first item in the pidl. pNetF->_pidnForProvider = (LPCIDNETRESOURCE)ILCloneFirst((LPCITEMIDLIST)pidnForProvider); } if (pszResName && *pszResName) pNetF->_pszResName = StrDup(pszResName); pNetF->_pidl = ILClone(pidlAbs); pNetF->_pidlTarget = ILClone(pidlTarget); if (pNetF->_pidl && (!pidlTarget || (pidlTarget && pNetF->_pidlTarget))) { if (uDisplayType == RESOURCEDISPLAYTYPE_SERVER) { // This is a remote computer. See if there are any remote // computer registry items. If so, aggregate with the registry // class. REGITEMSINFO riiComputer = { REGSTR_PATH_EXPLORER TEXT("\\RemoteComputer\\NameSpace"), NULL, TEXT(':'), SHID_NET_REMOTEREGITEM, -1, SFGAO_FOLDER | SFGAO_CANLINK, 0, // no required reg items NULL, RIISA_ORIGINAL, pszResName, 0, 0, }; CRegFolder_CreateInstance(&riiComputer, (IUnknown*) (IShellFolder*) pNetF, IID_PPV_ARG(IUnknown, &pNetF->_punkReg)); } else if (uDisplayType == RESOURCEDISPLAYTYPE_ROOT) { // // this is the entire net icon, so lets create an instance of the regitem folder // so we can merge in the items from there. // REGITEMSINFO riiEntireNet = { REGSTR_PATH_EXPLORER TEXT("\\NetworkNeighborhood\\EntireNetwork\\NameSpace"), NULL, TEXT(':'), SHID_NET_REGITEM, -1, SFGAO_CANLINK, 0, // no required reg items NULL, RIISA_ORIGINAL, NULL, 0, 0, }; CRegFolder_CreateInstance(&riiEntireNet, (IUnknown*) (IShellFolder*) pNetF, IID_PPV_ARG(IUnknown, &pNetF->_punkReg)); } else { ASSERT(hr == E_OUTOFMEMORY); } hr = pNetF->QueryInterface(riid, ppv); } pNetF->Release(); } else { ASSERT(hr == E_OUTOFMEMORY); } } else { ASSERT(0); hr = E_INVALIDARG; } return hr; } HRESULT CNetwork_CreateInstance(IUnknown* punkOuter, REFIID riid, void **ppv) { HRESULT hr = S_OK; *ppv = NULL; // Must enter critical section to avoid racing against v_HandleDelete ENTERCRITICAL; if (NULL != CNetRootFolder::_spThis) { hr = CNetRootFolder::_spThis->QueryInterface(riid, ppv); } else { CNetRootFolder* pNetRootF = new CNetRootFolder(punkOuter); if (pNetRootF) { // Initialize it ourselves to ensure that the cached value // is the correct one. hr = pNetRootF->Initialize((LPCITEMIDLIST)&c_idlNet); if (SUCCEEDED(hr)) { pNetRootF->_uDisplayType = RESOURCEDISPLAYTYPE_GENERIC; ASSERT(NULL == pNetRootF->_punkReg); if (SHRestricted(REST_NOSETFOLDERS)) g_riiNetRoot.iReqItems = 0; // create the regitems object, he has the NetRoot object as his outer guy. hr = CRegFolder_CreateInstance(&g_riiNetRoot, SAFECAST(pNetRootF, IShellFolder2*), IID_PPV_ARG(IUnknown, &pNetRootF->_punkReg)); // NOTE: not using SHInterlockedCompareExchange() because we have the critsec CNetRootFolder::_spThis = pNetRootF; hr = pNetRootF->QueryInterface(riid, ppv); } // Release the self-reference, but keep the the _spThis pointer intact // (it will be reset to NULL in the destructor) pNetRootF->Release(); } else { hr = E_OUTOFMEMORY; } } LEAVECRITICAL; return hr; } CNetFolder::CNetFolder(IUnknown* punkOuter) : CAggregatedUnknown (punkOuter) { // Assert that we're still using a zero-init flag inside the new operator ASSERT(NULL == _pidl); ASSERT(NULL == _pidlTarget); ASSERT(NULL == _pidnForProvider); ASSERT(NULL == _pszResName); ASSERT(0 == _uDisplayType); ASSERT(NULL == _psfFiles); ASSERT(NULL == _punkReg); DllAddRef(); } CNetFolder::~CNetFolder() { ILFree(_pidl); ILFree(_pidlTarget); ILFree((LPITEMIDLIST)_pidnForProvider); if (NULL != _pszResName) { LocalFree(_pszResName); } if (_psfFiles) { _psfFiles->Release(); } SHReleaseInnerInterface(SAFECAST(this, IShellFolder *), &_punkReg); DllRelease(); } CNetFolder *FolderToNetFolder(IUnknown *punk) { CNetFolder * pThis = NULL; return punk && SUCCEEDED(punk->QueryInterface(CLSID_CNetFldr, (void **)&pThis)) ? pThis : NULL; } HRESULT CNetFolder::v_InternalQueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CNetFolder, IShellFolder2), // IID_IShellFolder2 QITABENTMULTI(CNetFolder, IShellFolder, IShellFolder2), // IID_IShellFolder QITABENT(CNetFolder, IPersistFolder3), // IID_IPersistFolder3 QITABENT(CNetFolder, IShellIconOverlay), // IID_IShellIconOverlay QITABENTMULTI(CNetFolder, IPersistFolder2, IPersistFolder3), // IID_IPersistFolder2 QITABENTMULTI(CNetFolder, IPersistFolder, IPersistFolder3), // IID_IPersistFolder QITABENTMULTI(CNetFolder, IPersist, IPersistFolder3), // IID_IPersist QITABENTMULTI2(CNetFolder, IID_IPersistFreeThreadedObject, IPersist), // IID_IPersistFreeThreadedObject { 0 }, }; if (IsEqualIID(riid, CLSID_CNetFldr)) { *ppv = this; // get class pointer (unrefed!) return S_OK; } HRESULT hr; if (_punkReg && RegGetsFirstShot(riid)) { hr = _punkReg->QueryInterface(riid, ppv); } else { hr = QISearch(this, qit, riid, ppv); if ((E_NOINTERFACE == hr) && _punkReg) { hr = _punkReg->QueryInterface(riid, ppv); } } return hr; } BOOL CNetRootFolder::v_HandleDelete(PLONG pcRef) { ASSERT(NULL != pcRef); ENTERCRITICAL; // Once inside the critical section things are slightly more stable. // CNetwork_CreateInstance won't be able to rescue the cached reference // (and bump the refcount from 0 to 1). And we don't have to worry // about somebody Release()ing us down to zero a second time, since // no new references can show up. // // HOWEVER! All those scary things could've happened WHILE WE WERE // WAITING TO ENTER THE CRITICAL SECTION. // // While we were waiting, somebody could've called CNetwork_CreateInstance, // which bumps the reference count back up. So don't destroy ourselves // if our object got "rescued". // // What's more, while we were waiting, that somebody could've then // Release()d us back down to zero, causing us to be called on that // other thread, notice that the refcount is indeed zero, and destroy // the object, all on that other thread. So if we are not the cached // instance, then don't destroy ourselves since that other thread did // it already. // // And even more, somebody might call CNetwork_CreateInstance again // and create a brand new object, which might COINCIDENTALLY happen // to have the same address as the old object we are trying to destroy // here. But in that case, it's okay to destroy the new object because // it is indeed the case that the object's reference count is zero and // deserves to be destroyed. if (this == _spThis && 0 == *pcRef) { *pcRef = 1000; // protect against cached pointers bumping us up then down delete this; } LEAVECRITICAL; // return TRUE to indicate that we've implemented this function // (regardless of whether or not this object was actually deleted) return TRUE; } STDMETHODIMP CNetFolder::ParseDisplayName(HWND hwnd, LPBC pbc, WCHAR* pszName, ULONG* pchEaten, LPITEMIDLIST* ppidl, DWORD* pdwAttributes) { return E_NOTIMPL; } // new for Win2K, this enables enuming the hidden admin shares #ifndef RESOURCE_SHAREABLE #define RESOURCE_SHAREABLE 0x00000006 #endif // // in: // hwnd NULL indicates no UI. // grfFlags IShellFolder::EnumObjects() SHCONTF_ flags // pnr in/out params // // DWORD CNetFolder::_OpenEnum(HWND hwnd, DWORD grfFlags, LPNETRESOURCE pnr, HANDLE *phEnum) { DWORD dwType = (grfFlags & SHCONTF_NETPRINTERSRCH) ? RESOURCETYPE_PRINT : RESOURCETYPE_ANY; DWORD dwScope = pnr ? RESOURCE_GLOBALNET : RESOURCE_CONTEXT; if ((_uDisplayType == RESOURCEDISPLAYTYPE_SERVER) && (grfFlags & SHCONTF_SHAREABLE)) { dwScope = RESOURCE_SHAREABLE; // hidden admin shares for this server } DWORD err = WNetOpenEnum(dwScope, dwType, RESOURCEUSAGE_ALL, pnr, phEnum); if ((err != WN_SUCCESS) && hwnd) { // If it failed because you are not authenticated yet, // we need to let the user loggin to this network resource. // // REVIEW: Ask LenS to review this code. if (err == WN_NOT_AUTHENTICATED || err == ERROR_LOGON_FAILURE || err == WN_BAD_PASSWORD || err == WN_ACCESS_DENIED) { // Retry with password dialog box. err = WNetAddConnection3(hwnd, pnr, NULL, NULL, CONNECT_TEMPORARY | CONNECT_INTERACTIVE); if (err == WN_SUCCESS) err = WNetOpenEnum(dwScope, dwType, RESOURCEUSAGE_ALL, pnr, phEnum); } UINT idTemplate = pnr && pnr->lpRemoteName ? IDS_ENUMERR_NETTEMPLATE2 : IDS_ENUMERR_NETTEMPLATE1; SHEnumErrorMessageBox(hwnd, idTemplate, err, pnr ? pnr->lpRemoteName : NULL, TRUE, MB_OK | MB_ICONHAND); } return err; } // find the share part of a UNC // \\server\share // return pointer to "share" or pointer to empty string if none LPCTSTR PathFindShareName(LPCTSTR pszUNC) { LPCTSTR psz = SkipServerSlashes(pszUNC); if (*psz) { psz = StrChr(psz + 1, TEXT('\\')); if (psz) psz++; else psz = TEXT(""); } return psz; } // Flags for the dwRemote field #define RMF_CONTEXT 0x00000001 // Entire network is being enumerated #define RMF_SHOWREMOTE 0x00000002 // Return Remote Services for next enumeration #define RMF_STOP_ENUM 0x00000004 // Stop enumeration #define RMF_GETLINKENUM 0x00000008 // Hoodlinks enum needs to be fetched #define RMF_SHOWLINKS 0x00000010 // Hoodlinks need to be shown #define RMF_FAKENETROOT 0x00000020 // Don't enumerate the workgroup items #define RMF_ENTIRENETSHOWN 0x40000000 // Entire network object shown #define RMF_REMOTESHOWN 0x80000000 // Return Remote Services for next enumeration class CNetFolderEnum : public CEnumIDListBase { public: // IEnumIDList STDMETHOD(Next)(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched); private: CNetFolderEnum(CNetFolder *pnf, DWORD grfFlags, DWORD dwRemote, HANDLE hEnum); ~CNetFolderEnum(); friend HRESULT Create_NetFolderEnum(CNetFolder* pnsf, DWORD grfFlags, DWORD dwRemote, HANDLE hEnum, IEnumIDList** ppenum); CNetFolder *_pnsf; // CNetFolder object we're enumerating HANDLE _hEnum; DWORD _grfFlags; LONG _cItems; // Count of items in buffer LONG _iItem; // Current index of the item in the buffer DWORD _dwRemote; union { NETRESOURCE _anr[0]; BYTE _szBuffer[8192]; }; IEnumIDList *_peunk; // used for enumerating file system items (links) }; CNetFolderEnum::CNetFolderEnum(CNetFolder *pnsf, DWORD grfFlags, DWORD dwRemote, HANDLE hEnum) : CEnumIDListBase() { _pnsf = pnsf; _pnsf->AddRef(); _grfFlags = grfFlags; _dwRemote = dwRemote; _hEnum = hEnum; } HRESULT Create_NetFolderEnum(CNetFolder* pnf, DWORD grfFlags, DWORD dwRemote, HANDLE hEnum, IEnumIDList** ppenum) { HRESULT hr; CNetFolderEnum* p= new CNetFolderEnum(pnf, grfFlags, dwRemote, hEnum); if (p) { hr = p->QueryInterface(IID_PPV_ARG(IEnumIDList, ppenum)); p->Release(); } else { hr = E_OUTOFMEMORY; *ppenum = NULL; } return hr; } CNetFolderEnum::~CNetFolderEnum() { _pnsf->Release(); // release the "this" ptr we have if (_peunk) _peunk->Release(); if (_hEnum) WNetCloseEnum(_hEnum); } STDMETHODIMP CNetFolderEnum::Next(ULONG celt, LPITEMIDLIST *ppidl, ULONG *pceltFetched) { HRESULT hr; *ppidl = NULL; if (pceltFetched) *pceltFetched = 0; // Time to stop enumeration? if (_dwRemote & RMF_STOP_ENUM) return S_FALSE; // Yes // should we try and get the links enumerator? if (_dwRemote & RMF_GETLINKENUM) { IShellFolder2* psfNetHood; if (SUCCEEDED(_pnsf->v_GetFileFolder(&psfNetHood))) psfNetHood->EnumObjects(NULL, _grfFlags, &_peunk); if (_peunk) _dwRemote |= RMF_SHOWLINKS; _dwRemote &= ~RMF_GETLINKENUM; } // should we be showing the links? if (_dwRemote & RMF_SHOWLINKS) { if (_peunk) { ULONG celtFetched; LPITEMIDLIST pidl; hr = _peunk->Next(1, &pidl, &celtFetched); if (hr == S_OK && celtFetched == 1) { *ppidl = pidl; if (pceltFetched) *pceltFetched = celtFetched; return S_OK; // Added link } } _dwRemote &= ~RMF_SHOWLINKS; // Done enumerating links } hr = S_OK; // Do we add the remote folder? // (Note: as a hack to ensure that the remote folder is added // to the 'hood despite what MPR says, RMF_SHOWREMOTE can be // set without RMF_CONTEXT set.) if ((_dwRemote & RMF_SHOWREMOTE) && !(_dwRemote & RMF_REMOTESHOWN)) { // Yes // Only try to put the remote entry in once. _dwRemote |= RMF_REMOTESHOWN; // Is this not the Context container? // (See note above as to why we are asking this question.) if (!(_dwRemote & RMF_CONTEXT)) { // Yes; stop after the next time _dwRemote |= RMF_STOP_ENUM; } // We have fallen thru because the remote services is not // installed. // Is this not the Context container AND the remote folder // is not installed? if (!(_dwRemote & RMF_CONTEXT)) { // Yes; nothing else to enumerate return S_FALSE; } } if (_dwRemote & RMF_FAKENETROOT) { if ((!(_dwRemote & RMF_ENTIRENETSHOWN)) && (S_FALSE != SHShouldShowWizards(_punkSite))) { _pnsf->_CreateEntireNetwork(ppidl); // fake entire net _dwRemote |= RMF_ENTIRENETSHOWN; } else { return S_FALSE; // no more to enumerate } } else { while (TRUE) { ULONG err = WN_SUCCESS; LPNETRESOURCE pnr; if (_iItem >= _cItems) { DWORD dwSize = sizeof(_szBuffer); _cItems = -1; // its signed _iItem = 0; err = WNetEnumResource(_hEnum, (DWORD*)&_cItems, _szBuffer, &dwSize); DebugMsg(DM_TRACE, TEXT("Net EnumCallback: err=%d Count=%d"), err, _cItems); } pnr = &_anr[_iItem++]; // Note: the <= below is correct as we already incremented the index... if (err == WN_SUCCESS && (_iItem <= _cItems)) { // decide if the thing is a folder or not ULONG grfFlagsItem = ((pnr->dwUsage & RESOURCEUSAGE_CONTAINER) || (pnr->dwType == RESOURCETYPE_DISK) || (pnr->dwType == RESOURCETYPE_ANY)) ? SHCONTF_FOLDERS : SHCONTF_NONFOLDERS; // If this is the context enumeration, we want to insert the // Remote Services after the first container. // // Remember that we need to return the Remote Services in the next iteration. if ((pnr->dwUsage & RESOURCEUSAGE_CONTAINER) && (_dwRemote & RMF_CONTEXT)) { _dwRemote |= RMF_SHOWREMOTE; } if ((_pnsf->_uDisplayType == RESOURCEDISPLAYTYPE_SERVER) && (_grfFlags & SHCONTF_SHAREABLE)) { // filter out ADMIN$ and IPC$, based on str len if (lstrlen(PathFindShareName(pnr->lpRemoteName)) > 2) { grfFlagsItem = 0; } } // if this is a network object, work out if we should hide or note, so // convert the provider to its type number and open the key under: // // HKEY_CLASSES_ROOT\Network\Type\ if ((pnr->dwDisplayType == RESOURCEDISPLAYTYPE_NETWORK) && !(_grfFlags & SHCONTF_INCLUDEHIDDEN)) { DWORD dwType; if (WNetGetProviderType(pnr->lpProvider, &dwType) == WN_SUCCESS) { TCHAR szRegValue[MAX_PATH]; wsprintf(szRegValue, TEXT("Network\\Type\\%d"), HIWORD(dwType)); BOOL fHide = FALSE; DWORD cb = sizeof(fHide); if ((ERROR_SUCCESS == SHGetValue(HKEY_CLASSES_ROOT, szRegValue, TEXT("HideProvider"), NULL, &fHide, &cb)) && fHide) { grfFlagsItem = 0; } } } // Check if we found requested type of net resource. if (_grfFlags & grfFlagsItem) { if (SUCCEEDED(_pnsf->_NetResToIDList(pnr, FALSE, TRUE, (_grfFlags & SHCONTF_NONFOLDERS), ppidl))) { break; } } } else if (err == WN_NO_MORE_ENTRIES) { hr = S_FALSE; // no more element break; } else { DebugMsg(DM_ERROR, TEXT("sh ER - WNetEnumResource failed (%lx)"), err); hr = E_FAIL; break; } } } if (pceltFetched) *pceltFetched = (S_OK == hr) ? 1 : 0; return hr; } STDMETHODIMP CNetFolder::EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList** ppenum) { NETRESOURCE nr = {0}; TCHAR szProvider[MAX_PATH]; nr.lpProvider = (LPTSTR) _GetProvider(NULL, NULL, szProvider, ARRAYSIZE(szProvider)); if (_uDisplayType != RESOURCEDISPLAYTYPE_ROOT && _uDisplayType != RESOURCEDISPLAYTYPE_NETWORK) { nr.lpRemoteName = _pszResName; } HRESULT hr; HANDLE hEnum; DWORD err = _OpenEnum(hwnd, grfFlags, &nr, &hEnum); if (err == WN_SUCCESS) { hr = Create_NetFolderEnum(this, grfFlags, 0, hEnum, ppenum); if (FAILED(hr)) { WNetCloseEnum(hEnum); } } else { hr = HRESULT_FROM_WIN32(err); } return hr; } LPCIDNETRESOURCE NET_IsValidID(LPCITEMIDLIST pidl) { if (pidl && !ILIsEmpty(pidl) && ((pidl->mkid.abID[0] & SHID_GROUPMASK) == SHID_NET)) return (LPCIDNETRESOURCE)pidl; return NULL; } STDMETHODIMP CNetFolder::BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv) { HRESULT hr; LPCIDNETRESOURCE pidn; *ppv = NULL; pidn = NET_IsValidID(pidl); if (pidn) { IShellFolder *psfJunction; LPITEMIDLIST pidlInit = NULL; LPITEMIDLIST pidlTarget = NULL; LPCITEMIDLIST pidlRight = _ILNext(pidl); BOOL fRightIsEmpty = ILIsEmpty(pidlRight); LPCIDNETRESOURCE pidnProvider = NET_FHasProvider(pidn) ? pidn :_pidnForProvider; hr = S_OK; // lets get the IDLISTs we are going to use to initialize the shell folder // if we are doing a single level bind then then ILCombine otherwise // be more careful. pidlInit = ILCombineParentAndFirst(_pidl, pidl, pidlRight); if (_pidlTarget) pidlTarget = ILCombineParentAndFirst(_pidlTarget, pidl, pidlRight); if (!pidlInit || (!pidlTarget && _pidlTarget)) hr = E_OUTOFMEMORY; // now create the folder object we are using, and either return that // object to the caller, or continue the binding down. if (SUCCEEDED(hr)) { hr = _CreateFolderForItem(pbc, pidlInit, pidlTarget, pidnProvider, fRightIsEmpty ? riid : IID_IShellFolder, fRightIsEmpty ? ppv : (void **)&psfJunction); if (!fRightIsEmpty && SUCCEEDED(hr)) { hr = psfJunction->BindToObject(pidlRight, pbc, riid, ppv); psfJunction->Release(); } } ILFree(pidlInit); ILFree(pidlTarget); } else { hr = E_INVALIDARG; } return hr; } STDMETHODIMP CNetFolder::BindToStorage(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv) { return BindToObject(pidl, pbc, riid, ppv); } STDMETHODIMP CNetFolder::CompareIDs(LPARAM iCol, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2) { HRESULT hr = E_INVALIDARG; LPCIDNETRESOURCE pidn1 = NET_IsValidID(pidl1); LPCIDNETRESOURCE pidn2 = NET_IsValidID(pidl2); if (pidn1 && pidn2) { TCHAR szBuff1[MAX_PATH], szBuff2[MAX_PATH]; switch (iCol & SHCIDS_COLUMNMASK) { case ICOL_COMMENT: { hr = ResultFromShort(StrCmpLogicalRestricted(NET_CopyComment(pidn1, szBuff1, ARRAYSIZE(szBuff1)), NET_CopyComment(pidn2, szBuff2, ARRAYSIZE(szBuff2)))); if (hr != 0) return hr; // drop down into the name comparison } case ICOL_NAME: { // Compare by name. This is the one case where we need to handle // simple ids in either place. We will try to resync the items // if we find a case of this before do the compares. // Check for relative IDs. In particular if one item is at // a server and the other is at RestOfNet then try to resync // the two // if (NET_IsFake(pidn1) || NET_IsFake(pidn2)) { // if either pidn1 or pidn2 is fake then we assume they are identical, // this allows us to compare a simple net ID to a real net ID. we // assume that if this fails later then the world will be happy hr = 0; } else { // otherwise lets look at the names and provider strings accordingly NET_CopyResName(pidn1, szBuff1, ARRAYSIZE(szBuff1)); NET_CopyResName(pidn2, szBuff2, ARRAYSIZE(szBuff2)); hr = ResultFromShort(StrCmpLogicalRestricted(szBuff1, szBuff2)); // If they're still identical, compare provider names. if ((hr == 0) && (iCol & SHCIDS_ALLFIELDS)) { LPCTSTR pszProv1 = _GetProvider(pidn1, NULL, szBuff1, ARRAYSIZE(szBuff1)); LPCTSTR pszProv2 = _GetProvider(pidn2, NULL, szBuff2, ARRAYSIZE(szBuff2)); if (pszProv1 && pszProv2) hr = ResultFromShort(lstrcmp(pszProv1, pszProv2)); else { if (pszProv1 || pszProv2) hr = ResultFromShort(pszProv1 ? 1 : -1); else hr = ResultFromShort(0); } } } // If they identical, compare the rest of IDs. if (hr == 0) hr = ILCompareRelIDs((IShellFolder*)this, (LPCITEMIDLIST)pidn1, (LPCITEMIDLIST)pidn2, iCol); } } } return hr; } STDMETHODIMP CNetFolder::CreateViewObject(HWND hwnd, REFIID riid, void **ppv) { HRESULT hr; *ppv = NULL; if (IsEqualIID(riid, IID_IShellView)) { SFV_CREATE sSFV; sSFV.cbSize = sizeof(sSFV); sSFV.psvOuter = NULL; sSFV.psfvcb = new CNetFolderViewCB(this); // failure is OK, we just get generic support QueryInterface(IID_PPV_ARG(IShellFolder, &sSFV.pshf)); // in case we are agregated hr = SHCreateShellFolderView(&sSFV, (IShellView**) ppv); if (sSFV.pshf) sSFV.pshf->Release(); if (sSFV.psfvcb) sSFV.psfvcb->Release(); } else if (IsEqualIID(riid, IID_IContextMenu)) { IShellFolder* psfOuter; hr = QueryInterface(IID_PPV_ARG(IShellFolder, &psfOuter)); if (SUCCEEDED(hr)) { hr = CDefFolderMenu_Create(_pidl, hwnd, 0, NULL, psfOuter, CNetwork_DFMCallBackBG, NULL, NULL, (IContextMenu**) ppv); psfOuter->Release(); } } else { hr = E_NOINTERFACE; } return hr; } typedef HRESULT (CALLBACK *PFNGAOCALLBACK)(IShellFolder2 *psf, LPCITEMIDLIST pidl, ULONG* prgfInOut); STDAPI GetAttributesCallback(IShellFolder2 *psf, UINT cidl, LPCITEMIDLIST* apidl, ULONG *prgfInOut, PFNGAOCALLBACK pfnGAOCallback) { HRESULT hr = S_OK; ULONG rgfOut = 0; for (UINT i = 0; i < cidl; i++) { ULONG rgfT = *prgfInOut; hr = pfnGAOCallback(psf, apidl[i], &rgfT); if (FAILED(hr)) { rgfOut = 0; break; } rgfOut |= rgfT; } *prgfInOut &= rgfOut; return hr; } STDMETHODIMP CNetFolder::GetAttributesOf(UINT cidl, LPCITEMIDLIST* apidl, ULONG* prgfInOut) { HRESULT hr; if (IsSelf(cidl, apidl)) { *prgfInOut &= (SFGAO_CANLINK | SFGAO_HASPROPSHEET | SFGAO_HASSUBFOLDER | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR); hr = S_OK; } else { hr = GetAttributesCallback(SAFECAST(this, IShellFolder2*), cidl, apidl, prgfInOut, _AttributesCallback); } return hr; } STDMETHODIMP CNetFolder::GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD dwFlags, STRRET* pStrRet) { HRESULT hr; LPCIDNETRESOURCE pidn = NET_IsValidID(pidl); if (pidn) { TCHAR szPath[MAX_PATH]; LPCITEMIDLIST pidlNext = _ILNext(pidl); if (dwFlags & SHGDN_FORPARSING) { if ((dwFlags & SHGDN_INFOLDER) || ((dwFlags & SHGDN_FORADDRESSBAR) && (NET_GetDisplayType(pidn) == RESOURCEDISPLAYTYPE_ROOT))) // the non-infolder name for the root is not good for the address bar { NET_CopyResName(pidn, szPath, ARRAYSIZE(szPath)); if (ILIsEmpty(pidlNext)) { // we just need the last part of the display name (IN FOLDER) LPTSTR pszT = StrRChr(szPath, NULL, TEXT('\\')); if (!pszT) pszT = szPath; else pszT++; // move past '\' hr = StringToStrRet(pszT, pStrRet); } else { hr = ILGetRelDisplayName((IShellFolder*) this, pStrRet, pidl, szPath, MAKEINTRESOURCE(IDS_DSPTEMPLATE_WITH_BACKSLASH), dwFlags); } } else { LPCITEMIDLIST pidlRight = _ILNext(pidl); if (ILIsEmpty(pidlRight)) { hr = _GetPathForItem(pidn, szPath); if (SUCCEEDED(hr)) { hr = StringToStrRet(szPath, pStrRet); } } else { IShellFolder *psfJunction; //Get the pidn which has network provider information. LPCIDNETRESOURCE pidnProvider = NET_FHasProvider(pidn) ? pidn :_pidnForProvider; LPITEMIDLIST pidlInit, pidlTarget = NULL; pidlInit = ILCombineParentAndFirst(_pidl, pidl, pidlRight); if (_pidlTarget) pidlTarget = ILCombineParentAndFirst(_pidlTarget, pidl, pidlRight); if (!pidlInit || (_pidlTarget && !pidlTarget)) return E_OUTOFMEMORY; hr = _CreateFolderForItem(NULL, pidlInit, pidlTarget, pidnProvider, IID_PPV_ARG(IShellFolder, &psfJunction)); if (SUCCEEDED(hr)) { hr = psfJunction->GetDisplayNameOf(pidlRight, dwFlags, pStrRet); psfJunction->Release(); } ILFree(pidlInit); ILFree(pidlTarget); } } } else { hr = _GetFormatName(pidn, pStrRet); if (SUCCEEDED(hr) && !(dwFlags & SHGDN_INFOLDER) && (NET_GetFlags(pidn) & SHID_JUNCTION)) { TCHAR szServer[MAX_PATH]; SHGetNameAndFlags(_pidlTarget ? _pidlTarget:_pidl, SHGDN_FORPARSING, szServer, ARRAYSIZE(szServer), NULL); TCHAR szDisplay[MAX_PATH]; hr = SHGetComputerDisplayName(szServer, 0x0, szDisplay, ARRAYSIZE(szDisplay)); if (SUCCEEDED(hr)) { StrRetFormat(pStrRet, pidl, MAKEINTRESOURCE(IDS_DSPTEMPLATE_WITH_ON), szDisplay); } } } } else hr = E_INVALIDARG; return hr; } STDMETHODIMP CNetFolder::SetNameOf(HWND hwnd, LPCITEMIDLIST pidl, LPCOLESTR lpszName, DWORD dwRes, LPITEMIDLIST* ppidl) { if (ppidl) *ppidl = NULL; return E_NOTIMPL; // not supported } STDMETHODIMP CNetFolder::GetUIObjectOf(HWND hwnd, UINT cidl, LPCITEMIDLIST* apidl, REFIID riid, UINT* prgfInOut, void **ppv) { HRESULT hr = E_INVALIDARG; LPCIDNETRESOURCE pidn = cidl ? NET_IsValidID(apidl[0]) : NULL; *ppv = NULL; if ((IsEqualIID(riid, IID_IExtractIconA) || IsEqualIID(riid, IID_IExtractIconW)) && pidn) { UINT iIndex; if (_IsPrintShare(pidn)) iIndex = (UINT)EIRESID(IDI_PRINTER_NET); else if (NET_IsRemoteFld(pidn)) iIndex = II_RNA; else iIndex = SILGetIconIndex(apidl[0], c_aicmpNet, ARRAYSIZE(c_aicmpNet)); hr = SHCreateDefExtIcon(NULL, iIndex, iIndex, GIL_PERCLASS, II_FOLDER, riid, ppv); } else if (IsEqualIID(riid, IID_IContextMenu) && pidn) { HKEY ahkeys[NKID_COUNT]; hr = _OpenKeys(pidn, ahkeys); if (SUCCEEDED(hr)) { IShellFolder* psfOuter; hr = QueryInterface(IID_PPV_ARG(IShellFolder, &psfOuter)); if (SUCCEEDED(hr)) { hr = CDefFolderMenu_Create2(_pidl, hwnd, cidl, apidl, psfOuter, _GetCallbackType(pidn), ARRAYSIZE(ahkeys), ahkeys, (IContextMenu**) ppv); psfOuter->Release(); } SHRegCloseKeys(ahkeys, ARRAYSIZE(ahkeys)); } } else if (cidl && IsEqualIID(riid, IID_IDataObject)) { // Point & Print printer installation assumes that the // netresources from CNetData_GetData and the // pidls from CIDLData_GetData are in the same order. // Keep it this way. CNetData *pnd = new CNetData(_pidl, cidl, apidl); if (pnd) { hr = pnd->QueryInterface(riid, ppv); pnd->Release(); } else hr = E_OUTOFMEMORY; } else if (pidn && IsEqualIID(riid, IID_IDropTarget)) { // special support because this is an item (not a folder) if (_IsPrintShare(pidn)) { LPITEMIDLIST pidl; hr = SHILCombine(_pidl, apidl[0], &pidl); if (SUCCEEDED(hr)) { hr = CPrinterDropTarget_CreateInstance(hwnd, pidl, (IDropTarget**)ppv); ILFree(pidl); } } else { IShellFolder *psf; hr = BindToObject(apidl[0], NULL, IID_PPV_ARG(IShellFolder, &psf)); if (SUCCEEDED(hr)) { hr = psf->CreateViewObject(hwnd, riid, ppv); psf->Release(); } } } else if (pidn && IsEqualIID(riid, IID_IQueryInfo)) { if (NET_GetDisplayType(pidn) == RESOURCEDISPLAYTYPE_ROOT) { hr = CreateInfoTipFromText(MAKEINTRESOURCE(IDS_RESTOFNETTIP), riid, ppv); } else { // Someday maybe have infotips for other things too } } return hr; } STDMETHODIMP CNetFolder::GetDefaultSearchGUID(LPGUID pguid) { *pguid = SRCID_SFindComputer; return S_OK; } void WINAPI CNetFolder::_CopyEnumElement(void* pDest, const void* pSource, DWORD dwSize) { if (pDest && pSource) memcpy(pDest, pSource, dwSize); } STDMETHODIMP CNetFolder::EnumSearches(IEnumExtraSearch** ppenum) { HRESULT hr = E_NOTIMPL; *ppenum = NULL; // if the restriction is set then this item should be enumerated from the registry // so we fail, else enumerate it // only enumerate if we actually have a network to search against if (!SHRestricted(REST_HASFINDCOMPUTERS) && (GetSystemMetrics(SM_NETWORK) & RNC_NETWORKS)) { EXTRASEARCH *pxs = (EXTRASEARCH *)LocalAlloc(LPTR, sizeof(EXTRASEARCH)); if (pxs) { pxs->guidSearch = SRCID_SFindComputer; if (LoadStringW(g_hinst, IDS_FC_NAME, pxs->wszFriendlyName, sizeof(pxs->wszFriendlyName))) { *ppenum = (IEnumExtraSearch*)CStandardEnum_CreateInstance(IID_IEnumExtraSearch, FALSE, 1, sizeof(EXTRASEARCH), pxs, _CopyEnumElement); if (*ppenum == NULL) { LocalFree(pxs); hr = E_OUTOFMEMORY; } else hr = S_OK; } } else hr = E_OUTOFMEMORY; } return hr; } STDMETHODIMP CNetFolder::GetDefaultColumn(DWORD dwRes, ULONG* pSort, ULONG* pDisplay) { return E_NOTIMPL; } HRESULT CNetFolder::_GetDefaultColumnState(UINT cColumns, UINT iColumn, DWORD* pdwState) { *pdwState = 0; HRESULT hr = S_OK; if (iColumn < cColumns) { *pdwState = s_net_cols[iColumn].csFlags; if (iColumn >= 1) *pdwState |= SHCOLSTATE_SLOW; // comment is slow for net root } else { hr = E_INVALIDARG; } return hr; } STDMETHODIMP CNetFolder::GetDetailsEx(LPCITEMIDLIST pidl, const SHCOLUMNID* pscid, VARIANT* pv) { HRESULT hr = E_NOTIMPL; LPCIDNETRESOURCE pidn = NET_IsValidID(pidl); if (pidn) { if (IsEqualSCID(*pscid, SCID_NETRESOURCE)) { // Office calls SHGetDataFromIDList() with a large buffer to hold all // of the strings in the NETRESOURCE structure, so we need to make sure // that our variant can hold enough data to pass back to it: BYTE rgBuffer[sizeof(NETRESOURCEW) + (4 * MAX_PATH * sizeof(WCHAR))]; hr = _GetNetResource(pidn, (NETRESOURCEW*) rgBuffer, sizeof(rgBuffer)); if (SUCCEEDED(hr)) { hr = InitVariantFromBuffer(pv, rgBuffer, sizeof(rgBuffer)); if (SUCCEEDED(hr)) { // Fixup pointers in structure to point within the variant // instead of our stack variable (rgBuffer): ASSERT(pv->vt == (VT_ARRAY | VT_UI1)); NETRESOURCEW* pnrw = (NETRESOURCEW*) pv->parray->pvData; if (pnrw->lpLocalName) { pnrw->lpLocalName = (LPWSTR) ((BYTE*) pnrw + ((BYTE*) pnrw->lpLocalName - rgBuffer)); } if (pnrw->lpRemoteName) { pnrw->lpRemoteName = (LPWSTR) ((BYTE*) pnrw + ((BYTE*) pnrw->lpRemoteName - rgBuffer)); } if (pnrw->lpComment) { pnrw->lpComment = (LPWSTR) ((BYTE*) pnrw + ((BYTE*) pnrw->lpComment - rgBuffer)); } if (pnrw->lpProvider) { pnrw->lpProvider = (LPWSTR) ((BYTE*) pnrw + ((BYTE*) pnrw->lpProvider - rgBuffer)); } } } } else if (IsEqualSCID(*pscid, SCID_DESCRIPTIONID)) { SHDESCRIPTIONID did; switch(SIL_GetType(pidl) & SHID_TYPEMASK) { case SHID_NET_DOMAIN: did.dwDescriptionId = SHDID_NET_DOMAIN; break; case SHID_NET_SERVER: did.dwDescriptionId = SHDID_NET_SERVER; break; case SHID_NET_SHARE: did.dwDescriptionId = SHDID_NET_SHARE; break; case SHID_NET_RESTOFNET: did.dwDescriptionId = SHDID_NET_RESTOFNET; break; default: did.dwDescriptionId = SHDID_NET_OTHER; break; } did.clsid = CLSID_NULL; hr = InitVariantFromBuffer(pv, &did, sizeof(did)); } else if (IsEqualSCID(*pscid, SCID_Comment)) { TCHAR szTemp[MAX_PATH]; hr = InitVariantFromStr(pv, NET_CopyComment(pidn, szTemp, ARRAYSIZE(szTemp))); } else if (IsEqualSCID(*pscid, SCID_NAME)) { TCHAR szTemp[MAX_PATH]; hr = InitVariantFromStr(pv, NET_CopyResName(pidn, szTemp, ARRAYSIZE(szTemp))); } } else { IShellFolder2* psfFiles; hr = v_GetFileFolder(&psfFiles); if (SUCCEEDED(hr)) hr = psfFiles->GetDetailsEx(pidl, pscid, pv); } return hr; } HRESULT CNetFolder::_GetDetailsOf(UINT cColumns, LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *pDetails) { HRESULT hr = S_OK; pDetails->str.uType = STRRET_CSTR; pDetails->str.cStr[0] = 0; if (NULL == pidl) { hr = GetDetailsOfInfo(s_net_cols, cColumns, iColumn, pDetails); } else { SHCOLUMNID scid; hr = MapColumnToSCID(iColumn, &scid); if (SUCCEEDED(hr)) { VARIANT var; hr = GetDetailsEx(pidl, &scid, &var); if (SUCCEEDED(hr)) { TCHAR szTemp[MAX_PATH]; hr = SHFormatForDisplay(scid.fmtid, scid.pid, (PROPVARIANT*)&var, PUIFFDF_DEFAULT, szTemp, ARRAYSIZE(szTemp)); if (SUCCEEDED(hr)) { hr = StringToStrRet(szTemp, &pDetails->str); } VariantClear(&var); } } } return hr; } HRESULT CNetFolder::_MapColumnToSCID(UINT cColumns, UINT iColumn, SHCOLUMNID* pscid) { return MapColumnToSCIDImpl(s_net_cols, cColumns, iColumn, pscid); } // IPersist methods STDMETHODIMP CNetFolder::GetClassID(CLSID* pCLSID) { switch (_uDisplayType) { case RESOURCEDISPLAYTYPE_ROOT: *pCLSID = CLSID_NetworkRoot; break; case RESOURCEDISPLAYTYPE_SERVER: *pCLSID = CLSID_NetworkServer; break; case RESOURCEDISPLAYTYPE_DOMAIN: *pCLSID = CLSID_NetworkDomain; break; case RESOURCEDISPLAYTYPE_SHARE: *pCLSID = CLSID_NetworkShare; break; default: *pCLSID = CLSID_NULL; break; } return S_OK; } // IPersistFolder method STDMETHODIMP CNetFolder::Initialize(LPCITEMIDLIST pidl) { ILFree(_pidl); ILFree(_pidlTarget); _pidl = _pidlTarget = NULL; return SHILClone(pidl, &_pidl); } // IPersistFolder2 method STDMETHODIMP CNetFolder::GetCurFolder(LPITEMIDLIST* ppidl) { return GetCurFolderImpl(_pidl, ppidl); } // IPersistFolder3 methods STDMETHODIMP CNetFolder::InitializeEx(IBindCtx *pbc, LPCITEMIDLIST pidlRoot, const PERSIST_FOLDER_TARGET_INFO *pfti) { ILFree(_pidl); ILFree(_pidlTarget); _pidl = _pidlTarget = NULL; HRESULT hr = SHILClone(pidlRoot, &_pidl); if (SUCCEEDED(hr) && pfti && pfti->pidlTargetFolder) { hr = SHILClone(pfti->pidlTargetFolder, &_pidlTarget); } return hr; } STDMETHODIMP CNetFolder::GetFolderTargetInfo(PERSIST_FOLDER_TARGET_INFO *pfti) { HRESULT hr = S_OK; ZeroMemory(pfti, sizeof(*pfti)); if (_pidlTarget) hr = SHILClone(_pidlTarget, &pfti->pidlTargetFolder); pfti->dwAttributes = FILE_ATTRIBUTE_DIRECTORY; // maybe add system? pfti->csidl = -1; return hr; } // IShellIconOverlay HRESULT CNetFolder::_GetIconOverlayInfo(LPCIDNETRESOURCE pidn, int *pIndex, DWORD dwFlags) { HRESULT hr = E_FAIL; // // For netshare objects we want to get the icon overlay. // If the share is "pinned" to be available offline it will // have the "Offline Files" overlay. // if (RESOURCEDISPLAYTYPE_SHARE == NET_GetDisplayType(pidn)) { TCHAR szPath[MAX_PATH]; hr = _GetPathForItem(pidn, szPath); if (SUCCEEDED(hr)) { IShellIconOverlayManager *psiom; hr = GetIconOverlayManager(&psiom); if (SUCCEEDED(hr)) { WCHAR szPathW[MAX_PATH]; SHTCharToUnicode(szPath, szPathW, ARRAYSIZE(szPathW)); hr = psiom->GetFileOverlayInfo(szPathW, 0, pIndex, dwFlags); psiom->Release(); } } } return hr; } STDMETHODIMP CNetFolder::GetOverlayIndex(LPCITEMIDLIST pidl, int *pIndex) { HRESULT hr = E_FAIL; LPCIDNETRESOURCE pidn = NET_IsValidID(pidl); if (NULL != pidn) { hr = _GetIconOverlayInfo(pidn, pIndex, SIOM_OVERLAYINDEX); } return hr; } STDMETHODIMP CNetFolder::GetOverlayIconIndex(LPCITEMIDLIST pidl, int *pIndex) { HRESULT hr = E_FAIL; LPCIDNETRESOURCE pidn = NET_IsValidID(pidl); if (NULL != pidn) { hr = _GetIconOverlayInfo(pidn, pIndex, SIOM_ICONINDEX); } return hr; } // // Helper function to allow external callers to query information from a // network pidl... // // NOTE NOTE - This function returns a NETRESOURCE structure whose string // pointers are not valid. On Win95 they were pointers back into the pidl's // strings (even though the strings were copied into the supplied pv buffer.) // Now we make the pointers really point into the buffer. // HRESULT CNetFolder::_GetNetResource(LPCIDNETRESOURCE pidn, NETRESOURCEW* pnr, int cb) { TCHAR szStrings[3][MAX_PATH]; LPWSTR psz, lpsz[3] = {NULL, NULL, NULL}; int i, cchT; if (cb < sizeof(*pnr)) return DISP_E_BUFFERTOOSMALL; ZeroMemory(pnr, cb); NET_CopyResName(pidn, szStrings[0], ARRAYSIZE(szStrings[0])); NET_CopyComment(pidn, szStrings[1], ARRAYSIZE(szStrings[1])); _GetProvider(pidn, NULL, szStrings[2], ARRAYSIZE(szStrings[2])); // Fill in some of the stuff first. // pnr->dwScope = 0; pnr->dwType = NET_GetType(pidn); pnr->dwDisplayType = NET_GetDisplayType(pidn); pnr->dwUsage = NET_GetUsage(pidn); // pnr->lpLocalName = NULL; // Now lets copy the strings into the buffer and make the pointers // relative to the buffer... psz = (LPWSTR)(pnr + 1); cb -= sizeof(*pnr); for (i = 0; i < ARRAYSIZE(szStrings); i++) { if (*szStrings[i]) { cchT = (lstrlen(szStrings[i]) + 1) * sizeof(TCHAR); if (cchT <= cb) { SHTCharToUnicode(szStrings[i], psz, cb/sizeof(TCHAR)); lpsz[i] = psz; psz += cchT; cb -= cchT * sizeof(TCHAR); } else { // A hint that the structure is ok, // but the strings are missing SetLastError(ERROR_INSUFFICIENT_BUFFER); } } } pnr->lpRemoteName = lpsz[0]; pnr->lpComment = lpsz[1]; pnr->lpProvider = lpsz[2]; return S_OK; } // // This function opens a reg. database key based on the "network provider". // // Returns: hkey // // The caller is responsibe to close the key by calling RegCloseKey(). // HKEY CNetFolder::_OpenProviderKey(LPCIDNETRESOURCE pidn) { TCHAR szProvider[MAX_PATH]; if (_GetProvider(pidn, NULL, szProvider, ARRAYSIZE(szProvider))) { HKEY hkeyProgID = NULL; ReplaceSpacesWithUnderscore(szProvider); RegOpenKey(HKEY_CLASSES_ROOT, szProvider, &hkeyProgID); return hkeyProgID; } return NULL; } // // This function opens a reg. database key based on the network provider type. // The type is a number that is not localized, as opposed to the provider name // which may be localized. // // Arguments: // pidlAbs -- Absolute IDList to a network resource object. // // Returns: hkey // // Notes: // The caller is responsible to close the key by calling RegCloseKey(). // HKEY CNetFolder::_OpenProviderTypeKey(LPCIDNETRESOURCE pidn) { HKEY hkeyProgID = NULL; TCHAR szProvider[MAX_PATH]; if (_GetProvider(pidn, NULL, szProvider, ARRAYSIZE(szProvider))) { // Now that we've got the provider name, get the provider id. DWORD dwType; if (WNetGetProviderType(szProvider, &dwType) == WN_SUCCESS) { // convert nis.wNetType to a string, and then open the key // HKEY_CLASSES_ROOT\Network\Type\ TCHAR szRegValue[MAX_PATH]; wsprintf(szRegValue, TEXT("Network\\Type\\%d"), HIWORD(dwType)); RegOpenKey(HKEY_CLASSES_ROOT, szRegValue, &hkeyProgID); } } return hkeyProgID; } HRESULT CNetFolder::_OpenKeys(LPCIDNETRESOURCE pidn, HKEY ahkeys[NKID_COUNT]) { // See if there is a key specific to the type of Network object... COMPILETIME_ASSERT(6 == NKID_COUNT); ahkeys[0] = ahkeys[1] = ahkeys[2] = ahkeys[3] = ahkeys[4] = ahkeys[5] = NULL; ahkeys[NKID_PROVIDERTYPE] = _OpenProviderTypeKey(pidn); ahkeys[NKID_PROVIDER] = _OpenProviderKey(pidn); if (NET_GetDisplayType(pidn) == RESOURCEDISPLAYTYPE_SHARE) RegOpenKey(HKEY_CLASSES_ROOT, TEXT("NetShare"), &ahkeys[NKID_NETCLASS]); else if (NET_GetDisplayType(pidn) == RESOURCEDISPLAYTYPE_SERVER) RegOpenKey(HKEY_CLASSES_ROOT, TEXT("NetServer"), &ahkeys[NKID_NETCLASS]); RegOpenKey(HKEY_CLASSES_ROOT, TEXT("Network"), &ahkeys[NKID_NETWORK]); // make sure it is not a printer before adding "Folder" or "directory" if (!_IsPrintShare(pidn)) { // Shares should also support directory stuff... if (NET_GetDisplayType(pidn) == RESOURCEDISPLAYTYPE_SHARE) RegOpenKey(HKEY_CLASSES_ROOT, TEXT("Directory"), &ahkeys[NKID_DIRECTORY]); RegOpenKey(HKEY_CLASSES_ROOT, c_szFolderClass, &ahkeys[NKID_FOLDER]); } return S_OK; } #define WNFMT_PLATFORM WNFMT_ABBREVIATED | WNFMT_INENUM // // This function retrieves the formatted (display) name of the specified network object. // HRESULT CNetFolder::_GetFormatName(LPCIDNETRESOURCE pidn, STRRET* pStrRet) { HRESULT hr = E_FAIL; TCHAR szName[MAX_PATH]; NET_CopyResName(pidn, szName, ARRAYSIZE(szName)); if (NET_GetDisplayType(pidn) == RESOURCEDISPLAYTYPE_SERVER) { TCHAR szMachineName[MAX_PATH]; TCHAR szComment[MAX_PATH]; NET_CopyResName(pidn, szMachineName, ARRAYSIZE(szMachineName)); NET_CopyComment(pidn, szComment, ARRAYSIZE(szComment)); hr = SHBuildDisplayMachineName(szMachineName, szComment, szName, ARRAYSIZE(szName)); } if (FAILED(hr) && (NET_GetDisplayType(pidn) != RESOURCEDISPLAYTYPE_ROOT) && (NET_GetDisplayType(pidn) != RESOURCEDISPLAYTYPE_NETWORK)) { TCHAR szDisplayName[MAX_PATH], szProvider[MAX_PATH]; DWORD dwSize = ARRAYSIZE(szDisplayName); LPCTSTR pszProvider = _GetProvider(pidn, NULL, szProvider, ARRAYSIZE(szProvider)); if (pszProvider) { DWORD dwRes = WNetFormatNetworkName(pszProvider, szName, szDisplayName, &dwSize, WNFMT_PLATFORM, 8 + 1 + 3); if (dwRes == WN_SUCCESS) lstrcpy(szName, szDisplayName); } } return StringToStrRet(szName, pStrRet); } // // resolve non-UNC share names (novell) to UNC style names // // returns: // TRUE translated the name // FALSE didn't translate (maybe error case) // // WARNING: If we use too much stack space then we will cause // faults by over flowing the stack. Millennium #94818 BOOL CNetFolder::_GetPathForShare(LPCIDNETRESOURCE pidn, LPTSTR pszPath) { BOOL fRet = FALSE; *pszPath = TEXT('\0'); LPTSTR pszAccessName = (LPTSTR)LocalAlloc(LPTR, sizeof(TCHAR) * MAX_PATH * 3); if (pszAccessName) { LPTSTR pszRemoteName = pszAccessName + MAX_PATH; LPTSTR pszProviderName = pszRemoteName + MAX_PATH; NET_CopyResName(pidn, pszRemoteName, MAX_PATH); if (NULL != _pszResName) { // // Combine the folder name with the share name // to create a UNC path. // // Borrow the pszProviderName buffer for a bit. // PathCombine(pszProviderName, _pszResName, pszRemoteName); // // To be safe: UNC prefix implies that name is available using FS access // Theoretically it also should be routed to MPR, but it is late to do this // if (PathIsUNC(pszProviderName)) { lstrcpy(pszPath, pszProviderName); fRet = FALSE; } else { pszProviderName[0] = TEXT('\0'); } } if (!*pszPath) { // Check cache ENTERCRITICAL; if (lstrcmpi(g_szLastAttemptedJunctionName, pszRemoteName) == 0) { // cache hit lstrcpy(pszPath, g_szLastResolvedJunctionName); fRet = TRUE; } LEAVECRITICAL; } if (!*pszPath) { NETRESOURCE nr = {0}; DWORD err, dwRedir, dwResult; DWORD cchAccessName; nr.lpRemoteName = pszRemoteName; nr.lpProvider = (LPTSTR) _GetProvider(pidn, NULL, pszProviderName, MAX_PATH); nr.dwType = NET_GetType(pidn); nr.dwUsage = NET_GetUsage(pidn); nr.dwDisplayType = NET_GetDisplayType(pidn); dwRedir = CONNECT_TEMPORARY; // Prepare access name buffer and net resource request buffer // cchAccessName = MAX_PATH; pszAccessName[0] = 0; err = WNetUseConnection(NULL, &nr, NULL, NULL, dwRedir, pszAccessName, &cchAccessName, &dwResult); if ((WN_SUCCESS != err) || !pszAccessName[0]) { // perf idea: might be good to cache the last failed junction bind // and early out on the next attempt. One slight problem this // might encounter: what if we cache a failure, the user changes // state to fix the problem, but we hit our failure cache... // lstrcpy(pszPath, pszRemoteName); fRet = FALSE; } else { // Get the return name lstrcpy(pszPath, pszAccessName); fRet = TRUE; // Update success cache entry ENTERCRITICAL; lstrcpy(g_szLastAttemptedJunctionName, pszRemoteName); lstrcpy(g_szLastResolvedJunctionName, pszAccessName); LEAVECRITICAL; } } LocalFree(pszAccessName); } return fRet; } // in: // pidn may be multi-level net resource pidl like // [entire net] [provider] [server] [share] [... file sys] // or [server] [share] [... file sys] HRESULT CNetFolder::_GetPathForItem(LPCIDNETRESOURCE pidn, LPTSTR pszPath) { *pszPath = 0; // loop down for (; !ILIsEmpty((LPCITEMIDLIST)pidn) ; pidn = (LPCIDNETRESOURCE)_ILNext((LPCITEMIDLIST)pidn)) { if (NET_GetFlags(pidn) & SHID_JUNCTION) // \\server\share or strike/sys { _GetPathForShare(pidn, pszPath); break; // below this we don't know about any of the PIDLs } else { // if this is entire network then return the canonical name for // this object. if (NET_GetDisplayType(pidn) == RESOURCEDISPLAYTYPE_ROOT) StrCpyN(pszPath, TEXT("EntireNetwork"), MAX_PATH); else NET_CopyResName(pidn, pszPath, MAX_PATH); } } return *pszPath ? S_OK : E_NOTIMPL; } HRESULT CNetFolder::_GetPathForItemW(LPCIDNETRESOURCE pidn, LPWSTR pszPath) { return _GetPathForItem(pidn, pszPath); } // in: // pidl // // takes the last items and create a folder for it, assuming the first section is the // used to initialze. the riid and ppv are used to return an object. // HRESULT CNetFolder::_CreateFolderForItem(LPBC pbc, LPCITEMIDLIST pidl, LPCITEMIDLIST pidlTarget, LPCIDNETRESOURCE pidnForProvider, REFIID riid, void **ppv) { LPCITEMIDLIST pidlLast = ILFindLastID(pidl); LPCIDNETRESOURCE pidn = NET_IsValidID(pidlLast); if (!pidn) return E_INVALIDARG; HRESULT hr; if (NET_IsRemoteFld(pidn)) { // note: I think this is dead functionality. it was used in NT4 but we can't find // the impl of this CLSID_Remote anymore... IPersistFolder * ppf; hr = SHCoCreateInstance(NULL, &CLSID_Remote, NULL, IID_PPV_ARG(IPersistFolder, &ppf)); if (SUCCEEDED(hr)) { hr= ppf->Initialize(pidl); if (SUCCEEDED(hr)) hr = ppf->QueryInterface(riid, ppv); ppf->Release(); } } else if (NET_GetFlags(pidn) & SHID_JUNCTION) // \\server\share or strike/sys { PERSIST_FOLDER_TARGET_INFO * ppfti = (PERSIST_FOLDER_TARGET_INFO *) LocalAlloc(LPTR, sizeof(PERSIST_FOLDER_TARGET_INFO)); if (ppfti) { ppfti->pidlTargetFolder = (LPITEMIDLIST)pidlTarget; _GetPathForItemW(pidn, ppfti->szTargetParsingName); ppfti->csidl = -1; ppfti->dwAttributes = FILE_ATTRIBUTE_DIRECTORY; // maybe add system? hr = CFSFolder_CreateFolder(NULL, pbc, pidl, ppfti, riid, ppv); LocalFree(ppfti); } else hr = E_OUTOFMEMORY; } else { TCHAR szPath[MAX_PATH]; NET_CopyResName(pidn, szPath, ARRAYSIZE(szPath)); hr = _CreateInstance(pidl, pidlTarget, NET_GetDisplayType(pidn), pidnForProvider, szPath, riid, ppv); } return hr; } // get the provider for an item or the folder itself. since some items don't have the // provider stored we fall back to the folder to get the provider in that case // // in: // pidn item to get provider for. if NULL get provider for the folder // pbc IBindCtx to get provider for. if NULL get provider from pidn or folder. // // returns: // NULL no provider in the item or the folder // non NULL address of passed in buffer LPCTSTR CNetFolder::_GetProvider(LPCIDNETRESOURCE pidn, IBindCtx *pbc, LPTSTR pszProvider, UINT cchProvider) { // attempt to get the provider from the property bag IPropertyBag *ppb; if (pbc && SUCCEEDED(pbc->GetObjectParam(STR_PARSE_NETFOLDER_INFO, (IUnknown**)&ppb))) { HRESULT hr = SHPropertyBag_ReadStr(ppb, STR_PARSE_NETFOLDER_PROVIDERNAME, pszProvider, cchProvider); ppb->Release(); if (SUCCEEDED(hr) && *pszProvider) { return pszProvider; } } // from the IDLIST if (pidn && NET_CopyProviderName(pidn, pszProvider, cchProvider)) return pszProvider; // from our state if (_pidnForProvider) { NET_CopyProviderName(_pidnForProvider, pszProvider, cchProvider); return pszProvider; } *pszProvider = 0; return NULL; } const NETPROVIDERS c_rgProviderMap[] = { { TEXT("Microsoft Network"), HIWORD(WNNC_NET_LANMAN) }, { TEXT("NetWare"), HIWORD(WNNC_NET_NETWARE) } }; // construct a net idlist either copying the existing data from a pidl or // from a NETRESOURCE structure HRESULT CNetFolder::_CreateNetIDList(LPIDNETRESOURCE pidnIn, LPCTSTR pszName, LPCTSTR pszProvider, LPCTSTR pszComment, LPITEMIDLIST *ppidl) { LPBYTE pb; UINT cbmkid = sizeof(IDNETRESOURCE) - sizeof(CHAR); UINT cchName, cchProvider, cchComment, cbProviderType = 0; LPIDNETRESOURCE pidn; WORD wNetType = 0; BOOL fUnicode = FALSE; UINT cchAnsiName, cchAnsiProvider, cchAnsiComment; CHAR szAnsiName[MAX_PATH], szAnsiProvider[MAX_PATH], szAnsiComment[MAX_PATH]; ASSERT(ppidl != NULL); *ppidl = NULL; if (!pszName) pszName = c_szNULL; // For now put in an empty string... if (pszProvider) cbProviderType += sizeof(WORD); // Win9x shipped with one set of provider name which are // different on NT. Therefore lets convert the NT one to // something that Win9x can understand. if (pszProvider) { cbProviderType = sizeof(WORD); DWORD dwType, dwRes = WNetGetProviderType(pszProvider, &dwType); if (dwRes == WN_SUCCESS) { wNetType = HIWORD(dwType); for (int i = 0; i < ARRAYSIZE(c_rgProviderMap); i++) { if (c_rgProviderMap[i].wNetType == wNetType) { pszProvider = c_rgProviderMap[i].lpName; break; } } } } // compute the string lengths ready to build an IDLIST cchName = lstrlen(pszName)+1; cchProvider = pszProvider ? lstrlen(pszProvider)+1 : 0; cchComment = pszComment ? lstrlen(pszComment)+1 : 0; cchAnsiName = 0; cchAnsiProvider = 0; cchAnsiComment = 0; fUnicode = !DoesStringRoundTrip(pszName, szAnsiName, ARRAYSIZE(szAnsiProvider)); cchAnsiName = lstrlenA(szAnsiName)+1; if (pszProvider) { fUnicode |= !DoesStringRoundTrip(pszProvider, szAnsiProvider, ARRAYSIZE(szAnsiProvider)); cchAnsiProvider = lstrlenA(szAnsiProvider)+1; } if (pszComment) { fUnicode |= !DoesStringRoundTrip(pszComment, szAnsiComment, ARRAYSIZE(szAnsiComment)); cchAnsiComment = lstrlenA(szAnsiComment)+1; } // allocate and fill the IDLIST header cbmkid += cbProviderType+cchAnsiName + cchAnsiProvider + cchAnsiComment; if (fUnicode) cbmkid += (sizeof(WCHAR)*(cchName+cchProvider+cchComment)); pidn = (LPIDNETRESOURCE)_ILCreate(cbmkid + sizeof(USHORT)); if (!pidn) return E_OUTOFMEMORY; pidn->cb = (WORD)cbmkid; pidn->bFlags = pidnIn->bFlags; pidn->uType = pidnIn->uType; pidn->uUsage = pidnIn->uUsage; if (pszProvider) pidn->uUsage |= NET_HASPROVIDER; if (pszComment) pidn->uUsage |= NET_HASCOMMENT; pb = (LPBYTE) pidn->szNetResName; // // write the ANSI strings into the IDLIST // StrCpyA((PSTR) pb, szAnsiName); pb += cchAnsiName; if (pszProvider) { StrCpyA((PSTR) pb, szAnsiProvider); pb += cchAnsiProvider; } if (pszComment) { StrCpyA((PSTR) pb, szAnsiComment); pb += cchAnsiComment; } // if we are going to be UNICODE then lets write those strings also. // Note that we must use unaligned string copies since the is no // promse that the ANSI strings will have an even number of characters // in them. if (fUnicode) { pidn->uUsage |= NET_UNICODE; ualstrcpyW((UNALIGNED WCHAR *)pb, pszName); pb += cchName*sizeof(WCHAR); if (pszProvider) { ualstrcpyW((UNALIGNED WCHAR *)pb, pszProvider); pb += cchProvider*sizeof(WCHAR); } if (pszComment) { ualstrcpyW((UNALIGNED WCHAR *)pb, pszComment); pb += cchComment*sizeof(WCHAR); } } // // and the trailing provider type // if (cbProviderType) { // Store the provider type pb = (LPBYTE)pidn + pidn->cb - sizeof(WORD); *((UNALIGNED WORD *)pb) = wNetType; } *ppidl = (LPITEMIDLIST)pidn; return S_OK; } // wrapper for converting a NETRESOURCE into an IDLIST via _CreateNetPidl HRESULT CNetFolder::_NetResToIDList(NETRESOURCE *pnr, BOOL fKeepNullRemoteName, BOOL fKeepProviderName, BOOL fKeepComment, LPITEMIDLIST *ppidl) { NETRESOURCE nr = *pnr; LPITEMIDLIST pidl; LPTSTR pszName, pszProvider, pszComment; IDNETRESOURCE idn; LPTSTR psz; if (ppidl) *ppidl = NULL; switch (pnr->dwDisplayType) { case RESOURCEDISPLAYTYPE_NETWORK: pszName = pnr->lpProvider; break; case RESOURCEDISPLAYTYPE_ROOT: pszName =pnr->lpComment; break; default: { pszName = pnr->lpRemoteName; if (!fKeepNullRemoteName && (!pszName || !*pszName)) return E_FAIL; if (pszName && *pszName) { psz = (LPTSTR)SkipServerSlashes(pnr->lpRemoteName); if ( *psz ) PathMakePretty(psz); } } break; } pszProvider = fKeepProviderName ? nr.lpProvider:NULL; pszComment = fKeepComment ? nr.lpComment:NULL; idn.bFlags = (BYTE)(SHID_NET | (pnr->dwDisplayType & 0x0f)); idn.uType = (BYTE)(pnr->dwType & 0x0f); idn.uUsage = (BYTE)(pnr->dwUsage & 0x0f); // Is the current resource a share of some kind and not a container if ((pnr->dwDisplayType == RESOURCEDISPLAYTYPE_SHARE || pnr->dwDisplayType == RESOURCEDISPLAYTYPE_SHAREADMIN) && !(pnr->dwUsage & RESOURCEUSAGE_CONTAINER)) { // If so, remember to delegate children of this folder to FSFolder idn.bFlags |= (BYTE)SHID_JUNCTION; // \\server\share type thing } HRESULT hr = _CreateNetIDList(&idn, pszName, pszProvider, pszComment, &pidl); if (SUCCEEDED(hr)) { if (ppidl) *ppidl = pidl; } return hr; } HRESULT CNetFolder::_CreateEntireNetwork(LPITEMIDLIST *ppidl) { TCHAR szPath[MAX_PATH]; NETRESOURCE nr = {0}; // We need to add the Rest of network entry. This is psuedo // bogus, as we should either always do it ourself or have // MPR always do it, but here it goes... LoadString(HINST_THISDLL, IDS_RESTOFNET, szPath, ARRAYSIZE(szPath)); nr.dwDisplayType = RESOURCEDISPLAYTYPE_ROOT; nr.dwType = RESOURCETYPE_ANY; nr.dwUsage = RESOURCEUSAGE_CONTAINER; nr.lpComment = szPath; return _NetResToIDList(&nr, FALSE, FALSE, FALSE, ppidl); } HRESULT CNetFolder::_CreateEntireNetworkFullIDList(LPITEMIDLIST *ppidl) { // CLSID_NetworkPlaces\EntireNetwork return SHILCreateFromPath(TEXT("::{208D2C60-3AEA-1069-A2D7-08002B30309D}\\EntireNetwork"), ppidl, NULL); } // // To be called back from within CDefFolderMenu // STDAPI CNetwork_DFMCallBackBG(IShellFolder *psf, HWND hwnd, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam) { HRESULT hr = S_OK; CNetFolder *pThis = FolderToNetFolder(psf); if (NULL == pThis) return E_UNEXPECTED; switch(uMsg) { case DFM_MERGECONTEXTMENU_BOTTOM: if (!(wParam & (CMF_VERBSONLY | CMF_DVFILE))) { CDefFolderMenu_MergeMenu(HINST_THISDLL, POPUP_PROPERTIES_BG, 0, (LPQCMINFO)lParam); } break; case DFM_GETHELPTEXT: LoadStringA(HINST_THISDLL, LOWORD(wParam) + IDS_MH_FSIDM_FIRST, (LPSTR)lParam, HIWORD(wParam)); break; case DFM_GETHELPTEXTW: LoadStringW(HINST_THISDLL, LOWORD(wParam) + IDS_MH_FSIDM_FIRST, (LPWSTR)lParam, HIWORD(wParam)); break; case DFM_INVOKECOMMAND: switch (wParam) { case FSIDM_PROPERTIESBG: hr = SHPropertiesForPidl(hwnd, pThis->_pidl, (LPCTSTR)lParam); break; default: // This is one of view menu items, use the default code. hr = S_FALSE; break; } break; default: hr = E_NOTIMPL; break; } return hr; } // // To be called back from within CDefFolderMenu // STDAPI CNetFolder::DFMCallBack(IShellFolder* psf, HWND hwnd, IDataObject* pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam) { HRESULT hr = S_OK; switch(uMsg) { case DFM_MERGECONTEXTMENU: if (pdtobj) { STGMEDIUM medium; LPIDA pida; LPQCMINFO pqcm = (LPQCMINFO)lParam; UINT idCmdBase = pqcm->idCmdFirst; // must be called before merge CDefFolderMenu_MergeMenu(HINST_THISDLL, POPUP_NETWORK_ITEM, 0, pqcm); pida = DataObj_GetHIDA(pdtobj, &medium); if (pida) { if (pida->cidl > 0) { LPIDNETRESOURCE pidn = (LPIDNETRESOURCE)IDA_GetIDListPtr(pida, 0); // Only enable "connect" command if the first one is a share. if (pidn) { ULONG rgf = 0; if(NET_GetFlags(pidn) & SHID_JUNCTION && !SHRestricted(REST_NONETCONNECTDISCONNECT)) { EnableMenuItem(pqcm->hmenu, idCmdBase + FSIDM_CONNECT, MF_CHECKED | MF_BYCOMMAND); } } } HIDA_ReleaseStgMedium(pida, &medium); } } break; case DFM_GETHELPTEXT: LoadStringA(HINST_THISDLL, LOWORD(wParam) + IDS_MH_FSIDM_FIRST, (LPSTR)lParam, HIWORD(wParam)); break; case DFM_GETHELPTEXTW: LoadStringW(HINST_THISDLL, LOWORD(wParam) + IDS_MH_FSIDM_FIRST, (LPWSTR)lParam, HIWORD(wParam)); break; case DFM_INVOKECOMMAND: switch(wParam) { case DFM_CMD_PROPERTIES: hr = SHLaunchPropSheet(_PropertiesThreadProc, pdtobj, (LPCTSTR)lParam, psf, NULL); break; case DFM_CMD_LINK: { hr = S_FALSE; // do the default shortcut stuff CNetFolder *pThis = FolderToNetFolder(psf); if (pThis) { // net hood special case. in this case we want to create the shortuct // in the net hood, not offer to put this on the desktop IShellFolder2* psfFiles; if (SUCCEEDED(pThis->v_GetFileFolder(&psfFiles))) { CFSFolder_CreateLinks(hwnd, psfFiles, pdtobj, (LPCTSTR)lParam, CMIC_MASK_FLAG_NO_UI); hr = S_OK; // we created the links } } } break; case FSIDM_CONNECT: if (pdtobj) { STGMEDIUM medium; LPIDA pida = DataObj_GetHIDA(pdtobj, &medium); if (pida) { for (UINT i = 0; i < pida->cidl; i++) { LPIDNETRESOURCE pidn = (LPIDNETRESOURCE)IDA_GetIDListPtr(pida, i); // Only execute "connect" on shares. if (NET_GetFlags(pidn) & SHID_JUNCTION) { TCHAR szName[MAX_PATH]; LPTSTR pszName = NET_CopyResName(pidn, szName, ARRAYSIZE(szName)); DWORD err = SHStartNetConnectionDialog(hwnd, pszName, RESOURCETYPE_DISK); DebugMsg(DM_TRACE, TEXT("CNet FSIDM_CONNECT (%s, %x)"), szName, err); // events will get generated automatically } } HIDA_ReleaseStgMedium(pida, &medium); } } break; default: // This is one of view menu items, use the default code. hr = S_FALSE; break; } break; default: hr = E_NOTIMPL; break; } return hr; } STDAPI CNetFolder::PrinterDFMCallBack(IShellFolder* psf, HWND hwnd, IDataObject* pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam) { HRESULT hr = S_OK; switch(uMsg) { case DFM_MERGECONTEXTMENU: // // Returning S_FALSE indicates no need to get verbs from // extensions. // hr = S_FALSE; break; // if anyone hooks our context menu, we want to be on top (Open) case DFM_MERGECONTEXTMENU_TOP: if (pdtobj) { LPQCMINFO pqcm = (LPQCMINFO)lParam; // insert verbs CDefFolderMenu_MergeMenu(HINST_THISDLL, POPUP_NETWORK_PRINTER, 0, pqcm); SetMenuDefaultItem(pqcm->hmenu, 0, MF_BYPOSITION); } break; case DFM_GETHELPTEXT: LoadStringA(HINST_THISDLL, LOWORD(wParam) + IDS_MH_FSIDM_FIRST, (LPSTR)lParam, HIWORD(wParam)); break; case DFM_GETHELPTEXTW: LoadStringW(HINST_THISDLL, LOWORD(wParam) + IDS_MH_FSIDM_FIRST, (LPWSTR)lParam, HIWORD(wParam)); break; case DFM_INVOKECOMMAND: switch (wParam) { case DFM_CMD_PROPERTIES: hr = SHLaunchPropSheet(_PropertiesThreadProc, pdtobj, (LPCTSTR)lParam, psf, NULL); break; case DFM_CMD_LINK: // do the default create shortcut crap return S_FALSE; case FSIDM_OPENPRN: case FSIDM_NETPRN_INSTALL: { STGMEDIUM medium; LPIDA pida = DataObj_GetHIDA(pdtobj, &medium); if (pida) { UINT action; // set up the operation we are going to perform switch (wParam) { case FSIDM_OPENPRN: action = PRINTACTION_OPENNETPRN; break; case FSIDM_NETPRN_INSTALL: action = PRINTACTION_NETINSTALL; break; default: // FSIDM_CONNECT_PRN action = (UINT)-1; break; } for (UINT i = 0; i < pida->cidl; i++) { LPIDNETRESOURCE pidn = (LPIDNETRESOURCE)IDA_GetIDListPtr(pida, i); // Only execute command for a net print share if (_IsPrintShare(pidn)) { TCHAR szName[MAX_PATH]; NET_CopyResName(pidn,szName,ARRAYSIZE(szName)); SHInvokePrinterCommand(hwnd, action, szName, NULL, FALSE); } } // for (i... HIDA_ReleaseStgMedium(pida, &medium); } // if (medium.hGlobal) break; } // case ID_NETWORK_PRINTER_INSTALL, FSIDM_CONNECT_PRN default: hr = E_FAIL; break; } // switch(wparam) break; default: hr = E_NOTIMPL; break; } return hr; } // // REVIEW: Almost identical code in fstreex.c // DWORD CALLBACK CNetFolder::_PropertiesThreadProc(void *pv) { PROPSTUFF* pps = (PROPSTUFF *)pv; ULONG_PTR dwCookie = 0; ActivateActCtx(NULL, &dwCookie); CNetFolder *pThis = FolderToNetFolder(pps->psf); if (pThis) { STGMEDIUM medium; LPIDA pida = DataObj_GetHIDA(pps->pdtobj, &medium); if (pida) { // Yes, do context menu. HKEY ahkeys[NKID_COUNT]; LPCIDNETRESOURCE pnid = (LPCIDNETRESOURCE)IDA_GetIDListPtr(pida, 0); if (pnid) { HRESULT hr = pThis->_OpenKeys(pnid, ahkeys); if (SUCCEEDED(hr)) { LPTSTR pszCaption = SHGetCaption(medium.hGlobal); SHOpenPropSheet(pszCaption, ahkeys, ARRAYSIZE(ahkeys), &CLSID_ShellNetDefExt, pps->pdtobj, NULL, pps->pStartPage); if (pszCaption) SHFree(pszCaption); SHRegCloseKeys(ahkeys, ARRAYSIZE(ahkeys)); } } HIDA_ReleaseStgMedium(pida, &medium); } } return S_OK; } STDAPI CNetFolder::_AttributesCallback(IShellFolder2* psf, LPCITEMIDLIST pidl, ULONG* prgfInOut) { LPCIDNETRESOURCE pidn = (LPCIDNETRESOURCE)pidl; ULONG rgfOut = SFGAO_CANLINK | SFGAO_HASPROPSHEET | SFGAO_HASSUBFOLDER | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR | SFGAO_STORAGEANCESTOR; if (NET_GetFlags(pidn) & SHID_JUNCTION) { if ((NET_GetType(pidn) == RESOURCETYPE_DISK) || (NET_GetType(pidn) == RESOURCETYPE_ANY)) rgfOut |= SFGAO_FILESYSTEM | SFGAO_DROPTARGET | SFGAO_CANCOPY | SFGAO_CANMOVE | SFGAO_STORAGE; else rgfOut &= ~(SFGAO_FILESYSANCESTOR | SFGAO_STORAGEANCESTOR); } if (_IsPrintShare(pidn)) { rgfOut |= SFGAO_DROPTARGET; // for drag and drop printing rgfOut &= ~(SFGAO_FILESYSANCESTOR | SFGAO_STORAGEANCESTOR | SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_HASSUBFOLDER); } if (NET_IsRemoteFld(pidn)) { rgfOut &= ~(SFGAO_FILESYSANCESTOR | SFGAO_STORAGEANCESTOR | SFGAO_FILESYSTEM); } *prgfInOut = rgfOut; return S_OK; } // This is only used by the CNetRootFolder subclass, but because we can only QI for // CLSID_NetFldr, and we can't access protected members of any CNetFolder instance // from a member function of CNetRootFolder, we'll make it belong to CNetFolder HRESULT CALLBACK CNetFolder::_AttributesCallbackRoot(IShellFolder2* psf, LPCITEMIDLIST pidl, ULONG* prgfInOut) { HRESULT hr; CNetFolder* pNetF = FolderToNetFolder(psf); if (pNetF) { if (NET_IsValidID(pidl)) { hr = pNetF->CNetFolder::GetAttributesOf(1, &pidl, prgfInOut); } else { IShellFolder2* psfFiles; hr = pNetF->v_GetFileFolder(&psfFiles); if (SUCCEEDED(hr)) hr = psfFiles->GetAttributesOf(1, &pidl, prgfInOut); } } else hr = E_FAIL; return hr; } // this is called by netfind.c STDAPI CNetwork_EnumSearches(IShellFolder2* psf2, IEnumExtraSearch **ppenum) { *ppenum = NULL; CNetFolder* pNetF = FolderToNetFolder(psf2); return pNetF ? pNetF->EnumSearches(ppenum) : E_INVALIDARG; } // given the resulting ppidl and a pszRest continue to parse through and add in the remainder // of the file system path. HRESULT CNetFolder::_ParseRest(LPBC pbc, LPCWSTR pszRest, LPITEMIDLIST* ppidl, DWORD* pdwAttributes) { HRESULT hr = S_OK; // skip leading \ if there is one present if (pszRest && pszRest[0] == L'\\') pszRest++; if (pszRest && pszRest[0]) { // need to QI to get the agregated case IShellFolder* psfBind; hr = QueryInterface(IID_PPV_ARG(IShellFolder, &psfBind)); if (SUCCEEDED(hr)) { // pass down to pick off stuff below including regitems and file names IShellFolder* psfSub; hr = psfBind->BindToObject(*ppidl, NULL, IID_PPV_ARG(IShellFolder, &psfSub)); if (SUCCEEDED(hr)) { LPITEMIDLIST pidlSubDir; hr = psfSub->ParseDisplayName(NULL, pbc, (LPWSTR)pszRest, NULL, &pidlSubDir, pdwAttributes); if (SUCCEEDED(hr)) { hr = SHILAppend(pidlSubDir, ppidl); } psfSub->Release(); } psfBind->Release(); } } else { if (pdwAttributes) { LPCITEMIDLIST apidlLast[1] = { ILFindLastID(*ppidl) }; hr = GetAttributesOf(1, apidlLast, pdwAttributes); } } return hr; } // generate an IDLIST from the NETRESOURCESTRUCTURE we have by // walking up its parents trying to determine where we // are in the namespace BOOL _GetParentResource(NETRESOURCE *pnr, DWORD *pdwbuf) { if ((pnr->dwDisplayType == RESOURCEDISPLAYTYPE_ROOT) || (WN_SUCCESS != WNetGetResourceParent(pnr, pnr, pdwbuf))) { return FALSE; } return TRUE; } HRESULT CNetFolder::_NetResToIDLists(NETRESOURCE *pnr, DWORD dwbuf, LPITEMIDLIST *ppidl) { HRESULT hr = S_OK; do { LPITEMIDLIST pidlT; hr = _NetResToIDList(pnr, TRUE, TRUE, TRUE, &pidlT); if (SUCCEEDED(hr)) { hr = SHILPrepend(pidlT, ppidl); // NOTE: SHILPrepend frees on failure } } while (SUCCEEDED(hr) && _GetParentResource(pnr, &dwbuf)); return hr; } // get the parsable network name from the object LPTSTR CNetFolder::_GetNameForParsing(LPCWSTR pwszName, LPTSTR pszBuffer, INT cchBuffer, LPTSTR *ppszRegItem) { LPTSTR pszRegItem = NULL; INT cSlashes = 0; *ppszRegItem = NULL; SHUnicodeToTChar(pwszName, pszBuffer, cchBuffer); // remove the trailing \ if there is one, NTLanMan barfs if we pass a string containing it INT cchPath = lstrlen(pszBuffer)-1; if (cchPath > 2) { // We don't need to call CharPrev if cchPath <= 2. // Calling CharPrev is expensive. LPTSTR lpTmp = CharPrev(pszBuffer, pszBuffer + cchPath + 1); if (*lpTmp == TEXT('\\')) *lpTmp = TEXT('\0'); } // lets walk the name, look for \:: squence to signify the start of a regitem name, // and if the number of slashes is > 2 then we should bail LPTSTR pszUNC = pszBuffer+2; while (pszUNC && *pszUNC && (cSlashes < 2)) { if ((pszUNC[0] == TEXT('\\')) && (pszUNC[1] == TEXT(':')) && (pszUNC[2] == TEXT(':'))) { *ppszRegItem = pszUNC; break; } pszUNC = StrChr(pszUNC+1, TEXT('\\')); cSlashes++; } return pszUNC; } HRESULT CNetFolder::_ParseNetName(HWND hwnd, LPBC pbc, LPCWSTR pwszName, ULONG* pchEaten, LPITEMIDLIST *ppidl, DWORD *pdwAttrib) { HRESULT hr; struct _NRTEMP { NETRESOURCE nr; TCHAR szBuffer[1024]; } nrOut = { 0 }; TCHAR szPath[MAX_PATH]; DWORD dwres, dwbuf = sizeof(nrOut.szBuffer); LPTSTR pszServerShare = NULL; LPTSTR pszRestOfName = NULL; LPTSTR pszFakeRestOfName = NULL; LPTSTR pszRegItem = NULL; // validate the name before we start cracking it... pszFakeRestOfName = _GetNameForParsing(pwszName, szPath, ARRAYSIZE(szPath), &pszRegItem); NETRESOURCE nr = { 0 }; nr.lpRemoteName = szPath; nr.dwType = RESOURCETYPE_ANY; TCHAR szProviderTemp[256]; nr.lpProvider = (LPTSTR)_GetProvider(NULL, pbc, szProviderTemp, ARRAYSIZE(szProviderTemp)); dwres = WNetGetResourceInformation(&nr, &nrOut.nr, &dwbuf, &pszRestOfName); if (WN_SUCCESS != dwres) { TCHAR cT; LPTSTR pszTemp; // truncate the string at the \\server\share to try and parse the name, // note at this point if MPR resolves the alias on a Novel server this could // get very confusing (eg. \\strike\foo\bah may resolve to \\string\bla, // yet our concept of what pszRestOfName will be wrong! if (pszFakeRestOfName) { cT = *pszFakeRestOfName; *pszFakeRestOfName = TEXT('\0'); } dwres = WNetGetResourceInformation(&nr, &nrOut.nr, &dwbuf, &pszTemp); if (dwres != WN_SUCCESS) { // we failed to get a net connection using the truncated string, // so lets try and use a new connect (eg. prompt for creds) // NOTE: shouldn't we only be doing this if its an access denied type error? dwres = WNetUseConnection(hwnd, &nr, NULL, NULL, hwnd ? CONNECT_INTERACTIVE:0, NULL, NULL, NULL); if (dwres == WN_SUCCESS) { dwres = WNetGetResourceInformation(&nr, &nrOut, &dwbuf, &pszTemp); } } if (pszFakeRestOfName) *pszFakeRestOfName = cT; pszRestOfName = pszFakeRestOfName; } if (WN_SUCCESS == dwres) { WCHAR wszRestOfName[MAX_PATH] = { 0 }; if (pszRestOfName) SHTCharToUnicode(pszRestOfName, wszRestOfName, ARRAYSIZE(wszRestOfName)); // assume we are truncating at the regitem and parsing through if (pszRegItem) pszRestOfName = pszRegItem; // attempt to convert the NETRESOURCE to a string to IDLISTS by walking the // parents, then add in Entire Network hr = _NetResToIDLists(&nrOut.nr, dwbuf, ppidl); if (SUCCEEDED(hr)) { LPITEMIDLIST pidlT; hr = _CreateEntireNetwork(&pidlT); if (SUCCEEDED(hr)) { hr = SHILPrepend(pidlT, ppidl); // NOTE: SHILPrepend frees on failure } } // if we have a local string then lets continue to parse it by binding to // its parent folder, otherwise we just want to return the attributes if (SUCCEEDED(hr)) { if (SUCCEEDED(DisplayNameOf(this, *ppidl, SHGDN_FORPARSING, szPath, ARRAYSIZE(szPath)))) { NPTRegisterNameToPidlTranslation(szPath, *ppidl); // no _ILNext b/c this is relative to the Net Places folder } hr = _ParseRest(pbc, wszRestOfName, ppidl, pdwAttrib); } } else { hr = HRESULT_FROM_WIN32(dwres); } return hr; } // // simple name parsing for the network paths. this makes big assumptions about the // \\server\share format we are given, and the type of IDLISTs to return. // HRESULT CNetFolder::_AddUnknownIDList(DWORD dwDisplayType, LPITEMIDLIST *ppidl) { NETRESOURCE nr = { 0 }; nr.dwScope = RESOURCE_GLOBALNET; nr.dwDisplayType = dwDisplayType; nr.dwUsage = RESOURCEUSAGE_CONTAINER; nr.lpRemoteName = TEXT("\0"); // null name means fake item LPITEMIDLIST pidlT; HRESULT hr = _NetResToIDList(&nr, TRUE, FALSE, FALSE, &pidlT); if (SUCCEEDED(hr)) { hr = SHILAppend(pidlT, ppidl); if (FAILED(hr)) ILFree(pidlT); } return hr; } HRESULT CNetFolder::_ParseSimple(LPBC pbc, LPWSTR pszName, LPITEMIDLIST* ppidl, DWORD* pdwAttributes) { HRESULT hr = S_OK; NETRESOURCE nr = {0}; LPWSTR pszSlash; LPITEMIDLIST pidlT; USES_CONVERSION; *ppidl = NULL; // create the entire network IDLIST, provider and domain elements hr = _CreateEntireNetwork(ppidl); if (SUCCEEDED(hr)) hr = _AddUnknownIDList(RESOURCEDISPLAYTYPE_NETWORK, ppidl); if (SUCCEEDED(hr)) hr = _AddUnknownIDList(RESOURCEDISPLAYTYPE_DOMAIN, ppidl); // create the server IDLIST if (SUCCEEDED(hr)) { pszSlash = StrChrW(pszName+2, L'\\'); if (pszSlash) *pszSlash = L'\0'; nr.dwScope = RESOURCE_GLOBALNET; nr.dwDisplayType = RESOURCEDISPLAYTYPE_SERVER; nr.dwType = RESOURCETYPE_DISK; nr.dwUsage = RESOURCEUSAGE_CONTAINER; nr.lpRemoteName = W2T(pszName); hr = _NetResToIDList(&nr, FALSE, FALSE, FALSE, &pidlT); if (SUCCEEDED(hr)) hr = SHILAppend(pidlT, ppidl); if (pszSlash) *pszSlash = L'\\'; // if we have a trailing \ then lets add in the share part of the IDLIST if (SUCCEEDED(hr) && pszSlash) { pszSlash = StrChrW(pszSlash+1, L'\\'); if (pszSlash) *pszSlash = L'\0'; nr.dwDisplayType = RESOURCEDISPLAYTYPE_SHARE; nr.dwUsage = RESOURCEUSAGE_CONNECTABLE; nr.lpRemoteName = W2T(pszName); hr = _NetResToIDList(&nr, FALSE, FALSE, FALSE, &pidlT); if (SUCCEEDED(hr)) hr = SHILAppend(pidlT, ppidl); if (pszSlash) *pszSlash = L'\\'; } } if (SUCCEEDED(hr)) { hr = _ParseRest(pbc, pszSlash, ppidl, pdwAttributes); } return hr; } // try parsing out the EntireNet or localised version. if we find that object then try and // parse through that to the regitems or other objects which live below. this inturn // will cause an instance of CNetFolder to be created to generate the other parsing names. // // returns: // S_FALSE - not rest of net, try something else // S_OK - was rest of net, use this // FAILED(hr) - error result, return HRESULT CNetRootFolder::_TryParseEntireNet(HWND hwnd, LPBC pbc, WCHAR *pwszName, LPITEMIDLIST *ppidl, DWORD *pdwAttributes) { HRESULT hr = S_FALSE; // skip, not rest of net *ppidl = NULL; if (!PathIsUNCW(pwszName)) { const WCHAR szEntireNetwork[] = L"EntireNetwork"; WCHAR szRestOfNet[128]; INT cchRestOfNet = LoadStringW(HINST_THISDLL, IDS_RESTOFNET, szRestOfNet, ARRAYSIZE(szRestOfNet)); BOOL fRestOfNet = !StrCmpNIW(szRestOfNet, pwszName, cchRestOfNet); if (!fRestOfNet && !StrCmpNIW(szEntireNetwork, pwszName, ARRAYSIZE(szEntireNetwork)-1)) { fRestOfNet = TRUE; cchRestOfNet = ARRAYSIZE(szEntireNetwork)-1; } if (fRestOfNet) { hr = _CreateEntireNetwork(ppidl); if (SUCCEEDED(hr)) { if (pdwAttributes) { GetAttributesOf(1, (LPCITEMIDLIST *)ppidl, pdwAttributes); } hr = S_OK; } // // if we find extra stuff after the name then lets bind and continue the parsing // from there on. this is needed so the we can access regitems burried inside // entire net. // // eg: EntireNetwork\\::{clsid} // if (SUCCEEDED(hr) && (pwszName[cchRestOfNet] == L'\\') && pwszName[cchRestOfNet+1]) { IShellFolder *psfRestOfNet; hr = BindToObject(*ppidl, NULL, IID_PPV_ARG(IShellFolder, &psfRestOfNet)); if (SUCCEEDED(hr)) { LPITEMIDLIST pidl; hr = psfRestOfNet->ParseDisplayName(hwnd, pbc, pwszName+cchRestOfNet+1, NULL, &pidl, pdwAttributes); if (SUCCEEDED(hr)) { hr = SHILAppend(pidl, ppidl); } psfRestOfNet->Release(); } } } } return hr; } // CNetRootFolder::ParseDisplayname // - swtich based on the file system context to see if we need to do a simple parse or not, // - check for "EntireNet" and delegate parsing as required. STDMETHODIMP CNetRootFolder::ParseDisplayName(HWND hwnd, LPBC pbc, WCHAR* pszName, ULONG* pchEaten, LPITEMIDLIST* ppidl, DWORD* pdwAttributes) { if (!ppidl) return E_INVALIDARG; *ppidl = NULL; if (!pszName) return E_INVALIDARG; HRESULT hr = _TryParseEntireNet(hwnd, pbc, pszName, ppidl, pdwAttributes); if (hr == S_FALSE) { if (PathIsUNCW(pszName)) { LPCITEMIDLIST pidlMapped; LPTSTR pszRest = NPTMapNameToPidl(pszName, &pidlMapped); if (pidlMapped) { hr = SHILClone(pidlMapped, ppidl); if (SUCCEEDED(hr)) { hr = _ParseRest(pbc, pszRest, ppidl, pdwAttributes); } } else { if (S_OK == SHIsFileSysBindCtx(pbc, NULL)) { hr = _ParseSimple(pbc, pszName, ppidl, pdwAttributes); } else { hr = _ParseNetName(hwnd, pbc, pszName, pchEaten, ppidl, pdwAttributes); } } } else { hr = HRESULT_FROM_WIN32(ERROR_BAD_NET_NAME); } if ((HRESULT_FROM_WIN32(ERROR_BAD_NET_NAME) == hr)) { IShellFolder2 *psfFiles; if (SUCCEEDED(v_GetFileFolder(&psfFiles))) { hr = psfFiles->ParseDisplayName(hwnd, pbc, pszName, pchEaten, ppidl, pdwAttributes); } } } if (FAILED(hr)) { ILFree(*ppidl); *ppidl = NULL; } return hr; } STDMETHODIMP CNetRootFolder::EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList** ppenum) { DWORD dwRemote = RMF_GETLINKENUM; HANDLE hEnum = NULL; // Do we enumerate the workgroup? if (!SHRestricted(REST_ENUMWORKGROUP)) { // Don't enumerate the workgroup, if the restriction says so dwRemote |= RMF_FAKENETROOT; // Check the WNet policy to see if we should be showing the // entire net object. If not, mark it as shown so that the // enumerator doesn't return it. if (SHRestricted(REST_NOENTIRENETWORK)) dwRemote |= RMF_ENTIRENETSHOWN; } // if we are not faking the net root then lets call _OpenEnum, otherwise lets ignore if (!(dwRemote & RMF_FAKENETROOT)) { DWORD err = _OpenEnum(hwnd, grfFlags, NULL, &hEnum); // Always add the remote folder to the 'hood if (WN_SUCCESS != err) { // Yes; still show remote anyway (only) dwRemote |= RMF_SHOWREMOTE; } else { // No; allow everything to be enumerated in the 'hood. dwRemote |= RMF_CONTEXT; } } HRESULT hr = Create_NetFolderEnum(this, grfFlags, dwRemote, hEnum, ppenum); if (FAILED(hr) && hEnum) { WNetCloseEnum(hEnum); } return hr; } STDMETHODIMP CNetRootFolder::BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv) { HRESULT hr; if (NET_IsValidID(pidl)) hr = CNetFolder::BindToObject(pidl, pbc, riid, ppv); else { IShellFolder2* psfFiles; hr = v_GetFileFolder(&psfFiles); if (SUCCEEDED(hr)) hr = psfFiles->BindToObject(pidl, pbc, riid, ppv); } return hr; } STDMETHODIMP CNetRootFolder::CompareIDs(LPARAM iCol, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2) { HRESULT hr = E_INVALIDARG; // First obtain the collate type of the pidls and their respective // collate order. LONG iColateType1 = _GetFilePIDLType(pidl1); LONG iColateType2 = _GetFilePIDLType(pidl2); if (iColateType1 == iColateType2) { // pidls are of same type. if (iColateType1 == _HOOD_COL_FILE) // two file system pidls { IShellFolder2* psfFiles; if (SUCCEEDED(v_GetFileFolder(&psfFiles))) { if (0 == (iCol & SHCIDS_COLUMNMASK)) { // special case this for perf, this is the name compare hr = psfFiles->CompareIDs(iCol, pidl1, pidl2); } else { SHCOLUMNID scid; MapColumnToSCID((UINT)iCol & SHCIDS_COLUMNMASK, &scid); int iRet = CompareBySCID(psfFiles, &scid, pidl1, pidl2); hr = ResultFromShort(iRet); } } } else { // pidls same and are not of type file, // so both must be a type understood // by the CNetwork class - pass on to compare. hr = CNetFolder::CompareIDs(iCol, pidl1, pidl2); } } else { // ensure that entire network ends up at the head of the list LPCIDNETRESOURCE pidn1 = NET_IsValidID(pidl1); LPCIDNETRESOURCE pidn2 = NET_IsValidID(pidl2); if ((pidn1 && (NET_GetDisplayType(pidn1) == RESOURCEDISPLAYTYPE_ROOT)) || (pidn2 && (NET_GetDisplayType(pidn2) == RESOURCEDISPLAYTYPE_ROOT))) { if (iColateType1 == _HOOD_COL_FILE) return ResultFromShort(1); else return ResultFromShort(-1); } // pidls are not of same type, so have already been correctly // collated (consequently, sorting is first by type and // then by subfield). hr = ResultFromShort(((iColateType2 - iColateType1) > 0) ? 1 : -1); } return hr; } STDMETHODIMP CNetRootFolder::CreateViewObject(HWND hwnd, REFIID riid, void **ppv) { ASSERT(ILIsEqual(_pidl, (LPCITEMIDLIST)&c_idlNet)); if (IsEqualIID(riid, IID_IDropTarget)) { return CNetRootDropTarget_CreateInstance(hwnd, _pidl, (IDropTarget**) ppv); } return CNetFolder::CreateViewObject(hwnd, riid, ppv); } STDMETHODIMP CNetRootFolder::GetAttributesOf(UINT cidl, LPCITEMIDLIST *apidl, ULONG* prgfInOut) { HRESULT hr; if (IsSelf(cidl, apidl)) { // The user can rename links in the hood. hr = CNetFolder::GetAttributesOf(cidl, apidl, prgfInOut); *prgfInOut |= SFGAO_CANRENAME; } else { hr = GetAttributesCallback(SAFECAST(this, IShellFolder2*), cidl, apidl, prgfInOut, _AttributesCallbackRoot); } return hr; } STDMETHODIMP CNetRootFolder::GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD dwFlags, STRRET* pStrRet) { HRESULT hr; if (NET_IsValidID(pidl) || IsSelf(1, &pidl)) { hr = CNetFolder::GetDisplayNameOf(pidl, dwFlags, pStrRet); } else { IShellFolder2* psfFiles; hr = v_GetFileFolder(&psfFiles); if (SUCCEEDED(hr)) hr = psfFiles->GetDisplayNameOf(pidl, dwFlags, pStrRet); } return hr; } STDMETHODIMP CNetRootFolder::SetNameOf(HWND hwnd, LPCITEMIDLIST pidl, LPCOLESTR lpszName, DWORD dwRes, LPITEMIDLIST* ppidl) { HRESULT hr; if (NET_IsValidID(pidl)) { hr = CNetFolder::SetNameOf(hwnd, pidl, lpszName, dwRes, ppidl); } else { IShellFolder2* psfFiles; hr = v_GetFileFolder(&psfFiles); if (SUCCEEDED(hr)) hr = psfFiles->SetNameOf(hwnd, pidl, lpszName, dwRes, ppidl); } return hr; } STDMETHODIMP CNetRootFolder::GetUIObjectOf(HWND hwnd, UINT cidl, LPCITEMIDLIST* apidl, REFIID riid, UINT* prgfInOut, void **ppv) { HRESULT hr = E_INVALIDARG; LPCIDNETRESOURCE pidn = cidl ? NET_IsValidID(apidl[0]) : NULL; BOOL fStriped = FALSE; *ppv = NULL; if (pidn) { fStriped = _MakeStripToLikeKinds(&cidl, &apidl, TRUE); if (IsEqualIID(riid, IID_IContextMenu)) { HKEY ahkeys[NKID_COUNT]; hr = _OpenKeys(pidn, ahkeys); if (SUCCEEDED(hr)) { IShellFolder* psfOuter; hr = QueryInterface(IID_PPV_ARG(IShellFolder, &psfOuter)); if (SUCCEEDED(hr)) { hr = CDefFolderMenu_Create2(_pidl, hwnd, cidl, apidl, psfOuter, _GetCallbackType(pidn), ARRAYSIZE(ahkeys), ahkeys, (IContextMenu**) ppv); psfOuter->Release(); } SHRegCloseKeys(ahkeys, ARRAYSIZE(ahkeys)); } } else hr = CNetFolder::GetUIObjectOf(hwnd, cidl, apidl, riid, prgfInOut, ppv); } else { fStriped = _MakeStripToLikeKinds(&cidl, &apidl, FALSE); IShellFolder2* psfFiles; hr = v_GetFileFolder(&psfFiles); if (SUCCEEDED(hr)) hr = psfFiles->GetUIObjectOf(hwnd, cidl, apidl, riid, prgfInOut, ppv); } if (fStriped) LocalFree((HLOCAL)apidl); return hr; } STDMETHODIMP CNetRootFolder::GetClassID(CLSID* pCLSID) { *pCLSID = CLSID_NetworkPlaces; return S_OK; } STDMETHODIMP CNetRootFolder::Initialize(LPCITEMIDLIST pidl) { ASSERT(ILIsEqual(pidl, (LPCITEMIDLIST)&c_idlNet)); ASSERT(AssertIsIDListInNameSpace(pidl, &CLSID_NetworkPlaces) && ILIsEmpty(_ILNext(pidl))); // Only allow the Net root on the desktop // Don't initialize more than once; we are a singleton object. // This is theoretically redundant with the InterlockedCompareExchange // below, but redundant reinitialization is by far the common case // so we'll optimize it. if (_pidl) return S_OK; LPITEMIDLIST pidlNew; HRESULT hr = SHILClone(pidl, &pidlNew); if (SUCCEEDED(hr)) { if (SHInterlockedCompareExchange((void**)&_pidl, pidlNew, 0)) { // Some other thread raced with us, throw away our copy ILFree(pidlNew); } } return hr; } LONG CNetFolder::_GetFilePIDLType(LPCITEMIDLIST pidl) { if (NET_IsValidID(pidl)) { if (NET_IsRemoteFld((LPIDNETRESOURCE)pidl)) { return _HOOD_COL_REMOTE; } if (NET_GetDisplayType((LPIDNETRESOURCE)pidl) == RESOURCEDISPLAYTYPE_ROOT) { return _HOOD_COL_RON; } return _HOOD_COL_NET; } return _HOOD_COL_FILE; } /* This function adds a provider name to an IDLIST that doesn't already have one. */ /* A new IDLIST pointer is returned; the old pointer is no longer valid. */ LPITEMIDLIST CNetFolder::_AddProviderToPidl(LPITEMIDLIST pidl, LPCTSTR lpProvider) { LPIDNETRESOURCE pidn = (LPIDNETRESOURCE)pidl; if (!NET_FHasProvider(pidn)) { LPITEMIDLIST pidlres; TCHAR szName[MAX_PATH], szComment[MAX_PATH]; // construct a new IDLIST preserving the name, comment and other information NET_CopyResName(pidn, szName, ARRAYSIZE(szName)); NET_CopyComment(pidn, szComment, ARRAYSIZE(szComment)); HRESULT hr = _CreateNetIDList(pidn, szName, lpProvider, szComment[0] ? szComment:NULL, &pidlres); if (SUCCEEDED(hr) && !ILIsEmpty(_ILNext(pidl))) { LPITEMIDLIST pidlT; hr = SHILCombine(pidlres, _ILNext(pidl), &pidlT); if (SUCCEEDED(hr)) { ILFree(pidlres); pidlres = pidlT; } } // if we have a result, free the old PIDL and return the new if (SUCCEEDED(hr)) { ILFree(pidl); pidl = pidlres; } } return pidl; } BOOL CNetFolder::_MakeStripToLikeKinds(UINT *pcidl, LPCITEMIDLIST **papidl, BOOL fNetObjects) { BOOL bRet = FALSE; LPITEMIDLIST *apidl = (LPITEMIDLIST*)*papidl; int cidl = *pcidl; for (int i = 0; i < cidl; i++) { if ((NET_IsValidID(apidl[i]) != NULL) != fNetObjects) { LPCITEMIDLIST *apidlHomo = (LPCITEMIDLIST *)LocalAlloc(LPTR, sizeof(*apidlHomo) * cidl); if (!apidlHomo) return FALSE; int cpidlHomo = 0; for (i = 0; i < cidl; i++) { if ((NET_IsValidID(apidl[i]) != NULL) == fNetObjects) apidlHomo[cpidlHomo++] = apidl[i]; } // Setup to use the stripped version of the pidl array... *pcidl = cpidlHomo; *papidl = apidlHomo; bRet = TRUE; } } return bRet; } HRESULT CNetRootFolder::v_GetFileFolder(IShellFolder2 **psf) { HRESULT hr = SHCacheTrackingFolder((LPCITEMIDLIST)&c_idlNet, CSIDL_NETHOOD, &_psfFiles); *psf = _psfFiles; return hr; } // // pmedium and pformatetcIn == NULL if we are handling QueryGetData // HRESULT CNetData::GetHDrop(FORMATETC *pformatetcIn, STGMEDIUM *pmedium) { HRESULT hr = E_INVALIDARG; // assume error STGMEDIUM medium; LPIDA pida = DataObj_GetHIDA(this, &medium); if (pida) { // Get the first one to see the type. LPCIDNETRESOURCE pidn = (LPCIDNETRESOURCE)IDA_GetIDListPtr(pida, 0); if (NULL == pidn) hr = E_FAIL; if (pidn && (NET_GetFlags(pidn) & SHID_JUNCTION) && (NET_GetType(pidn) == RESOURCETYPE_DISK)) { // Get HDrop only if we are handling IDataObject::GetData (pmedium != NULL) if (pmedium) { // We have non-null FORMATETC and STGMEDIUM - get the HDrop hr = CFSIDLData::GetHDrop(pformatetcIn, pmedium); } else { hr = S_OK; // We were handling QueryGetData } } HIDA_ReleaseStgMedium(pida, &medium); } return hr; } LPTSTR NET_GetProviderFromRes(LPCIDNETRESOURCE pidn, LPTSTR pszBuff, UINT cchBuff); // By the way...Win95 shipped with the below provider // names. Since the name can be changed and be localized, // we have to try and map these correctly for net pidl // interop. // // get the network resource name from an item. this is not a file system path! // // example: // server \\server or strike/sys // share \\server\share or strike/sys // printer \\server\printer // provider "provider name" // entire net "Entire Network" // // in: // pidn the item // cchBuff size of buffer in chars. // // out: // pszBuff return buffer // // returns: // address of the input buffer (pszBuff) // LPTSTR NET_CopyResName(LPCIDNETRESOURCE pidn, LPTSTR pszBuff, UINT cchBuff) { if (NET_IsUnicode(pidn)) { LPBYTE pb = (LPBYTE)pidn->szNetResName; pb += lstrlenA((LPSTR)pb) + 1; // Skip over ansi net name if (NET_FHasProvider(pidn)) pb += lstrlenA((LPSTR)pb) + 1; // Skip over ansi provider if (NET_FHasComment(pidn)) pb += lstrlenA((LPSTR)pb) + 1; // Skip over comment ualstrcpyn(pszBuff, (LPNWSTR)pb, cchBuff); } else { SHAnsiToTChar(pidn->szNetResName, pszBuff, cchBuff); } return pszBuff; } // // get the provider name from an item. some items do not have providers stored // in them. for example the "*" indicates where the provider is stored in the // two different forms of network pidls. // [entire net] [provider *] [server] [share] [... file system] // [server *] [share] [... file system] // in: // pidn item (single item PIDL) to try to get the provider name from // cchBuff size in chars. // out: // pszBuff output // LPTSTR NET_CopyProviderName(LPCIDNETRESOURCE pidn, LPTSTR pszBuff, UINT cchBuff) { *pszBuff = 0; if (!NET_FHasProvider(pidn)) return NULL; // try the wNetType at the end of the pidl const BYTE *pb = (LPBYTE)pidn + pidn->cb - sizeof(WORD); DWORD dwNetType = *((UNALIGNED WORD *)pb) << 16; if (dwNetType && (dwNetType <= WNNC_NET_LARGEST) && (WNetGetProviderName(dwNetType, pszBuff, (ULONG*)&cchBuff) == WN_SUCCESS)) { return pszBuff; } // Try the old way... pb = (LPBYTE)pidn->szNetResName + lstrlenA(pidn->szNetResName) + 1; // Skip over ansi net name if (NET_IsUnicode(pidn)) { pb += lstrlenA((LPSTR)pb) + 1; // Skip over ansi provider if (NET_FHasComment(pidn)) pb += lstrlenA((LPSTR)pb) + 1; // Skip over comment pb += (ualstrlen((LPNWSTR)pb) + 1) * sizeof(WCHAR); // skip over unicode net name ualstrcpyn(pszBuff, (LPNWSTR)pb, cchBuff); } else { SHAnsiToTChar((LPSTR)pb, pszBuff, cchBuff); } // Map from Win95 net provider name if possible... for (int i = 0; i < ARRAYSIZE(c_rgProviderMap); i++) { if (lstrcmp(pszBuff, c_rgProviderMap[i].lpName) == 0) { DWORD dwNetType = c_rgProviderMap[i].wNetType << 16; if (dwNetType && (dwNetType <= WNNC_NET_LARGEST)) { *pszBuff = 0; WNetGetProviderName(dwNetType, pszBuff, (LPDWORD)&cchBuff); } break; } } return pszBuff; } // // get the comment if there is one from the net item // LPTSTR NET_CopyComment(LPCIDNETRESOURCE pidn, LPTSTR pszBuff, UINT cchBuff) { *pszBuff = 0; LPCSTR pszT = pidn->szNetResName + lstrlenA(pidn->szNetResName) + 1; if (NET_FHasComment(pidn)) { if (NET_FHasProvider(pidn)) pszT += lstrlenA(pszT) + 1; if (NET_IsUnicode(pidn)) { pszT += lstrlenA(pszT) + 1; // Skip Ansi comment LPNCWSTR pszW = (LPNCWSTR)pszT; // We're at the unicode portion of the pidl pszW += ualstrlen(pszW) + 1; // Skip Unicode Name if (NET_FHasProvider(pidn)) pszW += ualstrlen(pszW) + 1; // Skip Unicode Provider ualstrcpyn(pszBuff, pszW, cchBuff); } else { SHAnsiToUnicode(pszT, pszBuff, cchBuff); } } return pszBuff; } // pidlRemainder will be filled in (only in the TRUE return case) with a // pointer to the part of the IDL (if any) past the remote regitem. // This value may be used, for example, to differentiate between a remote // printer folder and a printer under a remote printer folder BOOL NET_IsRemoteRegItem(LPCITEMIDLIST pidl, REFCLSID rclsid, LPCITEMIDLIST* ppidlRemainder) { BOOL bRet = FALSE; // in "My Network Places" if (pidl && IsIDListInNameSpace(pidl, &CLSID_NetworkPlaces)) { LPCITEMIDLIST pidlStart = pidl; // save this // Now, search for a server item. HACKHACK: this assume everything from // the NetHood to the server item is a shell pidl with a bFlags field!! for (pidl = _ILNext(pidl); !ILIsEmpty(pidl); pidl = _ILNext(pidl)) { if ((SIL_GetType(pidl) & SHID_TYPEMASK) == SHID_NET_SERVER) { LPITEMIDLIST pidlToTest; // Found a server. Is the thing after it a remote registry item? pidl = _ILNext(pidl); *ppidlRemainder = _ILNext(pidl); pidlToTest = ILCloneUpTo(pidlStart, *ppidlRemainder); if (pidlToTest) { CLSID clsid; bRet = SUCCEEDED(GetCLSIDFromIDList(pidlToTest, &clsid)) && IsEqualCLSID(rclsid, clsid); ILFree(pidlToTest); } break; // done } } } return bRet; } // // Get the provider name from an absolute IDLIST. // Parameters: // pidlAbs -- Specifies the Absolute IDList to the file system object // LPTSTR NET_GetProviderFromIDList(LPCITEMIDLIST pidlAbs, LPTSTR pszBuff, UINT cchBuff) { return NET_GetProviderFromRes((LPCIDNETRESOURCE)_ILNext(pidlAbs), pszBuff, cchBuff); } // // Get the provider name from a relative IDLIST. // in: // pidn potentially multi level item to try to get the resource from // LPTSTR NET_GetProviderFromRes(LPCIDNETRESOURCE pidn, LPTSTR pszBuffer, UINT cchBuffer) { // If this guy is the REST of network item, we increment to the // next IDLIST - If at root return NULL if (pidn->cb == 0) return NULL; // // If the IDLIST starts with a ROOT_REGITEM, then skip to the // next item in the list... if (pidn->bFlags == SHID_ROOT_REGITEM) { pidn = (LPIDNETRESOURCE)_ILNext((LPITEMIDLIST)pidn); if (pidn->cb == 0) return NULL; } // If the IDLIST includes Entire Network, the provider will be // part of the next component. if (NET_GetDisplayType(pidn) == RESOURCEDISPLAYTYPE_ROOT) { pidn = (LPIDNETRESOURCE)_ILNext((LPITEMIDLIST)pidn); if (pidn->cb == 0) return NULL; } // If the next component after the 'hood or Entire Network is // a network object, its name is the provider name, else the // provider name comes after the remote name. if (NET_GetDisplayType(pidn) == RESOURCEDISPLAYTYPE_NETWORK) { // Simply return the name field back for the item. return NET_CopyResName(pidn, pszBuffer, cchBuffer); } else { // Nope one of the items in the neighborhood view was selected // The Provider name is stored after ther resource name return NET_CopyProviderName(pidn, pszBuffer, cchBuffer); } } #define PTROFFSET(pBase, p) ((int) ((LPBYTE)(p) - (LPBYTE)(pBase))) // // fill in pmedium with a NRESARRAY // // pmedium == NULL if we are handling QueryGetData // STDAPI CNetData_GetNetResource(IDataObject *pdtobj, STGMEDIUM *pmedium) { HRESULT hr = E_OUTOFMEMORY; LPITEMIDLIST pidl; STGMEDIUM medium; LPIDA pida = DataObj_GetHIDA(pdtobj, &medium); ASSERT(pida && pida->cidl); // First, get the provider name from the first one (assuming they are common). pidl = IDA_ILClone(pida, 0); if (pidl) { TCHAR szProvider[MAX_PATH]; LPCTSTR pszProvider = NET_GetProviderFromIDList(pidl, szProvider, ARRAYSIZE(szProvider)); if (pmedium) { TCHAR szName[MAX_PATH]; UINT cbHeader = sizeof(NRESARRAY) + (sizeof(NETRESOURCE) * (pida->cidl - 1)); UINT cbRequired, iItem; // Calculate required size cbRequired = cbHeader; if (pszProvider) cbRequired += (lstrlen(pszProvider) + 1) * sizeof(TCHAR); for (iItem = 0; iItem < pida->cidl; iItem++) { LPCIDNETRESOURCE pidn = (LPCIDNETRESOURCE)IDA_GetIDListPtr(pida, iItem); NET_CopyResName(pidn, szName, ARRAYSIZE(szName)); cbRequired += (lstrlen(szName) + 1) * sizeof(TCHAR); } // // Indicate that the caller should release hmem. // pmedium->pUnkForRelease = NULL; pmedium->tymed = TYMED_HGLOBAL; pmedium->hGlobal = GlobalAlloc(GPTR, cbRequired); if (pmedium->hGlobal) { LPNRESARRAY panr = (LPNRESARRAY)pmedium->hGlobal; LPTSTR pszT = (LPTSTR)((LPBYTE)panr + cbHeader); LPTSTR pszEnd = (LPTSTR)((LPBYTE)panr + cbRequired); UINT offProvider = 0; panr->cItems = pida->cidl; // Copy the provider name. This is not necessary, // if we are dragging providers. if (pszProvider) { lstrcpy(pszT, pszProvider); offProvider = PTROFFSET(panr, pszT); pszT += lstrlen(pszT) + 1; } // // For each item, fill each NETRESOURCE and append resource // name at the end. Note that we should put offsets in // lpProvider and lpRemoteName. // for (iItem = 0; iItem < pida->cidl; iItem++) { LPNETRESOURCE pnr = &panr->nr[iItem]; LPCIDNETRESOURCE pidn = (LPCIDNETRESOURCE)IDA_GetIDListPtr(pida, iItem); ASSERT(pnr->dwScope == 0); ASSERT(pnr->lpLocalName==NULL); ASSERT(pnr->lpComment==NULL); pnr->dwType = NET_GetType(pidn); pnr->dwDisplayType = NET_GetDisplayType(pidn); pnr->dwUsage = NET_GetUsage(pidn); NET_CopyResName(pidn, pszT, (UINT)(pszEnd-pszT)); if (pnr->dwDisplayType == RESOURCEDISPLAYTYPE_ROOT) { pnr->lpProvider = NULL; pnr->lpRemoteName = NULL; } else if (pnr->dwDisplayType == RESOURCEDISPLAYTYPE_NETWORK) { *((UINT *) &pnr->lpProvider) = PTROFFSET(panr, pszT); ASSERT(pnr->lpRemoteName == NULL); } else { *((UINT *) &pnr->lpProvider) = offProvider; *((UINT *) &pnr->lpRemoteName) = PTROFFSET(panr, pszT); } pszT += lstrlen(pszT) + 1; } ASSERT(pszEnd == pszT); hr = S_OK; } } else { hr = S_OK; // handing QueryGetData, yes, we have it } ILFree(pidl); } HIDA_ReleaseStgMedium(pida, &medium); return hr; } // fill in pmedium with an HGLOBAL version of a NRESARRAY STDAPI CNetData_GetNetResourceForFS(IDataObject *pdtobj, STGMEDIUM *pmedium) { HRESULT hr = E_OUTOFMEMORY; LPITEMIDLIST pidlAbs; STGMEDIUM medium; LPIDA pida = DataObj_GetHIDA(pdtobj, &medium); ASSERT(pida && medium.hGlobal); // we created this... // // NOTES: Even though we may have multiple FS objects in this HIDA, // we know that they share the root. Therefore, getting the pidl for // the first item is always sufficient. // pidlAbs = IDA_ILClone(pida, 0); if (pidlAbs) { LPITEMIDLIST pidl; ASSERT(AssertIsIDListInNameSpace(pidlAbs, &CLSID_NetworkPlaces)); // // Look for the JUNCTION point (starting from the second ID) // for (pidl = _ILNext(pidlAbs); !ILIsEmpty(pidl); pidl = _ILNext(pidl)) { LPIDNETRESOURCE pidn = (LPIDNETRESOURCE)pidl; if (NET_GetFlags(pidn) & SHID_JUNCTION) { // // We found the JUNCTION point (which is s share). // Return the HNRES to it. // TCHAR szProvider[MAX_PATH]; TCHAR szRemote[MAX_PATH]; UINT cbRequired; LPCTSTR pszProvider = NET_GetProviderFromIDList(pidlAbs, szProvider, ARRAYSIZE(szProvider)); LPCTSTR pszRemoteName = NET_CopyResName(pidn, szRemote, ARRAYSIZE(szRemote)); UINT cbProvider = lstrlen(pszProvider) * sizeof(TCHAR) + sizeof(TCHAR); // // This should not be a provider node. // This should not be the last ID in pidlAbs. // ASSERT(pszProvider != pszRemoteName); ASSERT(!ILIsEmpty(_ILNext(pidl))); cbRequired = sizeof(NRESARRAY) + cbProvider + lstrlen(pszRemoteName) * sizeof(TCHAR) + sizeof(TCHAR); pmedium->pUnkForRelease = NULL; pmedium->tymed = TYMED_HGLOBAL; pmedium->hGlobal = GlobalAlloc(GPTR, cbRequired); if (pmedium->hGlobal) { LPNRESARRAY panr = (LPNRESARRAY)pmedium->hGlobal; LPNETRESOURCE pnr = &panr->nr[0]; LPTSTR pszT = (LPTSTR)(panr + 1); ASSERT(pnr->dwScope == 0); ASSERT(pnr->lpLocalName == NULL); ASSERT(pnr->lpComment == NULL); panr->cItems = 1; pnr->dwType = NET_GetType(pidn); pnr->dwDisplayType = NET_GetDisplayType(pidn); pnr->dwUsage = NET_GetUsage(pidn); *((UINT *) &pnr->lpProvider) = sizeof(NRESARRAY); lstrcpy(pszT, pszProvider); ASSERT(PTROFFSET(panr, pszT) == sizeof(NRESARRAY)); pszT += cbProvider / sizeof(TCHAR); *((UINT *) &pnr->lpRemoteName) = sizeof(NRESARRAY) + cbProvider; ASSERT(PTROFFSET(panr, pszT) == (int)sizeof(NRESARRAY) + (int)cbProvider); lstrcpy(pszT, pszRemoteName); ASSERT(((LPBYTE)panr) + cbRequired == (LPBYTE)pszT + (lstrlen(pszT) + 1) * sizeof(TCHAR)); hr = S_OK; } else { hr = E_OUTOFMEMORY; } break; } } ASSERT(!ILIsEmpty(pidl)); // We should have found the junction point. ILFree(pidlAbs); } HIDA_ReleaseStgMedium(pida, &medium); return hr; } STDMETHODIMP CNetData::QueryGetData(FORMATETC *pformatetc) { if (pformatetc->tymed & TYMED_HGLOBAL) { if (pformatetc->cfFormat == g_cfNetResource) return CNetData_GetNetResource(this, NULL); if (pformatetc->cfFormat == CF_HDROP) return GetHDrop(NULL, NULL); } return CFSIDLData::QueryGetData(pformatetc); } STDMETHODIMP CNetData::GetData(FORMATETC *pformatetc, STGMEDIUM *pmedium) { if (pformatetc->tymed & TYMED_HGLOBAL) { if (pformatetc->cfFormat == g_cfNetResource) return CNetData_GetNetResource(this, pmedium); if (pformatetc->cfFormat == CF_HDROP) return GetHDrop(pformatetc, pmedium); } return CFSIDLData::GetData(pformatetc, pmedium); } BOOL GetPathFromDataObject(IDataObject *pdtobj, DWORD dwData, LPTSTR pszFileName) { BOOL bRet = FALSE; BOOL fUnicode = FALSE; HRESULT hr; if (dwData & (DTID_FDESCW | DTID_FDESCA)) { FORMATETC fmteW = {g_cfFileGroupDescriptorW, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; STGMEDIUM medium = {0}; hr = pdtobj->GetData(&fmteW, &medium); if (SUCCEEDED(hr)) { fUnicode = TRUE; } else { FORMATETC fmteA = {g_cfFileGroupDescriptorA, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; hr = pdtobj->GetData(&fmteA, &medium); } if (SUCCEEDED(hr)) { if (fUnicode) { FILEGROUPDESCRIPTORW *pfgdW = (FILEGROUPDESCRIPTORW *)GlobalLock(medium.hGlobal); if (pfgdW) { if (pfgdW->cItems == 1) { SHUnicodeToTChar(pfgdW->fgd[0].cFileName, pszFileName, MAX_PATH); } bRet = TRUE; GlobalUnlock(medium.hGlobal); } } else { FILEGROUPDESCRIPTORA *pfgdA = (FILEGROUPDESCRIPTORA*)GlobalLock(medium.hGlobal); if (pfgdA) { if (pfgdA->cItems == 1) { SHAnsiToTChar(pfgdA->fgd[0].cFileName, pszFileName, MAX_PATH); } bRet = TRUE; GlobalUnlock(medium.hGlobal); } } ReleaseStgMedium(&medium); } } return bRet; } class CNetRootDropTarget : public CIDLDropTarget { friend HRESULT CNetRootDropTarget_CreateInstance(HWND hwnd, LPCITEMIDLIST pidl, IDropTarget **ppdropt); public: CNetRootDropTarget(HWND hwnd) : CIDLDropTarget(hwnd) { }; // IDropTarget methods overwirte STDMETHODIMP DragEnter(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect); STDMETHODIMP Drop(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect); private: ~CNetRootDropTarget(); IDropTarget *_pdtgHood; // file system drop target }; CNetRootDropTarget::~CNetRootDropTarget() { if (_pdtgHood) _pdtgHood->Release(); } STDMETHODIMP CNetRootDropTarget::DragEnter(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) { CIDLDropTarget::DragEnter(pdtobj, grfKeyState, pt, pdwEffect); if ((m_dwData & (DTID_NETRES | DTID_HIDA)) == (DTID_NETRES | DTID_HIDA)) { // NETRESOURCE (DTID_NETRES) allow link *pdwEffect &= DROPEFFECT_LINK; } else if (((m_dwData & (DTID_FDESCW | DTID_CONTENTS)) == (DTID_FDESCW | DTID_CONTENTS)) || ((m_dwData & (DTID_FDESCA | DTID_CONTENTS)) == (DTID_FDESCA | DTID_CONTENTS)) ) { // dragging an URL from the web browser gives a FILECONTENTS version // of a .URL file. accept that here for Internet Shortcut (.url) TCHAR szFileName[MAX_PATH]; if (GetPathFromDataObject(pdtobj, m_dwData, szFileName) && (0 == lstrcmpi(PathFindExtension(szFileName), TEXT(".url")))) { *pdwEffect &= DROPEFFECT_LINK; } else { *pdwEffect = DROPEFFECT_NONE; } } else { *pdwEffect = DROPEFFECT_NONE; } m_dwEffectLastReturned = *pdwEffect; return S_OK; } STDMETHODIMP CNetRootDropTarget::Drop(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) { *pdwEffect &= DROPEFFECT_LINK; HRESULT hr = CIDLDropTarget::DragDropMenu(DROPEFFECT_LINK, pdtobj, pt, pdwEffect, NULL, NULL, POPUP_NONDEFAULTDD, grfKeyState); if (*pdwEffect) { if (!_pdtgHood) { LPITEMIDLIST pidl = SHCloneSpecialIDList(NULL, CSIDL_NETHOOD, FALSE); if (pidl) { IShellFolder *psf; if (SUCCEEDED(SHBindToObject(NULL, IID_X_PPV_ARG(IShellFolder, pidl, &psf)))) { psf->CreateViewObject(_GetWindow(), IID_PPV_ARG(IDropTarget, &_pdtgHood)); psf->Release(); } ILFree(pidl); } } if (_pdtgHood) { // force link through the dwEffect and keyboard *pdwEffect &= DROPEFFECT_LINK; grfKeyState = MK_LBUTTON | MK_CONTROL | MK_SHIFT | MK_FAKEDROP; hr = SHSimulateDrop(_pdtgHood, pdtobj, grfKeyState, NULL, pdwEffect); } else *pdwEffect = 0; } CIDLDropTarget::DragLeave(); return hr; } HRESULT CNetRootDropTarget_CreateInstance(HWND hwnd, LPCITEMIDLIST pidl, IDropTarget **ppdropt) { *ppdropt = NULL; HRESULT hr; CNetRootDropTarget *pnrdt = new CNetRootDropTarget(hwnd); if (pnrdt) { hr = pnrdt->_Init(pidl); if (SUCCEEDED(hr)) pnrdt->QueryInterface(IID_PPV_ARG(IDropTarget, ppdropt)); pnrdt->Release(); } else hr = E_OUTOFMEMORY; return hr; } // This part is psuedo bogus. Basically we have problems at times doing a // translation from things like \\pyrex\user to the appropriate PIDL, // especially if you want to avoid the overhead of hitting the network and // also problems of knowing if the server is in the "HOOD" // // We must maintain the mapping table in UNICODE internally, because // IShellFolder::ParseDisplayName uses UNICODE, and we don't want to have // to deal with lstrlen(dbcs) != lstrlen(sbcs) problems. // typedef struct _NPT_ITEM { struct _NPT_ITEM *pnptNext; // Pointer to next item; LPCITEMIDLIST pidl; // The pidl USHORT cchName; // size of the name in characters. WCHAR szName[1]; // The name to translate from } NPT_ITEM; // Each process will maintain their own list. NPT_ITEM *g_pnptHead = NULL; // // Function to register translations from Path to IDList translations. // void NPTRegisterNameToPidlTranslation(LPCTSTR pszPath, LPCITEMIDLIST pidl) { NPT_ITEM *pnpt; int cItemsRemoved = 0; WCHAR szPath[MAX_PATH]; // We currently are only interested in UNC Roots // If the table becomes large we can reduce this to only servers... if (!PathIsUNC(pszPath)) return; // Not interested. // // If this item is not a root we need to count how many items to remove // SHTCharToUnicode(pszPath, szPath, ARRAYSIZE(szPath)); while (!PathIsUNCServerShare(szPath)) { cItemsRemoved++; if (!PathRemoveFileSpecW(szPath)) return; // Did not get back to a valid root } ENTERCRITICAL; // We don't want to add duplicates for (pnpt = g_pnptHead; pnpt != NULL ; pnpt = pnpt->pnptNext) { if (StrCmpIW(szPath, pnpt->szName) == 0) break; } if (pnpt == NULL) { UINT cch = lstrlenW(szPath); pnpt = (NPT_ITEM *)LocalAlloc(LPTR, sizeof(NPT_ITEM) + cch * sizeof(WCHAR)); if (pnpt) { pnpt->pidl = ILClone(pidl); if (pnpt->pidl) { while (cItemsRemoved--) { ILRemoveLastID((LPITEMIDLIST)pnpt->pidl); } pnpt->pnptNext = g_pnptHead; g_pnptHead = pnpt; pnpt->cchName = (USHORT) cch; lstrcpyW(pnpt->szName, szPath); } else { LocalFree((HLOCAL)pnpt); } } } LEAVECRITICAL; } // The main function to attemp to map a portion of the name into an idlist // Right now limit it to UNC roots // LPWSTR NPTMapNameToPidl(LPCWSTR pszPath, LPCITEMIDLIST *ppidl) { NPT_ITEM *pnpt; *ppidl = NULL; ENTERCRITICAL; // See if we can find the item in the list. for (pnpt = g_pnptHead; pnpt != NULL ; pnpt = pnpt->pnptNext) { if (IntlStrEqNIW(pszPath, pnpt->szName, pnpt->cchName) && ((pszPath[pnpt->cchName] == TEXT('\\')) || (pszPath[pnpt->cchName] == TEXT('\0')))) { break; } } LEAVECRITICAL; // See if we found a match if (pnpt == NULL) return NULL; // Found a match *ppidl = pnpt->pidl; return (LPWSTR)pszPath + pnpt->cchName; // points to slash }