fusion: Add a stub implementation of InitializeFusion.
[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     static const WCHAR dot[] = {'.',0};
295     static const WCHAR dotdot[] = {'.','.',0};
296     static const WCHAR search_fmt[] = {'%','s','\\','*',0};
297     static const WCHAR dblunder[] = {'_','_',0};
298     static const WCHAR path_fmt[] = {'%','s','\\','%','s','\\','%','s','.','d','l','l',0};
299     static const WCHAR fmt[] = {'%','s',',',' ','V','e','r','s','i','o','n','=','%','s',',',' ',
300         'C','u','l','t','u','r','e','=','n','e','u','t','r','a','l',',',' ',
301         'P','u','b','l','i','c','K','e','y','T','o','k','e','n','=','%','s',0};
302     static const WCHAR ss_fmt[] = {'%','s','\\','%','s',0};
303     static const WCHAR v40[] = {'v','4','.','0','_'};
304     WIN32_FIND_DATAW ffd;
305     WCHAR buf[MAX_PATH], disp[MAX_PATH], asmpath[MAX_PATH];
306     static WCHAR parent[MAX_PATH];
307     ASMNAME *asmname;
308     HANDLE hfind;
309     WCHAR *ptr;
310     HRESULT hr = S_OK;
311
312     if (name)
313         parse_name(name, depth, path, buf);
314     else
315         sprintfW(buf, search_fmt, path);
316
317     hfind = FindFirstFileW(buf, &ffd);
318     if (hfind == INVALID_HANDLE_VALUE)
319         return S_OK;
320
321     do
322     {
323         if (!lstrcmpW(ffd.cFileName, dot) || !lstrcmpW(ffd.cFileName, dotdot))
324             continue;
325
326         if (depth == 0)
327         {
328             if (name)
329                 ptr = strrchrW(buf, '\\') + 1;
330             else
331                 ptr = ffd.cFileName;
332
333             lstrcpyW(parent, ptr);
334         }
335         else if (depth == 1)
336         {
337             unsigned int prefix_len = sizeof(v40)/sizeof(WCHAR);
338             const WCHAR *token, *version = ffd.cFileName;
339
340             sprintfW(asmpath, path_fmt, path, ffd.cFileName, parent);
341
342             ptr = strstrW(ffd.cFileName, dblunder);
343             *ptr = '\0';
344             token = ptr + 2;
345
346             if (strlenW(ffd.cFileName) >= prefix_len &&
347                 !memcmp(ffd.cFileName, v40, sizeof(v40))) version += prefix_len;
348
349             sprintfW(disp, fmt, parent, version, token);
350
351             asmname = HeapAlloc(GetProcessHeap(), 0, sizeof(ASMNAME));
352             if (!asmname)
353             {
354                 hr = E_OUTOFMEMORY;
355                 break;
356             }
357
358             hr = CreateAssemblyNameObject(&asmname->name, disp,
359                                           CANOF_PARSE_DISPLAY_NAME, NULL);
360             if (FAILED(hr))
361             {
362                 HeapFree(GetProcessHeap(), 0, asmname);
363                 break;
364             }
365
366             hr = IAssemblyName_SetPath(asmname->name, asmpath);
367             if (FAILED(hr))
368             {
369                 IAssemblyName_Release(asmname->name);
370                 HeapFree(GetProcessHeap(), 0, asmname);
371                 break;
372             }
373
374             insert_assembly(assemblies, asmname);
375             continue;
376         }
377
378         sprintfW(buf, ss_fmt, path, ffd.cFileName);
379         hr = enum_gac_assemblies(assemblies, name, depth + 1, buf);
380         if (FAILED(hr))
381             break;
382     } while (FindNextFileW(hfind, &ffd) != 0);
383
384     FindClose(hfind);
385     return hr;
386 }
387
388 static HRESULT enumerate_gac(IAssemblyEnumImpl *asmenum, IAssemblyName *pName)
389 {
390     static const WCHAR gac[] = {'\\','G','A','C',0};
391     static const WCHAR gac_32[] = {'\\','G','A','C','_','3','2',0};
392     static const WCHAR gac_64[] = {'\\','G','A','C','_','6','4',0};
393     static const WCHAR gac_msil[] = {'\\','G','A','C','_','M','S','I','L',0};
394     WCHAR path[MAX_PATH], buf[MAX_PATH];
395     SYSTEM_INFO info;
396     HRESULT hr;
397     DWORD size;
398
399     size = MAX_PATH;
400     hr = GetCachePath(ASM_CACHE_ROOT_EX, buf, &size);
401     if (FAILED(hr))
402         return hr;
403
404     strcpyW(path, buf);
405     GetNativeSystemInfo(&info);
406     if (info.u.s.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64)
407     {
408         strcpyW(path + size - 1, gac_64);
409         hr = enum_gac_assemblies(&asmenum->assemblies, pName, 0, path);
410         if (FAILED(hr))
411             return hr;
412     }
413     strcpyW(path + size - 1, gac_32);
414     hr = enum_gac_assemblies(&asmenum->assemblies, pName, 0, path);
415     if (FAILED(hr))
416         return hr;
417
418     strcpyW(path + size - 1, gac_msil);
419     hr = enum_gac_assemblies(&asmenum->assemblies, pName, 0, path);
420     if (FAILED(hr))
421         return hr;
422
423     size = MAX_PATH;
424     hr = GetCachePath(ASM_CACHE_ROOT, buf, &size);
425     if (FAILED(hr))
426         return hr;
427
428     strcpyW(path, buf);
429     if (info.u.s.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64)
430     {
431         strcpyW(path + size - 1, gac_64);
432         hr = enum_gac_assemblies(&asmenum->assemblies, pName, 0, path);
433         if (FAILED(hr))
434             return hr;
435     }
436     strcpyW(path + size - 1, gac_32);
437     hr = enum_gac_assemblies(&asmenum->assemblies, pName, 0, path);
438     if (FAILED(hr))
439         return hr;
440
441     strcpyW(path + size - 1, gac_msil);
442     hr = enum_gac_assemblies(&asmenum->assemblies, pName, 0, path);
443     if (FAILED(hr))
444         return hr;
445
446     strcpyW(path + size - 1, gac);
447     hr = enum_gac_assemblies(&asmenum->assemblies, pName, 0, path);
448     if (FAILED(hr))
449         return hr;
450
451     return S_OK;
452 }
453
454 /******************************************************************
455  *  CreateAssemblyEnum   (FUSION.@)
456  */
457 HRESULT WINAPI CreateAssemblyEnum(IAssemblyEnum **pEnum, IUnknown *pUnkReserved,
458                                   IAssemblyName *pName, DWORD dwFlags, LPVOID pvReserved)
459 {
460     IAssemblyEnumImpl *asmenum;
461     HRESULT hr;
462
463     TRACE("(%p, %p, %p, %08x, %p)\n", pEnum, pUnkReserved,
464           pName, dwFlags, pvReserved);
465
466     if (!pEnum)
467         return E_INVALIDARG;
468
469     if (dwFlags == 0 || dwFlags == ASM_CACHE_ROOT)
470         return E_INVALIDARG;
471
472     asmenum = HeapAlloc(GetProcessHeap(), 0, sizeof(IAssemblyEnumImpl));
473     if (!asmenum)
474         return E_OUTOFMEMORY;
475
476     asmenum->IAssemblyEnum_iface.lpVtbl = &AssemblyEnumVtbl;
477     asmenum->ref = 1;
478     list_init(&asmenum->assemblies);
479
480     if (dwFlags & ASM_CACHE_GAC)
481     {
482         hr = enumerate_gac(asmenum, pName);
483         if (FAILED(hr))
484         {
485             HeapFree(GetProcessHeap(), 0, asmenum);
486             return hr;
487         }
488     }
489
490     asmenum->iter = list_head(&asmenum->assemblies);
491     *pEnum = &asmenum->IAssemblyEnum_iface;
492
493     return S_OK;
494 }