mscoree/tests: Create csc.exe process as detached.
[wine.git] / dlls / mscoree / tests / mscoree.c
blob7da40d928873435ab2513ccffe8fc8ff4bb138c3
1 /*
2 * Copyright 2010 Louis Lenders
3 * Copyright 2011 André Hentschel
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 #include <stdio.h>
22 #define COBJMACROS
24 #include "corerror.h"
25 #include "mscoree.h"
26 #include "metahost.h"
27 #include "shlwapi.h"
28 #include "initguid.h"
29 #include "wine/test.h"
31 #if !defined(__i386__) && !defined(__x86_64__)
32 static int has_mono = 0;
33 #else
34 static int has_mono = 1;
35 #endif
37 DEFINE_GUID(IID__AppDomain, 0x05f696dc,0x2b29,0x3663,0xad,0x8b,0xc4,0x38,0x9c,0xf2,0xa7,0x13);
39 static const WCHAR v4_0[] = {'v','4','.','0','.','3','0','3','1','9',0};
41 static HMODULE hmscoree;
43 static HRESULT (WINAPI *pGetCORVersion)(LPWSTR, DWORD, DWORD*);
44 static HRESULT (WINAPI *pCorIsLatestSvc)(INT*, INT*);
45 static HRESULT (WINAPI *pGetCORSystemDirectory)(LPWSTR, DWORD, DWORD*);
46 static HRESULT (WINAPI *pGetRequestedRuntimeInfo)(LPCWSTR, LPCWSTR, LPCWSTR, DWORD, DWORD, LPWSTR, DWORD, DWORD*, LPWSTR, DWORD, DWORD*);
47 static HRESULT (WINAPI *pLoadLibraryShim)(LPCWSTR, LPCWSTR, LPVOID, HMODULE*);
48 static HRESULT (WINAPI *pCreateConfigStream)(LPCWSTR, IStream**);
49 static HRESULT (WINAPI *pCreateInterface)(REFCLSID, REFIID, VOID**);
50 static HRESULT (WINAPI *pCLRCreateInstance)(REFCLSID, REFIID, VOID**);
52 static BOOL no_legacy_runtimes;
54 static BOOL init_functionpointers(void)
56 hmscoree = LoadLibraryA("mscoree.dll");
58 if (!hmscoree)
60 win_skip("mscoree.dll not available\n");
61 return FALSE;
64 pGetCORVersion = (void *)GetProcAddress(hmscoree, "GetCORVersion");
65 pCorIsLatestSvc = (void *)GetProcAddress(hmscoree, "CorIsLatestSvc");
66 pGetCORSystemDirectory = (void *)GetProcAddress(hmscoree, "GetCORSystemDirectory");
67 pGetRequestedRuntimeInfo = (void *)GetProcAddress(hmscoree, "GetRequestedRuntimeInfo");
68 pLoadLibraryShim = (void *)GetProcAddress(hmscoree, "LoadLibraryShim");
69 pCreateConfigStream = (void *)GetProcAddress(hmscoree, "CreateConfigStream");
70 pCreateInterface = (void *)GetProcAddress(hmscoree, "CreateInterface");
71 pCLRCreateInstance = (void *)GetProcAddress(hmscoree, "CLRCreateInstance");
73 if (!pGetCORVersion || !pGetCORSystemDirectory || !pGetRequestedRuntimeInfo || !pLoadLibraryShim ||
74 !pCreateInterface || !pCLRCreateInstance || !pCorIsLatestSvc
77 win_skip("functions not available\n");
78 FreeLibrary(hmscoree);
79 return FALSE;
82 return TRUE;
85 static int check_runtime(void)
87 ICLRMetaHost *metahost;
88 ICLRRuntimeInfo *runtimeinfo;
89 ICorRuntimeHost *runtimehost;
90 HRESULT hr;
92 if (!pCLRCreateInstance)
94 win_skip("Function CLRCreateInstance not found.\n");
95 return 1;
98 hr = pCLRCreateInstance(&CLSID_CLRMetaHost, &IID_ICLRMetaHost, (void **)&metahost);
99 if (hr == E_NOTIMPL)
101 win_skip("CLRCreateInstance not implemented\n");
102 return 1;
104 ok(SUCCEEDED(hr), "CLRCreateInstance failed, hr=%#.8lx\n", hr);
105 if (FAILED(hr))
106 return 1;
108 hr = ICLRMetaHost_GetRuntime(metahost, v4_0, &IID_ICLRRuntimeInfo, (void **)&runtimeinfo);
109 ok(SUCCEEDED(hr), "ICLRMetaHost::GetRuntime failed, hr=%#.8lx\n", hr);
110 if (FAILED(hr))
111 return 1;
113 hr = ICLRRuntimeInfo_GetInterface(runtimeinfo, &CLSID_CorRuntimeHost, &IID_ICorRuntimeHost,
114 (void **)&runtimehost);
115 todo_wine_if(!has_mono) ok(SUCCEEDED(hr), "ICLRRuntimeInfo::GetInterface failed, hr=%#.8lx\n", hr);
116 if (FAILED(hr))
117 return 1;
119 hr = ICorRuntimeHost_Start(runtimehost);
120 ok(SUCCEEDED(hr), "ICorRuntimeHost::Start failed, hr=%#.8lx\n", hr);
121 if (FAILED(hr))
122 return 1;
124 ICorRuntimeHost_Release(runtimehost);
126 ICLRRuntimeInfo_Release(runtimeinfo);
128 ICLRMetaHost_ExitProcess(metahost, 0);
130 ok(0, "ICLRMetaHost_ExitProcess is not supposed to return\n");
131 return 1;
134 static BOOL runtime_is_usable(void)
136 static const char cmdline_format[] = "\"%s\" mscoree check_runtime";
137 char** argv;
138 char cmdline[MAX_PATH + sizeof(cmdline_format)];
139 STARTUPINFOA si = {0};
140 PROCESS_INFORMATION pi;
141 BOOL ret;
142 DWORD exitcode;
144 winetest_get_mainargs(&argv);
146 sprintf(cmdline, cmdline_format, argv[0]);
148 si.cb = sizeof(si);
150 ret = CreateProcessA(NULL, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
151 ok(ret, "CreateProcessA failed\n");
152 if (!ret)
153 return FALSE;
155 CloseHandle(pi.hThread);
157 WaitForSingleObject(pi.hProcess, INFINITE);
159 ret = GetExitCodeProcess(pi.hProcess, &exitcode);
160 CloseHandle(pi.hProcess);
162 ok(ret, "GetExitCodeProcess failed\n");
164 if (!ret || exitcode != 0)
166 todo_wine_if(!has_mono) win_skip(".NET 4.0 runtime is not usable\n");
167 return FALSE;
170 return TRUE;
173 static void test_versioninfo(void)
175 const WCHAR v9_0[] = {'v','9','.','0','.','3','0','3','1','9',0};
176 const WCHAR v2_0cap[] = {'V','2','.','0','.','5','0','7','2','7',0};
177 const WCHAR v2_0[] = {'v','2','.','0','.','5','0','7','2','7',0};
178 const WCHAR v2_0_0[] = {'v','2','.','0','.','0',0};
179 const WCHAR v1_1[] = {'v','1','.','1','.','4','3','2','2',0};
180 const WCHAR v1_1_0[] = {'v','1','.','1','.','0',0};
182 WCHAR version[MAX_PATH];
183 WCHAR path[MAX_PATH];
184 DWORD size, path_len;
185 HRESULT hr;
187 if (0) /* crashes on <= w2k3 */
189 hr = pGetCORVersion(NULL, MAX_PATH, &size);
190 ok(hr == E_POINTER,"GetCORVersion returned %08lx\n", hr);
193 hr = pGetCORVersion(version, 1, &size);
194 if (hr == CLR_E_SHIM_RUNTIME)
196 no_legacy_runtimes = TRUE;
197 win_skip("No legacy .NET runtimes are installed\n");
198 return;
201 ok(hr == E_NOT_SUFFICIENT_BUFFER, "GetCORVersion returned %08lx\n", hr);
203 hr = pGetCORVersion(version, MAX_PATH, &size);
204 ok(hr == S_OK,"GetCORVersion returned %08lx\n", hr);
206 trace("latest installed .net runtime: %s\n", wine_dbgstr_w(version));
208 hr = pGetCORSystemDirectory(path, MAX_PATH , &size);
209 todo_wine_if(!has_mono) ok(hr == S_OK, "GetCORSystemDirectory returned %08lx\n", hr);
210 /* size includes terminating null-character */
211 todo_wine_if(!has_mono) ok(size == (lstrlenW(path) + 1),"size is %ld instead of %d\n", size, (lstrlenW(path) + 1));
213 path_len = size;
215 hr = pGetCORSystemDirectory(path, path_len-1 , &size);
216 todo_wine_if(!has_mono) ok(hr == E_NOT_SUFFICIENT_BUFFER, "GetCORSystemDirectory returned %08lx\n", hr);
218 if (0) /* crashes on <= w2k3 */
220 hr = pGetCORSystemDirectory(NULL, MAX_PATH , &size);
221 ok(hr == E_NOT_SUFFICIENT_BUFFER, "GetCORSystemDirectory returned %08lx\n", hr);
224 hr = pGetCORSystemDirectory(path, MAX_PATH , NULL);
225 ok(hr == E_POINTER,"GetCORSystemDirectory returned %08lx\n", hr);
227 trace("latest installed .net installed in directory: %s\n", wine_dbgstr_w(path));
229 /* test GetRequestedRuntimeInfo, first get info about different versions of runtime */
230 hr = pGetRequestedRuntimeInfo( NULL, v2_0, NULL, 0, 0, path, MAX_PATH, &path_len, version, MAX_PATH, &size);
232 if(hr == CLR_E_SHIM_RUNTIME) return; /* skipping rest of tests on win2k as .net 2.0 not installed */
234 todo_wine_if(!has_mono) ok(hr == S_OK, "GetRequestedRuntimeInfo returned %08lx\n", hr);
235 trace(" installed in directory %s is .net version %s\n", wine_dbgstr_w(path), wine_dbgstr_w(version));
237 hr = pGetRequestedRuntimeInfo( NULL, v1_1, NULL, 0, 0, path, MAX_PATH, &path_len, version, MAX_PATH, &size);
238 todo_wine_if(!has_mono) ok(hr == S_OK || hr == CLR_E_SHIM_RUNTIME /*v1_1 not installed*/, "GetRequestedRuntimeInfo returned %08lx\n", hr);
239 if(hr == S_OK)
240 trace(" installed in directory %s is .net version %s\n", wine_dbgstr_w(path), wine_dbgstr_w(version));
241 /* version number NULL not allowed without RUNTIME_INFO_UPGRADE_VERSION flag */
242 hr = pGetRequestedRuntimeInfo( NULL, NULL, NULL, 0, 0, path, MAX_PATH, &path_len, version, MAX_PATH, &size);
243 ok(hr == CLR_E_SHIM_RUNTIME, "GetRequestedRuntimeInfo returned %08lx\n", hr);
244 /* with RUNTIME_INFO_UPGRADE_VERSION flag and version number NULL, latest installed version is returned */
245 hr = pGetRequestedRuntimeInfo( NULL, NULL, NULL, 0, RUNTIME_INFO_UPGRADE_VERSION, path, MAX_PATH, &path_len, version, MAX_PATH, &size);
246 todo_wine_if(!has_mono) ok(hr == S_OK, "GetRequestedRuntimeInfo returned %08lx\n", hr);
248 hr = pGetRequestedRuntimeInfo( NULL, v2_0, NULL, 0, 0, path, 1, &path_len, version, MAX_PATH, &size);
249 todo_wine_if(!has_mono) ok(hr == E_NOT_SUFFICIENT_BUFFER, "GetRequestedRuntimeInfo returned %08lx\n", hr);
251 /* if one of the buffers is NULL, the other one is still happily filled */
252 memset(version, 0, sizeof(version));
253 hr = pGetRequestedRuntimeInfo( NULL, v2_0, NULL, 0, 0, NULL, MAX_PATH, &path_len, version, MAX_PATH, &size);
254 todo_wine_if(!has_mono) ok(hr == S_OK, "GetRequestedRuntimeInfo returned %08lx\n", hr);
255 ok(!wcscmp(version, v2_0), "version is %s , expected %s\n", wine_dbgstr_w(version), wine_dbgstr_w(v2_0));
256 /* With NULL-pointer for bufferlength, the buffer itself still gets filled with correct string */
257 memset(version, 0, sizeof(version));
258 hr = pGetRequestedRuntimeInfo( NULL, v2_0, NULL, 0, 0, path, MAX_PATH, &path_len, version, MAX_PATH, NULL);
259 todo_wine_if(!has_mono) ok(hr == S_OK, "GetRequestedRuntimeInfo returned %08lx\n", hr);
260 ok(!wcscmp(version, v2_0), "version is %s , expected %s\n", wine_dbgstr_w(version), wine_dbgstr_w(v2_0));
262 memset(version, 0, sizeof(version));
263 hr = pGetRequestedRuntimeInfo( NULL, v2_0cap, NULL, 0, 0, path, MAX_PATH, &path_len, version, MAX_PATH, NULL);
264 todo_wine_if(!has_mono) ok(hr == S_OK, "GetRequestedRuntimeInfo returned %08lx\n", hr);
265 ok(!wcscmp(version, v2_0cap), "version is %s , expected %s\n", wine_dbgstr_w(version), wine_dbgstr_w(v2_0cap));
267 /* Invalid Version and RUNTIME_INFO_UPGRADE_VERSION flag*/
268 memset(version, 0, sizeof(version));
269 hr = pGetRequestedRuntimeInfo( NULL, v1_1, NULL, 0, RUNTIME_INFO_UPGRADE_VERSION, path, MAX_PATH, &path_len, version, MAX_PATH, NULL);
270 todo_wine_if(!has_mono) ok(hr == S_OK || hr == CLR_E_SHIM_RUNTIME , "GetRequestedRuntimeInfo returned %08lx\n", hr);
271 if(hr == S_OK)
273 /* .NET 1.1 may not be installed. */
274 ok(!wcscmp(version, v1_1) || !wcscmp(version, v2_0),
275 "version is %s , expected %s or %s\n", wine_dbgstr_w(version), wine_dbgstr_w(v1_1), wine_dbgstr_w(v2_0));
279 memset(version, 0, sizeof(version));
280 hr = pGetRequestedRuntimeInfo( NULL, v9_0, NULL, 0, RUNTIME_INFO_UPGRADE_VERSION, path, MAX_PATH, &path_len, version, MAX_PATH, NULL);
281 ok(hr == CLR_E_SHIM_RUNTIME, "GetRequestedRuntimeInfo returned %08lx\n", hr);
283 memset(version, 0, sizeof(version));
284 hr = pGetRequestedRuntimeInfo( NULL, v1_1_0, NULL, 0, 0, path, MAX_PATH, &path_len, version, MAX_PATH, NULL);
285 ok(hr == CLR_E_SHIM_RUNTIME, "GetRequestedRuntimeInfo returned %08lx\n", hr);
287 memset(version, 0, sizeof(version));
288 hr = pGetRequestedRuntimeInfo( NULL, v1_1_0, NULL, 0, RUNTIME_INFO_UPGRADE_VERSION, path, MAX_PATH, &path_len, version, MAX_PATH, NULL);
289 todo_wine_if(!has_mono) ok(hr == S_OK, "GetRequestedRuntimeInfo returned %08lx\n", hr);
290 ok(!wcscmp(version, v2_0), "version is %s , expected %s\n", wine_dbgstr_w(version), wine_dbgstr_w(v2_0));
292 memset(version, 0, sizeof(version));
293 hr = pGetRequestedRuntimeInfo( NULL, v2_0_0, NULL, 0, 0, path, MAX_PATH, &path_len, version, MAX_PATH, NULL);
294 ok(hr == CLR_E_SHIM_RUNTIME, "GetRequestedRuntimeInfo returned %08lx\n", hr);
296 memset(version, 0, sizeof(version));
297 hr = pGetRequestedRuntimeInfo( NULL, v2_0_0, NULL, 0, RUNTIME_INFO_UPGRADE_VERSION, path, MAX_PATH, &path_len, version, MAX_PATH, NULL);
298 todo_wine_if(!has_mono) ok(hr == S_OK, "GetRequestedRuntimeInfo returned %08lx\n", hr);
299 ok(!wcscmp(version, v2_0), "version is %s , expected %s\n", wine_dbgstr_w(version), wine_dbgstr_w(v2_0));
301 hr = pCorIsLatestSvc(NULL, NULL);
302 ok(hr == E_POINTER, "CorIsLatestSvc returned %08lx\n", hr);
305 static void test_loadlibraryshim(void)
307 const WCHAR v2_0[] = {'v','2','.','0','.','5','0','7','2','7',0};
308 const WCHAR v1_1[] = {'v','1','.','1','.','4','3','2','2',0};
309 const WCHAR vbogus[] = {'v','b','o','g','u','s',0};
310 const WCHAR fusion[] = {'f','u','s','i','o','n',0};
311 const WCHAR fusiondll[] = {'f','u','s','i','o','n','.','d','l','l',0};
312 const WCHAR nosuchdll[] = {'j','n','v','n','l','.','d','l','l',0};
313 const WCHAR gdidll[] = {'g','d','i','3','2','.','d','l','l',0};
314 HRESULT hr;
315 const WCHAR *latest = NULL;
316 CHAR latestA[MAX_PATH];
317 HMODULE hdll;
318 CHAR dllpath[MAX_PATH];
320 if (no_legacy_runtimes)
322 win_skip("No legacy .NET runtimes are installed\n");
323 return;
326 hr = pLoadLibraryShim(fusion, v1_1, NULL, &hdll);
327 ok(hr == S_OK || hr == E_HANDLE, "LoadLibraryShim failed, hr=%lx\n", hr);
328 if (SUCCEEDED(hr))
330 latest = v1_1;
332 GetModuleFileNameA(hdll, dllpath, MAX_PATH);
334 todo_wine_if(!has_mono) ok(StrStrIA(dllpath, "v1.1.4322") != 0, "incorrect fusion.dll path %s\n", dllpath);
335 ok(StrStrIA(dllpath, "fusion.dll") != 0, "incorrect fusion.dll path %s\n", dllpath);
337 FreeLibrary(hdll);
340 hr = pLoadLibraryShim(fusion, v2_0, NULL, &hdll);
341 ok(hr == S_OK || hr == E_HANDLE, "LoadLibraryShim failed, hr=%lx\n", hr);
342 if (SUCCEEDED(hr))
344 latest = v2_0;
346 GetModuleFileNameA(hdll, dllpath, MAX_PATH);
348 todo_wine_if(!has_mono) ok(StrStrIA(dllpath, "v2.0.50727") != 0, "incorrect fusion.dll path %s\n", dllpath);
349 ok(StrStrIA(dllpath, "fusion.dll") != 0, "incorrect fusion.dll path %s\n", dllpath);
351 FreeLibrary(hdll);
354 hr = pLoadLibraryShim(fusion, v4_0, NULL, &hdll);
355 ok(hr == S_OK || hr == E_HANDLE, "LoadLibraryShim failed, hr=%lx\n", hr);
356 if (SUCCEEDED(hr))
358 /* LoadLibraryShim with a NULL version prefers 2.0 and earlier */
359 if (!latest)
360 latest = v4_0;
362 GetModuleFileNameA(hdll, dllpath, MAX_PATH);
364 todo_wine_if(!has_mono) ok(StrStrIA(dllpath, "v4.0.30319") != 0, "incorrect fusion.dll path %s\n", dllpath);
365 ok(StrStrIA(dllpath, "fusion.dll") != 0, "incorrect fusion.dll path %s\n", dllpath);
367 FreeLibrary(hdll);
370 hr = pLoadLibraryShim(fusion, vbogus, NULL, &hdll);
371 ok(hr == E_HANDLE, "LoadLibraryShim failed, hr=%lx\n", hr);
372 if (SUCCEEDED(hr))
373 FreeLibrary(hdll);
375 WideCharToMultiByte(CP_ACP, 0, latest, -1, latestA, MAX_PATH, NULL, NULL);
377 hr = pLoadLibraryShim(fusion, NULL, NULL, &hdll);
378 ok(hr == S_OK, "LoadLibraryShim failed, hr=%lx\n", hr);
379 if (SUCCEEDED(hr))
381 GetModuleFileNameA(hdll, dllpath, MAX_PATH);
383 if (latest)
384 todo_wine_if(!has_mono) ok(StrStrIA(dllpath, latestA) != 0, "incorrect fusion.dll path %s\n", dllpath);
385 ok(StrStrIA(dllpath, "fusion.dll") != 0, "incorrect fusion.dll path %s\n", dllpath);
387 FreeLibrary(hdll);
390 hr = pLoadLibraryShim(fusiondll, NULL, NULL, &hdll);
391 ok(hr == S_OK, "LoadLibraryShim failed, hr=%lx\n", hr);
392 if (SUCCEEDED(hr))
394 GetModuleFileNameA(hdll, dllpath, MAX_PATH);
396 if (latest)
397 todo_wine_if(!has_mono) ok(StrStrIA(dllpath, latestA) != 0, "incorrect fusion.dll path %s\n", dllpath);
398 ok(StrStrIA(dllpath, "fusion.dll") != 0, "incorrect fusion.dll path %s\n", dllpath);
400 FreeLibrary(hdll);
403 hr = pLoadLibraryShim(nosuchdll, latest, NULL, &hdll);
404 ok(hr == E_HANDLE, "LoadLibraryShim failed, hr=%lx\n", hr);
405 if (SUCCEEDED(hr))
406 FreeLibrary(hdll);
408 hr = pLoadLibraryShim(gdidll, latest, NULL, &hdll);
409 ok(hr == E_HANDLE, "LoadLibraryShim failed, hr=%lx\n", hr);
410 if (SUCCEEDED(hr))
411 FreeLibrary(hdll);
414 static const char xmldata[] =
415 "<?xml version=\"1.0\" ?>\n"
416 "<!DOCTYPE Config>\n"
417 "<Configuration>\n"
418 " <Name>Test</Name>\n"
419 " <Value>1234</Value>\n"
420 "</Configuration>";
422 static void create_xml_file(LPCWSTR filename)
424 DWORD dwNumberOfBytesWritten;
425 HANDLE hfile = CreateFileW(filename, GENERIC_WRITE, 0, NULL,
426 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
427 ok(hfile != INVALID_HANDLE_VALUE, "File creation failed\n");
428 WriteFile(hfile, xmldata, sizeof(xmldata) - 1, &dwNumberOfBytesWritten, NULL);
429 CloseHandle(hfile);
432 static void test_createconfigstream(void)
434 IStream *stream = NULL;
435 WCHAR file[] = {'c', 'o', 'n', 'f', '.', 'x', 'm', 'l', 0};
436 WCHAR nonexistent[] = {'n', 'o', 'n', 'e', 'x', 'i', 's', 't', '.', 'x', 'm', 'l', 0};
437 WCHAR path[MAX_PATH];
438 HRESULT hr;
439 char buffer[256] = {0};
441 if (!pCreateConfigStream)
443 win_skip("CreateConfigStream not available\n");
444 return;
447 create_xml_file(file);
448 GetFullPathNameW(file, MAX_PATH, path, NULL);
450 hr = pCreateConfigStream(NULL, &stream);
451 ok(hr == E_FAIL ||
452 broken(hr == HRESULT_FROM_WIN32(ERROR_PROC_NOT_FOUND)) || /* some WinXP, Win2K3 and Win7 */
453 broken(hr == S_OK && !stream), /* some Win2K3 */
454 "CreateConfigStream returned %lx\n", hr);
456 hr = pCreateConfigStream(path, NULL);
457 ok(hr == COR_E_NULLREFERENCE, "CreateConfigStream returned %lx\n", hr);
459 hr = pCreateConfigStream(NULL, NULL);
460 ok(hr == COR_E_NULLREFERENCE, "CreateConfigStream returned %lx\n", hr);
462 hr = pCreateConfigStream(nonexistent, &stream);
463 ok(hr == COR_E_FILENOTFOUND, "CreateConfigStream returned %lx\n", hr);
464 ok(stream == NULL, "Expected stream to be NULL\n");
466 hr = pCreateConfigStream(path, &stream);
467 ok(hr == S_OK, "CreateConfigStream failed, hr=%lx\n", hr);
468 ok(stream != NULL, "Expected non-NULL stream\n");
470 if (stream)
472 DWORD count;
473 LARGE_INTEGER pos;
474 ULARGE_INTEGER size;
475 IStream *stream2 = NULL;
476 ULONG ref;
478 hr = IStream_Read(stream, buffer, strlen(xmldata), &count);
479 ok(hr == S_OK, "IStream_Read failed, hr=%lx\n", hr);
480 ok(count == strlen(xmldata), "wrong count: %lu\n", count);
481 ok(!strcmp(buffer, xmldata), "Strings do not match\n");
483 hr = IStream_Read(stream, buffer, sizeof(buffer), &count);
484 ok(hr == S_OK, "IStream_Read failed, hr=%lx\n", hr);
485 ok(!count, "wrong count: %lu\n", count);
487 hr = IStream_Write(stream, xmldata, strlen(xmldata), &count);
488 ok(hr == E_FAIL, "IStream_Write returned hr=%lx\n", hr);
490 pos.QuadPart = strlen(xmldata);
491 hr = IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL);
492 ok(hr == E_NOTIMPL, "IStream_Seek returned hr=%lx\n", hr);
494 size.QuadPart = strlen(xmldata);
495 hr = IStream_SetSize(stream, size);
496 ok(hr == E_NOTIMPL, "IStream_SetSize returned hr=%lx\n", hr);
498 hr = IStream_Clone(stream, &stream2);
499 ok(hr == E_NOTIMPL, "IStream_Clone returned hr=%lx\n", hr);
501 hr = IStream_Commit(stream, STGC_DEFAULT);
502 ok(hr == E_NOTIMPL, "IStream_Commit returned hr=%lx\n", hr);
504 hr = IStream_Revert(stream);
505 ok(hr == E_NOTIMPL, "IStream_Revert returned hr=%lx\n", hr);
507 ref = IStream_Release(stream);
508 ok(!ref, "IStream_Release returned %lu\n", ref);
510 DeleteFileW(file);
513 static void test_createinstance(void)
515 HRESULT hr;
516 ICLRMetaHost *host;
518 if (no_legacy_runtimes)
520 /* If we don't have 1.x or 2.0 runtimes, we should at least have .NET 4. */
521 ok(pCreateInterface != NULL, "no legacy runtimes or .NET 4 interfaces available\n");
524 if(!pCreateInterface)
526 win_skip("Function CreateInterface not found.\n");
527 return;
530 hr = pCreateInterface(&CLSID_CLRMetaHost, &IID_ICLRMetaHost, (void**)&host);
531 if(SUCCEEDED(hr))
533 ICLRMetaHost_Release(host);
535 else
537 win_skip(".NET 4 not installed.\n");
541 static BOOL write_resource(const WCHAR *resource, const WCHAR *filename)
543 HANDLE file;
544 HRSRC rsrc;
545 void *data;
546 DWORD size;
547 BOOL ret;
549 rsrc = FindResourceW(GetModuleHandleW(NULL), resource, MAKEINTRESOURCEW(RT_RCDATA));
550 if (!rsrc) return FALSE;
552 data = LockResource(LoadResource(GetModuleHandleA(NULL), rsrc));
553 if (!data) return FALSE;
555 size = SizeofResource(GetModuleHandleA(NULL), rsrc);
556 if (!size) return FALSE;
558 file = CreateFileW(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
559 if (file == INVALID_HANDLE_VALUE) return FALSE;
561 ret = WriteFile(file, data, size, &size, NULL);
562 CloseHandle(file);
563 return ret;
566 static BOOL compile_cs(const WCHAR *source, const WCHAR *target, const WCHAR *type, const WCHAR *args)
568 static const WCHAR *csc = L"C:\\windows\\Microsoft.NET\\Framework\\v2.0.50727\\csc.exe";
569 WCHAR cmdline[2 * MAX_PATH + 74];
570 PROCESS_INFORMATION pi;
571 STARTUPINFOW si = { 0 };
572 BOOL ret;
574 if (!PathFileExistsW(csc))
576 skip("Can't find csc.exe\n");
577 return FALSE;
580 swprintf(cmdline, ARRAY_SIZE(cmdline), L"%s /t:%s %s /out:\"%s\" \"%s\"", csc, type, args, target, source);
582 si.cb = sizeof(si);
583 ret = CreateProcessW(csc, cmdline, NULL, NULL, FALSE, DETACHED_PROCESS, NULL, NULL, &si, &pi);
584 ok(ret, "Could not create process: %lu\n", GetLastError());
586 wait_child_process(pi.hProcess);
587 CloseHandle(pi.hThread);
588 CloseHandle(pi.hProcess);
590 ret = PathFileExistsW(target);
591 ok(ret, "Compilation failed\n");
593 return ret;
596 static void test_loadpaths_execute(const WCHAR *exe_name, const WCHAR *dll_name, const WCHAR *cfg_name,
597 const WCHAR *dll_dest, BOOL expect_failure, BOOL todo)
599 WCHAR tmp[MAX_PATH], tmpdir[MAX_PATH], tmpexe[MAX_PATH], tmpcfg[MAX_PATH], tmpdll[MAX_PATH];
600 PROCESS_INFORMATION pi;
601 STARTUPINFOW si = { 0 };
602 WCHAR *ptr, *end;
603 DWORD exit_code = 0xdeadbeef;
604 LUID id;
605 BOOL ret;
607 GetTempPathW(MAX_PATH, tmp);
608 ret = AllocateLocallyUniqueId(&id);
609 ok(ret, "AllocateLocallyUniqueId failed: %lu\n", GetLastError());
610 ret = GetTempFileNameW(tmp, L"loadpaths", id.LowPart, tmpdir);
611 ok(ret, "GetTempFileNameW failed: %lu\n", GetLastError());
613 ret = CreateDirectoryW(tmpdir, NULL);
614 ok(ret, "CreateDirectoryW(%s) failed: %lu\n", debugstr_w(tmpdir), GetLastError());
616 wcscpy(tmpexe, tmpdir);
617 PathAppendW(tmpexe, exe_name);
618 ret = CopyFileW(exe_name, tmpexe, FALSE);
619 ok(ret, "CopyFileW(%s) failed: %lu\n", debugstr_w(tmpexe), GetLastError());
621 if (cfg_name)
623 wcscpy(tmpcfg, tmpdir);
624 PathAppendW(tmpcfg, cfg_name);
625 ret = CopyFileW(cfg_name, tmpcfg, FALSE);
626 ok(ret, "CopyFileW(%s) failed: %lu\n", debugstr_w(tmpcfg), GetLastError());
629 ptr = tmpdir + wcslen(tmpdir);
630 PathAppendW(tmpdir, dll_dest);
631 while (*ptr && (ptr = wcschr(ptr + 1, '\\')))
633 *ptr = '\0';
634 ret = CreateDirectoryW(tmpdir, NULL);
635 ok(ret, "CreateDirectoryW(%s) failed: %lu\n", debugstr_w(tmpdir), GetLastError());
636 *ptr = '\\';
639 wcscpy(tmpdll, tmpdir);
640 if ((ptr = wcsrchr(tmpdir, '\\'))) *ptr = '\0';
642 ret = CopyFileW(dll_name, tmpdll, FALSE);
643 ok(ret, "CopyFileW(%s) failed: %lu\n", debugstr_w(tmpdll), GetLastError());
645 si.cb = sizeof(si);
646 ret = CreateProcessW(tmpexe, tmpexe, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
647 ok(ret, "CreateProcessW(%s) failed: %lu\n", debugstr_w(tmpexe), GetLastError());
649 if (expect_failure) ret = WaitForSingleObject(pi.hProcess, 2000);
650 else
652 ret = WaitForSingleObject(pi.hProcess, 5000);
653 ok(ret == WAIT_OBJECT_0, "%s: WaitForSingleObject returned %d: %lu\n", debugstr_w(dll_dest), ret, GetLastError());
656 GetExitCodeProcess(pi.hProcess, &exit_code);
657 if (ret == WAIT_TIMEOUT) TerminateProcess(pi.hProcess, 0xdeadbeef);
658 CloseHandle(pi.hThread);
659 CloseHandle(pi.hProcess);
661 if (expect_failure) todo_wine_if(todo) ok(exit_code != 0, "%s: Succeeded to execute process\n", debugstr_w(dll_dest));
662 else ok(exit_code == 0, "%s: Failed to execute process\n", debugstr_w(dll_dest));
664 /* sometimes the failing process never returns, in which case cleaning up won't work */
665 if (ret == WAIT_TIMEOUT && expect_failure) return;
667 if (cfg_name)
669 ret = DeleteFileW(tmpcfg);
670 ok(ret, "DeleteFileW(%s) failed: %lu\n", debugstr_w(tmpcfg), GetLastError());
672 ret = DeleteFileW(tmpdll);
673 ok(ret, "DeleteFileW(%s) failed: %lu\n", debugstr_w(tmpdll), GetLastError());
674 ret = DeleteFileW(tmpexe);
675 ok(ret, "DeleteFileW(%s) failed: %lu\n", debugstr_w(tmpexe), GetLastError());
677 end = tmpdir + wcslen(tmp);
678 ptr = tmpdir + wcslen(tmpdir) - 1;
679 while (ptr > end && (ptr = wcsrchr(tmpdir, '\\')))
681 ret = RemoveDirectoryW(tmpdir);
682 ok(ret, "RemoveDirectoryW(%s) failed: %lu\n", debugstr_w(tmpdir), GetLastError());
683 *ptr = '\0';
687 static void test_loadpaths(BOOL neutral)
689 static const WCHAR *loadpaths[] = {L"", L"en", L"libloadpaths", L"en\\libloadpaths"};
690 static const WCHAR *dll_source = L"loadpaths.dll.cs";
691 static const WCHAR *dll_name = L"libloadpaths.dll";
692 static const WCHAR *exe_source = L"loadpaths.exe.cs";
693 static const WCHAR *exe_name = L"loadpaths.exe";
694 static const WCHAR *cfg_name = L"loadpaths.exe.config";
695 WCHAR tmp[MAX_PATH];
696 BOOL ret;
697 int i;
699 DeleteFileW(dll_source);
700 ret = write_resource(dll_source, dll_source);
701 ok(ret, "Could not write resource: %lu\n", GetLastError());
702 DeleteFileW(dll_name);
703 ret = compile_cs(dll_source, dll_name, L"library", neutral ? L"-define:NEUTRAL" : L"");
704 if (!ret) return;
705 ret = DeleteFileW(dll_source);
706 ok(ret, "DeleteFileW failed: %lu\n", GetLastError());
708 DeleteFileW(exe_source);
709 ret = write_resource(exe_source, exe_source);
710 ok(ret, "Could not write resource: %lu\n", GetLastError());
711 DeleteFileW(exe_name);
712 ret = compile_cs(exe_source, exe_name, L"winexe", L"/reference:libloadpaths.dll");
713 if (!ret) return;
714 ret = DeleteFileW(exe_source);
715 ok(ret, "DeleteFileW failed: %lu\n", GetLastError());
717 DeleteFileW(cfg_name);
718 ret = write_resource(cfg_name, cfg_name);
719 ok(ret, "Could not write resource: %lu\n", GetLastError());
721 for (i = 0; i < ARRAY_SIZE(loadpaths); ++i)
723 const WCHAR *path = loadpaths[i];
724 BOOL expect_failure = neutral ? wcsstr(path, L"en") != NULL
725 : wcsstr(path, L"en") == NULL;
727 wcscpy(tmp, path);
728 PathAppendW(tmp, dll_name);
729 test_loadpaths_execute(exe_name, dll_name, NULL, tmp, expect_failure, !neutral && !*path);
731 wcscpy(tmp, L"private");
732 if (*path) PathAppendW(tmp, path);
733 PathAppendW(tmp, dll_name);
735 test_loadpaths_execute(exe_name, dll_name, NULL, tmp, TRUE, FALSE);
736 test_loadpaths_execute(exe_name, dll_name, cfg_name, tmp, expect_failure, FALSE);
738 /* exe name for dll should work too */
739 if (*path)
741 wcscpy(tmp, path);
742 PathAppendW(tmp, dll_name);
743 wcscpy(tmp + wcslen(tmp) - 4, L".exe");
744 test_loadpaths_execute(exe_name, dll_name, NULL, tmp, expect_failure, FALSE);
747 wcscpy(tmp, L"private");
748 if (*path) PathAppendW(tmp, path);
749 PathAppendW(tmp, dll_name);
750 wcscpy(tmp + wcslen(tmp) - 4, L".exe");
752 test_loadpaths_execute(exe_name, dll_name, NULL, tmp, TRUE, FALSE);
753 test_loadpaths_execute(exe_name, dll_name, cfg_name, tmp, expect_failure, FALSE);
756 ret = DeleteFileW(cfg_name);
757 ok(ret, "DeleteFileW failed: %lu\n", GetLastError());
758 ret = DeleteFileW(exe_name);
759 ok(ret, "DeleteFileW failed: %lu\n", GetLastError());
760 ret = DeleteFileW(dll_name);
761 ok(ret, "DeleteFileW failed: %lu\n", GetLastError());
764 static void test_createdomain(void)
766 static const WCHAR test_name[] = {'t','e','s','t',0};
767 static const WCHAR test2_name[] = {'t','e','s','t','2',0};
768 ICLRMetaHost *metahost;
769 ICLRRuntimeInfo *runtimeinfo;
770 ICorRuntimeHost *runtimehost;
771 IUnknown *domain, *defaultdomain_unk, *defaultdomain, *newdomain_unk, *newdomain, *domainsetup,
772 *newdomain2_unk, *newdomain2;
773 HRESULT hr;
775 if (!pCLRCreateInstance)
777 win_skip("Function CLRCreateInstance not found.\n");
778 return;
781 hr = pCLRCreateInstance(&CLSID_CLRMetaHost, &IID_ICLRMetaHost, (void **)&metahost);
782 ok(SUCCEEDED(hr), "CLRCreateInstance failed, hr=%#.8lx\n", hr);
784 hr = ICLRMetaHost_GetRuntime(metahost, v4_0, &IID_ICLRRuntimeInfo, (void **)&runtimeinfo);
785 ok(SUCCEEDED(hr), "ICLRMetaHost::GetRuntime failed, hr=%#.8lx\n", hr);
787 hr = ICLRRuntimeInfo_GetInterface(runtimeinfo, &CLSID_CorRuntimeHost, &IID_ICorRuntimeHost,
788 (void **)&runtimehost);
789 ok(SUCCEEDED(hr), "ICLRRuntimeInfo::GetInterface failed, hr=%#.8lx\n", hr);
791 hr = ICorRuntimeHost_Start(runtimehost);
792 ok(SUCCEEDED(hr), "ICorRuntimeHost::Start failed, hr=%#.8lx\n", hr);
794 hr = ICorRuntimeHost_GetDefaultDomain(runtimehost, &domain);
795 ok(SUCCEEDED(hr), "ICorRuntimeHost::GetDefaultDomain failed, hr=%#.8lx\n", hr);
797 hr = IUnknown_QueryInterface(domain, &IID_IUnknown, (void **)&defaultdomain_unk);
798 ok(SUCCEEDED(hr), "COM object doesn't support IUnknown?!\n");
800 hr = IUnknown_QueryInterface(domain, &IID__AppDomain, (void **)&defaultdomain);
801 ok(SUCCEEDED(hr), "AppDomain object doesn't support _AppDomain interface\n");
803 IUnknown_Release(domain);
805 hr = ICorRuntimeHost_CreateDomain(runtimehost, test_name, NULL, &domain);
806 ok(SUCCEEDED(hr), "ICorRuntimeHost::CreateDomain failed, hr=%#.8lx\n", hr);
808 hr = IUnknown_QueryInterface(domain, &IID_IUnknown, (void **)&newdomain_unk);
809 ok(SUCCEEDED(hr), "COM object doesn't support IUnknown?!\n");
811 hr = IUnknown_QueryInterface(domain, &IID__AppDomain, (void **)&newdomain);
812 ok(SUCCEEDED(hr), "AppDomain object doesn't support _AppDomain interface\n");
814 IUnknown_Release(domain);
816 ok(defaultdomain_unk != newdomain_unk, "New and default domain objects are the same\n");
818 hr = ICorRuntimeHost_CreateDomainSetup(runtimehost, &domainsetup);
819 ok(SUCCEEDED(hr), "ICorRuntimeHost::CreateDomainSetup failed, hr=%#.8lx\n", hr);
821 hr = ICorRuntimeHost_CreateDomainEx(runtimehost, test2_name, domainsetup, NULL, &domain);
822 ok(SUCCEEDED(hr), "ICorRuntimeHost::CreateDomainEx failed, hr=%#.8lx\n", hr);
824 hr = IUnknown_QueryInterface(domain, &IID_IUnknown, (void **)&newdomain2_unk);
825 ok(SUCCEEDED(hr), "COM object doesn't support IUnknown?!\n");
827 hr = IUnknown_QueryInterface(domain, &IID__AppDomain, (void **)&newdomain2);
828 ok(SUCCEEDED(hr), "AppDomain object doesn't support _AppDomain interface\n");
830 IUnknown_Release(domain);
832 ok(defaultdomain_unk != newdomain2_unk, "New and default domain objects are the same\n");
833 ok(newdomain_unk != newdomain2_unk, "Both new domain objects are the same\n");
835 IUnknown_Release(newdomain2);
836 IUnknown_Release(newdomain2_unk);
837 IUnknown_Release(domainsetup);
838 IUnknown_Release(newdomain);
839 IUnknown_Release(newdomain_unk);
840 IUnknown_Release(defaultdomain);
841 IUnknown_Release(defaultdomain_unk);
843 ICorRuntimeHost_Release(runtimehost);
845 ICLRRuntimeInfo_Release(runtimeinfo);
847 ICLRMetaHost_Release(metahost);
850 START_TEST(mscoree)
852 int argc;
853 char** argv;
855 if (!init_functionpointers())
856 return;
858 argc = winetest_get_mainargs(&argv);
859 if (argc >= 3 && !strcmp(argv[2], "check_runtime"))
861 int result = check_runtime();
862 FreeLibrary(hmscoree);
863 exit(result);
866 test_versioninfo();
867 test_loadlibraryshim();
868 test_createconfigstream();
869 test_createinstance();
871 if (runtime_is_usable())
873 test_createdomain();
876 test_loadpaths(FALSE);
877 test_loadpaths(TRUE);
879 FreeLibrary(hmscoree);