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
30 #define NONAMELESSUNION
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
=
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
=
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);
84 DllGetClassObjectFunc DllGetClassObject
;
85 DllCanUnloadNowFunc DllCanUnloadNow
;
89 struct apartment_loaded_dll
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 */)
111 LeaveCriticalSection(&dlls_cs
);
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
;
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
);
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
);
156 /* another caller to this function already added the dll while we
157 * weren't in the critical section */
158 FreeLibrary(hLibrary
);
162 len
= lstrlenW(library_name
);
163 entry
= heap_alloc(sizeof(*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
;
171 entry
->DllCanUnloadNow
= DllCanUnloadNow
;
172 entry
->DllGetClassObject
= DllGetClassObject
;
173 list_add_tail(&dlls
, &entry
->entry
);
180 FreeLibrary(hLibrary
);
184 LeaveCriticalSection(&dlls_cs
);
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
);
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
);
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.
230 IServiceProvider IServiceProvider_iface
;
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
;
255 return E_NOINTERFACE
;
258 IUnknown_AddRef((IUnknown
*)*obj
);
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
);
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
);
281 assert(!local_server
->apt
);
282 heap_free(local_server
);
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();
295 TRACE("%p, %s, %s, %p\n", iface
, debugstr_guid(guid
), debugstr_guid(riid
), obj
);
297 if (!local_server
->apt
)
300 if ((unk
= com_get_registered_class_object(apt
, guid
, CLSCTX_LOCAL_SERVER
)))
302 hr
= IUnknown_QueryInterface(unk
, riid
, obj
);
303 IUnknown_Release(unk
);
309 static const IServiceProviderVtbl local_server_vtbl
=
311 local_server_QueryInterface
,
313 local_server_Release
,
314 local_server_QueryService
317 HRESULT
apartment_get_local_server_stream(struct apartment
*apt
, IStream
**ret
)
321 EnterCriticalSection(&apt
->cs
);
323 if (!apt
->local_server
)
325 struct local_server
*obj
;
327 obj
= heap_alloc(sizeof(*obj
));
330 obj
->IServiceProvider_iface
.lpVtbl
= &local_server_vtbl
;
334 hr
= CreateStreamOnHGlobal(0, TRUE
, &obj
->marshal_stream
);
337 hr
= CoMarshalInterface(obj
->marshal_stream
, &IID_IServiceProvider
, (IUnknown
*)&obj
->IServiceProvider_iface
,
338 MSHCTX_LOCAL
, NULL
, MSHLFLAGS_TABLESTRONG
);
340 IStream_Release(obj
->marshal_stream
);
344 apt
->local_server
= obj
;
353 hr
= IStream_Clone(apt
->local_server
->marshal_stream
, ret
);
355 LeaveCriticalSection(&apt
->cs
);
358 ERR("Failed: %#x\n", 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
);
379 apt
->remunk_exported
= FALSE
;
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;
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
);
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 */
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
);
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
)
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
);
461 /* destruction stuff that needs to happen under global */
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
);
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
;
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
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
);
534 heap_free(apartment_loaded_dll
);
537 apt
->cs
.DebugInfo
->Spare
[0] = 0;
538 DeleteCriticalSection(&apt
->cs
);
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);
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
;
559 com_get_tlsdata(&data
);
561 if (model
& COINIT_APARTMENTTHREADED
)
563 EnterCriticalSection(&apt_cs
);
565 apt
= apartment_construct(model
);
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
);
580 apartment_createwindowifneeded(apt
);
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
591 TRACE("entering the multithreaded apartment %s\n", wine_dbgstr_longlong(mta
->oxid
));
592 apartment_addref(mta
);
595 mta
= apartment_construct(model
);
597 data
->flags
|= OLETLS_MULTITHREADED
| OLETLS_DISABLE_OLE1DDE
;
601 LeaveCriticalSection(&apt_cs
);
609 struct apartment
* apartment_get_mta(void)
611 struct apartment
*apt
;
613 EnterCriticalSection(&apt_cs
);
616 apartment_addref(apt
);
618 LeaveCriticalSection(&apt_cs
);
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();
630 apartment_addref(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
)
647 apartment_addref(result
);
651 LeaveCriticalSection(&apt_cs
);
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
)
669 apartment_addref(result
);
673 LeaveCriticalSection(&apt_cs
);
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
);
688 if (result
) apartment_addref(result
);
690 LeaveCriticalSection(&apt_cs
);
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
)
711 if (regdata
->origin
== CLASS_REG_REGISTRY
)
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
;
725 const WCHAR
*quote_start
;
726 quote_start
= wcschr(src
, '\"');
729 const WCHAR
*quote_end
= wcschr(quote_start
+ 1, '\"');
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
);
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
);
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
)
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
);
775 ERR("DllGetClassObject returned error 0x%08x for dll %s\n", hr
, debugstr_w(dllpath
));
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
));
792 apartment_loaded_dll
= heap_alloc(sizeof(*apartment_loaded_dll
));
793 if (!apartment_loaded_dll
)
797 apartment_loaded_dll
->unload_time
= 0;
798 apartment_loaded_dll
->multi_threaded
= FALSE
;
799 hr
= apartment_add_dll(dllpath
, &apartment_loaded_dll
->dll
);
801 heap_free(apartment_loaded_dll
);
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
);
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
);
824 ERR("DllGetClassObject returned error 0x%08x for dll %s\n", hr
, debugstr_w(dllpath
));
830 static HRESULT
apartment_hostobject(struct apartment
*apt
,
831 const struct host_object_params
*params
);
833 struct host_thread_params
835 COINIT threading_model
;
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
;
847 struct apartment
*apt
;
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
);
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
);
879 TranslateMessage(&msg
);
880 DispatchMessageW(&msg
);
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;
902 if (!multi_threaded
&& main_apartment
)
904 struct apartment
*host_apt
= apartment_findmain();
907 apartment_hwnd
= apartment_getwindow(host_apt
);
908 apartment_release(host_apt
);
914 EnterCriticalSection(&apt
->cs
);
916 if (!apt
->host_apt_tid
)
918 struct host_thread_params thread_params
;
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
);
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
;
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();
960 apartment_hwnd
= apartment_getwindow(host_apt
);
961 apartment_release(host_apt
);
965 params
.regdata
= *regdata
;
966 params
.clsid
= *rclsid
;
968 hr
= CreateStreamOnHGlobal(NULL
, TRUE
, ¶ms
.stream
);
971 params
.apartment_threaded
= !multi_threaded
;
975 params
.event
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
976 if (!PostThreadMessageW(apartment_tid
, DM_HOSTOBJECT
, 0, (LPARAM
)¶ms
))
980 WaitForSingleObject(params
.event
, INFINITE
);
983 CloseHandle(params
.event
);
989 ERR("host apartment didn't create window\n");
993 hr
= SendMessageW(apartment_hwnd
, DM_HOSTOBJECT
, 0, (LPARAM
)¶ms
);
996 hr
= CoUnmarshalInterface(params
.stream
, riid
, ppv
);
997 IStream_Release(params
.stream
);
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
);
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
;
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
);
1060 apartment_threaded
= FALSE
;
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];
1082 TRACE("clsid %s, iid %s\n", debugstr_guid(¶ms
->clsid
), debugstr_guid(¶ms
->iid
));
1084 if (!get_object_dll_path(¶ms
->regdata
, dllpath
, ARRAY_SIZE(dllpath
)))
1086 /* failure: CLSID is not found in registry */
1087 WARN("class %s not registered inproc\n", debugstr_guid(¶ms
->clsid
));
1088 return REGDB_E_CLASSNOTREG
;
1091 hr
= apartment_getclassobject(apt
, dllpath
, params
->apartment_threaded
, ¶ms
->clsid
, ¶ms
->iid
, (void **)&object
);
1095 hr
= CoMarshalInterface(params
->stream
, ¶ms
->iid
, object
, MSHCTX_INPROC
, NULL
, MSHLFLAGS_NORMAL
);
1097 IUnknown_Release(object
);
1098 IStream_Seek(params
->stream
, llZero
, STREAM_SEEK_SET
, NULL
);
1103 struct dispatch_params
;
1105 static LRESULT CALLBACK
apartment_wndproc(HWND hWnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
1110 rpc_execute_call((struct dispatch_params
*)lParam
);
1113 return apartment_hostobject(com_get_current_apt(), (const struct host_object_params
*)lParam
);
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
)
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
;
1148 void leave_apartment(struct tlsdata
*data
)
1152 if (data
->ole_inits
)
1153 WARN( "Uninitializing apartment while Ole is still initialized\n" );
1154 apartment_release(data
->apt
);
1156 data
->flags
&= ~(OLETLS_DISABLE_OLE1DDE
| OLETLS_APARTMENTTHREADED
| OLETLS_MULTITHREADED
);
1165 HRESULT
apartment_increment_mta_usage(CO_MTA_USAGE_COOKIE
*cookie
)
1167 struct mta_cookie
*mta_cookie
;
1171 if (!(mta_cookie
= heap_alloc(sizeof(*mta_cookie
))))
1172 return E_OUTOFMEMORY
;
1174 EnterCriticalSection(&apt_cs
);
1177 apartment_addref(mta
);
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
;
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
);
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
);
1205 apartment_release(mta
);
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
)
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
);
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
)
1251 InitOnceExecuteOnce( &class_init_once
, register_class
, NULL
, NULL
);
1253 hwnd
= CreateWindowW(aptwinclassW
, NULL
, 0, 0, 0, 0, 0, HWND_MESSAGE
, 0, hProxyDll
, NULL
);
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
);
1267 /* retrieves the window for the main- or apartment-threaded apartment */
1268 HWND
apartment_getwindow(const struct apartment
*apt
)
1270 assert(!apt
->multi_threaded
);
1274 OXID
apartment_getoxid(const struct apartment
*apt
)
1279 void apartment_global_cleanup(void)
1282 UnregisterClassW((const WCHAR
*)MAKEINTATOM(apt_win_class
), hProxyDll
);
1283 apartment_release_dlls();
1284 DeleteCriticalSection(&apt_cs
);