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
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
=
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
=
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);
82 DllGetClassObjectFunc DllGetClassObject
;
83 DllCanUnloadNowFunc DllCanUnloadNow
;
87 struct apartment_loaded_dll
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 */)
109 LeaveCriticalSection(&dlls_cs
);
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
;
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
);
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
);
154 /* another caller to this function already added the dll while we
155 * weren't in the critical section */
156 FreeLibrary(hLibrary
);
160 len
= lstrlenW(library_name
);
161 entry
= malloc(sizeof(*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
;
169 entry
->DllCanUnloadNow
= DllCanUnloadNow
;
170 entry
->DllGetClassObject
= DllGetClassObject
;
171 list_add_tail(&dlls
, &entry
->entry
);
178 FreeLibrary(hLibrary
);
182 LeaveCriticalSection(&dlls_cs
);
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
);
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
);
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.
228 IServiceProvider IServiceProvider_iface
;
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
;
253 return E_NOINTERFACE
;
256 IUnknown_AddRef((IUnknown
*)*obj
);
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
);
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
);
279 assert(!local_server
->apt
);
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();
293 TRACE("%p, %s, %s, %p\n", iface
, debugstr_guid(guid
), debugstr_guid(riid
), obj
);
295 if (!local_server
->apt
)
298 if ((unk
= com_get_registered_class_object(apt
, guid
, CLSCTX_LOCAL_SERVER
)))
300 hr
= IUnknown_QueryInterface(unk
, riid
, obj
);
301 IUnknown_Release(unk
);
307 static const IServiceProviderVtbl local_server_vtbl
=
309 local_server_QueryInterface
,
311 local_server_Release
,
312 local_server_QueryService
315 HRESULT
apartment_get_local_server_stream(struct apartment
*apt
, IStream
**ret
)
319 EnterCriticalSection(&apt
->cs
);
321 if (!apt
->local_server
)
323 struct local_server
*obj
;
325 obj
= malloc(sizeof(*obj
));
328 obj
->IServiceProvider_iface
.lpVtbl
= &local_server_vtbl
;
332 hr
= CreateStreamOnHGlobal(0, TRUE
, &obj
->marshal_stream
);
335 hr
= CoMarshalInterface(obj
->marshal_stream
, &IID_IServiceProvider
, (IUnknown
*)&obj
->IServiceProvider_iface
,
336 MSHCTX_LOCAL
, NULL
, MSHLFLAGS_TABLESTRONG
);
338 IStream_Release(obj
->marshal_stream
);
342 apt
->local_server
= obj
;
351 hr
= IStream_Clone(apt
->local_server
->marshal_stream
, ret
);
353 LeaveCriticalSection(&apt
->cs
);
356 ERR("Failed: %#lx\n", 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
);
377 apt
->remunk_exported
= FALSE
;
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;
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
);
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 */
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
);
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
)
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
);
459 /* destruction stuff that needs to happen under global */
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
);
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
;
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
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
);
532 free(apartment_loaded_dll
);
535 apt
->cs
.DebugInfo
->Spare
[0] = 0;
536 DeleteCriticalSection(&apt
->cs
);
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);
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
;
557 com_get_tlsdata(&data
);
559 if (model
& COINIT_APARTMENTTHREADED
)
561 EnterCriticalSection(&apt_cs
);
563 apt
= apartment_construct(model
);
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
);
578 apartment_createwindowifneeded(apt
);
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
589 TRACE("entering the multithreaded apartment %s\n", wine_dbgstr_longlong(mta
->oxid
));
590 apartment_addref(mta
);
593 mta
= apartment_construct(model
);
595 data
->flags
|= OLETLS_MULTITHREADED
| OLETLS_DISABLE_OLE1DDE
;
599 LeaveCriticalSection(&apt_cs
);
607 struct apartment
* apartment_get_mta(void)
609 struct apartment
*apt
;
611 EnterCriticalSection(&apt_cs
);
614 apartment_addref(apt
);
616 LeaveCriticalSection(&apt_cs
);
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();
628 apartment_addref(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
)
645 apartment_addref(result
);
649 LeaveCriticalSection(&apt_cs
);
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
)
667 apartment_addref(result
);
672 if (!result
&& mta
&& mta
->tid
== tid
)
675 apartment_addref(result
);
678 LeaveCriticalSection(&apt_cs
);
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
);
693 if (result
) apartment_addref(result
);
695 LeaveCriticalSection(&apt_cs
);
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
)
716 if (regdata
->origin
== CLASS_REG_REGISTRY
)
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
;
730 const WCHAR
*quote_start
;
731 quote_start
= wcschr(src
, '\"');
734 const WCHAR
*quote_end
= wcschr(quote_start
+ 1, '\"');
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
);
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
);
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
)
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
);
780 ERR("DllGetClassObject returned error %#lx for dll %s\n", hr
, debugstr_w(dllpath
));
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
));
797 apartment_loaded_dll
= malloc(sizeof(*apartment_loaded_dll
));
798 if (!apartment_loaded_dll
)
802 apartment_loaded_dll
->unload_time
= 0;
803 apartment_loaded_dll
->multi_threaded
= FALSE
;
804 hr
= apartment_add_dll(dllpath
, &apartment_loaded_dll
->dll
);
806 free(apartment_loaded_dll
);
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
);
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
);
829 ERR("DllGetClassObject returned error %#lx for dll %s\n", hr
, debugstr_w(dllpath
));
835 static HRESULT
apartment_hostobject(struct apartment
*apt
,
836 const struct host_object_params
*params
);
838 struct host_thread_params
840 COINIT threading_model
;
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
;
852 struct apartment
*apt
;
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
);
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
);
884 TranslateMessage(&msg
);
885 DispatchMessageW(&msg
);
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;
907 if (!multi_threaded
&& main_apartment
)
909 struct apartment
*host_apt
= apartment_findmain();
912 apartment_hwnd
= apartment_getwindow(host_apt
);
913 apartment_release(host_apt
);
919 EnterCriticalSection(&apt
->cs
);
921 if (!apt
->host_apt_tid
)
923 struct host_thread_params thread_params
;
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
);
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
;
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();
965 apartment_hwnd
= apartment_getwindow(host_apt
);
966 apartment_release(host_apt
);
970 params
.regdata
= *regdata
;
971 params
.clsid
= *rclsid
;
973 hr
= CreateStreamOnHGlobal(NULL
, TRUE
, ¶ms
.stream
);
976 params
.apartment_threaded
= !multi_threaded
;
980 params
.event
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
981 if (!PostThreadMessageW(apartment_tid
, DM_HOSTOBJECT
, 0, (LPARAM
)¶ms
))
985 WaitForSingleObject(params
.event
, INFINITE
);
988 CloseHandle(params
.event
);
994 ERR("host apartment didn't create window\n");
998 hr
= SendMessageW(apartment_hwnd
, DM_HOSTOBJECT
, 0, (LPARAM
)¶ms
);
1001 hr
= CoUnmarshalInterface(params
.stream
, riid
, ppv
);
1002 IStream_Release(params
.stream
);
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
);
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
;
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
);
1065 apartment_threaded
= FALSE
;
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];
1087 TRACE("clsid %s, iid %s\n", debugstr_guid(¶ms
->clsid
), debugstr_guid(¶ms
->iid
));
1089 if (!get_object_dll_path(¶ms
->regdata
, dllpath
, ARRAY_SIZE(dllpath
)))
1091 /* failure: CLSID is not found in registry */
1092 WARN("class %s not registered inproc\n", debugstr_guid(¶ms
->clsid
));
1093 return REGDB_E_CLASSNOTREG
;
1096 hr
= apartment_getclassobject(apt
, dllpath
, params
->apartment_threaded
, ¶ms
->clsid
, ¶ms
->iid
, (void **)&object
);
1100 hr
= CoMarshalInterface(params
->stream
, ¶ms
->iid
, object
, MSHCTX_INPROC
, NULL
, MSHLFLAGS_NORMAL
);
1102 IUnknown_Release(object
);
1103 IStream_Seek(params
->stream
, llZero
, STREAM_SEEK_SET
, NULL
);
1108 struct dispatch_params
;
1110 static LRESULT CALLBACK
apartment_wndproc(HWND hWnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
1115 rpc_execute_call((struct dispatch_params
*)lParam
);
1118 return apartment_hostobject(com_get_current_apt(), (const struct host_object_params
*)lParam
);
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
)
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
;
1153 void leave_apartment(struct tlsdata
*data
)
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
;
1166 data
->flags
&= ~(OLETLS_DISABLE_OLE1DDE
| OLETLS_APARTMENTTHREADED
| OLETLS_MULTITHREADED
);
1175 HRESULT
apartment_increment_mta_usage(CO_MTA_USAGE_COOKIE
*cookie
)
1177 struct mta_cookie
*mta_cookie
;
1181 if (!(mta_cookie
= malloc(sizeof(*mta_cookie
))))
1182 return E_OUTOFMEMORY
;
1184 EnterCriticalSection(&apt_cs
);
1187 apartment_addref(mta
);
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
;
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
);
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
);
1215 apartment_release(mta
);
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
)
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
);
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
)
1261 InitOnceExecuteOnce( &class_init_once
, register_class
, NULL
, NULL
);
1263 hwnd
= CreateWindowW(aptwinclassW
, NULL
, 0, 0, 0, 0, 0, HWND_MESSAGE
, 0, hProxyDll
, NULL
);
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
);
1277 /* retrieves the window for the main- or apartment-threaded apartment */
1278 HWND
apartment_getwindow(const struct apartment
*apt
)
1280 assert(!apt
->multi_threaded
);
1284 OXID
apartment_getoxid(const struct apartment
*apt
)
1289 void apartment_global_cleanup(void)
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
;
1303 if (FAILED(hr
= com_get_tlsdata(&data
)))
1305 if ((apt
= data
->apt
) && (data
->implicit_mta_cookie
|| apt
->multi_threaded
))
1308 EnterCriticalSection(&apt_cs
);
1310 hr
= apartment_increment_mta_usage(&data
->implicit_mta_cookie
);
1312 hr
= CO_E_NOTINITIALIZED
;
1313 LeaveCriticalSection(&apt_cs
);
1317 ERR("Failed, hr %#lx.\n", hr
);