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