4a87768e166d9247132a02ade84322a75ca72484
[reactos.git] / dll / win32 / 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 "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 }