/* * Winefile * * Copyright 2000, 2003, 2004, 2005, 2008 Martin Fuchs * * 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 "winefile.h" #ifdef _SHELL_FOLDERS /* free shell allocated memory */ static void free_strret(STRRET* str) { if (str->uType == STRRET_WSTR) IMalloc_Free(Globals.iMalloc, str->UNION_MEMBER(pOleStr)); } #ifndef UNICODE /* copy up to count characters of the ANSI source string including the zero terminator to dest */ static LPSTR strcpyn(LPSTR dest, LPCSTR source, size_t count) { LPCSTR s; LPSTR d = dest; for(s=source; count&&(*d++=*s++); ) count--; return dest; } /* retreive an ANSI string from STRRET */ static void get_strretA(STRRET* str, const SHITEMID* shiid, LPSTR buffer, int len) { switch(str->uType) { case STRRET_WSTR: WideCharToMultiByte(CP_ACP, 0, str->UNION_MEMBER(pOleStr), -1, buffer, len, NULL, NULL); break; case STRRET_OFFSET: strcpyn(buffer, (LPCSTR)shiid+str->UNION_MEMBER(uOffset), len); break; case STRRET_CSTR: strcpyn(buffer, str->UNION_MEMBER(cStr), len); } } /* get the full file system path from item id list */ HRESULT path_from_pidlA(IShellFolder* folder, LPITEMIDLIST pidl, LPSTR buffer, int len) { STRRET str; /* SHGDN_FORPARSING: get full path of id list */ HRESULT hr = IShellFolder_GetDisplayNameOf(folder, pidl, SHGDN_FORPARSING, &str); if (SUCCEEDED(hr)) { get_strretA(&str, &pidl->mkid, buffer, len); free_strret(&str); } else buffer[0] = '\0'; return hr; } #endif /* copy up to count characters of the wide character source string including the zero terminator to dest */ static LPWSTR wcscpyn(LPWSTR dest, LPCWSTR source, size_t count) { LPCWSTR s; LPWSTR d = dest; for(s=source; count&&(*d++=*s++); ) count--; return dest; } /* retreive an wide character string from STRRET */ static void get_strretW(STRRET* str, const SHITEMID* shiid, LPWSTR buffer, int len) { switch(str->uType) { case STRRET_WSTR: wcscpyn(buffer, str->UNION_MEMBER(pOleStr), len); break; case STRRET_OFFSET: MultiByteToWideChar(CP_ACP, 0, (LPCSTR)shiid+str->UNION_MEMBER(uOffset), -1, buffer, len); break; case STRRET_CSTR: MultiByteToWideChar(CP_ACP, 0, str->UNION_MEMBER(cStr), -1, buffer, len); } } /* retrieve name string from an item id list */ static HRESULT name_from_pidl(IShellFolder* folder, LPITEMIDLIST pidl, LPTSTR buffer, int len, SHGDNF flags) { STRRET str; HRESULT hr = IShellFolder_GetDisplayNameOf(folder, pidl, flags, &str); if (SUCCEEDED(hr)) { get_strret(&str, &pidl->mkid, buffer, len); free_strret(&str); } else buffer[0] = '\0'; return hr; } /* retrieve path from an item id list */ HRESULT path_from_pidlW(IShellFolder* folder, LPITEMIDLIST pidl, LPWSTR buffer, int len) { STRRET str; /* SHGDN_FORPARSING: get full path of id list */ HRESULT hr = IShellFolder_GetDisplayNameOf(folder, pidl, SHGDN_FORPARSING, &str); if (SUCCEEDED(hr)) { get_strretW(&str, &pidl->mkid, buffer, len); free_strret(&str); } else buffer[0] = '\0'; return hr; } /* create an item id list from a file system path */ LPITEMIDLIST get_path_pidl(LPCTSTR path, HWND hwnd) { LPITEMIDLIST pidl; HRESULT hr; ULONG len; #ifdef UNICODE LPCWSTR buffer = path; #else WCHAR buffer[MAX_PATH]; MultiByteToWideChar(CP_ACP, 0, path, -1, buffer, MAX_PATH); #endif hr = IShellFolder_ParseDisplayName(Globals.iDesktop, hwnd, NULL, (LPOLESTR)buffer, &len, &pidl, NULL); if (FAILED(hr)) return NULL; return pidl; } /* convert an item id list from relative to absolute (=relative to the desktop) format */ LPITEMIDLIST get_to_absolute_pidl(Entry* entry, HWND hwnd) { if (entry->up && entry->up->etype==ET_SHELL) { IShellFolder* folder = entry->up->folder; WCHAR buffer[MAX_PATH]; HRESULT hr = path_from_pidlW(folder, entry->pidl, buffer, MAX_PATH); if (SUCCEEDED(hr)) { LPITEMIDLIST pidl; ULONG len; hr = IShellFolder_ParseDisplayName(Globals.iDesktop, hwnd, NULL, buffer, &len, &pidl, NULL); if (SUCCEEDED(hr)) return pidl; } } else if (entry->etype == ET_WINDOWS) { TCHAR path[MAX_PATH]; get_path(entry, path); return get_path_pidl(path, hwnd); } else if (entry->pidl) return ILClone(entry->pidl); return NULL; } /* retrieve display icon from the given item id list */ static HICON extract_icon(IShellFolder* folder, LPCITEMIDLIST pidl) { IExtractIcon* pExtract; if (SUCCEEDED(IShellFolder_GetUIObjectOf(folder, 0, 1, (LPCITEMIDLIST*)&pidl, &IID_IExtractIcon, 0, (LPVOID*)&pExtract))) { TCHAR path[_MAX_PATH]; unsigned flags; HICON hicon; int idx; if (SUCCEEDED((*pExtract->lpVtbl->GetIconLocation)(pExtract, GIL_FORSHELL, path, _MAX_PATH, &idx, &flags))) { if (!(flags & GIL_NOTFILENAME)) { if (idx == -1) idx = 0; /* special case for some control panel applications */ if ((int)ExtractIconEx(path, idx, 0, &hicon, 1) > 0) flags &= ~GIL_DONTCACHE; } else { HICON hIconLarge = 0; HRESULT hr = (*pExtract->lpVtbl->Extract)(pExtract, path, idx, &hIconLarge, &hicon, MAKELONG(0/*GetSystemMetrics(SM_CXICON)*/,GetSystemMetrics(SM_CXSMICON))); if (SUCCEEDED(hr)) DestroyIcon(hIconLarge); } return hicon; } } return 0; } /* search a Shell directory entry by item id list */ static Entry* find_entry_shell(Entry* dir, LPCITEMIDLIST pidl) { Entry* entry; for(entry=dir->down; entry; entry=entry->next) { if (entry->pidl->mkid.cb == pidl->mkid.cb && !memcmp(entry->pidl, pidl, entry->pidl->mkid.cb)) return entry; } return 0; } /* read a directory tree for the given item id list in the shell namespace */ Entry* read_tree_shell(Root* root, LPITEMIDLIST pidl, enum SORT_ORDER sortOrder, HWND hwnd) { Entry* entry = &root->entry; Entry* next; LPITEMIDLIST next_pidl = pidl; IShellFolder* folder; IShellFolder* child = NULL; HRESULT hr; HCURSOR old_cursor = SetCursor(LoadCursor(0, IDC_WAIT)); #ifndef _NO_EXTENSIONS entry->etype = ET_SHELL; #endif folder = Globals.iDesktop; while(entry) { entry->pidl = next_pidl; entry->folder = folder; if (!pidl->mkid.cb) break; /* copy first element of item id list */ next_pidl = IMalloc_Alloc(Globals.iMalloc, pidl->mkid.cb+sizeof(USHORT)); memcpy(next_pidl, pidl, pidl->mkid.cb); ((LPITEMIDLIST)((LPBYTE)next_pidl+pidl->mkid.cb))->mkid.cb = 0; hr = IShellFolder_BindToObject(folder, next_pidl, 0, &IID_IShellFolder, (void**)&child); if (!SUCCEEDED(hr)) break; read_directory(entry, NULL, sortOrder, hwnd); if (entry->down) entry->expanded = TRUE; next = find_entry_shell(entry, next_pidl); if (!next) break; folder = child; entry = next; /* go to next element */ pidl = (LPITEMIDLIST) ((LPBYTE)pidl+pidl->mkid.cb); } SetCursor(old_cursor); return entry; } /* fill a WIN32_FIND_DATA structure with information about a shell namespace object */ static void fill_w32fdata_shell(IShellFolder* folder, LPCITEMIDLIST pidl, SFGAOF attribs, WIN32_FIND_DATA* w32fdata) { if (!(attribs & SFGAO_FILESYSTEM) || FAILED(SHGetDataFromIDList(folder, pidl, SHGDFIL_FINDDATA, w32fdata, sizeof(WIN32_FIND_DATA)))) { WIN32_FILE_ATTRIBUTE_DATA fad; IDataObject* pDataObj; STGMEDIUM medium = {0, {0}, 0}; FORMATETC fmt = {Globals.cfStrFName, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; HRESULT hr = IShellFolder_GetUIObjectOf(folder, 0, 1, &pidl, &IID_IDataObject, 0, (LPVOID*)&pDataObj); if (SUCCEEDED(hr)) { hr = IDataObject_GetData(pDataObj, &fmt, &medium); IDataObject_Release(pDataObj); if (SUCCEEDED(hr)) { LPCTSTR path = (LPCTSTR)GlobalLock(medium.UNION_MEMBER(hGlobal)); UINT sem_org = SetErrorMode(SEM_FAILCRITICALERRORS); if (GetFileAttributesEx(path, GetFileExInfoStandard, &fad)) { w32fdata->dwFileAttributes = fad.dwFileAttributes; w32fdata->ftCreationTime = fad.ftCreationTime; w32fdata->ftLastAccessTime = fad.ftLastAccessTime; w32fdata->ftLastWriteTime = fad.ftLastWriteTime; if (!(fad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { w32fdata->nFileSizeLow = fad.nFileSizeLow; w32fdata->nFileSizeHigh = fad.nFileSizeHigh; } } SetErrorMode(sem_org); GlobalUnlock(medium.UNION_MEMBER(hGlobal)); GlobalFree(medium.UNION_MEMBER(hGlobal)); } } } if (attribs & (SFGAO_FOLDER|SFGAO_HASSUBFOLDER)) w32fdata->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY; if (attribs & SFGAO_READONLY) w32fdata->dwFileAttributes |= FILE_ATTRIBUTE_READONLY; if (attribs & SFGAO_COMPRESSED) w32fdata->dwFileAttributes |= FILE_ATTRIBUTE_COMPRESSED; } /* read a directory entry using the WIN32 file system API */ void read_directory_shell(Entry* dir, HWND hwnd) { IShellFolder* folder = dir->folder; int level = dir->level + 1; HRESULT hr; IShellFolder* child; IEnumIDList* idlist; Entry* first_entry = NULL; Entry* last = NULL; Entry* entry; if (!folder) return; hr = IShellFolder_EnumObjects(folder, hwnd, SHCONTF_FOLDERS|SHCONTF_NONFOLDERS|SHCONTF_INCLUDEHIDDEN|SHCONTF_SHAREABLE|SHCONTF_STORAGE, &idlist); if (SUCCEEDED(hr)) { for(;;) { #define FETCH_ITEM_COUNT 32 LPITEMIDLIST pidls[FETCH_ITEM_COUNT]; SFGAOF attribs; ULONG cnt = 0; ULONG n; memset(pidls, 0, sizeof(pidls)); hr = IEnumIDList_Next(idlist, FETCH_ITEM_COUNT, pidls, &cnt); if (!SUCCEEDED(hr)) break; if (hr == S_FALSE) break; for(n=0; nnext = entry; memset(&entry->data, 0, sizeof(WIN32_FIND_DATA)); entry->bhfi_valid = FALSE; attribs = ~SFGAO_FILESYSTEM; /*SFGAO_HASSUBFOLDER|SFGAO_FOLDER; SFGAO_FILESYSTEM sorgt dafür, daß "My Documents" anstatt von "Martin's Documents" angezeigt wird */ hr = IShellFolder_GetAttributesOf(folder, 1, (LPCITEMIDLIST*)&pidls[n], &attribs); if (SUCCEEDED(hr)) { if (attribs != (SFGAOF)~SFGAO_FILESYSTEM) { fill_w32fdata_shell(folder, pidls[n], attribs, &entry->data); entry->bhfi_valid = TRUE; } else attribs = 0; } else attribs = 0; entry->pidl = pidls[n]; if (entry->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { hr = IShellFolder_BindToObject(folder, pidls[n], 0, &IID_IShellFolder, (void**)&child); if (SUCCEEDED(hr)) entry->folder = child; else entry->folder = NULL; } else entry->folder = NULL; if (!entry->data.cFileName[0]) /*hr = */name_from_pidl(folder, pidls[n], entry->data.cFileName, MAX_PATH, /*SHGDN_INFOLDER*/0x2000/*0x2000=SHGDN_INCLUDE_NONFILESYS*/); /* get display icons for files and virtual objects */ if (!(entry->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) || !(attribs & SFGAO_FILESYSTEM)) { entry->hicon = extract_icon(folder, pidls[n]); if (!entry->hicon) entry->hicon = (HICON)-1; /* don't try again later */ } entry->down = NULL; entry->up = dir; entry->expanded = FALSE; entry->scanned = FALSE; entry->level = level; #ifndef _NO_EXTENSIONS entry->etype = ET_SHELL; entry->bhfi_valid = FALSE; #endif last = entry; } } IEnumIDList_Release(idlist); } if (last) last->next = NULL; dir->down = first_entry; dir->scanned = TRUE; } #endif /* _SHELL_FOLDERS */