shell32/tests: Fix a test failure on Vista and higher.
[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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include <stdarg.h>
22 #include <stdlib.h>
23 #include <string.h>
24
25 #define COBJMACROS
26
27 #include "wine/debug.h"
28 #include "wine/unicode.h"
29 #include "windef.h"
30 #include "winbase.h"
31 #include "winreg.h"
32 #include "shlwapi.h"
33
34 #include "pidl.h"
35 #include "enumidlist.h"
36
37 WINE_DEFAULT_DEBUG_CHANNEL(shell);
38
39 typedef struct tagENUMLIST
40 {
41         struct tagENUMLIST      *pNext;
42         LPITEMIDLIST            pidl;
43
44 } ENUMLIST, *LPENUMLIST;
45
46 typedef struct
47 {
48         const IEnumIDListVtbl          *lpVtbl;
49         LONG                            ref;
50         LPENUMLIST                      mpFirst;
51         LPENUMLIST                      mpLast;
52         LPENUMLIST                      mpCurrent;
53
54 } IEnumIDListImpl;
55
56 static const IEnumIDListVtbl eidlvt;
57
58 /**************************************************************************
59  *  AddToEnumList()
60  */
61 BOOL AddToEnumList(
62         IEnumIDList * iface,
63         LPITEMIDLIST pidl)
64 {
65         IEnumIDListImpl *This = (IEnumIDListImpl *)iface;
66
67         LPENUMLIST  pNew;
68
69         TRACE("(%p)->(pidl=%p)\n",This,pidl);
70
71     if (!iface || !pidl)
72         return FALSE;
73
74         pNew = SHAlloc(sizeof(ENUMLIST));
75         if(pNew)
76         {
77           /*set the next pointer */
78           pNew->pNext = NULL;
79           pNew->pidl = pidl;
80
81           /*is This the first item in the list? */
82           if(!This->mpFirst)
83           {
84             This->mpFirst = pNew;
85             This->mpCurrent = pNew;
86           }
87
88           if(This->mpLast)
89           {
90             /*add the new item to the end of the list */
91             This->mpLast->pNext = pNew;
92           }
93
94           /*update the last item pointer */
95           This->mpLast = pNew;
96           TRACE("-- (%p)->(first=%p, last=%p)\n",This,This->mpFirst,This->mpLast);
97           return TRUE;
98         }
99         return FALSE;
100 }
101
102 /**************************************************************************
103  *  CreateFolderEnumList()
104  */
105 BOOL CreateFolderEnumList(
106         IEnumIDList *list,
107         LPCWSTR lpszPath,
108         DWORD dwFlags)
109 {
110     LPITEMIDLIST pidl=NULL;
111     WIN32_FIND_DATAW stffile;
112     HANDLE hFile;
113     WCHAR  szPath[MAX_PATH];
114     BOOL succeeded = TRUE;
115     static const WCHAR stars[] = { '*','.','*',0 };
116     static const WCHAR dot[] = { '.',0 };
117     static const WCHAR dotdot[] = { '.','.',0 };
118
119     TRACE("(%p)->(path=%s flags=0x%08x)\n", list, debugstr_w(lpszPath), dwFlags);
120
121     if(!lpszPath || !lpszPath[0]) return FALSE;
122
123     strcpyW(szPath, lpszPath);
124     PathAddBackslashW(szPath);
125     strcatW(szPath,stars);
126
127     hFile = FindFirstFileW(szPath,&stffile);
128     if ( hFile != INVALID_HANDLE_VALUE )
129     {
130         BOOL findFinished = FALSE;
131
132         do
133         {
134             if ( !(stffile.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) 
135              || (dwFlags & SHCONTF_INCLUDEHIDDEN) )
136             {
137                 if ( (stffile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
138                  dwFlags & SHCONTF_FOLDERS &&
139                  strcmpW(stffile.cFileName, dot) && strcmpW(stffile.cFileName, dotdot))
140                 {
141                     pidl = _ILCreateFromFindDataW(&stffile);
142                     succeeded = succeeded && AddToEnumList(list, pidl);
143                 }
144                 else if (!(stffile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
145                  && dwFlags & SHCONTF_NONFOLDERS)
146                 {
147                     pidl = _ILCreateFromFindDataW(&stffile);
148                     succeeded = succeeded && AddToEnumList(list, pidl);
149                 }
150             }
151             if (succeeded)
152             {
153                 if (!FindNextFileW(hFile, &stffile))
154                 {
155                     if (GetLastError() == ERROR_NO_MORE_FILES)
156                         findFinished = TRUE;
157                     else
158                         succeeded = FALSE;
159                 }
160             }
161         } while (succeeded && !findFinished);
162         FindClose(hFile);
163     }
164     return succeeded;
165 }
166
167 /**************************************************************************
168 *   DeleteList()
169 */
170 static BOOL DeleteList(
171         IEnumIDList * iface)
172 {
173         IEnumIDListImpl *This = (IEnumIDListImpl *)iface;
174
175         LPENUMLIST  pDelete;
176
177         TRACE("(%p)->()\n",This);
178
179         while(This->mpFirst)
180         { pDelete = This->mpFirst;
181           This->mpFirst = pDelete->pNext;
182           SHFree(pDelete->pidl);
183           SHFree(pDelete);
184         }
185         This->mpFirst = This->mpLast = This->mpCurrent = NULL;
186         return TRUE;
187 }
188
189 /**************************************************************************
190  *  IEnumIDList_Folder_Constructor
191  *
192  */
193
194 IEnumIDList * IEnumIDList_Constructor(void)
195 {
196     IEnumIDListImpl *lpeidl = HeapAlloc(GetProcessHeap(),
197      HEAP_ZERO_MEMORY, sizeof(IEnumIDListImpl));
198
199     if (lpeidl)
200     {
201         lpeidl->ref = 1;
202         lpeidl->lpVtbl = &eidlvt;
203     }
204     TRACE("-- (%p)->()\n",lpeidl);
205
206     return (IEnumIDList*)lpeidl;
207 }
208
209 /**************************************************************************
210  *  EnumIDList_QueryInterface
211  */
212 static HRESULT WINAPI IEnumIDList_fnQueryInterface(
213         IEnumIDList * iface,
214         REFIID riid,
215         LPVOID *ppvObj)
216 {
217         IEnumIDListImpl *This = (IEnumIDListImpl *)iface;
218
219         TRACE("(%p)->(\n\tIID:\t%s,%p)\n",This,debugstr_guid(riid),ppvObj);
220
221         *ppvObj = NULL;
222
223         if(IsEqualIID(riid, &IID_IUnknown))          /*IUnknown*/
224         { *ppvObj = This;
225         }
226         else if(IsEqualIID(riid, &IID_IEnumIDList))  /*IEnumIDList*/
227         {    *ppvObj = This;
228         }
229
230         if(*ppvObj)
231         { IEnumIDList_AddRef((IEnumIDList*)*ppvObj);
232           TRACE("-- Interface: (%p)->(%p)\n",ppvObj,*ppvObj);
233           return S_OK;
234         }
235
236         TRACE("-- Interface: E_NOINTERFACE\n");
237         return E_NOINTERFACE;
238 }
239
240 /******************************************************************************
241  * IEnumIDList_fnAddRef
242  */
243 static ULONG WINAPI IEnumIDList_fnAddRef(
244         IEnumIDList * iface)
245 {
246         IEnumIDListImpl *This = (IEnumIDListImpl *)iface;
247         ULONG refCount = InterlockedIncrement(&This->ref);
248
249         TRACE("(%p)->(%u)\n", This, refCount - 1);
250
251         return refCount;
252 }
253 /******************************************************************************
254  * IEnumIDList_fnRelease
255  */
256 static ULONG WINAPI IEnumIDList_fnRelease(
257         IEnumIDList * iface)
258 {
259         IEnumIDListImpl *This = (IEnumIDListImpl *)iface;
260         ULONG refCount = InterlockedDecrement(&This->ref);
261
262         TRACE("(%p)->(%u)\n", This, refCount + 1);
263
264         if (!refCount) {
265           TRACE(" destroying IEnumIDList(%p)\n",This);
266           DeleteList((IEnumIDList*)This);
267           HeapFree(GetProcessHeap(),0,This);
268         }
269         return refCount;
270 }
271
272 /**************************************************************************
273  *  IEnumIDList_fnNext
274  */
275
276 static HRESULT WINAPI IEnumIDList_fnNext(
277         IEnumIDList * iface,
278         ULONG celt,
279         LPITEMIDLIST * rgelt,
280         ULONG *pceltFetched)
281 {
282         IEnumIDListImpl *This = (IEnumIDListImpl *)iface;
283
284         ULONG    i;
285         HRESULT  hr = S_OK;
286         LPITEMIDLIST  temp;
287
288         TRACE("(%p)->(%d,%p, %p)\n",This,celt,rgelt,pceltFetched);
289
290 /* It is valid to leave pceltFetched NULL when celt is 1. Some of explorer's
291  * subsystems actually use it (and so may a third party browser)
292  */
293         if(pceltFetched)
294           *pceltFetched = 0;
295
296         *rgelt=0;
297
298         if(celt > 1 && !pceltFetched)
299         { return E_INVALIDARG;
300         }
301
302         if(celt > 0 && !This->mpCurrent)
303         { return S_FALSE;
304         }
305
306         for(i = 0; i < celt; i++)
307         { if(!(This->mpCurrent))
308             break;
309
310           temp = ILClone(This->mpCurrent->pidl);
311           rgelt[i] = temp;
312           This->mpCurrent = This->mpCurrent->pNext;
313         }
314         if(pceltFetched)
315         {  *pceltFetched = i;
316         }
317
318         return hr;
319 }
320
321 /**************************************************************************
322 *  IEnumIDList_fnSkip
323 */
324 static HRESULT WINAPI IEnumIDList_fnSkip(
325         IEnumIDList * iface,ULONG celt)
326 {
327         IEnumIDListImpl *This = (IEnumIDListImpl *)iface;
328
329         DWORD    dwIndex;
330         HRESULT  hr = S_OK;
331
332         TRACE("(%p)->(%u)\n",This,celt);
333
334         for(dwIndex = 0; dwIndex < celt; dwIndex++)
335         { if(!This->mpCurrent)
336           { hr = S_FALSE;
337             break;
338           }
339           This->mpCurrent = This->mpCurrent->pNext;
340         }
341         return hr;
342 }
343 /**************************************************************************
344 *  IEnumIDList_fnReset
345 */
346 static HRESULT WINAPI IEnumIDList_fnReset(
347         IEnumIDList * iface)
348 {
349         IEnumIDListImpl *This = (IEnumIDListImpl *)iface;
350
351         TRACE("(%p)\n",This);
352         This->mpCurrent = This->mpFirst;
353         return S_OK;
354 }
355 /**************************************************************************
356 *  IEnumIDList_fnClone
357 */
358 static HRESULT WINAPI IEnumIDList_fnClone(
359         IEnumIDList * iface,LPENUMIDLIST * ppenum)
360 {
361         IEnumIDListImpl *This = (IEnumIDListImpl *)iface;
362
363         TRACE("(%p)->() to (%p)->() E_NOTIMPL\n",This,ppenum);
364         return E_NOTIMPL;
365 }
366
367 /**************************************************************************
368  *  IEnumIDList_fnVTable
369  */
370 static const IEnumIDListVtbl eidlvt =
371 {
372         IEnumIDList_fnQueryInterface,
373         IEnumIDList_fnAddRef,
374         IEnumIDList_fnRelease,
375         IEnumIDList_fnNext,
376         IEnumIDList_fnSkip,
377         IEnumIDList_fnReset,
378         IEnumIDList_fnClone,
379 };