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