msvcp60: Added missing 64-bit exports.
[wine] / dlls / fusion / tests / asmenum.c
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)(LPASSEMBLYNAME *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     LPSTR data;
221 } ASMNAME;
222
223 static BOOL enum_gac_assemblies(struct list *assemblies, int depth, LPSTR path)
224 {
225     WIN32_FIND_DATAA ffd;
226     CHAR buf[MAX_PATH];
227     CHAR disp[MAX_PATH];
228     ASMNAME *name;
229     HANDLE hfind;
230     LPSTR ptr;
231
232     static CHAR parent[MAX_PATH];
233
234     sprintf(buf, "%s\\*", path);
235     hfind = FindFirstFileA(buf, &ffd);
236     if (hfind == INVALID_HANDLE_VALUE)
237         return FALSE;
238
239     do
240     {
241         if (!lstrcmpA(ffd.cFileName, ".") || !lstrcmpA(ffd.cFileName, ".."))
242             continue;
243
244         if (depth == 0)
245         {
246             lstrcpyA(parent, ffd.cFileName);
247         }
248         else if (depth == 1)
249         {
250             char culture[MAX_PATH];
251             char dll[MAX_PATH], exe[MAX_PATH];
252
253             /* Directories with no dll or exe will not be enumerated */
254             sprintf(dll, "%s\\%s\\%s.dll", path, ffd.cFileName, parent);
255             sprintf(exe, "%s\\%s\\%s.exe", path, ffd.cFileName, parent);
256             if (GetFileAttributesA(dll) == INVALID_FILE_ATTRIBUTES &&
257                 GetFileAttributesA(exe) == INVALID_FILE_ATTRIBUTES)
258                 continue;
259
260             ptr = strstr(ffd.cFileName, "_");
261             *ptr = '\0';
262             ptr++;
263
264             if (*ptr != '_')
265             {
266                 lstrcpyA(culture, ptr);
267                 *strstr(culture, "_") = '\0';
268             }
269             else
270                 lstrcpyA(culture, "neutral");
271
272             ptr = strchr(ptr, '_');
273             ptr++;
274             sprintf(buf, ", Version=%s, Culture=%s, PublicKeyToken=%s",
275                     ffd.cFileName, culture, ptr);
276             lstrcpyA(disp, parent);
277             lstrcatA(disp, buf);
278
279             name = HeapAlloc(GetProcessHeap(), 0, sizeof(ASMNAME));
280             name->data = HeapAlloc(GetProcessHeap(), 0, lstrlenA(disp) + 1);
281             lstrcpyA(name->data, disp);
282             list_add_tail(assemblies, &name->entry);
283
284             continue;
285         }
286
287         sprintf(buf, "%s\\%s", path, ffd.cFileName);
288         enum_gac_assemblies(assemblies, depth + 1, buf);
289     } while (FindNextFileA(hfind, &ffd) != 0);
290
291     FindClose(hfind);
292     return TRUE;
293 }
294
295 static void test_enumerate(void)
296 {
297     struct list assemblies = LIST_INIT(assemblies);
298     struct list *item, *cursor;
299     IAssemblyEnum *asmenum;
300     IAssemblyName *next;
301     WCHAR buf[MAX_PATH];
302     CHAR path[MAX_PATH];
303     CHAR disp[MAX_PATH];
304     HRESULT hr;
305     BOOL found;
306     DWORD size;
307
308     size = MAX_PATH;
309     hr = pGetCachePath(ASM_CACHE_GAC, buf, &size);
310     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
311
312     to_multibyte(path, buf);
313     lstrcatA(path, "_32");
314     enum_gac_assemblies(&assemblies, 0, path);
315
316     to_multibyte(path, buf);
317     lstrcatA(path, "_64");
318     enum_gac_assemblies(&assemblies, 0, path);
319
320     to_multibyte(path, buf);
321     lstrcatA(path, "_MSIL");
322     enum_gac_assemblies(&assemblies, 0, path);
323
324     to_multibyte(path, buf);
325     enum_gac_assemblies(&assemblies, 0, path);
326
327     asmenum = NULL;
328     hr = pCreateAssemblyEnum(&asmenum, NULL, NULL, ASM_CACHE_GAC, NULL);
329     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
330     ok(asmenum != NULL, "Expected non-NULL asmenum\n");
331
332     while (IAssemblyEnum_GetNextAssembly(asmenum, NULL, &next, 0) == S_OK)
333     {
334         size = MAX_PATH;
335         IAssemblyName_GetDisplayName(next, buf, &size, 0);
336         to_multibyte(disp, buf);
337
338         found = FALSE;
339         LIST_FOR_EACH_SAFE(item, cursor, &assemblies)
340         {
341             ASMNAME *asmname = LIST_ENTRY(item, ASMNAME, entry);
342
343             if (!lstrcmpA(asmname->data, disp))
344             {
345                 found = TRUE;
346
347                 list_remove(&asmname->entry);
348                 HeapFree(GetProcessHeap(), 0, asmname->data);
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->data);
373         HeapFree(GetProcessHeap(), 0, asmname);
374     }
375
376     IAssemblyEnum_Release(asmenum);
377 }
378
379 static void test_enumerate_name(void)
380 {
381     IAssemblyEnum *asmenum;
382     IAssemblyName *asmname, *next;
383     WCHAR buf[MAX_PATH];
384     CHAR gac[MAX_PATH];
385     CHAR path[MAX_PATH];
386     CHAR disp[MAX_PATH];
387     WCHAR namestr[MAX_PATH];
388     CHAR exp[6][MAX_PATH];
389     HRESULT hr;
390     DWORD size;
391
392     lstrcpyA(exp[0], "wine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=16a3fcd171e93a8d");
393     lstrcpyA(exp[1], "wine, Version=1.0.1.2, Culture=neutral, PublicKeyToken=123456789abcdef0");
394     lstrcpyA(exp[2], "wine, Version=1.0.1.2, Culture=neutral, PublicKeyToken=16a3fcd171e93a8d");
395     lstrcpyA(exp[3], "Wine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=16a3fcd171e93a8d");
396     lstrcpyA(exp[4], "Wine, Version=1.0.1.2, Culture=neutral, PublicKeyToken=123456789abcdef0");
397     lstrcpyA(exp[5], "Wine, Version=1.0.1.2, Culture=neutral, PublicKeyToken=16a3fcd171e93a8d");
398
399     size = MAX_PATH;
400     hr = pGetCachePath(ASM_CACHE_GAC, buf, &size);
401     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
402
403     to_multibyte(gac, buf);
404     create_full_path(gac);
405
406     sprintf(path, "%s\\Wine", gac);
407     CreateDirectoryA(path, NULL);
408
409     sprintf(path, "%s\\Wine\\1.0.0.0__16a3fcd171e93a8d", gac);
410     CreateDirectoryA(path, NULL);
411
412     lstrcatA(path, "\\Wine.dll");
413     if (!create_file_data(path, path, 100))
414     {
415         win_skip("Failed to open file %s, skipping name enumeration tests\n", path);
416         goto done;
417     }
418
419     sprintf(path, "%s\\Wine\\1.0.1.2__16a3fcd171e93a8d", gac);
420     CreateDirectoryA(path, NULL);
421
422     lstrcatA(path, "\\Wine.dll");
423     if (!create_file_data(path, path, 100))
424     {
425         win_skip("Failed to open file %s, skipping name enumeration tests\n", path);
426         goto done;
427     }
428
429     sprintf(path, "%s\\Wine\\1.0.1.2__123456789abcdef0", gac);
430     CreateDirectoryA(path, NULL);
431
432     lstrcatA(path, "\\Wine.dll");
433     if (!create_file_data(path, path, 100))
434     {
435         win_skip("Failed to open file %s, skipping name enumeration tests\n", path);
436         goto done;
437     }
438
439     /* test case sensitivity */
440     to_widechar(namestr, "wine");
441     asmname = NULL;
442     hr = pCreateAssemblyNameObject(&asmname, namestr, CANOF_PARSE_DISPLAY_NAME, NULL);
443     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
444     ok(asmname != NULL, "Expected non-NULL asmname\n");
445
446     asmenum = NULL;
447     hr = pCreateAssemblyEnum(&asmenum, NULL, asmname, ASM_CACHE_GAC, NULL);
448     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
449     ok(asmenum != NULL, "Expected non-NULL asmenum\n");
450
451     next = NULL;
452     hr = IAssemblyEnum_GetNextAssembly(asmenum, NULL, &next, 0);
453     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
454     ok(next != NULL, "Expected non-NULL next\n");
455
456     size = MAX_PATH;
457     hr = IAssemblyName_GetDisplayName(next, buf, &size, 0);
458     to_multibyte(disp, buf);
459     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
460     ok(!lstrcmpA(disp, exp[0]),
461        "Expected \"%s\" or \"%s\", got \"%s\"\n", exp[0], exp[1], disp);
462
463     IAssemblyName_Release(next);
464
465     next = NULL;
466     hr = IAssemblyEnum_GetNextAssembly(asmenum, NULL, &next, 0);
467     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
468     ok(next != NULL, "Expected non-NULL next\n");
469
470     size = MAX_PATH;
471     hr = IAssemblyName_GetDisplayName(next, buf, &size, 0);
472     to_multibyte(disp, buf);
473     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
474     ok(!lstrcmpA(disp, exp[1]) ||
475        !lstrcmpA(disp, exp[2]), /* Win98 */
476        "Expected \"%s\" or \"%s\", got \"%s\"\n", exp[1], exp[2], disp);
477
478     IAssemblyName_Release(next);
479
480     next = NULL;
481     hr = IAssemblyEnum_GetNextAssembly(asmenum, NULL, &next, 0);
482     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
483     ok(next != NULL, "Expected non-NULL next\n");
484
485     size = MAX_PATH;
486     hr = IAssemblyName_GetDisplayName(next, buf, &size, 0);
487     to_multibyte(disp, buf);
488     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
489     ok(!lstrcmpA(disp, exp[2]) ||
490        !lstrcmpA(disp, exp[1]), /* Win98 */
491        "Expected \"%s\" or \"%s\", got \"%s\"\n", exp[2], exp[1], disp);
492
493     IAssemblyName_Release(next);
494
495     next = (IAssemblyName *)0xdeadbeef;
496     hr = IAssemblyEnum_GetNextAssembly(asmenum, NULL, &next, 0);
497     ok(hr == S_FALSE, "Expected S_FALSE, got %08x\n", hr);
498     ok(next == (IAssemblyName *)0xdeadbeef,
499        "Expected next to be unchanged, got %p\n", next);
500
501     IAssemblyEnum_Release(asmenum);
502     IAssemblyName_Release(asmname);
503
504     /* only Version */
505     to_widechar(namestr, "Wine, Version=1.0.1.2");
506     asmname = NULL;
507     hr = pCreateAssemblyNameObject(&asmname, namestr, CANOF_PARSE_DISPLAY_NAME, NULL);
508     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
509     ok(asmname != NULL, "Expected non-NULL asmname\n");
510
511     asmenum = NULL;
512     hr = pCreateAssemblyEnum(&asmenum, NULL, asmname, ASM_CACHE_GAC, NULL);
513     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
514     ok(asmenum != NULL, "Expected non-NULL asmenum\n");
515
516     next = NULL;
517     hr = IAssemblyEnum_GetNextAssembly(asmenum, NULL, &next, 0);
518     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
519     ok(next != NULL, "Expected non-NULL next\n");
520
521     size = MAX_PATH;
522     hr = IAssemblyName_GetDisplayName(next, buf, &size, 0);
523     to_multibyte(disp, buf);
524     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
525     ok(!lstrcmpA(disp, exp[4]) ||
526        !lstrcmpA(disp, exp[5]), /* Win98 */
527        "Expected \"%s\" or \"%s\", got \"%s\"\n", exp[4], exp[5], disp);
528
529     IAssemblyName_Release(next);
530
531     next = NULL;
532     hr = IAssemblyEnum_GetNextAssembly(asmenum, NULL, &next, 0);
533     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
534     ok(next != NULL, "Expected non-NULL next\n");
535
536     size = MAX_PATH;
537     hr = IAssemblyName_GetDisplayName(next, buf, &size, 0);
538     to_multibyte(disp, buf);
539     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
540     ok(!lstrcmpA(disp, exp[5]) ||
541        !lstrcmpA(disp, exp[4]), /* Win98 */
542        "Expected \"%s\" or \"%s\", got \"%s\"\n", exp[5], exp[4], disp);
543
544     IAssemblyName_Release(next);
545
546     next = (IAssemblyName *)0xdeadbeef;
547     hr = IAssemblyEnum_GetNextAssembly(asmenum, NULL, &next, 0);
548     ok(hr == S_FALSE, "Expected S_FALSE, got %08x\n", hr);
549     ok(next == (IAssemblyName *)0xdeadbeef,
550        "Expected next to be unchanged, got %p\n", next);
551
552     IAssemblyEnum_Release(asmenum);
553     IAssemblyName_Release(asmname);
554
555     /* only PublicKeyToken */
556     to_widechar(namestr, "Wine, PublicKeyToken=16a3fcd171e93a8d");
557     asmname = NULL;
558     hr = pCreateAssemblyNameObject(&asmname, namestr, CANOF_PARSE_DISPLAY_NAME, NULL);
559     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
560     ok(asmname != NULL, "Expected non-NULL asmname\n");
561
562     asmenum = NULL;
563     hr = pCreateAssemblyEnum(&asmenum, NULL, asmname, ASM_CACHE_GAC, NULL);
564     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
565     ok(asmenum != NULL, "Expected non-NULL asmenum\n");
566
567     next = NULL;
568     hr = IAssemblyEnum_GetNextAssembly(asmenum, NULL, &next, 0);
569     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
570     ok(next != NULL, "Expected non-NULL next\n");
571
572     size = MAX_PATH;
573     hr = IAssemblyName_GetDisplayName(next, buf, &size, 0);
574     to_multibyte(disp, buf);
575     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
576     ok(!lstrcmpA(disp, exp[3]), "Expected \"%s\", got \"%s\"\n", exp[3], disp);
577
578     IAssemblyName_Release(next);
579
580     next = NULL;
581     hr = IAssemblyEnum_GetNextAssembly(asmenum, NULL, &next, 0);
582     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
583     ok(next != NULL, "Expected non-NULL next\n");
584
585     size = MAX_PATH;
586     hr = IAssemblyName_GetDisplayName(next, buf, &size, 0);
587     to_multibyte(disp, buf);
588     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
589     ok(!lstrcmpA(disp, exp[5]), "Expected \"%s\", got \"%s\"\n", exp[5], disp);
590
591     IAssemblyName_Release(next);
592
593     next = (IAssemblyName *)0xdeadbeef;
594     hr = IAssemblyEnum_GetNextAssembly(asmenum, NULL, &next, 0);
595     ok(hr == S_FALSE, "Expected S_FALSE, got %08x\n", hr);
596     ok(next == (IAssemblyName *)0xdeadbeef,
597        "Expected next to be unchanged, got %p\n", next);
598
599     IAssemblyEnum_Release(asmenum);
600     IAssemblyName_Release(asmname);
601
602     /* only Culture */
603     to_widechar(namestr, "wine, Culture=neutral");
604     asmname = NULL;
605     hr = pCreateAssemblyNameObject(&asmname, namestr, CANOF_PARSE_DISPLAY_NAME, NULL);
606     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
607     ok(asmname != NULL, "Expected non-NULL asmname\n");
608
609     asmenum = NULL;
610     hr = pCreateAssemblyEnum(&asmenum, NULL, asmname, ASM_CACHE_GAC, NULL);
611     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
612     ok(asmenum != NULL, "Expected non-NULL asmenum\n");
613
614     next = NULL;
615     hr = IAssemblyEnum_GetNextAssembly(asmenum, NULL, &next, 0);
616     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
617     ok(next != NULL, "Expected non-NULL next\n");
618
619     size = MAX_PATH;
620     hr = IAssemblyName_GetDisplayName(next, buf, &size, 0);
621     to_multibyte(disp, buf);
622     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
623     ok(!lstrcmpA(disp, exp[0]), "Expected \"%s\", got \"%s\"\n", exp[0], disp);
624
625     IAssemblyName_Release(next);
626
627     next = NULL;
628     hr = IAssemblyEnum_GetNextAssembly(asmenum, NULL, &next, 0);
629     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
630     ok(next != NULL, "Expected non-NULL next\n");
631
632     size = MAX_PATH;
633     hr = IAssemblyName_GetDisplayName(next, buf, &size, 0);
634     to_multibyte(disp, buf);
635     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
636     ok(!lstrcmpA(disp, exp[1]) ||
637        !lstrcmpA(disp, exp[2]), /* Win98 */
638        "Expected \"%s\" or \"%s\", got \"%s\"\n", exp[1], exp[2], disp);
639
640     IAssemblyName_Release(next);
641
642     next = NULL;
643     hr = IAssemblyEnum_GetNextAssembly(asmenum, NULL, &next, 0);
644     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
645     ok(next != NULL, "Expected non-NULL next\n");
646
647     size = MAX_PATH;
648     hr = IAssemblyName_GetDisplayName(next, buf, &size, 0);
649     to_multibyte(disp, buf);
650     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
651     ok(!lstrcmpA(disp, exp[2]) ||
652        !lstrcmpA(disp, exp[1]), /* Win98 */
653        "Expected \"%s\" or \"%s\", got \"%s\"\n", exp[2], exp[1], disp);
654
655     IAssemblyName_Release(next);
656
657     next = (IAssemblyName *)0xdeadbeef;
658     hr = IAssemblyEnum_GetNextAssembly(asmenum, NULL, &next, 0);
659     ok(hr == S_FALSE, "Expected S_FALSE, got %08x\n", hr);
660     ok(next == (IAssemblyName *)0xdeadbeef,
661        "Expected next to be unchanged, got %p\n", next);
662
663     IAssemblyEnum_Release(asmenum);
664     IAssemblyName_Release(asmname);
665
666 done:
667     sprintf(path, "%s\\Wine\\1.0.0.0__16a3fcd171e93a8d\\Wine.dll", gac);
668     DeleteFileA(path);
669     sprintf(path, "%s\\Wine\\1.0.1.2__16a3fcd171e93a8d\\Wine.dll", gac);
670     DeleteFileA(path);
671     sprintf(path, "%s\\Wine\\1.0.1.2__123456789abcdef0\\Wine.dll", gac);
672     DeleteFileA(path);
673     sprintf(path, "%s\\Wine\\1.0.0.0__16a3fcd171e93a8d", gac);
674     RemoveDirectoryA(path);
675     sprintf(path, "%s\\Wine\\1.0.1.2__16a3fcd171e93a8d", gac);
676     RemoveDirectoryA(path);
677     sprintf(path, "%s\\Wine\\1.0.1.2__123456789abcdef0", gac);
678     RemoveDirectoryA(path);
679     sprintf(path, "%s\\Wine", gac);
680     RemoveDirectoryA(path);
681 }
682
683 START_TEST(asmenum)
684 {
685     if (!init_functionpointers())
686         return;
687
688     test_CreateAssemblyEnum();
689     test_enumerate();
690     test_enumerate_name();
691 }