From 5c15a6c1ec5b0ea250cc37c15d57d50248c5b26d Mon Sep 17 00:00:00 2001 From: Nikolay Sivov Date: Tue, 1 Sep 2020 15:16:33 +0100 Subject: [PATCH] combase: Move ORPC functionality. Signed-off-by: Nikolay Sivov Signed-off-by: Huw Davies Signed-off-by: Alexandre Julliard --- dlls/combase/apartment.c | 13 +- dlls/combase/combase.c | 11 + dlls/combase/combase.spec | 10 +- dlls/combase/combase_private.h | 31 +- dlls/combase/marshal.c | 24 +- dlls/combase/rpc.c | 1615 +++++++++++++++++++++++++++++++ dlls/combase/stubmanager.c | 22 +- dlls/ole32/Makefile.in | 1 - dlls/ole32/compobj.c | 21 - dlls/ole32/compobj_private.h | 146 +-- dlls/ole32/ole32.spec | 10 +- dlls/ole32/rpc.c | 1654 -------------------------------- 12 files changed, 1676 insertions(+), 1882 deletions(-) delete mode 100644 dlls/ole32/rpc.c diff --git a/dlls/combase/apartment.c b/dlls/combase/apartment.c index bb9e9f44032..125eaff1429 100644 --- a/dlls/combase/apartment.c +++ b/dlls/combase/apartment.c @@ -443,7 +443,7 @@ void apartment_freeunusedlibraries(struct apartment *apt, DWORD delay) LeaveCriticalSection(&apt->cs); } -void WINAPI apartment_release(struct apartment *apt) +void apartment_release(struct apartment *apt) { DWORD refcount; @@ -615,7 +615,7 @@ struct apartment * apartment_get_mta(void) /* Return the current apartment if it exists, or, failing that, the MTA. Caller * must free the returned apartment in either case. */ -struct apartment * WINAPI apartment_get_current_or_mta(void) +struct apartment * apartment_get_current_or_mta(void) { struct apartment *apt = com_get_current_apt(); if (apt) @@ -1098,14 +1098,13 @@ static HRESULT apartment_hostobject(struct apartment *apt, const struct host_obj } struct dispatch_params; -extern void WINAPI Internal_RPC_ExecuteCall(struct dispatch_params *params); static LRESULT CALLBACK apartment_wndproc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case DM_EXECUTERPC: - Internal_RPC_ExecuteCall((struct dispatch_params *)lParam); + rpc_execute_call((struct dispatch_params *)lParam); return 0; case DM_HOSTOBJECT: return apartment_hostobject(com_get_current_apt(), (const struct host_object_params *)lParam); @@ -1119,7 +1118,7 @@ static BOOL apartment_is_model(const struct apartment *apt, DWORD model) return (apt->multi_threaded == !(model & COINIT_APARTMENTTHREADED)); } -HRESULT WINAPI enter_apartment(struct tlsdata *data, DWORD model) +HRESULT enter_apartment(struct tlsdata *data, DWORD model) { HRESULT hr = S_OK; @@ -1143,7 +1142,7 @@ HRESULT WINAPI enter_apartment(struct tlsdata *data, DWORD model) return hr; } -void WINAPI leave_apartment(struct tlsdata *data) +void leave_apartment(struct tlsdata *data) { if (!--data->inits) { @@ -1262,7 +1261,7 @@ HRESULT apartment_createwindowifneeded(struct apartment *apt) } /* retrieves the window for the main- or apartment-threaded apartment */ -HWND WINAPI apartment_getwindow(const struct apartment *apt) +HWND apartment_getwindow(const struct apartment *apt) { assert(!apt->multi_threaded); return apt->win; diff --git a/dlls/combase/combase.c b/dlls/combase/combase.c index b3e3844a4db..d959034b57f 100644 --- a/dlls/combase/combase.c +++ b/dlls/combase/combase.c @@ -3052,6 +3052,16 @@ HRESULT WINAPI CoLockObjectExternal(IUnknown *object, BOOL lock, BOOL last_unloc return S_OK; } +/*********************************************************************** + * CoRegisterChannelHook (combase.@) + */ +HRESULT WINAPI CoRegisterChannelHook(REFGUID guidExtension, IChannelHook *channel_hook) +{ + TRACE("%s, %p\n", debugstr_guid(guidExtension), channel_hook); + + return rpc_register_channel_hook(guidExtension, channel_hook); +} + /*********************************************************************** * DllMain (combase.@) */ @@ -3068,6 +3078,7 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD reason, LPVOID reserved) if (reserved) break; apartment_global_cleanup(); DeleteCriticalSection(®istered_classes_cs); + rpc_unregister_channel_hooks(); break; case DLL_THREAD_DETACH: com_cleanup_tlsdata(); diff --git a/dlls/combase/combase.spec b/dlls/combase/combase.spec index f0429343f66..3a09ce064b0 100644 --- a/dlls/combase/combase.spec +++ b/dlls/combase/combase.spec @@ -139,6 +139,7 @@ @ stdcall CoQueryProxyBlanket(ptr ptr ptr ptr ptr ptr ptr ptr) @ stub CoReactivateObject @ stub CoRegisterActivationFilter +@ stdcall CoRegisterChannelHook(ptr ptr) @ stdcall CoRegisterClassObject(ptr ptr long long ptr) @ stdcall CoRegisterInitializeSpy(ptr ptr) @ stdcall CoRegisterMallocSpy(ptr) @@ -350,12 +351,3 @@ @ stdcall WindowsSubstringWithSpecifiedLength(ptr long long ptr) @ stdcall WindowsTrimStringEnd(ptr ptr ptr) @ stdcall WindowsTrimStringStart(ptr ptr ptr) - -@ stdcall apartment_get_current_or_mta() -@ stdcall apartment_release(ptr) -@ stdcall enter_apartment(ptr long) -@ stdcall leave_apartment(ptr) -@ stdcall apartment_getwindow(ptr) -@ stdcall stub_manager_int_release(ptr) -@ stdcall ipid_get_dispatch_params(ptr ptr ptr ptr ptr ptr ptr) -@ stdcall start_apartment_remote_unknown(ptr) diff --git a/dlls/combase/combase_private.h b/dlls/combase/combase_private.h index bcf0ad46102..703c1a26c20 100644 --- a/dlls/combase/combase_private.h +++ b/dlls/combase/combase_private.h @@ -102,7 +102,7 @@ static inline struct apartment* com_get_current_apt(void) return tlsdata->apt; } -HWND WINAPI apartment_getwindow(const struct apartment *apt) DECLSPEC_HIDDEN; +HWND apartment_getwindow(const struct apartment *apt) DECLSPEC_HIDDEN; HRESULT apartment_createwindowifneeded(struct apartment *apt) DECLSPEC_HIDDEN; void apartment_freeunusedlibraries(struct apartment *apt, DWORD unload_delay) DECLSPEC_HIDDEN; void apartment_global_cleanup(void) DECLSPEC_HIDDEN; @@ -114,6 +114,18 @@ 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_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; +HRESULT rpc_register_interface(REFIID riid) DECLSPEC_HIDDEN; +void rpc_unregister_interface(REFIID riid, BOOL wait) DECLSPEC_HIDDEN; +HRESULT rpc_resolve_oxid(OXID oxid, OXID_INFO *oxid_info) DECLSPEC_HIDDEN; +void rpc_start_remoting(struct apartment *apt) DECLSPEC_HIDDEN; +HRESULT rpc_register_channel_hook(REFGUID rguid, IChannelHook *hook) DECLSPEC_HIDDEN; +void rpc_unregister_channel_hooks(void) DECLSPEC_HIDDEN; + +struct dispatch_params; +void rpc_execute_call(struct dispatch_params *params); enum class_reg_data_origin { @@ -136,10 +148,10 @@ struct class_reg_data } u; }; -HRESULT WINAPI enter_apartment(struct tlsdata *data, DWORD model); -void WINAPI leave_apartment(struct tlsdata *data); -void WINAPI apartment_release(struct apartment *apt); -struct apartment * WINAPI apartment_get_current_or_mta(void); +HRESULT enter_apartment(struct tlsdata *data, DWORD model) DECLSPEC_HIDDEN; +void leave_apartment(struct tlsdata *data) DECLSPEC_HIDDEN; +void apartment_release(struct apartment *apt) DECLSPEC_HIDDEN; +struct apartment * apartment_get_current_or_mta(void) DECLSPEC_HIDDEN; HRESULT apartment_increment_mta_usage(CO_MTA_USAGE_COOKIE *cookie) DECLSPEC_HIDDEN; void apartment_decrement_mta_usage(CO_MTA_USAGE_COOKIE cookie) DECLSPEC_HIDDEN; struct apartment * apartment_get_mta(void) DECLSPEC_HIDDEN; @@ -152,6 +164,9 @@ void apartment_revoke_all_classes(const struct apartment *apt) DECLSPEC_HIDDEN; struct apartment * apartment_findfromoxid(OXID oxid) DECLSPEC_HIDDEN; struct apartment * apartment_findfromtid(DWORD tid) DECLSPEC_HIDDEN; +HRESULT marshal_object(struct apartment *apt, STDOBJREF *stdobjref, REFIID riid, IUnknown *object, + DWORD dest_context, void *dest_context_data, MSHLFLAGS mshlflags) DECLSPEC_HIDDEN; + /* Stub Manager */ /* signal to stub manager that this is a rem unknown object */ @@ -219,7 +234,7 @@ struct stub_manager BOOL disconnected; /* CoDisconnectObject has been called (CS lock) */ }; -ULONG WINAPI stub_manager_int_release(struct stub_manager *stub_manager) DECLSPEC_HIDDEN; +ULONG stub_manager_int_release(struct stub_manager *stub_manager) DECLSPEC_HIDDEN; struct stub_manager * get_stub_manager_from_object(struct apartment *apt, IUnknown *object, BOOL alloc) DECLSPEC_HIDDEN; void stub_manager_disconnect(struct stub_manager *m) DECLSPEC_HIDDEN; ULONG stub_manager_ext_addref(struct stub_manager *m, ULONG refs, BOOL tableweak) DECLSPEC_HIDDEN; @@ -231,3 +246,7 @@ BOOL stub_manager_notify_unmarshal(struct stub_manager *m, const IPID *ipid) DEC struct ifstub * stub_manager_find_ifstub(struct stub_manager *m, REFIID iid, MSHLFLAGS flags) DECLSPEC_HIDDEN; struct ifstub * stub_manager_new_ifstub(struct stub_manager *m, IRpcStubBuffer *sb, REFIID iid, DWORD dest_context, void *dest_context_data, MSHLFLAGS flags) DECLSPEC_HIDDEN; +HRESULT ipid_get_dispatch_params(const IPID *ipid, struct apartment **stub_apt, + struct stub_manager **manager, IRpcStubBuffer **stub, IRpcChannelBuffer **chan, + IID *iid, IUnknown **iface) DECLSPEC_HIDDEN; +HRESULT start_apartment_remote_unknown(struct apartment *apt) DECLSPEC_HIDDEN; diff --git a/dlls/combase/marshal.c b/dlls/combase/marshal.c index 977945cd013..bcc78132651 100644 --- a/dlls/combase/marshal.c +++ b/dlls/combase/marshal.c @@ -36,9 +36,6 @@ HRESULT WINAPI RPC_CreateClientChannel(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); -HRESULT WINAPI RPC_RegisterInterface(REFIID riid); -HRESULT WINAPI RPC_ResolveOxid(OXID oxid, OXID_INFO *oxid_info); -void WINAPI RPC_StartRemoting(struct apartment *apt); static HRESULT unmarshal_object(const STDOBJREF *stdobjref, struct apartment *apt, MSHCTX dest_context, void *dest_context_data, @@ -889,7 +886,7 @@ static inline HRESULT get_facbuf_for_iid(REFIID riid, IPSFactoryBuffer **facbuf) } /* marshals an object into a STDOBJREF structure */ -HRESULT WINAPI marshal_object(struct apartment *apt, STDOBJREF *stdobjref, REFIID riid, IUnknown *object, +HRESULT marshal_object(struct apartment *apt, STDOBJREF *stdobjref, REFIID riid, IUnknown *object, DWORD dest_context, void *dest_context_data, MSHLFLAGS mshlflags) { struct stub_manager *manager; @@ -971,7 +968,7 @@ HRESULT WINAPI marshal_object(struct apartment *apt, STDOBJREF *stdobjref, REFII } /* FIXME: check return value */ - RPC_RegisterInterface(riid); + rpc_register_interface(riid); stdobjref->ipid = ifstub->ipid; @@ -979,8 +976,6 @@ HRESULT WINAPI marshal_object(struct apartment *apt, STDOBJREF *stdobjref, REFII return S_OK; } - - /* Client-side identity of the server object */ static HRESULT proxy_manager_get_remunknown(struct proxy_manager * This, IRemUnknown **remunk); @@ -1583,7 +1578,7 @@ static HRESULT proxy_manager_construct( } else { - HRESULT hr = RPC_ResolveOxid(oxid, &This->oxid_info); + HRESULT hr = rpc_resolve_oxid(oxid, &This->oxid_info); if (FAILED(hr)) { CloseHandle(This->remoting_mutex); @@ -2081,7 +2076,7 @@ static HRESULT WINAPI StdMarshalImpl_MarshalInterface(IMarshal *iface, IStream * } /* make sure this apartment can be reached from other threads / processes */ - RPC_StartRemoting(apt); + rpc_start_remoting(apt); fill_std_objref(&objref, riid, NULL); hr = marshal_object(apt, &objref.u_objref.u_standard.std, riid, pv, dest_context, @@ -2134,14 +2129,11 @@ static HRESULT unmarshal_object(const STDOBJREF *stdobjref, struct apartment *ap if (hr == E_NOINTERFACE) { IRpcChannelBuffer *chanbuf; - hr = RPC_CreateClientChannel(&stdobjref->oxid, &stdobjref->ipid, - &proxy_manager->oxid_info, riid, - proxy_manager->dest_context, - proxy_manager->dest_context_data, - &chanbuf, apt); + hr = rpc_create_clientchannel(&stdobjref->oxid, &stdobjref->ipid, + &proxy_manager->oxid_info, riid, proxy_manager->dest_context, + proxy_manager->dest_context_data, &chanbuf, apt); if (hr == S_OK) - hr = proxy_manager_create_ifproxy(proxy_manager, stdobjref, - riid, chanbuf, &ifproxy); + hr = proxy_manager_create_ifproxy(proxy_manager, stdobjref, riid, chanbuf, &ifproxy); } else IUnknown_AddRef((IUnknown *)ifproxy->iface); diff --git a/dlls/combase/rpc.c b/dlls/combase/rpc.c index cb503e9ae7c..ebfc19329d8 100644 --- a/dlls/combase/rpc.c +++ b/dlls/combase/rpc.c @@ -1,4 +1,8 @@ /* + * Copyright 2001 Ove Kåven, TransGaming Technologies + * Copyright 2002 Marcus Meissner + * Copyright 2005 Mike Hearn, Rob Shearman for CodeWeavers + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either @@ -33,6 +37,138 @@ WINE_DEFAULT_DEBUG_CHANNEL(ole); +static void __RPC_STUB dispatch_rpc(RPC_MESSAGE *msg); + +/* we only use one function to dispatch calls for all methods - we use the + * RPC_IF_OLE flag to tell the RPC runtime that this is the case */ +static RPC_DISPATCH_FUNCTION rpc_dispatch_table[1] = { dispatch_rpc }; /* (RO) */ +static RPC_DISPATCH_TABLE rpc_dispatch = { 1, rpc_dispatch_table }; /* (RO) */ + +static struct list registered_interfaces = LIST_INIT(registered_interfaces); /* (CS csRegIf) */ +static CRITICAL_SECTION csRegIf; +static CRITICAL_SECTION_DEBUG csRegIf_debug = +{ + 0, 0, &csRegIf, + { &csRegIf_debug.ProcessLocksList, &csRegIf_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": dcom registered server interfaces") } +}; +static CRITICAL_SECTION csRegIf = { &csRegIf_debug, -1, 0, 0, 0, 0 }; + +static struct list channel_hooks = LIST_INIT(channel_hooks); /* (CS csChannelHook) */ +static CRITICAL_SECTION csChannelHook; +static CRITICAL_SECTION_DEBUG csChannelHook_debug = +{ + 0, 0, &csChannelHook, + { &csChannelHook_debug.ProcessLocksList, &csChannelHook_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": channel hooks") } +}; +static CRITICAL_SECTION csChannelHook = { &csChannelHook_debug, -1, 0, 0, 0, 0 }; + +static WCHAR rpctransportW[] = L"ncalrpc"; + +struct registered_if +{ + struct list entry; + DWORD refs; /* ref count */ + RPC_SERVER_INTERFACE If; /* interface registered with the RPC runtime */ +}; + +/* get the pipe endpoint specified of the specified apartment */ +static inline void get_rpc_endpoint(LPWSTR endpoint, const OXID *oxid) +{ + /* FIXME: should get endpoint from rpcss */ + static const WCHAR wszEndpointFormat[] = {'\\','p','i','p','e','\\','O','L','E','_','%','0','8','l','x','%','0','8','l','x',0}; + wsprintfW(endpoint, wszEndpointFormat, (DWORD)(*oxid >> 32),(DWORD)*oxid); +} + +typedef struct +{ + IRpcChannelBuffer IRpcChannelBuffer_iface; + LONG refs; + + DWORD dest_context; /* returned from GetDestCtx */ + void *dest_context_data; /* returned from GetDestCtx */ +} RpcChannelBuffer; + +typedef struct +{ + RpcChannelBuffer super; /* superclass */ + + RPC_BINDING_HANDLE bind; /* handle to the remote server */ + OXID oxid; /* apartment in which the channel is valid */ + DWORD server_pid; /* id of server process */ + HANDLE event; /* cached event handle */ + IID iid; /* IID of the proxy this belongs to */ +} ClientRpcChannelBuffer; + +struct dispatch_params +{ + RPCOLEMESSAGE *msg; /* message */ + IRpcStubBuffer *stub; /* stub buffer, if applicable */ + IRpcChannelBuffer *chan; /* server channel buffer, if applicable */ + IID iid; /* ID of interface being called */ + IUnknown *iface; /* interface being called */ + HANDLE handle; /* handle that will become signaled when call finishes */ + BOOL bypass_rpcrt; /* bypass RPC runtime? */ + RPC_STATUS status; /* status (out) */ + HRESULT hr; /* hresult (out) */ +}; + +struct message_state +{ + RPC_BINDING_HANDLE binding_handle; + ULONG prefix_data_len; + SChannelHookCallInfo channel_hook_info; + BOOL bypass_rpcrt; + + /* client only */ + HWND target_hwnd; + DWORD target_tid; + struct dispatch_params params; +}; + +typedef struct +{ + ULONG conformance; /* NDR */ + GUID id; + ULONG size; + /* [size_is((size+7)&~7)] */ unsigned char data[1]; +} WIRE_ORPC_EXTENT; + +typedef struct +{ + ULONG size; + ULONG reserved; + unsigned char extent[1]; +} WIRE_ORPC_EXTENT_ARRAY; + +typedef struct +{ + ULONG version; + ULONG flags; + ULONG reserved1; + GUID cid; + unsigned char extensions[1]; +} WIRE_ORPCTHIS; + +typedef struct +{ + ULONG flags; + unsigned char extensions[1]; +} WIRE_ORPCTHAT; + +struct channel_hook_entry +{ + struct list entry; + GUID id; + IChannelHook *hook; +}; + +struct channel_hook_buffer_data +{ + GUID id; + ULONG extension_size; +}; void * __RPC_USER MIDL_user_allocate(SIZE_T size) { return heap_alloc(size); @@ -629,3 +765,1482 @@ void rpc_stop_local_server(void *registration) CloseHandle(lsp->thread); HeapFree(GetProcessHeap(), 0, lsp); } + +static HRESULT unmarshal_ORPCTHAT(RPC_MESSAGE *msg, ORPCTHAT *orpcthat, ORPC_EXTENT_ARRAY *orpc_ext_array, + WIRE_ORPC_EXTENT **first_wire_orpc_extent); + +/* Channel Hook Functions */ + +static ULONG ChannelHooks_ClientGetSize(SChannelHookCallInfo *info, struct channel_hook_buffer_data **data, + unsigned int *hook_count, ULONG *extension_count) +{ + struct channel_hook_entry *entry; + ULONG total_size = 0; + unsigned int hook_index = 0; + + *hook_count = 0; + *extension_count = 0; + + EnterCriticalSection(&csChannelHook); + + LIST_FOR_EACH_ENTRY(entry, &channel_hooks, struct channel_hook_entry, entry) + (*hook_count)++; + + if (*hook_count) + *data = HeapAlloc(GetProcessHeap(), 0, *hook_count * sizeof(struct channel_hook_buffer_data)); + else + *data = NULL; + + LIST_FOR_EACH_ENTRY(entry, &channel_hooks, struct channel_hook_entry, entry) + { + ULONG extension_size = 0; + + IChannelHook_ClientGetSize(entry->hook, &entry->id, &info->iid, &extension_size); + + TRACE("%s: extension_size = %u\n", debugstr_guid(&entry->id), extension_size); + + extension_size = (extension_size+7)&~7; + (*data)[hook_index].id = entry->id; + (*data)[hook_index].extension_size = extension_size; + + /* an extension is only put onto the wire if it has data to write */ + if (extension_size) + { + total_size += FIELD_OFFSET(WIRE_ORPC_EXTENT, data[extension_size]); + (*extension_count)++; + } + + hook_index++; + } + + LeaveCriticalSection(&csChannelHook); + + return total_size; +} + +static unsigned char * ChannelHooks_ClientFillBuffer(SChannelHookCallInfo *info, + unsigned char *buffer, struct channel_hook_buffer_data *data, + unsigned int hook_count) +{ + struct channel_hook_entry *entry; + + EnterCriticalSection(&csChannelHook); + + LIST_FOR_EACH_ENTRY(entry, &channel_hooks, struct channel_hook_entry, entry) + { + unsigned int i; + ULONG extension_size = 0; + WIRE_ORPC_EXTENT *wire_orpc_extent = (WIRE_ORPC_EXTENT *)buffer; + + for (i = 0; i < hook_count; i++) + if (IsEqualGUID(&entry->id, &data[i].id)) + extension_size = data[i].extension_size; + + /* an extension is only put onto the wire if it has data to write */ + if (!extension_size) + continue; + + IChannelHook_ClientFillBuffer(entry->hook, &entry->id, &info->iid, + &extension_size, buffer + FIELD_OFFSET(WIRE_ORPC_EXTENT, data[0])); + + TRACE("%s: extension_size = %u\n", debugstr_guid(&entry->id), extension_size); + + /* FIXME: set unused portion of wire_orpc_extent->data to 0? */ + + wire_orpc_extent->conformance = (extension_size+7)&~7; + wire_orpc_extent->size = extension_size; + wire_orpc_extent->id = entry->id; + buffer += FIELD_OFFSET(WIRE_ORPC_EXTENT, data[wire_orpc_extent->conformance]); + } + + LeaveCriticalSection(&csChannelHook); + + return buffer; +} + +static void ChannelHooks_ServerNotify(SChannelHookCallInfo *info, + DWORD lDataRep, WIRE_ORPC_EXTENT *first_wire_orpc_extent, + ULONG extension_count) +{ + struct channel_hook_entry *entry; + ULONG i; + + EnterCriticalSection(&csChannelHook); + + LIST_FOR_EACH_ENTRY(entry, &channel_hooks, struct channel_hook_entry, entry) + { + WIRE_ORPC_EXTENT *wire_orpc_extent; + for (i = 0, wire_orpc_extent = first_wire_orpc_extent; + i < extension_count; + i++, wire_orpc_extent = (WIRE_ORPC_EXTENT *)&wire_orpc_extent->data[wire_orpc_extent->conformance]) + { + if (IsEqualGUID(&entry->id, &wire_orpc_extent->id)) + break; + } + if (i == extension_count) wire_orpc_extent = NULL; + + IChannelHook_ServerNotify(entry->hook, &entry->id, &info->iid, + wire_orpc_extent ? wire_orpc_extent->size : 0, + wire_orpc_extent ? wire_orpc_extent->data : NULL, + lDataRep); + } + + LeaveCriticalSection(&csChannelHook); +} + +static ULONG ChannelHooks_ServerGetSize(SChannelHookCallInfo *info, + struct channel_hook_buffer_data **data, unsigned int *hook_count, + ULONG *extension_count) +{ + struct channel_hook_entry *entry; + ULONG total_size = 0; + unsigned int hook_index = 0; + + *hook_count = 0; + *extension_count = 0; + + EnterCriticalSection(&csChannelHook); + + LIST_FOR_EACH_ENTRY(entry, &channel_hooks, struct channel_hook_entry, entry) + (*hook_count)++; + + if (*hook_count) + *data = HeapAlloc(GetProcessHeap(), 0, *hook_count * sizeof(struct channel_hook_buffer_data)); + else + *data = NULL; + + LIST_FOR_EACH_ENTRY(entry, &channel_hooks, struct channel_hook_entry, entry) + { + ULONG extension_size = 0; + + IChannelHook_ServerGetSize(entry->hook, &entry->id, &info->iid, S_OK, + &extension_size); + + TRACE("%s: extension_size = %u\n", debugstr_guid(&entry->id), extension_size); + + extension_size = (extension_size+7)&~7; + (*data)[hook_index].id = entry->id; + (*data)[hook_index].extension_size = extension_size; + + /* an extension is only put onto the wire if it has data to write */ + if (extension_size) + { + total_size += FIELD_OFFSET(WIRE_ORPC_EXTENT, data[extension_size]); + (*extension_count)++; + } + + hook_index++; + } + + LeaveCriticalSection(&csChannelHook); + + return total_size; +} + +static unsigned char * ChannelHooks_ServerFillBuffer(SChannelHookCallInfo *info, + unsigned char *buffer, struct channel_hook_buffer_data *data, + unsigned int hook_count) +{ + struct channel_hook_entry *entry; + + EnterCriticalSection(&csChannelHook); + + LIST_FOR_EACH_ENTRY(entry, &channel_hooks, struct channel_hook_entry, entry) + { + unsigned int i; + ULONG extension_size = 0; + WIRE_ORPC_EXTENT *wire_orpc_extent = (WIRE_ORPC_EXTENT *)buffer; + + for (i = 0; i < hook_count; i++) + if (IsEqualGUID(&entry->id, &data[i].id)) + extension_size = data[i].extension_size; + + /* an extension is only put onto the wire if it has data to write */ + if (!extension_size) + continue; + + IChannelHook_ServerFillBuffer(entry->hook, &entry->id, &info->iid, + &extension_size, buffer + FIELD_OFFSET(WIRE_ORPC_EXTENT, data[0]), + S_OK); + + TRACE("%s: extension_size = %u\n", debugstr_guid(&entry->id), extension_size); + + /* FIXME: set unused portion of wire_orpc_extent->data to 0? */ + + wire_orpc_extent->conformance = (extension_size+7)&~7; + wire_orpc_extent->size = extension_size; + wire_orpc_extent->id = entry->id; + buffer += FIELD_OFFSET(WIRE_ORPC_EXTENT, data[wire_orpc_extent->conformance]); + } + + LeaveCriticalSection(&csChannelHook); + + return buffer; +} + +static void ChannelHooks_ClientNotify(SChannelHookCallInfo *info, + DWORD lDataRep, WIRE_ORPC_EXTENT *first_wire_orpc_extent, + ULONG extension_count, HRESULT hrFault) +{ + struct channel_hook_entry *entry; + ULONG i; + + EnterCriticalSection(&csChannelHook); + + LIST_FOR_EACH_ENTRY(entry, &channel_hooks, struct channel_hook_entry, entry) + { + WIRE_ORPC_EXTENT *wire_orpc_extent; + for (i = 0, wire_orpc_extent = first_wire_orpc_extent; + i < extension_count; + i++, wire_orpc_extent = (WIRE_ORPC_EXTENT *)&wire_orpc_extent->data[wire_orpc_extent->conformance]) + { + if (IsEqualGUID(&entry->id, &wire_orpc_extent->id)) + break; + } + if (i == extension_count) wire_orpc_extent = NULL; + + IChannelHook_ClientNotify(entry->hook, &entry->id, &info->iid, + wire_orpc_extent ? wire_orpc_extent->size : 0, + wire_orpc_extent ? wire_orpc_extent->data : NULL, + lDataRep, hrFault); + } + + LeaveCriticalSection(&csChannelHook); +} + +HRESULT rpc_register_channel_hook(REFGUID rguid, IChannelHook *hook) +{ + struct channel_hook_entry *entry; + + entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry)); + if (!entry) + return E_OUTOFMEMORY; + + entry->id = *rguid; + entry->hook = hook; + IChannelHook_AddRef(hook); + + EnterCriticalSection(&csChannelHook); + list_add_tail(&channel_hooks, &entry->entry); + LeaveCriticalSection(&csChannelHook); + + return S_OK; +} + +void rpc_unregister_channel_hooks(void) +{ + struct channel_hook_entry *cursor; + struct channel_hook_entry *cursor2; + + EnterCriticalSection(&csChannelHook); + LIST_FOR_EACH_ENTRY_SAFE(cursor, cursor2, &channel_hooks, struct channel_hook_entry, entry) + HeapFree(GetProcessHeap(), 0, cursor); + LeaveCriticalSection(&csChannelHook); + DeleteCriticalSection(&csChannelHook); + DeleteCriticalSection(&csRegIf); +} + +/* RPC Channel Buffer Functions */ + +static HRESULT WINAPI RpcChannelBuffer_QueryInterface(IRpcChannelBuffer *iface, REFIID riid, LPVOID *ppv) +{ + *ppv = NULL; + if (IsEqualIID(riid,&IID_IRpcChannelBuffer) || IsEqualIID(riid,&IID_IUnknown)) + { + *ppv = iface; + IRpcChannelBuffer_AddRef(iface); + return S_OK; + } + return E_NOINTERFACE; +} + +static ULONG WINAPI RpcChannelBuffer_AddRef(LPRPCCHANNELBUFFER iface) +{ + RpcChannelBuffer *This = (RpcChannelBuffer *)iface; + return InterlockedIncrement(&This->refs); +} + +static ULONG WINAPI ServerRpcChannelBuffer_Release(LPRPCCHANNELBUFFER iface) +{ + RpcChannelBuffer *This = (RpcChannelBuffer *)iface; + ULONG ref; + + ref = InterlockedDecrement(&This->refs); + if (ref) + return ref; + + HeapFree(GetProcessHeap(), 0, This); + return 0; +} + +static ULONG WINAPI ClientRpcChannelBuffer_Release(LPRPCCHANNELBUFFER iface) +{ + ClientRpcChannelBuffer *This = (ClientRpcChannelBuffer *)iface; + ULONG ref; + + ref = InterlockedDecrement(&This->super.refs); + if (ref) + return ref; + + if (This->event) CloseHandle(This->event); + RpcBindingFree(&This->bind); + HeapFree(GetProcessHeap(), 0, This); + return 0; +} + +static HRESULT WINAPI ServerRpcChannelBuffer_GetBuffer(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE* olemsg, REFIID riid) +{ + RpcChannelBuffer *This = (RpcChannelBuffer *)iface; + RPC_MESSAGE *msg = (RPC_MESSAGE *)olemsg; + RPC_STATUS status; + ORPCTHAT *orpcthat; + struct message_state *message_state; + ULONG extensions_size; + struct channel_hook_buffer_data *channel_hook_data; + unsigned int channel_hook_count; + ULONG extension_count; + + TRACE("(%p)->(%p,%s)\n", This, olemsg, debugstr_guid(riid)); + + message_state = msg->Handle; + /* restore the binding handle and the real start of data */ + msg->Handle = message_state->binding_handle; + msg->Buffer = (char *)msg->Buffer - message_state->prefix_data_len; + + extensions_size = ChannelHooks_ServerGetSize(&message_state->channel_hook_info, + &channel_hook_data, &channel_hook_count, &extension_count); + + msg->BufferLength += FIELD_OFFSET(WIRE_ORPCTHAT, extensions) + sizeof(DWORD); + if (extensions_size) + { + msg->BufferLength += FIELD_OFFSET(WIRE_ORPC_EXTENT_ARRAY, extent[2*sizeof(DWORD) + extensions_size]); + if (extension_count & 1) + msg->BufferLength += FIELD_OFFSET(WIRE_ORPC_EXTENT, data[0]); + } + + if (message_state->bypass_rpcrt) + { + msg->Buffer = HeapAlloc(GetProcessHeap(), 0, msg->BufferLength); + if (msg->Buffer) + status = RPC_S_OK; + else + { + HeapFree(GetProcessHeap(), 0, channel_hook_data); + return E_OUTOFMEMORY; + } + } + else + status = I_RpcGetBuffer(msg); + + orpcthat = msg->Buffer; + msg->Buffer = (char *)msg->Buffer + FIELD_OFFSET(WIRE_ORPCTHAT, extensions); + + orpcthat->flags = ORPCF_NULL /* FIXME? */; + + /* NDR representation of orpcthat->extensions */ + *(DWORD *)msg->Buffer = extensions_size ? 1 : 0; + msg->Buffer = (char *)msg->Buffer + sizeof(DWORD); + + if (extensions_size) + { + WIRE_ORPC_EXTENT_ARRAY *orpc_extent_array = msg->Buffer; + orpc_extent_array->size = extension_count; + orpc_extent_array->reserved = 0; + msg->Buffer = (char *)msg->Buffer + FIELD_OFFSET(WIRE_ORPC_EXTENT_ARRAY, extent); + /* NDR representation of orpc_extent_array->extent */ + *(DWORD *)msg->Buffer = 1; + msg->Buffer = (char *)msg->Buffer + sizeof(DWORD); + /* NDR representation of [size_is] attribute of orpc_extent_array->extent */ + *(DWORD *)msg->Buffer = (extension_count + 1) & ~1; + msg->Buffer = (char *)msg->Buffer + sizeof(DWORD); + + msg->Buffer = ChannelHooks_ServerFillBuffer(&message_state->channel_hook_info, + msg->Buffer, channel_hook_data, channel_hook_count); + + /* we must add a dummy extension if there is an odd extension + * count to meet the contract specified by the size_is attribute */ + if (extension_count & 1) + { + WIRE_ORPC_EXTENT *wire_orpc_extent = msg->Buffer; + wire_orpc_extent->conformance = 0; + wire_orpc_extent->id = GUID_NULL; + wire_orpc_extent->size = 0; + msg->Buffer = (char *)msg->Buffer + FIELD_OFFSET(WIRE_ORPC_EXTENT, data[0]); + } + } + + HeapFree(GetProcessHeap(), 0, channel_hook_data); + + /* store the prefixed data length so that we can restore the real buffer + * later */ + message_state->prefix_data_len = (char *)msg->Buffer - (char *)orpcthat; + msg->BufferLength -= message_state->prefix_data_len; + /* save away the message state again */ + msg->Handle = message_state; + + TRACE("-- %d\n", status); + + return HRESULT_FROM_WIN32(status); +} + +static HANDLE ClientRpcChannelBuffer_GetEventHandle(ClientRpcChannelBuffer *This) +{ + HANDLE event = InterlockedExchangePointer(&This->event, NULL); + + /* Note: must be auto-reset event so we can reuse it without a call + * to ResetEvent */ + if (!event) event = CreateEventW(NULL, FALSE, FALSE, NULL); + + return event; +} + +static void ClientRpcChannelBuffer_ReleaseEventHandle(ClientRpcChannelBuffer *This, HANDLE event) +{ + if (InterlockedCompareExchangePointer(&This->event, event, NULL)) + /* already a handle cached in This */ + CloseHandle(event); +} + +static HRESULT WINAPI ClientRpcChannelBuffer_GetBuffer(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE* olemsg, REFIID riid) +{ + ClientRpcChannelBuffer *This = (ClientRpcChannelBuffer *)iface; + RPC_MESSAGE *msg = (RPC_MESSAGE *)olemsg; + RPC_CLIENT_INTERFACE *cif; + RPC_STATUS status; + ORPCTHIS *orpcthis; + struct message_state *message_state; + ULONG extensions_size; + struct channel_hook_buffer_data *channel_hook_data; + unsigned int channel_hook_count; + ULONG extension_count; + IPID ipid; + HRESULT hr; + struct apartment *apt = NULL; + + TRACE("(%p)->(%p,%s)\n", This, olemsg, debugstr_guid(riid)); + + cif = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(RPC_CLIENT_INTERFACE)); + if (!cif) + return E_OUTOFMEMORY; + + message_state = HeapAlloc(GetProcessHeap(), 0, sizeof(*message_state)); + if (!message_state) + { + HeapFree(GetProcessHeap(), 0, cif); + return E_OUTOFMEMORY; + } + + cif->Length = sizeof(RPC_CLIENT_INTERFACE); + /* RPC interface ID = COM interface ID */ + cif->InterfaceId.SyntaxGUID = This->iid; + /* COM objects always have a version of 0.0 */ + cif->InterfaceId.SyntaxVersion.MajorVersion = 0; + cif->InterfaceId.SyntaxVersion.MinorVersion = 0; + msg->Handle = This->bind; + msg->RpcInterfaceInformation = cif; + + message_state->prefix_data_len = 0; + message_state->binding_handle = This->bind; + + message_state->channel_hook_info.iid = *riid; + message_state->channel_hook_info.cbSize = sizeof(message_state->channel_hook_info); + CoGetCurrentLogicalThreadId(&message_state->channel_hook_info.uCausality); + message_state->channel_hook_info.dwServerPid = This->server_pid; + message_state->channel_hook_info.iMethod = msg->ProcNum & ~RPC_FLAGS_VALID_BIT; + message_state->channel_hook_info.pObject = NULL; /* only present on server-side */ + message_state->target_hwnd = NULL; + message_state->target_tid = 0; + memset(&message_state->params, 0, sizeof(message_state->params)); + + extensions_size = ChannelHooks_ClientGetSize(&message_state->channel_hook_info, + &channel_hook_data, &channel_hook_count, &extension_count); + + msg->BufferLength += FIELD_OFFSET(WIRE_ORPCTHIS, extensions) + sizeof(DWORD); + if (extensions_size) + { + msg->BufferLength += FIELD_OFFSET(WIRE_ORPC_EXTENT_ARRAY, extent[2*sizeof(DWORD) + extensions_size]); + if (extension_count & 1) + msg->BufferLength += FIELD_OFFSET(WIRE_ORPC_EXTENT, data[0]); + } + + RpcBindingInqObject(message_state->binding_handle, &ipid); + hr = ipid_get_dispatch_params(&ipid, &apt, NULL, &message_state->params.stub, + &message_state->params.chan, + &message_state->params.iid, + &message_state->params.iface); + if (hr == S_OK) + { + /* stub, chan, iface and iid are unneeded in multi-threaded case as we go + * via the RPC runtime */ + if (apt->multi_threaded) + { + IRpcStubBuffer_Release(message_state->params.stub); + message_state->params.stub = NULL; + IRpcChannelBuffer_Release(message_state->params.chan); + message_state->params.chan = NULL; + message_state->params.iface = NULL; + } + else + { + message_state->params.bypass_rpcrt = TRUE; + message_state->target_hwnd = apartment_getwindow(apt); + message_state->target_tid = apt->tid; + /* we assume later on that this being non-NULL is the indicator that + * means call directly instead of going through RPC runtime */ + if (!message_state->target_hwnd) + ERR("window for apartment %s is NULL\n", wine_dbgstr_longlong(apt->oxid)); + } + } + if (apt) apartment_release(apt); + message_state->params.handle = ClientRpcChannelBuffer_GetEventHandle(This); + /* Note: message_state->params.msg is initialised in + * ClientRpcChannelBuffer_SendReceive */ + + /* shortcut the RPC runtime */ + if (message_state->target_hwnd) + { + msg->Buffer = HeapAlloc(GetProcessHeap(), 0, msg->BufferLength); + if (msg->Buffer) + status = RPC_S_OK; + else + status = ERROR_OUTOFMEMORY; + } + else + status = I_RpcGetBuffer(msg); + + msg->Handle = message_state; + + if (status == RPC_S_OK) + { + orpcthis = msg->Buffer; + msg->Buffer = (char *)msg->Buffer + FIELD_OFFSET(WIRE_ORPCTHIS, extensions); + + orpcthis->version.MajorVersion = COM_MAJOR_VERSION; + orpcthis->version.MinorVersion = COM_MINOR_VERSION; + orpcthis->flags = message_state->channel_hook_info.dwServerPid ? ORPCF_LOCAL : ORPCF_NULL; + orpcthis->reserved1 = 0; + orpcthis->cid = message_state->channel_hook_info.uCausality; + + /* NDR representation of orpcthis->extensions */ + *(DWORD *)msg->Buffer = extensions_size ? 1 : 0; + msg->Buffer = (char *)msg->Buffer + sizeof(DWORD); + + if (extensions_size) + { + ORPC_EXTENT_ARRAY *orpc_extent_array = msg->Buffer; + orpc_extent_array->size = extension_count; + orpc_extent_array->reserved = 0; + msg->Buffer = (char *)msg->Buffer + FIELD_OFFSET(WIRE_ORPC_EXTENT_ARRAY, extent); + /* NDR representation of orpc_extent_array->extent */ + *(DWORD *)msg->Buffer = 1; + msg->Buffer = (char *)msg->Buffer + sizeof(DWORD); + /* NDR representation of [size_is] attribute of orpc_extent_array->extent */ + *(DWORD *)msg->Buffer = (extension_count + 1) & ~1; + msg->Buffer = (char *)msg->Buffer + sizeof(DWORD); + + msg->Buffer = ChannelHooks_ClientFillBuffer(&message_state->channel_hook_info, + msg->Buffer, channel_hook_data, channel_hook_count); + + /* we must add a dummy extension if there is an odd extension + * count to meet the contract specified by the size_is attribute */ + if (extension_count & 1) + { + WIRE_ORPC_EXTENT *wire_orpc_extent = msg->Buffer; + wire_orpc_extent->conformance = 0; + wire_orpc_extent->id = GUID_NULL; + wire_orpc_extent->size = 0; + msg->Buffer = (char *)msg->Buffer + FIELD_OFFSET(WIRE_ORPC_EXTENT, data[0]); + } + } + + /* store the prefixed data length so that we can restore the real buffer + * pointer in ClientRpcChannelBuffer_SendReceive. */ + message_state->prefix_data_len = (char *)msg->Buffer - (char *)orpcthis; + msg->BufferLength -= message_state->prefix_data_len; + } + + HeapFree(GetProcessHeap(), 0, channel_hook_data); + + TRACE("-- %d\n", status); + + return HRESULT_FROM_WIN32(status); +} + +static HRESULT WINAPI ServerRpcChannelBuffer_SendReceive(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE *olemsg, ULONG *pstatus) +{ + FIXME("stub\n"); + return E_NOTIMPL; +} + +/* this thread runs an outgoing RPC */ +static DWORD WINAPI rpc_sendreceive_thread(LPVOID param) +{ + struct dispatch_params *data = param; + + /* Note: I_RpcSendReceive doesn't raise exceptions like the higher-level + * RPC functions do */ + data->status = I_RpcSendReceive((RPC_MESSAGE *)data->msg); + + TRACE("completed with status 0x%x\n", data->status); + + SetEvent(data->handle); + + return 0; +} + +static inline HRESULT ClientRpcChannelBuffer_IsCorrectApartment(ClientRpcChannelBuffer *This, const struct apartment *apt) +{ + if (!apt) + return S_FALSE; + if (This->oxid != apartment_getoxid(apt)) + return S_FALSE; + return S_OK; +} + +static HRESULT WINAPI ClientRpcChannelBuffer_SendReceive(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE *olemsg, ULONG *pstatus) +{ + ClientRpcChannelBuffer *This = (ClientRpcChannelBuffer *)iface; + HRESULT hr; + RPC_MESSAGE *msg = (RPC_MESSAGE *)olemsg; + RPC_STATUS status; + DWORD index; + struct message_state *message_state; + ORPCTHAT orpcthat; + ORPC_EXTENT_ARRAY orpc_ext_array; + WIRE_ORPC_EXTENT *first_wire_orpc_extent = NULL; + HRESULT hrFault = S_OK; + struct apartment *apt = apartment_get_current_or_mta(); + struct tlsdata *tlsdata; + + TRACE("(%p) iMethod=%d\n", olemsg, olemsg->iMethod); + + hr = ClientRpcChannelBuffer_IsCorrectApartment(This, apt); + if (hr != S_OK) + { + ERR("called from wrong apartment, should have been 0x%s\n", + wine_dbgstr_longlong(This->oxid)); + if (apt) apartment_release(apt); + return RPC_E_WRONG_THREAD; + } + + if (FAILED(hr = com_get_tlsdata(&tlsdata))) + return hr; + + /* This situation should be impossible in multi-threaded apartments, + * because the calling thread isn't re-enterable. + * Note: doing a COM call during the processing of a sent message is + * only disallowed if a client call is already being waited for + * completion */ + if (!apt->multi_threaded && + tlsdata->pending_call_count_client && + InSendMessage()) + { + ERR("can't make an outgoing COM call in response to a sent message\n"); + apartment_release(apt); + return RPC_E_CANTCALLOUT_ININPUTSYNCCALL; + } + + message_state = msg->Handle; + /* restore the binding handle and the real start of data */ + msg->Handle = message_state->binding_handle; + msg->Buffer = (char *)msg->Buffer - message_state->prefix_data_len; + msg->BufferLength += message_state->prefix_data_len; + + /* Note: this is an optimization in the Microsoft OLE runtime that we need + * to copy, as shown by the test_no_couninitialize_client test. without + * short-circuiting the RPC runtime in the case below, the test will + * deadlock on the loader lock due to the RPC runtime needing to create + * a thread to process the RPC when this function is called indirectly + * from DllMain */ + + message_state->params.msg = olemsg; + if (message_state->params.bypass_rpcrt) + { + TRACE("Calling apartment thread 0x%08x...\n", message_state->target_tid); + + msg->ProcNum &= ~RPC_FLAGS_VALID_BIT; + + if (!PostMessageW(message_state->target_hwnd, DM_EXECUTERPC, 0, + (LPARAM)&message_state->params)) + { + ERR("PostMessage failed with error %u\n", GetLastError()); + + /* Note: message_state->params.iface doesn't have a reference and + * so doesn't need to be released */ + + hr = HRESULT_FROM_WIN32(GetLastError()); + } + } + else + { + /* we use a separate thread here because we need to be able to + * pump the message loop in the application thread: if we do not, + * any windows created by this thread will hang and RPCs that try + * and re-enter this STA from an incoming server thread will + * deadlock. InstallShield is an example of that. + */ + if (!QueueUserWorkItem(rpc_sendreceive_thread, &message_state->params, WT_EXECUTEDEFAULT)) + { + ERR("QueueUserWorkItem failed with error %u\n", GetLastError()); + hr = E_UNEXPECTED; + } + else + hr = S_OK; + } + + if (hr == S_OK) + { + if (WaitForSingleObject(message_state->params.handle, 0)) + { + tlsdata->pending_call_count_client++; + hr = CoWaitForMultipleHandles(0, INFINITE, 1, &message_state->params.handle, &index); + tlsdata->pending_call_count_client--; + } + } + ClientRpcChannelBuffer_ReleaseEventHandle(This, message_state->params.handle); + + /* for WM shortcut, faults are returned in params->hr */ + if (hr == S_OK) + hrFault = message_state->params.hr; + + status = message_state->params.status; + + orpcthat.flags = ORPCF_NULL; + orpcthat.extensions = NULL; + + TRACE("RPC call status: 0x%x\n", status); + if (status != RPC_S_OK) + hr = HRESULT_FROM_WIN32(status); + + TRACE("hrFault = 0x%08x\n", hrFault); + + /* FIXME: this condition should be + * "hr == S_OK && (!hrFault || msg->BufferLength > FIELD_OFFSET(ORPCTHAT, extensions) + 4)" + * but we don't currently reset the message length for PostMessage + * dispatched calls */ + if (hr == S_OK && hrFault == S_OK) + { + HRESULT hr2; + char *original_buffer = msg->Buffer; + + /* handle ORPCTHAT and client extensions */ + + hr2 = unmarshal_ORPCTHAT(msg, &orpcthat, &orpc_ext_array, &first_wire_orpc_extent); + if (FAILED(hr2)) + hr = hr2; + + message_state->prefix_data_len = (char *)msg->Buffer - original_buffer; + msg->BufferLength -= message_state->prefix_data_len; + } + else + message_state->prefix_data_len = 0; + + if (hr == S_OK) + { + ChannelHooks_ClientNotify(&message_state->channel_hook_info, + msg->DataRepresentation, + first_wire_orpc_extent, + orpcthat.extensions && first_wire_orpc_extent ? orpcthat.extensions->size : 0, + hrFault); + } + + /* save away the message state again */ + msg->Handle = message_state; + + if (pstatus) *pstatus = status; + + if (hr == S_OK) + hr = hrFault; + + TRACE("-- 0x%08x\n", hr); + + apartment_release(apt); + return hr; +} + +static HRESULT WINAPI ServerRpcChannelBuffer_FreeBuffer(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE* olemsg) +{ + RPC_MESSAGE *msg = (RPC_MESSAGE *)olemsg; + RPC_STATUS status; + struct message_state *message_state; + + TRACE("(%p)\n", msg); + + message_state = msg->Handle; + /* restore the binding handle and the real start of data */ + msg->Handle = message_state->binding_handle; + msg->Buffer = (char *)msg->Buffer - message_state->prefix_data_len; + msg->BufferLength += message_state->prefix_data_len; + message_state->prefix_data_len = 0; + + if (message_state->bypass_rpcrt) + { + HeapFree(GetProcessHeap(), 0, msg->Buffer); + status = RPC_S_OK; + } + else + status = I_RpcFreeBuffer(msg); + + msg->Handle = message_state; + + TRACE("-- %d\n", status); + + return HRESULT_FROM_WIN32(status); +} + +static HRESULT WINAPI ClientRpcChannelBuffer_FreeBuffer(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE* olemsg) +{ + RPC_MESSAGE *msg = (RPC_MESSAGE *)olemsg; + RPC_STATUS status; + struct message_state *message_state; + + TRACE("(%p)\n", msg); + + message_state = msg->Handle; + /* restore the binding handle and the real start of data */ + msg->Handle = message_state->binding_handle; + msg->Buffer = (char *)msg->Buffer - message_state->prefix_data_len; + msg->BufferLength += message_state->prefix_data_len; + + if (message_state->params.bypass_rpcrt) + { + HeapFree(GetProcessHeap(), 0, msg->Buffer); + status = RPC_S_OK; + } + else + status = I_RpcFreeBuffer(msg); + + HeapFree(GetProcessHeap(), 0, msg->RpcInterfaceInformation); + msg->RpcInterfaceInformation = NULL; + + if (message_state->params.stub) + IRpcStubBuffer_Release(message_state->params.stub); + if (message_state->params.chan) + IRpcChannelBuffer_Release(message_state->params.chan); + HeapFree(GetProcessHeap(), 0, message_state); + + TRACE("-- %d\n", status); + + return HRESULT_FROM_WIN32(status); +} + +static HRESULT WINAPI ClientRpcChannelBuffer_GetDestCtx(LPRPCCHANNELBUFFER iface, DWORD* pdwDestContext, void** ppvDestContext) +{ + ClientRpcChannelBuffer *This = (ClientRpcChannelBuffer *)iface; + + TRACE("(%p,%p)\n", pdwDestContext, ppvDestContext); + + *pdwDestContext = This->super.dest_context; + *ppvDestContext = This->super.dest_context_data; + + return S_OK; +} + +static HRESULT WINAPI ServerRpcChannelBuffer_GetDestCtx(LPRPCCHANNELBUFFER iface, DWORD* dest_context, void** dest_context_data) +{ + RpcChannelBuffer *This = (RpcChannelBuffer *)iface; + + TRACE("(%p,%p)\n", dest_context, dest_context_data); + + *dest_context = This->dest_context; + *dest_context_data = This->dest_context_data; + return S_OK; +} + +static HRESULT WINAPI RpcChannelBuffer_IsConnected(LPRPCCHANNELBUFFER iface) +{ + TRACE("()\n"); + /* native does nothing too */ + return S_OK; +} + +static const IRpcChannelBufferVtbl ClientRpcChannelBufferVtbl = +{ + RpcChannelBuffer_QueryInterface, + RpcChannelBuffer_AddRef, + ClientRpcChannelBuffer_Release, + ClientRpcChannelBuffer_GetBuffer, + ClientRpcChannelBuffer_SendReceive, + ClientRpcChannelBuffer_FreeBuffer, + ClientRpcChannelBuffer_GetDestCtx, + RpcChannelBuffer_IsConnected +}; + +static const IRpcChannelBufferVtbl ServerRpcChannelBufferVtbl = +{ + RpcChannelBuffer_QueryInterface, + RpcChannelBuffer_AddRef, + ServerRpcChannelBuffer_Release, + ServerRpcChannelBuffer_GetBuffer, + ServerRpcChannelBuffer_SendReceive, + ServerRpcChannelBuffer_FreeBuffer, + ServerRpcChannelBuffer_GetDestCtx, + RpcChannelBuffer_IsConnected +}; + +/* returns a channel buffer for proxies */ +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) +{ + ClientRpcChannelBuffer *This; + WCHAR endpoint[200]; + RPC_BINDING_HANDLE bind; + RPC_STATUS status; + LPWSTR string_binding; + + /* FIXME: get the endpoint from oxid_info->psa instead */ + get_rpc_endpoint(endpoint, oxid); + + TRACE("proxy pipe: connecting to endpoint: %s\n", debugstr_w(endpoint)); + + status = RpcStringBindingComposeW( + NULL, + rpctransportW, + NULL, + endpoint, + NULL, + &string_binding); + + if (status == RPC_S_OK) + { + status = RpcBindingFromStringBindingW(string_binding, &bind); + + if (status == RPC_S_OK) + { + IPID ipid2 = *ipid; /* why can't RpcBindingSetObject take a const? */ + status = RpcBindingSetObject(bind, &ipid2); + if (status != RPC_S_OK) + RpcBindingFree(&bind); + } + + RpcStringFreeW(&string_binding); + } + + if (status != RPC_S_OK) + { + ERR("Couldn't get binding for endpoint %s, status = %d\n", debugstr_w(endpoint), status); + return HRESULT_FROM_WIN32(status); + } + + This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This)); + if (!This) + { + RpcBindingFree(&bind); + return E_OUTOFMEMORY; + } + + This->super.IRpcChannelBuffer_iface.lpVtbl = &ClientRpcChannelBufferVtbl; + This->super.refs = 1; + This->super.dest_context = dest_context; + This->super.dest_context_data = dest_context_data; + This->bind = bind; + This->oxid = apartment_getoxid(apt); + This->server_pid = oxid_info->dwPid; + This->event = NULL; + This->iid = *iid; + + *chan = &This->super.IRpcChannelBuffer_iface; + + return S_OK; +} + +HRESULT rpc_create_serverchannel(DWORD dest_context, void *dest_context_data, IRpcChannelBuffer **chan) +{ + RpcChannelBuffer *This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This)); + if (!This) + return E_OUTOFMEMORY; + + This->IRpcChannelBuffer_iface.lpVtbl = &ServerRpcChannelBufferVtbl; + This->refs = 1; + This->dest_context = dest_context; + This->dest_context_data = dest_context_data; + + *chan = &This->IRpcChannelBuffer_iface; + + return S_OK; +} + +/* unmarshals ORPC_EXTENT_ARRAY according to NDR rules, but doesn't allocate + * any memory */ +static HRESULT unmarshal_ORPC_EXTENT_ARRAY(RPC_MESSAGE *msg, const char *end, + ORPC_EXTENT_ARRAY *extensions, + WIRE_ORPC_EXTENT **first_wire_orpc_extent) +{ + DWORD pointer_id; + DWORD i; + + memcpy(extensions, msg->Buffer, FIELD_OFFSET(WIRE_ORPC_EXTENT_ARRAY, extent)); + msg->Buffer = (char *)msg->Buffer + FIELD_OFFSET(WIRE_ORPC_EXTENT_ARRAY, extent); + + if ((const char *)msg->Buffer + 2 * sizeof(DWORD) > end) + return RPC_E_INVALID_HEADER; + + pointer_id = *(DWORD *)msg->Buffer; + msg->Buffer = (char *)msg->Buffer + sizeof(DWORD); + extensions->extent = NULL; + + if (pointer_id) + { + WIRE_ORPC_EXTENT *wire_orpc_extent; + + /* conformance */ + if (*(DWORD *)msg->Buffer != ((extensions->size+1)&~1)) + return RPC_S_INVALID_BOUND; + + msg->Buffer = (char *)msg->Buffer + sizeof(DWORD); + + /* arbitrary limit for security (don't know what native does) */ + if (extensions->size > 256) + { + ERR("too many extensions: %d\n", extensions->size); + return RPC_S_INVALID_BOUND; + } + + *first_wire_orpc_extent = wire_orpc_extent = msg->Buffer; + for (i = 0; i < ((extensions->size+1)&~1); i++) + { + if ((const char *)&wire_orpc_extent->data[0] > end) + return RPC_S_INVALID_BOUND; + if (wire_orpc_extent->conformance != ((wire_orpc_extent->size+7)&~7)) + return RPC_S_INVALID_BOUND; + if ((const char *)&wire_orpc_extent->data[wire_orpc_extent->conformance] > end) + return RPC_S_INVALID_BOUND; + TRACE("size %u, guid %s\n", wire_orpc_extent->size, debugstr_guid(&wire_orpc_extent->id)); + wire_orpc_extent = (WIRE_ORPC_EXTENT *)&wire_orpc_extent->data[wire_orpc_extent->conformance]; + } + msg->Buffer = wire_orpc_extent; + } + + return S_OK; +} + +/* unmarshals ORPCTHIS according to NDR rules, but doesn't allocate any memory */ +static HRESULT unmarshal_ORPCTHIS(RPC_MESSAGE *msg, ORPCTHIS *orpcthis, + ORPC_EXTENT_ARRAY *orpc_ext_array, WIRE_ORPC_EXTENT **first_wire_orpc_extent) +{ + const char *end = (char *)msg->Buffer + msg->BufferLength; + + *first_wire_orpc_extent = NULL; + + if (msg->BufferLength < FIELD_OFFSET(WIRE_ORPCTHIS, extensions) + sizeof(DWORD)) + { + ERR("invalid buffer length\n"); + return RPC_E_INVALID_HEADER; + } + + memcpy(orpcthis, msg->Buffer, FIELD_OFFSET(WIRE_ORPCTHIS, extensions)); + msg->Buffer = (char *)msg->Buffer + FIELD_OFFSET(WIRE_ORPCTHIS, extensions); + + if ((const char *)msg->Buffer + sizeof(DWORD) > end) + return RPC_E_INVALID_HEADER; + + if (*(DWORD *)msg->Buffer) + orpcthis->extensions = orpc_ext_array; + else + orpcthis->extensions = NULL; + + msg->Buffer = (char *)msg->Buffer + sizeof(DWORD); + + if (orpcthis->extensions) + { + HRESULT hr = unmarshal_ORPC_EXTENT_ARRAY(msg, end, orpc_ext_array, + first_wire_orpc_extent); + if (FAILED(hr)) + return hr; + } + + if ((orpcthis->version.MajorVersion != COM_MAJOR_VERSION) || + (orpcthis->version.MinorVersion > COM_MINOR_VERSION)) + { + ERR("COM version {%d, %d} not supported\n", + orpcthis->version.MajorVersion, orpcthis->version.MinorVersion); + return RPC_E_VERSION_MISMATCH; + } + + if (orpcthis->flags & ~(ORPCF_LOCAL|ORPCF_RESERVED1|ORPCF_RESERVED2|ORPCF_RESERVED3|ORPCF_RESERVED4)) + { + ERR("invalid flags 0x%x\n", orpcthis->flags & ~(ORPCF_LOCAL|ORPCF_RESERVED1|ORPCF_RESERVED2|ORPCF_RESERVED3|ORPCF_RESERVED4)); + return RPC_E_INVALID_HEADER; + } + + return S_OK; +} + +static HRESULT unmarshal_ORPCTHAT(RPC_MESSAGE *msg, ORPCTHAT *orpcthat, + ORPC_EXTENT_ARRAY *orpc_ext_array, WIRE_ORPC_EXTENT **first_wire_orpc_extent) +{ + const char *end = (char *)msg->Buffer + msg->BufferLength; + + *first_wire_orpc_extent = NULL; + + if (msg->BufferLength < FIELD_OFFSET(WIRE_ORPCTHAT, extensions) + sizeof(DWORD)) + { + ERR("invalid buffer length\n"); + return RPC_E_INVALID_HEADER; + } + + memcpy(orpcthat, msg->Buffer, FIELD_OFFSET(WIRE_ORPCTHAT, extensions)); + msg->Buffer = (char *)msg->Buffer + FIELD_OFFSET(WIRE_ORPCTHAT, extensions); + + if ((const char *)msg->Buffer + sizeof(DWORD) > end) + return RPC_E_INVALID_HEADER; + + if (*(DWORD *)msg->Buffer) + orpcthat->extensions = orpc_ext_array; + else + orpcthat->extensions = NULL; + + msg->Buffer = (char *)msg->Buffer + sizeof(DWORD); + + if (orpcthat->extensions) + { + HRESULT hr = unmarshal_ORPC_EXTENT_ARRAY(msg, end, orpc_ext_array, + first_wire_orpc_extent); + if (FAILED(hr)) + return hr; + } + + if (orpcthat->flags & ~(ORPCF_LOCAL|ORPCF_RESERVED1|ORPCF_RESERVED2|ORPCF_RESERVED3|ORPCF_RESERVED4)) + { + ERR("invalid flags 0x%x\n", orpcthat->flags & ~(ORPCF_LOCAL|ORPCF_RESERVED1|ORPCF_RESERVED2|ORPCF_RESERVED3|ORPCF_RESERVED4)); + return RPC_E_INVALID_HEADER; + } + + return S_OK; +} + +void rpc_execute_call(struct dispatch_params *params) +{ + struct message_state *message_state = NULL; + RPC_MESSAGE *msg = (RPC_MESSAGE *)params->msg; + char *original_buffer = msg->Buffer; + ORPCTHIS orpcthis; + ORPC_EXTENT_ARRAY orpc_ext_array; + WIRE_ORPC_EXTENT *first_wire_orpc_extent; + GUID old_causality_id; + struct tlsdata *tlsdata; + struct apartment *apt; + + if (FAILED(com_get_tlsdata(&tlsdata))) + return; + + apt = com_get_current_apt(); + + /* handle ORPCTHIS and server extensions */ + + params->hr = unmarshal_ORPCTHIS(msg, &orpcthis, &orpc_ext_array, &first_wire_orpc_extent); + if (params->hr != S_OK) + { + msg->Buffer = original_buffer; + goto exit; + } + + message_state = HeapAlloc(GetProcessHeap(), 0, sizeof(*message_state)); + if (!message_state) + { + params->hr = E_OUTOFMEMORY; + msg->Buffer = original_buffer; + goto exit; + } + + message_state->prefix_data_len = (char *)msg->Buffer - original_buffer; + message_state->binding_handle = msg->Handle; + message_state->bypass_rpcrt = params->bypass_rpcrt; + + message_state->channel_hook_info.iid = params->iid; + message_state->channel_hook_info.cbSize = sizeof(message_state->channel_hook_info); + message_state->channel_hook_info.uCausality = orpcthis.cid; + message_state->channel_hook_info.dwServerPid = GetCurrentProcessId(); + message_state->channel_hook_info.iMethod = msg->ProcNum; + message_state->channel_hook_info.pObject = params->iface; + + if (orpcthis.extensions && first_wire_orpc_extent && + orpcthis.extensions->size) + ChannelHooks_ServerNotify(&message_state->channel_hook_info, msg->DataRepresentation, first_wire_orpc_extent, orpcthis.extensions->size); + + msg->Handle = message_state; + msg->BufferLength -= message_state->prefix_data_len; + + /* call message filter */ + + if (apt->filter) + { + DWORD handlecall; + INTERFACEINFO interface_info; + CALLTYPE calltype; + + interface_info.pUnk = params->iface; + interface_info.iid = params->iid; + interface_info.wMethod = msg->ProcNum; + + if (IsEqualGUID(&orpcthis.cid, &tlsdata->causality_id)) + calltype = CALLTYPE_NESTED; + else if (tlsdata->pending_call_count_server == 0) + calltype = CALLTYPE_TOPLEVEL; + else + calltype = CALLTYPE_TOPLEVEL_CALLPENDING; + + handlecall = IMessageFilter_HandleInComingCall(apt->filter, + calltype, + UlongToHandle(GetCurrentProcessId()), + 0 /* FIXME */, + &interface_info); + TRACE("IMessageFilter_HandleInComingCall returned %d\n", handlecall); + switch (handlecall) + { + case SERVERCALL_REJECTED: + params->hr = RPC_E_CALL_REJECTED; + goto exit_reset_state; + case SERVERCALL_RETRYLATER: +#if 0 /* FIXME: handle retries on the client side before enabling this code */ + params->hr = RPC_E_RETRY; + goto exit_reset_state; +#else + FIXME("retry call later not implemented\n"); + break; +#endif + case SERVERCALL_ISHANDLED: + default: + break; + } + } + + /* invoke the method */ + + /* save the old causality ID - note: any calls executed while processing + * messages received during the SendReceive will appear to originate from + * this call - this should be checked with what Windows does */ + old_causality_id = tlsdata->causality_id; + tlsdata->causality_id = orpcthis.cid; + tlsdata->pending_call_count_server++; + params->hr = IRpcStubBuffer_Invoke(params->stub, params->msg, params->chan); + tlsdata->pending_call_count_server--; + tlsdata->causality_id = old_causality_id; + + /* the invoke allocated a new buffer, so free the old one */ + if (message_state->bypass_rpcrt && original_buffer != msg->Buffer) + HeapFree(GetProcessHeap(), 0, original_buffer); + +exit_reset_state: + message_state = msg->Handle; + msg->Handle = message_state->binding_handle; + msg->Buffer = (char *)msg->Buffer - message_state->prefix_data_len; + msg->BufferLength += message_state->prefix_data_len; + +exit: + HeapFree(GetProcessHeap(), 0, message_state); + if (params->handle) SetEvent(params->handle); +} + +static void __RPC_STUB dispatch_rpc(RPC_MESSAGE *msg) +{ + struct dispatch_params *params; + struct stub_manager *stub_manager; + struct apartment *apt; + IPID ipid; + HRESULT hr; + + RpcBindingInqObject(msg->Handle, &ipid); + + TRACE("ipid = %s, iMethod = %d\n", debugstr_guid(&ipid), msg->ProcNum); + + params = HeapAlloc(GetProcessHeap(), 0, sizeof(*params)); + if (!params) + { + RpcRaiseException(E_OUTOFMEMORY); + return; + } + + hr = ipid_get_dispatch_params(&ipid, &apt, &stub_manager, ¶ms->stub, ¶ms->chan, + ¶ms->iid, ¶ms->iface); + if (hr != S_OK) + { + ERR("no apartment found for ipid %s\n", debugstr_guid(&ipid)); + HeapFree(GetProcessHeap(), 0, params); + RpcRaiseException(hr); + return; + } + + params->msg = (RPCOLEMESSAGE *)msg; + params->status = RPC_S_OK; + params->hr = S_OK; + params->handle = NULL; + params->bypass_rpcrt = FALSE; + + /* Note: this is the important difference between STAs and MTAs - we + * always execute RPCs to STAs in the thread that originally created the + * apartment (i.e. the one that pumps messages to the window) */ + if (!apt->multi_threaded) + { + params->handle = CreateEventW(NULL, FALSE, FALSE, NULL); + + TRACE("Calling apartment thread 0x%08x...\n", apt->tid); + + if (PostMessageW(apartment_getwindow(apt), DM_EXECUTERPC, 0, (LPARAM)params)) + WaitForSingleObject(params->handle, INFINITE); + else + { + ERR("PostMessage failed with error %u\n", GetLastError()); + IRpcChannelBuffer_Release(params->chan); + IRpcStubBuffer_Release(params->stub); + } + CloseHandle(params->handle); + } + else + { + BOOL joined = FALSE; + struct tlsdata *tlsdata; + + com_get_tlsdata(&tlsdata); + + if (!tlsdata->apt) + { + enter_apartment(tlsdata, COINIT_MULTITHREADED); + joined = TRUE; + } + rpc_execute_call(params); + if (joined) + { + leave_apartment(tlsdata); + } + } + + hr = params->hr; + if (params->chan) + IRpcChannelBuffer_Release(params->chan); + if (params->stub) + IRpcStubBuffer_Release(params->stub); + HeapFree(GetProcessHeap(), 0, params); + + stub_manager_int_release(stub_manager); + apartment_release(apt); + + /* if IRpcStubBuffer_Invoke fails, we should raise an exception to tell + * the RPC runtime that the call failed */ + if (hr != S_OK) RpcRaiseException(hr); +} + +/* stub registration */ +HRESULT rpc_register_interface(REFIID riid) +{ + struct registered_if *rif; + BOOL found = FALSE; + HRESULT hr = S_OK; + + TRACE("(%s)\n", debugstr_guid(riid)); + + EnterCriticalSection(&csRegIf); + LIST_FOR_EACH_ENTRY(rif, ®istered_interfaces, struct registered_if, entry) + { + if (IsEqualGUID(&rif->If.InterfaceId.SyntaxGUID, riid)) + { + rif->refs++; + found = TRUE; + break; + } + } + if (!found) + { + TRACE("Creating new interface\n"); + + rif = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*rif)); + if (rif) + { + RPC_STATUS status; + + rif->refs = 1; + rif->If.Length = sizeof(RPC_SERVER_INTERFACE); + /* RPC interface ID = COM interface ID */ + rif->If.InterfaceId.SyntaxGUID = *riid; + rif->If.DispatchTable = &rpc_dispatch; + /* all other fields are 0, including the version asCOM objects + * always have a version of 0.0 */ + status = RpcServerRegisterIfEx( + (RPC_IF_HANDLE)&rif->If, + NULL, NULL, + RPC_IF_OLE | RPC_IF_AUTOLISTEN, + RPC_C_LISTEN_MAX_CALLS_DEFAULT, + NULL); + if (status == RPC_S_OK) + list_add_tail(®istered_interfaces, &rif->entry); + else + { + ERR("RpcServerRegisterIfEx failed with error %d\n", status); + HeapFree(GetProcessHeap(), 0, rif); + hr = HRESULT_FROM_WIN32(status); + } + } + else + hr = E_OUTOFMEMORY; + } + LeaveCriticalSection(&csRegIf); + return hr; +} + +/* stub unregistration */ +void rpc_unregister_interface(REFIID riid, BOOL wait) +{ + struct registered_if *rif; + EnterCriticalSection(&csRegIf); + LIST_FOR_EACH_ENTRY(rif, ®istered_interfaces, struct registered_if, entry) + { + if (IsEqualGUID(&rif->If.InterfaceId.SyntaxGUID, riid)) + { + if (!--rif->refs) + { + RpcServerUnregisterIf((RPC_IF_HANDLE)&rif->If, NULL, wait); + list_remove(&rif->entry); + HeapFree(GetProcessHeap(), 0, rif); + } + break; + } + } + LeaveCriticalSection(&csRegIf); +} + +/* get the info for an OXID, including the IPID for the rem unknown interface + * and the string binding */ +HRESULT rpc_resolve_oxid(OXID oxid, OXID_INFO *oxid_info) +{ + TRACE("%s\n", wine_dbgstr_longlong(oxid)); + + oxid_info->dwTid = 0; + oxid_info->dwPid = 0; + oxid_info->dwAuthnHint = RPC_C_AUTHN_LEVEL_NONE; + /* FIXME: this is a hack around not having an OXID resolver yet - + * this function should contact the machine's OXID resolver and then it + * should give us the IPID of the IRemUnknown interface */ + oxid_info->ipidRemUnknown.Data1 = 0xffffffff; + oxid_info->ipidRemUnknown.Data2 = 0xffff; + oxid_info->ipidRemUnknown.Data3 = 0xffff; + memcpy(oxid_info->ipidRemUnknown.Data4, &oxid, sizeof(OXID)); + oxid_info->psa = NULL /* FIXME */; + + return S_OK; +} + +/* make the apartment reachable by other threads and processes and create the + * IRemUnknown object */ +void rpc_start_remoting(struct apartment *apt) +{ + if (!InterlockedExchange(&apt->remoting_started, TRUE)) + { + WCHAR endpoint[200]; + RPC_STATUS status; + + get_rpc_endpoint(endpoint, &apt->oxid); + + status = RpcServerUseProtseqEpW( + rpctransportW, + RPC_C_PROTSEQ_MAX_REQS_DEFAULT, + endpoint, + NULL); + if (status != RPC_S_OK) + ERR("Couldn't register endpoint %s\n", debugstr_w(endpoint)); + + /* FIXME: move remote unknown exporting into this function */ + } + start_apartment_remote_unknown(apt); +} diff --git a/dlls/combase/stubmanager.c b/dlls/combase/stubmanager.c index a800c0b4844..16b8bb35d00 100644 --- a/dlls/combase/stubmanager.c +++ b/dlls/combase/stubmanager.c @@ -44,11 +44,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(ole); -extern HRESULT WINAPI marshal_object(struct apartment *apt, STDOBJREF *stdobjref, REFIID riid, IUnknown *object, - DWORD dest_context, void *dest_context_data, MSHLFLAGS mshlflags); -extern HRESULT WINAPI RPC_CreateServerChannel(DWORD dest_context, void *dest_context_data, IRpcChannelBuffer **chan); -extern void WINAPI RPC_UnregisterInterface(REFIID riid, BOOL wait); - /* generates an ipid in the following format (similar to native version): * Data1 = apartment-local ipid counter * Data2 = apartment creator thread ID @@ -92,7 +87,7 @@ struct ifstub * stub_manager_new_ifstub(struct stub_manager *m, IRpcStubBuffer * return NULL; } - hr = RPC_CreateServerChannel(dest_context, dest_context_data, &stub->chan); + hr = rpc_create_serverchannel(dest_context, dest_context_data, &stub->chan); if (hr != S_OK) { IUnknown_Release(stub->iface); @@ -131,7 +126,7 @@ static void stub_manager_delete_ifstub(struct stub_manager *m, struct ifstub *if list_remove(&ifstub->entry); if (!m->disconnected) - RPC_UnregisterInterface(&ifstub->iid, TRUE); + rpc_unregister_interface(&ifstub->iid, TRUE); if (ifstub->stubbuffer) IRpcStubBuffer_Release(ifstub->stubbuffer); IUnknown_Release(ifstub->iface); @@ -255,7 +250,7 @@ void stub_manager_disconnect(struct stub_manager *m) if (!m->disconnected) { LIST_FOR_EACH_ENTRY(ifstub, &m->ifstubs, struct ifstub, entry) - RPC_UnregisterInterface(&ifstub->iid, FALSE); + rpc_unregister_interface(&ifstub->iid, FALSE); m->disconnected = TRUE; } @@ -314,7 +309,7 @@ static ULONG stub_manager_int_addref(struct stub_manager *m) } /* decrements the internal refcount */ -ULONG WINAPI stub_manager_int_release(struct stub_manager *m) +ULONG stub_manager_int_release(struct stub_manager *m) { ULONG refs; struct apartment *apt = m->apt; @@ -542,10 +537,9 @@ static HRESULT ipid_to_stub_manager(const IPID *ipid, struct apartment **stub_ap /* gets the apartment, stub and channel of an object. the caller must * release the references to all objects (except iface) if the function * returned success, otherwise no references are returned. */ -HRESULT WINAPI ipid_get_dispatch_params(const IPID *ipid, struct apartment **stub_apt, - struct stub_manager **manager, - IRpcStubBuffer **stub, IRpcChannelBuffer **chan, - IID *iid, IUnknown **iface) +HRESULT ipid_get_dispatch_params(const IPID *ipid, struct apartment **stub_apt, + struct stub_manager **manager, IRpcStubBuffer **stub, IRpcChannelBuffer **chan, + IID *iid, IUnknown **iface) { struct stub_manager *stubmgr; struct ifstub *ifstub; @@ -826,7 +820,7 @@ static const IRemUnknownVtbl RemUnknown_Vtbl = }; /* starts the IRemUnknown listener for the current apartment */ -HRESULT WINAPI start_apartment_remote_unknown(struct apartment *apt) +HRESULT start_apartment_remote_unknown(struct apartment *apt) { IRemUnknown *pRemUnknown; HRESULT hr = S_OK; diff --git a/dlls/ole32/Makefile.in b/dlls/ole32/Makefile.in index ddbdfab30de..89807b94ef0 100644 --- a/dlls/ole32/Makefile.in +++ b/dlls/ole32/Makefile.in @@ -33,7 +33,6 @@ C_SRCS = \ oleobj.c \ oleproxy.c \ pointermoniker.c \ - rpc.c \ stg_prop.c \ stg_stream.c \ storage32.c \ diff --git a/dlls/ole32/compobj.c b/dlls/ole32/compobj.c index b92dfd87267..a9a9af53108 100644 --- a/dlls/ole32/compobj.c +++ b/dlls/ole32/compobj.c @@ -930,26 +930,6 @@ HRESULT WINAPI CoGetObject(LPCWSTR pszName, BIND_OPTS *pBindOptions, return hr; } -/*********************************************************************** - * CoRegisterChannelHook [OLE32.@] - * - * Registers a process-wide hook that is called during ORPC calls. - * - * PARAMS - * guidExtension [I] GUID of the channel hook to register. - * pChannelHook [I] Channel hook object to register. - * - * RETURNS - * Success: S_OK. - * Failure: HRESULT code. - */ -HRESULT WINAPI CoRegisterChannelHook(REFGUID guidExtension, IChannelHook *pChannelHook) -{ - TRACE("(%s, %p)\n", debugstr_guid(guidExtension), pChannelHook); - - return RPC_RegisterChannelHook(guidExtension, pChannelHook); -} - /* Returns expanded dll path from the registry or activation context. */ static BOOL get_object_dll_path(const struct class_reg_data *regdata, WCHAR *dst, DWORD dstlen) { @@ -1176,7 +1156,6 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID reserved) case DLL_PROCESS_DETACH: if (reserved) break; release_std_git(); - RPC_UnregisterAllChannelHooks(); break; } return TRUE; diff --git a/dlls/ole32/compobj_private.h b/dlls/ole32/compobj_private.h index 12acfc0a9e5..18b39d655f0 100644 --- a/dlls/ole32/compobj_private.h +++ b/dlls/ole32/compobj_private.h @@ -39,113 +39,9 @@ #include "winreg.h" #include "winternl.h" -struct apartment; - DEFINE_OLEGUID( CLSID_DfMarshal, 0x0000030b, 0, 0 ); -/* signal to stub manager that this is a rem unknown object */ -#define MSHLFLAGSP_REMUNKNOWN 0x80000000 - -/* Thread-safety Annotation Legend: - * - * RO - The value is read only. It never changes after creation, so no - * locking is required. - * LOCK - The value is written to only using Interlocked* functions. - * CS - The value is read or written to inside a critical section. - * The identifier following "CS" is the specific critical section that - * must be used. - * MUTEX - The value is read or written to with a mutex held. - * The identifier following "MUTEX" is the specific mutex that - * must be used. - */ - -typedef enum ifstub_state -{ - STUBSTATE_NORMAL_MARSHALED, - STUBSTATE_NORMAL_UNMARSHALED, - STUBSTATE_TABLE_WEAK_MARSHALED, - STUBSTATE_TABLE_WEAK_UNMARSHALED, - STUBSTATE_TABLE_STRONG, -} STUB_STATE; - -/* an interface stub */ -struct ifstub -{ - struct list entry; /* entry in stub_manager->ifstubs list (CS stub_manager->lock) */ - IRpcStubBuffer *stubbuffer; /* RO */ - IID iid; /* RO */ - IPID ipid; /* RO */ - IUnknown *iface; /* RO */ - MSHLFLAGS flags; /* so we can enforce process-local marshalling rules (RO) */ - IRpcChannelBuffer*chan; /* channel passed to IRpcStubBuffer::Invoke (RO) */ -}; - - -/* stub managers hold refs on the object and each interface stub */ -struct stub_manager -{ - struct list entry; /* entry in apartment stubmgr list (CS apt->cs) */ - struct list ifstubs; /* list of active ifstubs for the object (CS lock) */ - CRITICAL_SECTION lock; - struct apartment *apt; /* owning apt (RO) */ - - ULONG extrefs; /* number of 'external' references (CS lock) */ - ULONG refs; /* internal reference count (CS apt->cs) */ - ULONG weakrefs; /* number of weak references (CS lock) */ - OID oid; /* apartment-scoped unique identifier (RO) */ - IUnknown *object; /* the object we are managing the stub for (RO) */ - ULONG next_ipid; /* currently unused (LOCK) */ - OXID_INFO oxid_info; /* string binding, ipid of rem unknown and other information (RO) */ - - IExternalConnection *extern_conn; - - /* We need to keep a count of the outstanding marshals, so we can enforce the - * marshalling rules (ie, you can only unmarshal normal marshals once). Note - * that these counts do NOT include unmarshalled interfaces, once a stream is - * unmarshalled and a proxy set up, this count is decremented. - */ - - ULONG norm_refs; /* refcount of normal marshals (CS lock) */ - BOOL disconnected; /* CoDisconnectObject has been called (CS lock) */ -}; - -struct apartment -{ - struct list entry; - - LONG refs; /* refcount of the apartment (LOCK) */ - BOOL multi_threaded; /* multi-threaded or single-threaded apartment? (RO) */ - DWORD tid; /* thread id (RO) */ - OXID oxid; /* object exporter ID (RO) */ - LONG ipidc; /* interface pointer ID counter, starts at 1 (LOCK) */ - CRITICAL_SECTION cs; /* thread safety */ - struct list proxies; /* imported objects (CS cs) */ - struct list stubmgrs; /* stub managers for exported objects (CS cs) */ - BOOL remunk_exported; /* has the IRemUnknown interface for this apartment been created yet? (CS cs) */ - LONG remoting_started; /* has the RPC system been started for this apartment? (LOCK) */ - 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) */ - struct local_server *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) */ - - /* STA-only fields */ - HWND win; /* message window (LOCK) */ - LPMESSAGEFILTER filter; /* message filter (CS cs) */ - BOOL main; /* is this a main-threaded-apartment? (RO) */ - /* MTA-only */ - struct list usage_cookies; /* Used for refcount control with CoIncrementMTAUsage()/CoDecrementMTAUsage(). */ -}; - -struct init_spy -{ - struct list entry; - IInitializeSpy *spy; - unsigned int id; -}; +struct apartment; /* this is what is stored in TEB->ReservedForOle */ struct oletls @@ -170,7 +66,6 @@ struct oletls DWORD spies_lock; }; - /* Global Interface Table Functions */ extern void release_std_git(void) DECLSPEC_HIDDEN; extern HRESULT StdGlobalInterfaceTable_GetFactory(LPVOID *ppv) DECLSPEC_HIDDEN; @@ -179,48 +74,9 @@ HRESULT COM_OpenKeyForCLSID(REFCLSID clsid, LPCWSTR keyname, REGSAM access, HKEY HRESULT MARSHAL_GetStandardMarshalCF(LPVOID *ppv) DECLSPEC_HIDDEN; HRESULT FTMarshalCF_Create(REFIID riid, LPVOID *ppv) DECLSPEC_HIDDEN; -/* Stub Manager */ - -extern ULONG WINAPI stub_manager_int_release(struct stub_manager *This) DECLSPEC_HIDDEN; - -/* RPC Backend */ - -struct dispatch_params; - -HRESULT WINAPI RPC_CreateClientChannel(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 WINAPI RPC_RegisterInterface(REFIID riid) DECLSPEC_HIDDEN; -HRESULT RPC_RegisterChannelHook(REFGUID rguid, IChannelHook *hook) DECLSPEC_HIDDEN; -void RPC_UnregisterAllChannelHooks(void) DECLSPEC_HIDDEN; -HRESULT WINAPI RPC_ResolveOxid(OXID oxid, OXID_INFO *oxid_info) DECLSPEC_HIDDEN; - /* Drag and drop */ void OLEDD_UnInitialize(void) DECLSPEC_HIDDEN; -/* Apartment Functions */ - -extern void WINAPI apartment_release(struct apartment *apt) DECLSPEC_HIDDEN; -static inline HRESULT apartment_getoxid(const struct apartment *apt, OXID *oxid) -{ - *oxid = apt->oxid; - return S_OK; -} -extern HWND WINAPI apartment_getwindow(const struct apartment *apt) DECLSPEC_HIDDEN; -extern HRESULT WINAPI enter_apartment(struct oletls *info, DWORD model) DECLSPEC_HIDDEN; -void WINAPI leave_apartment(struct oletls *info) DECLSPEC_HIDDEN; -extern struct apartment * WINAPI apartment_get_current_or_mta(void) DECLSPEC_HIDDEN; -extern HRESULT WINAPI apartment_get_local_server_stream(struct apartment *apt, IStream **ret) DECLSPEC_HIDDEN; - -/* DCOM messages used by the apartment window (not compatible with native) */ -#define DM_EXECUTERPC (WM_USER + 0) /* WPARAM = 0, LPARAM = (struct dispatch_params *) */ -#define DM_HOSTOBJECT (WM_USER + 1) /* WPARAM = 0, LPARAM = (struct host_object_params *) */ - -/* - * Per-thread values are stored in the TEB on offset 0xF80 - */ - extern HRESULT WINAPI InternalTlsAllocData(struct oletls **tlsdata); /* will create if necessary */ diff --git a/dlls/ole32/ole32.spec b/dlls/ole32/ole32.spec index 1ea57416176..cf48d043b75 100644 --- a/dlls/ole32/ole32.spec +++ b/dlls/ole32/ole32.spec @@ -64,7 +64,7 @@ @ stdcall CoQueryClientBlanket(ptr ptr ptr ptr ptr ptr ptr) combase.CoQueryClientBlanket @ stdcall CoQueryProxyBlanket(ptr ptr ptr ptr ptr ptr ptr ptr) combase.CoQueryProxyBlanket @ stub CoQueryReleaseObject -@ stdcall CoRegisterChannelHook(ptr ptr) +@ stdcall CoRegisterChannelHook(ptr ptr) combase.CoRegisterChannelHook @ stdcall CoRegisterClassObject(ptr ptr long long ptr) combase.CoRegisterClassObject @ stdcall CoRegisterInitializeSpy(ptr ptr) combase.CoRegisterInitializeSpy @ stdcall CoRegisterMallocSpy(ptr) combase.CoRegisterMallocSpy @@ -298,11 +298,3 @@ @ stdcall WriteFmtUserTypeStg(ptr long ptr) @ stub WriteOleStg @ stub WriteStringStream - -@ stdcall Internal_RPC_ExecuteCall(ptr) -@ stdcall RPC_CreateServerChannel(long ptr ptr) -@ stdcall RPC_UnregisterInterface(ptr long) -@ stdcall RPC_RegisterInterface(ptr) -@ stdcall RPC_ResolveOxid(int64 ptr) -@ stdcall RPC_StartRemoting(ptr) -@ stdcall RPC_CreateClientChannel(ptr ptr ptr ptr long ptr ptr ptr) diff --git a/dlls/ole32/rpc.c b/dlls/ole32/rpc.c deleted file mode 100644 index a6783c45eae..00000000000 --- a/dlls/ole32/rpc.c +++ /dev/null @@ -1,1654 +0,0 @@ -/* - * RPC Manager - * - * Copyright 2001 Ove Kåven, TransGaming Technologies - * Copyright 2002 Marcus Meissner - * Copyright 2005 Mike Hearn, Rob Shearman for CodeWeavers - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA - */ - -#include -#include - -#define COBJMACROS -#define NONAMELESSUNION - -#include "windef.h" -#include "winbase.h" -#include "winuser.h" -#include "winsvc.h" -#include "objbase.h" -#include "ole2.h" -#include "rpc.h" -#include "winerror.h" -#include "winreg.h" -#include "servprov.h" - -#include "compobj_private.h" - -#include "wine/debug.h" - -WINE_DEFAULT_DEBUG_CHANNEL(ole); - -extern HRESULT WINAPI ipid_get_dispatch_params(const IPID *ipid, struct apartment **stub_apt, - struct stub_manager **manager, IRpcStubBuffer **stub, IRpcChannelBuffer **chan, IID *iid, IUnknown **iface); -extern HRESULT WINAPI start_apartment_remote_unknown(struct apartment *apt); - -static void __RPC_STUB dispatch_rpc(RPC_MESSAGE *msg); - -/* we only use one function to dispatch calls for all methods - we use the - * RPC_IF_OLE flag to tell the RPC runtime that this is the case */ -static RPC_DISPATCH_FUNCTION rpc_dispatch_table[1] = { dispatch_rpc }; /* (RO) */ -static RPC_DISPATCH_TABLE rpc_dispatch = { 1, rpc_dispatch_table }; /* (RO) */ - -static struct list registered_interfaces = LIST_INIT(registered_interfaces); /* (CS csRegIf) */ -static CRITICAL_SECTION csRegIf; -static CRITICAL_SECTION_DEBUG csRegIf_debug = -{ - 0, 0, &csRegIf, - { &csRegIf_debug.ProcessLocksList, &csRegIf_debug.ProcessLocksList }, - 0, 0, { (DWORD_PTR)(__FILE__ ": dcom registered server interfaces") } -}; -static CRITICAL_SECTION csRegIf = { &csRegIf_debug, -1, 0, 0, 0, 0 }; - -static struct list channel_hooks = LIST_INIT(channel_hooks); /* (CS csChannelHook) */ -static CRITICAL_SECTION csChannelHook; -static CRITICAL_SECTION_DEBUG csChannelHook_debug = -{ - 0, 0, &csChannelHook, - { &csChannelHook_debug.ProcessLocksList, &csChannelHook_debug.ProcessLocksList }, - 0, 0, { (DWORD_PTR)(__FILE__ ": channel hooks") } -}; -static CRITICAL_SECTION csChannelHook = { &csChannelHook_debug, -1, 0, 0, 0, 0 }; - -static WCHAR wszRpcTransport[] = {'n','c','a','l','r','p','c',0}; - - -struct registered_if -{ - struct list entry; - DWORD refs; /* ref count */ - RPC_SERVER_INTERFACE If; /* interface registered with the RPC runtime */ -}; - -/* get the pipe endpoint specified of the specified apartment */ -static inline void get_rpc_endpoint(LPWSTR endpoint, const OXID *oxid) -{ - /* FIXME: should get endpoint from rpcss */ - static const WCHAR wszEndpointFormat[] = {'\\','p','i','p','e','\\','O','L','E','_','%','0','8','l','x','%','0','8','l','x',0}; - wsprintfW(endpoint, wszEndpointFormat, (DWORD)(*oxid >> 32),(DWORD)*oxid); -} - -typedef struct -{ - IRpcChannelBuffer IRpcChannelBuffer_iface; - LONG refs; - - DWORD dest_context; /* returned from GetDestCtx */ - void *dest_context_data; /* returned from GetDestCtx */ -} RpcChannelBuffer; - -typedef struct -{ - RpcChannelBuffer super; /* superclass */ - - RPC_BINDING_HANDLE bind; /* handle to the remote server */ - OXID oxid; /* apartment in which the channel is valid */ - DWORD server_pid; /* id of server process */ - HANDLE event; /* cached event handle */ - IID iid; /* IID of the proxy this belongs to */ -} ClientRpcChannelBuffer; - -struct dispatch_params -{ - RPCOLEMESSAGE *msg; /* message */ - IRpcStubBuffer *stub; /* stub buffer, if applicable */ - IRpcChannelBuffer *chan; /* server channel buffer, if applicable */ - IID iid; /* ID of interface being called */ - IUnknown *iface; /* interface being called */ - HANDLE handle; /* handle that will become signaled when call finishes */ - BOOL bypass_rpcrt; /* bypass RPC runtime? */ - RPC_STATUS status; /* status (out) */ - HRESULT hr; /* hresult (out) */ -}; - -struct message_state -{ - RPC_BINDING_HANDLE binding_handle; - ULONG prefix_data_len; - SChannelHookCallInfo channel_hook_info; - BOOL bypass_rpcrt; - - /* client only */ - HWND target_hwnd; - DWORD target_tid; - struct dispatch_params params; -}; - -typedef struct -{ - ULONG conformance; /* NDR */ - GUID id; - ULONG size; - /* [size_is((size+7)&~7)] */ unsigned char data[1]; -} WIRE_ORPC_EXTENT; - -typedef struct -{ - ULONG size; - ULONG reserved; - unsigned char extent[1]; -} WIRE_ORPC_EXTENT_ARRAY; - -typedef struct -{ - ULONG version; - ULONG flags; - ULONG reserved1; - GUID cid; - unsigned char extensions[1]; -} WIRE_ORPCTHIS; - -typedef struct -{ - ULONG flags; - unsigned char extensions[1]; -} WIRE_ORPCTHAT; - -struct channel_hook_entry -{ - struct list entry; - GUID id; - IChannelHook *hook; -}; - -struct channel_hook_buffer_data -{ - GUID id; - ULONG extension_size; -}; - - -static HRESULT unmarshal_ORPCTHAT(RPC_MESSAGE *msg, ORPCTHAT *orpcthat, - ORPC_EXTENT_ARRAY *orpc_ext_array, WIRE_ORPC_EXTENT **first_wire_orpc_extent); - -/* Channel Hook Functions */ - -static ULONG ChannelHooks_ClientGetSize(SChannelHookCallInfo *info, - struct channel_hook_buffer_data **data, unsigned int *hook_count, - ULONG *extension_count) -{ - struct channel_hook_entry *entry; - ULONG total_size = 0; - unsigned int hook_index = 0; - - *hook_count = 0; - *extension_count = 0; - - EnterCriticalSection(&csChannelHook); - - LIST_FOR_EACH_ENTRY(entry, &channel_hooks, struct channel_hook_entry, entry) - (*hook_count)++; - - if (*hook_count) - *data = HeapAlloc(GetProcessHeap(), 0, *hook_count * sizeof(struct channel_hook_buffer_data)); - else - *data = NULL; - - LIST_FOR_EACH_ENTRY(entry, &channel_hooks, struct channel_hook_entry, entry) - { - ULONG extension_size = 0; - - IChannelHook_ClientGetSize(entry->hook, &entry->id, &info->iid, &extension_size); - - TRACE("%s: extension_size = %u\n", debugstr_guid(&entry->id), extension_size); - - extension_size = (extension_size+7)&~7; - (*data)[hook_index].id = entry->id; - (*data)[hook_index].extension_size = extension_size; - - /* an extension is only put onto the wire if it has data to write */ - if (extension_size) - { - total_size += FIELD_OFFSET(WIRE_ORPC_EXTENT, data[extension_size]); - (*extension_count)++; - } - - hook_index++; - } - - LeaveCriticalSection(&csChannelHook); - - return total_size; -} - -static unsigned char * ChannelHooks_ClientFillBuffer(SChannelHookCallInfo *info, - unsigned char *buffer, struct channel_hook_buffer_data *data, - unsigned int hook_count) -{ - struct channel_hook_entry *entry; - - EnterCriticalSection(&csChannelHook); - - LIST_FOR_EACH_ENTRY(entry, &channel_hooks, struct channel_hook_entry, entry) - { - unsigned int i; - ULONG extension_size = 0; - WIRE_ORPC_EXTENT *wire_orpc_extent = (WIRE_ORPC_EXTENT *)buffer; - - for (i = 0; i < hook_count; i++) - if (IsEqualGUID(&entry->id, &data[i].id)) - extension_size = data[i].extension_size; - - /* an extension is only put onto the wire if it has data to write */ - if (!extension_size) - continue; - - IChannelHook_ClientFillBuffer(entry->hook, &entry->id, &info->iid, - &extension_size, buffer + FIELD_OFFSET(WIRE_ORPC_EXTENT, data[0])); - - TRACE("%s: extension_size = %u\n", debugstr_guid(&entry->id), extension_size); - - /* FIXME: set unused portion of wire_orpc_extent->data to 0? */ - - wire_orpc_extent->conformance = (extension_size+7)&~7; - wire_orpc_extent->size = extension_size; - wire_orpc_extent->id = entry->id; - buffer += FIELD_OFFSET(WIRE_ORPC_EXTENT, data[wire_orpc_extent->conformance]); - } - - LeaveCriticalSection(&csChannelHook); - - return buffer; -} - -static void ChannelHooks_ServerNotify(SChannelHookCallInfo *info, - DWORD lDataRep, WIRE_ORPC_EXTENT *first_wire_orpc_extent, - ULONG extension_count) -{ - struct channel_hook_entry *entry; - ULONG i; - - EnterCriticalSection(&csChannelHook); - - LIST_FOR_EACH_ENTRY(entry, &channel_hooks, struct channel_hook_entry, entry) - { - WIRE_ORPC_EXTENT *wire_orpc_extent; - for (i = 0, wire_orpc_extent = first_wire_orpc_extent; - i < extension_count; - i++, wire_orpc_extent = (WIRE_ORPC_EXTENT *)&wire_orpc_extent->data[wire_orpc_extent->conformance]) - { - if (IsEqualGUID(&entry->id, &wire_orpc_extent->id)) - break; - } - if (i == extension_count) wire_orpc_extent = NULL; - - IChannelHook_ServerNotify(entry->hook, &entry->id, &info->iid, - wire_orpc_extent ? wire_orpc_extent->size : 0, - wire_orpc_extent ? wire_orpc_extent->data : NULL, - lDataRep); - } - - LeaveCriticalSection(&csChannelHook); -} - -static ULONG ChannelHooks_ServerGetSize(SChannelHookCallInfo *info, - struct channel_hook_buffer_data **data, unsigned int *hook_count, - ULONG *extension_count) -{ - struct channel_hook_entry *entry; - ULONG total_size = 0; - unsigned int hook_index = 0; - - *hook_count = 0; - *extension_count = 0; - - EnterCriticalSection(&csChannelHook); - - LIST_FOR_EACH_ENTRY(entry, &channel_hooks, struct channel_hook_entry, entry) - (*hook_count)++; - - if (*hook_count) - *data = HeapAlloc(GetProcessHeap(), 0, *hook_count * sizeof(struct channel_hook_buffer_data)); - else - *data = NULL; - - LIST_FOR_EACH_ENTRY(entry, &channel_hooks, struct channel_hook_entry, entry) - { - ULONG extension_size = 0; - - IChannelHook_ServerGetSize(entry->hook, &entry->id, &info->iid, S_OK, - &extension_size); - - TRACE("%s: extension_size = %u\n", debugstr_guid(&entry->id), extension_size); - - extension_size = (extension_size+7)&~7; - (*data)[hook_index].id = entry->id; - (*data)[hook_index].extension_size = extension_size; - - /* an extension is only put onto the wire if it has data to write */ - if (extension_size) - { - total_size += FIELD_OFFSET(WIRE_ORPC_EXTENT, data[extension_size]); - (*extension_count)++; - } - - hook_index++; - } - - LeaveCriticalSection(&csChannelHook); - - return total_size; -} - -static unsigned char * ChannelHooks_ServerFillBuffer(SChannelHookCallInfo *info, - unsigned char *buffer, struct channel_hook_buffer_data *data, - unsigned int hook_count) -{ - struct channel_hook_entry *entry; - - EnterCriticalSection(&csChannelHook); - - LIST_FOR_EACH_ENTRY(entry, &channel_hooks, struct channel_hook_entry, entry) - { - unsigned int i; - ULONG extension_size = 0; - WIRE_ORPC_EXTENT *wire_orpc_extent = (WIRE_ORPC_EXTENT *)buffer; - - for (i = 0; i < hook_count; i++) - if (IsEqualGUID(&entry->id, &data[i].id)) - extension_size = data[i].extension_size; - - /* an extension is only put onto the wire if it has data to write */ - if (!extension_size) - continue; - - IChannelHook_ServerFillBuffer(entry->hook, &entry->id, &info->iid, - &extension_size, buffer + FIELD_OFFSET(WIRE_ORPC_EXTENT, data[0]), - S_OK); - - TRACE("%s: extension_size = %u\n", debugstr_guid(&entry->id), extension_size); - - /* FIXME: set unused portion of wire_orpc_extent->data to 0? */ - - wire_orpc_extent->conformance = (extension_size+7)&~7; - wire_orpc_extent->size = extension_size; - wire_orpc_extent->id = entry->id; - buffer += FIELD_OFFSET(WIRE_ORPC_EXTENT, data[wire_orpc_extent->conformance]); - } - - LeaveCriticalSection(&csChannelHook); - - return buffer; -} - -static void ChannelHooks_ClientNotify(SChannelHookCallInfo *info, - DWORD lDataRep, WIRE_ORPC_EXTENT *first_wire_orpc_extent, - ULONG extension_count, HRESULT hrFault) -{ - struct channel_hook_entry *entry; - ULONG i; - - EnterCriticalSection(&csChannelHook); - - LIST_FOR_EACH_ENTRY(entry, &channel_hooks, struct channel_hook_entry, entry) - { - WIRE_ORPC_EXTENT *wire_orpc_extent; - for (i = 0, wire_orpc_extent = first_wire_orpc_extent; - i < extension_count; - i++, wire_orpc_extent = (WIRE_ORPC_EXTENT *)&wire_orpc_extent->data[wire_orpc_extent->conformance]) - { - if (IsEqualGUID(&entry->id, &wire_orpc_extent->id)) - break; - } - if (i == extension_count) wire_orpc_extent = NULL; - - IChannelHook_ClientNotify(entry->hook, &entry->id, &info->iid, - wire_orpc_extent ? wire_orpc_extent->size : 0, - wire_orpc_extent ? wire_orpc_extent->data : NULL, - lDataRep, hrFault); - } - - LeaveCriticalSection(&csChannelHook); -} - -HRESULT RPC_RegisterChannelHook(REFGUID rguid, IChannelHook *hook) -{ - struct channel_hook_entry *entry; - - TRACE("(%s, %p)\n", debugstr_guid(rguid), hook); - - entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry)); - if (!entry) - return E_OUTOFMEMORY; - - entry->id = *rguid; - entry->hook = hook; - IChannelHook_AddRef(hook); - - EnterCriticalSection(&csChannelHook); - list_add_tail(&channel_hooks, &entry->entry); - LeaveCriticalSection(&csChannelHook); - - return S_OK; -} - -void RPC_UnregisterAllChannelHooks(void) -{ - struct channel_hook_entry *cursor; - struct channel_hook_entry *cursor2; - - EnterCriticalSection(&csChannelHook); - LIST_FOR_EACH_ENTRY_SAFE(cursor, cursor2, &channel_hooks, struct channel_hook_entry, entry) - HeapFree(GetProcessHeap(), 0, cursor); - LeaveCriticalSection(&csChannelHook); - DeleteCriticalSection(&csChannelHook); - DeleteCriticalSection(&csRegIf); -} - -/* RPC Channel Buffer Functions */ - -static HRESULT WINAPI RpcChannelBuffer_QueryInterface(IRpcChannelBuffer *iface, REFIID riid, LPVOID *ppv) -{ - *ppv = NULL; - if (IsEqualIID(riid,&IID_IRpcChannelBuffer) || IsEqualIID(riid,&IID_IUnknown)) - { - *ppv = iface; - IRpcChannelBuffer_AddRef(iface); - return S_OK; - } - return E_NOINTERFACE; -} - -static ULONG WINAPI RpcChannelBuffer_AddRef(LPRPCCHANNELBUFFER iface) -{ - RpcChannelBuffer *This = (RpcChannelBuffer *)iface; - return InterlockedIncrement(&This->refs); -} - -static ULONG WINAPI ServerRpcChannelBuffer_Release(LPRPCCHANNELBUFFER iface) -{ - RpcChannelBuffer *This = (RpcChannelBuffer *)iface; - ULONG ref; - - ref = InterlockedDecrement(&This->refs); - if (ref) - return ref; - - HeapFree(GetProcessHeap(), 0, This); - return 0; -} - -static ULONG WINAPI ClientRpcChannelBuffer_Release(LPRPCCHANNELBUFFER iface) -{ - ClientRpcChannelBuffer *This = (ClientRpcChannelBuffer *)iface; - ULONG ref; - - ref = InterlockedDecrement(&This->super.refs); - if (ref) - return ref; - - if (This->event) CloseHandle(This->event); - RpcBindingFree(&This->bind); - HeapFree(GetProcessHeap(), 0, This); - return 0; -} - -static HRESULT WINAPI ServerRpcChannelBuffer_GetBuffer(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE* olemsg, REFIID riid) -{ - RpcChannelBuffer *This = (RpcChannelBuffer *)iface; - RPC_MESSAGE *msg = (RPC_MESSAGE *)olemsg; - RPC_STATUS status; - ORPCTHAT *orpcthat; - struct message_state *message_state; - ULONG extensions_size; - struct channel_hook_buffer_data *channel_hook_data; - unsigned int channel_hook_count; - ULONG extension_count; - - TRACE("(%p)->(%p,%s)\n", This, olemsg, debugstr_guid(riid)); - - message_state = msg->Handle; - /* restore the binding handle and the real start of data */ - msg->Handle = message_state->binding_handle; - msg->Buffer = (char *)msg->Buffer - message_state->prefix_data_len; - - extensions_size = ChannelHooks_ServerGetSize(&message_state->channel_hook_info, - &channel_hook_data, &channel_hook_count, &extension_count); - - msg->BufferLength += FIELD_OFFSET(WIRE_ORPCTHAT, extensions) + sizeof(DWORD); - if (extensions_size) - { - msg->BufferLength += FIELD_OFFSET(WIRE_ORPC_EXTENT_ARRAY, extent[2*sizeof(DWORD) + extensions_size]); - if (extension_count & 1) - msg->BufferLength += FIELD_OFFSET(WIRE_ORPC_EXTENT, data[0]); - } - - if (message_state->bypass_rpcrt) - { - msg->Buffer = HeapAlloc(GetProcessHeap(), 0, msg->BufferLength); - if (msg->Buffer) - status = RPC_S_OK; - else - { - HeapFree(GetProcessHeap(), 0, channel_hook_data); - return E_OUTOFMEMORY; - } - } - else - status = I_RpcGetBuffer(msg); - - orpcthat = msg->Buffer; - msg->Buffer = (char *)msg->Buffer + FIELD_OFFSET(WIRE_ORPCTHAT, extensions); - - orpcthat->flags = ORPCF_NULL /* FIXME? */; - - /* NDR representation of orpcthat->extensions */ - *(DWORD *)msg->Buffer = extensions_size ? 1 : 0; - msg->Buffer = (char *)msg->Buffer + sizeof(DWORD); - - if (extensions_size) - { - WIRE_ORPC_EXTENT_ARRAY *orpc_extent_array = msg->Buffer; - orpc_extent_array->size = extension_count; - orpc_extent_array->reserved = 0; - msg->Buffer = (char *)msg->Buffer + FIELD_OFFSET(WIRE_ORPC_EXTENT_ARRAY, extent); - /* NDR representation of orpc_extent_array->extent */ - *(DWORD *)msg->Buffer = 1; - msg->Buffer = (char *)msg->Buffer + sizeof(DWORD); - /* NDR representation of [size_is] attribute of orpc_extent_array->extent */ - *(DWORD *)msg->Buffer = (extension_count + 1) & ~1; - msg->Buffer = (char *)msg->Buffer + sizeof(DWORD); - - msg->Buffer = ChannelHooks_ServerFillBuffer(&message_state->channel_hook_info, - msg->Buffer, channel_hook_data, channel_hook_count); - - /* we must add a dummy extension if there is an odd extension - * count to meet the contract specified by the size_is attribute */ - if (extension_count & 1) - { - WIRE_ORPC_EXTENT *wire_orpc_extent = msg->Buffer; - wire_orpc_extent->conformance = 0; - wire_orpc_extent->id = GUID_NULL; - wire_orpc_extent->size = 0; - msg->Buffer = (char *)msg->Buffer + FIELD_OFFSET(WIRE_ORPC_EXTENT, data[0]); - } - } - - HeapFree(GetProcessHeap(), 0, channel_hook_data); - - /* store the prefixed data length so that we can restore the real buffer - * later */ - message_state->prefix_data_len = (char *)msg->Buffer - (char *)orpcthat; - msg->BufferLength -= message_state->prefix_data_len; - /* save away the message state again */ - msg->Handle = message_state; - - TRACE("-- %d\n", status); - - return HRESULT_FROM_WIN32(status); -} - -static HANDLE ClientRpcChannelBuffer_GetEventHandle(ClientRpcChannelBuffer *This) -{ - HANDLE event = InterlockedExchangePointer(&This->event, NULL); - - /* Note: must be auto-reset event so we can reuse it without a call - * to ResetEvent */ - if (!event) event = CreateEventW(NULL, FALSE, FALSE, NULL); - - return event; -} - -static void ClientRpcChannelBuffer_ReleaseEventHandle(ClientRpcChannelBuffer *This, HANDLE event) -{ - if (InterlockedCompareExchangePointer(&This->event, event, NULL)) - /* already a handle cached in This */ - CloseHandle(event); -} - -static HRESULT WINAPI ClientRpcChannelBuffer_GetBuffer(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE* olemsg, REFIID riid) -{ - ClientRpcChannelBuffer *This = (ClientRpcChannelBuffer *)iface; - RPC_MESSAGE *msg = (RPC_MESSAGE *)olemsg; - RPC_CLIENT_INTERFACE *cif; - RPC_STATUS status; - ORPCTHIS *orpcthis; - struct message_state *message_state; - ULONG extensions_size; - struct channel_hook_buffer_data *channel_hook_data; - unsigned int channel_hook_count; - ULONG extension_count; - IPID ipid; - HRESULT hr; - struct apartment *apt = NULL; - - TRACE("(%p)->(%p,%s)\n", This, olemsg, debugstr_guid(riid)); - - cif = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(RPC_CLIENT_INTERFACE)); - if (!cif) - return E_OUTOFMEMORY; - - message_state = HeapAlloc(GetProcessHeap(), 0, sizeof(*message_state)); - if (!message_state) - { - HeapFree(GetProcessHeap(), 0, cif); - return E_OUTOFMEMORY; - } - - cif->Length = sizeof(RPC_CLIENT_INTERFACE); - /* RPC interface ID = COM interface ID */ - cif->InterfaceId.SyntaxGUID = This->iid; - /* COM objects always have a version of 0.0 */ - cif->InterfaceId.SyntaxVersion.MajorVersion = 0; - cif->InterfaceId.SyntaxVersion.MinorVersion = 0; - msg->Handle = This->bind; - msg->RpcInterfaceInformation = cif; - - message_state->prefix_data_len = 0; - message_state->binding_handle = This->bind; - - message_state->channel_hook_info.iid = *riid; - message_state->channel_hook_info.cbSize = sizeof(message_state->channel_hook_info); - CoGetCurrentLogicalThreadId(&message_state->channel_hook_info.uCausality); - message_state->channel_hook_info.dwServerPid = This->server_pid; - message_state->channel_hook_info.iMethod = msg->ProcNum & ~RPC_FLAGS_VALID_BIT; - message_state->channel_hook_info.pObject = NULL; /* only present on server-side */ - message_state->target_hwnd = NULL; - message_state->target_tid = 0; - memset(&message_state->params, 0, sizeof(message_state->params)); - - extensions_size = ChannelHooks_ClientGetSize(&message_state->channel_hook_info, - &channel_hook_data, &channel_hook_count, &extension_count); - - msg->BufferLength += FIELD_OFFSET(WIRE_ORPCTHIS, extensions) + sizeof(DWORD); - if (extensions_size) - { - msg->BufferLength += FIELD_OFFSET(WIRE_ORPC_EXTENT_ARRAY, extent[2*sizeof(DWORD) + extensions_size]); - if (extension_count & 1) - msg->BufferLength += FIELD_OFFSET(WIRE_ORPC_EXTENT, data[0]); - } - - RpcBindingInqObject(message_state->binding_handle, &ipid); - hr = ipid_get_dispatch_params(&ipid, &apt, NULL, &message_state->params.stub, - &message_state->params.chan, - &message_state->params.iid, - &message_state->params.iface); - if (hr == S_OK) - { - /* stub, chan, iface and iid are unneeded in multi-threaded case as we go - * via the RPC runtime */ - if (apt->multi_threaded) - { - IRpcStubBuffer_Release(message_state->params.stub); - message_state->params.stub = NULL; - IRpcChannelBuffer_Release(message_state->params.chan); - message_state->params.chan = NULL; - message_state->params.iface = NULL; - } - else - { - message_state->params.bypass_rpcrt = TRUE; - message_state->target_hwnd = apartment_getwindow(apt); - message_state->target_tid = apt->tid; - /* we assume later on that this being non-NULL is the indicator that - * means call directly instead of going through RPC runtime */ - if (!message_state->target_hwnd) - ERR("window for apartment %s is NULL\n", wine_dbgstr_longlong(apt->oxid)); - } - } - if (apt) apartment_release(apt); - message_state->params.handle = ClientRpcChannelBuffer_GetEventHandle(This); - /* Note: message_state->params.msg is initialised in - * ClientRpcChannelBuffer_SendReceive */ - - /* shortcut the RPC runtime */ - if (message_state->target_hwnd) - { - msg->Buffer = HeapAlloc(GetProcessHeap(), 0, msg->BufferLength); - if (msg->Buffer) - status = RPC_S_OK; - else - status = ERROR_OUTOFMEMORY; - } - else - status = I_RpcGetBuffer(msg); - - msg->Handle = message_state; - - if (status == RPC_S_OK) - { - orpcthis = msg->Buffer; - msg->Buffer = (char *)msg->Buffer + FIELD_OFFSET(WIRE_ORPCTHIS, extensions); - - orpcthis->version.MajorVersion = COM_MAJOR_VERSION; - orpcthis->version.MinorVersion = COM_MINOR_VERSION; - orpcthis->flags = message_state->channel_hook_info.dwServerPid ? ORPCF_LOCAL : ORPCF_NULL; - orpcthis->reserved1 = 0; - orpcthis->cid = message_state->channel_hook_info.uCausality; - - /* NDR representation of orpcthis->extensions */ - *(DWORD *)msg->Buffer = extensions_size ? 1 : 0; - msg->Buffer = (char *)msg->Buffer + sizeof(DWORD); - - if (extensions_size) - { - ORPC_EXTENT_ARRAY *orpc_extent_array = msg->Buffer; - orpc_extent_array->size = extension_count; - orpc_extent_array->reserved = 0; - msg->Buffer = (char *)msg->Buffer + FIELD_OFFSET(WIRE_ORPC_EXTENT_ARRAY, extent); - /* NDR representation of orpc_extent_array->extent */ - *(DWORD *)msg->Buffer = 1; - msg->Buffer = (char *)msg->Buffer + sizeof(DWORD); - /* NDR representation of [size_is] attribute of orpc_extent_array->extent */ - *(DWORD *)msg->Buffer = (extension_count + 1) & ~1; - msg->Buffer = (char *)msg->Buffer + sizeof(DWORD); - - msg->Buffer = ChannelHooks_ClientFillBuffer(&message_state->channel_hook_info, - msg->Buffer, channel_hook_data, channel_hook_count); - - /* we must add a dummy extension if there is an odd extension - * count to meet the contract specified by the size_is attribute */ - if (extension_count & 1) - { - WIRE_ORPC_EXTENT *wire_orpc_extent = msg->Buffer; - wire_orpc_extent->conformance = 0; - wire_orpc_extent->id = GUID_NULL; - wire_orpc_extent->size = 0; - msg->Buffer = (char *)msg->Buffer + FIELD_OFFSET(WIRE_ORPC_EXTENT, data[0]); - } - } - - /* store the prefixed data length so that we can restore the real buffer - * pointer in ClientRpcChannelBuffer_SendReceive. */ - message_state->prefix_data_len = (char *)msg->Buffer - (char *)orpcthis; - msg->BufferLength -= message_state->prefix_data_len; - } - - HeapFree(GetProcessHeap(), 0, channel_hook_data); - - TRACE("-- %d\n", status); - - return HRESULT_FROM_WIN32(status); -} - -static HRESULT WINAPI ServerRpcChannelBuffer_SendReceive(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE *olemsg, ULONG *pstatus) -{ - FIXME("stub\n"); - return E_NOTIMPL; -} - -/* this thread runs an outgoing RPC */ -static DWORD WINAPI rpc_sendreceive_thread(LPVOID param) -{ - struct dispatch_params *data = param; - - /* Note: I_RpcSendReceive doesn't raise exceptions like the higher-level - * RPC functions do */ - data->status = I_RpcSendReceive((RPC_MESSAGE *)data->msg); - - TRACE("completed with status 0x%x\n", data->status); - - SetEvent(data->handle); - - return 0; -} - -static inline HRESULT ClientRpcChannelBuffer_IsCorrectApartment(ClientRpcChannelBuffer *This, struct apartment *apt) -{ - OXID oxid; - if (!apt) - return S_FALSE; - if (apartment_getoxid(apt, &oxid) != S_OK) - return S_FALSE; - if (This->oxid != oxid) - return S_FALSE; - return S_OK; -} - -static HRESULT WINAPI ClientRpcChannelBuffer_SendReceive(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE *olemsg, ULONG *pstatus) -{ - ClientRpcChannelBuffer *This = (ClientRpcChannelBuffer *)iface; - HRESULT hr; - RPC_MESSAGE *msg = (RPC_MESSAGE *)olemsg; - RPC_STATUS status; - DWORD index; - struct message_state *message_state; - ORPCTHAT orpcthat; - ORPC_EXTENT_ARRAY orpc_ext_array; - WIRE_ORPC_EXTENT *first_wire_orpc_extent = NULL; - HRESULT hrFault = S_OK; - struct apartment *apt = apartment_get_current_or_mta(); - - TRACE("(%p) iMethod=%d\n", olemsg, olemsg->iMethod); - - hr = ClientRpcChannelBuffer_IsCorrectApartment(This, apt); - if (hr != S_OK) - { - ERR("called from wrong apartment, should have been 0x%s\n", - wine_dbgstr_longlong(This->oxid)); - if (apt) apartment_release(apt); - return RPC_E_WRONG_THREAD; - } - /* This situation should be impossible in multi-threaded apartments, - * because the calling thread isn't re-enterable. - * Note: doing a COM call during the processing of a sent message is - * only disallowed if a client call is already being waited for - * completion */ - if (!apt->multi_threaded && - COM_CurrentInfo()->pending_call_count_client && - InSendMessage()) - { - ERR("can't make an outgoing COM call in response to a sent message\n"); - apartment_release(apt); - return RPC_E_CANTCALLOUT_ININPUTSYNCCALL; - } - - message_state = msg->Handle; - /* restore the binding handle and the real start of data */ - msg->Handle = message_state->binding_handle; - msg->Buffer = (char *)msg->Buffer - message_state->prefix_data_len; - msg->BufferLength += message_state->prefix_data_len; - - /* Note: this is an optimization in the Microsoft OLE runtime that we need - * to copy, as shown by the test_no_couninitialize_client test. without - * short-circuiting the RPC runtime in the case below, the test will - * deadlock on the loader lock due to the RPC runtime needing to create - * a thread to process the RPC when this function is called indirectly - * from DllMain */ - - message_state->params.msg = olemsg; - if (message_state->params.bypass_rpcrt) - { - TRACE("Calling apartment thread 0x%08x...\n", message_state->target_tid); - - msg->ProcNum &= ~RPC_FLAGS_VALID_BIT; - - if (!PostMessageW(message_state->target_hwnd, DM_EXECUTERPC, 0, - (LPARAM)&message_state->params)) - { - ERR("PostMessage failed with error %u\n", GetLastError()); - - /* Note: message_state->params.iface doesn't have a reference and - * so doesn't need to be released */ - - hr = HRESULT_FROM_WIN32(GetLastError()); - } - } - else - { - /* we use a separate thread here because we need to be able to - * pump the message loop in the application thread: if we do not, - * any windows created by this thread will hang and RPCs that try - * and re-enter this STA from an incoming server thread will - * deadlock. InstallShield is an example of that. - */ - if (!QueueUserWorkItem(rpc_sendreceive_thread, &message_state->params, WT_EXECUTEDEFAULT)) - { - ERR("QueueUserWorkItem failed with error %u\n", GetLastError()); - hr = E_UNEXPECTED; - } - else - hr = S_OK; - } - - if (hr == S_OK) - { - if (WaitForSingleObject(message_state->params.handle, 0)) - { - COM_CurrentInfo()->pending_call_count_client++; - hr = CoWaitForMultipleHandles(0, INFINITE, 1, &message_state->params.handle, &index); - COM_CurrentInfo()->pending_call_count_client--; - } - } - ClientRpcChannelBuffer_ReleaseEventHandle(This, message_state->params.handle); - - /* for WM shortcut, faults are returned in params->hr */ - if (hr == S_OK) - hrFault = message_state->params.hr; - - status = message_state->params.status; - - orpcthat.flags = ORPCF_NULL; - orpcthat.extensions = NULL; - - TRACE("RPC call status: 0x%x\n", status); - if (status != RPC_S_OK) - hr = HRESULT_FROM_WIN32(status); - - TRACE("hrFault = 0x%08x\n", hrFault); - - /* FIXME: this condition should be - * "hr == S_OK && (!hrFault || msg->BufferLength > FIELD_OFFSET(ORPCTHAT, extensions) + 4)" - * but we don't currently reset the message length for PostMessage - * dispatched calls */ - if (hr == S_OK && hrFault == S_OK) - { - HRESULT hr2; - char *original_buffer = msg->Buffer; - - /* handle ORPCTHAT and client extensions */ - - hr2 = unmarshal_ORPCTHAT(msg, &orpcthat, &orpc_ext_array, &first_wire_orpc_extent); - if (FAILED(hr2)) - hr = hr2; - - message_state->prefix_data_len = (char *)msg->Buffer - original_buffer; - msg->BufferLength -= message_state->prefix_data_len; - } - else - message_state->prefix_data_len = 0; - - if (hr == S_OK) - { - ChannelHooks_ClientNotify(&message_state->channel_hook_info, - msg->DataRepresentation, - first_wire_orpc_extent, - orpcthat.extensions && first_wire_orpc_extent ? orpcthat.extensions->size : 0, - hrFault); - } - - /* save away the message state again */ - msg->Handle = message_state; - - if (pstatus) *pstatus = status; - - if (hr == S_OK) - hr = hrFault; - - TRACE("-- 0x%08x\n", hr); - - apartment_release(apt); - return hr; -} - -static HRESULT WINAPI ServerRpcChannelBuffer_FreeBuffer(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE* olemsg) -{ - RPC_MESSAGE *msg = (RPC_MESSAGE *)olemsg; - RPC_STATUS status; - struct message_state *message_state; - - TRACE("(%p)\n", msg); - - message_state = msg->Handle; - /* restore the binding handle and the real start of data */ - msg->Handle = message_state->binding_handle; - msg->Buffer = (char *)msg->Buffer - message_state->prefix_data_len; - msg->BufferLength += message_state->prefix_data_len; - message_state->prefix_data_len = 0; - - if (message_state->bypass_rpcrt) - { - HeapFree(GetProcessHeap(), 0, msg->Buffer); - status = RPC_S_OK; - } - else - status = I_RpcFreeBuffer(msg); - - msg->Handle = message_state; - - TRACE("-- %d\n", status); - - return HRESULT_FROM_WIN32(status); -} - -static HRESULT WINAPI ClientRpcChannelBuffer_FreeBuffer(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE* olemsg) -{ - RPC_MESSAGE *msg = (RPC_MESSAGE *)olemsg; - RPC_STATUS status; - struct message_state *message_state; - - TRACE("(%p)\n", msg); - - message_state = msg->Handle; - /* restore the binding handle and the real start of data */ - msg->Handle = message_state->binding_handle; - msg->Buffer = (char *)msg->Buffer - message_state->prefix_data_len; - msg->BufferLength += message_state->prefix_data_len; - - if (message_state->params.bypass_rpcrt) - { - HeapFree(GetProcessHeap(), 0, msg->Buffer); - status = RPC_S_OK; - } - else - status = I_RpcFreeBuffer(msg); - - HeapFree(GetProcessHeap(), 0, msg->RpcInterfaceInformation); - msg->RpcInterfaceInformation = NULL; - - if (message_state->params.stub) - IRpcStubBuffer_Release(message_state->params.stub); - if (message_state->params.chan) - IRpcChannelBuffer_Release(message_state->params.chan); - HeapFree(GetProcessHeap(), 0, message_state); - - TRACE("-- %d\n", status); - - return HRESULT_FROM_WIN32(status); -} - -static HRESULT WINAPI ClientRpcChannelBuffer_GetDestCtx(LPRPCCHANNELBUFFER iface, DWORD* pdwDestContext, void** ppvDestContext) -{ - ClientRpcChannelBuffer *This = (ClientRpcChannelBuffer *)iface; - - TRACE("(%p,%p)\n", pdwDestContext, ppvDestContext); - - *pdwDestContext = This->super.dest_context; - *ppvDestContext = This->super.dest_context_data; - - return S_OK; -} - -static HRESULT WINAPI ServerRpcChannelBuffer_GetDestCtx(LPRPCCHANNELBUFFER iface, DWORD* dest_context, void** dest_context_data) -{ - RpcChannelBuffer *This = (RpcChannelBuffer *)iface; - - TRACE("(%p,%p)\n", dest_context, dest_context_data); - - *dest_context = This->dest_context; - *dest_context_data = This->dest_context_data; - return S_OK; -} - -static HRESULT WINAPI RpcChannelBuffer_IsConnected(LPRPCCHANNELBUFFER iface) -{ - TRACE("()\n"); - /* native does nothing too */ - return S_OK; -} - -static const IRpcChannelBufferVtbl ClientRpcChannelBufferVtbl = -{ - RpcChannelBuffer_QueryInterface, - RpcChannelBuffer_AddRef, - ClientRpcChannelBuffer_Release, - ClientRpcChannelBuffer_GetBuffer, - ClientRpcChannelBuffer_SendReceive, - ClientRpcChannelBuffer_FreeBuffer, - ClientRpcChannelBuffer_GetDestCtx, - RpcChannelBuffer_IsConnected -}; - -static const IRpcChannelBufferVtbl ServerRpcChannelBufferVtbl = -{ - RpcChannelBuffer_QueryInterface, - RpcChannelBuffer_AddRef, - ServerRpcChannelBuffer_Release, - ServerRpcChannelBuffer_GetBuffer, - ServerRpcChannelBuffer_SendReceive, - ServerRpcChannelBuffer_FreeBuffer, - ServerRpcChannelBuffer_GetDestCtx, - RpcChannelBuffer_IsConnected -}; - -/* returns a channel buffer for proxies */ -HRESULT WINAPI RPC_CreateClientChannel(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) -{ - ClientRpcChannelBuffer *This; - WCHAR endpoint[200]; - RPC_BINDING_HANDLE bind; - RPC_STATUS status; - LPWSTR string_binding; - - /* FIXME: get the endpoint from oxid_info->psa instead */ - get_rpc_endpoint(endpoint, oxid); - - TRACE("proxy pipe: connecting to endpoint: %s\n", debugstr_w(endpoint)); - - status = RpcStringBindingComposeW( - NULL, - wszRpcTransport, - NULL, - endpoint, - NULL, - &string_binding); - - if (status == RPC_S_OK) - { - status = RpcBindingFromStringBindingW(string_binding, &bind); - - if (status == RPC_S_OK) - { - IPID ipid2 = *ipid; /* why can't RpcBindingSetObject take a const? */ - status = RpcBindingSetObject(bind, &ipid2); - if (status != RPC_S_OK) - RpcBindingFree(&bind); - } - - RpcStringFreeW(&string_binding); - } - - if (status != RPC_S_OK) - { - ERR("Couldn't get binding for endpoint %s, status = %d\n", debugstr_w(endpoint), status); - return HRESULT_FROM_WIN32(status); - } - - This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This)); - if (!This) - { - RpcBindingFree(&bind); - return E_OUTOFMEMORY; - } - - This->super.IRpcChannelBuffer_iface.lpVtbl = &ClientRpcChannelBufferVtbl; - This->super.refs = 1; - This->super.dest_context = dest_context; - This->super.dest_context_data = dest_context_data; - This->bind = bind; - apartment_getoxid(apt, &This->oxid); - This->server_pid = oxid_info->dwPid; - This->event = NULL; - This->iid = *iid; - - *chan = &This->super.IRpcChannelBuffer_iface; - - return S_OK; -} - -HRESULT WINAPI RPC_CreateServerChannel(DWORD dest_context, void *dest_context_data, IRpcChannelBuffer **chan) -{ - RpcChannelBuffer *This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This)); - if (!This) - return E_OUTOFMEMORY; - - This->IRpcChannelBuffer_iface.lpVtbl = &ServerRpcChannelBufferVtbl; - This->refs = 1; - This->dest_context = dest_context; - This->dest_context_data = dest_context_data; - - *chan = &This->IRpcChannelBuffer_iface; - - return S_OK; -} - -/* unmarshals ORPC_EXTENT_ARRAY according to NDR rules, but doesn't allocate - * any memory */ -static HRESULT unmarshal_ORPC_EXTENT_ARRAY(RPC_MESSAGE *msg, const char *end, - ORPC_EXTENT_ARRAY *extensions, - WIRE_ORPC_EXTENT **first_wire_orpc_extent) -{ - DWORD pointer_id; - DWORD i; - - memcpy(extensions, msg->Buffer, FIELD_OFFSET(WIRE_ORPC_EXTENT_ARRAY, extent)); - msg->Buffer = (char *)msg->Buffer + FIELD_OFFSET(WIRE_ORPC_EXTENT_ARRAY, extent); - - if ((const char *)msg->Buffer + 2 * sizeof(DWORD) > end) - return RPC_E_INVALID_HEADER; - - pointer_id = *(DWORD *)msg->Buffer; - msg->Buffer = (char *)msg->Buffer + sizeof(DWORD); - extensions->extent = NULL; - - if (pointer_id) - { - WIRE_ORPC_EXTENT *wire_orpc_extent; - - /* conformance */ - if (*(DWORD *)msg->Buffer != ((extensions->size+1)&~1)) - return RPC_S_INVALID_BOUND; - - msg->Buffer = (char *)msg->Buffer + sizeof(DWORD); - - /* arbitrary limit for security (don't know what native does) */ - if (extensions->size > 256) - { - ERR("too many extensions: %d\n", extensions->size); - return RPC_S_INVALID_BOUND; - } - - *first_wire_orpc_extent = wire_orpc_extent = msg->Buffer; - for (i = 0; i < ((extensions->size+1)&~1); i++) - { - if ((const char *)&wire_orpc_extent->data[0] > end) - return RPC_S_INVALID_BOUND; - if (wire_orpc_extent->conformance != ((wire_orpc_extent->size+7)&~7)) - return RPC_S_INVALID_BOUND; - if ((const char *)&wire_orpc_extent->data[wire_orpc_extent->conformance] > end) - return RPC_S_INVALID_BOUND; - TRACE("size %u, guid %s\n", wire_orpc_extent->size, debugstr_guid(&wire_orpc_extent->id)); - wire_orpc_extent = (WIRE_ORPC_EXTENT *)&wire_orpc_extent->data[wire_orpc_extent->conformance]; - } - msg->Buffer = wire_orpc_extent; - } - - return S_OK; -} - -/* unmarshals ORPCTHIS according to NDR rules, but doesn't allocate any memory */ -static HRESULT unmarshal_ORPCTHIS(RPC_MESSAGE *msg, ORPCTHIS *orpcthis, - ORPC_EXTENT_ARRAY *orpc_ext_array, WIRE_ORPC_EXTENT **first_wire_orpc_extent) -{ - const char *end = (char *)msg->Buffer + msg->BufferLength; - - *first_wire_orpc_extent = NULL; - - if (msg->BufferLength < FIELD_OFFSET(WIRE_ORPCTHIS, extensions) + sizeof(DWORD)) - { - ERR("invalid buffer length\n"); - return RPC_E_INVALID_HEADER; - } - - memcpy(orpcthis, msg->Buffer, FIELD_OFFSET(WIRE_ORPCTHIS, extensions)); - msg->Buffer = (char *)msg->Buffer + FIELD_OFFSET(WIRE_ORPCTHIS, extensions); - - if ((const char *)msg->Buffer + sizeof(DWORD) > end) - return RPC_E_INVALID_HEADER; - - if (*(DWORD *)msg->Buffer) - orpcthis->extensions = orpc_ext_array; - else - orpcthis->extensions = NULL; - - msg->Buffer = (char *)msg->Buffer + sizeof(DWORD); - - if (orpcthis->extensions) - { - HRESULT hr = unmarshal_ORPC_EXTENT_ARRAY(msg, end, orpc_ext_array, - first_wire_orpc_extent); - if (FAILED(hr)) - return hr; - } - - if ((orpcthis->version.MajorVersion != COM_MAJOR_VERSION) || - (orpcthis->version.MinorVersion > COM_MINOR_VERSION)) - { - ERR("COM version {%d, %d} not supported\n", - orpcthis->version.MajorVersion, orpcthis->version.MinorVersion); - return RPC_E_VERSION_MISMATCH; - } - - if (orpcthis->flags & ~(ORPCF_LOCAL|ORPCF_RESERVED1|ORPCF_RESERVED2|ORPCF_RESERVED3|ORPCF_RESERVED4)) - { - ERR("invalid flags 0x%x\n", orpcthis->flags & ~(ORPCF_LOCAL|ORPCF_RESERVED1|ORPCF_RESERVED2|ORPCF_RESERVED3|ORPCF_RESERVED4)); - return RPC_E_INVALID_HEADER; - } - - return S_OK; -} - -static HRESULT unmarshal_ORPCTHAT(RPC_MESSAGE *msg, ORPCTHAT *orpcthat, - ORPC_EXTENT_ARRAY *orpc_ext_array, WIRE_ORPC_EXTENT **first_wire_orpc_extent) -{ - const char *end = (char *)msg->Buffer + msg->BufferLength; - - *first_wire_orpc_extent = NULL; - - if (msg->BufferLength < FIELD_OFFSET(WIRE_ORPCTHAT, extensions) + sizeof(DWORD)) - { - ERR("invalid buffer length\n"); - return RPC_E_INVALID_HEADER; - } - - memcpy(orpcthat, msg->Buffer, FIELD_OFFSET(WIRE_ORPCTHAT, extensions)); - msg->Buffer = (char *)msg->Buffer + FIELD_OFFSET(WIRE_ORPCTHAT, extensions); - - if ((const char *)msg->Buffer + sizeof(DWORD) > end) - return RPC_E_INVALID_HEADER; - - if (*(DWORD *)msg->Buffer) - orpcthat->extensions = orpc_ext_array; - else - orpcthat->extensions = NULL; - - msg->Buffer = (char *)msg->Buffer + sizeof(DWORD); - - if (orpcthat->extensions) - { - HRESULT hr = unmarshal_ORPC_EXTENT_ARRAY(msg, end, orpc_ext_array, - first_wire_orpc_extent); - if (FAILED(hr)) - return hr; - } - - if (orpcthat->flags & ~(ORPCF_LOCAL|ORPCF_RESERVED1|ORPCF_RESERVED2|ORPCF_RESERVED3|ORPCF_RESERVED4)) - { - ERR("invalid flags 0x%x\n", orpcthat->flags & ~(ORPCF_LOCAL|ORPCF_RESERVED1|ORPCF_RESERVED2|ORPCF_RESERVED3|ORPCF_RESERVED4)); - return RPC_E_INVALID_HEADER; - } - - return S_OK; -} - -void WINAPI Internal_RPC_ExecuteCall(struct dispatch_params *params) -{ - struct message_state *message_state = NULL; - RPC_MESSAGE *msg = (RPC_MESSAGE *)params->msg; - char *original_buffer = msg->Buffer; - ORPCTHIS orpcthis; - ORPC_EXTENT_ARRAY orpc_ext_array; - WIRE_ORPC_EXTENT *first_wire_orpc_extent; - GUID old_causality_id; - - /* handle ORPCTHIS and server extensions */ - - params->hr = unmarshal_ORPCTHIS(msg, &orpcthis, &orpc_ext_array, &first_wire_orpc_extent); - if (params->hr != S_OK) - { - msg->Buffer = original_buffer; - goto exit; - } - - message_state = HeapAlloc(GetProcessHeap(), 0, sizeof(*message_state)); - if (!message_state) - { - params->hr = E_OUTOFMEMORY; - msg->Buffer = original_buffer; - goto exit; - } - - message_state->prefix_data_len = (char *)msg->Buffer - original_buffer; - message_state->binding_handle = msg->Handle; - message_state->bypass_rpcrt = params->bypass_rpcrt; - - message_state->channel_hook_info.iid = params->iid; - message_state->channel_hook_info.cbSize = sizeof(message_state->channel_hook_info); - message_state->channel_hook_info.uCausality = orpcthis.cid; - message_state->channel_hook_info.dwServerPid = GetCurrentProcessId(); - message_state->channel_hook_info.iMethod = msg->ProcNum; - message_state->channel_hook_info.pObject = params->iface; - - if (orpcthis.extensions && first_wire_orpc_extent && - orpcthis.extensions->size) - ChannelHooks_ServerNotify(&message_state->channel_hook_info, msg->DataRepresentation, first_wire_orpc_extent, orpcthis.extensions->size); - - msg->Handle = message_state; - msg->BufferLength -= message_state->prefix_data_len; - - /* call message filter */ - - if (COM_CurrentApt()->filter) - { - DWORD handlecall; - INTERFACEINFO interface_info; - CALLTYPE calltype; - - interface_info.pUnk = params->iface; - interface_info.iid = params->iid; - interface_info.wMethod = msg->ProcNum; - - if (IsEqualGUID(&orpcthis.cid, &COM_CurrentInfo()->causality_id)) - calltype = CALLTYPE_NESTED; - else if (COM_CurrentInfo()->pending_call_count_server == 0) - calltype = CALLTYPE_TOPLEVEL; - else - calltype = CALLTYPE_TOPLEVEL_CALLPENDING; - - handlecall = IMessageFilter_HandleInComingCall(COM_CurrentApt()->filter, - calltype, - UlongToHandle(GetCurrentProcessId()), - 0 /* FIXME */, - &interface_info); - TRACE("IMessageFilter_HandleInComingCall returned %d\n", handlecall); - switch (handlecall) - { - case SERVERCALL_REJECTED: - params->hr = RPC_E_CALL_REJECTED; - goto exit_reset_state; - case SERVERCALL_RETRYLATER: -#if 0 /* FIXME: handle retries on the client side before enabling this code */ - params->hr = RPC_E_RETRY; - goto exit_reset_state; -#else - FIXME("retry call later not implemented\n"); - break; -#endif - case SERVERCALL_ISHANDLED: - default: - break; - } - } - - /* invoke the method */ - - /* save the old causality ID - note: any calls executed while processing - * messages received during the SendReceive will appear to originate from - * this call - this should be checked with what Windows does */ - old_causality_id = COM_CurrentInfo()->causality_id; - COM_CurrentInfo()->causality_id = orpcthis.cid; - COM_CurrentInfo()->pending_call_count_server++; - params->hr = IRpcStubBuffer_Invoke(params->stub, params->msg, params->chan); - COM_CurrentInfo()->pending_call_count_server--; - COM_CurrentInfo()->causality_id = old_causality_id; - - /* the invoke allocated a new buffer, so free the old one */ - if (message_state->bypass_rpcrt && original_buffer != msg->Buffer) - HeapFree(GetProcessHeap(), 0, original_buffer); - -exit_reset_state: - message_state = msg->Handle; - msg->Handle = message_state->binding_handle; - msg->Buffer = (char *)msg->Buffer - message_state->prefix_data_len; - msg->BufferLength += message_state->prefix_data_len; - -exit: - HeapFree(GetProcessHeap(), 0, message_state); - if (params->handle) SetEvent(params->handle); -} - -static void __RPC_STUB dispatch_rpc(RPC_MESSAGE *msg) -{ - struct dispatch_params *params; - struct stub_manager *stub_manager; - struct apartment *apt; - IPID ipid; - HRESULT hr; - - RpcBindingInqObject(msg->Handle, &ipid); - - TRACE("ipid = %s, iMethod = %d\n", debugstr_guid(&ipid), msg->ProcNum); - - params = HeapAlloc(GetProcessHeap(), 0, sizeof(*params)); - if (!params) - { - RpcRaiseException(E_OUTOFMEMORY); - return; - } - - hr = ipid_get_dispatch_params(&ipid, &apt, &stub_manager, ¶ms->stub, ¶ms->chan, - ¶ms->iid, ¶ms->iface); - if (hr != S_OK) - { - ERR("no apartment found for ipid %s\n", debugstr_guid(&ipid)); - HeapFree(GetProcessHeap(), 0, params); - RpcRaiseException(hr); - return; - } - - params->msg = (RPCOLEMESSAGE *)msg; - params->status = RPC_S_OK; - params->hr = S_OK; - params->handle = NULL; - params->bypass_rpcrt = FALSE; - - /* Note: this is the important difference between STAs and MTAs - we - * always execute RPCs to STAs in the thread that originally created the - * apartment (i.e. the one that pumps messages to the window) */ - if (!apt->multi_threaded) - { - params->handle = CreateEventW(NULL, FALSE, FALSE, NULL); - - TRACE("Calling apartment thread 0x%08x...\n", apt->tid); - - if (PostMessageW(apartment_getwindow(apt), DM_EXECUTERPC, 0, (LPARAM)params)) - WaitForSingleObject(params->handle, INFINITE); - else - { - ERR("PostMessage failed with error %u\n", GetLastError()); - IRpcChannelBuffer_Release(params->chan); - IRpcStubBuffer_Release(params->stub); - } - CloseHandle(params->handle); - } - else - { - BOOL joined = FALSE; - struct oletls *info = COM_CurrentInfo(); - - if (!info->apt) - { - enter_apartment(info, COINIT_MULTITHREADED); - joined = TRUE; - } - Internal_RPC_ExecuteCall(params); - if (joined) - { - leave_apartment(info); - } - } - - hr = params->hr; - if (params->chan) - IRpcChannelBuffer_Release(params->chan); - if (params->stub) - IRpcStubBuffer_Release(params->stub); - HeapFree(GetProcessHeap(), 0, params); - - stub_manager_int_release(stub_manager); - apartment_release(apt); - - /* if IRpcStubBuffer_Invoke fails, we should raise an exception to tell - * the RPC runtime that the call failed */ - if (hr != S_OK) RpcRaiseException(hr); -} - -/* stub registration */ -HRESULT WINAPI RPC_RegisterInterface(REFIID riid) -{ - struct registered_if *rif; - BOOL found = FALSE; - HRESULT hr = S_OK; - - TRACE("(%s)\n", debugstr_guid(riid)); - - EnterCriticalSection(&csRegIf); - LIST_FOR_EACH_ENTRY(rif, ®istered_interfaces, struct registered_if, entry) - { - if (IsEqualGUID(&rif->If.InterfaceId.SyntaxGUID, riid)) - { - rif->refs++; - found = TRUE; - break; - } - } - if (!found) - { - TRACE("Creating new interface\n"); - - rif = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*rif)); - if (rif) - { - RPC_STATUS status; - - rif->refs = 1; - rif->If.Length = sizeof(RPC_SERVER_INTERFACE); - /* RPC interface ID = COM interface ID */ - rif->If.InterfaceId.SyntaxGUID = *riid; - rif->If.DispatchTable = &rpc_dispatch; - /* all other fields are 0, including the version asCOM objects - * always have a version of 0.0 */ - status = RpcServerRegisterIfEx( - (RPC_IF_HANDLE)&rif->If, - NULL, NULL, - RPC_IF_OLE | RPC_IF_AUTOLISTEN, - RPC_C_LISTEN_MAX_CALLS_DEFAULT, - NULL); - if (status == RPC_S_OK) - list_add_tail(®istered_interfaces, &rif->entry); - else - { - ERR("RpcServerRegisterIfEx failed with error %d\n", status); - HeapFree(GetProcessHeap(), 0, rif); - hr = HRESULT_FROM_WIN32(status); - } - } - else - hr = E_OUTOFMEMORY; - } - LeaveCriticalSection(&csRegIf); - return hr; -} - -/* stub unregistration */ -void WINAPI RPC_UnregisterInterface(REFIID riid, BOOL wait) -{ - struct registered_if *rif; - EnterCriticalSection(&csRegIf); - LIST_FOR_EACH_ENTRY(rif, ®istered_interfaces, struct registered_if, entry) - { - if (IsEqualGUID(&rif->If.InterfaceId.SyntaxGUID, riid)) - { - if (!--rif->refs) - { - RpcServerUnregisterIf((RPC_IF_HANDLE)&rif->If, NULL, wait); - list_remove(&rif->entry); - HeapFree(GetProcessHeap(), 0, rif); - } - break; - } - } - LeaveCriticalSection(&csRegIf); -} - -/* get the info for an OXID, including the IPID for the rem unknown interface - * and the string binding */ -HRESULT WINAPI RPC_ResolveOxid(OXID oxid, OXID_INFO *oxid_info) -{ - TRACE("%s\n", wine_dbgstr_longlong(oxid)); - - oxid_info->dwTid = 0; - oxid_info->dwPid = 0; - oxid_info->dwAuthnHint = RPC_C_AUTHN_LEVEL_NONE; - /* FIXME: this is a hack around not having an OXID resolver yet - - * this function should contact the machine's OXID resolver and then it - * should give us the IPID of the IRemUnknown interface */ - oxid_info->ipidRemUnknown.Data1 = 0xffffffff; - oxid_info->ipidRemUnknown.Data2 = 0xffff; - oxid_info->ipidRemUnknown.Data3 = 0xffff; - memcpy(oxid_info->ipidRemUnknown.Data4, &oxid, sizeof(OXID)); - oxid_info->psa = NULL /* FIXME */; - - return S_OK; -} - -/* make the apartment reachable by other threads and processes and create the - * IRemUnknown object */ -void WINAPI RPC_StartRemoting(struct apartment *apt) -{ - if (!InterlockedExchange(&apt->remoting_started, TRUE)) - { - WCHAR endpoint[200]; - RPC_STATUS status; - - get_rpc_endpoint(endpoint, &apt->oxid); - - status = RpcServerUseProtseqEpW( - wszRpcTransport, - RPC_C_PROTSEQ_MAX_REQS_DEFAULT, - endpoint, - NULL); - if (status != RPC_S_OK) - ERR("Couldn't register endpoint %s\n", debugstr_w(endpoint)); - - /* FIXME: move remote unknown exporting into this function */ - } - start_apartment_remote_unknown(apt); -}