Release 1.5.29.
[wine] / dlls / mscoree / metahost.c
1 /*
2  * ICLRMetaHost - discovery and management of available .NET runtimes
3  *
4  * Copyright 2010 Vincent Povirk for CodeWeavers
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include <stdio.h>
22 #include <stdarg.h>
23 #include <assert.h>
24
25 #define COBJMACROS
26
27 #include "wine/unicode.h"
28 #include "wine/library.h"
29 #include "windef.h"
30 #include "winbase.h"
31 #include "winreg.h"
32 #include "ole2.h"
33
34 #include "corerror.h"
35 #include "cor.h"
36 #include "mscoree.h"
37 #include "corhdr.h"
38 #include "cordebug.h"
39 #include "metahost.h"
40 #include "fusion.h"
41 #include "wine/list.h"
42 #include "mscoree_private.h"
43
44 #include "wine/debug.h"
45
46 WINE_DEFAULT_DEBUG_CHANNEL( mscoree );
47
48 static const WCHAR net_20_subdir[] = {'2','.','0',0};
49 static const WCHAR net_40_subdir[] = {'4','.','0',0};
50
51 static const struct ICLRRuntimeInfoVtbl CLRRuntimeInfoVtbl;
52
53 #define NUM_RUNTIMES 3
54
55 static struct CLRRuntimeInfo runtimes[NUM_RUNTIMES] = {
56     {{&CLRRuntimeInfoVtbl}, net_20_subdir, 1, 1, 4322, 0},
57     {{&CLRRuntimeInfoVtbl}, net_20_subdir, 2, 0, 50727, 0},
58     {{&CLRRuntimeInfoVtbl}, net_40_subdir, 4, 0, 30319, 0}
59 };
60
61 static int runtimes_initialized;
62
63 static CRITICAL_SECTION runtime_list_cs;
64 static CRITICAL_SECTION_DEBUG runtime_list_cs_debug =
65 {
66     0, 0, &runtime_list_cs,
67     { &runtime_list_cs_debug.ProcessLocksList,
68       &runtime_list_cs_debug.ProcessLocksList },
69       0, 0, { (DWORD_PTR)(__FILE__ ": runtime_list_cs") }
70 };
71 static CRITICAL_SECTION runtime_list_cs = { &runtime_list_cs_debug, -1, 0, 0, 0, 0 };
72
73 #define NUM_ABI_VERSIONS 2
74
75 static loaded_mono loaded_monos[NUM_ABI_VERSIONS];
76
77 static BOOL find_mono_dll(LPCWSTR path, LPWSTR dll_path, int abi_version);
78
79 static MonoAssembly* mono_assembly_search_hook_fn(MonoAssemblyName *aname, char **assemblies_path, void *user_data);
80
81 static void mono_shutdown_callback_fn(MonoProfiler *prof);
82
83 static void set_environment(LPCWSTR bin_path)
84 {
85     WCHAR path_env[MAX_PATH];
86     int len;
87
88     static const WCHAR pathW[] = {'P','A','T','H',0};
89
90     /* We have to modify PATH as Mono loads other DLLs from this directory. */
91     GetEnvironmentVariableW(pathW, path_env, sizeof(path_env)/sizeof(WCHAR));
92     len = strlenW(path_env);
93     path_env[len++] = ';';
94     strcpyW(path_env+len, bin_path);
95     SetEnvironmentVariableW(pathW, path_env);
96 }
97
98 static void CDECL do_nothing(void)
99 {
100 }
101
102 static MonoImage* image_open_module_handle_dummy(HMODULE module_handle,
103     char* fname, UINT has_entry_point, MonoImageOpenStatus* status, int abi_version)
104 {
105     return loaded_monos[abi_version-1].mono_image_open(fname, status);
106 }
107
108 static CDECL MonoImage* image_open_module_handle_dummy_1(HMODULE module_handle,
109     char* fname, UINT has_entry_point, MonoImageOpenStatus* status)
110 {
111     return image_open_module_handle_dummy(module_handle, fname, has_entry_point, status, 1);
112 }
113
114 static CDECL MonoImage* image_open_module_handle_dummy_2(HMODULE module_handle,
115     char* fname, UINT has_entry_point, MonoImageOpenStatus* status)
116 {
117     return image_open_module_handle_dummy(module_handle, fname, has_entry_point, status, 2);
118 }
119
120 MonoImage* (CDECL * const image_from_handle_fn[NUM_ABI_VERSIONS])(HMODULE module_handle, char* fname, UINT has_entry_point, MonoImageOpenStatus* status) = {
121     image_open_module_handle_dummy_1,
122     image_open_module_handle_dummy_2
123 };
124
125 static void missing_runtime_message(void)
126 {
127     MESSAGE("wine: Install Mono for Windows to run .NET applications.\n");
128 }
129
130 static HRESULT load_mono(CLRRuntimeInfo *This, loaded_mono **result)
131 {
132     static const WCHAR bin[] = {'\\','b','i','n',0};
133     static const WCHAR lib[] = {'\\','l','i','b',0};
134     static const WCHAR etc[] = {'\\','e','t','c',0};
135     static const WCHAR glibdll[] = {'l','i','b','g','l','i','b','-','2','.','0','-','0','.','d','l','l',0};
136     WCHAR mono_dll_path[MAX_PATH+16], mono_bin_path[MAX_PATH+4];
137     WCHAR mono_lib_path[MAX_PATH+4], mono_etc_path[MAX_PATH+4];
138     char mono_lib_path_a[MAX_PATH], mono_etc_path_a[MAX_PATH];
139     int trace_size;
140     char trace_setting[256];
141     int verbose_size;
142     char verbose_setting[256];
143
144     if (This->mono_abi_version <= 0 || This->mono_abi_version > NUM_ABI_VERSIONS)
145     {
146         missing_runtime_message();
147         return E_FAIL;
148     }
149
150     *result = &loaded_monos[This->mono_abi_version-1];
151
152     if ((*result)->is_shutdown)
153     {
154         ERR("Cannot load Mono after it has been shut down.\n");
155         *result = NULL;
156         return E_FAIL;
157     }
158
159     if (!(*result)->mono_handle)
160     {
161         strcpyW(mono_bin_path, This->mono_path);
162         strcatW(mono_bin_path, bin);
163         set_environment(mono_bin_path);
164
165         strcpyW(mono_lib_path, This->mono_path);
166         strcatW(mono_lib_path, lib);
167         WideCharToMultiByte(CP_UTF8, 0, mono_lib_path, -1, mono_lib_path_a, MAX_PATH, NULL, NULL);
168
169         strcpyW(mono_etc_path, This->mono_path);
170         strcatW(mono_etc_path, etc);
171         WideCharToMultiByte(CP_UTF8, 0, mono_etc_path, -1, mono_etc_path_a, MAX_PATH, NULL, NULL);
172
173         if (!find_mono_dll(This->mono_path, mono_dll_path, This->mono_abi_version)) goto fail;
174
175         (*result)->mono_handle = LoadLibraryW(mono_dll_path);
176
177         if (!(*result)->mono_handle) goto fail;
178
179 #define LOAD_MONO_FUNCTION(x) do { \
180     (*result)->x = (void*)GetProcAddress((*result)->mono_handle, #x); \
181     if (!(*result)->x) { \
182         goto fail; \
183     } \
184 } while (0);
185
186         LOAD_MONO_FUNCTION(mono_assembly_get_image);
187         LOAD_MONO_FUNCTION(mono_assembly_load_from);
188         LOAD_MONO_FUNCTION(mono_assembly_open);
189         LOAD_MONO_FUNCTION(mono_config_parse);
190         LOAD_MONO_FUNCTION(mono_class_from_mono_type);
191         LOAD_MONO_FUNCTION(mono_class_from_name);
192         LOAD_MONO_FUNCTION(mono_class_get_method_from_name);
193         LOAD_MONO_FUNCTION(mono_domain_assembly_open);
194         LOAD_MONO_FUNCTION(mono_image_open);
195         LOAD_MONO_FUNCTION(mono_install_assembly_preload_hook);
196         LOAD_MONO_FUNCTION(mono_jit_exec);
197         LOAD_MONO_FUNCTION(mono_jit_init);
198         LOAD_MONO_FUNCTION(mono_jit_set_trace_options);
199         LOAD_MONO_FUNCTION(mono_marshal_get_vtfixup_ftnptr);
200         LOAD_MONO_FUNCTION(mono_object_get_domain);
201         LOAD_MONO_FUNCTION(mono_object_new);
202         LOAD_MONO_FUNCTION(mono_object_unbox);
203         LOAD_MONO_FUNCTION(mono_profiler_install);
204         LOAD_MONO_FUNCTION(mono_reflection_type_from_name);
205         LOAD_MONO_FUNCTION(mono_runtime_invoke);
206         LOAD_MONO_FUNCTION(mono_runtime_object_init);
207         LOAD_MONO_FUNCTION(mono_runtime_quit);
208         LOAD_MONO_FUNCTION(mono_set_dirs);
209         LOAD_MONO_FUNCTION(mono_set_verbose_level);
210         LOAD_MONO_FUNCTION(mono_stringify_assembly_name);
211         LOAD_MONO_FUNCTION(mono_string_new);
212         LOAD_MONO_FUNCTION(mono_thread_attach);
213         LOAD_MONO_FUNCTION(mono_trace_set_assembly);
214
215         /* GLib imports obsoleted by the 2.0 ABI */
216         if (This->mono_abi_version == 1)
217         {
218             (*result)->glib_handle = LoadLibraryW(glibdll);
219             if (!(*result)->glib_handle) goto fail;
220
221             (*result)->mono_free = (void*)GetProcAddress((*result)->glib_handle, "g_free");
222             if (!(*result)->mono_free) goto fail;
223         }
224         else
225         {
226             LOAD_MONO_FUNCTION(mono_free);
227         }
228
229 #undef LOAD_MONO_FUNCTION
230
231 #define LOAD_OPT_MONO_FUNCTION(x, default) do { \
232     (*result)->x = (void*)GetProcAddress((*result)->mono_handle, #x); \
233     if (!(*result)->x) { \
234         (*result)->x = default; \
235     } \
236 } while (0);
237
238         LOAD_OPT_MONO_FUNCTION(mono_image_open_from_module_handle, image_from_handle_fn[This->mono_abi_version-1]);
239         LOAD_OPT_MONO_FUNCTION(mono_runtime_set_shutting_down, do_nothing);
240         LOAD_OPT_MONO_FUNCTION(mono_thread_pool_cleanup, do_nothing);
241         LOAD_OPT_MONO_FUNCTION(mono_thread_suspend_all_other_threads, do_nothing);
242         LOAD_OPT_MONO_FUNCTION(mono_threads_set_shutting_down, do_nothing);
243
244 #undef LOAD_OPT_MONO_FUNCTION
245
246         (*result)->mono_profiler_install((MonoProfiler*)*result, mono_shutdown_callback_fn);
247
248         (*result)->mono_set_dirs(mono_lib_path_a, mono_etc_path_a);
249
250         (*result)->mono_config_parse(NULL);
251
252         (*result)->mono_install_assembly_preload_hook(mono_assembly_search_hook_fn, *result);
253
254         trace_size = GetEnvironmentVariableA("WINE_MONO_TRACE", trace_setting, sizeof(trace_setting));
255
256         if (trace_size)
257         {
258             (*result)->mono_jit_set_trace_options(trace_setting);
259         }
260
261         verbose_size = GetEnvironmentVariableA("WINE_MONO_VERBOSE", verbose_setting, sizeof(verbose_setting));
262
263         if (verbose_size)
264         {
265             (*result)->mono_set_verbose_level(verbose_setting[0] - '0');
266         }
267     }
268
269     return S_OK;
270
271 fail:
272     ERR("Could not load Mono into this process\n");
273     FreeLibrary((*result)->mono_handle);
274     FreeLibrary((*result)->glib_handle);
275     (*result)->mono_handle = NULL;
276     (*result)->glib_handle = NULL;
277     return E_FAIL;
278 }
279
280 static void mono_shutdown_callback_fn(MonoProfiler *prof)
281 {
282     loaded_mono *mono = (loaded_mono*)prof;
283
284     mono->is_shutdown = TRUE;
285 }
286
287 static HRESULT CLRRuntimeInfo_GetRuntimeHost(CLRRuntimeInfo *This, RuntimeHost **result)
288 {
289     HRESULT hr = S_OK;
290     loaded_mono *ploaded_mono;
291
292     if (This->loaded_runtime)
293     {
294         *result = This->loaded_runtime;
295         return hr;
296     }
297
298     EnterCriticalSection(&runtime_list_cs);
299
300     hr = load_mono(This, &ploaded_mono);
301
302     if (SUCCEEDED(hr))
303         hr = RuntimeHost_Construct(This, ploaded_mono, &This->loaded_runtime);
304
305     LeaveCriticalSection(&runtime_list_cs);
306
307     if (SUCCEEDED(hr))
308         *result = This->loaded_runtime;
309
310     return hr;
311 }
312
313 void unload_all_runtimes(void)
314 {
315     int i;
316     HMODULE handle;
317
318     /* If the only references to mscoree are through dll's that were loaded by
319      * Mono, shutting down the Mono runtime will free mscoree, so take a
320      * reference to prevent that from happening. */
321     GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const WCHAR *)unload_all_runtimes, &handle);
322
323     for (i=0; i<NUM_ABI_VERSIONS; i++)
324     {
325         loaded_mono *mono = &loaded_monos[i];
326         if (mono->mono_handle && mono->is_started && !mono->is_shutdown)
327         {
328             /* Copied from Mono's ves_icall_System_Environment_Exit */
329             mono->mono_threads_set_shutting_down();
330             mono->mono_runtime_set_shutting_down();
331             mono->mono_thread_pool_cleanup();
332             mono->mono_thread_suspend_all_other_threads();
333             mono->mono_runtime_quit();
334         }
335     }
336
337     for (i=0; i<NUM_RUNTIMES; i++)
338         if (runtimes[i].loaded_runtime)
339             RuntimeHost_Destroy(runtimes[i].loaded_runtime);
340 }
341
342 void expect_no_runtimes(void)
343 {
344     int i;
345
346     for (i=0; i<NUM_ABI_VERSIONS; i++)
347     {
348         loaded_mono *mono = &loaded_monos[i];
349         if (mono->mono_handle && mono->is_started && !mono->is_shutdown)
350         {
351             ERR("Process exited with a Mono runtime loaded.\n");
352             return;
353         }
354     }
355 }
356
357 static inline CLRRuntimeInfo *impl_from_ICLRRuntimeInfo(ICLRRuntimeInfo *iface)
358 {
359     return CONTAINING_RECORD(iface, CLRRuntimeInfo, ICLRRuntimeInfo_iface);
360 }
361
362 static HRESULT WINAPI CLRRuntimeInfo_QueryInterface(ICLRRuntimeInfo* iface,
363         REFIID riid,
364         void **ppvObject)
365 {
366     TRACE("%p %s %p\n", iface, debugstr_guid(riid), ppvObject);
367
368     if ( IsEqualGUID( riid, &IID_ICLRRuntimeInfo ) ||
369          IsEqualGUID( riid, &IID_IUnknown ) )
370     {
371         *ppvObject = iface;
372     }
373     else
374     {
375         FIXME("Unsupported interface %s\n", debugstr_guid(riid));
376         return E_NOINTERFACE;
377     }
378
379     ICLRRuntimeInfo_AddRef( iface );
380
381     return S_OK;
382 }
383
384 static ULONG WINAPI CLRRuntimeInfo_AddRef(ICLRRuntimeInfo* iface)
385 {
386     return 2;
387 }
388
389 static ULONG WINAPI CLRRuntimeInfo_Release(ICLRRuntimeInfo* iface)
390 {
391     return 1;
392 }
393
394 static HRESULT WINAPI CLRRuntimeInfo_GetVersionString(ICLRRuntimeInfo* iface,
395     LPWSTR pwzBuffer, DWORD *pcchBuffer)
396 {
397     struct CLRRuntimeInfo *This = impl_from_ICLRRuntimeInfo(iface);
398     DWORD buffer_size = *pcchBuffer;
399     HRESULT hr = S_OK;
400     char version[11];
401     DWORD size;
402
403     TRACE("%p %p %p\n", iface, pwzBuffer, pcchBuffer);
404
405     size = snprintf(version, sizeof(version), "v%u.%u.%u", This->major, This->minor, This->build);
406
407     assert(size <= sizeof(version));
408
409     *pcchBuffer = MultiByteToWideChar(CP_UTF8, 0, version, -1, NULL, 0);
410
411     if (pwzBuffer)
412     {
413         if (buffer_size >= *pcchBuffer)
414             MultiByteToWideChar(CP_UTF8, 0, version, -1, pwzBuffer, buffer_size);
415         else
416             hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
417     }
418
419     return hr;
420 }
421
422 static BOOL get_install_root(LPWSTR install_dir)
423 {
424     const WCHAR dotnet_key[] = {'S','O','F','T','W','A','R','E','\\','M','i','c','r','o','s','o','f','t','\\','.','N','E','T','F','r','a','m','e','w','o','r','k','\\',0};
425     const WCHAR install_root[] = {'I','n','s','t','a','l','l','R','o','o','t',0};
426
427     DWORD len;
428     HKEY key;
429
430     if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, dotnet_key, 0, KEY_READ, &key))
431         return FALSE;
432
433     len = MAX_PATH;
434     if (RegQueryValueExW(key, install_root, 0, NULL, (LPBYTE)install_dir, &len))
435     {
436         RegCloseKey(key);
437         return FALSE;
438     }
439     RegCloseKey(key);
440
441     return TRUE;
442 }
443
444 static HRESULT WINAPI CLRRuntimeInfo_GetRuntimeDirectory(ICLRRuntimeInfo* iface,
445     LPWSTR pwzBuffer, DWORD *pcchBuffer)
446 {
447     static const WCHAR slash[] = {'\\',0};
448     DWORD buffer_size = *pcchBuffer;
449     WCHAR system_dir[MAX_PATH];
450     WCHAR version[MAX_PATH];
451     DWORD version_size, size;
452     HRESULT hr = S_OK;
453
454     TRACE("%p %p %p\n", iface, pwzBuffer, pcchBuffer);
455
456     if (!get_install_root(system_dir))
457     {
458         ERR("error reading registry key for installroot\n");
459         return E_FAIL;
460     }
461     else
462     {
463         version_size = MAX_PATH;
464         ICLRRuntimeInfo_GetVersionString(iface, version, &version_size);
465         lstrcatW(system_dir, version);
466         lstrcatW(system_dir, slash);
467         size = lstrlenW(system_dir) + 1;
468     }
469
470     *pcchBuffer = size;
471
472     if (pwzBuffer)
473     {
474         if (buffer_size >= size)
475             strcpyW(pwzBuffer, system_dir);
476         else
477             hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
478     }
479
480     return hr;
481 }
482
483 static HRESULT WINAPI CLRRuntimeInfo_IsLoaded(ICLRRuntimeInfo* iface,
484     HANDLE hndProcess, BOOL *pbLoaded)
485 {
486     FIXME("%p %p %p\n", iface, hndProcess, pbLoaded);
487
488     return E_NOTIMPL;
489 }
490
491 static HRESULT WINAPI CLRRuntimeInfo_LoadErrorString(ICLRRuntimeInfo* iface,
492     UINT iResourceID, LPWSTR pwzBuffer, DWORD *pcchBuffer, LONG iLocaleid)
493 {
494     FIXME("%p %u %p %p %x\n", iface, iResourceID, pwzBuffer, pcchBuffer, iLocaleid);
495
496     return E_NOTIMPL;
497 }
498
499 static HRESULT WINAPI CLRRuntimeInfo_LoadLibrary(ICLRRuntimeInfo* iface,
500     LPCWSTR pwzDllName, HMODULE *phndModule)
501 {
502     WCHAR version[MAX_PATH];
503     HRESULT hr;
504     DWORD cchBuffer;
505
506     TRACE("%p %s %p\n", iface, debugstr_w(pwzDllName), phndModule);
507
508     cchBuffer = MAX_PATH;
509     hr = ICLRRuntimeInfo_GetVersionString(iface, version, &cchBuffer);
510     if (FAILED(hr)) return hr;
511
512     return LoadLibraryShim(pwzDllName, version, NULL, phndModule);
513 }
514
515 static HRESULT WINAPI CLRRuntimeInfo_GetProcAddress(ICLRRuntimeInfo* iface,
516     LPCSTR pszProcName, LPVOID *ppProc)
517 {
518     FIXME("%p %s %p\n", iface, debugstr_a(pszProcName), ppProc);
519
520     return E_NOTIMPL;
521 }
522
523 static HRESULT WINAPI CLRRuntimeInfo_GetInterface(ICLRRuntimeInfo* iface,
524     REFCLSID rclsid, REFIID riid, LPVOID *ppUnk)
525 {
526     struct CLRRuntimeInfo *This = impl_from_ICLRRuntimeInfo(iface);
527     RuntimeHost *host;
528     HRESULT hr;
529
530     TRACE("%p %s %s %p\n", iface, debugstr_guid(rclsid), debugstr_guid(riid), ppUnk);
531
532     hr = CLRRuntimeInfo_GetRuntimeHost(This, &host);
533
534     if (SUCCEEDED(hr))
535         hr = RuntimeHost_GetInterface(host, rclsid, riid, ppUnk);
536
537     return hr;
538 }
539
540 static HRESULT WINAPI CLRRuntimeInfo_IsLoadable(ICLRRuntimeInfo* iface,
541     BOOL *pbLoadable)
542 {
543     FIXME("%p %p\n", iface, pbLoadable);
544
545     return E_NOTIMPL;
546 }
547
548 static HRESULT WINAPI CLRRuntimeInfo_SetDefaultStartupFlags(ICLRRuntimeInfo* iface,
549     DWORD dwStartupFlags, LPCWSTR pwzHostConfigFile)
550 {
551     FIXME("%p %x %s\n", iface, dwStartupFlags, debugstr_w(pwzHostConfigFile));
552
553     return E_NOTIMPL;
554 }
555
556 static HRESULT WINAPI CLRRuntimeInfo_GetDefaultStartupFlags(ICLRRuntimeInfo* iface,
557     DWORD *pdwStartupFlags, LPWSTR pwzHostConfigFile, DWORD *pcchHostConfigFile)
558 {
559     FIXME("%p %p %p %p\n", iface, pdwStartupFlags, pwzHostConfigFile, pcchHostConfigFile);
560
561     return E_NOTIMPL;
562 }
563
564 static HRESULT WINAPI CLRRuntimeInfo_BindAsLegacyV2Runtime(ICLRRuntimeInfo* iface)
565 {
566     FIXME("%p\n", iface);
567
568     return E_NOTIMPL;
569 }
570
571 static HRESULT WINAPI CLRRuntimeInfo_IsStarted(ICLRRuntimeInfo* iface,
572     BOOL *pbStarted, DWORD *pdwStartupFlags)
573 {
574     FIXME("%p %p %p\n", iface, pbStarted, pdwStartupFlags);
575
576     return E_NOTIMPL;
577 }
578
579 static const struct ICLRRuntimeInfoVtbl CLRRuntimeInfoVtbl = {
580     CLRRuntimeInfo_QueryInterface,
581     CLRRuntimeInfo_AddRef,
582     CLRRuntimeInfo_Release,
583     CLRRuntimeInfo_GetVersionString,
584     CLRRuntimeInfo_GetRuntimeDirectory,
585     CLRRuntimeInfo_IsLoaded,
586     CLRRuntimeInfo_LoadErrorString,
587     CLRRuntimeInfo_LoadLibrary,
588     CLRRuntimeInfo_GetProcAddress,
589     CLRRuntimeInfo_GetInterface,
590     CLRRuntimeInfo_IsLoadable,
591     CLRRuntimeInfo_SetDefaultStartupFlags,
592     CLRRuntimeInfo_GetDefaultStartupFlags,
593     CLRRuntimeInfo_BindAsLegacyV2Runtime,
594     CLRRuntimeInfo_IsStarted
595 };
596
597 HRESULT ICLRRuntimeInfo_GetRuntimeHost(ICLRRuntimeInfo *iface, RuntimeHost **result)
598 {
599     struct CLRRuntimeInfo *This = impl_from_ICLRRuntimeInfo(iface);
600
601     assert(This->ICLRRuntimeInfo_iface.lpVtbl == &CLRRuntimeInfoVtbl);
602
603     return CLRRuntimeInfo_GetRuntimeHost(This, result);
604 }
605
606 #ifdef __i386__
607 static const WCHAR libmono2_arch_dll[] = {'\\','b','i','n','\\','l','i','b','m','o','n','o','-','2','.','0','-','x','8','6','.','d','l','l',0};
608 #elif defined(__x86_64__)
609 static const WCHAR libmono2_arch_dll[] = {'\\','b','i','n','\\','l','i','b','m','o','n','o','-','2','.','0','-','x','8','6','_','6','4','.','d','l','l',0};
610 #else
611 static const WCHAR libmono2_arch_dll[] = {'\\','b','i','n','\\','l','i','b','m','o','n','o','-','2','.','0','.','d','l','l',0};
612 #endif
613
614 static BOOL find_mono_dll(LPCWSTR path, LPWSTR dll_path, int abi_version)
615 {
616     static const WCHAR mono_dll[] = {'\\','b','i','n','\\','m','o','n','o','.','d','l','l',0};
617     static const WCHAR libmono_dll[] = {'\\','b','i','n','\\','l','i','b','m','o','n','o','.','d','l','l',0};
618     static const WCHAR mono2_dll[] = {'\\','b','i','n','\\','m','o','n','o','-','2','.','0','.','d','l','l',0};
619     static const WCHAR libmono2_dll[] = {'\\','b','i','n','\\','l','i','b','m','o','n','o','-','2','.','0','.','d','l','l',0};
620     DWORD attributes=INVALID_FILE_ATTRIBUTES;
621
622     if (abi_version == 1)
623     {
624         strcpyW(dll_path, path);
625         strcatW(dll_path, mono_dll);
626         attributes = GetFileAttributesW(dll_path);
627
628         if (attributes == INVALID_FILE_ATTRIBUTES)
629         {
630             strcpyW(dll_path, path);
631             strcatW(dll_path, libmono_dll);
632             attributes = GetFileAttributesW(dll_path);
633         }
634     }
635     else if (abi_version == 2)
636     {
637         strcpyW(dll_path, path);
638         strcatW(dll_path, libmono2_arch_dll);
639         attributes = GetFileAttributesW(dll_path);
640
641         if (attributes == INVALID_FILE_ATTRIBUTES)
642         {
643             strcpyW(dll_path, path);
644             strcatW(dll_path, mono2_dll);
645             attributes = GetFileAttributesW(dll_path);
646         }
647
648         if (attributes == INVALID_FILE_ATTRIBUTES)
649         {
650             strcpyW(dll_path, path);
651             strcatW(dll_path, libmono2_dll);
652             attributes = GetFileAttributesW(dll_path);
653         }
654     }
655
656     return (attributes != INVALID_FILE_ATTRIBUTES);
657 }
658
659 static BOOL get_mono_path_from_registry(LPWSTR path, int abi_version)
660 {
661     static const WCHAR mono_key[] = {'S','o','f','t','w','a','r','e','\\','N','o','v','e','l','l','\\','M','o','n','o',0};
662     static const WCHAR defaul_clr[] = {'D','e','f','a','u','l','t','C','L','R',0};
663     static const WCHAR install_root[] = {'S','d','k','I','n','s','t','a','l','l','R','o','o','t',0};
664     static const WCHAR slash[] = {'\\',0};
665
666     WCHAR version[64], version_key[MAX_PATH];
667     DWORD len;
668     HKEY key;
669     WCHAR dll_path[MAX_PATH];
670
671     if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, mono_key, 0, KEY_READ, &key))
672         return FALSE;
673
674     len = sizeof(version);
675     if (RegQueryValueExW(key, defaul_clr, 0, NULL, (LPBYTE)version, &len))
676     {
677         RegCloseKey(key);
678         return FALSE;
679     }
680     RegCloseKey(key);
681
682     lstrcpyW(version_key, mono_key);
683     lstrcatW(version_key, slash);
684     lstrcatW(version_key, version);
685
686     if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, version_key, 0, KEY_READ, &key))
687         return FALSE;
688
689     len = sizeof(WCHAR) * MAX_PATH;
690     if (RegQueryValueExW(key, install_root, 0, NULL, (LPBYTE)path, &len))
691     {
692         RegCloseKey(key);
693         return FALSE;
694     }
695     RegCloseKey(key);
696
697     return find_mono_dll(path, dll_path, abi_version);
698 }
699
700 static BOOL get_mono_path_from_folder(LPCWSTR folder, LPWSTR mono_path, int abi_version)
701 {
702     static const WCHAR mono_one_dot_zero[] = {'\\','m','o','n','o','-','1','.','0', 0};
703     static const WCHAR mono_two_dot_zero[] = {'\\','m','o','n','o','-','2','.','0', 0};
704     WCHAR mono_dll_path[MAX_PATH];
705     BOOL found = FALSE;
706
707     strcpyW(mono_path, folder);
708
709     if (abi_version == 1)
710         strcatW(mono_path, mono_one_dot_zero);
711     else if (abi_version == 2)
712         strcatW(mono_path, mono_two_dot_zero);
713
714     found = find_mono_dll(mono_path, mono_dll_path, abi_version);
715
716     return found;
717 }
718
719 static BOOL get_mono_path(LPWSTR path, int abi_version)
720 {
721     static const WCHAR subdir_mono[] = {'\\','m','o','n','o',0};
722     static const WCHAR sibling_mono[] = {'\\','.','.','\\','m','o','n','o',0};
723     WCHAR base_path[MAX_PATH];
724     const char *unix_data_dir;
725     WCHAR *dos_data_dir;
726     int build_tree=0;
727     static WCHAR* (CDECL *wine_get_dos_file_name)(const char*);
728
729     /* First try c:\windows\mono */
730     GetWindowsDirectoryW(base_path, MAX_PATH);
731     strcatW(base_path, subdir_mono);
732
733     if (get_mono_path_from_folder(base_path, path, abi_version))
734         return TRUE;
735
736     /* Next: /usr/share/wine/mono */
737     unix_data_dir = wine_get_data_dir();
738
739     if (!unix_data_dir)
740     {
741         unix_data_dir = wine_get_build_dir();
742         build_tree = 1;
743     }
744
745     if (unix_data_dir)
746     {
747         if (!wine_get_dos_file_name)
748             wine_get_dos_file_name = (void*)GetProcAddress(GetModuleHandleA("kernel32"), "wine_get_dos_file_name");
749
750         if (wine_get_dos_file_name)
751         {
752             dos_data_dir = wine_get_dos_file_name(unix_data_dir);
753
754             if (dos_data_dir)
755             {
756                 strcpyW(base_path, dos_data_dir);
757                 strcatW(base_path, build_tree ? sibling_mono : subdir_mono);
758
759                 HeapFree(GetProcessHeap(), 0, dos_data_dir);
760
761                 if (get_mono_path_from_folder(base_path, path, abi_version))
762                     return TRUE;
763             }
764         }
765     }
766
767     /* Last: the registry */
768     return get_mono_path_from_registry(path, abi_version);
769 }
770
771 static void find_runtimes(void)
772 {
773     int abi_version, i;
774     static const WCHAR libmono[] = {'\\','l','i','b','\\','m','o','n','o','\\',0};
775     static const WCHAR mscorlib[] = {'\\','m','s','c','o','r','l','i','b','.','d','l','l',0};
776     WCHAR mono_path[MAX_PATH], lib_path[MAX_PATH];
777     BOOL any_runtimes_found = FALSE;
778
779     if (runtimes_initialized) return;
780
781     EnterCriticalSection(&runtime_list_cs);
782
783     if (runtimes_initialized) goto end;
784
785     for (abi_version=NUM_ABI_VERSIONS; abi_version>0; abi_version--)
786     {
787         if (!get_mono_path(mono_path, abi_version))
788             continue;
789
790         for (i=0; i<NUM_RUNTIMES; i++)
791         {
792             if (runtimes[i].mono_abi_version == 0)
793             {
794                 strcpyW(lib_path, mono_path);
795                 strcatW(lib_path, libmono);
796                 strcatW(lib_path, runtimes[i].mono_libdir);
797                 strcatW(lib_path, mscorlib);
798
799                 if (GetFileAttributesW(lib_path) != INVALID_FILE_ATTRIBUTES)
800                 {
801                     runtimes[i].mono_abi_version = abi_version;
802
803                     strcpyW(runtimes[i].mono_path, mono_path);
804                     strcpyW(runtimes[i].mscorlib_path, lib_path);
805
806                     any_runtimes_found = TRUE;
807                 }
808             }
809         }
810     }
811
812     if (!any_runtimes_found)
813     {
814         /* Report all runtimes are available if Mono isn't installed.
815          * FIXME: Remove this when Mono is properly packaged. */
816         for (i=0; i<NUM_RUNTIMES; i++)
817             runtimes[i].mono_abi_version = -1;
818     }
819
820     runtimes_initialized = 1;
821
822 end:
823     LeaveCriticalSection(&runtime_list_cs);
824 }
825
826 struct InstalledRuntimeEnum
827 {
828     IEnumUnknown IEnumUnknown_iface;
829     LONG ref;
830     ULONG pos;
831 };
832
833 static const struct IEnumUnknownVtbl InstalledRuntimeEnum_Vtbl;
834
835 static inline struct InstalledRuntimeEnum *impl_from_IEnumUnknown(IEnumUnknown *iface)
836 {
837     return CONTAINING_RECORD(iface, struct InstalledRuntimeEnum, IEnumUnknown_iface);
838 }
839
840 static HRESULT WINAPI InstalledRuntimeEnum_QueryInterface(IEnumUnknown* iface, REFIID riid,
841         void **ppvObject)
842 {
843     TRACE("%p %s %p\n", iface, debugstr_guid(riid), ppvObject);
844
845     if ( IsEqualGUID( riid, &IID_IEnumUnknown ) ||
846          IsEqualGUID( riid, &IID_IUnknown ) )
847     {
848         *ppvObject = iface;
849     }
850     else
851     {
852         FIXME("Unsupported interface %s\n", debugstr_guid(riid));
853         return E_NOINTERFACE;
854     }
855
856     IEnumUnknown_AddRef( iface );
857
858     return S_OK;
859 }
860
861 static ULONG WINAPI InstalledRuntimeEnum_AddRef(IEnumUnknown* iface)
862 {
863     struct InstalledRuntimeEnum *This = impl_from_IEnumUnknown(iface);
864     ULONG ref = InterlockedIncrement(&This->ref);
865
866     TRACE("(%p) refcount=%u\n", iface, ref);
867
868     return ref;
869 }
870
871 static ULONG WINAPI InstalledRuntimeEnum_Release(IEnumUnknown* iface)
872 {
873     struct InstalledRuntimeEnum *This = impl_from_IEnumUnknown(iface);
874     ULONG ref = InterlockedDecrement(&This->ref);
875
876     TRACE("(%p) refcount=%u\n", iface, ref);
877
878     if (ref == 0)
879     {
880         HeapFree(GetProcessHeap(), 0, This);
881     }
882
883     return ref;
884 }
885
886 static HRESULT WINAPI InstalledRuntimeEnum_Next(IEnumUnknown *iface, ULONG celt,
887     IUnknown **rgelt, ULONG *pceltFetched)
888 {
889     struct InstalledRuntimeEnum *This = impl_from_IEnumUnknown(iface);
890     ULONG num_fetched = 0;
891     HRESULT hr=S_OK;
892     IUnknown *item;
893
894     TRACE("(%p,%u,%p,%p)\n", iface, celt, rgelt, pceltFetched);
895
896     while (num_fetched < celt)
897     {
898         if (This->pos >= NUM_RUNTIMES)
899         {
900             hr = S_FALSE;
901             break;
902         }
903         if (runtimes[This->pos].mono_abi_version)
904         {
905             item = (IUnknown*)&runtimes[This->pos].ICLRRuntimeInfo_iface;
906             IUnknown_AddRef(item);
907             rgelt[num_fetched] = item;
908             num_fetched++;
909         }
910         This->pos++;
911     }
912
913     if (pceltFetched)
914         *pceltFetched = num_fetched;
915
916     return hr;
917 }
918
919 static HRESULT WINAPI InstalledRuntimeEnum_Skip(IEnumUnknown *iface, ULONG celt)
920 {
921     struct InstalledRuntimeEnum *This = impl_from_IEnumUnknown(iface);
922     ULONG num_fetched = 0;
923     HRESULT hr=S_OK;
924
925     TRACE("(%p,%u)\n", iface, celt);
926
927     while (num_fetched < celt)
928     {
929         if (This->pos >= NUM_RUNTIMES)
930         {
931             hr = S_FALSE;
932             break;
933         }
934         if (runtimes[This->pos].mono_abi_version)
935         {
936             num_fetched++;
937         }
938         This->pos++;
939     }
940
941     return hr;
942 }
943
944 static HRESULT WINAPI InstalledRuntimeEnum_Reset(IEnumUnknown *iface)
945 {
946     struct InstalledRuntimeEnum *This = impl_from_IEnumUnknown(iface);
947
948     TRACE("(%p)\n", iface);
949
950     This->pos = 0;
951
952     return S_OK;
953 }
954
955 static HRESULT WINAPI InstalledRuntimeEnum_Clone(IEnumUnknown *iface, IEnumUnknown **ppenum)
956 {
957     struct InstalledRuntimeEnum *This = impl_from_IEnumUnknown(iface);
958     struct InstalledRuntimeEnum *new_enum;
959
960     TRACE("(%p)\n", iface);
961
962     new_enum = HeapAlloc(GetProcessHeap(), 0, sizeof(*new_enum));
963     if (!new_enum)
964         return E_OUTOFMEMORY;
965
966     new_enum->IEnumUnknown_iface.lpVtbl = &InstalledRuntimeEnum_Vtbl;
967     new_enum->ref = 1;
968     new_enum->pos = This->pos;
969
970     *ppenum = &new_enum->IEnumUnknown_iface;
971
972     return S_OK;
973 }
974
975 static const struct IEnumUnknownVtbl InstalledRuntimeEnum_Vtbl = {
976     InstalledRuntimeEnum_QueryInterface,
977     InstalledRuntimeEnum_AddRef,
978     InstalledRuntimeEnum_Release,
979     InstalledRuntimeEnum_Next,
980     InstalledRuntimeEnum_Skip,
981     InstalledRuntimeEnum_Reset,
982     InstalledRuntimeEnum_Clone
983 };
984
985 struct CLRMetaHost
986 {
987     ICLRMetaHost ICLRMetaHost_iface;
988 };
989
990 static struct CLRMetaHost GlobalCLRMetaHost;
991
992 static HRESULT WINAPI CLRMetaHost_QueryInterface(ICLRMetaHost* iface,
993         REFIID riid,
994         void **ppvObject)
995 {
996     TRACE("%s %p\n", debugstr_guid(riid), ppvObject);
997
998     if ( IsEqualGUID( riid, &IID_ICLRMetaHost ) ||
999          IsEqualGUID( riid, &IID_IUnknown ) )
1000     {
1001         *ppvObject = iface;
1002     }
1003     else
1004     {
1005         FIXME("Unsupported interface %s\n", debugstr_guid(riid));
1006         return E_NOINTERFACE;
1007     }
1008
1009     ICLRMetaHost_AddRef( iface );
1010
1011     return S_OK;
1012 }
1013
1014 static ULONG WINAPI CLRMetaHost_AddRef(ICLRMetaHost* iface)
1015 {
1016     return 2;
1017 }
1018
1019 static ULONG WINAPI CLRMetaHost_Release(ICLRMetaHost* iface)
1020 {
1021     return 1;
1022 }
1023
1024 static BOOL parse_runtime_version(LPCWSTR version, DWORD *major, DWORD *minor, DWORD *build)
1025 {
1026     *major = 0;
1027     *minor = 0;
1028     *build = 0;
1029
1030     if (version[0] == 'v' || version[0] == 'V')
1031     {
1032         version++;
1033         if (!isdigit(*version))
1034             return FALSE;
1035
1036         while (isdigit(*version))
1037             *major = *major * 10 + (*version++ - '0');
1038
1039         if (*version == 0)
1040             return TRUE;
1041
1042         if (*version++ != '.' || !isdigit(*version))
1043             return FALSE;
1044
1045         while (isdigit(*version))
1046             *minor = *minor * 10 + (*version++ - '0');
1047
1048         if (*version == 0)
1049             return TRUE;
1050
1051         if (*version++ != '.' || !isdigit(*version))
1052             return FALSE;
1053
1054         while (isdigit(*version))
1055             *build = *build * 10 + (*version++ - '0');
1056
1057         return *version == 0;
1058     }
1059     else
1060         return FALSE;
1061 }
1062
1063 HRESULT WINAPI CLRMetaHost_GetRuntime(ICLRMetaHost* iface,
1064     LPCWSTR pwzVersion, REFIID iid, LPVOID *ppRuntime)
1065 {
1066     int i;
1067     DWORD major, minor, build;
1068
1069     TRACE("%s %s %p\n", debugstr_w(pwzVersion), debugstr_guid(iid), ppRuntime);
1070
1071     if (!pwzVersion)
1072         return E_POINTER;
1073
1074     if (!parse_runtime_version(pwzVersion, &major, &minor, &build))
1075     {
1076         ERR("Cannot parse %s\n", debugstr_w(pwzVersion));
1077         return CLR_E_SHIM_RUNTIME;
1078     }
1079
1080     find_runtimes();
1081
1082     for (i=0; i<NUM_RUNTIMES; i++)
1083     {
1084         if (runtimes[i].major == major && runtimes[i].minor == minor &&
1085             runtimes[i].build == build)
1086         {
1087             if (runtimes[i].mono_abi_version)
1088                 return ICLRRuntimeInfo_QueryInterface(&runtimes[i].ICLRRuntimeInfo_iface, iid,
1089                         ppRuntime);
1090             else
1091             {
1092                 missing_runtime_message();
1093                 return CLR_E_SHIM_RUNTIME;
1094             }
1095         }
1096     }
1097
1098     FIXME("Unrecognized version %s\n", debugstr_w(pwzVersion));
1099     return CLR_E_SHIM_RUNTIME;
1100 }
1101
1102 HRESULT WINAPI CLRMetaHost_GetVersionFromFile(ICLRMetaHost* iface,
1103     LPCWSTR pwzFilePath, LPWSTR pwzBuffer, DWORD *pcchBuffer)
1104 {
1105     ASSEMBLY *assembly;
1106     HRESULT hr;
1107     LPSTR version;
1108     ULONG buffer_size=*pcchBuffer;
1109
1110     TRACE("%s %p %p\n", debugstr_w(pwzFilePath), pwzBuffer, pcchBuffer);
1111
1112     hr = assembly_create(&assembly, pwzFilePath);
1113
1114     if (SUCCEEDED(hr))
1115     {
1116         hr = assembly_get_runtime_version(assembly, &version);
1117
1118         if (SUCCEEDED(hr))
1119         {
1120             *pcchBuffer = MultiByteToWideChar(CP_UTF8, 0, version, -1, NULL, 0);
1121
1122             if (pwzBuffer)
1123             {
1124                 if (buffer_size >= *pcchBuffer)
1125                     MultiByteToWideChar(CP_UTF8, 0, version, -1, pwzBuffer, buffer_size);
1126                 else
1127                     hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
1128             }
1129         }
1130
1131         assembly_release(assembly);
1132     }
1133
1134     return hr;
1135 }
1136
1137 static HRESULT WINAPI CLRMetaHost_EnumerateInstalledRuntimes(ICLRMetaHost* iface,
1138     IEnumUnknown **ppEnumerator)
1139 {
1140     struct InstalledRuntimeEnum *new_enum;
1141
1142     TRACE("%p\n", ppEnumerator);
1143
1144     find_runtimes();
1145
1146     new_enum = HeapAlloc(GetProcessHeap(), 0, sizeof(*new_enum));
1147     if (!new_enum)
1148         return E_OUTOFMEMORY;
1149
1150     new_enum->IEnumUnknown_iface.lpVtbl = &InstalledRuntimeEnum_Vtbl;
1151     new_enum->ref = 1;
1152     new_enum->pos = 0;
1153
1154     *ppEnumerator = &new_enum->IEnumUnknown_iface;
1155
1156     return S_OK;
1157 }
1158
1159 static HRESULT WINAPI CLRMetaHost_EnumerateLoadedRuntimes(ICLRMetaHost* iface,
1160     HANDLE hndProcess, IEnumUnknown **ppEnumerator)
1161 {
1162     FIXME("%p %p\n", hndProcess, ppEnumerator);
1163
1164     return E_NOTIMPL;
1165 }
1166
1167 static HRESULT WINAPI CLRMetaHost_RequestRuntimeLoadedNotification(ICLRMetaHost* iface,
1168     RuntimeLoadedCallbackFnPtr pCallbackFunction)
1169 {
1170     FIXME("%p\n", pCallbackFunction);
1171
1172     return E_NOTIMPL;
1173 }
1174
1175 static HRESULT WINAPI CLRMetaHost_QueryLegacyV2RuntimeBinding(ICLRMetaHost* iface,
1176     REFIID riid, LPVOID *ppUnk)
1177 {
1178     FIXME("%s %p\n", debugstr_guid(riid), ppUnk);
1179
1180     return E_NOTIMPL;
1181 }
1182
1183 static HRESULT WINAPI CLRMetaHost_ExitProcess(ICLRMetaHost* iface, INT32 iExitCode)
1184 {
1185     FIXME("%i: stub\n", iExitCode);
1186
1187     ExitProcess(iExitCode);
1188 }
1189
1190 static const struct ICLRMetaHostVtbl CLRMetaHost_vtbl =
1191 {
1192     CLRMetaHost_QueryInterface,
1193     CLRMetaHost_AddRef,
1194     CLRMetaHost_Release,
1195     CLRMetaHost_GetRuntime,
1196     CLRMetaHost_GetVersionFromFile,
1197     CLRMetaHost_EnumerateInstalledRuntimes,
1198     CLRMetaHost_EnumerateLoadedRuntimes,
1199     CLRMetaHost_RequestRuntimeLoadedNotification,
1200     CLRMetaHost_QueryLegacyV2RuntimeBinding,
1201     CLRMetaHost_ExitProcess
1202 };
1203
1204 static struct CLRMetaHost GlobalCLRMetaHost = {
1205     { &CLRMetaHost_vtbl }
1206 };
1207
1208 HRESULT CLRMetaHost_CreateInstance(REFIID riid, void **ppobj)
1209 {
1210     return ICLRMetaHost_QueryInterface(&GlobalCLRMetaHost.ICLRMetaHost_iface, riid, ppobj);
1211 }
1212
1213 static MonoAssembly* mono_assembly_search_hook_fn(MonoAssemblyName *aname, char **assemblies_path, void *user_data)
1214 {
1215     loaded_mono *mono = user_data;
1216     HRESULT hr=S_OK;
1217     MonoAssembly *result=NULL;
1218     char *stringname=NULL;
1219     LPWSTR stringnameW;
1220     int stringnameW_size;
1221     IAssemblyCache *asmcache;
1222     ASSEMBLY_INFO info;
1223     WCHAR path[MAX_PATH];
1224     char *pathA;
1225     MonoImageOpenStatus stat;
1226     static WCHAR fusiondll[] = {'f','u','s','i','o','n',0};
1227     HMODULE hfusion=NULL;
1228     static HRESULT (WINAPI *pCreateAssemblyCache)(IAssemblyCache**,DWORD);
1229
1230     stringname = mono->mono_stringify_assembly_name(aname);
1231
1232     TRACE("%s\n", debugstr_a(stringname));
1233
1234     if (!stringname) return NULL;
1235
1236     /* FIXME: We should search the given paths before the GAC. */
1237
1238     if (!pCreateAssemblyCache)
1239     {
1240         hr = LoadLibraryShim(fusiondll, NULL, NULL, &hfusion);
1241
1242         if (SUCCEEDED(hr))
1243         {
1244             pCreateAssemblyCache = (void*)GetProcAddress(hfusion, "CreateAssemblyCache");
1245             if (!pCreateAssemblyCache)
1246                 hr = E_FAIL;
1247         }
1248     }
1249
1250     if (SUCCEEDED(hr))
1251         hr = pCreateAssemblyCache(&asmcache, 0);
1252
1253     if (SUCCEEDED(hr))
1254     {
1255         stringnameW_size = MultiByteToWideChar(CP_UTF8, 0, stringname, -1, NULL, 0);
1256
1257         stringnameW = HeapAlloc(GetProcessHeap(), 0, stringnameW_size * sizeof(WCHAR));
1258         if (stringnameW)
1259             MultiByteToWideChar(CP_UTF8, 0, stringname, -1, stringnameW, stringnameW_size);
1260         else
1261             hr = E_OUTOFMEMORY;
1262
1263         if (SUCCEEDED(hr))
1264         {
1265             info.cbAssemblyInfo = sizeof(info);
1266             info.pszCurrentAssemblyPathBuf = path;
1267             info.cchBuf = MAX_PATH;
1268             path[0] = 0;
1269
1270             hr = IAssemblyCache_QueryAssemblyInfo(asmcache, 0, stringnameW, &info);
1271         }
1272
1273         HeapFree(GetProcessHeap(), 0, stringnameW);
1274
1275         IAssemblyCache_Release(asmcache);
1276     }
1277
1278     if (SUCCEEDED(hr))
1279     {
1280         TRACE("found: %s\n", debugstr_w(path));
1281
1282         pathA = WtoA(path);
1283
1284         if (pathA)
1285         {
1286             result = mono->mono_assembly_open(pathA, &stat);
1287
1288             if (!result)
1289                 ERR("Failed to load %s, status=%u\n", debugstr_w(path), stat);
1290
1291             HeapFree(GetProcessHeap(), 0, pathA);
1292         }
1293     }
1294
1295     mono->mono_free(stringname);
1296
1297     return result;
1298 }
1299
1300 HRESULT get_runtime_info(LPCWSTR exefile, LPCWSTR version, LPCWSTR config_file,
1301     DWORD startup_flags, DWORD runtimeinfo_flags, BOOL legacy, ICLRRuntimeInfo **result)
1302 {
1303     static const WCHAR dotconfig[] = {'.','c','o','n','f','i','g',0};
1304     static const DWORD supported_startup_flags = 0;
1305     static const DWORD supported_runtime_flags = RUNTIME_INFO_UPGRADE_VERSION;
1306     int i;
1307     WCHAR local_version[MAX_PATH];
1308     ULONG local_version_size = MAX_PATH;
1309     WCHAR local_config_file[MAX_PATH];
1310     HRESULT hr;
1311     parsed_config_file parsed_config;
1312
1313     if (startup_flags & ~supported_startup_flags)
1314         FIXME("unsupported startup flags %x\n", startup_flags & ~supported_startup_flags);
1315
1316     if (runtimeinfo_flags & ~supported_runtime_flags)
1317         FIXME("unsupported runtimeinfo flags %x\n", runtimeinfo_flags & ~supported_runtime_flags);
1318
1319     if (exefile && !config_file)
1320     {
1321         strcpyW(local_config_file, exefile);
1322         strcatW(local_config_file, dotconfig);
1323
1324         config_file = local_config_file;
1325     }
1326
1327     if (config_file)
1328     {
1329         int found=0;
1330         hr = parse_config_file(config_file, &parsed_config);
1331
1332         if (SUCCEEDED(hr))
1333         {
1334             supported_runtime *entry;
1335             LIST_FOR_EACH_ENTRY(entry, &parsed_config.supported_runtimes, supported_runtime, entry)
1336             {
1337                 hr = CLRMetaHost_GetRuntime(0, entry->version, &IID_ICLRRuntimeInfo, (void**)result);
1338                 if (SUCCEEDED(hr))
1339                 {
1340                     found = 1;
1341                     break;
1342                 }
1343             }
1344         }
1345         else
1346         {
1347             WARN("failed to parse config file %s, hr=%x\n", debugstr_w(config_file), hr);
1348         }
1349
1350         free_parsed_config_file(&parsed_config);
1351
1352         if (found)
1353             return S_OK;
1354     }
1355
1356     if (exefile && !version)
1357     {
1358         hr = CLRMetaHost_GetVersionFromFile(0, exefile, local_version, &local_version_size);
1359
1360         version = local_version;
1361
1362         if (FAILED(hr)) return hr;
1363     }
1364
1365     if (version)
1366     {
1367         hr = CLRMetaHost_GetRuntime(0, version, &IID_ICLRRuntimeInfo, (void**)result);
1368         if(SUCCEEDED(hr))
1369             return hr;
1370     }
1371
1372     if (runtimeinfo_flags & RUNTIME_INFO_UPGRADE_VERSION)
1373     {
1374         DWORD major, minor, build;
1375
1376         if (version && !parse_runtime_version(version, &major, &minor, &build))
1377         {
1378             ERR("Cannot parse %s\n", debugstr_w(version));
1379             return CLR_E_SHIM_RUNTIME;
1380         }
1381
1382         find_runtimes();
1383
1384         if (legacy)
1385             i = 2;
1386         else
1387             i = NUM_RUNTIMES;
1388
1389         while (i--)
1390         {
1391             if (runtimes[i].mono_abi_version)
1392             {
1393                 /* Must be greater or equal to the version passed in. */
1394                 if (!version || ((runtimes[i].major >= major && runtimes[i].minor >= minor && runtimes[i].build >= build) ||
1395                      (runtimes[i].major >= major && runtimes[i].minor > minor) ||
1396                      (runtimes[i].major > major)))
1397                 {
1398                     return ICLRRuntimeInfo_QueryInterface(&runtimes[i].ICLRRuntimeInfo_iface,
1399                             &IID_ICLRRuntimeInfo, (void **)result);
1400                 }
1401             }
1402         }
1403
1404         missing_runtime_message();
1405
1406         return CLR_E_SHIM_RUNTIME;
1407     }
1408
1409     return CLR_E_SHIM_RUNTIME;
1410 }