[PATCH 3/5] ole32: Move apartment and activation management to a separate file.
Nikolay Sivov
nsivov at codeweavers.com
Tue Aug 25 10:18:15 CDT 2020
Signed-off-by: Nikolay Sivov <nsivov at codeweavers.com>
---
dlls/ole32/Makefile.in | 1 +
dlls/ole32/apartment.c | 1289 +++++++++++++++++++++++++++++
dlls/ole32/compobj.c | 1497 ++++------------------------------
dlls/ole32/compobj_private.h | 20 +-
4 files changed, 1456 insertions(+), 1351 deletions(-)
create mode 100644 dlls/ole32/apartment.c
diff --git a/dlls/ole32/Makefile.in b/dlls/ole32/Makefile.in
index 1c1e28fa4c5..5a2b21dee4f 100644
--- a/dlls/ole32/Makefile.in
+++ b/dlls/ole32/Makefile.in
@@ -8,6 +8,7 @@ EXTRADLLFLAGS = -mno-cygwin
C_SRCS = \
antimoniker.c \
+ apartment.c \
bindctx.c \
classmoniker.c \
clipboard.c \
diff --git a/dlls/ole32/apartment.c b/dlls/ole32/apartment.c
new file mode 100644
index 00000000000..456e6c80a36
--- /dev/null
+++ b/dlls/ole32/apartment.c
@@ -0,0 +1,1289 @@
+/*
+ * Copyright 1995 Martin von Loewis
+ * Copyright 1998 Justin Bradford
+ * Copyright 1999 Francis Beaudet
+ * Copyright 1999 Sylvain St-Germain
+ * Copyright 2002 Marcus Meissner
+ * Copyright 2004 Mike Hearn
+ * Copyright 2005-2006 Robert Shearman (for CodeWeavers)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ */
+
+#include <stdarg.h>
+#include <assert.h>
+
+#define COBJMACROS
+#define NONAMELESSUNION
+
+#include "windef.h"
+#include "winbase.h"
+#include "servprov.h"
+
+#include "compobj_private.h"
+
+#include "wine/debug.h"
+#include "wine/list.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(ole);
+
+enum comclass_threadingmodel
+{
+ ThreadingModel_Apartment = 1,
+ ThreadingModel_Free = 2,
+ ThreadingModel_No = 3,
+ ThreadingModel_Both = 4,
+ ThreadingModel_Neutral = 5
+};
+
+enum class_reg_data_origin
+{
+ CLASS_REG_ACTCTX,
+ CLASS_REG_REGISTRY,
+};
+
+struct class_reg_data
+{
+ enum class_reg_data_origin origin;
+ union
+ {
+ struct
+ {
+ const WCHAR *module_name;
+ DWORD threading_model;
+ HANDLE hactctx;
+ } actctx;
+ HKEY hkey;
+ } u;
+};
+
+static struct apartment *mta;
+static struct apartment *main_sta; /* the first STA */
+static struct list apts = LIST_INIT(apts);
+
+static CRITICAL_SECTION apt_cs;
+static CRITICAL_SECTION_DEBUG apt_cs_debug =
+{
+ 0, 0, &apt_cs,
+ { &apt_cs_debug.ProcessLocksList, &apt_cs_debug.ProcessLocksList },
+ 0, 0, { (DWORD_PTR)(__FILE__ ": apt_cs") }
+};
+static CRITICAL_SECTION apt_cs = { &apt_cs_debug, -1, 0, 0, 0, 0 };
+
+static struct list dlls = LIST_INIT(dlls);
+
+static CRITICAL_SECTION dlls_cs;
+static CRITICAL_SECTION_DEBUG dlls_cs_debug =
+{
+ 0, 0, &dlls_cs,
+ { &dlls_cs_debug.ProcessLocksList, &dlls_cs_debug.ProcessLocksList },
+ 0, 0, { (DWORD_PTR)(__FILE__ ": dlls_cs") }
+};
+static CRITICAL_SECTION dlls_cs = { &dlls_cs_debug, -1, 0, 0, 0, 0 };
+
+typedef HRESULT (WINAPI *DllGetClassObjectFunc)(REFCLSID clsid, REFIID iid, void **obj);
+typedef HRESULT (WINAPI *DllCanUnloadNowFunc)(void);
+
+struct opendll
+{
+ LONG refs;
+ LPWSTR library_name;
+ HANDLE library;
+ DllGetClassObjectFunc DllGetClassObject;
+ DllCanUnloadNowFunc DllCanUnloadNow;
+ struct list entry;
+};
+
+struct apartment_loaded_dll
+{
+ struct list entry;
+ struct opendll *dll;
+ DWORD unload_time;
+ BOOL multi_threaded;
+};
+
+static struct opendll *apartment_get_dll(const WCHAR *library_name)
+{
+ struct opendll *ptr, *ret = NULL;
+
+ EnterCriticalSection(&dlls_cs);
+ LIST_FOR_EACH_ENTRY(ptr, &dlls, struct opendll, entry)
+ {
+ if (!wcsicmp(library_name, ptr->library_name) &&
+ (InterlockedIncrement(&ptr->refs) != 1) /* entry is being destroyed if == 1 */)
+ {
+ ret = ptr;
+ break;
+ }
+ }
+ LeaveCriticalSection(&dlls_cs);
+
+ return ret;
+}
+
+/* caller must ensure that library_name is not already in the open dll list */
+static HRESULT apartment_add_dll(const WCHAR *library_name, struct opendll **ret)
+{
+ struct opendll *entry;
+ int len;
+ HRESULT hr = S_OK;
+ HANDLE hLibrary;
+ DllCanUnloadNowFunc DllCanUnloadNow;
+ DllGetClassObjectFunc DllGetClassObject;
+
+ TRACE("%s\n", debugstr_w(library_name));
+
+ *ret = apartment_get_dll(library_name);
+ if (*ret) return S_OK;
+
+ /* Load outside of dlls lock to avoid 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 is optional */
+ DllCanUnloadNow = (void *)GetProcAddress(hLibrary, "DllCanUnloadNow");
+ 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(&dlls_cs);
+
+ *ret = apartment_get_dll(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 = lstrlenW(library_name);
+ entry = heap_alloc(sizeof(*entry));
+ if (entry)
+ entry->library_name = heap_alloc((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(&dlls, &entry->entry);
+ *ret = entry;
+ }
+ else
+ {
+ heap_free(entry);
+ hr = E_OUTOFMEMORY;
+ FreeLibrary(hLibrary);
+ }
+ }
+
+ LeaveCriticalSection(&dlls_cs);
+
+ return hr;
+}
+
+/* pass FALSE for free_entry to release a reference without destroying the
+ * entry if it reaches zero or TRUE otherwise */
+static void apartment_release_dll(struct opendll *entry, BOOL free_entry)
+{
+ if (!InterlockedDecrement(&entry->refs) && free_entry)
+ {
+ EnterCriticalSection(&dlls_cs);
+ list_remove(&entry->entry);
+ LeaveCriticalSection(&dlls_cs);
+
+ TRACE("freeing %p\n", entry->library);
+ FreeLibrary(entry->library);
+
+ heap_free(entry->library_name);
+ heap_free(entry);
+ }
+}
+
+/* frees memory associated with active dll list */
+static void apartment_release_dlls(void)
+{
+ struct opendll *entry, *cursor2;
+ EnterCriticalSection(&dlls_cs);
+ LIST_FOR_EACH_ENTRY_SAFE(entry, cursor2, &dlls, struct opendll, entry)
+ {
+ list_remove(&entry->entry);
+ heap_free(entry->library_name);
+ heap_free(entry);
+ }
+ LeaveCriticalSection(&dlls_cs);
+ DeleteCriticalSection(&dlls_cs);
+}
+
+/*
+ * This is a marshallable object exposing registered local servers.
+ * IServiceProvider is used only because it happens meet requirements
+ * and already has proxy/stub code. If more functionality is needed,
+ * a custom interface may be used instead.
+ */
+struct local_server
+{
+ IServiceProvider IServiceProvider_iface;
+ LONG refcount;
+ struct apartment *apt;
+ IStream *marshal_stream;
+};
+
+static inline struct local_server *impl_from_IServiceProvider(IServiceProvider *iface)
+{
+ return CONTAINING_RECORD(iface, struct local_server, IServiceProvider_iface);
+}
+
+static HRESULT WINAPI local_server_QueryInterface(IServiceProvider *iface, REFIID riid, void **obj)
+{
+ struct local_server *local_server = impl_from_IServiceProvider(iface);
+
+ TRACE("%p, %s, %p\n", iface, debugstr_guid(riid), obj);
+
+ if (IsEqualGUID(riid, &IID_IUnknown) ||
+ IsEqualGUID(riid, &IID_IServiceProvider))
+ {
+ *obj = &local_server->IServiceProvider_iface;
+ }
+ else
+ {
+ *obj = NULL;
+ return E_NOINTERFACE;
+ }
+
+ IUnknown_AddRef((IUnknown *)*obj);
+ return S_OK;
+}
+
+static ULONG WINAPI local_server_AddRef(IServiceProvider *iface)
+{
+ struct local_server *local_server = impl_from_IServiceProvider(iface);
+ LONG refcount = InterlockedIncrement(&local_server->refcount);
+
+ TRACE("%p, refcount %d\n", iface, refcount);
+
+ return refcount;
+}
+
+static ULONG WINAPI local_server_Release(IServiceProvider *iface)
+{
+ struct local_server *local_server = impl_from_IServiceProvider(iface);
+ LONG refcount = InterlockedDecrement(&local_server->refcount);
+
+ TRACE("%p, refcount %d\n", iface, refcount);
+
+ if (!refcount)
+ {
+ assert(!local_server->apt);
+ heap_free(local_server);
+ }
+
+ return refcount;
+}
+
+static HRESULT WINAPI local_server_QueryService(IServiceProvider *iface, REFGUID guid, REFIID riid, void **obj)
+{
+ struct local_server *local_server = impl_from_IServiceProvider(iface);
+ struct apartment *apt = COM_CurrentApt();
+ HRESULT hr = E_FAIL;
+ IUnknown *unk;
+
+ TRACE("%p, %s, %s, %p\n", iface, debugstr_guid(guid), debugstr_guid(riid), obj);
+
+ if (!local_server->apt)
+ return E_UNEXPECTED;
+
+ if (SUCCEEDED(COM_GetRegisteredClassObject(apt, guid, CLSCTX_LOCAL_SERVER, &unk)))
+ {
+ hr = IUnknown_QueryInterface(unk, riid, obj);
+ IUnknown_Release(unk);
+ }
+
+ return hr;
+}
+
+static const IServiceProviderVtbl local_server_vtbl =
+{
+ local_server_QueryInterface,
+ local_server_AddRef,
+ local_server_Release,
+ local_server_QueryService
+};
+
+HRESULT apartment_get_local_server_stream(struct apartment *apt, IStream **ret)
+{
+ HRESULT hr = S_OK;
+
+ EnterCriticalSection(&apt->cs);
+
+ if (!apt->local_server)
+ {
+ struct local_server *obj;
+
+ obj = heap_alloc(sizeof(*obj));
+ if (obj)
+ {
+ obj->IServiceProvider_iface.lpVtbl = &local_server_vtbl;
+ obj->refcount = 1;
+ obj->apt = apt;
+
+ hr = CreateStreamOnHGlobal(0, TRUE, &obj->marshal_stream);
+ if (SUCCEEDED(hr))
+ {
+ hr = CoMarshalInterface(obj->marshal_stream, &IID_IServiceProvider, (IUnknown *)&obj->IServiceProvider_iface,
+ MSHCTX_LOCAL, NULL, MSHLFLAGS_TABLESTRONG);
+ if (FAILED(hr))
+ IStream_Release(obj->marshal_stream);
+ }
+
+ if (SUCCEEDED(hr))
+ apt->local_server = obj;
+ else
+ heap_free(obj);
+ }
+ else
+ hr = E_OUTOFMEMORY;
+ }
+
+ if (SUCCEEDED(hr))
+ hr = IStream_Clone(apt->local_server->marshal_stream, ret);
+
+ LeaveCriticalSection(&apt->cs);
+
+ if (FAILED(hr))
+ ERR("Failed: %#x\n", hr);
+
+ return hr;
+}
+
+/* Creates new apartment for given model */
+static struct apartment *apartment_construct(DWORD model)
+{
+ struct apartment *apt;
+
+ TRACE("creating new apartment, model %d\n", model);
+
+ apt = heap_alloc_zero(sizeof(*apt));
+ apt->tid = GetCurrentThreadId();
+
+ list_init(&apt->proxies);
+ list_init(&apt->stubmgrs);
+ list_init(&apt->loaded_dlls);
+ list_init(&apt->usage_cookies);
+ apt->ipidc = 0;
+ apt->refs = 1;
+ apt->remunk_exported = FALSE;
+ apt->oidc = 1;
+ InitializeCriticalSection(&apt->cs);
+ DEBUG_SET_CRITSEC_NAME(&apt->cs, "apartment");
+
+ apt->multi_threaded = !(model & COINIT_APARTMENTTHREADED);
+
+ if (apt->multi_threaded)
+ {
+ /* FIXME: should be randomly generated by in an RPC call to rpcss */
+ apt->oxid = ((OXID)GetCurrentProcessId() << 32) | 0xcafe;
+ }
+ else
+ {
+ /* FIXME: should be randomly generated by in an RPC call to rpcss */
+ apt->oxid = ((OXID)GetCurrentProcessId() << 32) | GetCurrentThreadId();
+ }
+
+ TRACE("Created apartment on OXID %s\n", wine_dbgstr_longlong(apt->oxid));
+
+ list_add_head(&apts, &apt->entry);
+
+ return apt;
+}
+
+/* Frees unused libraries loaded into apartment */
+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 && ((int)(GetTickCount() - entry->unload_time) > 0)))
+ {
+ list_remove(&entry->entry);
+ apartment_release_dll(entry->dll, TRUE);
+ heap_free(entry);
+ }
+ else
+ {
+ entry->unload_time = GetTickCount() + real_delay;
+ if (!entry->unload_time) entry->unload_time = 1;
+ }
+ }
+ else if (entry->unload_time)
+ entry->unload_time = 0;
+ }
+ LeaveCriticalSection(&apt->cs);
+}
+
+void apartment_release(struct apartment *apt)
+{
+ DWORD refcount;
+
+ EnterCriticalSection(&apt_cs);
+
+ refcount = InterlockedDecrement(&apt->refs);
+ TRACE("%s: after = %d\n", wine_dbgstr_longlong(apt->oxid), refcount);
+
+ if (apt->being_destroyed)
+ {
+ LeaveCriticalSection(&apt_cs);
+ return;
+ }
+
+ /* destruction stuff that needs to happen under global */
+ if (!refcount)
+ {
+ apt->being_destroyed = TRUE;
+ if (apt == mta) mta = NULL;
+ else if (apt == main_sta) main_sta = NULL;
+ list_remove(&apt->entry);
+ }
+
+ LeaveCriticalSection(&apt_cs);
+
+ if (!refcount)
+ {
+ struct list *cursor, *cursor2;
+
+ TRACE("destroying apartment %p, oxid %s\n", apt, wine_dbgstr_longlong(apt->oxid));
+
+ if (apt->local_server)
+ {
+ struct local_server *local_server = apt->local_server;
+ LARGE_INTEGER zero;
+
+ memset(&zero, 0, sizeof(zero));
+ IStream_Seek(local_server->marshal_stream, zero, STREAM_SEEK_SET, NULL);
+ CoReleaseMarshalData(local_server->marshal_stream);
+ IStream_Release(local_server->marshal_stream);
+ local_server->marshal_stream = NULL;
+
+ apt->local_server = NULL;
+ local_server->apt = NULL;
+ IServiceProvider_Release(&local_server->IServiceProvider_iface);
+ }
+
+ /* 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);
+ }
+
+ /* 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) IMessageFilter_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);
+ apartment_release_dll(apartment_loaded_dll->dll, FALSE);
+ list_remove(cursor);
+ heap_free(apartment_loaded_dll);
+ }
+
+ DEBUG_CLEAR_CRITSEC_NAME(&apt->cs);
+ DeleteCriticalSection(&apt->cs);
+
+ heap_free(apt);
+ }
+}
+
+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;
+}
+
+/* Gets existing apartment or creates a new one and enters it */
+static struct apartment *apartment_get_or_create(DWORD model)
+{
+ struct apartment *apt = COM_CurrentApt();
+
+ if (!apt)
+ {
+ if (model & COINIT_APARTMENTTHREADED)
+ {
+ EnterCriticalSection(&apt_cs);
+
+ apt = apartment_construct(model);
+ if (!main_sta)
+ {
+ main_sta = apt;
+ apt->main = TRUE;
+ TRACE("Created main-threaded apartment with OXID %s\n", wine_dbgstr_longlong(apt->oxid));
+ }
+
+ LeaveCriticalSection(&apt_cs);
+
+ if (apt->main)
+ apartment_createwindowifneeded(apt);
+ }
+ else
+ {
+ EnterCriticalSection(&apt_cs);
+
+ /* The multi-threaded apartment (MTA) contains zero or more threads interacting
+ * with free threaded (ie thread safe) COM objects. There is only ever one MTA
+ * in a process */
+ if (mta)
+ {
+ TRACE("entering the multithreaded apartment %s\n", wine_dbgstr_longlong(mta->oxid));
+ apartment_addref(mta);
+ }
+ else
+ mta = apartment_construct(model);
+
+ apt = mta;
+
+ LeaveCriticalSection(&apt_cs);
+ }
+ COM_CurrentInfo()->apt = apt;
+ }
+
+ return apt;
+}
+
+struct apartment *apartment_get_mta(void)
+{
+ struct apartment *apt;
+
+ EnterCriticalSection(&apt_cs);
+
+ if ((apt = mta))
+ apartment_addref(apt);
+
+ LeaveCriticalSection(&apt_cs);
+
+ return apt;
+}
+
+/* Return the current apartment if it exists, or, failing that, the MTA. Caller
+ * must free the returned apartment in either case. */
+struct apartment *apartment_get_current_or_mta(void)
+{
+ struct apartment *apt = COM_CurrentApt();
+ if (apt)
+ {
+ apartment_addref(apt);
+ return apt;
+ }
+ return apartment_get_mta();
+}
+
+/* The given OXID must be local to this process */
+struct apartment *apartment_findfromoxid(OXID oxid)
+{
+ struct apartment *result = NULL;
+ struct list *cursor;
+
+ EnterCriticalSection(&apt_cs);
+ LIST_FOR_EACH( cursor, &apts )
+ {
+ struct apartment *apt = LIST_ENTRY( cursor, struct apartment, entry );
+ if (apt->oxid == oxid)
+ {
+ result = apt;
+ apartment_addref(result);
+ break;
+ }
+ }
+ LeaveCriticalSection(&apt_cs);
+
+ return result;
+}
+
+/* gets the apartment which has a given creator thread ID. The caller must
+ * release the reference from the apartment as soon as the apartment pointer
+ * is no longer required. */
+struct apartment *apartment_findfromtid(DWORD tid)
+{
+ struct apartment *result = NULL;
+ struct list *cursor;
+
+ EnterCriticalSection(&apt_cs);
+ LIST_FOR_EACH( cursor, &apts )
+ {
+ struct apartment *apt = LIST_ENTRY( cursor, struct apartment, entry );
+ if (apt->tid == tid)
+ {
+ result = apt;
+ apartment_addref(result);
+ break;
+ }
+ }
+ LeaveCriticalSection(&apt_cs);
+
+ 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 struct apartment *apartment_findmain(void)
+{
+ struct apartment *result;
+
+ EnterCriticalSection(&apt_cs);
+
+ result = main_sta;
+ if (result) apartment_addref(result);
+
+ LeaveCriticalSection(&apt_cs);
+
+ return result;
+}
+
+struct host_object_params
+{
+ struct class_reg_data regdata;
+ CLSID clsid; /* clsid of object to marshal */
+ IID iid; /* interface to marshal */
+ HANDLE event; /* event signalling when ready for multi-threaded case */
+ HRESULT hr; /* result for multi-threaded case */
+ IStream *stream; /* stream that the object will be marshaled into */
+ BOOL apartment_threaded; /* is the component purely apartment-threaded? */
+};
+
+/* Returns expanded dll path from the registry or activation context. */
+static BOOL get_object_dll_path(const struct class_reg_data *regdata, WCHAR *dst, DWORD dstlen)
+{
+ DWORD ret;
+
+ if (regdata->origin == CLASS_REG_REGISTRY)
+ {
+ DWORD keytype;
+ WCHAR src[MAX_PATH];
+ DWORD dwLength = dstlen * sizeof(WCHAR);
+
+ if ((ret = RegQueryValueExW(regdata->u.hkey, NULL, NULL, &keytype, (BYTE*)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 = wcschr(src, '\"');
+ if (quote_start)
+ {
+ const WCHAR *quote_end = wcschr(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);
+ }
+ }
+ return !ret;
+ }
+ else
+ {
+ ULONG_PTR cookie;
+
+ *dst = 0;
+ ActivateActCtx(regdata->u.actctx.hactctx, &cookie);
+ ret = SearchPathW(NULL, regdata->u.actctx.module_name, L".dll", dstlen, dst, NULL);
+ DeactivateActCtx(0, cookie);
+ return *dst != 0;
+ }
+}
+
+/* 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)
+{
+ HRESULT hr = S_OK;
+ BOOL found = FALSE;
+ struct apartment_loaded_dll *apartment_loaded_dll;
+
+ if (!wcsicmp(dllpath, L"ole32.dll"))
+ {
+ /* 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 for dll %s\n", hr, debugstr_w(dllpath));
+
+ return hr;
+ }
+
+ EnterCriticalSection(&apt->cs);
+
+ LIST_FOR_EACH_ENTRY(apartment_loaded_dll, &apt->loaded_dlls, struct apartment_loaded_dll, entry)
+ if (!wcsicmp(dllpath, apartment_loaded_dll->dll->library_name))
+ {
+ TRACE("found %s already loaded\n", debugstr_w(dllpath));
+ found = TRUE;
+ break;
+ }
+
+ if (!found)
+ {
+ apartment_loaded_dll = heap_alloc(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 = apartment_add_dll(dllpath, &apartment_loaded_dll->dll);
+ if (FAILED(hr))
+ heap_free(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 for dll %s\n", hr, debugstr_w(dllpath));
+ }
+
+ return hr;
+}
+
+static HRESULT apartment_hostobject(struct apartment *apt,
+ const struct host_object_params *params);
+
+struct host_thread_params
+{
+ COINIT threading_model;
+ HANDLE ready_event;
+ 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(void *p)
+{
+ struct host_thread_params *params = p;
+ MSG msg;
+ HRESULT hr;
+ struct apartment *apt;
+
+ TRACE("\n");
+
+ hr = CoInitializeEx(NULL, params->threading_model);
+ if (FAILED(hr)) return hr;
+
+ apt = COM_CurrentApt();
+ if (params->threading_model == COINIT_APARTMENTTHREADED)
+ {
+ apartment_createwindowifneeded(apt);
+ params->apartment_hwnd = apartment_getwindow(apt);
+ }
+ else
+ params->apartment_hwnd = NULL;
+
+ /* force the message queue to be created before signaling parent thread */
+ PeekMessageW(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
+
+ SetEvent(params->ready_event);
+ params = NULL; /* can't touch params after here as it may be invalid */
+
+ while (GetMessageW(&msg, NULL, 0, 0))
+ {
+ if (!msg.hwnd && (msg.message == DM_HOSTOBJECT))
+ {
+ 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
+ {
+ TranslateMessage(&msg);
+ DispatchMessageW(&msg);
+ }
+ }
+
+ TRACE("exiting\n");
+
+ CoUninitialize();
+
+ return S_OK;
+}
+
+/* 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, const struct class_reg_data *regdata, REFCLSID rclsid, REFIID riid, void **ppv)
+{
+ struct host_object_params params;
+ HWND apartment_hwnd = NULL;
+ DWORD apartment_tid = 0;
+ HRESULT hr;
+
+ if (!multi_threaded && main_apartment)
+ {
+ struct apartment *host_apt = apartment_findmain();
+ if (host_apt)
+ {
+ apartment_hwnd = apartment_getwindow(host_apt);
+ apartment_release(host_apt);
+ }
+ }
+
+ if (!apartment_hwnd)
+ {
+ EnterCriticalSection(&apt->cs);
+
+ if (!apt->host_apt_tid)
+ {
+ struct host_thread_params thread_params;
+ HANDLE handles[2];
+ DWORD wait_value;
+
+ thread_params.threading_model = multi_threaded ? COINIT_MULTITHREADED : COINIT_APARTMENTTHREADED;
+ handles[0] = thread_params.ready_event = CreateEventW(NULL, FALSE, FALSE, NULL);
+ thread_params.apartment_hwnd = NULL;
+ handles[1] = CreateThread(NULL, 0, apartment_hostobject_thread, &thread_params, 0, &apt->host_apt_tid);
+ if (!handles[1])
+ {
+ CloseHandle(handles[0]);
+ LeaveCriticalSection(&apt->cs);
+ return E_OUTOFMEMORY;
+ }
+ wait_value = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
+ CloseHandle(handles[0]);
+ CloseHandle(handles[1]);
+ if (wait_value == WAIT_OBJECT_0)
+ apt->host_apt_hwnd = thread_params.apartment_hwnd;
+ else
+ {
+ LeaveCriticalSection(&apt->cs);
+ return E_OUTOFMEMORY;
+ }
+ }
+
+ if (multi_threaded || !main_apartment)
+ {
+ apartment_hwnd = apt->host_apt_hwnd;
+ apartment_tid = apt->host_apt_tid;
+ }
+
+ LeaveCriticalSection(&apt->cs);
+ }
+
+ /* another thread may have become the main apartment in the time it took
+ * us to create the thread for the host apartment */
+ if (!apartment_hwnd && !multi_threaded && main_apartment)
+ {
+ struct apartment *host_apt = apartment_findmain();
+ if (host_apt)
+ {
+ apartment_hwnd = apartment_getwindow(host_apt);
+ apartment_release(host_apt);
+ }
+ }
+
+ params.regdata = *regdata;
+ params.clsid = *rclsid;
+ params.iid = *riid;
+ hr = CreateStreamOnHGlobal(NULL, TRUE, ¶ms.stream);
+ if (FAILED(hr))
+ return hr;
+ params.apartment_threaded = !multi_threaded;
+ if (multi_threaded)
+ {
+ params.hr = S_OK;
+ params.event = CreateEventW(NULL, FALSE, FALSE, NULL);
+ if (!PostThreadMessageW(apartment_tid, DM_HOSTOBJECT, 0, (LPARAM)¶ms))
+ hr = E_OUTOFMEMORY;
+ else
+ {
+ WaitForSingleObject(params.event, INFINITE);
+ hr = params.hr;
+ }
+ CloseHandle(params.event);
+ }
+ else
+ {
+ if (!apartment_hwnd)
+ {
+ ERR("host apartment didn't create window\n");
+ hr = E_OUTOFMEMORY;
+ }
+ else
+ hr = SendMessageW(apartment_hwnd, DM_HOSTOBJECT, 0, (LPARAM)¶ms);
+ }
+ if (SUCCEEDED(hr))
+ hr = CoUnmarshalInterface(params.stream, riid, ppv);
+ IStream_Release(params.stream);
+ return hr;
+}
+
+static enum comclass_threadingmodel get_threading_model(const struct class_reg_data *data)
+{
+ if (data->origin == CLASS_REG_REGISTRY)
+ {
+ WCHAR threading_model[10 /* lstrlenW(L"apartment")+1 */];
+ DWORD dwLength = sizeof(threading_model);
+ DWORD keytype;
+ DWORD ret;
+
+ ret = RegQueryValueExW(data->u.hkey, L"ThreadingModel", NULL, &keytype, (BYTE*)threading_model, &dwLength);
+ if ((ret != ERROR_SUCCESS) || (keytype != REG_SZ))
+ threading_model[0] = '\0';
+
+ if (!wcsicmp(threading_model, L"Apartment")) return ThreadingModel_Apartment;
+ if (!wcsicmp(threading_model, L"Free")) return ThreadingModel_Free;
+ if (!wcsicmp(threading_model, L"Both")) return ThreadingModel_Both;
+
+ /* there's not specific handling for this case */
+ if (threading_model[0]) return ThreadingModel_Neutral;
+ return ThreadingModel_No;
+ }
+ else
+ return data->u.actctx.threading_model;
+}
+
+HRESULT apartment_get_inproc_class_object(struct apartment *apt, const struct class_reg_data *regdata,
+ REFCLSID rclsid, REFIID riid, BOOL hostifnecessary, void **ppv)
+{
+ WCHAR dllpath[MAX_PATH+1];
+ BOOL apartment_threaded;
+
+ if (hostifnecessary)
+ {
+ enum comclass_threadingmodel model = get_threading_model(regdata);
+
+ if (model == ThreadingModel_Apartment)
+ {
+ apartment_threaded = TRUE;
+ if (apt->multi_threaded)
+ return apartment_hostobject_in_hostapt(apt, FALSE, FALSE, regdata, rclsid, riid, ppv);
+ }
+ else if (model == ThreadingModel_Free)
+ {
+ apartment_threaded = FALSE;
+ if (!apt->multi_threaded)
+ return apartment_hostobject_in_hostapt(apt, TRUE, FALSE, regdata, rclsid, riid, ppv);
+ }
+ /* everything except "Apartment", "Free" and "Both" */
+ else if (model != ThreadingModel_Both)
+ {
+ apartment_threaded = TRUE;
+ /* everything else is main-threaded */
+ if (model != ThreadingModel_No)
+ FIXME("unrecognised threading model %d for object %s, should be main-threaded?\n", model, debugstr_guid(rclsid));
+
+ if (apt->multi_threaded || !apt->main)
+ return apartment_hostobject_in_hostapt(apt, FALSE, TRUE, regdata, rclsid, riid, ppv);
+ }
+ else
+ apartment_threaded = FALSE;
+ }
+ else
+ apartment_threaded = !apt->multi_threaded;
+
+ if (!get_object_dll_path(regdata, dllpath, ARRAY_SIZE(dllpath)))
+ {
+ /* failure: CLSID is not found in registry */
+ WARN("class %s not registered inproc\n", debugstr_guid(rclsid));
+ return REGDB_E_CLASSNOTREG;
+ }
+
+ return apartment_getclassobject(apt, dllpath, apartment_threaded, rclsid, riid, ppv);
+}
+
+static HRESULT apartment_hostobject(struct apartment *apt, const struct host_object_params *params)
+{
+ static const LARGE_INTEGER llZero;
+ WCHAR dllpath[MAX_PATH+1];
+ IUnknown *object;
+ HRESULT hr;
+
+ TRACE("clsid %s, iid %s\n", debugstr_guid(¶ms->clsid), debugstr_guid(¶ms->iid));
+
+ if (!get_object_dll_path(¶ms->regdata, dllpath, ARRAY_SIZE(dllpath)))
+ {
+ /* failure: CLSID is not found in registry */
+ WARN("class %s not registered inproc\n", debugstr_guid(¶ms->clsid));
+ return REGDB_E_CLASSNOTREG;
+ }
+
+ hr = apartment_getclassobject(apt, dllpath, params->apartment_threaded, ¶ms->clsid, ¶ms->iid, (void **)&object);
+ if (FAILED(hr))
+ return hr;
+
+ hr = CoMarshalInterface(params->stream, ¶ms->iid, object, MSHCTX_INPROC, NULL, MSHLFLAGS_NORMAL);
+ if (FAILED(hr))
+ IUnknown_Release(object);
+ IStream_Seek(params->stream, llZero, STREAM_SEEK_SET, NULL);
+
+ return hr;
+}
+
+static LRESULT CALLBACK apartment_wndproc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg)
+ {
+ case DM_EXECUTERPC:
+ RPC_ExecuteCall((struct dispatch_params *)lParam);
+ return 0;
+ case DM_HOSTOBJECT:
+ return apartment_hostobject(COM_CurrentApt(), (const struct host_object_params *)lParam);
+ default:
+ return DefWindowProcW(hWnd, msg, wParam, lParam);
+ }
+}
+
+static BOOL apartment_is_model(const struct apartment *apt, DWORD model)
+{
+ return (apt->multi_threaded == !(model & COINIT_APARTMENTTHREADED));
+}
+
+HRESULT enter_apartment(struct oletls *info, DWORD model)
+{
+ HRESULT hr = S_OK;
+
+ if (!info->apt)
+ {
+ if (!apartment_get_or_create(model))
+ return E_OUTOFMEMORY;
+ }
+ else if (!apartment_is_model(info->apt, model))
+ {
+ WARN( "Attempt to change threading model of this apartment from %s to %s\n",
+ info->apt->multi_threaded ? "multi-threaded" : "apartment threaded",
+ model & COINIT_APARTMENTTHREADED ? "apartment threaded" : "multi-threaded" );
+ return RPC_E_CHANGED_MODE;
+ }
+ else
+ hr = S_FALSE;
+
+ info->inits++;
+
+ return hr;
+}
+
+void leave_apartment(struct oletls *info)
+{
+ if (!--info->inits)
+ {
+ if (info->ole_inits)
+ WARN( "Uninitializing apartment while Ole is still initialized\n" );
+ apartment_release(info->apt);
+ info->apt = NULL;
+ }
+}
+
+struct mta_cookie
+{
+ struct list entry;
+};
+
+HRESULT apartment_increment_mta_usage(CO_MTA_USAGE_COOKIE *cookie)
+{
+ struct mta_cookie *mta_cookie;
+
+ *cookie = NULL;
+
+ if (!(mta_cookie = heap_alloc(sizeof(*mta_cookie))))
+ return E_OUTOFMEMORY;
+
+ EnterCriticalSection(&apt_cs);
+
+ if (mta)
+ apartment_addref(mta);
+ else
+ mta = apartment_construct(COINIT_MULTITHREADED);
+ list_add_head(&mta->usage_cookies, &mta_cookie->entry);
+
+ LeaveCriticalSection(&apt_cs);
+
+ *cookie = (CO_MTA_USAGE_COOKIE)mta_cookie;
+
+ return S_OK;
+}
+
+void apartment_decrement_mta_usage(CO_MTA_USAGE_COOKIE cookie)
+{
+ struct mta_cookie *mta_cookie = (struct mta_cookie *)cookie;
+
+ EnterCriticalSection(&apt_cs);
+
+ if (mta)
+ {
+ struct mta_cookie *cur;
+
+ LIST_FOR_EACH_ENTRY(cur, &mta->usage_cookies, struct mta_cookie, entry)
+ {
+ if (mta_cookie == cur)
+ {
+ list_remove(&cur->entry);
+ heap_free(cur);
+ apartment_release(mta);
+ break;
+ }
+ }
+ }
+
+ LeaveCriticalSection(&apt_cs);
+}
+
+static const WCHAR aptwinclassW[] = L"OleMainThreadWndClass";
+static ATOM apt_win_class;
+
+static BOOL WINAPI register_class( INIT_ONCE *once, void *param, void **context )
+{
+ WNDCLASSW wclass;
+
+ /* 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 = aptwinclassW;
+ apt_win_class = RegisterClassW(&wclass);
+ return TRUE;
+}
+
+/* create a window for the apartment or return the current one if one has
+ * already been created */
+HRESULT apartment_createwindowifneeded(struct apartment *apt)
+{
+ static INIT_ONCE class_init_once = INIT_ONCE_STATIC_INIT;
+
+ if (apt->multi_threaded)
+ return S_OK;
+
+ if (!apt->win)
+ {
+ HWND hwnd;
+
+ InitOnceExecuteOnce( &class_init_once, register_class, NULL, NULL );
+
+ hwnd = CreateWindowW(aptwinclassW, NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, 0, hProxyDll, NULL);
+ if (!hwnd)
+ {
+ ERR("CreateWindow failed with error %d\n", GetLastError());
+ return HRESULT_FROM_WIN32(GetLastError());
+ }
+ if (InterlockedCompareExchangePointer((void **)&apt->win, hwnd, NULL))
+ /* someone beat us to it */
+ DestroyWindow(hwnd);
+ }
+
+ return S_OK;
+}
+
+/* retrieves the window for the main- or apartment-threaded apartment */
+HWND apartment_getwindow(const struct apartment *apt)
+{
+ assert(!apt->multi_threaded);
+ return apt->win;
+}
+
+void apartment_global_cleanup(void)
+{
+ if (apt_win_class)
+ UnregisterClassW((const WCHAR *)MAKEINTATOM(apt_win_class), hProxyDll);
+ apartment_release_dlls();
+ DeleteCriticalSection(&apt_cs);
+}
diff --git a/dlls/ole32/compobj.c b/dlls/ole32/compobj.c
index a68cab0fe98..d59da4207a7 100644
--- a/dlls/ole32/compobj.c
+++ b/dlls/ole32/compobj.c
@@ -71,28 +71,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(ole);
* This section defines variables internal to the COM module.
*/
-static struct apartment *MTA; /* protected by csApartment */
-static struct apartment *MainApartment; /* the first STA apartment */
-static struct list apts = LIST_INIT( apts ); /* protected by csApartment */
-
-static CRITICAL_SECTION csApartment;
-static CRITICAL_SECTION_DEBUG critsect_debug =
-{
- 0, 0, &csApartment,
- { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
- 0, 0, { (DWORD_PTR)(__FILE__ ": csApartment") }
-};
-static CRITICAL_SECTION csApartment = { &critsect_debug, -1, 0, 0, 0, 0 };
-
-enum comclass_threadingmodel
-{
- ThreadingModel_Apartment = 1,
- ThreadingModel_Free = 2,
- ThreadingModel_No = 3,
- ThreadingModel_Both = 4,
- ThreadingModel_Neutral = 5
-};
-
enum comclass_miscfields
{
MiscStatus = 1,
@@ -164,20 +142,6 @@ struct class_reg_data
} u;
};
-/*
- * This is a marshallable object exposing registered local servers.
- * IServiceProvider is used only because it happens meet requirements
- * and already has proxy/stub code. If more functionality is needed,
- * a custom interface may be used instead.
- */
-struct LocalServer
-{
- IServiceProvider IServiceProvider_iface;
- LONG ref;
- struct apartment *apt;
- IStream *marshal_stream;
-};
-
/*
* This lock count counts the number of times CoInitialize is called. It is
* decreased every time CoUninitialize is called. When it hits 0, the COM
@@ -418,322 +382,6 @@ LSTATUS open_classes_key( HKEY hkey, const WCHAR *name, REGSAM access, HKEY *ret
return RtlNtStatusToDosError( NtOpenKey( (HANDLE *)retkey, access, &attr ) );
}
-/*****************************************************************************
- * This section contains OpenDllList definitions
- *
- * The OpenDllList contains only handles of dll loaded by CoGetClassObject or
- * other functions that do LoadLibrary _without_ giving back a HMODULE.
- * Without this list these handles would never be freed.
- *
- * FIXME: a DLL that says OK when asked for unloading is unloaded in the
- * next unload-call but not before 600 sec.
- */
-
-typedef HRESULT (CALLBACK *DllGetClassObjectFunc)(REFCLSID clsid, REFIID iid, LPVOID *ppv);
-typedef HRESULT (WINAPI *DllCanUnloadNowFunc)(void);
-
-typedef struct tagOpenDll
-{
- LONG refs;
- LPWSTR library_name;
- HANDLE library;
- DllGetClassObjectFunc DllGetClassObject;
- DllCanUnloadNowFunc DllCanUnloadNow;
- struct list entry;
-} OpenDll;
-
-static struct list openDllList = LIST_INIT(openDllList);
-
-static CRITICAL_SECTION csOpenDllList;
-static CRITICAL_SECTION_DEBUG dll_cs_debug =
-{
- 0, 0, &csOpenDllList,
- { &dll_cs_debug.ProcessLocksList, &dll_cs_debug.ProcessLocksList },
- 0, 0, { (DWORD_PTR)(__FILE__ ": csOpenDllList") }
-};
-static CRITICAL_SECTION csOpenDllList = { &dll_cs_debug, -1, 0, 0, 0, 0 };
-
-struct apartment_loaded_dll
-{
- struct list entry;
- OpenDll *dll;
- DWORD unload_time;
- BOOL multi_threaded;
-};
-
-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};
-
-static ATOM apt_win_class;
-
-/*****************************************************************************
- * This section contains OpenDllList implementation
- */
-
-static OpenDll *COMPOBJ_DllList_Get(LPCWSTR library_name)
-{
- OpenDll *ptr;
- OpenDll *ret = NULL;
- EnterCriticalSection(&csOpenDllList);
- LIST_FOR_EACH_ENTRY(ptr, &openDllList, OpenDll, entry)
- {
- if (!wcsicmp(library_name, ptr->library_name) &&
- (InterlockedIncrement(&ptr->refs) != 1) /* entry is being destroy if == 1 */)
- {
- ret = ptr;
- break;
- }
- }
- LeaveCriticalSection(&csOpenDllList);
- return ret;
-}
-
-/* caller must ensure that library_name is not already in the open dll list */
-static HRESULT COMPOBJ_DllList_Add(LPCWSTR library_name, OpenDll **ret)
-{
- OpenDll *entry;
- int len;
- HRESULT hr = S_OK;
- HANDLE hLibrary;
- DllCanUnloadNowFunc DllCanUnloadNow;
- DllGetClassObjectFunc DllGetClassObject;
-
- TRACE("%s\n", debugstr_w(library_name));
-
- *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 = lstrlenW(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);
- *ret = entry;
- }
- else
- {
- HeapFree(GetProcessHeap(), 0, entry);
- hr = E_OUTOFMEMORY;
- FreeLibrary(hLibrary);
- }
- }
-
- LeaveCriticalSection( &csOpenDllList );
-
- return hr;
-}
-
-/* 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)
- {
- 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);
- }
-}
-
-/* frees memory associated with active dll list */
-static void COMPOBJ_DllList_Free(void)
-{
- OpenDll *entry, *cursor2;
- EnterCriticalSection(&csOpenDllList);
- LIST_FOR_EACH_ENTRY_SAFE(entry, cursor2, &openDllList, OpenDll, entry)
- {
- list_remove(&entry->entry);
-
- HeapFree(GetProcessHeap(), 0, entry->library_name);
- HeapFree(GetProcessHeap(), 0, entry);
- }
- LeaveCriticalSection(&csOpenDllList);
- DeleteCriticalSection(&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 struct apartment *apartment_construct(DWORD model)
-{
- struct apartment *apt;
-
- TRACE("creating new apartment, model=%d\n", model);
-
- apt = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*apt));
- apt->tid = GetCurrentThreadId();
-
- list_init(&apt->proxies);
- list_init(&apt->stubmgrs);
- list_init(&apt->loaded_dlls);
- list_init(&apt->usage_cookies);
- apt->ipidc = 0;
- apt->refs = 1;
- apt->remunk_exported = FALSE;
- apt->oidc = 1;
- InitializeCriticalSection(&apt->cs);
- DEBUG_SET_CRITSEC_NAME(&apt->cs, "apartment");
-
- apt->multi_threaded = !(model & COINIT_APARTMENTTHREADED);
-
- if (apt->multi_threaded)
- {
- /* FIXME: should be randomly generated by in an RPC call to rpcss */
- apt->oxid = ((OXID)GetCurrentProcessId() << 32) | 0xcafe;
- }
- else
- {
- /* FIXME: should be randomly generated by in an RPC call to rpcss */
- apt->oxid = ((OXID)GetCurrentProcessId() << 32) | GetCurrentThreadId();
- }
-
- TRACE("Created apartment on OXID %s\n", wine_dbgstr_longlong(apt->oxid));
-
- list_add_head(&apts, &apt->entry);
-
- return apt;
-}
-
-/* gets and existing apartment if one exists or otherwise creates an apartment
- * structure which stores OLE apartment-local information and stores a pointer
- * to it in the thread-local storage */
-static struct apartment *apartment_get_or_create(DWORD model)
-{
- struct apartment *apt = COM_CurrentApt();
-
- if (!apt)
- {
- if (model & COINIT_APARTMENTTHREADED)
- {
- EnterCriticalSection(&csApartment);
-
- apt = apartment_construct(model);
- if (!MainApartment)
- {
- MainApartment = apt;
- apt->main = TRUE;
- TRACE("Created main-threaded apartment with OXID %s\n", wine_dbgstr_longlong(apt->oxid));
- }
-
- LeaveCriticalSection(&csApartment);
-
- if (apt->main)
- apartment_createwindowifneeded(apt);
- }
- else
- {
- EnterCriticalSection(&csApartment);
-
- /* The multi-threaded apartment (MTA) contains zero or more threads interacting
- * with free threaded (ie thread safe) COM objects. There is only ever one MTA
- * in a process */
- if (MTA)
- {
- TRACE("entering the multithreaded apartment %s\n", wine_dbgstr_longlong(MTA->oxid));
- apartment_addref(MTA);
- }
- else
- MTA = apartment_construct(model);
-
- apt = MTA;
-
- LeaveCriticalSection(&csApartment);
- }
- COM_CurrentInfo()->apt = apt;
- }
-
- return apt;
-}
-
-static inline BOOL apartment_is_model(const struct apartment *apt, DWORD model)
-{
- return (apt->multi_threaded == !(model & COINIT_APARTMENTTHREADED));
-}
-
-/* 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 struct apartment *apartment_find_mta(void)
-{
- struct apartment *apt;
-
- EnterCriticalSection(&csApartment);
-
- if ((apt = MTA))
- apartment_addref(apt);
-
- LeaveCriticalSection(&csApartment);
-
- return apt;
-}
-
-/* Return the current apartment if it exists, or, failing that, the MTA. Caller
- * must free the returned apartment in either case. */
-struct apartment *apartment_get_current_or_mta(void)
-{
- struct apartment *apt = COM_CurrentApt();
- if (apt)
- {
- apartment_addref(apt);
- return apt;
- }
- return apartment_find_mta();
-}
-
static void COM_RevokeRegisteredClassObject(RegisteredClass *curClass)
{
list_remove(&curClass->entry);
@@ -745,7 +393,7 @@ static void COM_RevokeRegisteredClassObject(RegisteredClass *curClass)
HeapFree(GetProcessHeap(), 0, curClass);
}
-static void COM_RevokeAllClasses(const struct apartment *apt)
+void COM_RevokeAllClasses(const struct apartment *apt)
{
RegisteredClass *curClass, *cursor;
@@ -851,852 +499,125 @@ static ISynchronizeVtbl vt_ISynchronize = {
ISynchronize_fnWait,
ISynchronize_fnSignal,
ISynchronize_fnReset
-};
-
-static inline MREImpl *impl_from_ISynchronizeHandle(ISynchronizeHandle *iface)
-{
- return CONTAINING_RECORD(iface, MREImpl, ISynchronizeHandle_iface);
-}
-
-static HRESULT WINAPI SynchronizeHandle_QueryInterface(ISynchronizeHandle *iface, REFIID riid, void **ppv)
-{
- MREImpl *This = impl_from_ISynchronizeHandle(iface);
- return ISynchronize_QueryInterface(&This->ISynchronize_iface, riid, ppv);
-}
-
-static ULONG WINAPI SynchronizeHandle_AddRef(ISynchronizeHandle *iface)
-{
- MREImpl *This = impl_from_ISynchronizeHandle(iface);
- return ISynchronize_AddRef(&This->ISynchronize_iface);
-}
-
-static ULONG WINAPI SynchronizeHandle_Release(ISynchronizeHandle *iface)
-{
- MREImpl *This = impl_from_ISynchronizeHandle(iface);
- return ISynchronize_Release(&This->ISynchronize_iface);
-}
-
-static HRESULT WINAPI SynchronizeHandle_GetHandle(ISynchronizeHandle *iface, HANDLE *ph)
-{
- MREImpl *This = impl_from_ISynchronizeHandle(iface);
-
- *ph = This->event;
- return S_OK;
-}
-
-static const ISynchronizeHandleVtbl SynchronizeHandleVtbl = {
- SynchronizeHandle_QueryInterface,
- SynchronizeHandle_AddRef,
- SynchronizeHandle_Release,
- SynchronizeHandle_GetHandle
-};
-
-HRESULT WINAPI ManualResetEvent_CreateInstance(IClassFactory *iface, IUnknown *outer, REFIID iid, void **ppv)
-{
- MREImpl *This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MREImpl));
- HRESULT hr;
-
- if (outer)
- FIXME("Aggregation not implemented.\n");
-
- This->ref = 1;
- This->ISynchronize_iface.lpVtbl = &vt_ISynchronize;
- This->ISynchronizeHandle_iface.lpVtbl = &SynchronizeHandleVtbl;
- This->event = CreateEventW(NULL, TRUE, FALSE, NULL);
-
- hr = ISynchronize_QueryInterface(&This->ISynchronize_iface, iid, ppv);
- ISynchronize_Release(&This->ISynchronize_iface);
- return hr;
-}
-
-static inline LocalServer *impl_from_IServiceProvider(IServiceProvider *iface)
-{
- return CONTAINING_RECORD(iface, LocalServer, IServiceProvider_iface);
-}
-
-static HRESULT WINAPI LocalServer_QueryInterface(IServiceProvider *iface, REFIID riid, void **ppv)
-{
- LocalServer *This = impl_from_IServiceProvider(iface);
-
- TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
-
- if(IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_IServiceProvider)) {
- *ppv = &This->IServiceProvider_iface;
- }else {
- *ppv = NULL;
- return E_NOINTERFACE;
- }
-
- IUnknown_AddRef((IUnknown*)*ppv);
- return S_OK;
-}
-
-static ULONG WINAPI LocalServer_AddRef(IServiceProvider *iface)
-{
- LocalServer *This = impl_from_IServiceProvider(iface);
- LONG ref = InterlockedIncrement(&This->ref);
-
- TRACE("(%p) ref=%d\n", This, ref);
-
- return ref;
-}
-
-static ULONG WINAPI LocalServer_Release(IServiceProvider *iface)
-{
- LocalServer *This = impl_from_IServiceProvider(iface);
- LONG ref = InterlockedDecrement(&This->ref);
-
- TRACE("(%p) ref=%d\n", This, ref);
-
- if(!ref) {
- assert(!This->apt);
- HeapFree(GetProcessHeap(), 0, This);
- }
-
- return ref;
-}
-
-static HRESULT WINAPI LocalServer_QueryService(IServiceProvider *iface, REFGUID guid, REFIID riid, void **ppv)
-{
- LocalServer *This = impl_from_IServiceProvider(iface);
- struct apartment *apt = COM_CurrentApt();
- RegisteredClass *iter;
- HRESULT hres = E_FAIL;
-
- TRACE("(%p)->(%s %s %p)\n", This, debugstr_guid(guid), debugstr_guid(riid), ppv);
-
- if(!This->apt)
- return E_UNEXPECTED;
-
- EnterCriticalSection(&csRegisteredClassList);
-
- LIST_FOR_EACH_ENTRY(iter, &RegisteredClassList, RegisteredClass, entry) {
- if(iter->apartment_id == apt->oxid
- && (iter->runContext & CLSCTX_LOCAL_SERVER)
- && IsEqualGUID(&iter->classIdentifier, guid)) {
- hres = IUnknown_QueryInterface(iter->classObject, riid, ppv);
- break;
- }
- }
-
- LeaveCriticalSection( &csRegisteredClassList );
-
- return hres;
-}
-
-static const IServiceProviderVtbl LocalServerVtbl = {
- LocalServer_QueryInterface,
- LocalServer_AddRef,
- LocalServer_Release,
- LocalServer_QueryService
-};
-
-static HRESULT get_local_server_stream(struct apartment *apt, IStream **ret)
-{
- HRESULT hres = S_OK;
-
- EnterCriticalSection(&apt->cs);
-
- if(!apt->local_server) {
- LocalServer *obj;
-
- obj = heap_alloc(sizeof(*obj));
- if(obj) {
- obj->IServiceProvider_iface.lpVtbl = &LocalServerVtbl;
- obj->ref = 1;
- obj->apt = apt;
-
- hres = CreateStreamOnHGlobal(0, TRUE, &obj->marshal_stream);
- if(SUCCEEDED(hres)) {
- hres = CoMarshalInterface(obj->marshal_stream, &IID_IServiceProvider, (IUnknown*)&obj->IServiceProvider_iface,
- MSHCTX_LOCAL, NULL, MSHLFLAGS_TABLESTRONG);
- if(FAILED(hres))
- IStream_Release(obj->marshal_stream);
- }
-
- if(SUCCEEDED(hres))
- apt->local_server = obj;
- else
- heap_free(obj);
- }else {
- hres = E_OUTOFMEMORY;
- }
- }
-
- if(SUCCEEDED(hres))
- hres = IStream_Clone(apt->local_server->marshal_stream, ret);
-
- LeaveCriticalSection(&apt->cs);
-
- if(FAILED(hres))
- ERR("Failed: %08x\n", hres);
- return hres;
-}
-
-/***********************************************************************
- * 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 DECLSPEC_HOTPATCH CoRevokeClassObject(
- DWORD dwRegister)
-{
- HRESULT hr = E_INVALIDARG;
- RegisteredClass *curClass;
- struct apartment *apt;
-
- TRACE("(%08x)\n",dwRegister);
-
- if (!(apt = apartment_get_current_or_mta()))
- {
- 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 );
- apartment_release(apt);
- 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 && ((int)(GetTickCount() - entry->unload_time) > 0)))
- {
- list_remove(&entry->entry);
- COMPOBJ_DllList_ReleaseRef(entry->dll, TRUE);
- HeapFree(GetProcessHeap(), 0, entry);
- }
- else
- {
- entry->unload_time = GetTickCount() + real_delay;
- if (!entry->unload_time) entry->unload_time = 1;
- }
- }
- 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);
-
- if (apt->being_destroyed)
- {
- LeaveCriticalSection(&csApartment);
- return ret;
- }
-
- /* destruction stuff that needs to happen under csApartment CS */
- if (ret == 0)
- {
- apt->being_destroyed = TRUE;
- 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));
-
- if(apt->local_server) {
- LocalServer *local_server = apt->local_server;
- LARGE_INTEGER zero;
-
- memset(&zero, 0, sizeof(zero));
- IStream_Seek(local_server->marshal_stream, zero, STREAM_SEEK_SET, NULL);
- CoReleaseMarshalData(local_server->marshal_stream);
- IStream_Release(local_server->marshal_stream);
- local_server->marshal_stream = NULL;
-
- apt->local_server = NULL;
- local_server->apt = NULL;
- IServiceProvider_Release(&local_server->IServiceProvider_iface);
- }
-
- /* 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);
- }
-
- /* 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) IMessageFilter_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 */
-struct apartment *apartment_findfromoxid(OXID oxid)
-{
- struct apartment *result = NULL;
- struct list *cursor;
-
- EnterCriticalSection(&csApartment);
- LIST_FOR_EACH( cursor, &apts )
- {
- struct apartment *apt = LIST_ENTRY( cursor, struct apartment, entry );
- if (apt->oxid == oxid)
- {
- result = apt;
- apartment_addref(result);
- break;
- }
- }
- LeaveCriticalSection(&csApartment);
-
- return result;
-}
-
-/* gets the apartment which has a given creator thread ID. The caller must
- * release the reference from the apartment as soon as the apartment pointer
- * is no longer required. */
-struct apartment *apartment_findfromtid(DWORD tid)
-{
- struct apartment *result = NULL;
- struct list *cursor;
-
- EnterCriticalSection(&csApartment);
- LIST_FOR_EACH( cursor, &apts )
- {
- struct apartment *apt = LIST_ENTRY( cursor, struct apartment, entry );
- if (apt->tid == tid)
- {
- result = apt;
- apartment_addref(result);
- break;
- }
- }
- 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 struct apartment *apartment_findmain(void)
-{
- struct apartment *result;
-
- EnterCriticalSection(&csApartment);
-
- result = MainApartment;
- if (result) apartment_addref(result);
-
- 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 (!wcsicmp(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 for dll %s\n", hr, debugstr_w(dllpath));
-
- return hr;
- }
-
- EnterCriticalSection(&apt->cs);
-
- LIST_FOR_EACH_ENTRY(apartment_loaded_dll, &apt->loaded_dlls, struct apartment_loaded_dll, entry)
- if (!wcsicmp(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 for dll %s\n", hr, debugstr_w(dllpath));
- }
-
- return hr;
-}
-
-/* Returns expanded dll path from the registry or activation context. */
-static BOOL get_object_dll_path(const struct class_reg_data *regdata, WCHAR *dst, DWORD dstlen)
-{
- DWORD ret;
-
- if (regdata->origin == CLASS_REG_REGISTRY)
- {
- DWORD keytype;
- WCHAR src[MAX_PATH];
- DWORD dwLength = dstlen * sizeof(WCHAR);
-
- if( (ret = RegQueryValueExW(regdata->u.hkey, NULL, NULL, &keytype, (BYTE*)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 = wcschr(src, '\"');
- if (quote_start) {
- const WCHAR *quote_end = wcschr(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);
- }
- }
- return !ret;
- }
- else
- {
- static const WCHAR dllW[] = {'.','d','l','l',0};
- ULONG_PTR cookie;
-
- *dst = 0;
- ActivateActCtx(regdata->u.actctx.hactctx, &cookie);
- ret = SearchPathW(NULL, regdata->u.actctx.module_name, dllW, dstlen, dst, NULL);
- DeactivateActCtx(0, cookie);
- return *dst != 0;
- }
-}
-
-struct host_object_params
-{
- struct class_reg_data regdata;
- CLSID clsid; /* clsid of object to marshal */
- IID iid; /* interface to marshal */
- HANDLE event; /* event signalling when ready for multi-threaded case */
- HRESULT hr; /* result for multi-threaded case */
- IStream *stream; /* stream that the object will be marshaled into */
- BOOL apartment_threaded; /* is the component purely apartment-threaded? */
-};
-
-static HRESULT apartment_hostobject(struct apartment *apt,
- const struct host_object_params *params)
-{
- IUnknown *object;
- HRESULT hr;
- static const LARGE_INTEGER llZero;
- WCHAR dllpath[MAX_PATH+1];
-
- TRACE("clsid %s, iid %s\n", debugstr_guid(¶ms->clsid), debugstr_guid(¶ms->iid));
-
- if (!get_object_dll_path(¶ms->regdata, dllpath, ARRAY_SIZE(dllpath)))
- {
- /* failure: CLSID is not found in registry */
- WARN("class %s not registered inproc\n", debugstr_guid(¶ms->clsid));
- return REGDB_E_CLASSNOTREG;
- }
-
- hr = apartment_getclassobject(apt, dllpath, params->apartment_threaded,
- ¶ms->clsid, ¶ms->iid, (void **)&object);
- if (FAILED(hr))
- return hr;
-
- hr = CoMarshalInterface(params->stream, ¶ms->iid, object, MSHCTX_INPROC, NULL, MSHLFLAGS_NORMAL);
- if (FAILED(hr))
- IUnknown_Release(object);
- IStream_Seek(params->stream, llZero, STREAM_SEEK_SET, NULL);
-
- return hr;
-}
-
-static LRESULT CALLBACK apartment_wndproc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
-{
- switch (msg)
- {
- case DM_EXECUTERPC:
- RPC_ExecuteCall((struct dispatch_params *)lParam);
- return 0;
- case DM_HOSTOBJECT:
- return apartment_hostobject(COM_CurrentApt(), (const struct host_object_params *)lParam);
- default:
- return DefWindowProcW(hWnd, msg, wParam, lParam);
- }
-}
-
-struct host_thread_params
-{
- COINIT threading_model;
- HANDLE ready_event;
- 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;
- MSG msg;
- HRESULT hr;
- struct apartment *apt;
-
- TRACE("\n");
-
- hr = CoInitializeEx(NULL, params->threading_model);
- if (FAILED(hr)) return hr;
-
- apt = COM_CurrentApt();
- if (params->threading_model == COINIT_APARTMENTTHREADED)
- {
- apartment_createwindowifneeded(apt);
- params->apartment_hwnd = apartment_getwindow(apt);
- }
- else
- params->apartment_hwnd = NULL;
-
- /* force the message queue to be created before signaling parent thread */
- PeekMessageW(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
-
- SetEvent(params->ready_event);
- params = NULL; /* can't touch params after here as it may be invalid */
-
- while (GetMessageW(&msg, NULL, 0, 0))
- {
- if (!msg.hwnd && (msg.message == DM_HOSTOBJECT))
- {
- 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
- {
- TranslateMessage(&msg);
- DispatchMessageW(&msg);
- }
- }
+};
- TRACE("exiting\n");
+static inline MREImpl *impl_from_ISynchronizeHandle(ISynchronizeHandle *iface)
+{
+ return CONTAINING_RECORD(iface, MREImpl, ISynchronizeHandle_iface);
+}
- CoUninitialize();
+static HRESULT WINAPI SynchronizeHandle_QueryInterface(ISynchronizeHandle *iface, REFIID riid, void **ppv)
+{
+ MREImpl *This = impl_from_ISynchronizeHandle(iface);
+ return ISynchronize_QueryInterface(&This->ISynchronize_iface, riid, ppv);
+}
- return S_OK;
+static ULONG WINAPI SynchronizeHandle_AddRef(ISynchronizeHandle *iface)
+{
+ MREImpl *This = impl_from_ISynchronizeHandle(iface);
+ return ISynchronize_AddRef(&This->ISynchronize_iface);
}
-/* 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,
- const struct class_reg_data *regdata, REFCLSID rclsid, REFIID riid, void **ppv)
+static ULONG WINAPI SynchronizeHandle_Release(ISynchronizeHandle *iface)
{
- struct host_object_params params;
- HWND apartment_hwnd = NULL;
- DWORD apartment_tid = 0;
- HRESULT hr;
+ MREImpl *This = impl_from_ISynchronizeHandle(iface);
+ return ISynchronize_Release(&This->ISynchronize_iface);
+}
- if (!multi_threaded && main_apartment)
- {
- struct apartment *host_apt = apartment_findmain();
- if (host_apt)
- {
- apartment_hwnd = apartment_getwindow(host_apt);
- apartment_release(host_apt);
- }
- }
+static HRESULT WINAPI SynchronizeHandle_GetHandle(ISynchronizeHandle *iface, HANDLE *ph)
+{
+ MREImpl *This = impl_from_ISynchronizeHandle(iface);
- if (!apartment_hwnd)
- {
- EnterCriticalSection(&apt->cs);
+ *ph = This->event;
+ return S_OK;
+}
- if (!apt->host_apt_tid)
- {
- struct host_thread_params thread_params;
- HANDLE handles[2];
- DWORD wait_value;
-
- thread_params.threading_model = multi_threaded ? COINIT_MULTITHREADED : COINIT_APARTMENTTHREADED;
- handles[0] = thread_params.ready_event = CreateEventW(NULL, FALSE, FALSE, NULL);
- thread_params.apartment_hwnd = NULL;
- handles[1] = CreateThread(NULL, 0, apartment_hostobject_thread, &thread_params, 0, &apt->host_apt_tid);
- if (!handles[1])
- {
- CloseHandle(handles[0]);
- LeaveCriticalSection(&apt->cs);
- return E_OUTOFMEMORY;
- }
- wait_value = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
- CloseHandle(handles[0]);
- CloseHandle(handles[1]);
- if (wait_value == WAIT_OBJECT_0)
- apt->host_apt_hwnd = thread_params.apartment_hwnd;
- else
- {
- LeaveCriticalSection(&apt->cs);
- return E_OUTOFMEMORY;
- }
- }
+static const ISynchronizeHandleVtbl SynchronizeHandleVtbl = {
+ SynchronizeHandle_QueryInterface,
+ SynchronizeHandle_AddRef,
+ SynchronizeHandle_Release,
+ SynchronizeHandle_GetHandle
+};
- if (multi_threaded || !main_apartment)
- {
- apartment_hwnd = apt->host_apt_hwnd;
- apartment_tid = apt->host_apt_tid;
- }
+HRESULT WINAPI ManualResetEvent_CreateInstance(IClassFactory *iface, IUnknown *outer, REFIID iid, void **ppv)
+{
+ MREImpl *This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MREImpl));
+ HRESULT hr;
- LeaveCriticalSection(&apt->cs);
- }
+ if (outer)
+ FIXME("Aggregation not implemented.\n");
- /* another thread may have become the main apartment in the time it took
- * us to create the thread for the host apartment */
- if (!apartment_hwnd && !multi_threaded && main_apartment)
- {
- struct apartment *host_apt = apartment_findmain();
- if (host_apt)
- {
- apartment_hwnd = apartment_getwindow(host_apt);
- apartment_release(host_apt);
- }
- }
+ This->ref = 1;
+ This->ISynchronize_iface.lpVtbl = &vt_ISynchronize;
+ This->ISynchronizeHandle_iface.lpVtbl = &SynchronizeHandleVtbl;
+ This->event = CreateEventW(NULL, TRUE, FALSE, NULL);
- params.regdata = *regdata;
- params.clsid = *rclsid;
- params.iid = *riid;
- hr = CreateStreamOnHGlobal(NULL, TRUE, ¶ms.stream);
- if (FAILED(hr))
- return hr;
- params.apartment_threaded = !multi_threaded;
- if (multi_threaded)
- {
- params.hr = S_OK;
- params.event = CreateEventW(NULL, FALSE, FALSE, NULL);
- if (!PostThreadMessageW(apartment_tid, DM_HOSTOBJECT, 0, (LPARAM)¶ms))
- hr = E_OUTOFMEMORY;
- else
- {
- WaitForSingleObject(params.event, INFINITE);
- hr = params.hr;
- }
- CloseHandle(params.event);
- }
- else
- {
- if (!apartment_hwnd)
- {
- ERR("host apartment didn't create window\n");
- hr = E_OUTOFMEMORY;
- }
- else
- hr = SendMessageW(apartment_hwnd, DM_HOSTOBJECT, 0, (LPARAM)¶ms);
- }
- if (SUCCEEDED(hr))
- hr = CoUnmarshalInterface(params.stream, riid, ppv);
- IStream_Release(params.stream);
+ hr = ISynchronize_QueryInterface(&This->ISynchronize_iface, iid, ppv);
+ ISynchronize_Release(&This->ISynchronize_iface);
return hr;
}
-static BOOL WINAPI register_class( INIT_ONCE *once, void *param, void **context )
+/***********************************************************************
+ * 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 DECLSPEC_HOTPATCH CoRevokeClassObject(
+ DWORD dwRegister)
{
- WNDCLASSW wclass;
+ HRESULT hr = E_INVALIDARG;
+ RegisteredClass *curClass;
+ struct apartment *apt;
- /* 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;
- apt_win_class = RegisterClassW(&wclass);
- return TRUE;
-}
+ TRACE("(%08x)\n",dwRegister);
-/* create a window for the apartment or return the current one if one has
- * already been created */
-HRESULT apartment_createwindowifneeded(struct apartment *apt)
-{
- static INIT_ONCE class_init_once = INIT_ONCE_STATIC_INIT;
+ if (!(apt = apartment_get_current_or_mta()))
+ {
+ ERR("COM was not initialized\n");
+ return CO_E_NOTINITIALIZED;
+ }
- if (apt->multi_threaded)
- return S_OK;
+ EnterCriticalSection( &csRegisteredClassList );
- if (!apt->win)
+ LIST_FOR_EACH_ENTRY(curClass, &RegisteredClassList, RegisteredClass, entry)
+ {
+ /*
+ * Check if we have a match on the cookie.
+ */
+ if (curClass->dwCookie == dwRegister)
{
- HWND hwnd;
-
- InitOnceExecuteOnce( &class_init_once, register_class, NULL, NULL );
-
- hwnd = CreateWindowW(wszAptWinClass, NULL, 0, 0, 0, 0, 0,
- HWND_MESSAGE, 0, hProxyDll, NULL);
- if (!hwnd)
- {
- ERR("CreateWindow failed with error %d\n", GetLastError());
- return HRESULT_FROM_WIN32(GetLastError());
- }
- if (InterlockedCompareExchangePointer((PVOID *)&apt->win, hwnd, NULL))
- /* someone beat us to it */
- DestroyWindow(hwnd);
+ 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;
}
+ }
- return S_OK;
-}
-
-/* retrieves the window for the main- or apartment-threaded apartment */
-HWND apartment_getwindow(const struct apartment *apt)
-{
- assert(!apt->multi_threaded);
- return apt->win;
+ LeaveCriticalSection( &csRegisteredClassList );
+ apartment_release(apt);
+ return hr;
}
static void COM_TlsDestroy(void)
@@ -1763,41 +684,6 @@ static void unlock_init_spies(struct oletls *info)
}
}
-HRESULT enter_apartment( struct oletls *info, DWORD model )
-{
- HRESULT hr = S_OK;
-
- if (!info->apt)
- {
- if (!apartment_get_or_create( model ))
- return E_OUTOFMEMORY;
- }
- else if (!apartment_is_model( info->apt, model ))
- {
- WARN( "Attempt to change threading model of this apartment from %s to %s\n",
- info->apt->multi_threaded ? "multi-threaded" : "apartment threaded",
- model & COINIT_APARTMENTTHREADED ? "apartment threaded" : "multi-threaded" );
- return RPC_E_CHANGED_MODE;
- }
- else
- hr = S_FALSE;
-
- info->inits++;
-
- return hr;
-}
-
-void leave_apartment( struct oletls *info )
-{
- if (!--info->inits)
- {
- if (info->ole_inits)
- WARN( "Uninitializing apartment while Ole is still initialized\n" );
- apartment_release( info->apt );
- info->apt = NULL;
- }
-}
-
/******************************************************************************
* CoInitialize [OLE32.@]
*
@@ -2123,7 +1009,7 @@ HRESULT COM_OpenKeyForAppIdFromCLSID(REFCLSID clsid, REGSAM access, HKEY *subkey
* to normal COM usage, this method will increase the
* reference count on this object.
*/
-static HRESULT COM_GetRegisteredClassObject(const struct apartment *apt, REFCLSID rclsid,
+HRESULT COM_GetRegisteredClassObject(const struct apartment *apt, REFCLSID rclsid,
DWORD dwClsContext, LPUNKNOWN* ppUnk)
{
HRESULT hr = S_FALSE;
@@ -2271,7 +1157,7 @@ HRESULT WINAPI CoRegisterClassObject(
if (dwClsContext & CLSCTX_LOCAL_SERVER) {
IStream *marshal_stream;
- hr = get_local_server_stream(apt, &marshal_stream);
+ hr = apartment_get_local_server_stream(apt, &marshal_stream);
if(FAILED(hr))
{
apartment_release(apt);
@@ -2288,86 +1174,6 @@ HRESULT WINAPI CoRegisterClassObject(
return S_OK;
}
-static enum comclass_threadingmodel get_threading_model(const struct class_reg_data *data)
-{
- if (data->origin == CLASS_REG_REGISTRY)
- {
- static const WCHAR wszThreadingModel[] = {'T','h','r','e','a','d','i','n','g','M','o','d','e','l',0};
- 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 /* lstrlenW(L"apartment")+1 */];
- DWORD dwLength = sizeof(threading_model);
- DWORD keytype;
- DWORD ret;
-
- ret = RegQueryValueExW(data->u.hkey, wszThreadingModel, NULL, &keytype, (BYTE*)threading_model, &dwLength);
- if ((ret != ERROR_SUCCESS) || (keytype != REG_SZ))
- threading_model[0] = '\0';
-
- if (!wcsicmp(threading_model, wszApartment)) return ThreadingModel_Apartment;
- if (!wcsicmp(threading_model, wszFree)) return ThreadingModel_Free;
- if (!wcsicmp(threading_model, wszBoth)) return ThreadingModel_Both;
-
- /* there's not specific handling for this case */
- if (threading_model[0]) return ThreadingModel_Neutral;
- return ThreadingModel_No;
- }
- else
- return data->u.actctx.threading_model;
-}
-
-static HRESULT get_inproc_class_object(struct apartment *apt, const struct class_reg_data *regdata,
- REFCLSID rclsid, REFIID riid,
- BOOL hostifnecessary, void **ppv)
-{
- WCHAR dllpath[MAX_PATH+1];
- BOOL apartment_threaded;
-
- if (hostifnecessary)
- {
- enum comclass_threadingmodel model = get_threading_model(regdata);
-
- if (model == ThreadingModel_Apartment)
- {
- apartment_threaded = TRUE;
- if (apt->multi_threaded)
- return apartment_hostobject_in_hostapt(apt, FALSE, FALSE, regdata, rclsid, riid, ppv);
- }
- else if (model == ThreadingModel_Free)
- {
- apartment_threaded = FALSE;
- if (!apt->multi_threaded)
- return apartment_hostobject_in_hostapt(apt, TRUE, FALSE, regdata, rclsid, riid, ppv);
- }
- /* everything except "Apartment", "Free" and "Both" */
- else if (model != ThreadingModel_Both)
- {
- apartment_threaded = TRUE;
- /* everything else is main-threaded */
- if (model != ThreadingModel_No)
- FIXME("unrecognised threading model %d for object %s, should be main-threaded?\n", model, debugstr_guid(rclsid));
-
- if (apt->multi_threaded || !apt->main)
- return apartment_hostobject_in_hostapt(apt, FALSE, TRUE, regdata, rclsid, riid, ppv);
- }
- else
- apartment_threaded = FALSE;
- }
- else
- apartment_threaded = !apt->multi_threaded;
-
- if (!get_object_dll_path(regdata, dllpath, ARRAY_SIZE(dllpath)))
- {
- /* failure: CLSID is not found in registry */
- WARN("class %s not registered inproc\n", debugstr_guid(rclsid));
- return REGDB_E_CLASSNOTREG;
- }
-
- return apartment_getclassobject(apt, dllpath, apartment_threaded,
- rclsid, riid, ppv);
-}
-
/***********************************************************************
* CoGetClassObject [OLE32.@]
*
@@ -2450,7 +1256,7 @@ HRESULT WINAPI DECLSPEC_HOTPATCH CoGetClassObject(
clsreg.u.actctx.threading_model = comclass->model;
clsreg.origin = CLASS_REG_ACTCTX;
- hres = get_inproc_class_object(apt, &clsreg, &comclass->clsid, iid, !(dwClsContext & WINE_CLSCTX_DONT_HOST), ppv);
+ hres = apartment_get_inproc_class_object(apt, &clsreg, &comclass->clsid, iid, !(dwClsContext & WINE_CLSCTX_DONT_HOST), ppv);
ReleaseActCtx(data.hActCtx);
apartment_release(apt);
return hres;
@@ -2500,7 +1306,7 @@ HRESULT WINAPI DECLSPEC_HOTPATCH CoGetClassObject(
clsreg.u.hkey = hkey;
clsreg.origin = CLASS_REG_REGISTRY;
- hres = get_inproc_class_object(apt, &clsreg, rclsid, iid, !(dwClsContext & WINE_CLSCTX_DONT_HOST), ppv);
+ hres = apartment_get_inproc_class_object(apt, &clsreg, rclsid, iid, !(dwClsContext & WINE_CLSCTX_DONT_HOST), ppv);
RegCloseKey(hkey);
}
@@ -2536,7 +1342,7 @@ HRESULT WINAPI DECLSPEC_HOTPATCH CoGetClassObject(
clsreg.u.hkey = hkey;
clsreg.origin = CLASS_REG_REGISTRY;
- hres = get_inproc_class_object(apt, &clsreg, rclsid, iid, !(dwClsContext & WINE_CLSCTX_DONT_HOST), ppv);
+ hres = apartment_get_inproc_class_object(apt, &clsreg, rclsid, iid, !(dwClsContext & WINE_CLSCTX_DONT_HOST), ppv);
RegCloseKey(hkey);
}
@@ -3108,6 +1914,49 @@ HRESULT WINAPI CoRegisterChannelHook(REFGUID guidExtension, IChannelHook *pChann
return RPC_RegisterChannelHook(guidExtension, pChannelHook);
}
+/* Returns expanded dll path from the registry or activation context. */
+static BOOL get_object_dll_path(const struct class_reg_data *regdata, WCHAR *dst, DWORD dstlen)
+{
+ DWORD ret;
+
+ if (regdata->origin == CLASS_REG_REGISTRY)
+ {
+ DWORD keytype;
+ WCHAR src[MAX_PATH];
+ DWORD dwLength = dstlen * sizeof(WCHAR);
+
+ if( (ret = RegQueryValueExW(regdata->u.hkey, NULL, NULL, &keytype, (BYTE*)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 = wcschr(src, '\"');
+ if (quote_start) {
+ const WCHAR *quote_end = wcschr(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);
+ }
+ }
+ return !ret;
+ }
+ else
+ {
+ static const WCHAR dllW[] = {'.','d','l','l',0};
+ ULONG_PTR cookie;
+
+ *dst = 0;
+ ActivateActCtx(regdata->u.actctx.hactctx, &cookie);
+ ret = SearchPathW(NULL, regdata->u.actctx.module_name, dllW, dstlen, dst, NULL);
+ DeactivateActCtx(0, cookie);
+ return *dst != 0;
+ }
+}
+
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};
@@ -3167,7 +2016,7 @@ HRESULT WINAPI CoGetApartmentType(APTTYPE *type, APTTYPEQUALIFIER *qualifier)
*qualifier = APTTYPEQUALIFIER_NONE;
- if (!info->apt && (apt = apartment_find_mta()))
+ if (!info->apt && (apt = apartment_get_mta()))
{
apartment_release(apt);
*type = APTTYPE_MTA;
@@ -3178,38 +2027,14 @@ HRESULT WINAPI CoGetApartmentType(APTTYPE *type, APTTYPEQUALIFIER *qualifier)
return info->apt ? S_OK : CO_E_NOTINITIALIZED;
}
-struct mta_cookie
-{
- struct list entry;
-};
-
/***********************************************************************
* CoIncrementMTAUsage [OLE32.@]
*/
HRESULT WINAPI CoIncrementMTAUsage(CO_MTA_USAGE_COOKIE *cookie)
{
- struct mta_cookie *mta_cookie;
-
TRACE("%p\n", cookie);
- *cookie = NULL;
-
- if (!(mta_cookie = heap_alloc(sizeof(*mta_cookie))))
- return E_OUTOFMEMORY;
-
- EnterCriticalSection(&csApartment);
-
- if (MTA)
- apartment_addref(MTA);
- else
- MTA = apartment_construct(COINIT_MULTITHREADED);
- list_add_head(&MTA->usage_cookies, &mta_cookie->entry);
-
- LeaveCriticalSection(&csApartment);
-
- *cookie = (CO_MTA_USAGE_COOKIE)mta_cookie;
-
- return S_OK;
+ return apartment_increment_mta_usage(cookie);
}
/***********************************************************************
@@ -3217,30 +2042,9 @@ HRESULT WINAPI CoIncrementMTAUsage(CO_MTA_USAGE_COOKIE *cookie)
*/
HRESULT WINAPI CoDecrementMTAUsage(CO_MTA_USAGE_COOKIE cookie)
{
- struct mta_cookie *mta_cookie = (struct mta_cookie *)cookie;
-
TRACE("%p\n", cookie);
- EnterCriticalSection(&csApartment);
-
- if (MTA)
- {
- struct mta_cookie *cur;
-
- LIST_FOR_EACH_ENTRY(cur, &MTA->usage_cookies, struct mta_cookie, entry)
- {
- if (mta_cookie == cur)
- {
- list_remove(&cur->entry);
- heap_free(cur);
- apartment_release(MTA);
- break;
- }
- }
- }
-
- LeaveCriticalSection(&csApartment);
-
+ apartment_decrement_mta_usage(cookie);
return S_OK;
}
@@ -3406,13 +2210,10 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID reserved)
case DLL_PROCESS_DETACH:
if (reserved) break;
release_std_git();
- if(apt_win_class)
- UnregisterClassW( (const WCHAR*)MAKEINTATOM(apt_win_class), hProxyDll );
RPC_UnregisterAllChannelHooks();
- COMPOBJ_DllList_Free();
DeleteCriticalSection(&csRegisteredClassList);
- DeleteCriticalSection(&csApartment);
- break;
+ apartment_global_cleanup();
+ break;
case DLL_THREAD_DETACH:
COM_TlsDestroy();
diff --git a/dlls/ole32/compobj_private.h b/dlls/ole32/compobj_private.h
index 6e8b7ae2cfc..156b6bb2f44 100644
--- a/dlls/ole32/compobj_private.h
+++ b/dlls/ole32/compobj_private.h
@@ -40,7 +40,6 @@
#include "winternl.h"
struct apartment;
-typedef struct LocalServer LocalServer;
DEFINE_OLEGUID( CLSID_DfMarshal, 0x0000030b, 0, 0 );
@@ -140,7 +139,7 @@ struct apartment
struct list loaded_dlls; /* list of dlls loaded by this apartment (CS cs) */
DWORD host_apt_tid; /* thread ID of apartment hosting objects of differing threading model (CS cs) */
HWND host_apt_hwnd; /* handle to apartment window of host apartment (CS cs) */
- LocalServer *local_server; /* A marshallable object exposing local servers (CS cs) */
+ struct local_server *local_server; /* A marshallable object exposing local servers (CS cs) */
BOOL being_destroyed; /* is currently being destroyed */
/* FIXME: OIDs should be given out by RPCSS */
@@ -248,7 +247,7 @@ void OLEDD_UnInitialize(void) DECLSPEC_HIDDEN;
struct apartment *apartment_findfromoxid(OXID oxid) DECLSPEC_HIDDEN;
struct apartment *apartment_findfromtid(DWORD tid) DECLSPEC_HIDDEN;
-DWORD apartment_release(struct apartment *apt) DECLSPEC_HIDDEN;
+void apartment_release(struct apartment *apt) DECLSPEC_HIDDEN;
HRESULT apartment_disconnectproxies(struct apartment *apt) DECLSPEC_HIDDEN;
static inline HRESULT apartment_getoxid(const struct apartment *apt, OXID *oxid)
{
@@ -261,6 +260,21 @@ HRESULT enter_apartment(struct oletls *info, DWORD model) DECLSPEC_HIDDEN;
void leave_apartment(struct oletls *info) DECLSPEC_HIDDEN;
struct apartment *apartment_get_current_or_mta(void) DECLSPEC_HIDDEN;
+struct class_reg_data;
+HRESULT apartment_get_inproc_class_object(struct apartment *apt, const struct class_reg_data *regdata,
+ REFCLSID rclsid, REFIID riid, BOOL hostifnecessary, void **ppv) DECLSPEC_HIDDEN;
+
+void apartment_decrement_mta_usage(CO_MTA_USAGE_COOKIE cookie) DECLSPEC_HIDDEN;
+HRESULT apartment_increment_mta_usage(CO_MTA_USAGE_COOKIE *cookie) DECLSPEC_HIDDEN;
+struct apartment *apartment_get_mta(void) DECLSPEC_HIDDEN;
+HRESULT apartment_get_local_server_stream(struct apartment *apt, IStream **ret) DECLSPEC_HIDDEN;
+void apartment_freeunusedlibraries(struct apartment *apt, DWORD delay) DECLSPEC_HIDDEN;
+void apartment_global_cleanup(void) DECLSPEC_HIDDEN;
+
+HRESULT COM_GetRegisteredClassObject(const struct apartment *apt, REFCLSID rclsid,
+ DWORD dwClsContext, IUnknown **ppUnk) DECLSPEC_HIDDEN;
+void COM_RevokeAllClasses(const struct apartment *apt) DECLSPEC_HIDDEN;
+
/* DCOM messages used by the apartment window (not compatible with native) */
#define DM_EXECUTERPC (WM_USER + 0) /* WPARAM = 0, LPARAM = (struct dispatch_params *) */
#define DM_HOSTOBJECT (WM_USER + 1) /* WPARAM = 0, LPARAM = (struct host_object_params *) */
--
2.28.0
More information about the wine-devel
mailing list