#include "shellprv.h" #include "util.h" #include "ids.h" #include "infotip.h" #include "fstreex.h" #include "lm.h" #include "shgina.h" #include "prop.h" #include "datautil.h" #include "filefldr.h" #include "buytasks.h" #pragma hdrstop // this define causes the shared folder code to work on domains (for debug) //#define SHOW_SHARED_FOLDERS // filter out the current user accounts #define FILTER_CURRENT_USER 0 // where do we store the doc folder paths #define REGSTR_PATH_DOCFOLDERPATH TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\DocFolderPaths") // state API for showing the shared documents folder STDAPI_(BOOL) SHShowSharedFolders() { #ifndef SHOW_SHARED_FOLDERS // restriction overrides all logic for the shared documents if (SHRestricted(REST_NOSHAREDDOCUMENTS)) return FALSE; // if we haven't computed the "show shared folders flag" then do so static int iShow = -1; if (iShow == -1) iShow = IsOS(OS_DOMAINMEMBER) ? 0:1; // only works if we are not a domain user return (iShow >= 1); #else return true; #endif } // implementation of a delegate shell folder for merging in shared documents STDAPI_(void) SHChangeNotifyRegisterAlias(LPCITEMIDLIST pidlReal, LPCITEMIDLIST pidlAlias); HRESULT CSharedDocsEnum_CreateInstance(HDPA hItems, DWORD grfFlags, IEnumIDList **ppenum); #pragma pack(1) typedef struct { // these memebers overlap DELEGATEITEMID struct // for our IDelegateFolder support WORD cbSize; WORD wOuter; WORD cbInner; // our stuff DWORD dwType; // our type of folder TCHAR wszID[1]; // unique ID for the user } SHAREDITEM; #pragma pack() typedef UNALIGNED SHAREDITEM * LPSHAREDITEM; typedef const UNALIGNED SHAREDITEM * LPCSHAREDITEM; #define SHAREDID_COMMON 0x0 #define SHAREDID_USER 0x2 class CSharedDocuments : public IDelegateFolder, IPersistFolder2, IShellFolder2, IShellIconOverlay { public: CSharedDocuments(); ~CSharedDocuments(); // IUnknown STDMETHOD(QueryInterface)(REFIID riid, void **ppv); STDMETHOD_(ULONG, AddRef)(); STDMETHOD_(ULONG, Release)(); // IDelegateFolder STDMETHODIMP SetItemAlloc(IMalloc *pmalloc); // IPersist STDMETHODIMP GetClassID(CLSID* pclsid) { *pclsid = CLSID_SharedDocuments; return S_OK; } // IPersistFolder STDMETHODIMP Initialize(LPCITEMIDLIST pidl); // IPersistFolder2 STDMETHODIMP GetCurFolder(LPITEMIDLIST* ppidl); // 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 **ppv) { return BindToObject(pidl, pbc, riid, ppv); } STDMETHODIMP CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2); STDMETHODIMP CreateViewObject(HWND hwnd, REFIID riid, void **ppv) { return E_NOTIMPL; } STDMETHODIMP GetAttributesOf(UINT cidl, LPCITEMIDLIST* apidl, ULONG* rgfInOut); STDMETHODIMP GetUIObjectOf(HWND hwnd, 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) { return E_NOTIMPL; } // IShellFolder2 STDMETHODIMP GetDefaultSearchGUID(LPGUID lpGuid) { return E_NOTIMPL; } STDMETHODIMP EnumSearches(LPENUMEXTRASEARCH *ppenum) { return E_NOTIMPL; } STDMETHODIMP GetDefaultColumn(DWORD dwRes, ULONG *pSort, ULONG *pDisplay) { return E_NOTIMPL; } STDMETHODIMP GetDefaultColumnState(UINT iColumn, DWORD *pbState) { return E_NOTIMPL; } STDMETHODIMP GetDetailsEx(LPCITEMIDLIST pidl, const SHCOLUMNID *pscid, VARIANT *pv); STDMETHODIMP GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *pDetails) { return E_NOTIMPL; } STDMETHODIMP MapColumnToSCID(UINT iCol, SHCOLUMNID *pscid) { return E_NOTIMPL; } // IShellIconOverlay STDMETHODIMP GetOverlayIndex(LPCITEMIDLIST pidl, int *pIndex) { return _GetOverlayIndex(pidl, pIndex, FALSE); } STDMETHODIMP GetOverlayIconIndex(LPCITEMIDLIST pidl, int *pIconIndex) { return _GetOverlayIndex(pidl, pIconIndex, TRUE); } private: LONG _cRef; IMalloc *_pmalloc; LPITEMIDLIST _pidl; CRITICAL_SECTION _cs; // critical section for managing lifetime of the cache TCHAR _szCurrentUser[UNLEN+1]; // user name (cached for current user) BOOL _fCachedAllUser:1; // cached the all user account TCHAR _szCachedUser[UNLEN+1]; // if (FALSE) then this contains the user ID. IUnknown *_punkCached; // IUnknown object (from FS folder) that we cache LPITEMIDLIST _pidlCached; // IDLIST of the cached folder void _ClearCachedObjects(); BOOL _IsCached(LPCITEMIDLIST pidl); HRESULT _CreateFolder(LPBC pbc, LPCITEMIDLIST pidl, REFIID riid, void **ppv, BOOL fRegisterAlias); HRESULT _GetTarget(LPCITEMIDLIST pidl, LPITEMIDLIST *ppidl); HRESULT _GetTargetIDList(BOOL fForceReCache, LPCITEMIDLIST pidl, LPITEMIDLIST *ppidl); HRESULT _AddIDList(HDPA hdpa, DWORD dwType, LPCTSTR pszUser); HRESULT _AllocIDList(DWORD dwType, LPCTSTR pszUser, LPITEMIDLIST *ppidl); HRESULT _GetSharedFolders(HDPA *phItems); HRESULT _GetAttributesOf(LPCITEMIDLIST pidl, DWORD rgfIn, DWORD *prgfOut); LPCTSTR _GetUserFromIDList(LPCITEMIDLIST pidl, LPTSTR pszBuffer, INT cchBuffer); HRESULT _GetPathForUser(LPCTSTR pcszUser, LPTSTR pszBuffer, int cchBuffer); HRESULT _GetOverlayIndex(LPCITEMIDLIST pidl, int *pIndex, BOOL fIcon); static HRESULT s_FolderMenuCB(IShellFolder *psf, HWND hwnd, IDataObject *pdo, UINT uMsg, WPARAM wParam, LPARAM lParam); friend class CSharedDocsEnum; }; // constructors CSharedDocuments::CSharedDocuments() : _cRef(1) { InitializeCriticalSection(&_cs); } CSharedDocuments::~CSharedDocuments() { ATOMICRELEASE(_pmalloc); ATOMICRELEASE(_punkCached); ILFree(_pidlCached); ILFree(_pidl); DeleteCriticalSection(&_cs); } STDAPI CSharedDocFolder_CreateInstance(IUnknown *punkOut, REFIID riid, void **ppv) { CSharedDocuments *psdf = new CSharedDocuments; if (!psdf) return E_OUTOFMEMORY; HRESULT hr = psdf->QueryInterface(riid, ppv); psdf->Release(); return hr; } // IUnknown handling STDMETHODIMP CSharedDocuments::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CSharedDocuments, IDelegateFolder), // IID_IDelegateFolder QITABENTMULTI(CSharedDocuments, IShellFolder, IShellFolder2), // IID_IShellFOlder QITABENT(CSharedDocuments, IShellFolder2), // IID_IShellFolder2 QITABENTMULTI(CSharedDocuments, IPersistFolder, IPersistFolder2), // IID_IPersistFolder QITABENTMULTI(CSharedDocuments, IPersist, IPersistFolder2), // IID_IPersist QITABENT(CSharedDocuments, IPersistFolder2), // IID_IPersistFolder2 QITABENT(CSharedDocuments, IShellIconOverlay), // IID_IShellIconOverlay QITABENTMULTI2(CSharedDocuments, IID_IPersistFreeThreadedObject, IPersist), // IID_IPersistFreeThreadedObject { 0 }, }; if (riid == CLSID_SharedDocuments) { *ppv = this; // no ref return S_OK; } return QISearch(this, qit, riid, ppv); } STDMETHODIMP_(ULONG) CSharedDocuments::AddRef() { return InterlockedIncrement(&_cRef); } STDMETHODIMP_(ULONG) CSharedDocuments::Release() { if (InterlockedDecrement(&_cRef)) return _cRef; delete this; return 0; } // IDelegateFolder HRESULT CSharedDocuments::SetItemAlloc(IMalloc *pmalloc) { IUnknown_Set((IUnknown**)&_pmalloc, pmalloc); return S_OK; } HRESULT CSharedDocuments::Initialize(LPCITEMIDLIST pidl) { ILFree(_pidl); return SHILClone(pidl, &_pidl); } HRESULT CSharedDocuments::GetCurFolder(LPITEMIDLIST* ppidl) { return SHILClone(_pidl, ppidl); } // single level cache for the objects void CSharedDocuments::_ClearCachedObjects() { ATOMICRELEASE(_punkCached); // clear out the cached items (old) ILFree(_pidlCached); _pidlCached = NULL; } BOOL CSharedDocuments::_IsCached(LPCITEMIDLIST pidl) { BOOL fResult = FALSE; TCHAR szUser[UNLEN+1]; if (_GetUserFromIDList(pidl, szUser, ARRAYSIZE(szUser))) { // did we cache the users account information? if (!_szCachedUser[0] || (StrCmpI(_szCachedUser, szUser) != 0)) { _fCachedAllUser = FALSE; StrCpyN(_szCachedUser, szUser, ARRAYSIZE(_szCachedUser)); _ClearCachedObjects(); } else { fResult = TRUE; // were set! } } else { // the all user case is keyed on a flag rather than the // account name we are supposed to be using. if (!_fCachedAllUser) { _fCachedAllUser = TRUE; _szCachedUser[0] = TEXT('\0'); _ClearCachedObjects(); } else { fResult = TRUE; // were set } } return fResult; } // IShellFolder methods HRESULT CSharedDocuments::_CreateFolder(LPBC pbc, LPCITEMIDLIST pidl, REFIID riid, void **ppv, BOOL fRegisterAlias) { HRESULT hr = S_OK; EnterCriticalSection(&_cs); // get the target folder (were already in a critical section) // and then bind down to the shell folder if we have not already // cached one for ourselves. if (!_IsCached(pidl) || !_punkCached) { LPITEMIDLIST pidlTarget; hr = _GetTargetIDList(TRUE, pidl, &pidlTarget); // clears _punkCached in here (so no leak) if (SUCCEEDED(hr)) { LPITEMIDLIST pidlInit; hr = SHILCombine(_pidl, pidl, &pidlInit); if (SUCCEEDED(hr)) { hr = SHCoCreateInstance(NULL, &CLSID_ShellFSFolder, NULL, IID_PPV_ARG(IUnknown, &_punkCached)); if (SUCCEEDED(hr)) { IPersistFolder3 *ppf; hr = _punkCached->QueryInterface(IID_PPV_ARG(IPersistFolder3, &ppf)); if (SUCCEEDED(hr)) { PERSIST_FOLDER_TARGET_INFO pfti = {0}; pfti.pidlTargetFolder = (LPITEMIDLIST)pidlTarget; pfti.dwAttributes = FILE_ATTRIBUTE_DIRECTORY; pfti.csidl = -1; hr = ppf->InitializeEx(NULL, pidlInit, &pfti); ppf->Release(); } if (SUCCEEDED(hr) && fRegisterAlias) SHChangeNotifyRegisterAlias(pidlTarget, pidlInit); if (FAILED(hr)) { _punkCached->Release(); _punkCached = NULL; } } ILFree(pidlInit); } ILFree(pidlTarget); } } if (SUCCEEDED(hr)) hr = _punkCached->QueryInterface(riid, ppv); LeaveCriticalSection(&_cs); return hr; } HRESULT CSharedDocuments::_GetTarget(LPCITEMIDLIST pidl, LPITEMIDLIST *ppidl) { EnterCriticalSection(&_cs); HRESULT hr = _GetTargetIDList(FALSE, pidl, ppidl); LeaveCriticalSection(&_cs); return hr; } HRESULT CSharedDocuments::_GetTargetIDList(BOOL fForceReCache, LPCITEMIDLIST pidl, LPITEMIDLIST *ppidl) { HRESULT hr = S_OK; if (fForceReCache || !_IsCached(pidl) || !_pidlCached) { _ClearCachedObjects(); // we don't have it cached now LPCSHAREDITEM psid = (LPCSHAREDITEM)pidl; if (psid->dwType == SHAREDID_COMMON) { hr = SHGetSpecialFolderLocation(NULL, CSIDL_COMMON_DOCUMENTS|CSIDL_FLAG_NO_ALIAS, &_pidlCached); } else if (psid->dwType == SHAREDID_USER) { TCHAR szPath[MAX_PATH], szUser[UNLEN+1]; hr = _GetPathForUser(_GetUserFromIDList(pidl, szUser, ARRAYSIZE(szUser)), szPath, ARRAYSIZE(szPath)); if (SUCCEEDED(hr)) { hr = ILCreateFromPathEx(szPath, NULL, ILCFP_FLAG_NO_MAP_ALIAS, &_pidlCached, NULL); } } else { hr = E_INVALIDARG; // invalid IDLIST passed } } if (SUCCEEDED(hr)) hr = SHILClone(_pidlCached, ppidl); return hr; } HRESULT CSharedDocuments::_AddIDList(HDPA hdpa, DWORD dwType, LPCTSTR pszUser) { LPITEMIDLIST pidl; HRESULT hr = _AllocIDList(dwType, pszUser, &pidl); if (SUCCEEDED(hr)) { DWORD grfFlags = SFGAO_FOLDER; hr = _GetAttributesOf(pidl, SFGAO_FOLDER, &grfFlags); if (SUCCEEDED(hr) && grfFlags & SFGAO_FOLDER) { if (-1 == DPA_AppendPtr(hdpa, pidl)) { ILFree(pidl); hr = E_OUTOFMEMORY; } else { hr = S_OK; } } else { ILFree(pidl); } } return hr; } HRESULT CSharedDocuments::_AllocIDList(DWORD dwType, LPCTSTR pszUser, LPITEMIDLIST *ppidl) { DWORD cb = sizeof(SHAREDITEM); int cchUser = pszUser ? lstrlen(pszUser) + 1 : 0; // ID list contains strings if its a user if (dwType == SHAREDID_USER) cb += sizeof(TCHAR) * cchUser; SHAREDITEM *psid = (SHAREDITEM*)_pmalloc->Alloc(cb); if (!psid) return E_OUTOFMEMORY; psid->dwType = dwType; // type is universal if (dwType == SHAREDID_USER) StrCpyW(psid->wszID, pszUser); *ppidl = (LPITEMIDLIST)psid; return S_OK; } LPCTSTR CSharedDocuments::_GetUserFromIDList(LPCITEMIDLIST pidl, LPTSTR pszUser, int cchUser) { LPCSHAREDITEM psid = (LPCSHAREDITEM)pidl; if (psid->dwType == SHAREDID_COMMON) { pszUser[0] = 0; // initialize return NULL; } ualstrcpynW(pszUser, psid->wszID, cchUser); return pszUser; } HRESULT CSharedDocuments::_GetPathForUser(LPCTSTR pszUser, LPTSTR pszBuffer, int cchBuffer) { HRESULT hr = E_FAIL; BOOL fResult = FALSE; if (!pszUser) { // get the common documents path (which covers all users), this user is always defined // so lets return TRUE if they just want to check to see if its defined, otherwise // just pass out the result from fetching the path. fResult = !pszBuffer || (SHGetSpecialFolderPath(NULL, pszBuffer, CSIDL_COMMON_DOCUMENTS, FALSE)); } else { // we have a user ID, so lets attempt to get the path fro that from the registry // if we get it then pass it back to the caller. DWORD dwType; DWORD cbBuffer = cchBuffer*sizeof(TCHAR); if (ERROR_SUCCESS == SHGetValue(HKEY_LOCAL_MACHINE, REGSTR_PATH_DOCFOLDERPATH, pszUser, &dwType, pszBuffer, &cbBuffer)) { fResult = ((dwType == REG_SZ) && cbBuffer); // did we get a value back? } } if (fResult) { hr = S_OK; } return hr; } HRESULT CSharedDocuments::_GetSharedFolders(HDPA *phItems) { HRESULT hr = E_OUTOFMEMORY; HDPA hItems = DPA_Create(16); if (hItems) { if (!IsUserAGuest()) // all other users' my documents folders should appear in my computer for non-guest users on workgroup machines { ILogonEnumUsers *peu; hr = SHCoCreateInstance(NULL, &CLSID_ShellLogonEnumUsers, NULL, IID_PPV_ARG(ILogonEnumUsers, &peu)); if (SUCCEEDED(hr)) { UINT cUsers, iUser; hr = peu->get_length(&cUsers); for (iUser = 0; (cUsers != iUser) && SUCCEEDED(hr); iUser++) { VARIANT varUser = {VT_I4}; InitVariantFromInt(&varUser, iUser); ILogonUser *plu; hr = peu->item(varUser, &plu); if (SUCCEEDED(hr)) { // only show document folders for users that can log in VARIANT_BOOL vbLogonAllowed; hr = plu->get_interactiveLogonAllowed(&vbLogonAllowed); if (SUCCEEDED(hr) && (vbLogonAllowed != VARIANT_FALSE)) { // get the user name as this is our key to to the users documents path VARIANT var = {0}; hr = plu->get_setting(L"LoginName", &var); if (SUCCEEDED(hr)) { #if FILTER_CURRENT_USER if (!_szCurrentUser[0]) { DWORD cchUser = ARRAYSIZE(_szCurrentUser); if (!GetUserName(_szCurrentUser, &cchUser)) { _szCurrentUser[0] = TEXT('\0'); } } if (!_szCurrentUser[0] || (StrCmpI(var.bstrVal, _szCurrentUser) != 0)) { HRESULT hrT = _AddIDList(hItems, SHAREDID_USER, var.bstrVal); if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hrT) { SHDeleteValue(HKEY_LOCAL_MACHINE, REGSTR_PATH_DOCFOLDERPATH, var.bstrVal); } } #else HRESULT hrT = _AddIDList(hItems, SHAREDID_USER, var.bstrVal); if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hrT) { SHDeleteValue(HKEY_LOCAL_MACHINE, REGSTR_PATH_DOCFOLDERPATH, var.bstrVal); } #endif VariantClear(&var); } } plu->Release(); } } peu->Release(); } } _AddIDList(hItems, SHAREDID_COMMON, NULL); hr = S_OK; } *phItems = hItems; return hr; } // parsing support allows us to pick off SharedDocuments from the root // of the shell namespace and navigate there - this a canonical name // that we use for binding to the shared documents folder attached // to the My Computer namespace. HRESULT CSharedDocuments::ParseDisplayName(HWND hwnd, LPBC pbc, LPTSTR pszName, ULONG* pchEaten, LPITEMIDLIST* ppidl, ULONG* pdwAttributes) { HRESULT hr = E_INVALIDARG; if (SHShowSharedFolders()) { if (0 == StrCmpI(pszName, L"SharedDocuments")) { hr = _AllocIDList(SHAREDID_COMMON, NULL, ppidl); if (SUCCEEDED(hr) && pdwAttributes) { hr = _GetAttributesOf(*ppidl, *pdwAttributes, pdwAttributes); } } } return hr; } // enumerate the shared documents folders HRESULT CSharedDocuments::EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList **ppenumIDList) { *ppenumIDList = NULL; // no enumerator yet HRESULT hr = S_FALSE; if (SHShowSharedFolders()) { HDPA hItems; hr = _GetSharedFolders(&hItems); if (SUCCEEDED(hr)) { hr = CSharedDocsEnum_CreateInstance(hItems, grfFlags, ppenumIDList); if (FAILED(hr)) { DPA_FreeIDArray(hItems); } } } return hr; } // return the display name for the folders that we have HRESULT CSharedDocuments::GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD uFlags, LPSTRRET lpName) { HRESULT hr = S_OK; TCHAR szName[MAX_PATH] = {0}; LPCSHAREDITEM psid = (LPCSHAREDITEM)pidl; if (((uFlags & (SHGDN_INFOLDER|SHGDN_FORPARSING)) == SHGDN_INFOLDER) && (psid && (psid->dwType == SHAREDID_USER))) { // compute the 's Documents name that we will show, we key this on // the user name we have in the IDList and its display string. USER_INFO_10 *pui; TCHAR szUser[MAX_PATH]; if (NERR_Success == NetUserGetInfo(NULL, _GetUserFromIDList(pidl, szUser, ARRAYSIZE(szUser)), 10, (LPBYTE*)&pui)) { if (*pui->usri10_full_name) { StrCpyN(szUser, pui->usri10_full_name, ARRAYSIZE(szUser)); } NetApiBufferFree(pui); } TCHAR szFmt[MAX_PATH]; LoadString(g_hinst, IDS_LOCALGDN_FLD_THEIRDOCUMENTS, szFmt, ARRAYSIZE(szFmt)); wnsprintf(szName, ARRAYSIZE(szName), szFmt, szUser); } else { // all other scenarios dump down to the real folder to get their display // name for this folder. LPITEMIDLIST pidlTarget; hr = _GetTarget(pidl, &pidlTarget); if (SUCCEEDED(hr)) { hr = SHGetNameAndFlags(pidlTarget, uFlags, szName, ARRAYSIZE(szName), NULL); ILFree(pidlTarget); } } if (SUCCEEDED(hr)) hr = StringToStrRet(szName, lpName); return hr; } LONG CSharedDocuments::_GetAttributesOf(LPCITEMIDLIST pidl, DWORD rgfIn, DWORD *prgfOut) { DWORD dwResult = rgfIn; LPITEMIDLIST pidlTarget; HRESULT hr = _GetTarget(pidl, &pidlTarget); if (SUCCEEDED(hr)) { IShellFolder *psf; LPCITEMIDLIST pidlChild; hr = SHBindToIDListParent(pidlTarget, IID_PPV_ARG(IShellFolder, &psf), &pidlChild); if (SUCCEEDED(hr)) { hr = psf->GetAttributesOf(1, &pidlChild, &dwResult); psf->Release(); } ILFree(pidlTarget); } if (!SHShowSharedFolders()) dwResult |= SFGAO_NONENUMERATED; *prgfOut = *prgfOut & (dwResult & ~(SFGAO_CANDELETE|SFGAO_CANRENAME|SFGAO_CANMOVE|SFGAO_CANCOPY)); return hr; } HRESULT CSharedDocuments::GetAttributesOf(UINT cidl, LPCITEMIDLIST* apidl, ULONG* rgfInOut) { ULONG rgfOut = *rgfInOut; if (!cidl || !apidl) return E_INVALIDARG; for (UINT i = 0; i < cidl; i++) _GetAttributesOf(apidl[i], *rgfInOut, &rgfOut); *rgfInOut = rgfOut; return S_OK; } // bind through our folder HRESULT CSharedDocuments::BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv) { HRESULT hr = E_OUTOFMEMORY; if (IsEqualIID(riid, IID_IShellIconOverlay)) { hr = this->QueryInterface(riid, ppv); } else { LPITEMIDLIST pidlFirst = ILCloneFirst(pidl); if (pidlFirst) { IShellFolder *psf; hr = _CreateFolder(pbc, pidlFirst, IID_PPV_ARG(IShellFolder, &psf), TRUE); if (SUCCEEDED(hr)) { LPCITEMIDLIST pidlNext = _ILNext(pidl); if (ILIsEmpty(pidlNext)) { hr = psf->QueryInterface(riid, ppv); } else { hr = psf->BindToObject(pidlNext, pbc, riid, ppv); } psf->Release(); } ILFree(pidlFirst); } } return hr; } // handle UI objects - for the most part we delegate to the real namespace implementation HRESULT CSharedDocuments::s_FolderMenuCB(IShellFolder *psf, HWND hwnd, IDataObject *pdo, UINT uMsg, WPARAM wParam, LPARAM lParam) { CSharedDocuments *psd; psf->QueryInterface(CLSID_SharedDocuments, (void **)&psd); // defcm will only add the default handlers (eg. Open/Explore) if we have a callback // and the DFM_MERGECONTEXTMENU is successful. so lets honor that so we can navigate if (uMsg == DFM_MERGECONTEXTMENU) { return S_OK; } else if (uMsg == DFM_INVOKECOMMAND) { HRESULT hr; DFMICS *pdfmics = (DFMICS *)lParam; switch (wParam) { case DFM_CMD_LINK: hr = SHCreateLinks(hwnd, NULL, pdo, SHCL_CONFIRM|SHCL_USETEMPLATE|SHCL_USEDESKTOP, NULL); break; case DFM_CMD_PROPERTIES: hr = SHLaunchPropSheet(CFSFolder_PropertiesThread, pdo, (LPCTSTR)lParam, NULL, (void *)&c_idlDesktop); break; default: hr = S_FALSE; // use the default handler for this item break; } return hr; } return E_NOTIMPL; } HRESULT CSharedDocuments::GetUIObjectOf(HWND hwnd, UINT cidl, LPCITEMIDLIST* apidl, REFIID riid, UINT* prgfInOut, void **ppv) { if (cidl != 1) return E_FAIL; HRESULT hr = E_FAIL; if (IsEqualIID(riid, IID_IContextMenu)) { // we must construct our own context menu for this item, we do this using the // shell default implementation and we pass it the information about a folder // that way we can navigate up and down through the namespace. IQueryAssociations *pqa; hr = GetUIObjectOf(hwnd, 1, apidl, IID_PPV_ARG_NULL(IQueryAssociations, &pqa)); if (SUCCEEDED(hr)) { // this is broken for docfiles (shell\ext\stgfldr's keys work though) // maybe because GetClassFile punts when it's not fs? HKEY ahk[MAX_ASSOC_KEYS]; DWORD cKeys = SHGetAssocKeys(pqa, ahk, ARRAYSIZE(ahk)); hr = CDefFolderMenu_Create2(_pidl, hwnd, cidl, apidl, this, s_FolderMenuCB, cKeys, ahk, (IContextMenu **)ppv); SHRegCloseKeys(ahk, cKeys); pqa->Release(); } } else if (IsEqualIID(riid, IID_IDataObject)) { hr = SHCreateFileDataObject(_pidl, cidl, apidl, NULL, (IDataObject **)ppv); } else if (IsEqualIID(riid, IID_IQueryInfo)) { IQueryAssociations *pqa; hr = AssocCreate(CLSID_QueryAssociations, IID_PPV_ARG(IQueryAssociations, &pqa)); if (SUCCEEDED(hr)) { WCHAR szCLSID[GUIDSTR_MAX]; SHStringFromGUIDW(CLSID_SharedDocuments, szCLSID, ARRAYSIZE(szCLSID)); hr = pqa->Init(0, szCLSID, NULL, NULL); if (SUCCEEDED(hr)) { WCHAR szInfotip[INFOTIPSIZE]; DWORD cchInfotip = ARRAYSIZE(szInfotip); hr = pqa->GetString(0, ASSOCSTR_INFOTIP, NULL, szInfotip, &cchInfotip); if (SUCCEEDED(hr)) { hr = CreateInfoTipFromText(szInfotip, IID_IQueryInfo, ppv); // _the_ InfoTip COM object } } pqa->Release(); } } else if (IsEqualIID(riid, IID_IQueryAssociations)) { LPITEMIDLIST pidlTarget; hr = _GetTarget(apidl[0], &pidlTarget); if (SUCCEEDED(hr)) { hr = SHGetUIObjectOf(pidlTarget, hwnd, riid, ppv); ILFree(pidlTarget); } } else if (IsEqualIID(riid, IID_IExtractIconA) || IsEqualIID(riid, IID_IExtractIconW)) { UINT iIcon = II_FOLDER; UINT iIconOpen = II_FOLDEROPEN; TCHAR szModule[MAX_PATH]; GetModuleFileName(HINST_THISDLL, szModule, ARRAYSIZE(szModule)); hr = SHCreateDefExtIcon(szModule, iIcon, iIconOpen, GIL_PERCLASS, -1, riid, ppv); } else if (IsEqualIID(riid, IID_IDropTarget)) { IShellFolder *psf; hr = _CreateFolder(NULL, *apidl, IID_PPV_ARG(IShellFolder, &psf), TRUE); if (SUCCEEDED(hr)) { hr = psf->CreateViewObject(hwnd, riid, ppv); psf->Release(); } } return hr; } HRESULT CSharedDocuments::CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2) { HRESULT hr = ResultFromShort(0); // compare the contents of our IDLIST before we attemt to compare other elements // within it. LPCSHAREDITEM psid1 = (LPCSHAREDITEM)pidl1; LPCSHAREDITEM psid2 = (LPCSHAREDITEM)pidl2; if (psid1->dwType == psid2->dwType) { if (psid1->dwType == SHAREDID_USER) { hr = ResultFromShort(ualstrcmpi(psid1->wszID, psid2->wszID)); } else { hr = ResultFromShort(0); // common item == common item? } } else { hr = ResultFromShort(psid1->dwType - psid2->dwType); } // if there was an exact match then lets compare the trailing elements of the IDLIST // if there are some (by binding down) etc. if (hr == ResultFromShort(0)) { LPITEMIDLIST pidlNext1 = _ILNext(pidl1); LPITEMIDLIST pidlNext2 = _ILNext(pidl2); if (ILIsEmpty(pidlNext1)) { if (ILIsEmpty(pidlNext2)) { hr = ResultFromShort(0); // pidl1 == pidl2 (in length) } else { hr = ResultFromShort(-1); // pidl1 < pidl2 (in length) } } else { // if IDLIST2 is shorter then return > otherwise we should just // recurse down the IDLIST and let the next level compare. if (ILIsEmpty(pidlNext2)) { hr = ResultFromShort(+1); // pidl1 > pidl2 (in lenght) } else { LPITEMIDLIST pidlFirst = ILCloneFirst(pidl1); if (pidlFirst) { IShellFolder *psf; hr = _CreateFolder(NULL, pidlFirst, IID_PPV_ARG(IShellFolder, &psf), FALSE); if (SUCCEEDED(hr)) { hr = psf->CompareIDs(lParam, pidlNext1, pidlNext2); psf->Release(); } ILFree(pidlFirst); } else { hr = E_OUTOFMEMORY; } } } } return hr; } HRESULT CSharedDocuments::GetDetailsEx(LPCITEMIDLIST pidl, const SHCOLUMNID *pscid, VARIANT *pv) { HRESULT hr = E_FAIL; if (IsEqualSCID(SCID_DESCRIPTIONID, *pscid)) { SHDESCRIPTIONID did = {0}; did.dwDescriptionId = SHDID_COMPUTER_SHAREDDOCS; did.clsid = CLSID_NULL; hr = InitVariantFromBuffer(pv, &did, sizeof(did)); } else { LPITEMIDLIST pidlTarget; hr = _GetTarget(pidl, &pidlTarget); if (SUCCEEDED(hr)) { IShellFolder2 *psf2; LPCITEMIDLIST pidlChild; hr = SHBindToIDListParent(pidlTarget, IID_PPV_ARG(IShellFolder2, &psf2), &pidlChild); if (SUCCEEDED(hr)) { hr = psf2->GetDetailsEx(pidlChild, pscid, pv); psf2->Release(); } ILFree(pidlTarget); } } return hr; } // icon overlay handling. deligate this to the right handler HRESULT CSharedDocuments::_GetOverlayIndex(LPCITEMIDLIST pidl, int *pIndex, BOOL fGetIconIndex) { LPITEMIDLIST pidlTarget; HRESULT hr = _GetTarget(pidl, &pidlTarget); if (SUCCEEDED(hr)) { IShellIconOverlay *psio; LPCITEMIDLIST pidlChild; hr = SHBindToIDListParent(pidlTarget, IID_PPV_ARG(IShellIconOverlay, &psio), &pidlChild); if (SUCCEEDED(hr)) { if (fGetIconIndex) { hr = psio->GetOverlayIconIndex(pidlChild, pIndex); } else { hr = psio->GetOverlayIndex(pidlChild, pIndex); } psio->Release(); } ILFree(pidlTarget); } return hr; } // enumerator for listing all the shared documents in the system. class CSharedDocsEnum : public IEnumIDList { private: LONG _cRef; HDPA _hItems; DWORD _grfFlags; int _index; public: CSharedDocsEnum(HDPA hItems, DWORD grf); ~CSharedDocsEnum(); // IUnknown STDMETHOD(QueryInterface)(REFIID riid, void **ppv); STDMETHOD_(ULONG, AddRef)(); STDMETHOD_(ULONG, Release)(); // IEnumIDList STDMETHODIMP Next(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched); STDMETHODIMP Skip(ULONG celt) { return E_NOTIMPL; } STDMETHODIMP Reset() { _index = 0; return S_OK; } STDMETHODIMP Clone(IEnumIDList **ppenum) { return E_NOTIMPL; }; }; CSharedDocsEnum::CSharedDocsEnum(HDPA hItems, DWORD grfFlags) : _cRef(1), _hItems(hItems), _grfFlags(grfFlags), _index(0) { } CSharedDocsEnum::~CSharedDocsEnum() { DPA_FreeIDArray(_hItems); } HRESULT CSharedDocsEnum_CreateInstance(HDPA hItems, DWORD grfFlags, IEnumIDList **ppenum) { CSharedDocsEnum *penum = new CSharedDocsEnum(hItems, grfFlags); if (!penum) return E_OUTOFMEMORY; HRESULT hr = penum->QueryInterface(IID_PPV_ARG(IEnumIDList, ppenum)); penum->Release(); return hr; } // IUnknown handling STDMETHODIMP CSharedDocsEnum::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CSharedDocsEnum, IEnumIDList), // IID_IEnumIDList { 0 }, }; return QISearch(this, qit, riid, ppv); } STDMETHODIMP_(ULONG) CSharedDocsEnum::AddRef() { return InterlockedIncrement(&_cRef); } STDMETHODIMP_(ULONG) CSharedDocsEnum::Release() { if (InterlockedDecrement(&_cRef)) return _cRef; delete this; return 0; } // enumeration handling HRESULT CSharedDocsEnum::Next(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched) { HRESULT hr = S_FALSE; ULONG cFetched = 0; if (_grfFlags & SHCONTF_FOLDERS) { // if we have more items to return and the buffer is still not full // then lets ensure that we return them. while (SUCCEEDED(hr) && (celt != cFetched) && (_index != DPA_GetPtrCount(_hItems))) { if (_index != DPA_GetPtrCount(_hItems)) { hr = SHILClone((LPITEMIDLIST)DPA_GetPtr(_hItems, _index), &rgelt[cFetched]); if (SUCCEEDED(hr)) { cFetched++; } } _index++; } } if (pceltFetched) *pceltFetched = cFetched; return hr; } // handle system initialization of the shared documents objects void _SetLocalizedName(INT csidl, LPTSTR pszResModule, INT idsRes) { TCHAR szPath[MAX_PATH]; if (SHGetSpecialFolderPath(NULL, szPath, csidl, TRUE)) { SHSetLocalizedName(szPath, pszResModule, idsRes); } } HRESULT SHGetSampleMediaFolder(int nAllUsersMediaFolder, LPITEMIDLIST *ppidlSampleMedia); #define PICTURES_BUYURL L"SamplePictures" #define SAMPLEMUSIC_BUYURL L"http://windowsmedia.com/redir/xpsample.asp" STDAPI_(void) InitializeSharedDocs(BOOL fWow64) { // ACL the DocFolder paths key so that users can touch the keys and store their paths // for the document folders they have. // we want the "Everyone" to have read/write access SHELL_USER_PERMISSION supEveryone; supEveryone.susID = susEveryone; supEveryone.dwAccessType = ACCESS_ALLOWED_ACE_TYPE; supEveryone.dwAccessMask = KEY_READ|KEY_WRITE; supEveryone.fInherit = TRUE; supEveryone.dwInheritMask = (OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE); supEveryone.dwInheritAccessMask = GENERIC_READ; // we want the "SYSTEM" to have full control SHELL_USER_PERMISSION supSystem; supSystem.susID = susSystem; supSystem.dwAccessType = ACCESS_ALLOWED_ACE_TYPE; supSystem.dwAccessMask = KEY_ALL_ACCESS; supSystem.fInherit = TRUE; supSystem.dwInheritMask = (OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE); supSystem.dwInheritAccessMask = GENERIC_ALL; // we want the "Administrators" to have full control SHELL_USER_PERMISSION supAdministrators; supAdministrators.susID = susAdministrators; supAdministrators.dwAccessType = ACCESS_ALLOWED_ACE_TYPE; supAdministrators.dwAccessMask = KEY_ALL_ACCESS; supAdministrators.fInherit = TRUE; supAdministrators.dwInheritMask = (OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE); supAdministrators.dwInheritAccessMask = GENERIC_ALL; PSHELL_USER_PERMISSION aPerms[3] = {&supEveryone, &supSystem, &supAdministrators}; SECURITY_DESCRIPTOR* psd = GetShellSecurityDescriptor(aPerms, ARRAYSIZE(aPerms)); if (psd) { HKEY hk; if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\DocFolderPaths"), 0, NULL, REG_OPTION_NON_VOLATILE, MAXIMUM_ALLOWED, NULL, &hk, NULL) == ERROR_SUCCESS) { RegSetKeySecurity(hk, DACL_SECURITY_INFORMATION, psd); RegCloseKey(hk); } LocalFree(psd); } // do file system initialization as needed so that the shared music/pictures folders // have the correct display names. if (!fWow64) { _SetLocalizedName(CSIDL_COMMON_PICTURES, TEXT("shell32.dll"), IDS_SHAREDPICTURES); _SetLocalizedName(CSIDL_COMMON_MUSIC, TEXT("shell32.dll"), IDS_SHAREDMUSIC); // Set the Sample Pictures buy URL LPITEMIDLIST pidl; if (SUCCEEDED(SHGetSampleMediaFolder(CSIDL_COMMON_PICTURES, &pidl))) { WCHAR szPath[MAX_PATH]; WCHAR szDesktopIni[MAX_PATH]; if (SUCCEEDED(SHGetPathFromIDList(pidl, szPath)) && PathCombine(szDesktopIni, szPath, L"desktop.ini")) { WritePrivateProfileString(L".ShellClassInfo", c_BuySamplePictures.szURLKey, PICTURES_BUYURL, szDesktopIni); // Ensure this is a system folder PathMakeSystemFolder(szPath); } ILFree(pidl); } // Set the Sample Music buy URL if (SUCCEEDED(SHGetSampleMediaFolder(CSIDL_COMMON_MUSIC, &pidl))) { WCHAR szPath[MAX_PATH]; WCHAR szDesktopIni[MAX_PATH]; if (SUCCEEDED(SHGetPathFromIDList(pidl, szPath)) && PathCombine(szDesktopIni, szPath, L"desktop.ini")) { WritePrivateProfileString(L".ShellClassInfo", c_BuySampleMusic.szURLKey, SAMPLEMUSIC_BUYURL, szDesktopIni); // Ensure this is a system folder PathMakeSystemFolder(szPath); } ILFree(pidl); } } }