dbghelp: Constify some variables.
[wine] / dlls / ole32 / compobj.c
index 95dff3b..28e5da1 100644 (file)
@@ -34,9 +34,6 @@
  *   - Implement the OXID resolver so we don't need magic endpoint names for
  *     clients and servers to meet up
  *
- *   - Make all ole interface marshaling use NDR to be wire compatible with
- *     native DCOM
- *
  */
 
 #include "config.h"
 #include "winerror.h"
 #include "winreg.h"
 #include "winuser.h"
+#define USE_COM_CONTEXT_DEF
 #include "objbase.h"
 #include "ole2.h"
 #include "ole2ver.h"
+#include "ctxtcall.h"
+#include "dde.h"
 
+#include "initguid.h"
 #include "compobj_private.h"
+#include "moniker.h"
 
 #include "wine/unicode.h"
 #include "wine/debug.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(ole);
 
-HINSTANCE OLE32_hInstance = 0; /* FIXME: make static ... */
-
 #define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
 
 /****************************************************************************
  * This section defines variables internal to the COM module.
- *
- * TODO: Most of these things will have to be made thread-safe.
  */
 
-static HRESULT COM_GetRegisteredClassObject(const struct apartment *apt, REFCLSID rclsid,
-                                            DWORD dwClsContext, LPUNKNOWN*  ppUnk);
-static void COM_RevokeAllClasses(const struct apartment *apt);
-static HRESULT get_inproc_class_object(APARTMENT *apt, HKEY hkeydll, REFCLSID rclsid, REFIID riid, void **ppv);
-
 static APARTMENT *MTA; /* protected by csApartment */
 static APARTMENT *MainApartment; /* the first STA apartment */
 static struct list apts = LIST_INIT( apts ); /* protected by csApartment */
@@ -116,7 +109,7 @@ static LONG s_COMServerProcessReferences = 0;
  * objects.
  *
  * TODO: Make this data structure aware of inter-process communication. This
- *       means that parts of this will be exported to the Wine Server.
+ *       means that parts of this will be exported to rpcss.
  */
 typedef struct tagRegisteredClass
 {
@@ -187,60 +180,147 @@ struct apartment_loaded_dll
 
 static const WCHAR wszAptWinClass[] = {'O','l','e','M','a','i','n','T','h','r','e','a','d','W','n','d','C','l','a','s','s',' ',
                                        '0','x','#','#','#','#','#','#','#','#',' ',0};
-static LRESULT CALLBACK apartment_wndproc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
-static HRESULT apartment_getclassobject(struct apartment *apt, LPCWSTR dllpath,
-                                        BOOL apartment_threaded,
-                                        REFCLSID rclsid, REFIID riid, void **ppv);
-static void apartment_freeunusedlibraries(struct apartment *apt, DWORD delay);
 
-static HRESULT COMPOBJ_DllList_Add(LPCWSTR library_name, OpenDll **ret);
-static OpenDll *COMPOBJ_DllList_Get(LPCWSTR library_name);
-static void COMPOBJ_DllList_ReleaseRef(OpenDll *entry, BOOL free_entry);
+/*****************************************************************************
+ * This section contains OpenDllList implementation
+ */
 
-static DWORD COM_RegReadPath(HKEY hkeyroot, const WCHAR *keyname, const WCHAR *valuename, WCHAR * dst, DWORD dstlen);
+static OpenDll *COMPOBJ_DllList_Get(LPCWSTR library_name)
+{
+    OpenDll *ptr;
+    OpenDll *ret = NULL;
+    EnterCriticalSection(&csOpenDllList);
+    LIST_FOR_EACH_ENTRY(ptr, &openDllList, OpenDll, entry)
+    {
+        if (!strcmpiW(library_name, ptr->library_name) &&
+            (InterlockedIncrement(&ptr->refs) != 1) /* entry is being destroy if == 1 */)
+        {
+            ret = ptr;
+            break;
+        }
+    }
+    LeaveCriticalSection(&csOpenDllList);
+    return ret;
+}
 
-static void COMPOBJ_InitProcess( void )
+/* caller must ensure that library_name is not already in the open dll list */
+static HRESULT COMPOBJ_DllList_Add(LPCWSTR library_name, OpenDll **ret)
 {
-    WNDCLASSW wclass;
+    OpenDll *entry;
+    int len;
+    HRESULT hr = S_OK;
+    HANDLE hLibrary;
+    DllCanUnloadNowFunc DllCanUnloadNow;
+    DllGetClassObjectFunc DllGetClassObject;
 
-    /* Dispatching to the correct thread in an apartment is done through
-     * window messages rather than RPC transports. When an interface is
-     * marshalled into another apartment in the same process, a window of the
-     * following class is created. The *caller* of CoMarshalInterface (ie the
-     * application) is responsible for pumping the message loop in that thread.
-     * The WM_USER messages which point to the RPCs are then dispatched to
-     * COM_AptWndProc by the user's code from the apartment in which the interface
-     * was unmarshalled.
-     */
-    memset(&wclass, 0, sizeof(wclass));
-    wclass.lpfnWndProc = apartment_wndproc;
-    wclass.hInstance = OLE32_hInstance;
-    wclass.lpszClassName = wszAptWinClass;
-    RegisterClassW(&wclass);
+    TRACE("\n");
+
+    *ret = COMPOBJ_DllList_Get(library_name);
+    if (*ret) return S_OK;
+
+    /* do this outside the csOpenDllList to avoid creating a lock dependency on
+     * the loader lock */
+    hLibrary = LoadLibraryExW(library_name, 0, LOAD_WITH_ALTERED_SEARCH_PATH);
+    if (!hLibrary)
+    {
+        ERR("couldn't load in-process dll %s\n", debugstr_w(library_name));
+        /* failure: DLL could not be loaded */
+        return E_ACCESSDENIED; /* FIXME: or should this be CO_E_DLLNOTFOUND? */
+    }
+
+    DllCanUnloadNow = (void *)GetProcAddress(hLibrary, "DllCanUnloadNow");
+    /* Note: failing to find DllCanUnloadNow is not a failure */
+    DllGetClassObject = (void *)GetProcAddress(hLibrary, "DllGetClassObject");
+    if (!DllGetClassObject)
+    {
+        /* failure: the dll did not export DllGetClassObject */
+        ERR("couldn't find function DllGetClassObject in %s\n", debugstr_w(library_name));
+        FreeLibrary(hLibrary);
+        return CO_E_DLLNOTFOUND;
+    }
+
+    EnterCriticalSection( &csOpenDllList );
+
+    *ret = COMPOBJ_DllList_Get(library_name);
+    if (*ret)
+    {
+        /* another caller to this function already added the dll while we
+         * weren't in the critical section */
+        FreeLibrary(hLibrary);
+    }
+    else
+    {
+        len = strlenW(library_name);
+        entry = HeapAlloc(GetProcessHeap(),0, sizeof(OpenDll));
+        if (entry)
+            entry->library_name = HeapAlloc(GetProcessHeap(), 0, (len + 1)*sizeof(WCHAR));
+        if (entry && entry->library_name)
+        {
+            memcpy(entry->library_name, library_name, (len + 1)*sizeof(WCHAR));
+            entry->library = hLibrary;
+            entry->refs = 1;
+            entry->DllCanUnloadNow = DllCanUnloadNow;
+            entry->DllGetClassObject = DllGetClassObject;
+            list_add_tail(&openDllList, &entry->entry);
+        }
+        else
+        {
+            HeapFree(GetProcessHeap(), 0, entry);
+            hr = E_OUTOFMEMORY;
+            FreeLibrary(hLibrary);
+        }
+        *ret = entry;
+    }
+
+    LeaveCriticalSection( &csOpenDllList );
+
+    return hr;
 }
 
-static void COMPOBJ_UninitProcess( void )
+/* pass FALSE for free_entry to release a reference without destroying the
+ * entry if it reaches zero or TRUE otherwise */
+static void COMPOBJ_DllList_ReleaseRef(OpenDll *entry, BOOL free_entry)
 {
-    UnregisterClassW(wszAptWinClass, OLE32_hInstance);
+    if (!InterlockedDecrement(&entry->refs) && free_entry)
+    {
+        EnterCriticalSection(&csOpenDllList);
+        list_remove(&entry->entry);
+        LeaveCriticalSection(&csOpenDllList);
+
+        TRACE("freeing %p\n", entry->library);
+        FreeLibrary(entry->library);
+
+        HeapFree(GetProcessHeap(), 0, entry->library_name);
+        HeapFree(GetProcessHeap(), 0, entry);
+    }
 }
 
-static void COM_TlsDestroy(void)
+/* frees memory associated with active dll list */
+static void COMPOBJ_DllList_Free(void)
 {
-    struct oletls *info = NtCurrentTeb()->ReservedForOle;
-    if (info)
+    OpenDll *entry, *cursor2;
+    EnterCriticalSection(&csOpenDllList);
+    LIST_FOR_EACH_ENTRY_SAFE(entry, cursor2, &openDllList, OpenDll, entry)
     {
-        if (info->apt) apartment_release(info->apt);
-        if (info->errorinfo) IErrorInfo_Release(info->errorinfo);
-        if (info->state) IUnknown_Release(info->state);
-        HeapFree(GetProcessHeap(), 0, info);
-        NtCurrentTeb()->ReservedForOle = NULL;
+        list_remove(&entry->entry);
+
+        HeapFree(GetProcessHeap(), 0, entry->library_name);
+        HeapFree(GetProcessHeap(), 0, entry);
     }
+    LeaveCriticalSection(&csOpenDllList);
 }
 
 /******************************************************************************
  * Manage apartments.
  */
 
+static DWORD apartment_addref(struct apartment *apt)
+{
+    DWORD refs = InterlockedIncrement(&apt->refs);
+    TRACE("%s: before = %d\n", wine_dbgstr_longlong(apt->oxid), refs - 1);
+    return refs;
+}
+
 /* allocates memory and fills in the necessary fields for a new apartment
  * object. must be called inside apartment cs */
 static APARTMENT *apartment_construct(DWORD model)
@@ -305,6 +385,9 @@ static APARTMENT *apartment_get_or_create(DWORD model)
             }
 
             LeaveCriticalSection(&csApartment);
+
+            if (apt->main)
+                apartment_createwindowifneeded(apt);
         }
         else
         {
@@ -336,102 +419,347 @@ static inline BOOL apartment_is_model(const APARTMENT *apt, DWORD model)
     return (apt->multi_threaded == !(model & COINIT_APARTMENTTHREADED));
 }
 
-DWORD apartment_addref(struct apartment *apt)
+static void COM_RevokeRegisteredClassObject(RegisteredClass *curClass)
 {
-    DWORD refs = InterlockedIncrement(&apt->refs);
-    TRACE("%s: before = %d\n", wine_dbgstr_longlong(apt->oxid), refs - 1);
-    return refs;
-}
+    list_remove(&curClass->entry);
 
-DWORD apartment_release(struct apartment *apt)
-{
-    DWORD ret;
+    if (curClass->runContext & CLSCTX_LOCAL_SERVER)
+        RPC_StopLocalServer(curClass->RpcRegistration);
 
-    EnterCriticalSection(&csApartment);
+    /*
+     * Release the reference to the class object.
+     */
+    IUnknown_Release(curClass->classObject);
 
-    ret = InterlockedDecrement(&apt->refs);
-    TRACE("%s: after = %d\n", wine_dbgstr_longlong(apt->oxid), ret);
-    /* destruction stuff that needs to happen under csApartment CS */
-    if (ret == 0)
+    if (curClass->pMarshaledData)
     {
-        if (apt == MTA) MTA = NULL;
-        else if (apt == MainApartment) MainApartment = NULL;
-        list_remove(&apt->entry);
+        LARGE_INTEGER zero;
+        memset(&zero, 0, sizeof(zero));
+        IStream_Seek(curClass->pMarshaledData, zero, STREAM_SEEK_SET, NULL);
+        CoReleaseMarshalData(curClass->pMarshaledData);
+        IStream_Release(curClass->pMarshaledData);
     }
 
-    LeaveCriticalSection(&csApartment);
+    HeapFree(GetProcessHeap(), 0, curClass);
+}
 
-    if (ret == 0)
-    {
-        struct list *cursor, *cursor2;
+static void COM_RevokeAllClasses(const struct apartment *apt)
+{
+  RegisteredClass *curClass, *cursor;
 
-        TRACE("destroying apartment %p, oxid %s\n", apt, wine_dbgstr_longlong(apt->oxid));
+  EnterCriticalSection( &csRegisteredClassList );
 
-        /* Release the references to the registered class objects */
-        COM_RevokeAllClasses(apt);
+  LIST_FOR_EACH_ENTRY_SAFE(curClass, cursor, &RegisteredClassList, RegisteredClass, entry)
+  {
+    if (curClass->apartment_id == apt->oxid)
+      COM_RevokeRegisteredClassObject(curClass);
+  }
 
-        /* no locking is needed for this apartment, because no other thread
-         * can access it at this point */
+  LeaveCriticalSection( &csRegisteredClassList );
+}
 
-        apartment_disconnectproxies(apt);
+/******************************************************************************
+ * Implementation of the manual reset event object. (CLSID_ManualResetEvent)
+ */
 
-        if (apt->win) DestroyWindow(apt->win);
-        if (apt->host_apt_tid) PostThreadMessageW(apt->host_apt_tid, WM_QUIT, 0, 0);
+typedef struct ManualResetEvent {
+    ISynchronize   ISynchronize_iface;
+    LONG ref;
+    HANDLE event;
+} MREImpl;
 
-        LIST_FOR_EACH_SAFE(cursor, cursor2, &apt->stubmgrs)
-        {
-            struct stub_manager *stubmgr = LIST_ENTRY(cursor, struct stub_manager, entry);
-            /* release the implicit reference given by the fact that the
-             * stub has external references (it must do since it is in the
-             * stub manager list in the apartment and all non-apartment users
-             * must have a ref on the apartment and so it cannot be destroyed).
-             */
-            stub_manager_int_release(stubmgr);
-        }
+static inline MREImpl *impl_from_ISynchronize(ISynchronize *iface)
+{
+    return CONTAINING_RECORD(iface, MREImpl, ISynchronize_iface);
+}
 
-        LIST_FOR_EACH_SAFE(cursor, cursor2, &apt->psclsids)
-        {
-            struct registered_psclsid *registered_psclsid =
-                LIST_ENTRY(cursor, struct registered_psclsid, entry);
+static HRESULT WINAPI ISynchronize_fnQueryInterface(ISynchronize *iface, REFIID riid, void **ppv)
+{
+    MREImpl *This = impl_from_ISynchronize(iface);
+    TRACE("%p (%s, %p)\n", This, debugstr_guid(riid), ppv);
 
-            list_remove(&registered_psclsid->entry);
-            HeapFree(GetProcessHeap(), 0, registered_psclsid);
-        }
+    *ppv = NULL;
+    if(IsEqualGUID(riid, &IID_IUnknown) ||
+       IsEqualGUID(riid, &IID_ISynchronize))
+        *ppv = This;
+    else
+        ERR("Unknown interface %s requested.\n", debugstr_guid(riid));
 
-        /* if this assert fires, then another thread took a reference to a
-         * stub manager without taking a reference to the containing
-         * apartment, which it must do. */
-        assert(list_empty(&apt->stubmgrs));
+    if(*ppv)
+    {
+        IUnknown_AddRef((IUnknown*)*ppv);
+        return S_OK;
+    }
 
-        if (apt->filter) IUnknown_Release(apt->filter);
+    return E_NOINTERFACE;
+}
 
-        /* free as many unused libraries as possible... */
-        apartment_freeunusedlibraries(apt, 0);
+static ULONG WINAPI ISynchronize_fnAddRef(ISynchronize *iface)
+{
+    MREImpl *This = impl_from_ISynchronize(iface);
+    LONG ref = InterlockedIncrement(&This->ref);
+    TRACE("%p - ref %d\n", This, ref);
 
-        /* ... and free the memory for the apartment loaded dll entry and
-         * release the dll list reference without freeing the library for the
-         * rest */
-        while ((cursor = list_head(&apt->loaded_dlls)))
-        {
-            struct apartment_loaded_dll *apartment_loaded_dll = LIST_ENTRY(cursor, struct apartment_loaded_dll, entry);
-            COMPOBJ_DllList_ReleaseRef(apartment_loaded_dll->dll, FALSE);
-            list_remove(cursor);
-            HeapFree(GetProcessHeap(), 0, apartment_loaded_dll);
-        }
+    return ref;
+}
 
-        DEBUG_CLEAR_CRITSEC_NAME(&apt->cs);
-        DeleteCriticalSection(&apt->cs);
+static ULONG WINAPI ISynchronize_fnRelease(ISynchronize *iface)
+{
+    MREImpl *This = impl_from_ISynchronize(iface);
+    LONG ref = InterlockedDecrement(&This->ref);
+    TRACE("%p - ref %d\n", This, ref);
 
-        HeapFree(GetProcessHeap(), 0, apt);
+    if(!ref)
+    {
+        CloseHandle(This->event);
+        HeapFree(GetProcessHeap(), 0, This);
     }
 
-    return ret;
+    return ref;
 }
 
-/* The given OXID must be local to this process: 
- *
- * The ref parameter is here mostly to ensure people remember that
- * they get one, you should normally take a ref for thread safety.
+static HRESULT WINAPI ISynchronize_fnWait(ISynchronize *iface, DWORD dwFlags, DWORD dwMilliseconds)
+{
+    MREImpl *This = impl_from_ISynchronize(iface);
+    UINT index;
+    TRACE("%p (%08x, %08x)\n", This, dwFlags, dwMilliseconds);
+    return CoWaitForMultipleHandles(dwFlags, dwMilliseconds, 1, &This->event, &index);
+}
+
+static HRESULT WINAPI ISynchronize_fnSignal(ISynchronize *iface)
+{
+    MREImpl *This = impl_from_ISynchronize(iface);
+    TRACE("%p\n", This);
+    SetEvent(This->event);
+    return S_OK;
+}
+
+static HRESULT WINAPI ISynchronize_fnReset(ISynchronize *iface)
+{
+    MREImpl *This = impl_from_ISynchronize(iface);
+    TRACE("%p\n", This);
+    ResetEvent(This->event);
+    return S_OK;
+}
+
+static ISynchronizeVtbl vt_ISynchronize = {
+    ISynchronize_fnQueryInterface,
+    ISynchronize_fnAddRef,
+    ISynchronize_fnRelease,
+    ISynchronize_fnWait,
+    ISynchronize_fnSignal,
+    ISynchronize_fnReset
+};
+
+static HRESULT ManualResetEvent_Construct(IUnknown *punkouter, REFIID iid, void **ppv)
+{
+    MREImpl *This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MREImpl));
+    HRESULT hr;
+
+    if(punkouter)
+        FIXME("Aggregation not implemented.\n");
+
+    This->ref = 1;
+    This->ISynchronize_iface.lpVtbl = &vt_ISynchronize;
+    This->event = CreateEventW(NULL, TRUE, FALSE, NULL);
+
+    hr = ISynchronize_QueryInterface(&This->ISynchronize_iface, iid, ppv);
+    ISynchronize_Release(&This->ISynchronize_iface);
+    return hr;
+}
+
+/***********************************************************************
+ *           CoRevokeClassObject [OLE32.@]
+ *
+ * Removes a class object from the class registry.
+ *
+ * PARAMS
+ *  dwRegister [I] Cookie returned from CoRegisterClassObject().
+ *
+ * RETURNS
+ *  Success: S_OK.
+ *  Failure: HRESULT code.
+ *
+ * NOTES
+ *  Must be called from the same apartment that called CoRegisterClassObject(),
+ *  otherwise it will fail with RPC_E_WRONG_THREAD.
+ *
+ * SEE ALSO
+ *  CoRegisterClassObject
+ */
+HRESULT WINAPI CoRevokeClassObject(
+        DWORD dwRegister)
+{
+  HRESULT hr = E_INVALIDARG;
+  RegisteredClass *curClass;
+  APARTMENT *apt;
+
+  TRACE("(%08x)\n",dwRegister);
+
+  apt = COM_CurrentApt();
+  if (!apt)
+  {
+    ERR("COM was not initialized\n");
+    return CO_E_NOTINITIALIZED;
+  }
+
+  EnterCriticalSection( &csRegisteredClassList );
+
+  LIST_FOR_EACH_ENTRY(curClass, &RegisteredClassList, RegisteredClass, entry)
+  {
+    /*
+     * Check if we have a match on the cookie.
+     */
+    if (curClass->dwCookie == dwRegister)
+    {
+      if (curClass->apartment_id == apt->oxid)
+      {
+          COM_RevokeRegisteredClassObject(curClass);
+          hr = S_OK;
+      }
+      else
+      {
+          ERR("called from wrong apartment, should be called from %s\n",
+              wine_dbgstr_longlong(curClass->apartment_id));
+          hr = RPC_E_WRONG_THREAD;
+      }
+      break;
+    }
+  }
+
+  LeaveCriticalSection( &csRegisteredClassList );
+
+  return hr;
+}
+
+/* frees unused libraries loaded by apartment_getclassobject by calling the
+ * DLL's DllCanUnloadNow entry point */
+static void apartment_freeunusedlibraries(struct apartment *apt, DWORD delay)
+{
+    struct apartment_loaded_dll *entry, *next;
+    EnterCriticalSection(&apt->cs);
+    LIST_FOR_EACH_ENTRY_SAFE(entry, next, &apt->loaded_dlls, struct apartment_loaded_dll, entry)
+    {
+       if (entry->dll->DllCanUnloadNow && (entry->dll->DllCanUnloadNow() == S_OK))
+        {
+            DWORD real_delay = delay;
+
+            if (real_delay == INFINITE)
+            {
+                /* DLLs that return multi-threaded objects aren't unloaded
+                 * straight away to cope for programs that have races between
+                 * last object destruction and threads in the DLLs that haven't
+                 * finished, despite DllCanUnloadNow returning S_OK */
+                if (entry->multi_threaded)
+                    real_delay = 10 * 60 * 1000; /* 10 minutes */
+                else
+                    real_delay = 0;
+            }
+
+            if (!real_delay || (entry->unload_time && (entry->unload_time < GetTickCount())))
+            {
+                list_remove(&entry->entry);
+                COMPOBJ_DllList_ReleaseRef(entry->dll, TRUE);
+                HeapFree(GetProcessHeap(), 0, entry);
+            }
+            else
+                entry->unload_time = GetTickCount() + real_delay;
+        }
+        else if (entry->unload_time)
+            entry->unload_time = 0;
+    }
+    LeaveCriticalSection(&apt->cs);
+}
+
+DWORD apartment_release(struct apartment *apt)
+{
+    DWORD ret;
+
+    EnterCriticalSection(&csApartment);
+
+    ret = InterlockedDecrement(&apt->refs);
+    TRACE("%s: after = %d\n", wine_dbgstr_longlong(apt->oxid), ret);
+    /* destruction stuff that needs to happen under csApartment CS */
+    if (ret == 0)
+    {
+        if (apt == MTA) MTA = NULL;
+        else if (apt == MainApartment) MainApartment = NULL;
+        list_remove(&apt->entry);
+    }
+
+    LeaveCriticalSection(&csApartment);
+
+    if (ret == 0)
+    {
+        struct list *cursor, *cursor2;
+
+        TRACE("destroying apartment %p, oxid %s\n", apt, wine_dbgstr_longlong(apt->oxid));
+
+        /* Release the references to the registered class objects */
+        COM_RevokeAllClasses(apt);
+
+        /* no locking is needed for this apartment, because no other thread
+         * can access it at this point */
+
+        apartment_disconnectproxies(apt);
+
+        if (apt->win) DestroyWindow(apt->win);
+        if (apt->host_apt_tid) PostThreadMessageW(apt->host_apt_tid, WM_QUIT, 0, 0);
+
+        LIST_FOR_EACH_SAFE(cursor, cursor2, &apt->stubmgrs)
+        {
+            struct stub_manager *stubmgr = LIST_ENTRY(cursor, struct stub_manager, entry);
+            /* release the implicit reference given by the fact that the
+             * stub has external references (it must do since it is in the
+             * stub manager list in the apartment and all non-apartment users
+             * must have a ref on the apartment and so it cannot be destroyed).
+             */
+            stub_manager_int_release(stubmgr);
+        }
+
+        LIST_FOR_EACH_SAFE(cursor, cursor2, &apt->psclsids)
+        {
+            struct registered_psclsid *registered_psclsid =
+                LIST_ENTRY(cursor, struct registered_psclsid, entry);
+
+            list_remove(&registered_psclsid->entry);
+            HeapFree(GetProcessHeap(), 0, registered_psclsid);
+        }
+
+        /* if this assert fires, then another thread took a reference to a
+         * stub manager without taking a reference to the containing
+         * apartment, which it must do. */
+        assert(list_empty(&apt->stubmgrs));
+
+        if (apt->filter) IUnknown_Release(apt->filter);
+
+        /* free as many unused libraries as possible... */
+        apartment_freeunusedlibraries(apt, 0);
+
+        /* ... and free the memory for the apartment loaded dll entry and
+         * release the dll list reference without freeing the library for the
+         * rest */
+        while ((cursor = list_head(&apt->loaded_dlls)))
+        {
+            struct apartment_loaded_dll *apartment_loaded_dll = LIST_ENTRY(cursor, struct apartment_loaded_dll, entry);
+            COMPOBJ_DllList_ReleaseRef(apartment_loaded_dll->dll, FALSE);
+            list_remove(cursor);
+            HeapFree(GetProcessHeap(), 0, apartment_loaded_dll);
+        }
+
+        DEBUG_CLEAR_CRITSEC_NAME(&apt->cs);
+        DeleteCriticalSection(&apt->cs);
+
+        HeapFree(GetProcessHeap(), 0, apt);
+    }
+
+    return ret;
+}
+
+/* The given OXID must be local to this process: 
+ *
+ * The ref parameter is here mostly to ensure people remember that
+ * they get one, you should normally take a ref for thread safety.
  */
 APARTMENT *apartment_findfromoxid(OXID oxid, BOOL ref)
 {
@@ -473,26 +801,161 @@ APARTMENT *apartment_findfromtid(DWORD tid)
             break;
         }
     }
-    LeaveCriticalSection(&csApartment);
+    LeaveCriticalSection(&csApartment);
+
+    return result;
+}
+
+/* gets the main apartment if it exists. The caller must
+ * release the reference from the apartment as soon as the apartment pointer
+ * is no longer required. */
+static APARTMENT *apartment_findmain(void)
+{
+    APARTMENT *result;
+
+    EnterCriticalSection(&csApartment);
+
+    result = MainApartment;
+    if (result) apartment_addref(result);
+
+    LeaveCriticalSection(&csApartment);
+
+    return result;
+}
+
+/* gets the multi-threaded apartment if it exists. The caller must
+ * release the reference from the apartment as soon as the apartment pointer
+ * is no longer required. */
+static APARTMENT *apartment_find_multi_threaded(void)
+{
+    APARTMENT *result = NULL;
+    struct list *cursor;
+
+    EnterCriticalSection(&csApartment);
+
+    LIST_FOR_EACH( cursor, &apts )
+    {
+        struct apartment *apt = LIST_ENTRY( cursor, struct apartment, entry );
+        if (apt->multi_threaded)
+        {
+            result = apt;
+            apartment_addref(result);
+            break;
+        }
+    }
+
+    LeaveCriticalSection(&csApartment);
+    return result;
+}
+
+/* gets the specified class object by loading the appropriate DLL, if
+ * necessary and calls the DllGetClassObject function for the DLL */
+static HRESULT apartment_getclassobject(struct apartment *apt, LPCWSTR dllpath,
+                                        BOOL apartment_threaded,
+                                        REFCLSID rclsid, REFIID riid, void **ppv)
+{
+    static const WCHAR wszOle32[] = {'o','l','e','3','2','.','d','l','l',0};
+    HRESULT hr = S_OK;
+    BOOL found = FALSE;
+    struct apartment_loaded_dll *apartment_loaded_dll;
+
+    if (!strcmpiW(dllpath, wszOle32))
+    {
+        /* we don't need to control the lifetime of this dll, so use the local
+         * implementation of DllGetClassObject directly */
+        TRACE("calling ole32!DllGetClassObject\n");
+        hr = DllGetClassObject(rclsid, riid, ppv);
+
+        if (hr != S_OK)
+            ERR("DllGetClassObject returned error 0x%08x\n", hr);
+
+        return hr;
+    }
+
+    EnterCriticalSection(&apt->cs);
+
+    LIST_FOR_EACH_ENTRY(apartment_loaded_dll, &apt->loaded_dlls, struct apartment_loaded_dll, entry)
+        if (!strcmpiW(dllpath, apartment_loaded_dll->dll->library_name))
+        {
+            TRACE("found %s already loaded\n", debugstr_w(dllpath));
+            found = TRUE;
+            break;
+        }
+
+    if (!found)
+    {
+        apartment_loaded_dll = HeapAlloc(GetProcessHeap(), 0, sizeof(*apartment_loaded_dll));
+        if (!apartment_loaded_dll)
+            hr = E_OUTOFMEMORY;
+        if (SUCCEEDED(hr))
+        {
+            apartment_loaded_dll->unload_time = 0;
+            apartment_loaded_dll->multi_threaded = FALSE;
+            hr = COMPOBJ_DllList_Add( dllpath, &apartment_loaded_dll->dll );
+            if (FAILED(hr))
+                HeapFree(GetProcessHeap(), 0, apartment_loaded_dll);
+        }
+        if (SUCCEEDED(hr))
+        {
+            TRACE("added new loaded dll %s\n", debugstr_w(dllpath));
+            list_add_tail(&apt->loaded_dlls, &apartment_loaded_dll->entry);
+        }
+    }
+
+    LeaveCriticalSection(&apt->cs);
+
+    if (SUCCEEDED(hr))
+    {
+        /* one component being multi-threaded overrides any number of
+         * apartment-threaded components */
+        if (!apartment_threaded)
+            apartment_loaded_dll->multi_threaded = TRUE;
+
+        TRACE("calling DllGetClassObject %p\n", apartment_loaded_dll->dll->DllGetClassObject);
+        /* OK: get the ClassObject */
+        hr = apartment_loaded_dll->dll->DllGetClassObject(rclsid, riid, ppv);
+
+        if (hr != S_OK)
+            ERR("DllGetClassObject returned error 0x%08x\n", hr);
+    }
 
-    return result;
+    return hr;
 }
 
-/* gets the main apartment if it exists. The caller must
- * release the reference from the apartment as soon as the apartment pointer
- * is no longer required. */
-static APARTMENT *apartment_findmain(void)
+/***********************************************************************
+ *     COM_RegReadPath [internal]
+ *
+ *     Reads a registry value and expands it when necessary
+ */
+static DWORD COM_RegReadPath(HKEY hkeyroot, const WCHAR *keyname, const WCHAR *valuename, WCHAR * dst, DWORD dstlen)
 {
-    APARTMENT *result;
-
-    EnterCriticalSection(&csApartment);
-
-    result = MainApartment;
-    if (result) apartment_addref(result);
-
-    LeaveCriticalSection(&csApartment);
+       DWORD ret;
+       HKEY key;
+       DWORD keytype;
+       WCHAR src[MAX_PATH];
+       DWORD dwLength = dstlen * sizeof(WCHAR);
 
-    return result;
+       if((ret = RegOpenKeyExW(hkeyroot, keyname, 0, KEY_READ, &key)) == ERROR_SUCCESS) {
+          if( (ret = RegQueryValueExW(key, NULL, NULL, &keytype, (LPBYTE)src, &dwLength)) == ERROR_SUCCESS ) {
+            if (keytype == REG_EXPAND_SZ) {
+              if (dstlen <= ExpandEnvironmentStringsW(src, dst, dstlen)) ret = ERROR_MORE_DATA;
+            } else {
+              const WCHAR *quote_start;
+              quote_start = strchrW(src, '\"');
+              if (quote_start) {
+                const WCHAR *quote_end = strchrW(quote_start + 1, '\"');
+                if (quote_end) {
+                  memmove(src, quote_start + 1,
+                          (quote_end - quote_start - 1) * sizeof(WCHAR));
+                  src[quote_end - quote_start - 1] = '\0';
+                }
+              }
+              lstrcpynW(dst, src, dstlen);
+            }
+         }
+          RegCloseKey (key);
+       }
+       return ret;
 }
 
 struct host_object_params
@@ -557,6 +1020,8 @@ struct host_thread_params
     HWND apartment_hwnd;
 };
 
+/* thread for hosting an object to allow an object to appear to be created in
+ * an apartment with an incompatible threading model */
 static DWORD CALLBACK apartment_hostobject_thread(LPVOID p)
 {
     struct host_thread_params *params = p;
@@ -588,9 +1053,9 @@ static DWORD CALLBACK apartment_hostobject_thread(LPVOID p)
     {
         if (!msg.hwnd && (msg.message == DM_HOSTOBJECT))
         {
-            struct host_object_params *params = (struct host_object_params *)msg.lParam;
-            params->hr = apartment_hostobject(apt, params);
-            SetEvent(params->event);
+            struct host_object_params *obj_params = (struct host_object_params *)msg.lParam;
+            obj_params->hr = apartment_hostobject(apt, obj_params);
+            SetEvent(obj_params->event);
         }
         else
         {
@@ -606,7 +1071,12 @@ static DWORD CALLBACK apartment_hostobject_thread(LPVOID p)
     return S_OK;
 }
 
-static HRESULT apartment_hostobject_in_hostapt(struct apartment *apt, BOOL multi_threaded, BOOL main_apartment, HKEY hkeydll, REFCLSID rclsid, REFIID riid, void **ppv)
+/* finds or creates a host apartment, creates the object inside it and returns
+ * a proxy to it so that the object can be used in the apartment of the
+ * caller of this function */
+static HRESULT apartment_hostobject_in_hostapt(
+    struct apartment *apt, BOOL multi_threaded, BOOL main_apartment,
+    HKEY hkeydll, REFCLSID rclsid, REFIID riid, void **ppv)
 {
     struct host_object_params params;
     HWND apartment_hwnd = NULL;
@@ -712,6 +1182,8 @@ static HRESULT apartment_hostobject_in_hostapt(struct apartment *apt, BOOL multi
     return hr;
 }
 
+/* create a window for the apartment or return the current one if one has
+ * already been created */
 HRESULT apartment_createwindowifneeded(struct apartment *apt)
 {
     if (apt->multi_threaded)
@@ -721,7 +1193,7 @@ HRESULT apartment_createwindowifneeded(struct apartment *apt)
     {
         HWND hwnd = CreateWindowW(wszAptWinClass, NULL, 0,
                                   0, 0, 0, 0,
-                                  0, 0, OLE32_hInstance, NULL);
+                                  HWND_MESSAGE, 0, hProxyDll, NULL);
         if (!hwnd)
         {
             ERR("CreateWindow failed with error %d\n", GetLastError());
@@ -735,6 +1207,7 @@ HRESULT apartment_createwindowifneeded(struct apartment *apt)
     return S_OK;
 }
 
+/* retrieves the window for the main- or apartment-threaded apartment */
 HWND apartment_getwindow(const struct apartment *apt)
 {
     assert(!apt->multi_threaded);
@@ -747,241 +1220,136 @@ void apartment_joinmta(void)
     COM_CurrentInfo()->apt = MTA;
 }
 
-static HRESULT apartment_getclassobject(struct apartment *apt, LPCWSTR dllpath,
-                                        BOOL apartment_threaded,
-                                        REFCLSID rclsid, REFIID riid, void **ppv)
+static void COMPOBJ_InitProcess( void )
 {
-    static const WCHAR wszOle32[] = {'o','l','e','3','2','.','d','l','l',0};
-    HRESULT hr = S_OK;
-    BOOL found = FALSE;
-    struct apartment_loaded_dll *apartment_loaded_dll;
-
-    if (!strcmpiW(dllpath, wszOle32))
-    {
-        /* we don't need to control the lifetime of this dll, so use the local
-         * implementation of DllGetClassObject directly */
-        TRACE("calling ole32!DllGetClassObject\n");
-        hr = DllGetClassObject(rclsid, riid, ppv);
-
-        if (hr != S_OK)
-            ERR("DllGetClassObject returned error 0x%08x\n", hr);
-
-        return hr;
-    }
-
-    EnterCriticalSection(&apt->cs);
-
-    LIST_FOR_EACH_ENTRY(apartment_loaded_dll, &apt->loaded_dlls, struct apartment_loaded_dll, entry)
-        if (!strcmpiW(dllpath, apartment_loaded_dll->dll->library_name))
-        {
-            TRACE("found %s already loaded\n", debugstr_w(dllpath));
-            found = TRUE;
-            break;
-        }
-
-    if (!found)
-    {
-        apartment_loaded_dll = HeapAlloc(GetProcessHeap(), 0, sizeof(*apartment_loaded_dll));
-        if (!apartment_loaded_dll)
-            hr = E_OUTOFMEMORY;
-        if (SUCCEEDED(hr))
-        {
-            apartment_loaded_dll->unload_time = 0;
-            apartment_loaded_dll->multi_threaded = FALSE;
-            hr = COMPOBJ_DllList_Add( dllpath, &apartment_loaded_dll->dll );
-            if (FAILED(hr))
-                HeapFree(GetProcessHeap(), 0, apartment_loaded_dll);
-        }
-        if (SUCCEEDED(hr))
-        {
-            TRACE("added new loaded dll %s\n", debugstr_w(dllpath));
-            list_add_tail(&apt->loaded_dlls, &apartment_loaded_dll->entry);
-        }
-    }
-
-    LeaveCriticalSection(&apt->cs);
-
-    if (SUCCEEDED(hr))
-    {
-        /* one component being multi-threaded overrides any number of
-         * apartment-threaded components */
-        if (!apartment_threaded)
-            apartment_loaded_dll->multi_threaded = TRUE;
-
-        TRACE("calling DllGetClassObject %p\n", apartment_loaded_dll->dll->DllGetClassObject);
-        /* OK: get the ClassObject */
-        hr = apartment_loaded_dll->dll->DllGetClassObject(rclsid, riid, ppv);
+    WNDCLASSW wclass;
 
-        if (hr != S_OK)
-            ERR("DllGetClassObject returned error 0x%08x\n", hr);
-    }
+    /* Dispatching to the correct thread in an apartment is done through
+     * window messages rather than RPC transports. When an interface is
+     * marshalled into another apartment in the same process, a window of the
+     * following class is created. The *caller* of CoMarshalInterface (i.e., the
+     * application) is responsible for pumping the message loop in that thread.
+     * The WM_USER messages which point to the RPCs are then dispatched to
+     * apartment_wndproc by the user's code from the apartment in which the
+     * interface was unmarshalled.
+     */
+    memset(&wclass, 0, sizeof(wclass));
+    wclass.lpfnWndProc = apartment_wndproc;
+    wclass.hInstance = hProxyDll;
+    wclass.lpszClassName = wszAptWinClass;
+    RegisterClassW(&wclass);
+}
 
-    return hr;
+static void COMPOBJ_UninitProcess( void )
+{
+    UnregisterClassW(wszAptWinClass, hProxyDll);
 }
 
-static void apartment_freeunusedlibraries(struct apartment *apt, DWORD delay)
+static void COM_TlsDestroy(void)
 {
-    struct apartment_loaded_dll *entry, *next;
-    EnterCriticalSection(&apt->cs);
-    LIST_FOR_EACH_ENTRY_SAFE(entry, next, &apt->loaded_dlls, struct apartment_loaded_dll, entry)
+    struct oletls *info = NtCurrentTeb()->ReservedForOle;
+    if (info)
     {
-       if (entry->dll->DllCanUnloadNow && (entry->dll->DllCanUnloadNow() == S_OK))
-        {
-            DWORD real_delay = delay;
-
-            if (real_delay == INFINITE)
-            {
-                if (entry->multi_threaded)
-                    real_delay = 10 * 60 * 1000; /* 10 minutes */
-                else
-                    real_delay = 0;
-            }
-
-            if (!real_delay || (entry->unload_time && (entry->unload_time < GetTickCount())))
-            {
-                list_remove(&entry->entry);
-                COMPOBJ_DllList_ReleaseRef(entry->dll, TRUE);
-                HeapFree(GetProcessHeap(), 0, entry);
-            }
-            else
-                entry->unload_time = GetTickCount() + real_delay;
-        }
-        else if (entry->unload_time)
-            entry->unload_time = 0;
+        if (info->apt) apartment_release(info->apt);
+        if (info->errorinfo) IErrorInfo_Release(info->errorinfo);
+        if (info->state) IUnknown_Release(info->state);
+        if (info->spy) IUnknown_Release(info->spy);
+        if (info->context_token) IObjContext_Release(info->context_token);
+        HeapFree(GetProcessHeap(), 0, info);
+        NtCurrentTeb()->ReservedForOle = NULL;
     }
-    LeaveCriticalSection(&apt->cs);
 }
 
-/*****************************************************************************
- * This section contains OpenDllList implementation
+/******************************************************************************
+ *           CoBuildVersion [OLE32.@]
+ *
+ * Gets the build version of the DLL.
+ *
+ * PARAMS
+ *
+ * RETURNS
+ *     Current build version, hiword is majornumber, loword is minornumber
  */
-
-/* caller must ensure that library_name is not already in the open dll list */
-static HRESULT COMPOBJ_DllList_Add(LPCWSTR library_name, OpenDll **ret)
+DWORD WINAPI CoBuildVersion(void)
 {
-    OpenDll *entry;
-    int len;
-    HRESULT hr = S_OK;
-    HANDLE hLibrary;
-    DllCanUnloadNowFunc DllCanUnloadNow;
-    DllGetClassObjectFunc DllGetClassObject;
-
-    TRACE("\n");
-
-    *ret = COMPOBJ_DllList_Get(library_name);
-    if (*ret) return S_OK;
-
-    /* do this outside the csOpenDllList to avoid creating a lock dependency on
-     * the loader lock */
-    hLibrary = LoadLibraryExW(library_name, 0, LOAD_WITH_ALTERED_SEARCH_PATH);
-    if (!hLibrary)
-    {
-        ERR("couldn't load in-process dll %s\n", debugstr_w(library_name));
-        /* failure: DLL could not be loaded */
-        return E_ACCESSDENIED; /* FIXME: or should this be CO_E_DLLNOTFOUND? */
-    }
-
-    DllCanUnloadNow = (void *)GetProcAddress(hLibrary, "DllCanUnloadNow");
-    /* Note: failing to find DllCanUnloadNow is not a failure */
-    DllGetClassObject = (void *)GetProcAddress(hLibrary, "DllGetClassObject");
-    if (!DllGetClassObject)
-    {
-        /* failure: the dll did not export DllGetClassObject */
-        ERR("couldn't find function DllGetClassObject in %s\n", debugstr_w(library_name));
-        FreeLibrary(hLibrary);
-        return CO_E_DLLNOTFOUND;
-    }
-
-    EnterCriticalSection( &csOpenDllList );
-
-    *ret = COMPOBJ_DllList_Get(library_name);
-    if (*ret)
-    {
-        /* another caller to this function already added the dll while we
-         * weren't in the critical section */
-        FreeLibrary(hLibrary);
-    }
-    else
-    {
-        len = strlenW(library_name);
-        entry = HeapAlloc(GetProcessHeap(),0, sizeof(OpenDll));
-        if (entry)
-            entry->library_name = HeapAlloc(GetProcessHeap(), 0, (len + 1)*sizeof(WCHAR));
-        if (entry && entry->library_name)
-        {
-            memcpy(entry->library_name, library_name, (len + 1)*sizeof(WCHAR));
-            entry->library = hLibrary;
-            entry->refs = 1;
-            entry->DllCanUnloadNow = DllCanUnloadNow;
-            entry->DllGetClassObject = DllGetClassObject;
-            list_add_tail(&openDllList, &entry->entry);
-        }
-        else
-        {
-            hr = E_OUTOFMEMORY;
-            FreeLibrary(hLibrary);
-        }
-        *ret = entry;
-    }
+    TRACE("Returning version %d, build %d.\n", rmm, rup);
+    return (rmm<<16)+rup;
+}
 
-    LeaveCriticalSection( &csOpenDllList );
+/******************************************************************************
+ *              CoRegisterInitializeSpy [OLE32.@]
+ *
+ * Add a Spy that watches CoInitializeEx calls
+ *
+ * PARAMS
+ *  spy [I] Pointer to IUnknown interface that will be QueryInterface'd.
+ *  cookie [II] cookie receiver
+ *
+ * RETURNS
+ *  Success: S_OK if not already initialized, S_FALSE otherwise.
+ *  Failure: HRESULT code.
+ *
+ * SEE ALSO
+ *   CoInitializeEx
+ */
+HRESULT WINAPI CoRegisterInitializeSpy(IInitializeSpy *spy, ULARGE_INTEGER *cookie)
+{
+    struct oletls *info = COM_CurrentInfo();
+    HRESULT hr;
 
-    return hr;
-}
+    TRACE("(%p, %p)\n", spy, cookie);
 
-static OpenDll *COMPOBJ_DllList_Get(LPCWSTR library_name)
-{
-    OpenDll *ptr;
-    OpenDll *ret = NULL;
-    EnterCriticalSection(&csOpenDllList);
-    LIST_FOR_EACH_ENTRY(ptr, &openDllList, OpenDll, entry)
+    if (!spy || !cookie || !info)
     {
-        if (!strcmpiW(library_name, ptr->library_name) &&
-            (InterlockedIncrement(&ptr->refs) != 1) /* entry is being destroy if == 1 */)
-        {
-            ret = ptr;
-            break;
-        }
+        if (!info)
+            WARN("Could not allocate tls\n");
+        return E_INVALIDARG;
     }
-    LeaveCriticalSection(&csOpenDllList);
-    return ret;
-}
 
-/* pass FALSE for free_entry to release a reference without destroying the
- * entry if it reaches zero or TRUE otherwise */
-static void COMPOBJ_DllList_ReleaseRef(OpenDll *entry, BOOL free_entry)
-{
-    if (!InterlockedDecrement(&entry->refs) && free_entry)
+    if (info->spy)
     {
-        EnterCriticalSection(&csOpenDllList);
-        list_remove(&entry->entry);
-        LeaveCriticalSection(&csOpenDllList);
-
-        TRACE("freeing %p\n", entry->library);
-        FreeLibrary(entry->library);
+        FIXME("Already registered?\n");
+        return E_UNEXPECTED;
+    }
 
-        HeapFree(GetProcessHeap(), 0, entry->library_name);
-        HeapFree(GetProcessHeap(), 0, entry);
+    hr = IUnknown_QueryInterface(spy, &IID_IInitializeSpy, (void **) &info->spy);
+    if (SUCCEEDED(hr))
+    {
+        cookie->QuadPart = (DWORD_PTR)spy;
+        return S_OK;
     }
+    return hr;
 }
 
 /******************************************************************************
- *           CoBuildVersion [OLE32.@]
- *           CoBuildVersion [COMPOBJ.1]
+ *              CoRevokeInitializeSpy [OLE32.@]
  *
- * Gets the build version of the DLL.
+ * Remove a spy that previously watched CoInitializeEx calls
  *
  * PARAMS
+ *  cookie [I] The cookie obtained from a previous CoRegisterInitializeSpy call
  *
  * RETURNS
- *     Current build version, hiword is majornumber, loword is minornumber
+ *  Success: S_OK if a spy is removed
+ *  Failure: E_INVALIDARG
+ *
+ * SEE ALSO
+ *   CoInitializeEx
  */
-DWORD WINAPI CoBuildVersion(void)
+HRESULT WINAPI CoRevokeInitializeSpy(ULARGE_INTEGER cookie)
 {
-    TRACE("Returning version %d, build %d.\n", rmm, rup);
-    return (rmm<<16)+rup;
+    struct oletls *info = COM_CurrentInfo();
+    TRACE("(%s)\n", wine_dbgstr_longlong(cookie.QuadPart));
+
+    if (!info || !info->spy || cookie.QuadPart != (DWORD_PTR)info->spy)
+        return E_INVALIDARG;
+
+    IUnknown_Release(info->spy);
+    info->spy = NULL;
+    return S_OK;
 }
 
+
 /******************************************************************************
  *             CoInitialize    [OLE32.@]
  *
@@ -1038,6 +1406,7 @@ HRESULT WINAPI CoInitialize(LPVOID lpReserved)
  */
 HRESULT WINAPI CoInitializeEx(LPVOID lpReserved, DWORD dwCoInit)
 {
+  struct oletls *info = COM_CurrentInfo();
   HRESULT hr = S_OK;
   APARTMENT *apt;
 
@@ -1065,7 +1434,10 @@ HRESULT WINAPI CoInitializeEx(LPVOID lpReserved, DWORD dwCoInit)
     RunningObjectTableImpl_Initialize();
   }
 
-  if (!(apt = COM_CurrentInfo()->apt))
+  if (info->spy)
+      IInitializeSpy_PreInitialize(info->spy, dwCoInit, info->inits);
+
+  if (!(apt = info->apt))
   {
     apt = apartment_get_or_create(dwCoInit);
     if (!apt) return E_OUTOFMEMORY;
@@ -1082,7 +1454,10 @@ HRESULT WINAPI CoInitializeEx(LPVOID lpReserved, DWORD dwCoInit)
   else
     hr = S_FALSE;
 
-  COM_CurrentInfo()->inits++;
+  info->inits++;
+
+  if (info->spy)
+      IInitializeSpy_PostInitialize(info->spy, hr, dwCoInit, info->inits);
 
   return hr;
 }
@@ -1113,10 +1488,16 @@ void WINAPI CoUninitialize(void)
   /* will only happen on OOM */
   if (!info) return;
 
+  if (info->spy)
+      IInitializeSpy_PreUninitialize(info->spy, info->inits);
+
   /* sanity check */
   if (!info->inits)
   {
     ERR("Mismatched CoUninitialize\n");
+
+    if (info->spy)
+        IInitializeSpy_PostUninitialize(info->spy, info->inits);
     return;
   }
 
@@ -1142,6 +1523,8 @@ void WINAPI CoUninitialize(void)
     ERR( "CoUninitialize() - not CoInitialized.\n" );
     InterlockedExchangeAdd(&s_COMLockCount,1); /* restore the lock count. */
   }
+  if (info->spy)
+      IInitializeSpy_PostUninitialize(info->spy, info->inits);
 }
 
 /******************************************************************************
@@ -1197,7 +1580,6 @@ HRESULT WINAPI CoDisconnectObject( LPUNKNOWN lpUnk, DWORD reserved )
 
 /******************************************************************************
  *             CoCreateGuid [OLE32.@]
- *             CoCreateGuid [COMPOBJ.73]
  *
  * Simply forwards to UuidCreate in RPCRT4.
  *
@@ -1213,7 +1595,18 @@ HRESULT WINAPI CoDisconnectObject( LPUNKNOWN lpUnk, DWORD reserved )
  */
 HRESULT WINAPI CoCreateGuid(GUID *pguid)
 {
-    return UuidCreate(pguid);
+    DWORD status = UuidCreate(pguid);
+    if (status == RPC_S_OK || status == RPC_S_UUID_LOCAL_ONLY) return S_OK;
+    return HRESULT_FROM_WIN32( status );
+}
+
+static inline BOOL is_valid_hex(WCHAR c)
+{
+    if (!(((c >= '0') && (c <= '9'))  ||
+          ((c >= 'a') && (c <= 'f'))  ||
+          ((c >= 'A') && (c <= 'F'))))
+        return FALSE;
+    return TRUE;
 }
 
 /******************************************************************************
@@ -1234,29 +1627,15 @@ HRESULT WINAPI CoCreateGuid(GUID *pguid)
  * SEE ALSO
  *  StringFromCLSID
  */
-static HRESULT WINAPI __CLSIDFromString(LPCWSTR s, CLSID *id)
+static HRESULT __CLSIDFromString(LPCWSTR s, LPCLSID id)
 {
   int  i;
   BYTE table[256];
 
-  if (!s) {
+  if (!s || s[0]!='{') {
     memset( id, 0, sizeof (CLSID) );
-    return S_OK;
-  }
-
-  /* validate the CLSID string */
-  if (strlenW(s) != 38)
+    if(!s) return S_OK;
     return CO_E_CLASSSTRING;
-
-  if ((s[0]!='{') || (s[9]!='-') || (s[14]!='-') || (s[19]!='-') || (s[24]!='-') || (s[37]!='}'))
-    return CO_E_CLASSSTRING;
-
-  for (i=1; i<37; i++) {
-    if ((i == 9)||(i == 14)||(i == 19)||(i == 24)) continue;
-    if (!(((s[i] >= '0') && (s[i] <= '9'))  ||
-          ((s[i] >= 'a') && (s[i] <= 'f'))  ||
-          ((s[i] >= 'A') && (s[i] <= 'F'))))
-       return CO_E_CLASSSTRING;
   }
 
   TRACE("%s -> %p\n", debugstr_w(s), id);
@@ -1274,27 +1653,45 @@ static HRESULT WINAPI __CLSIDFromString(LPCWSTR s, CLSID *id)
 
   /* in form {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX} */
 
-  id->Data1 = (table[s[1]] << 28 | table[s[2]] << 24 | table[s[3]] << 20 | table[s[4]] << 16 |
-               table[s[5]] << 12 | table[s[6]] << 8  | table[s[7]] << 4  | table[s[8]]);
-  id->Data2 = table[s[10]] << 12 | table[s[11]] << 8 | table[s[12]] << 4 | table[s[13]];
-  id->Data3 = table[s[15]] << 12 | table[s[16]] << 8 | table[s[17]] << 4 | table[s[18]];
-
-  /* these are just sequential bytes */
-  id->Data4[0] = table[s[20]] << 4 | table[s[21]];
-  id->Data4[1] = table[s[22]] << 4 | table[s[23]];
-  id->Data4[2] = table[s[25]] << 4 | table[s[26]];
-  id->Data4[3] = table[s[27]] << 4 | table[s[28]];
-  id->Data4[4] = table[s[29]] << 4 | table[s[30]];
-  id->Data4[5] = table[s[31]] << 4 | table[s[32]];
-  id->Data4[6] = table[s[33]] << 4 | table[s[34]];
-  id->Data4[7] = table[s[35]] << 4 | table[s[36]];
+  id->Data1 = 0;
+  for (i = 1; i < 9; i++) {
+    if (!is_valid_hex(s[i])) return CO_E_CLASSSTRING;
+    id->Data1 = (id->Data1 << 4) | table[s[i]];
+  }
+  if (s[9]!='-') return CO_E_CLASSSTRING;
+
+  id->Data2 = 0;
+  for (i = 10; i < 14; i++) {
+    if (!is_valid_hex(s[i])) return CO_E_CLASSSTRING;
+    id->Data2 = (id->Data2 << 4) | table[s[i]];
+  }
+  if (s[14]!='-') return CO_E_CLASSSTRING;
 
-  return S_OK;
+  id->Data3 = 0;
+  for (i = 15; i < 19; i++) {
+    if (!is_valid_hex(s[i])) return CO_E_CLASSSTRING;
+    id->Data3 = (id->Data3 << 4) | table[s[i]];
+  }
+  if (s[19]!='-') return CO_E_CLASSSTRING;
+
+  for (i = 20; i < 37; i+=2) {
+    if (i == 24) {
+      if (s[i]!='-') return CO_E_CLASSSTRING;
+      i++;
+    }
+    if (!is_valid_hex(s[i]) || !is_valid_hex(s[i+1])) return CO_E_CLASSSTRING;
+    id->Data4[(i-20)/2] = table[s[i]] << 4 | table[s[i+1]];
+  }
+
+  if (s[37] == '}' && s[38] == '\0')
+    return S_OK;
+
+  return CO_E_CLASSSTRING;
 }
 
 /*****************************************************************************/
 
-HRESULT WINAPI CLSIDFromString(LPOLESTR idstr, CLSID *id )
+HRESULT WINAPI CLSIDFromString(LPCOLESTR idstr, LPCLSID id )
 {
     HRESULT ret;
 
@@ -1303,45 +1700,14 @@ HRESULT WINAPI CLSIDFromString(LPOLESTR idstr, CLSID *id )
 
     ret = __CLSIDFromString(idstr, id);
     if(ret != S_OK) { /* It appears a ProgID is also valid */
-        ret = CLSIDFromProgID(idstr, id);
+        CLSID tmp_id;
+        ret = CLSIDFromProgID(idstr, &tmp_id);
+        if(SUCCEEDED(ret))
+            *id = tmp_id;
     }
     return ret;
 }
 
-/* Converts a GUID into the respective string representation. */
-HRESULT WINE_StringFromCLSID(
-       const CLSID *id,        /* [in] GUID to be converted */
-       LPSTR idstr             /* [out] pointer to buffer to contain converted guid */
-) {
-  static const char hex[] = "0123456789ABCDEF";
-  char *s;
-  int  i;
-
-  if (!id)
-       { ERR("called with id=Null\n");
-         *idstr = 0x00;
-         return E_FAIL;
-       }
-
-  sprintf(idstr, "{%08X-%04X-%04X-%02X%02X-",
-         id->Data1, id->Data2, id->Data3,
-         id->Data4[0], id->Data4[1]);
-  s = &idstr[25];
-
-  /* 6 hex bytes */
-  for (i = 2; i < 8; i++) {
-    *s++ = hex[id->Data4[i]>>4];
-    *s++ = hex[id->Data4[i] & 0xf];
-  }
-
-  *s++ = '}';
-  *s++ = '\0';
-
-  TRACE("%p->%s\n", id, idstr);
-
-  return S_OK;
-}
-
 
 /******************************************************************************
  *             StringFromCLSID [OLE32.@]
@@ -1363,25 +1729,17 @@ HRESULT WINE_StringFromCLSID(
  */
 HRESULT WINAPI StringFromCLSID(REFCLSID id, LPOLESTR *idstr)
 {
-       char            buf[80];
-       HRESULT       ret;
-       LPMALLOC        mllc;
-
-       if ((ret = CoGetMalloc(0,&mllc)))
-               return ret;
+    HRESULT ret;
+    LPMALLOC mllc;
 
-       ret=WINE_StringFromCLSID(id,buf);
-       if (!ret) {
-            DWORD len = MultiByteToWideChar( CP_ACP, 0, buf, -1, NULL, 0 );
-            *idstr = IMalloc_Alloc( mllc, len * sizeof(WCHAR) );
-            MultiByteToWideChar( CP_ACP, 0, buf, -1, *idstr, len );
-       }
-       return ret;
+    if ((ret = CoGetMalloc(0,&mllc))) return ret;
+    if (!(*idstr = IMalloc_Alloc( mllc, CHARS_IN_GUID * sizeof(WCHAR) ))) return E_OUTOFMEMORY;
+    StringFromGUID2( id, *idstr, CHARS_IN_GUID );
+    return S_OK;
 }
 
 /******************************************************************************
  *             StringFromGUID2 [OLE32.@]
- *             StringFromGUID2 [COMPOBJ.76]
  *
  * Modified version of StringFromCLSID that allows you to specify max
  * buffer size.
@@ -1397,11 +1755,15 @@ HRESULT WINAPI StringFromCLSID(REFCLSID id, LPOLESTR *idstr)
  */
 INT WINAPI StringFromGUID2(REFGUID id, LPOLESTR str, INT cmax)
 {
-  char         xguid[80];
-
-  if (WINE_StringFromCLSID(id,xguid))
-       return 0;
-  return MultiByteToWideChar( CP_ACP, 0, xguid, -1, str, cmax );
+    static const WCHAR formatW[] = { '{','%','0','8','X','-','%','0','4','X','-',
+                                     '%','0','4','X','-','%','0','2','X','%','0','2','X','-',
+                                     '%','0','2','X','%','0','2','X','%','0','2','X','%','0','2','X',
+                                     '%','0','2','X','%','0','2','X','}',0 };
+    if (!id || cmax < CHARS_IN_GUID) return 0;
+    sprintfW( str, formatW, id->Data1, id->Data2, id->Data3,
+              id->Data4[0], id->Data4[1], id->Data4[2], id->Data4[3],
+              id->Data4[4], id->Data4[5], id->Data4[6], id->Data4[7] );
+    return CHARS_IN_GUID;
 }
 
 /* open HKCR\\CLSID\\{string form of clsid}\\{keyname} key */
@@ -1572,7 +1934,7 @@ HRESULT WINAPI CLSIDFromProgID(LPCOLESTR progid, LPCLSID clsid)
         return CO_E_CLASSSTRING;
     }
     RegCloseKey(xhkey);
-    return CLSIDFromString(buf2,clsid);
+    return __CLSIDFromString(buf2,clsid);
 }
 
 
@@ -1672,8 +2034,8 @@ HRESULT WINAPI CoGetPSClsid(REFIID riid, CLSID *pclsid)
     }
     RegCloseKey(hkey);
 
-    /* We have the CLSid we want back from the registry as a string, so
-       lets convert it into a CLSID structure */
+    /* We have the CLSID we want back from the registry as a string, so
+       let's convert it into a CLSID structure */
     if (CLSIDFromString(value, pclsid) != NOERROR)
         return REGDB_E_IIDNOTREG;
 
@@ -1762,11 +2124,6 @@ static HRESULT COM_GetRegisteredClassObject(const struct apartment *apt, REFCLSI
   HRESULT hr = S_FALSE;
   RegisteredClass *curClass;
 
-  /*
-   * Sanity check
-   */
-  assert(ppUnk!=0);
-
   EnterCriticalSection( &csRegisteredClassList );
 
   LIST_FOR_EACH_ENTRY(curClass, &RegisteredClassList, RegisteredClass, entry)
@@ -1833,6 +2190,7 @@ HRESULT WINAPI CoRegisterClassObject(
     DWORD flags,
     LPDWORD lpdwRegister)
 {
+  static LONG next_cookie;
   RegisteredClass* newClass;
   LPUNKNOWN        foundObject;
   HRESULT          hr;
@@ -1886,11 +2244,8 @@ HRESULT WINAPI CoRegisterClassObject(
   newClass->pMarshaledData  = NULL;
   newClass->RpcRegistration = NULL;
 
-  /*
-   * Use the address of the chain node as the cookie since we are sure it's
-   * unique. FIXME: not on 64-bit platforms.
-   */
-  newClass->dwCookie        = (DWORD)newClass;
+  if (!(newClass->dwCookie = InterlockedIncrement( &next_cookie )))
+      newClass->dwCookie = InterlockedIncrement( &next_cookie );
 
   /*
    * Since we're making a copy of the object pointer, we have to increase its
@@ -1911,7 +2266,7 @@ HRESULT WINAPI CoRegisterClassObject(
           FIXME("Failed to create stream on hglobal, %x\n", hr);
           return hr;
       }
-      hr = CoMarshalInterface(newClass->pMarshaledData, &IID_IClassFactory,
+      hr = CoMarshalInterface(newClass->pMarshaledData, &IID_IUnknown,
                               newClass->classObject, MSHCTX_LOCAL, NULL,
                               MSHLFLAGS_TABLESTRONG);
       if (hr) {
@@ -1927,134 +2282,6 @@ HRESULT WINAPI CoRegisterClassObject(
   return S_OK;
 }
 
-static void COM_RevokeRegisteredClassObject(RegisteredClass *curClass)
-{
-    list_remove(&curClass->entry);
-
-    if (curClass->runContext & CLSCTX_LOCAL_SERVER)
-        RPC_StopLocalServer(curClass->RpcRegistration);
-
-    /*
-     * Release the reference to the class object.
-     */
-    IUnknown_Release(curClass->classObject);
-
-    if (curClass->pMarshaledData)
-    {
-        LARGE_INTEGER zero;
-        memset(&zero, 0, sizeof(zero));
-        IStream_Seek(curClass->pMarshaledData, zero, STREAM_SEEK_SET, NULL);
-        CoReleaseMarshalData(curClass->pMarshaledData);
-    }
-
-    HeapFree(GetProcessHeap(), 0, curClass);
-}
-
-static void COM_RevokeAllClasses(const struct apartment *apt)
-{
-  RegisteredClass *curClass, *cursor;
-
-  EnterCriticalSection( &csRegisteredClassList );
-
-  LIST_FOR_EACH_ENTRY_SAFE(curClass, cursor, &RegisteredClassList, RegisteredClass, entry)
-  {
-    if (curClass->apartment_id == apt->oxid)
-      COM_RevokeRegisteredClassObject(curClass);
-  }
-
-  LeaveCriticalSection( &csRegisteredClassList );
-}
-
-/***********************************************************************
- *           CoRevokeClassObject [OLE32.@]
- *
- * Removes a class object from the class registry.
- *
- * PARAMS
- *  dwRegister [I] Cookie returned from CoRegisterClassObject().
- *
- * RETURNS
- *  Success: S_OK.
- *  Failure: HRESULT code.
- *
- * NOTES
- *  Must be called from the same apartment that called CoRegisterClassObject(),
- *  otherwise it will fail with RPC_E_WRONG_THREAD.
- *
- * SEE ALSO
- *  CoRegisterClassObject
- */
-HRESULT WINAPI CoRevokeClassObject(
-        DWORD dwRegister)
-{
-  HRESULT hr = E_INVALIDARG;
-  RegisteredClass *curClass;
-  APARTMENT *apt;
-
-  TRACE("(%08x)\n",dwRegister);
-
-  apt = COM_CurrentApt();
-  if (!apt)
-  {
-    ERR("COM was not initialized\n");
-    return CO_E_NOTINITIALIZED;
-  }
-
-  EnterCriticalSection( &csRegisteredClassList );
-
-  LIST_FOR_EACH_ENTRY(curClass, &RegisteredClassList, RegisteredClass, entry)
-  {
-    /*
-     * Check if we have a match on the cookie.
-     */
-    if (curClass->dwCookie == dwRegister)
-    {
-      if (curClass->apartment_id == apt->oxid)
-      {
-          COM_RevokeRegisteredClassObject(curClass);
-          hr = S_OK;
-      }
-      else
-      {
-          ERR("called from wrong apartment, should be called from %s\n",
-              wine_dbgstr_longlong(curClass->apartment_id));
-          hr = RPC_E_WRONG_THREAD;
-      }
-      break;
-    }
-  }
-
-  LeaveCriticalSection( &csRegisteredClassList );
-
-  return hr;
-}
-
-/***********************************************************************
- *     COM_RegReadPath [internal]
- *
- *     Reads a registry value and expands it when necessary
- */
-static DWORD COM_RegReadPath(HKEY hkeyroot, const WCHAR *keyname, const WCHAR *valuename, WCHAR * dst, DWORD dstlen)
-{
-       DWORD ret;
-       HKEY key;
-       DWORD keytype;
-       WCHAR src[MAX_PATH];
-       DWORD dwLength = dstlen * sizeof(WCHAR);
-
-       if((ret = RegOpenKeyExW(hkeyroot, keyname, 0, KEY_READ, &key)) == ERROR_SUCCESS) {
-          if( (ret = RegQueryValueExW(key, NULL, NULL, &keytype, (LPBYTE)src, &dwLength)) == ERROR_SUCCESS ) {
-            if (keytype == REG_EXPAND_SZ) {
-              if (dstlen <= ExpandEnvironmentStringsW(src, dst, dstlen)) ret = ERROR_MORE_DATA;
-            } else {
-              lstrcpynW(dst, src, dstlen);
-            }
-         }
-          RegCloseKey (key);
-       }
-       return ret;
-}
-
 static void get_threading_model(HKEY key, LPWSTR value, DWORD len)
 {
     static const WCHAR wszThreadingModel[] = {'T','h','r','e','a','d','i','n','g','M','o','d','e','l',0};
@@ -2068,38 +2295,51 @@ static void get_threading_model(HKEY key, LPWSTR value, DWORD len)
 }
 
 static HRESULT get_inproc_class_object(APARTMENT *apt, HKEY hkeydll,
-                                       REFCLSID rclsid, REFIID riid, void **ppv)
+                                       REFCLSID rclsid, REFIID riid,
+                                       BOOL hostifnecessary, void **ppv)
 {
-    static const WCHAR wszApartment[] = {'A','p','a','r','t','m','e','n','t',0};
-    static const WCHAR wszFree[] = {'F','r','e','e',0};
-    static const WCHAR wszBoth[] = {'B','o','t','h',0};
     WCHAR dllpath[MAX_PATH+1];
-    WCHAR threading_model[10 /* strlenW(L"apartment")+1 */];
+    BOOL apartment_threaded;
 
-    get_threading_model(hkeydll, threading_model, ARRAYSIZE(threading_model));
-    /* "Apartment" */
-    if (!strcmpiW(threading_model, wszApartment))
-    {
-        if (apt->multi_threaded)
-            return apartment_hostobject_in_hostapt(apt, FALSE, FALSE, hkeydll, rclsid, riid, ppv);
-    }
-    /* "Free" */
-    else if (!strcmpiW(threading_model, wszFree))
-    {
-        if (!apt->multi_threaded)
-            return apartment_hostobject_in_hostapt(apt, TRUE, FALSE, hkeydll, rclsid, riid, ppv);
-    }
-    /* everything except "Apartment", "Free" and "Both" */
-    else if (strcmpiW(threading_model, wszBoth))
+    if (hostifnecessary)
     {
-        /* everything else is main-threaded */
-        if (threading_model[0])
-            FIXME("unrecognised threading model %s for object %s, should be main-threaded?\n",
-                debugstr_w(threading_model), debugstr_guid(rclsid));
-
-        if (apt->multi_threaded || !apt->main)
-            return apartment_hostobject_in_hostapt(apt, FALSE, TRUE, hkeydll, rclsid, riid, ppv);
+        static const WCHAR wszApartment[] = {'A','p','a','r','t','m','e','n','t',0};
+        static const WCHAR wszFree[] = {'F','r','e','e',0};
+        static const WCHAR wszBoth[] = {'B','o','t','h',0};
+        WCHAR threading_model[10 /* strlenW(L"apartment")+1 */];
+
+        get_threading_model(hkeydll, threading_model, ARRAYSIZE(threading_model));
+        /* "Apartment" */
+        if (!strcmpiW(threading_model, wszApartment))
+        {
+            apartment_threaded = TRUE;
+            if (apt->multi_threaded)
+                return apartment_hostobject_in_hostapt(apt, FALSE, FALSE, hkeydll, rclsid, riid, ppv);
+        }
+        /* "Free" */
+        else if (!strcmpiW(threading_model, wszFree))
+        {
+            apartment_threaded = FALSE;
+            if (!apt->multi_threaded)
+                return apartment_hostobject_in_hostapt(apt, TRUE, FALSE, hkeydll, rclsid, riid, ppv);
+        }
+        /* everything except "Apartment", "Free" and "Both" */
+        else if (strcmpiW(threading_model, wszBoth))
+        {
+            apartment_threaded = TRUE;
+            /* everything else is main-threaded */
+            if (threading_model[0])
+                FIXME("unrecognised threading model %s for object %s, should be main-threaded?\n",
+                    debugstr_w(threading_model), debugstr_guid(rclsid));
+
+            if (apt->multi_threaded || !apt->main)
+                return apartment_hostobject_in_hostapt(apt, FALSE, TRUE, hkeydll, rclsid, riid, ppv);
+        }
+        else
+            apartment_threaded = FALSE;
     }
+    else
+        apartment_threaded = !apt->multi_threaded;
 
     if (COM_RegReadPath(hkeydll, NULL, NULL, dllpath, ARRAYSIZE(dllpath)) != ERROR_SUCCESS)
     {
@@ -2108,8 +2348,7 @@ static HRESULT get_inproc_class_object(APARTMENT *apt, HKEY hkeydll,
         return REGDB_E_CLASSNOTREG;
     }
 
-    return apartment_getclassobject(apt, dllpath,
-                                    !strcmpiW(threading_model, wszApartment),
+    return apartment_getclassobject(apt, dllpath, apartment_threaded,
                                     rclsid, riid, ppv);
 }
 
@@ -2146,24 +2385,28 @@ HRESULT WINAPI CoGetClassObject(
     LPUNKNOWN  regClassObject;
     HRESULT    hres = E_UNEXPECTED;
     APARTMENT  *apt;
+    BOOL release_apt = FALSE;
 
-    TRACE("\n\tCLSID:\t%s,\n\tIID:\t%s\n", debugstr_guid(rclsid), debugstr_guid(iid));
+    TRACE("CLSID: %s,IID: %s\n", debugstr_guid(rclsid), debugstr_guid(iid));
 
     if (!ppv)
         return E_INVALIDARG;
 
     *ppv = NULL;
 
-    apt = COM_CurrentApt();
-    if (!apt)
+    if (!(apt = COM_CurrentApt()))
     {
-        ERR("apartment not initialised\n");
-        return CO_E_NOTINITIALIZED;
+        if (!(apt = apartment_find_multi_threaded()))
+        {
+            ERR("apartment not initialised\n");
+            return CO_E_NOTINITIALIZED;
+        }
+        release_apt = TRUE;
     }
 
     if (pServerInfo) {
-       FIXME("\tpServerInfo: name=%s\n",debugstr_w(pServerInfo->pwszName));
-       FIXME("\t\tpAuthInfo=%p\n",pServerInfo->pAuthInfo);
+       FIXME("pServerInfo->name=%s pAuthInfo=%p\n",
+              debugstr_w(pServerInfo->pwszName), pServerInfo->pAuthInfo);
     }
 
     /*
@@ -2182,7 +2425,7 @@ HRESULT WINAPI CoGetClassObject(
        * is good since we are not returning it in the "out" parameter.
        */
       IUnknown_Release(regClassObject);
-
+      if (release_apt) apartment_release(apt);
       return hres;
     }
 
@@ -2193,7 +2436,10 @@ HRESULT WINAPI CoGetClassObject(
         HKEY hkey;
 
         if (IsEqualCLSID(rclsid, &CLSID_InProcFreeMarshaler))
+        {
+            if (release_apt) apartment_release(apt);
             return FTMarshalCF_Create(iid, ppv);
+        }
 
         hres = COM_OpenKeyForCLSID(rclsid, wszInprocServer32, KEY_READ, &hkey);
         if (FAILED(hres))
@@ -2209,14 +2455,18 @@ HRESULT WINAPI CoGetClassObject(
 
         if (SUCCEEDED(hres))
         {
-            hres = get_inproc_class_object(apt, hkey, rclsid, iid, ppv);
+            hres = get_inproc_class_object(apt, hkey, rclsid, iid,
+                !(dwClsContext & WINE_CLSCTX_DONT_HOST), ppv);
             RegCloseKey(hkey);
         }
 
         /* return if we got a class, otherwise fall through to one of the
          * other types */
         if (SUCCEEDED(hres))
+        {
+            if (release_apt) apartment_release(apt);
             return hres;
+        }
     }
 
     /* Next try in-process handler */
@@ -2239,15 +2489,20 @@ HRESULT WINAPI CoGetClassObject(
 
         if (SUCCEEDED(hres))
         {
-            hres = get_inproc_class_object(apt, hkey, rclsid, iid, ppv);
+            hres = get_inproc_class_object(apt, hkey, rclsid, iid,
+                !(dwClsContext & WINE_CLSCTX_DONT_HOST), ppv);
             RegCloseKey(hkey);
         }
 
         /* return if we got a class, otherwise fall through to one of the
          * other types */
         if (SUCCEEDED(hres))
+        {
+            if (release_apt) apartment_release(apt);
             return hres;
+        }
     }
+    if (release_apt) apartment_release(apt);
 
     /* Next try out of process */
     if (CLSCTX_LOCAL_SERVER & dwClsContext)
@@ -2261,7 +2516,7 @@ HRESULT WINAPI CoGetClassObject(
     if (CLSCTX_REMOTE_SERVER & dwClsContext)
     {
         FIXME ("CLSCTX_REMOTE_SERVER not supported\n");
-        hres = E_NOINTERFACE;
+        hres = REGDB_E_CLASSNOTREG;
     }
 
     if (FAILED(hres))
@@ -2325,6 +2580,7 @@ HRESULT WINAPI CoCreateInstance(
 {
   HRESULT hres;
   LPCLASSFACTORY lpclf = 0;
+  APARTMENT *apt;
 
   TRACE("(rclsid=%s, pUnkOuter=%p, dwClsContext=%08x, riid=%s, ppv=%p)\n", debugstr_guid(rclsid),
         pUnkOuter, dwClsContext, debugstr_guid(iid), ppv);
@@ -2340,10 +2596,14 @@ HRESULT WINAPI CoCreateInstance(
    */
   *ppv = 0;
 
-  if (!COM_CurrentApt())
+  if (!(apt = COM_CurrentApt()))
   {
+    if (!(apt = apartment_find_multi_threaded()))
+    {
       ERR("apartment not initialised\n");
       return CO_E_NOTINITIALIZED;
+    }
+    apartment_release(apt);
   }
 
   /*
@@ -2360,6 +2620,9 @@ HRESULT WINAPI CoCreateInstance(
     return S_OK;
   }
 
+  if (IsEqualCLSID(rclsid, &CLSID_ManualResetEvent))
+      return ManualResetEvent_Construct(pUnkOuter, iid, ppv);
+
   /*
    * Get a class factory to construct the object we want.
    */
@@ -2378,8 +2641,12 @@ HRESULT WINAPI CoCreateInstance(
        hres = IClassFactory_CreateInstance(lpclf, pUnkOuter, iid, ppv);
        IClassFactory_Release(lpclf);
        if(FAILED(hres))
-          FIXME("no instance created for interface %s of class %s, hres is 0x%08x\n",
-               debugstr_guid(iid), debugstr_guid(rclsid),hres);
+        {
+          if (hres == CLASS_E_NOAGGREGATION && pUnkOuter)
+              FIXME("Class %s does not support aggregation\n", debugstr_guid(rclsid));
+          else
+              FIXME("no instance created for interface %s of class %s, hres is 0x%08x\n", debugstr_guid(iid), debugstr_guid(rclsid),hres);
+        }
 
        return hres;
 }
@@ -2547,7 +2814,6 @@ void WINAPI CoFreeUnusedLibrariesEx(DWORD dwUnloadDelay, DWORD dwReserved)
 
 /***********************************************************************
  *           CoFreeUnusedLibraries [OLE32.@]
- *           CoFreeUnusedLibraries [COMPOBJ.17]
  *
  * Frees any unused libraries. Unused are identified as those that return
  * S_OK from their DllCanUnloadNow function.
@@ -2565,7 +2831,6 @@ void WINAPI CoFreeUnusedLibraries(void)
 
 /***********************************************************************
  *           CoFileTimeNow [OLE32.@]
- *           CoFileTimeNow [COMPOBJ.82]
  *
  * Retrieves the current time in FILETIME format.
  *
@@ -2620,9 +2885,9 @@ HRESULT WINAPI CoLockObjectExternal(
     if (stubmgr)
     {
         if (fLock)
-            stub_manager_ext_addref(stubmgr, 1);
+            stub_manager_ext_addref(stubmgr, 1, FALSE);
         else
-            stub_manager_ext_release(stubmgr, 1, fLastUnlockReleases);
+            stub_manager_ext_release(stubmgr, 1, FALSE, fLastUnlockReleases);
         
         stub_manager_int_release(stubmgr);
 
@@ -2634,7 +2899,7 @@ HRESULT WINAPI CoLockObjectExternal(
 
         if (stubmgr)
         {
-            stub_manager_ext_addref(stubmgr, 1);
+            stub_manager_ext_addref(stubmgr, 1, FALSE);
             stub_manager_int_release(stubmgr);
         }
 
@@ -2772,7 +3037,7 @@ HRESULT WINAPI CoTreatAsClass(REFCLSID clsidOld, REFCLSID clsidNew)
     if (!memcmp( clsidOld, clsidNew, sizeof(*clsidOld) ))
     {
        if (!RegQueryValueW(hkey, wszAutoTreatAs, auto_treat_as, &auto_treat_as_size) &&
-           !CLSIDFromString(auto_treat_as, &id))
+           CLSIDFromString(auto_treat_as, &id) == S_OK)
        {
            if (RegSetValueW(hkey, wszTreatAs, REG_SZ, auto_treat_as, sizeof(auto_treat_as)))
            {
@@ -2822,12 +3087,15 @@ HRESULT WINAPI CoGetTreatAsClass(REFCLSID clsidOld, LPCLSID clsidNew)
     HRESULT res = S_OK;
     LONG len = sizeof(szClsidNew);
 
-    FIXME("(%s,%p)\n", debugstr_guid(clsidOld), clsidNew);
-    memcpy(clsidNew,clsidOld,sizeof(CLSID)); /* copy over old value */
+    TRACE("(%s,%p)\n", debugstr_guid(clsidOld), clsidNew);
+    *clsidNew = *clsidOld; /* copy over old value */
 
     res = COM_OpenKeyForCLSID(clsidOld, wszTreatAs, KEY_READ, &hkey);
     if (FAILED(res))
+    {
+        res = S_FALSE;
         goto done;
+    }
     if (RegQueryValueW(hkey, NULL, szClsidNew, &len))
     {
         res = S_FALSE;
@@ -2843,7 +3111,6 @@ done:
 
 /******************************************************************************
  *             CoGetCurrentProcess     [OLE32.@]
- *             CoGetCurrentProcess     [COMPOBJ.34]
  *
  * Gets the current process ID.
  *
@@ -3216,10 +3483,46 @@ HRESULT WINAPI CoCopyProxy(IUnknown *pProxy, IUnknown **ppCopy)
  */
 HRESULT WINAPI CoGetCallContext(REFIID riid, void **ppv)
 {
-    FIXME("(%s, %p): stub\n", debugstr_guid(riid), ppv);
+    struct oletls *info = COM_CurrentInfo();
 
-    *ppv = NULL;
-    return E_NOINTERFACE;
+    TRACE("(%s, %p)\n", debugstr_guid(riid), ppv);
+
+    if (!info)
+        return E_OUTOFMEMORY;
+
+    if (!info->call_state)
+        return RPC_E_CALL_COMPLETE;
+
+    return IUnknown_QueryInterface(info->call_state, riid, ppv);
+}
+
+/***********************************************************************
+ *           CoSwitchCallContext [OLE32.@]
+ *
+ * Switches the context of the currently executing server call in the current
+ * thread.
+ *
+ * PARAMS
+ *  pObject     [I] Pointer to new context object
+ *  ppOldObject [O] Pointer to memory that will receive old context object pointer
+ *
+ * RETURNS
+ *  Success: S_OK.
+ *  Failure: HRESULT code.
+ */
+HRESULT WINAPI CoSwitchCallContext(IUnknown *pObject, IUnknown **ppOldObject)
+{
+    struct oletls *info = COM_CurrentInfo();
+
+    TRACE("(%p, %p)\n", pObject, ppOldObject);
+
+    if (!info)
+        return E_OUTOFMEMORY;
+
+    *ppOldObject = info->call_state;
+    info->call_state = pObject; /* CoSwitchCallContext does not addref nor release objects */
+
+    return S_OK;
 }
 
 /***********************************************************************
@@ -3348,7 +3651,8 @@ static BOOL COM_PeekMessage(struct apartment *apt, MSG *msg)
     /* first try to retrieve messages for incoming COM calls to the apartment window */
     return PeekMessageW(msg, apt->win, WM_USER, WM_APP - 1, PM_REMOVE|PM_NOYIELD) ||
            /* next retrieve other messages necessary for the app to remain responsive */
-           PeekMessageW(msg, NULL, 0, 0, PM_QS_PAINT|PM_QS_POSTMESSAGE|PM_REMOVE|PM_NOYIELD);
+           PeekMessageW(msg, NULL, WM_DDE_FIRST, WM_DDE_LAST, PM_REMOVE|PM_NOYIELD) ||
+           PeekMessageW(msg, NULL, 0, 0, PM_QS_PAINT|PM_QS_SENDMESSAGE|PM_REMOVE|PM_NOYIELD);
 }
 
 /***********************************************************************
@@ -3392,7 +3696,7 @@ HRESULT WINAPI CoWaitForMultipleHandles(DWORD dwFlags, DWORD dwTimeout,
         DWORD now = GetTickCount();
         DWORD res;
 
-        if ((dwTimeout != INFINITE) && (start_time + dwTimeout >= now))
+        if (now - start_time > dwTimeout)
         {
             hr = RPC_S_CALLPENDING;
             break;
@@ -3400,8 +3704,8 @@ HRESULT WINAPI CoWaitForMultipleHandles(DWORD dwFlags, DWORD dwTimeout,
 
         if (message_loop)
         {
-            DWORD wait_flags = (dwFlags & COWAIT_WAITALL) ? MWMO_WAITALL : 0 |
-                    (dwFlags & COWAIT_ALERTABLE ) ? MWMO_ALERTABLE : 0;
+            DWORD wait_flags = ((dwFlags & COWAIT_WAITALL) ? MWMO_WAITALL : 0) |
+                    ((dwFlags & COWAIT_ALERTABLE ) ? MWMO_ALERTABLE : 0);
 
             TRACE("waiting for rpc completion or window message\n");
 
@@ -3471,23 +3775,19 @@ HRESULT WINAPI CoWaitForMultipleHandles(DWORD dwFlags, DWORD dwTimeout,
                 (dwFlags & COWAIT_ALERTABLE) ? TRUE : FALSE);
         }
 
-        if ((res >= WAIT_OBJECT_0) && (res < WAIT_OBJECT_0 + cHandles))
-        {
-            /* handle signaled, store index */
-            *lpdwindex = (res - WAIT_OBJECT_0);
-            break;
-        }
-        else if (res == WAIT_TIMEOUT)
+        switch (res)
         {
+        case WAIT_TIMEOUT:
             hr = RPC_S_CALLPENDING;
             break;
-        }
-        else
-        {
-            ERR("Unexpected wait termination: %d, %d\n", res, GetLastError());
-            hr = E_UNEXPECTED;
+        case WAIT_FAILED:
+            hr = HRESULT_FROM_WIN32( GetLastError() );
+            break;
+        default:
+            *lpdwindex = res;
             break;
         }
+        break;
     }
     TRACE("-- 0x%08x\n", hr);
     return hr;
@@ -3497,7 +3797,7 @@ HRESULT WINAPI CoWaitForMultipleHandles(DWORD dwFlags, DWORD dwTimeout,
 /***********************************************************************
  *           CoGetObject [OLE32.@]
  *
- * Gets the object named by coverting the name to a moniker and binding to it.
+ * Gets the object named by converting the name to a moniker and binding to it.
  *
  * PARAMS
  *  pszName      [I] String representing the object.
@@ -3565,6 +3865,410 @@ HRESULT WINAPI CoRegisterChannelHook(REFGUID guidExtension, IChannelHook *pChann
     return RPC_RegisterChannelHook(guidExtension, pChannelHook);
 }
 
+typedef struct Context
+{
+    IComThreadingInfo IComThreadingInfo_iface;
+    IContextCallback IContextCallback_iface;
+    IObjContext IObjContext_iface;
+    LONG refs;
+    APTTYPE apttype;
+} Context;
+
+static inline Context *impl_from_IComThreadingInfo( IComThreadingInfo *iface )
+{
+        return CONTAINING_RECORD(iface, Context, IComThreadingInfo_iface);
+}
+
+static inline Context *impl_from_IContextCallback( IContextCallback *iface )
+{
+        return CONTAINING_RECORD(iface, Context, IContextCallback_iface);
+}
+
+static inline Context *impl_from_IObjContext( IObjContext *iface )
+{
+        return CONTAINING_RECORD(iface, Context, IObjContext_iface);
+}
+
+static HRESULT Context_QueryInterface(Context *iface, REFIID riid, LPVOID *ppv)
+{
+    *ppv = NULL;
+
+    if (IsEqualIID(riid, &IID_IComThreadingInfo) ||
+        IsEqualIID(riid, &IID_IUnknown))
+    {
+        *ppv = &iface->IComThreadingInfo_iface;
+    }
+    else if (IsEqualIID(riid, &IID_IContextCallback))
+    {
+        *ppv = &iface->IContextCallback_iface;
+    }
+    else if (IsEqualIID(riid, &IID_IObjContext))
+    {
+        *ppv = &iface->IObjContext_iface;
+    }
+
+    if (*ppv)
+    {
+        IUnknown_AddRef((IUnknown*)*ppv);
+        return S_OK;
+    }
+
+    FIXME("interface not implemented %s\n", debugstr_guid(riid));
+    return E_NOINTERFACE;
+}
+
+static ULONG Context_AddRef(Context *This)
+{
+    return InterlockedIncrement(&This->refs);
+}
+
+static ULONG Context_Release(Context *This)
+{
+    ULONG refs = InterlockedDecrement(&This->refs);
+    if (!refs)
+        HeapFree(GetProcessHeap(), 0, This);
+    return refs;
+}
+
+static HRESULT WINAPI Context_CTI_QueryInterface(IComThreadingInfo *iface, REFIID riid, LPVOID *ppv)
+{
+    Context *This = impl_from_IComThreadingInfo(iface);
+    return Context_QueryInterface(This, riid, ppv);
+}
+
+static ULONG WINAPI Context_CTI_AddRef(IComThreadingInfo *iface)
+{
+    Context *This = impl_from_IComThreadingInfo(iface);
+    return Context_AddRef(This);
+}
+
+static ULONG WINAPI Context_CTI_Release(IComThreadingInfo *iface)
+{
+    Context *This = impl_from_IComThreadingInfo(iface);
+    return Context_Release(This);
+}
+
+static HRESULT WINAPI Context_CTI_GetCurrentApartmentType(IComThreadingInfo *iface, APTTYPE *apttype)
+{
+    Context *This = impl_from_IComThreadingInfo(iface);
+
+    TRACE("(%p)\n", apttype);
+
+    *apttype = This->apttype;
+    return S_OK;
+}
+
+static HRESULT WINAPI Context_CTI_GetCurrentThreadType(IComThreadingInfo *iface, THDTYPE *thdtype)
+{
+    Context *This = impl_from_IComThreadingInfo(iface);
+
+    TRACE("(%p)\n", thdtype);
+
+    switch (This->apttype)
+    {
+    case APTTYPE_STA:
+    case APTTYPE_MAINSTA:
+        *thdtype = THDTYPE_PROCESSMESSAGES;
+        break;
+    default:
+        *thdtype = THDTYPE_BLOCKMESSAGES;
+        break;
+    }
+    return S_OK;
+}
+
+static HRESULT WINAPI Context_CTI_GetCurrentLogicalThreadId(IComThreadingInfo *iface, GUID *logical_thread_id)
+{
+    FIXME("(%p): stub\n", logical_thread_id);
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Context_CTI_SetCurrentLogicalThreadId(IComThreadingInfo *iface, REFGUID logical_thread_id)
+{
+    FIXME("(%s): stub\n", debugstr_guid(logical_thread_id));
+    return E_NOTIMPL;
+}
+
+static const IComThreadingInfoVtbl Context_Threading_Vtbl =
+{
+    Context_CTI_QueryInterface,
+    Context_CTI_AddRef,
+    Context_CTI_Release,
+    Context_CTI_GetCurrentApartmentType,
+    Context_CTI_GetCurrentThreadType,
+    Context_CTI_GetCurrentLogicalThreadId,
+    Context_CTI_SetCurrentLogicalThreadId
+};
+
+static HRESULT WINAPI Context_CC_QueryInterface(IContextCallback *iface, REFIID riid, LPVOID *ppv)
+{
+    Context *This = impl_from_IContextCallback(iface);
+    return Context_QueryInterface(This, riid, ppv);
+}
+
+static ULONG WINAPI Context_CC_AddRef(IContextCallback *iface)
+{
+    Context *This = impl_from_IContextCallback(iface);
+    return Context_AddRef(This);
+}
+
+static ULONG WINAPI Context_CC_Release(IContextCallback *iface)
+{
+    Context *This = impl_from_IContextCallback(iface);
+    return Context_Release(This);
+}
+
+static HRESULT WINAPI Context_CC_ContextCallback(IContextCallback *iface, PFNCONTEXTCALL pCallback,
+                            ComCallData *param, REFIID riid, int method, IUnknown *punk)
+{
+    Context *This = impl_from_IContextCallback(iface);
+
+    FIXME("(%p/%p)->(%p, %p, %s, %d, %p)\n", This, iface, pCallback, param, debugstr_guid(riid), method, punk);
+    return E_NOTIMPL;
+}
+
+static const IContextCallbackVtbl Context_Callback_Vtbl =
+{
+    Context_CC_QueryInterface,
+    Context_CC_AddRef,
+    Context_CC_Release,
+    Context_CC_ContextCallback
+};
+
+static HRESULT WINAPI Context_OC_QueryInterface(IObjContext *iface, REFIID riid, LPVOID *ppv)
+{
+    Context *This = impl_from_IObjContext(iface);
+    return Context_QueryInterface(This, riid, ppv);
+}
+
+static ULONG WINAPI Context_OC_AddRef(IObjContext *iface)
+{
+    Context *This = impl_from_IObjContext(iface);
+    return Context_AddRef(This);
+}
+
+static ULONG WINAPI Context_OC_Release(IObjContext *iface)
+{
+    Context *This = impl_from_IObjContext(iface);
+    return Context_Release(This);
+}
+
+static HRESULT WINAPI Context_OC_SetProperty(IObjContext *iface, REFGUID propid, CPFLAGS flags, IUnknown *punk)
+{
+    Context *This = impl_from_IObjContext(iface);
+
+    FIXME("(%p/%p)->(%s, %x, %p)\n", This, iface, debugstr_guid(propid), flags, punk);
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Context_OC_RemoveProperty(IObjContext *iface, REFGUID propid)
+{
+    Context *This = impl_from_IObjContext(iface);
+
+    FIXME("(%p/%p)->(%s)\n", This, iface, debugstr_guid(propid));
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Context_OC_GetProperty(IObjContext *iface, REFGUID propid, CPFLAGS *flags, IUnknown **punk)
+{
+    Context *This = impl_from_IObjContext(iface);
+
+    FIXME("(%p/%p)->(%s, %p, %p)\n", This, iface, debugstr_guid(propid), flags, punk);
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Context_OC_EnumContextProps(IObjContext *iface, IEnumContextProps **props)
+{
+    Context *This = impl_from_IObjContext(iface);
+
+    FIXME("(%p/%p)->(%p)\n", This, iface, props);
+    return E_NOTIMPL;
+}
+
+static void WINAPI Context_OC_Reserved1(IObjContext *iface)
+{
+    Context *This = impl_from_IObjContext(iface);
+    FIXME("(%p/%p)\n", This, iface);
+}
+
+static void WINAPI Context_OC_Reserved2(IObjContext *iface)
+{
+    Context *This = impl_from_IObjContext(iface);
+    FIXME("(%p/%p)\n", This, iface);
+}
+
+static void WINAPI Context_OC_Reserved3(IObjContext *iface)
+{
+    Context *This = impl_from_IObjContext(iface);
+    FIXME("(%p/%p)\n", This, iface);
+}
+
+static void WINAPI Context_OC_Reserved4(IObjContext *iface)
+{
+    Context *This = impl_from_IObjContext(iface);
+    FIXME("(%p/%p)\n", This, iface);
+}
+
+static void WINAPI Context_OC_Reserved5(IObjContext *iface)
+{
+    Context *This = impl_from_IObjContext(iface);
+    FIXME("(%p/%p)\n", This, iface);
+}
+
+static void WINAPI Context_OC_Reserved6(IObjContext *iface)
+{
+    Context *This = impl_from_IObjContext(iface);
+    FIXME("(%p/%p)\n", This, iface);
+}
+
+static void WINAPI Context_OC_Reserved7(IObjContext *iface)
+{
+    Context *This = impl_from_IObjContext(iface);
+    FIXME("(%p/%p)\n", This, iface);
+}
+
+static const IObjContextVtbl Context_Object_Vtbl =
+{
+    Context_OC_QueryInterface,
+    Context_OC_AddRef,
+    Context_OC_Release,
+    Context_OC_SetProperty,
+    Context_OC_RemoveProperty,
+    Context_OC_GetProperty,
+    Context_OC_EnumContextProps,
+    Context_OC_Reserved1,
+    Context_OC_Reserved2,
+    Context_OC_Reserved3,
+    Context_OC_Reserved4,
+    Context_OC_Reserved5,
+    Context_OC_Reserved6,
+    Context_OC_Reserved7
+};
+
+/***********************************************************************
+ *           CoGetObjectContext [OLE32.@]
+ *
+ * Retrieves an object associated with the current context (i.e. apartment).
+ *
+ * PARAMS
+ *  riid [I] ID of the interface of the object to retrieve.
+ *  ppv  [O] Address where object will be stored on return.
+ *
+ * RETURNS
+ *  Success: S_OK.
+ *  Failure: HRESULT code.
+ */
+HRESULT WINAPI CoGetObjectContext(REFIID riid, void **ppv)
+{
+    APARTMENT *apt = COM_CurrentApt();
+    Context *context;
+    HRESULT hr;
+
+    TRACE("(%s, %p)\n", debugstr_guid(riid), ppv);
+
+    *ppv = NULL;
+    if (!apt)
+    {
+        if (!(apt = apartment_find_multi_threaded()))
+        {
+            ERR("apartment not initialised\n");
+            return CO_E_NOTINITIALIZED;
+        }
+        apartment_release(apt);
+    }
+
+    context = HeapAlloc(GetProcessHeap(), 0, sizeof(*context));
+    if (!context)
+        return E_OUTOFMEMORY;
+
+    context->IComThreadingInfo_iface.lpVtbl = &Context_Threading_Vtbl;
+    context->IContextCallback_iface.lpVtbl = &Context_Callback_Vtbl;
+    context->IObjContext_iface.lpVtbl = &Context_Object_Vtbl;
+    context->refs = 1;
+    if (apt->multi_threaded)
+        context->apttype = APTTYPE_MTA;
+    else if (apt->main)
+        context->apttype = APTTYPE_MAINSTA;
+    else
+        context->apttype = APTTYPE_STA;
+
+    hr = IUnknown_QueryInterface((IUnknown *)&context->IComThreadingInfo_iface, riid, ppv);
+    IUnknown_Release((IUnknown *)&context->IComThreadingInfo_iface);
+
+    return hr;
+}
+
+
+/***********************************************************************
+ *           CoGetContextToken [OLE32.@]
+ */
+HRESULT WINAPI CoGetContextToken( ULONG_PTR *token )
+{
+    struct oletls *info = COM_CurrentInfo();
+
+    TRACE("(%p)\n", token);
+
+    if (!info)
+        return E_OUTOFMEMORY;
+
+    if (!info->apt)
+    {
+        APARTMENT *apt;
+        if (!(apt = apartment_find_multi_threaded()))
+        {
+            ERR("apartment not initialised\n");
+            return CO_E_NOTINITIALIZED;
+        }
+        apartment_release(apt);
+    }
+
+    if (!token)
+        return E_POINTER;
+
+    if (!info->context_token)
+    {
+        HRESULT hr;
+        IObjContext *ctx;
+
+        hr = CoGetObjectContext(&IID_IObjContext, (void **)&ctx);
+        if (FAILED(hr)) return hr;
+        info->context_token = ctx;
+    }
+
+    *token = (ULONG_PTR)info->context_token;
+    TRACE("apt->context_token=%p\n", info->context_token);
+
+    return S_OK;
+}
+
+HRESULT Handler_DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
+{
+    static const WCHAR wszInprocHandler32[] = {'I','n','p','r','o','c','H','a','n','d','l','e','r','3','2',0};
+    HKEY hkey;
+    HRESULT hres;
+
+    hres = COM_OpenKeyForCLSID(rclsid, wszInprocHandler32, KEY_READ, &hkey);
+    if (SUCCEEDED(hres))
+    {
+        WCHAR dllpath[MAX_PATH+1];
+
+        if (COM_RegReadPath(hkey, NULL, NULL, dllpath, ARRAYSIZE(dllpath)) == ERROR_SUCCESS)
+        {
+            static const WCHAR wszOle32[] = {'o','l','e','3','2','.','d','l','l',0};
+            if (!strcmpiW(dllpath, wszOle32))
+            {
+                RegCloseKey(hkey);
+                return HandlerCF_Create(rclsid, riid, ppv);
+            }
+        }
+        else
+            WARN("not creating object for inproc handler path %s\n", debugstr_w(dllpath));
+        RegCloseKey(hkey);
+    }
+
+    return CLASS_E_CLASSNOTAVAILABLE;
+}
+
 /***********************************************************************
  *             DllMain (OLE32.@)
  */
@@ -3574,17 +4278,14 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID fImpLoad)
 
     switch(fdwReason) {
     case DLL_PROCESS_ATTACH:
-        OLE32_hInstance = hinstDLL;
+        hProxyDll = hinstDLL;
         COMPOBJ_InitProcess();
-       if (TRACE_ON(ole)) CoRegisterMallocSpy((LPVOID)-1);
        break;
 
     case DLL_PROCESS_DETACH:
-        if (TRACE_ON(ole)) CoRevokeMallocSpy();
-        OLEDD_UnInitialize();
         COMPOBJ_UninitProcess();
         RPC_UnregisterAllChannelHooks();
-        OLE32_hInstance = 0;
+        COMPOBJ_DllList_Free();
        break;
 
     case DLL_THREAD_DETACH:
@@ -3594,4 +4295,18 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID fImpLoad)
     return TRUE;
 }
 
-/* NOTE: DllRegisterServer and DllUnregisterServer are in regsvr.c */
+/***********************************************************************
+ *             DllRegisterServer (OLE32.@)
+ */
+HRESULT WINAPI DllRegisterServer(void)
+{
+    return OLE32_DllRegisterServer();
+}
+
+/***********************************************************************
+ *             DllUnregisterServer (OLE32.@)
+ */
+HRESULT WINAPI DllUnregisterServer(void)
+{
+    return OLE32_DllUnregisterServer();
+}