Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=43270 Signed-off-by: Fabian Maurer dark.shadow4@web.de --- v5: check for admin rights before trying to write into system32 fix test failure on xp. dll can't be in same folder as manifest give test runs better names v6: Fix test failure by compiling into temp folder and then moving file --- dlls/mscoree/tests/Makefile.in | 6 +- dlls/mscoree/tests/comtest.c | 356 ++++++++++++++++++++++++ dlls/mscoree/tests/comtest.cs | 23 ++ dlls/mscoree/tests/comtest_dll.manifest | 16 ++ dlls/mscoree/tests/comtest_exe.manifest | 11 + dlls/mscoree/tests/interfaces.idl | 33 +++ dlls/mscoree/tests/resource.rc | 30 ++ 7 files changed, 474 insertions(+), 1 deletion(-) create mode 100644 dlls/mscoree/tests/comtest.c create mode 100644 dlls/mscoree/tests/comtest.cs create mode 100644 dlls/mscoree/tests/comtest_dll.manifest create mode 100644 dlls/mscoree/tests/comtest_exe.manifest create mode 100644 dlls/mscoree/tests/interfaces.idl create mode 100644 dlls/mscoree/tests/resource.rc
diff --git a/dlls/mscoree/tests/Makefile.in b/dlls/mscoree/tests/Makefile.in index 7c1ba5cb11..f65e1bc6c2 100644 --- a/dlls/mscoree/tests/Makefile.in +++ b/dlls/mscoree/tests/Makefile.in @@ -1,7 +1,11 @@ TESTDLL = mscoree.dll -IMPORTS = ole32 shlwapi uuid +IMPORTS = ole32 shlwapi uuid shell32
C_SRCS = \ + comtest.c \ debugging.c \ metahost.c \ mscoree.c + +RC_SRCS = resource.rc +IDL_SRCS = interfaces.idl diff --git a/dlls/mscoree/tests/comtest.c b/dlls/mscoree/tests/comtest.c new file mode 100644 index 0000000000..e9812eb6b9 --- /dev/null +++ b/dlls/mscoree/tests/comtest.c @@ -0,0 +1,356 @@ +/* + * Copyright 2018 Fabian Maurer + * + * 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 <stdio.h> + +#include "windows.h" +#include "ole2.h" +#include "mscoree.h" +#include "corerror.h" +#include "shlwapi.h" +#include "shlobj.h" + +#include "wine/test.h" + +#include "initguid.h" +#include "interfaces.h" + +HMODULE hmscoree; + +typedef enum _run_type +{ + run_type_current_working_directory = 0, + run_type_exe_directory, + run_type_system32, +} run_type; + +static BOOL write_resource_file(const char *path_tmp, const char *name_res, const char *name_file, char *path_file) +{ + HRSRC rsrc; + void *rsrc_data; + DWORD rsrc_size; + BOOL ret; + HANDLE hfile; + + rsrc = FindResourceA(GetModuleHandleA(NULL), name_res, (LPCSTR)RT_RCDATA); + if (!rsrc) return FALSE; + + rsrc_data = LockResource(LoadResource(GetModuleHandleA(NULL), rsrc)); + if (!rsrc_data) return FALSE; + + rsrc_size = SizeofResource(GetModuleHandleA(NULL), rsrc); + if (!rsrc_size) return FALSE; + + strcpy(path_file, path_tmp); + PathAppendA(path_file, name_file); + hfile = CreateFileA(path_file, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); + if (hfile == INVALID_HANDLE_VALUE) return FALSE; + + ret = WriteFile(hfile, rsrc_data, rsrc_size, &rsrc_size, NULL); + + CloseHandle(hfile); + return ret; +} + +static BOOL compile_cs_to_dll(char *source_path, char *dest_path) +{ + const char *path_csc = "C:\windows\Microsoft.NET\Framework\v2.0.50727\csc.exe"; + char cmdline[MAX_PATH]; + char path_temp[MAX_PATH] = {0}; + PROCESS_INFORMATION pi; + STARTUPINFOA si = { 0 }; + BOOL ret; + + if (!PathFileExistsA(path_csc)) + { + skip("Can't find csc.exe\n"); + return FALSE; + } + + GetTempPathA(MAX_PATH, path_temp); + PathAppendA(path_temp, "comtest.dll"); + + sprintf(cmdline, "%s /t:library /out:"%s" "%s"", path_csc, path_temp, source_path); + + si.cb = sizeof(si); + ret = CreateProcessA(path_csc, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); + ok(ret, "Could not create process: %u\n", GetLastError()); + + WaitForSingleObject(pi.hProcess, 5000); + CloseHandle(pi.hThread); + CloseHandle(pi.hProcess); + + ret = PathFileExistsA(path_temp); + ok(ret, "Compilation failed\n"); + + ret = MoveFileA(path_temp, dest_path); + ok(ret, "Moving temporary file failed\n"); + return ret; +} + +static void run_test(BOOL expect_success) +{ + typedef HRESULT (WINAPI *_DllGetClassObject)(REFCLSID rclsid, REFIID riid, LPVOID *ppv); + ITest *test = NULL; + HRESULT hr; + _DllGetClassObject getClassObject; + IClassFactory *classFactory = NULL; + HRESULT result_expected = expect_success ? S_OK : HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + + hr = CoCreateInstance(&CLSID_Test, NULL, CLSCTX_INPROC_SERVER, &IID_ITest, (void**)&test); + todo_wine + ok(hr == result_expected, "Expected %x, got %x\n", result_expected, hr); + + if (hr == S_OK) + { + int i = 0; + hr = ITest_Func(test, &i); + ok(hr == S_OK, "Got %x\n", hr); + ok(i == 42, "Expected 42, got %d\n", i); + ITest_Release(test); + } + + getClassObject = (_DllGetClassObject)GetProcAddress(hmscoree, "DllGetClassObject"); + hr = getClassObject(&CLSID_Test, &IID_IClassFactory, (void **)&classFactory); + todo_wine_if(!expect_success) + ok(hr == result_expected, "Expected %x, got %x\n", result_expected, hr); + + if (hr == S_OK) + { + ITest *test2 = NULL; + hr = IClassFactory_CreateInstance(classFactory, NULL, &IID_ITest, (void **)&test2); + todo_wine + ok(hr == S_OK, "Got %x\n", hr); + + if (hr == S_OK) + { + int i = 0; + hr = ITest_Func(test2, &i); + ok(hr == S_OK, "Got %x\n", hr); + ok(i == 42, "Expected 42, got %d\n", i); + ITest_Release(test2); + } + IClassFactory_Release(classFactory); + } +} + +static void get_dll_path_for_run(char *path_dll, UINT path_dll_size, run_type run) +{ + char path_tmp[MAX_PATH] = {0}; + + GetTempPathA(MAX_PATH, path_tmp); + + switch (run) + { + case run_type_current_working_directory: + strcpy(path_dll, path_tmp); + PathAppendA(path_dll, "comtest.dll"); + break; + case run_type_exe_directory: + GetModuleFileNameA(NULL, path_dll, path_dll_size); + PathRemoveFileSpecA(path_dll); + PathAppendA(path_dll, "comtest.dll"); + break; + case run_type_system32: + GetSystemDirectoryA(path_dll, path_dll_size); + PathAppendA(path_dll, "comtest.dll"); + break; + } +} +static void prepare_and_run_test(const char *dll_source, run_type run) +{ + char path_tmp[MAX_PATH] = {0}; + char path_tmp_manifest[MAX_PATH] = {0}; + char path_dll[MAX_PATH] = {0}; + char path_dll_source[MAX_PATH] = {0}; + char path_manifest_dll[MAX_PATH] = {0}; + char path_manifest_exe[MAX_PATH] = {0}; + BOOL success; + ACTCTXA context = {0}; + ULONG_PTR cookie; + HANDLE handle_context = 0; + + GetTempPathA(MAX_PATH, path_tmp); + GetTempPathA(MAX_PATH, path_tmp_manifest); + PathAppendA(path_tmp_manifest, "manifests"); + + CreateDirectoryA(path_tmp_manifest, NULL); + + if (run == run_type_system32) + { + if (!IsUserAnAdmin()) + { + skip("Can't test dll in system32 due to user not being admin.\n"); + return; + } + } + + if (!write_resource_file(path_tmp, dll_source, "comtest.cs", path_dll_source)) + { + ok(0, "run: %d, Failed to create file for testing\n", run); + goto cleanup; + } + + get_dll_path_for_run(path_dll, sizeof(path_dll), run); + + if (!compile_cs_to_dll(path_dll_source, path_dll)) + goto cleanup; + + if (!write_resource_file(path_tmp_manifest, "comtest_exe.manifest", "exe.manifest", path_manifest_exe)) + { + ok(0, "run: %d, Failed to create file for testing\n", run); + goto cleanup; + } + + if (!write_resource_file(path_tmp_manifest, "comtest_dll.manifest", "comtest.manifest", path_manifest_dll)) + { + ok(0, "run: %d, Failed to create file for testing\n", run); + goto cleanup; + } + + context.cbSize = sizeof(ACTCTXA); + context.lpSource = path_manifest_exe; + context.lpAssemblyDirectory = path_tmp_manifest; + context.dwFlags = ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID; + + handle_context = CreateActCtxA(&context); + ok(handle_context != NULL && handle_context != INVALID_HANDLE_VALUE, "run: %d, CreateActCtxA failed: %d\n", run, GetLastError()); + + if (handle_context == NULL || handle_context == INVALID_HANDLE_VALUE) + { + ok(0, "run: %d, Failed to create activation context\n", run); + goto cleanup; + } + + success = ActivateActCtx(handle_context, &cookie); + ok(success, "run: %d, ActivateActCtx failed: %d\n", run, GetLastError()); + + if (run == run_type_current_working_directory) + SetCurrentDirectoryA(path_tmp); + + run_test(run == run_type_exe_directory); + +cleanup: + if (handle_context != NULL && handle_context != INVALID_HANDLE_VALUE) + { + success = DeactivateActCtx(0, cookie); + ok(success, "run: %d, DeactivateActCtx failed: %d\n", run, GetLastError()); + ReleaseActCtx(handle_context); + } + if (*path_manifest_exe) + { + success = DeleteFileA(path_manifest_exe); + ok(success, "run: %d, DeleteFileA failed: %d\n", run, GetLastError()); + } + if(*path_manifest_dll) + { + success = DeleteFileA(path_manifest_dll); + ok(success, "run: %d, DeleteFileA failed: %d\n", run, GetLastError()); + } + if(*path_dll_source) + { + success = DeleteFileA(path_dll_source); + ok(success, "run: %d, DeleteFileA failed: %d\n", run, GetLastError()); + } + RemoveDirectoryA(path_tmp_manifest); + /* dll cleanup is handled by the parent, because it might still be used by the child */ +} + + +static void cleanup_test(run_type run) +{ + char path_dll[MAX_PATH] = {0}; + BOOL success; + + get_dll_path_for_run(path_dll, sizeof(path_dll), run); + + if (!PathFileExistsA(path_dll)) + return; + + success = DeleteFileA(path_dll); + if (!success) + { + Sleep(500); + success = DeleteFileA(path_dll); + } + ok(success, "DeleteFileA failed: %d\n", GetLastError()); +} + +static void run_child_process(const char *dll_source, run_type run) +{ + char cmdline[MAX_PATH]; + char exe[MAX_PATH]; + char **argv; + PROCESS_INFORMATION pi; + STARTUPINFOA si = { 0 }; + BOOL ret; + + winetest_get_mainargs(&argv); + + if (strstr(argv[0], ".exe")) + sprintf(exe, "%s", argv[0]); + else + sprintf(exe, "%s.exe", argv[0]); + sprintf(cmdline, ""%s" %s %s %d", argv[0], argv[1], dll_source, run); + + si.cb = sizeof(si); + ret = CreateProcessA(exe, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); + ok(ret, "Could not create process: %u\n", GetLastError()); + + winetest_wait_child_process(pi.hProcess); + + CloseHandle(pi.hThread); + CloseHandle(pi.hProcess); + + /* Cleanup dll, because it might still have been used by the child */ + cleanup_test(run); +} + +START_TEST(comtest) +{ + int argc; + char **argv; + + CoInitialize(NULL); + + hmscoree = LoadLibraryA("mscoree.dll"); + if (!hmscoree) + { + skip(".NET or mono not available\n"); + return; + } + + argc = winetest_get_mainargs(&argv); + if (argc > 2) + { + const char *dll_source = argv[2]; + run_type run = atoi(argv[3]); + prepare_and_run_test(dll_source, run); + + goto cleanup; + } + + run_child_process("comtest.cs", run_type_current_working_directory); + run_child_process("comtest.cs", run_type_exe_directory); + run_child_process("comtest.cs", run_type_system32); + +cleanup: + FreeLibrary(hmscoree); + CoUninitialize(); +} diff --git a/dlls/mscoree/tests/comtest.cs b/dlls/mscoree/tests/comtest.cs new file mode 100644 index 0000000000..4fe2a718b2 --- /dev/null +++ b/dlls/mscoree/tests/comtest.cs @@ -0,0 +1,23 @@ +/* Compile with + csc /target:library /out:dll.dll comtest.cs +*/ + +using System.Runtime.InteropServices; + +namespace DLL +{ + [Guid("1dbc4491-080d-45c5-a15d-1e3c4610bdd9"), ComVisible(true), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface ITest + { + void Func(ref int i); + } + + [Guid("2e106e50-e7a4-4489-8538-83643f100fdc"), ComVisible(true), ClassInterface(ClassInterfaceType.None)] + public class Test : ITest + { + public void Func(ref int i) + { + i = 42; + } + } +} diff --git a/dlls/mscoree/tests/comtest_dll.manifest b/dlls/mscoree/tests/comtest_dll.manifest new file mode 100644 index 0000000000..ef6924de9e --- /dev/null +++ b/dlls/mscoree/tests/comtest_dll.manifest @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> + <assemblyIdentity + name="comtest" + version="1.0.0.0" + type="win32" + /> + <clrClass + clsid="{2e106e50-e7a4-4489-8538-83643f100fdc}" + threadingModel="Both" + name="DLL.Test" + runtimeVersion="v4.0.0.0"> + </clrClass> + <file name="comtest.dll"> + </file> +</assembly> diff --git a/dlls/mscoree/tests/comtest_exe.manifest b/dlls/mscoree/tests/comtest_exe.manifest new file mode 100644 index 0000000000..bc9ce4c045 --- /dev/null +++ b/dlls/mscoree/tests/comtest_exe.manifest @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> + <dependency> + <dependentAssembly> + <assemblyIdentity + name="comtest" + version="1.0.0.0" + type="win32"/> + </dependentAssembly> + </dependency> +</assembly> diff --git a/dlls/mscoree/tests/interfaces.idl b/dlls/mscoree/tests/interfaces.idl new file mode 100644 index 0000000000..9e64988197 --- /dev/null +++ b/dlls/mscoree/tests/interfaces.idl @@ -0,0 +1,33 @@ +/* + * Copyright 2018 Fabian Maurer + * + * 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 + */ + +#include "unknwn.idl" + +[ + object, + uuid(1dbc4491-080d-45c5-a15d-1e3c4610bdd9), + local +] +interface ITest : IUnknown { + HRESULT Func([in, out] int *i); +}; + +[ + uuid(2e106e50-e7a4-4489-8538-83643f100fdc), +] +coclass Test { interface ITest; }; diff --git a/dlls/mscoree/tests/resource.rc b/dlls/mscoree/tests/resource.rc new file mode 100644 index 0000000000..9a1b89f656 --- /dev/null +++ b/dlls/mscoree/tests/resource.rc @@ -0,0 +1,30 @@ +/* + * Resources for mscoree test suite. + * + * Copyright 2018 Fabian Maurer + * + * 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 + */ + +#include "windef.h" + +/* @makedep: comtest.cs */ +comtest.cs RCDATA comtest.cs + +/* @makedep: comtest_exe.manifest */ +comtest_exe.manifest RCDATA comtest_exe.manifest + +/* @makedep: comtest_dll.manifest */ +comtest_dll.manifest RCDATA comtest_dll.manifest
Signed-off-by: Fabian Maurer dark.shadow4@web.de --- dlls/mscoree/corruntimehost.c | 219 ++++++++++++++++++++++++---------- dlls/mscoree/tests/comtest.c | 4 +- 2 files changed, 160 insertions(+), 63 deletions(-)
diff --git a/dlls/mscoree/corruntimehost.c b/dlls/mscoree/corruntimehost.c index 5c4e7f1d6e..ef4685822d 100644 --- a/dlls/mscoree/corruntimehost.c +++ b/dlls/mscoree/corruntimehost.c @@ -29,6 +29,7 @@ #include "winreg.h" #include "ole2.h" #include "shellapi.h" +#include "shlwapi.h"
#include "cor.h" #include "mscoree.h" @@ -40,6 +41,7 @@
#include "wine/debug.h" #include "wine/unicode.h" +#include "wine/heap.h"
WINE_DEFAULT_DEBUG_CHANNEL( mscoree );
@@ -68,6 +70,43 @@ struct dll_fixup void *tokens; /* pointer into process heap */ };
+struct comclassredirect_data +{ + ULONG size; + BYTE res; + BYTE miscmask; + BYTE res1[2]; + DWORD model; + GUID clsid; + GUID alias; + GUID clsid2; + GUID tlbid; + ULONG name_len; + ULONG name_offset; + ULONG progid_len; + ULONG progid_offset; + ULONG clrdata_len; + ULONG clrdata_offset; + DWORD miscstatus; + DWORD miscstatuscontent; + DWORD miscstatusthumbnail; + DWORD miscstatusicon; + DWORD miscstatusdocprint; +}; + +struct clrclass_data +{ + ULONG size; + DWORD res[2]; + ULONG module_len; + ULONG module_offset; + ULONG name_len; + ULONG name_offset; + ULONG version_len; + ULONG version_offset; + DWORD res2[2]; +}; + static MonoDomain* domain_attach(MonoDomain *domain) { MonoDomain *prev_domain = mono_domain_get(); @@ -1606,6 +1645,57 @@ HRESULT RuntimeHost_GetInterface(RuntimeHost *This, REFCLSID clsid, REFIID riid, return CLASS_E_CLASSNOTAVAILABLE; }
+static BOOL try_create_registration_free_com(REFIID clsid, WCHAR *classname, WCHAR *filename, UINT filename_size) +{ + ACTCTX_SECTION_KEYED_DATA guid_info = { sizeof(ACTCTX_SECTION_KEYED_DATA) }; + ACTIVATION_CONTEXT_ASSEMBLY_DETAILED_INFORMATION *assembly_info; + SIZE_T bytes_assembly_info; + struct comclassredirect_data *redirect_data; + struct clrclass_data *class_data; + void *ptr_name; + const WCHAR *ptr_path_start, *ptr_path_end; + WCHAR path[MAX_PATH] = {0}; + WCHAR str_dll[] = {'.','d','l','l',0}; + + if (!FindActCtxSectionGuid(FIND_ACTCTX_SECTION_KEY_RETURN_HACTCTX, 0, ACTIVATION_CONTEXT_SECTION_COM_SERVER_REDIRECTION, clsid, &guid_info)) + { + DWORD error = GetLastError(); + if (error != ERROR_SXS_KEY_NOT_FOUND) + ERR("Failed to find guid: %d\n", error); + return FALSE; + } + + QueryActCtxW(0, guid_info.hActCtx, &guid_info.ulAssemblyRosterIndex, AssemblyDetailedInformationInActivationContext, NULL, 0, &bytes_assembly_info); + assembly_info = heap_alloc(bytes_assembly_info); + if (!QueryActCtxW(0, guid_info.hActCtx, &guid_info.ulAssemblyRosterIndex, + AssemblyDetailedInformationInActivationContext, assembly_info, bytes_assembly_info, &bytes_assembly_info)) + { + ERR("QueryActCtxW failed: %d!\n", GetLastError()); + heap_free(assembly_info); + ReleaseActCtx(guid_info.hActCtx); + return FALSE; + } + + redirect_data = guid_info.lpData; + class_data = (void *)((char*)redirect_data + redirect_data->clrdata_offset); + + ptr_name = (char *)class_data + class_data->name_offset; + strcpyW(classname, ptr_name); + + ptr_path_start = assembly_info->lpAssemblyEncodedAssemblyIdentity; + ptr_path_end = strchrW(ptr_path_start, ','); + memcpy(path, ptr_path_start, (char*)ptr_path_end - (char*)ptr_path_start); + + GetModuleFileNameW(NULL, filename, filename_size); + PathRemoveFileSpecW(filename); + PathAppendW(filename, path); + strcatW(filename, str_dll); + + heap_free(assembly_info); + ReleaseActCtx(guid_info.hActCtx); + return TRUE; +} + #define CHARS_IN_GUID 39
HRESULT create_monodata(REFIID riid, LPVOID *ppObj ) @@ -1640,75 +1730,82 @@ HRESULT create_monodata(REFIID riid, LPVOID *ppObj ) TRACE("Registry key: %s\n", debugstr_w(path));
res = RegOpenKeyExW(HKEY_CLASSES_ROOT, path, 0, KEY_READ, &key); - if (res == ERROR_FILE_NOT_FOUND) - return CLASS_E_CLASSNOTAVAILABLE; - - res = RegGetValueW( key, NULL, wszClass, RRF_RT_REG_SZ, NULL, classname, &dwBufLen); - if(res != ERROR_SUCCESS) + if (res != ERROR_FILE_NOT_FOUND) { - WARN("Class value cannot be found.\n"); - hr = CLASS_E_CLASSNOTAVAILABLE; - goto cleanup; - } + res = RegGetValueW( key, NULL, wszClass, RRF_RT_REG_SZ, NULL, classname, &dwBufLen); + if(res != ERROR_SUCCESS) + { + WARN("Class value cannot be found.\n"); + hr = CLASS_E_CLASSNOTAVAILABLE; + goto cleanup; + }
- TRACE("classname (%s)\n", debugstr_w(classname)); + TRACE("classname (%s)\n", debugstr_w(classname));
- dwBufLen = MAX_PATH + 8; - res = RegGetValueW( key, NULL, wszCodebase, RRF_RT_REG_SZ, NULL, codebase, &dwBufLen); - if(res == ERROR_SUCCESS) - { - /* Strip file:/// */ - if(strncmpW(codebase, wszFileSlash, strlenW(wszFileSlash)) == 0) - offset = strlenW(wszFileSlash); + dwBufLen = MAX_PATH + 8; + res = RegGetValueW( key, NULL, wszCodebase, RRF_RT_REG_SZ, NULL, codebase, &dwBufLen); + if(res == ERROR_SUCCESS) + { + /* Strip file:/// */ + if(strncmpW(codebase, wszFileSlash, strlenW(wszFileSlash)) == 0) + offset = strlenW(wszFileSlash); + + strcpyW(filename, codebase + offset); + } + else + { + WCHAR assemblyname[MAX_PATH + 8];
- strcpyW(filename, codebase + offset); + hr = CLASS_E_CLASSNOTAVAILABLE; + WARN("CodeBase value cannot be found, trying Assembly.\n"); + /* get the last subkey of InprocServer32 */ + res = RegQueryInfoKeyW(key, 0, 0, 0, &numKeys, 0, 0, 0, 0, 0, 0, 0); + if (res != ERROR_SUCCESS || numKeys == 0) + goto cleanup; + numKeys--; + keyLength = ARRAY_SIZE(subkeyName); + res = RegEnumKeyExW(key, numKeys, subkeyName, &keyLength, 0, 0, 0, 0); + if (res != ERROR_SUCCESS) + goto cleanup; + res = RegOpenKeyExW(key, subkeyName, 0, KEY_READ, &subkey); + if (res != ERROR_SUCCESS) + goto cleanup; + dwBufLen = MAX_PATH + 8; + res = RegGetValueW(subkey, NULL, wszAssembly, RRF_RT_REG_SZ, NULL, assemblyname, &dwBufLen); + RegCloseKey(subkey); + if (res != ERROR_SUCCESS) + goto cleanup; + + hr = get_file_from_strongname(assemblyname, filename, MAX_PATH); + if (FAILED(hr)) + { + /* + * The registry doesn't have a CodeBase entry and it's not in the GAC. + * + * Use the Assembly Key to retrieve the filename. + * Assembly : REG_SZ : AssemblyName, Version=X.X.X.X, Culture=neutral, PublicKeyToken=null + */ + WCHAR *ns; + + WARN("Attempt to load from the application directory.\n"); + GetModuleFileNameW(NULL, filename, MAX_PATH); + ns = strrchrW(filename, '\'); + *(ns+1) = '\0'; + + ns = strchrW(assemblyname, ','); + *(ns) = '\0'; + strcatW(filename, assemblyname); + *(ns) = '.'; + strcatW(filename, wszDLL); + } + } } else { - WCHAR assemblyname[MAX_PATH + 8]; - - hr = CLASS_E_CLASSNOTAVAILABLE; - WARN("CodeBase value cannot be found, trying Assembly.\n"); - /* get the last subkey of InprocServer32 */ - res = RegQueryInfoKeyW(key, 0, 0, 0, &numKeys, 0, 0, 0, 0, 0, 0, 0); - if (res != ERROR_SUCCESS || numKeys == 0) - goto cleanup; - numKeys--; - keyLength = ARRAY_SIZE(subkeyName); - res = RegEnumKeyExW(key, numKeys, subkeyName, &keyLength, 0, 0, 0, 0); - if (res != ERROR_SUCCESS) - goto cleanup; - res = RegOpenKeyExW(key, subkeyName, 0, KEY_READ, &subkey); - if (res != ERROR_SUCCESS) - goto cleanup; - dwBufLen = MAX_PATH + 8; - res = RegGetValueW(subkey, NULL, wszAssembly, RRF_RT_REG_SZ, NULL, assemblyname, &dwBufLen); - RegCloseKey(subkey); - if (res != ERROR_SUCCESS) - goto cleanup; + if (!try_create_registration_free_com(riid, classname, filename, sizeof(filename))) + return CLASS_E_CLASSNOTAVAILABLE;
- hr = get_file_from_strongname(assemblyname, filename, MAX_PATH); - if (FAILED(hr)) - { - /* - * The registry doesn't have a CodeBase entry and it's not in the GAC. - * - * Use the Assembly Key to retrieve the filename. - * Assembly : REG_SZ : AssemblyName, Version=X.X.X.X, Culture=neutral, PublicKeyToken=null - */ - WCHAR *ns; - - WARN("Attempt to load from the application directory.\n"); - GetModuleFileNameW(NULL, filename, MAX_PATH); - ns = strrchrW(filename, '\'); - *(ns+1) = '\0'; - - ns = strchrW(assemblyname, ','); - *(ns) = '\0'; - strcatW(filename, assemblyname); - *(ns) = '.'; - strcatW(filename, wszDLL); - } + TRACE("classname (%s)\n", debugstr_w(classname)); }
TRACE("filename (%s)\n", debugstr_w(filename)); diff --git a/dlls/mscoree/tests/comtest.c b/dlls/mscoree/tests/comtest.c index e9812eb6b9..bd1632300a 100644 --- a/dlls/mscoree/tests/comtest.c +++ b/dlls/mscoree/tests/comtest.c @@ -114,7 +114,7 @@ static void run_test(BOOL expect_success) HRESULT result_expected = expect_success ? S_OK : HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
hr = CoCreateInstance(&CLSID_Test, NULL, CLSCTX_INPROC_SERVER, &IID_ITest, (void**)&test); - todo_wine + todo_wine_if(!expect_success) ok(hr == result_expected, "Expected %x, got %x\n", result_expected, hr);
if (hr == S_OK) @@ -135,7 +135,7 @@ static void run_test(BOOL expect_success) { ITest *test2 = NULL; hr = IClassFactory_CreateInstance(classFactory, NULL, &IID_ITest, (void **)&test2); - todo_wine + todo_wine_if(!expect_success) ok(hr == S_OK, "Got %x\n", hr);
if (hr == S_OK)
To my knowledge, a .NET class name has no maximum size. This also passes in a size for the filename but doesn't fully check it.
On Montag, 5. November 2018 20:21:42 CET Vincent Povirk wrote:
To my knowledge, a .NET class name has no maximum size.
Yeah, but we already had a fixed size buffer for that, so I figured I could leave it like that.
This also passes in a size for the filename but doesn't fully check it.
What exactly has to be checked?
Regards, Fabian Maurer
On Montag, 5. November 2018 21:47:05 CET Fabian Maurer wrote:
On Montag, 5. November 2018 20:21:42 CET Vincent Povirk wrote:
To my knowledge, a .NET class name has no maximum size.
Yeah, but we already had a fixed size buffer for that, so I figured I could leave it like that.
This also passes in a size for the filename but doesn't fully check it.
What exactly has to be checked?
Any update on this?
Regards, Fabian Maurer
To my knowledge, a .NET class name has no maximum size.
Yeah, but we already had a fixed size buffer for that, so I figured I could leave it like that.
It's OK to have a fixed size buffer, but we need to make sure copying the class name doesn't overrun the buffer.
This also passes in a size for the filename but doesn't fully check it.
What exactly has to be checked?
You append to the path without checking that the string will fit.
On Dienstag, 13. November 2018 20:30:06 CET Vincent Povirk wrote:
To my knowledge, a .NET class name has no maximum size.
Yeah, but we already had a fixed size buffer for that, so I figured I could leave it like that.
It's OK to have a fixed size buffer, but we need to make sure copying the class name doesn't overrun the buffer.
This also passes in a size for the filename but doesn't fully check it.
What exactly has to be checked?
You append to the path without checking that the string will fit.
Makes sense, thanks!
Regards, Fabian Maurer
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=43270 Signed-off-by: Fabian Maurer dark.shadow4@web.de --- dlls/sxs/sxs.c | 148 ++++++++++++++++++ dlls/sxs/sxs.spec | 1 + dlls/sxs/tests/Makefile.in | 8 +- dlls/sxs/tests/comtest_dll.manifest | 16 ++ dlls/sxs/tests/comtest_exe.manifest | 11 ++ dlls/sxs/tests/interfaces.idl | 33 ++++ dlls/sxs/tests/resource.rc | 27 ++++ dlls/sxs/tests/sxs.c | 223 ++++++++++++++++++++++++++++ 8 files changed, 465 insertions(+), 2 deletions(-) create mode 100644 dlls/sxs/tests/comtest_dll.manifest create mode 100644 dlls/sxs/tests/comtest_exe.manifest create mode 100644 dlls/sxs/tests/interfaces.idl create mode 100644 dlls/sxs/tests/resource.rc create mode 100644 dlls/sxs/tests/sxs.c
diff --git a/dlls/sxs/sxs.c b/dlls/sxs/sxs.c index 06e6672dbf..7eec772de1 100644 --- a/dlls/sxs/sxs.c +++ b/dlls/sxs/sxs.c @@ -23,6 +23,10 @@ #include "windef.h" #include "winbase.h"
+#include "wine/heap.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(sxs);
/*********************************************************************** * DllMain (SXS.@) @@ -40,3 +44,147 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) } return TRUE; } + +typedef struct _SXS_GUID_INFORMATION_CLR +{ + DWORD cbSize; + DWORD dwFlags; + PCWSTR pcwszRuntimeVersion; + PCWSTR pcwszTypeName; + PCWSTR pcwszAssemblyIdentity; +} SXS_GUID_INFORMATION_CLR, *PSXS_GUID_INFORMATION_CLR; + +#define SXS_GUID_INFORMATION_CLR_FLAG_IS_SURROGATE 0x1 +#define SXS_GUID_INFORMATION_CLR_FLAG_IS_CLASS 0x2 + +#define SXS_LOOKUP_CLR_GUID_USE_ACTCTX 0x00000001 +#define SXS_LOOKUP_CLR_GUID_FIND_SURROGATE 0x00010000 +#define SXS_LOOKUP_CLR_GUID_FIND_CLR_CLASS 0x00020000 + +struct comclassredirect_data +{ + ULONG size; + BYTE res; + BYTE miscmask; + BYTE res1[2]; + DWORD model; + GUID clsid; + GUID alias; + GUID clsid2; + GUID tlbid; + ULONG name_len; + ULONG name_offset; + ULONG progid_len; + ULONG progid_offset; + ULONG clrdata_len; + ULONG clrdata_offset; + DWORD miscstatus; + DWORD miscstatuscontent; + DWORD miscstatusthumbnail; + DWORD miscstatusicon; + DWORD miscstatusdocprint; +}; + +struct clrclass_data +{ + ULONG size; + DWORD res[2]; + ULONG module_len; + ULONG module_offset; + ULONG name_len; + ULONG name_offset; + ULONG version_len; + ULONG version_offset; + DWORD res2[2]; +}; + +BOOL WINAPI SxsLookupClrGuid(DWORD flags, GUID *clsid, HANDLE actctx, void *buffer, SIZE_T buffer_len, SIZE_T *buffer_len_required) +{ + ACTCTX_SECTION_KEYED_DATA guid_info = { sizeof(ACTCTX_SECTION_KEYED_DATA) }; + ACTIVATION_CONTEXT_ASSEMBLY_DETAILED_INFORMATION *assembly_info; + SIZE_T bytes_assembly_info; + struct comclassredirect_data *redirect_data; + struct clrclass_data *class_data; + int len_version = 0, len_name, len_identity; + const void *ptr_name, *ptr_version, *ptr_identity; + SXS_GUID_INFORMATION_CLR *ret = buffer; + char *ret_strings; + + TRACE("(%x, %s, %p, %p, %08lx, %p): stub\n", flags, wine_dbgstr_guid(clsid), actctx, buffer, buffer_len, buffer_len_required); + + if (flags & (~SXS_LOOKUP_CLR_GUID_FIND_CLR_CLASS)) + FIXME("Ignored flags: %x\n", flags & (~SXS_LOOKUP_CLR_GUID_FIND_CLR_CLASS)); + + if (!FindActCtxSectionGuid(FIND_ACTCTX_SECTION_KEY_RETURN_HACTCTX, 0, ACTIVATION_CONTEXT_SECTION_COM_SERVER_REDIRECTION, clsid, &guid_info)) + { + SetLastError(ERROR_NOT_FOUND); + return FALSE; + } + + QueryActCtxW(0, guid_info.hActCtx, &guid_info.ulAssemblyRosterIndex, + AssemblyDetailedInformationInActivationContext, NULL, 0, &bytes_assembly_info); + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) + { + ERR("QueryActCtxW failed: %d!\n", GetLastError()); + ReleaseActCtx(guid_info.hActCtx); + return FALSE; + } + assembly_info = heap_alloc(bytes_assembly_info); + if(!QueryActCtxW(0, guid_info.hActCtx, &guid_info.ulAssemblyRosterIndex, + AssemblyDetailedInformationInActivationContext, assembly_info, bytes_assembly_info, &bytes_assembly_info)) + { + ERR("QueryActCtxW failed: %d!\n", GetLastError()); + heap_free(assembly_info); + ReleaseActCtx(guid_info.hActCtx); + return FALSE; + } + + redirect_data = guid_info.lpData; + class_data = (void *)((char*)redirect_data + redirect_data->clrdata_offset); + + ptr_identity = assembly_info->lpAssemblyEncodedAssemblyIdentity; + ptr_name = (char *)class_data + class_data->name_offset; + ptr_version = (char *)class_data + class_data->version_offset; + + len_identity = assembly_info->ulEncodedAssemblyIdentityLength + sizeof(WCHAR); + len_name = class_data->name_len + sizeof(WCHAR); + if (class_data->version_len > 0) + len_version = class_data->version_len + sizeof(WCHAR); + + *buffer_len_required = sizeof(SXS_GUID_INFORMATION_CLR) + len_identity + len_version + len_name; + if (!buffer || buffer_len < *buffer_len_required) + { + SetLastError(ERROR_INSUFFICIENT_BUFFER); + heap_free(assembly_info); + ReleaseActCtx(guid_info.hActCtx); + return FALSE; + } + + ret->cbSize = sizeof(SXS_GUID_INFORMATION_CLR); + ret->dwFlags = SXS_GUID_INFORMATION_CLR_FLAG_IS_CLASS; + + /* Copy strings into buffer */ + ret_strings = (char *)ret + sizeof(SXS_GUID_INFORMATION_CLR); + + memcpy(ret_strings, ptr_identity, len_identity); + ret->pcwszAssemblyIdentity = (WCHAR *)ret_strings; + ret_strings += len_identity; + + memcpy(ret_strings, ptr_name, len_name); + ret->pcwszTypeName = (WCHAR *)ret_strings; + ret_strings += len_name; + + if (len_version > 0) + { + memcpy(ret_strings, ptr_version, len_version); + ret->pcwszRuntimeVersion = (WCHAR *)ret_strings; + } + else + ret->pcwszRuntimeVersion = NULL; + + SetLastError(0); + + ReleaseActCtx(guid_info.hActCtx); + heap_free(assembly_info); + return TRUE; +} diff --git a/dlls/sxs/sxs.spec b/dlls/sxs/sxs.spec index 2a27313427..138d68f7d7 100644 --- a/dlls/sxs/sxs.spec +++ b/dlls/sxs/sxs.spec @@ -1,2 +1,3 @@ @ stdcall CreateAssemblyCache(ptr long) @ stdcall CreateAssemblyNameObject(ptr wstr long ptr) +@ stdcall SxsLookupClrGuid(long ptr ptr ptr long ptr) diff --git a/dlls/sxs/tests/Makefile.in b/dlls/sxs/tests/Makefile.in index 34609a14ae..46b5485d29 100644 --- a/dlls/sxs/tests/Makefile.in +++ b/dlls/sxs/tests/Makefile.in @@ -1,6 +1,10 @@ TESTDLL = sxs.dll -IMPORTS = sxs +IMPORTS = sxs shlwapi
C_SRCS = \ cache.c \ - name.c + name.c \ + sxs.c + +RC_SRCS = resource.rc +IDL_SRCS = interfaces.idl \ No newline at end of file diff --git a/dlls/sxs/tests/comtest_dll.manifest b/dlls/sxs/tests/comtest_dll.manifest new file mode 100644 index 0000000000..ef6924de9e --- /dev/null +++ b/dlls/sxs/tests/comtest_dll.manifest @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> + <assemblyIdentity + name="comtest" + version="1.0.0.0" + type="win32" + /> + <clrClass + clsid="{2e106e50-e7a4-4489-8538-83643f100fdc}" + threadingModel="Both" + name="DLL.Test" + runtimeVersion="v4.0.0.0"> + </clrClass> + <file name="comtest.dll"> + </file> +</assembly> diff --git a/dlls/sxs/tests/comtest_exe.manifest b/dlls/sxs/tests/comtest_exe.manifest new file mode 100644 index 0000000000..bc9ce4c045 --- /dev/null +++ b/dlls/sxs/tests/comtest_exe.manifest @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> + <dependency> + <dependentAssembly> + <assemblyIdentity + name="comtest" + version="1.0.0.0" + type="win32"/> + </dependentAssembly> + </dependency> +</assembly> diff --git a/dlls/sxs/tests/interfaces.idl b/dlls/sxs/tests/interfaces.idl new file mode 100644 index 0000000000..9e64988197 --- /dev/null +++ b/dlls/sxs/tests/interfaces.idl @@ -0,0 +1,33 @@ +/* + * Copyright 2018 Fabian Maurer + * + * 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 + */ + +#include "unknwn.idl" + +[ + object, + uuid(1dbc4491-080d-45c5-a15d-1e3c4610bdd9), + local +] +interface ITest : IUnknown { + HRESULT Func([in, out] int *i); +}; + +[ + uuid(2e106e50-e7a4-4489-8538-83643f100fdc), +] +coclass Test { interface ITest; }; diff --git a/dlls/sxs/tests/resource.rc b/dlls/sxs/tests/resource.rc new file mode 100644 index 0000000000..dd2c25bf37 --- /dev/null +++ b/dlls/sxs/tests/resource.rc @@ -0,0 +1,27 @@ +/* + * Resources for sxs test suite. + * + * Copyright 2018 Fabian Maurer + * + * 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 + */ + +#include "windef.h" + +/* @makedep: comtest_exe.manifest */ +comtest_exe.manifest RCDATA comtest_exe.manifest + +/* @makedep: comtest_dll.manifest */ +comtest_dll.manifest RCDATA comtest_dll.manifest diff --git a/dlls/sxs/tests/sxs.c b/dlls/sxs/tests/sxs.c new file mode 100644 index 0000000000..d92bfb65c1 --- /dev/null +++ b/dlls/sxs/tests/sxs.c @@ -0,0 +1,223 @@ +/* + * Copyright 2018 Fabian Maurer + * + * 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 + */ + +#include <stdio.h> + +#define COBJMACROS + +#include <windows.h> +#include <winsxs.h> +#include <corerror.h> +#include "shlwapi.h" + +#include "wine/test.h" +#include "wine/heap.h" + +#include "initguid.h" +#include "interfaces.h" + +BOOL (WINAPI *pSxsLookupClrGuid)(DWORD flags, GUID *clsid, HANDLE actctx, void *buffer, SIZE_T buffer_len, SIZE_T *buffer_len_required); + +#define SXS_LOOKUP_CLR_GUID_USE_ACTCTX 0x00000001 +#define SXS_LOOKUP_CLR_GUID_FIND_SURROGATE 0x00010000 +#define SXS_LOOKUP_CLR_GUID_FIND_CLR_CLASS 0x00020000 +#define SXS_LOOKUP_CLR_GUID_FIND_ANY (SXS_LOOKUP_CLR_GUID_FIND_SURROGATE | SXS_LOOKUP_CLR_GUID_FIND_CLR_CLASS) + +#define SXS_GUID_INFORMATION_CLR_FLAG_IS_SURROGATE 0x00000001 +#define SXS_GUID_INFORMATION_CLR_FLAG_IS_CLASS 0x00000002 + +typedef struct _SXS_GUID_INFORMATION_CLR +{ + DWORD cbSize; + DWORD dwFlags; + PCWSTR pcwszRuntimeVersion; + PCWSTR pcwszTypeName; + PCWSTR pcwszAssemblyIdentity; +} SXS_GUID_INFORMATION_CLR; + +static BOOL write_resource_file(const char *path_tmp, const char *name_res, const char *name_file, char *path_file) +{ + HRSRC rsrc; + void *rsrc_data; + DWORD rsrc_size; + BOOL ret; + HANDLE hfile; + + rsrc = FindResourceA(GetModuleHandleA(NULL), name_res, (LPCSTR)RT_RCDATA); + if (!rsrc) return FALSE; + + rsrc_data = LockResource(LoadResource(GetModuleHandleA(NULL), rsrc)); + if (!rsrc_data) return FALSE; + + rsrc_size = SizeofResource(GetModuleHandleA(NULL), rsrc); + if (!rsrc_size) return FALSE; + + strcpy(path_file, path_tmp); + PathAppendA(path_file, name_file); + hfile = CreateFileA(path_file, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); + if (hfile == INVALID_HANDLE_VALUE) return FALSE; + + ret = WriteFile(hfile, rsrc_data, rsrc_size, &rsrc_size, NULL); + + CloseHandle(hfile); + return ret; +} + +static void run_test(void) +{ + SIZE_T buffer_size; + BOOL ret; + SXS_GUID_INFORMATION_CLR *info; + WCHAR expected_type_name[] = {'D','L','L','.','T','e','s','t',0}; + WCHAR expected_runtime_version[] = {'v','4','.','0','.','0','.','0',0}; + WCHAR expected_assembly_identity[] = {'c','o','m','t','e','s','t',',','t','y','p','e','=','"','w','i','n','3','2','"',',','v','e','r','s','i','o','n','=','"','1','.','0','.','0','.','0','"',0}; + + ret = pSxsLookupClrGuid(SXS_LOOKUP_CLR_GUID_FIND_CLR_CLASS, (GUID*)&CLSID_Test, NULL, NULL, 0, &buffer_size); + ok(ret == FALSE, "Got %d\n", ret); + ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Got %d\n", GetLastError()); + + info = heap_alloc(buffer_size); + ret = pSxsLookupClrGuid(SXS_LOOKUP_CLR_GUID_FIND_CLR_CLASS, (GUID*)&CLSID_Test, NULL, info, buffer_size, &buffer_size); + ok(ret == TRUE, "Got %d\n", ret); + ok(GetLastError() == 0, "Got %d\n", GetLastError()); + + ok(info->dwFlags == SXS_GUID_INFORMATION_CLR_FLAG_IS_CLASS, "Got %d\n", info->dwFlags); + ok(lstrcmpW(info->pcwszTypeName, expected_type_name) == 0, "Got %s\n", wine_dbgstr_w(info->pcwszTypeName)); + ok(lstrcmpW(info->pcwszAssemblyIdentity, expected_assembly_identity) == 0, "Got %s\n", wine_dbgstr_w(info->pcwszAssemblyIdentity)); + ok(lstrcmpW(info->pcwszRuntimeVersion, expected_runtime_version) == 0, "Got %s\n", wine_dbgstr_w(info->pcwszRuntimeVersion)); + + heap_free(info); +} + +static void prepare_and_run_test(void) +{ + char path_tmp[MAX_PATH] = {0}; + char path_manifest_dll[MAX_PATH] = {0}; + char path_manifest_exe[MAX_PATH] = {0}; + BOOL success; + ACTCTXA context = {0}; + ULONG_PTR cookie; + HANDLE handle_context = 0; + + GetTempPathA(MAX_PATH, path_tmp); + + if (!write_resource_file(path_tmp, "comtest_exe.manifest", "exe.manifest", path_manifest_exe)) + { + ok(0, "Failed to create file for testing\n"); + goto cleanup; + } + + if (!write_resource_file(path_tmp, "comtest_dll.manifest", "comtest.manifest", path_manifest_dll)) + { + ok(0, "Failed to create file for testing\n"); + goto cleanup; + } + + context.cbSize = sizeof(ACTCTXA); + context.lpSource = path_manifest_exe; + context.lpAssemblyDirectory = path_tmp; + context.dwFlags = ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID; + + handle_context = CreateActCtxA(&context); + ok(handle_context != NULL && handle_context != INVALID_HANDLE_VALUE, "CreateActCtxA failed: %d\n", GetLastError()); + + if (handle_context == NULL || handle_context == INVALID_HANDLE_VALUE) + { + ok(0, "Failed to create activation context\n"); + goto cleanup; + } + + success = ActivateActCtx(handle_context, &cookie); + ok(success, "ActivateActCtx failed: %d\n", GetLastError()); + + run_test(); + +cleanup: + if (handle_context != NULL && handle_context != INVALID_HANDLE_VALUE) + { + success = DeactivateActCtx(0, cookie); + ok(success, "DeactivateActCtx failed: %d\n", GetLastError()); + ReleaseActCtx(handle_context); + } + if (*path_manifest_exe) + { + success = DeleteFileA(path_manifest_exe); + ok(success, "DeleteFileA failed: %d\n", GetLastError()); + } + if(*path_manifest_dll) + { + success = DeleteFileA(path_manifest_dll); + ok(success, "DeleteFileA failed: %d\n", GetLastError()); + } +} + +static void run_child_process(void) +{ + char cmdline[MAX_PATH]; + char exe[MAX_PATH]; + char **argv; + PROCESS_INFORMATION pi; + STARTUPINFOA si = { 0 }; + BOOL ret; + + winetest_get_mainargs(&argv); + + if (strstr(argv[0], ".exe")) + sprintf(exe, "%s", argv[0]); + else + sprintf(exe, "%s.exe", argv[0]); + sprintf(cmdline, ""%s" %s %s", argv[0], argv[1], "subtest"); + + si.cb = sizeof(si); + ret = CreateProcessA(exe, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); + ok(ret, "Could not create process: %u\n", GetLastError()); + + winetest_wait_child_process(pi.hProcess); + + CloseHandle(pi.hThread); + CloseHandle(pi.hProcess); +} + +static void test_SxsLookupClrGuid(void) +{ + SIZE_T buffer_size; + BOOL ret; + + ret = pSxsLookupClrGuid(SXS_LOOKUP_CLR_GUID_FIND_CLR_CLASS, (GUID*)&CLSID_Test, NULL, NULL, 0, &buffer_size); + ok(ret == FALSE, "Expected FALSE, got %d\n", ret); + ok(GetLastError() == ERROR_NOT_FOUND, "Expected ERROR_NOT_FOUND, got %d\n", GetLastError()); + + run_child_process(); +} + +START_TEST(sxs) +{ + int argc; + char **argv; + + pSxsLookupClrGuid = (void *)GetProcAddress(LoadLibraryA("sxs.dll"), "SxsLookupClrGuid"); + + argc = winetest_get_mainargs(&argv); + if (argc > 2) + { + prepare_and_run_test(); + return; + } + + test_SxsLookupClrGuid(); +}