include: Add STORAGE_HOTPLUG_INFO structure.
[wine.git] / dlls / combase / apartment.c
blob85e06a6fd57f3c6553a38f13d777f2e7c76beb1a
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 #include "windef.h"
31 #include "winbase.h"
32 #include "servprov.h"
34 #include "combase_private.h"
36 #include "wine/debug.h"
37 #include "wine/list.h"
39 WINE_DEFAULT_DEBUG_CHANNEL(ole);
41 enum comclass_threadingmodel
43 ThreadingModel_Apartment = 1,
44 ThreadingModel_Free = 2,
45 ThreadingModel_No = 3,
46 ThreadingModel_Both = 4,
47 ThreadingModel_Neutral = 5
50 static struct apartment *mta;
51 static struct apartment *main_sta; /* the first STA */
52 static struct list apts = LIST_INIT(apts);
54 static CRITICAL_SECTION apt_cs;
55 static CRITICAL_SECTION_DEBUG apt_cs_debug =
57 0, 0, &apt_cs,
58 { &apt_cs_debug.ProcessLocksList, &apt_cs_debug.ProcessLocksList },
59 0, 0, { (DWORD_PTR)(__FILE__ ": apt_cs") }
61 static CRITICAL_SECTION apt_cs = { &apt_cs_debug, -1, 0, 0, 0, 0 };
63 static struct list dlls = LIST_INIT(dlls);
65 static CRITICAL_SECTION dlls_cs;
66 static CRITICAL_SECTION_DEBUG dlls_cs_debug =
68 0, 0, &dlls_cs,
69 { &dlls_cs_debug.ProcessLocksList, &dlls_cs_debug.ProcessLocksList },
70 0, 0, { (DWORD_PTR)(__FILE__ ": dlls_cs") }
72 static CRITICAL_SECTION dlls_cs = { &dlls_cs_debug, -1, 0, 0, 0, 0 };
74 typedef HRESULT (WINAPI *DllGetClassObjectFunc)(REFCLSID clsid, REFIID iid, void **obj);
75 typedef HRESULT (WINAPI *DllCanUnloadNowFunc)(void);
77 struct opendll
79 LONG refs;
80 LPWSTR library_name;
81 HANDLE library;
82 DllGetClassObjectFunc DllGetClassObject;
83 DllCanUnloadNowFunc DllCanUnloadNow;
84 struct list entry;
87 struct apartment_loaded_dll
89 struct list entry;
90 struct opendll *dll;
91 DWORD unload_time;
92 BOOL multi_threaded;
95 static struct opendll *apartment_get_dll(const WCHAR *library_name)
97 struct opendll *ptr, *ret = NULL;
99 EnterCriticalSection(&dlls_cs);
100 LIST_FOR_EACH_ENTRY(ptr, &dlls, struct opendll, entry)
102 if (!wcsicmp(library_name, ptr->library_name) &&
103 (InterlockedIncrement(&ptr->refs) != 1) /* entry is being destroyed if == 1 */)
105 ret = ptr;
106 break;
109 LeaveCriticalSection(&dlls_cs);
111 return ret;
114 /* caller must ensure that library_name is not already in the open dll list */
115 static HRESULT apartment_add_dll(const WCHAR *library_name, struct opendll **ret)
117 struct opendll *entry;
118 int len;
119 HRESULT hr = S_OK;
120 HANDLE hLibrary;
121 DllCanUnloadNowFunc DllCanUnloadNow;
122 DllGetClassObjectFunc DllGetClassObject;
124 TRACE("%s\n", debugstr_w(library_name));
126 *ret = apartment_get_dll(library_name);
127 if (*ret) return S_OK;
129 /* Load outside of dlls lock to avoid dependency on the loader lock */
130 hLibrary = LoadLibraryExW(library_name, 0, LOAD_WITH_ALTERED_SEARCH_PATH);
131 if (!hLibrary)
133 ERR("couldn't load in-process dll %s\n", debugstr_w(library_name));
134 /* failure: DLL could not be loaded */
135 return E_ACCESSDENIED; /* FIXME: or should this be CO_E_DLLNOTFOUND? */
138 /* DllCanUnloadNow is optional */
139 DllCanUnloadNow = (void *)GetProcAddress(hLibrary, "DllCanUnloadNow");
140 DllGetClassObject = (void *)GetProcAddress(hLibrary, "DllGetClassObject");
141 if (!DllGetClassObject)
143 /* failure: the dll did not export DllGetClassObject */
144 ERR("couldn't find function DllGetClassObject in %s\n", debugstr_w(library_name));
145 FreeLibrary(hLibrary);
146 return CO_E_DLLNOTFOUND;
149 EnterCriticalSection(&dlls_cs);
151 *ret = apartment_get_dll(library_name);
152 if (*ret)
154 /* another caller to this function already added the dll while we
155 * weren't in the critical section */
156 FreeLibrary(hLibrary);
158 else
160 len = lstrlenW(library_name);
161 entry = malloc(sizeof(*entry));
162 if (entry)
163 entry->library_name = malloc((len + 1) * sizeof(WCHAR));
164 if (entry && entry->library_name)
166 memcpy(entry->library_name, library_name, (len + 1)*sizeof(WCHAR));
167 entry->library = hLibrary;
168 entry->refs = 1;
169 entry->DllCanUnloadNow = DllCanUnloadNow;
170 entry->DllGetClassObject = DllGetClassObject;
171 list_add_tail(&dlls, &entry->entry);
172 *ret = entry;
174 else
176 free(entry);
177 hr = E_OUTOFMEMORY;
178 FreeLibrary(hLibrary);
182 LeaveCriticalSection(&dlls_cs);
184 return hr;
187 /* pass FALSE for free_entry to release a reference without destroying the
188 * entry if it reaches zero or TRUE otherwise */
189 static void apartment_release_dll(struct opendll *entry, BOOL free_entry)
191 if (!InterlockedDecrement(&entry->refs) && free_entry)
193 EnterCriticalSection(&dlls_cs);
194 list_remove(&entry->entry);
195 LeaveCriticalSection(&dlls_cs);
197 TRACE("freeing %p\n", entry->library);
198 FreeLibrary(entry->library);
200 free(entry->library_name);
201 free(entry);
205 /* frees memory associated with active dll list */
206 static void apartment_release_dlls(void)
208 struct opendll *entry, *cursor2;
209 EnterCriticalSection(&dlls_cs);
210 LIST_FOR_EACH_ENTRY_SAFE(entry, cursor2, &dlls, struct opendll, entry)
212 list_remove(&entry->entry);
213 free(entry->library_name);
214 free(entry);
216 LeaveCriticalSection(&dlls_cs);
217 DeleteCriticalSection(&dlls_cs);
221 * This is a marshallable object exposing registered local servers.
222 * IServiceProvider is used only because it happens meet requirements
223 * and already has proxy/stub code. If more functionality is needed,
224 * a custom interface may be used instead.
226 struct local_server
228 IServiceProvider IServiceProvider_iface;
229 LONG refcount;
230 struct apartment *apt;
231 IStream *marshal_stream;
234 static inline struct local_server *impl_from_IServiceProvider(IServiceProvider *iface)
236 return CONTAINING_RECORD(iface, struct local_server, IServiceProvider_iface);
239 static HRESULT WINAPI local_server_QueryInterface(IServiceProvider *iface, REFIID riid, void **obj)
241 struct local_server *local_server = impl_from_IServiceProvider(iface);
243 TRACE("%p, %s, %p\n", iface, debugstr_guid(riid), obj);
245 if (IsEqualGUID(riid, &IID_IUnknown) ||
246 IsEqualGUID(riid, &IID_IServiceProvider))
248 *obj = &local_server->IServiceProvider_iface;
250 else
252 *obj = NULL;
253 return E_NOINTERFACE;
256 IUnknown_AddRef((IUnknown *)*obj);
257 return S_OK;
260 static ULONG WINAPI local_server_AddRef(IServiceProvider *iface)
262 struct local_server *local_server = impl_from_IServiceProvider(iface);
263 LONG refcount = InterlockedIncrement(&local_server->refcount);
265 TRACE("%p, refcount %ld\n", iface, refcount);
267 return refcount;
270 static ULONG WINAPI local_server_Release(IServiceProvider *iface)
272 struct local_server *local_server = impl_from_IServiceProvider(iface);
273 LONG refcount = InterlockedDecrement(&local_server->refcount);
275 TRACE("%p, refcount %ld\n", iface, refcount);
277 if (!refcount)
279 assert(!local_server->apt);
280 free(local_server);
283 return refcount;
286 static HRESULT WINAPI local_server_QueryService(IServiceProvider *iface, REFGUID guid, REFIID riid, void **obj)
288 struct local_server *local_server = impl_from_IServiceProvider(iface);
289 struct apartment *apt = com_get_current_apt();
290 HRESULT hr = E_FAIL;
291 IUnknown *unk;
293 TRACE("%p, %s, %s, %p\n", iface, debugstr_guid(guid), debugstr_guid(riid), obj);
295 if (!local_server->apt)
296 return E_UNEXPECTED;
298 if ((unk = com_get_registered_class_object(apt, guid, CLSCTX_LOCAL_SERVER)))
300 hr = IUnknown_QueryInterface(unk, riid, obj);
301 IUnknown_Release(unk);
304 return hr;
307 static const IServiceProviderVtbl local_server_vtbl =
309 local_server_QueryInterface,
310 local_server_AddRef,
311 local_server_Release,
312 local_server_QueryService
315 HRESULT apartment_get_local_server_stream(struct apartment *apt, IStream **ret)
317 HRESULT hr = S_OK;
319 EnterCriticalSection(&apt->cs);
321 if (!apt->local_server)
323 struct local_server *obj;
325 obj = malloc(sizeof(*obj));
326 if (obj)
328 obj->IServiceProvider_iface.lpVtbl = &local_server_vtbl;
329 obj->refcount = 1;
330 obj->apt = apt;
332 hr = CreateStreamOnHGlobal(0, TRUE, &obj->marshal_stream);
333 if (SUCCEEDED(hr))
335 hr = CoMarshalInterface(obj->marshal_stream, &IID_IServiceProvider, (IUnknown *)&obj->IServiceProvider_iface,
336 MSHCTX_LOCAL, NULL, MSHLFLAGS_TABLESTRONG);
337 if (FAILED(hr))
338 IStream_Release(obj->marshal_stream);
341 if (SUCCEEDED(hr))
342 apt->local_server = obj;
343 else
344 free(obj);
346 else
347 hr = E_OUTOFMEMORY;
350 if (SUCCEEDED(hr))
351 hr = IStream_Clone(apt->local_server->marshal_stream, ret);
353 LeaveCriticalSection(&apt->cs);
355 if (FAILED(hr))
356 ERR("Failed: %#lx\n", hr);
358 return hr;
361 /* Creates new apartment for given model */
362 static struct apartment *apartment_construct(DWORD model)
364 struct apartment *apt;
366 TRACE("creating new apartment, model %ld\n", model);
368 apt = calloc(1, sizeof(*apt));
369 apt->tid = GetCurrentThreadId();
371 list_init(&apt->proxies);
372 list_init(&apt->stubmgrs);
373 list_init(&apt->loaded_dlls);
374 list_init(&apt->usage_cookies);
375 apt->ipidc = 0;
376 apt->refs = 1;
377 apt->remunk_exported = FALSE;
378 apt->oidc = 1;
379 InitializeCriticalSectionEx(&apt->cs, 0, RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO);
380 apt->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": apartment");
382 apt->multi_threaded = !(model & COINIT_APARTMENTTHREADED);
384 if (apt->multi_threaded)
386 /* FIXME: should be randomly generated by in an RPC call to rpcss */
387 apt->oxid = ((OXID)GetCurrentProcessId() << 32) | 0xcafe;
389 else
391 /* FIXME: should be randomly generated by in an RPC call to rpcss */
392 apt->oxid = ((OXID)GetCurrentProcessId() << 32) | GetCurrentThreadId();
395 TRACE("Created apartment on OXID %s\n", wine_dbgstr_longlong(apt->oxid));
397 list_add_head(&apts, &apt->entry);
399 return apt;
402 /* Frees unused libraries loaded into apartment */
403 void apartment_freeunusedlibraries(struct apartment *apt, DWORD delay)
405 struct apartment_loaded_dll *entry, *next;
407 EnterCriticalSection(&apt->cs);
408 LIST_FOR_EACH_ENTRY_SAFE(entry, next, &apt->loaded_dlls, struct apartment_loaded_dll, entry)
410 if (entry->dll->DllCanUnloadNow && (entry->dll->DllCanUnloadNow() == S_OK))
412 DWORD real_delay = delay;
414 if (real_delay == INFINITE)
416 /* DLLs that return multi-threaded objects aren't unloaded
417 * straight away to cope for programs that have races between
418 * last object destruction and threads in the DLLs that haven't
419 * finished, despite DllCanUnloadNow returning S_OK */
420 if (entry->multi_threaded)
421 real_delay = 10 * 60 * 1000; /* 10 minutes */
422 else
423 real_delay = 0;
426 if (!real_delay || (entry->unload_time && ((int)(GetTickCount() - entry->unload_time) > 0)))
428 list_remove(&entry->entry);
429 apartment_release_dll(entry->dll, TRUE);
430 free(entry);
432 else
434 entry->unload_time = GetTickCount() + real_delay;
435 if (!entry->unload_time) entry->unload_time = 1;
438 else if (entry->unload_time)
439 entry->unload_time = 0;
441 LeaveCriticalSection(&apt->cs);
444 void apartment_release(struct apartment *apt)
446 DWORD refcount;
448 EnterCriticalSection(&apt_cs);
450 refcount = InterlockedDecrement(&apt->refs);
451 TRACE("%s: after = %ld\n", wine_dbgstr_longlong(apt->oxid), refcount);
453 if (apt->being_destroyed)
455 LeaveCriticalSection(&apt_cs);
456 return;
459 /* destruction stuff that needs to happen under global */
460 if (!refcount)
462 apt->being_destroyed = TRUE;
463 if (apt == mta) mta = NULL;
464 else if (apt == main_sta) main_sta = NULL;
465 list_remove(&apt->entry);
468 LeaveCriticalSection(&apt_cs);
470 if (!refcount)
472 struct list *cursor, *cursor2;
474 TRACE("destroying apartment %p, oxid %s\n", apt, wine_dbgstr_longlong(apt->oxid));
476 if (apt->local_server)
478 struct local_server *local_server = apt->local_server;
479 LARGE_INTEGER zero;
481 memset(&zero, 0, sizeof(zero));
482 IStream_Seek(local_server->marshal_stream, zero, STREAM_SEEK_SET, NULL);
483 CoReleaseMarshalData(local_server->marshal_stream);
484 IStream_Release(local_server->marshal_stream);
485 local_server->marshal_stream = NULL;
487 apt->local_server = NULL;
488 local_server->apt = NULL;
489 IServiceProvider_Release(&local_server->IServiceProvider_iface);
492 /* Release the references to the registered class objects */
493 apartment_revoke_all_classes(apt);
495 /* no locking is needed for this apartment, because no other thread
496 * can access it at this point */
498 apartment_disconnectproxies(apt);
500 if (apt->win) DestroyWindow(apt->win);
501 if (apt->host_apt_tid) PostThreadMessageW(apt->host_apt_tid, WM_QUIT, 0, 0);
503 LIST_FOR_EACH_SAFE(cursor, cursor2, &apt->stubmgrs)
505 struct stub_manager *stubmgr = LIST_ENTRY(cursor, struct stub_manager, entry);
506 /* release the implicit reference given by the fact that the
507 * stub has external references (it must do since it is in the
508 * stub manager list in the apartment and all non-apartment users
509 * must have a ref on the apartment and so it cannot be destroyed).
511 stub_manager_int_release(stubmgr);
514 /* if this assert fires, then another thread took a reference to a
515 * stub manager without taking a reference to the containing
516 * apartment, which it must do. */
517 assert(list_empty(&apt->stubmgrs));
519 if (apt->filter) IMessageFilter_Release(apt->filter);
521 /* free as many unused libraries as possible... */
522 apartment_freeunusedlibraries(apt, 0);
524 /* ... and free the memory for the apartment loaded dll entry and
525 * release the dll list reference without freeing the library for the
526 * rest */
527 while ((cursor = list_head(&apt->loaded_dlls)))
529 struct apartment_loaded_dll *apartment_loaded_dll = LIST_ENTRY(cursor, struct apartment_loaded_dll, entry);
530 apartment_release_dll(apartment_loaded_dll->dll, FALSE);
531 list_remove(cursor);
532 free(apartment_loaded_dll);
535 apt->cs.DebugInfo->Spare[0] = 0;
536 DeleteCriticalSection(&apt->cs);
538 free(apt);
542 static DWORD apartment_addref(struct apartment *apt)
544 DWORD refs = InterlockedIncrement(&apt->refs);
545 TRACE("%s: before = %ld\n", wine_dbgstr_longlong(apt->oxid), refs - 1);
546 return refs;
549 /* Gets existing apartment or creates a new one and enters it */
550 static struct apartment *apartment_get_or_create(DWORD model)
552 struct apartment *apt = com_get_current_apt();
553 struct tlsdata *data;
555 if (!apt)
557 com_get_tlsdata(&data);
559 if (model & COINIT_APARTMENTTHREADED)
561 EnterCriticalSection(&apt_cs);
563 apt = apartment_construct(model);
564 if (!main_sta)
566 main_sta = apt;
567 apt->main = TRUE;
568 TRACE("Created main-threaded apartment with OXID %s\n", wine_dbgstr_longlong(apt->oxid));
571 data->flags |= OLETLS_APARTMENTTHREADED;
572 if (model & COINIT_DISABLE_OLE1DDE)
573 data->flags |= OLETLS_DISABLE_OLE1DDE;
575 LeaveCriticalSection(&apt_cs);
577 if (apt->main)
578 apartment_createwindowifneeded(apt);
580 else
582 EnterCriticalSection(&apt_cs);
584 /* The multi-threaded apartment (MTA) contains zero or more threads interacting
585 * with free threaded (ie thread safe) COM objects. There is only ever one MTA
586 * in a process */
587 if (mta)
589 TRACE("entering the multithreaded apartment %s\n", wine_dbgstr_longlong(mta->oxid));
590 apartment_addref(mta);
592 else
593 mta = apartment_construct(model);
595 data->flags |= OLETLS_MULTITHREADED | OLETLS_DISABLE_OLE1DDE;
597 apt = mta;
599 LeaveCriticalSection(&apt_cs);
601 data->apt = apt;
604 return apt;
607 struct apartment * apartment_get_mta(void)
609 struct apartment *apt;
611 EnterCriticalSection(&apt_cs);
613 if ((apt = mta))
614 apartment_addref(apt);
616 LeaveCriticalSection(&apt_cs);
618 return apt;
621 /* Return the current apartment if it exists, or, failing that, the MTA. Caller
622 * must free the returned apartment in either case. */
623 struct apartment * apartment_get_current_or_mta(void)
625 struct apartment *apt = com_get_current_apt();
626 if (apt)
628 apartment_addref(apt);
629 return apt;
631 return apartment_get_mta();
634 /* The given OXID must be local to this process */
635 struct apartment * apartment_findfromoxid(OXID oxid)
637 struct apartment *result = NULL, *apt;
639 EnterCriticalSection(&apt_cs);
640 LIST_FOR_EACH_ENTRY(apt, &apts, struct apartment, entry)
642 if (apt->oxid == oxid)
644 result = apt;
645 apartment_addref(result);
646 break;
649 LeaveCriticalSection(&apt_cs);
651 return result;
654 /* gets the apartment which has a given creator thread ID. The caller must
655 * release the reference from the apartment as soon as the apartment pointer
656 * is no longer required. */
657 struct apartment * apartment_findfromtid(DWORD tid)
659 struct apartment *result = NULL, *apt;
661 EnterCriticalSection(&apt_cs);
662 LIST_FOR_EACH_ENTRY(apt, &apts, struct apartment, entry)
664 if (apt != mta && apt->tid == tid)
666 result = apt;
667 apartment_addref(result);
668 break;
672 if (!result && mta && mta->tid == tid)
674 result = mta;
675 apartment_addref(result);
678 LeaveCriticalSection(&apt_cs);
680 return result;
683 /* gets the main apartment if it exists. The caller must
684 * release the reference from the apartment as soon as the apartment pointer
685 * is no longer required. */
686 static struct apartment *apartment_findmain(void)
688 struct apartment *result;
690 EnterCriticalSection(&apt_cs);
692 result = main_sta;
693 if (result) apartment_addref(result);
695 LeaveCriticalSection(&apt_cs);
697 return result;
700 struct host_object_params
702 struct class_reg_data regdata;
703 CLSID clsid; /* clsid of object to marshal */
704 IID iid; /* interface to marshal */
705 HANDLE event; /* event signalling when ready for multi-threaded case */
706 HRESULT hr; /* result for multi-threaded case */
707 IStream *stream; /* stream that the object will be marshaled into */
708 BOOL apartment_threaded; /* is the component purely apartment-threaded? */
711 /* Returns expanded dll path from the registry or activation context. */
712 static BOOL get_object_dll_path(const struct class_reg_data *regdata, WCHAR *dst, DWORD dstlen)
714 DWORD ret;
716 if (regdata->origin == CLASS_REG_REGISTRY)
718 DWORD keytype;
719 WCHAR src[MAX_PATH];
720 DWORD dwLength = dstlen * sizeof(WCHAR);
722 if ((ret = RegQueryValueExW(regdata->u.hkey, NULL, NULL, &keytype, (BYTE*)src, &dwLength)) == ERROR_SUCCESS)
724 if (keytype == REG_EXPAND_SZ)
726 if (dstlen <= ExpandEnvironmentStringsW(src, dst, dstlen)) ret = ERROR_MORE_DATA;
728 else
730 const WCHAR *quote_start;
731 quote_start = wcschr(src, '\"');
732 if (quote_start)
734 const WCHAR *quote_end = wcschr(quote_start + 1, '\"');
735 if (quote_end)
737 memmove(src, quote_start + 1, (quote_end - quote_start - 1) * sizeof(WCHAR));
738 src[quote_end - quote_start - 1] = '\0';
741 lstrcpynW(dst, src, dstlen);
744 return !ret;
746 else
748 ULONG_PTR cookie;
750 *dst = 0;
751 ActivateActCtx(regdata->u.actctx.hactctx, &cookie);
752 ret = SearchPathW(NULL, regdata->u.actctx.module_name, L".dll", dstlen, dst, NULL);
753 DeactivateActCtx(0, cookie);
754 return *dst != 0;
758 /* gets the specified class object by loading the appropriate DLL, if
759 * necessary and calls the DllGetClassObject function for the DLL */
760 static HRESULT apartment_getclassobject(struct apartment *apt, LPCWSTR dllpath,
761 BOOL apartment_threaded,
762 REFCLSID rclsid, REFIID riid, void **ppv)
764 HRESULT hr = S_OK;
765 BOOL found = FALSE;
766 struct apartment_loaded_dll *apartment_loaded_dll;
768 if (!wcsicmp(dllpath, L"ole32.dll"))
770 HRESULT (WINAPI *p_ole32_DllGetClassObject)(REFCLSID clsid, REFIID riid, void **obj);
772 p_ole32_DllGetClassObject = (void *)GetProcAddress(GetModuleHandleW(L"ole32.dll"), "DllGetClassObject");
774 /* we don't need to control the lifetime of this dll, so use the local
775 * implementation of DllGetClassObject directly */
776 TRACE("calling ole32!DllGetClassObject\n");
777 hr = p_ole32_DllGetClassObject(rclsid, riid, ppv);
779 if (hr != S_OK)
780 ERR("DllGetClassObject returned error %#lx for dll %s\n", hr, debugstr_w(dllpath));
782 return hr;
785 EnterCriticalSection(&apt->cs);
787 LIST_FOR_EACH_ENTRY(apartment_loaded_dll, &apt->loaded_dlls, struct apartment_loaded_dll, entry)
788 if (!wcsicmp(dllpath, apartment_loaded_dll->dll->library_name))
790 TRACE("found %s already loaded\n", debugstr_w(dllpath));
791 found = TRUE;
792 break;
795 if (!found)
797 apartment_loaded_dll = malloc(sizeof(*apartment_loaded_dll));
798 if (!apartment_loaded_dll)
799 hr = E_OUTOFMEMORY;
800 if (SUCCEEDED(hr))
802 apartment_loaded_dll->unload_time = 0;
803 apartment_loaded_dll->multi_threaded = FALSE;
804 hr = apartment_add_dll(dllpath, &apartment_loaded_dll->dll);
805 if (FAILED(hr))
806 free(apartment_loaded_dll);
808 if (SUCCEEDED(hr))
810 TRACE("added new loaded dll %s\n", debugstr_w(dllpath));
811 list_add_tail(&apt->loaded_dlls, &apartment_loaded_dll->entry);
815 LeaveCriticalSection(&apt->cs);
817 if (SUCCEEDED(hr))
819 /* one component being multi-threaded overrides any number of
820 * apartment-threaded components */
821 if (!apartment_threaded)
822 apartment_loaded_dll->multi_threaded = TRUE;
824 TRACE("calling DllGetClassObject %p\n", apartment_loaded_dll->dll->DllGetClassObject);
825 /* OK: get the ClassObject */
826 hr = apartment_loaded_dll->dll->DllGetClassObject(rclsid, riid, ppv);
828 if (hr != S_OK)
829 ERR("DllGetClassObject returned error %#lx for dll %s\n", hr, debugstr_w(dllpath));
832 return hr;
835 static HRESULT apartment_hostobject(struct apartment *apt,
836 const struct host_object_params *params);
838 struct host_thread_params
840 COINIT threading_model;
841 HANDLE ready_event;
842 HWND apartment_hwnd;
845 /* thread for hosting an object to allow an object to appear to be created in
846 * an apartment with an incompatible threading model */
847 static DWORD CALLBACK apartment_hostobject_thread(void *p)
849 struct host_thread_params *params = p;
850 MSG msg;
851 HRESULT hr;
852 struct apartment *apt;
854 TRACE("\n");
856 hr = CoInitializeEx(NULL, params->threading_model);
857 if (FAILED(hr)) return hr;
859 apt = com_get_current_apt();
860 if (params->threading_model == COINIT_APARTMENTTHREADED)
862 apartment_createwindowifneeded(apt);
863 params->apartment_hwnd = apartment_getwindow(apt);
865 else
866 params->apartment_hwnd = NULL;
868 /* force the message queue to be created before signaling parent thread */
869 PeekMessageW(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
871 SetEvent(params->ready_event);
872 params = NULL; /* can't touch params after here as it may be invalid */
874 while (GetMessageW(&msg, NULL, 0, 0))
876 if (!msg.hwnd && (msg.message == DM_HOSTOBJECT))
878 struct host_object_params *obj_params = (struct host_object_params *)msg.lParam;
879 obj_params->hr = apartment_hostobject(apt, obj_params);
880 SetEvent(obj_params->event);
882 else
884 TranslateMessage(&msg);
885 DispatchMessageW(&msg);
889 TRACE("exiting\n");
891 CoUninitialize();
893 return S_OK;
896 /* finds or creates a host apartment, creates the object inside it and returns
897 * a proxy to it so that the object can be used in the apartment of the
898 * caller of this function */
899 static HRESULT apartment_hostobject_in_hostapt(struct apartment *apt, BOOL multi_threaded,
900 BOOL main_apartment, const struct class_reg_data *regdata, REFCLSID rclsid, REFIID riid, void **ppv)
902 struct host_object_params params;
903 HWND apartment_hwnd = NULL;
904 DWORD apartment_tid = 0;
905 HRESULT hr;
907 if (!multi_threaded && main_apartment)
909 struct apartment *host_apt = apartment_findmain();
910 if (host_apt)
912 apartment_hwnd = apartment_getwindow(host_apt);
913 apartment_release(host_apt);
917 if (!apartment_hwnd)
919 EnterCriticalSection(&apt->cs);
921 if (!apt->host_apt_tid)
923 struct host_thread_params thread_params;
924 HANDLE handles[2];
925 DWORD wait_value;
927 thread_params.threading_model = multi_threaded ? COINIT_MULTITHREADED : COINIT_APARTMENTTHREADED;
928 handles[0] = thread_params.ready_event = CreateEventW(NULL, FALSE, FALSE, NULL);
929 thread_params.apartment_hwnd = NULL;
930 handles[1] = CreateThread(NULL, 0, apartment_hostobject_thread, &thread_params, 0, &apt->host_apt_tid);
931 if (!handles[1])
933 CloseHandle(handles[0]);
934 LeaveCriticalSection(&apt->cs);
935 return E_OUTOFMEMORY;
937 wait_value = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
938 CloseHandle(handles[0]);
939 CloseHandle(handles[1]);
940 if (wait_value == WAIT_OBJECT_0)
941 apt->host_apt_hwnd = thread_params.apartment_hwnd;
942 else
944 LeaveCriticalSection(&apt->cs);
945 return E_OUTOFMEMORY;
949 if (multi_threaded || !main_apartment)
951 apartment_hwnd = apt->host_apt_hwnd;
952 apartment_tid = apt->host_apt_tid;
955 LeaveCriticalSection(&apt->cs);
958 /* another thread may have become the main apartment in the time it took
959 * us to create the thread for the host apartment */
960 if (!apartment_hwnd && !multi_threaded && main_apartment)
962 struct apartment *host_apt = apartment_findmain();
963 if (host_apt)
965 apartment_hwnd = apartment_getwindow(host_apt);
966 apartment_release(host_apt);
970 params.regdata = *regdata;
971 params.clsid = *rclsid;
972 params.iid = *riid;
973 hr = CreateStreamOnHGlobal(NULL, TRUE, &params.stream);
974 if (FAILED(hr))
975 return hr;
976 params.apartment_threaded = !multi_threaded;
977 if (multi_threaded)
979 params.hr = S_OK;
980 params.event = CreateEventW(NULL, FALSE, FALSE, NULL);
981 if (!PostThreadMessageW(apartment_tid, DM_HOSTOBJECT, 0, (LPARAM)&params))
982 hr = E_OUTOFMEMORY;
983 else
985 WaitForSingleObject(params.event, INFINITE);
986 hr = params.hr;
988 CloseHandle(params.event);
990 else
992 if (!apartment_hwnd)
994 ERR("host apartment didn't create window\n");
995 hr = E_OUTOFMEMORY;
997 else
998 hr = SendMessageW(apartment_hwnd, DM_HOSTOBJECT, 0, (LPARAM)&params);
1000 if (SUCCEEDED(hr))
1001 hr = CoUnmarshalInterface(params.stream, riid, ppv);
1002 IStream_Release(params.stream);
1003 return hr;
1006 static enum comclass_threadingmodel get_threading_model(const struct class_reg_data *data)
1008 if (data->origin == CLASS_REG_REGISTRY)
1010 WCHAR threading_model[10 /* lstrlenW(L"apartment")+1 */];
1011 DWORD dwLength = sizeof(threading_model);
1012 DWORD keytype;
1013 DWORD ret;
1015 ret = RegQueryValueExW(data->u.hkey, L"ThreadingModel", NULL, &keytype, (BYTE*)threading_model, &dwLength);
1016 if ((ret != ERROR_SUCCESS) || (keytype != REG_SZ))
1017 threading_model[0] = '\0';
1019 if (!wcsicmp(threading_model, L"Apartment")) return ThreadingModel_Apartment;
1020 if (!wcsicmp(threading_model, L"Free")) return ThreadingModel_Free;
1021 if (!wcsicmp(threading_model, L"Both")) return ThreadingModel_Both;
1023 /* there's not specific handling for this case */
1024 if (threading_model[0]) return ThreadingModel_Neutral;
1025 return ThreadingModel_No;
1027 else
1028 return data->u.actctx.threading_model;
1031 HRESULT apartment_get_inproc_class_object(struct apartment *apt, const struct class_reg_data *regdata,
1032 REFCLSID rclsid, REFIID riid, DWORD class_context, void **ppv)
1034 WCHAR dllpath[MAX_PATH+1];
1035 BOOL apartment_threaded;
1037 if (!(class_context & CLSCTX_PS_DLL))
1039 enum comclass_threadingmodel model = get_threading_model(regdata);
1041 if (model == ThreadingModel_Apartment)
1043 apartment_threaded = TRUE;
1044 if (apt->multi_threaded)
1045 return apartment_hostobject_in_hostapt(apt, FALSE, FALSE, regdata, rclsid, riid, ppv);
1047 else if (model == ThreadingModel_Free)
1049 apartment_threaded = FALSE;
1050 if (!apt->multi_threaded)
1051 return apartment_hostobject_in_hostapt(apt, TRUE, FALSE, regdata, rclsid, riid, ppv);
1053 /* everything except "Apartment", "Free" and "Both" */
1054 else if (model != ThreadingModel_Both)
1056 apartment_threaded = TRUE;
1057 /* everything else is main-threaded */
1058 if (model != ThreadingModel_No)
1059 FIXME("unrecognised threading model %d for object %s, should be main-threaded?\n", model, debugstr_guid(rclsid));
1061 if (apt->multi_threaded || !apt->main)
1062 return apartment_hostobject_in_hostapt(apt, FALSE, TRUE, regdata, rclsid, riid, ppv);
1064 else
1065 apartment_threaded = FALSE;
1067 else
1068 apartment_threaded = !apt->multi_threaded;
1070 if (!get_object_dll_path(regdata, dllpath, ARRAY_SIZE(dllpath)))
1072 /* failure: CLSID is not found in registry */
1073 WARN("class %s not registered inproc\n", debugstr_guid(rclsid));
1074 return REGDB_E_CLASSNOTREG;
1077 return apartment_getclassobject(apt, dllpath, apartment_threaded, rclsid, riid, ppv);
1080 static HRESULT apartment_hostobject(struct apartment *apt, const struct host_object_params *params)
1082 static const LARGE_INTEGER llZero;
1083 WCHAR dllpath[MAX_PATH+1];
1084 IUnknown *object;
1085 HRESULT hr;
1087 TRACE("clsid %s, iid %s\n", debugstr_guid(&params->clsid), debugstr_guid(&params->iid));
1089 if (!get_object_dll_path(&params->regdata, dllpath, ARRAY_SIZE(dllpath)))
1091 /* failure: CLSID is not found in registry */
1092 WARN("class %s not registered inproc\n", debugstr_guid(&params->clsid));
1093 return REGDB_E_CLASSNOTREG;
1096 hr = apartment_getclassobject(apt, dllpath, params->apartment_threaded, &params->clsid, &params->iid, (void **)&object);
1097 if (FAILED(hr))
1098 return hr;
1100 hr = CoMarshalInterface(params->stream, &params->iid, object, MSHCTX_INPROC, NULL, MSHLFLAGS_NORMAL);
1101 if (FAILED(hr))
1102 IUnknown_Release(object);
1103 IStream_Seek(params->stream, llZero, STREAM_SEEK_SET, NULL);
1105 return hr;
1108 struct dispatch_params;
1110 static LRESULT CALLBACK apartment_wndproc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1112 switch (msg)
1114 case DM_EXECUTERPC:
1115 rpc_execute_call((struct dispatch_params *)lParam);
1116 return 0;
1117 case DM_HOSTOBJECT:
1118 return apartment_hostobject(com_get_current_apt(), (const struct host_object_params *)lParam);
1119 default:
1120 return DefWindowProcW(hWnd, msg, wParam, lParam);
1124 static BOOL apartment_is_model(const struct apartment *apt, DWORD model)
1126 return (apt->multi_threaded == !(model & COINIT_APARTMENTTHREADED));
1129 HRESULT enter_apartment(struct tlsdata *data, DWORD model)
1131 HRESULT hr = S_OK;
1133 if (!data->apt)
1135 if (!apartment_get_or_create(model))
1136 return E_OUTOFMEMORY;
1138 else if (!apartment_is_model(data->apt, model))
1140 WARN("Attempt to change threading model of this apartment from %s to %s\n",
1141 data->apt->multi_threaded ? "multi-threaded" : "apartment threaded",
1142 model & COINIT_APARTMENTTHREADED ? "apartment threaded" : "multi-threaded" );
1143 return RPC_E_CHANGED_MODE;
1145 else
1146 hr = S_FALSE;
1148 data->inits++;
1150 return hr;
1153 void leave_apartment(struct tlsdata *data)
1155 if (!--data->inits)
1157 if (data->ole_inits)
1158 WARN( "Uninitializing apartment while Ole is still initialized\n" );
1159 apartment_release(data->apt);
1160 if (data->implicit_mta_cookie)
1162 apartment_decrement_mta_usage(data->implicit_mta_cookie);
1163 data->implicit_mta_cookie = NULL;
1165 data->apt = NULL;
1166 data->flags &= ~(OLETLS_DISABLE_OLE1DDE | OLETLS_APARTMENTTHREADED | OLETLS_MULTITHREADED);
1170 struct mta_cookie
1172 struct list entry;
1175 HRESULT apartment_increment_mta_usage(CO_MTA_USAGE_COOKIE *cookie)
1177 struct mta_cookie *mta_cookie;
1179 *cookie = NULL;
1181 if (!(mta_cookie = malloc(sizeof(*mta_cookie))))
1182 return E_OUTOFMEMORY;
1184 EnterCriticalSection(&apt_cs);
1186 if (mta)
1187 apartment_addref(mta);
1188 else
1189 mta = apartment_construct(COINIT_MULTITHREADED);
1190 list_add_head(&mta->usage_cookies, &mta_cookie->entry);
1192 LeaveCriticalSection(&apt_cs);
1194 *cookie = (CO_MTA_USAGE_COOKIE)mta_cookie;
1196 return S_OK;
1199 void apartment_decrement_mta_usage(CO_MTA_USAGE_COOKIE cookie)
1201 struct mta_cookie *mta_cookie = (struct mta_cookie *)cookie;
1203 EnterCriticalSection(&apt_cs);
1205 if (mta)
1207 struct mta_cookie *cur;
1209 LIST_FOR_EACH_ENTRY(cur, &mta->usage_cookies, struct mta_cookie, entry)
1211 if (mta_cookie == cur)
1213 list_remove(&cur->entry);
1214 free(cur);
1215 apartment_release(mta);
1216 break;
1221 LeaveCriticalSection(&apt_cs);
1224 static const WCHAR aptwinclassW[] = L"OleMainThreadWndClass";
1225 static ATOM apt_win_class;
1227 static BOOL WINAPI register_class( INIT_ONCE *once, void *param, void **context )
1229 WNDCLASSW wclass;
1231 /* Dispatching to the correct thread in an apartment is done through
1232 * window messages rather than RPC transports. When an interface is
1233 * marshalled into another apartment in the same process, a window of the
1234 * following class is created. The *caller* of CoMarshalInterface (i.e., the
1235 * application) is responsible for pumping the message loop in that thread.
1236 * The WM_USER messages which point to the RPCs are then dispatched to
1237 * apartment_wndproc by the user's code from the apartment in which the
1238 * interface was unmarshalled.
1240 memset(&wclass, 0, sizeof(wclass));
1241 wclass.lpfnWndProc = apartment_wndproc;
1242 wclass.hInstance = hProxyDll;
1243 wclass.lpszClassName = aptwinclassW;
1244 apt_win_class = RegisterClassW(&wclass);
1245 return TRUE;
1248 /* create a window for the apartment or return the current one if one has
1249 * already been created */
1250 HRESULT apartment_createwindowifneeded(struct apartment *apt)
1252 static INIT_ONCE class_init_once = INIT_ONCE_STATIC_INIT;
1254 if (apt->multi_threaded)
1255 return S_OK;
1257 if (!apt->win)
1259 HWND hwnd;
1261 InitOnceExecuteOnce( &class_init_once, register_class, NULL, NULL );
1263 hwnd = CreateWindowW(aptwinclassW, NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, 0, hProxyDll, NULL);
1264 if (!hwnd)
1266 ERR("CreateWindow failed with error %ld\n", GetLastError());
1267 return HRESULT_FROM_WIN32(GetLastError());
1269 if (InterlockedCompareExchangePointer((void **)&apt->win, hwnd, NULL))
1270 /* someone beat us to it */
1271 DestroyWindow(hwnd);
1274 return S_OK;
1277 /* retrieves the window for the main- or apartment-threaded apartment */
1278 HWND apartment_getwindow(const struct apartment *apt)
1280 assert(!apt->multi_threaded);
1281 return apt->win;
1284 OXID apartment_getoxid(const struct apartment *apt)
1286 return apt->oxid;
1289 void apartment_global_cleanup(void)
1291 if (apt_win_class)
1292 UnregisterClassW((const WCHAR *)MAKEINTATOM(apt_win_class), hProxyDll);
1293 apartment_release_dlls();
1294 DeleteCriticalSection(&apt_cs);
1297 HRESULT ensure_mta(void)
1299 struct apartment *apt;
1300 struct tlsdata *data;
1301 HRESULT hr;
1303 if (FAILED(hr = com_get_tlsdata(&data)))
1304 return hr;
1305 if ((apt = data->apt) && (data->implicit_mta_cookie || apt->multi_threaded))
1306 return S_OK;
1308 EnterCriticalSection(&apt_cs);
1309 if (apt || mta)
1310 hr = apartment_increment_mta_usage(&data->implicit_mta_cookie);
1311 else
1312 hr = CO_E_NOTINITIALIZED;
1313 LeaveCriticalSection(&apt_cs);
1315 if (FAILED(hr))
1317 ERR("Failed, hr %#lx.\n", hr);
1318 return hr;
1320 return S_OK;