/* * file system folder * * Copyright 1997 Marcus Meissner * Copyright 1998, 1999, 2002 Juergen Schmied * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #include "config.h" #include "wine/port.h" #include <stdlib.h> #include <string.h> #include <stdarg.h> #include <stdio.h> #define COBJMACROS #define NONAMELESSUNION #define NONAMELESSSTRUCT #include "winerror.h" #include "windef.h" #include "winbase.h" #include "winreg.h" #include "wingdi.h" #include "winuser.h" #include "ole2.h" #include "shlguid.h" #include "pidl.h" #include "undocshell.h" #include "shell32_main.h" #include "shresdef.h" #include "shlwapi.h" #include "shellfolder.h" #include "wine/debug.h" #include "debughlp.h" #include "shfldr.h" WINE_DEFAULT_DEBUG_CHANNEL (shell); /*********************************************************************** * IShellFolder implementation */ typedef struct { const IUnknownVtbl *lpVtbl; LONG ref; const IShellFolder2Vtbl *lpvtblShellFolder; const IPersistFolder3Vtbl *lpvtblPersistFolder3; const IDropTargetVtbl *lpvtblDropTarget; const ISFHelperVtbl *lpvtblSFHelper; IUnknown *pUnkOuter; /* used for aggregation */ CLSID *pclsid; /* both paths are parsible from the desktop */ LPWSTR sPathTarget; /* complete path to target used for enumeration and ChangeNotify */ LPITEMIDLIST pidlRoot; /* absolute pidl */ UINT cfShellIDList; /* clipboardformat for IDropTarget */ BOOL fAcceptFmt; /* flag for pending Drop */ } IGenericSFImpl; static const IUnknownVtbl unkvt; static const IShellFolder2Vtbl sfvt; static const IPersistFolder3Vtbl vt_FSFldr_PersistFolder3; /* IPersistFolder3 for a FS_Folder */ static const IDropTargetVtbl dtvt; static const ISFHelperVtbl shvt; static inline IGenericSFImpl *impl_from_IShellFolder2( IShellFolder2 *iface ) { return (IGenericSFImpl *)((char*)iface - FIELD_OFFSET(IGenericSFImpl, lpvtblShellFolder)); } static inline IGenericSFImpl *impl_from_IPersistFolder3( IPersistFolder3 *iface ) { return (IGenericSFImpl *)((char*)iface - FIELD_OFFSET(IGenericSFImpl, lpvtblPersistFolder3)); } static inline IGenericSFImpl *impl_from_IDropTarget( IDropTarget *iface ) { return (IGenericSFImpl *)((char*)iface - FIELD_OFFSET(IGenericSFImpl, lpvtblDropTarget)); } static inline IGenericSFImpl *impl_from_ISFHelper( ISFHelper *iface ) { return (IGenericSFImpl *)((char*)iface - FIELD_OFFSET(IGenericSFImpl, lpvtblSFHelper)); } /* converts This to an interface pointer */ #define _IUnknown_(This) ((IUnknown*)&(This)->lpVtbl) #define _IShellFolder_(This) ((IShellFolder*)&(This)->lpvtblShellFolder) #define _IShellFolder2_(This) ((IShellFolder2*)&(This)->lpvtblShellFolder) #define _IPersist_(This) (&(This)->lpvtblPersistFolder3) #define _IPersistFolder_(This) (&(This)->lpvtblPersistFolder3) #define _IPersistFolder2_(This) (&(This)->lpvtblPersistFolder3) #define _IPersistFolder3_(This) (&(This)->lpvtblPersistFolder3) #define _IDropTarget_(This) (&(This)->lpvtblDropTarget) #define _ISFHelper_(This) (&(This)->lpvtblSFHelper) /************************************************************************** * registers clipboardformat once */ static void SF_RegisterClipFmt (IGenericSFImpl * This) { TRACE ("(%p)\n", This); if (!This->cfShellIDList) { This->cfShellIDList = RegisterClipboardFormatW (CFSTR_SHELLIDLISTW); } } /************************************************************************** * we need a separate IUnknown to handle aggregation * (inner IUnknown) */ static HRESULT WINAPI IUnknown_fnQueryInterface (IUnknown * iface, REFIID riid, LPVOID * ppvObj) { IGenericSFImpl *This = (IGenericSFImpl *)iface; TRACE ("(%p)->(%s,%p)\n", This, shdebugstr_guid (riid), ppvObj); *ppvObj = NULL; if (IsEqualIID (riid, &IID_IUnknown)) *ppvObj = _IUnknown_ (This); else if (IsEqualIID (riid, &IID_IShellFolder)) *ppvObj = _IShellFolder_ (This); else if (IsEqualIID (riid, &IID_IShellFolder2)) *ppvObj = _IShellFolder_ (This); else if (IsEqualIID (riid, &IID_IPersist)) *ppvObj = _IPersist_ (This); else if (IsEqualIID (riid, &IID_IPersistFolder)) *ppvObj = _IPersistFolder_ (This); else if (IsEqualIID (riid, &IID_IPersistFolder2)) *ppvObj = _IPersistFolder2_ (This); else if (IsEqualIID (riid, &IID_IPersistFolder3)) *ppvObj = _IPersistFolder3_ (This); else if (IsEqualIID (riid, &IID_ISFHelper)) *ppvObj = _ISFHelper_ (This); else if (IsEqualIID (riid, &IID_IDropTarget)) { *ppvObj = _IDropTarget_ (This); SF_RegisterClipFmt (This); } if (*ppvObj) { IUnknown_AddRef ((IUnknown *) (*ppvObj)); TRACE ("-- Interface = %p\n", *ppvObj); return S_OK; } TRACE ("-- Interface: E_NOINTERFACE\n"); return E_NOINTERFACE; } static ULONG WINAPI IUnknown_fnAddRef (IUnknown * iface) { IGenericSFImpl *This = (IGenericSFImpl *)iface; ULONG refCount = InterlockedIncrement(&This->ref); TRACE ("(%p)->(count=%u)\n", This, refCount - 1); return refCount; } static ULONG WINAPI IUnknown_fnRelease (IUnknown * iface) { IGenericSFImpl *This = (IGenericSFImpl *)iface; ULONG refCount = InterlockedDecrement(&This->ref); TRACE ("(%p)->(count=%u)\n", This, refCount + 1); if (!refCount) { TRACE ("-- destroying IShellFolder(%p)\n", This); SHFree (This->pidlRoot); SHFree (This->sPathTarget); LocalFree (This); } return refCount; } static const IUnknownVtbl unkvt = { IUnknown_fnQueryInterface, IUnknown_fnAddRef, IUnknown_fnRelease, }; static const shvheader GenericSFHeader[] = { {IDS_SHV_COLUMN1, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 15}, {IDS_SHV_COLUMN2, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10}, {IDS_SHV_COLUMN3, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10}, {IDS_SHV_COLUMN4, SHCOLSTATE_TYPE_DATE | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 12}, {IDS_SHV_COLUMN5, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 5} }; #define GENERICSHELLVIEWCOLUMNS 5 /************************************************************************** * IFSFolder_Constructor * * NOTES * creating undocumented ShellFS_Folder as part of an aggregation * {F3364BA0-65B9-11CE-A9BA-00AA004AE837} * */ HRESULT WINAPI IFSFolder_Constructor (IUnknown * pUnkOuter, REFIID riid, LPVOID * ppv) { IGenericSFImpl *sf; TRACE ("unkOut=%p %s\n", pUnkOuter, shdebugstr_guid (riid)); if (pUnkOuter && !IsEqualIID (riid, &IID_IUnknown)) return CLASS_E_NOAGGREGATION; sf = LocalAlloc (LMEM_ZEROINIT, sizeof (IGenericSFImpl)); if (!sf) return E_OUTOFMEMORY; sf->ref = 0; sf->lpVtbl = &unkvt; sf->lpvtblShellFolder = &sfvt; sf->lpvtblPersistFolder3 = &vt_FSFldr_PersistFolder3; sf->lpvtblDropTarget = &dtvt; sf->lpvtblSFHelper = &shvt; sf->pclsid = (CLSID *) & CLSID_ShellFSFolder; sf->pUnkOuter = pUnkOuter ? pUnkOuter : _IUnknown_ (sf); if (FAILED (IUnknown_QueryInterface (_IUnknown_ (sf), riid, ppv))) { IUnknown_Release (_IUnknown_ (sf)); return E_NOINTERFACE; } TRACE ("--%p\n", *ppv); return S_OK; } /************************************************************************** * IShellFolder_fnQueryInterface * * PARAMETERS * REFIID riid [in ] Requested InterfaceID * LPVOID* ppvObject [out] Interface* to hold the result */ static HRESULT WINAPI IShellFolder_fnQueryInterface (IShellFolder2 * iface, REFIID riid, LPVOID * ppvObj) { IGenericSFImpl *This = impl_from_IShellFolder2(iface); TRACE ("(%p)->(%s,%p)\n", This, shdebugstr_guid (riid), ppvObj); return IUnknown_QueryInterface (This->pUnkOuter, riid, ppvObj); } /************************************************************************** * IShellFolder_AddRef */ static ULONG WINAPI IShellFolder_fnAddRef (IShellFolder2 * iface) { IGenericSFImpl *This = impl_from_IShellFolder2(iface); TRACE ("(%p)->(count=%u)\n", This, This->ref); return IUnknown_AddRef (This->pUnkOuter); } /************************************************************************** * IShellFolder_fnRelease */ static ULONG WINAPI IShellFolder_fnRelease (IShellFolder2 * iface) { IGenericSFImpl *This = impl_from_IShellFolder2(iface); TRACE ("(%p)->(count=%u)\n", This, This->ref); return IUnknown_Release (This->pUnkOuter); } /************************************************************************** * SHELL32_CreatePidlFromBindCtx [internal] * * If the caller bound File System Bind Data, assume it is the * find data for the path. * This allows binding of paths that don't exist. */ LPITEMIDLIST SHELL32_CreatePidlFromBindCtx(IBindCtx *pbc, LPCWSTR path) { static WCHAR szfsbc[] = { 'F','i','l','e',' ','S','y','s','t','e','m',' ', 'B','i','n','d',' ','D','a','t','a',0 }; IFileSystemBindData *fsbd = NULL; LPITEMIDLIST pidl = NULL; IUnknown *unk = NULL; HRESULT r; TRACE("%p %s\n", pbc, debugstr_w(path)); if (!pbc) return NULL; /* see if the caller bound File System Bind Data */ r = IBindCtx_GetObjectParam( pbc, szfsbc, &unk ); if (FAILED(r)) return NULL; r = IUnknown_QueryInterface( unk, &IID_IFileSystemBindData, (void**)&fsbd ); if (SUCCEEDED(r)) { WIN32_FIND_DATAW wfd; r = IFileSystemBindData_GetFindData( fsbd, &wfd ); if (SUCCEEDED(r)) { lstrcpynW( &wfd.cFileName[0], path, MAX_PATH ); pidl = _ILCreateFromFindDataW( &wfd ); } IFileSystemBindData_Release( fsbd ); } IUnknown_Release( unk ); return pidl; } /************************************************************************** * IShellFolder_ParseDisplayName {SHELL32} * * Parse a display name. * * PARAMS * hwndOwner [in] Parent window for any message's * pbc [in] optional FileSystemBindData context * lpszDisplayName [in] Unicode displayname. * pchEaten [out] (unicode) characters processed * ppidl [out] complex pidl to item * pdwAttributes [out] items attributes * * NOTES * Every folder tries to parse only its own (the leftmost) pidl and creates a * subfolder to evaluate the remaining parts. * Now we can parse into namespaces implemented by shell extensions * * Behaviour on win98: lpszDisplayName=NULL -> crash * lpszDisplayName="" -> returns mycoputer-pidl * * FIXME * pdwAttributes is not set * pchEaten is not set like in windows */ static HRESULT WINAPI IShellFolder_fnParseDisplayName (IShellFolder2 * iface, HWND hwndOwner, LPBC pbc, LPOLESTR lpszDisplayName, DWORD * pchEaten, LPITEMIDLIST * ppidl, DWORD * pdwAttributes) { IGenericSFImpl *This = impl_from_IShellFolder2(iface); HRESULT hr = E_INVALIDARG; LPCWSTR szNext = NULL; WCHAR szElement[MAX_PATH]; WCHAR szPath[MAX_PATH]; LPITEMIDLIST pidlTemp = NULL; DWORD len; TRACE ("(%p)->(HWND=%p,%p,%p=%s,%p,pidl=%p,%p)\n", This, hwndOwner, pbc, lpszDisplayName, debugstr_w (lpszDisplayName), pchEaten, ppidl, pdwAttributes); if (!lpszDisplayName || !ppidl) return E_INVALIDARG; if (pchEaten) *pchEaten = 0; /* strange but like the original */ pidlTemp = SHELL32_CreatePidlFromBindCtx(pbc, lpszDisplayName); if (!pidlTemp && *lpszDisplayName) { /* get the next element */ szNext = GetNextElementW (lpszDisplayName, szElement, MAX_PATH); /* build the full pathname to the element */ lstrcpynW(szPath, This->sPathTarget, MAX_PATH - 1); PathAddBackslashW(szPath); len = lstrlenW(szPath); lstrcpynW(szPath + len, szElement, MAX_PATH - len); /* get the pidl */ hr = _ILCreateFromPathW(szPath, &pidlTemp); if (SUCCEEDED(hr)) { if (szNext && *szNext) { /* try to analyse the next element */ hr = SHELL32_ParseNextElement (iface, hwndOwner, pbc, &pidlTemp, (LPOLESTR) szNext, pchEaten, pdwAttributes); } else { /* it's the last element */ if (pdwAttributes && *pdwAttributes) { hr = SHELL32_GetItemAttributes (_IShellFolder_ (This), pidlTemp, pdwAttributes); } } } } if (SUCCEEDED(hr)) *ppidl = pidlTemp; else *ppidl = NULL; TRACE ("(%p)->(-- pidl=%p ret=0x%08x)\n", This, *ppidl, hr); return hr; } /************************************************************************** * IShellFolder_fnEnumObjects * PARAMETERS * HWND hwndOwner, //[in ] Parent Window * DWORD grfFlags, //[in ] SHCONTF enumeration mask * LPENUMIDLIST* ppenumIDList //[out] IEnumIDList interface */ static HRESULT WINAPI IShellFolder_fnEnumObjects (IShellFolder2 * iface, HWND hwndOwner, DWORD dwFlags, LPENUMIDLIST * ppEnumIDList) { IGenericSFImpl *This = impl_from_IShellFolder2(iface); IEnumIDListImpl *list; TRACE ("(%p)->(HWND=%p flags=0x%08x pplist=%p)\n", This, hwndOwner, dwFlags, ppEnumIDList); if (!(list = IEnumIDList_Constructor())) return E_OUTOFMEMORY; CreateFolderEnumList(list, This->sPathTarget, dwFlags); *ppEnumIDList = &list->IEnumIDList_iface; TRACE ("-- (%p)->(new ID List: %p)\n", This, *ppEnumIDList); return S_OK; } /************************************************************************** * IShellFolder_fnBindToObject * PARAMETERS * LPCITEMIDLIST pidl, //[in ] relative pidl to open * LPBC pbc, //[in ] optional FileSystemBindData context * REFIID riid, //[in ] Initial Interface * LPVOID* ppvObject //[out] Interface* */ static HRESULT WINAPI IShellFolder_fnBindToObject (IShellFolder2 * iface, LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, LPVOID * ppvOut) { IGenericSFImpl *This = impl_from_IShellFolder2(iface); TRACE ("(%p)->(pidl=%p,%p,%s,%p)\n", This, pidl, pbc, shdebugstr_guid (riid), ppvOut); return SHELL32_BindToChild (This->pidlRoot, This->sPathTarget, pidl, riid, ppvOut); } /************************************************************************** * IShellFolder_fnBindToStorage * PARAMETERS * LPCITEMIDLIST pidl, //[in ] complex pidl to store * LPBC pbc, //[in ] reserved * REFIID riid, //[in ] Initial storage interface * LPVOID* ppvObject //[out] Interface* returned */ static HRESULT WINAPI IShellFolder_fnBindToStorage (IShellFolder2 * iface, LPCITEMIDLIST pidl, LPBC pbcReserved, REFIID riid, LPVOID * ppvOut) { IGenericSFImpl *This = impl_from_IShellFolder2(iface); FIXME ("(%p)->(pidl=%p,%p,%s,%p) stub\n", This, pidl, pbcReserved, shdebugstr_guid (riid), ppvOut); *ppvOut = NULL; return E_NOTIMPL; } /************************************************************************** * IShellFolder_fnCompareIDs */ static HRESULT WINAPI IShellFolder_fnCompareIDs (IShellFolder2 * iface, LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2) { IGenericSFImpl *This = impl_from_IShellFolder2(iface); int nReturn; TRACE ("(%p)->(0x%08lx,pidl1=%p,pidl2=%p)\n", This, lParam, pidl1, pidl2); nReturn = SHELL32_CompareIDs (_IShellFolder_ (This), lParam, pidl1, pidl2); TRACE ("-- %i\n", nReturn); return nReturn; } /************************************************************************** * IShellFolder_fnCreateViewObject */ static HRESULT WINAPI IShellFolder_fnCreateViewObject (IShellFolder2 * iface, HWND hwndOwner, REFIID riid, LPVOID * ppvOut) { IGenericSFImpl *This = impl_from_IShellFolder2(iface); LPSHELLVIEW pShellView; HRESULT hr = E_INVALIDARG; TRACE ("(%p)->(hwnd=%p,%s,%p)\n", This, hwndOwner, shdebugstr_guid (riid), ppvOut); if (ppvOut) { *ppvOut = NULL; if (IsEqualIID (riid, &IID_IDropTarget)) { hr = IShellFolder2_QueryInterface (iface, &IID_IDropTarget, ppvOut); } else if (IsEqualIID (riid, &IID_IContextMenu)) { FIXME ("IContextMenu not implemented\n"); hr = E_NOTIMPL; } else if (IsEqualIID (riid, &IID_IShellView)) { pShellView = IShellView_Constructor ((IShellFolder *) iface); if (pShellView) { hr = IShellView_QueryInterface (pShellView, riid, ppvOut); IShellView_Release (pShellView); } } } TRACE ("-- (%p)->(interface=%p)\n", This, ppvOut); return hr; } /************************************************************************** * IShellFolder_fnGetAttributesOf * * PARAMETERS * UINT cidl, //[in ] num elements in pidl array * LPCITEMIDLIST* apidl, //[in ] simple pidl array * ULONG* rgfInOut) //[out] result array * */ static HRESULT WINAPI IShellFolder_fnGetAttributesOf (IShellFolder2 * iface, UINT cidl, LPCITEMIDLIST * apidl, DWORD * rgfInOut) { IGenericSFImpl *This = impl_from_IShellFolder2(iface); HRESULT hr = S_OK; TRACE ("(%p)->(cidl=%d apidl=%p mask=%p (0x%08x))\n", This, cidl, apidl, rgfInOut, rgfInOut ? *rgfInOut : 0); if (!rgfInOut) return E_INVALIDARG; if (cidl && !apidl) return E_INVALIDARG; if (*rgfInOut == 0) *rgfInOut = ~0; if(cidl == 0){ IShellFolder *psfParent = NULL; LPCITEMIDLIST rpidl = NULL; hr = SHBindToParent(This->pidlRoot, &IID_IShellFolder, (LPVOID*)&psfParent, &rpidl); if(SUCCEEDED(hr)) { SHELL32_GetItemAttributes (psfParent, rpidl, rgfInOut); IShellFolder_Release(psfParent); } } else { while (cidl > 0 && *apidl) { pdump (*apidl); SHELL32_GetItemAttributes (_IShellFolder_ (This), *apidl, rgfInOut); apidl++; cidl--; } } /* make sure SFGAO_VALIDATE is cleared, some apps depend on that */ *rgfInOut &= ~SFGAO_VALIDATE; TRACE ("-- result=0x%08x\n", *rgfInOut); return hr; } /************************************************************************** * SHELL32_CreateExtensionUIObject (internal) */ HRESULT SHELL32_CreateExtensionUIObject(IShellFolder2 *iface, LPCITEMIDLIST pidl, REFIID riid, LPVOID *ppvOut) { static const WCHAR reg_blockedW[] = {'S','o','f','t','w','a','r','e','\\', 'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\', 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\', 'S','h','e','l','l',' ','E','x','t','e','n','s','i','o','n','s','\\', 'B','l','o','c','k','e','d',0}; static const WCHAR formatW[] = {'.','%','s','\\','S','h','e','l','l','E','x','\\', '{','%','0','8','x','-','%','0','4','x','-','%','0','4','x','-', '%','0','2','x','%','0','2','x','-','%','0','2','x','%','0','2','x', '%','0','2','x','%','0','2','x','%','0','2','x','%','0','2','x','}',0}; IPersistFile *persist_file; char extensionA[20]; WCHAR extensionW[20], buf[MAX_PATH]; DWORD size = MAX_PATH; STRRET path; WCHAR *file; GUID guid; HKEY key; HRESULT hr; if(!_ILGetExtension(pidl, extensionA, 20)) return S_FALSE; MultiByteToWideChar(CP_ACP, 0, extensionA, -1, extensionW, 20); sprintfW(buf, formatW, extensionW, riid->Data1, riid->Data2, riid->Data3, riid->Data4[0], riid->Data4[1], riid->Data4[2], riid->Data4[3], riid->Data4[4], riid->Data4[5], riid->Data4[6], riid->Data4[7]); if(RegGetValueW(HKEY_CLASSES_ROOT, buf, NULL, RRF_RT_REG_SZ, NULL, buf, &size) != ERROR_SUCCESS) return S_FALSE; if(RegCreateKeyExW(HKEY_LOCAL_MACHINE, reg_blockedW, 0, 0, 0, KEY_READ, NULL, &key, NULL) != ERROR_SUCCESS) return E_FAIL; if(RegQueryValueExW(key, buf, 0, NULL, NULL, NULL) != ERROR_FILE_NOT_FOUND) return E_ACCESSDENIED; RegCloseKey(key); if(RegCreateKeyExW(HKEY_CURRENT_USER, reg_blockedW, 0, 0, 0, KEY_READ, NULL, &key, NULL) != ERROR_SUCCESS) return E_FAIL; if(RegQueryValueExW(key, buf, 0, NULL, NULL, NULL) != ERROR_FILE_NOT_FOUND) return E_ACCESSDENIED; RegCloseKey(key); if(!GUIDFromStringW(buf, &guid)) return E_FAIL; hr = CoCreateInstance(&guid, NULL, CLSCTX_INPROC_SERVER, &IID_IPersistFile, (void**)&persist_file); if(FAILED(hr)) return hr; hr = IShellFolder2_GetDisplayNameOf(iface, pidl, SHGDN_FORPARSING, &path); if(SUCCEEDED(hr)) hr = StrRetToStrW(&path, NULL, &file); if(FAILED(hr)) { IPersistFile_Release(persist_file); return hr; } hr = IPersistFile_Load(persist_file, file, STGM_READ); CoTaskMemFree(file); if(FAILED(hr)) { IPersistFile_Release(persist_file); return hr; } hr = IPersistFile_QueryInterface(persist_file, riid, ppvOut); IPersistFile_Release(persist_file); return hr; } /************************************************************************** * IShellFolder_fnGetUIObjectOf * * PARAMETERS * HWND hwndOwner, //[in ] Parent window for any output * UINT cidl, //[in ] array size * LPCITEMIDLIST* apidl, //[in ] simple pidl array * REFIID riid, //[in ] Requested Interface * UINT* prgfInOut, //[ ] reserved * LPVOID* ppvObject) //[out] Resulting Interface * * NOTES * This function gets asked to return "view objects" for one or more (multiple * select) items: * The viewobject typically is an COM object with one of the following * interfaces: * IExtractIcon,IDataObject,IContextMenu * In order to support icon positions in the default Listview your DataObject * must implement the SetData method (in addition to GetData :) - the shell * passes a barely documented "Icon positions" structure to SetData when the * drag starts, and GetData's it if the drop is in another explorer window that * needs the positions. */ static HRESULT WINAPI IShellFolder_fnGetUIObjectOf (IShellFolder2 * iface, HWND hwndOwner, UINT cidl, LPCITEMIDLIST * apidl, REFIID riid, UINT * prgfInOut, LPVOID * ppvOut) { IGenericSFImpl *This = impl_from_IShellFolder2(iface); LPITEMIDLIST pidl; IUnknown *pObj = NULL; HRESULT hr = E_INVALIDARG; TRACE ("(%p)->(%p,%u,apidl=%p,%s,%p,%p)\n", This, hwndOwner, cidl, apidl, shdebugstr_guid (riid), prgfInOut, ppvOut); if (ppvOut) { *ppvOut = NULL; if(cidl == 1) { hr = SHELL32_CreateExtensionUIObject(iface, *apidl, riid, ppvOut); if(hr != S_FALSE) return hr; } if (IsEqualIID (riid, &IID_IContextMenu) && (cidl >= 1)) { return ItemMenu_Constructor((IShellFolder*)iface, This->pidlRoot, apidl, cidl, riid, ppvOut); } else if (IsEqualIID (riid, &IID_IDataObject) && (cidl >= 1)) { pObj = (LPUNKNOWN) IDataObject_Constructor (hwndOwner, This->pidlRoot, apidl, cidl); hr = S_OK; } else if (IsEqualIID (riid, &IID_IExtractIconA) && (cidl == 1)) { pidl = ILCombine (This->pidlRoot, apidl[0]); pObj = (LPUNKNOWN) IExtractIconA_Constructor (pidl); SHFree (pidl); hr = S_OK; } else if (IsEqualIID (riid, &IID_IExtractIconW) && (cidl == 1)) { pidl = ILCombine (This->pidlRoot, apidl[0]); pObj = (LPUNKNOWN) IExtractIconW_Constructor (pidl); SHFree (pidl); hr = S_OK; } else if (IsEqualIID (riid, &IID_IDropTarget) && (cidl >= 1)) { hr = IShellFolder2_QueryInterface (iface, &IID_IDropTarget, (LPVOID *) & pObj); } else if ((IsEqualIID(riid,&IID_IShellLinkW) || IsEqualIID(riid,&IID_IShellLinkA)) && (cidl == 1)) { pidl = ILCombine (This->pidlRoot, apidl[0]); hr = IShellLink_ConstructFromFile(NULL, riid, pidl, (LPVOID*)&pObj); SHFree (pidl); } else { hr = E_NOINTERFACE; } if (SUCCEEDED(hr) && !pObj) hr = E_OUTOFMEMORY; *ppvOut = pObj; } TRACE ("(%p)->hr=0x%08x\n", This, hr); return hr; } static const WCHAR AdvancedW[] = { 'S','O','F','T','W','A','R','E', '\\','M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\', 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\','E','x','p','l', 'o','r','e','r','\\','A','d','v','a','n','c','e','d',0 }; static const WCHAR HideFileExtW[] = { 'H','i','d','e','F','i','l','e','E','x', 't',0 }; static const WCHAR NeverShowExtW[] = { 'N','e','v','e','r','S','h','o','w','E', 'x','t',0 }; /****************************************************************************** * SHELL_FS_HideExtension [Internal] * * Query the registry if the filename extension of a given path should be * hidden. * * PARAMS * szPath [I] Relative or absolute path of a file * * RETURNS * TRUE, if the filename's extension should be hidden * FALSE, otherwise. */ BOOL SHELL_FS_HideExtension(LPCWSTR szPath) { HKEY hKey; DWORD dwData; DWORD dwDataSize = sizeof (DWORD); BOOL doHide = FALSE; /* The default value is FALSE (win98 at least) */ if (!RegCreateKeyExW(HKEY_CURRENT_USER, AdvancedW, 0, 0, 0, KEY_ALL_ACCESS, 0, &hKey, 0)) { if (!RegQueryValueExW(hKey, HideFileExtW, 0, 0, (LPBYTE) &dwData, &dwDataSize)) doHide = dwData; RegCloseKey (hKey); } if (!doHide) { LPWSTR ext = PathFindExtensionW(szPath); if (*ext != '\0') { WCHAR classname[MAX_PATH]; LONG classlen = sizeof(classname); if (!RegQueryValueW(HKEY_CLASSES_ROOT, ext, classname, &classlen)) if (!RegOpenKeyW(HKEY_CLASSES_ROOT, classname, &hKey)) { if (!RegQueryValueExW(hKey, NeverShowExtW, 0, NULL, NULL, NULL)) doHide = TRUE; RegCloseKey(hKey); } } } return doHide; } void SHELL_FS_ProcessDisplayFilename(LPWSTR szPath, DWORD dwFlags) { /*FIXME: MSDN also mentions SHGDN_FOREDITING which is not yet handled. */ if (!(dwFlags & SHGDN_FORPARSING) && ((dwFlags & SHGDN_INFOLDER) || (dwFlags == SHGDN_NORMAL))) { if (SHELL_FS_HideExtension(szPath) && szPath[0] != '.') PathRemoveExtensionW(szPath); } } /************************************************************************** * IShellFolder_fnGetDisplayNameOf * Retrieves the display name for the specified file object or subfolder * * PARAMETERS * LPCITEMIDLIST pidl, //[in ] complex pidl to item * DWORD dwFlags, //[in ] SHGNO formatting flags * LPSTRRET lpName) //[out] Returned display name * * FIXME * if the name is in the pidl the ret value should be a STRRET_OFFSET */ static HRESULT WINAPI IShellFolder_fnGetDisplayNameOf (IShellFolder2 * iface, LPCITEMIDLIST pidl, DWORD dwFlags, LPSTRRET strRet) { IGenericSFImpl *This = impl_from_IShellFolder2(iface); LPWSTR pszPath; HRESULT hr = S_OK; int len = 0; TRACE ("(%p)->(pidl=%p,0x%08x,%p)\n", This, pidl, dwFlags, strRet); pdump (pidl); if (!pidl || !strRet) return E_INVALIDARG; pszPath = CoTaskMemAlloc((MAX_PATH +1) * sizeof(WCHAR)); if (!pszPath) return E_OUTOFMEMORY; if (_ILIsDesktop(pidl)) { /* empty pidl */ if ((GET_SHGDN_FOR(dwFlags) & SHGDN_FORPARSING) && (GET_SHGDN_RELATION(dwFlags) != SHGDN_INFOLDER)) { if (This->sPathTarget) lstrcpynW(pszPath, This->sPathTarget, MAX_PATH); } else { /* pidl has to contain exactly one non null SHITEMID */ hr = E_INVALIDARG; } } else if (_ILIsPidlSimple(pidl)) { if ((GET_SHGDN_FOR(dwFlags) & SHGDN_FORPARSING) && (GET_SHGDN_RELATION(dwFlags) != SHGDN_INFOLDER) && This->sPathTarget) { lstrcpynW(pszPath, This->sPathTarget, MAX_PATH); PathAddBackslashW(pszPath); len = lstrlenW(pszPath); } _ILSimpleGetTextW(pidl, pszPath + len, MAX_PATH + 1 - len); if (!_ILIsFolder(pidl)) SHELL_FS_ProcessDisplayFilename(pszPath, dwFlags); } else { hr = SHELL32_GetDisplayNameOfChild(iface, pidl, dwFlags, pszPath, MAX_PATH); } if (SUCCEEDED(hr)) { /* Win9x always returns ANSI strings, NT always returns Unicode strings */ if (GetVersion() & 0x80000000) { strRet->uType = STRRET_CSTR; if (!WideCharToMultiByte(CP_ACP, 0, pszPath, -1, strRet->u.cStr, MAX_PATH, NULL, NULL)) strRet->u.cStr[0] = '\0'; CoTaskMemFree(pszPath); } else { strRet->uType = STRRET_WSTR; strRet->u.pOleStr = pszPath; } } else CoTaskMemFree(pszPath); TRACE ("-- (%p)->(%s)\n", This, strRet->uType == STRRET_CSTR ? strRet->u.cStr : debugstr_w(strRet->u.pOleStr)); return hr; } /************************************************************************** * IShellFolder_fnSetNameOf * Changes the name of a file object or subfolder, possibly changing its item * identifier in the process. * * PARAMETERS * HWND hwndOwner, //[in ] Owner window for output * LPCITEMIDLIST pidl, //[in ] simple pidl of item to change * LPCOLESTR lpszName, //[in ] the items new display name * DWORD dwFlags, //[in ] SHGNO formatting flags * LPITEMIDLIST* ppidlOut) //[out] simple pidl returned */ static HRESULT WINAPI IShellFolder_fnSetNameOf (IShellFolder2 * iface, HWND hwndOwner, LPCITEMIDLIST pidl, LPCOLESTR lpName, DWORD dwFlags, LPITEMIDLIST * pPidlOut) { IGenericSFImpl *This = impl_from_IShellFolder2(iface); WCHAR szSrc[MAX_PATH + 1], szDest[MAX_PATH + 1]; LPWSTR ptr; BOOL bIsFolder = _ILIsFolder (ILFindLastID (pidl)); TRACE ("(%p)->(%p,pidl=%p,%s,%u,%p)\n", This, hwndOwner, pidl, debugstr_w (lpName), dwFlags, pPidlOut); /* build source path */ lstrcpynW(szSrc, This->sPathTarget, MAX_PATH); ptr = PathAddBackslashW (szSrc); if (ptr) _ILSimpleGetTextW (pidl, ptr, MAX_PATH + 1 - (ptr - szSrc)); /* build destination path */ if (dwFlags == SHGDN_NORMAL || dwFlags & SHGDN_INFOLDER) { lstrcpynW(szDest, This->sPathTarget, MAX_PATH); ptr = PathAddBackslashW (szDest); if (ptr) lstrcpynW(ptr, lpName, MAX_PATH + 1 - (ptr - szDest)); } else lstrcpynW(szDest, lpName, MAX_PATH); if(!(dwFlags & SHGDN_FORPARSING) && SHELL_FS_HideExtension(szSrc)) { WCHAR *ext = PathFindExtensionW(szSrc); if(*ext != '\0') { INT len = strlenW(szDest); lstrcpynW(szDest + len, ext, MAX_PATH - len); } } TRACE ("src=%s dest=%s\n", debugstr_w(szSrc), debugstr_w(szDest)); if (MoveFileW (szSrc, szDest)) { HRESULT hr = S_OK; if (pPidlOut) hr = _ILCreateFromPathW(szDest, pPidlOut); SHChangeNotify (bIsFolder ? SHCNE_RENAMEFOLDER : SHCNE_RENAMEITEM, SHCNF_PATHW, szSrc, szDest); return hr; } return E_FAIL; } static HRESULT WINAPI IShellFolder_fnGetDefaultSearchGUID (IShellFolder2 *iface, GUID * pguid) { IGenericSFImpl *This = impl_from_IShellFolder2(iface); FIXME ("(%p)\n", This); return E_NOTIMPL; } static HRESULT WINAPI IShellFolder_fnEnumSearches (IShellFolder2 * iface, IEnumExtraSearch ** ppenum) { IGenericSFImpl *This = impl_from_IShellFolder2(iface); FIXME ("(%p)\n", This); return E_NOTIMPL; } static HRESULT WINAPI IShellFolder_fnGetDefaultColumn (IShellFolder2 * iface, DWORD dwRes, ULONG * pSort, ULONG * pDisplay) { IGenericSFImpl *This = impl_from_IShellFolder2(iface); TRACE ("(%p)\n", This); if (pSort) *pSort = 0; if (pDisplay) *pDisplay = 0; return S_OK; } static HRESULT WINAPI IShellFolder_fnGetDefaultColumnState (IShellFolder2 * iface, UINT iColumn, DWORD * pcsFlags) { IGenericSFImpl *This = impl_from_IShellFolder2(iface); TRACE ("(%p)\n", This); if (!pcsFlags || iColumn >= GENERICSHELLVIEWCOLUMNS) return E_INVALIDARG; *pcsFlags = GenericSFHeader[iColumn].pcsFlags; return S_OK; } static HRESULT WINAPI IShellFolder_fnGetDetailsEx (IShellFolder2 * iface, LPCITEMIDLIST pidl, const SHCOLUMNID * pscid, VARIANT * pv) { IGenericSFImpl *This = impl_from_IShellFolder2(iface); FIXME ("(%p)\n", This); return E_NOTIMPL; } static HRESULT WINAPI IShellFolder_fnGetDetailsOf (IShellFolder2 * iface, LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS * psd) { IGenericSFImpl *This = impl_from_IShellFolder2(iface); HRESULT hr = E_FAIL; TRACE ("(%p)->(%p %i %p)\n", This, pidl, iColumn, psd); if (!psd || iColumn >= GENERICSHELLVIEWCOLUMNS) return E_INVALIDARG; if (!pidl) { /* the header titles */ psd->fmt = GenericSFHeader[iColumn].fmt; psd->cxChar = GenericSFHeader[iColumn].cxChar; psd->str.uType = STRRET_CSTR; LoadStringA (shell32_hInstance, GenericSFHeader[iColumn].colnameid, psd->str.u.cStr, MAX_PATH); return S_OK; } else { hr = S_OK; psd->str.uType = STRRET_CSTR; /* the data from the pidl */ switch (iColumn) { case 0: /* name */ hr = IShellFolder2_GetDisplayNameOf (iface, pidl, SHGDN_NORMAL | SHGDN_INFOLDER, &psd->str); break; case 1: /* size */ _ILGetFileSize (pidl, psd->str.u.cStr, MAX_PATH); break; case 2: /* type */ _ILGetFileType (pidl, psd->str.u.cStr, MAX_PATH); break; case 3: /* date */ _ILGetFileDate (pidl, psd->str.u.cStr, MAX_PATH); break; case 4: /* attributes */ _ILGetFileAttributes (pidl, psd->str.u.cStr, MAX_PATH); break; } } return hr; } static HRESULT WINAPI IShellFolder_fnMapColumnToSCID (IShellFolder2 * iface, UINT column, SHCOLUMNID * pscid) { IGenericSFImpl *This = impl_from_IShellFolder2(iface); FIXME ("(%p)\n", This); return E_NOTIMPL; } static const IShellFolder2Vtbl sfvt = { IShellFolder_fnQueryInterface, IShellFolder_fnAddRef, IShellFolder_fnRelease, IShellFolder_fnParseDisplayName, IShellFolder_fnEnumObjects, IShellFolder_fnBindToObject, IShellFolder_fnBindToStorage, IShellFolder_fnCompareIDs, IShellFolder_fnCreateViewObject, IShellFolder_fnGetAttributesOf, IShellFolder_fnGetUIObjectOf, IShellFolder_fnGetDisplayNameOf, IShellFolder_fnSetNameOf, /* ShellFolder2 */ IShellFolder_fnGetDefaultSearchGUID, IShellFolder_fnEnumSearches, IShellFolder_fnGetDefaultColumn, IShellFolder_fnGetDefaultColumnState, IShellFolder_fnGetDetailsEx, IShellFolder_fnGetDetailsOf, IShellFolder_fnMapColumnToSCID }; /**************************************************************************** * ISFHelper for IShellFolder implementation */ static HRESULT WINAPI ISFHelper_fnQueryInterface (ISFHelper * iface, REFIID riid, LPVOID * ppvObj) { IGenericSFImpl *This = impl_from_ISFHelper(iface); TRACE ("(%p)->(count=%u)\n", This, This->ref); return IUnknown_QueryInterface (This->pUnkOuter, riid, ppvObj); } static ULONG WINAPI ISFHelper_fnAddRef (ISFHelper * iface) { IGenericSFImpl *This = impl_from_ISFHelper(iface); TRACE ("(%p)->(count=%u)\n", This, This->ref); return IUnknown_AddRef (This->pUnkOuter); } static ULONG WINAPI ISFHelper_fnRelease (ISFHelper * iface) { IGenericSFImpl *This = impl_from_ISFHelper(iface); TRACE ("(%p)\n", This); return IUnknown_Release (This->pUnkOuter); } /**************************************************************************** * ISFHelper_fnGetUniqueName * * creates a unique folder name */ static HRESULT WINAPI ISFHelper_fnGetUniqueName (ISFHelper * iface, LPWSTR pwszName, UINT uLen) { IGenericSFImpl *This = impl_from_ISFHelper(iface); IEnumIDList *penum; HRESULT hr; WCHAR wszText[MAX_PATH]; WCHAR wszNewFolder[25]; const WCHAR wszFormat[] = {'%','s',' ','%','d',0 }; TRACE ("(%p)(%p %u)\n", This, pwszName, uLen); LoadStringW(shell32_hInstance, IDS_NEWFOLDER, wszNewFolder, sizeof(wszNewFolder)/sizeof(WCHAR)); if (uLen < sizeof(wszNewFolder)/sizeof(WCHAR) + 3) return E_POINTER; lstrcpynW (pwszName, wszNewFolder, uLen); hr = IShellFolder_fnEnumObjects (_IShellFolder2_ (This), 0, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS | SHCONTF_INCLUDEHIDDEN, &penum); if (penum) { LPITEMIDLIST pidl; DWORD dwFetched; int i = 1; next: IEnumIDList_Reset (penum); while (S_OK == IEnumIDList_Next (penum, 1, &pidl, &dwFetched) && dwFetched) { _ILSimpleGetTextW (pidl, wszText, MAX_PATH); if (0 == lstrcmpiW (wszText, pwszName)) { snprintfW (pwszName, uLen, wszFormat, wszNewFolder, i++); if (i > 99) { hr = E_FAIL; break; } goto next; } } IEnumIDList_Release (penum); } return hr; } /**************************************************************************** * ISFHelper_fnAddFolder * * adds a new folder. */ static HRESULT WINAPI ISFHelper_fnAddFolder (ISFHelper * iface, HWND hwnd, LPCWSTR pwszName, LPITEMIDLIST * ppidlOut) { IGenericSFImpl *This = impl_from_ISFHelper(iface); WCHAR wszNewDir[MAX_PATH]; DWORD bRes; HRESULT hres = E_FAIL; TRACE ("(%p)(%s %p)\n", This, debugstr_w(pwszName), ppidlOut); wszNewDir[0] = 0; if (This->sPathTarget) lstrcpynW(wszNewDir, This->sPathTarget, MAX_PATH); PathAppendW(wszNewDir, pwszName); bRes = CreateDirectoryW (wszNewDir, NULL); if (bRes) { LPITEMIDLIST relPidl; lstrcpyW(wszNewDir, pwszName); hres = IShellFolder_ParseDisplayName((IShellFolder*)&This->lpvtblShellFolder, hwnd, NULL, wszNewDir, NULL, &relPidl, NULL); if (SUCCEEDED(hres)) { LPITEMIDLIST fullPidl; fullPidl = ILCombine(This->pidlRoot, relPidl); if (fullPidl) { SHChangeNotify(SHCNE_MKDIR, SHCNF_IDLIST, fullPidl, NULL); ILFree(fullPidl); if (ppidlOut) *ppidlOut = relPidl; else ILFree(relPidl); } else { WARN("failed to combine %s into a full PIDL\n", wine_dbgstr_w(pwszName)); ILFree(relPidl); } } else WARN("failed to parse %s into a PIDL\n", wine_dbgstr_w(pwszName)); } else { WCHAR wszText[128 + MAX_PATH]; WCHAR wszTempText[128]; WCHAR wszCaption[256]; /* Cannot Create folder because of permissions */ LoadStringW (shell32_hInstance, IDS_CREATEFOLDER_DENIED, wszTempText, sizeof (wszTempText)/sizeof (wszTempText[0])); LoadStringW (shell32_hInstance, IDS_CREATEFOLDER_CAPTION, wszCaption, sizeof (wszCaption)/sizeof (wszCaption[0])); sprintfW (wszText, wszTempText, wszNewDir); MessageBoxW (hwnd, wszText, wszCaption, MB_OK | MB_ICONEXCLAMATION); } return hres; } /**************************************************************************** * build_paths_list * * Builds a list of paths like the one used in SHFileOperation from a table of * PIDLs relative to the given base folder */ static WCHAR *build_paths_list(LPCWSTR wszBasePath, int cidl, const LPCITEMIDLIST *pidls) { WCHAR *wszPathsList; WCHAR *wszListPos; int iPathLen; int i; iPathLen = lstrlenW(wszBasePath); wszPathsList = HeapAlloc(GetProcessHeap(), 0, MAX_PATH*sizeof(WCHAR)*cidl+1); wszListPos = wszPathsList; for (i = 0; i < cidl; i++) { if (!_ILIsFolder(pidls[i]) && !_ILIsValue(pidls[i])) continue; lstrcpynW(wszListPos, wszBasePath, MAX_PATH); /* FIXME: abort if path too long */ _ILSimpleGetTextW(pidls[i], wszListPos+iPathLen, MAX_PATH-iPathLen); wszListPos += lstrlenW(wszListPos)+1; } *wszListPos=0; return wszPathsList; } /**************************************************************************** * ISFHelper_fnDeleteItems * * deletes items in folder */ static HRESULT WINAPI ISFHelper_fnDeleteItems (ISFHelper * iface, UINT cidl, LPCITEMIDLIST * apidl) { IGenericSFImpl *This = impl_from_ISFHelper(iface); UINT i; SHFILEOPSTRUCTW op; WCHAR wszPath[MAX_PATH]; WCHAR *wszPathsList; HRESULT ret; WCHAR *wszCurrentPath; TRACE ("(%p)(%u %p)\n", This, cidl, apidl); if (cidl==0) return S_OK; if (This->sPathTarget) lstrcpynW(wszPath, This->sPathTarget, MAX_PATH); else wszPath[0] = '\0'; PathAddBackslashW(wszPath); wszPathsList = build_paths_list(wszPath, cidl, apidl); ZeroMemory(&op, sizeof(op)); op.hwnd = GetActiveWindow(); op.wFunc = FO_DELETE; op.pFrom = wszPathsList; op.fFlags = FOF_ALLOWUNDO; if (SHFileOperationW(&op)) { WARN("SHFileOperation failed\n"); ret = E_FAIL; } else ret = S_OK; /* we currently need to manually send the notifies */ wszCurrentPath = wszPathsList; for (i = 0; i < cidl; i++) { LONG wEventId; if (_ILIsFolder(apidl[i])) wEventId = SHCNE_RMDIR; else if (_ILIsValue(apidl[i])) wEventId = SHCNE_DELETE; else continue; /* check if file exists */ if (GetFileAttributesW(wszCurrentPath) == INVALID_FILE_ATTRIBUTES) { LPITEMIDLIST pidl = ILCombine(This->pidlRoot, apidl[i]); SHChangeNotify(wEventId, SHCNF_IDLIST, pidl, NULL); SHFree(pidl); } wszCurrentPath += lstrlenW(wszCurrentPath)+1; } HeapFree(GetProcessHeap(), 0, wszPathsList); return ret; } /**************************************************************************** * ISFHelper_fnCopyItems * * copies items to this folder */ static HRESULT WINAPI ISFHelper_fnCopyItems (ISFHelper * iface, IShellFolder * pSFFrom, UINT cidl, LPCITEMIDLIST * apidl) { HRESULT ret=E_FAIL; IPersistFolder2 *ppf2 = NULL; WCHAR wszSrcPathRoot[MAX_PATH], wszDstPath[MAX_PATH+1]; WCHAR *wszSrcPathsList; IGenericSFImpl *This = impl_from_ISFHelper(iface); SHFILEOPSTRUCTW fop; TRACE ("(%p)->(%p,%u,%p)\n", This, pSFFrom, cidl, apidl); IShellFolder_QueryInterface (pSFFrom, &IID_IPersistFolder2, (LPVOID *) & ppf2); if (ppf2) { LPITEMIDLIST pidl; if (SUCCEEDED (IPersistFolder2_GetCurFolder (ppf2, &pidl))) { SHGetPathFromIDListW (pidl, wszSrcPathRoot); ZeroMemory(wszDstPath, MAX_PATH+1); if (This->sPathTarget) lstrcpynW(wszDstPath, This->sPathTarget, MAX_PATH); PathAddBackslashW(wszSrcPathRoot); PathAddBackslashW(wszDstPath); wszSrcPathsList = build_paths_list(wszSrcPathRoot, cidl, apidl); ZeroMemory(&fop, sizeof(fop)); fop.hwnd = GetActiveWindow(); fop.wFunc = FO_COPY; fop.pFrom = wszSrcPathsList; fop.pTo = wszDstPath; fop.fFlags = FOF_ALLOWUNDO; ret = S_OK; if(SHFileOperationW(&fop)) { WARN("Copy failed\n"); ret = E_FAIL; } HeapFree(GetProcessHeap(), 0, wszSrcPathsList); } SHFree(pidl); IPersistFolder2_Release(ppf2); } return ret; } static const ISFHelperVtbl shvt = { ISFHelper_fnQueryInterface, ISFHelper_fnAddRef, ISFHelper_fnRelease, ISFHelper_fnGetUniqueName, ISFHelper_fnAddFolder, ISFHelper_fnDeleteItems, ISFHelper_fnCopyItems }; /************************************************************************ * IFSFldr_PersistFolder3_QueryInterface * */ static HRESULT WINAPI IFSFldr_PersistFolder3_QueryInterface (IPersistFolder3 * iface, REFIID iid, LPVOID * ppvObj) { IGenericSFImpl *This = impl_from_IPersistFolder3(iface); TRACE ("(%p)\n", This); return IUnknown_QueryInterface (This->pUnkOuter, iid, ppvObj); } /************************************************************************ * IFSFldr_PersistFolder3_AddRef * */ static ULONG WINAPI IFSFldr_PersistFolder3_AddRef (IPersistFolder3 * iface) { IGenericSFImpl *This = impl_from_IPersistFolder3(iface); TRACE ("(%p)->(count=%u)\n", This, This->ref); return IUnknown_AddRef (This->pUnkOuter); } /************************************************************************ * IFSFldr_PersistFolder3_Release * */ static ULONG WINAPI IFSFldr_PersistFolder3_Release (IPersistFolder3 * iface) { IGenericSFImpl *This = impl_from_IPersistFolder3(iface); TRACE ("(%p)->(count=%u)\n", This, This->ref); return IUnknown_Release (This->pUnkOuter); } /************************************************************************ * IFSFldr_PersistFolder3_GetClassID */ static HRESULT WINAPI IFSFldr_PersistFolder3_GetClassID (IPersistFolder3 * iface, CLSID * lpClassId) { IGenericSFImpl *This = impl_from_IPersistFolder3(iface); TRACE ("(%p)\n", This); if (!lpClassId) return E_POINTER; *lpClassId = *This->pclsid; return S_OK; } /************************************************************************ * IFSFldr_PersistFolder3_Initialize * * NOTES * sPathTarget is not set. Don't know how to handle in a non rooted environment. */ static HRESULT WINAPI IFSFldr_PersistFolder3_Initialize (IPersistFolder3 * iface, LPCITEMIDLIST pidl) { WCHAR wszTemp[MAX_PATH]; IGenericSFImpl *This = impl_from_IPersistFolder3(iface); TRACE ("(%p)->(%p)\n", This, pidl); SHFree (This->pidlRoot); /* free the old pidl */ This->pidlRoot = ILClone (pidl); /* set my pidl */ SHFree (This->sPathTarget); This->sPathTarget = NULL; /* set my path */ if (SHGetPathFromIDListW (pidl, wszTemp)) { int len = strlenW(wszTemp); This->sPathTarget = SHAlloc((len + 1) * sizeof(WCHAR)); if (!This->sPathTarget) return E_OUTOFMEMORY; memcpy(This->sPathTarget, wszTemp, (len + 1) * sizeof(WCHAR)); } TRACE ("--(%p)->(%s)\n", This, debugstr_w(This->sPathTarget)); return S_OK; } /************************************************************************** * IFSFldr_PersistFolder3_GetCurFolder */ static HRESULT WINAPI IFSFldr_PersistFolder3_fnGetCurFolder (IPersistFolder3 * iface, LPITEMIDLIST * pidl) { IGenericSFImpl *This = impl_from_IPersistFolder3(iface); TRACE ("(%p)->(%p)\n", This, pidl); if (!pidl) return E_POINTER; *pidl = ILClone (This->pidlRoot); return S_OK; } /************************************************************************** * IFSFldr_PersistFolder3_InitializeEx * * FIXME: error handling */ static HRESULT WINAPI IFSFldr_PersistFolder3_InitializeEx (IPersistFolder3 * iface, IBindCtx * pbc, LPCITEMIDLIST pidlRoot, const PERSIST_FOLDER_TARGET_INFO * ppfti) { WCHAR wszTemp[MAX_PATH]; IGenericSFImpl *This = impl_from_IPersistFolder3(iface); TRACE ("(%p)->(%p,%p,%p)\n", This, pbc, pidlRoot, ppfti); if (ppfti) TRACE ("--%p %s %s 0x%08x 0x%08x\n", ppfti->pidlTargetFolder, debugstr_w (ppfti->szTargetParsingName), debugstr_w (ppfti->szNetworkProvider), ppfti->dwAttributes, ppfti->csidl); pdump (pidlRoot); if (ppfti && ppfti->pidlTargetFolder) pdump (ppfti->pidlTargetFolder); if (This->pidlRoot) __SHFreeAndNil (&This->pidlRoot); /* free the old */ if (This->sPathTarget) __SHFreeAndNil (&This->sPathTarget); /* * Root path and pidl */ This->pidlRoot = ILClone (pidlRoot); /* * the target folder is specified in csidl OR pidlTargetFolder OR * szTargetParsingName */ if (ppfti) { if (ppfti->csidl != -1) { if (SHGetSpecialFolderPathW (0, wszTemp, ppfti->csidl, ppfti->csidl & CSIDL_FLAG_CREATE)) { int len = strlenW(wszTemp); This->sPathTarget = SHAlloc((len + 1) * sizeof(WCHAR)); if (!This->sPathTarget) return E_OUTOFMEMORY; memcpy(This->sPathTarget, wszTemp, (len + 1) * sizeof(WCHAR)); } } else if (ppfti->szTargetParsingName[0]) { int len = strlenW(ppfti->szTargetParsingName); This->sPathTarget = SHAlloc((len + 1) * sizeof(WCHAR)); if (!This->sPathTarget) return E_OUTOFMEMORY; memcpy(This->sPathTarget, ppfti->szTargetParsingName, (len + 1) * sizeof(WCHAR)); } else if (ppfti->pidlTargetFolder) { if (SHGetPathFromIDListW(ppfti->pidlTargetFolder, wszTemp)) { int len = strlenW(wszTemp); This->sPathTarget = SHAlloc((len + 1) * sizeof(WCHAR)); if (!This->sPathTarget) return E_OUTOFMEMORY; memcpy(This->sPathTarget, wszTemp, (len + 1) * sizeof(WCHAR)); } } } TRACE ("--(%p)->(target=%s)\n", This, debugstr_w(This->sPathTarget)); pdump (This->pidlRoot); return (This->sPathTarget) ? S_OK : E_FAIL; } static HRESULT WINAPI IFSFldr_PersistFolder3_GetFolderTargetInfo (IPersistFolder3 * iface, PERSIST_FOLDER_TARGET_INFO * ppfti) { IGenericSFImpl *This = impl_from_IPersistFolder3(iface); FIXME ("(%p)->(%p)\n", This, ppfti); ZeroMemory (ppfti, sizeof (*ppfti)); return E_NOTIMPL; } static const IPersistFolder3Vtbl vt_FSFldr_PersistFolder3 = { IFSFldr_PersistFolder3_QueryInterface, IFSFldr_PersistFolder3_AddRef, IFSFldr_PersistFolder3_Release, IFSFldr_PersistFolder3_GetClassID, IFSFldr_PersistFolder3_Initialize, IFSFldr_PersistFolder3_fnGetCurFolder, IFSFldr_PersistFolder3_InitializeEx, IFSFldr_PersistFolder3_GetFolderTargetInfo }; /**************************************************************************** * ISFDropTarget implementation */ static BOOL ISFDropTarget_QueryDrop (IDropTarget * iface, DWORD dwKeyState, LPDWORD pdwEffect) { DWORD dwEffect = *pdwEffect; IGenericSFImpl *This = impl_from_IDropTarget(iface); *pdwEffect = DROPEFFECT_NONE; if (This->fAcceptFmt) { /* Does our interpretation of the keystate ... */ *pdwEffect = KeyStateToDropEffect (dwKeyState); /* ... matches the desired effect ? */ if (dwEffect & *pdwEffect) { return TRUE; } } return FALSE; } static HRESULT WINAPI ISFDropTarget_QueryInterface (IDropTarget * iface, REFIID riid, LPVOID * ppvObj) { IGenericSFImpl *This = impl_from_IDropTarget(iface); TRACE ("(%p)\n", This); return IUnknown_QueryInterface (This->pUnkOuter, riid, ppvObj); } static ULONG WINAPI ISFDropTarget_AddRef (IDropTarget * iface) { IGenericSFImpl *This = impl_from_IDropTarget(iface); TRACE ("(%p)\n", This); return IUnknown_AddRef (This->pUnkOuter); } static ULONG WINAPI ISFDropTarget_Release (IDropTarget * iface) { IGenericSFImpl *This = impl_from_IDropTarget(iface); TRACE ("(%p)\n", This); return IUnknown_Release (This->pUnkOuter); } static HRESULT WINAPI ISFDropTarget_DragEnter (IDropTarget * iface, IDataObject * pDataObject, DWORD dwKeyState, POINTL pt, DWORD * pdwEffect) { FORMATETC fmt; IGenericSFImpl *This = impl_from_IDropTarget(iface); TRACE ("(%p)->(DataObject=%p)\n", This, pDataObject); InitFormatEtc (fmt, This->cfShellIDList, TYMED_HGLOBAL); This->fAcceptFmt = IDataObject_QueryGetData (pDataObject, &fmt) == S_OK; ISFDropTarget_QueryDrop (iface, dwKeyState, pdwEffect); return S_OK; } static HRESULT WINAPI ISFDropTarget_DragOver (IDropTarget * iface, DWORD dwKeyState, POINTL pt, DWORD * pdwEffect) { IGenericSFImpl *This = impl_from_IDropTarget(iface); TRACE ("(%p)\n", This); if (!pdwEffect) return E_INVALIDARG; ISFDropTarget_QueryDrop (iface, dwKeyState, pdwEffect); return S_OK; } static HRESULT WINAPI ISFDropTarget_DragLeave (IDropTarget * iface) { IGenericSFImpl *This = impl_from_IDropTarget(iface); TRACE ("(%p)\n", This); This->fAcceptFmt = FALSE; return S_OK; } static HRESULT WINAPI ISFDropTarget_Drop (IDropTarget * iface, IDataObject * pDataObject, DWORD dwKeyState, POINTL pt, DWORD * pdwEffect) { IGenericSFImpl *This = impl_from_IDropTarget(iface); FIXME ("(%p) object dropped\n", This); return E_NOTIMPL; } static const IDropTargetVtbl dtvt = { ISFDropTarget_QueryInterface, ISFDropTarget_AddRef, ISFDropTarget_Release, ISFDropTarget_DragEnter, ISFDropTarget_DragOver, ISFDropTarget_DragLeave, ISFDropTarget_Drop };