It's necessary to introduce the being_destroyed field since a destroyed apartment may end up releasing proxy objects, which will require calling various functions, e.g. CoGetPSClsid(), potentially triggering an infinite recursion otherwise.
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/ole32/compobj.c | 165 +++++++++++++++++++++++++------------------ dlls/ole32/compobj_private.h | 1 + dlls/ole32/tests/compobj.c | 23 ++++++ 3 files changed, 120 insertions(+), 69 deletions(-)
diff --git a/dlls/ole32/compobj.c b/dlls/ole32/compobj.c index cd0e67f..61a8ae6 100644 --- a/dlls/ole32/compobj.c +++ b/dlls/ole32/compobj.c @@ -717,6 +717,44 @@ static inline BOOL apartment_is_model(const APARTMENT *apt, DWORD model) return (apt->multi_threaded == !(model & COINIT_APARTMENTTHREADED)); }
+/* gets the multi-threaded apartment if it exists. The caller must + * release the reference from the apartment as soon as the apartment pointer + * is no longer required. */ +static APARTMENT *apartment_find_multi_threaded(void) +{ + APARTMENT *result = NULL; + struct list *cursor; + + EnterCriticalSection(&csApartment); + + LIST_FOR_EACH( cursor, &apts ) + { + struct apartment *apt = LIST_ENTRY( cursor, struct apartment, entry ); + if (apt->multi_threaded) + { + result = apt; + apartment_addref(result); + break; + } + } + + LeaveCriticalSection(&csApartment); + return result; +} + +/* Return the current apartment if it exists, or, failing that, the MTA. Caller + * must free the returned apartment in either case. */ +static APARTMENT *apartment_get_current_or_mta(void) +{ + APARTMENT *apt = COM_CurrentApt(); + if (apt) + { + apartment_addref(apt); + return apt; + } + return apartment_find_multi_threaded(); +} + static void COM_RevokeRegisteredClassObject(RegisteredClass *curClass) { list_remove(&curClass->entry); @@ -1059,8 +1097,7 @@ HRESULT WINAPI DECLSPEC_HOTPATCH CoRevokeClassObject(
TRACE("(%08x)\n",dwRegister);
- apt = COM_CurrentApt(); - if (!apt) + if (!(apt = apartment_get_current_or_mta())) { ERR("COM was not initialized\n"); return CO_E_NOTINITIALIZED; @@ -1091,7 +1128,7 @@ HRESULT WINAPI DECLSPEC_HOTPATCH CoRevokeClassObject( }
LeaveCriticalSection( &csRegisteredClassList ); - + apartment_release(apt); return hr; }
@@ -1145,9 +1182,17 @@ DWORD apartment_release(struct apartment *apt)
ret = InterlockedDecrement(&apt->refs); TRACE("%s: after = %d\n", wine_dbgstr_longlong(apt->oxid), ret); + + if (apt->being_destroyed) + { + LeaveCriticalSection(&csApartment); + return ret; + } + /* destruction stuff that needs to happen under csApartment CS */ if (ret == 0) { + apt->being_destroyed = TRUE; if (apt == MTA) MTA = NULL; else if (apt == MainApartment) MainApartment = NULL; list_remove(&apt->entry); @@ -1295,31 +1340,6 @@ static APARTMENT *apartment_findmain(void) return result; }
-/* gets the multi-threaded apartment if it exists. The caller must - * release the reference from the apartment as soon as the apartment pointer - * is no longer required. */ -static APARTMENT *apartment_find_multi_threaded(void) -{ - APARTMENT *result = NULL; - struct list *cursor; - - EnterCriticalSection(&csApartment); - - LIST_FOR_EACH( cursor, &apts ) - { - struct apartment *apt = LIST_ENTRY( cursor, struct apartment, entry ); - if (apt->multi_threaded) - { - result = apt; - apartment_addref(result); - break; - } - } - - LeaveCriticalSection(&csApartment); - return result; -} - /* gets the specified class object by loading the appropriate DLL, if * necessary and calls the DllGetClassObject function for the DLL */ static HRESULT apartment_getclassobject(struct apartment *apt, LPCWSTR dllpath, @@ -2059,9 +2079,11 @@ HRESULT WINAPI CoDisconnectObject( LPUNKNOWN lpUnk, DWORD reserved ) return hr; }
- apt = COM_CurrentApt(); - if (!apt) + if (!(apt = apartment_get_current_or_mta())) + { + ERR("apartment not initialised\n"); return CO_E_NOTINITIALIZED; + }
manager = get_stub_manager_from_object(apt, lpUnk, FALSE); if (manager) { @@ -2076,6 +2098,7 @@ HRESULT WINAPI CoDisconnectObject( LPUNKNOWN lpUnk, DWORD reserved ) * not found, making apps think that the object was disconnected, when * it actually wasn't */
+ apartment_release(apt); return S_OK; }
@@ -2583,7 +2606,7 @@ HRESULT WINAPI CoGetPSClsid(REFIID riid, CLSID *pclsid) static const WCHAR wszInterface[] = {'I','n','t','e','r','f','a','c','e','\',0}; static const WCHAR wszPSC[] = {'\','P','r','o','x','y','S','t','u','b','C','l','s','i','d','3','2',0}; WCHAR path[ARRAYSIZE(wszInterface) - 1 + CHARS_IN_GUID - 1 + ARRAYSIZE(wszPSC)]; - APARTMENT *apt = COM_CurrentApt(); + APARTMENT *apt; struct registered_psclsid *registered_psclsid; ACTCTX_SECTION_KEYED_DATA data; HRESULT hr; @@ -2592,11 +2615,12 @@ HRESULT WINAPI CoGetPSClsid(REFIID riid, CLSID *pclsid)
TRACE("() riid=%s, pclsid=%p\n", debugstr_guid(riid), pclsid);
- if (!apt) + if (!(apt = apartment_get_current_or_mta())) { ERR("apartment not initialised\n"); return CO_E_NOTINITIALIZED; } + apartment_release(apt);
if (!pclsid) return E_INVALIDARG; @@ -2667,16 +2691,17 @@ HRESULT WINAPI CoGetPSClsid(REFIID riid, CLSID *pclsid) */ HRESULT WINAPI CoRegisterPSClsid(REFIID riid, REFCLSID rclsid) { - APARTMENT *apt = COM_CurrentApt(); + APARTMENT *apt; struct registered_psclsid *registered_psclsid;
TRACE("(%s, %s)\n", debugstr_guid(riid), debugstr_guid(rclsid));
- if (!apt) + if (!(apt = apartment_get_current_or_mta())) { ERR("apartment not initialised\n"); return CO_E_NOTINITIALIZED; } + apartment_release(apt);
EnterCriticalSection(&cs_registered_psclsid_list);
@@ -2802,11 +2827,10 @@ HRESULT WINAPI CoRegisterClassObject( if ( (lpdwRegister==0) || (pUnk==0) ) return E_INVALIDARG;
- apt = COM_CurrentApt(); - if (!apt) + if (!(apt = apartment_get_current_or_mta())) { - ERR("COM was not initialized\n"); - return CO_E_NOTINITIALIZED; + ERR("apartment not initialised\n"); + return CO_E_NOTINITIALIZED; }
*lpdwRegister = 0; @@ -2826,16 +2850,21 @@ HRESULT WINAPI CoRegisterClassObject( if (dwClsContext & CLSCTX_LOCAL_SERVER) hr = CoLockObjectExternal(foundObject, TRUE, FALSE); IUnknown_Release(foundObject); + apartment_release(apt); return hr; } IUnknown_Release(foundObject); ERR("object already registered for class %s\n", debugstr_guid(rclsid)); + apartment_release(apt); return CO_E_OBJISREG; }
newClass = HeapAlloc(GetProcessHeap(), 0, sizeof(RegisteredClass)); if ( newClass == NULL ) + { + apartment_release(apt); return E_OUTOFMEMORY; + }
newClass->classIdentifier = *rclsid; newClass->apartment_id = apt->oxid; @@ -2864,7 +2893,10 @@ HRESULT WINAPI CoRegisterClassObject(
hr = get_local_server_stream(apt, &marshal_stream); if(FAILED(hr)) + { + apartment_release(apt); return hr; + }
hr = RPC_StartLocalServer(&newClass->classIdentifier, marshal_stream, @@ -2872,6 +2904,7 @@ HRESULT WINAPI CoRegisterClassObject( &newClass->RpcRegistration); IStream_Release(marshal_stream); } + apartment_release(apt); return S_OK; }
@@ -2989,7 +3022,6 @@ HRESULT WINAPI DECLSPEC_HOTPATCH CoGetClassObject( IUnknown *regClassObject; HRESULT hres = E_UNEXPECTED; APARTMENT *apt; - BOOL release_apt = FALSE;
TRACE("CLSID: %s,IID: %s\n", debugstr_guid(rclsid), debugstr_guid(iid));
@@ -2998,14 +3030,10 @@ HRESULT WINAPI DECLSPEC_HOTPATCH CoGetClassObject(
*ppv = NULL;
- if (!(apt = COM_CurrentApt())) + if (!(apt = apartment_get_current_or_mta())) { - if (!(apt = apartment_find_multi_threaded())) - { - ERR("apartment not initialised\n"); - return CO_E_NOTINITIALIZED; - } - release_apt = TRUE; + ERR("apartment not initialised\n"); + return CO_E_NOTINITIALIZED; }
if (pServerInfo) { @@ -3017,7 +3045,7 @@ HRESULT WINAPI DECLSPEC_HOTPATCH CoGetClassObject( { if (IsEqualCLSID(rclsid, &CLSID_InProcFreeMarshaler)) { - if (release_apt) apartment_release(apt); + apartment_release(apt); return FTMarshalCF_Create(iid, ppv); } if (IsEqualCLSID(rclsid, &CLSID_GlobalOptions)) @@ -3043,7 +3071,7 @@ HRESULT WINAPI DECLSPEC_HOTPATCH CoGetClassObject(
hres = get_inproc_class_object(apt, &clsreg, &comclass->clsid, iid, !(dwClsContext & WINE_CLSCTX_DONT_HOST), ppv); ReleaseActCtx(data.hActCtx); - if (release_apt) apartment_release(apt); + apartment_release(apt); return hres; } } @@ -3064,7 +3092,7 @@ HRESULT WINAPI DECLSPEC_HOTPATCH CoGetClassObject( * is good since we are not returning it in the "out" parameter. */ IUnknown_Release(regClassObject); - if (release_apt) apartment_release(apt); + apartment_release(apt); return hres; }
@@ -3099,7 +3127,7 @@ HRESULT WINAPI DECLSPEC_HOTPATCH CoGetClassObject( * other types */ if (SUCCEEDED(hres)) { - if (release_apt) apartment_release(apt); + apartment_release(apt); return hres; } } @@ -3135,11 +3163,11 @@ HRESULT WINAPI DECLSPEC_HOTPATCH CoGetClassObject( * other types */ if (SUCCEEDED(hres)) { - if (release_apt) apartment_release(apt); + apartment_release(apt); return hres; } } - if (release_apt) apartment_release(apt); + apartment_release(apt);
/* Next try out of process */ if (CLSCTX_LOCAL_SERVER & dwClsContext) @@ -3298,15 +3326,12 @@ HRESULT WINAPI DECLSPEC_HOTPATCH CoCreateInstanceEx( if(FAILED(hres)) clsid = *rclsid;
- if (!(apt = COM_CurrentApt())) + if (!(apt = apartment_get_current_or_mta())) { - if (!(apt = apartment_find_multi_threaded())) - { - ERR("apartment not initialised\n"); - return CO_E_NOTINITIALIZED; - } - apartment_release(apt); + ERR("apartment not initialised\n"); + return CO_E_NOTINITIALIZED; } + apartment_release(apt);
/* * The Standard Global Interface Table (GIT) object is a process-wide singleton. @@ -3640,8 +3665,11 @@ HRESULT WINAPI CoLockObjectExternal( TRACE("pUnk=%p, fLock=%s, fLastUnlockReleases=%s\n", pUnk, fLock ? "TRUE" : "FALSE", fLastUnlockReleases ? "TRUE" : "FALSE");
- apt = COM_CurrentApt(); - if (!apt) return CO_E_NOTINITIALIZED; + if (!(apt = apartment_get_current_or_mta())) + { + ERR("apartment not initialised\n"); + return CO_E_NOTINITIALIZED; + }
stubmgr = get_stub_manager_from_object(apt, pUnk, fLock); if (!stubmgr) @@ -3650,6 +3678,7 @@ HRESULT WINAPI CoLockObjectExternal( /* Note: native is pretty broken here because it just silently * fails, without returning an appropriate error code, making apps * think that the object was disconnected, when it actually wasn't */ + apartment_release(apt); return S_OK; }
@@ -3659,6 +3688,7 @@ HRESULT WINAPI CoLockObjectExternal( stub_manager_ext_release(stubmgr, 1, FALSE, fLastUnlockReleases);
stub_manager_int_release(stubmgr); + apartment_release(apt); return S_OK; }
@@ -4998,22 +5028,19 @@ HRESULT WINAPI CoGetObjectContext(REFIID riid, void **ppv) HRESULT WINAPI CoGetContextToken( ULONG_PTR *token ) { struct oletls *info = COM_CurrentInfo(); + APARTMENT *apt;
TRACE("(%p)\n", token);
if (!info) return E_OUTOFMEMORY;
- if (!info->apt) + if (!(apt = apartment_get_current_or_mta())) { - APARTMENT *apt; - if (!(apt = apartment_find_multi_threaded())) - { - ERR("apartment not initialised\n"); - return CO_E_NOTINITIALIZED; - } - apartment_release(apt); + ERR("apartment not initialised\n"); + return CO_E_NOTINITIALIZED; } + apartment_release(apt);
if (!token) return E_POINTER; diff --git a/dlls/ole32/compobj_private.h b/dlls/ole32/compobj_private.h index 9e65c3e..12413f7 100644 --- a/dlls/ole32/compobj_private.h +++ b/dlls/ole32/compobj_private.h @@ -142,6 +142,7 @@ struct apartment DWORD host_apt_tid; /* thread ID of apartment hosting objects of differing threading model (CS cs) */ HWND host_apt_hwnd; /* handle to apartment window of host apartment (CS cs) */ LocalServer *local_server; /* A marshallable object exposing local servers (CS cs) */ + BOOL being_destroyed; /* is currently being destroyed */
/* FIXME: OIDs should be given out by RPCSS */ OID oidc; /* object ID counter, starts at 1, zero is invalid OID (CS cs) */ diff --git a/dlls/ole32/tests/compobj.c b/dlls/ole32/tests/compobj.c index da4223b..ecfbac8 100644 --- a/dlls/ole32/tests/compobj.c +++ b/dlls/ole32/tests/compobj.c @@ -3701,6 +3701,7 @@ static DWORD CALLBACK implicit_mta_proc(void *param) IComThreadingInfo *threading_info; ULONG_PTR token; IUnknown *unk; + DWORD cookie; CLSID clsid; HRESULT hr;
@@ -3721,6 +3722,28 @@ static DWORD CALLBACK implicit_mta_proc(void *param) hr = CoGetContextToken(&token); ok_ole_success(hr, "CoGetContextToken");
+ hr = CoRegisterPSClsid(&IID_IWineTest, &CLSID_WineTestPSFactoryBuffer); + ok_ole_success(hr, "CoRegisterPSClsid"); + + hr = CoGetPSClsid(&IID_IClassFactory, &clsid); + ok_ole_success(hr, "CoGetPSClsid"); + + hr = CoRegisterClassObject(&CLSID_WineOOPTest, (IUnknown *)&Test_ClassFactory, + CLSCTX_INPROC_SERVER, REGCLS_SINGLEUSE, &cookie); + ok_ole_success(hr, "CoRegisterClassObject"); + + hr = CoRevokeClassObject(cookie); + ok_ole_success(hr, "CoRevokeClassObject"); + + hr = CoRegisterMessageFilter(NULL, NULL); + ok(hr == CO_E_NOT_SUPPORTED, "got %#x\n", hr); + + hr = CoLockObjectExternal((IUnknown *)&Test_Unknown, TRUE, TRUE); + ok_ole_success(hr, "CoLockObjectExternal"); + + hr = CoDisconnectObject((IUnknown *)&Test_Unknown, 0); + ok_ole_success(hr, "CoDisconnectObject"); + return 0; }