>From ad3f164f35b23a9694861c4be91c5f99b57c3a13 Mon Sep 17 00:00:00 2001
From: Alistair Leslie-Hughes <leslie_alistair@hotmail.com>
Date: Fri, 6 Jan 2012 14:33:19 +1100
Subject: [PATCH] Implement DllGetClassObject
To: wine-patches <wine-patches@winehq.org>

---
 dlls/mscoree/corruntimehost.c  |  200 ++++++++++++++++++++++++++++++++++++++++
 dlls/mscoree/mscoree_main.c    |   99 +++++++++++++++++++-
 dlls/mscoree/mscoree_private.h |    2 +
 3 files changed, 299 insertions(+), 2 deletions(-)

diff --git a/dlls/mscoree/corruntimehost.c b/dlls/mscoree/corruntimehost.c
index 5042f20..7462b0a 100644
--- a/dlls/mscoree/corruntimehost.c
+++ b/dlls/mscoree/corruntimehost.c
@@ -38,6 +38,7 @@
 #include "mscoree_private.h"
 
 #include "wine/debug.h"
+#include "wine/unicode.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL( mscoree );
 
@@ -769,6 +770,70 @@ HRESULT RuntimeHost_GetIUnknownForObject(RuntimeHost *This, MonoObject *obj,
     return S_OK;
 }
 
+HRESULT RuntimeHost_GetIDispatchForObject(RuntimeHost *This, MonoObject *obj,
+    IDispatch **ppUnk)
+{
+    MonoDomain *domain;
+    MonoAssembly *assembly;
+    MonoImage *image;
+    MonoClass *klass;
+    MonoMethod *method;
+    MonoObject *result;
+    MonoObject *exception = NULL;
+    void *args[2];
+
+    domain = This->mono->mono_object_get_domain(obj);
+
+    assembly = This->mono->mono_domain_assembly_open(domain, "mscorlib");
+    if (!assembly)
+    {
+        ERR("Cannot load mscorlib\n");
+        return E_FAIL;
+    }
+
+    image = This->mono->mono_assembly_get_image(assembly);
+    if (!image)
+    {
+        ERR("Couldn't get assembly image\n");
+        return E_FAIL;
+    }
+
+    klass = This->mono->mono_class_from_name(image, "System.Runtime.InteropServices", "Marshal");
+    if (!klass)
+    {
+        ERR("Couldn't get class from image\n");
+        return E_FAIL;
+    }
+
+    method = This->mono->mono_class_get_method_from_name(klass, "GetIDispatchForObject", 1);
+    if (!method)
+    {
+        ERR("Couldn't get method from class\n");
+        return E_FAIL;
+    }
+
+    args[0] = obj;
+    args[1] = NULL;
+    /*
+     * If ComVisible isn't set to True then an exception will be thrown.
+     */
+    result = This->mono->mono_runtime_invoke(method, NULL, args, &exception);
+    if (exception)
+    {
+        ERR("Couldn't get result pointer\n");
+        return E_FAIL;
+    }
+
+    *ppUnk = *(IDispatch**)This->mono->mono_object_unbox(result);
+    if (!*ppUnk)
+    {
+        ERR("GetIDispatchForObject returned 0\n");
+        return E_FAIL;
+    }
+
+    return S_OK;
+}
+
 static void get_utf8_args(int *argc, char ***argv)
 {
     WCHAR **argvw;
@@ -943,3 +1008,138 @@ HRESULT RuntimeHost_Destroy(RuntimeHost *This)
     HeapFree( GetProcessHeap(), 0, This );
     return S_OK;
 }
+
+#define CHARS_IN_GUID 39
+#define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
+
+HRESULT create_monodata(REFIID riid, LPVOID *ppObj )
+{
+    static const WCHAR wszCodebase[] = {'C','o','d','e','B','a','s','e',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];
+    MonoDomain *domain;
+    MonoAssembly *assembly;
+    ICLRRuntimeInfo *info;
+    RuntimeHost *host;
+    HRESULT hr;
+    HKEY key;
+    LONG res;
+    int offset = 0;
+    WCHAR codebase[MAX_PATH + 8];
+    char classname[350], *ns;
+    WCHAR filename[MAX_PATH];
+
+    DWORD dwBufLen = 350;
+
+    lstrcpyW(path, wszCLSIDSlash);
+    StringFromGUID2(riid, path + lstrlenW(wszCLSIDSlash), CHARS_IN_GUID);
+    lstrcatW(path, wszInprocServer32);
+
+    TRACE("Registry key: %s\n", debugstr_w(path));
+
+    res = RegOpenKeyExW(HKEY_CLASSES_ROOT, path, 0, KEY_READ, &key);
+    if (res == ERROR_FILE_NOT_FOUND)
+        return CLASS_E_CLASSNOTAVAILABLE;
+
+    res = RegGetValueA( key, NULL, "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", 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));
+
+    *ppObj = NULL;
+
+    hr = get_runtime_info(filen, NULL, NULL, 0, 0, FALSE, &info);
+    if (SUCCEEDED(hr))
+    {
+        hr = ICLRRuntimeInfo_GetRuntimeHost(info, &host);
+
+        if (SUCCEEDED(hr))
+            hr = RuntimeHost_GetDefaultDomain(host, &domain);
+
+        if (SUCCEEDED(hr))
+        {
+            MonoImage *image;
+            MonoClass *klass;
+            MonoObject *result;
+            IDispatch *disp = NULL;
+            char *filenameA;
+
+            filenameA = WtoA(filename);
+            assembly = host->mono->mono_domain_assembly_open(domain, filenameA);
+            HeapFree(GetProcessHeap(), 0, filenameA);
+            if (!assembly)
+            {
+                ERR("Cannot open assembly %s\n", filenameA);
+                goto cleanup;
+            }
+
+            image = host->mono->mono_assembly_get_image(assembly);
+            if (!image)
+            {
+                ERR("Couldn't get assembly image\n");
+                goto cleanup;
+            }
+
+            ns = strrchr(classname, '.');
+            *ns = '\0';
+            klass = host->mono->mono_class_from_name(image, classname, ns+1);
+            if (!klass)
+            {
+                ERR("Couldn't get class from image\n");
+                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);
+
+            hr = RuntimeHost_GetIDispatchForObject(host, result, &disp);
+            if (SUCCEEDED(hr))
+            {
+                hr = IDispatch_QueryInterface(disp, &IID_IDispatch, ppObj);
+
+                IDispatch_Release(disp);
+            }
+            else
+                hr = CLASS_E_CLASSNOTAVAILABLE;
+        }
+        else
+            hr = CLASS_E_CLASSNOTAVAILABLE;
+    }
+    else
+        hr = CLASS_E_CLASSNOTAVAILABLE;
+
+cleanup:
+    if(info)
+        ICLRRuntimeInfo_Release(info);
+
+    RegCloseKey(key);
+
+    return hr;
+}
diff --git a/dlls/mscoree/mscoree_main.c b/dlls/mscoree/mscoree_main.c
index 8b72ec8..8412cf9 100644
--- a/dlls/mscoree/mscoree_main.c
+++ b/dlls/mscoree/mscoree_main.c
@@ -52,6 +52,8 @@ WINE_DEFAULT_DEBUG_CHANNEL( mscoree );
 
 static HINSTANCE MSCOREE_hInstance;
 
+typedef HRESULT (*fnCreateInstance)(REFIID riid, LPVOID *ppObj);
+
 char *WtoA(LPCWSTR wstr)
 {
     int length;
@@ -89,6 +91,90 @@ static BOOL get_install_root(LPWSTR install_dir)
     return TRUE;
 }
 
+typedef struct mscorecf
+{
+    IClassFactory    IClassFactory_iface;
+    fnCreateInstance pfnCreateInstance;
+
+    IID iid;
+} mscorecf;
+
+static inline mscorecf *impl_from_IClassFactory( IClassFactory *iface )
+{
+    return CONTAINING_RECORD(iface, mscorecf, IClassFactory_iface);
+}
+
+static HRESULT WINAPI mscorecf_QueryInterface(IClassFactory *iface, REFIID riid, LPVOID *ppobj )
+{
+    TRACE("%s %p\n", debugstr_guid(riid), ppobj);
+
+    if (IsEqualGUID(riid, &IID_IUnknown) ||
+        IsEqualGUID(riid, &IID_IClassFactory))
+    {
+        IClassFactory_AddRef( iface );
+        *ppobj = iface;
+        return S_OK;
+    }
+
+    ERR("interface %s not implemented\n", debugstr_guid(riid));
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI mscorecf_AddRef(IClassFactory *iface )
+{
+    return 2;
+}
+
+static ULONG WINAPI mscorecf_Release(IClassFactory *iface )
+{
+    return 1;
+}
+
+static HRESULT WINAPI mscorecf_CreateInstance(IClassFactory *iface,LPUNKNOWN pOuter,
+                            REFIID riid, LPVOID *ppobj )
+{
+    mscorecf *This = impl_from_IClassFactory( iface );
+    HRESULT hr;
+    IUnknown *punk;
+
+    TRACE("%p %s %p\n", pOuter, debugstr_guid(riid), ppobj );
+
+    *ppobj = NULL;
+
+    if (pOuter)
+        return CLASS_E_NOAGGREGATION;
+
+    hr = This->pfnCreateInstance( &This->iid, (LPVOID*) &punk );
+    if (SUCCEEDED(hr))
+    {
+        hr = IUnknown_QueryInterface( punk, riid, ppobj );
+
+        IUnknown_Release( punk );
+    }
+    else
+    {
+        WARN("Cannot create an instance object. 0x%08x\n", hr);
+    }
+    return hr;
+}
+
+static HRESULT WINAPI mscorecf_LockServer(IClassFactory *iface, BOOL dolock)
+{
+    FIXME("(%p)->(%d),stub!\n",iface,dolock);
+    return S_OK;
+}
+
+static const struct IClassFactoryVtbl mscorecf_vtbl =
+{
+    mscorecf_QueryInterface,
+    mscorecf_AddRef,
+    mscorecf_Release,
+    mscorecf_CreateInstance,
+    mscorecf_LockServer
+};
+
+static mscorecf monoccf = { {&mscorecf_vtbl}, create_monodata };
+
 HRESULT WINAPI CorBindToRuntimeHost(LPCWSTR pwszVersion, LPCWSTR pwszBuildFlavor,
                                     LPCWSTR pwszHostConfigFile, VOID *pReserved,
                                     DWORD startupFlags, REFCLSID rclsid,
@@ -504,11 +590,20 @@ HRESULT WINAPI CLRCreateInstance(REFCLSID clsid, REFIID riid, LPVOID *ppInterfac
 
 HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
 {
-    FIXME("(%s, %s, %p): stub\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
+    IClassFactory *cf = NULL;
+    mscorecf *This;
+
+    TRACE("(%s, %s, %p): stub\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
+
     if(!ppv)
         return E_INVALIDARG;
 
-    return E_NOTIMPL;
+    cf = &monoccf.IClassFactory_iface;
+
+    This = impl_from_IClassFactory(cf);
+    This->iid = *rclsid;
+
+    return IClassFactory_QueryInterface( cf, riid, ppv );
 }
 
 HRESULT WINAPI DllRegisterServer(void)
diff --git a/dlls/mscoree/mscoree_private.h b/dlls/mscoree/mscoree_private.h
index a45a405..d033275 100644
--- a/dlls/mscoree/mscoree_private.h
+++ b/dlls/mscoree/mscoree_private.h
@@ -186,4 +186,6 @@ HRESULT WINAPI CLRMetaHost_GetRuntime(ICLRMetaHost* iface, LPCWSTR pwzVersion, R
 
 extern HRESULT CorDebug_Create(ICLRRuntimeHost *runtimehost, IUnknown** ppUnk) DECLSPEC_HIDDEN;
 
+extern HRESULT create_monodata(REFIID riid, LPVOID *ppObj) DECLSPEC_HIDDEN;
+
 #endif   /* __MSCOREE_PRIVATE__ */
-- 
1.7.5.4

