d3drm: Implement IDirect3DRMMesh_GetGroup.
[wine] / dlls / fusion / asmenum.c
1 /*
2  * IAssemblyEnum implementation
3  *
4  * Copyright 2008 James Hawkins
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
23 #define COBJMACROS
24 #define NONAMELESSUNION
25 #define NONAMELESSSTRUCT
26
27 #include "windef.h"
28 #include "winbase.h"
29 #include "winuser.h"
30 #include "ole2.h"
31 #include "guiddef.h"
32 #include "fusion.h"
33 #include "corerror.h"
34 #include "fusionpriv.h"
35
36 #include "wine/debug.h"
37 #include "wine/unicode.h"
38 #include "wine/list.h"
39
40 WINE_DEFAULT_DEBUG_CHANNEL(fusion);
41
42 typedef struct _tagASMNAME
43 {
44     struct list entry;
45     IAssemblyName *name;
46 } ASMNAME;
47
48 typedef struct
49 {
50     IAssemblyEnum IAssemblyEnum_iface;
51
52     struct list assemblies;
53     struct list *iter;
54     LONG ref;
55 } IAssemblyEnumImpl;
56
57 static inline IAssemblyEnumImpl *impl_from_IAssemblyEnum(IAssemblyEnum *iface)
58 {
59     return CONTAINING_RECORD(iface, IAssemblyEnumImpl, IAssemblyEnum_iface);
60 }
61
62 static HRESULT WINAPI IAssemblyEnumImpl_QueryInterface(IAssemblyEnum *iface,
63                                                        REFIID riid, LPVOID *ppobj)
64 {
65     IAssemblyEnumImpl *This = impl_from_IAssemblyEnum(iface);
66
67     TRACE("(%p, %s, %p)\n", This, debugstr_guid(riid), ppobj);
68
69     *ppobj = NULL;
70
71     if (IsEqualIID(riid, &IID_IUnknown) ||
72         IsEqualIID(riid, &IID_IAssemblyEnum))
73     {
74         IUnknown_AddRef(iface);
75         *ppobj = This;
76         return S_OK;
77     }
78
79     WARN("(%p, %s, %p): not found\n", This, debugstr_guid(riid), ppobj);
80     return E_NOINTERFACE;
81 }
82
83 static ULONG WINAPI IAssemblyEnumImpl_AddRef(IAssemblyEnum *iface)
84 {
85     IAssemblyEnumImpl *This = impl_from_IAssemblyEnum(iface);
86     ULONG refCount = InterlockedIncrement(&This->ref);
87
88     TRACE("(%p)->(ref before = %u)\n", This, refCount - 1);
89
90     return refCount;
91 }
92
93 static ULONG WINAPI IAssemblyEnumImpl_Release(IAssemblyEnum *iface)
94 {
95     IAssemblyEnumImpl *This = impl_from_IAssemblyEnum(iface);
96     ULONG refCount = InterlockedDecrement(&This->ref);
97     struct list *item, *cursor;
98
99     TRACE("(%p)->(ref before = %u)\n", This, refCount + 1);
100
101     if (!refCount)
102     {
103         LIST_FOR_EACH_SAFE(item, cursor, &This->assemblies)
104         {
105             ASMNAME *asmname = LIST_ENTRY(item, ASMNAME, entry);
106
107             list_remove(&asmname->entry);
108             IAssemblyName_Release(asmname->name);
109             HeapFree(GetProcessHeap(), 0, asmname);
110         }
111
112         HeapFree(GetProcessHeap(), 0, This);
113     }
114
115     return refCount;
116 }
117
118 static HRESULT WINAPI IAssemblyEnumImpl_GetNextAssembly(IAssemblyEnum *iface,
119                                                         LPVOID pvReserved,
120                                                         IAssemblyName **ppName,
121                                                         DWORD dwFlags)
122 {
123     IAssemblyEnumImpl *asmenum = impl_from_IAssemblyEnum(iface);
124     ASMNAME *asmname;
125
126     TRACE("(%p, %p, %p, %d)\n", iface, pvReserved, ppName, dwFlags);
127
128     if (!ppName)
129         return E_INVALIDARG;
130
131     asmname = LIST_ENTRY(asmenum->iter, ASMNAME, entry);
132     if (!asmname)
133         return S_FALSE;
134
135     *ppName = asmname->name;
136     IAssemblyName_AddRef(*ppName);
137
138     asmenum->iter = list_next(&asmenum->assemblies, asmenum->iter);
139
140     return S_OK;
141 }
142
143 static HRESULT WINAPI IAssemblyEnumImpl_Reset(IAssemblyEnum *iface)
144 {
145     IAssemblyEnumImpl *asmenum = impl_from_IAssemblyEnum(iface);
146
147     TRACE("(%p)\n", iface);
148
149     asmenum->iter = list_head(&asmenum->assemblies);
150     return S_OK;
151 }
152
153 static HRESULT WINAPI IAssemblyEnumImpl_Clone(IAssemblyEnum *iface,
154                                                IAssemblyEnum **ppEnum)
155 {
156     FIXME("(%p, %p) stub!\n", iface, ppEnum);
157     return E_NOTIMPL;
158 }
159
160 static const IAssemblyEnumVtbl AssemblyEnumVtbl = {
161     IAssemblyEnumImpl_QueryInterface,
162     IAssemblyEnumImpl_AddRef,
163     IAssemblyEnumImpl_Release,
164     IAssemblyEnumImpl_GetNextAssembly,
165     IAssemblyEnumImpl_Reset,
166     IAssemblyEnumImpl_Clone
167 };
168
169 static void parse_name(IAssemblyName *name, int depth, LPWSTR path, LPWSTR buf)
170 {
171     WCHAR disp[MAX_PATH];
172     LPCWSTR verptr, pubkeyptr;
173     HRESULT hr;
174     DWORD size, major_size, minor_size, build_size, revision_size;
175     WORD major, minor, build, revision;
176
177     static const WCHAR star[] = {'*',0};
178     static const WCHAR ss_fmt[] = {'%','s','\\','%','s',0};
179     static const WCHAR verpubkey[] = {'%','s','\\','%','s','_','_','%','s',0};
180     static const WCHAR ver_fmt[] = {'%','u','.','%','u','.','%','u','.','%','u',0};
181
182     WCHAR version[24]; /* strlen("65535") * 4 + 3 + 1 */
183     WCHAR token_str[TOKEN_LENGTH + 1];
184     BYTE token[BYTES_PER_TOKEN];
185
186     if (depth == 0)
187     {
188         size = MAX_PATH;
189         *disp = '\0';
190         hr = IAssemblyName_GetName(name, &size, disp);
191         if (SUCCEEDED(hr))
192             sprintfW(buf, ss_fmt, path, disp);
193         else
194             sprintfW(buf, ss_fmt, path, star);
195     }
196     else if (depth == 1)
197     {
198         major_size = sizeof(major);
199         IAssemblyName_GetProperty(name, ASM_NAME_MAJOR_VERSION, &major, &major_size);
200
201         minor_size = sizeof(minor);
202         IAssemblyName_GetProperty(name, ASM_NAME_MINOR_VERSION, &minor, &minor_size);
203
204         build_size = sizeof(build);
205         IAssemblyName_GetProperty(name, ASM_NAME_BUILD_NUMBER, &build, &build_size);
206
207         revision_size = sizeof(revision);
208         IAssemblyName_GetProperty(name, ASM_NAME_REVISION_NUMBER, &revision, &revision_size);
209
210         if (!major_size || !minor_size || !build_size || !revision_size) verptr = star;
211         else
212         {
213             sprintfW(version, ver_fmt, major, minor, build, revision);
214             verptr = version;
215         }
216
217         size = sizeof(token);
218         IAssemblyName_GetProperty(name, ASM_NAME_PUBLIC_KEY_TOKEN, token, &size);
219
220         if (!size) pubkeyptr = star;
221         else
222         {
223             token_to_str(token, token_str);
224             pubkeyptr = token_str;
225         }
226
227         sprintfW(buf, verpubkey, path, verptr, pubkeyptr);
228     }
229 }
230
231 static int compare_assembly_names(ASMNAME *asmname1, ASMNAME *asmname2)
232 {
233     int ret;
234     WORD version1, version2;
235     WCHAR name1[MAX_PATH], name2[MAX_PATH];
236     WCHAR token_str1[TOKEN_LENGTH + 1], token_str2[TOKEN_LENGTH + 1];
237     BYTE token1[BYTES_PER_TOKEN], token2[BYTES_PER_TOKEN];
238     DWORD size, i;
239
240     size = sizeof(name1);
241     IAssemblyName_GetProperty(asmname1->name, ASM_NAME_NAME, name1, &size);
242     size = sizeof(name2);
243     IAssemblyName_GetProperty(asmname2->name, ASM_NAME_NAME, name2, &size);
244
245     if ((ret = strcmpiW(name1, name2))) return ret;
246
247     for (i = ASM_NAME_MAJOR_VERSION; i < ASM_NAME_CULTURE; i++)
248     {
249         size = sizeof(version1);
250         IAssemblyName_GetProperty(asmname1->name, i, &version1, &size);
251         size = sizeof(version2);
252         IAssemblyName_GetProperty(asmname2->name, i, &version2, &size);
253
254         if (version1 < version2) return -1;
255         if (version1 > version2) return 1;
256     }
257
258     /* FIXME: compare cultures */
259
260     size = sizeof(token1);
261     IAssemblyName_GetProperty(asmname1->name, ASM_NAME_PUBLIC_KEY_TOKEN, token1, &size);
262     size = sizeof(token2);
263     IAssemblyName_GetProperty(asmname2->name, ASM_NAME_PUBLIC_KEY_TOKEN, token2, &size);
264
265     token_to_str(token1, token_str1);
266     token_to_str(token2, token_str2);
267
268     if ((ret = strcmpiW(token_str1, token_str2))) return ret;
269
270     return 0;
271 }
272
273 /* insert assembly in list preserving sort order */
274 static void insert_assembly(struct list *assemblies, ASMNAME *to_insert)
275 {
276     struct list *item;
277
278     LIST_FOR_EACH(item, assemblies)
279     {
280         ASMNAME *name = LIST_ENTRY(item, ASMNAME, entry);
281
282         if (compare_assembly_names(name, to_insert) > 0)
283         {
284             list_add_before(&name->entry, &to_insert->entry);
285             return;
286         }
287     }
288     list_add_tail(assemblies, &to_insert->entry);
289 }
290
291 static HRESULT enum_gac_assemblies(struct list *assemblies, IAssemblyName *name,
292                                    int depth, LPWSTR path)
293 {
294     WIN32_FIND_DATAW ffd;
295     WCHAR buf[MAX_PATH];
296     WCHAR disp[MAX_PATH];
297     WCHAR asmpath[MAX_PATH];
298     ASMNAME *asmname;
299     HANDLE hfind;
300     LPWSTR ptr;
301     HRESULT hr = S_OK;
302
303     static WCHAR parent[MAX_PATH];
304
305     static const WCHAR dot[] = {'.',0};
306     static const WCHAR dotdot[] = {'.','.',0};
307     static const WCHAR search_fmt[] = {'%','s','\\','*',0};
308     static const WCHAR dblunder[] = {'_','_',0};
309     static const WCHAR path_fmt[] = {'%','s','\\','%','s','\\','%','s','.','d','l','l',0};
310     static const WCHAR fmt[] = {'%','s',',',' ','V','e','r','s','i','o','n','=','%','s',',',' ',
311         'C','u','l','t','u','r','e','=','n','e','u','t','r','a','l',',',' ',
312         'P','u','b','l','i','c','K','e','y','T','o','k','e','n','=','%','s',0};
313     static const WCHAR ss_fmt[] = {'%','s','\\','%','s',0};
314
315     if (name)
316         parse_name(name, depth, path, buf);
317     else
318         sprintfW(buf, search_fmt, path);
319
320     hfind = FindFirstFileW(buf, &ffd);
321     if (hfind == INVALID_HANDLE_VALUE)
322         return S_OK;
323
324     do
325     {
326         if (!lstrcmpW(ffd.cFileName, dot) || !lstrcmpW(ffd.cFileName, dotdot))
327             continue;
328
329         if (depth == 0)
330         {
331             if (name)
332                 ptr = strrchrW(buf, '\\') + 1;
333             else
334                 ptr = ffd.cFileName;
335
336             lstrcpyW(parent, ptr);
337         }
338         else if (depth == 1)
339         {
340             sprintfW(asmpath, path_fmt, path, ffd.cFileName, parent);
341
342             ptr = strstrW(ffd.cFileName, dblunder);
343             *ptr = '\0';
344             ptr += 2;
345
346             sprintfW(disp, fmt, parent, ffd.cFileName, ptr);
347
348             asmname = HeapAlloc(GetProcessHeap(), 0, sizeof(ASMNAME));
349             if (!asmname)
350             {
351                 hr = E_OUTOFMEMORY;
352                 break;
353             }
354
355             hr = CreateAssemblyNameObject(&asmname->name, disp,
356                                           CANOF_PARSE_DISPLAY_NAME, NULL);
357             if (FAILED(hr))
358             {
359                 HeapFree(GetProcessHeap(), 0, asmname);
360                 break;
361             }
362
363             hr = IAssemblyName_SetPath(asmname->name, asmpath);
364             if (FAILED(hr))
365             {
366                 IAssemblyName_Release(asmname->name);
367                 HeapFree(GetProcessHeap(), 0, asmname);
368                 break;
369             }
370
371             insert_assembly(assemblies, asmname);
372             continue;
373         }
374
375         sprintfW(buf, ss_fmt, path, ffd.cFileName);
376         hr = enum_gac_assemblies(assemblies, name, depth + 1, buf);
377         if (FAILED(hr))
378             break;
379     } while (FindNextFileW(hfind, &ffd) != 0);
380
381     FindClose(hfind);
382     return hr;
383 }
384
385 static HRESULT enumerate_gac(IAssemblyEnumImpl *asmenum, IAssemblyName *pName)
386 {
387     static const WCHAR gac[] = {'\\','G','A','C',0};
388     static const WCHAR gac_32[] = {'\\','G','A','C','_','3','2',0};
389     static const WCHAR gac_64[] = {'\\','G','A','C','_','6','4',0};
390     static const WCHAR gac_msil[] = {'\\','G','A','C','_','M','S','I','L',0};
391     WCHAR path[MAX_PATH], buf[MAX_PATH];
392     SYSTEM_INFO info;
393     HRESULT hr;
394     DWORD size;
395
396     size = MAX_PATH;
397     hr = GetCachePath(ASM_CACHE_ROOT_EX, buf, &size);
398     if (FAILED(hr))
399         return hr;
400
401     strcpyW(path, buf);
402     GetNativeSystemInfo(&info);
403     if (info.u.s.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64)
404     {
405         strcpyW(path + size - 1, gac_64);
406         hr = enum_gac_assemblies(&asmenum->assemblies, pName, 0, path);
407         if (FAILED(hr))
408             return hr;
409     }
410     strcpyW(path + size - 1, gac_32);
411     hr = enum_gac_assemblies(&asmenum->assemblies, pName, 0, path);
412     if (FAILED(hr))
413         return hr;
414
415     strcpyW(path + size - 1, gac_msil);
416     hr = enum_gac_assemblies(&asmenum->assemblies, pName, 0, path);
417     if (FAILED(hr))
418         return hr;
419
420     size = MAX_PATH;
421     hr = GetCachePath(ASM_CACHE_ROOT, buf, &size);
422     if (FAILED(hr))
423         return hr;
424
425     strcpyW(path, buf);
426     if (info.u.s.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64)
427     {
428         strcpyW(path + size - 1, gac_64);
429         hr = enum_gac_assemblies(&asmenum->assemblies, pName, 0, path);
430         if (FAILED(hr))
431             return hr;
432     }
433     strcpyW(path + size - 1, gac_32);
434     hr = enum_gac_assemblies(&asmenum->assemblies, pName, 0, path);
435     if (FAILED(hr))
436         return hr;
437
438     strcpyW(path + size - 1, gac_msil);
439     hr = enum_gac_assemblies(&asmenum->assemblies, pName, 0, path);
440     if (FAILED(hr))
441         return hr;
442
443     strcpyW(path + size - 1, gac);
444     hr = enum_gac_assemblies(&asmenum->assemblies, pName, 0, path);
445     if (FAILED(hr))
446         return hr;
447
448     return S_OK;
449 }
450
451 /******************************************************************
452  *  CreateAssemblyEnum   (FUSION.@)
453  */
454 HRESULT WINAPI CreateAssemblyEnum(IAssemblyEnum **pEnum, IUnknown *pUnkReserved,
455                                   IAssemblyName *pName, DWORD dwFlags, LPVOID pvReserved)
456 {
457     IAssemblyEnumImpl *asmenum;
458     HRESULT hr;
459
460     TRACE("(%p, %p, %p, %08x, %p)\n", pEnum, pUnkReserved,
461           pName, dwFlags, pvReserved);
462
463     if (!pEnum)
464         return E_INVALIDARG;
465
466     if (dwFlags == 0 || dwFlags == ASM_CACHE_ROOT)
467         return E_INVALIDARG;
468
469     asmenum = HeapAlloc(GetProcessHeap(), 0, sizeof(IAssemblyEnumImpl));
470     if (!asmenum)
471         return E_OUTOFMEMORY;
472
473     asmenum->IAssemblyEnum_iface.lpVtbl = &AssemblyEnumVtbl;
474     asmenum->ref = 1;
475     list_init(&asmenum->assemblies);
476
477     if (dwFlags & ASM_CACHE_GAC)
478     {
479         hr = enumerate_gac(asmenum, pName);
480         if (FAILED(hr))
481         {
482             HeapFree(GetProcessHeap(), 0, asmenum);
483             return hr;
484         }
485     }
486
487     asmenum->iter = list_head(&asmenum->assemblies);
488     *pEnum = &asmenum->IAssemblyEnum_iface;
489
490     return S_OK;
491 }