xref: /reactos/dll/win32/ole32/rpc.c (revision 64daf542)
1 /*
2  *	RPC Manager
3  *
4  * Copyright 2001  Ove Kåven, TransGaming Technologies
5  * Copyright 2002  Marcus Meissner
6  * Copyright 2005  Mike Hearn, Rob Shearman for CodeWeavers
7  *
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.
12  *
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.
17  *
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
21  */
22 
23 #include "precomp.h"
24 
25 #include <winsvc.h>
26 
27 WINE_DEFAULT_DEBUG_CHANNEL(ole);
28 
29 static void __RPC_STUB dispatch_rpc(RPC_MESSAGE *msg);
30 
31 /* we only use one function to dispatch calls for all methods - we use the
32  * RPC_IF_OLE flag to tell the RPC runtime that this is the case */
33 static RPC_DISPATCH_FUNCTION rpc_dispatch_table[1] = { dispatch_rpc }; /* (RO) */
34 static RPC_DISPATCH_TABLE rpc_dispatch = { 1, rpc_dispatch_table }; /* (RO) */
35 
36 static struct list registered_interfaces = LIST_INIT(registered_interfaces); /* (CS csRegIf) */
37 static CRITICAL_SECTION csRegIf;
38 static CRITICAL_SECTION_DEBUG csRegIf_debug =
39 {
40     0, 0, &csRegIf,
41     { &csRegIf_debug.ProcessLocksList, &csRegIf_debug.ProcessLocksList },
42       0, 0, { (DWORD_PTR)(__FILE__ ": dcom registered server interfaces") }
43 };
44 static CRITICAL_SECTION csRegIf = { &csRegIf_debug, -1, 0, 0, 0, 0 };
45 
46 static struct list channel_hooks = LIST_INIT(channel_hooks); /* (CS csChannelHook) */
47 static CRITICAL_SECTION csChannelHook;
48 static CRITICAL_SECTION_DEBUG csChannelHook_debug =
49 {
50     0, 0, &csChannelHook,
51     { &csChannelHook_debug.ProcessLocksList, &csChannelHook_debug.ProcessLocksList },
52       0, 0, { (DWORD_PTR)(__FILE__ ": channel hooks") }
53 };
54 static CRITICAL_SECTION csChannelHook = { &csChannelHook_debug, -1, 0, 0, 0, 0 };
55 
56 static WCHAR wszRpcTransport[] = {'n','c','a','l','r','p','c',0};
57 
58 
59 struct registered_if
60 {
61     struct list entry;
62     DWORD refs; /* ref count */
63     RPC_SERVER_INTERFACE If; /* interface registered with the RPC runtime */
64 };
65 
66 /* get the pipe endpoint specified of the specified apartment */
67 static inline void get_rpc_endpoint(LPWSTR endpoint, const OXID *oxid)
68 {
69     /* FIXME: should get endpoint from rpcss */
70     static const WCHAR wszEndpointFormat[] = {'\\','p','i','p','e','\\','O','L','E','_','%','0','8','l','x','%','0','8','l','x',0};
71     wsprintfW(endpoint, wszEndpointFormat, (DWORD)(*oxid >> 32),(DWORD)*oxid);
72 }
73 
74 typedef struct
75 {
76     IRpcChannelBuffer IRpcChannelBuffer_iface;
77     LONG refs;
78 
79     DWORD dest_context; /* returned from GetDestCtx */
80     void *dest_context_data; /* returned from GetDestCtx */
81 } RpcChannelBuffer;
82 
83 typedef struct
84 {
85     RpcChannelBuffer       super; /* superclass */
86 
87     RPC_BINDING_HANDLE     bind; /* handle to the remote server */
88     OXID                   oxid; /* apartment in which the channel is valid */
89     DWORD                  server_pid; /* id of server process */
90     HANDLE                 event; /* cached event handle */
91 } ClientRpcChannelBuffer;
92 
93 struct dispatch_params
94 {
95     RPCOLEMESSAGE     *msg; /* message */
96     IRpcStubBuffer    *stub; /* stub buffer, if applicable */
97     IRpcChannelBuffer *chan; /* server channel buffer, if applicable */
98     IID                iid; /* ID of interface being called */
99     IUnknown          *iface; /* interface being called */
100     HANDLE             handle; /* handle that will become signaled when call finishes */
101     BOOL               bypass_rpcrt; /* bypass RPC runtime? */
102     RPC_STATUS         status; /* status (out) */
103     HRESULT            hr; /* hresult (out) */
104 };
105 
106 struct message_state
107 {
108     RPC_BINDING_HANDLE binding_handle;
109     ULONG prefix_data_len;
110     SChannelHookCallInfo channel_hook_info;
111     BOOL bypass_rpcrt;
112 
113     /* client only */
114     HWND target_hwnd;
115     DWORD target_tid;
116     struct dispatch_params params;
117 };
118 
119 typedef struct
120 {
121     ULONG conformance; /* NDR */
122     GUID id;
123     ULONG size;
124     /* [size_is((size+7)&~7)] */ unsigned char data[1];
125 } WIRE_ORPC_EXTENT;
126 
127 typedef struct
128 {
129     ULONG size;
130     ULONG reserved;
131     unsigned char extent[1];
132 } WIRE_ORPC_EXTENT_ARRAY;
133 
134 typedef struct
135 {
136     ULONG version;
137     ULONG flags;
138     ULONG reserved1;
139     GUID  cid;
140     unsigned char extensions[1];
141 } WIRE_ORPCTHIS;
142 
143 typedef struct
144 {
145     ULONG flags;
146     unsigned char extensions[1];
147 } WIRE_ORPCTHAT;
148 
149 struct channel_hook_entry
150 {
151     struct list entry;
152     GUID id;
153     IChannelHook *hook;
154 };
155 
156 struct channel_hook_buffer_data
157 {
158     GUID id;
159     ULONG extension_size;
160 };
161 
162 
163 static HRESULT unmarshal_ORPCTHAT(RPC_MESSAGE *msg, ORPCTHAT *orpcthat,
164                                   ORPC_EXTENT_ARRAY *orpc_ext_array, WIRE_ORPC_EXTENT **first_wire_orpc_extent);
165 
166 /* Channel Hook Functions */
167 
168 static ULONG ChannelHooks_ClientGetSize(SChannelHookCallInfo *info,
169     struct channel_hook_buffer_data **data, unsigned int *hook_count,
170     ULONG *extension_count)
171 {
172     struct channel_hook_entry *entry;
173     ULONG total_size = 0;
174     unsigned int hook_index = 0;
175 
176     *hook_count = 0;
177     *extension_count = 0;
178 
179     EnterCriticalSection(&csChannelHook);
180 
181     LIST_FOR_EACH_ENTRY(entry, &channel_hooks, struct channel_hook_entry, entry)
182         (*hook_count)++;
183 
184     if (*hook_count)
185         *data = HeapAlloc(GetProcessHeap(), 0, *hook_count * sizeof(struct channel_hook_buffer_data));
186     else
187         *data = NULL;
188 
189     LIST_FOR_EACH_ENTRY(entry, &channel_hooks, struct channel_hook_entry, entry)
190     {
191         ULONG extension_size = 0;
192 
193         IChannelHook_ClientGetSize(entry->hook, &entry->id, &info->iid, &extension_size);
194 
195         TRACE("%s: extension_size = %u\n", debugstr_guid(&entry->id), extension_size);
196 
197         extension_size = (extension_size+7)&~7;
198         (*data)[hook_index].id = entry->id;
199         (*data)[hook_index].extension_size = extension_size;
200 
201         /* an extension is only put onto the wire if it has data to write */
202         if (extension_size)
203         {
204             total_size += FIELD_OFFSET(WIRE_ORPC_EXTENT, data[extension_size]);
205             (*extension_count)++;
206         }
207 
208         hook_index++;
209     }
210 
211     LeaveCriticalSection(&csChannelHook);
212 
213     return total_size;
214 }
215 
216 static unsigned char * ChannelHooks_ClientFillBuffer(SChannelHookCallInfo *info,
217     unsigned char *buffer, struct channel_hook_buffer_data *data,
218     unsigned int hook_count)
219 {
220     struct channel_hook_entry *entry;
221 
222     EnterCriticalSection(&csChannelHook);
223 
224     LIST_FOR_EACH_ENTRY(entry, &channel_hooks, struct channel_hook_entry, entry)
225     {
226         unsigned int i;
227         ULONG extension_size = 0;
228         WIRE_ORPC_EXTENT *wire_orpc_extent = (WIRE_ORPC_EXTENT *)buffer;
229 
230         for (i = 0; i < hook_count; i++)
231             if (IsEqualGUID(&entry->id, &data[i].id))
232                 extension_size = data[i].extension_size;
233 
234         /* an extension is only put onto the wire if it has data to write */
235         if (!extension_size)
236             continue;
237 
238         IChannelHook_ClientFillBuffer(entry->hook, &entry->id, &info->iid,
239             &extension_size, buffer + FIELD_OFFSET(WIRE_ORPC_EXTENT, data[0]));
240 
241         TRACE("%s: extension_size = %u\n", debugstr_guid(&entry->id), extension_size);
242 
243         /* FIXME: set unused portion of wire_orpc_extent->data to 0? */
244 
245         wire_orpc_extent->conformance = (extension_size+7)&~7;
246         wire_orpc_extent->size = extension_size;
247         wire_orpc_extent->id = entry->id;
248         buffer += FIELD_OFFSET(WIRE_ORPC_EXTENT, data[wire_orpc_extent->conformance]);
249     }
250 
251     LeaveCriticalSection(&csChannelHook);
252 
253     return buffer;
254 }
255 
256 static void ChannelHooks_ServerNotify(SChannelHookCallInfo *info,
257     DWORD lDataRep, WIRE_ORPC_EXTENT *first_wire_orpc_extent,
258     ULONG extension_count)
259 {
260     struct channel_hook_entry *entry;
261     ULONG i;
262 
263     EnterCriticalSection(&csChannelHook);
264 
265     LIST_FOR_EACH_ENTRY(entry, &channel_hooks, struct channel_hook_entry, entry)
266     {
267         WIRE_ORPC_EXTENT *wire_orpc_extent;
268         for (i = 0, wire_orpc_extent = first_wire_orpc_extent;
269              i < extension_count;
270              i++, wire_orpc_extent = (WIRE_ORPC_EXTENT *)&wire_orpc_extent->data[wire_orpc_extent->conformance])
271         {
272             if (IsEqualGUID(&entry->id, &wire_orpc_extent->id))
273                 break;
274         }
275         if (i == extension_count) wire_orpc_extent = NULL;
276 
277         IChannelHook_ServerNotify(entry->hook, &entry->id, &info->iid,
278             wire_orpc_extent ? wire_orpc_extent->size : 0,
279             wire_orpc_extent ? wire_orpc_extent->data : NULL,
280             lDataRep);
281     }
282 
283     LeaveCriticalSection(&csChannelHook);
284 }
285 
286 static ULONG ChannelHooks_ServerGetSize(SChannelHookCallInfo *info,
287                                         struct channel_hook_buffer_data **data, unsigned int *hook_count,
288                                         ULONG *extension_count)
289 {
290     struct channel_hook_entry *entry;
291     ULONG total_size = 0;
292     unsigned int hook_index = 0;
293 
294     *hook_count = 0;
295     *extension_count = 0;
296 
297     EnterCriticalSection(&csChannelHook);
298 
299     LIST_FOR_EACH_ENTRY(entry, &channel_hooks, struct channel_hook_entry, entry)
300         (*hook_count)++;
301 
302     if (*hook_count)
303         *data = HeapAlloc(GetProcessHeap(), 0, *hook_count * sizeof(struct channel_hook_buffer_data));
304     else
305         *data = NULL;
306 
307     LIST_FOR_EACH_ENTRY(entry, &channel_hooks, struct channel_hook_entry, entry)
308     {
309         ULONG extension_size = 0;
310 
311         IChannelHook_ServerGetSize(entry->hook, &entry->id, &info->iid, S_OK,
312                                    &extension_size);
313 
314         TRACE("%s: extension_size = %u\n", debugstr_guid(&entry->id), extension_size);
315 
316         extension_size = (extension_size+7)&~7;
317         (*data)[hook_index].id = entry->id;
318         (*data)[hook_index].extension_size = extension_size;
319 
320         /* an extension is only put onto the wire if it has data to write */
321         if (extension_size)
322         {
323             total_size += FIELD_OFFSET(WIRE_ORPC_EXTENT, data[extension_size]);
324             (*extension_count)++;
325         }
326 
327         hook_index++;
328     }
329 
330     LeaveCriticalSection(&csChannelHook);
331 
332     return total_size;
333 }
334 
335 static unsigned char * ChannelHooks_ServerFillBuffer(SChannelHookCallInfo *info,
336                                                      unsigned char *buffer, struct channel_hook_buffer_data *data,
337                                                      unsigned int hook_count)
338 {
339     struct channel_hook_entry *entry;
340 
341     EnterCriticalSection(&csChannelHook);
342 
343     LIST_FOR_EACH_ENTRY(entry, &channel_hooks, struct channel_hook_entry, entry)
344     {
345         unsigned int i;
346         ULONG extension_size = 0;
347         WIRE_ORPC_EXTENT *wire_orpc_extent = (WIRE_ORPC_EXTENT *)buffer;
348 
349         for (i = 0; i < hook_count; i++)
350             if (IsEqualGUID(&entry->id, &data[i].id))
351                 extension_size = data[i].extension_size;
352 
353         /* an extension is only put onto the wire if it has data to write */
354         if (!extension_size)
355             continue;
356 
357         IChannelHook_ServerFillBuffer(entry->hook, &entry->id, &info->iid,
358                                       &extension_size, buffer + FIELD_OFFSET(WIRE_ORPC_EXTENT, data[0]),
359                                       S_OK);
360 
361         TRACE("%s: extension_size = %u\n", debugstr_guid(&entry->id), extension_size);
362 
363         /* FIXME: set unused portion of wire_orpc_extent->data to 0? */
364 
365         wire_orpc_extent->conformance = (extension_size+7)&~7;
366         wire_orpc_extent->size = extension_size;
367         wire_orpc_extent->id = entry->id;
368         buffer += FIELD_OFFSET(WIRE_ORPC_EXTENT, data[wire_orpc_extent->conformance]);
369     }
370 
371     LeaveCriticalSection(&csChannelHook);
372 
373     return buffer;
374 }
375 
376 static void ChannelHooks_ClientNotify(SChannelHookCallInfo *info,
377                                       DWORD lDataRep, WIRE_ORPC_EXTENT *first_wire_orpc_extent,
378                                       ULONG extension_count, HRESULT hrFault)
379 {
380     struct channel_hook_entry *entry;
381     ULONG i;
382 
383     EnterCriticalSection(&csChannelHook);
384 
385     LIST_FOR_EACH_ENTRY(entry, &channel_hooks, struct channel_hook_entry, entry)
386     {
387         WIRE_ORPC_EXTENT *wire_orpc_extent;
388         for (i = 0, wire_orpc_extent = first_wire_orpc_extent;
389              i < extension_count;
390              i++, wire_orpc_extent = (WIRE_ORPC_EXTENT *)&wire_orpc_extent->data[wire_orpc_extent->conformance])
391         {
392             if (IsEqualGUID(&entry->id, &wire_orpc_extent->id))
393                 break;
394         }
395         if (i == extension_count) wire_orpc_extent = NULL;
396 
397         IChannelHook_ClientNotify(entry->hook, &entry->id, &info->iid,
398                                   wire_orpc_extent ? wire_orpc_extent->size : 0,
399                                   wire_orpc_extent ? wire_orpc_extent->data : NULL,
400                                   lDataRep, hrFault);
401     }
402 
403     LeaveCriticalSection(&csChannelHook);
404 }
405 
406 HRESULT RPC_RegisterChannelHook(REFGUID rguid, IChannelHook *hook)
407 {
408     struct channel_hook_entry *entry;
409 
410     TRACE("(%s, %p)\n", debugstr_guid(rguid), hook);
411 
412     entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
413     if (!entry)
414         return E_OUTOFMEMORY;
415 
416     entry->id = *rguid;
417     entry->hook = hook;
418     IChannelHook_AddRef(hook);
419 
420     EnterCriticalSection(&csChannelHook);
421     list_add_tail(&channel_hooks, &entry->entry);
422     LeaveCriticalSection(&csChannelHook);
423 
424     return S_OK;
425 }
426 
427 void RPC_UnregisterAllChannelHooks(void)
428 {
429     struct channel_hook_entry *cursor;
430     struct channel_hook_entry *cursor2;
431 
432     EnterCriticalSection(&csChannelHook);
433     LIST_FOR_EACH_ENTRY_SAFE(cursor, cursor2, &channel_hooks, struct channel_hook_entry, entry)
434         HeapFree(GetProcessHeap(), 0, cursor);
435     LeaveCriticalSection(&csChannelHook);
436     DeleteCriticalSection(&csChannelHook);
437     DeleteCriticalSection(&csRegIf);
438 }
439 
440 /* RPC Channel Buffer Functions */
441 
442 static HRESULT WINAPI RpcChannelBuffer_QueryInterface(IRpcChannelBuffer *iface, REFIID riid, LPVOID *ppv)
443 {
444     *ppv = NULL;
445     if (IsEqualIID(riid,&IID_IRpcChannelBuffer) || IsEqualIID(riid,&IID_IUnknown))
446     {
447         *ppv = iface;
448         IRpcChannelBuffer_AddRef(iface);
449         return S_OK;
450     }
451     return E_NOINTERFACE;
452 }
453 
454 static ULONG WINAPI RpcChannelBuffer_AddRef(LPRPCCHANNELBUFFER iface)
455 {
456     RpcChannelBuffer *This = (RpcChannelBuffer *)iface;
457     return InterlockedIncrement(&This->refs);
458 }
459 
460 static ULONG WINAPI ServerRpcChannelBuffer_Release(LPRPCCHANNELBUFFER iface)
461 {
462     RpcChannelBuffer *This = (RpcChannelBuffer *)iface;
463     ULONG ref;
464 
465     ref = InterlockedDecrement(&This->refs);
466     if (ref)
467         return ref;
468 
469     HeapFree(GetProcessHeap(), 0, This);
470     return 0;
471 }
472 
473 static ULONG WINAPI ClientRpcChannelBuffer_Release(LPRPCCHANNELBUFFER iface)
474 {
475     ClientRpcChannelBuffer *This = (ClientRpcChannelBuffer *)iface;
476     ULONG ref;
477 
478     ref = InterlockedDecrement(&This->super.refs);
479     if (ref)
480         return ref;
481 
482     if (This->event) CloseHandle(This->event);
483     RpcBindingFree(&This->bind);
484     HeapFree(GetProcessHeap(), 0, This);
485     return 0;
486 }
487 
488 static HRESULT WINAPI ServerRpcChannelBuffer_GetBuffer(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE* olemsg, REFIID riid)
489 {
490     RpcChannelBuffer *This = (RpcChannelBuffer *)iface;
491     RPC_MESSAGE *msg = (RPC_MESSAGE *)olemsg;
492     RPC_STATUS status;
493     ORPCTHAT *orpcthat;
494     struct message_state *message_state;
495     ULONG extensions_size;
496     struct channel_hook_buffer_data *channel_hook_data;
497     unsigned int channel_hook_count;
498     ULONG extension_count;
499 
500     TRACE("(%p)->(%p,%s)\n", This, olemsg, debugstr_guid(riid));
501 
502     message_state = msg->Handle;
503     /* restore the binding handle and the real start of data */
504     msg->Handle = message_state->binding_handle;
505     msg->Buffer = (char *)msg->Buffer - message_state->prefix_data_len;
506 
507     extensions_size = ChannelHooks_ServerGetSize(&message_state->channel_hook_info,
508                                                  &channel_hook_data, &channel_hook_count, &extension_count);
509 
510     msg->BufferLength += FIELD_OFFSET(WIRE_ORPCTHAT, extensions) + sizeof(DWORD);
511     if (extensions_size)
512     {
513         msg->BufferLength += FIELD_OFFSET(WIRE_ORPC_EXTENT_ARRAY, extent[2*sizeof(DWORD) + extensions_size]);
514         if (extension_count & 1)
515             msg->BufferLength += FIELD_OFFSET(WIRE_ORPC_EXTENT, data[0]);
516     }
517 
518     if (message_state->bypass_rpcrt)
519     {
520         msg->Buffer = HeapAlloc(GetProcessHeap(), 0, msg->BufferLength);
521         if (msg->Buffer)
522             status = RPC_S_OK;
523         else
524         {
525             HeapFree(GetProcessHeap(), 0, channel_hook_data);
526             return E_OUTOFMEMORY;
527         }
528     }
529     else
530         status = I_RpcGetBuffer(msg);
531 
532     orpcthat = msg->Buffer;
533     msg->Buffer = (char *)msg->Buffer + FIELD_OFFSET(WIRE_ORPCTHAT, extensions);
534 
535     orpcthat->flags = ORPCF_NULL /* FIXME? */;
536 
537     /* NDR representation of orpcthat->extensions */
538     *(DWORD *)msg->Buffer = extensions_size ? 1 : 0;
539     msg->Buffer = (char *)msg->Buffer + sizeof(DWORD);
540 
541     if (extensions_size)
542     {
543         WIRE_ORPC_EXTENT_ARRAY *orpc_extent_array = msg->Buffer;
544         orpc_extent_array->size = extension_count;
545         orpc_extent_array->reserved = 0;
546         msg->Buffer = (char *)msg->Buffer + FIELD_OFFSET(WIRE_ORPC_EXTENT_ARRAY, extent);
547         /* NDR representation of orpc_extent_array->extent */
548         *(DWORD *)msg->Buffer = 1;
549         msg->Buffer = (char *)msg->Buffer + sizeof(DWORD);
550         /* NDR representation of [size_is] attribute of orpc_extent_array->extent */
551         *(DWORD *)msg->Buffer = (extension_count + 1) & ~1;
552         msg->Buffer = (char *)msg->Buffer + sizeof(DWORD);
553 
554         msg->Buffer = ChannelHooks_ServerFillBuffer(&message_state->channel_hook_info,
555                                                     msg->Buffer, channel_hook_data, channel_hook_count);
556 
557         /* we must add a dummy extension if there is an odd extension
558          * count to meet the contract specified by the size_is attribute */
559         if (extension_count & 1)
560         {
561             WIRE_ORPC_EXTENT *wire_orpc_extent = msg->Buffer;
562             wire_orpc_extent->conformance = 0;
563             wire_orpc_extent->id = GUID_NULL;
564             wire_orpc_extent->size = 0;
565             msg->Buffer = (char *)msg->Buffer + FIELD_OFFSET(WIRE_ORPC_EXTENT, data[0]);
566         }
567     }
568 
569     HeapFree(GetProcessHeap(), 0, channel_hook_data);
570 
571     /* store the prefixed data length so that we can restore the real buffer
572      * later */
573     message_state->prefix_data_len = (char *)msg->Buffer - (char *)orpcthat;
574     msg->BufferLength -= message_state->prefix_data_len;
575     /* save away the message state again */
576     msg->Handle = message_state;
577 
578     TRACE("-- %d\n", status);
579 
580     return HRESULT_FROM_WIN32(status);
581 }
582 
583 static HANDLE ClientRpcChannelBuffer_GetEventHandle(ClientRpcChannelBuffer *This)
584 {
585     HANDLE event = InterlockedExchangePointer(&This->event, NULL);
586 
587     /* Note: must be auto-reset event so we can reuse it without a call
588     * to ResetEvent */
589     if (!event) event = CreateEventW(NULL, FALSE, FALSE, NULL);
590 
591     return event;
592 }
593 
594 static void ClientRpcChannelBuffer_ReleaseEventHandle(ClientRpcChannelBuffer *This, HANDLE event)
595 {
596     if (InterlockedCompareExchangePointer(&This->event, event, NULL))
597         /* already a handle cached in This */
598         CloseHandle(event);
599 }
600 
601 static HRESULT WINAPI ClientRpcChannelBuffer_GetBuffer(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE* olemsg, REFIID riid)
602 {
603     ClientRpcChannelBuffer *This = (ClientRpcChannelBuffer *)iface;
604     RPC_MESSAGE *msg = (RPC_MESSAGE *)olemsg;
605     RPC_CLIENT_INTERFACE *cif;
606     RPC_STATUS status;
607     ORPCTHIS *orpcthis;
608     struct message_state *message_state;
609     ULONG extensions_size;
610     struct channel_hook_buffer_data *channel_hook_data;
611     unsigned int channel_hook_count;
612     ULONG extension_count;
613     IPID ipid;
614     HRESULT hr;
615     APARTMENT *apt = NULL;
616 
617     TRACE("(%p)->(%p,%s)\n", This, olemsg, debugstr_guid(riid));
618 
619     cif = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(RPC_CLIENT_INTERFACE));
620     if (!cif)
621         return E_OUTOFMEMORY;
622 
623     message_state = HeapAlloc(GetProcessHeap(), 0, sizeof(*message_state));
624     if (!message_state)
625     {
626         HeapFree(GetProcessHeap(), 0, cif);
627         return E_OUTOFMEMORY;
628     }
629 
630     cif->Length = sizeof(RPC_CLIENT_INTERFACE);
631     /* RPC interface ID = COM interface ID */
632     cif->InterfaceId.SyntaxGUID = *riid;
633     /* COM objects always have a version of 0.0 */
634     cif->InterfaceId.SyntaxVersion.MajorVersion = 0;
635     cif->InterfaceId.SyntaxVersion.MinorVersion = 0;
636     msg->Handle = This->bind;
637     msg->RpcInterfaceInformation = cif;
638 
639     message_state->prefix_data_len = 0;
640     message_state->binding_handle = This->bind;
641 
642     message_state->channel_hook_info.iid = *riid;
643     message_state->channel_hook_info.cbSize = sizeof(message_state->channel_hook_info);
644     message_state->channel_hook_info.uCausality = COM_CurrentCausalityId();
645     message_state->channel_hook_info.dwServerPid = This->server_pid;
646     message_state->channel_hook_info.iMethod = msg->ProcNum & ~RPC_FLAGS_VALID_BIT;
647     message_state->channel_hook_info.pObject = NULL; /* only present on server-side */
648     message_state->target_hwnd = NULL;
649     message_state->target_tid = 0;
650     memset(&message_state->params, 0, sizeof(message_state->params));
651 
652     extensions_size = ChannelHooks_ClientGetSize(&message_state->channel_hook_info,
653         &channel_hook_data, &channel_hook_count, &extension_count);
654 
655     msg->BufferLength += FIELD_OFFSET(WIRE_ORPCTHIS, extensions) + sizeof(DWORD);
656     if (extensions_size)
657     {
658         msg->BufferLength += FIELD_OFFSET(WIRE_ORPC_EXTENT_ARRAY, extent[2*sizeof(DWORD) + extensions_size]);
659         if (extension_count & 1)
660             msg->BufferLength += FIELD_OFFSET(WIRE_ORPC_EXTENT, data[0]);
661     }
662 
663     RpcBindingInqObject(message_state->binding_handle, &ipid);
664     hr = ipid_get_dispatch_params(&ipid, &apt, NULL, &message_state->params.stub,
665                                   &message_state->params.chan,
666                                   &message_state->params.iid,
667                                   &message_state->params.iface);
668     if (hr == S_OK)
669     {
670         /* stub, chan, iface and iid are unneeded in multi-threaded case as we go
671          * via the RPC runtime */
672         if (apt->multi_threaded)
673         {
674             IRpcStubBuffer_Release(message_state->params.stub);
675             message_state->params.stub = NULL;
676             IRpcChannelBuffer_Release(message_state->params.chan);
677             message_state->params.chan = NULL;
678             message_state->params.iface = NULL;
679         }
680         else
681         {
682             message_state->params.bypass_rpcrt = TRUE;
683             message_state->target_hwnd = apartment_getwindow(apt);
684             message_state->target_tid = apt->tid;
685             /* we assume later on that this being non-NULL is the indicator that
686              * means call directly instead of going through RPC runtime */
687             if (!message_state->target_hwnd)
688                 ERR("window for apartment %s is NULL\n", wine_dbgstr_longlong(apt->oxid));
689         }
690     }
691     if (apt) apartment_release(apt);
692     message_state->params.handle = ClientRpcChannelBuffer_GetEventHandle(This);
693     /* Note: message_state->params.msg is initialised in
694      * ClientRpcChannelBuffer_SendReceive */
695 
696     /* shortcut the RPC runtime */
697     if (message_state->target_hwnd)
698     {
699         msg->Buffer = HeapAlloc(GetProcessHeap(), 0, msg->BufferLength);
700         if (msg->Buffer)
701             status = RPC_S_OK;
702         else
703             status = ERROR_OUTOFMEMORY;
704     }
705     else
706         status = I_RpcGetBuffer(msg);
707 
708     msg->Handle = message_state;
709 
710     if (status == RPC_S_OK)
711     {
712         orpcthis = msg->Buffer;
713         msg->Buffer = (char *)msg->Buffer + FIELD_OFFSET(WIRE_ORPCTHIS, extensions);
714 
715         orpcthis->version.MajorVersion = COM_MAJOR_VERSION;
716         orpcthis->version.MinorVersion = COM_MINOR_VERSION;
717         orpcthis->flags = message_state->channel_hook_info.dwServerPid ? ORPCF_LOCAL : ORPCF_NULL;
718         orpcthis->reserved1 = 0;
719         orpcthis->cid = message_state->channel_hook_info.uCausality;
720 
721         /* NDR representation of orpcthis->extensions */
722         *(DWORD *)msg->Buffer = extensions_size ? 1 : 0;
723         msg->Buffer = (char *)msg->Buffer + sizeof(DWORD);
724 
725         if (extensions_size)
726         {
727             ORPC_EXTENT_ARRAY *orpc_extent_array = msg->Buffer;
728             orpc_extent_array->size = extension_count;
729             orpc_extent_array->reserved = 0;
730             msg->Buffer = (char *)msg->Buffer + FIELD_OFFSET(WIRE_ORPC_EXTENT_ARRAY, extent);
731             /* NDR representation of orpc_extent_array->extent */
732             *(DWORD *)msg->Buffer = 1;
733             msg->Buffer = (char *)msg->Buffer + sizeof(DWORD);
734             /* NDR representation of [size_is] attribute of orpc_extent_array->extent */
735             *(DWORD *)msg->Buffer = (extension_count + 1) & ~1;
736             msg->Buffer = (char *)msg->Buffer + sizeof(DWORD);
737 
738             msg->Buffer = ChannelHooks_ClientFillBuffer(&message_state->channel_hook_info,
739                 msg->Buffer, channel_hook_data, channel_hook_count);
740 
741             /* we must add a dummy extension if there is an odd extension
742              * count to meet the contract specified by the size_is attribute */
743             if (extension_count & 1)
744             {
745                 WIRE_ORPC_EXTENT *wire_orpc_extent = msg->Buffer;
746                 wire_orpc_extent->conformance = 0;
747                 wire_orpc_extent->id = GUID_NULL;
748                 wire_orpc_extent->size = 0;
749                 msg->Buffer = (char *)msg->Buffer + FIELD_OFFSET(WIRE_ORPC_EXTENT, data[0]);
750             }
751         }
752 
753         /* store the prefixed data length so that we can restore the real buffer
754          * pointer in ClientRpcChannelBuffer_SendReceive. */
755         message_state->prefix_data_len = (char *)msg->Buffer - (char *)orpcthis;
756         msg->BufferLength -= message_state->prefix_data_len;
757     }
758 
759     HeapFree(GetProcessHeap(), 0, channel_hook_data);
760 
761     TRACE("-- %d\n", status);
762 
763     return HRESULT_FROM_WIN32(status);
764 }
765 
766 static HRESULT WINAPI ServerRpcChannelBuffer_SendReceive(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE *olemsg, ULONG *pstatus)
767 {
768     FIXME("stub\n");
769     return E_NOTIMPL;
770 }
771 
772 /* this thread runs an outgoing RPC */
773 static DWORD WINAPI rpc_sendreceive_thread(LPVOID param)
774 {
775     struct dispatch_params *data = param;
776 
777     /* Note: I_RpcSendReceive doesn't raise exceptions like the higher-level
778      * RPC functions do */
779     data->status = I_RpcSendReceive((RPC_MESSAGE *)data->msg);
780 
781     TRACE("completed with status 0x%x\n", data->status);
782 
783     SetEvent(data->handle);
784 
785     return 0;
786 }
787 
788 static inline HRESULT ClientRpcChannelBuffer_IsCorrectApartment(ClientRpcChannelBuffer *This, APARTMENT *apt)
789 {
790     OXID oxid;
791     if (!apt)
792         return S_FALSE;
793     if (apartment_getoxid(apt, &oxid) != S_OK)
794         return S_FALSE;
795     if (This->oxid != oxid)
796         return S_FALSE;
797     return S_OK;
798 }
799 
800 static HRESULT WINAPI ClientRpcChannelBuffer_SendReceive(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE *olemsg, ULONG *pstatus)
801 {
802     ClientRpcChannelBuffer *This = (ClientRpcChannelBuffer *)iface;
803     HRESULT hr;
804     RPC_MESSAGE *msg = (RPC_MESSAGE *)olemsg;
805     RPC_STATUS status;
806     DWORD index;
807     struct message_state *message_state;
808     ORPCTHAT orpcthat;
809     ORPC_EXTENT_ARRAY orpc_ext_array;
810     WIRE_ORPC_EXTENT *first_wire_orpc_extent = NULL;
811     HRESULT hrFault = S_OK;
812 
813     TRACE("(%p) iMethod=%d\n", olemsg, olemsg->iMethod);
814 
815     hr = ClientRpcChannelBuffer_IsCorrectApartment(This, COM_CurrentApt());
816     if (hr != S_OK)
817     {
818         ERR("called from wrong apartment, should have been 0x%s\n",
819             wine_dbgstr_longlong(This->oxid));
820         return RPC_E_WRONG_THREAD;
821     }
822     /* This situation should be impossible in multi-threaded apartments,
823      * because the calling thread isn't re-enterable.
824      * Note: doing a COM call during the processing of a sent message is
825      * only disallowed if a client call is already being waited for
826      * completion */
827     if (!COM_CurrentApt()->multi_threaded &&
828         COM_CurrentInfo()->pending_call_count_client &&
829         InSendMessage())
830     {
831         ERR("can't make an outgoing COM call in response to a sent message\n");
832         return RPC_E_CANTCALLOUT_ININPUTSYNCCALL;
833     }
834 
835     message_state = msg->Handle;
836     /* restore the binding handle and the real start of data */
837     msg->Handle = message_state->binding_handle;
838     msg->Buffer = (char *)msg->Buffer - message_state->prefix_data_len;
839     msg->BufferLength += message_state->prefix_data_len;
840 
841     /* Note: this is an optimization in the Microsoft OLE runtime that we need
842      * to copy, as shown by the test_no_couninitialize_client test. without
843      * short-circuiting the RPC runtime in the case below, the test will
844      * deadlock on the loader lock due to the RPC runtime needing to create
845      * a thread to process the RPC when this function is called indirectly
846      * from DllMain */
847 
848     message_state->params.msg = olemsg;
849     if (message_state->params.bypass_rpcrt)
850     {
851         TRACE("Calling apartment thread 0x%08x...\n", message_state->target_tid);
852 
853         msg->ProcNum &= ~RPC_FLAGS_VALID_BIT;
854 
855         if (!PostMessageW(message_state->target_hwnd, DM_EXECUTERPC, 0,
856                           (LPARAM)&message_state->params))
857         {
858             ERR("PostMessage failed with error %u\n", GetLastError());
859 
860             /* Note: message_state->params.iface doesn't have a reference and
861              * so doesn't need to be released */
862 
863             hr = HRESULT_FROM_WIN32(GetLastError());
864         }
865     }
866     else
867     {
868         /* we use a separate thread here because we need to be able to
869          * pump the message loop in the application thread: if we do not,
870          * any windows created by this thread will hang and RPCs that try
871          * and re-enter this STA from an incoming server thread will
872          * deadlock. InstallShield is an example of that.
873          */
874         if (!QueueUserWorkItem(rpc_sendreceive_thread, &message_state->params, WT_EXECUTEDEFAULT))
875         {
876             ERR("QueueUserWorkItem failed with error %u\n", GetLastError());
877             hr = E_UNEXPECTED;
878         }
879         else
880             hr = S_OK;
881     }
882 
883     if (hr == S_OK)
884     {
885         if (WaitForSingleObject(message_state->params.handle, 0))
886         {
887             COM_CurrentInfo()->pending_call_count_client++;
888             hr = CoWaitForMultipleHandles(0, INFINITE, 1, &message_state->params.handle, &index);
889             COM_CurrentInfo()->pending_call_count_client--;
890         }
891     }
892     ClientRpcChannelBuffer_ReleaseEventHandle(This, message_state->params.handle);
893 
894     /* for WM shortcut, faults are returned in params->hr */
895     if (hr == S_OK)
896         hrFault = message_state->params.hr;
897 
898     status = message_state->params.status;
899 
900     orpcthat.flags = ORPCF_NULL;
901     orpcthat.extensions = NULL;
902 
903     TRACE("RPC call status: 0x%x\n", status);
904     if (status != RPC_S_OK)
905         hr = HRESULT_FROM_WIN32(status);
906 
907     TRACE("hrFault = 0x%08x\n", hrFault);
908 
909     /* FIXME: this condition should be
910      * "hr == S_OK && (!hrFault || msg->BufferLength > FIELD_OFFSET(ORPCTHAT, extensions) + 4)"
911      * but we don't currently reset the message length for PostMessage
912      * dispatched calls */
913     if (hr == S_OK && hrFault == S_OK)
914     {
915         HRESULT hr2;
916         char *original_buffer = msg->Buffer;
917 
918         /* handle ORPCTHAT and client extensions */
919 
920         hr2 = unmarshal_ORPCTHAT(msg, &orpcthat, &orpc_ext_array, &first_wire_orpc_extent);
921         if (FAILED(hr2))
922             hr = hr2;
923 
924         message_state->prefix_data_len = (char *)msg->Buffer - original_buffer;
925         msg->BufferLength -= message_state->prefix_data_len;
926     }
927     else
928         message_state->prefix_data_len = 0;
929 
930     if (hr == S_OK)
931     {
932         ChannelHooks_ClientNotify(&message_state->channel_hook_info,
933                                   msg->DataRepresentation,
934                                   first_wire_orpc_extent,
935                                   orpcthat.extensions && first_wire_orpc_extent ? orpcthat.extensions->size : 0,
936                                   hrFault);
937     }
938 
939     /* save away the message state again */
940     msg->Handle = message_state;
941 
942     if (pstatus) *pstatus = status;
943 
944     if (hr == S_OK)
945         hr = hrFault;
946 
947     TRACE("-- 0x%08x\n", hr);
948 
949     return hr;
950 }
951 
952 static HRESULT WINAPI ServerRpcChannelBuffer_FreeBuffer(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE* olemsg)
953 {
954     RPC_MESSAGE *msg = (RPC_MESSAGE *)olemsg;
955     RPC_STATUS status;
956     struct message_state *message_state;
957 
958     TRACE("(%p)\n", msg);
959 
960     message_state = msg->Handle;
961     /* restore the binding handle and the real start of data */
962     msg->Handle = message_state->binding_handle;
963     msg->Buffer = (char *)msg->Buffer - message_state->prefix_data_len;
964     msg->BufferLength += message_state->prefix_data_len;
965     message_state->prefix_data_len = 0;
966 
967     if (message_state->bypass_rpcrt)
968     {
969         HeapFree(GetProcessHeap(), 0, msg->Buffer);
970         status = RPC_S_OK;
971     }
972     else
973         status = I_RpcFreeBuffer(msg);
974 
975     msg->Handle = message_state;
976 
977     TRACE("-- %d\n", status);
978 
979     return HRESULT_FROM_WIN32(status);
980 }
981 
982 static HRESULT WINAPI ClientRpcChannelBuffer_FreeBuffer(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE* olemsg)
983 {
984     RPC_MESSAGE *msg = (RPC_MESSAGE *)olemsg;
985     RPC_STATUS status;
986     struct message_state *message_state;
987 
988     TRACE("(%p)\n", msg);
989 
990     message_state = msg->Handle;
991     /* restore the binding handle and the real start of data */
992     msg->Handle = message_state->binding_handle;
993     msg->Buffer = (char *)msg->Buffer - message_state->prefix_data_len;
994     msg->BufferLength += message_state->prefix_data_len;
995 
996     if (message_state->params.bypass_rpcrt)
997     {
998         HeapFree(GetProcessHeap(), 0, msg->Buffer);
999         status = RPC_S_OK;
1000     }
1001     else
1002         status = I_RpcFreeBuffer(msg);
1003 
1004     HeapFree(GetProcessHeap(), 0, msg->RpcInterfaceInformation);
1005     msg->RpcInterfaceInformation = NULL;
1006 
1007     if (message_state->params.stub)
1008         IRpcStubBuffer_Release(message_state->params.stub);
1009     if (message_state->params.chan)
1010         IRpcChannelBuffer_Release(message_state->params.chan);
1011     HeapFree(GetProcessHeap(), 0, message_state);
1012 
1013     TRACE("-- %d\n", status);
1014 
1015     return HRESULT_FROM_WIN32(status);
1016 }
1017 
1018 static HRESULT WINAPI ClientRpcChannelBuffer_GetDestCtx(LPRPCCHANNELBUFFER iface, DWORD* pdwDestContext, void** ppvDestContext)
1019 {
1020     ClientRpcChannelBuffer *This = (ClientRpcChannelBuffer *)iface;
1021 
1022     TRACE("(%p,%p)\n", pdwDestContext, ppvDestContext);
1023 
1024     *pdwDestContext = This->super.dest_context;
1025     *ppvDestContext = This->super.dest_context_data;
1026 
1027     return S_OK;
1028 }
1029 
1030 static HRESULT WINAPI ServerRpcChannelBuffer_GetDestCtx(LPRPCCHANNELBUFFER iface, DWORD* dest_context, void** dest_context_data)
1031 {
1032     RpcChannelBuffer *This = (RpcChannelBuffer *)iface;
1033 
1034     TRACE("(%p,%p)\n", dest_context, dest_context_data);
1035 
1036     *dest_context = This->dest_context;
1037     *dest_context_data = This->dest_context_data;
1038     return S_OK;
1039 }
1040 
1041 static HRESULT WINAPI RpcChannelBuffer_IsConnected(LPRPCCHANNELBUFFER iface)
1042 {
1043     TRACE("()\n");
1044     /* native does nothing too */
1045     return S_OK;
1046 }
1047 
1048 static const IRpcChannelBufferVtbl ClientRpcChannelBufferVtbl =
1049 {
1050     RpcChannelBuffer_QueryInterface,
1051     RpcChannelBuffer_AddRef,
1052     ClientRpcChannelBuffer_Release,
1053     ClientRpcChannelBuffer_GetBuffer,
1054     ClientRpcChannelBuffer_SendReceive,
1055     ClientRpcChannelBuffer_FreeBuffer,
1056     ClientRpcChannelBuffer_GetDestCtx,
1057     RpcChannelBuffer_IsConnected
1058 };
1059 
1060 static const IRpcChannelBufferVtbl ServerRpcChannelBufferVtbl =
1061 {
1062     RpcChannelBuffer_QueryInterface,
1063     RpcChannelBuffer_AddRef,
1064     ServerRpcChannelBuffer_Release,
1065     ServerRpcChannelBuffer_GetBuffer,
1066     ServerRpcChannelBuffer_SendReceive,
1067     ServerRpcChannelBuffer_FreeBuffer,
1068     ServerRpcChannelBuffer_GetDestCtx,
1069     RpcChannelBuffer_IsConnected
1070 };
1071 
1072 /* returns a channel buffer for proxies */
1073 HRESULT RPC_CreateClientChannel(const OXID *oxid, const IPID *ipid,
1074                                 const OXID_INFO *oxid_info,
1075                                 DWORD dest_context, void *dest_context_data,
1076                                 IRpcChannelBuffer **chan)
1077 {
1078     ClientRpcChannelBuffer *This;
1079     WCHAR                   endpoint[200];
1080     RPC_BINDING_HANDLE      bind;
1081     RPC_STATUS              status;
1082     LPWSTR                  string_binding;
1083 
1084     /* FIXME: get the endpoint from oxid_info->psa instead */
1085     get_rpc_endpoint(endpoint, oxid);
1086 
1087     TRACE("proxy pipe: connecting to endpoint: %s\n", debugstr_w(endpoint));
1088 
1089     status = RpcStringBindingComposeW(
1090         NULL,
1091         wszRpcTransport,
1092         NULL,
1093         endpoint,
1094         NULL,
1095         &string_binding);
1096 
1097     if (status == RPC_S_OK)
1098     {
1099         status = RpcBindingFromStringBindingW(string_binding, &bind);
1100 
1101         if (status == RPC_S_OK)
1102         {
1103             IPID ipid2 = *ipid; /* why can't RpcBindingSetObject take a const? */
1104             status = RpcBindingSetObject(bind, &ipid2);
1105             if (status != RPC_S_OK)
1106                 RpcBindingFree(&bind);
1107         }
1108 
1109         RpcStringFreeW(&string_binding);
1110     }
1111 
1112     if (status != RPC_S_OK)
1113     {
1114         ERR("Couldn't get binding for endpoint %s, status = %d\n", debugstr_w(endpoint), status);
1115         return HRESULT_FROM_WIN32(status);
1116     }
1117 
1118     This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
1119     if (!This)
1120     {
1121         RpcBindingFree(&bind);
1122         return E_OUTOFMEMORY;
1123     }
1124 
1125     This->super.IRpcChannelBuffer_iface.lpVtbl = &ClientRpcChannelBufferVtbl;
1126     This->super.refs = 1;
1127     This->super.dest_context = dest_context;
1128     This->super.dest_context_data = dest_context_data;
1129     This->bind = bind;
1130     apartment_getoxid(COM_CurrentApt(), &This->oxid);
1131     This->server_pid = oxid_info->dwPid;
1132     This->event = NULL;
1133 
1134     *chan = &This->super.IRpcChannelBuffer_iface;
1135 
1136     return S_OK;
1137 }
1138 
1139 HRESULT RPC_CreateServerChannel(DWORD dest_context, void *dest_context_data, IRpcChannelBuffer **chan)
1140 {
1141     RpcChannelBuffer *This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
1142     if (!This)
1143         return E_OUTOFMEMORY;
1144 
1145     This->IRpcChannelBuffer_iface.lpVtbl = &ServerRpcChannelBufferVtbl;
1146     This->refs = 1;
1147     This->dest_context = dest_context;
1148     This->dest_context_data = dest_context_data;
1149 
1150     *chan = &This->IRpcChannelBuffer_iface;
1151 
1152     return S_OK;
1153 }
1154 
1155 /* unmarshals ORPC_EXTENT_ARRAY according to NDR rules, but doesn't allocate
1156  * any memory */
1157 static HRESULT unmarshal_ORPC_EXTENT_ARRAY(RPC_MESSAGE *msg, const char *end,
1158                                            ORPC_EXTENT_ARRAY *extensions,
1159                                            WIRE_ORPC_EXTENT **first_wire_orpc_extent)
1160 {
1161     DWORD pointer_id;
1162     DWORD i;
1163 
1164     memcpy(extensions, msg->Buffer, FIELD_OFFSET(WIRE_ORPC_EXTENT_ARRAY, extent));
1165     msg->Buffer = (char *)msg->Buffer + FIELD_OFFSET(WIRE_ORPC_EXTENT_ARRAY, extent);
1166 
1167     if ((const char *)msg->Buffer + 2 * sizeof(DWORD) > end)
1168         return RPC_E_INVALID_HEADER;
1169 
1170     pointer_id = *(DWORD *)msg->Buffer;
1171     msg->Buffer = (char *)msg->Buffer + sizeof(DWORD);
1172     extensions->extent = NULL;
1173 
1174     if (pointer_id)
1175     {
1176         WIRE_ORPC_EXTENT *wire_orpc_extent;
1177 
1178         /* conformance */
1179         if (*(DWORD *)msg->Buffer != ((extensions->size+1)&~1))
1180             return RPC_S_INVALID_BOUND;
1181 
1182         msg->Buffer = (char *)msg->Buffer + sizeof(DWORD);
1183 
1184         /* arbitrary limit for security (don't know what native does) */
1185         if (extensions->size > 256)
1186         {
1187             ERR("too many extensions: %d\n", extensions->size);
1188             return RPC_S_INVALID_BOUND;
1189         }
1190 
1191         *first_wire_orpc_extent = wire_orpc_extent = msg->Buffer;
1192         for (i = 0; i < ((extensions->size+1)&~1); i++)
1193         {
1194             if ((const char *)&wire_orpc_extent->data[0] > end)
1195                 return RPC_S_INVALID_BOUND;
1196             if (wire_orpc_extent->conformance != ((wire_orpc_extent->size+7)&~7))
1197                 return RPC_S_INVALID_BOUND;
1198             if ((const char *)&wire_orpc_extent->data[wire_orpc_extent->conformance] > end)
1199                 return RPC_S_INVALID_BOUND;
1200             TRACE("size %u, guid %s\n", wire_orpc_extent->size, debugstr_guid(&wire_orpc_extent->id));
1201             wire_orpc_extent = (WIRE_ORPC_EXTENT *)&wire_orpc_extent->data[wire_orpc_extent->conformance];
1202         }
1203         msg->Buffer = wire_orpc_extent;
1204     }
1205 
1206     return S_OK;
1207 }
1208 
1209 /* unmarshals ORPCTHIS according to NDR rules, but doesn't allocate any memory */
1210 static HRESULT unmarshal_ORPCTHIS(RPC_MESSAGE *msg, ORPCTHIS *orpcthis,
1211     ORPC_EXTENT_ARRAY *orpc_ext_array, WIRE_ORPC_EXTENT **first_wire_orpc_extent)
1212 {
1213     const char *end = (char *)msg->Buffer + msg->BufferLength;
1214 
1215     *first_wire_orpc_extent = NULL;
1216 
1217     if (msg->BufferLength < FIELD_OFFSET(WIRE_ORPCTHIS, extensions) + sizeof(DWORD))
1218     {
1219         ERR("invalid buffer length\n");
1220         return RPC_E_INVALID_HEADER;
1221     }
1222 
1223     memcpy(orpcthis, msg->Buffer, FIELD_OFFSET(WIRE_ORPCTHIS, extensions));
1224     msg->Buffer = (char *)msg->Buffer + FIELD_OFFSET(WIRE_ORPCTHIS, extensions);
1225 
1226     if ((const char *)msg->Buffer + sizeof(DWORD) > end)
1227         return RPC_E_INVALID_HEADER;
1228 
1229     if (*(DWORD *)msg->Buffer)
1230         orpcthis->extensions = orpc_ext_array;
1231     else
1232         orpcthis->extensions = NULL;
1233 
1234     msg->Buffer = (char *)msg->Buffer + sizeof(DWORD);
1235 
1236     if (orpcthis->extensions)
1237     {
1238         HRESULT hr = unmarshal_ORPC_EXTENT_ARRAY(msg, end, orpc_ext_array,
1239                                                  first_wire_orpc_extent);
1240         if (FAILED(hr))
1241             return hr;
1242     }
1243 
1244     if ((orpcthis->version.MajorVersion != COM_MAJOR_VERSION) ||
1245         (orpcthis->version.MinorVersion > COM_MINOR_VERSION))
1246     {
1247         ERR("COM version {%d, %d} not supported\n",
1248             orpcthis->version.MajorVersion, orpcthis->version.MinorVersion);
1249         return RPC_E_VERSION_MISMATCH;
1250     }
1251 
1252     if (orpcthis->flags & ~(ORPCF_LOCAL|ORPCF_RESERVED1|ORPCF_RESERVED2|ORPCF_RESERVED3|ORPCF_RESERVED4))
1253     {
1254         ERR("invalid flags 0x%x\n", orpcthis->flags & ~(ORPCF_LOCAL|ORPCF_RESERVED1|ORPCF_RESERVED2|ORPCF_RESERVED3|ORPCF_RESERVED4));
1255         return RPC_E_INVALID_HEADER;
1256     }
1257 
1258     return S_OK;
1259 }
1260 
1261 static HRESULT unmarshal_ORPCTHAT(RPC_MESSAGE *msg, ORPCTHAT *orpcthat,
1262                                   ORPC_EXTENT_ARRAY *orpc_ext_array, WIRE_ORPC_EXTENT **first_wire_orpc_extent)
1263 {
1264     const char *end = (char *)msg->Buffer + msg->BufferLength;
1265 
1266     *first_wire_orpc_extent = NULL;
1267 
1268     if (msg->BufferLength < FIELD_OFFSET(WIRE_ORPCTHAT, extensions) + sizeof(DWORD))
1269     {
1270         ERR("invalid buffer length\n");
1271         return RPC_E_INVALID_HEADER;
1272     }
1273 
1274     memcpy(orpcthat, msg->Buffer, FIELD_OFFSET(WIRE_ORPCTHAT, extensions));
1275     msg->Buffer = (char *)msg->Buffer + FIELD_OFFSET(WIRE_ORPCTHAT, extensions);
1276 
1277     if ((const char *)msg->Buffer + sizeof(DWORD) > end)
1278         return RPC_E_INVALID_HEADER;
1279 
1280     if (*(DWORD *)msg->Buffer)
1281         orpcthat->extensions = orpc_ext_array;
1282     else
1283         orpcthat->extensions = NULL;
1284 
1285     msg->Buffer = (char *)msg->Buffer + sizeof(DWORD);
1286 
1287     if (orpcthat->extensions)
1288     {
1289         HRESULT hr = unmarshal_ORPC_EXTENT_ARRAY(msg, end, orpc_ext_array,
1290                                                  first_wire_orpc_extent);
1291         if (FAILED(hr))
1292             return hr;
1293     }
1294 
1295     if (orpcthat->flags & ~(ORPCF_LOCAL|ORPCF_RESERVED1|ORPCF_RESERVED2|ORPCF_RESERVED3|ORPCF_RESERVED4))
1296     {
1297         ERR("invalid flags 0x%x\n", orpcthat->flags & ~(ORPCF_LOCAL|ORPCF_RESERVED1|ORPCF_RESERVED2|ORPCF_RESERVED3|ORPCF_RESERVED4));
1298         return RPC_E_INVALID_HEADER;
1299     }
1300 
1301     return S_OK;
1302 }
1303 
1304 void RPC_ExecuteCall(struct dispatch_params *params)
1305 {
1306     struct message_state *message_state = NULL;
1307     RPC_MESSAGE *msg = (RPC_MESSAGE *)params->msg;
1308     char *original_buffer = msg->Buffer;
1309     ORPCTHIS orpcthis;
1310     ORPC_EXTENT_ARRAY orpc_ext_array;
1311     WIRE_ORPC_EXTENT *first_wire_orpc_extent;
1312     GUID old_causality_id;
1313 
1314     /* handle ORPCTHIS and server extensions */
1315 
1316     params->hr = unmarshal_ORPCTHIS(msg, &orpcthis, &orpc_ext_array, &first_wire_orpc_extent);
1317     if (params->hr != S_OK)
1318     {
1319         msg->Buffer = original_buffer;
1320         goto exit;
1321     }
1322 
1323     message_state = HeapAlloc(GetProcessHeap(), 0, sizeof(*message_state));
1324     if (!message_state)
1325     {
1326         params->hr = E_OUTOFMEMORY;
1327         msg->Buffer = original_buffer;
1328         goto exit;
1329     }
1330 
1331     message_state->prefix_data_len = (char *)msg->Buffer - original_buffer;
1332     message_state->binding_handle = msg->Handle;
1333     message_state->bypass_rpcrt = params->bypass_rpcrt;
1334 
1335     message_state->channel_hook_info.iid = params->iid;
1336     message_state->channel_hook_info.cbSize = sizeof(message_state->channel_hook_info);
1337     message_state->channel_hook_info.uCausality = orpcthis.cid;
1338     message_state->channel_hook_info.dwServerPid = GetCurrentProcessId();
1339     message_state->channel_hook_info.iMethod = msg->ProcNum;
1340     message_state->channel_hook_info.pObject = params->iface;
1341 
1342     if (orpcthis.extensions && first_wire_orpc_extent &&
1343         orpcthis.extensions->size)
1344         ChannelHooks_ServerNotify(&message_state->channel_hook_info, msg->DataRepresentation, first_wire_orpc_extent, orpcthis.extensions->size);
1345 
1346     msg->Handle = message_state;
1347     msg->BufferLength -= message_state->prefix_data_len;
1348 
1349     /* call message filter */
1350 
1351     if (COM_CurrentApt()->filter)
1352     {
1353         DWORD handlecall;
1354         INTERFACEINFO interface_info;
1355         CALLTYPE calltype;
1356 
1357         interface_info.pUnk = params->iface;
1358         interface_info.iid = params->iid;
1359         interface_info.wMethod = msg->ProcNum;
1360 
1361         if (IsEqualGUID(&orpcthis.cid, &COM_CurrentInfo()->causality_id))
1362             calltype = CALLTYPE_NESTED;
1363         else if (COM_CurrentInfo()->pending_call_count_server == 0)
1364             calltype = CALLTYPE_TOPLEVEL;
1365         else
1366             calltype = CALLTYPE_TOPLEVEL_CALLPENDING;
1367 
1368         handlecall = IMessageFilter_HandleInComingCall(COM_CurrentApt()->filter,
1369                                                        calltype,
1370                                                        UlongToHandle(GetCurrentProcessId()),
1371                                                        0 /* FIXME */,
1372                                                        &interface_info);
1373         TRACE("IMessageFilter_HandleInComingCall returned %d\n", handlecall);
1374         switch (handlecall)
1375         {
1376         case SERVERCALL_REJECTED:
1377             params->hr = RPC_E_CALL_REJECTED;
1378             goto exit_reset_state;
1379         case SERVERCALL_RETRYLATER:
1380 #if 0 /* FIXME: handle retries on the client side before enabling this code */
1381             params->hr = RPC_E_RETRY;
1382             goto exit_reset_state;
1383 #else
1384             FIXME("retry call later not implemented\n");
1385             break;
1386 #endif
1387         case SERVERCALL_ISHANDLED:
1388         default:
1389             break;
1390         }
1391     }
1392 
1393     /* invoke the method */
1394 
1395     /* save the old causality ID - note: any calls executed while processing
1396      * messages received during the SendReceive will appear to originate from
1397      * this call - this should be checked with what Windows does */
1398     old_causality_id = COM_CurrentInfo()->causality_id;
1399     COM_CurrentInfo()->causality_id = orpcthis.cid;
1400     COM_CurrentInfo()->pending_call_count_server++;
1401     params->hr = IRpcStubBuffer_Invoke(params->stub, params->msg, params->chan);
1402     COM_CurrentInfo()->pending_call_count_server--;
1403     COM_CurrentInfo()->causality_id = old_causality_id;
1404 
1405     /* the invoke allocated a new buffer, so free the old one */
1406     if (message_state->bypass_rpcrt && original_buffer != msg->Buffer)
1407         HeapFree(GetProcessHeap(), 0, original_buffer);
1408 
1409 exit_reset_state:
1410     message_state = msg->Handle;
1411     msg->Handle = message_state->binding_handle;
1412     msg->Buffer = (char *)msg->Buffer - message_state->prefix_data_len;
1413     msg->BufferLength += message_state->prefix_data_len;
1414 
1415 exit:
1416     HeapFree(GetProcessHeap(), 0, message_state);
1417     if (params->handle) SetEvent(params->handle);
1418 }
1419 
1420 static void __RPC_STUB dispatch_rpc(RPC_MESSAGE *msg)
1421 {
1422     struct dispatch_params *params;
1423     struct stub_manager *stub_manager;
1424     APARTMENT *apt;
1425     IPID ipid;
1426     HRESULT hr;
1427 
1428     RpcBindingInqObject(msg->Handle, &ipid);
1429 
1430     TRACE("ipid = %s, iMethod = %d\n", debugstr_guid(&ipid), msg->ProcNum);
1431 
1432     params = HeapAlloc(GetProcessHeap(), 0, sizeof(*params));
1433     if (!params)
1434     {
1435         RpcRaiseException(E_OUTOFMEMORY);
1436         return;
1437     }
1438 
1439     hr = ipid_get_dispatch_params(&ipid, &apt, &stub_manager, &params->stub, &params->chan,
1440                                   &params->iid, &params->iface);
1441     if (hr != S_OK)
1442     {
1443         ERR("no apartment found for ipid %s\n", debugstr_guid(&ipid));
1444         HeapFree(GetProcessHeap(), 0, params);
1445         RpcRaiseException(hr);
1446         return;
1447     }
1448 
1449     params->msg = (RPCOLEMESSAGE *)msg;
1450     params->status = RPC_S_OK;
1451     params->hr = S_OK;
1452     params->handle = NULL;
1453     params->bypass_rpcrt = FALSE;
1454 
1455     /* Note: this is the important difference between STAs and MTAs - we
1456      * always execute RPCs to STAs in the thread that originally created the
1457      * apartment (i.e. the one that pumps messages to the window) */
1458     if (!apt->multi_threaded)
1459     {
1460         params->handle = CreateEventW(NULL, FALSE, FALSE, NULL);
1461 
1462         TRACE("Calling apartment thread 0x%08x...\n", apt->tid);
1463 
1464         if (PostMessageW(apartment_getwindow(apt), DM_EXECUTERPC, 0, (LPARAM)params))
1465             WaitForSingleObject(params->handle, INFINITE);
1466         else
1467         {
1468             ERR("PostMessage failed with error %u\n", GetLastError());
1469             IRpcChannelBuffer_Release(params->chan);
1470             IRpcStubBuffer_Release(params->stub);
1471         }
1472         CloseHandle(params->handle);
1473     }
1474     else
1475     {
1476         BOOL joined = FALSE;
1477         struct oletls *info = COM_CurrentInfo();
1478 
1479         if (!info->apt)
1480         {
1481             enter_apartment(info, COINIT_MULTITHREADED);
1482             joined = TRUE;
1483         }
1484         RPC_ExecuteCall(params);
1485         if (joined)
1486         {
1487             leave_apartment(info);
1488         }
1489     }
1490 
1491     hr = params->hr;
1492     if (params->chan)
1493         IRpcChannelBuffer_Release(params->chan);
1494     if (params->stub)
1495         IRpcStubBuffer_Release(params->stub);
1496     HeapFree(GetProcessHeap(), 0, params);
1497 
1498     stub_manager_int_release(stub_manager);
1499     apartment_release(apt);
1500 
1501     /* if IRpcStubBuffer_Invoke fails, we should raise an exception to tell
1502      * the RPC runtime that the call failed */
1503     if (hr != S_OK) RpcRaiseException(hr);
1504 }
1505 
1506 /* stub registration */
1507 HRESULT RPC_RegisterInterface(REFIID riid)
1508 {
1509     struct registered_if *rif;
1510     BOOL found = FALSE;
1511     HRESULT hr = S_OK;
1512 
1513     TRACE("(%s)\n", debugstr_guid(riid));
1514 
1515     EnterCriticalSection(&csRegIf);
1516     LIST_FOR_EACH_ENTRY(rif, &registered_interfaces, struct registered_if, entry)
1517     {
1518         if (IsEqualGUID(&rif->If.InterfaceId.SyntaxGUID, riid))
1519         {
1520             rif->refs++;
1521             found = TRUE;
1522             break;
1523         }
1524     }
1525     if (!found)
1526     {
1527         TRACE("Creating new interface\n");
1528 
1529         rif = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*rif));
1530         if (rif)
1531         {
1532             RPC_STATUS status;
1533 
1534             rif->refs = 1;
1535             rif->If.Length = sizeof(RPC_SERVER_INTERFACE);
1536             /* RPC interface ID = COM interface ID */
1537             rif->If.InterfaceId.SyntaxGUID = *riid;
1538             rif->If.DispatchTable = &rpc_dispatch;
1539             /* all other fields are 0, including the version asCOM objects
1540              * always have a version of 0.0 */
1541             status = RpcServerRegisterIfEx(
1542                 (RPC_IF_HANDLE)&rif->If,
1543                 NULL, NULL,
1544                 RPC_IF_OLE | RPC_IF_AUTOLISTEN,
1545                 RPC_C_LISTEN_MAX_CALLS_DEFAULT,
1546                 NULL);
1547             if (status == RPC_S_OK)
1548                 list_add_tail(&registered_interfaces, &rif->entry);
1549             else
1550             {
1551                 ERR("RpcServerRegisterIfEx failed with error %d\n", status);
1552                 HeapFree(GetProcessHeap(), 0, rif);
1553                 hr = HRESULT_FROM_WIN32(status);
1554             }
1555         }
1556         else
1557             hr = E_OUTOFMEMORY;
1558     }
1559     LeaveCriticalSection(&csRegIf);
1560     return hr;
1561 }
1562 
1563 /* stub unregistration */
1564 void RPC_UnregisterInterface(REFIID riid, BOOL wait)
1565 {
1566     struct registered_if *rif;
1567     EnterCriticalSection(&csRegIf);
1568     LIST_FOR_EACH_ENTRY(rif, &registered_interfaces, struct registered_if, entry)
1569     {
1570         if (IsEqualGUID(&rif->If.InterfaceId.SyntaxGUID, riid))
1571         {
1572             if (!--rif->refs)
1573             {
1574                 RpcServerUnregisterIf((RPC_IF_HANDLE)&rif->If, NULL, wait);
1575                 list_remove(&rif->entry);
1576                 HeapFree(GetProcessHeap(), 0, rif);
1577             }
1578             break;
1579         }
1580     }
1581     LeaveCriticalSection(&csRegIf);
1582 }
1583 
1584 /* get the info for an OXID, including the IPID for the rem unknown interface
1585  * and the string binding */
1586 HRESULT RPC_ResolveOxid(OXID oxid, OXID_INFO *oxid_info)
1587 {
1588     TRACE("%s\n", wine_dbgstr_longlong(oxid));
1589 
1590     oxid_info->dwTid = 0;
1591     oxid_info->dwPid = 0;
1592     oxid_info->dwAuthnHint = RPC_C_AUTHN_LEVEL_NONE;
1593     /* FIXME: this is a hack around not having an OXID resolver yet -
1594      * this function should contact the machine's OXID resolver and then it
1595      * should give us the IPID of the IRemUnknown interface */
1596     oxid_info->ipidRemUnknown.Data1 = 0xffffffff;
1597     oxid_info->ipidRemUnknown.Data2 = 0xffff;
1598     oxid_info->ipidRemUnknown.Data3 = 0xffff;
1599     memcpy(oxid_info->ipidRemUnknown.Data4, &oxid, sizeof(OXID));
1600     oxid_info->psa = NULL /* FIXME */;
1601 
1602     return S_OK;
1603 }
1604 
1605 /* make the apartment reachable by other threads and processes and create the
1606  * IRemUnknown object */
1607 void RPC_StartRemoting(struct apartment *apt)
1608 {
1609     if (!InterlockedExchange(&apt->remoting_started, TRUE))
1610     {
1611         WCHAR endpoint[200];
1612         RPC_STATUS status;
1613 
1614         get_rpc_endpoint(endpoint, &apt->oxid);
1615 
1616         status = RpcServerUseProtseqEpW(
1617             wszRpcTransport,
1618             RPC_C_PROTSEQ_MAX_REQS_DEFAULT,
1619             endpoint,
1620             NULL);
1621         if (status != RPC_S_OK)
1622             ERR("Couldn't register endpoint %s\n", debugstr_w(endpoint));
1623 
1624         /* FIXME: move remote unknown exporting into this function */
1625     }
1626     start_apartment_remote_unknown();
1627 }
1628 
1629 
1630 static HRESULT create_server(REFCLSID rclsid, HANDLE *process)
1631 {
1632     static const WCHAR  wszLocalServer32[] = { 'L','o','c','a','l','S','e','r','v','e','r','3','2',0 };
1633     static const WCHAR  embedding[] = { ' ', '-','E','m','b','e','d','d','i','n','g',0 };
1634     HKEY                key;
1635     HRESULT             hres;
1636     WCHAR               command[MAX_PATH+sizeof(embedding)/sizeof(WCHAR)];
1637     DWORD               size = (MAX_PATH+1) * sizeof(WCHAR);
1638     STARTUPINFOW        sinfo;
1639     PROCESS_INFORMATION pinfo;
1640     LONG ret;
1641 
1642     hres = COM_OpenKeyForCLSID(rclsid, wszLocalServer32, KEY_READ, &key);
1643     if (FAILED(hres)) {
1644         ERR("class %s not registered\n", debugstr_guid(rclsid));
1645         return hres;
1646     }
1647 
1648     ret = RegQueryValueExW(key, NULL, NULL, NULL, (LPBYTE)command, &size);
1649     RegCloseKey(key);
1650     if (ret) {
1651         WARN("No default value for LocalServer32 key\n");
1652         return REGDB_E_CLASSNOTREG; /* FIXME: check retval */
1653     }
1654 
1655     memset(&sinfo,0,sizeof(sinfo));
1656     sinfo.cb = sizeof(sinfo);
1657 
1658     /* EXE servers are started with the -Embedding switch. */
1659 
1660     strcatW(command, embedding);
1661 
1662     TRACE("activating local server %s for %s\n", debugstr_w(command), debugstr_guid(rclsid));
1663 
1664     /* FIXME: Win2003 supports a ServerExecutable value that is passed into
1665      * CreateProcess */
1666     if (!CreateProcessW(NULL, command, NULL, NULL, FALSE, DETACHED_PROCESS, NULL, NULL, &sinfo, &pinfo)) {
1667         WARN("failed to run local server %s\n", debugstr_w(command));
1668         return HRESULT_FROM_WIN32(GetLastError());
1669     }
1670     *process = pinfo.hProcess;
1671     CloseHandle(pinfo.hThread);
1672 
1673     return S_OK;
1674 }
1675 
1676 /*
1677  * start_local_service()  - start a service given its name and parameters
1678  */
1679 static DWORD start_local_service(LPCWSTR name, DWORD num, LPCWSTR *params)
1680 {
1681     SC_HANDLE handle, hsvc;
1682     DWORD     r = ERROR_FUNCTION_FAILED;
1683 
1684     TRACE("Starting service %s %d params\n", debugstr_w(name), num);
1685 
1686     handle = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
1687     if (!handle)
1688         return r;
1689     hsvc = OpenServiceW(handle, name, SERVICE_START);
1690     if (hsvc)
1691     {
1692         if(StartServiceW(hsvc, num, params))
1693             r = ERROR_SUCCESS;
1694         else
1695             r = GetLastError();
1696         if (r == ERROR_SERVICE_ALREADY_RUNNING)
1697             r = ERROR_SUCCESS;
1698         CloseServiceHandle(hsvc);
1699     }
1700     else
1701         r = GetLastError();
1702     CloseServiceHandle(handle);
1703 
1704     TRACE("StartService returned error %u (%s)\n", r, (r == ERROR_SUCCESS) ? "ok":"failed");
1705 
1706     return r;
1707 }
1708 
1709 /*
1710  * create_local_service()  - start a COM server in a service
1711  *
1712  *   To start a Local Service, we read the AppID value under
1713  * the class's CLSID key, then open the HKCR\\AppId key specified
1714  * there and check for a LocalService value.
1715  *
1716  * Note:  Local Services are not supported under Windows 9x
1717  */
1718 static HRESULT create_local_service(REFCLSID rclsid)
1719 {
1720     HRESULT hres;
1721     WCHAR buf[CHARS_IN_GUID];
1722     static const WCHAR szLocalService[] = { 'L','o','c','a','l','S','e','r','v','i','c','e',0 };
1723     static const WCHAR szServiceParams[] = {'S','e','r','v','i','c','e','P','a','r','a','m','s',0};
1724     HKEY hkey;
1725     LONG r;
1726     DWORD type, sz;
1727 
1728     TRACE("Attempting to start Local service for %s\n", debugstr_guid(rclsid));
1729 
1730     hres = COM_OpenKeyForAppIdFromCLSID(rclsid, KEY_READ, &hkey);
1731     if (FAILED(hres))
1732         return hres;
1733 
1734     /* read the LocalService and ServiceParameters values from the AppID key */
1735     sz = sizeof buf;
1736     r = RegQueryValueExW(hkey, szLocalService, NULL, &type, (LPBYTE)buf, &sz);
1737     if (r==ERROR_SUCCESS && type==REG_SZ)
1738     {
1739         DWORD num_args = 0;
1740         LPWSTR args[1] = { NULL };
1741 
1742         /*
1743          * FIXME: I'm not really sure how to deal with the service parameters.
1744          *        I suspect that the string returned from RegQueryValueExW
1745          *        should be split into a number of arguments by spaces.
1746          *        It would make more sense if ServiceParams contained a
1747          *        REG_MULTI_SZ here, but it's a REG_SZ for the services
1748          *        that I'm interested in for the moment.
1749          */
1750         r = RegQueryValueExW(hkey, szServiceParams, NULL, &type, NULL, &sz);
1751         if (r == ERROR_SUCCESS && type == REG_SZ && sz)
1752         {
1753             args[0] = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sz);
1754             num_args++;
1755             RegQueryValueExW(hkey, szServiceParams, NULL, &type, (LPBYTE)args[0], &sz);
1756         }
1757         r = start_local_service(buf, num_args, (LPCWSTR *)args);
1758         if (r != ERROR_SUCCESS)
1759             hres = REGDB_E_CLASSNOTREG; /* FIXME: check retval */
1760         HeapFree(GetProcessHeap(),0,args[0]);
1761     }
1762     else
1763     {
1764         WARN("No LocalService value\n");
1765         hres = REGDB_E_CLASSNOTREG; /* FIXME: check retval */
1766     }
1767     RegCloseKey(hkey);
1768 
1769     return hres;
1770 }
1771 
1772 
1773 static void get_localserver_pipe_name(WCHAR *pipefn, REFCLSID rclsid)
1774 {
1775     static const WCHAR wszPipeRef[] = {'\\','\\','.','\\','p','i','p','e','\\',0};
1776     strcpyW(pipefn, wszPipeRef);
1777     StringFromGUID2(rclsid, pipefn + sizeof(wszPipeRef)/sizeof(wszPipeRef[0]) - 1, CHARS_IN_GUID);
1778 }
1779 
1780 /* FIXME: should call to rpcss instead */
1781 HRESULT RPC_GetLocalClassObject(REFCLSID rclsid, REFIID iid, LPVOID *ppv)
1782 {
1783     HRESULT        hres;
1784     HANDLE         hPipe;
1785     WCHAR          pipefn[100];
1786     DWORD          res, bufferlen;
1787     char           marshalbuffer[200];
1788     IStream       *pStm;
1789     LARGE_INTEGER  seekto;
1790     ULARGE_INTEGER newpos;
1791     int            tries = 0;
1792     IServiceProvider *local_server;
1793 
1794     static const int MAXTRIES = 30; /* 30 seconds */
1795 
1796     TRACE("rclsid=%s, iid=%s\n", debugstr_guid(rclsid), debugstr_guid(iid));
1797 
1798     get_localserver_pipe_name(pipefn, rclsid);
1799 
1800     while (tries++ < MAXTRIES) {
1801         TRACE("waiting for %s\n", debugstr_w(pipefn));
1802 
1803         WaitNamedPipeW( pipefn, NMPWAIT_WAIT_FOREVER );
1804         hPipe = CreateFileW(pipefn, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
1805         if (hPipe == INVALID_HANDLE_VALUE) {
1806             DWORD index;
1807             DWORD start_ticks;
1808             HANDLE process = 0;
1809             if (tries == 1) {
1810                 if ( (hres = create_local_service(rclsid)) &&
1811                      (hres = create_server(rclsid, &process)) )
1812                     return hres;
1813             } else {
1814                 WARN("Connecting to %s, no response yet, retrying: le is %u\n", debugstr_w(pipefn), GetLastError());
1815             }
1816             /* wait for one second, even if messages arrive */
1817             start_ticks = GetTickCount();
1818             do {
1819                 if (SUCCEEDED(CoWaitForMultipleHandles(0, 1000, (process != 0),
1820                                                        &process, &index)) && process && !index)
1821                 {
1822                     WARN( "server for %s failed to start\n", debugstr_guid(rclsid) );
1823                     CloseHandle( hPipe );
1824                     CloseHandle( process );
1825                     return E_NOINTERFACE;
1826                 }
1827             } while (GetTickCount() - start_ticks < 1000);
1828             if (process) CloseHandle( process );
1829             continue;
1830         }
1831         bufferlen = 0;
1832         if (!ReadFile(hPipe,marshalbuffer,sizeof(marshalbuffer),&bufferlen,NULL)) {
1833             FIXME("Failed to read marshal id from classfactory of %s.\n",debugstr_guid(rclsid));
1834             CloseHandle(hPipe);
1835             Sleep(1000);
1836             continue;
1837         }
1838         TRACE("read marshal id from pipe\n");
1839         CloseHandle(hPipe);
1840         break;
1841     }
1842 
1843     if (tries >= MAXTRIES)
1844         return E_NOINTERFACE;
1845 
1846     hres = CreateStreamOnHGlobal(0,TRUE,&pStm);
1847     if (hres != S_OK) return hres;
1848     hres = IStream_Write(pStm,marshalbuffer,bufferlen,&res);
1849     if (hres != S_OK) goto out;
1850     seekto.u.LowPart = 0;seekto.u.HighPart = 0;
1851     hres = IStream_Seek(pStm,seekto,STREAM_SEEK_SET,&newpos);
1852 
1853     TRACE("unmarshalling local server\n");
1854     hres = CoUnmarshalInterface(pStm, &IID_IServiceProvider, (void**)&local_server);
1855     if(SUCCEEDED(hres))
1856         hres = IServiceProvider_QueryService(local_server, rclsid, iid, ppv);
1857     IServiceProvider_Release(local_server);
1858 out:
1859     IStream_Release(pStm);
1860     return hres;
1861 }
1862 
1863 
1864 struct local_server_params
1865 {
1866     CLSID clsid;
1867     IStream *stream;
1868     HANDLE pipe;
1869     HANDLE stop_event;
1870     HANDLE thread;
1871     BOOL multi_use;
1872 };
1873 
1874 /* FIXME: should call to rpcss instead */
1875 static DWORD WINAPI local_server_thread(LPVOID param)
1876 {
1877     struct local_server_params * lsp = param;
1878     WCHAR 		pipefn[100];
1879     HRESULT		hres;
1880     IStream		*pStm = lsp->stream;
1881     STATSTG		ststg;
1882     unsigned char	*buffer;
1883     int 		buflen;
1884     LARGE_INTEGER	seekto;
1885     ULARGE_INTEGER	newpos;
1886     ULONG		res;
1887     BOOL multi_use = lsp->multi_use;
1888     OVERLAPPED ovl;
1889     HANDLE pipe_event, hPipe = lsp->pipe, new_pipe;
1890     DWORD  bytes;
1891 
1892     TRACE("Starting threader for %s.\n",debugstr_guid(&lsp->clsid));
1893 
1894     memset(&ovl, 0, sizeof(ovl));
1895     get_localserver_pipe_name(pipefn, &lsp->clsid);
1896     ovl.hEvent = pipe_event = CreateEventW(NULL, FALSE, FALSE, NULL);
1897 
1898     while (1) {
1899         if (!ConnectNamedPipe(hPipe, &ovl))
1900         {
1901             DWORD error = GetLastError();
1902             if (error == ERROR_IO_PENDING)
1903             {
1904                 HANDLE handles[2] = { pipe_event, lsp->stop_event };
1905                 DWORD ret;
1906                 ret = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
1907                 if (ret != WAIT_OBJECT_0)
1908                     break;
1909             }
1910             /* client already connected isn't an error */
1911             else if (error != ERROR_PIPE_CONNECTED)
1912             {
1913                 ERR("ConnectNamedPipe failed with error %d\n", GetLastError());
1914                 break;
1915             }
1916         }
1917 
1918         TRACE("marshalling LocalServer to client\n");
1919 
1920         hres = IStream_Stat(pStm,&ststg,STATFLAG_NONAME);
1921         if (hres != S_OK)
1922             break;
1923 
1924         seekto.u.LowPart = 0;
1925         seekto.u.HighPart = 0;
1926         hres = IStream_Seek(pStm,seekto,STREAM_SEEK_SET,&newpos);
1927         if (hres != S_OK) {
1928             FIXME("IStream_Seek failed, %x\n",hres);
1929             break;
1930         }
1931 
1932         buflen = ststg.cbSize.u.LowPart;
1933         buffer = HeapAlloc(GetProcessHeap(),0,buflen);
1934 
1935         hres = IStream_Read(pStm,buffer,buflen,&res);
1936         if (hres != S_OK) {
1937             FIXME("Stream Read failed, %x\n",hres);
1938             HeapFree(GetProcessHeap(),0,buffer);
1939             break;
1940         }
1941 
1942         WriteFile(hPipe,buffer,buflen,&res,&ovl);
1943         GetOverlappedResult(hPipe, &ovl, &bytes, TRUE);
1944         HeapFree(GetProcessHeap(),0,buffer);
1945 
1946         FlushFileBuffers(hPipe);
1947         DisconnectNamedPipe(hPipe);
1948         TRACE("done marshalling LocalServer\n");
1949 
1950         if (!multi_use)
1951         {
1952             TRACE("single use object, shutting down pipe %s\n", debugstr_w(pipefn));
1953             break;
1954         }
1955         new_pipe = CreateNamedPipeW( pipefn, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
1956                                      PIPE_TYPE_BYTE|PIPE_WAIT, PIPE_UNLIMITED_INSTANCES,
1957                                      4096, 4096, 500 /* 0.5 second timeout */, NULL );
1958         if (new_pipe == INVALID_HANDLE_VALUE)
1959         {
1960             FIXME("pipe creation failed for %s, le is %u\n", debugstr_w(pipefn), GetLastError());
1961             break;
1962         }
1963         CloseHandle(hPipe);
1964         hPipe = new_pipe;
1965     }
1966 
1967     CloseHandle(pipe_event);
1968     CloseHandle(hPipe);
1969     return 0;
1970 }
1971 
1972 /* starts listening for a local server */
1973 HRESULT RPC_StartLocalServer(REFCLSID clsid, IStream *stream, BOOL multi_use, void **registration)
1974 {
1975     DWORD tid, err;
1976     struct local_server_params *lsp;
1977     WCHAR pipefn[100];
1978 
1979     lsp = HeapAlloc(GetProcessHeap(), 0, sizeof(*lsp));
1980     if (!lsp)
1981         return E_OUTOFMEMORY;
1982 
1983     lsp->clsid = *clsid;
1984     lsp->stream = stream;
1985     IStream_AddRef(stream);
1986     lsp->stop_event = CreateEventW(NULL, FALSE, FALSE, NULL);
1987     if (!lsp->stop_event)
1988     {
1989         HeapFree(GetProcessHeap(), 0, lsp);
1990         return HRESULT_FROM_WIN32(GetLastError());
1991     }
1992     lsp->multi_use = multi_use;
1993 
1994     get_localserver_pipe_name(pipefn, &lsp->clsid);
1995     lsp->pipe = CreateNamedPipeW(pipefn, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
1996                                  PIPE_TYPE_BYTE|PIPE_WAIT, PIPE_UNLIMITED_INSTANCES,
1997                                  4096, 4096, 500 /* 0.5 second timeout */, NULL);
1998     if (lsp->pipe == INVALID_HANDLE_VALUE)
1999     {
2000         err = GetLastError();
2001         FIXME("pipe creation failed for %s, le is %u\n", debugstr_w(pipefn), GetLastError());
2002         CloseHandle(lsp->stop_event);
2003         HeapFree(GetProcessHeap(), 0, lsp);
2004         return HRESULT_FROM_WIN32(err);
2005     }
2006 
2007     lsp->thread = CreateThread(NULL, 0, local_server_thread, lsp, 0, &tid);
2008     if (!lsp->thread)
2009     {
2010         CloseHandle(lsp->pipe);
2011         CloseHandle(lsp->stop_event);
2012         HeapFree(GetProcessHeap(), 0, lsp);
2013         return HRESULT_FROM_WIN32(GetLastError());
2014     }
2015 
2016     *registration = lsp;
2017     return S_OK;
2018 }
2019 
2020 /* stops listening for a local server */
2021 void RPC_StopLocalServer(void *registration)
2022 {
2023     struct local_server_params *lsp = registration;
2024 
2025     /* signal local_server_thread to stop */
2026     SetEvent(lsp->stop_event);
2027     /* wait for it to exit */
2028     WaitForSingleObject(lsp->thread, INFINITE);
2029 
2030     IStream_Release(lsp->stream);
2031     CloseHandle(lsp->stop_event);
2032     CloseHandle(lsp->thread);
2033     HeapFree(GetProcessHeap(), 0, lsp);
2034 }
2035