dinput: Factor out IDirectInputDevice ansi vtable.
[wine.git] / dlls / combase / apartment.c
blob108d6a71c5c5a6577a8a0a6293df3522c00bb605
1 /*
2 * Copyright 1995 Martin von Loewis
3 * Copyright 1998 Justin Bradford
4 * Copyright 1999 Francis Beaudet
5 * Copyright 1999 Sylvain St-Germain
6 * Copyright 2002 Marcus Meissner
7 * Copyright 2004 Mike Hearn
8 * Copyright 2005-2006 Robert Shearman (for CodeWeavers)
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 #include <stdarg.h>
27 #include <assert.h>
29 #define COBJMACROS
30 #define NONAMELESSUNION
32 #include "windef.h"
33 #include "winbase.h"
34 #include "servprov.h"
36 #include "combase_private.h"
38 #include "wine/debug.h"
39 #include "wine/list.h"
41 WINE_DEFAULT_DEBUG_CHANNEL(ole);
43 enum comclass_threadingmodel
45 ThreadingModel_Apartment = 1,
46 ThreadingModel_Free = 2,
47 ThreadingModel_No = 3,
48 ThreadingModel_Both = 4,
49 ThreadingModel_Neutral = 5
52 static struct apartment *mta;
53 static struct apartment *main_sta; /* the first STA */
54 static struct list apts = LIST_INIT(apts);
56 static CRITICAL_SECTION apt_cs;
57 static CRITICAL_SECTION_DEBUG apt_cs_debug =
59 0, 0, &apt_cs,
60 { &apt_cs_debug.ProcessLocksList, &apt_cs_debug.ProcessLocksList },
61 0, 0, { (DWORD_PTR)(__FILE__ ": apt_cs") }
63 static CRITICAL_SECTION apt_cs = { &apt_cs_debug, -1, 0, 0, 0, 0 };
65 static struct list dlls = LIST_INIT(dlls);
67 static CRITICAL_SECTION dlls_cs;
68 static CRITICAL_SECTION_DEBUG dlls_cs_debug =
70 0, 0, &dlls_cs,
71 { &dlls_cs_debug.ProcessLocksList, &dlls_cs_debug.ProcessLocksList },
72 0, 0, { (DWORD_PTR)(__FILE__ ": dlls_cs") }
74 static CRITICAL_SECTION dlls_cs = { &dlls_cs_debug, -1, 0, 0, 0, 0 };
76 typedef HRESULT (WINAPI *DllGetClassObjectFunc)(REFCLSID clsid, REFIID iid, void **obj);
77 typedef HRESULT (WINAPI *DllCanUnloadNowFunc)(void);
79 struct opendll
81 LONG refs;
82 LPWSTR library_name;
83 HANDLE library;
84 DllGetClassObjectFunc DllGetClassObject;
85 DllCanUnloadNowFunc DllCanUnloadNow;
86 struct list entry;
89 struct apartment_loaded_dll
91 struct list entry;
92 struct opendll *dll;
93 DWORD unload_time;
94 BOOL multi_threaded;
97 static struct opendll *apartment_get_dll(const WCHAR *library_name)
99 struct opendll *ptr, *ret = NULL;
101 EnterCriticalSection(&dlls_cs);
102 LIST_FOR_EACH_ENTRY(ptr, &dlls, struct opendll, entry)
104 if (!wcsicmp(library_name, ptr->library_name) &&
105 (InterlockedIncrement(&ptr->refs) != 1) /* entry is being destroyed if == 1 */)
107 ret = ptr;
108 break;
111 LeaveCriticalSection(&dlls_cs);
113 return ret;
116 /* caller must ensure that library_name is not already in the open dll list */
117 static HRESULT apartment_add_dll(const WCHAR *library_name, struct opendll **ret)
119 struct opendll *entry;
120 int len;
121 HRESULT hr = S_OK;
122 HANDLE hLibrary;
123 DllCanUnloadNowFunc DllCanUnloadNow;
124 DllGetClassObjectFunc DllGetClassObject;
126 TRACE("%s\n", debugstr_w(library_name));
128 *ret = apartment_get_dll(library_name);
129 if (*ret) return S_OK;
131 /* Load outside of dlls lock to avoid dependency on the loader lock */
132 hLibrary = LoadLibraryExW(library_name, 0, LOAD_WITH_ALTERED_SEARCH_PATH);
133 if (!hLibrary)
135 ERR("couldn't load in-process dll %s\n", debugstr_w(library_name));
136 /* failure: DLL could not be loaded */
137 return E_ACCESSDENIED; /* FIXME: or should this be CO_E_DLLNOTFOUND? */
140 /* DllCanUnloadNow is optional */
141 DllCanUnloadNow = (void *)GetProcAddress(hLibrary, "DllCanUnloadNow");
142 DllGetClassObject = (void *)GetProcAddress(hLibrary, "DllGetClassObject");
143 if (!DllGetClassObject)
145 /* failure: the dll did not export DllGetClassObject */
146 ERR("couldn't find function DllGetClassObject in %s\n", debugstr_w(library_name));
147 FreeLibrary(hLibrary);
148 return CO_E_DLLNOTFOUND;
151 EnterCriticalSection(&dlls_cs);
153 *ret = apartment_get_dll(library_name);
154 if (*ret)
156 /* another caller to this function already added the dll while we
157 * weren't in the critical section */
158 FreeLibrary(hLibrary);
160 else
162 len = lstrlenW(library_name);
163 entry = heap_alloc(sizeof(*entry));
164 if (entry)
165 entry->library_name = heap_alloc((len + 1) * sizeof(WCHAR));
166 if (entry && entry->library_name)
168 memcpy(entry->library_name, library_name, (len + 1)*sizeof(WCHAR));
169 entry->library = hLibrary;
170 entry->refs = 1;
171 entry->DllCanUnloadNow = DllCanUnloadNow;
172 entry->DllGetClassObject = DllGetClassObject;
173 list_add_tail(&dlls, &entry->entry);
174 *ret = entry;
176 else
178 heap_free(entry);
179 hr = E_OUTOFMEMORY;
180 FreeLibrary(hLibrary);
184 LeaveCriticalSection(&dlls_cs);
186 return hr;
189 /* pass FALSE for free_entry to release a reference without destroying the
190 * entry if it reaches zero or TRUE otherwise */
191 static void apartment_release_dll(struct opendll *entry, BOOL free_entry)
193 if (!InterlockedDecrement(&entry->refs) && free_entry)
195 EnterCriticalSection(&dlls_cs);
196 list_remove(&entry->entry);
197 LeaveCriticalSection(&dlls_cs);
199 TRACE("freeing %p\n", entry->library);
200 FreeLibrary(entry->library);
202 heap_free(entry->library_name);
203 heap_free(entry);
207 /* frees memory associated with active dll list */
208 static void apartment_release_dlls(void)
210 struct opendll *entry, *cursor2;
211 EnterCriticalSection(&dlls_cs);
212 LIST_FOR_EACH_ENTRY_SAFE(entry, cursor2, &dlls, struct opendll, entry)
214 list_remove(&entry->entry);
215 heap_free(entry->library_name);
216 heap_free(entry);
218 LeaveCriticalSection(&dlls_cs);
219 DeleteCriticalSection(&dlls_cs);
223 * This is a marshallable object exposing registered local servers.
224 * IServiceProvider is used only because it happens meet requirements
225 * and already has proxy/stub code. If more functionality is needed,
226 * a custom interface may be used instead.
228 struct local_server
230 IServiceProvider IServiceProvider_iface;
231 LONG refcount;
232 struct apartment *apt;
233 IStream *marshal_stream;
236 static inline struct local_server *impl_from_IServiceProvider(IServiceProvider *iface)
238 return CONTAINING_RECORD(iface, struct local_server, IServiceProvider_iface);
241 static HRESULT WINAPI local_server_QueryInterface(IServiceProvider *iface, REFIID riid, void **obj)
243 struct local_server *local_server = impl_from_IServiceProvider(iface);
245 TRACE("%p, %s, %p\n", iface, debugstr_guid(riid), obj);
247 if (IsEqualGUID(riid, &IID_IUnknown) ||
248 IsEqualGUID(riid, &IID_IServiceProvider))
250 *obj = &local_server->IServiceProvider_iface;
252 else
254 *obj = NULL;
255 return E_NOINTERFACE;
258 IUnknown_AddRef((IUnknown *)*obj);
259 return S_OK;
262 static ULONG WINAPI local_server_AddRef(IServiceProvider *iface)
264 struct local_server *local_server = impl_from_IServiceProvider(iface);
265 LONG refcount = InterlockedIncrement(&local_server->refcount);
267 TRACE("%p, refcount %d\n", iface, refcount);
269 return refcount;
272 static ULONG WINAPI local_server_Release(IServiceProvider *iface)
274 struct local_server *local_server = impl_from_IServiceProvider(iface);
275 LONG refcount = InterlockedDecrement(&local_server->refcount);
277 TRACE("%p, refcount %d\n", iface, refcount);
279 if (!refcount)
281 assert(!local_server->apt);
282 heap_free(local_server);
285 return refcount;
288 static HRESULT WINAPI local_server_QueryService(IServiceProvider *iface, REFGUID guid, REFIID riid, void **obj)
290 struct local_server *local_server = impl_from_IServiceProvider(iface);
291 struct apartment *apt = com_get_current_apt();
292 HRESULT hr = E_FAIL;
293 IUnknown *unk;
295 TRACE("%p, %s, %s, %p\n", iface, debugstr_guid(guid), debugstr_guid(riid), obj);
297 if (!local_server->apt)
298 return E_UNEXPECTED;
300 if ((unk = com_get_registered_class_object(apt, guid, CLSCTX_LOCAL_SERVER)))
302 hr = IUnknown_QueryInterface(unk, riid, obj);
303 IUnknown_Release(unk);
306 return hr;
309 static const IServiceProviderVtbl local_server_vtbl =
311 local_server_QueryInterface,
312 local_server_AddRef,
313 local_server_Release,
314 local_server_QueryService
317 HRESULT apartment_get_local_server_stream(struct apartment *apt, IStream **ret)
319 HRESULT hr = S_OK;
321 EnterCriticalSection(&apt->cs);
323 if (!apt->local_server)
325 struct local_server *obj;
327 obj = heap_alloc(sizeof(*obj));
328 if (obj)
330 obj->IServiceProvider_iface.lpVtbl = &local_server_vtbl;
331 obj->refcount = 1;
332 obj->apt = apt;
334 hr = CreateStreamOnHGlobal(0, TRUE, &obj->marshal_stream);
335 if (SUCCEEDED(hr))
337 hr = CoMarshalInterface(obj->marshal_stream, &IID_IServiceProvider, (IUnknown *)&obj->IServiceProvider_iface,
338 MSHCTX_LOCAL, NULL, MSHLFLAGS_TABLESTRONG);
339 if (FAILED(hr))
340 IStream_Release(obj->marshal_stream);
343 if (SUCCEEDED(hr))
344 apt->local_server = obj;
345 else
346 heap_free(obj);
348 else
349 hr = E_OUTOFMEMORY;
352 if (SUCCEEDED(hr))
353 hr = IStream_Clone(apt->local_server->marshal_stream, ret);
355 LeaveCriticalSection(&apt->cs);
357 if (FAILED(hr))
358 ERR("Failed: %#x\n", hr);
360 return hr;
363 /* Creates new apartment for given model */
364 static struct apartment *apartment_construct(DWORD model)
366 struct apartment *apt;
368 TRACE("creating new apartment, model %d\n", model);
370 apt = heap_alloc_zero(sizeof(*apt));
371 apt->tid = GetCurrentThreadId();
373 list_init(&apt->proxies);
374 list_init(&apt->stubmgrs);
375 list_init(&apt->loaded_dlls);
376 list_init(&apt->usage_cookies);
377 apt->ipidc = 0;
378 apt->refs = 1;
379 apt->remunk_exported = FALSE;
380 apt->oidc = 1;
381 InitializeCriticalSection(&apt->cs);
382 apt->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": apartment");
384 apt->multi_threaded = !(model & COINIT_APARTMENTTHREADED);
386 if (apt->multi_threaded)
388 /* FIXME: should be randomly generated by in an RPC call to rpcss */
389 apt->oxid = ((OXID)GetCurrentProcessId() << 32) | 0xcafe;
391 else
393 /* FIXME: should be randomly generated by in an RPC call to rpcss */
394 apt->oxid = ((OXID)GetCurrentProcessId() << 32) | GetCurrentThreadId();
397 TRACE("Created apartment on OXID %s\n", wine_dbgstr_longlong(apt->oxid));
399 list_add_head(&apts, &apt->entry);
401 return apt;
404 /* Frees unused libraries loaded into apartment */
405 void apartment_freeunusedlibraries(struct apartment *apt, DWORD delay)
407 struct apartment_loaded_dll *entry, *next;
409 EnterCriticalSection(&apt->cs);
410 LIST_FOR_EACH_ENTRY_SAFE(entry, next, &apt->loaded_dlls, struct apartment_loaded_dll, entry)
412 if (entry->dll->DllCanUnloadNow && (entry->dll->DllCanUnloadNow() == S_OK))
414 DWORD real_delay = delay;
416 if (real_delay == INFINITE)
418 /* DLLs that return multi-threaded objects aren't unloaded
419 * straight away to cope for programs that have races between
420 * last object destruction and threads in the DLLs that haven't
421 * finished, despite DllCanUnloadNow returning S_OK */
422 if (entry->multi_threaded)
423 real_delay = 10 * 60 * 1000; /* 10 minutes */
424 else
425 real_delay = 0;
428 if (!real_delay || (entry->unload_time && ((int)(GetTickCount() - entry->unload_time) > 0)))
430 list_remove(&entry->entry);
431 apartment_release_dll(entry->dll, TRUE);
432 heap_free(entry);
434 else
436 entry->unload_time = GetTickCount() + real_delay;
437 if (!entry->unload_time) entry->unload_time = 1;
440 else if (entry->unload_time)
441 entry->unload_time = 0;
443 LeaveCriticalSection(&apt->cs);
446 void apartment_release(struct apartment *apt)
448 DWORD refcount;
450 EnterCriticalSection(&apt_cs);
452 refcount = InterlockedDecrement(&apt->refs);
453 TRACE("%s: after = %d\n", wine_dbgstr_longlong(apt->oxid), refcount);
455 if (apt->being_destroyed)
457 LeaveCriticalSection(&apt_cs);
458 return;
461 /* destruction stuff that needs to happen under global */
462 if (!refcount)
464 apt->being_destroyed = TRUE;
465 if (apt == mta) mta = NULL;
466 else if (apt == main_sta) main_sta = NULL;
467 list_remove(&apt->entry);
470 LeaveCriticalSection(&apt_cs);
472 if (!refcount)
474 struct list *cursor, *cursor2;
476 TRACE("destroying apartment %p, oxid %s\n", apt, wine_dbgstr_longlong(apt->oxid));
478 if (apt->local_server)
480 struct local_server *local_server = apt->local_server;
481 LARGE_INTEGER zero;
483 memset(&zero, 0, sizeof(zero));
484 IStream_Seek(local_server->marshal_stream, zero, STREAM_SEEK_SET, NULL);
485 CoReleaseMarshalData(local_server->marshal_stream);
486 IStream_Release(local_server->marshal_stream);
487 local_server->marshal_stream = NULL;
489 apt->local_server = NULL;
490 local_server->apt = NULL;
491 IServiceProvider_Release(&local_server->IServiceProvider_iface);
494 /* Release the references to the registered class objects */
495 apartment_revoke_all_classes(apt);
497 /* no locking is needed for this apartment, because no other thread
498 * can access it at this point */
500 apartment_disconnectproxies(apt);
502 if (apt->win) DestroyWindow(apt->win);
503 if (apt->host_apt_tid) PostThreadMessageW(apt->host_apt_tid, WM_QUIT, 0, 0);
505 LIST_FOR_EACH_SAFE(cursor, cursor2, &apt->stubmgrs)
507 struct stub_manager *stubmgr = LIST_ENTRY(cursor, struct stub_manager, entry);
508 /* release the implicit reference given by the fact that the
509 * stub has external references (it must do since it is in the
510 * stub manager list in the apartment and all non-apartment users
511 * must have a ref on the apartment and so it cannot be destroyed).
513 stub_manager_int_release(stubmgr);
516 /* if this assert fires, then another thread took a reference to a
517 * stub manager without taking a reference to the containing
518 * apartment, which it must do. */
519 assert(list_empty(&apt->stubmgrs));
521 if (apt->filter) IMessageFilter_Release(apt->filter);
523 /* free as many unused libraries as possible... */
524 apartment_freeunusedlibraries(apt, 0);
526 /* ... and free the memory for the apartment loaded dll entry and
527 * release the dll list reference without freeing the library for the
528 * rest */
529 while ((cursor = list_head(&apt->loaded_dlls)))
531 struct apartment_loaded_dll *apartment_loaded_dll = LIST_ENTRY(cursor, struct apartment_loaded_dll, entry);
532 apartment_release_dll(apartment_loaded_dll->dll, FALSE);
533 list_remove(cursor);
534 heap_free(apartment_loaded_dll);
537 apt->cs.DebugInfo->Spare[0] = 0;
538 DeleteCriticalSection(&apt->cs);
540 heap_free(apt);
544 static DWORD apartment_addref(struct apartment *apt)
546 DWORD refs = InterlockedIncrement(&apt->refs);
547 TRACE("%s: before = %d\n", wine_dbgstr_longlong(apt->oxid), refs - 1);
548 return refs;
551 /* Gets existing apartment or creates a new one and enters it */
552 static struct apartment *apartment_get_or_create(DWORD model)
554 struct apartment *apt = com_get_current_apt();
555 struct tlsdata *data;
557 if (!apt)
559 com_get_tlsdata(&data);
561 if (model & COINIT_APARTMENTTHREADED)
563 EnterCriticalSection(&apt_cs);
565 apt = apartment_construct(model);
566 if (!main_sta)
568 main_sta = apt;
569 apt->main = TRUE;
570 TRACE("Created main-threaded apartment with OXID %s\n", wine_dbgstr_longlong(apt->oxid));
573 data->flags |= OLETLS_APARTMENTTHREADED;
574 if (model & COINIT_DISABLE_OLE1DDE)
575 data->flags |= OLETLS_DISABLE_OLE1DDE;
577 LeaveCriticalSection(&apt_cs);
579 if (apt->main)
580 apartment_createwindowifneeded(apt);
582 else
584 EnterCriticalSection(&apt_cs);
586 /* The multi-threaded apartment (MTA) contains zero or more threads interacting
587 * with free threaded (ie thread safe) COM objects. There is only ever one MTA
588 * in a process */
589 if (mta)
591 TRACE("entering the multithreaded apartment %s\n", wine_dbgstr_longlong(mta->oxid));
592 apartment_addref(mta);
594 else
595 mta = apartment_construct(model);
597 data->flags |= OLETLS_MULTITHREADED | OLETLS_DISABLE_OLE1DDE;
599 apt = mta;
601 LeaveCriticalSection(&apt_cs);
603 data->apt = apt;
606 return apt;
609 struct apartment * apartment_get_mta(void)
611 struct apartment *apt;
613 EnterCriticalSection(&apt_cs);
615 if ((apt = mta))
616 apartment_addref(apt);
618 LeaveCriticalSection(&apt_cs);
620 return apt;
623 /* Return the current apartment if it exists, or, failing that, the MTA. Caller
624 * must free the returned apartment in either case. */
625 struct apartment * apartment_get_current_or_mta(void)
627 struct apartment *apt = com_get_current_apt();
628 if (apt)
630 apartment_addref(apt);
631 return apt;
633 return apartment_get_mta();
636 /* The given OXID must be local to this process */
637 struct apartment * apartment_findfromoxid(OXID oxid)
639 struct apartment *result = NULL, *apt;
641 EnterCriticalSection(&apt_cs);
642 LIST_FOR_EACH_ENTRY(apt, &apts, struct apartment, entry)
644 if (apt->oxid == oxid)
646 result = apt;
647 apartment_addref(result);
648 break;
651 LeaveCriticalSection(&apt_cs);
653 return result;
656 /* gets the apartment which has a given creator thread ID. The caller must
657 * release the reference from the apartment as soon as the apartment pointer
658 * is no longer required. */
659 struct apartment * apartment_findfromtid(DWORD tid)
661 struct apartment *result = NULL, *apt;
663 EnterCriticalSection(&apt_cs);
664 LIST_FOR_EACH_ENTRY(apt, &apts, struct apartment, entry)
666 if (apt->tid == tid)
668 result = apt;
669 apartment_addref(result);
670 break;
673 LeaveCriticalSection(&apt_cs);
675 return result;
678 /* gets the main apartment if it exists. The caller must
679 * release the reference from the apartment as soon as the apartment pointer
680 * is no longer required. */
681 static struct apartment *apartment_findmain(void)
683 struct apartment *result;
685 EnterCriticalSection(&apt_cs);
687 result = main_sta;
688 if (result) apartment_addref(result);
690 LeaveCriticalSection(&apt_cs);
692 return result;
695 struct host_object_params
697 struct class_reg_data regdata;
698 CLSID clsid; /* clsid of object to marshal */
699 IID iid; /* interface to marshal */
700 HANDLE event; /* event signalling when ready for multi-threaded case */
701 HRESULT hr; /* result for multi-threaded case */
702 IStream *stream; /* stream that the object will be marshaled into */
703 BOOL apartment_threaded; /* is the component purely apartment-threaded? */
706 /* Returns expanded dll path from the registry or activation context. */
707 static BOOL get_object_dll_path(const struct class_reg_data *regdata, WCHAR *dst, DWORD dstlen)
709 DWORD ret;
711 if (regdata->origin == CLASS_REG_REGISTRY)
713 DWORD keytype;
714 WCHAR src[MAX_PATH];
715 DWORD dwLength = dstlen * sizeof(WCHAR);
717 if ((ret = RegQueryValueExW(regdata->u.hkey, NULL, NULL, &keytype, (BYTE*)src, &dwLength)) == ERROR_SUCCESS)
719 if (keytype == REG_EXPAND_SZ)
721 if (dstlen <= ExpandEnvironmentStringsW(src, dst, dstlen)) ret = ERROR_MORE_DATA;
723 else
725 const WCHAR *quote_start;
726 quote_start = wcschr(src, '\"');
727 if (quote_start)
729 const WCHAR *quote_end = wcschr(quote_start + 1, '\"');
730 if (quote_end)
732 memmove(src, quote_start + 1, (quote_end - quote_start - 1) * sizeof(WCHAR));
733 src[quote_end - quote_start - 1] = '\0';
736 lstrcpynW(dst, src, dstlen);
739 return !ret;
741 else
743 ULONG_PTR cookie;
745 *dst = 0;
746 ActivateActCtx(regdata->u.actctx.hactctx, &cookie);
747 ret = SearchPathW(NULL, regdata->u.actctx.module_name, L".dll", dstlen, dst, NULL);
748 DeactivateActCtx(0, cookie);
749 return *dst != 0;
753 /* gets the specified class object by loading the appropriate DLL, if
754 * necessary and calls the DllGetClassObject function for the DLL */
755 static HRESULT apartment_getclassobject(struct apartment *apt, LPCWSTR dllpath,
756 BOOL apartment_threaded,
757 REFCLSID rclsid, REFIID riid, void **ppv)
759 HRESULT hr = S_OK;
760 BOOL found = FALSE;
761 struct apartment_loaded_dll *apartment_loaded_dll;
763 if (!wcsicmp(dllpath, L"ole32.dll"))
765 HRESULT (WINAPI *p_ole32_DllGetClassObject)(REFCLSID clsid, REFIID riid, void **obj);
767 p_ole32_DllGetClassObject = (void *)GetProcAddress(GetModuleHandleW(L"ole32.dll"), "DllGetClassObject");
769 /* we don't need to control the lifetime of this dll, so use the local
770 * implementation of DllGetClassObject directly */
771 TRACE("calling ole32!DllGetClassObject\n");
772 hr = p_ole32_DllGetClassObject(rclsid, riid, ppv);
774 if (hr != S_OK)
775 ERR("DllGetClassObject returned error 0x%08x for dll %s\n", hr, debugstr_w(dllpath));
777 return hr;
780 EnterCriticalSection(&apt->cs);
782 LIST_FOR_EACH_ENTRY(apartment_loaded_dll, &apt->loaded_dlls, struct apartment_loaded_dll, entry)
783 if (!wcsicmp(dllpath, apartment_loaded_dll->dll->library_name))
785 TRACE("found %s already loaded\n", debugstr_w(dllpath));
786 found = TRUE;
787 break;
790 if (!found)
792 apartment_loaded_dll = heap_alloc(sizeof(*apartment_loaded_dll));
793 if (!apartment_loaded_dll)
794 hr = E_OUTOFMEMORY;
795 if (SUCCEEDED(hr))
797 apartment_loaded_dll->unload_time = 0;
798 apartment_loaded_dll->multi_threaded = FALSE;
799 hr = apartment_add_dll(dllpath, &apartment_loaded_dll->dll);
800 if (FAILED(hr))
801 heap_free(apartment_loaded_dll);
803 if (SUCCEEDED(hr))
805 TRACE("added new loaded dll %s\n", debugstr_w(dllpath));
806 list_add_tail(&apt->loaded_dlls, &apartment_loaded_dll->entry);
810 LeaveCriticalSection(&apt->cs);
812 if (SUCCEEDED(hr))
814 /* one component being multi-threaded overrides any number of
815 * apartment-threaded components */
816 if (!apartment_threaded)
817 apartment_loaded_dll->multi_threaded = TRUE;
819 TRACE("calling DllGetClassObject %p\n", apartment_loaded_dll->dll->DllGetClassObject);
820 /* OK: get the ClassObject */
821 hr = apartment_loaded_dll->dll->DllGetClassObject(rclsid, riid, ppv);
823 if (hr != S_OK)
824 ERR("DllGetClassObject returned error 0x%08x for dll %s\n", hr, debugstr_w(dllpath));
827 return hr;
830 static HRESULT apartment_hostobject(struct apartment *apt,
831 const struct host_object_params *params);
833 struct host_thread_params
835 COINIT threading_model;
836 HANDLE ready_event;
837 HWND apartment_hwnd;
840 /* thread for hosting an object to allow an object to appear to be created in
841 * an apartment with an incompatible threading model */
842 static DWORD CALLBACK apartment_hostobject_thread(void *p)
844 struct host_thread_params *params = p;
845 MSG msg;
846 HRESULT hr;
847 struct apartment *apt;
849 TRACE("\n");
851 hr = CoInitializeEx(NULL, params->threading_model);
852 if (FAILED(hr)) return hr;
854 apt = com_get_current_apt();
855 if (params->threading_model == COINIT_APARTMENTTHREADED)
857 apartment_createwindowifneeded(apt);
858 params->apartment_hwnd = apartment_getwindow(apt);
860 else
861 params->apartment_hwnd = NULL;
863 /* force the message queue to be created before signaling parent thread */
864 PeekMessageW(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
866 SetEvent(params->ready_event);
867 params = NULL; /* can't touch params after here as it may be invalid */
869 while (GetMessageW(&msg, NULL, 0, 0))
871 if (!msg.hwnd && (msg.message == DM_HOSTOBJECT))
873 struct host_object_params *obj_params = (struct host_object_params *)msg.lParam;
874 obj_params->hr = apartment_hostobject(apt, obj_params);
875 SetEvent(obj_params->event);
877 else
879 TranslateMessage(&msg);
880 DispatchMessageW(&msg);
884 TRACE("exiting\n");
886 CoUninitialize();
888 return S_OK;
891 /* finds or creates a host apartment, creates the object inside it and returns
892 * a proxy to it so that the object can be used in the apartment of the
893 * caller of this function */
894 static HRESULT apartment_hostobject_in_hostapt(struct apartment *apt, BOOL multi_threaded,
895 BOOL main_apartment, const struct class_reg_data *regdata, REFCLSID rclsid, REFIID riid, void **ppv)
897 struct host_object_params params;
898 HWND apartment_hwnd = NULL;
899 DWORD apartment_tid = 0;
900 HRESULT hr;
902 if (!multi_threaded && main_apartment)
904 struct apartment *host_apt = apartment_findmain();
905 if (host_apt)
907 apartment_hwnd = apartment_getwindow(host_apt);
908 apartment_release(host_apt);
912 if (!apartment_hwnd)
914 EnterCriticalSection(&apt->cs);
916 if (!apt->host_apt_tid)
918 struct host_thread_params thread_params;
919 HANDLE handles[2];
920 DWORD wait_value;
922 thread_params.threading_model = multi_threaded ? COINIT_MULTITHREADED : COINIT_APARTMENTTHREADED;
923 handles[0] = thread_params.ready_event = CreateEventW(NULL, FALSE, FALSE, NULL);
924 thread_params.apartment_hwnd = NULL;
925 handles[1] = CreateThread(NULL, 0, apartment_hostobject_thread, &thread_params, 0, &apt->host_apt_tid);
926 if (!handles[1])
928 CloseHandle(handles[0]);
929 LeaveCriticalSection(&apt->cs);
930 return E_OUTOFMEMORY;
932 wait_value = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
933 CloseHandle(handles[0]);
934 CloseHandle(handles[1]);
935 if (wait_value == WAIT_OBJECT_0)
936 apt->host_apt_hwnd = thread_params.apartment_hwnd;
937 else
939 LeaveCriticalSection(&apt->cs);
940 return E_OUTOFMEMORY;
944 if (multi_threaded || !main_apartment)
946 apartment_hwnd = apt->host_apt_hwnd;
947 apartment_tid = apt->host_apt_tid;
950 LeaveCriticalSection(&apt->cs);
953 /* another thread may have become the main apartment in the time it took
954 * us to create the thread for the host apartment */
955 if (!apartment_hwnd && !multi_threaded && main_apartment)
957 struct apartment *host_apt = apartment_findmain();
958 if (host_apt)
960 apartment_hwnd = apartment_getwindow(host_apt);
961 apartment_release(host_apt);
965 params.regdata = *regdata;
966 params.clsid = *rclsid;
967 params.iid = *riid;
968 hr = CreateStreamOnHGlobal(NULL, TRUE, &params.stream);
969 if (FAILED(hr))
970 return hr;
971 params.apartment_threaded = !multi_threaded;
972 if (multi_threaded)
974 params.hr = S_OK;
975 params.event = CreateEventW(NULL, FALSE, FALSE, NULL);
976 if (!PostThreadMessageW(apartment_tid, DM_HOSTOBJECT, 0, (LPARAM)&params))
977 hr = E_OUTOFMEMORY;
978 else
980 WaitForSingleObject(params.event, INFINITE);
981 hr = params.hr;
983 CloseHandle(params.event);
985 else
987 if (!apartment_hwnd)
989 ERR("host apartment didn't create window\n");
990 hr = E_OUTOFMEMORY;
992 else
993 hr = SendMessageW(apartment_hwnd, DM_HOSTOBJECT, 0, (LPARAM)&params);
995 if (SUCCEEDED(hr))
996 hr = CoUnmarshalInterface(params.stream, riid, ppv);
997 IStream_Release(params.stream);
998 return hr;
1001 static enum comclass_threadingmodel get_threading_model(const struct class_reg_data *data)
1003 if (data->origin == CLASS_REG_REGISTRY)
1005 WCHAR threading_model[10 /* lstrlenW(L"apartment")+1 */];
1006 DWORD dwLength = sizeof(threading_model);
1007 DWORD keytype;
1008 DWORD ret;
1010 ret = RegQueryValueExW(data->u.hkey, L"ThreadingModel", NULL, &keytype, (BYTE*)threading_model, &dwLength);
1011 if ((ret != ERROR_SUCCESS) || (keytype != REG_SZ))
1012 threading_model[0] = '\0';
1014 if (!wcsicmp(threading_model, L"Apartment")) return ThreadingModel_Apartment;
1015 if (!wcsicmp(threading_model, L"Free")) return ThreadingModel_Free;
1016 if (!wcsicmp(threading_model, L"Both")) return ThreadingModel_Both;
1018 /* there's not specific handling for this case */
1019 if (threading_model[0]) return ThreadingModel_Neutral;
1020 return ThreadingModel_No;
1022 else
1023 return data->u.actctx.threading_model;
1026 HRESULT apartment_get_inproc_class_object(struct apartment *apt, const struct class_reg_data *regdata,
1027 REFCLSID rclsid, REFIID riid, DWORD class_context, void **ppv)
1029 WCHAR dllpath[MAX_PATH+1];
1030 BOOL apartment_threaded;
1032 if (!(class_context & CLSCTX_PS_DLL))
1034 enum comclass_threadingmodel model = get_threading_model(regdata);
1036 if (model == ThreadingModel_Apartment)
1038 apartment_threaded = TRUE;
1039 if (apt->multi_threaded)
1040 return apartment_hostobject_in_hostapt(apt, FALSE, FALSE, regdata, rclsid, riid, ppv);
1042 else if (model == ThreadingModel_Free)
1044 apartment_threaded = FALSE;
1045 if (!apt->multi_threaded)
1046 return apartment_hostobject_in_hostapt(apt, TRUE, FALSE, regdata, rclsid, riid, ppv);
1048 /* everything except "Apartment", "Free" and "Both" */
1049 else if (model != ThreadingModel_Both)
1051 apartment_threaded = TRUE;
1052 /* everything else is main-threaded */
1053 if (model != ThreadingModel_No)
1054 FIXME("unrecognised threading model %d for object %s, should be main-threaded?\n", model, debugstr_guid(rclsid));
1056 if (apt->multi_threaded || !apt->main)
1057 return apartment_hostobject_in_hostapt(apt, FALSE, TRUE, regdata, rclsid, riid, ppv);
1059 else
1060 apartment_threaded = FALSE;
1062 else
1063 apartment_threaded = !apt->multi_threaded;
1065 if (!get_object_dll_path(regdata, dllpath, ARRAY_SIZE(dllpath)))
1067 /* failure: CLSID is not found in registry */
1068 WARN("class %s not registered inproc\n", debugstr_guid(rclsid));
1069 return REGDB_E_CLASSNOTREG;
1072 return apartment_getclassobject(apt, dllpath, apartment_threaded, rclsid, riid, ppv);
1075 static HRESULT apartment_hostobject(struct apartment *apt, const struct host_object_params *params)
1077 static const LARGE_INTEGER llZero;
1078 WCHAR dllpath[MAX_PATH+1];
1079 IUnknown *object;
1080 HRESULT hr;
1082 TRACE("clsid %s, iid %s\n", debugstr_guid(&params->clsid), debugstr_guid(&params->iid));
1084 if (!get_object_dll_path(&params->regdata, dllpath, ARRAY_SIZE(dllpath)))
1086 /* failure: CLSID is not found in registry */
1087 WARN("class %s not registered inproc\n", debugstr_guid(&params->clsid));
1088 return REGDB_E_CLASSNOTREG;
1091 hr = apartment_getclassobject(apt, dllpath, params->apartment_threaded, &params->clsid, &params->iid, (void **)&object);
1092 if (FAILED(hr))
1093 return hr;
1095 hr = CoMarshalInterface(params->stream, &params->iid, object, MSHCTX_INPROC, NULL, MSHLFLAGS_NORMAL);
1096 if (FAILED(hr))
1097 IUnknown_Release(object);
1098 IStream_Seek(params->stream, llZero, STREAM_SEEK_SET, NULL);
1100 return hr;
1103 struct dispatch_params;
1105 static LRESULT CALLBACK apartment_wndproc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1107 switch (msg)
1109 case DM_EXECUTERPC:
1110 rpc_execute_call((struct dispatch_params *)lParam);
1111 return 0;
1112 case DM_HOSTOBJECT:
1113 return apartment_hostobject(com_get_current_apt(), (const struct host_object_params *)lParam);
1114 default:
1115 return DefWindowProcW(hWnd, msg, wParam, lParam);
1119 static BOOL apartment_is_model(const struct apartment *apt, DWORD model)
1121 return (apt->multi_threaded == !(model & COINIT_APARTMENTTHREADED));
1124 HRESULT enter_apartment(struct tlsdata *data, DWORD model)
1126 HRESULT hr = S_OK;
1128 if (!data->apt)
1130 if (!apartment_get_or_create(model))
1131 return E_OUTOFMEMORY;
1133 else if (!apartment_is_model(data->apt, model))
1135 WARN("Attempt to change threading model of this apartment from %s to %s\n",
1136 data->apt->multi_threaded ? "multi-threaded" : "apartment threaded",
1137 model & COINIT_APARTMENTTHREADED ? "apartment threaded" : "multi-threaded" );
1138 return RPC_E_CHANGED_MODE;
1140 else
1141 hr = S_FALSE;
1143 data->inits++;
1145 return hr;
1148 void leave_apartment(struct tlsdata *data)
1150 if (!--data->inits)
1152 if (data->ole_inits)
1153 WARN( "Uninitializing apartment while Ole is still initialized\n" );
1154 apartment_release(data->apt);
1155 data->apt = NULL;
1156 data->flags &= ~(OLETLS_DISABLE_OLE1DDE | OLETLS_APARTMENTTHREADED | OLETLS_MULTITHREADED);
1160 struct mta_cookie
1162 struct list entry;
1165 HRESULT apartment_increment_mta_usage(CO_MTA_USAGE_COOKIE *cookie)
1167 struct mta_cookie *mta_cookie;
1169 *cookie = NULL;
1171 if (!(mta_cookie = heap_alloc(sizeof(*mta_cookie))))
1172 return E_OUTOFMEMORY;
1174 EnterCriticalSection(&apt_cs);
1176 if (mta)
1177 apartment_addref(mta);
1178 else
1179 mta = apartment_construct(COINIT_MULTITHREADED);
1180 list_add_head(&mta->usage_cookies, &mta_cookie->entry);
1182 LeaveCriticalSection(&apt_cs);
1184 *cookie = (CO_MTA_USAGE_COOKIE)mta_cookie;
1186 return S_OK;
1189 void apartment_decrement_mta_usage(CO_MTA_USAGE_COOKIE cookie)
1191 struct mta_cookie *mta_cookie = (struct mta_cookie *)cookie;
1193 EnterCriticalSection(&apt_cs);
1195 if (mta)
1197 struct mta_cookie *cur;
1199 LIST_FOR_EACH_ENTRY(cur, &mta->usage_cookies, struct mta_cookie, entry)
1201 if (mta_cookie == cur)
1203 list_remove(&cur->entry);
1204 heap_free(cur);
1205 apartment_release(mta);
1206 break;
1211 LeaveCriticalSection(&apt_cs);
1214 static const WCHAR aptwinclassW[] = L"OleMainThreadWndClass";
1215 static ATOM apt_win_class;
1217 static BOOL WINAPI register_class( INIT_ONCE *once, void *param, void **context )
1219 WNDCLASSW wclass;
1221 /* Dispatching to the correct thread in an apartment is done through
1222 * window messages rather than RPC transports. When an interface is
1223 * marshalled into another apartment in the same process, a window of the
1224 * following class is created. The *caller* of CoMarshalInterface (i.e., the
1225 * application) is responsible for pumping the message loop in that thread.
1226 * The WM_USER messages which point to the RPCs are then dispatched to
1227 * apartment_wndproc by the user's code from the apartment in which the
1228 * interface was unmarshalled.
1230 memset(&wclass, 0, sizeof(wclass));
1231 wclass.lpfnWndProc = apartment_wndproc;
1232 wclass.hInstance = hProxyDll;
1233 wclass.lpszClassName = aptwinclassW;
1234 apt_win_class = RegisterClassW(&wclass);
1235 return TRUE;
1238 /* create a window for the apartment or return the current one if one has
1239 * already been created */
1240 HRESULT apartment_createwindowifneeded(struct apartment *apt)
1242 static INIT_ONCE class_init_once = INIT_ONCE_STATIC_INIT;
1244 if (apt->multi_threaded)
1245 return S_OK;
1247 if (!apt->win)
1249 HWND hwnd;
1251 InitOnceExecuteOnce( &class_init_once, register_class, NULL, NULL );
1253 hwnd = CreateWindowW(aptwinclassW, NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, 0, hProxyDll, NULL);
1254 if (!hwnd)
1256 ERR("CreateWindow failed with error %d\n", GetLastError());
1257 return HRESULT_FROM_WIN32(GetLastError());
1259 if (InterlockedCompareExchangePointer((void **)&apt->win, hwnd, NULL))
1260 /* someone beat us to it */
1261 DestroyWindow(hwnd);
1264 return S_OK;
1267 /* retrieves the window for the main- or apartment-threaded apartment */
1268 HWND apartment_getwindow(const struct apartment *apt)
1270 assert(!apt->multi_threaded);
1271 return apt->win;
1274 OXID apartment_getoxid(const struct apartment *apt)
1276 return apt->oxid;
1279 void apartment_global_cleanup(void)
1281 if (apt_win_class)
1282 UnregisterClassW((const WCHAR *)MAKEINTATOM(apt_win_class), hProxyDll);
1283 apartment_release_dlls();
1284 DeleteCriticalSection(&apt_cs);