[PATCH 2/6] ole32: Allow more functions to use the implicit MTA.
Zebediah Figura
z.figura12 at gmail.com
Sun Mar 25 12:33:58 CDT 2018
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 at 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;
}
--
2.7.4
More information about the wine-devel
mailing list