2 * IAssemblyEnum implementation
4 * Copyright 2008 James Hawkins
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.
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.
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
24 #define NONAMELESSUNION
25 #define NONAMELESSSTRUCT
34 #include "fusionpriv.h"
36 #include "wine/debug.h"
37 #include "wine/list.h"
39 WINE_DEFAULT_DEBUG_CHANNEL(fusion
);
41 typedef struct _tagASMNAME
49 IAssemblyEnum IAssemblyEnum_iface
;
51 struct list assemblies
;
56 static inline IAssemblyEnumImpl
*impl_from_IAssemblyEnum(IAssemblyEnum
*iface
)
58 return CONTAINING_RECORD(iface
, IAssemblyEnumImpl
, IAssemblyEnum_iface
);
61 static HRESULT WINAPI
IAssemblyEnumImpl_QueryInterface(IAssemblyEnum
*iface
,
62 REFIID riid
, LPVOID
*ppobj
)
64 IAssemblyEnumImpl
*This
= impl_from_IAssemblyEnum(iface
);
66 TRACE("(%p, %s, %p)\n", This
, debugstr_guid(riid
), ppobj
);
70 if (IsEqualIID(riid
, &IID_IUnknown
) ||
71 IsEqualIID(riid
, &IID_IAssemblyEnum
))
73 IAssemblyEnum_AddRef(iface
);
74 *ppobj
= &This
->IAssemblyEnum_iface
;
78 WARN("(%p, %s, %p): not found\n", This
, debugstr_guid(riid
), ppobj
);
82 static ULONG WINAPI
IAssemblyEnumImpl_AddRef(IAssemblyEnum
*iface
)
84 IAssemblyEnumImpl
*This
= impl_from_IAssemblyEnum(iface
);
85 ULONG refCount
= InterlockedIncrement(&This
->ref
);
87 TRACE("(%p)->(ref before = %u)\n", This
, refCount
- 1);
92 static ULONG WINAPI
IAssemblyEnumImpl_Release(IAssemblyEnum
*iface
)
94 IAssemblyEnumImpl
*This
= impl_from_IAssemblyEnum(iface
);
95 ULONG refCount
= InterlockedDecrement(&This
->ref
);
96 struct list
*item
, *cursor
;
98 TRACE("(%p)->(ref before = %u)\n", This
, refCount
+ 1);
102 LIST_FOR_EACH_SAFE(item
, cursor
, &This
->assemblies
)
104 ASMNAME
*asmname
= LIST_ENTRY(item
, ASMNAME
, entry
);
106 list_remove(&asmname
->entry
);
107 IAssemblyName_Release(asmname
->name
);
117 static HRESULT WINAPI
IAssemblyEnumImpl_GetNextAssembly(IAssemblyEnum
*iface
,
119 IAssemblyName
**ppName
,
122 IAssemblyEnumImpl
*asmenum
= impl_from_IAssemblyEnum(iface
);
125 TRACE("(%p, %p, %p, %d)\n", iface
, pvReserved
, ppName
, dwFlags
);
130 asmname
= LIST_ENTRY(asmenum
->iter
, ASMNAME
, entry
);
134 *ppName
= asmname
->name
;
135 IAssemblyName_AddRef(*ppName
);
137 asmenum
->iter
= list_next(&asmenum
->assemblies
, asmenum
->iter
);
142 static HRESULT WINAPI
IAssemblyEnumImpl_Reset(IAssemblyEnum
*iface
)
144 IAssemblyEnumImpl
*asmenum
= impl_from_IAssemblyEnum(iface
);
146 TRACE("(%p)\n", iface
);
148 asmenum
->iter
= list_head(&asmenum
->assemblies
);
152 static HRESULT WINAPI
IAssemblyEnumImpl_Clone(IAssemblyEnum
*iface
,
153 IAssemblyEnum
**ppEnum
)
155 FIXME("(%p, %p) stub!\n", iface
, ppEnum
);
159 static const IAssemblyEnumVtbl AssemblyEnumVtbl
= {
160 IAssemblyEnumImpl_QueryInterface
,
161 IAssemblyEnumImpl_AddRef
,
162 IAssemblyEnumImpl_Release
,
163 IAssemblyEnumImpl_GetNextAssembly
,
164 IAssemblyEnumImpl_Reset
,
165 IAssemblyEnumImpl_Clone
168 static void build_file_mask(IAssemblyName
*name
, int depth
, const WCHAR
*path
,
169 const WCHAR
*prefix
, WCHAR
*buf
)
171 static const WCHAR star
[] = {'*',0};
172 static const WCHAR ss_fmt
[] = {'%','s','\\','%','s',0};
173 static const WCHAR sss_fmt
[] = {'%','s','\\','%','s','_','_','%','s',0};
174 static const WCHAR ssss_fmt
[] = {'%','s','\\','%','s','%','s','_','_','%','s',0};
175 static const WCHAR ver_fmt
[] = {'%','u','.','%','u','.','%','u','.','%','u',0};
176 static const WCHAR star_fmt
[] = {'%','s','\\','*',0};
177 static const WCHAR star_prefix_fmt
[] = {'%','s','\\','%','s','*',0};
178 WCHAR disp
[MAX_PATH
], version
[24]; /* strlen("65535") * 4 + 3 + 1 */
179 LPCWSTR verptr
, pubkeyptr
;
181 DWORD size
, major_size
, minor_size
, build_size
, revision_size
;
182 WORD major
, minor
, build
, revision
;
183 WCHAR token_str
[TOKEN_LENGTH
+ 1];
184 BYTE token
[BYTES_PER_TOKEN
];
188 if (prefix
&& depth
== 1)
189 swprintf(buf
, MAX_PATH
, star_prefix_fmt
, path
, prefix
);
191 swprintf(buf
, MAX_PATH
, star_fmt
, path
);
198 hr
= IAssemblyName_GetName(name
, &size
, disp
);
200 swprintf(buf
, MAX_PATH
, ss_fmt
, path
, disp
);
202 swprintf(buf
, MAX_PATH
, ss_fmt
, path
, star
);
206 major_size
= sizeof(major
);
207 IAssemblyName_GetProperty(name
, ASM_NAME_MAJOR_VERSION
, &major
, &major_size
);
209 minor_size
= sizeof(minor
);
210 IAssemblyName_GetProperty(name
, ASM_NAME_MINOR_VERSION
, &minor
, &minor_size
);
212 build_size
= sizeof(build
);
213 IAssemblyName_GetProperty(name
, ASM_NAME_BUILD_NUMBER
, &build
, &build_size
);
215 revision_size
= sizeof(revision
);
216 IAssemblyName_GetProperty(name
, ASM_NAME_REVISION_NUMBER
, &revision
, &revision_size
);
218 if (!major_size
|| !minor_size
|| !build_size
|| !revision_size
) verptr
= star
;
221 swprintf(version
, ARRAY_SIZE(version
), ver_fmt
, major
, minor
, build
, revision
);
225 size
= sizeof(token
);
226 IAssemblyName_GetProperty(name
, ASM_NAME_PUBLIC_KEY_TOKEN
, token
, &size
);
228 if (!size
) pubkeyptr
= star
;
231 token_to_str(token
, token_str
);
232 pubkeyptr
= token_str
;
236 swprintf(buf
, MAX_PATH
, ssss_fmt
, path
, prefix
, verptr
, pubkeyptr
);
238 swprintf(buf
, MAX_PATH
, sss_fmt
, path
, verptr
, pubkeyptr
);
242 static int compare_assembly_names(ASMNAME
*asmname1
, ASMNAME
*asmname2
)
245 WORD version1
, version2
;
246 WCHAR name1
[MAX_PATH
], name2
[MAX_PATH
];
247 WCHAR token_str1
[TOKEN_LENGTH
+ 1], token_str2
[TOKEN_LENGTH
+ 1];
248 BYTE token1
[BYTES_PER_TOKEN
], token2
[BYTES_PER_TOKEN
];
251 size
= sizeof(name1
);
252 IAssemblyName_GetProperty(asmname1
->name
, ASM_NAME_NAME
, name1
, &size
);
253 size
= sizeof(name2
);
254 IAssemblyName_GetProperty(asmname2
->name
, ASM_NAME_NAME
, name2
, &size
);
256 if ((ret
= wcsicmp(name1
, name2
))) return ret
;
258 for (i
= ASM_NAME_MAJOR_VERSION
; i
< ASM_NAME_CULTURE
; i
++)
260 size
= sizeof(version1
);
261 IAssemblyName_GetProperty(asmname1
->name
, i
, &version1
, &size
);
262 size
= sizeof(version2
);
263 IAssemblyName_GetProperty(asmname2
->name
, i
, &version2
, &size
);
265 if (version1
< version2
) return -1;
266 if (version1
> version2
) return 1;
269 /* FIXME: compare cultures */
271 size
= sizeof(token1
);
272 IAssemblyName_GetProperty(asmname1
->name
, ASM_NAME_PUBLIC_KEY_TOKEN
, token1
, &size
);
273 size
= sizeof(token2
);
274 IAssemblyName_GetProperty(asmname2
->name
, ASM_NAME_PUBLIC_KEY_TOKEN
, token2
, &size
);
276 token_to_str(token1
, token_str1
);
277 token_to_str(token2
, token_str2
);
279 if ((ret
= wcsicmp(token_str1
, token_str2
))) return ret
;
284 /* insert assembly in list preserving sort order */
285 static void insert_assembly(struct list
*assemblies
, ASMNAME
*to_insert
)
289 LIST_FOR_EACH(item
, assemblies
)
291 ASMNAME
*name
= LIST_ENTRY(item
, ASMNAME
, entry
);
293 if (compare_assembly_names(name
, to_insert
) > 0)
295 list_add_before(&name
->entry
, &to_insert
->entry
);
299 list_add_tail(assemblies
, &to_insert
->entry
);
302 static HRESULT
enum_gac_assemblies(struct list
*assemblies
, IAssemblyName
*name
,
303 int depth
, const WCHAR
*prefix
, LPWSTR path
)
305 static const WCHAR dot
[] = {'.',0};
306 static const WCHAR dotdot
[] = {'.','.',0};
307 static const WCHAR dblunder
[] = {'_','_',0};
308 static const WCHAR path_fmt
[] = {'%','s','\\','%','s','\\','%','s','.','d','l','l',0};
309 static const WCHAR name_fmt
[] = {'%','s',',',' ','V','e','r','s','i','o','n','=','%','s',',',' ',
310 'C','u','l','t','u','r','e','=','n','e','u','t','r','a','l',',',' ',
311 'P','u','b','l','i','c','K','e','y','T','o','k','e','n','=','%','s',0};
312 static const WCHAR ss_fmt
[] = {'%','s','\\','%','s',0};
313 WIN32_FIND_DATAW ffd
;
314 WCHAR buf
[MAX_PATH
], disp
[MAX_PATH
], asmpath
[MAX_PATH
], *ptr
;
315 static WCHAR parent
[MAX_PATH
];
320 build_file_mask(name
, depth
, path
, prefix
, buf
);
321 hfind
= FindFirstFileW(buf
, &ffd
);
322 if (hfind
== INVALID_HANDLE_VALUE
)
327 if (!lstrcmpW(ffd
.cFileName
, dot
) || !lstrcmpW(ffd
.cFileName
, dotdot
))
333 ptr
= wcsrchr(buf
, '\\') + 1;
337 lstrcpyW(parent
, ptr
);
341 const WCHAR
*token
, *version
= ffd
.cFileName
;
343 swprintf(asmpath
, ARRAY_SIZE(asmpath
), path_fmt
, path
, ffd
.cFileName
, parent
);
344 ptr
= wcsstr(ffd
.cFileName
, dblunder
);
350 unsigned int prefix_len
= lstrlenW(prefix
);
351 if (lstrlenW(ffd
.cFileName
) >= prefix_len
&&
352 !wcsnicmp(ffd
.cFileName
, prefix
, prefix_len
))
353 version
+= prefix_len
;
355 swprintf(disp
, ARRAY_SIZE(disp
), name_fmt
, parent
, version
, token
);
357 if (!(asmname
= heap_alloc(sizeof(*asmname
))))
363 hr
= CreateAssemblyNameObject(&asmname
->name
, disp
,
364 CANOF_PARSE_DISPLAY_NAME
, NULL
);
371 hr
= IAssemblyName_SetPath(asmname
->name
, asmpath
);
374 IAssemblyName_Release(asmname
->name
);
379 insert_assembly(assemblies
, asmname
);
383 swprintf(buf
, ARRAY_SIZE(buf
), ss_fmt
, path
, ffd
.cFileName
);
384 hr
= enum_gac_assemblies(assemblies
, name
, depth
+ 1, prefix
, buf
);
387 } while (FindNextFileW(hfind
, &ffd
) != 0);
393 static HRESULT
enumerate_gac(IAssemblyEnumImpl
*asmenum
, IAssemblyName
*pName
)
395 static const WCHAR gac
[] = {'\\','G','A','C',0};
396 static const WCHAR gac_32
[] = {'\\','G','A','C','_','3','2',0};
397 static const WCHAR gac_64
[] = {'\\','G','A','C','_','6','4',0};
398 static const WCHAR gac_msil
[] = {'\\','G','A','C','_','M','S','I','L',0};
399 static const WCHAR v40
[] = {'v','4','.','0','_',0};
400 WCHAR path
[MAX_PATH
], buf
[MAX_PATH
];
406 hr
= GetCachePath(ASM_CACHE_ROOT_EX
, buf
, &size
);
411 GetNativeSystemInfo(&info
);
412 if (info
.u
.s
.wProcessorArchitecture
== PROCESSOR_ARCHITECTURE_AMD64
)
414 lstrcpyW(path
+ size
- 1, gac_64
);
415 hr
= enum_gac_assemblies(&asmenum
->assemblies
, pName
, 0, v40
, path
);
419 lstrcpyW(path
+ size
- 1, gac_32
);
420 hr
= enum_gac_assemblies(&asmenum
->assemblies
, pName
, 0, v40
, path
);
424 lstrcpyW(path
+ size
- 1, gac_msil
);
425 hr
= enum_gac_assemblies(&asmenum
->assemblies
, pName
, 0, v40
, path
);
430 hr
= GetCachePath(ASM_CACHE_ROOT
, buf
, &size
);
435 if (info
.u
.s
.wProcessorArchitecture
== PROCESSOR_ARCHITECTURE_AMD64
)
437 lstrcpyW(path
+ size
- 1, gac_64
);
438 hr
= enum_gac_assemblies(&asmenum
->assemblies
, pName
, 0, NULL
, path
);
442 lstrcpyW(path
+ size
- 1, gac_32
);
443 hr
= enum_gac_assemblies(&asmenum
->assemblies
, pName
, 0, NULL
, path
);
447 lstrcpyW(path
+ size
- 1, gac_msil
);
448 hr
= enum_gac_assemblies(&asmenum
->assemblies
, pName
, 0, NULL
, path
);
452 lstrcpyW(path
+ size
- 1, gac
);
453 hr
= enum_gac_assemblies(&asmenum
->assemblies
, pName
, 0, NULL
, path
);
460 /******************************************************************
461 * CreateAssemblyEnum (FUSION.@)
463 HRESULT WINAPI
CreateAssemblyEnum(IAssemblyEnum
**pEnum
, IUnknown
*pUnkReserved
,
464 IAssemblyName
*pName
, DWORD dwFlags
, LPVOID pvReserved
)
466 IAssemblyEnumImpl
*asmenum
;
469 TRACE("(%p, %p, %p, %08x, %p)\n", pEnum
, pUnkReserved
,
470 pName
, dwFlags
, pvReserved
);
475 if (dwFlags
== 0 || dwFlags
== ASM_CACHE_ROOT
)
478 if (!(asmenum
= heap_alloc(sizeof(*asmenum
)))) return E_OUTOFMEMORY
;
480 asmenum
->IAssemblyEnum_iface
.lpVtbl
= &AssemblyEnumVtbl
;
482 list_init(&asmenum
->assemblies
);
484 if (dwFlags
& ASM_CACHE_GAC
)
486 hr
= enumerate_gac(asmenum
, pName
);
494 asmenum
->iter
= list_head(&asmenum
->assemblies
);
495 *pEnum
= &asmenum
->IAssemblyEnum_iface
;