1804 lines
46 KiB
C
1804 lines
46 KiB
C
/*
|
|
*
|
|
* 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 <assert.h>
|
|
#include <stdarg.h>
|
|
|
|
#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 "corhdr.h"
|
|
#include "cordebug.h"
|
|
#include "wine/list.h"
|
|
#include "mscoree_private.h"
|
|
|
|
#include "wine/debug.h"
|
|
#include "wine/unicode.h"
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL( mscoree );
|
|
|
|
#include "initguid.h"
|
|
|
|
DEFINE_GUID(IID__AppDomain, 0x05f696dc,0x2b29,0x3663,0xad,0x8b,0xc4,0x38,0x9c,0xf2,0xa7,0x13);
|
|
|
|
struct DomainEntry
|
|
{
|
|
struct list entry;
|
|
MonoDomain *domain;
|
|
};
|
|
|
|
static HANDLE dll_fixup_heap; /* using a separate heap so we can have execute permission */
|
|
|
|
static struct list dll_fixups;
|
|
|
|
struct dll_fixup
|
|
{
|
|
struct list entry;
|
|
BOOL done;
|
|
HMODULE dll;
|
|
void *thunk_code; /* pointer into dll_fixup_heap */
|
|
VTableFixup *fixup;
|
|
void *vtable;
|
|
void *tokens; /* pointer into process heap */
|
|
};
|
|
|
|
static MonoDomain* domain_attach(MonoDomain *domain)
|
|
{
|
|
MonoDomain *prev_domain = mono_domain_get();
|
|
|
|
if (prev_domain == domain)
|
|
/* Do not set or restore domain. */
|
|
return NULL;
|
|
|
|
mono_thread_attach(domain);
|
|
|
|
return prev_domain;
|
|
}
|
|
|
|
static void domain_restore(MonoDomain *prev_domain)
|
|
{
|
|
if (prev_domain != NULL)
|
|
mono_domain_set(prev_domain, FALSE);
|
|
}
|
|
|
|
static HRESULT RuntimeHost_AddDefaultDomain(RuntimeHost *This, MonoDomain **result)
|
|
{
|
|
struct DomainEntry *entry;
|
|
HRESULT res=S_OK;
|
|
|
|
EnterCriticalSection(&This->lock);
|
|
|
|
entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
|
|
if (!entry)
|
|
{
|
|
res = E_OUTOFMEMORY;
|
|
goto end;
|
|
}
|
|
|
|
/* FIXME: Use exe filename to name the domain? */
|
|
entry->domain = mono_jit_init_version("mscorlib.dll", "v4.0.30319");
|
|
|
|
if (!entry->domain)
|
|
{
|
|
HeapFree(GetProcessHeap(), 0, entry);
|
|
res = E_FAIL;
|
|
goto end;
|
|
}
|
|
|
|
is_mono_started = TRUE;
|
|
|
|
list_add_tail(&This->domains, &entry->entry);
|
|
|
|
*result = entry->domain;
|
|
|
|
end:
|
|
LeaveCriticalSection(&This->lock);
|
|
|
|
return res;
|
|
}
|
|
|
|
static HRESULT RuntimeHost_GetDefaultDomain(RuntimeHost *This, const WCHAR *config_path, MonoDomain **result)
|
|
{
|
|
WCHAR config_dir[MAX_PATH];
|
|
WCHAR base_dir[MAX_PATH];
|
|
char *base_dirA, *config_pathA, *slash;
|
|
HRESULT res=S_OK;
|
|
|
|
EnterCriticalSection(&This->lock);
|
|
|
|
if (This->default_domain) goto end;
|
|
|
|
res = RuntimeHost_AddDefaultDomain(This, &This->default_domain);
|
|
|
|
if (!config_path)
|
|
{
|
|
DWORD len = ARRAY_SIZE(config_dir);
|
|
|
|
static const WCHAR machine_configW[] = {'\\','C','O','N','F','I','G','\\','m','a','c','h','i','n','e','.','c','o','n','f','i','g',0};
|
|
|
|
res = ICLRRuntimeInfo_GetRuntimeDirectory(&This->version->ICLRRuntimeInfo_iface,
|
|
config_dir, &len);
|
|
if (FAILED(res))
|
|
goto end;
|
|
|
|
lstrcatW(config_dir, machine_configW);
|
|
|
|
config_path = config_dir;
|
|
}
|
|
|
|
config_pathA = WtoA(config_path);
|
|
if (!config_pathA)
|
|
{
|
|
res = E_OUTOFMEMORY;
|
|
goto end;
|
|
}
|
|
|
|
GetModuleFileNameW(NULL, base_dir, ARRAY_SIZE(base_dir));
|
|
base_dirA = WtoA(base_dir);
|
|
if (!base_dirA)
|
|
{
|
|
HeapFree(GetProcessHeap(), 0, config_pathA);
|
|
res = E_OUTOFMEMORY;
|
|
goto end;
|
|
}
|
|
|
|
slash = strrchr(base_dirA, '\\');
|
|
if (slash)
|
|
*(slash + 1) = 0;
|
|
|
|
TRACE("setting base_dir: %s, config_path: %s\n", base_dirA, config_pathA);
|
|
mono_domain_set_config(This->default_domain, base_dirA, config_pathA);
|
|
|
|
HeapFree(GetProcessHeap(), 0, config_pathA);
|
|
HeapFree(GetProcessHeap(), 0, base_dirA);
|
|
|
|
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 BOOL RuntimeHost_GetMethod(MonoDomain *domain, const char *assemblyname,
|
|
const char *namespace, const char *typename, const char *methodname, int arg_count,
|
|
MonoMethod **method)
|
|
{
|
|
MonoAssembly *assembly;
|
|
MonoImage *image;
|
|
MonoClass *klass;
|
|
|
|
assembly = mono_domain_assembly_open(domain, assemblyname);
|
|
if (!assembly)
|
|
{
|
|
ERR("Cannot load assembly %s\n", assemblyname);
|
|
return FALSE;
|
|
}
|
|
|
|
image = mono_assembly_get_image(assembly);
|
|
if (!image)
|
|
{
|
|
ERR("Couldn't get assembly image for %s\n", assemblyname);
|
|
return FALSE;
|
|
}
|
|
|
|
klass = mono_class_from_name(image, namespace, typename);
|
|
if (!klass)
|
|
{
|
|
ERR("Couldn't get class %s.%s from image\n", namespace, typename);
|
|
return FALSE;
|
|
}
|
|
|
|
*method = mono_class_get_method_from_name(klass, methodname, arg_count);
|
|
if (!*method)
|
|
{
|
|
ERR("Couldn't get method %s from class %s.%s\n", methodname, namespace, typename);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static HRESULT RuntimeHost_Invoke(RuntimeHost *This, MonoDomain *domain,
|
|
const char *assemblyname, const char *namespace, const char *typename, const char *methodname,
|
|
MonoObject *obj, void **args, int arg_count, MonoObject **result);
|
|
|
|
static HRESULT RuntimeHost_DoInvoke(RuntimeHost *This, MonoDomain *domain,
|
|
const char *methodname, MonoMethod *method, MonoObject *obj, void **args, MonoObject **result)
|
|
{
|
|
MonoObject *exc;
|
|
static const char *get_hresult = "get_HResult";
|
|
|
|
*result = mono_runtime_invoke(method, obj, args, &exc);
|
|
if (exc)
|
|
{
|
|
HRESULT hr;
|
|
MonoObject *hr_object;
|
|
|
|
if (methodname != get_hresult)
|
|
{
|
|
/* Map the exception to an HRESULT. */
|
|
hr = RuntimeHost_Invoke(This, domain, "mscorlib", "System", "Exception", get_hresult,
|
|
exc, NULL, 0, &hr_object);
|
|
if (SUCCEEDED(hr))
|
|
hr = *(HRESULT*)mono_object_unbox(hr_object);
|
|
if (SUCCEEDED(hr))
|
|
hr = E_FAIL;
|
|
}
|
|
else
|
|
hr = E_FAIL;
|
|
*result = NULL;
|
|
return hr;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT RuntimeHost_Invoke(RuntimeHost *This, MonoDomain *domain,
|
|
const char *assemblyname, const char *namespace, const char *typename, const char *methodname,
|
|
MonoObject *obj, void **args, int arg_count, MonoObject **result)
|
|
{
|
|
MonoMethod *method;
|
|
MonoDomain *prev_domain;
|
|
HRESULT hr;
|
|
|
|
*result = NULL;
|
|
|
|
prev_domain = domain_attach(domain);
|
|
|
|
if (!RuntimeHost_GetMethod(domain, assemblyname, namespace, typename, methodname,
|
|
arg_count, &method))
|
|
{
|
|
domain_restore(prev_domain);
|
|
return E_FAIL;
|
|
}
|
|
|
|
hr = RuntimeHost_DoInvoke(This, domain, methodname, method, obj, args, result);
|
|
if (FAILED(hr))
|
|
{
|
|
ERR("Method %s.%s:%s raised an exception, hr=%x\n", namespace, typename, methodname, hr);
|
|
}
|
|
|
|
domain_restore(prev_domain);
|
|
|
|
return hr;
|
|
}
|
|
|
|
static HRESULT RuntimeHost_VirtualInvoke(RuntimeHost *This, MonoDomain *domain,
|
|
const char *assemblyname, const char *namespace, const char *typename, const char *methodname,
|
|
MonoObject *obj, void **args, int arg_count, MonoObject **result)
|
|
{
|
|
MonoMethod *method;
|
|
MonoDomain *prev_domain;
|
|
HRESULT hr;
|
|
|
|
*result = NULL;
|
|
|
|
if (!obj)
|
|
{
|
|
ERR("\"this\" object cannot be null\n");
|
|
return E_POINTER;
|
|
}
|
|
|
|
prev_domain = domain_attach(domain);
|
|
|
|
if (!RuntimeHost_GetMethod(domain, assemblyname, namespace, typename, methodname,
|
|
arg_count, &method))
|
|
{
|
|
domain_restore(prev_domain);
|
|
return E_FAIL;
|
|
}
|
|
|
|
method = mono_object_get_virtual_method(obj, method);
|
|
if (!method)
|
|
{
|
|
ERR("Object %p does not support method %s.%s:%s\n", obj, namespace, typename, methodname);
|
|
domain_restore(prev_domain);
|
|
return E_FAIL;
|
|
}
|
|
|
|
hr = RuntimeHost_DoInvoke(This, domain, methodname, method, obj, args, result);
|
|
if (FAILED(hr))
|
|
{
|
|
ERR("Method %s.%s:%s raised an exception, hr=%x\n", namespace, typename, methodname, hr);
|
|
}
|
|
|
|
domain_restore(prev_domain);
|
|
|
|
return hr;
|
|
}
|
|
|
|
static HRESULT RuntimeHost_GetObjectForIUnknown(RuntimeHost *This, MonoDomain *domain,
|
|
IUnknown *unk, MonoObject **obj)
|
|
{
|
|
HRESULT hr;
|
|
void *args[1];
|
|
MonoObject *result;
|
|
|
|
args[0] = &unk;
|
|
hr = RuntimeHost_Invoke(This, domain, "mscorlib", "System.Runtime.InteropServices", "Marshal", "GetObjectForIUnknown",
|
|
NULL, args, 1, &result);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
*obj = result;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
static HRESULT RuntimeHost_AddDomain(RuntimeHost *This, const WCHAR *name, IUnknown *setup,
|
|
IUnknown *evidence, MonoDomain **result)
|
|
{
|
|
HRESULT res;
|
|
char *nameA;
|
|
MonoDomain *domain;
|
|
void *args[3];
|
|
MonoObject *new_domain, *id;
|
|
|
|
res = RuntimeHost_GetDefaultDomain(This, NULL, &domain);
|
|
if (FAILED(res))
|
|
{
|
|
return res;
|
|
}
|
|
|
|
nameA = WtoA(name);
|
|
if (!nameA)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
args[0] = mono_string_new(domain, nameA);
|
|
HeapFree(GetProcessHeap(), 0, nameA);
|
|
|
|
if (!args[0])
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
if (evidence)
|
|
{
|
|
res = RuntimeHost_GetObjectForIUnknown(This, domain, evidence, (MonoObject **)&args[1]);
|
|
if (FAILED(res))
|
|
{
|
|
return res;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
args[1] = NULL;
|
|
}
|
|
|
|
if (setup)
|
|
{
|
|
res = RuntimeHost_GetObjectForIUnknown(This, domain, setup, (MonoObject **)&args[2]);
|
|
if (FAILED(res))
|
|
{
|
|
return res;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
args[2] = NULL;
|
|
}
|
|
|
|
res = RuntimeHost_Invoke(This, domain, "mscorlib", "System", "AppDomain", "CreateDomain",
|
|
NULL, args, 3, &new_domain);
|
|
|
|
if (FAILED(res))
|
|
{
|
|
return res;
|
|
}
|
|
|
|
/* new_domain is not the AppDomain itself, but a transparent proxy.
|
|
* So, we'll retrieve its ID, and use that to get the real domain object.
|
|
* We can't do a regular invoke, because that will bypass the proxy.
|
|
* Instead, do a vcall.
|
|
*/
|
|
|
|
res = RuntimeHost_VirtualInvoke(This, domain, "mscorlib", "System", "AppDomain", "get_Id",
|
|
new_domain, NULL, 0, &id);
|
|
|
|
if (FAILED(res))
|
|
{
|
|
return res;
|
|
}
|
|
|
|
TRACE("returning domain id %d\n", *(int *)mono_object_unbox(id));
|
|
|
|
*result = mono_domain_get_by_id(*(int *)mono_object_unbox(id));
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT RuntimeHost_GetIUnknownForDomain(RuntimeHost *This, MonoDomain *domain, IUnknown **punk)
|
|
{
|
|
HRESULT hr;
|
|
MonoObject *appdomain_object;
|
|
IUnknown *unk;
|
|
|
|
hr = RuntimeHost_Invoke(This, domain, "mscorlib", "System", "AppDomain", "get_CurrentDomain",
|
|
NULL, NULL, 0, &appdomain_object);
|
|
|
|
if (SUCCEEDED(hr))
|
|
hr = RuntimeHost_GetIUnknownForObject(This, appdomain_object, &unk);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = IUnknown_QueryInterface(unk, &IID__AppDomain, (void**)punk);
|
|
|
|
IUnknown_Release(unk);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
void RuntimeHost_ExitProcess(RuntimeHost *This, INT exitcode)
|
|
{
|
|
HRESULT hr;
|
|
void *args[2];
|
|
MonoDomain *domain;
|
|
MonoObject *dummy;
|
|
|
|
hr = RuntimeHost_GetDefaultDomain(This, NULL, &domain);
|
|
if (FAILED(hr))
|
|
{
|
|
ERR("Cannot get domain, hr=%x\n", hr);
|
|
return;
|
|
}
|
|
|
|
args[0] = &exitcode;
|
|
args[1] = NULL;
|
|
RuntimeHost_Invoke(This, domain, "mscorlib", "System", "Environment", "Exit",
|
|
NULL, args, 1, &dummy);
|
|
|
|
ERR("Process should have exited\n");
|
|
}
|
|
|
|
static inline RuntimeHost *impl_from_ICLRRuntimeHost( ICLRRuntimeHost *iface )
|
|
{
|
|
return CONTAINING_RECORD(iface, RuntimeHost, ICLRRuntimeHost_iface);
|
|
}
|
|
|
|
static inline RuntimeHost *impl_from_ICorRuntimeHost( ICorRuntimeHost *iface )
|
|
{
|
|
return CONTAINING_RECORD(iface, RuntimeHost, ICorRuntimeHost_iface);
|
|
}
|
|
|
|
/*** 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)
|
|
{
|
|
RuntimeHost *This = impl_from_ICorRuntimeHost( iface );
|
|
MonoDomain *dummy;
|
|
|
|
TRACE("%p\n", This);
|
|
|
|
return RuntimeHost_GetDefaultDomain(This, NULL, &dummy);
|
|
}
|
|
|
|
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)
|
|
{
|
|
return ICorRuntimeHost_CreateDomainEx(iface, friendlyName, NULL, NULL, appDomain);
|
|
}
|
|
|
|
static HRESULT WINAPI corruntimehost_GetDefaultDomain(
|
|
ICorRuntimeHost* iface,
|
|
IUnknown **pAppDomain)
|
|
{
|
|
RuntimeHost *This = impl_from_ICorRuntimeHost( iface );
|
|
HRESULT hr;
|
|
MonoDomain *domain;
|
|
|
|
TRACE("(%p)\n", iface);
|
|
|
|
hr = RuntimeHost_GetDefaultDomain(This, NULL, &domain);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = RuntimeHost_GetIUnknownForDomain(This, domain, pAppDomain);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
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)
|
|
{
|
|
RuntimeHost *This = impl_from_ICorRuntimeHost( iface );
|
|
HRESULT hr;
|
|
MonoDomain *domain;
|
|
|
|
if (!friendlyName || !appDomain)
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
if (!is_mono_started)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
TRACE("(%p)\n", iface);
|
|
|
|
hr = RuntimeHost_AddDomain(This, friendlyName, setup, evidence, &domain);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = RuntimeHost_GetIUnknownForDomain(This, domain, appDomain);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
static HRESULT WINAPI corruntimehost_CreateDomainSetup(
|
|
ICorRuntimeHost* iface,
|
|
IUnknown **appDomainSetup)
|
|
{
|
|
RuntimeHost *This = impl_from_ICorRuntimeHost( iface );
|
|
HRESULT hr;
|
|
MonoDomain *domain;
|
|
MonoObject *obj;
|
|
static const WCHAR classnameW[] = {'S','y','s','t','e','m','.','A','p','p','D','o','m','a','i','n','S','e','t','u','p',',','m','s','c','o','r','l','i','b',0};
|
|
|
|
TRACE("(%p)\n", iface);
|
|
|
|
hr = RuntimeHost_GetDefaultDomain(This, NULL, &domain);
|
|
|
|
if (SUCCEEDED(hr))
|
|
hr = RuntimeHost_CreateManagedInstance(This, classnameW, domain, &obj);
|
|
|
|
if (SUCCEEDED(hr))
|
|
hr = RuntimeHost_GetIUnknownForObject(This, obj, appDomainSetup);
|
|
|
|
return hr;
|
|
}
|
|
|
|
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 ICorRuntimeHost_AddRef(&This->ICorRuntimeHost_iface);
|
|
}
|
|
|
|
static ULONG WINAPI CLRRuntimeHost_Release(ICLRRuntimeHost* iface)
|
|
{
|
|
RuntimeHost *This = impl_from_ICLRRuntimeHost( iface );
|
|
return ICorRuntimeHost_Release(&This->ICorRuntimeHost_iface);
|
|
}
|
|
|
|
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)
|
|
{
|
|
RuntimeHost *This = impl_from_ICLRRuntimeHost( iface );
|
|
HRESULT hr;
|
|
MonoDomain *domain, *prev_domain;
|
|
MonoObject *result;
|
|
MonoString *str;
|
|
char *filenameA = NULL, *classA = NULL, *methodA = NULL;
|
|
char *argsA = NULL, *ns;
|
|
|
|
TRACE("(%p,%s,%s,%s,%s)\n", iface, debugstr_w(pwzAssemblyPath),
|
|
debugstr_w(pwzTypeName), debugstr_w(pwzMethodName), debugstr_w(pwzArgument));
|
|
|
|
hr = RuntimeHost_GetDefaultDomain(This, NULL, &domain);
|
|
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
prev_domain = domain_attach(domain);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
filenameA = WtoA(pwzAssemblyPath);
|
|
if (!filenameA) hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
classA = WtoA(pwzTypeName);
|
|
if (!classA) hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
ns = strrchr(classA, '.');
|
|
if (ns)
|
|
*ns = '\0';
|
|
else
|
|
hr = E_INVALIDARG;
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
methodA = WtoA(pwzMethodName);
|
|
if (!methodA) hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
/* The .NET function we are calling has the following declaration
|
|
* public static int functionName(String param)
|
|
*/
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
argsA = WtoA(pwzArgument);
|
|
if (!argsA) hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
str = mono_string_new(domain, argsA);
|
|
if (!str) hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = RuntimeHost_Invoke(This, domain, filenameA, classA, ns+1, methodA,
|
|
NULL, (void**)&str, 1, &result);
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
*pReturnValue = *(DWORD*)mono_object_unbox(result);
|
|
|
|
domain_restore(prev_domain);
|
|
|
|
HeapFree(GetProcessHeap(), 0, filenameA);
|
|
HeapFree(GetProcessHeap(), 0, classA);
|
|
HeapFree(GetProcessHeap(), 0, argsA);
|
|
HeapFree(GetProcessHeap(), 0, methodA);
|
|
|
|
return hr;
|
|
}
|
|
|
|
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;
|
|
MonoDomain *prev_domain;
|
|
|
|
if (!domain)
|
|
hr = RuntimeHost_GetDefaultDomain(This, NULL, &domain);
|
|
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
prev_domain = domain_attach(domain);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
nameA = WtoA(name);
|
|
if (!nameA)
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
type = 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 = 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 = 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? */
|
|
mono_runtime_object_init(obj);
|
|
*result = obj;
|
|
}
|
|
|
|
domain_restore(prev_domain);
|
|
|
|
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;
|
|
MonoObject *result;
|
|
HRESULT hr;
|
|
|
|
domain = mono_object_get_domain(obj);
|
|
|
|
hr = RuntimeHost_Invoke(This, domain, "mscorlib", "System.Runtime.InteropServices", "Marshal", "GetIUnknownForObject",
|
|
NULL, (void**)&obj, 1, &result);
|
|
|
|
if (SUCCEEDED(hr))
|
|
*ppUnk = *(IUnknown**)mono_object_unbox(result);
|
|
else
|
|
*ppUnk = NULL;
|
|
|
|
return hr;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
#if __i386__
|
|
|
|
# define CAN_FIXUP_VTABLE 1
|
|
|
|
#include "pshpack1.h"
|
|
|
|
struct vtable_fixup_thunk
|
|
{
|
|
/* push %ecx */
|
|
BYTE i7;
|
|
/* sub $0x4,%esp */
|
|
BYTE i1[3];
|
|
/* mov fixup,(%esp) */
|
|
BYTE i2[3];
|
|
struct dll_fixup *fixup;
|
|
/* mov function,%eax */
|
|
BYTE i3;
|
|
void (CDECL *function)(struct dll_fixup *);
|
|
/* call *%eax */
|
|
BYTE i4[2];
|
|
/* pop %eax */
|
|
BYTE i5;
|
|
/* pop %ecx */
|
|
BYTE i8;
|
|
/* jmp *vtable_entry */
|
|
BYTE i6[2];
|
|
void *vtable_entry;
|
|
};
|
|
|
|
static const struct vtable_fixup_thunk thunk_template = {
|
|
0x51,
|
|
{0x83,0xec,0x04},
|
|
{0xc7,0x04,0x24},
|
|
NULL,
|
|
0xb8,
|
|
NULL,
|
|
{0xff,0xd0},
|
|
0x58,
|
|
0x59,
|
|
{0xff,0x25},
|
|
NULL
|
|
};
|
|
|
|
#include "poppack.h"
|
|
|
|
#elif __x86_64__ /* !__i386__ */
|
|
|
|
# define CAN_FIXUP_VTABLE 1
|
|
|
|
#include "pshpack1.h"
|
|
|
|
struct vtable_fixup_thunk
|
|
{
|
|
/* push %rbp;
|
|
mov %rsp, %rbp
|
|
sub $0x80, %rsp ; 0x8*4 + 0x10*4 + 0x20
|
|
*/
|
|
BYTE i1[11];
|
|
/*
|
|
mov %rcx, 0x60(%rsp); mov %rdx, 0x68(%rsp); mov %r8, 0x70(%rsp); mov %r9, 0x78(%rsp);
|
|
movaps %xmm0,0x20(%rsp); ...; movaps %xmm3,0x50(%esp)
|
|
*/
|
|
BYTE i2[40];
|
|
/* mov function,%rax */
|
|
BYTE i3[2];
|
|
void (CDECL *function)(struct dll_fixup *);
|
|
/* mov fixup,%rcx */
|
|
BYTE i4[2];
|
|
struct dll_fixup *fixup;
|
|
/* call *%rax */
|
|
BYTE i5[2];
|
|
/*
|
|
mov 0x60(%rsp),%rcx; mov 0x68(%rsp),%rdx; mov 0x70(%rsp),%r8; mov 0x78(%rsp),%r9;
|
|
movaps 0x20(%rsp),xmm0; ...; movaps 0x50(%esp),xmm3
|
|
*/
|
|
BYTE i6[40];
|
|
/* mov %rbp, %rsp
|
|
pop %rbp
|
|
*/
|
|
BYTE i7[4];
|
|
/* mov vtable_entry, %rax */
|
|
BYTE i8[2];
|
|
void *vtable_entry;
|
|
/* mov [%rax],%rax
|
|
jmp %rax */
|
|
BYTE i9[5];
|
|
};
|
|
|
|
static const struct vtable_fixup_thunk thunk_template = {
|
|
{0x55,0x48,0x89,0xE5, 0x48,0x81,0xEC,0x80,0x00,0x00,0x00},
|
|
{0x48,0x89,0x4C,0x24,0x60, 0x48,0x89,0x54,0x24,0x68,
|
|
0x4C,0x89,0x44,0x24,0x70, 0x4C,0x89,0x4C,0x24,0x78,
|
|
0x0F,0x29,0x44,0x24,0x20, 0x0F,0x29,0x4C,0x24,0x30,
|
|
0x0F,0x29,0x54,0x24,0x40, 0x0F,0x29,0x5C,0x24,0x50,
|
|
},
|
|
{0x48,0xB8},
|
|
NULL,
|
|
{0x48,0xB9},
|
|
NULL,
|
|
{0xFF,0xD0},
|
|
{0x48,0x8B,0x4C,0x24,0x60, 0x48,0x8B,0x54,0x24,0x68,
|
|
0x4C,0x8B,0x44,0x24,0x70, 0x4C,0x8B,0x4C,0x24,0x78,
|
|
0x0F,0x28,0x44,0x24,0x20, 0x0F,0x28,0x4C,0x24,0x30,
|
|
0x0F,0x28,0x54,0x24,0x40, 0x0F,0x28,0x5C,0x24,0x50,
|
|
},
|
|
{0x48,0x89,0xEC, 0x5D},
|
|
{0x48,0xB8},
|
|
NULL,
|
|
{0x48,0x8B,0x00,0xFF,0xE0}
|
|
};
|
|
|
|
#include "poppack.h"
|
|
|
|
#else /* !__i386__ && !__x86_64__ */
|
|
|
|
# define CAN_FIXUP_VTABLE 0
|
|
|
|
struct vtable_fixup_thunk
|
|
{
|
|
struct dll_fixup *fixup;
|
|
void (CDECL *function)(struct dll_fixup *fixup);
|
|
void *vtable_entry;
|
|
};
|
|
|
|
static const struct vtable_fixup_thunk thunk_template = {0};
|
|
|
|
#endif
|
|
|
|
static void CDECL ReallyFixupVTable(struct dll_fixup *fixup)
|
|
{
|
|
HRESULT hr=S_OK;
|
|
WCHAR filename[MAX_PATH];
|
|
ICLRRuntimeInfo *info=NULL;
|
|
RuntimeHost *host;
|
|
char *filenameA;
|
|
MonoImage *image=NULL;
|
|
MonoAssembly *assembly=NULL;
|
|
MonoImageOpenStatus status=0;
|
|
MonoDomain *domain;
|
|
|
|
if (fixup->done) return;
|
|
|
|
/* It's possible we'll have two threads doing this at once. This is
|
|
* considered preferable to the potential deadlock if we use a mutex. */
|
|
|
|
GetModuleFileNameW(fixup->dll, filename, MAX_PATH);
|
|
|
|
TRACE("%p,%p,%s\n", fixup, fixup->dll, debugstr_w(filename));
|
|
|
|
filenameA = WtoA(filename);
|
|
if (!filenameA)
|
|
hr = E_OUTOFMEMORY;
|
|
|
|
if (SUCCEEDED(hr))
|
|
hr = get_runtime_info(filename, NULL, NULL, 0, 0, FALSE, &info);
|
|
|
|
if (SUCCEEDED(hr))
|
|
hr = ICLRRuntimeInfo_GetRuntimeHost(info, &host);
|
|
|
|
if (SUCCEEDED(hr))
|
|
hr = RuntimeHost_GetDefaultDomain(host, NULL, &domain);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
MonoDomain *prev_domain;
|
|
|
|
prev_domain = domain_attach(domain);
|
|
|
|
assembly = mono_assembly_open(filenameA, &status);
|
|
|
|
if (assembly)
|
|
{
|
|
int i;
|
|
|
|
/* Mono needs an image that belongs to an assembly. */
|
|
image = mono_assembly_get_image(assembly);
|
|
|
|
#if __x86_64__
|
|
if (fixup->fixup->type & COR_VTABLE_64BIT)
|
|
#else
|
|
if (fixup->fixup->type & COR_VTABLE_32BIT)
|
|
#endif
|
|
{
|
|
void **vtable = fixup->vtable;
|
|
ULONG_PTR *tokens = fixup->tokens;
|
|
for (i=0; i<fixup->fixup->count; i++)
|
|
{
|
|
TRACE("%#lx\n", tokens[i]);
|
|
vtable[i] = mono_marshal_get_vtfixup_ftnptr(
|
|
image, tokens[i], fixup->fixup->type);
|
|
}
|
|
}
|
|
|
|
fixup->done = TRUE;
|
|
}
|
|
|
|
domain_restore(prev_domain);
|
|
}
|
|
|
|
if (info != NULL)
|
|
ICLRRuntimeInfo_Release(info);
|
|
|
|
HeapFree(GetProcessHeap(), 0, filenameA);
|
|
|
|
if (!fixup->done)
|
|
{
|
|
ERR("unable to fixup vtable, hr=%x, status=%d\n", hr, status);
|
|
/* If we returned now, we'd get an infinite loop. */
|
|
assert(0);
|
|
}
|
|
}
|
|
|
|
static void FixupVTableEntry(HMODULE hmodule, VTableFixup *vtable_fixup)
|
|
{
|
|
/* We can't actually generate code for the functions without loading mono,
|
|
* and loading mono inside DllMain is a terrible idea. So we make thunks
|
|
* that call ReallyFixupVTable, which will load the runtime and fill in the
|
|
* vtable, then do an indirect jump using the (now filled in) vtable. Note
|
|
* that we have to keep the thunks around forever, as one of them may get
|
|
* called while we're filling in the table, and we can never be sure all
|
|
* threads are clear. */
|
|
struct dll_fixup *fixup;
|
|
|
|
fixup = HeapAlloc(GetProcessHeap(), 0, sizeof(*fixup));
|
|
|
|
fixup->dll = hmodule;
|
|
fixup->thunk_code = HeapAlloc(dll_fixup_heap, 0, sizeof(struct vtable_fixup_thunk) * vtable_fixup->count);
|
|
fixup->fixup = vtable_fixup;
|
|
fixup->vtable = (BYTE*)hmodule + vtable_fixup->rva;
|
|
fixup->done = FALSE;
|
|
|
|
TRACE("vtable_fixup->type=0x%x\n",vtable_fixup->type);
|
|
#if __x86_64__
|
|
if (vtable_fixup->type & COR_VTABLE_64BIT)
|
|
#else
|
|
if (vtable_fixup->type & COR_VTABLE_32BIT)
|
|
#endif
|
|
{
|
|
void **vtable = fixup->vtable;
|
|
ULONG_PTR *tokens;
|
|
int i;
|
|
struct vtable_fixup_thunk *thunks = fixup->thunk_code;
|
|
|
|
tokens = fixup->tokens = HeapAlloc(GetProcessHeap(), 0, sizeof(*tokens) * vtable_fixup->count);
|
|
memcpy(tokens, vtable, sizeof(*tokens) * vtable_fixup->count);
|
|
for (i=0; i<vtable_fixup->count; i++)
|
|
{
|
|
thunks[i] = thunk_template;
|
|
thunks[i].fixup = fixup;
|
|
thunks[i].function = ReallyFixupVTable;
|
|
thunks[i].vtable_entry = &vtable[i];
|
|
vtable[i] = &thunks[i];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ERR("unsupported vtable fixup flags %x\n", vtable_fixup->type);
|
|
HeapFree(dll_fixup_heap, 0, fixup->thunk_code);
|
|
HeapFree(GetProcessHeap(), 0, fixup);
|
|
return;
|
|
}
|
|
|
|
list_add_tail(&dll_fixups, &fixup->entry);
|
|
}
|
|
|
|
static void FixupVTable_Assembly(HMODULE hmodule, ASSEMBLY *assembly)
|
|
{
|
|
VTableFixup *vtable_fixups;
|
|
ULONG vtable_fixup_count, i;
|
|
|
|
assembly_get_vtable_fixups(assembly, &vtable_fixups, &vtable_fixup_count);
|
|
if (CAN_FIXUP_VTABLE)
|
|
for (i=0; i<vtable_fixup_count; i++)
|
|
FixupVTableEntry(hmodule, &vtable_fixups[i]);
|
|
else if (vtable_fixup_count)
|
|
FIXME("cannot fixup vtable; expect a crash\n");
|
|
}
|
|
|
|
static void FixupVTable(HMODULE hmodule)
|
|
{
|
|
ASSEMBLY *assembly;
|
|
HRESULT hr;
|
|
|
|
hr = assembly_from_hmodule(&assembly, hmodule);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
FixupVTable_Assembly(hmodule, assembly);
|
|
assembly_release(assembly);
|
|
}
|
|
else
|
|
ERR("failed to read CLR headers, hr=%x\n", hr);
|
|
}
|
|
|
|
__int32 WINAPI _CorExeMain(void)
|
|
{
|
|
int exit_code;
|
|
int argc;
|
|
char **argv;
|
|
MonoDomain *domain=NULL;
|
|
MonoImage *image;
|
|
MonoImageOpenStatus status;
|
|
MonoAssembly *assembly=NULL;
|
|
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; i<argc; i++)
|
|
TRACE(" %s", debugstr_a(argv[i]));
|
|
TRACE("\n");
|
|
|
|
filenameA = WtoA(filename);
|
|
if (!filenameA)
|
|
{
|
|
HeapFree(GetProcessHeap(), 0, argv);
|
|
return -1;
|
|
}
|
|
|
|
FixupVTable(GetModuleHandleW(NULL));
|
|
|
|
hr = get_runtime_info(filename, NULL, NULL, 0, 0, FALSE, &info);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = ICLRRuntimeInfo_GetRuntimeHost(info, &host);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
WCHAR config_file[MAX_PATH];
|
|
static const WCHAR dotconfig[] = {'.','c','o','n','f','i','g',0};
|
|
|
|
strcpyW(config_file, filename);
|
|
strcatW(config_file, dotconfig);
|
|
|
|
hr = RuntimeHost_GetDefaultDomain(host, config_file, &domain);
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
image = mono_image_open_from_module_handle(GetModuleHandleW(NULL),
|
|
filenameA, 1, &status);
|
|
|
|
if (image)
|
|
assembly = mono_assembly_load_from(image, filenameA, &status);
|
|
|
|
if (assembly)
|
|
{
|
|
mono_callspec_set_assembly(assembly);
|
|
|
|
exit_code = mono_jit_exec(domain, assembly, argc, argv);
|
|
}
|
|
else
|
|
{
|
|
ERR("couldn't load %s, status=%d\n", debugstr_w(filename), status);
|
|
exit_code = -1;
|
|
}
|
|
|
|
RuntimeHost_DeleteDomain(host, domain);
|
|
}
|
|
else
|
|
exit_code = -1;
|
|
|
|
ICLRRuntimeInfo_Release(info);
|
|
}
|
|
else
|
|
exit_code = -1;
|
|
|
|
HeapFree(GetProcessHeap(), 0, argv);
|
|
|
|
if (domain)
|
|
{
|
|
mono_thread_manage();
|
|
mono_runtime_quit();
|
|
}
|
|
|
|
return exit_code;
|
|
}
|
|
|
|
BOOL WINAPI _CorDllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
|
|
{
|
|
ASSEMBLY *assembly=NULL;
|
|
HRESULT hr;
|
|
|
|
TRACE("(%p, %d, %p)\n", hinstDLL, fdwReason, lpvReserved);
|
|
|
|
hr = assembly_from_hmodule(&assembly, hinstDLL);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
NativeEntryPointFunc NativeEntryPoint=NULL;
|
|
|
|
assembly_get_native_entrypoint(assembly, &NativeEntryPoint);
|
|
if (fdwReason == DLL_PROCESS_ATTACH)
|
|
{
|
|
if (!NativeEntryPoint)
|
|
DisableThreadLibraryCalls(hinstDLL);
|
|
FixupVTable_Assembly(hinstDLL,assembly);
|
|
}
|
|
assembly_release(assembly);
|
|
/* FIXME: clean up the vtables on DLL_PROCESS_DETACH */
|
|
if (NativeEntryPoint)
|
|
return NativeEntryPoint(hinstDLL, fdwReason, lpvReserved);
|
|
}
|
|
else
|
|
ERR("failed to read CLR headers, hr=%x\n", hr);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* called from DLL_PROCESS_ATTACH */
|
|
void runtimehost_init(void)
|
|
{
|
|
dll_fixup_heap = HeapCreate(HEAP_CREATE_ENABLE_EXECUTE, 0, 0);
|
|
list_init(&dll_fixups);
|
|
}
|
|
|
|
/* called from DLL_PROCESS_DETACH */
|
|
void runtimehost_uninit(void)
|
|
{
|
|
struct dll_fixup *fixup, *fixup2;
|
|
|
|
HeapDestroy(dll_fixup_heap);
|
|
LIST_FOR_EACH_ENTRY_SAFE(fixup, fixup2, &dll_fixups, struct dll_fixup, entry)
|
|
{
|
|
HeapFree(GetProcessHeap(), 0, fixup->tokens);
|
|
HeapFree(GetProcessHeap(), 0, fixup);
|
|
}
|
|
}
|
|
|
|
HRESULT RuntimeHost_Construct(CLRRuntimeInfo *runtime_version, RuntimeHost** result)
|
|
{
|
|
RuntimeHost *This;
|
|
|
|
This = HeapAlloc( GetProcessHeap(), 0, sizeof *This );
|
|
if ( !This )
|
|
return E_OUTOFMEMORY;
|
|
|
|
This->ICorRuntimeHost_iface.lpVtbl = &corruntimehost_vtbl;
|
|
This->ICLRRuntimeHost_iface.lpVtbl = &CLRHostVtbl;
|
|
|
|
This->ref = 1;
|
|
This->version = runtime_version;
|
|
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->ICorRuntimeHost_iface;
|
|
IUnknown_AddRef(unk);
|
|
}
|
|
else if (IsEqualGUID(clsid, &CLSID_CLRRuntimeHost))
|
|
{
|
|
unk = (IUnknown*)&This->ICLRRuntimeHost_iface;
|
|
IUnknown_AddRef(unk);
|
|
}
|
|
else if (IsEqualGUID(clsid, &CLSID_CorMetaDataDispenser) ||
|
|
IsEqualGUID(clsid, &CLSID_CorMetaDataDispenserRuntime))
|
|
{
|
|
hr = MetaDataDispenser_CreateInstance(&unk);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
}
|
|
else if (IsEqualGUID(clsid, &CLSID_CLRDebuggingLegacy))
|
|
{
|
|
hr = CorDebug_Create(&This->ICLRRuntimeHost_iface, &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;
|
|
}
|
|
|
|
#define CHARS_IN_GUID 39
|
|
|
|
HRESULT create_monodata(REFIID riid, LPVOID *ppObj )
|
|
{
|
|
static const WCHAR wszAssembly[] = {'A','s','s','e','m','b','l','y',0};
|
|
static const WCHAR wszCodebase[] = {'C','o','d','e','B','a','s','e',0};
|
|
static const WCHAR wszClass[] = {'C','l','a','s','s',0};
|
|
static const WCHAR wszFileSlash[] = {'f','i','l','e',':','/','/','/',0};
|
|
static const WCHAR wszCLSIDSlash[] = {'C','L','S','I','D','\\',0};
|
|
static const WCHAR wszInprocServer32[] = {'\\','I','n','p','r','o','c','S','e','r','v','e','r','3','2',0};
|
|
static const WCHAR wszDLL[] = {'.','d','l','l',0};
|
|
WCHAR path[CHARS_IN_GUID + ARRAY_SIZE(wszCLSIDSlash) + ARRAY_SIZE(wszInprocServer32) - 1];
|
|
MonoDomain *domain;
|
|
MonoAssembly *assembly;
|
|
ICLRRuntimeInfo *info = NULL;
|
|
RuntimeHost *host;
|
|
HRESULT hr;
|
|
HKEY key, subkey;
|
|
LONG res;
|
|
int offset = 0;
|
|
DWORD numKeys, keyLength;
|
|
WCHAR codebase[MAX_PATH + 8];
|
|
WCHAR classname[350], subkeyName[256];
|
|
WCHAR filename[MAX_PATH];
|
|
|
|
DWORD dwBufLen = 350;
|
|
|
|
lstrcpyW(path, wszCLSIDSlash);
|
|
StringFromGUID2(riid, path + lstrlenW(wszCLSIDSlash), CHARS_IN_GUID);
|
|
lstrcatW(path, wszInprocServer32);
|
|
|
|
TRACE("Registry key: %s\n", debugstr_w(path));
|
|
|
|
res = RegOpenKeyExW(HKEY_CLASSES_ROOT, path, 0, KEY_READ, &key);
|
|
if (res == ERROR_FILE_NOT_FOUND)
|
|
return CLASS_E_CLASSNOTAVAILABLE;
|
|
|
|
res = RegGetValueW( key, NULL, wszClass, RRF_RT_REG_SZ, NULL, classname, &dwBufLen);
|
|
if(res != ERROR_SUCCESS)
|
|
{
|
|
WARN("Class value cannot be found.\n");
|
|
hr = CLASS_E_CLASSNOTAVAILABLE;
|
|
goto cleanup;
|
|
}
|
|
|
|
TRACE("classname (%s)\n", debugstr_w(classname));
|
|
|
|
dwBufLen = MAX_PATH + 8;
|
|
res = RegGetValueW( key, NULL, wszCodebase, RRF_RT_REG_SZ, NULL, codebase, &dwBufLen);
|
|
if(res == ERROR_SUCCESS)
|
|
{
|
|
/* Strip file:/// */
|
|
if(strncmpW(codebase, wszFileSlash, strlenW(wszFileSlash)) == 0)
|
|
offset = strlenW(wszFileSlash);
|
|
|
|
strcpyW(filename, codebase + offset);
|
|
}
|
|
else
|
|
{
|
|
WCHAR assemblyname[MAX_PATH + 8];
|
|
|
|
hr = CLASS_E_CLASSNOTAVAILABLE;
|
|
WARN("CodeBase value cannot be found, trying Assembly.\n");
|
|
/* get the last subkey of InprocServer32 */
|
|
res = RegQueryInfoKeyW(key, 0, 0, 0, &numKeys, 0, 0, 0, 0, 0, 0, 0);
|
|
if (res != ERROR_SUCCESS || numKeys == 0)
|
|
goto cleanup;
|
|
numKeys--;
|
|
keyLength = ARRAY_SIZE(subkeyName);
|
|
res = RegEnumKeyExW(key, numKeys, subkeyName, &keyLength, 0, 0, 0, 0);
|
|
if (res != ERROR_SUCCESS)
|
|
goto cleanup;
|
|
res = RegOpenKeyExW(key, subkeyName, 0, KEY_READ, &subkey);
|
|
if (res != ERROR_SUCCESS)
|
|
goto cleanup;
|
|
dwBufLen = MAX_PATH + 8;
|
|
res = RegGetValueW(subkey, NULL, wszAssembly, RRF_RT_REG_SZ, NULL, assemblyname, &dwBufLen);
|
|
RegCloseKey(subkey);
|
|
if (res != ERROR_SUCCESS)
|
|
goto cleanup;
|
|
|
|
hr = get_file_from_strongname(assemblyname, filename, MAX_PATH);
|
|
if (FAILED(hr))
|
|
{
|
|
/*
|
|
* The registry doesn't have a CodeBase entry and it's not in the GAC.
|
|
*
|
|
* Use the Assembly Key to retrieve the filename.
|
|
* Assembly : REG_SZ : AssemblyName, Version=X.X.X.X, Culture=neutral, PublicKeyToken=null
|
|
*/
|
|
WCHAR *ns;
|
|
|
|
WARN("Attempt to load from the application directory.\n");
|
|
GetModuleFileNameW(NULL, filename, MAX_PATH);
|
|
ns = strrchrW(filename, '\\');
|
|
*(ns+1) = '\0';
|
|
|
|
ns = strchrW(assemblyname, ',');
|
|
*(ns) = '\0';
|
|
strcatW(filename, assemblyname);
|
|
*(ns) = '.';
|
|
strcatW(filename, wszDLL);
|
|
}
|
|
}
|
|
|
|
TRACE("filename (%s)\n", debugstr_w(filename));
|
|
|
|
*ppObj = NULL;
|
|
|
|
|
|
hr = get_runtime_info(filename, NULL, NULL, 0, 0, FALSE, &info);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = ICLRRuntimeInfo_GetRuntimeHost(info, &host);
|
|
|
|
if (SUCCEEDED(hr))
|
|
hr = RuntimeHost_GetDefaultDomain(host, NULL, &domain);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
MonoImage *image;
|
|
MonoClass *klass;
|
|
MonoObject *result;
|
|
MonoDomain *prev_domain;
|
|
IUnknown *unk = NULL;
|
|
char *filenameA, *ns;
|
|
char *classA;
|
|
|
|
hr = CLASS_E_CLASSNOTAVAILABLE;
|
|
|
|
prev_domain = domain_attach(domain);
|
|
|
|
filenameA = WtoA(filename);
|
|
assembly = mono_domain_assembly_open(domain, filenameA);
|
|
HeapFree(GetProcessHeap(), 0, filenameA);
|
|
if (!assembly)
|
|
{
|
|
ERR("Cannot open assembly %s\n", filenameA);
|
|
domain_restore(prev_domain);
|
|
goto cleanup;
|
|
}
|
|
|
|
image = mono_assembly_get_image(assembly);
|
|
if (!image)
|
|
{
|
|
ERR("Couldn't get assembly image\n");
|
|
domain_restore(prev_domain);
|
|
goto cleanup;
|
|
}
|
|
|
|
classA = WtoA(classname);
|
|
ns = strrchr(classA, '.');
|
|
*ns = '\0';
|
|
|
|
klass = mono_class_from_name(image, classA, ns+1);
|
|
HeapFree(GetProcessHeap(), 0, classA);
|
|
if (!klass)
|
|
{
|
|
ERR("Couldn't get class from image\n");
|
|
domain_restore(prev_domain);
|
|
goto cleanup;
|
|
}
|
|
|
|
/*
|
|
* Use the default constructor for the .NET class.
|
|
*/
|
|
result = mono_object_new(domain, klass);
|
|
mono_runtime_object_init(result);
|
|
|
|
hr = RuntimeHost_GetIUnknownForObject(host, result, &unk);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = IUnknown_QueryInterface(unk, &IID_IUnknown, ppObj);
|
|
|
|
IUnknown_Release(unk);
|
|
}
|
|
else
|
|
hr = CLASS_E_CLASSNOTAVAILABLE;
|
|
|
|
domain_restore(prev_domain);
|
|
}
|
|
else
|
|
hr = CLASS_E_CLASSNOTAVAILABLE;
|
|
}
|
|
else
|
|
hr = CLASS_E_CLASSNOTAVAILABLE;
|
|
|
|
cleanup:
|
|
if(info)
|
|
ICLRRuntimeInfo_Release(info);
|
|
|
|
RegCloseKey(key);
|
|
|
|
return hr;
|
|
}
|