Main point of this change is to get rid of auxiliary thread that is causing issues when CoRevokeClassObject() has to wait on it, potentially deadlocking when called from DllMain.
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/combase/combase.c | 33 ++-- dlls/combase/combase_private.h | 4 +- dlls/combase/rpc.c | 306 +++++++++------------------------ dlls/ole32/compobj.c | 2 - include/wine/irpcss.idl | 17 ++ programs/rpcss/rpcss_main.c | 100 +++++++++++ 6 files changed, 222 insertions(+), 240 deletions(-)
diff --git a/dlls/combase/combase.c b/dlls/combase/combase.c index 99352712f78..c90cba6185c 100644 --- a/dlls/combase/combase.c +++ b/dlls/combase/combase.c @@ -115,10 +115,6 @@ static CRITICAL_SECTION_DEBUG psclsid_cs_debug = }; static CRITICAL_SECTION cs_registered_ps = { &psclsid_cs_debug, -1, 0, 0, 0, 0 };
-/* - * TODO: Make this data structure aware of inter-process communication. This - * means that parts of this will be exported to rpcss. - */ struct registered_class { struct list entry; @@ -127,8 +123,8 @@ struct registered_class IUnknown *object; DWORD clscontext; DWORD flags; - DWORD cookie; - void *RpcRegistration; + unsigned int cookie; + unsigned int rpcss_cookie; };
static struct list registered_classes = LIST_INIT(registered_classes); @@ -2824,7 +2820,7 @@ HRESULT WINAPI CoRegisterClassObject(REFCLSID rclsid, IUnknown *object, DWORD cl return CO_E_OBJISREG; }
- newclass = heap_alloc(sizeof(*newclass)); + newclass = heap_alloc_zero(sizeof(*newclass)); if (!newclass) { apartment_release(apt); @@ -2835,7 +2831,6 @@ HRESULT WINAPI CoRegisterClassObject(REFCLSID rclsid, IUnknown *object, DWORD cl newclass->apartment_id = apt->oxid; newclass->clscontext = clscontext; newclass->flags = flags; - newclass->RpcRegistration = NULL;
if (!(newclass->cookie = InterlockedIncrement(&next_cookie))) newclass->cookie = InterlockedIncrement(&next_cookie); @@ -2860,8 +2855,7 @@ HRESULT WINAPI CoRegisterClassObject(REFCLSID rclsid, IUnknown *object, DWORD cl return hr; }
- hr = rpc_start_local_server(&newclass->clsid, marshal_stream, flags & (REGCLS_MULTIPLEUSE|REGCLS_MULTI_SEPARATE), - &newclass->RpcRegistration); + hr = rpc_register_local_server(&newclass->clsid, marshal_stream, flags, &newclass->rpcss_cookie); IStream_Release(marshal_stream); }
@@ -2874,12 +2868,28 @@ static void com_revoke_class_object(struct registered_class *entry) list_remove(&entry->entry);
if (entry->clscontext & CLSCTX_LOCAL_SERVER) - rpc_stop_local_server(entry->RpcRegistration); + rpc_revoke_local_server(entry->rpcss_cookie);
IUnknown_Release(entry->object); heap_free(entry); }
+/* Cleans up rpcss registry */ +static void com_revoke_local_servers(void) +{ + struct registered_class *cur, *cur2; + + EnterCriticalSection(®istered_classes_cs); + + LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, ®istered_classes, struct registered_class, entry) + { + if (cur->clscontext & CLSCTX_LOCAL_SERVER) + com_revoke_class_object(cur); + } + + LeaveCriticalSection(®istered_classes_cs); +} + void apartment_revoke_all_classes(const struct apartment *apt) { struct registered_class *cur, *cur2; @@ -3164,6 +3174,7 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD reason, LPVOID reserved) hProxyDll = hinstDLL; break; case DLL_PROCESS_DETACH: + com_revoke_local_servers(); if (reserved) break; apartment_global_cleanup(); DeleteCriticalSection(®istered_classes_cs); diff --git a/dlls/combase/combase_private.h b/dlls/combase/combase_private.h index 703c1a26c20..ca8a994f4e2 100644 --- a/dlls/combase/combase_private.h +++ b/dlls/combase/combase_private.h @@ -112,8 +112,8 @@ HRESULT apartment_disconnectproxies(struct apartment *apt) DECLSPEC_HIDDEN; /* RpcSs interface */ HRESULT rpcss_get_next_seqid(DWORD *id) DECLSPEC_HIDDEN; HRESULT rpc_get_local_class_object(REFCLSID rclsid, REFIID riid, void **obj) DECLSPEC_HIDDEN; -HRESULT rpc_start_local_server(REFCLSID clsid, IStream *stream, BOOL multi_use, void **registration) DECLSPEC_HIDDEN; -void rpc_stop_local_server(void *registration) DECLSPEC_HIDDEN; +HRESULT rpc_register_local_server(REFCLSID clsid, IStream *stream, DWORD flags, unsigned int *cookie) DECLSPEC_HIDDEN; +HRESULT rpc_revoke_local_server(unsigned int cookie) DECLSPEC_HIDDEN; HRESULT rpc_create_clientchannel(const OXID *oxid, const IPID *ipid, const OXID_INFO *oxid_info, const IID *iid, DWORD dest_context, void *dest_context_data, IRpcChannelBuffer **chan, struct apartment *apt) DECLSPEC_HIDDEN; HRESULT rpc_create_serverchannel(DWORD dest_context, void *dest_context_data, IRpcChannelBuffer **chan) DECLSPEC_HIDDEN; diff --git a/dlls/combase/rpc.c b/dlls/combase/rpc.c index 513c1e6c13a..c4bc8983a2c 100644 --- a/dlls/combase/rpc.c +++ b/dlls/combase/rpc.c @@ -363,11 +363,25 @@ HRESULT WINAPI InternalIrotRevoke(IrotCookie cookie, IrotContextHandle *ctxt_han RPCSS_CALL_END }
-static void get_localserver_pipe_name(WCHAR *pipefn, REFCLSID rclsid) +static HRESULT rpcss_server_register(REFCLSID clsid, DWORD flags, MInterfacePointer *obj, unsigned int *cookie) { - static const WCHAR wszPipeRef[] = {'\','\','.','\','p','i','p','e','\',0}; - lstrcpyW(pipefn, wszPipeRef); - StringFromGUID2(rclsid, pipefn + ARRAY_SIZE(wszPipeRef) - 1, CHARS_IN_GUID); + RPCSS_CALL_START + hr = irpcss_server_register(get_irpcss_handle(), clsid, flags, obj, cookie); + RPCSS_CALL_END +} + +HRESULT rpc_revoke_local_server(unsigned int cookie) +{ + RPCSS_CALL_START + hr = irpcss_server_revoke(get_irpcss_handle(), cookie); + RPCSS_CALL_END +} + +static HRESULT rpcss_get_class_object(REFCLSID rclsid, PMInterfacePointer *objref) +{ + RPCSS_CALL_START + hr = irpcss_get_class_object(get_irpcss_handle(), rclsid, objref); + RPCSS_CALL_END }
static DWORD start_local_service(const WCHAR *name, DWORD num, LPCWSTR *params) @@ -509,261 +523,103 @@ static HRESULT create_server(REFCLSID rclsid, HANDLE *process) return S_OK; }
-/* FIXME: should call to rpcss instead */ HRESULT rpc_get_local_class_object(REFCLSID rclsid, REFIID riid, void **obj) { - HRESULT hr; - HANDLE hPipe; - WCHAR pipefn[100]; - DWORD res, bufferlen; - char marshalbuffer[200]; - IStream *pStm; - LARGE_INTEGER seekto; - ULARGE_INTEGER newpos; - int tries = 0; + PMInterfacePointer objref = NULL; IServiceProvider *local_server; - + IStream *stream = NULL; + ULARGE_INTEGER newpos; + LARGE_INTEGER seekto; + int tries = 0; + ULONG length; + HRESULT hr; static const int MAXTRIES = 30; /* 30 seconds */
- TRACE("rclsid %s, riid %s\n", debugstr_guid(rclsid), debugstr_guid(riid)); - - get_localserver_pipe_name(pipefn, rclsid); + TRACE("clsid %s, riid %s\n", debugstr_guid(rclsid), debugstr_guid(riid));
while (tries++ < MAXTRIES) { - TRACE("waiting for %s\n", debugstr_w(pipefn)); + DWORD index, start_ticks; + HANDLE process = 0;
- WaitNamedPipeW(pipefn, NMPWAIT_WAIT_FOREVER); - hPipe = CreateFileW(pipefn, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0); - if (hPipe == INVALID_HANDLE_VALUE) - { - DWORD index; - DWORD start_ticks; - HANDLE process = 0; - if (tries == 1) - { - if ((hr = create_local_service(rclsid)) && (hr = create_server(rclsid, &process))) - return hr; - } - else - { - WARN("Connecting to %s, no response yet, retrying: le is %u\n", debugstr_w(pipefn), GetLastError()); - } - /* wait for one second, even if messages arrive */ - start_ticks = GetTickCount(); - do - { - if (SUCCEEDED(CoWaitForMultipleHandles(0, 1000, (process != 0), &process, &index)) && process && !index) - { - WARN("server for %s failed to start\n", debugstr_guid(rclsid)); - CloseHandle( hPipe ); - CloseHandle( process ); - return E_NOINTERFACE; - } - } while (GetTickCount() - start_ticks < 1000); - if (process) CloseHandle(process); - continue; - } - bufferlen = 0; - if (!ReadFile(hPipe, marshalbuffer, sizeof(marshalbuffer), &bufferlen, NULL)) + if (SUCCEEDED(hr = rpcss_get_class_object(rclsid, &objref))) + break; + + if (tries == 1) { - FIXME("Failed to read marshal id from classfactory of %s.\n", debugstr_guid(rclsid)); - CloseHandle(hPipe); - Sleep(1000); - continue; + if ((hr = create_local_service(rclsid)) && (hr = create_server(rclsid, &process)) ) + return hr; } - TRACE("read marshal id from pipe\n"); - CloseHandle(hPipe); - break; - }
- if (tries >= MAXTRIES) - return E_NOINTERFACE; - - hr = CreateStreamOnHGlobal(0, TRUE, &pStm); - if (hr != S_OK) return hr; - hr = IStream_Write(pStm, marshalbuffer, bufferlen, &res); - if (hr != S_OK) goto out; - seekto.u.LowPart = 0;seekto.u.HighPart = 0; - hr = IStream_Seek(pStm, seekto, STREAM_SEEK_SET, &newpos); - TRACE("unmarshalling local server\n"); - hr = CoUnmarshalInterface(pStm, &IID_IServiceProvider, (void **)&local_server); - if(SUCCEEDED(hr)) - hr = IServiceProvider_QueryService(local_server, rclsid, riid, obj); - IServiceProvider_Release(local_server); -out: - IStream_Release(pStm); - return hr; -} - -struct local_server_params -{ - CLSID clsid; - IStream *stream; - HANDLE pipe; - HANDLE stop_event; - HANDLE thread; - BOOL multi_use; -}; - -/* FIXME: should call to rpcss instead */ -static DWORD WINAPI local_server_thread(void *param) -{ - struct local_server_params * lsp = param; - WCHAR pipefn[100]; - HRESULT hres; - IStream *pStm = lsp->stream; - STATSTG ststg; - unsigned char *buffer; - int buflen; - LARGE_INTEGER seekto; - ULARGE_INTEGER newpos; - ULONG res; - BOOL multi_use = lsp->multi_use; - OVERLAPPED ovl; - HANDLE pipe_event, hPipe = lsp->pipe, new_pipe; - DWORD bytes; - - TRACE("Starting threader for %s.\n",debugstr_guid(&lsp->clsid)); - - memset(&ovl, 0, sizeof(ovl)); - get_localserver_pipe_name(pipefn, &lsp->clsid); - ovl.hEvent = pipe_event = CreateEventW(NULL, FALSE, FALSE, NULL); - - while (1) { - if (!ConnectNamedPipe(hPipe, &ovl)) + /* Wait for one second, even if messages arrive. */ + start_ticks = GetTickCount(); + do { - DWORD error = GetLastError(); - if (error == ERROR_IO_PENDING) + if (SUCCEEDED(CoWaitForMultipleHandles(0, 1000, (process != 0), &process, &index)) && process && !index) { - HANDLE handles[2] = { pipe_event, lsp->stop_event }; - DWORD ret; - ret = WaitForMultipleObjects(2, handles, FALSE, INFINITE); - if (ret != WAIT_OBJECT_0) - break; + WARN("Server for %s failed to start.\n", debugstr_guid(rclsid)); + CloseHandle(process); + return E_NOINTERFACE; } - /* client already connected isn't an error */ - else if (error != ERROR_PIPE_CONNECTED) - { - ERR("ConnectNamedPipe failed with error %d\n", GetLastError()); - break; - } - } - - TRACE("marshalling LocalServer to client\n"); + } while (GetTickCount() - start_ticks < 1000);
- hres = IStream_Stat(pStm,&ststg,STATFLAG_NONAME); - if (hres != S_OK) - break; - - seekto.u.LowPart = 0; - seekto.u.HighPart = 0; - hres = IStream_Seek(pStm,seekto,STREAM_SEEK_SET,&newpos); - if (hres != S_OK) { - FIXME("IStream_Seek failed, %x\n",hres); - break; - } + if (process) CloseHandle(process); + }
- buflen = ststg.cbSize.u.LowPart; - buffer = HeapAlloc(GetProcessHeap(),0,buflen); + if (!objref || tries >= MAXTRIES) + return E_NOINTERFACE;
- hres = IStream_Read(pStm,buffer,buflen,&res); - if (hres != S_OK) { - FIXME("Stream Read failed, %x\n",hres); - HeapFree(GetProcessHeap(),0,buffer); - break; - } + if (SUCCEEDED(hr = CreateStreamOnHGlobal(0, TRUE, &stream))) + hr = IStream_Write(stream, objref->abData, objref->ulCntData, &length);
- WriteFile(hPipe,buffer,buflen,&res,&ovl); - GetOverlappedResult(hPipe, &ovl, &bytes, TRUE); - HeapFree(GetProcessHeap(),0,buffer); + MIDL_user_free(objref);
- FlushFileBuffers(hPipe); - DisconnectNamedPipe(hPipe); - TRACE("done marshalling LocalServer\n"); + if (SUCCEEDED(hr)) + { + seekto.QuadPart = 0; + IStream_Seek(stream, seekto, STREAM_SEEK_SET, &newpos);
- if (!multi_use) + TRACE("Unmarshalling local server.\n"); + hr = CoUnmarshalInterface(stream, &IID_IServiceProvider, (void **)&local_server); + if (SUCCEEDED(hr)) { - TRACE("single use object, shutting down pipe %s\n", debugstr_w(pipefn)); - break; + hr = IServiceProvider_QueryService(local_server, rclsid, riid, obj); + IServiceProvider_Release(local_server); } - new_pipe = CreateNamedPipeW( pipefn, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, - PIPE_TYPE_BYTE|PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, - 4096, 4096, 500 /* 0.5 second timeout */, NULL ); - if (new_pipe == INVALID_HANDLE_VALUE) - { - FIXME("pipe creation failed for %s, le is %u\n", debugstr_w(pipefn), GetLastError()); - break; - } - CloseHandle(hPipe); - hPipe = new_pipe; }
- CloseHandle(pipe_event); - CloseHandle(hPipe); - return 0; + if (stream) + IStream_Release(stream); + + return hr; }
-HRESULT rpc_start_local_server(REFCLSID clsid, IStream *stream, BOOL multi_use, void **registration) +HRESULT rpc_register_local_server(REFCLSID clsid, IStream *stream, DWORD flags, unsigned int *cookie) { - DWORD tid, err; - struct local_server_params *lsp; - WCHAR pipefn[100]; - - lsp = HeapAlloc(GetProcessHeap(), 0, sizeof(*lsp)); - if (!lsp) - return E_OUTOFMEMORY; + MInterfacePointer *obj; + const void *ptr; + HGLOBAL hmem; + SIZE_T size; + HRESULT hr;
- lsp->clsid = *clsid; - lsp->stream = stream; - IStream_AddRef(stream); - lsp->stop_event = CreateEventW(NULL, FALSE, FALSE, NULL); - if (!lsp->stop_event) - { - HeapFree(GetProcessHeap(), 0, lsp); - return HRESULT_FROM_WIN32(GetLastError()); - } - lsp->multi_use = multi_use; + TRACE("%s, %#x\n", debugstr_guid(clsid), flags);
- get_localserver_pipe_name(pipefn, &lsp->clsid); - lsp->pipe = CreateNamedPipeW(pipefn, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, - PIPE_TYPE_BYTE|PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, - 4096, 4096, 500 /* 0.5 second timeout */, NULL); - if (lsp->pipe == INVALID_HANDLE_VALUE) - { - err = GetLastError(); - FIXME("pipe creation failed for %s, le is %u\n", debugstr_w(pipefn), GetLastError()); - CloseHandle(lsp->stop_event); - HeapFree(GetProcessHeap(), 0, lsp); - return HRESULT_FROM_WIN32(err); - } + hr = GetHGlobalFromStream(stream, &hmem); + if (FAILED(hr)) return hr;
- lsp->thread = CreateThread(NULL, 0, local_server_thread, lsp, 0, &tid); - if (!lsp->thread) - { - CloseHandle(lsp->pipe); - CloseHandle(lsp->stop_event); - HeapFree(GetProcessHeap(), 0, lsp); - return HRESULT_FROM_WIN32(GetLastError()); - } - - *registration = lsp; - return S_OK; -} + size = GlobalSize(hmem); + if (!(obj = heap_alloc(FIELD_OFFSET(MInterfacePointer, abData[size])))) + return E_OUTOFMEMORY; + obj->ulCntData = size; + ptr = GlobalLock(hmem); + memcpy(obj->abData, ptr, size); + GlobalUnlock(hmem);
-void rpc_stop_local_server(void *registration) -{ - struct local_server_params *lsp = registration; + hr = rpcss_server_register(clsid, flags, obj, cookie);
- /* signal local_server_thread to stop */ - SetEvent(lsp->stop_event); - /* wait for it to exit */ - WaitForSingleObject(lsp->thread, INFINITE); + heap_free(obj);
- IStream_Release(lsp->stream); - CloseHandle(lsp->stop_event); - CloseHandle(lsp->thread); - HeapFree(GetProcessHeap(), 0, lsp); + return hr; }
static HRESULT unmarshal_ORPCTHAT(RPC_MESSAGE *msg, ORPCTHAT *orpcthat, ORPC_EXTENT_ARRAY *orpc_ext_array, diff --git a/dlls/ole32/compobj.c b/dlls/ole32/compobj.c index f4fe82de436..c37a8ac6098 100644 --- a/dlls/ole32/compobj.c +++ b/dlls/ole32/compobj.c @@ -29,8 +29,6 @@ * * TODO list: (items bunched together depend on each other) * - * - Implement the service control manager (in rpcss) to keep track - * of registered class objects: ISCM::ServerRegisterClsid et al * - Implement the OXID resolver so we don't need magic endpoint names for * clients and servers to meet up * diff --git a/include/wine/irpcss.idl b/include/wine/irpcss.idl index 1874e100254..5d27658eba5 100644 --- a/include/wine/irpcss.idl +++ b/include/wine/irpcss.idl @@ -17,6 +17,7 @@ */
import "wtypes.idl"; +#include "wine/orpc.idl"
cpp_quote("#define IRPCSS_PROTSEQ {'n','c','a','l','r','p','c',0}") cpp_quote("#define IRPCSS_ENDPOINT {'i','r','p','c','s','s',0}") @@ -27,6 +28,22 @@ cpp_quote("#define IRPCSS_ENDPOINT {'i','r','p','c','s','s',0}") ] interface Irpcss { + HRESULT irpcss_server_register( + [in] handle_t handle, + [in] const GUID *clsid, + [in] unsigned int flags, + [in] PMInterfacePointer object, + [out] unsigned int *cookie); + + HRESULT irpcss_server_revoke( + [in] handle_t handle, + [in] unsigned int cookie); + + HRESULT irpcss_get_class_object( + [in] handle_t handle, + [in] const GUID *clsid, + [out] PMInterfacePointer *object); + HRESULT irpcss_get_thread_seq_id( [in] handle_t handle, [out] DWORD *sequence_id); diff --git a/programs/rpcss/rpcss_main.c b/programs/rpcss/rpcss_main.c index 6e85ef2c337..e3859e4c3c9 100644 --- a/programs/rpcss/rpcss_main.c +++ b/programs/rpcss/rpcss_main.c @@ -31,6 +31,8 @@ #include "irpcss.h"
#include "wine/debug.h" +#include "wine/heap.h" +#include "wine/list.h"
WINE_DEFAULT_DEBUG_CHANNEL(ole);
@@ -38,6 +40,104 @@ static WCHAR rpcssW[] = {'R','p','c','S','s',0}; static HANDLE exit_event; static SERVICE_STATUS_HANDLE service_handle;
+struct registered_class +{ + struct list entry; + GUID clsid; + unsigned int cookie; + PMInterfacePointer object; + unsigned int single_use : 1; +}; + +static CRITICAL_SECTION registered_classes_cs = { NULL, -1, 0, 0, 0, 0 }; +static struct list registered_classes = LIST_INIT(registered_classes); + +HRESULT __cdecl irpcss_server_register(handle_t h, const GUID *clsid, DWORD flags, + PMInterfacePointer object, unsigned int *cookie) +{ + struct registered_class *entry; + static int next_cookie; + + if (!(entry = heap_alloc_zero(sizeof(*entry)))) + return E_OUTOFMEMORY; + + entry->clsid = *clsid; + entry->single_use = !(flags & (REGCLS_MULTIPLEUSE | REGCLS_MULTI_SEPARATE)); + if (!(entry->object = heap_alloc(FIELD_OFFSET(MInterfacePointer, abData[object->ulCntData])))) + { + heap_free(entry); + return E_OUTOFMEMORY; + } + entry->object->ulCntData = object->ulCntData; + memcpy(&entry->object->abData, object->abData, object->ulCntData); + *cookie = entry->cookie = InterlockedIncrement(&next_cookie); + + EnterCriticalSection(®istered_classes_cs); + list_add_tail(®istered_classes, &entry->entry); + LeaveCriticalSection(®istered_classes_cs); + + return S_OK; +} + +static void scm_revoke_class(struct registered_class *_class) +{ + list_remove(&_class->entry); + heap_free(_class->object); + heap_free(_class); +} + +HRESULT __cdecl irpcss_server_revoke(handle_t h, unsigned int cookie) +{ + struct registered_class *cur; + + EnterCriticalSection(®istered_classes_cs); + + LIST_FOR_EACH_ENTRY(cur, ®istered_classes, struct registered_class, entry) + { + if (cur->cookie == cookie) + { + scm_revoke_class(cur); + break; + } + } + + LeaveCriticalSection(®istered_classes_cs); + + return S_OK; +} + +HRESULT __cdecl irpcss_get_class_object(handle_t h, const GUID *clsid, + PMInterfacePointer *object) +{ + struct registered_class *cur; + + *object = NULL; + + EnterCriticalSection(®istered_classes_cs); + + LIST_FOR_EACH_ENTRY(cur, ®istered_classes, struct registered_class, entry) + { + if (!memcmp(clsid, &cur->clsid, sizeof(*clsid))) + { + *object = MIDL_user_allocate(FIELD_OFFSET(MInterfacePointer, abData[cur->object->ulCntData])); + if (*object) + { + (*object)->ulCntData = cur->object->ulCntData; + memcpy((*object)->abData, cur->object->abData, cur->object->ulCntData); + } + + if (cur->single_use) + scm_revoke_class(cur); + + break; + } + } + + LeaveCriticalSection(®istered_classes_cs); + + return *object ? S_OK : E_NOINTERFACE; +} + HRESULT __cdecl irpcss_get_thread_seq_id(handle_t h, DWORD *id) { static LONG thread_seq_id;