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