diff --git a/dlls/fusion/tests/Makefile.in b/dlls/fusion/tests/Makefile.in index 6919379f9c9..5dd9febd7f7 100644 --- a/dlls/fusion/tests/Makefile.in +++ b/dlls/fusion/tests/Makefile.in @@ -7,6 +7,7 @@ IMPORTS = user32 kernel32 CTESTS = \ asmcache.c \ + asmenum.c \ asmname.c \ fusion.c diff --git a/dlls/fusion/tests/asmenum.c b/dlls/fusion/tests/asmenum.c new file mode 100644 index 00000000000..0349eb36e2f --- /dev/null +++ b/dlls/fusion/tests/asmenum.c @@ -0,0 +1,640 @@ +/* + * Copyright 2008 James Hawkins + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#define COBJMACROS + +#include + +#include +#include +#include +#include +#include + +#include "wine/test.h" +#include "wine/list.h" + +static HRESULT (WINAPI *pCreateAssemblyEnum)(IAssemblyEnum **pEnum, + IUnknown *pUnkReserved, + IAssemblyName *pName, + DWORD dwFlags, LPVOID pvReserved); +static HRESULT (WINAPI *pCreateAssemblyNameObject)(LPASSEMBLYNAME *ppAssemblyNameObj, + LPCWSTR szAssemblyName, DWORD dwFlags, + LPVOID pvReserved); +static HRESULT (WINAPI *pGetCachePath)(ASM_CACHE_FLAGS dwCacheFlags, + LPWSTR pwzCachePath, PDWORD pcchPath); +static HRESULT (WINAPI *pLoadLibraryShim)(LPCWSTR szDllName, LPCWSTR szVersion, + LPVOID pvReserved, HMODULE *phModDll); + +static BOOL init_functionpointers(void) +{ + HRESULT hr; + HMODULE hfusion; + HMODULE hmscoree; + + static const WCHAR szFusion[] = {'f','u','s','i','o','n','.','d','l','l',0}; + + hmscoree = LoadLibraryA("mscoree.dll"); + if (!hmscoree) + { + skip("mscoree.dll not available\n"); + return FALSE; + } + + pLoadLibraryShim = (void *)GetProcAddress(hmscoree, "LoadLibraryShim"); + if (!pLoadLibraryShim) + { + skip("LoadLibraryShim not available\n"); + FreeLibrary(hmscoree); + return FALSE; + } + + hr = pLoadLibraryShim(szFusion, NULL, NULL, &hfusion); + if (FAILED(hr)) + { + skip("fusion.dll not available\n"); + FreeLibrary(hmscoree); + return FALSE; + } + + pCreateAssemblyEnum = (void *)GetProcAddress(hfusion, "CreateAssemblyEnum"); + pCreateAssemblyNameObject = (void *)GetProcAddress(hfusion, "CreateAssemblyNameObject"); + pGetCachePath = (void *)GetProcAddress(hfusion, "GetCachePath"); + + if (!pCreateAssemblyEnum || + !pCreateAssemblyNameObject || !pGetCachePath) + { + skip("fusion.dll not implemented\n"); + return FALSE; + } + + FreeLibrary(hmscoree); + return TRUE; +} + +static inline void to_widechar(LPWSTR dest, LPCSTR src) +{ + MultiByteToWideChar(CP_ACP, 0, src, -1, dest, MAX_PATH); +} + +static inline void to_multibyte(LPSTR dest, LPWSTR src) +{ + WideCharToMultiByte(CP_ACP, 0, src, -1, dest, MAX_PATH, NULL, NULL); +} + +static BOOL create_full_path(LPCSTR path) +{ + LPSTR new_path; + BOOL ret = TRUE; + int len; + + new_path = HeapAlloc(GetProcessHeap(), 0, lstrlenA(path) + 1); + if (!new_path) + return FALSE; + + lstrcpyA(new_path, path); + + while ((len = lstrlenA(new_path)) && new_path[len - 1] == '\\') + new_path[len - 1] = 0; + + while (!CreateDirectoryA(new_path, NULL)) + { + LPSTR slash; + DWORD last_error = GetLastError(); + + if(last_error == ERROR_ALREADY_EXISTS) + break; + + if(last_error != ERROR_PATH_NOT_FOUND) + { + ret = FALSE; + break; + } + + if(!(slash = strrchr(new_path, '\\'))) + { + ret = FALSE; + break; + } + + len = slash - new_path; + new_path[len] = 0; + if(!create_full_path(new_path)) + { + ret = FALSE; + break; + } + + new_path[len] = '\\'; + } + + HeapFree(GetProcessHeap(), 0, new_path); + return ret; +} + +static void create_file_data(LPCSTR name, LPCSTR data, DWORD size) +{ + HANDLE file; + DWORD written; + + file = CreateFileA(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL); + ok(file != INVALID_HANDLE_VALUE, "Failure to open file %s\n", name); + WriteFile(file, data, strlen(data), &written, NULL); + + if (size) + { + SetFilePointer(file, size, NULL, FILE_BEGIN); + SetEndOfFile(file); + } + + CloseHandle(file); +} + +#define create_file(name, size) create_file_data(name, name, size) + +static void test_CreateAssemblyEnum(void) +{ + HRESULT hr; + WCHAR namestr[MAX_PATH]; + IAssemblyEnum *asmenum; + IAssemblyName *asmname; + + to_widechar(namestr, "wine"); + asmname = NULL; + hr = pCreateAssemblyNameObject(&asmname, namestr, CANOF_PARSE_DISPLAY_NAME, NULL); + ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); + ok(asmname != NULL, "Expected non-NULL asmname\n"); + + /* pEnum is NULL */ + hr = pCreateAssemblyEnum(NULL, NULL, asmname, ASM_CACHE_GAC, NULL); + ok(hr == E_INVALIDARG, "Expected E_INVALIDARG, got %08x\n", hr); + + /* pName is NULL */ + asmenum = NULL; + hr = pCreateAssemblyEnum(&asmenum, NULL, NULL, ASM_CACHE_GAC, NULL); + ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); + ok(asmenum != NULL, "Expected non-NULL asmenum\n"); + + IAssemblyEnum_Release(asmenum); + + /* dwFlags is ASM_CACHE_ROOT */ + asmenum = (IAssemblyEnum *)0xdeadbeef; + hr = pCreateAssemblyEnum(&asmenum, NULL, NULL, ASM_CACHE_ROOT, NULL); + ok(hr == E_INVALIDARG, "Expected E_INVALIDARG, got %08x\n", hr); + ok(asmenum == (IAssemblyEnum *)0xdeadbeef, + "Expected asmenum to be unchanged, got %p\n", asmenum); + + /* invalid dwFlags */ + asmenum = (IAssemblyEnum *)0xdeadbeef; + hr = pCreateAssemblyEnum(&asmenum, NULL, NULL, 0, NULL); + ok(hr == E_INVALIDARG, "Expected E_INVALIDARG, got %08x\n", hr); + ok(asmenum == (IAssemblyEnum *)0xdeadbeef, + "Expected asmenum to be unchanged, got %p\n", asmenum); +} + +typedef struct _tagASMNAME +{ + struct list entry; + LPSTR data; +} ASMNAME; + +static BOOL enum_gac_assemblies(struct list *assemblies, int depth, LPSTR path) +{ + WIN32_FIND_DATAA ffd; + CHAR buf[MAX_PATH]; + CHAR disp[MAX_PATH]; + ASMNAME *name; + HANDLE hfind; + LPSTR ptr; + + static CHAR parent[MAX_PATH]; + + sprintf(buf, "%s\\*", path); + hfind = FindFirstFileA(buf, &ffd); + if (hfind == INVALID_HANDLE_VALUE) + return FALSE; + + do + { + if (!lstrcmpA(ffd.cFileName, ".") || !lstrcmpA(ffd.cFileName, "..")) + continue; + + if (depth == 0) + { + sprintf(parent, "%s, ", ffd.cFileName); + } + else if (depth == 1) + { + ptr = strstr(ffd.cFileName, "__"); + *ptr = '\0'; + ptr += 2; + sprintf(buf, "Version=%s, Culture=neutral, PublicKeyToken=%s", + ffd.cFileName, ptr); + lstrcpyA(disp, parent); + lstrcatA(disp, buf); + + name = HeapAlloc(GetProcessHeap(), 0, sizeof(ASMNAME)); + name->data = strdup(disp); + list_add_tail(assemblies, &name->entry); + + continue; + } + + sprintf(buf, "%s\\%s", path, ffd.cFileName); + enum_gac_assemblies(assemblies, depth + 1, buf); + } while (FindNextFileA(hfind, &ffd) != 0); + + FindClose(hfind); + return TRUE; +} + +static void test_enumerate(void) +{ + struct list assemblies = LIST_INIT(assemblies); + struct list *item, *cursor; + IAssemblyEnum *asmenum; + IAssemblyName *next; + WCHAR buf[MAX_PATH]; + CHAR path[MAX_PATH]; + CHAR disp[MAX_PATH]; + HRESULT hr; + BOOL found; + DWORD size; + + size = MAX_PATH; + hr = pGetCachePath(ASM_CACHE_GAC, buf, &size); + ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); + + to_multibyte(path, buf); + lstrcatA(path, "_32"); + enum_gac_assemblies(&assemblies, 0, path); + + to_multibyte(path, buf); + lstrcatA(path, "_MSIL"); + enum_gac_assemblies(&assemblies, 0, path); + + to_multibyte(path, buf); + enum_gac_assemblies(&assemblies, 0, path); + + asmenum = NULL; + hr = pCreateAssemblyEnum(&asmenum, NULL, NULL, ASM_CACHE_GAC, NULL); + ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); + ok(asmenum != NULL, "Expected non-NULL asmenum\n"); + + while (IAssemblyEnum_GetNextAssembly(asmenum, NULL, &next, 0) == S_OK) + { + size = MAX_PATH; + IAssemblyName_GetDisplayName(next, buf, &size, 0); + to_multibyte(disp, buf); + + found = FALSE; + LIST_FOR_EACH_SAFE(item, cursor, &assemblies) + { + ASMNAME *asmname = LIST_ENTRY(item, ASMNAME, entry); + + if (!lstrcmpA(asmname->data, disp)) + { + found = TRUE; + + list_remove(&asmname->entry); + HeapFree(GetProcessHeap(), 0, asmname->data); + HeapFree(GetProcessHeap(), 0, asmname); + break; + } + } + + ok(found, "Extra assembly enumerated: %s\n", disp); + IAssemblyName_Release(next); + } + + /* enumeration is exhausted */ + next = (IAssemblyName *)0xdeadbeef; + hr = IAssemblyEnum_GetNextAssembly(asmenum, NULL, &next, 0); + ok(hr == S_FALSE, "Expected S_FALSE, got %08x\n", hr); + ok(next == (IAssemblyName *)0xdeadbeef, + "Expected next to be unchanged, got %p\n", next); + + LIST_FOR_EACH_SAFE(item, cursor, &assemblies) + { + ASMNAME *asmname = LIST_ENTRY(item, ASMNAME, entry); + + ok(FALSE, "Assembly not enumerated: %s\n", asmname->data); + + list_remove(&asmname->entry); + HeapFree(GetProcessHeap(), 0, asmname->data); + HeapFree(GetProcessHeap(), 0, asmname); + } + + IAssemblyEnum_Release(asmenum); +} + +static void test_enumerate_name(void) +{ + IAssemblyEnum *asmenum; + IAssemblyName *asmname, *next; + WCHAR buf[MAX_PATH]; + CHAR gac[MAX_PATH]; + CHAR path[MAX_PATH]; + CHAR disp[MAX_PATH]; + WCHAR namestr[MAX_PATH]; + CHAR exp[6][MAX_PATH]; + HRESULT hr; + DWORD size; + + lstrcpyA(exp[0], "wine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=16a3fcd171e93a8d"); + lstrcpyA(exp[1], "wine, Version=1.0.1.2, Culture=neutral, PublicKeyToken=123456789abcdef0"); + lstrcpyA(exp[2], "wine, Version=1.0.1.2, Culture=neutral, PublicKeyToken=16a3fcd171e93a8d"); + lstrcpyA(exp[3], "Wine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=16a3fcd171e93a8d"); + lstrcpyA(exp[4], "Wine, Version=1.0.1.2, Culture=neutral, PublicKeyToken=123456789abcdef0"); + lstrcpyA(exp[5], "Wine, Version=1.0.1.2, Culture=neutral, PublicKeyToken=16a3fcd171e93a8d"); + + size = MAX_PATH; + hr = pGetCachePath(ASM_CACHE_GAC, buf, &size); + ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); + + to_multibyte(gac, buf); + create_full_path(gac); + + sprintf(path, "%s\\Wine", gac); + CreateDirectoryA(path, NULL); + + sprintf(path, "%s\\Wine\\1.0.0.0__16a3fcd171e93a8d", gac); + CreateDirectoryA(path, NULL); + + lstrcatA(path, "\\Wine.dll"); + create_file(path, 100); + + sprintf(path, "%s\\Wine\\1.0.1.2__16a3fcd171e93a8d", gac); + CreateDirectoryA(path, NULL); + + lstrcatA(path, "\\Wine.dll"); + create_file(path, 100); + + sprintf(path, "%s\\Wine\\1.0.1.2__123456789abcdef0", gac); + CreateDirectoryA(path, NULL); + + lstrcatA(path, "\\Wine.dll"); + create_file(path, 100); + + /* test case sensitivity */ + to_widechar(namestr, "wine"); + asmname = NULL; + hr = pCreateAssemblyNameObject(&asmname, namestr, CANOF_PARSE_DISPLAY_NAME, NULL); + ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); + ok(asmname != NULL, "Expected non-NULL asmname\n"); + + asmenum = NULL; + hr = pCreateAssemblyEnum(&asmenum, NULL, asmname, ASM_CACHE_GAC, NULL); + ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); + ok(asmenum != NULL, "Expected non-NULL asmenum\n"); + + next = NULL; + hr = IAssemblyEnum_GetNextAssembly(asmenum, NULL, &next, 0); + ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); + ok(next != NULL, "Expected non-NULL next\n"); + + size = MAX_PATH; + hr = IAssemblyName_GetDisplayName(next, buf, &size, 0); + to_multibyte(disp, buf); + ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); + ok(!lstrcmpA(disp, exp[0]) || + !lstrcmpA(disp, exp[1]), + "Expected \"%s\" or \"%s\", got \"%s\"\n", exp[0], exp[1], disp); + + IAssemblyName_Release(next); + + next = NULL; + hr = IAssemblyEnum_GetNextAssembly(asmenum, NULL, &next, 0); + ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); + ok(next != NULL, "Expected non-NULL next\n"); + + size = MAX_PATH; + hr = IAssemblyName_GetDisplayName(next, buf, &size, 0); + to_multibyte(disp, buf); + ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); + ok(!lstrcmpA(disp, exp[1]) || + !lstrcmpA(disp, exp[2]), + "Expected \"%s\" or \"%s\", got \"%s\"\n", exp[1], exp[2], disp); + + IAssemblyName_Release(next); + + next = NULL; + hr = IAssemblyEnum_GetNextAssembly(asmenum, NULL, &next, 0); + ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); + ok(next != NULL, "Expected non-NULL next\n"); + + size = MAX_PATH; + hr = IAssemblyName_GetDisplayName(next, buf, &size, 0); + to_multibyte(disp, buf); + ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); + ok(!lstrcmpA(disp, exp[2]), "Expected \"%s\", got \"%s\"\n", exp[2], disp); + + IAssemblyName_Release(next); + + next = (IAssemblyName *)0xdeadbeef; + hr = IAssemblyEnum_GetNextAssembly(asmenum, NULL, &next, 0); + ok(hr == S_FALSE, "Expected S_FALSE, got %08x\n", hr); + ok(next == (IAssemblyName *)0xdeadbeef, + "Expected next to be unchanged, got %p\n", next); + + IAssemblyEnum_Release(asmenum); + IAssemblyName_Release(asmname); + + /* only Version */ + to_widechar(namestr, "Wine, Version=1.0.1.2"); + asmname = NULL; + hr = pCreateAssemblyNameObject(&asmname, namestr, CANOF_PARSE_DISPLAY_NAME, NULL); + ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); + ok(asmname != NULL, "Expected non-NULL asmname\n"); + + asmenum = NULL; + hr = pCreateAssemblyEnum(&asmenum, NULL, asmname, ASM_CACHE_GAC, NULL); + ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); + ok(asmenum != NULL, "Expected non-NULL asmenum\n"); + + next = NULL; + hr = IAssemblyEnum_GetNextAssembly(asmenum, NULL, &next, 0); + ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); + ok(next != NULL, "Expected non-NULL next\n"); + + size = MAX_PATH; + hr = IAssemblyName_GetDisplayName(next, buf, &size, 0); + to_multibyte(disp, buf); + ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); + ok(!lstrcmpA(disp, exp[4]), "Expected \"%s\", got \"%s\"\n", exp[4], disp); + + IAssemblyName_Release(next); + + next = NULL; + hr = IAssemblyEnum_GetNextAssembly(asmenum, NULL, &next, 0); + ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); + ok(next != NULL, "Expected non-NULL next\n"); + + size = MAX_PATH; + hr = IAssemblyName_GetDisplayName(next, buf, &size, 0); + to_multibyte(disp, buf); + ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); + ok(!lstrcmpA(disp, exp[5]), "Expected \"%s\", got \"%s\"\n", exp[5], disp); + + IAssemblyName_Release(next); + + next = (IAssemblyName *)0xdeadbeef; + hr = IAssemblyEnum_GetNextAssembly(asmenum, NULL, &next, 0); + ok(hr == S_FALSE, "Expected S_FALSE, got %08x\n", hr); + ok(next == (IAssemblyName *)0xdeadbeef, + "Expected next to be unchanged, got %p\n", next); + + IAssemblyEnum_Release(asmenum); + IAssemblyName_Release(asmname); + + /* only PublicKeyToken */ + to_widechar(namestr, "Wine, PublicKeyToken=16a3fcd171e93a8d"); + asmname = NULL; + hr = pCreateAssemblyNameObject(&asmname, namestr, CANOF_PARSE_DISPLAY_NAME, NULL); + ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); + ok(asmname != NULL, "Expected non-NULL asmname\n"); + + asmenum = NULL; + hr = pCreateAssemblyEnum(&asmenum, NULL, asmname, ASM_CACHE_GAC, NULL); + ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); + ok(asmenum != NULL, "Expected non-NULL asmenum\n"); + + next = NULL; + hr = IAssemblyEnum_GetNextAssembly(asmenum, NULL, &next, 0); + ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); + ok(next != NULL, "Expected non-NULL next\n"); + + size = MAX_PATH; + hr = IAssemblyName_GetDisplayName(next, buf, &size, 0); + to_multibyte(disp, buf); + ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); + ok(!lstrcmpA(disp, exp[3]), "Expected \"%s\", got \"%s\"\n", exp[3], disp); + + IAssemblyName_Release(next); + + next = NULL; + hr = IAssemblyEnum_GetNextAssembly(asmenum, NULL, &next, 0); + ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); + ok(next != NULL, "Expected non-NULL next\n"); + + size = MAX_PATH; + hr = IAssemblyName_GetDisplayName(next, buf, &size, 0); + to_multibyte(disp, buf); + ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); + ok(!lstrcmpA(disp, exp[5]), "Expected \"%s\", got \"%s\"\n", exp[5], disp); + + IAssemblyName_Release(next); + + next = (IAssemblyName *)0xdeadbeef; + hr = IAssemblyEnum_GetNextAssembly(asmenum, NULL, &next, 0); + ok(hr == S_FALSE, "Expected S_FALSE, got %08x\n", hr); + ok(next == (IAssemblyName *)0xdeadbeef, + "Expected next to be unchanged, got %p\n", next); + + IAssemblyEnum_Release(asmenum); + IAssemblyName_Release(asmname); + + /* only Culture */ + to_widechar(namestr, "wine, Culture=neutral"); + asmname = NULL; + hr = pCreateAssemblyNameObject(&asmname, namestr, CANOF_PARSE_DISPLAY_NAME, NULL); + ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); + ok(asmname != NULL, "Expected non-NULL asmname\n"); + + asmenum = NULL; + hr = pCreateAssemblyEnum(&asmenum, NULL, asmname, ASM_CACHE_GAC, NULL); + ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); + ok(asmenum != NULL, "Expected non-NULL asmenum\n"); + + next = NULL; + hr = IAssemblyEnum_GetNextAssembly(asmenum, NULL, &next, 0); + ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); + ok(next != NULL, "Expected non-NULL next\n"); + + size = MAX_PATH; + hr = IAssemblyName_GetDisplayName(next, buf, &size, 0); + to_multibyte(disp, buf); + ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); + ok(!lstrcmpA(disp, exp[0]), "Expected \"%s\", got \"%s\"\n", exp[0], disp); + + IAssemblyName_Release(next); + + next = NULL; + hr = IAssemblyEnum_GetNextAssembly(asmenum, NULL, &next, 0); + ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); + ok(next != NULL, "Expected non-NULL next\n"); + + size = MAX_PATH; + hr = IAssemblyName_GetDisplayName(next, buf, &size, 0); + to_multibyte(disp, buf); + ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); + ok(!lstrcmpA(disp, exp[1]) || + !lstrcmpA(disp, exp[2]), + "Expected \"%s\" or \"%s\", got \"%s\"\n", exp[1], exp[2], disp); + + IAssemblyName_Release(next); + + next = NULL; + hr = IAssemblyEnum_GetNextAssembly(asmenum, NULL, &next, 0); + ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); + ok(next != NULL, "Expected non-NULL next\n"); + + size = MAX_PATH; + hr = IAssemblyName_GetDisplayName(next, buf, &size, 0); + to_multibyte(disp, buf); + ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); + ok(!lstrcmpA(disp, exp[1]) || + !lstrcmpA(disp, exp[2]), + "Expected \"%s\" or \"%s\", got \"%s\"\n", exp[1], exp[2], disp); + + IAssemblyName_Release(next); + + next = (IAssemblyName *)0xdeadbeef; + hr = IAssemblyEnum_GetNextAssembly(asmenum, NULL, &next, 0); + ok(hr == S_FALSE, "Expected S_FALSE, got %08x\n", hr); + ok(next == (IAssemblyName *)0xdeadbeef, + "Expected next to be unchanged, got %p\n", next); + + IAssemblyEnum_Release(asmenum); + IAssemblyName_Release(asmname); + + sprintf(path, "%s\\Wine\\1.0.0.0__16a3fcd171e93a8d\\Wine.dll", gac); + DeleteFileA(path); + sprintf(path, "%s\\Wine\\1.0.1.2__16a3fcd171e93a8d\\Wine.dll", gac); + DeleteFileA(path); + sprintf(path, "%s\\Wine\\1.0.1.2__123456789abcdef0\\Wine.dll", gac); + DeleteFileA(path); + sprintf(path, "%s\\Wine\\1.0.0.0__16a3fcd171e93a8d", gac); + RemoveDirectoryA(path); + sprintf(path, "%s\\Wine\\1.0.1.2__16a3fcd171e93a8d", gac); + RemoveDirectoryA(path); + sprintf(path, "%s\\Wine\\1.0.1.2__123456789abcdef0", gac); + RemoveDirectoryA(path); + sprintf(path, "%s\\Wine", gac); + RemoveDirectoryA(path); +} + +START_TEST(asmenum) +{ + if (!init_functionpointers()) + return; + + test_CreateAssemblyEnum(); + test_enumerate(); + test_enumerate_name(); +}