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 %ld\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 %ld\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: %#lx\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 %ld\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 = %ld\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 = %ld\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
)
666 if (apt
!= mta
&& apt
->tid
== tid
)
669 apartment_addref(result
);
674 if (!result
&& mta
&& mta
->tid
== tid
)
677 apartment_addref(result
);
680 LeaveCriticalSection(&apt_cs
);
685 /* gets the main apartment if it exists. The caller must
686 * release the reference from the apartment as soon as the apartment pointer
687 * is no longer required. */
688 static struct apartment
*apartment_findmain(void)
690 struct apartment
*result
;
692 EnterCriticalSection(&apt_cs
);
695 if (result
) apartment_addref(result
);
697 LeaveCriticalSection(&apt_cs
);
702 struct host_object_params
704 struct class_reg_data regdata
;
705 CLSID clsid
; /* clsid of object to marshal */
706 IID iid
; /* interface to marshal */
707 HANDLE event
; /* event signalling when ready for multi-threaded case */
708 HRESULT hr
; /* result for multi-threaded case */
709 IStream
*stream
; /* stream that the object will be marshaled into */
710 BOOL apartment_threaded
; /* is the component purely apartment-threaded? */
713 /* Returns expanded dll path from the registry or activation context. */
714 static BOOL
get_object_dll_path(const struct class_reg_data
*regdata
, WCHAR
*dst
, DWORD dstlen
)
718 if (regdata
->origin
== CLASS_REG_REGISTRY
)
722 DWORD dwLength
= dstlen
* sizeof(WCHAR
);
724 if ((ret
= RegQueryValueExW(regdata
->u
.hkey
, NULL
, NULL
, &keytype
, (BYTE
*)src
, &dwLength
)) == ERROR_SUCCESS
)
726 if (keytype
== REG_EXPAND_SZ
)
728 if (dstlen
<= ExpandEnvironmentStringsW(src
, dst
, dstlen
)) ret
= ERROR_MORE_DATA
;
732 const WCHAR
*quote_start
;
733 quote_start
= wcschr(src
, '\"');
736 const WCHAR
*quote_end
= wcschr(quote_start
+ 1, '\"');
739 memmove(src
, quote_start
+ 1, (quote_end
- quote_start
- 1) * sizeof(WCHAR
));
740 src
[quote_end
- quote_start
- 1] = '\0';
743 lstrcpynW(dst
, src
, dstlen
);
753 ActivateActCtx(regdata
->u
.actctx
.hactctx
, &cookie
);
754 ret
= SearchPathW(NULL
, regdata
->u
.actctx
.module_name
, L
".dll", dstlen
, dst
, NULL
);
755 DeactivateActCtx(0, cookie
);
760 /* gets the specified class object by loading the appropriate DLL, if
761 * necessary and calls the DllGetClassObject function for the DLL */
762 static HRESULT
apartment_getclassobject(struct apartment
*apt
, LPCWSTR dllpath
,
763 BOOL apartment_threaded
,
764 REFCLSID rclsid
, REFIID riid
, void **ppv
)
768 struct apartment_loaded_dll
*apartment_loaded_dll
;
770 if (!wcsicmp(dllpath
, L
"ole32.dll"))
772 HRESULT (WINAPI
*p_ole32_DllGetClassObject
)(REFCLSID clsid
, REFIID riid
, void **obj
);
774 p_ole32_DllGetClassObject
= (void *)GetProcAddress(GetModuleHandleW(L
"ole32.dll"), "DllGetClassObject");
776 /* we don't need to control the lifetime of this dll, so use the local
777 * implementation of DllGetClassObject directly */
778 TRACE("calling ole32!DllGetClassObject\n");
779 hr
= p_ole32_DllGetClassObject(rclsid
, riid
, ppv
);
782 ERR("DllGetClassObject returned error %#lx for dll %s\n", hr
, debugstr_w(dllpath
));
787 EnterCriticalSection(&apt
->cs
);
789 LIST_FOR_EACH_ENTRY(apartment_loaded_dll
, &apt
->loaded_dlls
, struct apartment_loaded_dll
, entry
)
790 if (!wcsicmp(dllpath
, apartment_loaded_dll
->dll
->library_name
))
792 TRACE("found %s already loaded\n", debugstr_w(dllpath
));
799 apartment_loaded_dll
= heap_alloc(sizeof(*apartment_loaded_dll
));
800 if (!apartment_loaded_dll
)
804 apartment_loaded_dll
->unload_time
= 0;
805 apartment_loaded_dll
->multi_threaded
= FALSE
;
806 hr
= apartment_add_dll(dllpath
, &apartment_loaded_dll
->dll
);
808 heap_free(apartment_loaded_dll
);
812 TRACE("added new loaded dll %s\n", debugstr_w(dllpath
));
813 list_add_tail(&apt
->loaded_dlls
, &apartment_loaded_dll
->entry
);
817 LeaveCriticalSection(&apt
->cs
);
821 /* one component being multi-threaded overrides any number of
822 * apartment-threaded components */
823 if (!apartment_threaded
)
824 apartment_loaded_dll
->multi_threaded
= TRUE
;
826 TRACE("calling DllGetClassObject %p\n", apartment_loaded_dll
->dll
->DllGetClassObject
);
827 /* OK: get the ClassObject */
828 hr
= apartment_loaded_dll
->dll
->DllGetClassObject(rclsid
, riid
, ppv
);
831 ERR("DllGetClassObject returned error %#lx for dll %s\n", hr
, debugstr_w(dllpath
));
837 static HRESULT
apartment_hostobject(struct apartment
*apt
,
838 const struct host_object_params
*params
);
840 struct host_thread_params
842 COINIT threading_model
;
847 /* thread for hosting an object to allow an object to appear to be created in
848 * an apartment with an incompatible threading model */
849 static DWORD CALLBACK
apartment_hostobject_thread(void *p
)
851 struct host_thread_params
*params
= p
;
854 struct apartment
*apt
;
858 hr
= CoInitializeEx(NULL
, params
->threading_model
);
859 if (FAILED(hr
)) return hr
;
861 apt
= com_get_current_apt();
862 if (params
->threading_model
== COINIT_APARTMENTTHREADED
)
864 apartment_createwindowifneeded(apt
);
865 params
->apartment_hwnd
= apartment_getwindow(apt
);
868 params
->apartment_hwnd
= NULL
;
870 /* force the message queue to be created before signaling parent thread */
871 PeekMessageW(&msg
, NULL
, WM_USER
, WM_USER
, PM_NOREMOVE
);
873 SetEvent(params
->ready_event
);
874 params
= NULL
; /* can't touch params after here as it may be invalid */
876 while (GetMessageW(&msg
, NULL
, 0, 0))
878 if (!msg
.hwnd
&& (msg
.message
== DM_HOSTOBJECT
))
880 struct host_object_params
*obj_params
= (struct host_object_params
*)msg
.lParam
;
881 obj_params
->hr
= apartment_hostobject(apt
, obj_params
);
882 SetEvent(obj_params
->event
);
886 TranslateMessage(&msg
);
887 DispatchMessageW(&msg
);
898 /* finds or creates a host apartment, creates the object inside it and returns
899 * a proxy to it so that the object can be used in the apartment of the
900 * caller of this function */
901 static HRESULT
apartment_hostobject_in_hostapt(struct apartment
*apt
, BOOL multi_threaded
,
902 BOOL main_apartment
, const struct class_reg_data
*regdata
, REFCLSID rclsid
, REFIID riid
, void **ppv
)
904 struct host_object_params params
;
905 HWND apartment_hwnd
= NULL
;
906 DWORD apartment_tid
= 0;
909 if (!multi_threaded
&& main_apartment
)
911 struct apartment
*host_apt
= apartment_findmain();
914 apartment_hwnd
= apartment_getwindow(host_apt
);
915 apartment_release(host_apt
);
921 EnterCriticalSection(&apt
->cs
);
923 if (!apt
->host_apt_tid
)
925 struct host_thread_params thread_params
;
929 thread_params
.threading_model
= multi_threaded
? COINIT_MULTITHREADED
: COINIT_APARTMENTTHREADED
;
930 handles
[0] = thread_params
.ready_event
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
931 thread_params
.apartment_hwnd
= NULL
;
932 handles
[1] = CreateThread(NULL
, 0, apartment_hostobject_thread
, &thread_params
, 0, &apt
->host_apt_tid
);
935 CloseHandle(handles
[0]);
936 LeaveCriticalSection(&apt
->cs
);
937 return E_OUTOFMEMORY
;
939 wait_value
= WaitForMultipleObjects(2, handles
, FALSE
, INFINITE
);
940 CloseHandle(handles
[0]);
941 CloseHandle(handles
[1]);
942 if (wait_value
== WAIT_OBJECT_0
)
943 apt
->host_apt_hwnd
= thread_params
.apartment_hwnd
;
946 LeaveCriticalSection(&apt
->cs
);
947 return E_OUTOFMEMORY
;
951 if (multi_threaded
|| !main_apartment
)
953 apartment_hwnd
= apt
->host_apt_hwnd
;
954 apartment_tid
= apt
->host_apt_tid
;
957 LeaveCriticalSection(&apt
->cs
);
960 /* another thread may have become the main apartment in the time it took
961 * us to create the thread for the host apartment */
962 if (!apartment_hwnd
&& !multi_threaded
&& main_apartment
)
964 struct apartment
*host_apt
= apartment_findmain();
967 apartment_hwnd
= apartment_getwindow(host_apt
);
968 apartment_release(host_apt
);
972 params
.regdata
= *regdata
;
973 params
.clsid
= *rclsid
;
975 hr
= CreateStreamOnHGlobal(NULL
, TRUE
, ¶ms
.stream
);
978 params
.apartment_threaded
= !multi_threaded
;
982 params
.event
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
983 if (!PostThreadMessageW(apartment_tid
, DM_HOSTOBJECT
, 0, (LPARAM
)¶ms
))
987 WaitForSingleObject(params
.event
, INFINITE
);
990 CloseHandle(params
.event
);
996 ERR("host apartment didn't create window\n");
1000 hr
= SendMessageW(apartment_hwnd
, DM_HOSTOBJECT
, 0, (LPARAM
)¶ms
);
1003 hr
= CoUnmarshalInterface(params
.stream
, riid
, ppv
);
1004 IStream_Release(params
.stream
);
1008 static enum comclass_threadingmodel
get_threading_model(const struct class_reg_data
*data
)
1010 if (data
->origin
== CLASS_REG_REGISTRY
)
1012 WCHAR threading_model
[10 /* lstrlenW(L"apartment")+1 */];
1013 DWORD dwLength
= sizeof(threading_model
);
1017 ret
= RegQueryValueExW(data
->u
.hkey
, L
"ThreadingModel", NULL
, &keytype
, (BYTE
*)threading_model
, &dwLength
);
1018 if ((ret
!= ERROR_SUCCESS
) || (keytype
!= REG_SZ
))
1019 threading_model
[0] = '\0';
1021 if (!wcsicmp(threading_model
, L
"Apartment")) return ThreadingModel_Apartment
;
1022 if (!wcsicmp(threading_model
, L
"Free")) return ThreadingModel_Free
;
1023 if (!wcsicmp(threading_model
, L
"Both")) return ThreadingModel_Both
;
1025 /* there's not specific handling for this case */
1026 if (threading_model
[0]) return ThreadingModel_Neutral
;
1027 return ThreadingModel_No
;
1030 return data
->u
.actctx
.threading_model
;
1033 HRESULT
apartment_get_inproc_class_object(struct apartment
*apt
, const struct class_reg_data
*regdata
,
1034 REFCLSID rclsid
, REFIID riid
, DWORD class_context
, void **ppv
)
1036 WCHAR dllpath
[MAX_PATH
+1];
1037 BOOL apartment_threaded
;
1039 if (!(class_context
& CLSCTX_PS_DLL
))
1041 enum comclass_threadingmodel model
= get_threading_model(regdata
);
1043 if (model
== ThreadingModel_Apartment
)
1045 apartment_threaded
= TRUE
;
1046 if (apt
->multi_threaded
)
1047 return apartment_hostobject_in_hostapt(apt
, FALSE
, FALSE
, regdata
, rclsid
, riid
, ppv
);
1049 else if (model
== ThreadingModel_Free
)
1051 apartment_threaded
= FALSE
;
1052 if (!apt
->multi_threaded
)
1053 return apartment_hostobject_in_hostapt(apt
, TRUE
, FALSE
, regdata
, rclsid
, riid
, ppv
);
1055 /* everything except "Apartment", "Free" and "Both" */
1056 else if (model
!= ThreadingModel_Both
)
1058 apartment_threaded
= TRUE
;
1059 /* everything else is main-threaded */
1060 if (model
!= ThreadingModel_No
)
1061 FIXME("unrecognised threading model %d for object %s, should be main-threaded?\n", model
, debugstr_guid(rclsid
));
1063 if (apt
->multi_threaded
|| !apt
->main
)
1064 return apartment_hostobject_in_hostapt(apt
, FALSE
, TRUE
, regdata
, rclsid
, riid
, ppv
);
1067 apartment_threaded
= FALSE
;
1070 apartment_threaded
= !apt
->multi_threaded
;
1072 if (!get_object_dll_path(regdata
, dllpath
, ARRAY_SIZE(dllpath
)))
1074 /* failure: CLSID is not found in registry */
1075 WARN("class %s not registered inproc\n", debugstr_guid(rclsid
));
1076 return REGDB_E_CLASSNOTREG
;
1079 return apartment_getclassobject(apt
, dllpath
, apartment_threaded
, rclsid
, riid
, ppv
);
1082 static HRESULT
apartment_hostobject(struct apartment
*apt
, const struct host_object_params
*params
)
1084 static const LARGE_INTEGER llZero
;
1085 WCHAR dllpath
[MAX_PATH
+1];
1089 TRACE("clsid %s, iid %s\n", debugstr_guid(¶ms
->clsid
), debugstr_guid(¶ms
->iid
));
1091 if (!get_object_dll_path(¶ms
->regdata
, dllpath
, ARRAY_SIZE(dllpath
)))
1093 /* failure: CLSID is not found in registry */
1094 WARN("class %s not registered inproc\n", debugstr_guid(¶ms
->clsid
));
1095 return REGDB_E_CLASSNOTREG
;
1098 hr
= apartment_getclassobject(apt
, dllpath
, params
->apartment_threaded
, ¶ms
->clsid
, ¶ms
->iid
, (void **)&object
);
1102 hr
= CoMarshalInterface(params
->stream
, ¶ms
->iid
, object
, MSHCTX_INPROC
, NULL
, MSHLFLAGS_NORMAL
);
1104 IUnknown_Release(object
);
1105 IStream_Seek(params
->stream
, llZero
, STREAM_SEEK_SET
, NULL
);
1110 struct dispatch_params
;
1112 static LRESULT CALLBACK
apartment_wndproc(HWND hWnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
1117 rpc_execute_call((struct dispatch_params
*)lParam
);
1120 return apartment_hostobject(com_get_current_apt(), (const struct host_object_params
*)lParam
);
1122 return DefWindowProcW(hWnd
, msg
, wParam
, lParam
);
1126 static BOOL
apartment_is_model(const struct apartment
*apt
, DWORD model
)
1128 return (apt
->multi_threaded
== !(model
& COINIT_APARTMENTTHREADED
));
1131 HRESULT
enter_apartment(struct tlsdata
*data
, DWORD model
)
1137 if (!apartment_get_or_create(model
))
1138 return E_OUTOFMEMORY
;
1140 else if (!apartment_is_model(data
->apt
, model
))
1142 WARN("Attempt to change threading model of this apartment from %s to %s\n",
1143 data
->apt
->multi_threaded
? "multi-threaded" : "apartment threaded",
1144 model
& COINIT_APARTMENTTHREADED
? "apartment threaded" : "multi-threaded" );
1145 return RPC_E_CHANGED_MODE
;
1155 void leave_apartment(struct tlsdata
*data
)
1159 if (data
->ole_inits
)
1160 WARN( "Uninitializing apartment while Ole is still initialized\n" );
1161 apartment_release(data
->apt
);
1163 data
->flags
&= ~(OLETLS_DISABLE_OLE1DDE
| OLETLS_APARTMENTTHREADED
| OLETLS_MULTITHREADED
);
1172 HRESULT
apartment_increment_mta_usage(CO_MTA_USAGE_COOKIE
*cookie
)
1174 struct mta_cookie
*mta_cookie
;
1178 if (!(mta_cookie
= heap_alloc(sizeof(*mta_cookie
))))
1179 return E_OUTOFMEMORY
;
1181 EnterCriticalSection(&apt_cs
);
1184 apartment_addref(mta
);
1186 mta
= apartment_construct(COINIT_MULTITHREADED
);
1187 list_add_head(&mta
->usage_cookies
, &mta_cookie
->entry
);
1189 LeaveCriticalSection(&apt_cs
);
1191 *cookie
= (CO_MTA_USAGE_COOKIE
)mta_cookie
;
1196 void apartment_decrement_mta_usage(CO_MTA_USAGE_COOKIE cookie
)
1198 struct mta_cookie
*mta_cookie
= (struct mta_cookie
*)cookie
;
1200 EnterCriticalSection(&apt_cs
);
1204 struct mta_cookie
*cur
;
1206 LIST_FOR_EACH_ENTRY(cur
, &mta
->usage_cookies
, struct mta_cookie
, entry
)
1208 if (mta_cookie
== cur
)
1210 list_remove(&cur
->entry
);
1212 apartment_release(mta
);
1218 LeaveCriticalSection(&apt_cs
);
1221 static const WCHAR aptwinclassW
[] = L
"OleMainThreadWndClass";
1222 static ATOM apt_win_class
;
1224 static BOOL WINAPI
register_class( INIT_ONCE
*once
, void *param
, void **context
)
1228 /* Dispatching to the correct thread in an apartment is done through
1229 * window messages rather than RPC transports. When an interface is
1230 * marshalled into another apartment in the same process, a window of the
1231 * following class is created. The *caller* of CoMarshalInterface (i.e., the
1232 * application) is responsible for pumping the message loop in that thread.
1233 * The WM_USER messages which point to the RPCs are then dispatched to
1234 * apartment_wndproc by the user's code from the apartment in which the
1235 * interface was unmarshalled.
1237 memset(&wclass
, 0, sizeof(wclass
));
1238 wclass
.lpfnWndProc
= apartment_wndproc
;
1239 wclass
.hInstance
= hProxyDll
;
1240 wclass
.lpszClassName
= aptwinclassW
;
1241 apt_win_class
= RegisterClassW(&wclass
);
1245 /* create a window for the apartment or return the current one if one has
1246 * already been created */
1247 HRESULT
apartment_createwindowifneeded(struct apartment
*apt
)
1249 static INIT_ONCE class_init_once
= INIT_ONCE_STATIC_INIT
;
1251 if (apt
->multi_threaded
)
1258 InitOnceExecuteOnce( &class_init_once
, register_class
, NULL
, NULL
);
1260 hwnd
= CreateWindowW(aptwinclassW
, NULL
, 0, 0, 0, 0, 0, HWND_MESSAGE
, 0, hProxyDll
, NULL
);
1263 ERR("CreateWindow failed with error %ld\n", GetLastError());
1264 return HRESULT_FROM_WIN32(GetLastError());
1266 if (InterlockedCompareExchangePointer((void **)&apt
->win
, hwnd
, NULL
))
1267 /* someone beat us to it */
1268 DestroyWindow(hwnd
);
1274 /* retrieves the window for the main- or apartment-threaded apartment */
1275 HWND
apartment_getwindow(const struct apartment
*apt
)
1277 assert(!apt
->multi_threaded
);
1281 OXID
apartment_getoxid(const struct apartment
*apt
)
1286 void apartment_global_cleanup(void)
1289 UnregisterClassW((const WCHAR
*)MAKEINTATOM(apt_win_class
), hProxyDll
);
1290 apartment_release_dlls();
1291 DeleteCriticalSection(&apt_cs
);