1 /*
2  * Copyright 2008 James Hawkins
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18 
19 #define COBJMACROS
20 
21 #include <stdio.h>
22 
23 #include <windows.h>
24 #include <shlwapi.h>
25 #include <mscoree.h>
26 #include <fusion.h>
27 #include <corerror.h>
28 
29 #include "wine/test.h"
30 #include "wine/list.h"
31 
32 static HRESULT (WINAPI *pCreateAssemblyEnum)(IAssemblyEnum **pEnum,
33                                              IUnknown *pUnkReserved,
34                                              IAssemblyName *pName,
35                                              DWORD dwFlags, LPVOID pvReserved);
36 static HRESULT (WINAPI *pCreateAssemblyNameObject)(IAssemblyName **ppAssemblyNameObj,
37                                                    LPCWSTR szAssemblyName, DWORD dwFlags,
38                                                    LPVOID pvReserved);
39 static HRESULT (WINAPI *pGetCachePath)(ASM_CACHE_FLAGS dwCacheFlags,
40                                        LPWSTR pwzCachePath, PDWORD pcchPath);
41 static HRESULT (WINAPI *pLoadLibraryShim)(LPCWSTR szDllName, LPCWSTR szVersion,
42                                           LPVOID pvReserved, HMODULE *phModDll);
43 
44 static BOOL init_functionpointers(void)
45 {
46     HRESULT hr;
47     HMODULE hfusion;
48     HMODULE hmscoree;
49 
50     static const WCHAR szFusion[] = {'f','u','s','i','o','n','.','d','l','l',0};
51 
52     hmscoree = LoadLibraryA("mscoree.dll");
53     if (!hmscoree)
54     {
55         win_skip("mscoree.dll not available\n");
56         return FALSE;
57     }
58 
59     pLoadLibraryShim = (void *)GetProcAddress(hmscoree, "LoadLibraryShim");
60     if (!pLoadLibraryShim)
61     {
62         win_skip("LoadLibraryShim not available\n");
63         FreeLibrary(hmscoree);
64         return FALSE;
65     }
66 
67     hr = pLoadLibraryShim(szFusion, NULL, NULL, &hfusion);
68     if (FAILED(hr))
69     {
70         win_skip("fusion.dll not available\n");
71         FreeLibrary(hmscoree);
72         return FALSE;
73     }
74 
75     pCreateAssemblyEnum = (void *)GetProcAddress(hfusion, "CreateAssemblyEnum");
76     pCreateAssemblyNameObject = (void *)GetProcAddress(hfusion, "CreateAssemblyNameObject");
77     pGetCachePath = (void *)GetProcAddress(hfusion, "GetCachePath");
78 
79     if (!pCreateAssemblyEnum ||
80         !pCreateAssemblyNameObject || !pGetCachePath)
81     {
82         win_skip("fusion.dll not implemented\n");
83         return FALSE;
84     }
85 
86     FreeLibrary(hmscoree);
87     return TRUE;
88 }
89 
90 static inline void to_widechar(LPWSTR dest, LPCSTR src)
91 {
92     MultiByteToWideChar(CP_ACP, 0, src, -1, dest, MAX_PATH);
93 }
94 
95 static inline void to_multibyte(LPSTR dest, LPWSTR src)
96 {
97     WideCharToMultiByte(CP_ACP, 0, src, -1, dest, MAX_PATH, NULL, NULL);
98 }
99 
100 static BOOL create_full_path(LPCSTR path)
101 {
102     LPSTR new_path;
103     BOOL ret = TRUE;
104     int len;
105 
106     new_path = HeapAlloc(GetProcessHeap(), 0, lstrlenA(path) + 1);
107     if (!new_path)
108         return FALSE;
109 
110     lstrcpyA(new_path, path);
111 
112     while ((len = lstrlenA(new_path)) && new_path[len - 1] == '\\')
113         new_path[len - 1] = 0;
114 
115     while (!CreateDirectoryA(new_path, NULL))
116     {
117         LPSTR slash;
118         DWORD last_error = GetLastError();
119 
120         if(last_error == ERROR_ALREADY_EXISTS)
121             break;
122 
123         if(last_error != ERROR_PATH_NOT_FOUND)
124         {
125             ret = FALSE;
126             break;
127         }
128 
129         if(!(slash = strrchr(new_path, '\\')))
130         {
131             ret = FALSE;
132             break;
133         }
134 
135         len = slash - new_path;
136         new_path[len] = 0;
137         if(!create_full_path(new_path))
138         {
139             ret = FALSE;
140             break;
141         }
142 
143         new_path[len] = '\\';
144     }
145 
146     HeapFree(GetProcessHeap(), 0, new_path);
147     return ret;
148 }
149 
150 static BOOL create_file_data(LPCSTR name, LPCSTR data, DWORD size)
151 {
152     HANDLE file;
153     DWORD written;
154 
155     file = CreateFileA(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
156     if (file == INVALID_HANDLE_VALUE)
157         return FALSE;
158 
159     WriteFile(file, data, strlen(data), &written, NULL);
160 
161     if (size)
162     {
163         SetFilePointer(file, size, NULL, FILE_BEGIN);
164         SetEndOfFile(file);
165     }
166 
167     CloseHandle(file);
168     return TRUE;
169 }
170 
171 static void test_CreateAssemblyEnum(void)
172 {
173     HRESULT hr;
174     WCHAR namestr[MAX_PATH];
175     IAssemblyEnum *asmenum;
176     IAssemblyName *asmname;
177 
178     to_widechar(namestr, "wine");
179     asmname = NULL;
180     hr = pCreateAssemblyNameObject(&asmname, namestr, CANOF_PARSE_DISPLAY_NAME, NULL);
181     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
182     ok(asmname != NULL, "Expected non-NULL asmname\n");
183 
184     /* pEnum is NULL */
185     if (0)
186     {
187         /* Crashes on .NET 1.x */
188         hr = pCreateAssemblyEnum(NULL, NULL, asmname, ASM_CACHE_GAC, NULL);
189         ok(hr == E_INVALIDARG, "Expected E_INVALIDARG, got %08x\n", hr);
190     }
191 
192     /* pName is NULL */
193     asmenum = NULL;
194     hr = pCreateAssemblyEnum(&asmenum, NULL, NULL, ASM_CACHE_GAC, NULL);
195     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
196     ok(asmenum != NULL, "Expected non-NULL asmenum\n");
197 
198     IAssemblyEnum_Release(asmenum);
199 
200     /* dwFlags is ASM_CACHE_ROOT */
201     asmenum = (IAssemblyEnum *)0xdeadbeef;
202     hr = pCreateAssemblyEnum(&asmenum, NULL, NULL, ASM_CACHE_ROOT, NULL);
203     ok(hr == E_INVALIDARG, "Expected E_INVALIDARG, got %08x\n", hr);
204     ok(asmenum == (IAssemblyEnum *)0xdeadbeef,
205        "Expected asmenum to be unchanged, got %p\n", asmenum);
206 
207     /* invalid dwFlags */
208     asmenum = (IAssemblyEnum *)0xdeadbeef;
209     hr = pCreateAssemblyEnum(&asmenum, NULL, NULL, 0, NULL);
210     ok(hr == E_INVALIDARG, "Expected E_INVALIDARG, got %08x\n", hr);
211     ok(asmenum == (IAssemblyEnum *)0xdeadbeef,
212        "Expected asmenum to be unchanged, got %p\n", asmenum);
213 
214     IAssemblyName_Release(asmname);
215 }
216 
217 typedef struct _tagASMNAME
218 {
219     struct list entry;
220     char data[1];
221 } ASMNAME;
222 
223 static void enum_gac_assembly_dirs(struct list *assemblies, const char *parent, char path[MAX_PATH])
224 {
225     static const char format[] = "%s, Version=%s, Culture=%s, PublicKeyToken=%s";
226     WIN32_FIND_DATAA ffd;
227     ASMNAME *name;
228     HANDLE hfind;
229     int len;
230     char *ptr, *end = path + strlen( path );
231 
232     lstrcpynA( end, "\\*", path + MAX_PATH - end );
233     hfind = FindFirstFileA(path, &ffd);
234     if (hfind == INVALID_HANDLE_VALUE) return;
235     end++;
236 
237     do
238     {
239         char culture[MAX_PATH];
240 
241         if (!strcmp(ffd.cFileName, ".") || !strcmp(ffd.cFileName, "..")) continue;
242 
243         *end = 0;
244         /* Directories with no dll or exe will not be enumerated */
245         sprintf(end, "%s\\%s.dll", ffd.cFileName, parent);
246         if (GetFileAttributesA(path) == INVALID_FILE_ATTRIBUTES)
247         {
248             sprintf(end, "%s\\%s.exe", ffd.cFileName, parent);
249             if (GetFileAttributesA(path) == INVALID_FILE_ATTRIBUTES) continue;
250         }
251 
252         if (!(ptr = strchr(ffd.cFileName, '_'))) continue;
253         *ptr++ = 0;
254 
255         if (*ptr != '_')
256         {
257             lstrcpyA(culture, ptr);
258             *strchr(culture, '_') = 0;
259         }
260         else
261             lstrcpyA(culture, "neutral");
262 
263         ptr = strchr(ptr, '_');
264         ptr++;
265         len = sizeof(format) + strlen(parent) + strlen(ffd.cFileName) + strlen(culture) + strlen(ptr);
266 
267         name = HeapAlloc(GetProcessHeap(), 0, offsetof( ASMNAME, data[len] ));
268         sprintf( name->data, format, parent, ffd.cFileName, culture, ptr);
269         list_add_tail(assemblies, &name->entry);
270     } while (FindNextFileA(hfind, &ffd) != 0);
271 
272     FindClose(hfind);
273 }
274 
275 static void enum_gac_assemblies(struct list *assemblies, char path[MAX_PATH])
276 {
277     WIN32_FIND_DATAA ffd;
278     HANDLE hfind;
279     char *end = path + strlen( path );
280 
281     lstrcpynA( end, "\\*", path + MAX_PATH - end );
282     hfind = FindFirstFileA(path, &ffd);
283     if (hfind == INVALID_HANDLE_VALUE) return;
284     end++;
285 
286     do
287     {
288         if (!strcmp(ffd.cFileName, ".") || !strcmp(ffd.cFileName, "..")) continue;
289         lstrcpynA( end, ffd.cFileName, path + MAX_PATH - end );
290         enum_gac_assembly_dirs( assemblies, ffd.cFileName, path );
291     } while (FindNextFileA(hfind, &ffd) != 0);
292 
293     FindClose(hfind);
294 }
295 
296 static void test_enumerate(void)
297 {
298     struct list assemblies = LIST_INIT(assemblies);
299     struct list *item, *cursor;
300     IAssemblyEnum *asmenum;
301     IAssemblyName *next;
302     WCHAR buf[MAX_PATH];
303     CHAR path[MAX_PATH];
304     CHAR disp[MAX_PATH];
305     HRESULT hr;
306     BOOL found;
307     DWORD size;
308 
309     size = MAX_PATH;
310     hr = pGetCachePath(ASM_CACHE_GAC, buf, &size);
311     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
312 
313     to_multibyte(path, buf);
314     lstrcatA(path, "_32");
315     enum_gac_assemblies(&assemblies, path);
316 
317     to_multibyte(path, buf);
318     lstrcatA(path, "_64");
319     enum_gac_assemblies(&assemblies, path);
320 
321     to_multibyte(path, buf);
322     lstrcatA(path, "_MSIL");
323     enum_gac_assemblies(&assemblies, path);
324 
325     to_multibyte(path, buf);
326     enum_gac_assemblies(&assemblies, path);
327 
328     asmenum = NULL;
329     hr = pCreateAssemblyEnum(&asmenum, NULL, NULL, ASM_CACHE_GAC, NULL);
330     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
331     ok(asmenum != NULL, "Expected non-NULL asmenum\n");
332 
333     while (IAssemblyEnum_GetNextAssembly(asmenum, NULL, &next, 0) == S_OK)
334     {
335         size = MAX_PATH;
336         IAssemblyName_GetDisplayName(next, buf, &size, 0);
337         to_multibyte(disp, buf);
338 
339         found = FALSE;
340         LIST_FOR_EACH_SAFE(item, cursor, &assemblies)
341         {
342             ASMNAME *asmname = LIST_ENTRY(item, ASMNAME, entry);
343 
344             if (!lstrcmpA(asmname->data, disp))
345             {
346                 found = TRUE;
347 
348                 list_remove(&asmname->entry);
349                 HeapFree(GetProcessHeap(), 0, asmname);
350                 break;
351             }
352         }
353 
354         ok(found, "Extra assembly enumerated: %s\n", disp);
355         IAssemblyName_Release(next);
356     }
357 
358     /* enumeration is exhausted */
359     next = (IAssemblyName *)0xdeadbeef;
360     hr = IAssemblyEnum_GetNextAssembly(asmenum, NULL, &next, 0);
361     ok(hr == S_FALSE, "Expected S_FALSE, got %08x\n", hr);
362     ok(next == (IAssemblyName *)0xdeadbeef,
363        "Expected next to be unchanged, got %p\n", next);
364 
365     LIST_FOR_EACH_SAFE(item, cursor, &assemblies)
366     {
367         ASMNAME *asmname = LIST_ENTRY(item, ASMNAME, entry);
368 
369         ok(FALSE, "Assembly not enumerated: %s\n", asmname->data);
370 
371         list_remove(&asmname->entry);
372         HeapFree(GetProcessHeap(), 0, asmname);
373     }
374 
375     IAssemblyEnum_Release(asmenum);
376 }
377 
378 static void test_enumerate_name(void)
379 {
380     IAssemblyEnum *asmenum;
381     IAssemblyName *asmname, *next;
382     WCHAR buf[MAX_PATH];
383     CHAR gac[MAX_PATH];
384     CHAR path[MAX_PATH];
385     CHAR disp[MAX_PATH];
386     WCHAR namestr[MAX_PATH];
387     CHAR exp[6][MAX_PATH];
388     HRESULT hr;
389     DWORD size;
390 
391     lstrcpyA(exp[0], "wine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=16a3fcd171e93a8d");
392     lstrcpyA(exp[1], "wine, Version=1.0.1.2, Culture=neutral, PublicKeyToken=123456789abcdef0");
393     lstrcpyA(exp[2], "wine, Version=1.0.1.2, Culture=neutral, PublicKeyToken=16a3fcd171e93a8d");
394     lstrcpyA(exp[3], "Wine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=16a3fcd171e93a8d");
395     lstrcpyA(exp[4], "Wine, Version=1.0.1.2, Culture=neutral, PublicKeyToken=123456789abcdef0");
396     lstrcpyA(exp[5], "Wine, Version=1.0.1.2, Culture=neutral, PublicKeyToken=16a3fcd171e93a8d");
397 
398     size = MAX_PATH;
399     hr = pGetCachePath(ASM_CACHE_GAC, buf, &size);
400     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
401 
402     to_multibyte(gac, buf);
403     create_full_path(gac);
404 
405     sprintf(path, "%s\\Wine", gac);
406     CreateDirectoryA(path, NULL);
407 
408     sprintf(path, "%s\\Wine\\1.0.0.0__16a3fcd171e93a8d", gac);
409     CreateDirectoryA(path, NULL);
410 
411     lstrcatA(path, "\\Wine.dll");
412     if (!create_file_data(path, path, 100))
413     {
414         win_skip("Failed to open file %s, skipping name enumeration tests\n", path);
415         goto done;
416     }
417 
418     sprintf(path, "%s\\Wine\\1.0.1.2__16a3fcd171e93a8d", gac);
419     CreateDirectoryA(path, NULL);
420 
421     lstrcatA(path, "\\Wine.dll");
422     if (!create_file_data(path, path, 100))
423     {
424         win_skip("Failed to open file %s, skipping name enumeration tests\n", path);
425         goto done;
426     }
427 
428     sprintf(path, "%s\\Wine\\1.0.1.2__123456789abcdef0", gac);
429     CreateDirectoryA(path, NULL);
430 
431     lstrcatA(path, "\\Wine.dll");
432     if (!create_file_data(path, path, 100))
433     {
434         win_skip("Failed to open file %s, skipping name enumeration tests\n", path);
435         goto done;
436     }
437 
438     /* test case sensitivity */
439     to_widechar(namestr, "wine");
440     asmname = NULL;
441     hr = pCreateAssemblyNameObject(&asmname, namestr, CANOF_PARSE_DISPLAY_NAME, NULL);
442     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
443     ok(asmname != NULL, "Expected non-NULL asmname\n");
444 
445     asmenum = NULL;
446     hr = pCreateAssemblyEnum(&asmenum, NULL, asmname, ASM_CACHE_GAC, NULL);
447     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
448     ok(asmenum != NULL, "Expected non-NULL asmenum\n");
449 
450     next = NULL;
451     hr = IAssemblyEnum_GetNextAssembly(asmenum, NULL, &next, 0);
452     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
453     ok(next != NULL, "Expected non-NULL next\n");
454 
455     size = MAX_PATH;
456     hr = IAssemblyName_GetDisplayName(next, buf, &size, 0);
457     to_multibyte(disp, buf);
458     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
459     ok(!lstrcmpA(disp, exp[0]),
460        "Expected \"%s\" or \"%s\", got \"%s\"\n", exp[0], exp[1], disp);
461 
462     IAssemblyName_Release(next);
463 
464     next = NULL;
465     hr = IAssemblyEnum_GetNextAssembly(asmenum, NULL, &next, 0);
466     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
467     ok(next != NULL, "Expected non-NULL next\n");
468 
469     size = MAX_PATH;
470     hr = IAssemblyName_GetDisplayName(next, buf, &size, 0);
471     to_multibyte(disp, buf);
472     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
473     ok(!lstrcmpA(disp, exp[1]) ||
474        !lstrcmpA(disp, exp[2]), /* Win98 */
475        "Expected \"%s\" or \"%s\", got \"%s\"\n", exp[1], exp[2], disp);
476 
477     IAssemblyName_Release(next);
478 
479     next = NULL;
480     hr = IAssemblyEnum_GetNextAssembly(asmenum, NULL, &next, 0);
481     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
482     ok(next != NULL, "Expected non-NULL next\n");
483 
484     size = MAX_PATH;
485     hr = IAssemblyName_GetDisplayName(next, buf, &size, 0);
486     to_multibyte(disp, buf);
487     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
488     ok(!lstrcmpA(disp, exp[2]) ||
489        !lstrcmpA(disp, exp[1]), /* Win98 */
490        "Expected \"%s\" or \"%s\", got \"%s\"\n", exp[2], exp[1], disp);
491 
492     IAssemblyName_Release(next);
493 
494     next = (IAssemblyName *)0xdeadbeef;
495     hr = IAssemblyEnum_GetNextAssembly(asmenum, NULL, &next, 0);
496     ok(hr == S_FALSE, "Expected S_FALSE, got %08x\n", hr);
497     ok(next == (IAssemblyName *)0xdeadbeef,
498        "Expected next to be unchanged, got %p\n", next);
499 
500     IAssemblyEnum_Release(asmenum);
501     IAssemblyName_Release(asmname);
502 
503     /* only Version */
504     to_widechar(namestr, "Wine, Version=1.0.1.2");
505     asmname = NULL;
506     hr = pCreateAssemblyNameObject(&asmname, namestr, CANOF_PARSE_DISPLAY_NAME, NULL);
507     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
508     ok(asmname != NULL, "Expected non-NULL asmname\n");
509 
510     asmenum = NULL;
511     hr = pCreateAssemblyEnum(&asmenum, NULL, asmname, ASM_CACHE_GAC, NULL);
512     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
513     ok(asmenum != NULL, "Expected non-NULL asmenum\n");
514 
515     next = NULL;
516     hr = IAssemblyEnum_GetNextAssembly(asmenum, NULL, &next, 0);
517     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
518     ok(next != NULL, "Expected non-NULL next\n");
519 
520     size = MAX_PATH;
521     hr = IAssemblyName_GetDisplayName(next, buf, &size, 0);
522     to_multibyte(disp, buf);
523     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
524     ok(!lstrcmpA(disp, exp[4]) ||
525        !lstrcmpA(disp, exp[5]), /* Win98 */
526        "Expected \"%s\" or \"%s\", got \"%s\"\n", exp[4], exp[5], disp);
527 
528     IAssemblyName_Release(next);
529 
530     next = NULL;
531     hr = IAssemblyEnum_GetNextAssembly(asmenum, NULL, &next, 0);
532     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
533     ok(next != NULL, "Expected non-NULL next\n");
534 
535     size = MAX_PATH;
536     hr = IAssemblyName_GetDisplayName(next, buf, &size, 0);
537     to_multibyte(disp, buf);
538     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
539     ok(!lstrcmpA(disp, exp[5]) ||
540        !lstrcmpA(disp, exp[4]), /* Win98 */
541        "Expected \"%s\" or \"%s\", got \"%s\"\n", exp[5], exp[4], disp);
542 
543     IAssemblyName_Release(next);
544 
545     next = (IAssemblyName *)0xdeadbeef;
546     hr = IAssemblyEnum_GetNextAssembly(asmenum, NULL, &next, 0);
547     ok(hr == S_FALSE, "Expected S_FALSE, got %08x\n", hr);
548     ok(next == (IAssemblyName *)0xdeadbeef,
549        "Expected next to be unchanged, got %p\n", next);
550 
551     IAssemblyEnum_Release(asmenum);
552     IAssemblyName_Release(asmname);
553 
554     /* only PublicKeyToken */
555     to_widechar(namestr, "Wine, PublicKeyToken=16a3fcd171e93a8d");
556     asmname = NULL;
557     hr = pCreateAssemblyNameObject(&asmname, namestr, CANOF_PARSE_DISPLAY_NAME, NULL);
558     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
559     ok(asmname != NULL, "Expected non-NULL asmname\n");
560 
561     asmenum = NULL;
562     hr = pCreateAssemblyEnum(&asmenum, NULL, asmname, ASM_CACHE_GAC, NULL);
563     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
564     ok(asmenum != NULL, "Expected non-NULL asmenum\n");
565 
566     next = NULL;
567     hr = IAssemblyEnum_GetNextAssembly(asmenum, NULL, &next, 0);
568     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
569     ok(next != NULL, "Expected non-NULL next\n");
570 
571     size = MAX_PATH;
572     hr = IAssemblyName_GetDisplayName(next, buf, &size, 0);
573     to_multibyte(disp, buf);
574     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
575     ok(!lstrcmpA(disp, exp[3]), "Expected \"%s\", got \"%s\"\n", exp[3], disp);
576 
577     IAssemblyName_Release(next);
578 
579     next = NULL;
580     hr = IAssemblyEnum_GetNextAssembly(asmenum, NULL, &next, 0);
581     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
582     ok(next != NULL, "Expected non-NULL next\n");
583 
584     size = MAX_PATH;
585     hr = IAssemblyName_GetDisplayName(next, buf, &size, 0);
586     to_multibyte(disp, buf);
587     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
588     ok(!lstrcmpA(disp, exp[5]), "Expected \"%s\", got \"%s\"\n", exp[5], disp);
589 
590     IAssemblyName_Release(next);
591 
592     next = (IAssemblyName *)0xdeadbeef;
593     hr = IAssemblyEnum_GetNextAssembly(asmenum, NULL, &next, 0);
594     ok(hr == S_FALSE, "Expected S_FALSE, got %08x\n", hr);
595     ok(next == (IAssemblyName *)0xdeadbeef,
596        "Expected next to be unchanged, got %p\n", next);
597 
598     IAssemblyEnum_Release(asmenum);
599     IAssemblyName_Release(asmname);
600 
601     /* only Culture */
602     to_widechar(namestr, "wine, Culture=neutral");
603     asmname = NULL;
604     hr = pCreateAssemblyNameObject(&asmname, namestr, CANOF_PARSE_DISPLAY_NAME, NULL);
605     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
606     ok(asmname != NULL, "Expected non-NULL asmname\n");
607 
608     asmenum = NULL;
609     hr = pCreateAssemblyEnum(&asmenum, NULL, asmname, ASM_CACHE_GAC, NULL);
610     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
611     ok(asmenum != NULL, "Expected non-NULL asmenum\n");
612 
613     next = NULL;
614     hr = IAssemblyEnum_GetNextAssembly(asmenum, NULL, &next, 0);
615     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
616     ok(next != NULL, "Expected non-NULL next\n");
617 
618     size = MAX_PATH;
619     hr = IAssemblyName_GetDisplayName(next, buf, &size, 0);
620     to_multibyte(disp, buf);
621     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
622     ok(!lstrcmpA(disp, exp[0]), "Expected \"%s\", got \"%s\"\n", exp[0], disp);
623 
624     IAssemblyName_Release(next);
625 
626     next = NULL;
627     hr = IAssemblyEnum_GetNextAssembly(asmenum, NULL, &next, 0);
628     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
629     ok(next != NULL, "Expected non-NULL next\n");
630 
631     size = MAX_PATH;
632     hr = IAssemblyName_GetDisplayName(next, buf, &size, 0);
633     to_multibyte(disp, buf);
634     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
635     ok(!lstrcmpA(disp, exp[1]) ||
636        !lstrcmpA(disp, exp[2]), /* Win98 */
637        "Expected \"%s\" or \"%s\", got \"%s\"\n", exp[1], exp[2], disp);
638 
639     IAssemblyName_Release(next);
640 
641     next = NULL;
642     hr = IAssemblyEnum_GetNextAssembly(asmenum, NULL, &next, 0);
643     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
644     ok(next != NULL, "Expected non-NULL next\n");
645 
646     size = MAX_PATH;
647     hr = IAssemblyName_GetDisplayName(next, buf, &size, 0);
648     to_multibyte(disp, buf);
649     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
650     ok(!lstrcmpA(disp, exp[2]) ||
651        !lstrcmpA(disp, exp[1]), /* Win98 */
652        "Expected \"%s\" or \"%s\", got \"%s\"\n", exp[2], exp[1], disp);
653 
654     IAssemblyName_Release(next);
655 
656     next = (IAssemblyName *)0xdeadbeef;
657     hr = IAssemblyEnum_GetNextAssembly(asmenum, NULL, &next, 0);
658     ok(hr == S_FALSE, "Expected S_FALSE, got %08x\n", hr);
659     ok(next == (IAssemblyName *)0xdeadbeef,
660        "Expected next to be unchanged, got %p\n", next);
661 
662     IAssemblyEnum_Release(asmenum);
663     IAssemblyName_Release(asmname);
664 
665 done:
666     sprintf(path, "%s\\Wine\\1.0.0.0__16a3fcd171e93a8d\\Wine.dll", gac);
667     DeleteFileA(path);
668     sprintf(path, "%s\\Wine\\1.0.1.2__16a3fcd171e93a8d\\Wine.dll", gac);
669     DeleteFileA(path);
670     sprintf(path, "%s\\Wine\\1.0.1.2__123456789abcdef0\\Wine.dll", gac);
671     DeleteFileA(path);
672     sprintf(path, "%s\\Wine\\1.0.0.0__16a3fcd171e93a8d", gac);
673     RemoveDirectoryA(path);
674     sprintf(path, "%s\\Wine\\1.0.1.2__16a3fcd171e93a8d", gac);
675     RemoveDirectoryA(path);
676     sprintf(path, "%s\\Wine\\1.0.1.2__123456789abcdef0", gac);
677     RemoveDirectoryA(path);
678     sprintf(path, "%s\\Wine", gac);
679     RemoveDirectoryA(path);
680 }
681 
682 START_TEST(asmenum)
683 {
684     if (!init_functionpointers())
685         return;
686 
687     test_CreateAssemblyEnum();
688     test_enumerate();
689     test_enumerate_name();
690 }
691