widl: Move support for stub arguments to typegen.c to make it usable for proxies...
[wine/multimedia.git] / dlls / ole32 / rpc.c
blobfdb81edd3d0d18e832e866081f7110e6cd42f148
1 /*
2 * RPC Manager
4 * Copyright 2001 Ove Kåven, TransGaming Technologies
5 * Copyright 2002 Marcus Meissner
6 * Copyright 2005 Mike Hearn, Rob Shearman for CodeWeavers
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 #include "config.h"
24 #include "wine/port.h"
26 #include <stdarg.h>
27 #include <string.h>
29 #define COBJMACROS
30 #define NONAMELESSUNION
31 #define NONAMELESSSTRUCT
33 #include "windef.h"
34 #include "winbase.h"
35 #include "winuser.h"
36 #include "winsvc.h"
37 #include "objbase.h"
38 #include "ole2.h"
39 #include "rpc.h"
40 #include "winerror.h"
41 #include "winreg.h"
42 #include "wine/unicode.h"
44 #include "compobj_private.h"
46 #include "wine/debug.h"
48 WINE_DEFAULT_DEBUG_CHANNEL(ole);
50 static void __RPC_STUB dispatch_rpc(RPC_MESSAGE *msg);
52 /* we only use one function to dispatch calls for all methods - we use the
53 * RPC_IF_OLE flag to tell the RPC runtime that this is the case */
54 static RPC_DISPATCH_FUNCTION rpc_dispatch_table[1] = { dispatch_rpc }; /* (RO) */
55 static RPC_DISPATCH_TABLE rpc_dispatch = { 1, rpc_dispatch_table }; /* (RO) */
57 static struct list registered_interfaces = LIST_INIT(registered_interfaces); /* (CS csRegIf) */
58 static CRITICAL_SECTION csRegIf;
59 static CRITICAL_SECTION_DEBUG csRegIf_debug =
61 0, 0, &csRegIf,
62 { &csRegIf_debug.ProcessLocksList, &csRegIf_debug.ProcessLocksList },
63 0, 0, { (DWORD_PTR)(__FILE__ ": dcom registered server interfaces") }
65 static CRITICAL_SECTION csRegIf = { &csRegIf_debug, -1, 0, 0, 0, 0 };
67 static struct list channel_hooks = LIST_INIT(channel_hooks); /* (CS csChannelHook) */
68 static CRITICAL_SECTION csChannelHook;
69 static CRITICAL_SECTION_DEBUG csChannelHook_debug =
71 0, 0, &csChannelHook,
72 { &csChannelHook_debug.ProcessLocksList, &csChannelHook_debug.ProcessLocksList },
73 0, 0, { (DWORD_PTR)(__FILE__ ": channel hooks") }
75 static CRITICAL_SECTION csChannelHook = { &csChannelHook_debug, -1, 0, 0, 0, 0 };
77 static WCHAR wszRpcTransport[] = {'n','c','a','l','r','p','c',0};
80 struct registered_if
82 struct list entry;
83 DWORD refs; /* ref count */
84 RPC_SERVER_INTERFACE If; /* interface registered with the RPC runtime */
87 /* get the pipe endpoint specified of the specified apartment */
88 static inline void get_rpc_endpoint(LPWSTR endpoint, const OXID *oxid)
90 /* FIXME: should get endpoint from rpcss */
91 static const WCHAR wszEndpointFormat[] = {'\\','p','i','p','e','\\','O','L','E','_','%','0','8','l','x','%','0','8','l','x',0};
92 wsprintfW(endpoint, wszEndpointFormat, (DWORD)(*oxid >> 32),(DWORD)*oxid);
95 typedef struct
97 const IRpcChannelBufferVtbl *lpVtbl;
98 LONG refs;
99 } RpcChannelBuffer;
101 typedef struct
103 RpcChannelBuffer super; /* superclass */
105 RPC_BINDING_HANDLE bind; /* handle to the remote server */
106 OXID oxid; /* apartment in which the channel is valid */
107 DWORD dest_context; /* returned from GetDestCtx */
108 LPVOID dest_context_data; /* returned from GetDestCtx */
109 HANDLE event; /* cached event handle */
110 } ClientRpcChannelBuffer;
112 struct dispatch_params
114 RPCOLEMESSAGE *msg; /* message */
115 IRpcStubBuffer *stub; /* stub buffer, if applicable */
116 IRpcChannelBuffer *chan; /* server channel buffer, if applicable */
117 IID iid; /* ID of interface being called */
118 IUnknown *iface; /* interface being called */
119 HANDLE handle; /* handle that will become signaled when call finishes */
120 RPC_STATUS status; /* status (out) */
121 HRESULT hr; /* hresult (out) */
124 struct message_state
126 RPC_BINDING_HANDLE binding_handle;
127 ULONG prefix_data_len;
128 SChannelHookCallInfo channel_hook_info;
131 typedef struct
133 ULONG conformance; /* NDR */
134 GUID id;
135 ULONG size;
136 /* [size_is((size+7)&~7)] */ unsigned char data[1];
137 } WIRE_ORPC_EXTENT;
139 struct channel_hook_entry
141 struct list entry;
142 GUID id;
143 IChannelHook *hook;
146 struct channel_hook_buffer_data
148 GUID id;
149 ULONG extension_size;
153 static HRESULT unmarshal_ORPCTHAT(RPC_MESSAGE *msg, ORPCTHAT *orpcthat,
154 ORPC_EXTENT_ARRAY *orpc_ext_array, WIRE_ORPC_EXTENT **first_wire_orpc_extent);
156 /* Channel Hook Functions */
158 static ULONG ChannelHooks_ClientGetSize(SChannelHookCallInfo *info,
159 struct channel_hook_buffer_data **data, unsigned int *hook_count,
160 ULONG *extension_count)
162 struct channel_hook_entry *entry;
163 ULONG total_size = 0;
164 unsigned int hook_index = 0;
166 *hook_count = 0;
167 *extension_count = 0;
169 EnterCriticalSection(&csChannelHook);
171 LIST_FOR_EACH_ENTRY(entry, &channel_hooks, struct channel_hook_entry, entry)
172 (*hook_count)++;
174 if (*hook_count)
175 *data = HeapAlloc(GetProcessHeap(), 0, *hook_count * sizeof(struct channel_hook_buffer_data));
176 else
177 *data = NULL;
179 LIST_FOR_EACH_ENTRY(entry, &channel_hooks, struct channel_hook_entry, entry)
181 ULONG extension_size = 0;
183 IChannelHook_ClientGetSize(entry->hook, &entry->id, &info->iid, &extension_size);
185 TRACE("%s: extension_size = %u\n", debugstr_guid(&entry->id), extension_size);
187 extension_size = (extension_size+7)&~7;
188 (*data)[hook_index].id = entry->id;
189 (*data)[hook_index].extension_size = extension_size;
191 /* an extension is only put onto the wire if it has data to write */
192 if (extension_size)
194 total_size += FIELD_OFFSET(WIRE_ORPC_EXTENT, data[extension_size]);
195 (*extension_count)++;
198 hook_index++;
201 LeaveCriticalSection(&csChannelHook);
203 return total_size;
206 static unsigned char * ChannelHooks_ClientFillBuffer(SChannelHookCallInfo *info,
207 unsigned char *buffer, struct channel_hook_buffer_data *data,
208 unsigned int hook_count)
210 struct channel_hook_entry *entry;
212 EnterCriticalSection(&csChannelHook);
214 LIST_FOR_EACH_ENTRY(entry, &channel_hooks, struct channel_hook_entry, entry)
216 unsigned int i;
217 ULONG extension_size = 0;
218 WIRE_ORPC_EXTENT *wire_orpc_extent = (WIRE_ORPC_EXTENT *)buffer;
220 for (i = 0; i < hook_count; i++)
221 if (IsEqualGUID(&entry->id, &data[i].id))
222 extension_size = data[i].extension_size;
224 /* an extension is only put onto the wire if it has data to write */
225 if (!extension_size)
226 continue;
228 IChannelHook_ClientFillBuffer(entry->hook, &entry->id, &info->iid,
229 &extension_size, buffer + FIELD_OFFSET(WIRE_ORPC_EXTENT, data[0]));
231 TRACE("%s: extension_size = %u\n", debugstr_guid(&entry->id), extension_size);
233 /* FIXME: set unused portion of wire_orpc_extent->data to 0? */
235 wire_orpc_extent->conformance = (extension_size+7)&~7;
236 wire_orpc_extent->size = extension_size;
237 memcpy(&wire_orpc_extent->id, &entry->id, sizeof(wire_orpc_extent->id));
238 buffer += FIELD_OFFSET(WIRE_ORPC_EXTENT, data[wire_orpc_extent->conformance]);
241 LeaveCriticalSection(&csChannelHook);
243 HeapFree(GetProcessHeap(), 0, data);
245 return buffer;
248 static void ChannelHooks_ServerNotify(SChannelHookCallInfo *info,
249 DWORD lDataRep, WIRE_ORPC_EXTENT *first_wire_orpc_extent,
250 ULONG extension_count)
252 struct channel_hook_entry *entry;
253 ULONG i;
255 EnterCriticalSection(&csChannelHook);
257 LIST_FOR_EACH_ENTRY(entry, &channel_hooks, struct channel_hook_entry, entry)
259 WIRE_ORPC_EXTENT *wire_orpc_extent;
260 for (i = 0, wire_orpc_extent = first_wire_orpc_extent;
261 i < extension_count;
262 i++, wire_orpc_extent = (WIRE_ORPC_EXTENT *)&wire_orpc_extent->data[wire_orpc_extent->conformance])
264 if (IsEqualGUID(&entry->id, &wire_orpc_extent->id))
265 break;
267 if (i == extension_count) wire_orpc_extent = NULL;
269 IChannelHook_ServerNotify(entry->hook, &entry->id, &info->iid,
270 wire_orpc_extent ? wire_orpc_extent->size : 0,
271 wire_orpc_extent ? wire_orpc_extent->data : NULL,
272 lDataRep);
275 LeaveCriticalSection(&csChannelHook);
278 static ULONG ChannelHooks_ServerGetSize(SChannelHookCallInfo *info,
279 struct channel_hook_buffer_data **data, unsigned int *hook_count,
280 ULONG *extension_count)
282 struct channel_hook_entry *entry;
283 ULONG total_size = 0;
284 unsigned int hook_index = 0;
286 *hook_count = 0;
287 *extension_count = 0;
289 EnterCriticalSection(&csChannelHook);
291 LIST_FOR_EACH_ENTRY(entry, &channel_hooks, struct channel_hook_entry, entry)
292 (*hook_count)++;
294 if (*hook_count)
295 *data = HeapAlloc(GetProcessHeap(), 0, *hook_count * sizeof(struct channel_hook_buffer_data));
296 else
297 *data = NULL;
299 LIST_FOR_EACH_ENTRY(entry, &channel_hooks, struct channel_hook_entry, entry)
301 ULONG extension_size = 0;
303 IChannelHook_ServerGetSize(entry->hook, &entry->id, &info->iid, S_OK,
304 &extension_size);
306 TRACE("%s: extension_size = %u\n", debugstr_guid(&entry->id), extension_size);
308 extension_size = (extension_size+7)&~7;
309 (*data)[hook_index].id = entry->id;
310 (*data)[hook_index].extension_size = extension_size;
312 /* an extension is only put onto the wire if it has data to write */
313 if (extension_size)
315 total_size += FIELD_OFFSET(WIRE_ORPC_EXTENT, data[extension_size]);
316 (*extension_count)++;
319 hook_index++;
322 LeaveCriticalSection(&csChannelHook);
324 return total_size;
327 static unsigned char * ChannelHooks_ServerFillBuffer(SChannelHookCallInfo *info,
328 unsigned char *buffer, struct channel_hook_buffer_data *data,
329 unsigned int hook_count)
331 struct channel_hook_entry *entry;
333 EnterCriticalSection(&csChannelHook);
335 LIST_FOR_EACH_ENTRY(entry, &channel_hooks, struct channel_hook_entry, entry)
337 unsigned int i;
338 ULONG extension_size = 0;
339 WIRE_ORPC_EXTENT *wire_orpc_extent = (WIRE_ORPC_EXTENT *)buffer;
341 for (i = 0; i < hook_count; i++)
342 if (IsEqualGUID(&entry->id, &data[i].id))
343 extension_size = data[i].extension_size;
345 /* an extension is only put onto the wire if it has data to write */
346 if (!extension_size)
347 continue;
349 IChannelHook_ServerFillBuffer(entry->hook, &entry->id, &info->iid,
350 &extension_size, buffer + FIELD_OFFSET(WIRE_ORPC_EXTENT, data[0]),
351 S_OK);
353 TRACE("%s: extension_size = %u\n", debugstr_guid(&entry->id), extension_size);
355 /* FIXME: set unused portion of wire_orpc_extent->data to 0? */
357 wire_orpc_extent->conformance = (extension_size+7)&~7;
358 wire_orpc_extent->size = extension_size;
359 memcpy(&wire_orpc_extent->id, &entry->id, sizeof(wire_orpc_extent->id));
360 buffer += FIELD_OFFSET(WIRE_ORPC_EXTENT, data[wire_orpc_extent->conformance]);
363 LeaveCriticalSection(&csChannelHook);
365 HeapFree(GetProcessHeap(), 0, data);
367 return buffer;
370 static void ChannelHooks_ClientNotify(SChannelHookCallInfo *info,
371 DWORD lDataRep, WIRE_ORPC_EXTENT *first_wire_orpc_extent,
372 ULONG extension_count, HRESULT hrFault)
374 struct channel_hook_entry *entry;
375 ULONG i;
377 EnterCriticalSection(&csChannelHook);
379 LIST_FOR_EACH_ENTRY(entry, &channel_hooks, struct channel_hook_entry, entry)
381 WIRE_ORPC_EXTENT *wire_orpc_extent;
382 for (i = 0, wire_orpc_extent = first_wire_orpc_extent;
383 i < extension_count;
384 i++, wire_orpc_extent = (WIRE_ORPC_EXTENT *)&wire_orpc_extent->data[wire_orpc_extent->conformance])
386 if (IsEqualGUID(&entry->id, &wire_orpc_extent->id))
387 break;
389 if (i == extension_count) wire_orpc_extent = NULL;
391 IChannelHook_ClientNotify(entry->hook, &entry->id, &info->iid,
392 wire_orpc_extent ? wire_orpc_extent->size : 0,
393 wire_orpc_extent ? wire_orpc_extent->data : NULL,
394 lDataRep, hrFault);
397 LeaveCriticalSection(&csChannelHook);
400 HRESULT RPC_RegisterChannelHook(REFGUID rguid, IChannelHook *hook)
402 struct channel_hook_entry *entry;
404 TRACE("(%s, %p)\n", debugstr_guid(rguid), hook);
406 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
407 if (!entry)
408 return E_OUTOFMEMORY;
410 memcpy(&entry->id, rguid, sizeof(entry->id));
411 entry->hook = hook;
412 IChannelHook_AddRef(hook);
414 EnterCriticalSection(&csChannelHook);
415 list_add_tail(&channel_hooks, &entry->entry);
416 LeaveCriticalSection(&csChannelHook);
418 return S_OK;
421 void RPC_UnregisterAllChannelHooks(void)
423 struct channel_hook_entry *cursor;
424 struct channel_hook_entry *cursor2;
426 EnterCriticalSection(&csChannelHook);
427 LIST_FOR_EACH_ENTRY_SAFE(cursor, cursor2, &channel_hooks, struct channel_hook_entry, entry)
428 HeapFree(GetProcessHeap(), 0, cursor);
429 LeaveCriticalSection(&csChannelHook);
432 /* RPC Channel Buffer Functions */
434 static HRESULT WINAPI RpcChannelBuffer_QueryInterface(LPRPCCHANNELBUFFER iface, REFIID riid, LPVOID *ppv)
436 *ppv = NULL;
437 if (IsEqualIID(riid,&IID_IRpcChannelBuffer) || IsEqualIID(riid,&IID_IUnknown))
439 *ppv = (LPVOID)iface;
440 IUnknown_AddRef(iface);
441 return S_OK;
443 return E_NOINTERFACE;
446 static ULONG WINAPI RpcChannelBuffer_AddRef(LPRPCCHANNELBUFFER iface)
448 RpcChannelBuffer *This = (RpcChannelBuffer *)iface;
449 return InterlockedIncrement(&This->refs);
452 static ULONG WINAPI ServerRpcChannelBuffer_Release(LPRPCCHANNELBUFFER iface)
454 RpcChannelBuffer *This = (RpcChannelBuffer *)iface;
455 ULONG ref;
457 ref = InterlockedDecrement(&This->refs);
458 if (ref)
459 return ref;
461 HeapFree(GetProcessHeap(), 0, This);
462 return 0;
465 static ULONG WINAPI ClientRpcChannelBuffer_Release(LPRPCCHANNELBUFFER iface)
467 ClientRpcChannelBuffer *This = (ClientRpcChannelBuffer *)iface;
468 ULONG ref;
470 ref = InterlockedDecrement(&This->super.refs);
471 if (ref)
472 return ref;
474 if (This->event) CloseHandle(This->event);
475 RpcBindingFree(&This->bind);
476 HeapFree(GetProcessHeap(), 0, This);
477 return 0;
480 static HRESULT WINAPI ServerRpcChannelBuffer_GetBuffer(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE* olemsg, REFIID riid)
482 RpcChannelBuffer *This = (RpcChannelBuffer *)iface;
483 RPC_MESSAGE *msg = (RPC_MESSAGE *)olemsg;
484 RPC_STATUS status;
485 ORPCTHAT *orpcthat;
486 struct message_state *message_state;
487 ULONG extensions_size;
488 struct channel_hook_buffer_data *channel_hook_data;
489 unsigned int channel_hook_count;
490 ULONG extension_count;
492 TRACE("(%p)->(%p,%s)\n", This, olemsg, debugstr_guid(riid));
494 message_state = (struct message_state *)msg->Handle;
495 /* restore the binding handle and the real start of data */
496 msg->Handle = message_state->binding_handle;
497 msg->Buffer = (char *)msg->Buffer - message_state->prefix_data_len;
499 extensions_size = ChannelHooks_ServerGetSize(&message_state->channel_hook_info,
500 &channel_hook_data, &channel_hook_count, &extension_count);
502 msg->BufferLength += FIELD_OFFSET(ORPCTHAT, extensions) + 4;
503 if (extensions_size)
505 msg->BufferLength += FIELD_OFFSET(ORPC_EXTENT_ARRAY, extent) + 2*sizeof(DWORD) + extensions_size;
506 if (extension_count & 1)
507 msg->BufferLength += FIELD_OFFSET(WIRE_ORPC_EXTENT, data[0]);
510 status = I_RpcGetBuffer(msg);
512 orpcthat = (ORPCTHAT *)msg->Buffer;
513 msg->Buffer = (char *)msg->Buffer + FIELD_OFFSET(ORPCTHAT, extensions);
515 orpcthat->flags = ORPCF_NULL /* FIXME? */;
517 /* NDR representation of orpcthat->extensions */
518 *(DWORD *)msg->Buffer = extensions_size ? 1 : 0;
519 msg->Buffer = (char *)msg->Buffer + sizeof(DWORD);
521 if (extensions_size)
523 ORPC_EXTENT_ARRAY *orpc_extent_array = msg->Buffer;
524 orpc_extent_array->size = extension_count;
525 orpc_extent_array->reserved = 0;
526 msg->Buffer = (char *)msg->Buffer + FIELD_OFFSET(ORPC_EXTENT_ARRAY, extent);
527 /* NDR representation of orpc_extent_array->extent */
528 *(DWORD *)msg->Buffer = 1;
529 msg->Buffer = (char *)msg->Buffer + sizeof(DWORD);
530 /* NDR representation of [size_is] attribute of orpc_extent_array->extent */
531 *(DWORD *)msg->Buffer = (extension_count + 1) & ~1;
532 msg->Buffer = (char *)msg->Buffer + sizeof(DWORD);
534 msg->Buffer = ChannelHooks_ServerFillBuffer(&message_state->channel_hook_info,
535 msg->Buffer, channel_hook_data, channel_hook_count);
537 /* we must add a dummy extension if there is an odd extension
538 * count to meet the contract specified by the size_is attribute */
539 if (extension_count & 1)
541 WIRE_ORPC_EXTENT *wire_orpc_extent = msg->Buffer;
542 wire_orpc_extent->conformance = 0;
543 memcpy(&wire_orpc_extent->id, &GUID_NULL, sizeof(wire_orpc_extent->id));
544 wire_orpc_extent->size = 0;
545 msg->Buffer = (char *)msg->Buffer + FIELD_OFFSET(WIRE_ORPC_EXTENT, data[0]);
549 /* store the prefixed data length so that we can restore the real buffer
550 * later */
551 message_state->prefix_data_len = (char *)msg->Buffer - (char *)orpcthat;
552 msg->BufferLength -= message_state->prefix_data_len;
553 /* save away the message state again */
554 msg->Handle = message_state;
556 TRACE("-- %ld\n", status);
558 return HRESULT_FROM_WIN32(status);
561 static HRESULT WINAPI ClientRpcChannelBuffer_GetBuffer(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE* olemsg, REFIID riid)
563 ClientRpcChannelBuffer *This = (ClientRpcChannelBuffer *)iface;
564 RPC_MESSAGE *msg = (RPC_MESSAGE *)olemsg;
565 RPC_CLIENT_INTERFACE *cif;
566 RPC_STATUS status;
567 ORPCTHIS *orpcthis;
568 struct message_state *message_state;
569 ULONG extensions_size;
570 struct channel_hook_buffer_data *channel_hook_data;
571 unsigned int channel_hook_count;
572 ULONG extension_count;
574 TRACE("(%p)->(%p,%s)\n", This, olemsg, debugstr_guid(riid));
576 cif = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(RPC_CLIENT_INTERFACE));
577 if (!cif)
578 return E_OUTOFMEMORY;
580 message_state = HeapAlloc(GetProcessHeap(), 0, sizeof(*message_state));
581 if (!message_state)
583 HeapFree(GetProcessHeap(), 0, cif);
584 return E_OUTOFMEMORY;
587 cif->Length = sizeof(RPC_CLIENT_INTERFACE);
588 /* RPC interface ID = COM interface ID */
589 cif->InterfaceId.SyntaxGUID = *riid;
590 /* COM objects always have a version of 0.0 */
591 cif->InterfaceId.SyntaxVersion.MajorVersion = 0;
592 cif->InterfaceId.SyntaxVersion.MinorVersion = 0;
593 msg->Handle = This->bind;
594 msg->RpcInterfaceInformation = cif;
596 message_state->channel_hook_info.iid = *riid;
597 message_state->channel_hook_info.cbSize = sizeof(message_state->channel_hook_info);
598 message_state->channel_hook_info.uCausality = COM_CurrentCausalityId();
599 message_state->channel_hook_info.dwServerPid = 0; /* FIXME */
600 message_state->channel_hook_info.iMethod = msg->ProcNum;
601 message_state->channel_hook_info.pObject = NULL; /* only present on server-side */
603 extensions_size = ChannelHooks_ClientGetSize(&message_state->channel_hook_info,
604 &channel_hook_data, &channel_hook_count, &extension_count);
606 msg->BufferLength += FIELD_OFFSET(ORPCTHIS, extensions) + 4;
607 if (extensions_size)
609 msg->BufferLength += FIELD_OFFSET(ORPC_EXTENT_ARRAY, extent) + 2*sizeof(DWORD) + extensions_size;
610 if (extension_count & 1)
611 msg->BufferLength += FIELD_OFFSET(WIRE_ORPC_EXTENT, data[0]);
614 status = I_RpcGetBuffer(msg);
616 message_state->prefix_data_len = 0;
617 message_state->binding_handle = This->bind;
618 msg->Handle = message_state;
620 if (status == RPC_S_OK)
622 orpcthis = (ORPCTHIS *)msg->Buffer;
623 msg->Buffer = (char *)msg->Buffer + FIELD_OFFSET(ORPCTHIS, extensions);
625 orpcthis->version.MajorVersion = COM_MAJOR_VERSION;
626 orpcthis->version.MinorVersion = COM_MINOR_VERSION;
627 orpcthis->flags = message_state->channel_hook_info.dwServerPid ? ORPCF_LOCAL : ORPCF_NULL;
628 orpcthis->reserved1 = 0;
629 orpcthis->cid = message_state->channel_hook_info.uCausality;
631 /* NDR representation of orpcthis->extensions */
632 *(DWORD *)msg->Buffer = extensions_size ? 1 : 0;
633 msg->Buffer = (char *)msg->Buffer + sizeof(DWORD);
635 if (extensions_size)
637 ORPC_EXTENT_ARRAY *orpc_extent_array = msg->Buffer;
638 orpc_extent_array->size = extension_count;
639 orpc_extent_array->reserved = 0;
640 msg->Buffer = (char *)msg->Buffer + FIELD_OFFSET(ORPC_EXTENT_ARRAY, extent);
641 /* NDR representation of orpc_extent_array->extent */
642 *(DWORD *)msg->Buffer = 1;
643 msg->Buffer = (char *)msg->Buffer + sizeof(DWORD);
644 /* NDR representation of [size_is] attribute of orpc_extent_array->extent */
645 *(DWORD *)msg->Buffer = (extension_count + 1) & ~1;
646 msg->Buffer = (char *)msg->Buffer + sizeof(DWORD);
648 msg->Buffer = ChannelHooks_ClientFillBuffer(&message_state->channel_hook_info,
649 msg->Buffer, channel_hook_data, channel_hook_count);
651 /* we must add a dummy extension if there is an odd extension
652 * count to meet the contract specified by the size_is attribute */
653 if (extension_count & 1)
655 WIRE_ORPC_EXTENT *wire_orpc_extent = msg->Buffer;
656 wire_orpc_extent->conformance = 0;
657 memcpy(&wire_orpc_extent->id, &GUID_NULL, sizeof(wire_orpc_extent->id));
658 wire_orpc_extent->size = 0;
659 msg->Buffer = (char *)msg->Buffer + FIELD_OFFSET(WIRE_ORPC_EXTENT, data[0]);
663 /* store the prefixed data length so that we can restore the real buffer
664 * pointer in ClientRpcChannelBuffer_SendReceive. */
665 message_state->prefix_data_len = (char *)msg->Buffer - (char *)orpcthis;
666 msg->BufferLength -= message_state->prefix_data_len;
669 TRACE("-- %ld\n", status);
671 return HRESULT_FROM_WIN32(status);
674 static HRESULT WINAPI ServerRpcChannelBuffer_SendReceive(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE *olemsg, ULONG *pstatus)
676 FIXME("stub\n");
677 return E_NOTIMPL;
680 static HANDLE ClientRpcChannelBuffer_GetEventHandle(ClientRpcChannelBuffer *This)
682 HANDLE event = InterlockedExchangePointer(&This->event, NULL);
684 /* Note: must be auto-reset event so we can reuse it without a call
685 * to ResetEvent */
686 if (!event) event = CreateEventW(NULL, FALSE, FALSE, NULL);
688 return event;
691 static void ClientRpcChannelBuffer_ReleaseEventHandle(ClientRpcChannelBuffer *This, HANDLE event)
693 if (InterlockedCompareExchangePointer(&This->event, event, NULL))
694 /* already a handle cached in This */
695 CloseHandle(event);
698 /* this thread runs an outgoing RPC */
699 static DWORD WINAPI rpc_sendreceive_thread(LPVOID param)
701 struct dispatch_params *data = (struct dispatch_params *) param;
703 /* Note: I_RpcSendReceive doesn't raise exceptions like the higher-level
704 * RPC functions do */
705 data->status = I_RpcSendReceive((RPC_MESSAGE *)data->msg);
707 TRACE("completed with status 0x%lx\n", data->status);
709 SetEvent(data->handle);
711 return 0;
714 static inline HRESULT ClientRpcChannelBuffer_IsCorrectApartment(ClientRpcChannelBuffer *This, APARTMENT *apt)
716 OXID oxid;
717 if (!apt)
718 return S_FALSE;
719 if (apartment_getoxid(apt, &oxid) != S_OK)
720 return S_FALSE;
721 if (This->oxid != oxid)
722 return S_FALSE;
723 return S_OK;
726 static HRESULT WINAPI ClientRpcChannelBuffer_SendReceive(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE *olemsg, ULONG *pstatus)
728 ClientRpcChannelBuffer *This = (ClientRpcChannelBuffer *)iface;
729 HRESULT hr;
730 RPC_MESSAGE *msg = (RPC_MESSAGE *)olemsg;
731 RPC_STATUS status;
732 DWORD index;
733 struct dispatch_params *params;
734 APARTMENT *apt = NULL;
735 IPID ipid;
736 struct message_state *message_state;
737 ORPCTHAT orpcthat;
738 ORPC_EXTENT_ARRAY orpc_ext_array;
739 WIRE_ORPC_EXTENT *first_wire_orpc_extent = NULL;
740 HRESULT hrFault = S_OK;
742 TRACE("(%p) iMethod=%d\n", olemsg, olemsg->iMethod);
744 hr = ClientRpcChannelBuffer_IsCorrectApartment(This, COM_CurrentApt());
745 if (hr != S_OK)
747 ERR("called from wrong apartment, should have been 0x%s\n",
748 wine_dbgstr_longlong(This->oxid));
749 return RPC_E_WRONG_THREAD;
751 /* this situation should be impossible in multi-threaded apartments,
752 * because the calling thread isn't re-entrable.
753 * Note: doing a COM call during the processing of a sent message is
754 * only disallowed if a client call is already being waited for
755 * completion */
756 if (!COM_CurrentApt()->multi_threaded &&
757 COM_CurrentInfo()->pending_call_count_client &&
758 InSendMessage())
760 ERR("can't make an outgoing COM call in response to a sent message\n");
761 return RPC_E_CANTCALLOUT_ININPUTSYNCCALL;
764 params = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*params));
765 if (!params) return E_OUTOFMEMORY;
767 message_state = (struct message_state *)msg->Handle;
768 /* restore the binding handle and the real start of data */
769 msg->Handle = message_state->binding_handle;
770 msg->Buffer = (char *)msg->Buffer - message_state->prefix_data_len;
771 msg->BufferLength += message_state->prefix_data_len;
773 params->msg = olemsg;
774 params->status = RPC_S_OK;
775 params->hr = S_OK;
777 /* Note: this is an optimization in the Microsoft OLE runtime that we need
778 * to copy, as shown by the test_no_couninitialize_client test. without
779 * short-circuiting the RPC runtime in the case below, the test will
780 * deadlock on the loader lock due to the RPC runtime needing to create
781 * a thread to process the RPC when this function is called indirectly
782 * from DllMain */
784 RpcBindingInqObject(message_state->binding_handle, &ipid);
785 hr = ipid_get_dispatch_params(&ipid, &apt, &params->stub, &params->chan,
786 &params->iid, &params->iface);
787 params->handle = ClientRpcChannelBuffer_GetEventHandle(This);
788 if ((hr == S_OK) && !apt->multi_threaded)
790 TRACE("Calling apartment thread 0x%08x...\n", apt->tid);
792 if (!PostMessageW(apartment_getwindow(apt), DM_EXECUTERPC, 0, (LPARAM)params))
794 ERR("PostMessage failed with error %u\n", GetLastError());
795 hr = HRESULT_FROM_WIN32(GetLastError());
798 else
800 if (hr == S_OK)
802 /* otherwise, we go via RPC runtime so the stub and channel aren't
803 * needed here */
804 IRpcStubBuffer_Release(params->stub);
805 params->stub = NULL;
806 IRpcChannelBuffer_Release(params->chan);
807 params->chan = NULL;
810 /* we use a separate thread here because we need to be able to
811 * pump the message loop in the application thread: if we do not,
812 * any windows created by this thread will hang and RPCs that try
813 * and re-enter this STA from an incoming server thread will
814 * deadlock. InstallShield is an example of that.
816 if (!QueueUserWorkItem(rpc_sendreceive_thread, params, WT_EXECUTEDEFAULT))
818 ERR("QueueUserWorkItem failed with error %u\n", GetLastError());
819 hr = E_UNEXPECTED;
821 else
822 hr = S_OK;
824 if (apt) apartment_release(apt);
826 if (hr == S_OK)
828 if (WaitForSingleObject(params->handle, 0))
830 COM_CurrentInfo()->pending_call_count_client++;
831 hr = CoWaitForMultipleHandles(0, INFINITE, 1, &params->handle, &index);
832 COM_CurrentInfo()->pending_call_count_client--;
835 ClientRpcChannelBuffer_ReleaseEventHandle(This, params->handle);
837 /* for WM shortcut, faults are returned in params->hr */
838 if (hr == S_OK)
839 hrFault = params->hr;
841 status = params->status;
842 HeapFree(GetProcessHeap(), 0, params);
843 params = NULL;
845 orpcthat.flags = ORPCF_NULL;
846 orpcthat.extensions = NULL;
848 /* for normal RPC calls, faults are returned in first 4 bytes of the
849 * buffer */
850 TRACE("RPC call status: 0x%lx\n", status);
851 if (status == RPC_S_CALL_FAILED)
852 hrFault = *(HRESULT *)olemsg->Buffer;
853 else if (status != RPC_S_OK)
854 hr = HRESULT_FROM_WIN32(status);
856 TRACE("hrFault = 0x%08x\n", hrFault);
858 /* FIXME: this condition should be
859 * "hr == S_OK && (!hrFault || msg->BufferLength > FIELD_OFFSET(ORPCTHAT, extensions) + 4)"
860 * but we don't currently reset the message length for PostMessage
861 * dispatched calls */
862 if (hr == S_OK && hrFault == S_OK)
864 HRESULT hr2;
865 char *original_buffer = msg->Buffer;
867 /* handle ORPCTHAT and client extensions */
869 hr2 = unmarshal_ORPCTHAT(msg, &orpcthat, &orpc_ext_array, &first_wire_orpc_extent);
870 if (FAILED(hr2))
871 hr = hr2;
873 message_state->prefix_data_len = (char *)msg->Buffer - original_buffer;
874 msg->BufferLength -= message_state->prefix_data_len;
876 else
877 message_state->prefix_data_len = 0;
879 if (hr == S_OK)
881 ChannelHooks_ClientNotify(&message_state->channel_hook_info,
882 msg->DataRepresentation,
883 first_wire_orpc_extent,
884 orpcthat.extensions && first_wire_orpc_extent ? orpcthat.extensions->size : 0,
885 hrFault);
888 /* save away the message state again */
889 msg->Handle = message_state;
891 if (pstatus) *pstatus = status;
893 if (hr == S_OK)
894 hr = hrFault;
896 TRACE("-- 0x%08x\n", hr);
898 return hr;
901 static HRESULT WINAPI ServerRpcChannelBuffer_FreeBuffer(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE* olemsg)
903 RPC_MESSAGE *msg = (RPC_MESSAGE *)olemsg;
904 RPC_STATUS status;
905 struct message_state *message_state;
907 TRACE("(%p)\n", msg);
909 message_state = (struct message_state *)msg->Handle;
910 /* restore the binding handle and the real start of data */
911 msg->Handle = message_state->binding_handle;
912 msg->Buffer = (char *)msg->Buffer - message_state->prefix_data_len;
913 msg->BufferLength += message_state->prefix_data_len;
914 message_state->prefix_data_len = 0;
916 status = I_RpcFreeBuffer(msg);
918 msg->Handle = message_state;
920 TRACE("-- %ld\n", status);
922 return HRESULT_FROM_WIN32(status);
925 static HRESULT WINAPI ClientRpcChannelBuffer_FreeBuffer(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE* olemsg)
927 RPC_MESSAGE *msg = (RPC_MESSAGE *)olemsg;
928 RPC_STATUS status;
929 struct message_state *message_state;
931 TRACE("(%p)\n", msg);
933 message_state = (struct message_state *)msg->Handle;
934 /* restore the binding handle and the real start of data */
935 msg->Handle = message_state->binding_handle;
936 msg->Buffer = (char *)msg->Buffer - message_state->prefix_data_len;
937 msg->BufferLength += message_state->prefix_data_len;
939 status = I_RpcFreeBuffer(msg);
941 HeapFree(GetProcessHeap(), 0, msg->RpcInterfaceInformation);
942 msg->RpcInterfaceInformation = NULL;
943 HeapFree(GetProcessHeap(), 0, message_state);
945 TRACE("-- %ld\n", status);
947 return HRESULT_FROM_WIN32(status);
950 static HRESULT WINAPI ClientRpcChannelBuffer_GetDestCtx(LPRPCCHANNELBUFFER iface, DWORD* pdwDestContext, void** ppvDestContext)
952 ClientRpcChannelBuffer *This = (ClientRpcChannelBuffer *)iface;
954 TRACE("(%p,%p)\n", pdwDestContext, ppvDestContext);
956 *pdwDestContext = This->dest_context;
957 *ppvDestContext = This->dest_context_data;
959 return S_OK;
962 static HRESULT WINAPI ServerRpcChannelBuffer_GetDestCtx(LPRPCCHANNELBUFFER iface, DWORD* pdwDestContext, void** ppvDestContext)
964 FIXME("(%p,%p), stub!\n", pdwDestContext, ppvDestContext);
965 return E_FAIL;
968 static HRESULT WINAPI RpcChannelBuffer_IsConnected(LPRPCCHANNELBUFFER iface)
970 TRACE("()\n");
971 /* native does nothing too */
972 return S_OK;
975 static const IRpcChannelBufferVtbl ClientRpcChannelBufferVtbl =
977 RpcChannelBuffer_QueryInterface,
978 RpcChannelBuffer_AddRef,
979 ClientRpcChannelBuffer_Release,
980 ClientRpcChannelBuffer_GetBuffer,
981 ClientRpcChannelBuffer_SendReceive,
982 ClientRpcChannelBuffer_FreeBuffer,
983 ClientRpcChannelBuffer_GetDestCtx,
984 RpcChannelBuffer_IsConnected
987 static const IRpcChannelBufferVtbl ServerRpcChannelBufferVtbl =
989 RpcChannelBuffer_QueryInterface,
990 RpcChannelBuffer_AddRef,
991 ServerRpcChannelBuffer_Release,
992 ServerRpcChannelBuffer_GetBuffer,
993 ServerRpcChannelBuffer_SendReceive,
994 ServerRpcChannelBuffer_FreeBuffer,
995 ServerRpcChannelBuffer_GetDestCtx,
996 RpcChannelBuffer_IsConnected
999 /* returns a channel buffer for proxies */
1000 HRESULT RPC_CreateClientChannel(const OXID *oxid, const IPID *ipid,
1001 DWORD dest_context, void *dest_context_data,
1002 IRpcChannelBuffer **chan)
1004 ClientRpcChannelBuffer *This;
1005 WCHAR endpoint[200];
1006 RPC_BINDING_HANDLE bind;
1007 RPC_STATUS status;
1008 LPWSTR string_binding;
1010 /* connect to the apartment listener thread */
1011 get_rpc_endpoint(endpoint, oxid);
1013 TRACE("proxy pipe: connecting to endpoint: %s\n", debugstr_w(endpoint));
1015 status = RpcStringBindingComposeW(
1016 NULL,
1017 wszRpcTransport,
1018 NULL,
1019 endpoint,
1020 NULL,
1021 &string_binding);
1023 if (status == RPC_S_OK)
1025 status = RpcBindingFromStringBindingW(string_binding, &bind);
1027 if (status == RPC_S_OK)
1029 IPID ipid2 = *ipid; /* why can't RpcBindingSetObject take a const? */
1030 status = RpcBindingSetObject(bind, &ipid2);
1031 if (status != RPC_S_OK)
1032 RpcBindingFree(&bind);
1035 RpcStringFreeW(&string_binding);
1038 if (status != RPC_S_OK)
1040 ERR("Couldn't get binding for endpoint %s, status = %ld\n", debugstr_w(endpoint), status);
1041 return HRESULT_FROM_WIN32(status);
1044 This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
1045 if (!This)
1047 RpcBindingFree(&bind);
1048 return E_OUTOFMEMORY;
1051 This->super.lpVtbl = &ClientRpcChannelBufferVtbl;
1052 This->super.refs = 1;
1053 This->bind = bind;
1054 apartment_getoxid(COM_CurrentApt(), &This->oxid);
1055 This->dest_context = dest_context;
1056 This->dest_context_data = dest_context_data;
1057 This->event = NULL;
1059 *chan = (IRpcChannelBuffer*)This;
1061 return S_OK;
1064 HRESULT RPC_CreateServerChannel(IRpcChannelBuffer **chan)
1066 RpcChannelBuffer *This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
1067 if (!This)
1068 return E_OUTOFMEMORY;
1070 This->lpVtbl = &ServerRpcChannelBufferVtbl;
1071 This->refs = 1;
1073 *chan = (IRpcChannelBuffer*)This;
1075 return S_OK;
1078 /* unmarshals ORPC_EXTENT_ARRAY according to NDR rules, but doesn't allocate
1079 * any memory */
1080 static HRESULT unmarshal_ORPC_EXTENT_ARRAY(RPC_MESSAGE *msg, const char *end,
1081 ORPC_EXTENT_ARRAY *extensions,
1082 WIRE_ORPC_EXTENT **first_wire_orpc_extent)
1084 DWORD pointer_id;
1085 DWORD i;
1087 memcpy(extensions, msg->Buffer, FIELD_OFFSET(ORPC_EXTENT_ARRAY, extent));
1088 msg->Buffer = (char *)msg->Buffer + FIELD_OFFSET(ORPC_EXTENT_ARRAY, extent);
1090 if ((const char *)msg->Buffer + 2 * sizeof(DWORD) > end)
1091 return RPC_E_INVALID_HEADER;
1093 pointer_id = *(DWORD *)msg->Buffer;
1094 msg->Buffer = (char *)msg->Buffer + sizeof(DWORD);
1095 extensions->extent = NULL;
1097 if (pointer_id)
1099 WIRE_ORPC_EXTENT *wire_orpc_extent;
1101 /* conformance */
1102 if (*(DWORD *)msg->Buffer != ((extensions->size+1)&~1))
1103 return RPC_S_INVALID_BOUND;
1105 msg->Buffer = (char *)msg->Buffer + sizeof(DWORD);
1107 /* arbritary limit for security (don't know what native does) */
1108 if (extensions->size > 256)
1110 ERR("too many extensions: %ld\n", extensions->size);
1111 return RPC_S_INVALID_BOUND;
1114 *first_wire_orpc_extent = wire_orpc_extent = (WIRE_ORPC_EXTENT *)msg->Buffer;
1115 for (i = 0; i < ((extensions->size+1)&~1); i++)
1117 if ((const char *)&wire_orpc_extent->data[0] > end)
1118 return RPC_S_INVALID_BOUND;
1119 if (wire_orpc_extent->conformance != ((wire_orpc_extent->size+7)&~7))
1120 return RPC_S_INVALID_BOUND;
1121 if ((const char *)&wire_orpc_extent->data[wire_orpc_extent->conformance] > end)
1122 return RPC_S_INVALID_BOUND;
1123 TRACE("size %u, guid %s\n", wire_orpc_extent->size, debugstr_guid(&wire_orpc_extent->id));
1124 wire_orpc_extent = (WIRE_ORPC_EXTENT *)&wire_orpc_extent->data[wire_orpc_extent->conformance];
1126 msg->Buffer = wire_orpc_extent;
1129 return S_OK;
1132 /* unmarshals ORPCTHIS according to NDR rules, but doesn't allocate any memory */
1133 static HRESULT unmarshal_ORPCTHIS(RPC_MESSAGE *msg, ORPCTHIS *orpcthis,
1134 ORPC_EXTENT_ARRAY *orpc_ext_array, WIRE_ORPC_EXTENT **first_wire_orpc_extent)
1136 const char *end = (char *)msg->Buffer + msg->BufferLength;
1138 *first_wire_orpc_extent = NULL;
1140 if (msg->BufferLength < FIELD_OFFSET(ORPCTHIS, extensions) + 4)
1142 ERR("invalid buffer length\n");
1143 return RPC_E_INVALID_HEADER;
1146 memcpy(orpcthis, msg->Buffer, FIELD_OFFSET(ORPCTHIS, extensions));
1147 msg->Buffer = (char *)msg->Buffer + FIELD_OFFSET(ORPCTHIS, extensions);
1149 if ((const char *)msg->Buffer + sizeof(DWORD) > end)
1150 return RPC_E_INVALID_HEADER;
1152 if (*(DWORD *)msg->Buffer)
1153 orpcthis->extensions = orpc_ext_array;
1154 else
1155 orpcthis->extensions = NULL;
1157 msg->Buffer = (char *)msg->Buffer + sizeof(DWORD);
1159 if (orpcthis->extensions)
1161 HRESULT hr = unmarshal_ORPC_EXTENT_ARRAY(msg, end, orpc_ext_array,
1162 first_wire_orpc_extent);
1163 if (FAILED(hr))
1164 return hr;
1167 if ((orpcthis->version.MajorVersion != COM_MAJOR_VERSION) ||
1168 (orpcthis->version.MinorVersion > COM_MINOR_VERSION))
1170 ERR("COM version {%d, %d} not supported\n",
1171 orpcthis->version.MajorVersion, orpcthis->version.MinorVersion);
1172 return RPC_E_VERSION_MISMATCH;
1175 if (orpcthis->flags & ~(ORPCF_LOCAL|ORPCF_RESERVED1|ORPCF_RESERVED2|ORPCF_RESERVED3|ORPCF_RESERVED4))
1177 ERR("invalid flags 0x%lx\n", orpcthis->flags & ~(ORPCF_LOCAL|ORPCF_RESERVED1|ORPCF_RESERVED2|ORPCF_RESERVED3|ORPCF_RESERVED4));
1178 return RPC_E_INVALID_HEADER;
1181 return S_OK;
1184 static HRESULT unmarshal_ORPCTHAT(RPC_MESSAGE *msg, ORPCTHAT *orpcthat,
1185 ORPC_EXTENT_ARRAY *orpc_ext_array, WIRE_ORPC_EXTENT **first_wire_orpc_extent)
1187 const char *end = (char *)msg->Buffer + msg->BufferLength;
1189 *first_wire_orpc_extent = NULL;
1191 if (msg->BufferLength < FIELD_OFFSET(ORPCTHAT, extensions) + 4)
1193 ERR("invalid buffer length\n");
1194 return RPC_E_INVALID_HEADER;
1197 memcpy(orpcthat, msg->Buffer, FIELD_OFFSET(ORPCTHAT, extensions));
1198 msg->Buffer = (char *)msg->Buffer + FIELD_OFFSET(ORPCTHAT, extensions);
1200 if ((const char *)msg->Buffer + sizeof(DWORD) > end)
1201 return RPC_E_INVALID_HEADER;
1203 if (*(DWORD *)msg->Buffer)
1204 orpcthat->extensions = orpc_ext_array;
1205 else
1206 orpcthat->extensions = NULL;
1208 msg->Buffer = (char *)msg->Buffer + sizeof(DWORD);
1210 if (orpcthat->extensions)
1212 HRESULT hr = unmarshal_ORPC_EXTENT_ARRAY(msg, end, orpc_ext_array,
1213 first_wire_orpc_extent);
1214 if (FAILED(hr))
1215 return hr;
1218 if (orpcthat->flags & ~(ORPCF_LOCAL|ORPCF_RESERVED1|ORPCF_RESERVED2|ORPCF_RESERVED3|ORPCF_RESERVED4))
1220 ERR("invalid flags 0x%lx\n", orpcthat->flags & ~(ORPCF_LOCAL|ORPCF_RESERVED1|ORPCF_RESERVED2|ORPCF_RESERVED3|ORPCF_RESERVED4));
1221 return RPC_E_INVALID_HEADER;
1224 return S_OK;
1227 void RPC_ExecuteCall(struct dispatch_params *params)
1229 struct message_state *message_state = NULL;
1230 RPC_MESSAGE *msg = (RPC_MESSAGE *)params->msg;
1231 char *original_buffer = msg->Buffer;
1232 ORPCTHIS orpcthis;
1233 ORPC_EXTENT_ARRAY orpc_ext_array;
1234 WIRE_ORPC_EXTENT *first_wire_orpc_extent;
1235 GUID old_causality_id;
1237 /* handle ORPCTHIS and server extensions */
1239 params->hr = unmarshal_ORPCTHIS(msg, &orpcthis, &orpc_ext_array, &first_wire_orpc_extent);
1240 if (params->hr != S_OK)
1242 msg->Buffer = original_buffer;
1243 goto exit;
1246 message_state = HeapAlloc(GetProcessHeap(), 0, sizeof(*message_state));
1247 if (!message_state)
1249 params->hr = E_OUTOFMEMORY;
1250 msg->Buffer = original_buffer;
1251 goto exit;
1254 message_state->prefix_data_len = (char *)msg->Buffer - original_buffer;
1255 message_state->binding_handle = msg->Handle;
1257 message_state->channel_hook_info.iid = params->iid;
1258 message_state->channel_hook_info.cbSize = sizeof(message_state->channel_hook_info);
1259 message_state->channel_hook_info.uCausality = orpcthis.cid;
1260 message_state->channel_hook_info.dwServerPid = GetCurrentProcessId();
1261 message_state->channel_hook_info.iMethod = msg->ProcNum;
1262 message_state->channel_hook_info.pObject = params->iface;
1264 if (orpcthis.extensions && first_wire_orpc_extent &&
1265 orpcthis.extensions->size)
1266 ChannelHooks_ServerNotify(&message_state->channel_hook_info, msg->DataRepresentation, first_wire_orpc_extent, orpcthis.extensions->size);
1268 msg->Handle = message_state;
1269 msg->BufferLength -= message_state->prefix_data_len;
1271 /* call message filter */
1273 if (COM_CurrentApt()->filter)
1275 DWORD handlecall;
1276 INTERFACEINFO interface_info;
1277 CALLTYPE calltype;
1279 interface_info.pUnk = params->iface;
1280 interface_info.iid = params->iid;
1281 interface_info.wMethod = msg->ProcNum;
1283 if (IsEqualGUID(&orpcthis.cid, &COM_CurrentInfo()->causality_id))
1284 calltype = CALLTYPE_NESTED;
1285 else if (COM_CurrentInfo()->pending_call_count_server == 0)
1286 calltype = CALLTYPE_TOPLEVEL;
1287 else
1288 calltype = CALLTYPE_TOPLEVEL_CALLPENDING;
1290 handlecall = IMessageFilter_HandleInComingCall(COM_CurrentApt()->filter,
1291 calltype,
1292 (HTASK)GetCurrentProcessId(),
1293 0 /* FIXME */,
1294 &interface_info);
1295 TRACE("IMessageFilter_HandleInComingCall returned %d\n", handlecall);
1296 switch (handlecall)
1298 case SERVERCALL_REJECTED:
1299 params->hr = RPC_E_CALL_REJECTED;
1300 goto exit_reset_state;
1301 case SERVERCALL_RETRYLATER:
1302 #if 0 /* FIXME: handle retries on the client side before enabling this code */
1303 params->hr = RPC_E_RETRY;
1304 goto exit_reset_state;
1305 #else
1306 FIXME("retry call later not implemented\n");
1307 break;
1308 #endif
1309 case SERVERCALL_ISHANDLED:
1310 default:
1311 break;
1315 /* invoke the method */
1317 /* save the old causality ID - note: any calls executed while processing
1318 * messages received during the SendReceive will appear to originate from
1319 * this call - this should be checked with what Windows does */
1320 old_causality_id = COM_CurrentInfo()->causality_id;
1321 COM_CurrentInfo()->causality_id = orpcthis.cid;
1322 COM_CurrentInfo()->pending_call_count_server++;
1323 params->hr = IRpcStubBuffer_Invoke(params->stub, params->msg, params->chan);
1324 COM_CurrentInfo()->pending_call_count_server--;
1325 COM_CurrentInfo()->causality_id = old_causality_id;
1327 exit_reset_state:
1328 message_state = (struct message_state *)msg->Handle;
1329 msg->Handle = message_state->binding_handle;
1330 msg->Buffer = (char *)msg->Buffer - message_state->prefix_data_len;
1331 msg->BufferLength += message_state->prefix_data_len;
1333 exit:
1334 HeapFree(GetProcessHeap(), 0, message_state);
1335 IRpcStubBuffer_Release(params->stub);
1336 IRpcChannelBuffer_Release(params->chan);
1337 if (params->handle) SetEvent(params->handle);
1340 static void __RPC_STUB dispatch_rpc(RPC_MESSAGE *msg)
1342 struct dispatch_params *params;
1343 APARTMENT *apt;
1344 IPID ipid;
1345 HRESULT hr;
1347 RpcBindingInqObject(msg->Handle, &ipid);
1349 TRACE("ipid = %s, iMethod = %d\n", debugstr_guid(&ipid), msg->ProcNum);
1351 params = HeapAlloc(GetProcessHeap(), 0, sizeof(*params));
1352 if (!params) return RpcRaiseException(E_OUTOFMEMORY);
1354 hr = ipid_get_dispatch_params(&ipid, &apt, &params->stub, &params->chan,
1355 &params->iid, &params->iface);
1356 if (hr != S_OK)
1358 ERR("no apartment found for ipid %s\n", debugstr_guid(&ipid));
1359 HeapFree(GetProcessHeap(), 0, params);
1360 return RpcRaiseException(hr);
1363 params->msg = (RPCOLEMESSAGE *)msg;
1364 params->status = RPC_S_OK;
1365 params->hr = S_OK;
1366 params->handle = NULL;
1368 /* Note: this is the important difference between STAs and MTAs - we
1369 * always execute RPCs to STAs in the thread that originally created the
1370 * apartment (i.e. the one that pumps messages to the window) */
1371 if (!apt->multi_threaded)
1373 params->handle = CreateEventW(NULL, FALSE, FALSE, NULL);
1375 TRACE("Calling apartment thread 0x%08x...\n", apt->tid);
1377 if (PostMessageW(apartment_getwindow(apt), DM_EXECUTERPC, 0, (LPARAM)params))
1378 WaitForSingleObject(params->handle, INFINITE);
1379 else
1381 ERR("PostMessage failed with error %u\n", GetLastError());
1382 IRpcChannelBuffer_Release(params->chan);
1383 IRpcStubBuffer_Release(params->stub);
1385 CloseHandle(params->handle);
1387 else
1389 BOOL joined = FALSE;
1390 if (!COM_CurrentInfo()->apt)
1392 apartment_joinmta();
1393 joined = TRUE;
1395 RPC_ExecuteCall(params);
1396 if (joined)
1398 apartment_release(COM_CurrentInfo()->apt);
1399 COM_CurrentInfo()->apt = NULL;
1403 hr = params->hr;
1404 HeapFree(GetProcessHeap(), 0, params);
1406 apartment_release(apt);
1408 /* if IRpcStubBuffer_Invoke fails, we should raise an exception to tell
1409 * the RPC runtime that the call failed */
1410 if (hr) RpcRaiseException(hr);
1413 /* stub registration */
1414 HRESULT RPC_RegisterInterface(REFIID riid)
1416 struct registered_if *rif;
1417 BOOL found = FALSE;
1418 HRESULT hr = S_OK;
1420 TRACE("(%s)\n", debugstr_guid(riid));
1422 EnterCriticalSection(&csRegIf);
1423 LIST_FOR_EACH_ENTRY(rif, &registered_interfaces, struct registered_if, entry)
1425 if (IsEqualGUID(&rif->If.InterfaceId.SyntaxGUID, riid))
1427 rif->refs++;
1428 found = TRUE;
1429 break;
1432 if (!found)
1434 TRACE("Creating new interface\n");
1436 rif = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*rif));
1437 if (rif)
1439 RPC_STATUS status;
1441 rif->refs = 1;
1442 rif->If.Length = sizeof(RPC_SERVER_INTERFACE);
1443 /* RPC interface ID = COM interface ID */
1444 rif->If.InterfaceId.SyntaxGUID = *riid;
1445 rif->If.DispatchTable = &rpc_dispatch;
1446 /* all other fields are 0, including the version asCOM objects
1447 * always have a version of 0.0 */
1448 status = RpcServerRegisterIfEx(
1449 (RPC_IF_HANDLE)&rif->If,
1450 NULL, NULL,
1451 RPC_IF_OLE | RPC_IF_AUTOLISTEN,
1452 RPC_C_LISTEN_MAX_CALLS_DEFAULT,
1453 NULL);
1454 if (status == RPC_S_OK)
1455 list_add_tail(&registered_interfaces, &rif->entry);
1456 else
1458 ERR("RpcServerRegisterIfEx failed with error %ld\n", status);
1459 HeapFree(GetProcessHeap(), 0, rif);
1460 hr = HRESULT_FROM_WIN32(status);
1463 else
1464 hr = E_OUTOFMEMORY;
1466 LeaveCriticalSection(&csRegIf);
1467 return hr;
1470 /* stub unregistration */
1471 void RPC_UnregisterInterface(REFIID riid)
1473 struct registered_if *rif;
1474 EnterCriticalSection(&csRegIf);
1475 LIST_FOR_EACH_ENTRY(rif, &registered_interfaces, struct registered_if, entry)
1477 if (IsEqualGUID(&rif->If.InterfaceId.SyntaxGUID, riid))
1479 if (!--rif->refs)
1481 RpcServerUnregisterIf((RPC_IF_HANDLE)&rif->If, NULL, TRUE);
1482 list_remove(&rif->entry);
1483 HeapFree(GetProcessHeap(), 0, rif);
1485 break;
1488 LeaveCriticalSection(&csRegIf);
1491 /* make the apartment reachable by other threads and processes and create the
1492 * IRemUnknown object */
1493 void RPC_StartRemoting(struct apartment *apt)
1495 if (!InterlockedExchange(&apt->remoting_started, TRUE))
1497 WCHAR endpoint[200];
1498 RPC_STATUS status;
1500 get_rpc_endpoint(endpoint, &apt->oxid);
1502 status = RpcServerUseProtseqEpW(
1503 wszRpcTransport,
1504 RPC_C_PROTSEQ_MAX_REQS_DEFAULT,
1505 endpoint,
1506 NULL);
1507 if (status != RPC_S_OK)
1508 ERR("Couldn't register endpoint %s\n", debugstr_w(endpoint));
1510 /* FIXME: move remote unknown exporting into this function */
1512 start_apartment_remote_unknown();
1516 static HRESULT create_server(REFCLSID rclsid)
1518 static const WCHAR wszLocalServer32[] = { 'L','o','c','a','l','S','e','r','v','e','r','3','2',0 };
1519 static const WCHAR embedding[] = { ' ', '-','E','m','b','e','d','d','i','n','g',0 };
1520 HKEY key;
1521 HRESULT hres;
1522 WCHAR command[MAX_PATH+sizeof(embedding)/sizeof(WCHAR)];
1523 DWORD size = (MAX_PATH+1) * sizeof(WCHAR);
1524 STARTUPINFOW sinfo;
1525 PROCESS_INFORMATION pinfo;
1527 hres = COM_OpenKeyForCLSID(rclsid, wszLocalServer32, KEY_READ, &key);
1528 if (FAILED(hres)) {
1529 ERR("class %s not registered\n", debugstr_guid(rclsid));
1530 return hres;
1533 hres = RegQueryValueExW(key, NULL, NULL, NULL, (LPBYTE)command, &size);
1534 RegCloseKey(key);
1535 if (hres) {
1536 WARN("No default value for LocalServer32 key\n");
1537 return REGDB_E_CLASSNOTREG; /* FIXME: check retval */
1540 memset(&sinfo,0,sizeof(sinfo));
1541 sinfo.cb = sizeof(sinfo);
1543 /* EXE servers are started with the -Embedding switch. */
1545 strcatW(command, embedding);
1547 TRACE("activating local server %s for %s\n", debugstr_w(command), debugstr_guid(rclsid));
1549 /* FIXME: Win2003 supports a ServerExecutable value that is passed into
1550 * CreateProcess */
1551 if (!CreateProcessW(NULL, command, NULL, NULL, FALSE, 0, NULL, NULL, &sinfo, &pinfo)) {
1552 WARN("failed to run local server %s\n", debugstr_w(command));
1553 return HRESULT_FROM_WIN32(GetLastError());
1555 CloseHandle(pinfo.hProcess);
1556 CloseHandle(pinfo.hThread);
1558 return S_OK;
1562 * start_local_service() - start a service given its name and parameters
1564 static DWORD start_local_service(LPCWSTR name, DWORD num, LPCWSTR *params)
1566 SC_HANDLE handle, hsvc;
1567 DWORD r = ERROR_FUNCTION_FAILED;
1569 TRACE("Starting service %s %d params\n", debugstr_w(name), num);
1571 handle = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
1572 if (!handle)
1573 return r;
1574 hsvc = OpenServiceW(handle, name, SERVICE_START);
1575 if (hsvc)
1577 if(StartServiceW(hsvc, num, params))
1578 r = ERROR_SUCCESS;
1579 else
1580 r = GetLastError();
1581 if (r == ERROR_SERVICE_ALREADY_RUNNING)
1582 r = ERROR_SUCCESS;
1583 CloseServiceHandle(hsvc);
1585 else
1586 r = GetLastError();
1587 CloseServiceHandle(handle);
1589 TRACE("StartService returned error %u (%s)\n", r, (r == ERROR_SUCCESS) ? "ok":"failed");
1591 return r;
1595 * create_local_service() - start a COM server in a service
1597 * To start a Local Service, we read the AppID value under
1598 * the class's CLSID key, then open the HKCR\\AppId key specified
1599 * there and check for a LocalService value.
1601 * Note: Local Services are not supported under Windows 9x
1603 static HRESULT create_local_service(REFCLSID rclsid)
1605 HRESULT hres;
1606 WCHAR buf[CHARS_IN_GUID];
1607 static const WCHAR szLocalService[] = { 'L','o','c','a','l','S','e','r','v','i','c','e',0 };
1608 static const WCHAR szServiceParams[] = {'S','e','r','v','i','c','e','P','a','r','a','m','s',0};
1609 HKEY hkey;
1610 LONG r;
1611 DWORD type, sz;
1613 TRACE("Attempting to start Local service for %s\n", debugstr_guid(rclsid));
1615 hres = COM_OpenKeyForAppIdFromCLSID(rclsid, KEY_READ, &hkey);
1616 if (FAILED(hres))
1617 return hres;
1619 /* read the LocalService and ServiceParameters values from the AppID key */
1620 sz = sizeof buf;
1621 r = RegQueryValueExW(hkey, szLocalService, NULL, &type, (LPBYTE)buf, &sz);
1622 if (r==ERROR_SUCCESS && type==REG_SZ)
1624 DWORD num_args = 0;
1625 LPWSTR args[1] = { NULL };
1628 * FIXME: I'm not really sure how to deal with the service parameters.
1629 * I suspect that the string returned from RegQueryValueExW
1630 * should be split into a number of arguments by spaces.
1631 * It would make more sense if ServiceParams contained a
1632 * REG_MULTI_SZ here, but it's a REG_SZ for the services
1633 * that I'm interested in for the moment.
1635 r = RegQueryValueExW(hkey, szServiceParams, NULL, &type, NULL, &sz);
1636 if (r == ERROR_SUCCESS && type == REG_SZ && sz)
1638 args[0] = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sz);
1639 num_args++;
1640 RegQueryValueExW(hkey, szServiceParams, NULL, &type, (LPBYTE)args[0], &sz);
1642 r = start_local_service(buf, num_args, (LPCWSTR *)args);
1643 if (r != ERROR_SUCCESS)
1644 hres = REGDB_E_CLASSNOTREG; /* FIXME: check retval */
1645 HeapFree(GetProcessHeap(),0,args[0]);
1647 else
1649 WARN("No LocalService value\n");
1650 hres = REGDB_E_CLASSNOTREG; /* FIXME: check retval */
1652 RegCloseKey(hkey);
1654 return hres;
1658 static void get_localserver_pipe_name(WCHAR *pipefn, REFCLSID rclsid)
1660 static const WCHAR wszPipeRef[] = {'\\','\\','.','\\','p','i','p','e','\\',0};
1661 strcpyW(pipefn, wszPipeRef);
1662 StringFromGUID2(rclsid, pipefn + sizeof(wszPipeRef)/sizeof(wszPipeRef[0]) - 1, CHARS_IN_GUID);
1665 /* FIXME: should call to rpcss instead */
1666 HRESULT RPC_GetLocalClassObject(REFCLSID rclsid, REFIID iid, LPVOID *ppv)
1668 HRESULT hres;
1669 HANDLE hPipe;
1670 WCHAR pipefn[100];
1671 DWORD res, bufferlen;
1672 char marshalbuffer[200];
1673 IStream *pStm;
1674 LARGE_INTEGER seekto;
1675 ULARGE_INTEGER newpos;
1676 int tries = 0;
1678 static const int MAXTRIES = 30; /* 30 seconds */
1680 TRACE("rclsid=%s, iid=%s\n", debugstr_guid(rclsid), debugstr_guid(iid));
1682 get_localserver_pipe_name(pipefn, rclsid);
1684 while (tries++ < MAXTRIES) {
1685 TRACE("waiting for %s\n", debugstr_w(pipefn));
1687 WaitNamedPipeW( pipefn, NMPWAIT_WAIT_FOREVER );
1688 hPipe = CreateFileW(pipefn, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
1689 if (hPipe == INVALID_HANDLE_VALUE) {
1690 if (tries == 1) {
1691 if ( (hres = create_local_service(rclsid)) &&
1692 (hres = create_server(rclsid)) )
1693 return hres;
1694 Sleep(1000);
1695 } else {
1696 WARN("Connecting to %s, no response yet, retrying: le is %u\n", debugstr_w(pipefn), GetLastError());
1697 Sleep(1000);
1699 continue;
1701 bufferlen = 0;
1702 if (!ReadFile(hPipe,marshalbuffer,sizeof(marshalbuffer),&bufferlen,NULL)) {
1703 FIXME("Failed to read marshal id from classfactory of %s.\n",debugstr_guid(rclsid));
1704 Sleep(1000);
1705 continue;
1707 TRACE("read marshal id from pipe\n");
1708 CloseHandle(hPipe);
1709 break;
1712 if (tries >= MAXTRIES)
1713 return E_NOINTERFACE;
1715 hres = CreateStreamOnHGlobal(0,TRUE,&pStm);
1716 if (hres) return hres;
1717 hres = IStream_Write(pStm,marshalbuffer,bufferlen,&res);
1718 if (hres) goto out;
1719 seekto.u.LowPart = 0;seekto.u.HighPart = 0;
1720 hres = IStream_Seek(pStm,seekto,SEEK_SET,&newpos);
1722 TRACE("unmarshalling classfactory\n");
1723 hres = CoUnmarshalInterface(pStm,&IID_IClassFactory,ppv);
1724 out:
1725 IStream_Release(pStm);
1726 return hres;
1730 struct local_server_params
1732 CLSID clsid;
1733 IStream *stream;
1734 HANDLE ready_event;
1737 /* FIXME: should call to rpcss instead */
1738 static DWORD WINAPI local_server_thread(LPVOID param)
1740 struct local_server_params * lsp = (struct local_server_params *)param;
1741 HANDLE hPipe;
1742 WCHAR pipefn[100];
1743 HRESULT hres;
1744 IStream *pStm = lsp->stream;
1745 STATSTG ststg;
1746 unsigned char *buffer;
1747 int buflen;
1748 LARGE_INTEGER seekto;
1749 ULARGE_INTEGER newpos;
1750 ULONG res;
1752 TRACE("Starting threader for %s.\n",debugstr_guid(&lsp->clsid));
1754 get_localserver_pipe_name(pipefn, &lsp->clsid);
1756 hPipe = CreateNamedPipeW( pipefn, PIPE_ACCESS_DUPLEX,
1757 PIPE_TYPE_BYTE|PIPE_WAIT, PIPE_UNLIMITED_INSTANCES,
1758 4096, 4096, 500 /* 0.5 second timeout */, NULL );
1760 SetEvent(lsp->ready_event);
1762 HeapFree(GetProcessHeap(), 0, lsp);
1764 if (hPipe == INVALID_HANDLE_VALUE)
1766 FIXME("pipe creation failed for %s, le is %u\n", debugstr_w(pipefn), GetLastError());
1767 return 1;
1770 while (1) {
1771 if (!ConnectNamedPipe(hPipe,NULL) && GetLastError() != ERROR_PIPE_CONNECTED) {
1772 ERR("Failure during ConnectNamedPipe %u, ABORT!\n",GetLastError());
1773 break;
1776 TRACE("marshalling IClassFactory to client\n");
1778 hres = IStream_Stat(pStm,&ststg,0);
1779 if (hres) return hres;
1781 seekto.u.LowPart = 0;
1782 seekto.u.HighPart = 0;
1783 hres = IStream_Seek(pStm,seekto,SEEK_SET,&newpos);
1784 if (hres) {
1785 FIXME("IStream_Seek failed, %x\n",hres);
1786 return hres;
1789 buflen = ststg.cbSize.u.LowPart;
1790 buffer = HeapAlloc(GetProcessHeap(),0,buflen);
1792 hres = IStream_Read(pStm,buffer,buflen,&res);
1793 if (hres) {
1794 FIXME("Stream Read failed, %x\n",hres);
1795 HeapFree(GetProcessHeap(),0,buffer);
1796 return hres;
1799 WriteFile(hPipe,buffer,buflen,&res,NULL);
1800 HeapFree(GetProcessHeap(),0,buffer);
1802 FlushFileBuffers(hPipe);
1803 DisconnectNamedPipe(hPipe);
1805 TRACE("done marshalling IClassFactory\n");
1807 CloseHandle(hPipe);
1808 IStream_Release(pStm);
1809 return 0;
1812 void RPC_StartLocalServer(REFCLSID clsid, IStream *stream)
1814 DWORD tid;
1815 HANDLE thread, ready_event;
1816 struct local_server_params *lsp = HeapAlloc(GetProcessHeap(), 0, sizeof(*lsp));
1818 lsp->clsid = *clsid;
1819 lsp->stream = stream;
1820 IStream_AddRef(stream);
1821 lsp->ready_event = ready_event = CreateEventW(NULL, FALSE, FALSE, NULL);
1823 thread = CreateThread(NULL, 0, local_server_thread, lsp, 0, &tid);
1824 CloseHandle(thread);
1825 /* FIXME: failure handling */
1827 WaitForSingleObject(ready_event, INFINITE);
1828 CloseHandle(ready_event);