357 lines
10 KiB
C
357 lines
10 KiB
C
![]() |
/*
|
||
|
* 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];
|
||
|
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];
|
||
|
|
||
|
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;
|
||
|
|
||
|
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];
|
||
|
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();
|
||
|
}
|