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