Added some more tests.
[wine] / dlls / shell32 / enumidlist.c
1 /*
2  *      IEnumIDList
3  *
4  *      Copyright 1998  Juergen Schmied <juergen.schmied@metronet.de>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include <stdlib.h>
22 #include <string.h>
23 #include "wine/debug.h"
24 #include "winreg.h"
25 #include "undocshell.h"
26 #include "shlwapi.h"
27 #include "winerror.h"
28 #include "wine/obj_base.h"
29 #include "wine/obj_enumidlist.h"
30
31 #include "pidl.h"
32 #include "shlguid.h"
33 #include "shell32_main.h"
34
35 WINE_DEFAULT_DEBUG_CHANNEL(shell);
36
37 typedef struct tagENUMLIST
38 {
39         struct tagENUMLIST      *pNext;
40         LPITEMIDLIST            pidl;
41
42 } ENUMLIST, *LPENUMLIST;
43
44 typedef struct
45 {
46         ICOM_VFIELD(IEnumIDList);
47         DWORD                           ref;
48         LPENUMLIST                      mpFirst;
49         LPENUMLIST                      mpLast;
50         LPENUMLIST                      mpCurrent;
51
52 } IEnumIDListImpl;
53
54 static struct ICOM_VTABLE(IEnumIDList) eidlvt;
55
56 /**************************************************************************
57  *  AddToEnumList()
58  */
59 static BOOL AddToEnumList(
60         IEnumIDList * iface,
61         LPITEMIDLIST pidl)
62 {
63         ICOM_THIS(IEnumIDListImpl,iface);
64
65         LPENUMLIST  pNew;
66
67         TRACE("(%p)->(pidl=%p)\n",This,pidl);
68         pNew = (LPENUMLIST)SHAlloc(sizeof(ENUMLIST));
69         if(pNew)
70         {
71           /*set the next pointer */
72           pNew->pNext = NULL;
73           pNew->pidl = pidl;
74
75           /*is This the first item in the list? */
76           if(!This->mpFirst)
77           {
78             This->mpFirst = pNew;
79             This->mpCurrent = pNew;
80           }
81
82           if(This->mpLast)
83           {
84             /*add the new item to the end of the list */
85             This->mpLast->pNext = pNew;
86           }
87          
88           /*update the last item pointer */
89           This->mpLast = pNew;
90           TRACE("-- (%p)->(first=%p, last=%p)\n",This,This->mpFirst,This->mpLast);
91           return TRUE;
92         }
93         return FALSE;
94 }
95
96 /**************************************************************************
97  *  CreateFolderEnumList()
98  */
99 static BOOL CreateFolderEnumList(
100         IEnumIDList * iface,
101         LPCSTR lpszPath,
102         DWORD dwFlags)
103 {
104         ICOM_THIS(IEnumIDListImpl,iface);
105
106         LPITEMIDLIST    pidl=NULL;
107         WIN32_FIND_DATAA stffile;       
108         HANDLE hFile;
109         CHAR  szPath[MAX_PATH];
110
111         TRACE("(%p)->(path=%s flags=0x%08lx) \n",This,debugstr_a(lpszPath),dwFlags);
112
113         if(!lpszPath || !lpszPath[0]) return FALSE;
114
115         strcpy(szPath, lpszPath);
116         PathAddBackslashA(szPath);
117         strcat(szPath,"*.*");
118
119         /*enumerate the folders*/
120         if(dwFlags & SHCONTF_FOLDERS)
121         { 
122           TRACE("-- (%p)-> enumerate SHCONTF_FOLDERS of %s\n",This,debugstr_a(szPath));
123           hFile = FindFirstFileA(szPath,&stffile);
124           if ( hFile != INVALID_HANDLE_VALUE )
125           {
126             do
127             {
128               if ( (stffile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && strcmp (stffile.cFileName, ".") && strcmp (stffile.cFileName, ".."))
129               {
130                 pidl = _ILCreateFolder (&stffile);
131                 if(pidl && AddToEnumList((IEnumIDList*)This, pidl))
132                 {
133                   continue;
134                 }
135                 return FALSE;
136               }
137             } while( FindNextFileA(hFile,&stffile));
138             FindClose (hFile);
139           }
140         }
141
142         /*enumerate the non-folder items (values) */
143         if(dwFlags & SHCONTF_NONFOLDERS)
144         {
145           TRACE("-- (%p)-> enumerate SHCONTF_NONFOLDERS of %s\n",This,debugstr_a(szPath));
146           hFile = FindFirstFileA(szPath,&stffile);
147           if ( hFile != INVALID_HANDLE_VALUE )
148           {
149             do
150             {
151               if (! (stffile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) )
152               {
153                 pidl = _ILCreateValue(&stffile);
154                 if(pidl && AddToEnumList((IEnumIDList*)This, pidl))
155                 {
156                   continue;
157                 }
158                 return FALSE;
159               }
160             } while( FindNextFileA(hFile,&stffile));
161             FindClose (hFile);
162           } 
163         }
164         return TRUE;
165 }
166
167 /**************************************************************************
168  *  CreateDesktopEnumList()
169  */
170 static BOOL CreateDesktopEnumList(
171         IEnumIDList * iface,
172         DWORD dwFlags)
173 {
174         ICOM_THIS(IEnumIDListImpl,iface);
175
176         LPITEMIDLIST    pidl=NULL;
177         HKEY hkey;
178         char    szPath[MAX_PATH];
179
180         TRACE("(%p)->(flags=0x%08lx) \n",This,dwFlags);
181
182         /*enumerate the root folders */
183         if(dwFlags & SHCONTF_FOLDERS)
184         { 
185           /*create the pidl for This item */
186           pidl = _ILCreateMyComputer();
187           if(pidl)
188           {
189             if(!AddToEnumList((IEnumIDList*)This, pidl))
190               return FALSE;
191           }
192
193           if (! RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\explorer\\desktop\\NameSpace", 0, KEY_READ, &hkey))
194           {
195             char iid[50];
196             int i=0;
197
198             while (1)
199             {
200               DWORD size = sizeof (iid);
201
202               if (ERROR_SUCCESS!=RegEnumKeyExA(hkey, i, iid, &size, 0, NULL, NULL, NULL))
203                 break;
204
205               pidl = _ILCreateSpecial(iid);
206
207               if(pidl)
208                 AddToEnumList((IEnumIDList*)This, pidl);
209
210               i++;
211             }
212             RegCloseKey(hkey);
213           }
214         }
215
216         /*enumerate the elements in %windir%\desktop */
217         SHGetSpecialFolderPathA(0, szPath, CSIDL_DESKTOPDIRECTORY, FALSE);
218         CreateFolderEnumList( (IEnumIDList*)This, szPath, dwFlags);
219         
220         return TRUE;
221 }
222
223 /**************************************************************************
224  *  CreateMyCompEnumList()
225  */
226 static BOOL CreateMyCompEnumList(
227         IEnumIDList * iface,
228         DWORD dwFlags)
229 {
230         ICOM_THIS(IEnumIDListImpl,iface);
231
232         LPITEMIDLIST    pidl=NULL;
233         DWORD           dwDrivemap;
234         CHAR            szDriveName[4];
235         HKEY            hkey;
236
237         TRACE("(%p)->(flags=0x%08lx) \n",This,dwFlags);
238
239         /*enumerate the folders*/
240         if(dwFlags & SHCONTF_FOLDERS)
241         {
242           dwDrivemap = GetLogicalDrives();
243           strcpy (szDriveName,"A:\\");
244           while (szDriveName[0]<='Z')
245           {
246             if(dwDrivemap & 0x00000001L)
247             {
248               pidl = _ILCreateDrive(szDriveName);
249               if(pidl)
250               {
251                 if(!AddToEnumList((IEnumIDList*)This, pidl))
252                   return FALSE;
253               }
254             }
255             szDriveName[0]++;
256             dwDrivemap = dwDrivemap >> 1;
257           }
258
259           TRACE("-- (%p)-> enumerate (mycomputer shell extensions)\n",This);
260           if (! RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\explorer\\mycomputer\\NameSpace", 0, KEY_READ, &hkey))
261           {
262             char iid[50];
263             int i=0;
264
265             while (1)
266             {
267               DWORD size = sizeof (iid);
268
269               if (ERROR_SUCCESS!=RegEnumKeyExA(hkey, i, iid, &size, 0, NULL, NULL, NULL))
270                 break;
271
272               pidl = _ILCreateSpecial(iid);
273
274               if(pidl)
275                 AddToEnumList((IEnumIDList*)This, pidl);
276
277               i++;
278             }
279             RegCloseKey(hkey);
280           }
281         }
282         return TRUE;
283 }
284
285 /**************************************************************************
286 *   DeleteList()
287 */
288 static BOOL DeleteList(
289         IEnumIDList * iface)
290 {
291         ICOM_THIS(IEnumIDListImpl,iface);
292
293         LPENUMLIST  pDelete;
294
295         TRACE("(%p)->()\n",This);
296         
297         while(This->mpFirst)
298         { pDelete = This->mpFirst;
299           This->mpFirst = pDelete->pNext;
300           SHFree(pDelete->pidl);
301           SHFree(pDelete);
302         }
303         This->mpFirst = This->mpLast = This->mpCurrent = NULL;
304         return TRUE;
305 }
306
307 /**************************************************************************
308  *  IEnumIDList_Folder_Constructor
309  *
310  */
311
312 IEnumIDList * IEnumIDList_Constructor(
313         LPCSTR lpszPath,
314         DWORD dwFlags,
315         DWORD dwKind)
316 {
317         IEnumIDListImpl*        lpeidl;
318         BOOL                    ret = FALSE;    
319
320         lpeidl = (IEnumIDListImpl*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IEnumIDListImpl));
321
322         TRACE("(%p)->(%s flags=0x%08lx kind=0x%08lx)\n",lpeidl,debugstr_a(lpszPath),dwFlags, dwKind);
323
324         if (lpeidl)
325         {
326           lpeidl->ref = 1;
327           ICOM_VTBL(lpeidl) = &eidlvt;
328
329           switch (dwKind)
330           {
331             case EIDL_DESK:
332               ret = CreateDesktopEnumList((IEnumIDList*)lpeidl, dwFlags);
333               break;
334
335             case EIDL_MYCOMP:
336               ret = CreateMyCompEnumList((IEnumIDList*)lpeidl, dwFlags);
337               break;
338
339             case EIDL_FILE:
340               ret = CreateFolderEnumList((IEnumIDList*)lpeidl, lpszPath, dwFlags);
341               break;
342           }
343
344           if(ret)
345           {
346             shell32_ObjCount++;
347           }
348           else
349           {
350             if (lpeidl)
351             {
352               HeapFree(GetProcessHeap(),0,lpeidl);
353             }
354           }
355         }
356
357         TRACE("-- (%p)->()\n",lpeidl);
358
359         return (IEnumIDList*)lpeidl;
360 }
361
362 /**************************************************************************
363  *  EnumIDList_QueryInterface
364  */
365 static HRESULT WINAPI IEnumIDList_fnQueryInterface(
366         IEnumIDList * iface,
367         REFIID riid,
368         LPVOID *ppvObj)
369 {
370         ICOM_THIS(IEnumIDListImpl,iface);
371
372         TRACE("(%p)->(\n\tIID:\t%s,%p)\n",This,debugstr_guid(riid),ppvObj);
373
374         *ppvObj = NULL;
375
376         if(IsEqualIID(riid, &IID_IUnknown))          /*IUnknown*/
377         { *ppvObj = This; 
378         }
379         else if(IsEqualIID(riid, &IID_IEnumIDList))  /*IEnumIDList*/
380         {    *ppvObj = (IEnumIDList*)This;
381         }
382
383         if(*ppvObj)
384         { IEnumIDList_AddRef((IEnumIDList*)*ppvObj);
385           TRACE("-- Interface: (%p)->(%p)\n",ppvObj,*ppvObj);
386           return S_OK;
387         }
388         
389         TRACE("-- Interface: E_NOINTERFACE\n");
390         return E_NOINTERFACE;
391 }
392
393 /******************************************************************************
394  * IEnumIDList_fnAddRef
395  */
396 static ULONG WINAPI IEnumIDList_fnAddRef(
397         IEnumIDList * iface)
398 {
399         ICOM_THIS(IEnumIDListImpl,iface);
400
401         TRACE("(%p)->(%lu)\n",This,This->ref);
402
403         shell32_ObjCount++;
404         return ++(This->ref);
405 }
406 /******************************************************************************
407  * IEnumIDList_fnRelease
408  */
409 static ULONG WINAPI IEnumIDList_fnRelease(
410         IEnumIDList * iface)
411 {
412         ICOM_THIS(IEnumIDListImpl,iface);
413
414         TRACE("(%p)->(%lu)\n",This,This->ref);
415
416         shell32_ObjCount--;
417
418         if (!--(This->ref)) 
419         { TRACE(" destroying IEnumIDList(%p)\n",This);
420           DeleteList((IEnumIDList*)This);
421           HeapFree(GetProcessHeap(),0,This);
422           return 0;
423         }
424         return This->ref;
425 }
426    
427 /**************************************************************************
428  *  IEnumIDList_fnNext
429  */
430
431 static HRESULT WINAPI IEnumIDList_fnNext(
432         IEnumIDList * iface,
433         ULONG celt,
434         LPITEMIDLIST * rgelt,
435         ULONG *pceltFetched) 
436 {
437         ICOM_THIS(IEnumIDListImpl,iface);
438
439         ULONG    i;
440         HRESULT  hr = S_OK;
441         LPITEMIDLIST  temp;
442
443         TRACE("(%p)->(%ld,%p, %p)\n",This,celt,rgelt,pceltFetched);
444
445 /* It is valid to leave pceltFetched NULL when celt is 1. Some of explorer's
446  * subsystems actually use it (and so may a third party browser)
447  */
448         if(pceltFetched)
449           *pceltFetched = 0;
450
451         *rgelt=0;
452         
453         if(celt > 1 && !pceltFetched)
454         { return E_INVALIDARG;
455         }
456
457         for(i = 0; i < celt; i++)
458         { if(!(This->mpCurrent))
459           { hr =  S_FALSE;
460             break;
461           }
462           temp = ILClone(This->mpCurrent->pidl);
463           rgelt[i] = temp;
464           This->mpCurrent = This->mpCurrent->pNext;
465         }
466         if(pceltFetched)
467         {  *pceltFetched = i;
468         }
469
470         return hr;
471 }
472
473 /**************************************************************************
474 *  IEnumIDList_fnSkip
475 */
476 static HRESULT WINAPI IEnumIDList_fnSkip(
477         IEnumIDList * iface,ULONG celt)
478 {
479         ICOM_THIS(IEnumIDListImpl,iface);
480
481         DWORD    dwIndex;
482         HRESULT  hr = S_OK;
483
484         TRACE("(%p)->(%lu)\n",This,celt);
485
486         for(dwIndex = 0; dwIndex < celt; dwIndex++)
487         { if(!This->mpCurrent)
488           { hr = S_FALSE;
489             break;
490           }
491           This->mpCurrent = This->mpCurrent->pNext;
492         }
493         return hr;
494 }
495 /**************************************************************************
496 *  IEnumIDList_fnReset
497 */
498 static HRESULT WINAPI IEnumIDList_fnReset(
499         IEnumIDList * iface)
500 {
501         ICOM_THIS(IEnumIDListImpl,iface);
502
503         TRACE("(%p)\n",This);
504         This->mpCurrent = This->mpFirst;
505         return S_OK;
506 }
507 /**************************************************************************
508 *  IEnumIDList_fnClone
509 */
510 static HRESULT WINAPI IEnumIDList_fnClone(
511         IEnumIDList * iface,LPENUMIDLIST * ppenum)
512 {
513         ICOM_THIS(IEnumIDListImpl,iface);
514
515         TRACE("(%p)->() to (%p)->() E_NOTIMPL\n",This,ppenum);
516         return E_NOTIMPL;
517 }
518
519 /**************************************************************************
520  *  IEnumIDList_fnVTable
521  */
522 static ICOM_VTABLE (IEnumIDList) eidlvt = 
523 {
524         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
525         IEnumIDList_fnQueryInterface,
526         IEnumIDList_fnAddRef,
527         IEnumIDList_fnRelease,
528         IEnumIDList_fnNext,
529         IEnumIDList_fnSkip,
530         IEnumIDList_fnReset,
531         IEnumIDList_fnClone,
532 };