diff --git a/dll/win32/mscoree/CMakeLists.txt b/dll/win32/mscoree/CMakeLists.txt index 090fa4876c1..08ce3a448f3 100644 --- a/dll/win32/mscoree/CMakeLists.txt +++ b/dll/win32/mscoree/CMakeLists.txt @@ -1,6 +1,6 @@ remove_definitions(-D_WIN32_WINNT=0x502) -add_definitions(-D_WIN32_WINNT=0x600) +add_definitions(-D_WIN32_WINNT=0x600 -DUSE_NEW_WINE_REGISTER_RESOURCES) spec2def(mscoree.dll mscoree.spec) @@ -11,7 +11,8 @@ list(APPEND SOURCE corruntimehost.c metadata.c metahost.c - mscoree_main.c) + mscoree_main.c + reactos.c) list(APPEND PCH_SKIP_SOURCE guid.c @@ -24,8 +25,8 @@ add_library(mscoree MODULE ${CMAKE_CURRENT_BINARY_DIR}/mscoree.def) set_module_type(mscoree win32dll) -target_link_libraries(mscoree uuid wine) -add_importlibs(mscoree dbghelp advapi32 shell32 ole32 shlwapi msvcrt kernel32 ntdll) -add_pch(mscoree mscoree_private.h "${PCH_SKIP_SOURCE}") +target_link_libraries(mscoree uuid wine oldnames) +add_importlibs(mscoree dbghelp advapi32 shell32 kernel32_vista ole32 shlwapi msvcrt kernel32 ntdll) +#add_pch(mscoree mscoree_private.h "${PCH_SKIP_SOURCE}") add_cd_file(TARGET mscoree DESTINATION reactos/system32 FOR all) set_wine_module_FIXME(mscoree) # CORE-5743: No CONST_VTABLE diff --git a/dll/win32/mscoree/assembly.c b/dll/win32/mscoree/assembly.c index 310726fc28e..e361270a89b 100644 --- a/dll/win32/mscoree/assembly.c +++ b/dll/win32/mscoree/assembly.c @@ -18,10 +18,23 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "winuser.h" +#include "winver.h" +#include "dbghelp.h" +#include "ole2.h" +#include "mscoree.h" +#include "corhdr.h" +#include "metahost.h" +#include "cordebug.h" +#include "wine/list.h" #include "mscoree_private.h" -#include -#include +#include "wine/debug.h" typedef struct { @@ -49,7 +62,7 @@ typedef struct tagCLRTABLE struct tagASSEMBLY { - int is_mapped_file; + BOOL is_mapped_file; /* mapped files */ LPWSTR path; @@ -64,20 +77,6 @@ struct tagASSEMBLY METADATAHDR *metadatahdr; }; -static inline LPWSTR strdupW(LPCWSTR src) -{ - LPWSTR dest; - - if (!src) - return NULL; - - dest = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(src) + 1) * sizeof(WCHAR)); - if (dest) - lstrcpyW(dest, src); - - return dest; -} - static void* assembly_rva_to_va(ASSEMBLY *assembly, ULONG rva) { if (assembly->is_mapped_file) @@ -115,7 +114,7 @@ static HRESULT parse_metadata_header(ASSEMBLY *assembly, DWORD *hdrsz) metadatahdr = (METADATAHDR *)ptr; - assembly->metadatahdr = HeapAlloc(GetProcessHeap(), 0, sizeof(METADATAHDR)); + assembly->metadatahdr = malloc(sizeof(METADATAHDR)); if (!assembly->metadatahdr) return E_OUTOFMEMORY; @@ -195,13 +194,13 @@ HRESULT assembly_create(ASSEMBLY **out, LPCWSTR file) *out = NULL; - assembly = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ASSEMBLY)); + assembly = calloc(1, sizeof(ASSEMBLY)); if (!assembly) return E_OUTOFMEMORY; - assembly->is_mapped_file = 1; + assembly->is_mapped_file = TRUE; - assembly->path = strdupW(file); + assembly->path = wcsdup(file); if (!assembly->path) { hr = E_OUTOFMEMORY; @@ -249,11 +248,11 @@ HRESULT assembly_from_hmodule(ASSEMBLY **out, HMODULE hmodule) *out = NULL; - assembly = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ASSEMBLY)); + assembly = calloc(1, sizeof(ASSEMBLY)); if (!assembly) return E_OUTOFMEMORY; - assembly->is_mapped_file = 0; + assembly->is_mapped_file = FALSE; assembly->data = (BYTE*)hmodule; @@ -277,9 +276,9 @@ HRESULT assembly_release(ASSEMBLY *assembly) CloseHandle(assembly->hmap); CloseHandle(assembly->hfile); } - HeapFree(GetProcessHeap(), 0, assembly->metadatahdr); - HeapFree(GetProcessHeap(), 0, assembly->path); - HeapFree(GetProcessHeap(), 0, assembly); + free(assembly->metadatahdr); + free(assembly->path); + free(assembly); return S_OK; } @@ -300,3 +299,17 @@ HRESULT assembly_get_vtable_fixups(ASSEMBLY *assembly, VTableFixup **fixups, DWO return S_OK; } + +HRESULT assembly_get_native_entrypoint(ASSEMBLY *assembly, NativeEntryPointFunc *func) +{ + if (assembly->corhdr->Flags & COMIMAGE_FLAGS_NATIVE_ENTRYPOINT) + { + *func = assembly_rva_to_va(assembly, assembly->corhdr->EntryPointRVA); + return S_OK; + } + else + { + *func = NULL; + return S_FALSE; + } +} diff --git a/dll/win32/mscoree/config.c b/dll/win32/mscoree/config.c index b36cbb5f1fd..770facf93ef 100644 --- a/dll/win32/mscoree/config.c +++ b/dll/win32/mscoree/config.c @@ -18,17 +18,34 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ +#define COBJMACROS + +#include + +#include "windef.h" +#include "winbase.h" +#include "winreg.h" +#include "ole2.h" +#include "msxml2.h" +#include "mscoree.h" +#include "corhdr.h" +#include "corerror.h" +#include "metahost.h" +#include "cordebug.h" +#include "wine/list.h" #include "mscoree_private.h" -#include -#include -#include -#include +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL( mscoree ); enum parse_state { + STATE_ASSEMBLY_BINDING, STATE_ROOT, STATE_CONFIGURATION, + STATE_PROBING, + STATE_RUNTIME, STATE_STARTUP, STATE_UNKNOWN }; @@ -43,6 +60,192 @@ typedef struct ConfigFileHandler parsed_config_file *result; } ConfigFileHandler; +typedef struct +{ + IStream IStream_iface; + LONG ref; + HANDLE file; +} ConfigStream; + +static inline ConfigStream *impl_from_IStream(IStream *iface) +{ + return CONTAINING_RECORD(iface, ConfigStream, IStream_iface); +} + +static HRESULT WINAPI ConfigStream_QueryInterface(IStream *iface, REFIID riid, void **ppv) +{ + ConfigStream *This = impl_from_IStream(iface); + + TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv); + + if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IStream)) + *ppv = &This->IStream_iface; + else + { + WARN("Not supported iface %s\n", debugstr_guid(riid)); + *ppv = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown*)*ppv); + return S_OK; +} + +static ULONG WINAPI ConfigStream_AddRef(IStream *iface) +{ + ConfigStream *This = impl_from_IStream(iface); + ULONG ref = InterlockedIncrement(&This->ref); + + TRACE("(%p) ref=%lu\n", This, ref); + + return ref; +} + +static ULONG WINAPI ConfigStream_Release(IStream *iface) +{ + ConfigStream *This = impl_from_IStream(iface); + ULONG ref = InterlockedDecrement(&This->ref); + + TRACE("(%p) ref=%lu\n",This, ref); + + if (!ref) + { + CloseHandle(This->file); + free(This); + } + + return ref; +} + +static HRESULT WINAPI ConfigStream_Read(IStream *iface, void *buf, ULONG size, ULONG *ret_read) +{ + ConfigStream *This = impl_from_IStream(iface); + DWORD read = 0; + + TRACE("(%p)->(%p %lu %p)\n", This, buf, size, ret_read); + + if (!ReadFile(This->file, buf, size, &read, NULL)) + { + WARN("error %ld reading file\n", GetLastError()); + return HRESULT_FROM_WIN32(GetLastError()); + } + + if (ret_read) *ret_read = read; + return S_OK; +} + +static HRESULT WINAPI ConfigStream_Write(IStream *iface, const void *buf, ULONG size, ULONG *written) +{ + ConfigStream *This = impl_from_IStream(iface); + TRACE("(%p)->(%p %lu %p)\n", This, buf, size, written); + return E_FAIL; +} + +static HRESULT WINAPI ConfigStream_Seek(IStream *iface, LARGE_INTEGER dlibMove, + DWORD dwOrigin, ULARGE_INTEGER *pNewPos) +{ + ConfigStream *This = impl_from_IStream(iface); + TRACE("(%p)->(%ld %ld %p)\n", This, dlibMove.u.LowPart, dwOrigin, pNewPos); + return E_NOTIMPL; +} + +static HRESULT WINAPI ConfigStream_SetSize(IStream *iface, ULARGE_INTEGER libNewSize) +{ + ConfigStream *This = impl_from_IStream(iface); + TRACE("(%p)->(%ld)\n", This, libNewSize.u.LowPart); + return E_NOTIMPL; +} + +static HRESULT WINAPI ConfigStream_CopyTo(IStream *iface, IStream *stream, ULARGE_INTEGER size, + ULARGE_INTEGER *read, ULARGE_INTEGER *written) +{ + ConfigStream *This = impl_from_IStream(iface); + FIXME("(%p)->(%p %ld %p %p)\n", This, stream, size.u.LowPart, read, written); + return E_NOTIMPL; +} + +static HRESULT WINAPI ConfigStream_Commit(IStream *iface, DWORD flags) +{ + ConfigStream *This = impl_from_IStream(iface); + FIXME("(%p,%ld)\n", This, flags); + return E_NOTIMPL; +} + +static HRESULT WINAPI ConfigStream_Revert(IStream *iface) +{ + ConfigStream *This = impl_from_IStream(iface); + TRACE("(%p)\n", This); + return E_NOTIMPL; +} + +static HRESULT WINAPI ConfigStream_LockUnlockRegion(IStream *iface, ULARGE_INTEGER libOffset, + ULARGE_INTEGER cb, DWORD dwLockType) +{ + ConfigStream *This = impl_from_IStream(iface); + TRACE("(%p,%ld,%ld,%ld)\n", This, libOffset.u.LowPart, cb.u.LowPart, dwLockType); + return E_NOTIMPL; +} + +static HRESULT WINAPI ConfigStream_Stat(IStream *iface, STATSTG *lpStat, DWORD grfStatFlag) +{ + ConfigStream *This = impl_from_IStream(iface); + FIXME("(%p,%p,%ld)\n", This, lpStat, grfStatFlag); + return E_NOTIMPL; +} + +static HRESULT WINAPI ConfigStream_Clone(IStream *iface, IStream **ppstm) +{ + ConfigStream *This = impl_from_IStream(iface); + TRACE("(%p)\n",This); + return E_NOTIMPL; +} + +static const IStreamVtbl ConfigStreamVtbl = { + ConfigStream_QueryInterface, + ConfigStream_AddRef, + ConfigStream_Release, + ConfigStream_Read, + ConfigStream_Write, + ConfigStream_Seek, + ConfigStream_SetSize, + ConfigStream_CopyTo, + ConfigStream_Commit, + ConfigStream_Revert, + ConfigStream_LockUnlockRegion, + ConfigStream_LockUnlockRegion, + ConfigStream_Stat, + ConfigStream_Clone +}; + +HRESULT WINAPI CreateConfigStream(const WCHAR *filename, IStream **stream) +{ + ConfigStream *config_stream; + HANDLE file; + + TRACE("(%s, %p)\n", debugstr_w(filename), stream); + + if (!stream) + return COR_E_NULLREFERENCE; + + file = CreateFileW(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0); + if (file == INVALID_HANDLE_VALUE) + return GetLastError() == ERROR_FILE_NOT_FOUND ? COR_E_FILENOTFOUND : E_FAIL; + + config_stream = malloc(sizeof(*config_stream)); + if (!config_stream) + { + CloseHandle(file); + return E_OUTOFMEMORY; + } + + config_stream->IStream_iface.lpVtbl = &ConfigStreamVtbl; + config_stream->ref = 1; + config_stream->file = file; + + *stream = &config_stream->IStream_iface; + return S_OK; +} + static inline ConfigFileHandler *impl_from_ISAXContentHandler(ISAXContentHandler *iface) { return CONTAINING_RECORD(iface, ConfigFileHandler, ISAXContentHandler_iface); @@ -56,18 +259,19 @@ static inline ConfigFileHandler *impl_from_ISAXErrorHandler(ISAXErrorHandler *if static HRESULT WINAPI ConfigFileHandler_QueryInterface(ISAXContentHandler *iface, REFIID riid, void **ppvObject) { - if (IsEqualGUID(riid, &IID_ISAXContentHandler) || - IsEqualGUID(riid, &IID_IUnknown)) - { - *ppvObject = iface; - } + ConfigFileHandler *This = impl_from_ISAXContentHandler(iface); + + if (IsEqualGUID(riid, &IID_ISAXContentHandler) || IsEqualGUID(riid, &IID_IUnknown)) + *ppvObject = &This->ISAXContentHandler_iface; + else if (IsEqualGUID(riid, &IID_ISAXErrorHandler)) + *ppvObject = &This->ISAXErrorHandler_iface; else { WARN("Unsupported interface %s\n", debugstr_guid(riid)); return E_NOINTERFACE; } - ISAXContentHandler_AddRef(iface); + IUnknown_AddRef((IUnknown*)*ppvObject); return S_OK; } @@ -84,7 +288,7 @@ static ULONG WINAPI ConfigFileHandler_Release(ISAXContentHandler *iface) ULONG ref = InterlockedDecrement(&This->ref); if (ref == 0) - HeapFree(GetProcessHeap(), 0, This); + free(This); return ref; } @@ -133,6 +337,28 @@ static HRESULT parse_startup(ConfigFileHandler *This, ISAXAttributes *pAttr) return hr; } +static HRESULT parse_probing(ConfigFileHandler *This, ISAXAttributes *pAttr) +{ + static const WCHAR privatePath[] = {'p','r','i','v','a','t','e','P','a','t','h',0}; + static const WCHAR empty[] = {0}; + LPCWSTR value; + int value_size; + HRESULT hr; + + hr = ISAXAttributes_getValueFromName(pAttr, empty, 0, privatePath, lstrlenW(privatePath), &value, &value_size); + if (SUCCEEDED(hr)) + { + TRACE("%s\n", debugstr_wn(value, value_size)); + + This->result->private_path = wcsdup(value); + if (!This->result->private_path) + hr = E_OUTOFMEMORY; + } + + return hr; +} + + static HRESULT parse_supported_runtime(ConfigFileHandler *This, ISAXAttributes *pAttr) { static const WCHAR version[] = {'v','e','r','s','i','o','n',0}; @@ -147,18 +373,17 @@ static HRESULT parse_supported_runtime(ConfigFileHandler *This, ISAXAttributes * if (SUCCEEDED(hr)) { TRACE("%s\n", debugstr_wn(value, value_size)); - entry = HeapAlloc(GetProcessHeap(), 0, sizeof(supported_runtime)); + entry = malloc(sizeof(supported_runtime)); if (entry) { - entry->version = HeapAlloc(GetProcessHeap(), 0, (value_size + 1) * sizeof(WCHAR)); + entry->version = wcsdup(value); if (entry->version) { - lstrcpyW(entry->version, value); list_add_tail(&This->result->supported_runtimes, &entry->entry); } else { - HeapFree(GetProcessHeap(), 0, entry); + free(entry); hr = E_OUTOFMEMORY; } } @@ -185,14 +410,18 @@ static HRESULT WINAPI ConfigFileHandler_startElement(ISAXContentHandler *iface, { ConfigFileHandler *This = impl_from_ISAXContentHandler(iface); static const WCHAR configuration[] = {'c','o','n','f','i','g','u','r','a','t','i','o','n',0}; + static const WCHAR assemblyBinding[] = {'a','s','s','e','m','b','l','y','B','i','n','d','i','n','g',0}; + static const WCHAR probing[] = {'p','r','o','b','i','n','g',0}; + static const WCHAR runtime[] = {'r','u','n','t','i','m','e',0}; static const WCHAR startup[] = {'s','t','a','r','t','u','p',0}; static const WCHAR supportedRuntime[] = {'s','u','p','p','o','r','t','e','d','R','u','n','t','i','m','e',0}; + HRESULT hr = S_OK; TRACE("%s %s %s\n", debugstr_wn(pNamespaceUri,nNamespaceUri), debugstr_wn(pLocalName,nLocalName), debugstr_wn(pQName,nQName)); - if (This->statenum == sizeof(This->states) / sizeof(This->states[0]) - 1) + if (This->statenum == ARRAY_SIZE(This->states) - 1) { ERR("file has too much nesting\n"); return E_FAIL; @@ -201,8 +430,7 @@ static HRESULT WINAPI ConfigFileHandler_startElement(ISAXContentHandler *iface, switch (This->states[This->statenum]) { case STATE_ROOT: - if (nLocalName == sizeof(configuration)/sizeof(WCHAR)-1 && - lstrcmpW(pLocalName, configuration) == 0) + if (nLocalName == ARRAY_SIZE(configuration) - 1 && wcscmp(pLocalName, configuration) == 0) { This->states[++This->statenum] = STATE_CONFIGURATION; break; @@ -210,18 +438,40 @@ static HRESULT WINAPI ConfigFileHandler_startElement(ISAXContentHandler *iface, else goto unknown; case STATE_CONFIGURATION: - if (nLocalName == sizeof(startup)/sizeof(WCHAR)-1 && - lstrcmpW(pLocalName, startup) == 0) + if (nLocalName == ARRAY_SIZE(startup) - 1 && wcscmp(pLocalName, startup) == 0) { hr = parse_startup(This, pAttr); This->states[++This->statenum] = STATE_STARTUP; break; } + else if (nLocalName == ARRAY_SIZE(runtime) - 1 && wcscmp(pLocalName, runtime) == 0) + { + This->states[++This->statenum] = STATE_RUNTIME; + break; + } + else + goto unknown; + case STATE_RUNTIME: + if (nLocalName == ARRAY_SIZE(assemblyBinding) - 1 && + wcscmp(pLocalName, assemblyBinding) == 0) + { + This->states[++This->statenum] = STATE_ASSEMBLY_BINDING; + break; + } + else + goto unknown; + case STATE_ASSEMBLY_BINDING: + if (nLocalName == ARRAY_SIZE(probing) - 1 && wcscmp(pLocalName, probing) == 0) + { + hr = parse_probing(This, pAttr); + This->states[++This->statenum] = STATE_PROBING; + break; + } else goto unknown; case STATE_STARTUP: - if (nLocalName == sizeof(supportedRuntime)/sizeof(WCHAR)-1 && - lstrcmpW(pLocalName, supportedRuntime) == 0) + if (nLocalName == ARRAY_SIZE(supportedRuntime) - 1 && + wcscmp(pLocalName, supportedRuntime) == 0) { hr = parse_supported_runtime(This, pAttr); This->states[++This->statenum] = STATE_UNKNOWN; @@ -236,7 +486,7 @@ static HRESULT WINAPI ConfigFileHandler_startElement(ISAXContentHandler *iface, return hr; unknown: - FIXME("Unknown element %s in state %u\n", debugstr_wn(pLocalName,nLocalName), + TRACE("Unknown element %s in state %u\n", debugstr_wn(pLocalName,nLocalName), This->states[This->statenum]); This->states[++This->statenum] = STATE_UNKNOWN; @@ -314,52 +564,40 @@ static const struct ISAXContentHandlerVtbl ConfigFileHandlerVtbl = static HRESULT WINAPI ConfigFileHandler_Error_QueryInterface(ISAXErrorHandler *iface, REFIID riid, void **ppvObject) { - if (IsEqualGUID(riid, &IID_ISAXErrorHandler) || - IsEqualGUID(riid, &IID_IUnknown)) - { - *ppvObject = iface; - } - else - { - WARN("Unsupported interface %s\n", debugstr_guid(riid)); - return E_NOINTERFACE; - } - - ISAXErrorHandler_AddRef(iface); - - return S_OK; + ConfigFileHandler *This = impl_from_ISAXErrorHandler(iface); + return ISAXContentHandler_QueryInterface(&This->ISAXContentHandler_iface, riid, ppvObject); } static ULONG WINAPI ConfigFileHandler_Error_AddRef(ISAXErrorHandler *iface) { ConfigFileHandler *This = impl_from_ISAXErrorHandler(iface); - return IUnknown_AddRef((IUnknown*)This); + return ISAXContentHandler_AddRef(&This->ISAXContentHandler_iface); } static ULONG WINAPI ConfigFileHandler_Error_Release(ISAXErrorHandler *iface) { ConfigFileHandler *This = impl_from_ISAXErrorHandler(iface); - return IUnknown_Release((IUnknown*)This); + return ISAXContentHandler_Release(&This->ISAXContentHandler_iface); } static HRESULT WINAPI ConfigFileHandler_error(ISAXErrorHandler *iface, ISAXLocator * pLocator, const WCHAR * pErrorMessage, HRESULT hrErrorCode) { - WARN("%s,%x\n", debugstr_w(pErrorMessage), hrErrorCode); + WARN("%s,%lx\n", debugstr_w(pErrorMessage), hrErrorCode); return S_OK; } static HRESULT WINAPI ConfigFileHandler_fatalError(ISAXErrorHandler *iface, ISAXLocator * pLocator, const WCHAR * pErrorMessage, HRESULT hrErrorCode) { - WARN("%s,%x\n", debugstr_w(pErrorMessage), hrErrorCode); + WARN("%s,%lx\n", debugstr_w(pErrorMessage), hrErrorCode); return S_OK; } static HRESULT WINAPI ConfigFileHandler_ignorableWarning(ISAXErrorHandler *iface, ISAXLocator * pLocator, const WCHAR * pErrorMessage, HRESULT hrErrorCode) { - WARN("%s,%x\n", debugstr_w(pErrorMessage), hrErrorCode); + WARN("%s,%lx\n", debugstr_w(pErrorMessage), hrErrorCode); return S_OK; } @@ -376,6 +614,7 @@ static const struct ISAXErrorHandlerVtbl ConfigFileHandlerErrorVtbl = static void init_config(parsed_config_file *config) { list_init(&config->supported_runtimes); + config->private_path = NULL; } static HRESULT parse_config(VARIANT input, parsed_config_file *result) @@ -384,7 +623,7 @@ static HRESULT parse_config(VARIANT input, parsed_config_file *result) ConfigFileHandler *handler; HRESULT hr; - handler = HeapAlloc(GetProcessHeap(), 0, sizeof(ConfigFileHandler)); + handler = malloc(sizeof(ConfigFileHandler)); if (!handler) return E_OUTOFMEMORY; @@ -416,9 +655,8 @@ static HRESULT parse_config(VARIANT input, parsed_config_file *result) return S_OK; } -HRESULT parse_config_file(LPCWSTR filename, parsed_config_file *result) +HRESULT parse_config_stream(IStream *stream, parsed_config_file *result) { - IStream *stream; VARIANT var; HRESULT hr; HRESULT initresult; @@ -426,18 +664,10 @@ HRESULT parse_config_file(LPCWSTR filename, parsed_config_file *result) init_config(result); initresult = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + V_VT(&var) = VT_UNKNOWN; + V_UNKNOWN(&var) = (IUnknown*)stream; - hr = SHCreateStreamOnFileW(filename, STGM_SHARE_DENY_WRITE | STGM_READ | STGM_FAILIFTHERE, &stream); - - if (SUCCEEDED(hr)) - { - V_VT(&var) = VT_UNKNOWN; - V_UNKNOWN(&var) = (IUnknown*)stream; - - hr = parse_config(var, result); - - IStream_Release(stream); - } + hr = parse_config(var, result); if (SUCCEEDED(initresult)) CoUninitialize(); @@ -445,14 +675,34 @@ HRESULT parse_config_file(LPCWSTR filename, parsed_config_file *result) return hr; } +HRESULT parse_config_file(LPCWSTR filename, parsed_config_file *result) +{ + HRESULT hr; + IStream *stream; + + init_config(result); + + hr = CreateConfigStream(filename, &stream); + if (FAILED(hr)) + return hr; + + hr = parse_config_stream(stream, result); + + IStream_Release(stream); + + return hr; +} + void free_parsed_config_file(parsed_config_file *file) { supported_runtime *cursor, *cursor2; LIST_FOR_EACH_ENTRY_SAFE(cursor, cursor2, &file->supported_runtimes, supported_runtime, entry) { - HeapFree(GetProcessHeap(), 0, cursor->version); + free(cursor->version); list_remove(&cursor->entry); - HeapFree(GetProcessHeap(), 0, cursor); + free(cursor); } + + free(file->private_path); } diff --git a/dll/win32/mscoree/cordebug.c b/dll/win32/mscoree/cordebug.c index 0a06a941789..0c6767ca636 100644 --- a/dll/win32/mscoree/cordebug.c +++ b/dll/win32/mscoree/cordebug.c @@ -17,7 +17,28 @@ * 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 "mscoree.h" +#include "corhdr.h" +#include "metahost.h" +#include "cordebug.h" +#include "wine/list.h" #include "mscoree_private.h" +#include "wine/debug.h" + + +WINE_DEFAULT_DEBUG_CHANNEL( mscoree ); typedef struct DebugProcess { @@ -77,7 +98,7 @@ static ULONG WINAPI cordebugprocess_AddRef(ICorDebugProcess *iface) DebugProcess *This = impl_from_ICorDebugProcess(iface); ULONG ref = InterlockedIncrement(&This->ref); - TRACE("%p ref=%u\n", This, ref); + TRACE("%p ref=%lu\n", This, ref); return ref; } @@ -87,7 +108,7 @@ static ULONG WINAPI cordebugprocess_Release(ICorDebugProcess *iface) DebugProcess *This = impl_from_ICorDebugProcess(iface); ULONG ref = InterlockedDecrement(&This->ref); - TRACE("%p ref=%u\n", This, ref); + TRACE("%p ref=%lu\n", This, ref); if (ref == 0) { @@ -100,7 +121,7 @@ static ULONG WINAPI cordebugprocess_Release(ICorDebugProcess *iface) if(This->cordebug) ICorDebug_Release(&This->cordebug->ICorDebug_iface); - HeapFree(GetProcessHeap(), 0, This); + free(This); } return ref; @@ -384,7 +405,7 @@ static HRESULT CorDebugProcess_Create(CorDebug *cordebug, IUnknown** ppUnk, LPPR { DebugProcess *This; - This = HeapAlloc( GetProcessHeap(), 0, sizeof *This ); + This = malloc(sizeof *This); if ( !This ) return E_OUTOFMEMORY; @@ -392,7 +413,7 @@ static HRESULT CorDebugProcess_Create(CorDebug *cordebug, IUnknown** ppUnk, LPPR GetCurrentProcess(), &This->handle, 0, FALSE, DUPLICATE_SAME_ACCESS)) { ERR("Failed to duplicate process handle\n"); - HeapFree(GetProcessHeap(), 0, This); + free(This); return E_FAIL; } if(!DuplicateHandle(GetCurrentProcess(), lpProcessInformation->hThread, @@ -401,7 +422,7 @@ static HRESULT CorDebugProcess_Create(CorDebug *cordebug, IUnknown** ppUnk, LPPR CloseHandle(This->handle); ERR("Failed to duplicate thread handle\n"); - HeapFree(GetProcessHeap(), 0, This); + free(This); return E_FAIL; } @@ -445,7 +466,7 @@ static HRESULT WINAPI process_enum_QueryInterface(ICorDebugProcessEnum *iface, R static ULONG WINAPI process_enum_AddRef(ICorDebugProcessEnum *iface) { CorDebug *This = impl_from_ICorDebugProcessEnum(iface); - TRACE("%p ref=%u\n", This, This->ref); + TRACE("%p ref=%lu\n", This, This->ref); return ICorDebug_AddRef(&This->ICorDebug_iface); } @@ -453,7 +474,7 @@ static ULONG WINAPI process_enum_AddRef(ICorDebugProcessEnum *iface) static ULONG WINAPI process_enum_Release(ICorDebugProcessEnum *iface) { CorDebug *This = impl_from_ICorDebugProcessEnum(iface); - TRACE("%p ref=%u\n", This, This->ref); + TRACE("%p ref=%lu\n", This, This->ref); return ICorDebug_Release(&This->ICorDebug_iface); } @@ -496,7 +517,7 @@ static HRESULT WINAPI process_enum_Next(ICorDebugProcessEnum *iface, ULONG celt, ICorDebugProcess * processes[], ULONG *pceltFetched) { CorDebug *This = impl_from_ICorDebugProcessEnum(iface); - FIXME("stub %p %d %p %p\n", This, celt, processes, pceltFetched); + FIXME("stub %p %ld %p %p\n", This, celt, processes, pceltFetched); return E_NOTIMPL; } @@ -540,7 +561,7 @@ static ULONG WINAPI CorDebug_AddRef(ICorDebug *iface) CorDebug *This = impl_from_ICorDebug( iface ); ULONG ref = InterlockedIncrement(&This->ref); - TRACE("%p ref=%u\n", This, ref); + TRACE("%p ref=%lu\n", This, ref); return ref; } @@ -550,7 +571,7 @@ static ULONG WINAPI CorDebug_Release(ICorDebug *iface) CorDebug *This = impl_from_ICorDebug( iface ); ULONG ref = InterlockedDecrement(&This->ref); - TRACE("%p ref=%u\n", This, ref); + TRACE("%p ref=%lu\n", This, ref); if (ref == 0) { @@ -560,13 +581,13 @@ static ULONG WINAPI CorDebug_Release(ICorDebug *iface) if(This->runtimehost) ICLRRuntimeHost_Release(This->runtimehost); - if(This->pCallback) + if(This->pCallback2) ICorDebugManagedCallback2_Release(This->pCallback2); if(This->pCallback) ICorDebugManagedCallback_Release(This->pCallback); - HeapFree(GetProcessHeap(), 0, This); + free(This); } return ref; @@ -595,7 +616,7 @@ static HRESULT WINAPI CorDebug_Terminate(ICorDebug *iface) } list_remove(&cursor->entry); - HeapFree(GetProcessHeap(), 0, cursor); + free(cursor); } return S_OK; @@ -652,7 +673,7 @@ static HRESULT WINAPI CorDebug_CreateProcess(ICorDebug *iface, LPCWSTR lpApplica ICorDebugProcess *pDebugProcess; HRESULT hr; - TRACE("stub %p %s %s %p %p %d %d %p %s %p %p %d %p\n", This, debugstr_w(lpApplicationName), + TRACE("stub %p %s %s %p %p %d %ld %p %s %p %p %d %p\n", This, debugstr_w(lpApplicationName), debugstr_w(lpCommandLine), lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, debugstr_w(lpCurrentDirectory), lpStartupInfo, lpProcessInformation, debuggingFlags, ppProcess); @@ -664,7 +685,7 @@ static HRESULT WINAPI CorDebug_CreateProcess(ICorDebug *iface, LPCWSTR lpApplica hr = CorDebugProcess_Create(This, (IUnknown**)&pDebugProcess, lpProcessInformation); if(hr == S_OK) { - struct CorProcess *new_process = HeapAlloc( GetProcessHeap(), 0, sizeof(CorProcess) ); + struct CorProcess *new_process = malloc(sizeof(CorProcess)); new_process->pProcess = pDebugProcess; list_add_tail(&This->processes, &new_process->entry); @@ -690,7 +711,7 @@ static HRESULT WINAPI CorDebug_DebugActiveProcess(ICorDebug *iface, DWORD id, BO ICorDebugProcess **ppProcess) { CorDebug *This = impl_from_ICorDebug( iface ); - FIXME("stub %p %d %d %p\n", This, id, win32Attach, ppProcess); + FIXME("stub %p %ld %d %p\n", This, id, win32Attach, ppProcess); return E_NOTIMPL; } @@ -711,7 +732,7 @@ static HRESULT WINAPI CorDebug_EnumerateProcesses( ICorDebug *iface, ICorDebugPr static HRESULT WINAPI CorDebug_GetProcess(ICorDebug *iface, DWORD dwProcessId, ICorDebugProcess **ppProcess) { CorDebug *This = impl_from_ICorDebug( iface ); - FIXME("stub %p %d %p\n", This, dwProcessId, ppProcess); + FIXME("stub %p %ld %p\n", This, dwProcessId, ppProcess); return E_NOTIMPL; } @@ -719,7 +740,7 @@ static HRESULT WINAPI CorDebug_CanLaunchOrAttach(ICorDebug *iface, DWORD dwProce BOOL win32DebuggingEnabled) { CorDebug *This = impl_from_ICorDebug( iface ); - FIXME("stub %p %d %d\n", This, dwProcessId, win32DebuggingEnabled); + FIXME("stub %p %ld %d\n", This, dwProcessId, win32DebuggingEnabled); return S_OK; } @@ -743,7 +764,7 @@ HRESULT CorDebug_Create(ICLRRuntimeHost *runtimehost, IUnknown** ppUnk) { CorDebug *This; - This = HeapAlloc( GetProcessHeap(), 0, sizeof *This ); + This = malloc(sizeof *This); if ( !This ) return E_OUTOFMEMORY; diff --git a/dll/win32/mscoree/corruntimehost.c b/dll/win32/mscoree/corruntimehost.c index 2c8f2cbfd33..b4fd710fd22 100644 --- a/dll/win32/mscoree/corruntimehost.c +++ b/dll/win32/mscoree/corruntimehost.c @@ -17,11 +17,33 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ -#include "mscoree_private.h" +#define COBJMACROS #include -#include -#include +#include + +#include "windef.h" +#include "winbase.h" +#include "winuser.h" +#include "winnls.h" +#include "winreg.h" +#include "ole2.h" +#include "shellapi.h" +#include "shlwapi.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" + +WINE_DEFAULT_DEBUG_CHANNEL( mscoree ); + +#include "initguid.h" DEFINE_GUID(IID__AppDomain, 0x05f696dc,0x2b29,0x3663,0xad,0x8b,0xc4,0x38,0x9c,0xf2,0xa7,0x13); @@ -33,12 +55,24 @@ struct DomainEntry static HANDLE dll_fixup_heap; /* using a separate heap so we can have execute permission */ +static CRITICAL_SECTION fixup_list_cs; +static CRITICAL_SECTION_DEBUG fixup_list_cs_debug = +{ + 0, 0, &fixup_list_cs, + { &fixup_list_cs_debug.ProcessLocksList, + &fixup_list_cs_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": fixup_list_cs") } +}; +static CRITICAL_SECTION fixup_list_cs = { &fixup_list_cs_debug, -1, 0, 0, 0, 0 }; + static struct list dll_fixups; +WCHAR **private_path = NULL; + struct dll_fixup { struct list entry; - int done; + BOOL done; HMODULE dll; void *thunk_code; /* pointer into dll_fixup_heap */ VTableFixup *fixup; @@ -46,141 +80,386 @@ struct dll_fixup void *tokens; /* pointer into process heap */ }; -static HRESULT RuntimeHost_AddDomain(RuntimeHost *This, MonoDomain **result) +struct comclassredirect_data { - struct DomainEntry *entry; - char *mscorlib_path; - HRESULT res=S_OK; + ULONG size; + ULONG flags; + DWORD model; + GUID clsid; + GUID alias; + GUID clsid2; + GUID tlbid; + ULONG name_len; + ULONG name_offset; + ULONG progid_len; + ULONG progid_offset; + ULONG clrdata_len; + ULONG clrdata_offset; + DWORD miscstatus; + DWORD miscstatuscontent; + DWORD miscstatusthumbnail; + DWORD miscstatusicon; + DWORD miscstatusdocprint; +}; - EnterCriticalSection(&This->lock); +struct clrclass_data +{ + ULONG size; + DWORD res[2]; + ULONG module_len; + ULONG module_offset; + ULONG name_len; + ULONG name_offset; + ULONG version_len; + ULONG version_offset; + DWORD res2[2]; +}; - entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry)); - if (!entry) - { - res = E_OUTOFMEMORY; - goto end; - } +static MonoDomain* domain_attach(MonoDomain *domain) +{ + MonoDomain *prev_domain = mono_domain_get(); - mscorlib_path = WtoA(This->version->mscorlib_path); - if (!mscorlib_path) - { - HeapFree(GetProcessHeap(), 0, entry); - res = E_OUTOFMEMORY; - goto end; - } + if (prev_domain == domain) + /* Do not set or restore domain. */ + return NULL; - entry->domain = This->mono->mono_jit_init(mscorlib_path); + mono_thread_attach(domain); - 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; + return prev_domain; } -static HRESULT RuntimeHost_GetDefaultDomain(RuntimeHost *This, MonoDomain **result) +static void domain_restore(MonoDomain *prev_domain) { + if (prev_domain != NULL) + mono_domain_set(prev_domain, FALSE); +} + +static HRESULT RuntimeHost_GetDefaultDomain(RuntimeHost *This, const WCHAR *config_path, MonoDomain **result) +{ + WCHAR exe_config[MAX_PATH]; + WCHAR base_dir[MAX_PATH]; + char *base_dirA, *config_pathA, *slash; HRESULT res=S_OK; + static BOOL configured_domain; + + *result = get_root_domain(); EnterCriticalSection(&This->lock); - if (This->default_domain) goto end; + if (configured_domain) goto end; - res = RuntimeHost_AddDomain(This, &This->default_domain); + if (!config_path) + { + GetModuleFileNameW(NULL, exe_config, MAX_PATH); + lstrcatW(exe_config, L".config"); + + config_path = exe_config; + } + + 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) + { + free(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(*result, base_dirA, config_pathA); + + free(config_pathA); + free(base_dirA); end: - *result = This->default_domain; + + configured_domain = TRUE; LeaveCriticalSection(&This->lock); return res; } -static void RuntimeHost_DeleteDomain(RuntimeHost *This, MonoDomain *domain) +static BOOL RuntimeHost_GetMethod(MonoDomain *domain, const char *assemblyname, + const char *namespace, const char *typename, const char *methodname, int arg_count, + MonoMethod **method) { - struct DomainEntry *entry; + MonoAssembly *assembly; + MonoImage *image; + MonoClass *klass; - EnterCriticalSection(&This->lock); - - LIST_FOR_EACH_ENTRY(entry, &This->domains, struct DomainEntry, entry) + if (!assemblyname) { - if (entry->domain == domain) + image = mono_get_corlib(); + } + else + { + MonoImageOpenStatus status; + assembly = mono_assembly_open(assemblyname, &status); + if (!assembly) { - list_remove(&entry->entry); - if (This->default_domain == domain) - This->default_domain = NULL; - HeapFree(GetProcessHeap(), 0, entry); - break; + ERR("Cannot load assembly %s, status=%i\n", assemblyname, status); + return FALSE; + } + + image = mono_assembly_get_image(assembly); + if (!image) + { + ERR("Couldn't get assembly image for %s\n", assemblyname); + return FALSE; } } - LeaveCriticalSection(&This->lock); + 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, NULL, "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=%lx\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=%lx\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, NULL, "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); + free(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, NULL, "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, NULL, "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; - void *args[1]; - MonoAssembly *assembly; - MonoImage *image; - MonoClass *klass; - MonoMethod *method; MonoObject *appdomain_object; IUnknown *unk; - This->mono->mono_thread_attach(domain); + hr = RuntimeHost_Invoke(This, domain, NULL, "System", "AppDomain", "get_CurrentDomain", + NULL, NULL, 0, &appdomain_object); - 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", "AppDomain"); - if (!klass) - { - ERR("Couldn't get class from image\n"); - return E_FAIL; - } - - method = This->mono->mono_class_get_method_from_name(klass, "get_CurrentDomain", 0); - if (!method) - { - ERR("Couldn't get method from class\n"); - return E_FAIL; - } - - args[0] = NULL; - appdomain_object = This->mono->mono_runtime_invoke(method, NULL, args, NULL); - if (!appdomain_object) - { - ERR("Couldn't get result pointer\n"); - return E_FAIL; - } - - hr = RuntimeHost_GetIUnknownForObject(This, appdomain_object, &unk); + if (SUCCEEDED(hr)) + hr = RuntimeHost_GetIUnknownForObject(This, appdomain_object, &unk); if (SUCCEEDED(hr)) { @@ -192,6 +471,28 @@ static HRESULT RuntimeHost_GetIUnknownForDomain(RuntimeHost *This, MonoDomain *d 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=%lx\n", hr); + return; + } + + args[0] = &exitcode; + args[1] = NULL; + RuntimeHost_Invoke(This, domain, NULL, "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); @@ -302,8 +603,12 @@ static HRESULT WINAPI corruntimehost_GetConfiguration( static HRESULT WINAPI corruntimehost_Start( ICorRuntimeHost* iface) { - FIXME("stub %p\n", iface); - return S_OK; + RuntimeHost *This = impl_from_ICorRuntimeHost( iface ); + MonoDomain *dummy; + + TRACE("%p\n", This); + + return RuntimeHost_GetDefaultDomain(This, NULL, &dummy); } static HRESULT WINAPI corruntimehost_Stop( @@ -319,8 +624,7 @@ static HRESULT WINAPI corruntimehost_CreateDomain( IUnknown *identityArray, IUnknown **appDomain) { - FIXME("stub %p\n", iface); - return E_NOTIMPL; + return ICorRuntimeHost_CreateDomainEx(iface, friendlyName, NULL, NULL, appDomain); } static HRESULT WINAPI corruntimehost_GetDefaultDomain( @@ -333,7 +637,7 @@ static HRESULT WINAPI corruntimehost_GetDefaultDomain( TRACE("(%p)\n", iface); - hr = RuntimeHost_GetDefaultDomain(This, &domain); + hr = RuntimeHost_GetDefaultDomain(This, NULL, &domain); if (SUCCEEDED(hr)) { @@ -375,16 +679,52 @@ static HRESULT WINAPI corruntimehost_CreateDomainEx( IUnknown *evidence, IUnknown **appDomain) { - FIXME("stub %p\n", iface); - return E_NOTIMPL; + 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) { - FIXME("stub %p\n", iface); - return E_NOTIMPL; + 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( @@ -474,8 +814,12 @@ static ULONG WINAPI CLRRuntimeHost_Release(ICLRRuntimeHost* iface) static HRESULT WINAPI CLRRuntimeHost_Start(ICLRRuntimeHost* iface) { - FIXME("(%p)\n", iface); - return E_NOTIMPL; + RuntimeHost *This = impl_from_ICLRRuntimeHost( iface ); + MonoDomain *dummy; + + TRACE("%p\n", This); + + return RuntimeHost_GetDefaultDomain(This, NULL, &dummy); } static HRESULT WINAPI CLRRuntimeHost_Stop(ICLRRuntimeHost* iface) @@ -501,14 +845,14 @@ static HRESULT WINAPI CLRRuntimeHost_GetCLRControl(ICLRRuntimeHost* iface, static HRESULT WINAPI CLRRuntimeHost_UnloadAppDomain(ICLRRuntimeHost* iface, DWORD dwAppDomainId, BOOL fWaitUntilDone) { - FIXME("(%p,%u,%i)\n", iface, dwAppDomainId, fWaitUntilDone); + FIXME("(%p,%lu,%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); + FIXME("(%p,%lu,%p,%p)\n", iface, dwAppDomainId, pCallback, cookie); return E_NOTIMPL; } @@ -523,7 +867,7 @@ 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); + FIXME("(%p,%s,%lu,%lu)\n", iface, debugstr_w(pwzAppFullName), dwManifestPaths, dwActivationData); return E_NOTIMPL; } @@ -533,85 +877,79 @@ static HRESULT WINAPI CLRRuntimeHost_ExecuteInDefaultAppDomain(ICLRRuntimeHost* { RuntimeHost *This = impl_from_ICLRRuntimeHost( iface ); HRESULT hr; - MonoDomain *domain; - MonoAssembly *assembly; - MonoImage *image; - MonoClass *klass; - MonoMethod *method; + MonoDomain *domain, *prev_domain; MonoObject *result; MonoString *str; - void *args[2]; 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, &domain); - if(hr != S_OK) - { - ERR("Couldn't get Default Domain\n"); + 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; } - hr = E_FAIL; - - This->mono->mono_thread_attach(domain); - - filenameA = WtoA(pwzAssemblyPath); - assembly = This->mono->mono_domain_assembly_open(domain, filenameA); - if (!assembly) + if (SUCCEEDED(hr)) { - ERR("Cannot open assembly %s\n", filenameA); - goto cleanup; + classA = WtoA(pwzTypeName); + if (!classA) hr = E_OUTOFMEMORY; } - image = This->mono->mono_assembly_get_image(assembly); - if (!image) + if (SUCCEEDED(hr)) { - ERR("Couldn't get assembly image\n"); - goto cleanup; + ns = strrchr(classA, '.'); + if (ns) + *ns = '\0'; + else + hr = E_INVALIDARG; } - classA = WtoA(pwzTypeName); - ns = strrchr(classA, '.'); - *ns = '\0'; - klass = This->mono->mono_class_from_name(image, classA, ns+1); - if (!klass) + if (SUCCEEDED(hr)) { - ERR("Couldn't get class from image\n"); - goto cleanup; - } - - methodA = WtoA(pwzMethodName); - method = This->mono->mono_class_get_method_from_name(klass, methodA, 1); - if (!method) - { - ERR("Couldn't get method from class\n"); - goto cleanup; + methodA = WtoA(pwzMethodName); + if (!methodA) hr = E_OUTOFMEMORY; } /* The .NET function we are calling has the following declaration * public static int functionName(String param) */ - argsA = WtoA(pwzArgument); - str = This->mono->mono_string_new(domain, argsA); - args[0] = str; - args[1] = NULL; - result = This->mono->mono_runtime_invoke(method, NULL, args, NULL); - if (!result) - ERR("Couldn't get result pointer\n"); - else + if (SUCCEEDED(hr)) { - *pReturnValue = *(DWORD*)This->mono->mono_object_unbox(result); - hr = S_OK; + argsA = WtoA(pwzArgument); + if (!argsA) hr = E_OUTOFMEMORY; } -cleanup: - HeapFree(GetProcessHeap(), 0, filenameA); - HeapFree(GetProcessHeap(), 0, classA); - HeapFree(GetProcessHeap(), 0, argsA); - HeapFree(GetProcessHeap(), 0, methodA); + 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); + + free(filenameA); + free(classA); + free(argsA); + free(methodA); return hr; } @@ -643,9 +981,15 @@ HRESULT RuntimeHost_CreateManagedInstance(RuntimeHost *This, LPCWSTR name, MonoType *type; MonoClass *klass; MonoObject *obj; + MonoDomain *prev_domain; if (!domain) - hr = RuntimeHost_GetDefaultDomain(This, &domain); + hr = RuntimeHost_GetDefaultDomain(This, NULL, &domain); + + if (FAILED(hr)) + return hr; + + prev_domain = domain_attach(domain); if (SUCCEEDED(hr)) { @@ -656,9 +1000,7 @@ HRESULT RuntimeHost_CreateManagedInstance(RuntimeHost *This, LPCWSTR name, if (SUCCEEDED(hr)) { - This->mono->mono_thread_attach(domain); - - type = This->mono->mono_reflection_type_from_name(nameA, NULL); + type = mono_reflection_type_from_name(nameA, NULL); if (!type) { ERR("Cannot find type %s\n", debugstr_w(name)); @@ -668,7 +1010,7 @@ HRESULT RuntimeHost_CreateManagedInstance(RuntimeHost *This, LPCWSTR name, if (SUCCEEDED(hr)) { - klass = This->mono->mono_class_from_mono_type(type); + klass = mono_class_from_mono_type(type); if (!klass) { ERR("Cannot convert type %s to a class\n", debugstr_w(name)); @@ -678,7 +1020,7 @@ HRESULT RuntimeHost_CreateManagedInstance(RuntimeHost *This, LPCWSTR name, if (SUCCEEDED(hr)) { - obj = This->mono->mono_object_new(domain, klass); + obj = mono_object_new(domain, klass); if (!obj) { ERR("Cannot allocate object of type %s\n", debugstr_w(name)); @@ -689,11 +1031,13 @@ HRESULT RuntimeHost_CreateManagedInstance(RuntimeHost *This, LPCWSTR name, if (SUCCEEDED(hr)) { /* FIXME: Detect exceptions from the constructor? */ - This->mono->mono_runtime_object_init(obj); + mono_runtime_object_init(obj); *result = obj; } - HeapFree(GetProcessHeap(), 0, nameA); + domain_restore(prev_domain); + + free(nameA); return hr; } @@ -705,67 +1049,25 @@ HRESULT RuntimeHost_CreateManagedInstance(RuntimeHost *This, LPCWSTR name, * * 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. - * - * mono_thread_attach must have already been called for this thread. */ + * 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]; + HRESULT hr; - domain = This->mono->mono_object_get_domain(obj); + domain = mono_object_get_domain(obj); - assembly = This->mono->mono_domain_assembly_open(domain, "mscorlib"); - if (!assembly) - { - ERR("Cannot load mscorlib\n"); - return E_FAIL; - } + hr = RuntimeHost_Invoke(This, domain, NULL, "System.Runtime.InteropServices", "Marshal", "GetIUnknownForObject", + NULL, (void**)&obj, 1, &result); - image = This->mono->mono_assembly_get_image(assembly); - if (!image) - { - ERR("Couldn't get assembly image\n"); - return E_FAIL; - } + if (SUCCEEDED(hr)) + *ppUnk = *(IUnknown**)mono_object_unbox(result); + else + *ppUnk = NULL; - 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; + return hr; } static void get_utf8_args(int *argc, char ***argv) @@ -783,7 +1085,7 @@ static void get_utf8_args(int *argc, char ***argv) } size += sizeof(char*); - *argv = HeapAlloc(GetProcessHeap(), 0, size); + *argv = malloc(size); current_arg = (char*)(*argv + *argc + 1); for (i=0; i<*argc; i++) @@ -794,7 +1096,7 @@ static void get_utf8_args(int *argc, char ***argv) (*argv)[*argc] = NULL; - HeapFree(GetProcessHeap(), 0, argvw); + LocalFree(argvw); } #if __i386__ @@ -805,6 +1107,8 @@ static void get_utf8_args(int *argc, char ***argv) struct vtable_fixup_thunk { + /* push %ecx */ + BYTE i7; /* sub $0x4,%esp */ BYTE i1[3]; /* mov fixup,(%esp) */ @@ -817,12 +1121,15 @@ struct vtable_fixup_thunk 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, @@ -830,13 +1137,82 @@ static const struct vtable_fixup_thunk thunk_template = { NULL, {0xff,0xd0}, 0x58, + 0x59, {0xff,0x25}, NULL }; #include "poppack.h" -#else /* !defined(__i386__) */ +#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 @@ -851,6 +1227,34 @@ static const struct vtable_fixup_thunk thunk_template = {0}; #endif +DWORD WINAPI GetTokenForVTableEntry(HINSTANCE hinst, BYTE **ppVTEntry) +{ + struct dll_fixup *fixup; + DWORD result = 0; + DWORD rva; + int i; + + TRACE("%p,%p\n", hinst, ppVTEntry); + + rva = (BYTE*)ppVTEntry - (BYTE*)hinst; + + EnterCriticalSection(&fixup_list_cs); + LIST_FOR_EACH_ENTRY(fixup, &dll_fixups, struct dll_fixup, entry) + { + if (fixup->dll != hinst) + continue; + if (rva < fixup->fixup->rva || (rva - fixup->fixup->rva >= fixup->fixup->count * sizeof(ULONG_PTR))) + continue; + i = (rva - fixup->fixup->rva) / sizeof(ULONG_PTR); + result = ((ULONG_PTR*)fixup->tokens)[i]; + break; + } + LeaveCriticalSection(&fixup_list_cs); + + TRACE("<-- %lx\n", result); + return result; +} + static void CDECL ReallyFixupVTable(struct dll_fixup *fixup) { HRESULT hr=S_OK; @@ -877,55 +1281,58 @@ static void CDECL ReallyFixupVTable(struct dll_fixup *fixup) hr = E_OUTOFMEMORY; if (SUCCEEDED(hr)) - hr = get_runtime_info(filename, NULL, NULL, 0, 0, FALSE, &info); + hr = get_runtime_info(filename, NULL, NULL, NULL, 0, 0, FALSE, &info); if (SUCCEEDED(hr)) hr = ICLRRuntimeInfo_GetRuntimeHost(info, &host); if (SUCCEEDED(hr)) - hr = RuntimeHost_GetDefaultDomain(host, &domain); + hr = RuntimeHost_GetDefaultDomain(host, NULL, &domain); if (SUCCEEDED(hr)) { - host->mono->mono_thread_attach(domain); + MonoDomain *prev_domain; - image = host->mono->mono_image_open_from_module_handle(fixup->dll, - filenameA, 1, &status); - } + prev_domain = domain_attach(domain); - if (image) - assembly = host->mono->mono_assembly_load_from(image, filenameA, &status); + assembly = mono_assembly_open(filenameA, &status); - if (assembly) - { - int i; - - /* Mono needs an image that belongs to an assembly. */ - image = host->mono->mono_assembly_get_image(assembly); - - if (fixup->fixup->type & COR_VTABLE_32BIT) + if (assembly) { - DWORD *vtable = fixup->vtable; - DWORD *tokens = fixup->tokens; - for (i=0; ifixup->count; i++) + 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 { - TRACE("%x\n", tokens[i]); - vtable[i] = PtrToUint(host->mono->mono_marshal_get_vtfixup_ftnptr( - image, tokens[i], fixup->fixup->type)); + void **vtable = fixup->vtable; + ULONG_PTR *tokens = fixup->tokens; + for (i=0; ifixup->count; i++) + { + vtable[i] = mono_marshal_get_vtfixup_ftnptr( + image, tokens[i], fixup->fixup->type); + } } + + fixup->done = TRUE; } - fixup->done = 1; + domain_restore(prev_domain); } if (info != NULL) ICLRRuntimeInfo_Release(info); - HeapFree(GetProcessHeap(), 0, filenameA); + free(filenameA); if (!fixup->done) { - ERR("unable to fixup vtable, hr=%x, status=%d\n", hr, status); + ERR("unable to fixup vtable, hr=%lx, status=%d\n", hr, status); /* If we returned now, we'd get an infinite loop. */ assert(0); } @@ -942,128 +1349,178 @@ static void FixupVTableEntry(HMODULE hmodule, VTableFixup *vtable_fixup) * threads are clear. */ struct dll_fixup *fixup; - fixup = HeapAlloc(GetProcessHeap(), 0, sizeof(*fixup)); + fixup = malloc(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 = 0; + fixup->done = FALSE; +#if __x86_64__ + if (vtable_fixup->type & COR_VTABLE_64BIT) +#else if (vtable_fixup->type & COR_VTABLE_32BIT) +#endif { - DWORD *vtable = fixup->vtable; - DWORD *tokens; + void **vtable = fixup->vtable; + ULONG_PTR *tokens; int i; struct vtable_fixup_thunk *thunks = fixup->thunk_code; - if (sizeof(void*) > 4) - ERR("32-bit fixup in 64-bit mode; broken image?\n"); - - tokens = fixup->tokens = HeapAlloc(GetProcessHeap(), 0, sizeof(*tokens) * vtable_fixup->count); + tokens = fixup->tokens = malloc(sizeof(*tokens) * vtable_fixup->count); memcpy(tokens, vtable, sizeof(*tokens) * vtable_fixup->count); for (i=0; icount; i++) { - memcpy(&thunks[i], &thunk_template, sizeof(thunk_template)); + thunks[i] = thunk_template; thunks[i].fixup = fixup; thunks[i].function = ReallyFixupVTable; thunks[i].vtable_entry = &vtable[i]; - vtable[i] = PtrToUint(&thunks[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); + free(fixup); return; } + EnterCriticalSection(&fixup_list_cs); list_add_tail(&dll_fixups, &fixup->entry); + LeaveCriticalSection(&fixup_list_cs); +} + +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; imono->mono_image_open_from_module_handle(GetModuleHandleW(NULL), + image = mono_image_open_from_module_handle(GetModuleHandleW(NULL), filenameA, 1, &status); if (image) - assembly = host->mono->mono_assembly_load_from(image, filenameA, &status); + assembly = mono_assembly_load_from(image, filenameA, &status); if (assembly) { - exit_code = host->mono->mono_jit_exec(domain, assembly, argc, argv); + 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; @@ -1073,27 +1530,46 @@ __int32 WINAPI _CorExeMain(void) else exit_code = -1; - HeapFree(GetProcessHeap(), 0, argv); + free(argv); - unload_all_runtimes(); + if (domain) + { + mono_thread_manage(); + mono_runtime_quit(); + } + + ExitProcess(exit_code); return exit_code; } BOOL WINAPI _CorDllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { - TRACE("(%p, %d, %p)\n", hinstDLL, fdwReason, lpvReserved); + ASSEMBLY *assembly=NULL; + HRESULT hr; - switch (fdwReason) + TRACE("(%p, %ld, %p)\n", hinstDLL, fdwReason, lpvReserved); + + hr = assembly_from_hmodule(&assembly, hinstDLL); + if (SUCCEEDED(hr)) { - case DLL_PROCESS_ATTACH: - DisableThreadLibraryCalls(hinstDLL); - FixupVTable(hinstDLL); - break; - case DLL_PROCESS_DETACH: - /* FIXME: clean up the vtables */ - break; + 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=%lx\n", hr); + return TRUE; } @@ -1112,17 +1588,16 @@ void runtimehost_uninit(void) 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); + free(fixup->tokens); + free(fixup); } } -HRESULT RuntimeHost_Construct(const CLRRuntimeInfo *runtime_version, - loaded_mono *loaded_mono, RuntimeHost** result) +HRESULT RuntimeHost_Construct(CLRRuntimeInfo *runtime_version, RuntimeHost** result) { RuntimeHost *This; - This = HeapAlloc( GetProcessHeap(), 0, sizeof *This ); + This = malloc(sizeof *This); if ( !This ) return E_OUTOFMEMORY; @@ -1131,10 +1606,7 @@ HRESULT RuntimeHost_Construct(const CLRRuntimeInfo *runtime_version, This->ref = 1; This->version = runtime_version; - This->mono = loaded_mono; - list_init(&This->domains); - This->default_domain = NULL; - InitializeCriticalSection(&This->lock); + InitializeCriticalSectionEx(&This->lock, 0, RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO); This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": RuntimeHost.lock"); *result = This; @@ -1187,123 +1659,287 @@ HRESULT RuntimeHost_GetInterface(RuntimeHost *This, REFCLSID clsid, REFIID riid, return CLASS_E_CLASSNOTAVAILABLE; } -HRESULT RuntimeHost_Destroy(RuntimeHost *This) +static BOOL try_create_registration_free_com(REFIID clsid, WCHAR *classname, UINT classname_size, WCHAR *filename, UINT filename_size) { - struct DomainEntry *cursor, *cursor2; + ACTCTX_SECTION_KEYED_DATA guid_info = { sizeof(ACTCTX_SECTION_KEYED_DATA) }; + ACTIVATION_CONTEXT_ASSEMBLY_DETAILED_INFORMATION *assembly_info = NULL; + SIZE_T bytes_assembly_info; + struct comclassredirect_data *redirect_data; + struct clrclass_data *class_data; + void *ptr_name; + const WCHAR *ptr_path_start, *ptr_path_end; + WCHAR path[MAX_PATH] = {0}; + WCHAR str_dll[] = {'.','d','l','l',0}; + BOOL ret = FALSE; - This->lock.DebugInfo->Spare[0] = 0; - DeleteCriticalSection(&This->lock); - - LIST_FOR_EACH_ENTRY_SAFE(cursor, cursor2, &This->domains, struct DomainEntry, entry) + if (!FindActCtxSectionGuid(FIND_ACTCTX_SECTION_KEY_RETURN_HACTCTX, 0, ACTIVATION_CONTEXT_SECTION_COM_SERVER_REDIRECTION, clsid, &guid_info)) { - list_remove(&cursor->entry); - HeapFree(GetProcessHeap(), 0, cursor); + DWORD error = GetLastError(); + if (error != ERROR_SXS_KEY_NOT_FOUND) + ERR("Failed to find guid: %ld\n", error); + goto end; } - HeapFree( GetProcessHeap(), 0, This ); - return S_OK; + QueryActCtxW(0, guid_info.hActCtx, &guid_info.ulAssemblyRosterIndex, AssemblyDetailedInformationInActivationContext, NULL, 0, &bytes_assembly_info); + assembly_info = malloc(bytes_assembly_info); + if (!QueryActCtxW(0, guid_info.hActCtx, &guid_info.ulAssemblyRosterIndex, + AssemblyDetailedInformationInActivationContext, assembly_info, bytes_assembly_info, &bytes_assembly_info)) + { + ERR("QueryActCtxW failed: %ld!\n", GetLastError()); + goto end; + } + + redirect_data = guid_info.lpData; + class_data = (void *)((char *)redirect_data + redirect_data->clrdata_offset); + + ptr_name = (char *)class_data + class_data->name_offset; + if (lstrlenW(ptr_name) + 1 > classname_size) /* Include null-terminator */ + { + ERR("Buffer is too small\n"); + goto end; + } + lstrcpyW(classname, ptr_name); + + ptr_path_start = assembly_info->lpAssemblyEncodedAssemblyIdentity; + ptr_path_end = wcschr(ptr_path_start, ','); + memcpy(path, ptr_path_start, (char*)ptr_path_end - (char*)ptr_path_start); + + GetModuleFileNameW(NULL, filename, filename_size); + PathRemoveFileSpecW(filename); + + if (lstrlenW(filename) + lstrlenW(path) + ARRAY_SIZE(str_dll) + 1 > filename_size) /* Include blackslash */ + { + ERR("Buffer is too small\n"); + goto end; + } + + PathAppendW(filename, path); + lstrcatW(filename, str_dll); + + ret = TRUE; + +end: + free(assembly_info); + + if (guid_info.hActCtx) + ReleaseActCtx(guid_info.hActCtx); + + return ret; } #define CHARS_IN_GUID 39 -#undef ARRAYSIZE -#define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0])) -HRESULT create_monodata(REFIID riid, LPVOID *ppObj ) +HRESULT create_monodata(REFCLSID clsid, LPVOID *ppObj) { - 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}; - WCHAR path[CHARS_IN_GUID + ARRAYSIZE(wszCLSIDSlash) + ARRAYSIZE(wszInprocServer32) - 1]; + static const WCHAR wszFileSlash[] = L"file:///"; + static const WCHAR wszCLSIDSlash[] = L"CLSID\\"; + static const WCHAR wszInprocServer32[] = L"\\InprocServer32"; + 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; + HKEY key, subkey; LONG res; int offset = 0; + HANDLE file = INVALID_HANDLE_VALUE; + DWORD numKeys, keyLength; WCHAR codebase[MAX_PATH + 8]; - WCHAR classname[350]; + WCHAR classname[350], subkeyName[256]; WCHAR filename[MAX_PATH]; - - DWORD dwBufLen = 350; + DWORD dwBufLen; lstrcpyW(path, wszCLSIDSlash); - StringFromGUID2(riid, path + lstrlenW(wszCLSIDSlash), CHARS_IN_GUID); + StringFromGUID2(clsid, 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) + if (res != ERROR_FILE_NOT_FOUND) { - WARN("Class value cannot be found.\n"); - hr = CLASS_E_CLASSNOTAVAILABLE; - goto cleanup; + res = RegOpenKeyExW( key, L"Server", 0, KEY_READ, &subkey ); + if (res == ERROR_SUCCESS) + { + /* Not a managed class, just chain through LoadLibraryShim */ + HMODULE module; + HRESULT (WINAPI *pDllGetClassObject)(REFCLSID,REFIID,LPVOID*); + IClassFactory *classfactory; + + dwBufLen = sizeof( filename ); + res = RegGetValueW( subkey, NULL, NULL, RRF_RT_REG_SZ, NULL, filename, &dwBufLen ); + + RegCloseKey( subkey ); + + if (res != ERROR_SUCCESS) + { + WARN("Can't read default value from Server subkey.\n"); + hr = CLASS_E_CLASSNOTAVAILABLE; + goto cleanup; + } + + hr = LoadLibraryShim( filename, L"v4.0.30319", NULL, &module); + if (FAILED(hr)) + { + WARN("Can't load %s.\n", debugstr_w(filename)); + goto cleanup; + } + + pDllGetClassObject = (void*)GetProcAddress( module, "DllGetClassObject" ); + if (!pDllGetClassObject) + { + WARN("Can't get DllGetClassObject from %s.\n", debugstr_w(filename)); + hr = CLASS_E_CLASSNOTAVAILABLE; + goto cleanup; + } + + hr = pDllGetClassObject( clsid, &IID_IClassFactory, (void**)&classfactory ); + if (SUCCEEDED(hr)) + { + hr = IClassFactory_CreateInstance( classfactory, NULL, &IID_IUnknown, ppObj ); + + IClassFactory_Release( classfactory ); + } + + goto cleanup; + } + + dwBufLen = sizeof( classname ); + res = RegGetValueW( key, NULL, L"Class", 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 = sizeof( codebase ); + res = RegGetValueW( key, NULL, L"CodeBase", RRF_RT_REG_SZ, NULL, codebase, &dwBufLen); + if(res == ERROR_SUCCESS) + { + /* Strip file:/// */ + if(wcsncmp(codebase, wszFileSlash, lstrlenW(wszFileSlash)) == 0) + offset = lstrlenW(wszFileSlash); + + lstrcpyW(filename, codebase + offset); + + file = CreateFileW(filename, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, 0); + } + + if (file != INVALID_HANDLE_VALUE) + CloseHandle(file); + 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) + goto cleanup; + if (numKeys > 0) + { + 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 = sizeof( assemblyname ); + res = RegGetValueW(subkey, NULL, L"Assembly", RRF_RT_REG_SZ, NULL, assemblyname, &dwBufLen); + RegCloseKey(subkey); + if (res != ERROR_SUCCESS) + goto cleanup; + } + else + { + dwBufLen = sizeof( assemblyname ); + res = RegGetValueW(key, NULL, L"Assembly", RRF_RT_REG_SZ, NULL, assemblyname, &dwBufLen); + 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 or the file isn't there, 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 = wcsrchr(filename, '\\'); + *(ns+1) = '\0'; + + ns = wcschr(assemblyname, ','); + *(ns) = '\0'; + lstrcatW(filename, assemblyname); + *(ns) = '.'; + lstrcatW(filename, L".dll"); + } + } + } + else + { + if (!try_create_registration_free_com(clsid, classname, ARRAY_SIZE(classname), filename, ARRAY_SIZE(filename))) + return CLASS_E_CLASSNOTAVAILABLE; + + TRACE("classname (%s)\n", debugstr_w(classname)); } - 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) - { - WARN("CodeBase value cannot be found.\n"); - hr = CLASS_E_CLASSNOTAVAILABLE; - goto cleanup; - } - - /* Strip file:/// */ - if(strncmpW(codebase, wszFileSlash, strlenW(wszFileSlash)) == 0) - offset = strlenW(wszFileSlash); - - strcpyW(filename, codebase + offset); - - TRACE("codebase (%s)\n", debugstr_w(filename)); + TRACE("filename (%s)\n", debugstr_w(filename)); *ppObj = NULL; - hr = get_runtime_info(filename, NULL, NULL, 0, 0, FALSE, &info); + hr = get_runtime_info(filename, NULL, NULL, NULL, 0, 0, FALSE, &info); if (SUCCEEDED(hr)) { hr = ICLRRuntimeInfo_GetRuntimeHost(info, &host); if (SUCCEEDED(hr)) - hr = RuntimeHost_GetDefaultDomain(host, &domain); + hr = RuntimeHost_GetDefaultDomain(host, NULL, &domain); if (SUCCEEDED(hr)) { MonoImage *image; MonoClass *klass; MonoObject *result; + MonoDomain *prev_domain; + MonoImageOpenStatus status; IUnknown *unk = NULL; char *filenameA, *ns; char *classA; hr = CLASS_E_CLASSNOTAVAILABLE; - host->mono->mono_thread_attach(domain); + prev_domain = domain_attach(domain); filenameA = WtoA(filename); - assembly = host->mono->mono_domain_assembly_open(domain, filenameA); - HeapFree(GetProcessHeap(), 0, filenameA); + assembly = mono_assembly_open(filenameA, &status); + free(filenameA); if (!assembly) { - ERR("Cannot open assembly %s\n", filenameA); + ERR("Cannot open assembly %s, status=%i\n", debugstr_w(filename), status); + domain_restore(prev_domain); goto cleanup; } - image = host->mono->mono_assembly_get_image(assembly); + image = mono_assembly_get_image(assembly); if (!image) { ERR("Couldn't get assembly image\n"); + domain_restore(prev_domain); goto cleanup; } @@ -1311,19 +1947,20 @@ HRESULT create_monodata(REFIID riid, LPVOID *ppObj ) ns = strrchr(classA, '.'); *ns = '\0'; - klass = host->mono->mono_class_from_name(image, classA, ns+1); - HeapFree(GetProcessHeap(), 0, classA); + klass = mono_class_from_name(image, classA, ns+1); + free(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 = host->mono->mono_object_new(domain, klass); - host->mono->mono_runtime_object_init(result); + result = mono_object_new(domain, klass); + mono_runtime_object_init(result); hr = RuntimeHost_GetIUnknownForObject(host, result, &unk); if (SUCCEEDED(hr)) @@ -1334,6 +1971,8 @@ HRESULT create_monodata(REFIID riid, LPVOID *ppObj ) } else hr = CLASS_E_CLASSNOTAVAILABLE; + + domain_restore(prev_domain); } else hr = CLASS_E_CLASSNOTAVAILABLE; diff --git a/dll/win32/mscoree/metadata.c b/dll/win32/mscoree/metadata.c index b80a2dcb699..e41d61071ae 100644 --- a/dll/win32/mscoree/metadata.c +++ b/dll/win32/mscoree/metadata.c @@ -18,8 +18,28 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ +#include +#include +#include + +#define COBJMACROS + +#include "windef.h" +#include "winbase.h" +#include "winreg.h" +#include "ole2.h" +#include "cor.h" +#include "mscoree.h" +#include "corhdr.h" +#include "cordebug.h" +#include "metahost.h" +#include "wine/list.h" #include "mscoree_private.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL( mscoree ); + typedef struct MetaDataDispenser { IMetaDataDispenserEx IMetaDataDispenserEx_iface; @@ -58,7 +78,7 @@ static ULONG WINAPI MetaDataDispenser_AddRef(IMetaDataDispenserEx* iface) MetaDataDispenser *This = impl_from_IMetaDataDispenserEx(iface); ULONG ref = InterlockedIncrement(&This->ref); - TRACE("%p ref=%u\n", This, ref); + TRACE("%p ref=%lu\n", This, ref); return ref; } @@ -68,11 +88,11 @@ static ULONG WINAPI MetaDataDispenser_Release(IMetaDataDispenserEx* iface) MetaDataDispenser *This = impl_from_IMetaDataDispenserEx(iface); ULONG ref = InterlockedDecrement(&This->ref); - TRACE("%p ref=%u\n", This, ref); + TRACE("%p ref=%lu\n", This, ref); if (ref == 0) { - HeapFree(GetProcessHeap(), 0, This); + free(This); } return ref; @@ -81,7 +101,7 @@ static ULONG WINAPI MetaDataDispenser_Release(IMetaDataDispenserEx* iface) static HRESULT WINAPI MetaDataDispenser_DefineScope(IMetaDataDispenserEx* iface, REFCLSID rclsid, DWORD dwCreateFlags, REFIID riid, IUnknown **ppIUnk) { - FIXME("%p %s %x %s %p\n", iface, debugstr_guid(rclsid), dwCreateFlags, + FIXME("%p %s %lx %s %p\n", iface, debugstr_guid(rclsid), dwCreateFlags, debugstr_guid(riid), ppIUnk); return E_NOTIMPL; } @@ -89,7 +109,7 @@ static HRESULT WINAPI MetaDataDispenser_DefineScope(IMetaDataDispenserEx* iface, static HRESULT WINAPI MetaDataDispenser_OpenScope(IMetaDataDispenserEx* iface, LPCWSTR szScope, DWORD dwOpenFlags, REFIID riid, IUnknown **ppIUnk) { - FIXME("%p %s %x %s %p\n", iface, debugstr_w(szScope), dwOpenFlags, + FIXME("%p %s %lx %s %p\n", iface, debugstr_w(szScope), dwOpenFlags, debugstr_guid(riid), ppIUnk); return E_NOTIMPL; } @@ -97,7 +117,7 @@ static HRESULT WINAPI MetaDataDispenser_OpenScope(IMetaDataDispenserEx* iface, static HRESULT WINAPI MetaDataDispenser_OpenScopeOnMemory(IMetaDataDispenserEx* iface, const void *pData, ULONG cbData, DWORD dwOpenFlags, REFIID riid, IUnknown **ppIUnk) { - FIXME("%p %p %u %x %s %p\n", iface, pData, cbData, dwOpenFlags, + FIXME("%p %p %lu %lx %s %p\n", iface, pData, cbData, dwOpenFlags, debugstr_guid(riid), ppIUnk); return E_NOTIMPL; } @@ -119,14 +139,14 @@ static HRESULT WINAPI MetaDataDispenser_GetOption(IMetaDataDispenserEx* iface, static HRESULT WINAPI MetaDataDispenser_OpenScopeOnITypeInfo(IMetaDataDispenserEx* iface, ITypeInfo *pITI, DWORD dwOpenFlags, REFIID riid, IUnknown **ppIUnk) { - FIXME("%p %p %u %s %p\n", iface, pITI, dwOpenFlags, debugstr_guid(riid), ppIUnk); + FIXME("%p %p %lu %s %p\n", iface, pITI, dwOpenFlags, debugstr_guid(riid), ppIUnk); return E_NOTIMPL; } static HRESULT WINAPI MetaDataDispenser_GetCORSystemDirectory(IMetaDataDispenserEx* iface, LPWSTR szBuffer, DWORD cchBuffer, DWORD *pchBuffer) { - FIXME("%p %p %u %p\n", iface, szBuffer, cchBuffer, pchBuffer); + FIXME("%p %p %lu %p\n", iface, szBuffer, cchBuffer, pchBuffer); return E_NOTIMPL; } @@ -134,7 +154,7 @@ static HRESULT WINAPI MetaDataDispenser_FindAssembly(IMetaDataDispenserEx* iface LPCWSTR szAppBase, LPCWSTR szPrivateBin, LPCWSTR szGlobalBin, LPCWSTR szAssemblyName, LPWSTR szName, ULONG cchName, ULONG *pcName) { - FIXME("%p %s %s %s %s %p %u %p\n", iface, debugstr_w(szAppBase), + FIXME("%p %s %s %s %s %p %lu %p\n", iface, debugstr_w(szAppBase), debugstr_w(szPrivateBin), debugstr_w(szGlobalBin), debugstr_w(szAssemblyName), szName, cchName, pcName); return E_NOTIMPL; @@ -144,7 +164,7 @@ static HRESULT WINAPI MetaDataDispenser_FindAssemblyModule(IMetaDataDispenserEx* LPCWSTR szAppBase, LPCWSTR szPrivateBin, LPCWSTR szGlobalBin, LPCWSTR szAssemblyName, LPCWSTR szModuleName, LPWSTR szName, ULONG cchName, ULONG *pcName) { - FIXME("%p %s %s %s %s %s %p %u %p\n", iface, debugstr_w(szAppBase), + FIXME("%p %s %s %s %s %s %p %lu %p\n", iface, debugstr_w(szAppBase), debugstr_w(szPrivateBin), debugstr_w(szGlobalBin), debugstr_w(szAssemblyName), debugstr_w(szModuleName), szName, cchName, pcName); return E_NOTIMPL; @@ -170,7 +190,7 @@ HRESULT MetaDataDispenser_CreateInstance(IUnknown **ppUnk) { MetaDataDispenser *This; - This = HeapAlloc(GetProcessHeap(), 0, sizeof(MetaDataDispenser)); + This = malloc(sizeof(MetaDataDispenser)); if (!This) return E_OUTOFMEMORY; @@ -178,7 +198,7 @@ HRESULT MetaDataDispenser_CreateInstance(IUnknown **ppUnk) This->IMetaDataDispenserEx_iface.lpVtbl = &MetaDataDispenserVtbl; This->ref = 1; - *ppUnk = (IUnknown*)This; + *ppUnk = (IUnknown*)&This->IMetaDataDispenserEx_iface; return S_OK; } diff --git a/dll/win32/mscoree/metahost.c b/dll/win32/mscoree/metahost.c index a405a121d2d..8d5cbb350cd 100644 --- a/dll/win32/mscoree/metahost.c +++ b/dll/win32/mscoree/metahost.c @@ -18,31 +18,44 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ -#include "mscoree_private.h" - #include +#include #include -#include +#define COBJMACROS -#include +#include "windef.h" +#include "winbase.h" +#include "winreg.h" +#include "winternl.h" +#include "ole2.h" +#include "shlwapi.h" -static const WCHAR net_11_subdir[] = {'1','.','0',0}; -static const WCHAR net_20_subdir[] = {'2','.','0',0}; -static const WCHAR net_40_subdir[] = {'4','.','0',0}; +#include "corerror.h" +#include "cor.h" +#include "mscoree.h" +#include "corhdr.h" +#include "cordebug.h" +#include "metahost.h" +#include "fusion.h" +#include "wine/list.h" +#include "mscoree_private.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL( mscoree ); static const struct ICLRRuntimeInfoVtbl CLRRuntimeInfoVtbl; -#define NUM_RUNTIMES 3 +#define NUM_RUNTIMES 4 static struct CLRRuntimeInfo runtimes[NUM_RUNTIMES] = { - {{&CLRRuntimeInfoVtbl}, net_11_subdir, 1, 1, 4322, 0}, - {{&CLRRuntimeInfoVtbl}, net_20_subdir, 2, 0, 50727, 0}, - {{&CLRRuntimeInfoVtbl}, net_40_subdir, 4, 0, 30319, 0} + {{&CLRRuntimeInfoVtbl}, 1, 0, 3705, 0}, + {{&CLRRuntimeInfoVtbl}, 1, 1, 4322, 0}, + {{&CLRRuntimeInfoVtbl}, 2, 0, 50727, 0}, + {{&CLRRuntimeInfoVtbl}, 4, 0, 30319, 0} }; -static int runtimes_initialized; - static CRITICAL_SECTION runtime_list_cs; static CRITICAL_SECTION_DEBUG runtime_list_cs_debug = { @@ -53,182 +66,276 @@ static CRITICAL_SECTION_DEBUG runtime_list_cs_debug = }; static CRITICAL_SECTION runtime_list_cs = { &runtime_list_cs_debug, -1, 0, 0, 0, 0 }; -#define NUM_ABI_VERSIONS 2 - -static loaded_mono loaded_monos[NUM_ABI_VERSIONS]; - -static BOOL find_mono_dll(LPCWSTR path, LPWSTR dll_path, int abi_version); - -static MonoAssembly* mono_assembly_search_hook_fn(MonoAssemblyName *aname, char **assemblies_path, void *user_data); - -static void mono_shutdown_callback_fn(MonoProfiler *prof); - -static void set_environment(LPCWSTR bin_path) +struct CLRMetaHost { - WCHAR path_env[MAX_PATH]; - int len; + ICLRMetaHost ICLRMetaHost_iface; - static const WCHAR pathW[] = {'P','A','T','H',0}; + RuntimeLoadedCallbackFnPtr callback; +}; - /* We have to modify PATH as Mono loads other DLLs from this directory. */ - GetEnvironmentVariableW(pathW, path_env, sizeof(path_env)/sizeof(WCHAR)); - len = strlenW(path_env); - path_env[len++] = ';'; - strcpyW(path_env+len, bin_path); - SetEnvironmentVariableW(pathW, path_env); +static struct CLRMetaHost GlobalCLRMetaHost; + +static HMODULE mono_handle; + +BOOL is_mono_started; +static BOOL is_mono_shutdown; + +typedef struct _MonoProfilerDesc *MonoProfilerHandle; + +typedef void (CDECL *MonoProfilerRuntimeShutdownBeginCallback) (MonoProfiler *prof); + +MonoImage* (CDECL *mono_assembly_get_image)(MonoAssembly *assembly); +MonoAssembly* (CDECL *mono_assembly_load_from)(MonoImage *image, const char *fname, MonoImageOpenStatus *status); +const char* (CDECL *mono_assembly_name_get_name)(MonoAssemblyName *aname); +const char* (CDECL *mono_assembly_name_get_culture)(MonoAssemblyName *aname); +WORD (CDECL *mono_assembly_name_get_version)(MonoAssemblyName *aname, WORD *minor, WORD *build, WORD *revision); +MonoAssembly* (CDECL *mono_assembly_open)(const char *filename, MonoImageOpenStatus *status); +void (CDECL *mono_callspec_set_assembly)(MonoAssembly *assembly); +MonoClass* (CDECL *mono_class_from_mono_type)(MonoType *type); +MonoClass* (CDECL *mono_class_from_name)(MonoImage *image, const char* name_space, const char *name); +MonoMethod* (CDECL *mono_class_get_method_from_name)(MonoClass *klass, const char *name, int param_count); +static void (CDECL *mono_config_parse)(const char *filename); +MonoDomain* (CDECL *mono_domain_get)(void); +MonoDomain* (CDECL *mono_domain_get_by_id)(int id); +BOOL (CDECL *mono_domain_set)(MonoDomain *domain,BOOL force); +void (CDECL *mono_domain_set_config)(MonoDomain *domain,const char *base_dir,const char *config_file_name); +static void (CDECL *mono_free)(void *); +MonoImage* (CDECL *mono_get_corlib)(void); +static MonoImage* (CDECL *mono_image_open)(const char *fname, MonoImageOpenStatus *status); +MonoImage* (CDECL *mono_image_open_from_module_handle)(HMODULE module_handle, char* fname, UINT has_entry_point, MonoImageOpenStatus* status); +static void (CDECL *mono_install_assembly_preload_hook)(MonoAssemblyPreLoadFunc func, void *user_data); +int (CDECL *mono_jit_exec)(MonoDomain *domain, MonoAssembly *assembly, int argc, char *argv[]); +MonoDomain* (CDECL *mono_jit_init_version)(const char *domain_name, const char *runtime_version); +static void (CDECL *mono_jit_set_aot_mode)(MonoAotMode mode); +static int (CDECL *mono_jit_set_trace_options)(const char* options); +void* (CDECL *mono_marshal_get_vtfixup_ftnptr)(MonoImage *image, DWORD token, WORD type); +MonoDomain* (CDECL *mono_object_get_domain)(MonoObject *obj); +MonoMethod* (CDECL *mono_object_get_virtual_method)(MonoObject *obj, MonoMethod *method); +MonoObject* (CDECL *mono_object_new)(MonoDomain *domain, MonoClass *klass); +void* (CDECL *mono_object_unbox)(MonoObject *obj); +static MonoProfilerHandle (CDECL *mono_profiler_create)(MonoProfiler *prof); +static void (CDECL *mono_profiler_install)(MonoProfiler *prof, MonoProfileFunc shutdown_callback); +static void (CDECL *mono_profiler_set_runtime_shutdown_begin_callback)(MonoProfilerHandle handle, MonoProfilerRuntimeShutdownBeginCallback cb); +MonoType* (CDECL *mono_reflection_type_from_name)(char *name, MonoImage *image); +MonoObject* (CDECL *mono_runtime_invoke)(MonoMethod *method, void *obj, void **params, MonoObject **exc); +void (CDECL *mono_runtime_object_init)(MonoObject *this_obj); +void (CDECL *mono_runtime_quit)(void); +static void (CDECL *mono_set_crash_chaining)(BOOL chain_signals); +static void (CDECL *mono_set_dirs)(const char *assembly_dir, const char *config_dir); +static void (CDECL *mono_set_verbose_level)(DWORD level); +MonoString* (CDECL *mono_string_new)(MonoDomain *domain, const char *str); +static char* (CDECL *mono_stringify_assembly_name)(MonoAssemblyName *aname); +MonoThread* (CDECL *mono_thread_attach)(MonoDomain *domain); +void (CDECL *mono_thread_manage)(void); +void (CDECL *mono_trace_set_print_handler)(MonoPrintCallback callback); +void (CDECL *mono_trace_set_printerr_handler)(MonoPrintCallback callback); +void (CDECL *mono_trace_set_log_handler)(MonoLogCallback callback, void *user_data); +static MonoAssembly* (CDECL *wine_mono_assembly_load_from_gac)(MonoAssemblyName *aname, MonoImageOpenStatus *status, int refonly); +static void (CDECL *wine_mono_install_assembly_preload_hook)(WineMonoAssemblyPreLoadFunc func, void *user_data); +static void (CDECL *wine_mono_install_assembly_preload_hook_v2)(WineMonoAssemblyPreLoadFunc func, void *user_data); + +static BOOL find_mono_dll(LPCWSTR path, LPWSTR dll_path); + +static MonoAssembly* CDECL mono_assembly_preload_hook_fn(MonoAssemblyName *aname, char **assemblies_path, void *user_data); +static MonoAssembly* CDECL wine_mono_assembly_preload_hook_fn(MonoAssemblyName *aname, char **assemblies_path, int *search_path, void *user_data); +static MonoAssembly* CDECL wine_mono_assembly_preload_hook_v2_fn(MonoAssemblyName *aname, char **assemblies_path, int *flags, void *user_data); + +static void CDECL mono_shutdown_callback_fn(MonoProfiler *prof); + +static MonoImage* CDECL image_open_module_handle_dummy(HMODULE module_handle, + char* fname, UINT has_entry_point, MonoImageOpenStatus* status) +{ + return mono_image_open(fname, status); } -static void CDECL do_nothing(void) +static void CDECL set_crash_chaining_dummy(BOOL crash_chaining) { } -#ifdef __REACTOS__ -int WINAPIV ShellMessageBoxA(HINSTANCE hAppInst, HWND hWnd, LPCSTR lpcText, LPCSTR lpcTitle, UINT fuStyle, ...); -#undef MESSAGE -#define MESSAGE(msg) \ -do { \ - WINE_MESSAGE((msg)); \ - ShellMessageBoxA(NULL, NULL, (msg), "Wine Mono", MB_OK | MB_ICONSTOP); \ -} while(0) -// -// NOTE for wine-syncs: This warning is gradually removed in Wine commits: -// c99754ef15a8, 6b889fe9188a, 5cd6db03495d, and 26c9bd9f15c3 -// -#endif -static void missing_runtime_message(const CLRRuntimeInfo *This) +static void CDECL set_print_handler_dummy(MonoPrintCallback callback) { - if (This->major == 1) - MESSAGE("wine: Install Mono 2.6 for Windows to run .NET 1.1 applications.\n"); - else if (This->major == 2) - MESSAGE("wine: Install Mono for Windows to run .NET 2.0 applications.\n"); - else if (This->major == 4) - MESSAGE("wine: Install Mono 2.8 or greater for Windows to run .NET 4.0 applications.\n"); } -static HRESULT load_mono(CLRRuntimeInfo *This, loaded_mono **result) +static void CDECL set_log_handler_dummy (MonoLogCallback callback, void *user_data) +{ +} + +static HRESULT load_mono(LPCWSTR mono_path) { - static const WCHAR bin[] = {'\\','b','i','n',0}; static const WCHAR lib[] = {'\\','l','i','b',0}; static const WCHAR etc[] = {'\\','e','t','c',0}; - static const WCHAR glibdll[] = {'l','i','b','g','l','i','b','-','2','.','0','-','0','.','d','l','l',0}; - WCHAR mono_dll_path[MAX_PATH+16], mono_bin_path[MAX_PATH+4]; + WCHAR mono_dll_path[MAX_PATH+16]; WCHAR mono_lib_path[MAX_PATH+4], mono_etc_path[MAX_PATH+4]; char mono_lib_path_a[MAX_PATH], mono_etc_path_a[MAX_PATH]; + int aot_size; + char aot_setting[256]; int trace_size; char trace_setting[256]; + int verbose_size; + char verbose_setting[256]; - if (This->mono_abi_version <= 0 || This->mono_abi_version > NUM_ABI_VERSIONS) - { - missing_runtime_message(This); - return E_FAIL; - } - - *result = &loaded_monos[This->mono_abi_version-1]; - - if ((*result)->is_shutdown) + if (is_mono_shutdown) { ERR("Cannot load Mono after it has been shut down.\n"); - *result = NULL; return E_FAIL; } - if (!(*result)->mono_handle) + if (!mono_handle) { - strcpyW(mono_bin_path, This->mono_path); - strcatW(mono_bin_path, bin); - set_environment(mono_bin_path); - - strcpyW(mono_lib_path, This->mono_path); - strcatW(mono_lib_path, lib); + lstrcpyW(mono_lib_path, mono_path); + lstrcatW(mono_lib_path, lib); WideCharToMultiByte(CP_UTF8, 0, mono_lib_path, -1, mono_lib_path_a, MAX_PATH, NULL, NULL); - strcpyW(mono_etc_path, This->mono_path); - strcatW(mono_etc_path, etc); + lstrcpyW(mono_etc_path, mono_path); + lstrcatW(mono_etc_path, etc); WideCharToMultiByte(CP_UTF8, 0, mono_etc_path, -1, mono_etc_path_a, MAX_PATH, NULL, NULL); - if (!find_mono_dll(This->mono_path, mono_dll_path, This->mono_abi_version)) goto fail; + if (!find_mono_dll(mono_path, mono_dll_path)) goto fail; - (*result)->mono_handle = LoadLibraryW(mono_dll_path); + mono_handle = LoadLibraryW(mono_dll_path); - if (!(*result)->mono_handle) goto fail; + if (!mono_handle) goto fail; #define LOAD_MONO_FUNCTION(x) do { \ - (*result)->x = (void*)GetProcAddress((*result)->mono_handle, #x); \ - if (!(*result)->x) { \ + x = (void*)GetProcAddress(mono_handle, #x); \ + if (!x) { \ goto fail; \ } \ } while (0); LOAD_MONO_FUNCTION(mono_assembly_get_image); LOAD_MONO_FUNCTION(mono_assembly_load_from); + LOAD_MONO_FUNCTION(mono_assembly_name_get_name); + LOAD_MONO_FUNCTION(mono_assembly_name_get_culture); + LOAD_MONO_FUNCTION(mono_assembly_name_get_version); LOAD_MONO_FUNCTION(mono_assembly_open); LOAD_MONO_FUNCTION(mono_config_parse); LOAD_MONO_FUNCTION(mono_class_from_mono_type); LOAD_MONO_FUNCTION(mono_class_from_name); LOAD_MONO_FUNCTION(mono_class_get_method_from_name); - LOAD_MONO_FUNCTION(mono_domain_assembly_open); - LOAD_MONO_FUNCTION(mono_image_open_from_module_handle); + LOAD_MONO_FUNCTION(mono_domain_get); + LOAD_MONO_FUNCTION(mono_domain_get_by_id); + LOAD_MONO_FUNCTION(mono_domain_set); + LOAD_MONO_FUNCTION(mono_domain_set_config); + LOAD_MONO_FUNCTION(mono_free); + LOAD_MONO_FUNCTION(mono_get_corlib); + LOAD_MONO_FUNCTION(mono_image_open); LOAD_MONO_FUNCTION(mono_install_assembly_preload_hook); LOAD_MONO_FUNCTION(mono_jit_exec); - LOAD_MONO_FUNCTION(mono_jit_init); + LOAD_MONO_FUNCTION(mono_jit_init_version); LOAD_MONO_FUNCTION(mono_jit_set_trace_options); LOAD_MONO_FUNCTION(mono_marshal_get_vtfixup_ftnptr); LOAD_MONO_FUNCTION(mono_object_get_domain); + LOAD_MONO_FUNCTION(mono_object_get_virtual_method); LOAD_MONO_FUNCTION(mono_object_new); LOAD_MONO_FUNCTION(mono_object_unbox); - LOAD_MONO_FUNCTION(mono_profiler_install); LOAD_MONO_FUNCTION(mono_reflection_type_from_name); LOAD_MONO_FUNCTION(mono_runtime_invoke); LOAD_MONO_FUNCTION(mono_runtime_object_init); LOAD_MONO_FUNCTION(mono_runtime_quit); LOAD_MONO_FUNCTION(mono_set_dirs); + LOAD_MONO_FUNCTION(mono_set_verbose_level); LOAD_MONO_FUNCTION(mono_stringify_assembly_name); LOAD_MONO_FUNCTION(mono_string_new); LOAD_MONO_FUNCTION(mono_thread_attach); - - /* GLib imports obsoleted by the 2.0 ABI */ - if (This->mono_abi_version == 1) - { - (*result)->glib_handle = LoadLibraryW(glibdll); - if (!(*result)->glib_handle) goto fail; - - (*result)->mono_free = (void*)GetProcAddress((*result)->glib_handle, "g_free"); - if (!(*result)->mono_free) goto fail; - } - else - { - LOAD_MONO_FUNCTION(mono_free); - } + LOAD_MONO_FUNCTION(mono_thread_manage); #undef LOAD_MONO_FUNCTION -#define LOAD_OPT_VOID_MONO_FUNCTION(x) do { \ - (*result)->x = (void*)GetProcAddress((*result)->mono_handle, #x); \ - if (!(*result)->x) { \ - (*result)->x = do_nothing; \ +#define LOAD_OPT_MONO_FUNCTION(x, default) do { \ + x = (void*)GetProcAddress(mono_handle, #x); \ + if (!x) { \ + x = default; \ } \ } while (0); - LOAD_OPT_VOID_MONO_FUNCTION(mono_runtime_set_shutting_down); - LOAD_OPT_VOID_MONO_FUNCTION(mono_thread_pool_cleanup); - LOAD_OPT_VOID_MONO_FUNCTION(mono_thread_suspend_all_other_threads); - LOAD_OPT_VOID_MONO_FUNCTION(mono_threads_set_shutting_down); + LOAD_OPT_MONO_FUNCTION(mono_callspec_set_assembly, NULL); + LOAD_OPT_MONO_FUNCTION(mono_image_open_from_module_handle, image_open_module_handle_dummy); + LOAD_OPT_MONO_FUNCTION(mono_jit_set_aot_mode, NULL); + LOAD_OPT_MONO_FUNCTION(mono_profiler_create, NULL); + LOAD_OPT_MONO_FUNCTION(mono_profiler_install, NULL); + LOAD_OPT_MONO_FUNCTION(mono_profiler_set_runtime_shutdown_begin_callback, NULL); + LOAD_OPT_MONO_FUNCTION(mono_set_crash_chaining, set_crash_chaining_dummy); + LOAD_OPT_MONO_FUNCTION(mono_trace_set_print_handler, set_print_handler_dummy); + LOAD_OPT_MONO_FUNCTION(mono_trace_set_printerr_handler, set_print_handler_dummy); + LOAD_OPT_MONO_FUNCTION(mono_trace_set_log_handler, set_log_handler_dummy); + LOAD_OPT_MONO_FUNCTION(wine_mono_assembly_load_from_gac, NULL); + LOAD_OPT_MONO_FUNCTION(wine_mono_install_assembly_preload_hook, NULL); + LOAD_OPT_MONO_FUNCTION(wine_mono_install_assembly_preload_hook_v2, NULL); -#undef LOAD_OPT_VOID_MONO_FUNCTION +#undef LOAD_OPT_MONO_FUNCTION - (*result)->mono_profiler_install((MonoProfiler*)*result, mono_shutdown_callback_fn); + if (mono_callspec_set_assembly == NULL) + { + mono_callspec_set_assembly = (void*)GetProcAddress(mono_handle, "mono_trace_set_assembly"); + if (!mono_callspec_set_assembly) goto fail; + } - (*result)->mono_set_dirs(mono_lib_path_a, mono_etc_path_a); + if (mono_profiler_create != NULL) + { + /* Profiler API v2 */ + MonoProfilerHandle handle = mono_profiler_create(NULL); + mono_profiler_set_runtime_shutdown_begin_callback(handle, mono_shutdown_callback_fn); + } + else if (mono_profiler_install != NULL) + { + /* Profiler API v1 */ + mono_profiler_install(NULL, mono_shutdown_callback_fn); + } - (*result)->mono_config_parse(NULL); + mono_set_crash_chaining(TRUE); - (*result)->mono_install_assembly_preload_hook(mono_assembly_search_hook_fn, *result); + mono_trace_set_print_handler(mono_print_handler_fn); + mono_trace_set_printerr_handler(mono_print_handler_fn); + mono_trace_set_log_handler(mono_log_handler_fn, NULL); + + mono_set_dirs(mono_lib_path_a, mono_etc_path_a); + + mono_config_parse(NULL); + + if (wine_mono_install_assembly_preload_hook_v2) + wine_mono_install_assembly_preload_hook_v2(wine_mono_assembly_preload_hook_v2_fn, NULL); + else if (wine_mono_install_assembly_preload_hook) + wine_mono_install_assembly_preload_hook(wine_mono_assembly_preload_hook_fn, NULL); + else + mono_install_assembly_preload_hook(mono_assembly_preload_hook_fn, NULL); + + aot_size = GetEnvironmentVariableA("WINE_MONO_AOT", aot_setting, sizeof(aot_setting)); + + if (aot_size) + { + MonoAotMode mode; + if (strcmp(aot_setting, "interp") == 0) + mode = MONO_AOT_MODE_INTERP_ONLY; + else if (strcmp(aot_setting, "none") == 0) + mode = MONO_AOT_MODE_NONE; + else + { + ERR("unknown WINE_MONO_AOT setting, valid settings are interp and none\n"); + mode = MONO_AOT_MODE_NONE; + } + if (mono_jit_set_aot_mode != NULL) + { + mono_jit_set_aot_mode(mode); + } + else + { + ERR("mono_jit_set_aot_mode export not found\n"); + } + } trace_size = GetEnvironmentVariableA("WINE_MONO_TRACE", trace_setting, sizeof(trace_setting)); if (trace_size) { - (*result)->mono_jit_set_trace_options(trace_setting); + mono_jit_set_trace_options(trace_setting); + } + + verbose_size = GetEnvironmentVariableA("WINE_MONO_VERBOSE", verbose_setting, sizeof(verbose_setting)); + + if (verbose_size) + { + mono_set_verbose_level(verbose_setting[0] - '0'); } } @@ -236,24 +343,74 @@ static HRESULT load_mono(CLRRuntimeInfo *This, loaded_mono **result) fail: ERR("Could not load Mono into this process\n"); - FreeLibrary((*result)->mono_handle); - FreeLibrary((*result)->glib_handle); - (*result)->mono_handle = NULL; - (*result)->glib_handle = NULL; + FreeLibrary(mono_handle); + mono_handle = NULL; return E_FAIL; } -static void mono_shutdown_callback_fn(MonoProfiler *prof) +static char* get_exe_basename_utf8(void) { - loaded_mono *mono = (loaded_mono*)prof; + WCHAR filenameW[MAX_PATH], *basenameW; - mono->is_shutdown = TRUE; + GetModuleFileNameW(NULL, filenameW, MAX_PATH); + + basenameW = wcsrchr(filenameW, '\\'); + if (basenameW) + basenameW += 1; + else + basenameW = filenameW; + + return WtoA(basenameW); +} + +MonoDomain* get_root_domain(void) +{ + static MonoDomain* root_domain; + + if (root_domain != NULL) + return root_domain; + + EnterCriticalSection(&runtime_list_cs); + + if (root_domain == NULL) + { + char *exe_basename; + + exe_basename = get_exe_basename_utf8(); + + root_domain = mono_jit_init_version(exe_basename, "v4.0.30319"); + + free(exe_basename); + + is_mono_started = TRUE; + } + + LeaveCriticalSection(&runtime_list_cs); + + return root_domain; +} + +static void CDECL mono_shutdown_callback_fn(MonoProfiler *prof) +{ + is_mono_shutdown = TRUE; +} + +static HRESULT WINAPI thread_set_fn(void) +{ + WARN("stub\n"); + return S_OK; +} + +static HRESULT WINAPI thread_unset_fn(void) +{ + WARN("stub\n"); + return S_OK; } static HRESULT CLRRuntimeInfo_GetRuntimeHost(CLRRuntimeInfo *This, RuntimeHost **result) { HRESULT hr = S_OK; - loaded_mono *ploaded_mono; + WCHAR mono_path[MAX_PATH]; if (This->loaded_runtime) { @@ -261,12 +418,30 @@ static HRESULT CLRRuntimeInfo_GetRuntimeHost(CLRRuntimeInfo *This, RuntimeHost * return hr; } + if (!get_mono_path(mono_path, FALSE)) + { + ERR("Wine Mono is not installed\n"); + return CLR_E_SHIM_RUNTIME; + } + EnterCriticalSection(&runtime_list_cs); - hr = load_mono(This, &ploaded_mono); + if (This->loaded_runtime) + { + *result = This->loaded_runtime; + LeaveCriticalSection(&runtime_list_cs); + return hr; + } + + if (GlobalCLRMetaHost.callback) + { + GlobalCLRMetaHost.callback(&This->ICLRRuntimeInfo_iface, thread_set_fn, thread_unset_fn); + } + + hr = load_mono(mono_path); if (SUCCEEDED(hr)) - hr = RuntimeHost_Construct(This, ploaded_mono, &This->loaded_runtime); + hr = RuntimeHost_Construct(This, &This->loaded_runtime); LeaveCriticalSection(&runtime_list_cs); @@ -276,41 +451,12 @@ static HRESULT CLRRuntimeInfo_GetRuntimeHost(CLRRuntimeInfo *This, RuntimeHost * return hr; } -void unload_all_runtimes(void) -{ - int i; - - for (i=0; imono_handle && mono->is_started && !mono->is_shutdown) - { - /* Copied from Mono's ves_icall_System_Environment_Exit */ - mono->mono_threads_set_shutting_down(); - mono->mono_runtime_set_shutting_down(); - mono->mono_thread_pool_cleanup(); - mono->mono_thread_suspend_all_other_threads(); - mono->mono_runtime_quit(); - } - } - - for (i=0; imono_handle && mono->is_started && !mono->is_shutdown) - { - ERR("Process exited with a Mono runtime loaded.\n"); - return; - } + ERR("Process exited with a Mono runtime loaded.\n"); + return; } } @@ -362,7 +508,7 @@ static HRESULT WINAPI CLRRuntimeInfo_GetVersionString(ICLRRuntimeInfo* iface, TRACE("%p %p %p\n", iface, pwzBuffer, pcchBuffer); - size = snprintf(version, sizeof(version), "v%u.%u.%u", This->major, This->minor, This->build); + size = snprintf(version, sizeof(version), "v%lu.%lu.%lu", This->major, This->minor, This->build); assert(size <= sizeof(version)); @@ -373,7 +519,7 @@ static HRESULT WINAPI CLRRuntimeInfo_GetVersionString(ICLRRuntimeInfo* iface, if (buffer_size >= *pcchBuffer) MultiByteToWideChar(CP_UTF8, 0, version, -1, pwzBuffer, buffer_size); else - hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + hr = E_NOT_SUFFICIENT_BUFFER; } return hr; @@ -381,8 +527,8 @@ static HRESULT WINAPI CLRRuntimeInfo_GetVersionString(ICLRRuntimeInfo* iface, static BOOL get_install_root(LPWSTR install_dir) { - 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}; - const WCHAR install_root[] = {'I','n','s','t','a','l','l','R','o','o','t',0}; + static 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}; + static const WCHAR install_root[] = {'I','n','s','t','a','l','l','R','o','o','t',0}; DWORD len; HKEY key; @@ -432,9 +578,9 @@ static HRESULT WINAPI CLRRuntimeInfo_GetRuntimeDirectory(ICLRRuntimeInfo* iface, if (pwzBuffer) { if (buffer_size >= size) - strcpyW(pwzBuffer, system_dir); + lstrcpyW(pwzBuffer, system_dir); else - hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + hr = E_NOT_SUFFICIENT_BUFFER; } return hr; @@ -451,7 +597,7 @@ static HRESULT WINAPI CLRRuntimeInfo_IsLoaded(ICLRRuntimeInfo* iface, static HRESULT WINAPI CLRRuntimeInfo_LoadErrorString(ICLRRuntimeInfo* iface, UINT iResourceID, LPWSTR pwzBuffer, DWORD *pcchBuffer, LONG iLocaleid) { - FIXME("%p %u %p %p %x\n", iface, iResourceID, pwzBuffer, pcchBuffer, iLocaleid); + FIXME("%p %u %p %p %lx\n", iface, iResourceID, pwzBuffer, pcchBuffer, iLocaleid); return E_NOTIMPL; } @@ -508,7 +654,7 @@ static HRESULT WINAPI CLRRuntimeInfo_IsLoadable(ICLRRuntimeInfo* iface, static HRESULT WINAPI CLRRuntimeInfo_SetDefaultStartupFlags(ICLRRuntimeInfo* iface, DWORD dwStartupFlags, LPCWSTR pwzHostConfigFile) { - FIXME("%p %x %s\n", iface, dwStartupFlags, debugstr_w(pwzHostConfigFile)); + FIXME("%p %lx %s\n", iface, dwStartupFlags, debugstr_w(pwzHostConfigFile)); return E_NOTIMPL; } @@ -571,216 +717,175 @@ static const WCHAR libmono2_arch_dll[] = {'\\','b','i','n','\\','l','i','b','m', static const WCHAR libmono2_arch_dll[] = {'\\','b','i','n','\\','l','i','b','m','o','n','o','-','2','.','0','.','d','l','l',0}; #endif -static BOOL find_mono_dll(LPCWSTR path, LPWSTR dll_path, int abi_version) +static BOOL find_mono_dll(LPCWSTR path, LPWSTR dll_path) { - static const WCHAR mono_dll[] = {'\\','b','i','n','\\','m','o','n','o','.','d','l','l',0}; - static const WCHAR libmono_dll[] = {'\\','b','i','n','\\','l','i','b','m','o','n','o','.','d','l','l',0}; static const WCHAR mono2_dll[] = {'\\','b','i','n','\\','m','o','n','o','-','2','.','0','.','d','l','l',0}; static const WCHAR libmono2_dll[] = {'\\','b','i','n','\\','l','i','b','m','o','n','o','-','2','.','0','.','d','l','l',0}; DWORD attributes=INVALID_FILE_ATTRIBUTES; - if (abi_version == 1) - { - strcpyW(dll_path, path); - strcatW(dll_path, mono_dll); - attributes = GetFileAttributesW(dll_path); + lstrcpyW(dll_path, path); + lstrcatW(dll_path, libmono2_arch_dll); + attributes = GetFileAttributesW(dll_path); - if (attributes == INVALID_FILE_ATTRIBUTES) - { - strcpyW(dll_path, path); - strcatW(dll_path, libmono_dll); - attributes = GetFileAttributesW(dll_path); - } + if (attributes == INVALID_FILE_ATTRIBUTES) + { + lstrcpyW(dll_path, path); + lstrcatW(dll_path, mono2_dll); + attributes = GetFileAttributesW(dll_path); } - else if (abi_version == 2) + + if (attributes == INVALID_FILE_ATTRIBUTES) { - strcpyW(dll_path, path); - strcatW(dll_path, libmono2_arch_dll); + lstrcpyW(dll_path, path); + lstrcatW(dll_path, libmono2_dll); attributes = GetFileAttributesW(dll_path); - - if (attributes == INVALID_FILE_ATTRIBUTES) - { - strcpyW(dll_path, path); - strcatW(dll_path, mono2_dll); - attributes = GetFileAttributesW(dll_path); - } - - if (attributes == INVALID_FILE_ATTRIBUTES) - { - strcpyW(dll_path, path); - strcatW(dll_path, libmono2_dll); - attributes = GetFileAttributesW(dll_path); - } } return (attributes != INVALID_FILE_ATTRIBUTES); } -static BOOL get_mono_path_from_registry(LPWSTR path, int abi_version) +static BOOL get_mono_path_local(LPWSTR path) { - static const WCHAR mono_key[] = {'S','o','f','t','w','a','r','e','\\','N','o','v','e','l','l','\\','M','o','n','o',0}; - static const WCHAR defaul_clr[] = {'D','e','f','a','u','l','t','C','L','R',0}; - static const WCHAR install_root[] = {'S','d','k','I','n','s','t','a','l','l','R','o','o','t',0}; - static const WCHAR slash[] = {'\\',0}; + static const WCHAR subdir_mono[] = {'\\','m','o','n','o','\\','m','o','n','o','-','2','.','0', 0}; + WCHAR base_path[MAX_PATH], mono_dll_path[MAX_PATH]; - WCHAR version[64], version_key[MAX_PATH]; - DWORD len; - HKEY key; - WCHAR dll_path[MAX_PATH]; - - if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, mono_key, 0, KEY_READ, &key)) - return FALSE; - - len = sizeof(version); - if (RegQueryValueExW(key, defaul_clr, 0, NULL, (LPBYTE)version, &len)) - { - RegCloseKey(key); - return FALSE; - } - RegCloseKey(key); - - lstrcpyW(version_key, mono_key); - lstrcatW(version_key, slash); - lstrcatW(version_key, version); - - if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, version_key, 0, KEY_READ, &key)) - return FALSE; - - len = sizeof(WCHAR) * MAX_PATH; - if (RegQueryValueExW(key, install_root, 0, NULL, (LPBYTE)path, &len)) - { - RegCloseKey(key); - return FALSE; - } - RegCloseKey(key); - - return find_mono_dll(path, dll_path, abi_version); -} - -static BOOL get_mono_path_from_folder(LPCWSTR folder, LPWSTR mono_path, int abi_version) -{ - static const WCHAR mono_one_dot_zero[] = {'\\','m','o','n','o','-','1','.','0', 0}; - static const WCHAR mono_two_dot_zero[] = {'\\','m','o','n','o','-','2','.','0', 0}; - WCHAR mono_dll_path[MAX_PATH]; - BOOL found = FALSE; - - strcpyW(mono_path, folder); - - if (abi_version == 1) - strcatW(mono_path, mono_one_dot_zero); - else if (abi_version == 2) - strcatW(mono_path, mono_two_dot_zero); - - found = find_mono_dll(mono_path, mono_dll_path, abi_version); - - return found; -} - -static BOOL get_mono_path(LPWSTR path, int abi_version) -{ - static const WCHAR subdir_mono[] = {'\\','m','o','n','o',0}; - static const WCHAR sibling_mono[] = {'\\','.','.','\\','m','o','n','o',0}; - WCHAR base_path[MAX_PATH]; - const char *unix_data_dir; - WCHAR *dos_data_dir; - int build_tree=0; - static WCHAR* (CDECL *wine_get_dos_file_name)(const char*); - - /* First try c:\windows\mono */ + /* c:\windows\mono\mono-2.0 */ GetWindowsDirectoryW(base_path, MAX_PATH); - strcatW(base_path, subdir_mono); + lstrcatW(base_path, subdir_mono); - if (get_mono_path_from_folder(base_path, path, abi_version)) + if (find_mono_dll(base_path, mono_dll_path)) + { + lstrcpyW(path, base_path); return TRUE; - - /* Next: /usr/share/wine/mono */ - unix_data_dir = wine_get_data_dir(); - - if (!unix_data_dir) - { - unix_data_dir = wine_get_build_dir(); - build_tree = 1; } - if (unix_data_dir) - { - if (!wine_get_dos_file_name) - wine_get_dos_file_name = (void*)GetProcAddress(GetModuleHandleA("kernel32"), "wine_get_dos_file_name"); - - if (wine_get_dos_file_name) - { - dos_data_dir = wine_get_dos_file_name(unix_data_dir); - - if (dos_data_dir) - { - strcpyW(base_path, dos_data_dir); - strcatW(base_path, build_tree ? sibling_mono : subdir_mono); - - HeapFree(GetProcessHeap(), 0, dos_data_dir); - - if (get_mono_path_from_folder(base_path, path, abi_version)) - return TRUE; - } - } - } - - /* Last: the registry */ - return get_mono_path_from_registry(path, abi_version); + return FALSE; } -static void find_runtimes(void) +static BOOL get_mono_path_registry(LPWSTR path) { - int abi_version, i; - static const WCHAR libmono[] = {'\\','l','i','b','\\','m','o','n','o','\\',0}; - static const WCHAR mscorlib[] = {'\\','m','s','c','o','r','l','i','b','.','d','l','l',0}; - WCHAR mono_path[MAX_PATH], lib_path[MAX_PATH]; - BOOL any_runtimes_found = FALSE; + static const WCHAR keyname[] = {'S','o','f','t','w','a','r','e','\\','W','i','n','e','\\','M','o','n','o',0}; + static const WCHAR valuename[] = {'R','u','n','t','i','m','e','P','a','t','h',0}; + WCHAR base_path[MAX_PATH], mono_dll_path[MAX_PATH]; + HKEY hkey; + DWORD res, valuesize; + BOOL ret=FALSE; - if (runtimes_initialized) return; + /* @@ Wine registry key: HKCU\Software\Wine\Mono */ + res = RegOpenKeyW(HKEY_CURRENT_USER, keyname, &hkey); + if (res != ERROR_SUCCESS) + return FALSE; - EnterCriticalSection(&runtime_list_cs); - - if (runtimes_initialized) goto end; - - for (abi_version=NUM_ABI_VERSIONS; abi_version>0; abi_version--) + valuesize = sizeof(base_path); + res = RegGetValueW(hkey, NULL, valuename, RRF_RT_REG_SZ, NULL, base_path, &valuesize); + if (res == ERROR_SUCCESS && find_mono_dll(base_path, mono_dll_path)) { - if (!get_mono_path(mono_path, abi_version)) - continue; - - for (i=0; iref); - TRACE("(%p) refcount=%u\n", iface, ref); + TRACE("(%p) refcount=%lu\n", iface, ref); return ref; } @@ -833,11 +938,11 @@ static ULONG WINAPI InstalledRuntimeEnum_Release(IEnumUnknown* iface) struct InstalledRuntimeEnum *This = impl_from_IEnumUnknown(iface); ULONG ref = InterlockedDecrement(&This->ref); - TRACE("(%p) refcount=%u\n", iface, ref); + TRACE("(%p) refcount=%lu\n", iface, ref); if (ref == 0) { - HeapFree(GetProcessHeap(), 0, This); + free(This); } return ref; @@ -847,11 +952,11 @@ static HRESULT WINAPI InstalledRuntimeEnum_Next(IEnumUnknown *iface, ULONG celt, IUnknown **rgelt, ULONG *pceltFetched) { struct InstalledRuntimeEnum *This = impl_from_IEnumUnknown(iface); - int num_fetched = 0; + ULONG num_fetched = 0; HRESULT hr=S_OK; IUnknown *item; - TRACE("(%p,%u,%p,%p)\n", iface, celt, rgelt, pceltFetched); + TRACE("(%p,%lu,%p,%p)\n", iface, celt, rgelt, pceltFetched); while (num_fetched < celt) { @@ -860,13 +965,10 @@ static HRESULT WINAPI InstalledRuntimeEnum_Next(IEnumUnknown *iface, ULONG celt, hr = S_FALSE; break; } - if (runtimes[This->pos].mono_abi_version) - { - item = (IUnknown*)&runtimes[This->pos].ICLRRuntimeInfo_iface; - IUnknown_AddRef(item); - rgelt[num_fetched] = item; - num_fetched++; - } + item = (IUnknown*)&runtimes[This->pos].ICLRRuntimeInfo_iface; + IUnknown_AddRef(item); + rgelt[num_fetched] = item; + num_fetched++; This->pos++; } @@ -879,10 +981,10 @@ static HRESULT WINAPI InstalledRuntimeEnum_Next(IEnumUnknown *iface, ULONG celt, static HRESULT WINAPI InstalledRuntimeEnum_Skip(IEnumUnknown *iface, ULONG celt) { struct InstalledRuntimeEnum *This = impl_from_IEnumUnknown(iface); - int num_fetched = 0; + ULONG num_fetched = 0; HRESULT hr=S_OK; - TRACE("(%p,%u)\n", iface, celt); + TRACE("(%p,%lu)\n", iface, celt); while (num_fetched < celt) { @@ -891,10 +993,7 @@ static HRESULT WINAPI InstalledRuntimeEnum_Skip(IEnumUnknown *iface, ULONG celt) hr = S_FALSE; break; } - if (runtimes[This->pos].mono_abi_version) - { - num_fetched++; - } + num_fetched++; This->pos++; } @@ -919,7 +1018,7 @@ static HRESULT WINAPI InstalledRuntimeEnum_Clone(IEnumUnknown *iface, IEnumUnkno TRACE("(%p)\n", iface); - new_enum = HeapAlloc(GetProcessHeap(), 0, sizeof(*new_enum)); + new_enum = malloc(sizeof(*new_enum)); if (!new_enum) return E_OUTOFMEMORY; @@ -942,13 +1041,6 @@ static const struct IEnumUnknownVtbl InstalledRuntimeEnum_Vtbl = { InstalledRuntimeEnum_Clone }; -struct CLRMetaHost -{ - ICLRMetaHost ICLRMetaHost_iface; -}; - -static struct CLRMetaHost GlobalCLRMetaHost; - static HRESULT WINAPI CLRMetaHost_QueryInterface(ICLRMetaHost* iface, REFIID riid, void **ppvObject) @@ -981,6 +1073,11 @@ static ULONG WINAPI CLRMetaHost_Release(ICLRMetaHost* iface) return 1; } +static inline BOOL isDigit(WCHAR c) +{ + return c >= '0' && c <= '9'; +} + static BOOL parse_runtime_version(LPCWSTR version, DWORD *major, DWORD *minor, DWORD *build) { *major = 0; @@ -990,28 +1087,28 @@ static BOOL parse_runtime_version(LPCWSTR version, DWORD *major, DWORD *minor, D if (version[0] == 'v' || version[0] == 'V') { version++; - if (!isdigit(*version)) + if (!isDigit(*version)) return FALSE; - while (isdigit(*version)) + while (isDigit(*version)) *major = *major * 10 + (*version++ - '0'); if (*version == 0) return TRUE; - if (*version++ != '.' || !isdigit(*version)) + if (*version++ != '.' || !isDigit(*version)) return FALSE; - while (isdigit(*version)) + while (isDigit(*version)) *minor = *minor * 10 + (*version++ - '0'); if (*version == 0) return TRUE; - if (*version++ != '.' || !isdigit(*version)) + if (*version++ != '.' || !isDigit(*version)) return FALSE; - while (isdigit(*version)) + while (isDigit(*version)) *build = *build * 10 + (*version++ - '0'); return *version == 0; @@ -1020,14 +1117,12 @@ static BOOL parse_runtime_version(LPCWSTR version, DWORD *major, DWORD *minor, D return FALSE; } -HRESULT WINAPI CLRMetaHost_GetRuntime(ICLRMetaHost* iface, - LPCWSTR pwzVersion, REFIID iid, LPVOID *ppRuntime) +static HRESULT get_runtime(LPCWSTR pwzVersion, BOOL allow_short, + REFIID iid, LPVOID *ppRuntime) { int i; DWORD major, minor, build; - TRACE("%s %s %p\n", debugstr_w(pwzVersion), debugstr_guid(iid), ppRuntime); - if (!pwzVersion) return E_POINTER; @@ -1037,21 +1132,13 @@ HRESULT WINAPI CLRMetaHost_GetRuntime(ICLRMetaHost* iface, return CLR_E_SHIM_RUNTIME; } - find_runtimes(); - for (i=0; i= 4 && build == 0))) { - if (runtimes[i].mono_abi_version) - return ICLRRuntimeInfo_QueryInterface(&runtimes[i].ICLRRuntimeInfo_iface, iid, - ppRuntime); - else - { - missing_runtime_message(&runtimes[i]); - return CLR_E_SHIM_RUNTIME; - } + return ICLRRuntimeInfo_QueryInterface(&runtimes[i].ICLRRuntimeInfo_iface, iid, + ppRuntime); } } @@ -1059,6 +1146,14 @@ HRESULT WINAPI CLRMetaHost_GetRuntime(ICLRMetaHost* iface, return CLR_E_SHIM_RUNTIME; } +HRESULT WINAPI CLRMetaHost_GetRuntime(ICLRMetaHost* iface, + LPCWSTR pwzVersion, REFIID iid, LPVOID *ppRuntime) +{ + TRACE("%s %s %p\n", debugstr_w(pwzVersion), debugstr_guid(iid), ppRuntime); + + return get_runtime(pwzVersion, FALSE, iid, ppRuntime); +} + HRESULT WINAPI CLRMetaHost_GetVersionFromFile(ICLRMetaHost* iface, LPCWSTR pwzFilePath, LPWSTR pwzBuffer, DWORD *pcchBuffer) { @@ -1084,7 +1179,7 @@ HRESULT WINAPI CLRMetaHost_GetVersionFromFile(ICLRMetaHost* iface, if (buffer_size >= *pcchBuffer) MultiByteToWideChar(CP_UTF8, 0, version, -1, pwzBuffer, buffer_size); else - hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + hr = E_NOT_SUFFICIENT_BUFFER; } } @@ -1101,9 +1196,7 @@ static HRESULT WINAPI CLRMetaHost_EnumerateInstalledRuntimes(ICLRMetaHost* iface TRACE("%p\n", ppEnumerator); - find_runtimes(); - - new_enum = HeapAlloc(GetProcessHeap(), 0, sizeof(*new_enum)); + new_enum = malloc(sizeof(*new_enum)); if (!new_enum) return E_OUTOFMEMORY; @@ -1127,9 +1220,17 @@ static HRESULT WINAPI CLRMetaHost_EnumerateLoadedRuntimes(ICLRMetaHost* iface, static HRESULT WINAPI CLRMetaHost_RequestRuntimeLoadedNotification(ICLRMetaHost* iface, RuntimeLoadedCallbackFnPtr pCallbackFunction) { - FIXME("%p\n", pCallbackFunction); + TRACE("%p\n", pCallbackFunction); - return E_NOTIMPL; + if(!pCallbackFunction) + return E_POINTER; + + if (GlobalCLRMetaHost.callback) + return HOST_E_INVALIDOPERATION; + + GlobalCLRMetaHost.callback = pCallbackFunction; + + return S_OK; } static HRESULT WINAPI CLRMetaHost_QueryLegacyV2RuntimeBinding(ICLRMetaHost* iface, @@ -1140,9 +1241,21 @@ static HRESULT WINAPI CLRMetaHost_QueryLegacyV2RuntimeBinding(ICLRMetaHost* ifac return E_NOTIMPL; } -static HRESULT WINAPI CLRMetaHost_ExitProcess(ICLRMetaHost* iface, INT32 iExitCode) +HRESULT WINAPI CLRMetaHost_ExitProcess(ICLRMetaHost* iface, INT32 iExitCode) { - FIXME("%i: stub\n", iExitCode); + TRACE("%i\n", iExitCode); + + EnterCriticalSection(&runtime_list_cs); + + if (is_mono_started && !is_mono_shutdown) + { + /* search for a runtime and call System.Environment.Exit() */ + int i; + + for (i=0; iflags = ASSEMBLY_SEARCH_DEFAULT; + + while (string && string_len > 0) + { + next_key = memchr(string, ',', string_len); + + if (next_key) + { + kvp_len = next_key - string; + next_key++; + } + else + kvp_len = string_len; + + equals = memchr(string, '=', kvp_len); + + if (equals) + { + key_len = equals - string; + value = equals + 1; + switch (key_len) { + case 3: + if (!_strnicmp(string, "gac", 3)) { + if (IS_OPTION_TRUE(*value)) + entry->flags |= ASSEMBLY_SEARCH_GAC; + else if (IS_OPTION_FALSE(*value)) + entry->flags &= ~ASSEMBLY_SEARCH_GAC; + } + break; + case 7: + if (!_strnicmp(string, "monogac", 7)) { + if (IS_OPTION_TRUE(*value)) + entry->flags |= ASSEMBLY_SEARCH_MONOGAC; + else if (IS_OPTION_FALSE(*value)) + entry->flags &= ~ASSEMBLY_SEARCH_MONOGAC; + } + break; + case 11: + if (!_strnicmp(string, "privatepath", 11)) { + if (IS_OPTION_TRUE(*value)) + entry->flags |= ASSEMBLY_SEARCH_PRIVATEPATH; + else if (IS_OPTION_FALSE(*value)) + entry->flags &= ~ASSEMBLY_SEARCH_PRIVATEPATH; + } + break; + default: + break; + } + } + + string = next_key; + string_len -= kvp_len + 1; + } +} + +static BOOL WINAPI parse_env_overrides(INIT_ONCE *once, void *param, void **context) +{ + const char *override_string = getenv("WINE_MONO_OVERRIDES"); + struct override_entry *entry; + + if (override_string) + { + const char *entry_start; + + entry_start = override_string; + + while (entry_start && *entry_start) + { + const char *next_entry, *basename_end; + UINT entry_len; + + next_entry = strchr(entry_start, ';'); + + if (next_entry) + { + entry_len = next_entry - entry_start; + next_entry++; + } + else + entry_len = strlen(entry_start); + + basename_end = memchr(entry_start, ',', entry_len); + + if (!basename_end) + { + entry_start = next_entry; + continue; + } + + entry = calloc(1, sizeof(*entry)); + if (!entry) + { + ERR("out of memory\n"); + break; + } + + entry->name = calloc(1, basename_end - entry_start + 1); + if (!entry->name) + { + ERR("out of memory\n"); + free(entry); + break; + } + + memcpy(entry->name, entry_start, basename_end - entry_start); + + entry_len -= basename_end - entry_start + 1; + entry_start = basename_end + 1; + + parse_override_entry(entry, entry_start, entry_len); + + list_add_tail(&env_overrides, &entry->entry); + + entry_start = next_entry; + } + } + + return TRUE; +} + +static DWORD get_basename_search_flags(const char *basename, MonoAssemblyName *aname, HKEY userkey, HKEY appkey) +{ + struct override_entry *entry; + char buffer[256]; + DWORD buffer_size; + static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT; + + InitOnceExecuteOnce(&init_once, parse_env_overrides, NULL, NULL); + + LIST_FOR_EACH_ENTRY(entry, &env_overrides, override_entry, entry) + { + if (strcmp(basename, entry->name) == 0) + { + return entry->flags; + } + } + + buffer_size = sizeof(buffer); + if (appkey && !RegQueryValueExA(appkey, basename, 0, NULL, (LPBYTE)buffer, &buffer_size)) + { + override_entry reg_entry; + + memset(®_entry, 0, sizeof(reg_entry)); + + parse_override_entry(®_entry, buffer, strlen(buffer)); + + return reg_entry.flags; + } + + buffer_size = sizeof(buffer); + if (userkey && !RegQueryValueExA(userkey, basename, 0, NULL, (LPBYTE)buffer, &buffer_size)) + { + override_entry reg_entry; + + memset(®_entry, 0, sizeof(reg_entry)); + + parse_override_entry(®_entry, buffer, strlen(buffer)); + + return reg_entry.flags; + } + + if (strcmp(basename, "Microsoft.Xna.Framework.*") == 0 && + mono_assembly_name_get_version(aname, NULL, NULL, NULL) == 4) + /* Use FNA as a replacement for XNA4. */ + return ASSEMBLY_SEARCH_MONOGAC; + + return ASSEMBLY_SEARCH_UNDEFINED; +} + +static HKEY get_app_overrides_key(void) +{ + static const WCHAR subkeyW[] = {'\\','M','o','n','o','\\','A','s','m','O','v','e','r','r','i','d','e','s',0}; + WCHAR bufferW[MAX_PATH+18]; + HKEY appkey = 0; + DWORD len; + + len = (GetModuleFileNameW( 0, bufferW, MAX_PATH )); + if (len && len < MAX_PATH) + { + HKEY tmpkey; + WCHAR *p, *appname = bufferW; + if ((p = wcsrchr( appname, '/' ))) appname = p + 1; + if ((p = wcsrchr( appname, '\\' ))) appname = p + 1; + lstrcatW( appname, subkeyW ); + /* @@ Wine registry key: HKCU\Software\Wine\AppDefaults\app.exe\Mono\AsmOverrides */ + if (!RegOpenKeyA( HKEY_CURRENT_USER, "Software\\Wine\\AppDefaults", &tmpkey )) + { + if (RegOpenKeyW( tmpkey, appname, &appkey )) appkey = 0; + RegCloseKey( tmpkey ); + } + } + + return appkey; +} + +static DWORD get_assembly_search_flags(MonoAssemblyName *aname) +{ + const char *name = mono_assembly_name_get_name(aname); + char *name_copy, *name_end; + DWORD result; + HKEY appkey = 0, userkey; + + /* @@ Wine registry key: HKCU\Software\Wine\Mono\AsmOverrides */ + if (RegOpenKeyA( HKEY_CURRENT_USER, "Software\\Wine\\Mono\\AsmOverrides", &userkey )) userkey = 0; + + appkey = get_app_overrides_key(); + + result = get_basename_search_flags(name, aname, userkey, appkey); + if (result != ASSEMBLY_SEARCH_UNDEFINED) + { + if (userkey) RegCloseKey(userkey); + if (appkey) RegCloseKey(appkey); + return result; + } + + name_copy = malloc(strlen(name) + 3); + if (!name_copy) + { + ERR("out of memory\n"); + if (userkey) RegCloseKey(userkey); + if (appkey) RegCloseKey(appkey); + return ASSEMBLY_SEARCH_DEFAULT; + } + + strcpy(name_copy, name); + name_end = name_copy + strlen(name); + + do + { + strcpy(name_end, ".*"); + result = get_basename_search_flags(name_copy, aname, userkey, appkey); + if (result != ASSEMBLY_SEARCH_UNDEFINED) break; + + *name_end = 0; + name_end = strrchr(name_copy, '.'); + } while (name_end != NULL); + + /* default flags */ + if (result == ASSEMBLY_SEARCH_UNDEFINED) + { + result = get_basename_search_flags("*", aname, userkey, appkey); + if (result == ASSEMBLY_SEARCH_UNDEFINED) + result = ASSEMBLY_SEARCH_DEFAULT; + } + + free(name_copy); + if (appkey) RegCloseKey(appkey); + if (userkey) RegCloseKey(userkey); + + return result; +} + +HRESULT get_file_from_strongname(WCHAR* stringnameW, WCHAR* assemblies_path, int path_length) { - loaded_mono *mono = user_data; HRESULT hr=S_OK; - MonoAssembly *result=NULL; - char *stringname=NULL; - LPWSTR stringnameW; - int stringnameW_size; IAssemblyCache *asmcache; ASSEMBLY_INFO info; - WCHAR path[MAX_PATH]; - char *pathA; - MonoImageOpenStatus stat; - static WCHAR fusiondll[] = {'f','u','s','i','o','n',0}; + static const WCHAR fusiondll[] = {'f','u','s','i','o','n',0}; HMODULE hfusion=NULL; static HRESULT (WINAPI *pCreateAssemblyCache)(IAssemblyCache**,DWORD); - stringname = mono->mono_stringify_assembly_name(aname); - - TRACE("%s\n", debugstr_a(stringname)); - - if (!stringname) return NULL; - - /* FIXME: We should search the given paths before the GAC. */ - if (!pCreateAssemblyCache) { hr = LoadLibraryShim(fusiondll, NULL, NULL, &hfusion); @@ -1211,54 +1709,201 @@ static MonoAssembly* mono_assembly_search_hook_fn(MonoAssemblyName *aname, char hr = pCreateAssemblyCache(&asmcache, 0); if (SUCCEEDED(hr)) + { + info.cbAssemblyInfo = sizeof(info); + info.pszCurrentAssemblyPathBuf = assemblies_path; + info.cchBuf = path_length; + assemblies_path[0] = 0; + + hr = IAssemblyCache_QueryAssemblyInfo(asmcache, 0, stringnameW, &info); + + IAssemblyCache_Release(asmcache); + } + + return hr; +} + +static MonoAssembly* mono_assembly_try_load(WCHAR *path) +{ + MonoAssembly *result = NULL; + MonoImageOpenStatus stat; + char *pathA; + + if (!(pathA = WtoA(path))) return NULL; + + result = mono_assembly_open(pathA, &stat); + free(pathA); + + if (result) TRACE("found: %s\n", debugstr_w(path)); + return result; +} + +static MonoAssembly* CDECL mono_assembly_preload_hook_fn(MonoAssemblyName *aname, char **assemblies_path, void *user_data) +{ + int flags = 0; + return wine_mono_assembly_preload_hook_v2_fn(aname, assemblies_path, &flags, user_data); +} + +static MonoAssembly* CDECL wine_mono_assembly_preload_hook_fn(MonoAssemblyName *aname, char **assemblies_path, int *halt_search, void *user_data) +{ + int flags = 0; + MonoAssembly* result = wine_mono_assembly_preload_hook_v2_fn(aname, assemblies_path, &flags, user_data); + if (flags & WINE_PRELOAD_SKIP_PRIVATE_PATH) + *halt_search = 1; + return result; +} + +static MonoAssembly* CDECL wine_mono_assembly_preload_hook_v2_fn(MonoAssemblyName *aname, char **assemblies_path, int *flags, void *user_data) +{ + HRESULT hr; + MonoAssembly *result=NULL; + char *stringname=NULL; + const char *assemblyname; + const char *culture; + LPWSTR stringnameW, cultureW; + int stringnameW_size, cultureW_size; + WCHAR path[MAX_PATH]; + char *pathA; + MonoImageOpenStatus stat; + DWORD search_flags; + int i; + static const WCHAR dotdllW[] = {'.','d','l','l',0}; + static const WCHAR dotexeW[] = {'.','e','x','e',0}; + + stringname = mono_stringify_assembly_name(aname); + assemblyname = mono_assembly_name_get_name(aname); + culture = mono_assembly_name_get_culture(aname); + if (culture) + { + cultureW_size = MultiByteToWideChar(CP_UTF8, 0, culture, -1, NULL, 0); + cultureW = malloc(cultureW_size * sizeof(WCHAR)); + if (cultureW) MultiByteToWideChar(CP_UTF8, 0, culture, -1, cultureW, cultureW_size); + } + else cultureW = NULL; + + TRACE("%s\n", debugstr_a(stringname)); + + if (!stringname || !assemblyname) return NULL; + + search_flags = get_assembly_search_flags(aname); + if (private_path && (search_flags & ASSEMBLY_SEARCH_PRIVATEPATH) != 0) + { + stringnameW_size = MultiByteToWideChar(CP_UTF8, 0, assemblyname, -1, NULL, 0); + stringnameW = malloc(stringnameW_size * sizeof(WCHAR)); + if (stringnameW) + { + MultiByteToWideChar(CP_UTF8, 0, assemblyname, -1, stringnameW, stringnameW_size); + for (i = 0; private_path[i] != NULL; i++) + { + /* This is the lookup order used in Mono */ + /* 1st try: [culture]/[name].dll (culture may be empty) */ + wcscpy(path, private_path[i]); + if (cultureW) PathAppendW(path, cultureW); + PathAppendW(path, stringnameW); + wcscat(path, dotdllW); + result = mono_assembly_try_load(path); + if (result) break; + + /* 2nd try: [culture]/[name].exe (culture may be empty) */ + wcscpy(path + wcslen(path) - wcslen(dotdllW), dotexeW); + result = mono_assembly_try_load(path); + if (result) break; + + /* 3rd try: [culture]/[name]/[name].dll (culture may be empty) */ + path[wcslen(path) - wcslen(dotexeW)] = 0; + PathAppendW(path, stringnameW); + wcscat(path, dotdllW); + result = mono_assembly_try_load(path); + if (result) break; + + /* 4th try: [culture]/[name]/[name].exe (culture may be empty) */ + wcscpy(path + wcslen(path) - wcslen(dotdllW), dotexeW); + result = mono_assembly_try_load(path); + if (result) break; + } + free(stringnameW); + if (result) goto done; + } + } + + if ((search_flags & ASSEMBLY_SEARCH_GAC) != 0) { stringnameW_size = MultiByteToWideChar(CP_UTF8, 0, stringname, -1, NULL, 0); - stringnameW = HeapAlloc(GetProcessHeap(), 0, stringnameW_size * sizeof(WCHAR)); + stringnameW = malloc(stringnameW_size * sizeof(WCHAR)); if (stringnameW) + { MultiByteToWideChar(CP_UTF8, 0, stringname, -1, stringnameW, stringnameW_size); + + hr = get_file_from_strongname(stringnameW, path, MAX_PATH); + + free(stringnameW); + } else hr = E_OUTOFMEMORY; if (SUCCEEDED(hr)) { - info.cbAssemblyInfo = sizeof(info); - info.pszCurrentAssemblyPathBuf = path; - info.cchBuf = MAX_PATH; - path[0] = 0; + TRACE("found: %s\n", debugstr_w(path)); - hr = IAssemblyCache_QueryAssemblyInfo(asmcache, 0, stringnameW, &info); + pathA = WtoA(path); + + if (pathA) + { + result = mono_assembly_open(pathA, &stat); + + if (!result) + ERR("Failed to load %s, status=%u\n", debugstr_w(path), stat); + + free(pathA); + + if (result) + { + *flags |= WINE_PRELOAD_SET_GAC; + goto done; + } + } } - - HeapFree(GetProcessHeap(), 0, stringnameW); - - IAssemblyCache_Release(asmcache); } + else + TRACE("skipping Windows GAC search due to override setting\n"); - if (SUCCEEDED(hr)) + if (wine_mono_assembly_load_from_gac) { - TRACE("found: %s\n", debugstr_w(path)); - - pathA = WtoA(path); - - if (pathA) + if (search_flags & ASSEMBLY_SEARCH_MONOGAC) { - result = mono->mono_assembly_open(pathA, &stat); + result = wine_mono_assembly_load_from_gac (aname, &stat, FALSE); - if (!result) - ERR("Failed to load %s, status=%u\n", debugstr_w(path), stat); - - HeapFree(GetProcessHeap(), 0, pathA); + if (result) + { + TRACE("found in Mono GAC\n"); + *flags |= WINE_PRELOAD_SET_GAC; + goto done; + } + } + else + { + *flags |= WINE_PRELOAD_SKIP_GAC; + TRACE("skipping Mono GAC search due to override setting\n"); } } - mono->mono_free(stringname); + if ((search_flags & ASSEMBLY_SEARCH_PRIVATEPATH) == 0) + { + TRACE("skipping AppDomain search path due to override setting\n"); + *flags |= WINE_PRELOAD_SKIP_PRIVATE_PATH; + } + +done: + free(cultureW); + mono_free(stringname); return result; } HRESULT get_runtime_info(LPCWSTR exefile, LPCWSTR version, LPCWSTR config_file, - DWORD startup_flags, DWORD runtimeinfo_flags, BOOL legacy, ICLRRuntimeInfo **result) + IStream *config_stream, DWORD startup_flags, DWORD runtimeinfo_flags, + BOOL legacy, ICLRRuntimeInfo **result) { static const WCHAR dotconfig[] = {'.','c','o','n','f','i','g',0}; static const DWORD supported_startup_flags = 0; @@ -1271,40 +1916,46 @@ HRESULT get_runtime_info(LPCWSTR exefile, LPCWSTR version, LPCWSTR config_file, parsed_config_file parsed_config; if (startup_flags & ~supported_startup_flags) - FIXME("unsupported startup flags %x\n", startup_flags & ~supported_startup_flags); + FIXME("unsupported startup flags %lx\n", startup_flags & ~supported_startup_flags); if (runtimeinfo_flags & ~supported_runtime_flags) - FIXME("unsupported runtimeinfo flags %x\n", runtimeinfo_flags & ~supported_runtime_flags); + FIXME("unsupported runtimeinfo flags %lx\n", runtimeinfo_flags & ~supported_runtime_flags); - if (exefile && !config_file) + if (exefile && !exefile[0]) + exefile = NULL; + + if (exefile && !config_file && !config_stream) { - strcpyW(local_config_file, exefile); - strcatW(local_config_file, dotconfig); + lstrcpyW(local_config_file, exefile); + lstrcatW(local_config_file, dotconfig); config_file = local_config_file; } - if (config_file) + if (config_file || config_stream) { - int found=0; - hr = parse_config_file(config_file, &parsed_config); + BOOL found = FALSE; + if (config_file) + hr = parse_config_file(config_file, &parsed_config); + else + hr = parse_config_stream(config_stream, &parsed_config); if (SUCCEEDED(hr)) { supported_runtime *entry; LIST_FOR_EACH_ENTRY(entry, &parsed_config.supported_runtimes, supported_runtime, entry) { - hr = CLRMetaHost_GetRuntime(0, entry->version, &IID_ICLRRuntimeInfo, (void**)result); + hr = get_runtime(entry->version, TRUE, &IID_ICLRRuntimeInfo, (void**)result); if (SUCCEEDED(hr)) { - found = 1; + found = TRUE; break; } } } else { - WARN("failed to parse config file %s, hr=%x\n", debugstr_w(config_file), hr); + WARN("failed to parse config file %s, hr=%lx\n", debugstr_w(config_file), hr); } free_parsed_config_file(&parsed_config); @@ -1315,11 +1966,35 @@ HRESULT get_runtime_info(LPCWSTR exefile, LPCWSTR version, LPCWSTR config_file, if (exefile && !version) { + DWORD major, minor, build; + hr = CLRMetaHost_GetVersionFromFile(0, exefile, local_version, &local_version_size); version = local_version; if (FAILED(hr)) return hr; + + /* When running an executable, specifically when getting the version number from + * the exe, native accepts a matching major.minor with build <= expected build. */ + if (!parse_runtime_version(version, &major, &minor, &build)) + { + ERR("Cannot parse %s\n", debugstr_w(version)); + return CLR_E_SHIM_RUNTIME; + } + + if (legacy) + i = 3; + else + i = NUM_RUNTIMES; + + while (i--) + { + if (runtimes[i].major == major && runtimes[i].minor == minor && runtimes[i].build >= build) + { + return ICLRRuntimeInfo_QueryInterface(&runtimes[i].ICLRRuntimeInfo_iface, + &IID_ICLRRuntimeInfo, (void **)result); + } + } } if (version) @@ -1339,33 +2014,23 @@ HRESULT get_runtime_info(LPCWSTR exefile, LPCWSTR version, LPCWSTR config_file, return CLR_E_SHIM_RUNTIME; } - find_runtimes(); - if (legacy) - i = 2; + i = 3; else i = NUM_RUNTIMES; while (i--) { - if (runtimes[i].mono_abi_version) + /* Must be greater or equal to the version passed in. */ + if (!version || ((runtimes[i].major >= major && runtimes[i].minor >= minor && runtimes[i].build >= build) || + (runtimes[i].major >= major && runtimes[i].minor > minor) || + (runtimes[i].major > major))) { - /* Must be greater or equal to the version passed in. */ - if (!version || ((runtimes[i].major >= major && runtimes[i].minor >= minor && runtimes[i].build >= build) || - (runtimes[i].major >= major && runtimes[i].minor > minor) || - (runtimes[i].major > major))) - { - return ICLRRuntimeInfo_QueryInterface(&runtimes[i].ICLRRuntimeInfo_iface, - &IID_ICLRRuntimeInfo, (void **)result); - } + return ICLRRuntimeInfo_QueryInterface(&runtimes[i].ICLRRuntimeInfo_iface, + &IID_ICLRRuntimeInfo, (void **)result); } } - if (legacy) - missing_runtime_message(&runtimes[1]); - else - missing_runtime_message(&runtimes[NUM_RUNTIMES-1]); - return CLR_E_SHIM_RUNTIME; } diff --git a/dll/win32/mscoree/mscoree.spec b/dll/win32/mscoree/mscoree.spec index a51a2f1c599..92e0f60b718 100644 --- a/dll/win32/mscoree/mscoree.spec +++ b/dll/win32/mscoree/mscoree.spec @@ -24,12 +24,13 @@ @ stdcall CorBindToRuntimeHost(wstr wstr wstr ptr long ptr ptr ptr) @ stub CorDllMainWorker @ stdcall CorExitProcess(long) -@ stub CorGetSvc -@ stub CorIsLatestSvc +@ stdcall CorGetSvc(ptr) +@ stdcall CorIsLatestSvc(ptr ptr) @ stub CorMarkThreadInThreadPool @ stub CorTickleSvc @ stdcall CreateConfigStream(wstr ptr) @ stdcall CreateDebuggingInterfaceFromVersion(long wstr ptr) +@ stdcall CreateInterface(ptr ptr ptr) @ stdcall -private DllCanUnloadNow() @ stdcall -private DllGetClassObject(ptr ptr ptr) @ stdcall -private DllRegisterServer() @@ -37,7 +38,7 @@ @ stub EEDllGetClassObjectFromClass @ stub EEDllRegisterServer @ stub EEDllUnregisterServer -@ stdcall GetAssemblyMDImport(ptr ptr ptr) +@ stdcall GetAssemblyMDImport(wstr ptr ptr) @ stub GetCORRequiredVersion @ stub GetCORRootDirectory @ stdcall GetCORSystemDirectory(ptr long ptr) @@ -63,12 +64,12 @@ @ stub GetRequestedRuntimeVersionForCLSID @ stub GetStartupFlags @ stub GetTargetForVTableEntry -@ stub GetTokenForVTableEntry +@ stdcall GetTokenForVTableEntry(ptr ptr) @ stdcall GetVersionFromProcess(ptr ptr long ptr) @ stub GetXMLElement @ stub GetXMLElementAttribute @ stub GetXMLObject -@ stdcall LoadLibraryShim(ptr ptr ptr ptr) +@ stdcall LoadLibraryShim(wstr wstr ptr ptr) @ stub LoadLibraryWithPolicyShim @ stdcall LoadStringRCEx(long long ptr long long ptr) @ stdcall LockClrVersion(ptr ptr ptr) @@ -81,7 +82,7 @@ @ stdcall ND_RU1(ptr long) @ stdcall ND_WI2(ptr long long) @ stdcall ND_WI4(ptr long long) -@ stdcall ND_WI8(ptr long double) +@ stdcall ND_WI8(ptr long int64) @ stdcall ND_WU1(ptr long long) @ stub OpenCtrs @ stub ReOpenMetaDataWithMemoryEx @@ -107,7 +108,7 @@ @ stdcall StrongNameSignatureVerification(wstr long ptr) @ stdcall StrongNameSignatureVerificationEx(wstr long ptr) @ stub StrongNameSignatureVerificationFromImage -@ stub StrongNameTokenFromAssembly +@ stdcall StrongNameTokenFromAssembly(wstr ptr ptr) @ stub StrongNameTokenFromAssemblyEx @ stub StrongNameTokenFromPublicKey @ stub TranslateSecurityAttributes @@ -115,4 +116,4 @@ @ stdcall _CorExeMain2(ptr long ptr ptr ptr) @ stdcall _CorExeMain() @ stdcall _CorImageUnloading(ptr) -@ stdcall _CorValidateImage(ptr ptr) +@ stdcall _CorValidateImage(ptr wstr) diff --git a/dll/win32/mscoree/mscoree_classes.idl b/dll/win32/mscoree/mscoree_classes.idl new file mode 100644 index 00000000000..a29ef6c7ea3 --- /dev/null +++ b/dll/win32/mscoree/mscoree_classes.idl @@ -0,0 +1,74 @@ +/* + * Copyright 2011 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 + */ + +#pragma makedep register + +[ + helpstring("Microsoft Common Language Runtime Host V2"), + threading(both), + progid("CLRMetaData.CLRRuntimeHost.1"), + vi_progid("CLRMetaData.CLRRuntimeHost"), + uuid(90F1A06E-7712-4762-86B5-7A5EBA6BDB01), +] +coclass ICLRRuntimeHost +{ +} + +[ + helpstring("Microsoft Common Language Runtime Host V2"), + threading(both), + progid("CLRMetaData.CLRRuntimeHost.2"), + vi_progid("CLRMetaData.CLRRuntimeHost"), + uuid(90F1A06E-7712-4762-86B5-7A5EBA6BDB02), +] +coclass CLRRuntimeHost +{ +} + +[ + helpstring("Microsoft Common Language Runtime Meta Data"), + threading(both), + progid("CLRMetaData.CorMetaDataDispenser.2"), + vi_progid("CLRMetaData.CorMetaDataDispenser"), + uuid(E5CB7A31-7512-11D2-89CE-0080C792E5D8), +] +coclass CorMetaDataDispenser +{ +} + +[ + helpstring("Microsoft Common Language Runtime Meta Data"), + threading(both), + progid("CLRMetaData.CorMetaDataDispenserRuntime.2"), + vi_progid("CLRMetaData.CorMetaDataDispenserRuntime"), + uuid(1EC2DE53-75CC-11d2-9775-00A0C9B4D50C), +] +coclass CorMetaDataDispenserRuntime +{ +} + +[ + helpstring("Microsoft Common Language Runtime Host"), + threading(both), + progid("CLRMetaData.CorRuntimeHost.2"), + vi_progid("CLRMetaData.CorRuntimeHost"), + uuid(CB2F6723-AB3A-11d2-9C40-00C04FA30A3E), +] +coclass CorRuntimeHost +{ +} diff --git a/dll/win32/mscoree/mscoree_main.c b/dll/win32/mscoree/mscoree_main.c index e3507c4d275..2b9f78a3955 100644 --- a/dll/win32/mscoree/mscoree_main.c +++ b/dll/win32/mscoree/mscoree_main.c @@ -19,11 +19,44 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ +#include + +#define COBJMACROS +#include "windef.h" +#include "winbase.h" +#include "winuser.h" +#include "winnls.h" +#include "winreg.h" +#include "ole2.h" +#include "ocidl.h" +#include "shellapi.h" +#include "strongname.h" + +#include "initguid.h" +#include "msxml2.h" +#include "corerror.h" +#include "cor.h" +#include "mscoree.h" +#include "corhdr.h" +#include "cordebug.h" +#include "metahost.h" +#include "fusion.h" +#include "wine/list.h" #include "mscoree_private.h" +#include "rpcproxy.h" -#include +#include "wine/debug.h" -static HINSTANCE MSCOREE_hInstance; +WINE_DEFAULT_DEBUG_CHANNEL( mscoree ); +WINE_DECLARE_DEBUG_CHANNEL(winediag); + +struct print_handler_tls +{ + int length; + char buffer[1018]; +}; + +static DWORD print_tls_index = TLS_OUT_OF_INDEXES; typedef HRESULT (*fnCreateInstance)(REFIID riid, LPVOID *ppObj); @@ -34,7 +67,7 @@ char *WtoA(LPCWSTR wstr) length = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL); - result = HeapAlloc(GetProcessHeap(), 0, length); + result = malloc(length); if (result) WideCharToMultiByte(CP_UTF8, 0, wstr, -1, result, length, NULL, NULL); @@ -44,8 +77,8 @@ char *WtoA(LPCWSTR wstr) static BOOL get_install_root(LPWSTR install_dir) { - 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}; - const WCHAR install_root[] = {'I','n','s','t','a','l','l','R','o','o','t',0}; + static 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}; + static const WCHAR install_root[] = {'I','n','s','t','a','l','l','R','o','o','t',0}; DWORD len; HKEY key; @@ -100,7 +133,7 @@ static ULONG WINAPI mscorecf_AddRef(IClassFactory *iface ) mscorecf *This = impl_from_IClassFactory(iface); ULONG ref = InterlockedIncrement(&This->ref); - TRACE("%p ref=%u\n", This, ref); + TRACE("%p ref=%lu\n", This, ref); return ref; } @@ -110,11 +143,11 @@ static ULONG WINAPI mscorecf_Release(IClassFactory *iface ) mscorecf *This = impl_from_IClassFactory(iface); ULONG ref = InterlockedDecrement(&This->ref); - TRACE("%p ref=%u\n", This, ref); + TRACE("%p ref=%lu\n", This, ref); if (ref == 0) { - HeapFree(GetProcessHeap(), 0, This); + free(This); } return ref; @@ -143,7 +176,7 @@ static HRESULT WINAPI mscorecf_CreateInstance(IClassFactory *iface,LPUNKNOWN pOu } else { - WARN("Cannot create an instance object. 0x%08x\n", hr); + WARN("Cannot create an instance object. 0x%08lx\n", hr); } return hr; } @@ -171,13 +204,13 @@ HRESULT WINAPI CorBindToRuntimeHost(LPCWSTR pwszVersion, LPCWSTR pwszBuildFlavor HRESULT ret; ICLRRuntimeInfo *info; - TRACE("(%s, %s, %s, %p, %d, %s, %s, %p)\n", debugstr_w(pwszVersion), + TRACE("(%s, %s, %s, %p, %ld, %s, %s, %p)\n", debugstr_w(pwszVersion), debugstr_w(pwszBuildFlavor), debugstr_w(pwszHostConfigFile), pReserved, startupFlags, debugstr_guid(rclsid), debugstr_guid(riid), ppv); *ppv = NULL; - ret = get_runtime_info(NULL, pwszVersion, pwszHostConfigFile, startupFlags, 0, TRUE, &info); + ret = get_runtime_info(NULL, pwszVersion, pwszHostConfigFile, NULL, startupFlags, 0, TRUE, &info); if (SUCCEEDED(ret)) { @@ -189,26 +222,88 @@ HRESULT WINAPI CorBindToRuntimeHost(LPCWSTR pwszVersion, LPCWSTR pwszBuildFlavor return ret; } +void CDECL mono_print_handler_fn(const char *string, INT is_stdout) +{ + struct print_handler_tls *tls = TlsGetValue(print_tls_index); + + if (!tls) + { + tls = malloc(sizeof(*tls)); + tls->length = 0; + TlsSetValue(print_tls_index, tls); + } + + while (*string) + { + int remaining_buffer = sizeof(tls->buffer) - tls->length; + int length = strlen(string); + const char *newline = memchr(string, '\n', min(length, remaining_buffer)); + + if (newline) + { + length = newline - string + 1; + wine_dbg_printf("%.*s%.*s", tls->length, tls->buffer, length, string); + tls->length = 0; + string += length; + } + else if (length > remaining_buffer) + { + /* this would overflow Wine's debug buffer */ + wine_dbg_printf("%.*s%.*s\n", tls->length, tls->buffer, remaining_buffer, string); + tls->length = 0; + string += remaining_buffer; + } + else + { + memcpy(tls->buffer + tls->length, string, length); + tls->length += length; + break; + } + } +} + +void CDECL mono_log_handler_fn(const char *log_domain, const char *log_level, const char *message, INT fatal, void *user_data) +{ + SIZE_T len = (log_domain ? strlen(log_domain) + 2 : 0) + strlen(message) + strlen("\n") + 1; + char *msg = calloc(len, sizeof(char)); + + if (msg) + { + sprintf(msg, "%s%s%s\n", log_domain ? log_domain : "", log_domain ? ": " : "", message); + mono_print_handler_fn(msg, 0); + } + + free(msg); +} + BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { - TRACE("(%p, %d, %p)\n", hinstDLL, fdwReason, lpvReserved); - - MSCOREE_hInstance = hinstDLL; + TRACE("(%p, %ld, %p)\n", hinstDLL, fdwReason, lpvReserved); switch (fdwReason) { -#ifndef __REACTOS__ - case DLL_WINE_PREATTACH: - return FALSE; /* prefer native version */ -#endif case DLL_PROCESS_ATTACH: runtimehost_init(); - DisableThreadLibraryCalls(hinstDLL); + + print_tls_index = TlsAlloc(); + + if (print_tls_index == TLS_OUT_OF_INDEXES) + return FALSE; + + break; + case DLL_THREAD_DETACH: + if (print_tls_index != TLS_OUT_OF_INDEXES) + free(TlsGetValue(print_tls_index)); break; case DLL_PROCESS_DETACH: expect_no_runtimes(); if (lpvReserved) break; /* process is terminating */ runtimehost_uninit(); + if (print_tls_index != TLS_OUT_OF_INDEXES) + { + free(TlsGetValue(print_tls_index)); + TlsFree(print_tls_index); + } break; } return TRUE; @@ -216,7 +311,7 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) __int32 WINAPI _CorExeMain2(PBYTE ptrMemory, DWORD cntMemory, LPWSTR imageName, LPWSTR loaderName, LPWSTR cmdLine) { - TRACE("(%p, %u, %s, %s, %s)\n", ptrMemory, cntMemory, debugstr_w(imageName), debugstr_w(loaderName), debugstr_w(cmdLine)); + TRACE("(%p, %lu, %s, %s, %s)\n", ptrMemory, cntMemory, debugstr_w(imageName), debugstr_w(loaderName), debugstr_w(cmdLine)); FIXME("Directly running .NET applications not supported.\n"); return -1; } @@ -224,8 +319,7 @@ __int32 WINAPI _CorExeMain2(PBYTE ptrMemory, DWORD cntMemory, LPWSTR imageName, void WINAPI CorExitProcess(int exitCode) { TRACE("(%x)\n", exitCode); - unload_all_runtimes(); - ExitProcess(exitCode); + CLRMetaHost_ExitProcess(0, exitCode); } VOID WINAPI _CorImageUnloading(PVOID imageBase) @@ -244,12 +338,12 @@ HRESULT WINAPI GetCORSystemDirectory(LPWSTR pbuffer, DWORD cchBuffer, DWORD *dwL ICLRRuntimeInfo *info; HRESULT ret; - TRACE("(%p, %d, %p)!\n", pbuffer, cchBuffer, dwLength); + TRACE("(%p, %ld, %p)!\n", pbuffer, cchBuffer, dwLength); if (!dwLength || !pbuffer) return E_POINTER; - ret = get_runtime_info(NULL, NULL, NULL, 0, RUNTIME_INFO_UPGRADE_VERSION, TRUE, &info); + ret = get_runtime_info(NULL, NULL, NULL, NULL, 0, RUNTIME_INFO_UPGRADE_VERSION, TRUE, &info); if (SUCCEEDED(ret)) { @@ -267,12 +361,12 @@ HRESULT WINAPI GetCORVersion(LPWSTR pbuffer, DWORD cchBuffer, DWORD *dwLength) ICLRRuntimeInfo *info; HRESULT ret; - TRACE("(%p, %d, %p)!\n", pbuffer, cchBuffer, dwLength); + TRACE("(%p, %ld, %p)!\n", pbuffer, cchBuffer, dwLength); if (!dwLength || !pbuffer) return E_POINTER; - ret = get_runtime_info(NULL, NULL, NULL, 0, RUNTIME_INFO_UPGRADE_VERSION, TRUE, &info); + ret = get_runtime_info(NULL, NULL, NULL, NULL, 0, RUNTIME_INFO_UPGRADE_VERSION, TRUE, &info); if (SUCCEEDED(ret)) { @@ -285,6 +379,23 @@ HRESULT WINAPI GetCORVersion(LPWSTR pbuffer, DWORD cchBuffer, DWORD *dwLength) return ret; } +HRESULT WINAPI CorIsLatestSvc(int *unk1, int *unk2) +{ + ERR_(winediag)("If this function is called, it is likely the result of a broken .NET installation\n"); + + if (!unk1 || !unk2) + return E_POINTER; + + return S_OK; +} + +HRESULT WINAPI CorGetSvc(void *unk) +{ + ERR_(winediag)("If this function is called, it is likely the result of a broken .NET installation\n"); + + return E_NOTIMPL; +} + HRESULT WINAPI GetRequestedRuntimeInfo(LPCWSTR pExe, LPCWSTR pwszVersion, LPCWSTR pConfigurationFile, DWORD startupFlags, DWORD runtimeInfoFlags, LPWSTR pDirectory, DWORD dwDirectory, DWORD *dwDirectoryLength, LPWSTR pVersion, DWORD cchBuffer, DWORD *dwlength) @@ -293,7 +404,7 @@ HRESULT WINAPI GetRequestedRuntimeInfo(LPCWSTR pExe, LPCWSTR pwszVersion, LPCWST ICLRRuntimeInfo *info; DWORD length_dummy; - TRACE("(%s, %s, %s, 0x%08x, 0x%08x, %p, 0x%08x, %p, %p, 0x%08x, %p)\n", debugstr_w(pExe), + TRACE("(%s, %s, %s, 0x%08lx, 0x%08lx, %p, 0x%08lx, %p, %p, 0x%08lx, %p)\n", debugstr_w(pExe), debugstr_w(pwszVersion), debugstr_w(pConfigurationFile), startupFlags, runtimeInfoFlags, pDirectory, dwDirectory, dwDirectoryLength, pVersion, cchBuffer, dwlength); @@ -301,7 +412,7 @@ HRESULT WINAPI GetRequestedRuntimeInfo(LPCWSTR pExe, LPCWSTR pwszVersion, LPCWST if (!dwlength) dwlength = &length_dummy; - ret = get_runtime_info(pExe, pwszVersion, pConfigurationFile, startupFlags, runtimeInfoFlags, TRUE, &info); + ret = get_runtime_info(pExe, pwszVersion, pConfigurationFile, NULL, startupFlags, runtimeInfoFlags, TRUE, &info); if (SUCCEEDED(ret)) { @@ -325,7 +436,7 @@ HRESULT WINAPI GetRequestedRuntimeInfo(LPCWSTR pExe, LPCWSTR pwszVersion, LPCWST HRESULT WINAPI GetRequestedRuntimeVersion(LPWSTR pExe, LPWSTR pVersion, DWORD cchBuffer, DWORD *dwlength) { - TRACE("(%s, %p, %d, %p)\n", debugstr_w(pExe), pVersion, cchBuffer, dwlength); + TRACE("(%s, %p, %ld, %p)\n", debugstr_w(pExe), pVersion, cchBuffer, dwlength); if(!dwlength) return E_POINTER; @@ -341,7 +452,7 @@ HRESULT WINAPI GetRealProcAddress(LPCSTR procname, void **ppv) HRESULT WINAPI GetFileVersion(LPCWSTR szFilename, LPWSTR szBuffer, DWORD cchBuffer, DWORD *dwLength) { - TRACE("(%s, %p, %d, %p)\n", debugstr_w(szFilename), szBuffer, cchBuffer, dwLength); + TRACE("(%s, %p, %ld, %p)\n", debugstr_w(szFilename), szBuffer, cchBuffer, dwLength); if (!szFilename || !dwLength) return E_POINTER; @@ -379,11 +490,11 @@ HRESULT WINAPI LoadLibraryShim( LPCWSTR szDllName, LPCWSTR szVersion, LPVOID pvR else szVersion = default_version; } - strcatW(dll_filename, szVersion); - strcatW(dll_filename, slash); + lstrcatW(dll_filename, szVersion); + lstrcatW(dll_filename, slash); } - strcatW(dll_filename, szDllName); + lstrcatW(dll_filename, szDllName); *phModDll = LoadLibraryW(dll_filename); @@ -398,7 +509,7 @@ HRESULT WINAPI LockClrVersion(FLockClrVersionCallback hostCallback, FLockClrVers HRESULT WINAPI CoInitializeCor(DWORD fFlags) { - FIXME("(0x%08x): stub\n", fFlags); + FIXME("(0x%08lx): stub\n", fFlags); return S_OK; } @@ -410,7 +521,7 @@ HRESULT WINAPI GetAssemblyMDImport(LPCWSTR szFileName, REFIID riid, IUnknown **p HRESULT WINAPI GetVersionFromProcess(HANDLE hProcess, LPWSTR pVersion, DWORD cchBuffer, DWORD *dwLength) { - FIXME("(%p, %p, %d, %p): stub\n", hProcess, pVersion, cchBuffer, dwLength); + FIXME("(%p, %p, %ld, %p): stub\n", hProcess, pVersion, cchBuffer, dwLength); return E_NOTIMPL; } @@ -421,7 +532,7 @@ HRESULT WINAPI LoadStringRCEx(LCID culture, UINT resId, LPWSTR pBuffer, int iBuf return E_INVALIDARG; pBuffer[0] = 0; if (resId) { - FIXME("(%d, %x, %p, %d, %d, %p): semi-stub\n", culture, resId, pBuffer, iBufLen, bQuiet, pBufLen); + FIXME("(%ld, %x, %p, %d, %d, %p): semi-stub\n", culture, resId, pBuffer, iBufLen, bQuiet, pBufLen); res = E_NOTIMPL; } else @@ -442,12 +553,12 @@ HRESULT WINAPI CorBindToRuntimeEx(LPWSTR szVersion, LPWSTR szBuildFlavor, DWORD HRESULT ret; ICLRRuntimeInfo *info; - TRACE("%s %s %d %s %s %p\n", debugstr_w(szVersion), debugstr_w(szBuildFlavor), nflags, debugstr_guid( rslsid ), + TRACE("%s %s %ld %s %s %p\n", debugstr_w(szVersion), debugstr_w(szBuildFlavor), nflags, debugstr_guid( rslsid ), debugstr_guid( riid ), ppv); *ppv = NULL; - ret = get_runtime_info(NULL, szVersion, NULL, nflags, RUNTIME_INFO_UPGRADE_VERSION, TRUE, &info); + ret = get_runtime_info(NULL, szVersion, NULL, NULL, nflags, RUNTIME_INFO_UPGRADE_VERSION, TRUE, &info); if (SUCCEEDED(ret)) { @@ -461,8 +572,23 @@ HRESULT WINAPI CorBindToRuntimeEx(LPWSTR szVersion, LPWSTR szBuildFlavor, DWORD HRESULT WINAPI CorBindToCurrentRuntime(LPCWSTR filename, REFCLSID rclsid, REFIID riid, LPVOID *ppv) { - FIXME("(%s, %s, %s, %p): stub\n", debugstr_w(filename), debugstr_guid(rclsid), debugstr_guid(riid), ppv); - return E_NOTIMPL; + HRESULT ret; + ICLRRuntimeInfo *info; + + TRACE("(%s, %s, %s, %p)\n", debugstr_w(filename), debugstr_guid(rclsid), debugstr_guid(riid), ppv); + + *ppv = NULL; + + ret = get_runtime_info(NULL, NULL, filename, NULL, 0, RUNTIME_INFO_UPGRADE_VERSION, TRUE, &info); + + if (SUCCEEDED(ret)) + { + ret = ICLRRuntimeInfo_GetInterface(info, rclsid, riid, ppv); + + ICLRRuntimeInfo_Release(info); + } + + return ret; } STDAPI ClrCreateManagedInstance(LPCWSTR pTypeName, REFIID riid, void **ppObject) @@ -476,7 +602,7 @@ STDAPI ClrCreateManagedInstance(LPCWSTR pTypeName, REFIID riid, void **ppObject) TRACE("(%s,%s,%p)\n", debugstr_w(pTypeName), debugstr_guid(riid), ppObject); /* FIXME: How to determine which runtime version to use? */ - ret = get_runtime_info(NULL, NULL, NULL, 0, RUNTIME_INFO_UPGRADE_VERSION, TRUE, &info); + ret = get_runtime_info(NULL, NULL, NULL, NULL, 0, RUNTIME_INFO_UPGRADE_VERSION, TRUE, &info); if (SUCCEEDED(ret)) { @@ -500,27 +626,28 @@ STDAPI ClrCreateManagedInstance(LPCWSTR pTypeName, REFIID riid, void **ppObject) return ret; } -BOOL WINAPI StrongNameSignatureVerification(LPCWSTR filename, DWORD inFlags, DWORD* pOutFlags) +BOOLEAN WINAPI StrongNameSignatureVerification(LPCWSTR filename, DWORD inFlags, DWORD *pOutFlags) { - FIXME("(%s, 0x%X, %p): stub\n", debugstr_w(filename), inFlags, pOutFlags); + FIXME("(%s, 0x%lX, %p): stub\n", debugstr_w(filename), inFlags, pOutFlags); return FALSE; } -BOOL WINAPI StrongNameSignatureVerificationEx(LPCWSTR filename, BOOL forceVerification, BOOL* pVerified) +BOOLEAN WINAPI StrongNameSignatureVerificationEx(LPCWSTR filename, BOOLEAN forceVerification, BOOLEAN *pVerified) { FIXME("(%s, %u, %p): stub\n", debugstr_w(filename), forceVerification, pVerified); - return FALSE; + *pVerified = TRUE; + return TRUE; } -HRESULT WINAPI CreateConfigStream(LPCWSTR filename, IStream **stream) +BOOLEAN WINAPI StrongNameTokenFromAssembly(LPCWSTR path, BYTE **token, ULONG *size) { - FIXME("(%s, %p): stub\n", debugstr_w(filename), stream); - return E_NOTIMPL; + FIXME("(%s, %p, %p): stub\n", debugstr_w(path), token, size); + return FALSE; } HRESULT WINAPI CreateDebuggingInterfaceFromVersion(int nDebugVersion, LPCWSTR version, IUnknown **ppv) { - const WCHAR v2_0[] = {'v','2','.','0','.','5','0','7','2','7',0}; + static const WCHAR v2_0[] = {'v','2','.','0','.','5','0','7','2','7',0}; HRESULT hr = E_FAIL; ICLRRuntimeInfo *runtimeinfo; @@ -534,7 +661,7 @@ HRESULT WINAPI CreateDebuggingInterfaceFromVersion(int nDebugVersion, LPCWSTR ve *ppv = NULL; - if(strcmpW(version, v2_0) != 0) + if(wcscmp(version, v2_0) != 0) { FIXME("Currently .NET Version '%s' not support.\n", debugstr_w(version)); return E_INVALIDARG; @@ -563,12 +690,21 @@ HRESULT WINAPI CLRCreateInstance(REFCLSID clsid, REFIID riid, LPVOID *ppInterfac if (IsEqualGUID(clsid, &CLSID_CLRMetaHost)) return CLRMetaHost_CreateInstance(riid, ppInterface); + if (IsEqualGUID(clsid, &CLSID_CLRMetaHostPolicy)) + return CLRMetaHostPolicy_CreateInstance(riid, ppInterface); FIXME("not implemented for class %s\n", debugstr_guid(clsid)); return CLASS_E_CLASSNOTAVAILABLE; } +HRESULT WINAPI CreateInterface(REFCLSID clsid, REFIID riid, LPVOID *ppInterface) +{ + TRACE("(%s,%s,%p)\n", debugstr_guid(clsid), debugstr_guid(riid), ppInterface); + + return CLRCreateInstance(clsid, riid, ppInterface); +} + HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv) { mscorecf *This; @@ -579,10 +715,10 @@ HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv) if(!ppv) return E_INVALIDARG; - This = HeapAlloc(GetProcessHeap(), 0, sizeof(mscorecf)); + This = malloc(sizeof(mscorecf)); This->IClassFactory_iface.lpVtbl = &mscorecf_vtbl; - This->pfnCreateInstance = &create_monodata; + This->pfnCreateInstance = create_monodata; This->ref = 1; This->clsid = *rclsid; @@ -592,19 +728,245 @@ HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv) return hr; } +static void parse_msi_version_string(const char *version, int *parts) +{ + const char *minor_start, *build_start; + + parts[0] = atoi(version); + + parts[1] = parts[2] = 0; + + minor_start = strchr(version, '.'); + if (minor_start) + { + minor_start++; + parts[1] = atoi(minor_start); + + build_start = strchr(minor_start, '.'); + if (build_start) + parts[2] = atoi(build_start+1); + } +} + +static int compare_versions(const char *a, const char *b) +{ + int a_parts[3], b_parts[3], i; + + parse_msi_version_string(a, a_parts); + parse_msi_version_string(b, b_parts); + + for (i=0; i<3; i++) + if (a_parts[i] != b_parts[i]) + return a_parts[i] - b_parts[i]; + + return 0; +} + +static BOOL invoke_appwiz(void) +{ + PROCESS_INFORMATION pi; + STARTUPINFOW si; + WCHAR app[MAX_PATH]; + WCHAR *args; + LONG len; + BOOL ret; + + static const WCHAR controlW[] = {'\\','c','o','n','t','r','o','l','.','e','x','e',0}; + static const WCHAR argsW[] = + {' ','a','p','p','w','i','z','.','c','p','l',' ','i','n','s','t','a','l','l','_','m','o','n','o',0}; + + len = GetSystemDirectoryW(app, MAX_PATH - ARRAY_SIZE(controlW)); + memcpy(app+len, controlW, sizeof(controlW)); + + args = malloc(len * sizeof(WCHAR) + sizeof(controlW) + sizeof(argsW)); + if(!args) + return FALSE; + + memcpy(args, app, len*sizeof(WCHAR) + sizeof(controlW)); + memcpy(args + len + ARRAY_SIZE(controlW) - 1, argsW, sizeof(argsW)); + + TRACE("starting %s\n", debugstr_w(args)); + + memset(&si, 0, sizeof(si)); + si.cb = sizeof(si); + ret = CreateProcessW(app, args, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); + free(args); + if (ret) { + CloseHandle(pi.hThread); + WaitForSingleObject(pi.hProcess, INFINITE); + CloseHandle(pi.hProcess); + } + + return ret; +} + +static BOOL get_support_msi(LPCWSTR mono_path, LPWSTR msi_path) +{ + static const WCHAR support_msi_relative[] = {'\\','s','u','p','p','o','r','t','\\','w','i','n','e','m','o','n','o','-','s','u','p','p','o','r','t','.','m','s','i',0}; + UINT (WINAPI *pMsiOpenPackageW)(LPCWSTR,ULONG*); + UINT (WINAPI *pMsiGetProductPropertyA)(ULONG,LPCSTR,LPSTR,LPDWORD); + UINT (WINAPI *pMsiCloseHandle)(ULONG); + HMODULE hmsi = NULL; + char versionstringbuf[15]; + UINT res; + DWORD buffer_size; + ULONG msiproduct; + BOOL ret=FALSE; + + hmsi = GetModuleHandleA("msi"); + + lstrcpyW(msi_path, mono_path); + lstrcatW(msi_path, support_msi_relative); + + pMsiOpenPackageW = (void*)GetProcAddress(hmsi, "MsiOpenPackageW"); + + res = pMsiOpenPackageW(msi_path, &msiproduct); + + if (res == ERROR_SUCCESS) + { + buffer_size = sizeof(versionstringbuf); + + pMsiGetProductPropertyA = (void*)GetProcAddress(hmsi, "MsiGetProductPropertyA"); + + res = pMsiGetProductPropertyA(msiproduct, "ProductVersion", versionstringbuf, &buffer_size); + + pMsiCloseHandle = (void*)GetProcAddress(hmsi, "MsiCloseHandle"); + + pMsiCloseHandle(msiproduct); + } + + if (res == ERROR_SUCCESS) { + TRACE("found support msi version %s at %s\n", versionstringbuf, debugstr_w(msi_path)); + + if (compare_versions(WINE_MONO_VERSION, versionstringbuf) <= 0) + { + ret = TRUE; + } + } + + return ret; +} + +static BOOL install_wine_mono(void) +{ + BOOL is_wow64 = FALSE; + HMODULE hmsi = NULL; + HRESULT initresult = E_FAIL; + UINT (WINAPI *pMsiEnumRelatedProductsA)(LPCSTR,DWORD,DWORD,LPSTR); + UINT (WINAPI *pMsiGetProductInfoA)(LPCSTR,LPCSTR,LPSTR,DWORD*); + UINT (WINAPI *pMsiInstallProductW)(LPCWSTR,LPCWSTR); + char versionstringbuf[15]; + char productcodebuf[39]; + UINT res; + DWORD buffer_size; + BOOL ret; + WCHAR mono_path[MAX_PATH]; + WCHAR support_msi_path[MAX_PATH]; + + static const char* mono_upgrade_code = "{DE624609-C6B5-486A-9274-EF0B854F6BC5}"; + + IsWow64Process(GetCurrentProcess(), &is_wow64); + + if (is_wow64) + { + TRACE("not installing mono in wow64 process\n"); + return TRUE; + } + + TRACE("searching for mono runtime\n"); + + if (!get_mono_path(mono_path, FALSE)) + { + TRACE("mono runtime not found\n"); + return invoke_appwiz(); + } + + TRACE("mono runtime is at %s\n", debugstr_w(mono_path)); + + hmsi = LoadLibraryA("msi"); + + if (!hmsi) + { + ERR("couldn't load msi.dll\n"); + return FALSE; + } + + pMsiEnumRelatedProductsA = (void*)GetProcAddress(hmsi, "MsiEnumRelatedProductsA"); + + res = pMsiEnumRelatedProductsA(mono_upgrade_code, 0, 0, productcodebuf); + + if (res == ERROR_SUCCESS) + { + pMsiGetProductInfoA = (void*)GetProcAddress(hmsi, "MsiGetProductInfoA"); + + buffer_size = sizeof(versionstringbuf); + + res = pMsiGetProductInfoA(productcodebuf, "VersionString", versionstringbuf, &buffer_size); + } + else if (res != ERROR_NO_MORE_ITEMS) + { + ERR("MsiEnumRelatedProducts failed, err=%u\n", res); + } + + if (res == ERROR_SUCCESS) + { + TRACE("found installed support package %s\n", versionstringbuf); + + if (compare_versions(WINE_MONO_VERSION, versionstringbuf) <= 0) + { + TRACE("support package is at least %s, quitting\n", WINE_MONO_VERSION); + ret = TRUE; + goto end; + } + } + + initresult = CoInitialize(NULL); + + ret = get_support_msi(mono_path, support_msi_path); + if (!ret) + { + /* Try looking outside c:\windows\mono */ + ret = (get_mono_path(mono_path, TRUE) && + get_support_msi(mono_path, support_msi_path)); + } + + if (ret) + { + TRACE("installing support msi\n"); + + pMsiInstallProductW = (void*)GetProcAddress(hmsi, "MsiInstallProductW"); + + res = pMsiInstallProductW(support_msi_path, NULL); + + if (res == ERROR_SUCCESS) + { + ret = TRUE; + goto end; + } + else + ERR("MsiInstallProduct failed, err=%i\n", res); + } + + ret = invoke_appwiz(); + +end: + if (hmsi) + FreeLibrary(hmsi); + if (SUCCEEDED(initresult)) + CoUninitialize(); + return ret; +} + HRESULT WINAPI DllRegisterServer(void) { - return __wine_register_resources( MSCOREE_hInstance ); + install_wine_mono(); + + return __wine_register_resources(); } HRESULT WINAPI DllUnregisterServer(void) { - return __wine_unregister_resources( MSCOREE_hInstance ); -} - -HRESULT WINAPI DllCanUnloadNow(VOID) -{ - return S_FALSE; + return __wine_unregister_resources(); } void WINAPI CoEEShutDownCOM(void) diff --git a/dll/win32/mscoree/mscoree_private.h b/dll/win32/mscoree/mscoree_private.h index eb533bf72da..eb7c75bacb9 100644 --- a/dll/win32/mscoree/mscoree_private.h +++ b/dll/win32/mscoree/mscoree_private.h @@ -20,34 +20,13 @@ #ifndef __MSCOREE_PRIVATE__ #define __MSCOREE_PRIVATE__ -#include +extern char *WtoA(const WCHAR *wstr) __WINE_DEALLOC(free) __WINE_MALLOC; -#define WIN32_NO_STATUS -#define _INC_WINDOWS -#define COM_NO_WINDOWS_H - -#define COBJMACROS - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -WINE_DEFAULT_DEBUG_CHANNEL( mscoree ); - -extern char *WtoA(LPCWSTR wstr) DECLSPEC_HIDDEN; - -extern HRESULT CLRMetaHost_CreateInstance(REFIID riid, void **ppobj) DECLSPEC_HIDDEN; +extern HRESULT CLRMetaHost_CreateInstance(REFIID riid, void **ppobj); +extern HRESULT CLRMetaHostPolicy_CreateInstance(REFIID riid, void **ppobj); extern HRESULT WINAPI CLRMetaHost_GetVersionFromFile(ICLRMetaHost* iface, - LPCWSTR pwzFilePath, LPWSTR pwzBuffer, DWORD *pcchBuffer) DECLSPEC_HIDDEN; + LPCWSTR pwzFilePath, LPWSTR pwzBuffer, DWORD *pcchBuffer); typedef struct _VTableFixup { DWORD rva; @@ -57,11 +36,16 @@ typedef struct _VTableFixup { typedef struct tagASSEMBLY ASSEMBLY; -extern HRESULT assembly_create(ASSEMBLY **out, LPCWSTR file) DECLSPEC_HIDDEN; -extern HRESULT assembly_from_hmodule(ASSEMBLY **out, HMODULE hmodule) DECLSPEC_HIDDEN; -extern HRESULT assembly_release(ASSEMBLY *assembly) DECLSPEC_HIDDEN; -extern HRESULT assembly_get_runtime_version(ASSEMBLY *assembly, LPSTR *version) DECLSPEC_HIDDEN; -extern HRESULT assembly_get_vtable_fixups(ASSEMBLY *assembly, VTableFixup **fixups, DWORD *count) DECLSPEC_HIDDEN; +typedef BOOL (WINAPI *NativeEntryPointFunc)(HINSTANCE, DWORD, LPVOID); + +extern HRESULT assembly_create(ASSEMBLY **out, LPCWSTR file); +extern HRESULT assembly_from_hmodule(ASSEMBLY **out, HMODULE hmodule); +extern HRESULT assembly_release(ASSEMBLY *assembly); +extern HRESULT assembly_get_runtime_version(ASSEMBLY *assembly, LPSTR *version); +extern HRESULT assembly_get_vtable_fixups(ASSEMBLY *assembly, VTableFixup **fixups, DWORD *count); +extern HRESULT assembly_get_native_entrypoint(ASSEMBLY *assembly, NativeEntryPointFunc *func); + +#define WINE_MONO_VERSION "9.4.0" /* Mono embedding */ typedef struct _MonoDomain MonoDomain; @@ -76,19 +60,14 @@ typedef struct _MonoMethod MonoMethod; typedef struct _MonoProfiler MonoProfiler; typedef struct _MonoThread MonoThread; -typedef struct loaded_mono loaded_mono; typedef struct RuntimeHost RuntimeHost; typedef struct CLRRuntimeInfo { ICLRRuntimeInfo ICLRRuntimeInfo_iface; - LPCWSTR mono_libdir; DWORD major; DWORD minor; DWORD build; - int mono_abi_version; - WCHAR mono_path[MAX_PATH]; - WCHAR mscorlib_path[MAX_PATH]; struct RuntimeHost *loaded_runtime; } CLRRuntimeInfo; @@ -96,10 +75,7 @@ struct RuntimeHost { ICorRuntimeHost ICorRuntimeHost_iface; ICLRRuntimeHost ICLRRuntimeHost_iface; - const CLRRuntimeInfo *version; - loaded_mono *mono; - struct list domains; - MonoDomain *default_domain; + CLRRuntimeInfo *version; CRITICAL_SECTION lock; LONG ref; }; @@ -127,15 +103,21 @@ typedef struct CorDebug } CorDebug; extern HRESULT get_runtime_info(LPCWSTR exefile, LPCWSTR version, LPCWSTR config_file, - DWORD startup_flags, DWORD runtimeinfo_flags, BOOL legacy, ICLRRuntimeInfo **result) DECLSPEC_HIDDEN; + IStream *config_stream, DWORD startup_flags, DWORD runtimeinfo_flags, BOOL legacy, + ICLRRuntimeInfo **result); -extern HRESULT ICLRRuntimeInfo_GetRuntimeHost(ICLRRuntimeInfo *iface, RuntimeHost **result) DECLSPEC_HIDDEN; +extern BOOL get_mono_path(LPWSTR path, BOOL skip_local); -extern HRESULT MetaDataDispenser_CreateInstance(IUnknown **ppUnk) DECLSPEC_HIDDEN; +extern MonoDomain* get_root_domain(void); + +extern HRESULT ICLRRuntimeInfo_GetRuntimeHost(ICLRRuntimeInfo *iface, RuntimeHost **result); + +extern HRESULT MetaDataDispenser_CreateInstance(IUnknown **ppUnk); typedef struct parsed_config_file { struct list supported_runtimes; + LPWSTR private_path; } parsed_config_file; typedef struct supported_runtime @@ -144,9 +126,13 @@ typedef struct supported_runtime LPWSTR version; } supported_runtime; -extern HRESULT parse_config_file(LPCWSTR filename, parsed_config_file *result) DECLSPEC_HIDDEN; +extern WCHAR **private_path; -extern void free_parsed_config_file(parsed_config_file *file) DECLSPEC_HIDDEN; +extern HRESULT parse_config_file(LPCWSTR filename, parsed_config_file *result); + +extern HRESULT parse_config_stream(IStream *stream, parsed_config_file *result); + +extern void free_parsed_config_file(parsed_config_file *file); typedef enum { MONO_IMAGE_OK, @@ -155,75 +141,93 @@ typedef enum { MONO_IMAGE_IMAGE_INVALID } MonoImageOpenStatus; -typedef MonoAssembly* (*MonoAssemblyPreLoadFunc)(MonoAssemblyName *aname, char **assemblies_path, void *user_data); +typedef MonoAssembly* (CDECL *MonoAssemblyPreLoadFunc)(MonoAssemblyName *aname, char **assemblies_path, void *user_data); -typedef void (*MonoProfileFunc)(MonoProfiler *prof); +#define WINE_PRELOAD_CONTINUE 0 +#define WINE_PRELOAD_SKIP_PRIVATE_PATH 1 +#define WINE_PRELOAD_SKIP_GAC 2 +#define WINE_PRELOAD_SET_GAC 4 -struct loaded_mono -{ - HMODULE mono_handle; - HMODULE glib_handle; +typedef MonoAssembly* (CDECL *WineMonoAssemblyPreLoadFunc)(MonoAssemblyName *aname, char **assemblies_path, int *flags, void *user_data); - BOOL is_started; - BOOL is_shutdown; +typedef void (CDECL *MonoProfileFunc)(MonoProfiler *prof); - MonoImage* (CDECL *mono_assembly_get_image)(MonoAssembly *assembly); - MonoAssembly* (CDECL *mono_assembly_load_from)(MonoImage *image, const char *fname, MonoImageOpenStatus *status); - MonoAssembly* (CDECL *mono_assembly_open)(const char *filename, MonoImageOpenStatus *status); - MonoClass* (CDECL *mono_class_from_mono_type)(MonoType *type); - MonoClass* (CDECL *mono_class_from_name)(MonoImage *image, const char* name_space, const char *name); - MonoMethod* (CDECL *mono_class_get_method_from_name)(MonoClass *klass, const char *name, int param_count); - void (CDECL *mono_config_parse)(const char *filename); - MonoAssembly* (CDECL *mono_domain_assembly_open) (MonoDomain *domain, const char *name); - void (CDECL *mono_free)(void *); - MonoImage* (CDECL *mono_image_open_from_module_handle)(HMODULE module_handle, char* fname, UINT has_entry_point, MonoImageOpenStatus* status); - void (CDECL *mono_install_assembly_preload_hook)(MonoAssemblyPreLoadFunc func, void *user_data); - int (CDECL *mono_jit_exec)(MonoDomain *domain, MonoAssembly *assembly, int argc, char *argv[]); - MonoDomain* (CDECL *mono_jit_init)(const char *file); - int (CDECL *mono_jit_set_trace_options)(const char* options); - void* (CDECL *mono_marshal_get_vtfixup_ftnptr)(MonoImage *image, DWORD token, WORD type); - MonoDomain* (CDECL *mono_object_get_domain)(MonoObject *obj); - MonoObject* (CDECL *mono_object_new)(MonoDomain *domain, MonoClass *klass); - void* (CDECL *mono_object_unbox)(MonoObject *obj); - void (CDECL *mono_profiler_install)(MonoProfiler *prof, MonoProfileFunc shutdown_callback); - MonoType* (CDECL *mono_reflection_type_from_name)(char *name, MonoImage *image); - MonoObject* (CDECL *mono_runtime_invoke)(MonoMethod *method, void *obj, void **params, MonoObject **exc); - void (CDECL *mono_runtime_object_init)(MonoObject *this_obj); - void (CDECL *mono_runtime_quit)(void); - void (CDECL *mono_runtime_set_shutting_down)(void); - void (CDECL *mono_set_dirs)(const char *assembly_dir, const char *config_dir); - char* (CDECL *mono_stringify_assembly_name)(MonoAssemblyName *aname); - void (CDECL *mono_thread_pool_cleanup)(void); - void (CDECL *mono_thread_suspend_all_other_threads)(void); - void (CDECL *mono_threads_set_shutting_down)(void); - MonoString* (CDECL *mono_string_new)(MonoDomain *domain, const char *str); - MonoThread* (CDECL *mono_thread_attach)(MonoDomain *domain); -}; +typedef void (CDECL *MonoPrintCallback) (const char *string, INT is_stdout); +typedef void (*MonoLogCallback) (const char *log_domain, const char *log_level, const char *message, INT fatal, void *user_data); + +typedef enum { + MONO_AOT_MODE_NONE, + MONO_AOT_MODE_NORMAL, + MONO_AOT_MODE_HYBRID, + MONO_AOT_MODE_FULL, + MONO_AOT_MODE_LLVMONLY, + MONO_AOT_MODE_INTERP, + MONO_AOT_MODE_INTERP_LLVMONLY, + MONO_AOT_MODE_LLVMONLY_INTERP, + MONO_AOT_MODE_INTERP_ONLY +} MonoAotMode; + +extern BOOL is_mono_started; + +extern MonoImage* (CDECL *mono_assembly_get_image)(MonoAssembly *assembly); +extern MonoAssembly* (CDECL *mono_assembly_load_from)(MonoImage *image, const char *fname, MonoImageOpenStatus *status); +extern const char* (CDECL *mono_assembly_name_get_name)(MonoAssemblyName *aname); +extern MonoAssembly* (CDECL *mono_assembly_open)(const char *filename, MonoImageOpenStatus *status); +extern void (CDECL *mono_callspec_set_assembly)(MonoAssembly *assembly); +extern MonoClass* (CDECL *mono_class_from_mono_type)(MonoType *type); +extern MonoClass* (CDECL *mono_class_from_name)(MonoImage *image, const char* name_space, const char *name); +extern MonoMethod* (CDECL *mono_class_get_method_from_name)(MonoClass *klass, const char *name, int param_count); +extern MonoDomain* (CDECL *mono_domain_get)(void); +extern MonoDomain* (CDECL *mono_domain_get_by_id)(int id); +extern BOOL (CDECL *mono_domain_set)(MonoDomain *domain, BOOL force); +extern void (CDECL *mono_domain_set_config)(MonoDomain *domain,const char *base_dir,const char *config_file_name); +extern MonoImage* (CDECL *mono_get_corlib)(void); +extern int (CDECL *mono_jit_exec)(MonoDomain *domain, MonoAssembly *assembly, int argc, char *argv[]); +extern MonoDomain* (CDECL *mono_jit_init_version)(const char *domain_name, const char *runtime_version); +extern MonoImage* (CDECL *mono_image_open_from_module_handle)(HMODULE module_handle, char* fname, UINT has_entry_point, MonoImageOpenStatus* status); +extern void* (CDECL *mono_marshal_get_vtfixup_ftnptr)(MonoImage *image, DWORD token, WORD type); +extern MonoDomain* (CDECL *mono_object_get_domain)(MonoObject *obj); +extern MonoMethod* (CDECL *mono_object_get_virtual_method)(MonoObject *obj, MonoMethod *method); +extern MonoObject* (CDECL *mono_object_new)(MonoDomain *domain, MonoClass *klass); +extern void* (CDECL *mono_object_unbox)(MonoObject *obj); +extern MonoType* (CDECL *mono_reflection_type_from_name)(char *name, MonoImage *image); +extern MonoObject* (CDECL *mono_runtime_invoke)(MonoMethod *method, void *obj, void **params, MonoObject **exc); +extern void (CDECL *mono_runtime_object_init)(MonoObject *this_obj); +extern void (CDECL *mono_runtime_quit)(void); +extern MonoString* (CDECL *mono_string_new)(MonoDomain *domain, const char *str); +extern MonoThread* (CDECL *mono_thread_attach)(MonoDomain *domain); +extern void (CDECL *mono_thread_manage)(void); +extern void (CDECL *mono_trace_set_print_handler)(MonoPrintCallback callback); +extern void (CDECL *mono_trace_set_printerr_handler)(MonoPrintCallback callback); /* loaded runtime interfaces */ -extern void unload_all_runtimes(void) DECLSPEC_HIDDEN; +extern void expect_no_runtimes(void); -extern void expect_no_runtimes(void) DECLSPEC_HIDDEN; +extern HRESULT RuntimeHost_Construct(CLRRuntimeInfo *runtime_version, RuntimeHost** result); -extern HRESULT RuntimeHost_Construct(const CLRRuntimeInfo *runtime_version, - loaded_mono *loaded_mono, RuntimeHost** result) DECLSPEC_HIDDEN; +extern void RuntimeHost_ExitProcess(RuntimeHost *This, INT exitcode); -extern HRESULT RuntimeHost_GetInterface(RuntimeHost *This, REFCLSID clsid, REFIID riid, void **ppv) DECLSPEC_HIDDEN; +extern HRESULT RuntimeHost_GetInterface(RuntimeHost *This, REFCLSID clsid, REFIID riid, void **ppv); -extern HRESULT RuntimeHost_GetIUnknownForObject(RuntimeHost *This, MonoObject *obj, IUnknown **ppUnk) DECLSPEC_HIDDEN; +extern HRESULT RuntimeHost_GetIUnknownForObject(RuntimeHost *This, MonoObject *obj, IUnknown **ppUnk); extern HRESULT RuntimeHost_CreateManagedInstance(RuntimeHost *This, LPCWSTR name, - MonoDomain *domain, MonoObject **result) DECLSPEC_HIDDEN; + MonoDomain *domain, MonoObject **result); -extern HRESULT RuntimeHost_Destroy(RuntimeHost *This) DECLSPEC_HIDDEN; +HRESULT WINAPI CLRMetaHost_ExitProcess(ICLRMetaHost* iface, INT32 iExitCode); -HRESULT WINAPI CLRMetaHost_GetRuntime(ICLRMetaHost* iface, LPCWSTR pwzVersion, REFIID iid, LPVOID *ppRuntime) DECLSPEC_HIDDEN; +HRESULT WINAPI CLRMetaHost_GetRuntime(ICLRMetaHost* iface, LPCWSTR pwzVersion, REFIID iid, LPVOID *ppRuntime); -extern HRESULT CorDebug_Create(ICLRRuntimeHost *runtimehost, IUnknown** ppUnk) DECLSPEC_HIDDEN; +extern HRESULT CorDebug_Create(ICLRRuntimeHost *runtimehost, IUnknown** ppUnk); -extern HRESULT create_monodata(REFIID riid, LPVOID *ppObj) DECLSPEC_HIDDEN; +extern HRESULT create_monodata(REFCLSID clsid, LPVOID *ppObj); + +extern HRESULT get_file_from_strongname(WCHAR* stringnameW, WCHAR* assemblies_path, int path_length); extern void runtimehost_init(void); extern void runtimehost_uninit(void); +extern void CDECL mono_print_handler_fn(const char *string, INT is_stdout); +extern void CDECL mono_log_handler_fn(const char *log_domain, const char *log_level, const char *message, INT fatal, void *user_data); + #endif /* __MSCOREE_PRIVATE__ */ diff --git a/dll/win32/mscoree/reactos.c b/dll/win32/mscoree/reactos.c new file mode 100644 index 00000000000..0af0eda9c2c --- /dev/null +++ b/dll/win32/mscoree/reactos.c @@ -0,0 +1,6 @@ +#include + +HRESULT WINAPI DllCanUnloadNow(void) +{ + return S_FALSE; +} diff --git a/modules/rostests/winetests/mscoree/CMakeLists.txt b/modules/rostests/winetests/mscoree/CMakeLists.txt index 0e3cd68b2f7..89f6a4bdd1c 100644 --- a/modules/rostests/winetests/mscoree/CMakeLists.txt +++ b/modules/rostests/winetests/mscoree/CMakeLists.txt @@ -1,13 +1,18 @@ +remove_definitions(-D_CRT_NON_CONFORMING_SWPRINTFS) add_definitions(-DUSE_WINE_TODOS) add_executable(mscoree_winetest debugging.c metahost.c mscoree.c - testlist.c) + testlist.c + resource.rc) + +add_idl_headers(mscoree_winetest_idlheader interfaces.idl) target_link_libraries(mscoree_winetest wine uuid) set_module_type(mscoree_winetest win32cui) add_importlibs(mscoree_winetest ole32 shlwapi msvcrt kernel32 ntdll) +add_dependencies(mscoree_winetest mscoree_winetest_idlheader) add_rostests_file(TARGET mscoree_winetest) diff --git a/modules/rostests/winetests/mscoree/comtest.c b/modules/rostests/winetests/mscoree/comtest.c new file mode 100644 index 00000000000..2881fdb8943 --- /dev/null +++ b/modules/rostests/winetests/mscoree/comtest.c @@ -0,0 +1,417 @@ +/* + * Copyright 2018 Fabian Maurer + * + * 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 "windows.h" +#include "ole2.h" +#include "mscoree.h" +#include "corerror.h" +#include "shlwapi.h" +#include "shlobj.h" + +#include "wine/test.h" + +#include "initguid.h" +#include "interfaces.h" + +HMODULE hmscoree; + +DEFINE_GUID(IID_ITest2, 0x50adb433, 0xf6c5, 0x3b30, 0x92,0x0a, 0x55,0x57,0x11,0x86,0x75,0x09); + +typedef enum _run_type +{ + run_type_current_working_directory = 0, + run_type_exe_directory, + run_type_system32, +} run_type; + +static BOOL write_resource_file(const char *path_tmp, const char *name_res, const char *name_file, char *path_file) +{ + HRSRC rsrc; + void *rsrc_data; + DWORD rsrc_size; + BOOL ret; + HANDLE hfile; + + rsrc = FindResourceA(GetModuleHandleA(NULL), name_res, (LPCSTR)RT_RCDATA); + if (!rsrc) return FALSE; + + rsrc_data = LockResource(LoadResource(GetModuleHandleA(NULL), rsrc)); + if (!rsrc_data) return FALSE; + + rsrc_size = SizeofResource(GetModuleHandleA(NULL), rsrc); + if (!rsrc_size) return FALSE; + + strcpy(path_file, path_tmp); + PathAppendA(path_file, name_file); + hfile = CreateFileA(path_file, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); + if (hfile == INVALID_HANDLE_VALUE) return FALSE; + + ret = WriteFile(hfile, rsrc_data, rsrc_size, &rsrc_size, NULL); + + CloseHandle(hfile); + return ret; +} + +static BOOL compile_cs_to_dll(char *source_path, char *dest_path) +{ + const char *path_csc = "C:\\windows\\Microsoft.NET\\Framework\\v2.0.50727\\csc.exe"; + char cmdline[2 * MAX_PATH + 74]; + char path_temp[MAX_PATH]; + PROCESS_INFORMATION pi; + STARTUPINFOA si = { 0 }; + BOOL ret; + + if (!PathFileExistsA(path_csc)) + { + skip("Can't find csc.exe\n"); + return FALSE; + } + + GetTempPathA(MAX_PATH, path_temp); + PathAppendA(path_temp, "comtest.dll"); + + sprintf(cmdline, "%s /t:library /out:\"%s\" \"%s\"", path_csc, path_temp, source_path); + + si.cb = sizeof(si); + ret = CreateProcessA(path_csc, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); + ok(ret, "Could not create process: %lu\n", GetLastError()); + + wait_child_process(pi.hProcess); + CloseHandle(pi.hThread); + CloseHandle(pi.hProcess); + + ret = PathFileExistsA(path_temp); + ok(ret, "Compilation failed\n"); + + ret = MoveFileA(path_temp, dest_path); + ok(ret, "Could not move %s to %s: %lu\n", path_temp, dest_path, GetLastError()); + return ret; +} + +static void run_test(BOOL expect_success) +{ + typedef HRESULT (WINAPI *_DllGetClassObject)(REFCLSID rclsid, REFIID riid, LPVOID *ppv); + ITest *test = NULL; + HRESULT hr; + _DllGetClassObject getClassObject; + IClassFactory *classFactory = NULL; + HRESULT result_expected = expect_success ? S_OK : HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + + hr = CoCreateInstance(&CLSID_Test, NULL, CLSCTX_INPROC_SERVER, &IID_ITest, (void**)&test); + todo_wine_if(!expect_success) + ok(hr == result_expected, "Expected %lx, got %lx\n", result_expected, hr); + + if (hr == S_OK) + { + int i = 0; + hr = ITest_Func(test, &i); + ok(hr == S_OK, "Got %lx\n", hr); + ok(i == 42, "Expected 42, got %d\n", i); + ITest_Release(test); + } + + getClassObject = (_DllGetClassObject)GetProcAddress(hmscoree, "DllGetClassObject"); + hr = getClassObject(&CLSID_Test, &IID_IClassFactory, (void **)&classFactory); + todo_wine_if(!expect_success) + ok(hr == result_expected, "Expected %lx, got %lx\n", result_expected, hr); + + if (hr == S_OK) + { + ITest *test2 = NULL; + hr = IClassFactory_CreateInstance(classFactory, NULL, &IID_ITest, (void **)&test2); + todo_wine_if(!expect_success) + ok(hr == S_OK, "Got %lx\n", hr); + + if (hr == S_OK) + { + int i = 0; + hr = ITest_Func(test2, &i); + ok(hr == S_OK, "Got %lx\n", hr); + ok(i == 42, "Expected 42, got %d\n", i); + ITest_Release(test2); + } + IClassFactory_Release(classFactory); + } + +} + +static void run_registry_test(run_type run) +{ + char buffer[256]; + ITest *test = NULL; + HRESULT hr, result_expected; + IUnknown *unk = NULL; + HKEY hkey; + DWORD ret; + int i = 0; + + if (run == run_type_exe_directory) result_expected = S_OK; + else result_expected = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + + sprintf(buffer, "CLSID\\%s", wine_dbgstr_guid(&CLSID_Test)); + ret = RegCreateKeyA( HKEY_CLASSES_ROOT, buffer, &hkey ); + if (ret == ERROR_ACCESS_DENIED && !IsUserAnAdmin()) + { + win_skip("cannot run the registry tests due to user not being admin\n"); + RegCloseKey(hkey); + return; + } + ok(ret == ERROR_SUCCESS, "RegCreateKeyA returned %lx\n", ret); + + ret = RegSetKeyValueA(hkey, "InprocServer32", NULL, REG_SZ, "mscoree.dll", 11); + ok(ret == ERROR_SUCCESS, "RegSetKeyValueA returned %lx\n", ret); + ret = RegSetKeyValueA(hkey, "InprocServer32", "Assembly", REG_SZ, "comtest, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", 74); + ok(ret == ERROR_SUCCESS, "RegSetKeyValueA returned %lx\n", ret); + ret = RegSetKeyValueA(hkey, "InprocServer32", "Class", REG_SZ, "DLL.Test", 8); + ok(ret == ERROR_SUCCESS, "RegSetKeyValueA returned %lx\n", ret); + ret = RegSetKeyValueA(hkey, "InprocServer32", "CodeBase", REG_SZ, "file:///U:/invalid/path/to/comtest.dll", 41); + ok(ret == ERROR_SUCCESS, "RegSetKeyValueA returned %lx\n", ret); + + hr = CoCreateInstance(&CLSID_Test, NULL, CLSCTX_INPROC_SERVER, &IID_ITest, (void**)&test); + todo_wine_if(result_expected != S_OK) + ok(hr == result_expected, "Expected %lx, got %lx\n", result_expected, hr); + + if (hr == S_OK) + { + hr = ITest_Func(test, &i); + ok(hr == S_OK, "Got %lx\n", hr); + ok(i == 42, "Expected 42, got %d\n", i); + hr = ITest_QueryInterface(test, &IID_ITest2, (void**)&unk); + ok(hr == S_OK, "ITest_QueryInterface returned %lx\n", hr); + if (hr == S_OK) IUnknown_Release(unk); + ITest_Release(test); + } + + RegDeleteKeyValueA(hkey, "InprocServer32", "CodeBase"); + RegDeleteKeyValueA(hkey, "InprocServer32", "Class"); + RegDeleteKeyValueA(hkey, "InprocServer32", "Assembly"); + RegDeleteKeyValueA(hkey, "InprocServer32", NULL); + RegDeleteKeyA(hkey, "InprocServer32"); + RegCloseKey(hkey); +} + +static void get_dll_path_for_run(char *path_dll, UINT path_dll_size, run_type run) +{ + char path_tmp[MAX_PATH]; + + GetTempPathA(MAX_PATH, path_tmp); + + switch (run) + { + case run_type_current_working_directory: + strcpy(path_dll, path_tmp); + PathAppendA(path_dll, "comtest.dll"); + break; + case run_type_exe_directory: + GetModuleFileNameA(NULL, path_dll, path_dll_size); + PathRemoveFileSpecA(path_dll); + PathAppendA(path_dll, "comtest.dll"); + break; + case run_type_system32: + GetSystemDirectoryA(path_dll, path_dll_size); + PathAppendA(path_dll, "comtest.dll"); + break; + } +} +static void prepare_and_run_test(const char *dll_source, run_type run) +{ + char path_tmp[MAX_PATH]; + char path_tmp_manifest[MAX_PATH]; + char path_dll[MAX_PATH]; + char path_dll_source[MAX_PATH]; + char path_manifest_dll[MAX_PATH]; + char path_manifest_exe[MAX_PATH]; + BOOL success; + ACTCTXA context = {0}; + ULONG_PTR cookie; + HANDLE handle_context = 0; + + path_manifest_exe[0] = path_manifest_dll[0] = path_dll_source[0] = 0; + + GetTempPathA(MAX_PATH, path_tmp); + GetTempPathA(MAX_PATH, path_tmp_manifest); + PathAppendA(path_tmp_manifest, "manifests"); + + CreateDirectoryA(path_tmp_manifest, NULL); + + if (run == run_type_system32) + { + if (!IsUserAnAdmin()) + { + skip("Can't test dll in system32 due to user not being admin.\n"); + return; + } + } + + if (!write_resource_file(path_tmp, dll_source, "comtest.cs", path_dll_source)) + { + ok(0, "run: %d, Failed to create file for testing\n", run); + goto cleanup; + } + + get_dll_path_for_run(path_dll, sizeof(path_dll), run); + + if (!compile_cs_to_dll(path_dll_source, path_dll)) + goto cleanup; + + if (!write_resource_file(path_tmp_manifest, "comtest_exe.manifest", "exe.manifest", path_manifest_exe)) + { + ok(0, "run: %d, Failed to create file for testing\n", run); + goto cleanup; + } + + if (!write_resource_file(path_tmp_manifest, "comtest_dll.manifest", "comtest.manifest", path_manifest_dll)) + { + ok(0, "run: %d, Failed to create file for testing\n", run); + goto cleanup; + } + + context.cbSize = sizeof(ACTCTXA); + context.lpSource = path_manifest_exe; + context.lpAssemblyDirectory = path_tmp_manifest; + context.dwFlags = ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID; + + handle_context = CreateActCtxA(&context); + ok(handle_context != NULL && handle_context != INVALID_HANDLE_VALUE, "run: %d, CreateActCtxA failed: %ld\n", run, GetLastError()); + + if (handle_context == NULL || handle_context == INVALID_HANDLE_VALUE) + { + ok(0, "run: %d, Failed to create activation context\n", run); + goto cleanup; + } + + success = ActivateActCtx(handle_context, &cookie); + ok(success, "run: %d, ActivateActCtx failed: %ld\n", run, GetLastError()); + + if (run == run_type_current_working_directory) + SetCurrentDirectoryA(path_tmp); + + run_test(run == run_type_exe_directory); + run_registry_test(run); + +cleanup: + if (handle_context != NULL && handle_context != INVALID_HANDLE_VALUE) + { + success = DeactivateActCtx(0, cookie); + ok(success, "run: %d, DeactivateActCtx failed: %ld\n", run, GetLastError()); + ReleaseActCtx(handle_context); + } + if (*path_manifest_exe) + { + success = DeleteFileA(path_manifest_exe); + ok(success, "run: %d, DeleteFileA failed: %ld\n", run, GetLastError()); + } + if(*path_manifest_dll) + { + success = DeleteFileA(path_manifest_dll); + ok(success, "run: %d, DeleteFileA failed: %ld\n", run, GetLastError()); + } + if(*path_dll_source) + { + success = DeleteFileA(path_dll_source); + ok(success, "run: %d, DeleteFileA failed: %ld\n", run, GetLastError()); + } + RemoveDirectoryA(path_tmp_manifest); + /* dll cleanup is handled by the parent, because it might still be used by the child */ +} + + +static void cleanup_test(run_type run) +{ + char path_dll[MAX_PATH]; + BOOL success; + + get_dll_path_for_run(path_dll, sizeof(path_dll), run); + + if (!PathFileExistsA(path_dll)) + return; + + success = DeleteFileA(path_dll); + if (!success) + { + Sleep(500); + success = DeleteFileA(path_dll); + } + ok(success, "DeleteFileA failed: %ld\n", GetLastError()); +} + +static void run_child_process(const char *dll_source, run_type run) +{ + char cmdline[MAX_PATH]; + char exe[MAX_PATH]; + char **argv; + PROCESS_INFORMATION pi; + STARTUPINFOA si = { 0 }; + BOOL ret; + + winetest_get_mainargs(&argv); + + if (strstr(argv[0], ".exe")) + sprintf(exe, "%s", argv[0]); + else + sprintf(exe, "%s.exe", argv[0]); + sprintf(cmdline, "\"%s\" %s %s %d", argv[0], argv[1], dll_source, run); + + si.cb = sizeof(si); + ret = CreateProcessA(exe, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); + ok(ret, "Could not create process: %lu\n", GetLastError()); + + wait_child_process(pi.hProcess); + + CloseHandle(pi.hThread); + CloseHandle(pi.hProcess); + + /* Cleanup dll, because it might still have been used by the child */ + cleanup_test(run); +} + +START_TEST(comtest) +{ + int argc; + char **argv; + + CoInitialize(NULL); + + hmscoree = LoadLibraryA("mscoree.dll"); + if (!hmscoree) + { + skip(".NET or mono not available\n"); + return; + } + + argc = winetest_get_mainargs(&argv); + if (argc > 2) + { + const char *dll_source = argv[2]; + run_type run = atoi(argv[3]); + prepare_and_run_test(dll_source, run); + + goto cleanup; + } + + run_child_process("comtest.cs", run_type_current_working_directory); + run_child_process("comtest.cs", run_type_exe_directory); + run_child_process("comtest.cs", run_type_system32); + +cleanup: + FreeLibrary(hmscoree); + CoUninitialize(); +} diff --git a/modules/rostests/winetests/mscoree/comtest.cs b/modules/rostests/winetests/mscoree/comtest.cs new file mode 100644 index 00000000000..536cd807691 --- /dev/null +++ b/modules/rostests/winetests/mscoree/comtest.cs @@ -0,0 +1,51 @@ +/* + * Copyright 2018 Fabian Maurer + * + * 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 + */ + +/* Compile with + csc /target:library /out:dll.dll comtest.cs +*/ + +using System.Runtime.InteropServices; + +namespace DLL +{ + [Guid("1dbc4491-080d-45c5-a15d-1e3c4610bdd9"), ComVisible(true), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface ITest + { + void Func(ref int i); + } + + [ComVisible(true), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface ITest2 + { + void Func2(ref int i); + } + + [Guid("2e106e50-e7a4-4489-8538-83643f100fdc"), ComVisible(true), ClassInterface(ClassInterfaceType.None)] + public class Test : ITest, ITest2 + { + public void Func(ref int i) + { + i = 42; + } + public void Func2(ref int i) + { + i = 43; + } + } +} diff --git a/modules/rostests/winetests/mscoree/comtest_dll.manifest b/modules/rostests/winetests/mscoree/comtest_dll.manifest new file mode 100644 index 00000000000..ef6924de9e8 --- /dev/null +++ b/modules/rostests/winetests/mscoree/comtest_dll.manifest @@ -0,0 +1,16 @@ + + + + + + + + diff --git a/modules/rostests/winetests/mscoree/comtest_exe.manifest b/modules/rostests/winetests/mscoree/comtest_exe.manifest new file mode 100644 index 00000000000..bc9ce4c0457 --- /dev/null +++ b/modules/rostests/winetests/mscoree/comtest_exe.manifest @@ -0,0 +1,11 @@ + + + + + + + + diff --git a/modules/rostests/winetests/mscoree/debugging.c b/modules/rostests/winetests/mscoree/debugging.c index 020ee392a4e..5c17c27712c 100644 --- a/modules/rostests/winetests/mscoree/debugging.c +++ b/modules/rostests/winetests/mscoree/debugging.c @@ -44,7 +44,7 @@ static HRESULT WINAPI ManagedCallback2_QueryInterface(ICorDebugManagedCallback2 return S_OK; } - ok(0, "unexpected riid (%s)\n", debugstr_guid(riid)); + ok(0, "unexpected riid %s\n", wine_dbgstr_guid(riid)); *ppv = NULL; return E_NOINTERFACE; @@ -152,7 +152,7 @@ static HRESULT WINAPI ManagedCallback_QueryInterface(ICorDebugManagedCallback *i return S_OK; } - ok(0, "unexpected riid (%s)\n", debugstr_guid(riid)); + ok(0, "unexpected riid %s\n", wine_dbgstr_guid(riid)); *ppv = NULL; return E_NOINTERFACE; } @@ -410,17 +410,17 @@ static void _check_process_enum(unsigned line, ICorDebug *pCorDebug, ULONG nExpe ICorDebugProcessEnum *pProcessEnum = NULL; hr = ICorDebug_EnumerateProcesses(pCorDebug, NULL); - ok_(__FILE__,line) (hr == E_INVALIDARG, "expected E_INVALIDARG got %08x\n", hr); + ok_(__FILE__,line) (hr == E_INVALIDARG, "expected E_INVALIDARG got %08lx\n", hr); hr = ICorDebug_EnumerateProcesses(pCorDebug, &pProcessEnum); - ok_(__FILE__,line) (hr == S_OK, "expected S_OK got %08x\n", hr); + ok_(__FILE__,line) (hr == S_OK, "expected S_OK got %08lx\n", hr); if(hr == S_OK) { ULONG cnt; hr = ICorDebugProcessEnum_GetCount(pProcessEnum, &cnt); - ok_(__FILE__,line) (hr == S_OK, "expected S_OK got %08x\n", hr); - ok_(__FILE__,line) (cnt == nExpected, "expected %d got %d\n", nExpected, cnt); + ok_(__FILE__,line) (hr == S_OK, "expected S_OK got %08lx\n", hr); + ok_(__FILE__,line) (cnt == nExpected, "expected %ld got %ld\n", nExpected, cnt); ICorDebugProcessEnum_Release(pProcessEnum); } @@ -433,36 +433,36 @@ static void test_createDebugger(void) ICorDebug *pCorDebug; hr = pCreateDebuggingInterfaceFromVersion(0, v2_0, &pUnk); - ok(hr == E_INVALIDARG, "CreateDebuggingInterfaceFromVersion returned %08x\n", hr); + ok(hr == E_INVALIDARG, "CreateDebuggingInterfaceFromVersion returned %08lx\n", hr); hr = pCreateDebuggingInterfaceFromVersion(1, v2_0, &pUnk); - ok(hr == E_INVALIDARG, "CreateDebuggingInterfaceFromVersion returned %08x\n", hr); + ok(hr == E_INVALIDARG, "CreateDebuggingInterfaceFromVersion returned %08lx\n", hr); hr = pCreateDebuggingInterfaceFromVersion(2, v2_0, &pUnk); - ok(hr == E_INVALIDARG, "CreateDebuggingInterfaceFromVersion returned %08x\n", hr); + ok(hr == E_INVALIDARG, "CreateDebuggingInterfaceFromVersion returned %08lx\n", hr); hr = pCreateDebuggingInterfaceFromVersion(4, v2_0, &pUnk); - ok(hr == E_INVALIDARG, "CreateDebuggingInterfaceFromVersion returned %08x\n", hr); + ok(hr == E_INVALIDARG, "CreateDebuggingInterfaceFromVersion returned %08lx\n", hr); hr = pCreateDebuggingInterfaceFromVersion(3, v2_0, NULL); - ok(hr == E_INVALIDARG, "CreateDebuggingInterfaceFromVersion returned %08x\n", hr); + ok(hr == E_INVALIDARG, "CreateDebuggingInterfaceFromVersion returned %08lx\n", hr); hr = pCreateDebuggingInterfaceFromVersion(3, v2_0, &pUnk); if(hr == S_OK) { hr = IUnknown_QueryInterface(pUnk, &IID_ICorDebug, (void**)&pCorDebug); - ok(hr == S_OK, "expected S_OK got %08x\n", hr); + ok(hr == S_OK, "expected S_OK got %08lx\n", hr); if(hr == S_OK) { hr = ICorDebug_Initialize(pCorDebug); - ok(hr == S_OK, "expected S_OK got %08x\n", hr); + ok(hr == S_OK, "expected S_OK got %08lx\n", hr); if(hr == S_OK) { hr = ICorDebug_SetManagedHandler(pCorDebug, NULL); - ok(hr == E_INVALIDARG, "expected E_INVALIDARG got %08x\n", hr); + ok(hr == E_INVALIDARG, "expected E_INVALIDARG got %08lx\n", hr); hr = ICorDebug_SetManagedHandler(pCorDebug, &ManagedCallback); - ok(hr == S_OK, "expected S_OK got %08x\n", hr); + ok(hr == S_OK, "expected S_OK got %08lx\n", hr); /* We should have no processes */ check_process_enum(pCorDebug, 0); diff --git a/modules/rostests/winetests/mscoree/interfaces.idl b/modules/rostests/winetests/mscoree/interfaces.idl new file mode 100644 index 00000000000..6f8ac615bf2 --- /dev/null +++ b/modules/rostests/winetests/mscoree/interfaces.idl @@ -0,0 +1,35 @@ +/* + * Copyright 2018 Fabian Maurer + * + * 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 + */ + +#pragma makedep header + +#include "unknwn.idl" + +[ + object, + uuid(1dbc4491-080d-45c5-a15d-1e3c4610bdd9), + local +] +interface ITest : IUnknown { + HRESULT Func([in, out] int *i); +}; + +[ + uuid(2e106e50-e7a4-4489-8538-83643f100fdc), +] +coclass Test { interface ITest; }; diff --git a/modules/rostests/winetests/mscoree/loadpaths.dll.cs b/modules/rostests/winetests/mscoree/loadpaths.dll.cs new file mode 100644 index 00000000000..99732b011d8 --- /dev/null +++ b/modules/rostests/winetests/mscoree/loadpaths.dll.cs @@ -0,0 +1,33 @@ +/* + * Copyright 2021 Rémi Bernon for CodeWeavers + * + * 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 + */ + +using System.Reflection; + +#if NEUTRAL +[assembly: AssemblyCulture("")] +#else +[assembly: AssemblyCulture("en")] +#endif + +namespace LoadPaths +{ + public class Test2 + { + public int Foo() { return 0; } + } +} diff --git a/modules/rostests/winetests/mscoree/loadpaths.exe.config b/modules/rostests/winetests/mscoree/loadpaths.exe.config new file mode 100644 index 00000000000..be6d9fb95cd --- /dev/null +++ b/modules/rostests/winetests/mscoree/loadpaths.exe.config @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/modules/rostests/winetests/mscoree/loadpaths.exe.cs b/modules/rostests/winetests/mscoree/loadpaths.exe.cs new file mode 100644 index 00000000000..298c05c31a2 --- /dev/null +++ b/modules/rostests/winetests/mscoree/loadpaths.exe.cs @@ -0,0 +1,43 @@ +/* + * Copyright 2021 Rémi Bernon for CodeWeavers + * + * 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 + */ + +using System; + +namespace LoadPaths +{ + public static class Test + { + public static int RunExternal() + { + return new Test2().Foo(); + } + } + + public static class MainClass + { + static int Main(string[] args) + { + try { + return Test.RunExternal(); + } + catch { + return 1; + } + } + } +} diff --git a/modules/rostests/winetests/mscoree/metahost.c b/modules/rostests/winetests/mscoree/metahost.c index a9872f0f388..76f9f86abf9 100644 --- a/modules/rostests/winetests/mscoree/metahost.c +++ b/modules/rostests/winetests/mscoree/metahost.c @@ -29,12 +29,22 @@ #include "metahost.h" #include "wine/test.h" +#if !defined(__i386__) && !defined(__x86_64__) +static int has_mono = 0; +#else +static int has_mono = 1; +#endif + static HMODULE hmscoree; static HRESULT (WINAPI *pCLRCreateInstance)(REFCLSID clsid, REFIID riid, LPVOID *ppInterface); static ICLRMetaHost *metahost; +static const WCHAR v4_0[] = {'v','4','.','0','.','3','0','3','1','9',0}; + +static DWORD expect_runtime_tid; + static BOOL init_pointers(void) { HRESULT hr = E_FAIL; @@ -64,6 +74,37 @@ static void cleanup(void) FreeLibrary(hmscoree); } +static void test_getruntime(WCHAR *version) +{ + static const WCHAR dotzero[] = {'.','0',0}; + WCHAR *dot; + HRESULT hr; + ICLRRuntimeInfo *info; + DWORD count; + WCHAR buf[MAX_PATH]; + + hr = ICLRMetaHost_GetRuntime(metahost, NULL, &IID_ICLRRuntimeInfo, (void**)&info); + ok(hr == E_POINTER, "GetVersion failed, hr=%lx\n", hr); + + hr = ICLRMetaHost_GetRuntime(metahost, version, &IID_ICLRRuntimeInfo, (void**)&info); + ok(hr == S_OK, "GetVersion failed, hr=%lx\n", hr); + if (hr != S_OK) return; + + count = MAX_PATH; + hr = ICLRRuntimeInfo_GetVersionString(info, buf, &count); + ok(hr == S_OK, "GetVersionString returned %lx\n", hr); + ok(count == lstrlenW(buf)+1, "GetVersionString returned count %lu but string of length %u\n", count, lstrlenW(buf)+1); + ok(lstrcmpW(buf, version) == 0, "got unexpected version %s\n", wine_dbgstr_w(buf)); + + ICLRRuntimeInfo_Release(info); + + /* Versions must match exactly. */ + dot = wcsrchr(version, '.'); + lstrcpyW(dot, dotzero); + hr = ICLRMetaHost_GetRuntime(metahost, version, &IID_ICLRRuntimeInfo, (void**)&info); + ok(hr == CLR_E_SHIM_RUNTIME, "GetVersion failed, hr=%lx\n", hr); +} + static void test_enumruntimes(void) { IEnumUnknown *runtime_enum; @@ -74,72 +115,116 @@ static void test_enumruntimes(void) WCHAR buf[MAX_PATH]; hr = ICLRMetaHost_EnumerateInstalledRuntimes(metahost, &runtime_enum); - ok(hr == S_OK, "EnumerateInstalledRuntimes returned %x\n", hr); + ok(hr == S_OK, "EnumerateInstalledRuntimes returned %lx\n", hr); if (FAILED(hr)) return; while ((hr = IEnumUnknown_Next(runtime_enum, 1, &unk, &count)) == S_OK) { hr = IUnknown_QueryInterface(unk, &IID_ICLRRuntimeInfo, (void**)&runtime_info); - ok(hr == S_OK, "QueryInterface returned %x\n", hr); + ok(hr == S_OK, "QueryInterface returned %lx\n", hr); count = 1; hr = ICLRRuntimeInfo_GetVersionString(runtime_info, buf, &count); - ok(hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER), "GetVersionString returned %x\n", hr); - ok(count > 1, "GetVersionString returned count %u\n", count); + ok(hr == E_NOT_SUFFICIENT_BUFFER, "GetVersionString returned %lx\n", hr); + ok(count > 1, "GetVersionString returned count %lu\n", count); count = 0xdeadbeef; hr = ICLRRuntimeInfo_GetVersionString(runtime_info, NULL, &count); - ok(hr == S_OK, "GetVersionString returned %x\n", hr); - ok(count > 1 && count != 0xdeadbeef, "GetVersionString returned count %u\n", count); + ok(hr == S_OK, "GetVersionString returned %lx\n", hr); + ok(count > 1 && count != 0xdeadbeef, "GetVersionString returned count %lu\n", count); count = MAX_PATH; hr = ICLRRuntimeInfo_GetVersionString(runtime_info, buf, &count); - ok(hr == S_OK, "GetVersionString returned %x\n", hr); - ok(count > 1, "GetVersionString returned count %u\n", count); + ok(hr == S_OK, "GetVersionString returned %lx\n", hr); + ok(count > 1, "GetVersionString returned count %lu\n", count); trace("runtime found: %s\n", wine_dbgstr_w(buf)); ICLRRuntimeInfo_Release(runtime_info); - IUnknown_Release(unk); + + test_getruntime(buf); } - ok(hr == S_FALSE, "IEnumUnknown_Next returned %x\n", hr); + ok(hr == S_FALSE, "IEnumUnknown_Next returned %lx\n", hr); IEnumUnknown_Release(runtime_enum); } -static void test_getruntime(void) +static void WINAPI notification_dummy_callback(ICLRRuntimeInfo *pRuntimeInfo, CallbackThreadSetFnPtr pfnCallbackThreadSet, + CallbackThreadUnsetFnPtr pfnCallbackThreadUnset) +{ + ok(0, "unexpected call\n"); +} + +static void WINAPI notification_callback(ICLRRuntimeInfo *pRuntimeInfo, CallbackThreadSetFnPtr pfnCallbackThreadSet, + CallbackThreadUnsetFnPtr pfnCallbackThreadUnset) +{ + HRESULT hr; + WCHAR buf[20]; + DWORD buf_size = 20; + + ok(expect_runtime_tid != 0, "unexpected call\n"); + + if (expect_runtime_tid != 0) + { + ok(GetCurrentThreadId() == expect_runtime_tid, + "expected call on thread %04lx, got thread %04lx\n", expect_runtime_tid, GetCurrentThreadId()); + expect_runtime_tid = 0; + } + + hr = ICLRRuntimeInfo_GetVersionString(pRuntimeInfo, buf, &buf_size); + ok(hr == S_OK, "GetVersion returned %lx\n", hr); + ok(lstrcmpW(buf, v4_0) == 0, "GetVersion returned %s\n", wine_dbgstr_w(buf)); + + hr = pfnCallbackThreadSet(); + ok(hr == S_OK, "pfnCallbackThreadSet returned %lx\n", hr); + + hr = pfnCallbackThreadUnset(); + ok(hr == S_OK, "pfnCallbackThreadUnset returned %lx\n", hr); +} + +static void test_notification(void) +{ + HRESULT hr; + + hr = ICLRMetaHost_RequestRuntimeLoadedNotification(metahost, NULL); + ok(hr == E_POINTER, "RequestRuntimeLoadedNotification returned %lx\n", hr); + + hr = ICLRMetaHost_RequestRuntimeLoadedNotification(metahost,notification_callback); + ok(hr == S_OK, "RequestRuntimeLoadedNotification failed, hr=%lx\n", hr); + + hr = ICLRMetaHost_RequestRuntimeLoadedNotification(metahost,notification_dummy_callback); + ok(hr == HOST_E_INVALIDOPERATION, "RequestRuntimeLoadedNotification returned %lx\n", hr); +} + +static void test_notification_cb(void) { - static const WCHAR twodotzero[] = {'v','2','.','0','.','5','0','7','2','7',0}; - static const WCHAR twodotzerodotzero[] = {'v','2','.','0','.','0',0}; HRESULT hr; ICLRRuntimeInfo *info; - DWORD count; - WCHAR buf[MAX_PATH]; + ICLRRuntimeHost *host; - hr = ICLRMetaHost_GetRuntime(metahost, NULL, &IID_ICLRRuntimeInfo, (void**)&info); - ok(hr == E_POINTER, "GetVersion failed, hr=%x\n", hr); + hr = ICLRMetaHost_GetRuntime(metahost, v4_0, &IID_ICLRRuntimeInfo, (void**)&info); + ok(hr == S_OK, "GetRuntime returned %lx\n", hr); - hr = ICLRMetaHost_GetRuntime(metahost, twodotzero, &IID_ICLRRuntimeInfo, (void**)&info); - if (hr == CLR_E_SHIM_RUNTIME) - /* FIXME: Get Mono properly packaged so we can fail here. */ - todo_wine ok(hr == S_OK, "GetVersion failed, hr=%x\n", hr); - else - ok(hr == S_OK, "GetVersion failed, hr=%x\n", hr); - if (hr != S_OK) return; + expect_runtime_tid = GetCurrentThreadId(); + hr = ICLRRuntimeInfo_GetInterface(info, &CLSID_CLRRuntimeHost, &IID_ICLRRuntimeHost, (void**)&host); +#ifdef __REACTOS__ + if (hr != S_OK) + { + // skip test if mono is not installed + win_skip("mono runtime is not installed\n"); + return; + } +#endif - count = MAX_PATH; - hr = ICLRRuntimeInfo_GetVersionString(info, buf, &count); - ok(hr == S_OK, "GetVersionString returned %x\n", hr); - ok(count == lstrlenW(buf)+1, "GetVersionString returned count %u but string of length %u\n", count, lstrlenW(buf)+1); - ok(lstrcmpW(buf, twodotzero) == 0, "got unexpected version %s\n", wine_dbgstr_w(buf)); + todo_wine_if(!has_mono) ok(hr == S_OK, "GetInterface returned %lx\n", hr); + todo_wine if(!has_mono) ok(expect_runtime_tid == 0, "notification_callback was not called\n"); + + if(has_mono) + ICLRRuntimeHost_Release(host); ICLRRuntimeInfo_Release(info); - - /* Versions must match exactly. */ - hr = ICLRMetaHost_GetRuntime(metahost, twodotzerodotzero, &IID_ICLRRuntimeInfo, (void**)&info); - ok(hr == CLR_E_SHIM_RUNTIME, "GetVersion failed, hr=%x\n", hr); } START_TEST(metahost) @@ -147,9 +232,9 @@ START_TEST(metahost) if (!init_pointers()) return; + test_notification(); test_enumruntimes(); - - test_getruntime(); + test_notification_cb(); cleanup(); } diff --git a/modules/rostests/winetests/mscoree/mscoree.c b/modules/rostests/winetests/mscoree/mscoree.c index f8de6faa81c..36594972866 100644 --- a/modules/rostests/winetests/mscoree/mscoree.c +++ b/modules/rostests/winetests/mscoree/mscoree.c @@ -17,20 +17,39 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ +#include + #define COBJMACROS #include "corerror.h" #include "mscoree.h" +#include "metahost.h" #include "shlwapi.h" +#include "initguid.h" #include "wine/test.h" +#if !defined(__i386__) && !defined(__x86_64__) +static int has_mono = 0; +#else +static int has_mono = 1; +#endif + +DEFINE_GUID(IID__AppDomain, 0x05f696dc,0x2b29,0x3663,0xad,0x8b,0xc4,0x38,0x9c,0xf2,0xa7,0x13); + +static const WCHAR v4_0[] = {'v','4','.','0','.','3','0','3','1','9',0}; + static HMODULE hmscoree; static HRESULT (WINAPI *pGetCORVersion)(LPWSTR, DWORD, DWORD*); +static HRESULT (WINAPI *pCorIsLatestSvc)(INT*, INT*); static HRESULT (WINAPI *pGetCORSystemDirectory)(LPWSTR, DWORD, DWORD*); static HRESULT (WINAPI *pGetRequestedRuntimeInfo)(LPCWSTR, LPCWSTR, LPCWSTR, DWORD, DWORD, LPWSTR, DWORD, DWORD*, LPWSTR, DWORD, DWORD*); static HRESULT (WINAPI *pLoadLibraryShim)(LPCWSTR, LPCWSTR, LPVOID, HMODULE*); static HRESULT (WINAPI *pCreateConfigStream)(LPCWSTR, IStream**); +static HRESULT (WINAPI *pCreateInterface)(REFCLSID, REFIID, VOID**); +static HRESULT (WINAPI *pCLRCreateInstance)(REFCLSID, REFIID, VOID**); + +static BOOL no_legacy_runtimes; static BOOL init_functionpointers(void) { @@ -43,12 +62,17 @@ static BOOL init_functionpointers(void) } pGetCORVersion = (void *)GetProcAddress(hmscoree, "GetCORVersion"); + pCorIsLatestSvc = (void *)GetProcAddress(hmscoree, "CorIsLatestSvc"); pGetCORSystemDirectory = (void *)GetProcAddress(hmscoree, "GetCORSystemDirectory"); pGetRequestedRuntimeInfo = (void *)GetProcAddress(hmscoree, "GetRequestedRuntimeInfo"); pLoadLibraryShim = (void *)GetProcAddress(hmscoree, "LoadLibraryShim"); pCreateConfigStream = (void *)GetProcAddress(hmscoree, "CreateConfigStream"); + pCreateInterface = (void *)GetProcAddress(hmscoree, "CreateInterface"); + pCLRCreateInstance = (void *)GetProcAddress(hmscoree, "CLRCreateInstance"); - if (!pGetCORVersion || !pGetCORSystemDirectory || !pGetRequestedRuntimeInfo || !pLoadLibraryShim) + if (!pGetCORVersion || !pGetCORSystemDirectory || !pGetRequestedRuntimeInfo || !pLoadLibraryShim || + !pCreateInterface || !pCLRCreateInstance || !pCorIsLatestSvc + ) { win_skip("functions not available\n"); FreeLibrary(hmscoree); @@ -58,6 +82,93 @@ static BOOL init_functionpointers(void) return TRUE; } +static int check_runtime(void) +{ + ICLRMetaHost *metahost; + ICLRRuntimeInfo *runtimeinfo; + ICorRuntimeHost *runtimehost; + HRESULT hr; + + if (!pCLRCreateInstance) + { + win_skip("Function CLRCreateInstance not found.\n"); + return 1; + } + + hr = pCLRCreateInstance(&CLSID_CLRMetaHost, &IID_ICLRMetaHost, (void **)&metahost); + if (hr == E_NOTIMPL) + { + win_skip("CLRCreateInstance not implemented\n"); + return 1; + } + ok(SUCCEEDED(hr), "CLRCreateInstance failed, hr=%#.8lx\n", hr); + if (FAILED(hr)) + return 1; + + hr = ICLRMetaHost_GetRuntime(metahost, v4_0, &IID_ICLRRuntimeInfo, (void **)&runtimeinfo); + ok(SUCCEEDED(hr), "ICLRMetaHost::GetRuntime failed, hr=%#.8lx\n", hr); + if (FAILED(hr)) + return 1; + + hr = ICLRRuntimeInfo_GetInterface(runtimeinfo, &CLSID_CorRuntimeHost, &IID_ICorRuntimeHost, + (void **)&runtimehost); + todo_wine_if(!has_mono) ok(SUCCEEDED(hr), "ICLRRuntimeInfo::GetInterface failed, hr=%#.8lx\n", hr); + if (FAILED(hr)) + return 1; + + hr = ICorRuntimeHost_Start(runtimehost); + ok(SUCCEEDED(hr), "ICorRuntimeHost::Start failed, hr=%#.8lx\n", hr); + if (FAILED(hr)) + return 1; + + ICorRuntimeHost_Release(runtimehost); + + ICLRRuntimeInfo_Release(runtimeinfo); + + ICLRMetaHost_ExitProcess(metahost, 0); + + ok(0, "ICLRMetaHost_ExitProcess is not supposed to return\n"); + return 1; +} + +static BOOL runtime_is_usable(void) +{ + static const char cmdline_format[] = "\"%s\" mscoree check_runtime"; + char** argv; + char cmdline[MAX_PATH + sizeof(cmdline_format)]; + STARTUPINFOA si = {0}; + PROCESS_INFORMATION pi; + BOOL ret; + DWORD exitcode; + + winetest_get_mainargs(&argv); + + sprintf(cmdline, cmdline_format, argv[0]); + + si.cb = sizeof(si); + + ret = CreateProcessA(NULL, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); + ok(ret, "Could not create process: %lu\n", GetLastError()); + if (!ret) + return FALSE; + + CloseHandle(pi.hThread); + + WaitForSingleObject(pi.hProcess, INFINITE); + + ret = GetExitCodeProcess(pi.hProcess, &exitcode); + ok(ret, "GetExitCodeProcess failed: %lu\n", GetLastError()); + CloseHandle(pi.hProcess); + + if (!ret || exitcode != 0) + { + todo_wine_if(!has_mono) win_skip(".NET 4.0 runtime is not usable\n"); + return FALSE; + } + + return TRUE; +} + static void test_versioninfo(void) { const WCHAR v9_0[] = {'v','9','.','0','.','3','0','3','1','9',0}; @@ -75,43 +186,42 @@ static void test_versioninfo(void) if (0) /* crashes on <= w2k3 */ { hr = pGetCORVersion(NULL, MAX_PATH, &size); - ok(hr == E_POINTER,"GetCORVersion returned %08x\n", hr); + ok(hr == E_POINTER,"GetCORVersion returned %08lx\n", hr); } hr = pGetCORVersion(version, 1, &size); if (hr == CLR_E_SHIM_RUNTIME) { - /* FIXME: Get Mono packaged properly so we can fail here. */ - todo_wine ok(hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER),"GetCORVersion returned %08x\n", hr); - skip("No .NET runtimes are installed\n"); + no_legacy_runtimes = TRUE; + win_skip("No legacy .NET runtimes are installed\n"); return; } - ok(hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER),"GetCORVersion returned %08x\n", hr); + ok(hr == E_NOT_SUFFICIENT_BUFFER, "GetCORVersion returned %08lx\n", hr); hr = pGetCORVersion(version, MAX_PATH, &size); - ok(hr == S_OK,"GetCORVersion returned %08x\n", hr); + ok(hr == S_OK,"GetCORVersion returned %08lx\n", hr); trace("latest installed .net runtime: %s\n", wine_dbgstr_w(version)); hr = pGetCORSystemDirectory(path, MAX_PATH , &size); - ok(hr == S_OK, "GetCORSystemDirectory returned %08x\n", hr); + todo_wine_if(!has_mono) ok(hr == S_OK, "GetCORSystemDirectory returned %08lx\n", hr); /* size includes terminating null-character */ - ok(size == (lstrlenW(path) + 1),"size is %d instead of %d\n", size, (lstrlenW(path) + 1)); + todo_wine_if(!has_mono) ok(size == (lstrlenW(path) + 1),"size is %ld instead of %d\n", size, (lstrlenW(path) + 1)); path_len = size; hr = pGetCORSystemDirectory(path, path_len-1 , &size); - ok(hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER), "GetCORSystemDirectory returned %08x\n", hr); + todo_wine_if(!has_mono) ok(hr == E_NOT_SUFFICIENT_BUFFER, "GetCORSystemDirectory returned %08lx\n", hr); if (0) /* crashes on <= w2k3 */ { hr = pGetCORSystemDirectory(NULL, MAX_PATH , &size); - ok(hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER), "GetCORSystemDirectory returned %08x\n", hr); + ok(hr == E_NOT_SUFFICIENT_BUFFER, "GetCORSystemDirectory returned %08lx\n", hr); } hr = pGetCORSystemDirectory(path, MAX_PATH , NULL); - ok(hr == E_POINTER,"GetCORSystemDirectory returned %08x\n", hr); + ok(hr == E_POINTER,"GetCORSystemDirectory returned %08lx\n", hr); trace("latest installed .net installed in directory: %s\n", wine_dbgstr_w(path)); @@ -120,77 +230,79 @@ static void test_versioninfo(void) if(hr == CLR_E_SHIM_RUNTIME) return; /* skipping rest of tests on win2k as .net 2.0 not installed */ - ok(hr == S_OK, "GetRequestedRuntimeInfo returned %08x\n", hr); + todo_wine_if(!has_mono) ok(hr == S_OK, "GetRequestedRuntimeInfo returned %08lx\n", hr); trace(" installed in directory %s is .net version %s\n", wine_dbgstr_w(path), wine_dbgstr_w(version)); hr = pGetRequestedRuntimeInfo( NULL, v1_1, NULL, 0, 0, path, MAX_PATH, &path_len, version, MAX_PATH, &size); - ok(hr == S_OK || hr == CLR_E_SHIM_RUNTIME /*v1_1 not installed*/, "GetRequestedRuntimeInfo returned %08x\n", hr); + todo_wine_if(!has_mono) ok(hr == S_OK || hr == CLR_E_SHIM_RUNTIME /*v1_1 not installed*/, "GetRequestedRuntimeInfo returned %08lx\n", hr); if(hr == S_OK) trace(" installed in directory %s is .net version %s\n", wine_dbgstr_w(path), wine_dbgstr_w(version)); /* version number NULL not allowed without RUNTIME_INFO_UPGRADE_VERSION flag */ hr = pGetRequestedRuntimeInfo( NULL, NULL, NULL, 0, 0, path, MAX_PATH, &path_len, version, MAX_PATH, &size); - ok(hr == CLR_E_SHIM_RUNTIME, "GetRequestedRuntimeInfo returned %08x\n", hr); + ok(hr == CLR_E_SHIM_RUNTIME, "GetRequestedRuntimeInfo returned %08lx\n", hr); /* with RUNTIME_INFO_UPGRADE_VERSION flag and version number NULL, latest installed version is returned */ hr = pGetRequestedRuntimeInfo( NULL, NULL, NULL, 0, RUNTIME_INFO_UPGRADE_VERSION, path, MAX_PATH, &path_len, version, MAX_PATH, &size); - ok(hr == S_OK, "GetRequestedRuntimeInfo returned %08x\n", hr); + todo_wine_if(!has_mono) ok(hr == S_OK, "GetRequestedRuntimeInfo returned %08lx\n", hr); hr = pGetRequestedRuntimeInfo( NULL, v2_0, NULL, 0, 0, path, 1, &path_len, version, MAX_PATH, &size); - ok(hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER), "GetRequestedRuntimeInfo returned %08x\n", hr); + todo_wine_if(!has_mono) ok(hr == E_NOT_SUFFICIENT_BUFFER, "GetRequestedRuntimeInfo returned %08lx\n", hr); /* if one of the buffers is NULL, the other one is still happily filled */ memset(version, 0, sizeof(version)); hr = pGetRequestedRuntimeInfo( NULL, v2_0, NULL, 0, 0, NULL, MAX_PATH, &path_len, version, MAX_PATH, &size); - ok(hr == S_OK, "GetRequestedRuntimeInfo returned %08x\n", hr); - ok(!winetest_strcmpW(version, v2_0), "version is %s , expected %s\n", wine_dbgstr_w(version), wine_dbgstr_w(v2_0)); + todo_wine_if(!has_mono) ok(hr == S_OK, "GetRequestedRuntimeInfo returned %08lx\n", hr); + ok(!wcscmp(version, v2_0), "version is %s , expected %s\n", wine_dbgstr_w(version), wine_dbgstr_w(v2_0)); /* With NULL-pointer for bufferlength, the buffer itself still gets filled with correct string */ memset(version, 0, sizeof(version)); hr = pGetRequestedRuntimeInfo( NULL, v2_0, NULL, 0, 0, path, MAX_PATH, &path_len, version, MAX_PATH, NULL); - ok(hr == S_OK, "GetRequestedRuntimeInfo returned %08x\n", hr); - ok(!winetest_strcmpW(version, v2_0), "version is %s , expected %s\n", wine_dbgstr_w(version), wine_dbgstr_w(v2_0)); + todo_wine_if(!has_mono) ok(hr == S_OK, "GetRequestedRuntimeInfo returned %08lx\n", hr); + ok(!wcscmp(version, v2_0), "version is %s , expected %s\n", wine_dbgstr_w(version), wine_dbgstr_w(v2_0)); memset(version, 0, sizeof(version)); hr = pGetRequestedRuntimeInfo( NULL, v2_0cap, NULL, 0, 0, path, MAX_PATH, &path_len, version, MAX_PATH, NULL); - ok(hr == S_OK, "GetRequestedRuntimeInfo returned %08x\n", hr); - ok(!winetest_strcmpW(version, v2_0cap), "version is %s , expected %s\n", wine_dbgstr_w(version), wine_dbgstr_w(v2_0cap)); + todo_wine_if(!has_mono) ok(hr == S_OK, "GetRequestedRuntimeInfo returned %08lx\n", hr); + ok(!wcscmp(version, v2_0cap), "version is %s , expected %s\n", wine_dbgstr_w(version), wine_dbgstr_w(v2_0cap)); /* Invalid Version and RUNTIME_INFO_UPGRADE_VERSION flag*/ memset(version, 0, sizeof(version)); hr = pGetRequestedRuntimeInfo( NULL, v1_1, NULL, 0, RUNTIME_INFO_UPGRADE_VERSION, path, MAX_PATH, &path_len, version, MAX_PATH, NULL); - ok(hr == S_OK || hr == CLR_E_SHIM_RUNTIME , "GetRequestedRuntimeInfo returned %08x\n", hr); + todo_wine_if(!has_mono) ok(hr == S_OK || hr == CLR_E_SHIM_RUNTIME , "GetRequestedRuntimeInfo returned %08lx\n", hr); if(hr == S_OK) { /* .NET 1.1 may not be installed. */ - ok(!winetest_strcmpW(version, v1_1) || !winetest_strcmpW(version, v2_0), + ok(!wcscmp(version, v1_1) || !wcscmp(version, v2_0), "version is %s , expected %s or %s\n", wine_dbgstr_w(version), wine_dbgstr_w(v1_1), wine_dbgstr_w(v2_0)); } memset(version, 0, sizeof(version)); hr = pGetRequestedRuntimeInfo( NULL, v9_0, NULL, 0, RUNTIME_INFO_UPGRADE_VERSION, path, MAX_PATH, &path_len, version, MAX_PATH, NULL); - ok(hr == CLR_E_SHIM_RUNTIME, "GetRequestedRuntimeInfo returned %08x\n", hr); + ok(hr == CLR_E_SHIM_RUNTIME, "GetRequestedRuntimeInfo returned %08lx\n", hr); memset(version, 0, sizeof(version)); hr = pGetRequestedRuntimeInfo( NULL, v1_1_0, NULL, 0, 0, path, MAX_PATH, &path_len, version, MAX_PATH, NULL); - ok(hr == CLR_E_SHIM_RUNTIME, "GetRequestedRuntimeInfo returned %08x\n", hr); + ok(hr == CLR_E_SHIM_RUNTIME, "GetRequestedRuntimeInfo returned %08lx\n", hr); memset(version, 0, sizeof(version)); hr = pGetRequestedRuntimeInfo( NULL, v1_1_0, NULL, 0, RUNTIME_INFO_UPGRADE_VERSION, path, MAX_PATH, &path_len, version, MAX_PATH, NULL); - ok(hr == S_OK, "GetRequestedRuntimeInfo returned %08x\n", hr); - ok(!winetest_strcmpW(version, v2_0), "version is %s , expected %s\n", wine_dbgstr_w(version), wine_dbgstr_w(v2_0)); + todo_wine_if(!has_mono) ok(hr == S_OK, "GetRequestedRuntimeInfo returned %08lx\n", hr); + ok(!wcscmp(version, v2_0), "version is %s , expected %s\n", wine_dbgstr_w(version), wine_dbgstr_w(v2_0)); memset(version, 0, sizeof(version)); hr = pGetRequestedRuntimeInfo( NULL, v2_0_0, NULL, 0, 0, path, MAX_PATH, &path_len, version, MAX_PATH, NULL); - ok(hr == CLR_E_SHIM_RUNTIME, "GetRequestedRuntimeInfo returned %08x\n", hr); + ok(hr == CLR_E_SHIM_RUNTIME, "GetRequestedRuntimeInfo returned %08lx\n", hr); memset(version, 0, sizeof(version)); hr = pGetRequestedRuntimeInfo( NULL, v2_0_0, NULL, 0, RUNTIME_INFO_UPGRADE_VERSION, path, MAX_PATH, &path_len, version, MAX_PATH, NULL); - ok(hr == S_OK, "GetRequestedRuntimeInfo returned %08x\n", hr); - ok(!winetest_strcmpW(version, v2_0), "version is %s , expected %s\n", wine_dbgstr_w(version), wine_dbgstr_w(v2_0)); + todo_wine_if(!has_mono) ok(hr == S_OK, "GetRequestedRuntimeInfo returned %08lx\n", hr); + ok(!wcscmp(version, v2_0), "version is %s , expected %s\n", wine_dbgstr_w(version), wine_dbgstr_w(v2_0)); + + hr = pCorIsLatestSvc(NULL, NULL); + ok(hr == E_POINTER, "CorIsLatestSvc returned %08lx\n", hr); } static void test_loadlibraryshim(void) { - const WCHAR v4_0[] = {'v','4','.','0','.','3','0','3','1','9',0}; const WCHAR v2_0[] = {'v','2','.','0','.','5','0','7','2','7',0}; const WCHAR v1_1[] = {'v','1','.','1','.','4','3','2','2',0}; const WCHAR vbogus[] = {'v','b','o','g','u','s',0}; @@ -204,36 +316,42 @@ static void test_loadlibraryshim(void) HMODULE hdll; CHAR dllpath[MAX_PATH]; + if (no_legacy_runtimes) + { + win_skip("No legacy .NET runtimes are installed\n"); + return; + } + hr = pLoadLibraryShim(fusion, v1_1, NULL, &hdll); - ok(hr == S_OK || hr == E_HANDLE, "LoadLibraryShim failed, hr=%x\n", hr); + ok(hr == S_OK || hr == E_HANDLE, "LoadLibraryShim failed, hr=%lx\n", hr); if (SUCCEEDED(hr)) { latest = v1_1; GetModuleFileNameA(hdll, dllpath, MAX_PATH); - todo_wine ok(StrStrIA(dllpath, "v1.1.4322") != 0, "incorrect fusion.dll path %s\n", dllpath); + todo_wine_if(!has_mono) ok(StrStrIA(dllpath, "v1.1.4322") != 0, "incorrect fusion.dll path %s\n", dllpath); ok(StrStrIA(dllpath, "fusion.dll") != 0, "incorrect fusion.dll path %s\n", dllpath); FreeLibrary(hdll); } hr = pLoadLibraryShim(fusion, v2_0, NULL, &hdll); - ok(hr == S_OK || hr == E_HANDLE, "LoadLibraryShim failed, hr=%x\n", hr); + ok(hr == S_OK || hr == E_HANDLE, "LoadLibraryShim failed, hr=%lx\n", hr); if (SUCCEEDED(hr)) { latest = v2_0; GetModuleFileNameA(hdll, dllpath, MAX_PATH); - todo_wine ok(StrStrIA(dllpath, "v2.0.50727") != 0, "incorrect fusion.dll path %s\n", dllpath); + todo_wine_if(!has_mono) ok(StrStrIA(dllpath, "v2.0.50727") != 0, "incorrect fusion.dll path %s\n", dllpath); ok(StrStrIA(dllpath, "fusion.dll") != 0, "incorrect fusion.dll path %s\n", dllpath); FreeLibrary(hdll); } hr = pLoadLibraryShim(fusion, v4_0, NULL, &hdll); - ok(hr == S_OK || hr == E_HANDLE, "LoadLibraryShim failed, hr=%x\n", hr); + ok(hr == S_OK || hr == E_HANDLE, "LoadLibraryShim failed, hr=%lx\n", hr); if (SUCCEEDED(hr)) { /* LoadLibraryShim with a NULL version prefers 2.0 and earlier */ @@ -242,52 +360,52 @@ static void test_loadlibraryshim(void) GetModuleFileNameA(hdll, dllpath, MAX_PATH); - todo_wine ok(StrStrIA(dllpath, "v4.0.30319") != 0, "incorrect fusion.dll path %s\n", dllpath); + todo_wine_if(!has_mono) ok(StrStrIA(dllpath, "v4.0.30319") != 0, "incorrect fusion.dll path %s\n", dllpath); ok(StrStrIA(dllpath, "fusion.dll") != 0, "incorrect fusion.dll path %s\n", dllpath); FreeLibrary(hdll); } hr = pLoadLibraryShim(fusion, vbogus, NULL, &hdll); - todo_wine ok(hr == E_HANDLE, "LoadLibraryShim failed, hr=%x\n", hr); + ok(hr == E_HANDLE, "LoadLibraryShim failed, hr=%lx\n", hr); if (SUCCEEDED(hr)) FreeLibrary(hdll); WideCharToMultiByte(CP_ACP, 0, latest, -1, latestA, MAX_PATH, NULL, NULL); hr = pLoadLibraryShim(fusion, NULL, NULL, &hdll); - ok(hr == S_OK, "LoadLibraryShim failed, hr=%x\n", hr); + ok(hr == S_OK, "LoadLibraryShim failed, hr=%lx\n", hr); if (SUCCEEDED(hr)) { GetModuleFileNameA(hdll, dllpath, MAX_PATH); if (latest) - todo_wine ok(StrStrIA(dllpath, latestA) != 0, "incorrect fusion.dll path %s\n", dllpath); + todo_wine_if(!has_mono) ok(StrStrIA(dllpath, latestA) != 0, "incorrect fusion.dll path %s\n", dllpath); ok(StrStrIA(dllpath, "fusion.dll") != 0, "incorrect fusion.dll path %s\n", dllpath); FreeLibrary(hdll); } hr = pLoadLibraryShim(fusiondll, NULL, NULL, &hdll); - ok(hr == S_OK, "LoadLibraryShim failed, hr=%x\n", hr); + ok(hr == S_OK, "LoadLibraryShim failed, hr=%lx\n", hr); if (SUCCEEDED(hr)) { GetModuleFileNameA(hdll, dllpath, MAX_PATH); if (latest) - todo_wine ok(StrStrIA(dllpath, latestA) != 0, "incorrect fusion.dll path %s\n", dllpath); + todo_wine_if(!has_mono) ok(StrStrIA(dllpath, latestA) != 0, "incorrect fusion.dll path %s\n", dllpath); ok(StrStrIA(dllpath, "fusion.dll") != 0, "incorrect fusion.dll path %s\n", dllpath); FreeLibrary(hdll); } hr = pLoadLibraryShim(nosuchdll, latest, NULL, &hdll); - ok(hr == E_HANDLE, "LoadLibraryShim failed, hr=%x\n", hr); + ok(hr == E_HANDLE, "LoadLibraryShim failed, hr=%lx\n", hr); if (SUCCEEDED(hr)) FreeLibrary(hdll); hr = pLoadLibraryShim(gdidll, latest, NULL, &hdll); - todo_wine ok(hr == E_HANDLE, "LoadLibraryShim failed, hr=%x\n", hr); + ok(hr == E_HANDLE, "LoadLibraryShim failed, hr=%lx\n", hr); if (SUCCEEDED(hr)) FreeLibrary(hdll); } @@ -305,7 +423,7 @@ static void create_xml_file(LPCWSTR filename) DWORD dwNumberOfBytesWritten; HANDLE hfile = CreateFileW(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - ok(hfile != INVALID_HANDLE_VALUE, "File creation failed\n"); + ok(hfile != INVALID_HANDLE_VALUE, "Could not open %s for writing: %lu\n", wine_dbgstr_w(filename), GetLastError()); WriteFile(hfile, xmldata, sizeof(xmldata) - 1, &dwNumberOfBytesWritten, NULL); CloseHandle(hfile); } @@ -329,24 +447,24 @@ static void test_createconfigstream(void) GetFullPathNameW(file, MAX_PATH, path, NULL); hr = pCreateConfigStream(NULL, &stream); - todo_wine ok(hr == E_FAIL || - broken(hr == HRESULT_FROM_WIN32(ERROR_PROC_NOT_FOUND)) || /* some WinXP, Win2K3 and Win7 */ - broken(hr == S_OK && !stream), /* some Win2K3 */ - "CreateConfigStream returned %x\n", hr); + ok(hr == E_FAIL || + broken(hr == HRESULT_FROM_WIN32(ERROR_PROC_NOT_FOUND)) || /* some WinXP, Win2K3 and Win7 */ + broken(hr == S_OK && !stream), /* some Win2K3 */ + "CreateConfigStream returned %lx\n", hr); hr = pCreateConfigStream(path, NULL); - todo_wine ok(hr == COR_E_NULLREFERENCE, "CreateConfigStream returned %x\n", hr); + ok(hr == COR_E_NULLREFERENCE, "CreateConfigStream returned %lx\n", hr); hr = pCreateConfigStream(NULL, NULL); - todo_wine ok(hr == COR_E_NULLREFERENCE, "CreateConfigStream returned %x\n", hr); + ok(hr == COR_E_NULLREFERENCE, "CreateConfigStream returned %lx\n", hr); hr = pCreateConfigStream(nonexistent, &stream); - todo_wine ok(hr == COR_E_FILENOTFOUND, "CreateConfigStream returned %x\n", hr); + ok(hr == COR_E_FILENOTFOUND, "CreateConfigStream returned %lx\n", hr); ok(stream == NULL, "Expected stream to be NULL\n"); hr = pCreateConfigStream(path, &stream); - todo_wine ok(hr == S_OK, "CreateConfigStream failed, hr=%x\n", hr); - todo_wine ok(stream != NULL, "Expected non-NULL stream\n"); + ok(hr == S_OK, "CreateConfigStream failed, hr=%lx\n", hr); + ok(stream != NULL, "Expected non-NULL stream\n"); if (stream) { @@ -354,46 +472,456 @@ static void test_createconfigstream(void) LARGE_INTEGER pos; ULARGE_INTEGER size; IStream *stream2 = NULL; + ULONG ref; hr = IStream_Read(stream, buffer, strlen(xmldata), &count); - ok(hr == S_OK, "IStream_Read failed, hr=%x\n", hr); - ok(count == strlen(xmldata), "wrong count: %u\n", count); + ok(hr == S_OK, "IStream_Read failed, hr=%lx\n", hr); + ok(count == strlen(xmldata), "wrong count: %lu\n", count); ok(!strcmp(buffer, xmldata), "Strings do not match\n"); + hr = IStream_Read(stream, buffer, sizeof(buffer), &count); + ok(hr == S_OK, "IStream_Read failed, hr=%lx\n", hr); + ok(!count, "wrong count: %lu\n", count); + hr = IStream_Write(stream, xmldata, strlen(xmldata), &count); - ok(hr == E_FAIL, "IStream_Write returned hr=%x\n", hr); + ok(hr == E_FAIL, "IStream_Write returned hr=%lx\n", hr); pos.QuadPart = strlen(xmldata); hr = IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL); - ok(hr == E_NOTIMPL, "IStream_Seek returned hr=%x\n", hr); + ok(hr == E_NOTIMPL, "IStream_Seek returned hr=%lx\n", hr); size.QuadPart = strlen(xmldata); hr = IStream_SetSize(stream, size); - ok(hr == E_NOTIMPL, "IStream_SetSize returned hr=%x\n", hr); + ok(hr == E_NOTIMPL, "IStream_SetSize returned hr=%lx\n", hr); hr = IStream_Clone(stream, &stream2); - ok(hr == E_NOTIMPL, "IStream_Clone returned hr=%x\n", hr); + ok(hr == E_NOTIMPL, "IStream_Clone returned hr=%lx\n", hr); hr = IStream_Commit(stream, STGC_DEFAULT); - ok(hr == E_NOTIMPL, "IStream_Commit returned hr=%x\n", hr); + ok(hr == E_NOTIMPL, "IStream_Commit returned hr=%lx\n", hr); hr = IStream_Revert(stream); - ok(hr == E_NOTIMPL, "IStream_Revert returned hr=%x\n", hr); + ok(hr == E_NOTIMPL, "IStream_Revert returned hr=%lx\n", hr); - hr = IStream_Release(stream); - ok(hr == S_OK, "IStream_Release returned hr=%x\n", hr); + ref = IStream_Release(stream); + ok(!ref, "IStream_Release returned %lu\n", ref); } DeleteFileW(file); } +static void test_createinstance(void) +{ + HRESULT hr; + ICLRMetaHost *host; + + if (no_legacy_runtimes) + { + /* If we don't have 1.x or 2.0 runtimes, we should at least have .NET 4. */ + ok(pCreateInterface != NULL, "no legacy runtimes or .NET 4 interfaces available\n"); + } + + if(!pCreateInterface) + { + win_skip("Function CreateInterface not found.\n"); + return; + } + + hr = pCreateInterface(&CLSID_CLRMetaHost, &IID_ICLRMetaHost, (void**)&host); + if(SUCCEEDED(hr)) + { + ICLRMetaHost_Release(host); + } + else + { + win_skip(".NET 4 not installed.\n"); + } +} + +static BOOL write_resource(const WCHAR *resource, const WCHAR *filename) +{ + HANDLE file; + HRSRC rsrc; + void *data; + DWORD size; + BOOL ret; + + rsrc = FindResourceW(GetModuleHandleW(NULL), resource, MAKEINTRESOURCEW(RT_RCDATA)); + if (!rsrc) return FALSE; + + data = LockResource(LoadResource(GetModuleHandleA(NULL), rsrc)); + if (!data) return FALSE; + + size = SizeofResource(GetModuleHandleA(NULL), rsrc); + if (!size) return FALSE; + + file = CreateFileW(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); + if (file == INVALID_HANDLE_VALUE) return FALSE; + + ret = WriteFile(file, data, size, &size, NULL); + CloseHandle(file); + return ret; +} + +static BOOL compile_cs(const WCHAR *source, const WCHAR *target, const WCHAR *type, const WCHAR *args) +{ + static const WCHAR *csc = L"C:\\windows\\Microsoft.NET\\Framework\\v2.0.50727\\csc.exe"; + WCHAR cmdline[2 * MAX_PATH + 74]; + PROCESS_INFORMATION pi; + STARTUPINFOW si = { 0 }; + BOOL ret; + + if (!PathFileExistsW(csc)) + { + skip("Can't find csc.exe\n"); + return FALSE; + } + + swprintf(cmdline, ARRAY_SIZE(cmdline), L"%s /t:%s %s /out:\"%s\" \"%s\"", csc, type, args, target, source); + + si.cb = sizeof(si); + ret = CreateProcessW(csc, cmdline, NULL, NULL, FALSE, DETACHED_PROCESS, NULL, NULL, &si, &pi); + ok(ret, "Could not create process: %lu\n", GetLastError()); + + wait_child_process(pi.hProcess); + CloseHandle(pi.hThread); + CloseHandle(pi.hProcess); + + ret = PathFileExistsW(target); + ok(ret, "Compilation failed\n"); + + return ret; +} + +static BOOL create_new_dir(WCHAR newdir[MAX_PATH], const WCHAR* prefix) +{ + WCHAR path[MAX_PATH]; + BOOL try_tmpdir = TRUE; + static unsigned i = 0; + + GetCurrentDirectoryW(ARRAY_SIZE(path), path); + + while (1) + { + swprintf(newdir, MAX_PATH, L"%s\\%s%04d", path, prefix, i); + if (CreateDirectoryW(newdir, NULL)) + return TRUE; + switch (GetLastError()) + { + case ERROR_ACCESS_DENIED: + if (!try_tmpdir) + return FALSE; + try_tmpdir = FALSE; + GetTempPathW(ARRAY_SIZE(path), path); + path[wcslen(path) - 1] = 0; /* redundant trailing backslash */ + break; + case ERROR_ALREADY_EXISTS: + i++; + break; + default: + return FALSE; + } + } +} + +static void test_loadpaths_execute(const WCHAR *exe_name, const WCHAR *dll_name, const WCHAR *cfg_name, + const WCHAR *dll_dest, BOOL expect_failure, BOOL todo) +{ + WCHAR tmpdir[MAX_PATH], tmpexe[MAX_PATH], tmpcfg[MAX_PATH], tmpdll[MAX_PATH]; + PROCESS_INFORMATION pi; + STARTUPINFOW si = { 0 }; + WCHAR *ptr, *end; + DWORD exit_code = 0xdeadbeef, err; + BOOL ret; + + ret = create_new_dir(tmpdir, L"loadpaths"); + ok(ret, "failed to create a new dir %lu\n", GetLastError()); + end = tmpdir + wcslen(tmpdir); + + wcscpy(tmpexe, tmpdir); + PathAppendW(tmpexe, exe_name); + ret = CopyFileW(exe_name, tmpexe, FALSE); + ok(ret, "CopyFileW(%s) failed: %lu\n", debugstr_w(tmpexe), GetLastError()); + + if (cfg_name) + { + wcscpy(tmpcfg, tmpdir); + PathAppendW(tmpcfg, cfg_name); + ret = CopyFileW(cfg_name, tmpcfg, FALSE); + ok(ret, "CopyFileW(%s) failed: %lu\n", debugstr_w(tmpcfg), GetLastError()); + } + + ptr = tmpdir + wcslen(tmpdir); + PathAppendW(tmpdir, dll_dest); + while (*ptr && (ptr = wcschr(ptr + 1, '\\'))) + { + *ptr = '\0'; + ret = CreateDirectoryW(tmpdir, NULL); + ok(ret, "CreateDirectoryW(%s) failed: %lu\n", debugstr_w(tmpdir), GetLastError()); + *ptr = '\\'; + } + + wcscpy(tmpdll, tmpdir); + if ((ptr = wcsrchr(tmpdir, '\\'))) *ptr = '\0'; + + ret = CopyFileW(dll_name, tmpdll, FALSE); + ok(ret, "CopyFileW(%s) failed: %lu\n", debugstr_w(tmpdll), GetLastError()); + + si.cb = sizeof(si); + ret = CreateProcessW(tmpexe, tmpexe, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); + ok(ret, "CreateProcessW(%s) failed: %lu\n", debugstr_w(tmpexe), GetLastError()); + + if (expect_failure) ret = WaitForSingleObject(pi.hProcess, 2000); + else + { + ret = WaitForSingleObject(pi.hProcess, 5000); + ok(ret == WAIT_OBJECT_0, "%s: WaitForSingleObject returned %d: %lu\n", debugstr_w(dll_dest), ret, GetLastError()); + } + + GetExitCodeProcess(pi.hProcess, &exit_code); + if (ret == WAIT_TIMEOUT) TerminateProcess(pi.hProcess, 0xdeadbeef); + CloseHandle(pi.hThread); + CloseHandle(pi.hProcess); + + if (expect_failure) todo_wine_if(todo) ok(exit_code != 0, "%s: Succeeded to execute process\n", debugstr_w(dll_dest)); + else ok(exit_code == 0, "%s: Failed to execute process\n", debugstr_w(dll_dest)); + + /* sometimes the failing process never returns, in which case cleaning up won't work */ + if (ret == WAIT_TIMEOUT && expect_failure) return; + + if (cfg_name) + { + ret = DeleteFileW(tmpcfg); + ok(ret, "DeleteFileW(%s) failed: %lu\n", debugstr_w(tmpcfg), GetLastError()); + } + ret = DeleteFileW(tmpdll); + ok(ret, "DeleteFileW(%s) failed: %lu\n", debugstr_w(tmpdll), GetLastError()); + ret = DeleteFileW(tmpexe); + ok(ret, "DeleteFileW(%s) failed: %lu\n", debugstr_w(tmpexe), GetLastError()); + + ptr = end; + while (ptr >= end && (ptr = wcsrchr(tmpdir, '\\'))) + { + ret = RemoveDirectoryW(tmpdir); + err = GetLastError(); + ok(ret, "RemoveDirectoryW(%s) failed: %lu\n", debugstr_w(tmpdir), err); + + if (!ret && err == ERROR_DIR_NOT_EMPTY) + { + WIN32_FIND_DATAW fd; + HANDLE hfind; + + wcscat(tmpdir, L"\\*"); + hfind = FindFirstFileW(tmpdir, &fd); + while (hfind != INVALID_HANDLE_VALUE && (!wcscmp(fd.cFileName, L".") || !wcscmp(fd.cFileName, L".."))) + { + if (!FindNextFileW(hfind, &fd)) + { + FindClose(hfind); + hfind = INVALID_HANDLE_VALUE; + } + } + if (hfind != INVALID_HANDLE_VALUE) + { + trace("file %s still present in tmpdir\n", debugstr_w(fd.cFileName)); + FindClose(hfind); + } + } + + *ptr = '\0'; + } +} + +static void test_loadpaths(BOOL neutral) +{ + static const WCHAR *loadpaths[] = {L"", L"en", L"libloadpaths", L"en\\libloadpaths"}; + static const WCHAR *dll_source = L"loadpaths.dll.cs"; + static const WCHAR *dll_name = L"libloadpaths.dll"; + static const WCHAR *exe_source = L"loadpaths.exe.cs"; + static const WCHAR *exe_name = L"loadpaths.exe"; + static const WCHAR *cfg_name = L"loadpaths.exe.config"; + WCHAR tmp[MAX_PATH]; + BOOL ret; + int i; + + DeleteFileW(dll_source); + ret = write_resource(dll_source, dll_source); + ok(ret, "Could not write resource: %lu\n", GetLastError()); + DeleteFileW(dll_name); + ret = compile_cs(dll_source, dll_name, L"library", neutral ? L"-define:NEUTRAL" : L""); + if (!ret) return; + ret = DeleteFileW(dll_source); + ok(ret, "DeleteFileW failed: %lu\n", GetLastError()); + + DeleteFileW(exe_source); + ret = write_resource(exe_source, exe_source); + ok(ret, "Could not write resource: %lu\n", GetLastError()); + DeleteFileW(exe_name); + ret = compile_cs(exe_source, exe_name, L"winexe", L"/reference:libloadpaths.dll"); + if (!ret) return; + ret = DeleteFileW(exe_source); + ok(ret, "DeleteFileW failed: %lu\n", GetLastError()); + + DeleteFileW(cfg_name); + ret = write_resource(cfg_name, cfg_name); + ok(ret, "Could not write resource: %lu\n", GetLastError()); + + for (i = 0; i < ARRAY_SIZE(loadpaths); ++i) + { + const WCHAR *path = loadpaths[i]; + BOOL expect_failure = neutral ? wcsstr(path, L"en") != NULL + : wcsstr(path, L"en") == NULL; + + wcscpy(tmp, path); + PathAppendW(tmp, dll_name); + test_loadpaths_execute(exe_name, dll_name, NULL, tmp, expect_failure, !neutral && !*path); + + wcscpy(tmp, L"private"); + if (*path) PathAppendW(tmp, path); + PathAppendW(tmp, dll_name); + + test_loadpaths_execute(exe_name, dll_name, NULL, tmp, TRUE, FALSE); + test_loadpaths_execute(exe_name, dll_name, cfg_name, tmp, expect_failure, FALSE); + + /* exe name for dll should work too */ + if (*path) + { + wcscpy(tmp, path); + PathAppendW(tmp, dll_name); + wcscpy(tmp + wcslen(tmp) - 4, L".exe"); + test_loadpaths_execute(exe_name, dll_name, NULL, tmp, expect_failure, FALSE); + } + + wcscpy(tmp, L"private"); + if (*path) PathAppendW(tmp, path); + PathAppendW(tmp, dll_name); + wcscpy(tmp + wcslen(tmp) - 4, L".exe"); + + test_loadpaths_execute(exe_name, dll_name, NULL, tmp, TRUE, FALSE); + test_loadpaths_execute(exe_name, dll_name, cfg_name, tmp, expect_failure, FALSE); + } + + ret = DeleteFileW(cfg_name); + ok(ret, "DeleteFileW failed: %lu\n", GetLastError()); + ret = DeleteFileW(exe_name); + ok(ret, "DeleteFileW failed: %lu\n", GetLastError()); + ret = DeleteFileW(dll_name); + ok(ret, "DeleteFileW failed: %lu\n", GetLastError()); +} + +static void test_createdomain(void) +{ + static const WCHAR test_name[] = {'t','e','s','t',0}; + static const WCHAR test2_name[] = {'t','e','s','t','2',0}; + ICLRMetaHost *metahost; + ICLRRuntimeInfo *runtimeinfo; + ICorRuntimeHost *runtimehost; + IUnknown *domain, *defaultdomain_unk, *defaultdomain, *newdomain_unk, *newdomain, *domainsetup, + *newdomain2_unk, *newdomain2; + HRESULT hr; + + if (!pCLRCreateInstance) + { + win_skip("Function CLRCreateInstance not found.\n"); + return; + } + + hr = pCLRCreateInstance(&CLSID_CLRMetaHost, &IID_ICLRMetaHost, (void **)&metahost); + ok(SUCCEEDED(hr), "CLRCreateInstance failed, hr=%#.8lx\n", hr); + + hr = ICLRMetaHost_GetRuntime(metahost, v4_0, &IID_ICLRRuntimeInfo, (void **)&runtimeinfo); + ok(SUCCEEDED(hr), "ICLRMetaHost::GetRuntime failed, hr=%#.8lx\n", hr); + + hr = ICLRRuntimeInfo_GetInterface(runtimeinfo, &CLSID_CorRuntimeHost, &IID_ICorRuntimeHost, + (void **)&runtimehost); + ok(SUCCEEDED(hr), "ICLRRuntimeInfo::GetInterface failed, hr=%#.8lx\n", hr); + + hr = ICorRuntimeHost_Start(runtimehost); + ok(SUCCEEDED(hr), "ICorRuntimeHost::Start failed, hr=%#.8lx\n", hr); + + hr = ICorRuntimeHost_GetDefaultDomain(runtimehost, &domain); + ok(SUCCEEDED(hr), "ICorRuntimeHost::GetDefaultDomain failed, hr=%#.8lx\n", hr); + + hr = IUnknown_QueryInterface(domain, &IID_IUnknown, (void **)&defaultdomain_unk); + ok(SUCCEEDED(hr), "COM object doesn't support IUnknown?!\n"); + + hr = IUnknown_QueryInterface(domain, &IID__AppDomain, (void **)&defaultdomain); + ok(SUCCEEDED(hr), "AppDomain object doesn't support _AppDomain interface\n"); + + IUnknown_Release(domain); + + hr = ICorRuntimeHost_CreateDomain(runtimehost, test_name, NULL, &domain); + ok(SUCCEEDED(hr), "ICorRuntimeHost::CreateDomain failed, hr=%#.8lx\n", hr); + + hr = IUnknown_QueryInterface(domain, &IID_IUnknown, (void **)&newdomain_unk); + ok(SUCCEEDED(hr), "COM object doesn't support IUnknown?!\n"); + + hr = IUnknown_QueryInterface(domain, &IID__AppDomain, (void **)&newdomain); + ok(SUCCEEDED(hr), "AppDomain object doesn't support _AppDomain interface\n"); + + IUnknown_Release(domain); + + ok(defaultdomain_unk != newdomain_unk, "New and default domain objects are the same\n"); + + hr = ICorRuntimeHost_CreateDomainSetup(runtimehost, &domainsetup); + ok(SUCCEEDED(hr), "ICorRuntimeHost::CreateDomainSetup failed, hr=%#.8lx\n", hr); + + hr = ICorRuntimeHost_CreateDomainEx(runtimehost, test2_name, domainsetup, NULL, &domain); + ok(SUCCEEDED(hr), "ICorRuntimeHost::CreateDomainEx failed, hr=%#.8lx\n", hr); + + hr = IUnknown_QueryInterface(domain, &IID_IUnknown, (void **)&newdomain2_unk); + ok(SUCCEEDED(hr), "COM object doesn't support IUnknown?!\n"); + + hr = IUnknown_QueryInterface(domain, &IID__AppDomain, (void **)&newdomain2); + ok(SUCCEEDED(hr), "AppDomain object doesn't support _AppDomain interface\n"); + + IUnknown_Release(domain); + + ok(defaultdomain_unk != newdomain2_unk, "New and default domain objects are the same\n"); + ok(newdomain_unk != newdomain2_unk, "Both new domain objects are the same\n"); + + IUnknown_Release(newdomain2); + IUnknown_Release(newdomain2_unk); + IUnknown_Release(domainsetup); + IUnknown_Release(newdomain); + IUnknown_Release(newdomain_unk); + IUnknown_Release(defaultdomain); + IUnknown_Release(defaultdomain_unk); + + ICorRuntimeHost_Release(runtimehost); + + ICLRRuntimeInfo_Release(runtimeinfo); + + ICLRMetaHost_Release(metahost); +} + START_TEST(mscoree) { + int argc; + char** argv; + if (!init_functionpointers()) return; + argc = winetest_get_mainargs(&argv); + if (argc >= 3 && !strcmp(argv[2], "check_runtime")) + { + int result = check_runtime(); + FreeLibrary(hmscoree); + exit(result); + } + test_versioninfo(); test_loadlibraryshim(); test_createconfigstream(); + test_createinstance(); + + if (runtime_is_usable()) + { + test_createdomain(); + } + + test_loadpaths(FALSE); + test_loadpaths(TRUE); FreeLibrary(hmscoree); } diff --git a/modules/rostests/winetests/mscoree/resource.rc b/modules/rostests/winetests/mscoree/resource.rc new file mode 100644 index 00000000000..34516f744a0 --- /dev/null +++ b/modules/rostests/winetests/mscoree/resource.rc @@ -0,0 +1,39 @@ +/* + * Resources for mscoree test suite. + * + * Copyright 2018 Fabian Maurer + * + * 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 + */ + +#include "windef.h" + +/* @makedep: comtest.cs */ +comtest.cs RCDATA comtest.cs + +/* @makedep: comtest_exe.manifest */ +comtest_exe.manifest RCDATA comtest_exe.manifest + +/* @makedep: comtest_dll.manifest */ +comtest_dll.manifest RCDATA comtest_dll.manifest + +/* @makedep: loadpaths.exe.cs */ +loadpaths.exe.cs RCDATA loadpaths.exe.cs + +/* @makedep: loadpaths.dll.cs */ +loadpaths.dll.cs RCDATA loadpaths.dll.cs + +/* @makedep: loadpaths.exe.config */ +loadpaths.exe.config RCDATA loadpaths.exe.config diff --git a/modules/rostests/winetests/mscoree/testlist.c b/modules/rostests/winetests/mscoree/testlist.c index 6c7c5401224..875f53c1aa6 100644 --- a/modules/rostests/winetests/mscoree/testlist.c +++ b/modules/rostests/winetests/mscoree/testlist.c @@ -4,12 +4,14 @@ #define STANDALONE #include "wine/test.h" +//extern void func_comtest(void); extern void func_debugging(void); extern void func_metahost(void); extern void func_mscoree(void); const struct test winetest_testlist[] = { + //{ "comtest", func_comtest }, // Disabled because mono and mono sdk is not installed { "debugging", func_debugging }, { "metahost", func_metahost }, { "mscoree", func_mscoree },