ole32: Call ServerNotify for channel hooks.
[wine] / dlls / ole32 / rpc.c
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 "config.h"
24 #include "wine/port.h"
25
26 #include <stdlib.h>
27 #include <stdarg.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <assert.h>
31
32 #define COBJMACROS
33 #define NONAMELESSUNION
34 #define NONAMELESSSTRUCT
35
36 #include "windef.h"
37 #include "winbase.h"
38 #include "winuser.h"
39 #include "winsvc.h"
40 #include "objbase.h"
41 #include "ole2.h"
42 #include "rpc.h"
43 #include "winerror.h"
44 #include "winreg.h"
45 #include "wtypes.h"
46 #include "wine/unicode.h"
47
48 #include "compobj_private.h"
49
50 #include "wine/debug.h"
51
52 WINE_DEFAULT_DEBUG_CHANNEL(ole);
53
54 static void __RPC_STUB dispatch_rpc(RPC_MESSAGE *msg);
55
56 /* we only use one function to dispatch calls for all methods - we use the
57  * RPC_IF_OLE flag to tell the RPC runtime that this is the case */
58 static RPC_DISPATCH_FUNCTION rpc_dispatch_table[1] = { dispatch_rpc }; /* (RO) */
59 static RPC_DISPATCH_TABLE rpc_dispatch = { 1, rpc_dispatch_table }; /* (RO) */
60
61 static struct list registered_interfaces = LIST_INIT(registered_interfaces); /* (CS csRegIf) */
62 static CRITICAL_SECTION csRegIf;
63 static CRITICAL_SECTION_DEBUG csRegIf_debug =
64 {
65     0, 0, &csRegIf,
66     { &csRegIf_debug.ProcessLocksList, &csRegIf_debug.ProcessLocksList },
67       0, 0, { (DWORD_PTR)(__FILE__ ": dcom registered server interfaces") }
68 };
69 static CRITICAL_SECTION csRegIf = { &csRegIf_debug, -1, 0, 0, 0, 0 };
70
71 static struct list channel_hooks = LIST_INIT(channel_hooks); /* (CS csChannelHook) */
72 static CRITICAL_SECTION csChannelHook;
73 static CRITICAL_SECTION_DEBUG csChannelHook_debug =
74 {
75     0, 0, &csChannelHook,
76     { &csChannelHook_debug.ProcessLocksList, &csChannelHook_debug.ProcessLocksList },
77       0, 0, { (DWORD_PTR)(__FILE__ ": channel hooks") }
78 };
79 static CRITICAL_SECTION csChannelHook = { &csChannelHook_debug, -1, 0, 0, 0, 0 };
80
81 static WCHAR wszRpcTransport[] = {'n','c','a','l','r','p','c',0};
82
83
84 struct registered_if
85 {
86     struct list entry;
87     DWORD refs; /* ref count */
88     RPC_SERVER_INTERFACE If; /* interface registered with the RPC runtime */
89 };
90
91 /* get the pipe endpoint specified of the specified apartment */
92 static inline void get_rpc_endpoint(LPWSTR endpoint, const OXID *oxid)
93 {
94     /* FIXME: should get endpoint from rpcss */
95     static const WCHAR wszEndpointFormat[] = {'\\','p','i','p','e','\\','O','L','E','_','%','0','8','l','x','%','0','8','l','x',0};
96     wsprintfW(endpoint, wszEndpointFormat, (DWORD)(*oxid >> 32),(DWORD)*oxid);
97 }
98
99 typedef struct
100 {
101     const IRpcChannelBufferVtbl *lpVtbl;
102     LONG                  refs;
103 } RpcChannelBuffer;
104
105 typedef struct
106 {
107     RpcChannelBuffer       super; /* superclass */
108
109     RPC_BINDING_HANDLE     bind; /* handle to the remote server */
110     OXID                   oxid; /* apartment in which the channel is valid */
111     DWORD                  dest_context; /* returned from GetDestCtx */
112     LPVOID                 dest_context_data; /* returned from GetDestCtx */
113     HANDLE                 event; /* cached event handle */
114 } ClientRpcChannelBuffer;
115
116 struct dispatch_params
117 {
118     RPCOLEMESSAGE     *msg; /* message */
119     IRpcStubBuffer    *stub; /* stub buffer, if applicable */
120     IRpcChannelBuffer *chan; /* server channel buffer, if applicable */
121     HANDLE             handle; /* handle that will become signaled when call finishes */
122     RPC_STATUS         status; /* status (out) */
123     HRESULT            hr; /* hresult (out) */
124 };
125
126 struct message_state
127 {
128     RPC_BINDING_HANDLE binding_handle;
129     ULONG prefix_data_len;
130     SChannelHookCallInfo channel_hook_info;
131 };
132
133 typedef struct
134 {
135     ULONG conformance; /* NDR */
136     GUID id;
137     ULONG size;
138     /* [size_is((size+7)&~7)] */ unsigned char data[1];
139 } WIRE_ORPC_EXTENT;
140
141 struct channel_hook_entry
142 {
143     struct list entry;
144     GUID id;
145     IChannelHook *hook;
146 };
147
148 struct channel_hook_buffer_data
149 {
150     GUID id;
151     ULONG extension_size;
152 };
153
154
155 /* Channel Hook Functions */
156
157 static ULONG ChannelHooks_ClientGetSize(SChannelHookCallInfo *info,
158     struct channel_hook_buffer_data **data, unsigned int *hook_count,
159     ULONG *extension_count)
160 {
161     struct channel_hook_entry *entry;
162     ULONG total_size = 0;
163     unsigned int hook_index = 0;
164
165     *hook_count = 0;
166     *extension_count = 0;
167
168     EnterCriticalSection(&csChannelHook);
169
170     LIST_FOR_EACH_ENTRY(entry, &channel_hooks, struct channel_hook_entry, entry)
171         (*hook_count)++;
172
173     if (hook_count)
174         *data = HeapAlloc(GetProcessHeap(), 0, *hook_count * sizeof(struct channel_hook_buffer_data));
175     else
176         *data = NULL;
177
178     LIST_FOR_EACH_ENTRY(entry, &channel_hooks, struct channel_hook_entry, entry)
179     {
180         ULONG extension_size = 0;
181
182         IChannelHook_ClientGetSize(entry->hook, &entry->id, &info->iid, &extension_size);
183
184         TRACE("%s: extension_size = %u\n", debugstr_guid(&entry->id), extension_size);
185
186         extension_size = (extension_size+7)&~7;
187         (*data)[hook_index].id = entry->id;
188         (*data)[hook_index].extension_size = extension_size;
189
190         /* an extension is only put onto the wire if it has data to write */
191         if (extension_size)
192         {
193             total_size += FIELD_OFFSET(WIRE_ORPC_EXTENT, data[extension_size]);
194             (*extension_count)++;
195         }
196
197         hook_index++;
198     }
199
200     LeaveCriticalSection(&csChannelHook);
201
202     return total_size;
203 }
204
205 static unsigned char * ChannelHooks_ClientFillBuffer(SChannelHookCallInfo *info,
206     unsigned char *buffer, struct channel_hook_buffer_data *data,
207     unsigned int hook_count)
208 {
209     struct channel_hook_entry *entry;
210
211     EnterCriticalSection(&csChannelHook);
212
213     LIST_FOR_EACH_ENTRY(entry, &channel_hooks, struct channel_hook_entry, entry)
214     {
215         unsigned int i;
216         ULONG extension_size = 0;
217         WIRE_ORPC_EXTENT *wire_orpc_extent = (WIRE_ORPC_EXTENT *)buffer;
218
219         for (i = 0; i < hook_count; i++)
220             if (IsEqualGUID(&entry->id, &data[i].id))
221                 extension_size = data[i].extension_size;
222
223         /* an extension is only put onto the wire if it has data to write */
224         if (!extension_size)
225             continue;
226
227         IChannelHook_ClientFillBuffer(entry->hook, &entry->id, &info->iid,
228             &extension_size, buffer + FIELD_OFFSET(WIRE_ORPC_EXTENT, data[0]));
229
230         TRACE("%s: extension_size = %u\n", debugstr_guid(&entry->id), extension_size);
231
232         /* FIXME: set unused portion of wire_orpc_extent->data to 0? */
233
234         wire_orpc_extent->conformance = (extension_size+7)&~7;
235         wire_orpc_extent->size = extension_size;
236         memcpy(&wire_orpc_extent->id, &entry->id, sizeof(wire_orpc_extent->id));
237         buffer += FIELD_OFFSET(WIRE_ORPC_EXTENT, data[wire_orpc_extent->conformance]);
238     }
239
240     LeaveCriticalSection(&csChannelHook);
241
242     HeapFree(GetProcessHeap(), 0, data);
243
244     return buffer;
245 }
246
247 static void ChannelHooks_ServerNotify(SChannelHookCallInfo *info,
248     DWORD lDataRep, WIRE_ORPC_EXTENT *first_wire_orpc_extent,
249     ULONG extension_count)
250 {
251     struct channel_hook_entry *entry;
252     ULONG i;
253
254     EnterCriticalSection(&csChannelHook);
255
256     LIST_FOR_EACH_ENTRY(entry, &channel_hooks, struct channel_hook_entry, entry)
257     {
258         WIRE_ORPC_EXTENT *wire_orpc_extent;
259         for (i = 0, wire_orpc_extent = first_wire_orpc_extent;
260              i < extension_count;
261              i++, wire_orpc_extent = (WIRE_ORPC_EXTENT *)&wire_orpc_extent->data[wire_orpc_extent->conformance])
262         {
263             if (IsEqualGUID(&entry->id, &wire_orpc_extent->id))
264                 break;
265         }
266         if (i == extension_count) wire_orpc_extent = NULL;
267
268         IChannelHook_ServerNotify(entry->hook, &entry->id, &info->iid,
269             wire_orpc_extent ? wire_orpc_extent->size : 0,
270             wire_orpc_extent ? wire_orpc_extent->data : NULL,
271             lDataRep);
272     }
273
274     LeaveCriticalSection(&csChannelHook);
275 }
276
277 HRESULT RPC_RegisterChannelHook(REFGUID rguid, IChannelHook *hook)
278 {
279     struct channel_hook_entry *entry;
280
281     TRACE("(%s, %p)\n", debugstr_guid(rguid), hook);
282
283     entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
284     if (!entry)
285         return E_OUTOFMEMORY;
286
287     memcpy(&entry->id, rguid, sizeof(entry->id));
288     entry->hook = hook;
289     IChannelHook_AddRef(hook);
290
291     EnterCriticalSection(&csChannelHook);
292     list_add_tail(&channel_hooks, &entry->entry);
293     LeaveCriticalSection(&csChannelHook);
294
295     return S_OK;
296 }
297
298 void RPC_UnregisterAllChannelHooks(void)
299 {
300     struct channel_hook_entry *cursor;
301     struct channel_hook_entry *cursor2;
302
303     EnterCriticalSection(&csChannelHook);
304     LIST_FOR_EACH_ENTRY_SAFE(cursor, cursor2, &channel_hooks, struct channel_hook_entry, entry)
305         HeapFree(GetProcessHeap(), 0, cursor);
306     LeaveCriticalSection(&csChannelHook);
307 }
308
309 /* RPC Channel Buffer Functions */
310
311 static HRESULT WINAPI RpcChannelBuffer_QueryInterface(LPRPCCHANNELBUFFER iface, REFIID riid, LPVOID *ppv)
312 {
313     *ppv = NULL;
314     if (IsEqualIID(riid,&IID_IRpcChannelBuffer) || IsEqualIID(riid,&IID_IUnknown))
315     {
316         *ppv = (LPVOID)iface;
317         IUnknown_AddRef(iface);
318         return S_OK;
319     }
320     return E_NOINTERFACE;
321 }
322
323 static ULONG WINAPI RpcChannelBuffer_AddRef(LPRPCCHANNELBUFFER iface)
324 {
325     RpcChannelBuffer *This = (RpcChannelBuffer *)iface;
326     return InterlockedIncrement(&This->refs);
327 }
328
329 static ULONG WINAPI ServerRpcChannelBuffer_Release(LPRPCCHANNELBUFFER iface)
330 {
331     RpcChannelBuffer *This = (RpcChannelBuffer *)iface;
332     ULONG ref;
333
334     ref = InterlockedDecrement(&This->refs);
335     if (ref)
336         return ref;
337
338     HeapFree(GetProcessHeap(), 0, This);
339     return 0;
340 }
341
342 static ULONG WINAPI ClientRpcChannelBuffer_Release(LPRPCCHANNELBUFFER iface)
343 {
344     ClientRpcChannelBuffer *This = (ClientRpcChannelBuffer *)iface;
345     ULONG ref;
346
347     ref = InterlockedDecrement(&This->super.refs);
348     if (ref)
349         return ref;
350
351     if (This->event) CloseHandle(This->event);
352     RpcBindingFree(&This->bind);
353     HeapFree(GetProcessHeap(), 0, This);
354     return 0;
355 }
356
357 static HRESULT WINAPI ServerRpcChannelBuffer_GetBuffer(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE* olemsg, REFIID riid)
358 {
359     RpcChannelBuffer *This = (RpcChannelBuffer *)iface;
360     RPC_MESSAGE *msg = (RPC_MESSAGE *)olemsg;
361     RPC_STATUS status;
362     struct message_state *message_state;
363
364     TRACE("(%p)->(%p,%s)\n", This, olemsg, debugstr_guid(riid));
365
366     message_state = (struct message_state *)msg->Handle;
367     /* restore the binding handle and the real start of data */
368     msg->Handle = message_state->binding_handle;
369     msg->Buffer = (char *)msg->Buffer - message_state->prefix_data_len;
370
371     status = I_RpcGetBuffer(msg);
372
373     /* save away the message state again */
374     msg->Handle = message_state;
375     message_state->prefix_data_len = 0;
376
377     TRACE("-- %ld\n", status);
378
379     return HRESULT_FROM_WIN32(status);
380 }
381
382 static HRESULT WINAPI ClientRpcChannelBuffer_GetBuffer(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE* olemsg, REFIID riid)
383 {
384     ClientRpcChannelBuffer *This = (ClientRpcChannelBuffer *)iface;
385     RPC_MESSAGE *msg = (RPC_MESSAGE *)olemsg;
386     RPC_CLIENT_INTERFACE *cif;
387     RPC_STATUS status;
388     ORPCTHIS *orpcthis;
389     struct message_state *message_state;
390     ULONG extensions_size;
391     struct channel_hook_buffer_data *channel_hook_data;
392     unsigned int channel_hook_count;
393     ULONG extension_count;
394
395     TRACE("(%p)->(%p,%s)\n", This, olemsg, debugstr_guid(riid));
396
397     cif = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(RPC_CLIENT_INTERFACE));
398     if (!cif)
399         return E_OUTOFMEMORY;
400
401     message_state = HeapAlloc(GetProcessHeap(), 0, sizeof(*message_state));
402     if (!message_state)
403     {
404         HeapFree(GetProcessHeap(), 0, cif);
405         return E_OUTOFMEMORY;
406     }
407
408     cif->Length = sizeof(RPC_CLIENT_INTERFACE);
409     /* RPC interface ID = COM interface ID */
410     cif->InterfaceId.SyntaxGUID = *riid;
411     /* COM objects always have a version of 0.0 */
412     cif->InterfaceId.SyntaxVersion.MajorVersion = 0;
413     cif->InterfaceId.SyntaxVersion.MinorVersion = 0;
414     msg->Handle = This->bind;
415     msg->RpcInterfaceInformation = cif;
416
417     message_state->channel_hook_info.iid = *riid;
418     message_state->channel_hook_info.cbSize = sizeof(message_state->channel_hook_info);
419     message_state->channel_hook_info.uCausality = GUID_NULL; /* FIXME */
420     message_state->channel_hook_info.dwServerPid = 0; /* FIXME */
421     message_state->channel_hook_info.iMethod = msg->ProcNum;
422     message_state->channel_hook_info.pObject = NULL; /* only present on server-side */
423
424     extensions_size = ChannelHooks_ClientGetSize(&message_state->channel_hook_info,
425         &channel_hook_data, &channel_hook_count, &extension_count);
426
427     msg->BufferLength += FIELD_OFFSET(ORPCTHIS, extensions) + 4;
428     if (extensions_size)
429     {
430         msg->BufferLength += FIELD_OFFSET(ORPC_EXTENT_ARRAY, extent) + 2*sizeof(DWORD) + extensions_size;
431         if (extension_count & 1)
432             msg->BufferLength += FIELD_OFFSET(WIRE_ORPC_EXTENT, data[0]);
433     }
434
435     status = I_RpcGetBuffer(msg);
436
437     message_state->prefix_data_len = 0;
438     message_state->binding_handle = This->bind;
439     msg->Handle = message_state;
440
441     if (status == RPC_S_OK)
442     {
443         orpcthis = (ORPCTHIS *)msg->Buffer;
444         msg->Buffer = (char *)msg->Buffer + FIELD_OFFSET(ORPCTHIS, extensions);
445
446         orpcthis->version.MajorVersion = COM_MAJOR_VERSION;
447         orpcthis->version.MinorVersion = COM_MINOR_VERSION;
448         orpcthis->flags = message_state->channel_hook_info.dwServerPid ? ORPCF_LOCAL : ORPCF_NULL;
449         orpcthis->reserved1 = 0;
450         orpcthis->cid = message_state->channel_hook_info.uCausality;
451
452         /* NDR representation of orpcthis->extensions */
453         *(DWORD *)msg->Buffer = extensions_size ? 1 : 0;
454         msg->Buffer = (char *)msg->Buffer + sizeof(DWORD);
455
456         if (extensions_size)
457         {
458             ORPC_EXTENT_ARRAY *orpc_extent_array = msg->Buffer;
459             orpc_extent_array->size = extension_count;
460             orpc_extent_array->reserved = 0;
461             msg->Buffer = (char *)msg->Buffer + FIELD_OFFSET(ORPC_EXTENT_ARRAY, extent);
462             /* NDR representation of orpc_extent_array->extent */
463             *(DWORD *)msg->Buffer = 1;
464             msg->Buffer = (char *)msg->Buffer + sizeof(DWORD);
465             /* NDR representation of [size_is] attribute of orpc_extent_array->extent */
466             *(DWORD *)msg->Buffer = (extension_count + 1) & ~1;
467             msg->Buffer = (char *)msg->Buffer + sizeof(DWORD);
468
469             msg->Buffer = ChannelHooks_ClientFillBuffer(&message_state->channel_hook_info,
470                 msg->Buffer, channel_hook_data, channel_hook_count);
471
472             /* we must add a dummy extension if there is an odd extension
473              * count to meet the contract specified by the size_is attribute */
474             if (extension_count & 1)
475             {
476                 WIRE_ORPC_EXTENT *wire_orpc_extent = msg->Buffer;
477                 wire_orpc_extent->conformance = 0;
478                 memcpy(&wire_orpc_extent->id, &GUID_NULL, sizeof(wire_orpc_extent->id));
479                 wire_orpc_extent->size = 0;
480                 msg->Buffer = (char *)msg->Buffer + FIELD_OFFSET(WIRE_ORPC_EXTENT, data[0]);
481             }
482         }
483
484         /* store the prefixed data length so that we can restore the real buffer
485          * pointer in ClientRpcChannelBuffer_SendReceive. */
486         message_state->prefix_data_len = (char *)msg->Buffer - (char *)orpcthis;
487         msg->BufferLength -= message_state->prefix_data_len;
488     }
489
490     TRACE("-- %ld\n", status);
491
492     return HRESULT_FROM_WIN32(status);
493 }
494
495 static HRESULT WINAPI ServerRpcChannelBuffer_SendReceive(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE *olemsg, ULONG *pstatus)
496 {
497     FIXME("stub\n");
498     return E_NOTIMPL;
499 }
500
501 static HANDLE ClientRpcChannelBuffer_GetEventHandle(ClientRpcChannelBuffer *This)
502 {
503     HANDLE event = InterlockedExchangePointer(&This->event, NULL);
504
505     /* Note: must be auto-reset event so we can reuse it without a call
506      * to ResetEvent */
507     if (!event) event = CreateEventW(NULL, FALSE, FALSE, NULL);
508
509     return event;
510 }
511
512 static void ClientRpcChannelBuffer_ReleaseEventHandle(ClientRpcChannelBuffer *This, HANDLE event)
513 {
514     if (InterlockedCompareExchangePointer(&This->event, event, NULL))
515         /* already a handle cached in This */
516         CloseHandle(event);
517 }
518
519 /* this thread runs an outgoing RPC */
520 static DWORD WINAPI rpc_sendreceive_thread(LPVOID param)
521 {
522     struct dispatch_params *data = (struct dispatch_params *) param;
523
524     /* FIXME: trap and rethrow RPC exceptions in app thread */
525     data->status = I_RpcSendReceive((RPC_MESSAGE *)data->msg);
526
527     TRACE("completed with status 0x%lx\n", data->status);
528
529     SetEvent(data->handle);
530
531     return 0;
532 }
533
534 static inline HRESULT ClientRpcChannelBuffer_IsCorrectApartment(ClientRpcChannelBuffer *This, APARTMENT *apt)
535 {
536     OXID oxid;
537     if (!apt)
538         return S_FALSE;
539     if (apartment_getoxid(apt, &oxid) != S_OK)
540         return S_FALSE;
541     if (This->oxid != oxid)
542         return S_FALSE;
543     return S_OK;
544 }
545
546 static HRESULT WINAPI ClientRpcChannelBuffer_SendReceive(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE *olemsg, ULONG *pstatus)
547 {
548     ClientRpcChannelBuffer *This = (ClientRpcChannelBuffer *)iface;
549     HRESULT hr;
550     RPC_MESSAGE *msg = (RPC_MESSAGE *)olemsg;
551     RPC_STATUS status;
552     DWORD index;
553     struct dispatch_params *params;
554     APARTMENT *apt = NULL;
555     IPID ipid;
556     struct message_state *message_state;
557
558     TRACE("(%p) iMethod=%d\n", olemsg, olemsg->iMethod);
559
560     hr = ClientRpcChannelBuffer_IsCorrectApartment(This, COM_CurrentApt());
561     if (hr != S_OK)
562     {
563         ERR("called from wrong apartment, should have been 0x%s\n",
564             wine_dbgstr_longlong(This->oxid));
565         return RPC_E_WRONG_THREAD;
566     }
567
568     params = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*params));
569     if (!params) return E_OUTOFMEMORY;
570
571     message_state = (struct message_state *)msg->Handle;
572     /* restore the binding handle and the real start of data */
573     msg->Handle = message_state->binding_handle;
574     msg->Buffer = (char *)msg->Buffer - message_state->prefix_data_len;
575     msg->BufferLength += message_state->prefix_data_len;
576
577     params->msg = olemsg;
578     params->status = RPC_S_OK;
579     params->hr = S_OK;
580
581     /* Note: this is an optimization in the Microsoft OLE runtime that we need
582      * to copy, as shown by the test_no_couninitialize_client test. without
583      * short-circuiting the RPC runtime in the case below, the test will
584      * deadlock on the loader lock due to the RPC runtime needing to create
585      * a thread to process the RPC when this function is called indirectly
586      * from DllMain */
587
588     RpcBindingInqObject(message_state->binding_handle, &ipid);
589     hr = ipid_get_dispatch_params(&ipid, &apt, &params->stub, &params->chan);
590     params->handle = ClientRpcChannelBuffer_GetEventHandle(This);
591     if ((hr == S_OK) && !apt->multi_threaded)
592     {
593         TRACE("Calling apartment thread 0x%08x...\n", apt->tid);
594
595         if (!PostMessageW(apartment_getwindow(apt), DM_EXECUTERPC, 0, (LPARAM)params))
596         {
597             ERR("PostMessage failed with error %d\n", GetLastError());
598             hr = HRESULT_FROM_WIN32(GetLastError());
599         }
600     }
601     else
602     {
603         if (hr == S_OK)
604         {
605             /* otherwise, we go via RPC runtime so the stub and channel aren't
606              * needed here */
607             IRpcStubBuffer_Release(params->stub);
608             params->stub = NULL;
609             IRpcChannelBuffer_Release(params->chan);
610             params->chan = NULL;
611         }
612
613         /* we use a separate thread here because we need to be able to
614          * pump the message loop in the application thread: if we do not,
615          * any windows created by this thread will hang and RPCs that try
616          * and re-enter this STA from an incoming server thread will
617          * deadlock. InstallShield is an example of that.
618          */
619         if (!QueueUserWorkItem(rpc_sendreceive_thread, params, WT_EXECUTEDEFAULT))
620         {
621             ERR("QueueUserWorkItem failed with error %x\n", GetLastError());
622             hr = E_UNEXPECTED;
623         }
624         else
625             hr = S_OK;
626     }
627     if (apt) apartment_release(apt);
628
629     if (hr == S_OK)
630     {
631         if (WaitForSingleObject(params->handle, 0))
632             hr = CoWaitForMultipleHandles(0, INFINITE, 1, &params->handle, &index);
633     }
634     ClientRpcChannelBuffer_ReleaseEventHandle(This, params->handle);
635
636     /* save away the message state again */
637     msg->Handle = message_state;
638     message_state->prefix_data_len = 0;
639
640     if (hr == S_OK) hr = params->hr;
641
642     status = params->status;
643     HeapFree(GetProcessHeap(), 0, params);
644     params = NULL;
645
646     if (hr) return hr;
647     
648     if (pstatus) *pstatus = status;
649
650     TRACE("RPC call status: 0x%lx\n", status);
651     if (status == RPC_S_OK)
652         hr = S_OK;
653     else if (status == RPC_S_CALL_FAILED)
654         hr = *(HRESULT *)olemsg->Buffer;
655     else
656         hr = HRESULT_FROM_WIN32(status);
657
658     TRACE("-- 0x%08x\n", hr);
659
660     return hr;
661 }
662
663 static HRESULT WINAPI ServerRpcChannelBuffer_FreeBuffer(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE* olemsg)
664 {
665     RPC_MESSAGE *msg = (RPC_MESSAGE *)olemsg;
666     RPC_STATUS status;
667     struct message_state *message_state;
668
669     TRACE("(%p)\n", msg);
670
671     message_state = (struct message_state *)msg->Handle;
672     /* restore the binding handle and the real start of data */
673     msg->Handle = message_state->binding_handle;
674     msg->Buffer = (char *)msg->Buffer - message_state->prefix_data_len;
675     msg->BufferLength += message_state->prefix_data_len;
676     message_state->prefix_data_len = 0;
677
678     status = I_RpcFreeBuffer(msg);
679
680     msg->Handle = message_state;
681
682     TRACE("-- %ld\n", status);
683
684     return HRESULT_FROM_WIN32(status);
685 }
686
687 static HRESULT WINAPI ClientRpcChannelBuffer_FreeBuffer(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE* olemsg)
688 {
689     RPC_MESSAGE *msg = (RPC_MESSAGE *)olemsg;
690     RPC_STATUS status;
691     struct message_state *message_state;
692
693     TRACE("(%p)\n", msg);
694
695     message_state = (struct message_state *)msg->Handle;
696     /* restore the binding handle and the real start of data */
697     msg->Handle = message_state->binding_handle;
698     msg->Buffer = (char *)msg->Buffer - message_state->prefix_data_len;
699     msg->BufferLength += message_state->prefix_data_len;
700
701     status = I_RpcFreeBuffer(msg);
702
703     HeapFree(GetProcessHeap(), 0, msg->RpcInterfaceInformation);
704     msg->RpcInterfaceInformation = NULL;
705     HeapFree(GetProcessHeap(), 0, message_state);
706
707     TRACE("-- %ld\n", status);
708
709     return HRESULT_FROM_WIN32(status);
710 }
711
712 static HRESULT WINAPI ClientRpcChannelBuffer_GetDestCtx(LPRPCCHANNELBUFFER iface, DWORD* pdwDestContext, void** ppvDestContext)
713 {
714     ClientRpcChannelBuffer *This = (ClientRpcChannelBuffer *)iface;
715
716     TRACE("(%p,%p)\n", pdwDestContext, ppvDestContext);
717
718     *pdwDestContext = This->dest_context;
719     *ppvDestContext = This->dest_context_data;
720
721     return S_OK;
722 }
723
724 static HRESULT WINAPI ServerRpcChannelBuffer_GetDestCtx(LPRPCCHANNELBUFFER iface, DWORD* pdwDestContext, void** ppvDestContext)
725 {
726     FIXME("(%p,%p), stub!\n", pdwDestContext, ppvDestContext);
727     return E_FAIL;
728 }
729
730 static HRESULT WINAPI RpcChannelBuffer_IsConnected(LPRPCCHANNELBUFFER iface)
731 {
732     TRACE("()\n");
733     /* native does nothing too */
734     return S_OK;
735 }
736
737 static const IRpcChannelBufferVtbl ClientRpcChannelBufferVtbl =
738 {
739     RpcChannelBuffer_QueryInterface,
740     RpcChannelBuffer_AddRef,
741     ClientRpcChannelBuffer_Release,
742     ClientRpcChannelBuffer_GetBuffer,
743     ClientRpcChannelBuffer_SendReceive,
744     ClientRpcChannelBuffer_FreeBuffer,
745     ClientRpcChannelBuffer_GetDestCtx,
746     RpcChannelBuffer_IsConnected
747 };
748
749 static const IRpcChannelBufferVtbl ServerRpcChannelBufferVtbl =
750 {
751     RpcChannelBuffer_QueryInterface,
752     RpcChannelBuffer_AddRef,
753     ServerRpcChannelBuffer_Release,
754     ServerRpcChannelBuffer_GetBuffer,
755     ServerRpcChannelBuffer_SendReceive,
756     ServerRpcChannelBuffer_FreeBuffer,
757     ServerRpcChannelBuffer_GetDestCtx,
758     RpcChannelBuffer_IsConnected
759 };
760
761 /* returns a channel buffer for proxies */
762 HRESULT RPC_CreateClientChannel(const OXID *oxid, const IPID *ipid,
763                                 DWORD dest_context, void *dest_context_data,
764                                 IRpcChannelBuffer **chan)
765 {
766     ClientRpcChannelBuffer *This;
767     WCHAR                   endpoint[200];
768     RPC_BINDING_HANDLE      bind;
769     RPC_STATUS              status;
770     LPWSTR                  string_binding;
771
772     /* connect to the apartment listener thread */
773     get_rpc_endpoint(endpoint, oxid);
774
775     TRACE("proxy pipe: connecting to endpoint: %s\n", debugstr_w(endpoint));
776
777     status = RpcStringBindingComposeW(
778         NULL,
779         wszRpcTransport,
780         NULL,
781         endpoint,
782         NULL,
783         &string_binding);
784         
785     if (status == RPC_S_OK)
786     {
787         status = RpcBindingFromStringBindingW(string_binding, &bind);
788
789         if (status == RPC_S_OK)
790         {
791             IPID ipid2 = *ipid; /* why can't RpcBindingSetObject take a const? */
792             status = RpcBindingSetObject(bind, &ipid2);
793             if (status != RPC_S_OK)
794                 RpcBindingFree(&bind);
795         }
796
797         RpcStringFreeW(&string_binding);
798     }
799
800     if (status != RPC_S_OK)
801     {
802         ERR("Couldn't get binding for endpoint %s, status = %ld\n", debugstr_w(endpoint), status);
803         return HRESULT_FROM_WIN32(status);
804     }
805
806     This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
807     if (!This)
808     {
809         RpcBindingFree(&bind);
810         return E_OUTOFMEMORY;
811     }
812
813     This->super.lpVtbl = &ClientRpcChannelBufferVtbl;
814     This->super.refs = 1;
815     This->bind = bind;
816     apartment_getoxid(COM_CurrentApt(), &This->oxid);
817     This->dest_context = dest_context;
818     This->dest_context_data = dest_context_data;
819     This->event = NULL;
820
821     *chan = (IRpcChannelBuffer*)This;
822
823     return S_OK;
824 }
825
826 HRESULT RPC_CreateServerChannel(IRpcChannelBuffer **chan)
827 {
828     RpcChannelBuffer *This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
829     if (!This)
830         return E_OUTOFMEMORY;
831
832     This->lpVtbl = &ServerRpcChannelBufferVtbl;
833     This->refs = 1;
834     
835     *chan = (IRpcChannelBuffer*)This;
836
837     return S_OK;
838 }
839
840 /* unmarshals ORPCTHIS according to NDR rules, but doesn't allocate any memory */
841 static HRESULT unmarshal_ORPCTHIS(RPC_MESSAGE *msg, ORPCTHIS *orpcthis,
842     ORPC_EXTENT_ARRAY *orpc_ext_array, WIRE_ORPC_EXTENT **first_wire_orpc_extent)
843 {
844     const char *end = (char *)msg->Buffer + msg->BufferLength;
845
846     *first_wire_orpc_extent = NULL;
847
848     if (msg->BufferLength < FIELD_OFFSET(ORPCTHIS, extensions) + 4)
849     {
850         ERR("invalid buffer length\n");
851         return RPC_E_INVALID_HEADER;
852     }
853
854     memcpy(orpcthis, msg->Buffer, FIELD_OFFSET(ORPCTHIS, extensions));
855     msg->Buffer = (char *)msg->Buffer + FIELD_OFFSET(ORPCTHIS, extensions);
856
857     if ((const char *)msg->Buffer + sizeof(DWORD) > end)
858         return RPC_E_INVALID_HEADER;
859
860     if (*(DWORD *)msg->Buffer)
861         orpcthis->extensions = orpc_ext_array;
862     else
863         orpcthis->extensions = NULL;
864
865     msg->Buffer = (char *)msg->Buffer + sizeof(DWORD);
866
867     if (orpcthis->extensions)
868     {
869         DWORD pointer_id;
870         DWORD i;
871
872         memcpy(orpcthis->extensions, msg->Buffer, FIELD_OFFSET(ORPC_EXTENT_ARRAY, extent));
873         msg->Buffer = (char *)msg->Buffer + FIELD_OFFSET(ORPC_EXTENT_ARRAY, extent);
874
875         if ((const char *)msg->Buffer + 2 * sizeof(DWORD) > end)
876             return RPC_E_INVALID_HEADER;
877
878         pointer_id = *(DWORD *)msg->Buffer;
879         msg->Buffer = (char *)msg->Buffer + sizeof(DWORD);
880         orpcthis->extensions->extent = NULL;
881
882         if (pointer_id)
883         {
884             WIRE_ORPC_EXTENT *wire_orpc_extent;
885
886             /* conformance */
887             if (*(DWORD *)msg->Buffer != ((orpcthis->extensions->size+1)&~1))
888                 return RPC_S_INVALID_BOUND;
889
890             msg->Buffer = (char *)msg->Buffer + sizeof(DWORD);
891
892             /* arbritary limit for security (don't know what native does) */
893             if (orpcthis->extensions->size > 256)
894             {
895                 ERR("too many extensions: %ld\n", orpcthis->extensions->size);
896                 return RPC_S_INVALID_BOUND;
897             }
898
899             *first_wire_orpc_extent = wire_orpc_extent = (WIRE_ORPC_EXTENT *)msg->Buffer;
900             for (i = 0; i < ((orpcthis->extensions->size+1)&~1); i++)
901             {
902                 if ((const char *)&wire_orpc_extent->data[0] > end)
903                     return RPC_S_INVALID_BOUND;
904                 if (wire_orpc_extent->conformance != ((wire_orpc_extent->size+7)&~7))
905                     return RPC_S_INVALID_BOUND;
906                 if ((const char *)&wire_orpc_extent->data[wire_orpc_extent->conformance] > end)
907                     return RPC_S_INVALID_BOUND;
908                 TRACE("size %u, guid %s\n", wire_orpc_extent->size, debugstr_guid(&wire_orpc_extent->id));
909                 wire_orpc_extent = (WIRE_ORPC_EXTENT *)&wire_orpc_extent->data[wire_orpc_extent->conformance];
910             }
911             msg->Buffer = wire_orpc_extent;
912         }
913     }
914
915     if ((orpcthis->version.MajorVersion != COM_MAJOR_VERSION) ||
916         (orpcthis->version.MinorVersion > COM_MINOR_VERSION))
917     {
918         ERR("COM version {%d, %d} not supported\n",
919             orpcthis->version.MajorVersion, orpcthis->version.MinorVersion);
920         return RPC_E_VERSION_MISMATCH;
921     }
922
923     if (orpcthis->flags & ~(ORPCF_LOCAL|ORPCF_RESERVED1|ORPCF_RESERVED2|ORPCF_RESERVED3|ORPCF_RESERVED4))
924     {
925         ERR("invalid flags 0x%lx\n", orpcthis->flags & ~(ORPCF_LOCAL|ORPCF_RESERVED1|ORPCF_RESERVED2|ORPCF_RESERVED3|ORPCF_RESERVED4));
926         return RPC_E_INVALID_HEADER;
927     }
928
929     return S_OK;
930 }
931
932 void RPC_ExecuteCall(struct dispatch_params *params)
933 {
934     struct message_state *message_state;
935     RPC_MESSAGE *msg = (RPC_MESSAGE *)params->msg;
936     char *original_buffer = msg->Buffer;
937     ORPCTHIS orpcthis;
938     ORPC_EXTENT_ARRAY orpc_ext_array;
939     WIRE_ORPC_EXTENT *first_wire_orpc_extent;
940
941     params->hr = unmarshal_ORPCTHIS(msg, &orpcthis, &orpc_ext_array, &first_wire_orpc_extent);
942     if (params->hr != S_OK)
943         goto exit;
944
945     message_state = HeapAlloc(GetProcessHeap(), 0, sizeof(*message_state));
946     if (!message_state)
947     {
948         params->hr = E_OUTOFMEMORY;
949         goto exit;
950     }
951
952     message_state->prefix_data_len = original_buffer - (char *)msg->Buffer;
953     message_state->binding_handle = msg->Handle;
954
955     message_state->channel_hook_info.iid = IID_NULL; /* FIXME */
956     message_state->channel_hook_info.cbSize = sizeof(message_state->channel_hook_info);
957     message_state->channel_hook_info.uCausality = orpcthis.cid;
958     message_state->channel_hook_info.dwServerPid = GetCurrentProcessId();
959     message_state->channel_hook_info.iMethod = msg->ProcNum;
960     message_state->channel_hook_info.pObject = NULL; /* FIXME */
961
962     if (orpcthis.extensions && first_wire_orpc_extent &&
963         orpcthis.extensions->size)
964         ChannelHooks_ServerNotify(&message_state->channel_hook_info, msg->DataRepresentation, first_wire_orpc_extent, orpcthis.extensions->size);
965
966     msg->Handle = message_state;
967     msg->BufferLength -= message_state->prefix_data_len;
968
969     /* invoke the method */
970
971     params->hr = IRpcStubBuffer_Invoke(params->stub, params->msg, params->chan);
972
973     message_state = (struct message_state *)msg->Handle;
974     msg->Handle = message_state->binding_handle;
975     msg->Buffer = (char *)msg->Buffer - message_state->prefix_data_len;
976     msg->BufferLength += message_state->prefix_data_len;
977     HeapFree(GetProcessHeap(), 0, message_state);
978
979 exit:
980     IRpcStubBuffer_Release(params->stub);
981     IRpcChannelBuffer_Release(params->chan);
982     if (params->handle) SetEvent(params->handle);
983 }
984
985 static void __RPC_STUB dispatch_rpc(RPC_MESSAGE *msg)
986 {
987     struct dispatch_params *params;
988     APARTMENT *apt;
989     IPID ipid;
990     HRESULT hr;
991
992     RpcBindingInqObject(msg->Handle, &ipid);
993
994     TRACE("ipid = %s, iMethod = %d\n", debugstr_guid(&ipid), msg->ProcNum);
995
996     params = HeapAlloc(GetProcessHeap(), 0, sizeof(*params));
997     if (!params) return RpcRaiseException(E_OUTOFMEMORY);
998
999     hr = ipid_get_dispatch_params(&ipid, &apt, &params->stub, &params->chan);
1000     if (hr != S_OK)
1001     {
1002         ERR("no apartment found for ipid %s\n", debugstr_guid(&ipid));
1003         HeapFree(GetProcessHeap(), 0, params);
1004         return RpcRaiseException(hr);
1005     }
1006
1007     params->msg = (RPCOLEMESSAGE *)msg;
1008     params->status = RPC_S_OK;
1009     params->hr = S_OK;
1010     params->handle = NULL;
1011
1012     /* Note: this is the important difference between STAs and MTAs - we
1013      * always execute RPCs to STAs in the thread that originally created the
1014      * apartment (i.e. the one that pumps messages to the window) */
1015     if (!apt->multi_threaded)
1016     {
1017         params->handle = CreateEventW(NULL, FALSE, FALSE, NULL);
1018
1019         TRACE("Calling apartment thread 0x%08x...\n", apt->tid);
1020
1021         if (PostMessageW(apartment_getwindow(apt), DM_EXECUTERPC, 0, (LPARAM)params))
1022             WaitForSingleObject(params->handle, INFINITE);
1023         else
1024         {
1025             ERR("PostMessage failed with error %d\n", GetLastError());
1026             IRpcChannelBuffer_Release(params->chan);
1027             IRpcStubBuffer_Release(params->stub);
1028         }
1029         CloseHandle(params->handle);
1030     }
1031     else
1032     {
1033         BOOL joined = FALSE;
1034         if (!COM_CurrentInfo()->apt)
1035         {
1036             apartment_joinmta();
1037             joined = TRUE;
1038         }
1039         RPC_ExecuteCall(params);
1040         if (joined)
1041         {
1042             apartment_release(COM_CurrentInfo()->apt);
1043             COM_CurrentInfo()->apt = NULL;
1044         }
1045     }
1046
1047     hr = params->hr;
1048     HeapFree(GetProcessHeap(), 0, params);
1049
1050     apartment_release(apt);
1051
1052     /* if IRpcStubBuffer_Invoke fails, we should raise an exception to tell
1053      * the RPC runtime that the call failed */
1054     if (hr) RpcRaiseException(hr);
1055 }
1056
1057 /* stub registration */
1058 HRESULT RPC_RegisterInterface(REFIID riid)
1059 {
1060     struct registered_if *rif;
1061     BOOL found = FALSE;
1062     HRESULT hr = S_OK;
1063     
1064     TRACE("(%s)\n", debugstr_guid(riid));
1065
1066     EnterCriticalSection(&csRegIf);
1067     LIST_FOR_EACH_ENTRY(rif, &registered_interfaces, struct registered_if, entry)
1068     {
1069         if (IsEqualGUID(&rif->If.InterfaceId.SyntaxGUID, riid))
1070         {
1071             rif->refs++;
1072             found = TRUE;
1073             break;
1074         }
1075     }
1076     if (!found)
1077     {
1078         TRACE("Creating new interface\n");
1079
1080         rif = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*rif));
1081         if (rif)
1082         {
1083             RPC_STATUS status;
1084
1085             rif->refs = 1;
1086             rif->If.Length = sizeof(RPC_SERVER_INTERFACE);
1087             /* RPC interface ID = COM interface ID */
1088             rif->If.InterfaceId.SyntaxGUID = *riid;
1089             rif->If.DispatchTable = &rpc_dispatch;
1090             /* all other fields are 0, including the version asCOM objects
1091              * always have a version of 0.0 */
1092             status = RpcServerRegisterIfEx(
1093                 (RPC_IF_HANDLE)&rif->If,
1094                 NULL, NULL,
1095                 RPC_IF_OLE | RPC_IF_AUTOLISTEN,
1096                 RPC_C_LISTEN_MAX_CALLS_DEFAULT,
1097                 NULL);
1098             if (status == RPC_S_OK)
1099                 list_add_tail(&registered_interfaces, &rif->entry);
1100             else
1101             {
1102                 ERR("RpcServerRegisterIfEx failed with error %ld\n", status);
1103                 HeapFree(GetProcessHeap(), 0, rif);
1104                 hr = HRESULT_FROM_WIN32(status);
1105             }
1106         }
1107         else
1108             hr = E_OUTOFMEMORY;
1109     }
1110     LeaveCriticalSection(&csRegIf);
1111     return hr;
1112 }
1113
1114 /* stub unregistration */
1115 void RPC_UnregisterInterface(REFIID riid)
1116 {
1117     struct registered_if *rif;
1118     EnterCriticalSection(&csRegIf);
1119     LIST_FOR_EACH_ENTRY(rif, &registered_interfaces, struct registered_if, entry)
1120     {
1121         if (IsEqualGUID(&rif->If.InterfaceId.SyntaxGUID, riid))
1122         {
1123             if (!--rif->refs)
1124             {
1125                 RpcServerUnregisterIf((RPC_IF_HANDLE)&rif->If, NULL, TRUE);
1126                 list_remove(&rif->entry);
1127                 HeapFree(GetProcessHeap(), 0, rif);
1128             }
1129             break;
1130         }
1131     }
1132     LeaveCriticalSection(&csRegIf);
1133 }
1134
1135 /* make the apartment reachable by other threads and processes and create the
1136  * IRemUnknown object */
1137 void RPC_StartRemoting(struct apartment *apt)
1138 {
1139     if (!InterlockedExchange(&apt->remoting_started, TRUE))
1140     {
1141         WCHAR endpoint[200];
1142         RPC_STATUS status;
1143
1144         get_rpc_endpoint(endpoint, &apt->oxid);
1145     
1146         status = RpcServerUseProtseqEpW(
1147             wszRpcTransport,
1148             RPC_C_PROTSEQ_MAX_REQS_DEFAULT,
1149             endpoint,
1150             NULL);
1151         if (status != RPC_S_OK)
1152             ERR("Couldn't register endpoint %s\n", debugstr_w(endpoint));
1153
1154         /* FIXME: move remote unknown exporting into this function */
1155     }
1156     start_apartment_remote_unknown();
1157 }
1158
1159
1160 static HRESULT create_server(REFCLSID rclsid)
1161 {
1162     static const WCHAR  wszLocalServer32[] = { 'L','o','c','a','l','S','e','r','v','e','r','3','2',0 };
1163     static const WCHAR  embedding[] = { ' ', '-','E','m','b','e','d','d','i','n','g',0 };
1164     HKEY                key;
1165     HRESULT             hres;
1166     WCHAR               command[MAX_PATH+sizeof(embedding)/sizeof(WCHAR)];
1167     DWORD               size = (MAX_PATH+1) * sizeof(WCHAR);
1168     STARTUPINFOW        sinfo;
1169     PROCESS_INFORMATION pinfo;
1170
1171     hres = COM_OpenKeyForCLSID(rclsid, wszLocalServer32, KEY_READ, &key);
1172     if (FAILED(hres)) {
1173         ERR("class %s not registered\n", debugstr_guid(rclsid));
1174         return hres;
1175     }
1176
1177     hres = RegQueryValueExW(key, NULL, NULL, NULL, (LPBYTE)command, &size);
1178     RegCloseKey(key);
1179     if (hres) {
1180         WARN("No default value for LocalServer32 key\n");
1181         return REGDB_E_CLASSNOTREG; /* FIXME: check retval */
1182     }
1183
1184     memset(&sinfo,0,sizeof(sinfo));
1185     sinfo.cb = sizeof(sinfo);
1186
1187     /* EXE servers are started with the -Embedding switch. */
1188
1189     strcatW(command, embedding);
1190
1191     TRACE("activating local server %s for %s\n", debugstr_w(command), debugstr_guid(rclsid));
1192
1193     /* FIXME: Win2003 supports a ServerExecutable value that is passed into
1194      * CreateProcess */
1195     if (!CreateProcessW(NULL, command, NULL, NULL, FALSE, 0, NULL, NULL, &sinfo, &pinfo)) {
1196         WARN("failed to run local server %s\n", debugstr_w(command));
1197         return HRESULT_FROM_WIN32(GetLastError());
1198     }
1199     CloseHandle(pinfo.hProcess);
1200     CloseHandle(pinfo.hThread);
1201
1202     return S_OK;
1203 }
1204
1205 /*
1206  * start_local_service()  - start a service given its name and parameters
1207  */
1208 static DWORD start_local_service(LPCWSTR name, DWORD num, LPCWSTR *params)
1209 {
1210     SC_HANDLE handle, hsvc;
1211     DWORD     r = ERROR_FUNCTION_FAILED;
1212
1213     TRACE("Starting service %s %d params\n", debugstr_w(name), num);
1214
1215     handle = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
1216     if (!handle)
1217         return r;
1218     hsvc = OpenServiceW(handle, name, SERVICE_START);
1219     if (hsvc)
1220     {
1221         if(StartServiceW(hsvc, num, params))
1222             r = ERROR_SUCCESS;
1223         else
1224             r = GetLastError();
1225         if (r == ERROR_SERVICE_ALREADY_RUNNING)
1226             r = ERROR_SUCCESS;
1227         CloseServiceHandle(hsvc);
1228     }
1229     else
1230         r = GetLastError();
1231     CloseServiceHandle(handle);
1232
1233     TRACE("StartService returned error %d (%s)\n", r, (r == ERROR_SUCCESS) ? "ok":"failed");
1234
1235     return r;
1236 }
1237
1238 /*
1239  * create_local_service()  - start a COM server in a service
1240  *
1241  *   To start a Local Service, we read the AppID value under
1242  * the class's CLSID key, then open the HKCR\\AppId key specified
1243  * there and check for a LocalService value.
1244  *
1245  * Note:  Local Services are not supported under Windows 9x
1246  */
1247 static HRESULT create_local_service(REFCLSID rclsid)
1248 {
1249     HRESULT hres;
1250     WCHAR buf[CHARS_IN_GUID];
1251     static const WCHAR szLocalService[] = { 'L','o','c','a','l','S','e','r','v','i','c','e',0 };
1252     static const WCHAR szServiceParams[] = {'S','e','r','v','i','c','e','P','a','r','a','m','s',0};
1253     HKEY hkey;
1254     LONG r;
1255     DWORD type, sz;
1256
1257     TRACE("Attempting to start Local service for %s\n", debugstr_guid(rclsid));
1258
1259     hres = COM_OpenKeyForAppIdFromCLSID(rclsid, KEY_READ, &hkey);
1260     if (FAILED(hres))
1261         return hres;
1262
1263     /* read the LocalService and ServiceParameters values from the AppID key */
1264     sz = sizeof buf;
1265     r = RegQueryValueExW(hkey, szLocalService, NULL, &type, (LPBYTE)buf, &sz);
1266     if (r==ERROR_SUCCESS && type==REG_SZ)
1267     {
1268         DWORD num_args = 0;
1269         LPWSTR args[1] = { NULL };
1270
1271         /*
1272          * FIXME: I'm not really sure how to deal with the service parameters.
1273          *        I suspect that the string returned from RegQueryValueExW
1274          *        should be split into a number of arguments by spaces.
1275          *        It would make more sense if ServiceParams contained a
1276          *        REG_MULTI_SZ here, but it's a REG_SZ for the services
1277          *        that I'm interested in for the moment.
1278          */
1279         r = RegQueryValueExW(hkey, szServiceParams, NULL, &type, NULL, &sz);
1280         if (r == ERROR_SUCCESS && type == REG_SZ && sz)
1281         {
1282             args[0] = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sz);
1283             num_args++;
1284             RegQueryValueExW(hkey, szServiceParams, NULL, &type, (LPBYTE)args[0], &sz);
1285         }
1286         r = start_local_service(buf, num_args, (LPCWSTR *)args);
1287         if (r != ERROR_SUCCESS)
1288             hres = REGDB_E_CLASSNOTREG; /* FIXME: check retval */
1289         HeapFree(GetProcessHeap(),0,args[0]);
1290     }
1291     else
1292     {
1293         WARN("No LocalService value\n");
1294         hres = REGDB_E_CLASSNOTREG; /* FIXME: check retval */
1295     }
1296     RegCloseKey(hkey);
1297
1298     return hres;
1299 }
1300
1301
1302 static void get_localserver_pipe_name(WCHAR *pipefn, REFCLSID rclsid)
1303 {
1304     static const WCHAR wszPipeRef[] = {'\\','\\','.','\\','p','i','p','e','\\',0};
1305     strcpyW(pipefn, wszPipeRef);
1306     StringFromGUID2(rclsid, pipefn + sizeof(wszPipeRef)/sizeof(wszPipeRef[0]) - 1, CHARS_IN_GUID);
1307 }
1308
1309 /* FIXME: should call to rpcss instead */
1310 HRESULT RPC_GetLocalClassObject(REFCLSID rclsid, REFIID iid, LPVOID *ppv)
1311 {
1312     HRESULT        hres;
1313     HANDLE         hPipe;
1314     WCHAR          pipefn[100];
1315     DWORD          res, bufferlen;
1316     char           marshalbuffer[200];
1317     IStream       *pStm;
1318     LARGE_INTEGER  seekto;
1319     ULARGE_INTEGER newpos;
1320     int            tries = 0;
1321
1322     static const int MAXTRIES = 30; /* 30 seconds */
1323
1324     TRACE("rclsid=%s, iid=%s\n", debugstr_guid(rclsid), debugstr_guid(iid));
1325
1326     get_localserver_pipe_name(pipefn, rclsid);
1327
1328     while (tries++ < MAXTRIES) {
1329         TRACE("waiting for %s\n", debugstr_w(pipefn));
1330       
1331         WaitNamedPipeW( pipefn, NMPWAIT_WAIT_FOREVER );
1332         hPipe = CreateFileW(pipefn, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
1333         if (hPipe == INVALID_HANDLE_VALUE) {
1334             if (tries == 1) {
1335                 if ( (hres = create_local_service(rclsid)) &&
1336                      (hres = create_server(rclsid)) )
1337                     return hres;
1338                 Sleep(1000);
1339             } else {
1340                 WARN("Connecting to %s, no response yet, retrying: le is %x\n", debugstr_w(pipefn), GetLastError());
1341                 Sleep(1000);
1342             }
1343             continue;
1344         }
1345         bufferlen = 0;
1346         if (!ReadFile(hPipe,marshalbuffer,sizeof(marshalbuffer),&bufferlen,NULL)) {
1347             FIXME("Failed to read marshal id from classfactory of %s.\n",debugstr_guid(rclsid));
1348             Sleep(1000);
1349             continue;
1350         }
1351         TRACE("read marshal id from pipe\n");
1352         CloseHandle(hPipe);
1353         break;
1354     }
1355     
1356     if (tries >= MAXTRIES)
1357         return E_NOINTERFACE;
1358     
1359     hres = CreateStreamOnHGlobal(0,TRUE,&pStm);
1360     if (hres) return hres;
1361     hres = IStream_Write(pStm,marshalbuffer,bufferlen,&res);
1362     if (hres) goto out;
1363     seekto.u.LowPart = 0;seekto.u.HighPart = 0;
1364     hres = IStream_Seek(pStm,seekto,SEEK_SET,&newpos);
1365     
1366     TRACE("unmarshalling classfactory\n");
1367     hres = CoUnmarshalInterface(pStm,&IID_IClassFactory,ppv);
1368 out:
1369     IStream_Release(pStm);
1370     return hres;
1371 }
1372
1373
1374 struct local_server_params
1375 {
1376     CLSID clsid;
1377     IStream *stream;
1378     HANDLE ready_event;
1379 };
1380
1381 /* FIXME: should call to rpcss instead */
1382 static DWORD WINAPI local_server_thread(LPVOID param)
1383 {
1384     struct local_server_params * lsp = (struct local_server_params *)param;
1385     HANDLE              hPipe;
1386     WCHAR               pipefn[100];
1387     HRESULT             hres;
1388     IStream             *pStm = lsp->stream;
1389     STATSTG             ststg;
1390     unsigned char       *buffer;
1391     int                 buflen;
1392     LARGE_INTEGER       seekto;
1393     ULARGE_INTEGER      newpos;
1394     ULONG               res;
1395
1396     TRACE("Starting threader for %s.\n",debugstr_guid(&lsp->clsid));
1397
1398     get_localserver_pipe_name(pipefn, &lsp->clsid);
1399
1400     hPipe = CreateNamedPipeW( pipefn, PIPE_ACCESS_DUPLEX,
1401                               PIPE_TYPE_BYTE|PIPE_WAIT, PIPE_UNLIMITED_INSTANCES,
1402                               4096, 4096, 500 /* 0.5 second timeout */, NULL );
1403
1404     SetEvent(lsp->ready_event);
1405
1406     HeapFree(GetProcessHeap(), 0, lsp);
1407
1408     if (hPipe == INVALID_HANDLE_VALUE)
1409     {
1410         FIXME("pipe creation failed for %s, le is %d\n", debugstr_w(pipefn), GetLastError());
1411         return 1;
1412     }
1413     
1414     while (1) {
1415         if (!ConnectNamedPipe(hPipe,NULL) && GetLastError() != ERROR_PIPE_CONNECTED) {
1416             ERR("Failure during ConnectNamedPipe %d, ABORT!\n",GetLastError());
1417             break;
1418         }
1419
1420         TRACE("marshalling IClassFactory to client\n");
1421         
1422         hres = IStream_Stat(pStm,&ststg,0);
1423         if (hres) return hres;
1424
1425         seekto.u.LowPart = 0;
1426         seekto.u.HighPart = 0;
1427         hres = IStream_Seek(pStm,seekto,SEEK_SET,&newpos);
1428         if (hres) {
1429             FIXME("IStream_Seek failed, %x\n",hres);
1430             return hres;
1431         }
1432
1433         buflen = ststg.cbSize.u.LowPart;
1434         buffer = HeapAlloc(GetProcessHeap(),0,buflen);
1435         
1436         hres = IStream_Read(pStm,buffer,buflen,&res);
1437         if (hres) {
1438             FIXME("Stream Read failed, %x\n",hres);
1439             HeapFree(GetProcessHeap(),0,buffer);
1440             return hres;
1441         }
1442         
1443         WriteFile(hPipe,buffer,buflen,&res,NULL);
1444         HeapFree(GetProcessHeap(),0,buffer);
1445
1446         FlushFileBuffers(hPipe);
1447         DisconnectNamedPipe(hPipe);
1448
1449         TRACE("done marshalling IClassFactory\n");
1450     }
1451     CloseHandle(hPipe);
1452     IStream_Release(pStm);
1453     return 0;
1454 }
1455
1456 void RPC_StartLocalServer(REFCLSID clsid, IStream *stream)
1457 {
1458     DWORD tid;
1459     HANDLE thread, ready_event;
1460     struct local_server_params *lsp = HeapAlloc(GetProcessHeap(), 0, sizeof(*lsp));
1461
1462     lsp->clsid = *clsid;
1463     lsp->stream = stream;
1464     IStream_AddRef(stream);
1465     lsp->ready_event = ready_event = CreateEventW(NULL, FALSE, FALSE, NULL);
1466
1467     thread = CreateThread(NULL, 0, local_server_thread, lsp, 0, &tid);
1468     CloseHandle(thread);
1469     /* FIXME: failure handling */
1470
1471     WaitForSingleObject(ready_event, INFINITE);
1472     CloseHandle(ready_event);
1473 }