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