| /* | |
| * 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; | |
| DEFINE_GUID(IID_ITest2, 0x50adb433, 0xf6c5, 0x3b30, 0x92,0x0a, 0x55,0x57,0x11,0x86,0x75,0x09); | |
| 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[2 * MAX_PATH + 74]; | |
| char path_temp[MAX_PATH]; | |
| 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: %lu\n", GetLastError()); | |
| wait_child_process(&pi); | |
| ret = PathFileExistsA(path_temp); | |
| ok(ret, "Compilation failed\n"); | |
| ret = MoveFileA(path_temp, dest_path); | |
| ok(ret, "Could not move %s to %s: %lu\n", path_temp, dest_path, GetLastError()); | |
| 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_if(!expect_success) | |
| ok(hr == result_expected, "Expected %lx, got %lx\n", result_expected, hr); | |
| if (hr == S_OK) | |
| { | |
| int i = 0; | |
| hr = ITest_Func(test, &i); | |
| ok(hr == S_OK, "Got %lx\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 %lx, got %lx\n", result_expected, hr); | |
| if (hr == S_OK) | |
| { | |
| ITest *test2 = NULL; | |
| hr = IClassFactory_CreateInstance(classFactory, NULL, &IID_ITest, (void **)&test2); | |
| todo_wine_if(!expect_success) | |
| ok(hr == S_OK, "Got %lx\n", hr); | |
| if (hr == S_OK) | |
| { | |
| int i = 0; | |
| hr = ITest_Func(test2, &i); | |
| ok(hr == S_OK, "Got %lx\n", hr); | |
| ok(i == 42, "Expected 42, got %d\n", i); | |
| ITest_Release(test2); | |
| } | |
| IClassFactory_Release(classFactory); | |
| } | |
| } | |
| static void run_registry_test(run_type run) | |
| { | |
| char buffer[256]; | |
| ITest *test = NULL; | |
| HRESULT hr, result_expected; | |
| IUnknown *unk = NULL; | |
| HKEY hkey; | |
| DWORD ret; | |
| int i = 0; | |
| if (run == run_type_exe_directory) result_expected = S_OK; | |
| else result_expected = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); | |
| sprintf(buffer, "CLSID\\%s", wine_dbgstr_guid(&CLSID_Test)); | |
| ret = RegCreateKeyA( HKEY_CLASSES_ROOT, buffer, &hkey ); | |
| if (ret == ERROR_ACCESS_DENIED && !IsUserAnAdmin()) | |
| { | |
| win_skip("cannot run the registry tests due to user not being admin\n"); | |
| RegCloseKey(hkey); | |
| return; | |
| } | |
| ok(ret == ERROR_SUCCESS, "RegCreateKeyA returned %lx\n", ret); | |
| ret = RegSetKeyValueA(hkey, "InprocServer32", NULL, REG_SZ, "mscoree.dll", 12); | |
| ok(ret == ERROR_SUCCESS, "RegSetKeyValueA returned %lx\n", ret); | |
| ret = RegSetKeyValueA(hkey, "InprocServer32", "Assembly", REG_SZ, "comtest, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", 63); | |
| ok(ret == ERROR_SUCCESS, "RegSetKeyValueA returned %lx\n", ret); | |
| ret = RegSetKeyValueA(hkey, "InprocServer32", "Class", REG_SZ, "DLL.Test", 9); | |
| ok(ret == ERROR_SUCCESS, "RegSetKeyValueA returned %lx\n", ret); | |
| ret = RegSetKeyValueA(hkey, "InprocServer32", "CodeBase", REG_SZ, "file:///U:/invalid/path/to/comtest.dll", 39); | |
| ok(ret == ERROR_SUCCESS, "RegSetKeyValueA returned %lx\n", ret); | |
| hr = CoCreateInstance(&CLSID_Test, NULL, CLSCTX_INPROC_SERVER, &IID_ITest, (void**)&test); | |
| todo_wine_if(result_expected != S_OK) | |
| ok(hr == result_expected, "Expected %lx, got %lx\n", result_expected, hr); | |
| if (hr == S_OK) | |
| { | |
| hr = ITest_Func(test, &i); | |
| ok(hr == S_OK, "Got %lx\n", hr); | |
| ok(i == 42, "Expected 42, got %d\n", i); | |
| hr = ITest_QueryInterface(test, &IID_ITest2, (void**)&unk); | |
| ok(hr == S_OK, "ITest_QueryInterface returned %lx\n", hr); | |
| if (hr == S_OK) IUnknown_Release(unk); | |
| ITest_Release(test); | |
| } | |
| RegDeleteKeyValueA(hkey, "InprocServer32", "CodeBase"); | |
| RegDeleteKeyValueA(hkey, "InprocServer32", "Class"); | |
| RegDeleteKeyValueA(hkey, "InprocServer32", "Assembly"); | |
| RegDeleteKeyValueA(hkey, "InprocServer32", NULL); | |
| RegDeleteKeyA(hkey, "InprocServer32"); | |
| RegCloseKey(hkey); | |
| } | |
| static void get_dll_path_for_run(char *path_dll, UINT path_dll_size, run_type run) | |
| { | |
| char path_tmp[MAX_PATH]; | |
| 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]; | |
| char path_tmp_manifest[MAX_PATH]; | |
| char path_dll[MAX_PATH]; | |
| char path_dll_source[MAX_PATH]; | |
| char path_manifest_dll[MAX_PATH]; | |
| char path_manifest_exe[MAX_PATH]; | |
| BOOL success; | |
| ACTCTXA context = {0}; | |
| ULONG_PTR cookie; | |
| HANDLE handle_context = 0; | |
| path_manifest_exe[0] = path_manifest_dll[0] = path_dll_source[0] = 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: %ld\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: %ld\n", run, GetLastError()); | |
| if (run == run_type_current_working_directory) | |
| SetCurrentDirectoryA(path_tmp); | |
| run_test(run == run_type_exe_directory); | |
| run_registry_test(run); | |
| cleanup: | |
| if (handle_context != NULL && handle_context != INVALID_HANDLE_VALUE) | |
| { | |
| success = DeactivateActCtx(0, cookie); | |
| ok(success, "run: %d, DeactivateActCtx failed: %ld\n", run, GetLastError()); | |
| ReleaseActCtx(handle_context); | |
| } | |
| if (*path_manifest_exe) | |
| { | |
| success = DeleteFileA(path_manifest_exe); | |
| ok(success, "run: %d, DeleteFileA failed: %ld\n", run, GetLastError()); | |
| } | |
| if(*path_manifest_dll) | |
| { | |
| success = DeleteFileA(path_manifest_dll); | |
| ok(success, "run: %d, DeleteFileA failed: %ld\n", run, GetLastError()); | |
| } | |
| if(*path_dll_source) | |
| { | |
| success = DeleteFileA(path_dll_source); | |
| ok(success, "run: %d, DeleteFileA failed: %ld\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]; | |
| 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: %ld\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: %lu\n", GetLastError()); | |
| wait_child_process(&pi); | |
| /* 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(); | |
| } |