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