shellfs.c

Go to the documentation of this file.
00001 /*
00002  * Winefile
00003  *
00004  * Copyright 2000, 2003, 2004, 2005, 2008 Martin Fuchs
00005  *
00006  * This library is free software; you can redistribute it and/or
00007  * modify it under the terms of the GNU Lesser General Public
00008  * License as published by the Free Software Foundation; either
00009  * version 2.1 of the License, or (at your option) any later version.
00010  *
00011  * This library is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014  * Lesser General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU Lesser General Public
00017  * License along with this library; if not, write to the Free Software
00018  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
00019  */
00020 
00021 #include "winefile.h"
00022 
00023 
00024 #ifdef _SHELL_FOLDERS
00025 
00026 /* free shell allocated memory */
00027 static void free_strret(STRRET* str)
00028 {
00029     if (str->uType == STRRET_WSTR)
00030         IMalloc_Free(Globals.iMalloc, str->UNION_MEMBER(pOleStr));
00031 }
00032 
00033 
00034 #ifndef UNICODE
00035 
00036 /* copy up to count characters of the ANSI source string including the zero terminator to dest */
00037 static LPSTR strcpyn(LPSTR dest, LPCSTR source, size_t count)
00038 {
00039  LPCSTR s;
00040  LPSTR d = dest;
00041 
00042  for(s=source; count&&(*d++=*s++); )
00043   count--;
00044 
00045  return dest;
00046 }
00047 
00048 /* retreive an ANSI string from STRRET */
00049 static void get_strretA(STRRET* str, const SHITEMID* shiid, LPSTR buffer, int len)
00050 {
00051  switch(str->uType) {
00052   case STRRET_WSTR:
00053     WideCharToMultiByte(CP_ACP, 0, str->UNION_MEMBER(pOleStr), -1, buffer, len, NULL, NULL);
00054     break;
00055 
00056   case STRRET_OFFSET:
00057     strcpyn(buffer, (LPCSTR)shiid+str->UNION_MEMBER(uOffset), len);
00058     break;
00059 
00060   case STRRET_CSTR:
00061     strcpyn(buffer, str->UNION_MEMBER(cStr), len);
00062  }
00063 }
00064 
00065 /* get the full file system path from item id list */
00066 HRESULT path_from_pidlA(IShellFolder* folder, LPITEMIDLIST pidl, LPSTR buffer, int len)
00067 {
00068     STRRET str;
00069 
00070     /* SHGDN_FORPARSING: get full path of id list */
00071     HRESULT hr = IShellFolder_GetDisplayNameOf(folder, pidl, SHGDN_FORPARSING, &str);
00072 
00073     if (SUCCEEDED(hr)) {
00074         get_strretA(&str, &pidl->mkid, buffer, len);
00075         free_strret(&str);
00076     } else
00077         buffer[0] = '\0';
00078 
00079     return hr;
00080 }
00081 
00082 #endif
00083 
00084 /* copy up to count characters of the wide character source string including the zero terminator to dest */
00085 static LPWSTR wcscpyn(LPWSTR dest, LPCWSTR source, size_t count)
00086 {
00087  LPCWSTR s;
00088  LPWSTR d = dest;
00089 
00090  for(s=source; count&&(*d++=*s++); )
00091   count--;
00092 
00093  return dest;
00094 }
00095 
00096 /* retreive an wide character string from STRRET */
00097 static void get_strretW(STRRET* str, const SHITEMID* shiid, LPWSTR buffer, int len)
00098 {
00099  switch(str->uType) {
00100   case STRRET_WSTR:
00101     wcscpyn(buffer, str->UNION_MEMBER(pOleStr), len);
00102     break;
00103 
00104   case STRRET_OFFSET:
00105     MultiByteToWideChar(CP_ACP, 0, (LPCSTR)shiid+str->UNION_MEMBER(uOffset), -1, buffer, len);
00106     break;
00107 
00108   case STRRET_CSTR:
00109     MultiByteToWideChar(CP_ACP, 0, str->UNION_MEMBER(cStr), -1, buffer, len);
00110  }
00111 }
00112 
00113 
00114 /* retrieve name string from an item id list */
00115 
00116 static HRESULT name_from_pidl(IShellFolder* folder, LPITEMIDLIST pidl, LPTSTR buffer, int len, SHGDNF flags)
00117 {
00118     STRRET str;
00119 
00120     HRESULT hr = IShellFolder_GetDisplayNameOf(folder, pidl, flags, &str);
00121 
00122     if (SUCCEEDED(hr)) {
00123         get_strret(&str, &pidl->mkid, buffer, len);
00124         free_strret(&str);
00125     } else
00126         buffer[0] = '\0';
00127 
00128     return hr;
00129 }
00130 
00131 
00132 /* retrieve path from an item id list */
00133 
00134 HRESULT path_from_pidlW(IShellFolder* folder, LPITEMIDLIST pidl, LPWSTR buffer, int len)
00135 {
00136     STRRET str;
00137 
00138     /* SHGDN_FORPARSING: get full path of id list */
00139     HRESULT hr = IShellFolder_GetDisplayNameOf(folder, pidl, SHGDN_FORPARSING, &str);
00140 
00141     if (SUCCEEDED(hr)) {
00142         get_strretW(&str, &pidl->mkid, buffer, len);
00143         free_strret(&str);
00144     } else
00145         buffer[0] = '\0';
00146 
00147     return hr;
00148 }
00149 
00150 
00151 /* create an item id list from a file system path */
00152 
00153 LPITEMIDLIST get_path_pidl(LPCTSTR path, HWND hwnd)
00154 {
00155     LPITEMIDLIST pidl;
00156     HRESULT hr;
00157     ULONG len;
00158 
00159 #ifdef UNICODE
00160     LPCWSTR buffer = path;
00161 #else
00162     WCHAR buffer[MAX_PATH];
00163     MultiByteToWideChar(CP_ACP, 0, path, -1, buffer, MAX_PATH);
00164 #endif
00165 
00166     hr = IShellFolder_ParseDisplayName(Globals.iDesktop, hwnd, NULL, (LPOLESTR)buffer, &len, &pidl, NULL);
00167     if (FAILED(hr))
00168         return NULL;
00169 
00170     return pidl;
00171 }
00172 
00173 
00174 /* convert an item id list from relative to absolute (=relative to the desktop) format */
00175 
00176 LPITEMIDLIST get_to_absolute_pidl(Entry* entry, HWND hwnd)
00177 {
00178     if (entry->up && entry->up->etype==ET_SHELL) {
00179         IShellFolder* folder = entry->up->folder;
00180         WCHAR buffer[MAX_PATH];
00181 
00182         HRESULT hr = path_from_pidlW(folder, entry->pidl, buffer, MAX_PATH);
00183 
00184         if (SUCCEEDED(hr)) {
00185             LPITEMIDLIST pidl;
00186             ULONG len;
00187 
00188             hr = IShellFolder_ParseDisplayName(Globals.iDesktop, hwnd, NULL, buffer, &len, &pidl, NULL);
00189 
00190             if (SUCCEEDED(hr))
00191                 return pidl;
00192         }
00193     } else if (entry->etype == ET_WINDOWS) {
00194         TCHAR path[MAX_PATH];
00195 
00196         get_path(entry, path);
00197 
00198         return get_path_pidl(path, hwnd);
00199     } else if (entry->pidl)
00200         return ILClone(entry->pidl);
00201 
00202     return NULL;
00203 }
00204 
00205 
00206 /* retrieve display icon from the given item id list */
00207 
00208 static HICON extract_icon(IShellFolder* folder, LPCITEMIDLIST pidl)
00209 {
00210     IExtractIcon* pExtract;
00211 
00212     if (SUCCEEDED(IShellFolder_GetUIObjectOf(folder, 0, 1, (LPCITEMIDLIST*)&pidl, &IID_IExtractIcon, 0, (LPVOID*)&pExtract))) {
00213         TCHAR path[_MAX_PATH];
00214         unsigned flags;
00215         HICON hicon;
00216         int idx;
00217 
00218         if (SUCCEEDED((*pExtract->lpVtbl->GetIconLocation)(pExtract, GIL_FORSHELL, path, _MAX_PATH, &idx, &flags))) {
00219             if (!(flags & GIL_NOTFILENAME)) {
00220                 if (idx == -1)
00221                     idx = 0;    /* special case for some control panel applications */
00222 
00223                 if ((int)ExtractIconEx(path, idx, 0, &hicon, 1) > 0)
00224                     flags &= ~GIL_DONTCACHE;
00225             } else {
00226                 HICON hIconLarge = 0;
00227 
00228                 HRESULT hr = (*pExtract->lpVtbl->Extract)(pExtract, path, idx, &hIconLarge, &hicon, MAKELONG(0/*GetSystemMetrics(SM_CXICON)*/,GetSystemMetrics(SM_CXSMICON)));
00229 
00230                 if (SUCCEEDED(hr))
00231                     DestroyIcon(hIconLarge);
00232             }
00233 
00234             return hicon;
00235         }
00236     }
00237 
00238     return 0;
00239 }
00240 
00241 
00242 /* search a Shell directory entry by item id list */
00243 static Entry* find_entry_shell(Entry* dir, LPCITEMIDLIST pidl)
00244 {
00245     Entry* entry;
00246 
00247     for(entry=dir->down; entry; entry=entry->next) {
00248         if (entry->pidl->mkid.cb == pidl->mkid.cb &&
00249             !memcmp(entry->pidl, pidl, entry->pidl->mkid.cb))
00250             return entry;
00251     }
00252 
00253     return 0;
00254 }
00255 
00256 /* read a directory tree for the given item id list in the shell namespace */
00257 Entry* read_tree_shell(Root* root, LPITEMIDLIST pidl, enum SORT_ORDER sortOrder, HWND hwnd)
00258 {
00259     Entry* entry = &root->entry;
00260     Entry* next;
00261     LPITEMIDLIST next_pidl = pidl;
00262     IShellFolder* folder;
00263     IShellFolder* child = NULL;
00264     HRESULT hr;
00265 
00266     HCURSOR old_cursor = SetCursor(LoadCursor(0, IDC_WAIT));
00267 
00268 #ifndef _NO_EXTENSIONS
00269     entry->etype = ET_SHELL;
00270 #endif
00271 
00272     folder = Globals.iDesktop;
00273 
00274     while(entry) {
00275         entry->pidl = next_pidl;
00276         entry->folder = folder;
00277 
00278         if (!pidl->mkid.cb)
00279             break;
00280 
00281         /* copy first element of item id list */
00282         next_pidl = IMalloc_Alloc(Globals.iMalloc, pidl->mkid.cb+sizeof(USHORT));
00283         memcpy(next_pidl, pidl, pidl->mkid.cb);
00284         ((LPITEMIDLIST)((LPBYTE)next_pidl+pidl->mkid.cb))->mkid.cb = 0;
00285 
00286         hr = IShellFolder_BindToObject(folder, next_pidl, 0, &IID_IShellFolder, (void**)&child);
00287         if (!SUCCEEDED(hr))
00288             break;
00289 
00290         read_directory(entry, NULL, sortOrder, hwnd);
00291 
00292         if (entry->down)
00293             entry->expanded = TRUE;
00294 
00295         next = find_entry_shell(entry, next_pidl);
00296         if (!next)
00297             break;
00298 
00299         folder = child;
00300         entry = next;
00301 
00302         /* go to next element */
00303         pidl = (LPITEMIDLIST) ((LPBYTE)pidl+pidl->mkid.cb);
00304     }
00305 
00306     SetCursor(old_cursor);
00307 
00308     return entry;
00309 }
00310 
00311 
00312 /* fill a WIN32_FIND_DATA structure with information about a shell namespace object */
00313 
00314 static void fill_w32fdata_shell(IShellFolder* folder, LPCITEMIDLIST pidl, SFGAOF attribs, WIN32_FIND_DATA* w32fdata)
00315 {
00316     if (!(attribs & SFGAO_FILESYSTEM) ||
00317           FAILED(SHGetDataFromIDList(folder, pidl, SHGDFIL_FINDDATA, w32fdata, sizeof(WIN32_FIND_DATA)))) {
00318         WIN32_FILE_ATTRIBUTE_DATA fad;
00319         IDataObject* pDataObj;
00320 
00321         STGMEDIUM medium = {0, {0}, 0};
00322         FORMATETC fmt = {Globals.cfStrFName, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
00323 
00324         HRESULT hr = IShellFolder_GetUIObjectOf(folder, 0, 1, &pidl, &IID_IDataObject, 0, (LPVOID*)&pDataObj);
00325 
00326         if (SUCCEEDED(hr)) {
00327             hr = IDataObject_GetData(pDataObj, &fmt, &medium);
00328 
00329             IDataObject_Release(pDataObj);
00330 
00331             if (SUCCEEDED(hr)) {
00332                 LPCTSTR path = (LPCTSTR)GlobalLock(medium.UNION_MEMBER(hGlobal));
00333                 UINT sem_org = SetErrorMode(SEM_FAILCRITICALERRORS);
00334 
00335                 if (GetFileAttributesEx(path, GetFileExInfoStandard, &fad)) {
00336                     w32fdata->dwFileAttributes = fad.dwFileAttributes;
00337                     w32fdata->ftCreationTime = fad.ftCreationTime;
00338                     w32fdata->ftLastAccessTime = fad.ftLastAccessTime;
00339                     w32fdata->ftLastWriteTime = fad.ftLastWriteTime;
00340 
00341                     if (!(fad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
00342                         w32fdata->nFileSizeLow = fad.nFileSizeLow;
00343                         w32fdata->nFileSizeHigh = fad.nFileSizeHigh;
00344                     }
00345                 }
00346 
00347                 SetErrorMode(sem_org);
00348 
00349                 GlobalUnlock(medium.UNION_MEMBER(hGlobal));
00350                 GlobalFree(medium.UNION_MEMBER(hGlobal));
00351             }
00352         }
00353     }
00354 
00355     if (attribs & (SFGAO_FOLDER|SFGAO_HASSUBFOLDER))
00356         w32fdata->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
00357 
00358     if (attribs & SFGAO_READONLY)
00359         w32fdata->dwFileAttributes |= FILE_ATTRIBUTE_READONLY;
00360 
00361     if (attribs & SFGAO_COMPRESSED)
00362         w32fdata->dwFileAttributes |= FILE_ATTRIBUTE_COMPRESSED;
00363 }
00364 
00365 
00366 /* read a directory entry using the WIN32 file system API */
00367 
00368 void read_directory_shell(Entry* dir, HWND hwnd)
00369 {
00370     IShellFolder* folder = dir->folder;
00371     int level = dir->level + 1;
00372     HRESULT hr;
00373 
00374     IShellFolder* child;
00375     IEnumIDList* idlist;
00376 
00377     Entry* first_entry = NULL;
00378     Entry* last = NULL;
00379     Entry* entry;
00380 
00381     if (!folder)
00382         return;
00383 
00384     hr = IShellFolder_EnumObjects(folder, hwnd, SHCONTF_FOLDERS|SHCONTF_NONFOLDERS|SHCONTF_INCLUDEHIDDEN|SHCONTF_SHAREABLE|SHCONTF_STORAGE, &idlist);
00385 
00386     if (SUCCEEDED(hr)) {
00387         for(;;) {
00388 #define FETCH_ITEM_COUNT    32
00389             LPITEMIDLIST pidls[FETCH_ITEM_COUNT];
00390             SFGAOF attribs;
00391             ULONG cnt = 0;
00392             ULONG n;
00393 
00394             memset(pidls, 0, sizeof(pidls));
00395 
00396             hr = IEnumIDList_Next(idlist, FETCH_ITEM_COUNT, pidls, &cnt);
00397             if (!SUCCEEDED(hr))
00398                 break;
00399 
00400             if (hr == S_FALSE)
00401                 break;
00402 
00403             for(n=0; n<cnt; ++n) {
00404                 entry = alloc_entry();
00405 
00406                 if (!first_entry)
00407                     first_entry = entry;
00408 
00409                 if (last)
00410                     last->next = entry;
00411 
00412                 memset(&entry->data, 0, sizeof(WIN32_FIND_DATA));
00413                 entry->bhfi_valid = FALSE;
00414 
00415                 attribs = ~SFGAO_FILESYSTEM;    /*SFGAO_HASSUBFOLDER|SFGAO_FOLDER; SFGAO_FILESYSTEM sorgt dafür, daß "My Documents" anstatt von "Martin's Documents" angezeigt wird */
00416 
00417                 hr = IShellFolder_GetAttributesOf(folder, 1, (LPCITEMIDLIST*)&pidls[n], &attribs);
00418 
00419                 if (SUCCEEDED(hr)) {
00420                     if (attribs != (SFGAOF)~SFGAO_FILESYSTEM) {
00421                         fill_w32fdata_shell(folder, pidls[n], attribs, &entry->data);
00422 
00423                         entry->bhfi_valid = TRUE;
00424                     } else
00425                         attribs = 0;
00426                 } else
00427                     attribs = 0;
00428 
00429                 entry->pidl = pidls[n];
00430 
00431                 if (entry->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
00432                     hr = IShellFolder_BindToObject(folder, pidls[n], 0, &IID_IShellFolder, (void**)&child);
00433 
00434                     if (SUCCEEDED(hr))
00435                         entry->folder = child;
00436                     else
00437                         entry->folder = NULL;
00438                 }
00439                 else
00440                     entry->folder = NULL;
00441 
00442                 if (!entry->data.cFileName[0])
00443                     /*hr = */name_from_pidl(folder, pidls[n], entry->data.cFileName, MAX_PATH, /*SHGDN_INFOLDER*/0x2000/*0x2000=SHGDN_INCLUDE_NONFILESYS*/);
00444 
00445                 /* get display icons for files and virtual objects */
00446                 if (!(entry->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ||
00447                     !(attribs & SFGAO_FILESYSTEM)) {
00448                     entry->hicon = extract_icon(folder, pidls[n]);
00449 
00450                     if (!entry->hicon)
00451                         entry->hicon = (HICON)-1;   /* don't try again later */
00452                 }
00453 
00454                 entry->down = NULL;
00455                 entry->up = dir;
00456                 entry->expanded = FALSE;
00457                 entry->scanned = FALSE;
00458                 entry->level = level;
00459 
00460 #ifndef _NO_EXTENSIONS
00461                 entry->etype = ET_SHELL;
00462                 entry->bhfi_valid = FALSE;
00463 #endif
00464 
00465                 last = entry;
00466             }
00467         }
00468 
00469         IEnumIDList_Release(idlist);
00470     }
00471 
00472     if (last)
00473         last->next = NULL;
00474 
00475     dir->down = first_entry;
00476     dir->scanned = TRUE;
00477 }
00478 
00479 #endif /* _SHELL_FOLDERS */

Winefile Source Code Documentation
generated on 15.09.2008 by doxygen

Winefile Homepage