* TODO: Most of these things will have to be made thread-safe.
*/
-static HRESULT COM_GetRegisteredClassObject(REFCLSID rclsid, DWORD dwClsContext, LPUNKNOWN* ppUnk);
-static void COM_RevokeAllClasses(void);
-static HRESULT get_inproc_class_object(HKEY hkeydll, REFCLSID rclsid, REFIID riid, void **ppv);
+static HRESULT COM_GetRegisteredClassObject(const struct apartment *apt, REFCLSID rclsid,
+ DWORD dwClsContext, LPUNKNOWN* ppUnk);
+static void COM_RevokeAllClasses(const struct apartment *apt);
+static HRESULT get_inproc_class_object(APARTMENT *apt, HKEY hkeydll, REFCLSID rclsid, REFIID riid, void **ppv);
static APARTMENT *MTA; /* protected by csApartment */
static APARTMENT *MainApartment; /* the first STA apartment */
{
struct list entry;
CLSID classIdentifier;
+ OXID apartment_id;
LPUNKNOWN classObject;
DWORD runContext;
DWORD connectFlags;
static LRESULT CALLBACK apartment_wndproc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
static HRESULT apartment_getclassobject(struct apartment *apt, LPCWSTR dllpath,
REFCLSID rclsid, REFIID riid, void **ppv);
+static void apartment_freeunusedlibraries(struct apartment *apt);
static HRESULT COMPOBJ_DllList_Add(LPCWSTR library_name, OpenDll **ret);
static OpenDll *COMPOBJ_DllList_Get(LPCWSTR library_name);
-static void COMPOBJ_DllList_ReleaseRef(OpenDll *entry);
+static void COMPOBJ_DllList_ReleaseRef(OpenDll *entry, BOOL free_entry);
static DWORD COM_RegReadPath(HKEY hkeyroot, const WCHAR *keyname, const WCHAR *valuename, WCHAR * dst, DWORD dstlen);
return apt;
}
-static inline BOOL apartment_is_model(APARTMENT *apt, DWORD model)
+static inline BOOL apartment_is_model(const APARTMENT *apt, DWORD model)
{
return (apt->multi_threaded == !(model & COINIT_APARTMENTTHREADED));
}
TRACE("destroying apartment %p, oxid %s\n", apt, wine_dbgstr_longlong(apt->oxid));
+ /* Release the references to the registered class objects */
+ COM_RevokeAllClasses(apt);
+
/* no locking is needed for this apartment, because no other thread
* can access it at this point */
apartment_disconnectproxies(apt);
if (apt->win) DestroyWindow(apt->win);
+ if (apt->host_apt_tid) PostThreadMessageW(apt->host_apt_tid, WM_QUIT, 0, 0);
LIST_FOR_EACH_SAFE(cursor, cursor2, &apt->stubmgrs)
{
if (apt->filter) IUnknown_Release(apt->filter);
+ /* free as many unused libraries as possible... */
+ apartment_freeunusedlibraries(apt);
+
+ /* ... and free the memory for the apartment loaded dll entry and
+ * release the dll list reference without freeing the library for the
+ * rest */
while ((cursor = list_head(&apt->loaded_dlls)))
{
struct apartment_loaded_dll *apartment_loaded_dll = LIST_ENTRY(cursor, struct apartment_loaded_dll, entry);
- COMPOBJ_DllList_ReleaseRef(apartment_loaded_dll->dll);
+ COMPOBJ_DllList_ReleaseRef(apartment_loaded_dll->dll, FALSE);
list_remove(cursor);
HeapFree(GetProcessHeap(), 0, apartment_loaded_dll);
}
return result;
}
-/* gets an apartment which has a given type. The caller must
+/* gets the main 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_findfromtype(BOOL multi_threaded, BOOL main_apartment)
+static APARTMENT *apartment_findmain(void)
{
- APARTMENT *result = NULL;
- struct apartment *apt;
+ APARTMENT *result;
EnterCriticalSection(&csApartment);
- if (!multi_threaded && main_apartment)
- {
- result = MainApartment;
- if (result) apartment_addref(result);
- LeaveCriticalSection(&csApartment);
- return result;
- }
+ result = MainApartment;
+ if (result) apartment_addref(result);
- LIST_FOR_EACH_ENTRY( apt, &apts, struct apartment, entry )
- {
- if (apt->multi_threaded == multi_threaded)
- {
- result = apt;
- apartment_addref(result);
- break;
- }
- }
LeaveCriticalSection(&csApartment);
return result;
HKEY hkeydll;
CLSID clsid; /* clsid of object to marshal */
IID iid; /* interface to marshal */
+ HANDLE event; /* event signalling when ready for multi-threaded case */
+ HRESULT hr; /* result for multi-threaded case */
IStream *stream; /* stream that the object will be marshaled into */
};
static const LARGE_INTEGER llZero;
WCHAR dllpath[MAX_PATH+1];
- TRACE("\n");
+ TRACE("clsid %s, iid %s\n", debugstr_guid(¶ms->clsid), debugstr_guid(¶ms->iid));
if (COM_RegReadPath(params->hkeydll, NULL, NULL, dllpath, ARRAYSIZE(dllpath)) != ERROR_SUCCESS)
{
}
}
+struct host_thread_params
+{
+ COINIT threading_model;
+ HANDLE ready_event;
+ HWND apartment_hwnd;
+};
+
+static DWORD CALLBACK apartment_hostobject_thread(LPVOID p)
+{
+ struct host_thread_params *params = p;
+ MSG msg;
+ HRESULT hr;
+ struct apartment *apt;
+
+ TRACE("\n");
+
+ hr = CoInitializeEx(NULL, params->threading_model);
+ if (FAILED(hr)) return hr;
+
+ apt = COM_CurrentApt();
+ if (params->threading_model == COINIT_APARTMENTTHREADED)
+ {
+ apartment_createwindowifneeded(apt);
+ params->apartment_hwnd = apartment_getwindow(apt);
+ }
+ else
+ params->apartment_hwnd = NULL;
+
+ /* force the message queue to be created before signaling parent thread */
+ PeekMessageW(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
+
+ SetEvent(params->ready_event);
+ params = NULL; /* can't touch params after here as it may be invalid */
+
+ while (GetMessageW(&msg, NULL, 0, 0))
+ {
+ if (!msg.hwnd && (msg.message == DM_HOSTOBJECT))
+ {
+ struct host_object_params *params = (struct host_object_params *)msg.lParam;
+ params->hr = apartment_hostobject(apt, params);
+ SetEvent(params->event);
+ }
+ else
+ {
+ TranslateMessage(&msg);
+ DispatchMessageW(&msg);
+ }
+ }
+
+ TRACE("exiting\n");
+
+ CoUninitialize();
+
+ return S_OK;
+}
+
+static HRESULT apartment_hostobject_in_hostapt(struct apartment *apt, BOOL multi_threaded, BOOL main_apartment, HKEY hkeydll, REFCLSID rclsid, REFIID riid, void **ppv)
+{
+ struct host_object_params params;
+ HWND apartment_hwnd = NULL;
+ DWORD apartment_tid = 0;
+ HRESULT hr;
+
+ if (!multi_threaded && main_apartment)
+ {
+ APARTMENT *host_apt = apartment_findmain();
+ if (host_apt)
+ {
+ apartment_hwnd = apartment_getwindow(host_apt);
+ apartment_release(host_apt);
+ }
+ }
+
+ if (!apartment_hwnd)
+ {
+ EnterCriticalSection(&apt->cs);
+
+ if (!apt->host_apt_tid)
+ {
+ struct host_thread_params thread_params;
+ HANDLE handles[2];
+ DWORD wait_value;
+
+ thread_params.threading_model = multi_threaded ? COINIT_MULTITHREADED : COINIT_APARTMENTTHREADED;
+ handles[0] = thread_params.ready_event = CreateEventW(NULL, FALSE, FALSE, NULL);
+ thread_params.apartment_hwnd = NULL;
+ handles[1] = CreateThread(NULL, 0, apartment_hostobject_thread, &thread_params, 0, &apt->host_apt_tid);
+ if (!handles[1])
+ {
+ CloseHandle(handles[0]);
+ LeaveCriticalSection(&apt->cs);
+ return E_OUTOFMEMORY;
+ }
+ wait_value = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
+ CloseHandle(handles[0]);
+ CloseHandle(handles[1]);
+ if (wait_value == WAIT_OBJECT_0)
+ apt->host_apt_hwnd = thread_params.apartment_hwnd;
+ else
+ {
+ LeaveCriticalSection(&apt->cs);
+ return E_OUTOFMEMORY;
+ }
+ }
+
+ if (multi_threaded || !main_apartment)
+ {
+ apartment_hwnd = apt->host_apt_hwnd;
+ apartment_tid = apt->host_apt_tid;
+ }
+
+ LeaveCriticalSection(&apt->cs);
+ }
+
+ /* another thread may have become the main apartment in the time it took
+ * us to create the thread for the host apartment */
+ if (!apartment_hwnd && !multi_threaded && main_apartment)
+ {
+ APARTMENT *host_apt = apartment_findmain();
+ if (host_apt)
+ {
+ apartment_hwnd = apartment_getwindow(host_apt);
+ apartment_release(host_apt);
+ }
+ }
+
+ params.hkeydll = hkeydll;
+ params.clsid = *rclsid;
+ params.iid = *riid;
+ hr = CreateStreamOnHGlobal(NULL, TRUE, ¶ms.stream);
+ if (FAILED(hr))
+ return hr;
+ if (multi_threaded)
+ {
+ params.hr = S_OK;
+ params.event = CreateEventW(NULL, FALSE, FALSE, NULL);
+ if (!PostThreadMessageW(apartment_tid, DM_HOSTOBJECT, 0, (LPARAM)¶ms))
+ hr = E_OUTOFMEMORY;
+ else
+ {
+ WaitForSingleObject(params.event, INFINITE);
+ hr = params.hr;
+ }
+ CloseHandle(params.event);
+ }
+ else
+ {
+ if (!apartment_hwnd)
+ {
+ ERR("host apartment didn't create window\n");
+ hr = E_OUTOFMEMORY;
+ }
+ else
+ hr = SendMessageW(apartment_hwnd, DM_HOSTOBJECT, 0, (LPARAM)¶ms);
+ }
+ if (SUCCEEDED(hr))
+ hr = CoUnmarshalInterface(params.stream, riid, ppv);
+ IStream_Release(params.stream);
+ return hr;
+}
+
HRESULT apartment_createwindowifneeded(struct apartment *apt)
{
if (apt->multi_threaded)
return S_OK;
}
-HWND apartment_getwindow(struct apartment *apt)
+HWND apartment_getwindow(const struct apartment *apt)
{
assert(!apt->multi_threaded);
return apt->win;
static HRESULT apartment_getclassobject(struct apartment *apt, LPCWSTR dllpath,
REFCLSID rclsid, REFIID riid, void **ppv)
{
+ static const WCHAR wszOle32[] = {'o','l','e','3','2','.','d','l','l',0};
HRESULT hr = S_OK;
BOOL found = FALSE;
struct apartment_loaded_dll *apartment_loaded_dll;
+ if (!strcmpiW(dllpath, wszOle32))
+ {
+ /* we don't need to control the lifetime of this dll, so use the local
+ * implementation of DllGetClassObject directly */
+ TRACE("calling ole32!DllGetClassObject\n");
+ hr = DllGetClassObject(rclsid, riid, ppv);
+
+ if (hr != S_OK)
+ ERR("DllGetClassObject returned error 0x%08x\n", hr);
+
+ return hr;
+ }
+
EnterCriticalSection(&apt->cs);
LIST_FOR_EACH_ENTRY(apartment_loaded_dll, &apt->loaded_dlls, struct apartment_loaded_dll, entry)
if (entry->dll->DllCanUnloadNow && (entry->dll->DllCanUnloadNow() == S_OK))
{
list_remove(&entry->entry);
- COMPOBJ_DllList_ReleaseRef(entry->dll);
+ COMPOBJ_DllList_ReleaseRef(entry->dll, TRUE);
HeapFree(GetProcessHeap(), 0, entry);
}
}
return E_ACCESSDENIED; /* FIXME: or should this be CO_E_DLLNOTFOUND? */
}
- DllCanUnloadNow = GetProcAddress(hLibrary, "DllCanUnloadNow");
+ DllCanUnloadNow = (void *)GetProcAddress(hLibrary, "DllCanUnloadNow");
/* Note: failing to find DllCanUnloadNow is not a failure */
- DllGetClassObject = GetProcAddress(hLibrary, "DllGetClassObject");
+ DllGetClassObject = (void *)GetProcAddress(hLibrary, "DllGetClassObject");
if (!DllGetClassObject)
{
/* failure: the dll did not export DllGetClassObject */
return ret;
}
-static void COMPOBJ_DllList_ReleaseRef(OpenDll *entry)
+/* pass FALSE for free_entry to release a reference without destroying the
+ * entry if it reaches zero or TRUE otherwise */
+static void COMPOBJ_DllList_ReleaseRef(OpenDll *entry, BOOL free_entry)
{
- if (!InterlockedDecrement(&entry->refs))
+ if (!InterlockedDecrement(&entry->refs) && free_entry)
{
EnterCriticalSection(&csOpenDllList);
list_remove(&entry->entry);
return hr;
}
-/* On COM finalization for a STA thread, the message queue is flushed to ensure no
- pending RPCs are ignored. Non-COM messages are discarded at this point.
- */
-static void COM_FlushMessageQueue(void)
-{
- MSG message;
- APARTMENT *apt = COM_CurrentApt();
-
- if (!apt || !apt->win) return;
-
- TRACE("Flushing STA message queue\n");
-
- while (PeekMessageA(&message, NULL, 0, 0, PM_REMOVE))
- {
- if (message.hwnd != apt->win)
- {
- WARN("discarding message 0x%x for window %p\n", message.message, message.hwnd);
- continue;
- }
-
- TranslateMessage(&message);
- DispatchMessageA(&message);
- }
-}
-
/***********************************************************************
* CoUninitialize [OLE32.@]
*
TRACE("() - Releasing the COM libraries\n");
RunningObjectTableImpl_UnInitialize();
-
- /* Release the references to the registered class objects */
- COM_RevokeAllClasses();
-
- /* This will free the loaded COM Dlls */
- CoFreeAllLibraries();
-
- /* This ensures we deal with any pending RPCs */
- COM_FlushMessageQueue();
}
else if (lCOMRefCnt<1) {
ERR( "CoUninitialize() - not CoInitialized.\n" );
* to normal COM usage, this method will increase the
* reference count on this object.
*/
-static HRESULT COM_GetRegisteredClassObject(
- REFCLSID rclsid,
- DWORD dwClsContext,
- LPUNKNOWN* ppUnk)
+static HRESULT COM_GetRegisteredClassObject(const struct apartment *apt, REFCLSID rclsid,
+ DWORD dwClsContext, LPUNKNOWN* ppUnk)
{
HRESULT hr = S_FALSE;
RegisteredClass *curClass;
/*
* Check if we have a match on the class ID and context.
*/
- if ((dwClsContext & curClass->runContext) &&
+ if ((apt->oxid == curClass->apartment_id) &&
+ (dwClsContext & curClass->runContext) &&
IsEqualGUID(&(curClass->classIdentifier), rclsid))
{
/*
* SEE ALSO
* CoRevokeClassObject, CoGetClassObject
*
+ * NOTES
+ * In-process objects are only registered for the current apartment.
+ * CoGetClassObject() and CoCreateInstance() will not return objects registered
+ * in other apartments.
+ *
* BUGS
* MSDN claims that multiple interface registrations are legal, but we
* can't do that with our current implementation.
RegisteredClass* newClass;
LPUNKNOWN foundObject;
HRESULT hr;
+ APARTMENT *apt;
TRACE("(%s,%p,0x%08x,0x%08x,%p)\n",
debugstr_guid(rclsid),pUnk,dwClsContext,flags,lpdwRegister);
if ( (lpdwRegister==0) || (pUnk==0) )
return E_INVALIDARG;
- if (!COM_CurrentApt())
+ apt = COM_CurrentApt();
+ if (!apt)
{
ERR("COM was not initialized\n");
return CO_E_NOTINITIALIZED;
* First, check if the class is already registered.
* If it is, this should cause an error.
*/
- hr = COM_GetRegisteredClassObject(rclsid, dwClsContext, &foundObject);
+ hr = COM_GetRegisteredClassObject(apt, rclsid, dwClsContext, &foundObject);
if (hr == S_OK) {
if (flags & REGCLS_MULTIPLEUSE) {
if (dwClsContext & CLSCTX_LOCAL_SERVER)
return E_OUTOFMEMORY;
newClass->classIdentifier = *rclsid;
+ newClass->apartment_id = apt->oxid;
newClass->runContext = dwClsContext;
newClass->connectFlags = flags;
newClass->pMarshaledData = NULL;
*lpdwRegister = newClass->dwCookie;
if (dwClsContext & CLSCTX_LOCAL_SERVER) {
- IClassFactory *classfac;
-
- hr = IUnknown_QueryInterface(newClass->classObject, &IID_IClassFactory,
- (LPVOID*)&classfac);
- if (hr) return hr;
-
hr = CreateStreamOnHGlobal(0, TRUE, &newClass->pMarshaledData);
if (hr) {
FIXME("Failed to create stream on hglobal, %x\n", hr);
- IUnknown_Release(classfac);
return hr;
}
hr = CoMarshalInterface(newClass->pMarshaledData, &IID_IClassFactory,
- (LPVOID)classfac, MSHCTX_LOCAL, NULL,
+ newClass->classObject, MSHCTX_LOCAL, NULL,
MSHLFLAGS_TABLESTRONG);
if (hr) {
FIXME("CoMarshalInterface failed, %x!\n",hr);
- IUnknown_Release(classfac);
return hr;
}
- IUnknown_Release(classfac);
-
hr = RPC_StartLocalServer(&newClass->classIdentifier,
newClass->pMarshaledData,
flags & (REGCLS_MULTIPLEUSE|REGCLS_MULTI_SEPARATE),
return S_OK;
}
+static void COM_RevokeRegisteredClassObject(RegisteredClass *curClass)
+{
+ list_remove(&curClass->entry);
+
+ if (curClass->runContext & CLSCTX_LOCAL_SERVER)
+ RPC_StopLocalServer(curClass->RpcRegistration);
+
+ /*
+ * Release the reference to the class object.
+ */
+ IUnknown_Release(curClass->classObject);
+
+ if (curClass->pMarshaledData)
+ {
+ LARGE_INTEGER zero;
+ memset(&zero, 0, sizeof(zero));
+ IStream_Seek(curClass->pMarshaledData, zero, STREAM_SEEK_SET, NULL);
+ CoReleaseMarshalData(curClass->pMarshaledData);
+ }
+
+ HeapFree(GetProcessHeap(), 0, curClass);
+}
+
+static void COM_RevokeAllClasses(const struct apartment *apt)
+{
+ RegisteredClass *curClass, *cursor;
+
+ EnterCriticalSection( &csRegisteredClassList );
+
+ LIST_FOR_EACH_ENTRY_SAFE(curClass, cursor, &RegisteredClassList, RegisteredClass, entry)
+ {
+ if (curClass->apartment_id == apt->oxid)
+ COM_RevokeRegisteredClassObject(curClass);
+ }
+
+ LeaveCriticalSection( &csRegisteredClassList );
+}
+
/***********************************************************************
* CoRevokeClassObject [OLE32.@]
*
* Success: S_OK.
* Failure: HRESULT code.
*
+ * NOTES
+ * Must be called from the same apartment that called CoRegisterClassObject(),
+ * otherwise it will fail with RPC_E_WRONG_THREAD.
+ *
* SEE ALSO
* CoRegisterClassObject
*/
{
HRESULT hr = E_INVALIDARG;
RegisteredClass *curClass;
+ APARTMENT *apt;
TRACE("(%08x)\n",dwRegister);
+ apt = COM_CurrentApt();
+ if (!apt)
+ {
+ ERR("COM was not initialized\n");
+ return CO_E_NOTINITIALIZED;
+ }
+
EnterCriticalSection( &csRegisteredClassList );
LIST_FOR_EACH_ENTRY(curClass, &RegisteredClassList, RegisteredClass, entry)
*/
if (curClass->dwCookie == dwRegister)
{
- list_remove(&curClass->entry);
-
- if (curClass->runContext & CLSCTX_LOCAL_SERVER)
- RPC_StopLocalServer(curClass->RpcRegistration);
-
- /*
- * Release the reference to the class object.
- */
- IUnknown_Release(curClass->classObject);
-
- if (curClass->pMarshaledData)
+ if (curClass->apartment_id == apt->oxid)
{
- LARGE_INTEGER zero;
- memset(&zero, 0, sizeof(zero));
- IStream_Seek(curClass->pMarshaledData, zero, STREAM_SEEK_SET, NULL);
- CoReleaseMarshalData(curClass->pMarshaledData);
+ COM_RevokeRegisteredClassObject(curClass);
+ hr = S_OK;
+ }
+ else
+ {
+ ERR("called from wrong apartment, should be called from %s\n",
+ wine_dbgstr_longlong(curClass->apartment_id));
+ hr = RPC_E_WRONG_THREAD;
}
-
- /*
- * Free the memory used by the chain node.
- */
- HeapFree(GetProcessHeap(), 0, curClass);
-
- hr = S_OK;
break;
}
}
value[0] = '\0';
}
-static HRESULT get_inproc_class_object(HKEY hkeydll, REFCLSID rclsid, REFIID riid, void **ppv)
+static HRESULT get_inproc_class_object(APARTMENT *apt, HKEY hkeydll,
+ REFCLSID rclsid, REFIID riid, void **ppv)
{
static const WCHAR wszApartment[] = {'A','p','a','r','t','m','e','n','t',0};
static const WCHAR wszFree[] = {'F','r','e','e',0};
static const WCHAR wszBoth[] = {'B','o','t','h',0};
WCHAR dllpath[MAX_PATH+1];
WCHAR threading_model[10 /* strlenW(L"apartment")+1 */];
- HRESULT hr;
- APARTMENT *apt = COM_CurrentApt();
get_threading_model(hkeydll, threading_model, ARRAYSIZE(threading_model));
/* "Apartment" */
if (!strcmpiW(threading_model, wszApartment))
{
if (apt->multi_threaded)
- {
- /* try to find an STA */
- APARTMENT *host_apt = apartment_findfromtype(FALSE, FALSE);
- if (!host_apt)
- FIXME("create a host apartment for apartment-threaded object %s\n", debugstr_guid(rclsid));
- if (host_apt)
- {
- struct host_object_params params;
- HWND hwnd = apartment_getwindow(host_apt);
-
- params.hkeydll = hkeydll;
- params.clsid = *rclsid;
- params.iid = *riid;
- hr = CreateStreamOnHGlobal(NULL, TRUE, ¶ms.stream);
- if (FAILED(hr))
- return hr;
- hr = SendMessageW(hwnd, DM_HOSTOBJECT, 0, (LPARAM)¶ms);
- if (SUCCEEDED(hr))
- hr = CoUnmarshalInterface(params.stream, riid, ppv);
- IStream_Release(params.stream);
- return hr;
- }
- }
+ return apartment_hostobject_in_hostapt(apt, FALSE, FALSE, hkeydll, rclsid, riid, ppv);
}
/* "Free" */
else if (!strcmpiW(threading_model, wszFree))
{
if (!apt->multi_threaded)
- {
- FIXME("should create object %s in multi-threaded apartment\n",
- debugstr_guid(rclsid));
- }
+ return apartment_hostobject_in_hostapt(apt, TRUE, FALSE, hkeydll, rclsid, riid, ppv);
}
/* everything except "Apartment", "Free" and "Both" */
else if (strcmpiW(threading_model, wszBoth))
debugstr_w(threading_model), debugstr_guid(rclsid));
if (apt->multi_threaded || !apt->main)
- {
- /* try to find an STA */
- APARTMENT *host_apt = apartment_findfromtype(FALSE, TRUE);
- if (!host_apt)
- FIXME("create a host apartment for main-threaded object %s\n", debugstr_guid(rclsid));
- if (host_apt)
- {
- struct host_object_params params;
- HWND hwnd = apartment_getwindow(host_apt);
-
- params.hkeydll = hkeydll;
- params.clsid = *rclsid;
- params.iid = *riid;
- hr = CreateStreamOnHGlobal(NULL, TRUE, ¶ms.stream);
- if (FAILED(hr))
- return hr;
- hr = SendMessageW(hwnd, DM_HOSTOBJECT, 0, (LPARAM)¶ms);
- if (SUCCEEDED(hr))
- hr = CoUnmarshalInterface(params.stream, riid, ppv);
- IStream_Release(params.stream);
- return hr;
- }
- }
+ return apartment_hostobject_in_hostapt(apt, FALSE, TRUE, hkeydll, rclsid, riid, ppv);
}
if (COM_RegReadPath(hkeydll, NULL, NULL, dllpath, ARRAYSIZE(dllpath)) != ERROR_SUCCESS)
{
LPUNKNOWN regClassObject;
HRESULT hres = E_UNEXPECTED;
+ APARTMENT *apt;
TRACE("\n\tCLSID:\t%s,\n\tIID:\t%s\n", debugstr_guid(rclsid), debugstr_guid(iid));
*ppv = NULL;
- if (!COM_CurrentApt())
+ apt = COM_CurrentApt();
+ if (!apt)
{
ERR("apartment not initialised\n");
return CO_E_NOTINITIALIZED;
* First, try and see if we can't match the class ID with one of the
* registered classes.
*/
- if (S_OK == COM_GetRegisteredClassObject(rclsid, dwClsContext, ®ClassObject))
+ if (S_OK == COM_GetRegisteredClassObject(apt, rclsid, dwClsContext,
+ ®ClassObject))
{
/* Get the required interface from the retrieved pointer. */
hres = IUnknown_QueryInterface(regClassObject, iid, ppv);
{
if (hres == REGDB_E_CLASSNOTREG)
ERR("class %s not registered\n", debugstr_guid(rclsid));
- else
+ else if (hres == REGDB_E_KEYMISSING)
+ {
WARN("class %s not registered as in-proc server\n", debugstr_guid(rclsid));
+ hres = REGDB_E_CLASSNOTREG;
+ }
}
if (SUCCEEDED(hres))
{
- hres = get_inproc_class_object(hkey, rclsid, iid, ppv);
+ hres = get_inproc_class_object(apt, hkey, rclsid, iid, ppv);
RegCloseKey(hkey);
}
{
if (hres == REGDB_E_CLASSNOTREG)
ERR("class %s not registered\n", debugstr_guid(rclsid));
- else
+ else if (hres == REGDB_E_KEYMISSING)
+ {
WARN("class %s not registered in-proc handler\n", debugstr_guid(rclsid));
+ hres = REGDB_E_CLASSNOTREG;
+ }
}
if (SUCCEEDED(hres))
{
- hres = get_inproc_class_object(hkey, rclsid, iid, ppv);
+ hres = get_inproc_class_object(apt, hkey, rclsid, iid, ppv);
RegCloseKey(hkey);
}
return S_OK;
}
-static void COM_RevokeAllClasses(void)
-{
- EnterCriticalSection( &csRegisteredClassList );
-
- while (list_head(&RegisteredClassList))
- {
- RegisteredClass *curClass = LIST_ENTRY(list_head(&RegisteredClassList),
- RegisteredClass, entry);
- CoRevokeClassObject(curClass->dwCookie);
- }
-
- LeaveCriticalSection( &csRegisteredClassList );
-}
-
/******************************************************************************
* CoLockObjectExternal [OLE32.@]
*
{
hr = IRemUnknown_RemQueryInterface(remunk, ipid, NORMALEXTREFS,
nonlocal_mqis, iids, &qiresults);
+ IRemUnknown_Release(remunk);
if (FAILED(hr))
ERR("IRemUnknown_RemQueryInterface failed with error 0x%08x\n", hr);
}
ULONG index = mapping[i];
HRESULT hrobj = qiresults[i].hResult;
if (hrobj == S_OK)
- hrobj = unmarshal_object(&qiresults[i].std, This->parent,
+ hrobj = unmarshal_object(&qiresults[i].std, COM_CurrentApt(),
This->dest_context,
This->dest_context_data,
pMQIs[index].pIID, &This->oxid_info,
if (SUCCEEDED(hr))
{
STDOBJREF stdobjref = ifproxy->stdobjref;
- ULONG cPublicRefs = ifproxy->refs;
- ULONG cPublicRefsOld;
- /* optimization - share out proxy's public references if possible
- * instead of making new proxy do a roundtrip through the server */
- do
+ stdobjref.cPublicRefs = 0;
+
+ if ((mshlflags != MSHLFLAGS_TABLEWEAK) &&
+ (mshlflags != MSHLFLAGS_TABLESTRONG))
{
- ULONG cPublicRefsNew;
- cPublicRefsOld = cPublicRefs;
- stdobjref.cPublicRefs = cPublicRefs / 2;
- cPublicRefsNew = cPublicRefs - stdobjref.cPublicRefs;
- cPublicRefs = InterlockedCompareExchange(
- (LONG *)&ifproxy->refs, cPublicRefsNew, cPublicRefsOld);
- } while (cPublicRefs != cPublicRefsOld);
-
- if (!stdobjref.cPublicRefs)
+ ULONG cPublicRefs = ifproxy->refs;
+ ULONG cPublicRefsOld;
+ /* optimization - share out proxy's public references if possible
+ * instead of making new proxy do a roundtrip through the server */
+ do
+ {
+ ULONG cPublicRefsNew;
+ cPublicRefsOld = cPublicRefs;
+ stdobjref.cPublicRefs = cPublicRefs / 2;
+ cPublicRefsNew = cPublicRefs - stdobjref.cPublicRefs;
+ cPublicRefs = InterlockedCompareExchange(
+ (LONG *)&ifproxy->refs, cPublicRefsNew, cPublicRefsOld);
+ } while (cPublicRefs != cPublicRefsOld);
+ }
+
+ /* normal and table-strong marshaling need at least one reference */
+ if (!stdobjref.cPublicRefs && (mshlflags != MSHLFLAGS_TABLEWEAK))
{
IRemUnknown *remunk;
hr = proxy_manager_get_remunknown(This, &remunk);
HRESULT hrref = S_OK;
REMINTERFACEREF rif;
rif.ipid = ifproxy->stdobjref.ipid;
- rif.cPublicRefs = NORMALEXTREFS;
+ rif.cPublicRefs = (mshlflags == MSHLFLAGS_TABLESTRONG) ? 1 : NORMALEXTREFS;
rif.cPrivateRefs = 0;
hr = IRemUnknown_RemAddRef(remunk, 1, &rif, &hrref);
+ IRemUnknown_Release(remunk);
if (hr == S_OK && hrref == S_OK)
- stdobjref.cPublicRefs = rif.cPublicRefs;
+ {
+ /* table-strong marshaling doesn't give the refs to the
+ * client that unmarshals the STDOBJREF */
+ if (mshlflags != MSHLFLAGS_TABLESTRONG)
+ stdobjref.cPublicRefs = rif.cPublicRefs;
+ }
else
ERR("IRemUnknown_RemAddRef returned with 0x%08x, hrref = 0x%08x\n", hr, hrref);
}
}
else
ERR("IRemUnknown_RemQueryInterface failed with error 0x%08x\n", hr);
+ IRemUnknown_Release(remunk);
}
}
rif.cPublicRefs = NORMALEXTREFS;
rif.cPrivateRefs = 0;
hr = IRemUnknown_RemAddRef(remunk, 1, &rif, &hrref);
+ IRemUnknown_Release(remunk);
if (hr == S_OK && hrref == S_OK)
InterlockedExchangeAdd((LONG *)&This->refs, NORMALEXTREFS);
else
rif.cPublicRefs = public_refs;
rif.cPrivateRefs = 0;
hr = IRemUnknown_RemRelease(remunk, 1, &rif);
+ IRemUnknown_Release(remunk);
if (hr == S_OK)
InterlockedExchangeAdd((LONG *)&This->refs, -public_refs);
else if (hr == RPC_E_DISCONNECTED)
static HRESULT proxy_manager_get_remunknown(struct proxy_manager * This, IRemUnknown **remunk)
{
HRESULT hr = S_OK;
+ struct apartment *apt;
+ BOOL called_in_original_apt;
/* we don't want to try and unmarshal or use IRemUnknown if we don't want
* lifetime management */
if (This->sorflags & SORFP_NOLIFETIMEMGMT)
return S_FALSE;
+ apt = COM_CurrentApt();
+ if (!apt)
+ return CO_E_NOTINITIALIZED;
+
+ called_in_original_apt = This->parent && (This->parent->oxid == apt->oxid);
+
EnterCriticalSection(&This->cs);
- if (This->remunk)
+ /* only return the cached object if called from the original apartment.
+ * in future, we might want to make the IRemUnknown proxy callable from any
+ * apartment to avoid these checks */
+ if (This->remunk && called_in_original_apt)
+ {
/* already created - return existing object */
*remunk = This->remunk;
+ IRemUnknown_AddRef(*remunk);
+ }
else if (!This->parent)
/* disconnected - we can't create IRemUnknown */
hr = S_FALSE;
stdobjref.ipid = This->oxid_info.ipidRemUnknown;
/* do the unmarshal */
- hr = unmarshal_object(&stdobjref, This->parent, This->dest_context,
+ hr = unmarshal_object(&stdobjref, COM_CurrentApt(), This->dest_context,
This->dest_context_data, &IID_IRemUnknown,
- &This->oxid_info, (void**)&This->remunk);
- if (hr == S_OK)
- *remunk = This->remunk;
+ &This->oxid_info, (void**)remunk);
+ if (hr == S_OK && called_in_original_apt)
+ {
+ This->remunk = *remunk;
+ IRemUnknown_AddRef(This->remunk);
+ }
}
LeaveCriticalSection(&This->cs);