Commit 04e11b58 authored by Nikolay Sivov's avatar Nikolay Sivov Committed by Alexandre Julliard

ole32: Move apartment and activation management to a separate file.

parent 89d8e13b
......@@ -8,6 +8,7 @@ EXTRADLLFLAGS = -mno-cygwin
C_SRCS = \
antimoniker.c \
apartment.c \
bindctx.c \
classmoniker.c \
clipboard.c \
......
/*
* 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, &params.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)&params))
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)&params);
}
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(&params->clsid), debugstr_guid(&params->iid));
if (!get_object_dll_path(&params->regdata, dllpath, ARRAY_SIZE(dllpath)))
{
/* failure: CLSID is not found in registry */
WARN("class %s not registered inproc\n", debugstr_guid(&params->clsid));
return REGDB_E_CLASSNOTREG;
}
hr = apartment_getclassobject(apt, dllpath, params->apartment_threaded, &params->clsid, &params->iid, (void **)&object);
if (FAILED(hr))
return hr;
hr = CoMarshalInterface(params->stream, &params->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);
}
......@@ -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,
......@@ -165,20 +143,6 @@ struct class_reg_data
};
/*
* 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
* libraries are freed
......@@ -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(&params->clsid), debugstr_guid(&params->iid));
if (!get_object_dll_path(&params->regdata, dllpath, ARRAY_SIZE(dllpath)))
{
/* failure: CLSID is not found in registry */
WARN("class %s not registered inproc\n", debugstr_guid(&params->clsid));
return REGDB_E_CLASSNOTREG;
}
hr = apartment_getclassobject(apt, dllpath, params->apartment_threaded,
&params->clsid, &params->iid, (void **)&object);
if (FAILED(hr))
return hr;
hr = CoMarshalInterface(params->stream, &params->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, &params.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)&params))
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)&params);
}
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();
......
......@@ -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 *) */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment