ole32: Create host apartments to enable isolation of objects of incompatible threading models.

There should be one host apartment per apartment. Existing apartments 
should not be re-used, except in the case of the main apartment.
This commit is contained in:
Rob Shearman 2007-05-17 17:06:16 +01:00 committed by Alexandre Julliard
parent df0d77b007
commit 600143d4fa
3 changed files with 170 additions and 53 deletions

View File

@ -372,6 +372,7 @@ DWORD apartment_release(struct apartment *apt)
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)
{
@ -510,6 +511,8 @@ struct host_object_params
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 */
};
@ -521,7 +524,7 @@ static HRESULT apartment_hostobject(struct apartment *apt,
static const LARGE_INTEGER llZero;
WCHAR dllpath[MAX_PATH+1];
TRACE("\n");
TRACE("clsid %s, iid %s\n", debugstr_guid(&params->clsid), debugstr_guid(&params->iid));
if (COM_RegReadPath(params->hkeydll, NULL, NULL, dllpath, ARRAYSIZE(dllpath)) != ERROR_SUCCESS)
{
@ -556,6 +559,167 @@ static LRESULT CALLBACK apartment_wndproc(HWND hWnd, UINT msg, WPARAM wParam, LP
}
}
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_findfromtype(FALSE, FALSE);
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_findfromtype(FALSE, FALSE);
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, &params.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)&params))
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)&params);
}
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)
@ -1927,45 +2091,19 @@ static HRESULT get_inproc_class_object(APARTMENT *apt, HKEY hkeydll,
static const WCHAR wszBoth[] = {'B','o','t','h',0};
WCHAR dllpath[MAX_PATH+1];
WCHAR threading_model[10 /* strlenW(L"apartment")+1 */];
HRESULT hr;
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, &params.stream);
if (FAILED(hr))
return hr;
hr = SendMessageW(hwnd, DM_HOSTOBJECT, 0, (LPARAM)&params);
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))
@ -1976,29 +2114,7 @@ static HRESULT get_inproc_class_object(APARTMENT *apt, HKEY hkeydll,
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, &params.stream);
if (FAILED(hr))
return hr;
hr = SendMessageW(hwnd, DM_HOSTOBJECT, 0, (LPARAM)&params);
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)

View File

@ -158,6 +158,8 @@ struct apartment
LONG remoting_started; /* has the RPC system been started for this apartment? (LOCK) */
struct list psclsids; /* list of registered PS CLSIDs (CS cs) */
struct list loaded_dlls; /* list of dlls loaded by this apartment (CS cs) */
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) */
/* FIXME: OID's should be given out by RPCSS */
OID oidc; /* object ID counter, starts at 1, zero is invalid OID (CS cs) */

View File

@ -78,7 +78,6 @@ static void test_cocreateinstance_proxy(void)
hr = CoCreateInstance(&CLSID_ShellDesktop, NULL, CLSCTX_INPROC, &IID_IUnknown, (void **)&pProxy);
ok_ole_success(hr, CoCreateInstance);
hr = IUnknown_QueryInterface(pProxy, &IID_IMultiQI, (void **)&pMQI);
todo_wine
ok(hr == S_OK, "created object is not a proxy, so was created in the wrong apartment\n");
if (hr == S_OK)
IMultiQI_Release(pMQI);