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