/* * * Copyright 2008 Alistair Leslie-Hughes * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #define COBJMACROS #include #include "windef.h" #include "winbase.h" #include "winuser.h" #include "winnls.h" #include "winreg.h" #include "ole2.h" #include "shellapi.h" #include "cor.h" #include "mscoree.h" #include "metahost.h" #include "wine/list.h" #include "mscoree_private.h" #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL( mscoree ); struct RuntimeHost { const struct ICorRuntimeHostVtbl *lpVtbl; const struct ICLRRuntimeHostVtbl *lpCLRHostVtbl; const CLRRuntimeInfo *version; loaded_mono *mono; struct list domains; MonoDomain *default_domain; CRITICAL_SECTION lock; LONG ref; }; struct DomainEntry { struct list entry; MonoDomain *domain; }; static HRESULT RuntimeHost_AddDomain(RuntimeHost *This, MonoDomain **result) { struct DomainEntry *entry; char *mscorlib_path; HRESULT res=S_OK; EnterCriticalSection(&This->lock); entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry)); if (!entry) { res = E_OUTOFMEMORY; goto end; } mscorlib_path = WtoA(This->version->mscorlib_path); if (!mscorlib_path) { HeapFree(GetProcessHeap(), 0, entry); res = E_OUTOFMEMORY; goto end; } entry->domain = This->mono->mono_jit_init(mscorlib_path); HeapFree(GetProcessHeap(), 0, mscorlib_path); if (!entry->domain) { HeapFree(GetProcessHeap(), 0, entry); res = E_FAIL; goto end; } This->mono->is_started = TRUE; list_add_tail(&This->domains, &entry->entry); *result = entry->domain; end: LeaveCriticalSection(&This->lock); return res; } static HRESULT RuntimeHost_GetDefaultDomain(RuntimeHost *This, MonoDomain **result) { HRESULT res=S_OK; EnterCriticalSection(&This->lock); if (This->default_domain) goto end; res = RuntimeHost_AddDomain(This, &This->default_domain); end: *result = This->default_domain; LeaveCriticalSection(&This->lock); return res; } static void RuntimeHost_DeleteDomain(RuntimeHost *This, MonoDomain *domain) { struct DomainEntry *entry; EnterCriticalSection(&This->lock); LIST_FOR_EACH_ENTRY(entry, &This->domains, struct DomainEntry, entry) { if (entry->domain == domain) { list_remove(&entry->entry); if (This->default_domain == domain) This->default_domain = NULL; HeapFree(GetProcessHeap(), 0, entry); break; } } LeaveCriticalSection(&This->lock); } static inline RuntimeHost *impl_from_ICLRRuntimeHost( ICLRRuntimeHost *iface ) { return (RuntimeHost *)((char*)iface - FIELD_OFFSET(RuntimeHost, lpCLRHostVtbl)); } static inline RuntimeHost *impl_from_ICorRuntimeHost( ICorRuntimeHost *iface ) { return (RuntimeHost *)((char*)iface - FIELD_OFFSET(RuntimeHost, lpVtbl)); } /*** IUnknown methods ***/ static HRESULT WINAPI corruntimehost_QueryInterface(ICorRuntimeHost* iface, REFIID riid, void **ppvObject) { RuntimeHost *This = impl_from_ICorRuntimeHost( iface ); TRACE("%p %s %p\n", This, debugstr_guid(riid), ppvObject); if ( IsEqualGUID( riid, &IID_ICorRuntimeHost ) || IsEqualGUID( riid, &IID_IUnknown ) ) { *ppvObject = iface; } else { FIXME("Unsupported interface %s\n", debugstr_guid(riid)); return E_NOINTERFACE; } ICorRuntimeHost_AddRef( iface ); return S_OK; } static ULONG WINAPI corruntimehost_AddRef(ICorRuntimeHost* iface) { RuntimeHost *This = impl_from_ICorRuntimeHost( iface ); return InterlockedIncrement( &This->ref ); } static ULONG WINAPI corruntimehost_Release(ICorRuntimeHost* iface) { RuntimeHost *This = impl_from_ICorRuntimeHost( iface ); ULONG ref; ref = InterlockedDecrement( &This->ref ); return ref; } /*** ICorRuntimeHost methods ***/ static HRESULT WINAPI corruntimehost_CreateLogicalThreadState( ICorRuntimeHost* iface) { FIXME("stub %p\n", iface); return E_NOTIMPL; } static HRESULT WINAPI corruntimehost_DeleteLogicalThreadState( ICorRuntimeHost* iface) { FIXME("stub %p\n", iface); return E_NOTIMPL; } static HRESULT WINAPI corruntimehost_SwitchInLogicalThreadState( ICorRuntimeHost* iface, DWORD *fiberCookie) { FIXME("stub %p\n", iface); return E_NOTIMPL; } static HRESULT WINAPI corruntimehost_SwitchOutLogicalThreadState( ICorRuntimeHost* iface, DWORD **fiberCookie) { FIXME("stub %p\n", iface); return E_NOTIMPL; } static HRESULT WINAPI corruntimehost_LocksHeldByLogicalThread( ICorRuntimeHost* iface, DWORD *pCount) { FIXME("stub %p\n", iface); return E_NOTIMPL; } static HRESULT WINAPI corruntimehost_MapFile( ICorRuntimeHost* iface, HANDLE hFile, HMODULE *mapAddress) { FIXME("stub %p\n", iface); return E_NOTIMPL; } static HRESULT WINAPI corruntimehost_GetConfiguration( ICorRuntimeHost* iface, ICorConfiguration **pConfiguration) { FIXME("stub %p\n", iface); return E_NOTIMPL; } static HRESULT WINAPI corruntimehost_Start( ICorRuntimeHost* iface) { FIXME("stub %p\n", iface); return E_NOTIMPL; } static HRESULT WINAPI corruntimehost_Stop( ICorRuntimeHost* iface) { FIXME("stub %p\n", iface); return E_NOTIMPL; } static HRESULT WINAPI corruntimehost_CreateDomain( ICorRuntimeHost* iface, LPCWSTR friendlyName, IUnknown *identityArray, IUnknown **appDomain) { FIXME("stub %p\n", iface); return E_NOTIMPL; } static HRESULT WINAPI corruntimehost_GetDefaultDomain( ICorRuntimeHost* iface, IUnknown **pAppDomain) { FIXME("stub %p\n", iface); return E_NOTIMPL; } static HRESULT WINAPI corruntimehost_EnumDomains( ICorRuntimeHost* iface, HDOMAINENUM *hEnum) { FIXME("stub %p\n", iface); return E_NOTIMPL; } static HRESULT WINAPI corruntimehost_NextDomain( ICorRuntimeHost* iface, HDOMAINENUM hEnum, IUnknown **appDomain) { FIXME("stub %p\n", iface); return E_NOTIMPL; } static HRESULT WINAPI corruntimehost_CloseEnum( ICorRuntimeHost* iface, HDOMAINENUM hEnum) { FIXME("stub %p\n", iface); return E_NOTIMPL; } static HRESULT WINAPI corruntimehost_CreateDomainEx( ICorRuntimeHost* iface, LPCWSTR friendlyName, IUnknown *setup, IUnknown *evidence, IUnknown **appDomain) { FIXME("stub %p\n", iface); return E_NOTIMPL; } static HRESULT WINAPI corruntimehost_CreateDomainSetup( ICorRuntimeHost* iface, IUnknown **appDomainSetup) { FIXME("stub %p\n", iface); return E_NOTIMPL; } static HRESULT WINAPI corruntimehost_CreateEvidence( ICorRuntimeHost* iface, IUnknown **evidence) { FIXME("stub %p\n", iface); return E_NOTIMPL; } static HRESULT WINAPI corruntimehost_UnloadDomain( ICorRuntimeHost* iface, IUnknown *appDomain) { FIXME("stub %p\n", iface); return E_NOTIMPL; } static HRESULT WINAPI corruntimehost_CurrentDomain( ICorRuntimeHost* iface, IUnknown **appDomain) { FIXME("stub %p\n", iface); return E_NOTIMPL; } static const struct ICorRuntimeHostVtbl corruntimehost_vtbl = { corruntimehost_QueryInterface, corruntimehost_AddRef, corruntimehost_Release, corruntimehost_CreateLogicalThreadState, corruntimehost_DeleteLogicalThreadState, corruntimehost_SwitchInLogicalThreadState, corruntimehost_SwitchOutLogicalThreadState, corruntimehost_LocksHeldByLogicalThread, corruntimehost_MapFile, corruntimehost_GetConfiguration, corruntimehost_Start, corruntimehost_Stop, corruntimehost_CreateDomain, corruntimehost_GetDefaultDomain, corruntimehost_EnumDomains, corruntimehost_NextDomain, corruntimehost_CloseEnum, corruntimehost_CreateDomainEx, corruntimehost_CreateDomainSetup, corruntimehost_CreateEvidence, corruntimehost_UnloadDomain, corruntimehost_CurrentDomain }; static HRESULT WINAPI CLRRuntimeHost_QueryInterface(ICLRRuntimeHost* iface, REFIID riid, void **ppvObject) { RuntimeHost *This = impl_from_ICLRRuntimeHost( iface ); TRACE("%p %s %p\n", This, debugstr_guid(riid), ppvObject); if ( IsEqualGUID( riid, &IID_ICLRRuntimeHost ) || IsEqualGUID( riid, &IID_IUnknown ) ) { *ppvObject = iface; } else { FIXME("Unsupported interface %s\n", debugstr_guid(riid)); return E_NOINTERFACE; } ICLRRuntimeHost_AddRef( iface ); return S_OK; } static ULONG WINAPI CLRRuntimeHost_AddRef(ICLRRuntimeHost* iface) { RuntimeHost *This = impl_from_ICLRRuntimeHost( iface ); return IUnknown_AddRef((IUnknown*)&This->lpVtbl); } static ULONG WINAPI CLRRuntimeHost_Release(ICLRRuntimeHost* iface) { RuntimeHost *This = impl_from_ICLRRuntimeHost( iface ); return IUnknown_Release((IUnknown*)&This->lpVtbl); } static HRESULT WINAPI CLRRuntimeHost_Start(ICLRRuntimeHost* iface) { FIXME("(%p)\n", iface); return E_NOTIMPL; } static HRESULT WINAPI CLRRuntimeHost_Stop(ICLRRuntimeHost* iface) { FIXME("(%p)\n", iface); return E_NOTIMPL; } static HRESULT WINAPI CLRRuntimeHost_SetHostControl(ICLRRuntimeHost* iface, IHostControl *pHostControl) { FIXME("(%p,%p)\n", iface, pHostControl); return E_NOTIMPL; } static HRESULT WINAPI CLRRuntimeHost_GetCLRControl(ICLRRuntimeHost* iface, ICLRControl **pCLRControl) { FIXME("(%p,%p)\n", iface, pCLRControl); return E_NOTIMPL; } static HRESULT WINAPI CLRRuntimeHost_UnloadAppDomain(ICLRRuntimeHost* iface, DWORD dwAppDomainId, BOOL fWaitUntilDone) { FIXME("(%p,%u,%i)\n", iface, dwAppDomainId, fWaitUntilDone); return E_NOTIMPL; } static HRESULT WINAPI CLRRuntimeHost_ExecuteInAppDomain(ICLRRuntimeHost* iface, DWORD dwAppDomainId, FExecuteInAppDomainCallback pCallback, void *cookie) { FIXME("(%p,%u,%p,%p)\n", iface, dwAppDomainId, pCallback, cookie); return E_NOTIMPL; } static HRESULT WINAPI CLRRuntimeHost_GetCurrentAppDomainId(ICLRRuntimeHost* iface, DWORD *pdwAppDomainId) { FIXME("(%p,%p)\n", iface, pdwAppDomainId); return E_NOTIMPL; } static HRESULT WINAPI CLRRuntimeHost_ExecuteApplication(ICLRRuntimeHost* iface, LPCWSTR pwzAppFullName, DWORD dwManifestPaths, LPCWSTR *ppwzManifestPaths, DWORD dwActivationData, LPCWSTR *ppwzActivationData, int *pReturnValue) { FIXME("(%p,%s,%u,%u)\n", iface, debugstr_w(pwzAppFullName), dwManifestPaths, dwActivationData); return E_NOTIMPL; } static HRESULT WINAPI CLRRuntimeHost_ExecuteInDefaultAppDomain(ICLRRuntimeHost* iface, LPCWSTR pwzAssemblyPath, LPCWSTR pwzTypeName, LPCWSTR pwzMethodName, LPCWSTR pwzArgument, DWORD *pReturnValue) { FIXME("(%p,%s,%s,%s,%s)\n", iface, debugstr_w(pwzAssemblyPath), debugstr_w(pwzTypeName), debugstr_w(pwzMethodName), debugstr_w(pwzArgument)); return E_NOTIMPL; } static const struct ICLRRuntimeHostVtbl CLRHostVtbl = { CLRRuntimeHost_QueryInterface, CLRRuntimeHost_AddRef, CLRRuntimeHost_Release, CLRRuntimeHost_Start, CLRRuntimeHost_Stop, CLRRuntimeHost_SetHostControl, CLRRuntimeHost_GetCLRControl, CLRRuntimeHost_UnloadAppDomain, CLRRuntimeHost_ExecuteInAppDomain, CLRRuntimeHost_GetCurrentAppDomainId, CLRRuntimeHost_ExecuteApplication, CLRRuntimeHost_ExecuteInDefaultAppDomain }; /* Create an instance of a type given its name, by calling its constructor with * no arguments. Note that result MUST be in the stack, or the garbage * collector may free it prematurely. */ HRESULT RuntimeHost_CreateManagedInstance(RuntimeHost *This, LPCWSTR name, MonoDomain *domain, MonoObject **result) { HRESULT hr=S_OK; char *nameA=NULL; MonoType *type; MonoClass *klass; MonoObject *obj; if (!domain) hr = RuntimeHost_GetDefaultDomain(This, &domain); if (SUCCEEDED(hr)) { nameA = WtoA(name); if (!nameA) hr = E_OUTOFMEMORY; } if (SUCCEEDED(hr)) { type = This->mono->mono_reflection_type_from_name(nameA, NULL); if (!type) { ERR("Cannot find type %s\n", debugstr_w(name)); hr = E_FAIL; } } if (SUCCEEDED(hr)) { klass = This->mono->mono_class_from_mono_type(type); if (!klass) { ERR("Cannot convert type %s to a class\n", debugstr_w(name)); hr = E_FAIL; } } if (SUCCEEDED(hr)) { obj = This->mono->mono_object_new(domain, klass); if (!obj) { ERR("Cannot allocate object of type %s\n", debugstr_w(name)); hr = E_FAIL; } } if (SUCCEEDED(hr)) { /* FIXME: Detect exceptions from the constructor? */ This->mono->mono_runtime_object_init(obj); *result = obj; } HeapFree(GetProcessHeap(), 0, nameA); return hr; } /* Get an IUnknown pointer for a Mono object. * * This is just a "light" wrapper around * System.Runtime.InteropServices.Marshal:GetIUnknownForObject * * NOTE: The IUnknown* is created with a reference to the object. * Until they have a reference, objects must be in the stack to prevent the * garbage collector from freeing them. */ HRESULT RuntimeHost_GetIUnknownForObject(RuntimeHost *This, MonoObject *obj, IUnknown **ppUnk) { MonoDomain *domain; MonoAssembly *assembly; MonoImage *image; MonoClass *klass; MonoMethod *method; MonoObject *result; void *args[2]; domain = This->mono->mono_object_get_domain(obj); assembly = This->mono->mono_domain_assembly_open(domain, "mscorlib"); if (!assembly) { ERR("Cannot load mscorlib\n"); return E_FAIL; } image = This->mono->mono_assembly_get_image(assembly); if (!image) { ERR("Couldn't get assembly image\n"); return E_FAIL; } klass = This->mono->mono_class_from_name(image, "System.Runtime.InteropServices", "Marshal"); if (!klass) { ERR("Couldn't get class from image\n"); return E_FAIL; } method = This->mono->mono_class_get_method_from_name(klass, "GetIUnknownForObject", 1); if (!method) { ERR("Couldn't get method from class\n"); return E_FAIL; } args[0] = obj; args[1] = NULL; result = This->mono->mono_runtime_invoke(method, NULL, args, NULL); if (!result) { ERR("Couldn't get result pointer\n"); return E_FAIL; } *ppUnk = *(IUnknown**)This->mono->mono_object_unbox(result); if (!*ppUnk) { ERR("GetIUnknownForObject returned 0\n"); return E_FAIL; } return S_OK; } static void get_utf8_args(int *argc, char ***argv) { WCHAR **argvw; int size=0, i; char *current_arg; argvw = CommandLineToArgvW(GetCommandLineW(), argc); for (i=0; i<*argc; i++) { size += sizeof(char*); size += WideCharToMultiByte(CP_UTF8, 0, argvw[i], -1, NULL, 0, NULL, NULL); } size += sizeof(char*); *argv = HeapAlloc(GetProcessHeap(), 0, size); current_arg = (char*)(*argv + *argc + 1); for (i=0; i<*argc; i++) { (*argv)[i] = current_arg; current_arg += WideCharToMultiByte(CP_UTF8, 0, argvw[i], -1, current_arg, size, NULL, NULL); } (*argv)[*argc] = NULL; HeapFree(GetProcessHeap(), 0, argvw); } __int32 WINAPI _CorExeMain(void) { int exit_code; int argc; char **argv; MonoDomain *domain; MonoAssembly *assembly; WCHAR filename[MAX_PATH]; char *filenameA; ICLRRuntimeInfo *info; RuntimeHost *host; HRESULT hr; int i; get_utf8_args(&argc, &argv); GetModuleFileNameW(NULL, filename, MAX_PATH); TRACE("%s", debugstr_w(filename)); for (i=0; imono->mono_domain_assembly_open(domain, filenameA); exit_code = host->mono->mono_jit_exec(domain, assembly, argc, argv); RuntimeHost_DeleteDomain(host, domain); } else exit_code = -1; ICLRRuntimeInfo_Release(info); } else exit_code = -1; HeapFree(GetProcessHeap(), 0, argv); unload_all_runtimes(); return exit_code; } HRESULT RuntimeHost_Construct(const CLRRuntimeInfo *runtime_version, loaded_mono *loaded_mono, RuntimeHost** result) { RuntimeHost *This; This = HeapAlloc( GetProcessHeap(), 0, sizeof *This ); if ( !This ) return E_OUTOFMEMORY; This->lpVtbl = &corruntimehost_vtbl; This->lpCLRHostVtbl = &CLRHostVtbl; This->ref = 1; This->version = runtime_version; This->mono = loaded_mono; list_init(&This->domains); This->default_domain = NULL; InitializeCriticalSection(&This->lock); This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": RuntimeHost.lock"); *result = This; return S_OK; } HRESULT RuntimeHost_GetInterface(RuntimeHost *This, REFCLSID clsid, REFIID riid, void **ppv) { IUnknown *unk; HRESULT hr; if (IsEqualGUID(clsid, &CLSID_CorRuntimeHost)) { unk = (IUnknown*)&This->lpVtbl; IUnknown_AddRef(unk); } else if (IsEqualGUID(clsid, &CLSID_CLRRuntimeHost)) { unk = (IUnknown*)&This->lpCLRHostVtbl; IUnknown_AddRef(unk); } else if (IsEqualGUID(clsid, &CLSID_CorMetaDataDispenser) || IsEqualGUID(clsid, &CLSID_CorMetaDataDispenserRuntime)) { hr = MetaDataDispenser_CreateInstance(&unk); if (FAILED(hr)) return hr; } else unk = NULL; if (unk) { hr = IUnknown_QueryInterface(unk, riid, ppv); IUnknown_Release(unk); return hr; } else FIXME("not implemented for class %s\n", debugstr_guid(clsid)); return CLASS_E_CLASSNOTAVAILABLE; } HRESULT RuntimeHost_Destroy(RuntimeHost *This) { struct DomainEntry *cursor, *cursor2; This->lock.DebugInfo->Spare[0] = 0; DeleteCriticalSection(&This->lock); LIST_FOR_EACH_ENTRY_SAFE(cursor, cursor2, &This->domains, struct DomainEntry, entry) { list_remove(&cursor->entry); HeapFree(GetProcessHeap(), 0, cursor); } HeapFree( GetProcessHeap(), 0, This ); return S_OK; }