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