ole: Fix mis-handling of return value in StgStreamImpl_Read.
[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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22
23 #include "config.h"
24
25 #include <stdlib.h>
26 #include <stdarg.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <assert.h>
30
31 #define COBJMACROS
32 #define NONAMELESSUNION
33 #define NONAMELESSSTRUCT
34
35 #include "windef.h"
36 #include "winbase.h"
37 #include "winuser.h"
38 #include "winsvc.h"
39 #include "objbase.h"
40 #include "ole2.h"
41 #include "rpc.h"
42 #include "winerror.h"
43 #include "winreg.h"
44 #include "wtypes.h"
45 #include "excpt.h"
46 #include "wine/unicode.h"
47 #include "wine/exception.h"
48
49 #include "compobj_private.h"
50
51 #include "wine/debug.h"
52
53 WINE_DEFAULT_DEBUG_CHANNEL(ole);
54
55 static void __RPC_STUB dispatch_rpc(RPC_MESSAGE *msg);
56
57 /* we only use one function to dispatch calls for all methods - we use the
58  * RPC_IF_OLE flag to tell the RPC runtime that this is the case */
59 static RPC_DISPATCH_FUNCTION rpc_dispatch_table[1] = { dispatch_rpc }; /* (RO) */
60 static RPC_DISPATCH_TABLE rpc_dispatch = { 1, rpc_dispatch_table }; /* (RO) */
61
62 static struct list registered_interfaces = LIST_INIT(registered_interfaces); /* (CS csRegIf) */
63 static CRITICAL_SECTION csRegIf;
64 static CRITICAL_SECTION_DEBUG csRegIf_debug =
65 {
66     0, 0, &csRegIf,
67     { &csRegIf_debug.ProcessLocksList, &csRegIf_debug.ProcessLocksList },
68       0, 0, { (DWORD_PTR)(__FILE__ ": dcom registered server interfaces") }
69 };
70 static CRITICAL_SECTION csRegIf = { &csRegIf_debug, -1, 0, 0, 0, 0 };
71
72 static WCHAR wszPipeTransport[] = {'n','c','a','c','n','_','n','p',0};
73
74
75 struct registered_if
76 {
77     struct list entry;
78     DWORD refs; /* ref count */
79     RPC_SERVER_INTERFACE If; /* interface registered with the RPC runtime */
80 };
81
82 /* get the pipe endpoint specified of the specified apartment */
83 static inline void get_rpc_endpoint(LPWSTR endpoint, const OXID *oxid)
84 {
85     /* FIXME: should get endpoint from rpcss */
86     static const WCHAR wszEndpointFormat[] = {'\\','p','i','p','e','\\','O','L','E','_','%','0','8','l','x','%','0','8','l','x',0};
87     wsprintfW(endpoint, wszEndpointFormat, (DWORD)(*oxid >> 32),(DWORD)*oxid);
88 }
89
90 typedef struct
91 {
92     const IRpcChannelBufferVtbl *lpVtbl;
93     LONG                  refs;
94 } RpcChannelBuffer;
95
96 typedef struct
97 {
98     RpcChannelBuffer       super; /* superclass */
99
100     RPC_BINDING_HANDLE     bind; /* handle to the remote server */
101 } ClientRpcChannelBuffer;
102
103 struct dispatch_params
104 {
105     RPCOLEMESSAGE     *msg; /* message */
106     IRpcStubBuffer    *stub; /* stub buffer, if applicable */
107     IRpcChannelBuffer *chan; /* server channel buffer, if applicable */
108     HANDLE             handle; /* handle that will become signaled when call finishes */
109     RPC_STATUS         status; /* status (out) */
110     HRESULT            hr; /* hresult (out) */
111 };
112
113 static WINE_EXCEPTION_FILTER(ole_filter)
114 {
115     if (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION)
116         return EXCEPTION_CONTINUE_SEARCH;
117     return EXCEPTION_EXECUTE_HANDLER;
118 }
119
120 static HRESULT WINAPI RpcChannelBuffer_QueryInterface(LPRPCCHANNELBUFFER iface, REFIID riid, LPVOID *ppv)
121 {
122     *ppv = NULL;
123     if (IsEqualIID(riid,&IID_IRpcChannelBuffer) || IsEqualIID(riid,&IID_IUnknown))
124     {
125         *ppv = (LPVOID)iface;
126         IUnknown_AddRef(iface);
127         return S_OK;
128     }
129     return E_NOINTERFACE;
130 }
131
132 static ULONG WINAPI RpcChannelBuffer_AddRef(LPRPCCHANNELBUFFER iface)
133 {
134     RpcChannelBuffer *This = (RpcChannelBuffer *)iface;
135     return InterlockedIncrement(&This->refs);
136 }
137
138 static ULONG WINAPI ServerRpcChannelBuffer_Release(LPRPCCHANNELBUFFER iface)
139 {
140     RpcChannelBuffer *This = (RpcChannelBuffer *)iface;
141     ULONG ref;
142
143     ref = InterlockedDecrement(&This->refs);
144     if (ref)
145         return ref;
146
147     HeapFree(GetProcessHeap(), 0, This);
148     return 0;
149 }
150
151 static ULONG WINAPI ClientRpcChannelBuffer_Release(LPRPCCHANNELBUFFER iface)
152 {
153     ClientRpcChannelBuffer *This = (ClientRpcChannelBuffer *)iface;
154     ULONG ref;
155
156     ref = InterlockedDecrement(&This->super.refs);
157     if (ref)
158         return ref;
159
160     RpcBindingFree(&This->bind);
161     HeapFree(GetProcessHeap(), 0, This);
162     return 0;
163 }
164
165 static HRESULT WINAPI ServerRpcChannelBuffer_GetBuffer(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE* olemsg, REFIID riid)
166 {
167     RpcChannelBuffer *This = (RpcChannelBuffer *)iface;
168     RPC_MESSAGE *msg = (RPC_MESSAGE *)olemsg;
169     RPC_STATUS status;
170
171     TRACE("(%p)->(%p,%s)\n", This, olemsg, debugstr_guid(riid));
172
173     status = I_RpcGetBuffer(msg);
174
175     TRACE("-- %ld\n", status);
176
177     return HRESULT_FROM_WIN32(status);
178 }
179
180 static HRESULT WINAPI ClientRpcChannelBuffer_GetBuffer(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE* olemsg, REFIID riid)
181 {
182     ClientRpcChannelBuffer *This = (ClientRpcChannelBuffer *)iface;
183     RPC_MESSAGE *msg = (RPC_MESSAGE *)olemsg;
184     RPC_CLIENT_INTERFACE *cif;
185     RPC_STATUS status;
186
187     TRACE("(%p)->(%p,%s)\n", This, olemsg, debugstr_guid(riid));
188
189     cif = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(RPC_CLIENT_INTERFACE));
190     if (!cif)
191         return E_OUTOFMEMORY;
192
193     cif->Length = sizeof(RPC_CLIENT_INTERFACE);
194     /* RPC interface ID = COM interface ID */
195     cif->InterfaceId.SyntaxGUID = *riid;
196     /* COM objects always have a version of 0.0 */
197     cif->InterfaceId.SyntaxVersion.MajorVersion = 0;
198     cif->InterfaceId.SyntaxVersion.MinorVersion = 0;
199     msg->RpcInterfaceInformation = cif;
200     msg->Handle = This->bind;
201     
202     status = I_RpcGetBuffer(msg);
203
204     TRACE("-- %ld\n", status);
205
206     return HRESULT_FROM_WIN32(status);
207 }
208
209 /* this thread runs an outgoing RPC */
210 static DWORD WINAPI rpc_sendreceive_thread(LPVOID param)
211 {
212     struct dispatch_params *data = (struct dispatch_params *) param;
213     
214     /* FIXME: trap and rethrow RPC exceptions in app thread */
215     data->status = I_RpcSendReceive((RPC_MESSAGE *)data->msg);
216
217     TRACE("completed with status 0x%lx\n", data->status);
218     
219     return 0;
220 }
221
222 static HRESULT WINAPI RpcChannelBuffer_SendReceive(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE *olemsg, ULONG *pstatus)
223 {
224     HRESULT hr = S_OK;
225     RPC_MESSAGE *msg = (RPC_MESSAGE *)olemsg;
226     RPC_STATUS status;
227     DWORD index;
228     struct dispatch_params *params;
229     DWORD tid;
230     IRpcStubBuffer *stub;
231     APARTMENT *apt;
232     IPID ipid;
233
234     TRACE("(%p) iMethod=%ld\n", olemsg, olemsg->iMethod);
235
236     params = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*params));
237     if (!params) return E_OUTOFMEMORY;
238     
239     params->msg = olemsg;
240     params->status = RPC_S_OK;
241     params->hr = S_OK;
242
243     /* Note: this is an optimization in the Microsoft OLE runtime that we need
244      * to copy, as shown by the test_no_couninitialize_client test. without
245      * short-circuiting the RPC runtime in the case below, the test will
246      * deadlock on the loader lock due to the RPC runtime needing to create
247      * a thread to process the RPC when this function is called indirectly
248      * from DllMain */
249
250     RpcBindingInqObject(msg->Handle, &ipid);
251     stub = ipid_to_apt_and_stubbuffer(&ipid, &apt);
252     if (apt && (apt->model & COINIT_APARTMENTTHREADED))
253     {
254         params->stub = stub;
255         params->chan = NULL; /* FIXME: pass server channel */
256         params->handle = CreateEventW(NULL, FALSE, FALSE, NULL);
257
258         TRACE("Calling apartment thread 0x%08lx...\n", apt->tid);
259
260         PostMessageW(apt->win, DM_EXECUTERPC, 0, (LPARAM)params);
261     }
262     else
263     {
264         if (stub) IRpcStubBuffer_Release(stub);
265
266         /* we use a separate thread here because we need to be able to
267          * pump the message loop in the application thread: if we do not,
268          * any windows created by this thread will hang and RPCs that try
269          * and re-enter this STA from an incoming server thread will
270          * deadlock. InstallShield is an example of that.
271          */
272         params->handle = CreateThread(NULL, 0, rpc_sendreceive_thread, params, 0, &tid);
273         if (!params->handle)
274         {
275             ERR("Could not create RpcSendReceive thread, error %lx\n", GetLastError());
276             hr = E_UNEXPECTED;
277         }
278     }
279     if (apt) apartment_release(apt);
280
281     if (hr == S_OK)
282         hr = CoWaitForMultipleHandles(0, INFINITE, 1, &params->handle, &index);
283     CloseHandle(params->handle);
284
285     if (hr == S_OK) hr = params->hr;
286
287     status = params->status;
288     HeapFree(GetProcessHeap(), 0, params);
289     params = NULL;
290
291     if (hr) return hr;
292     
293     if (pstatus) *pstatus = status;
294
295     TRACE("RPC call status: 0x%lx\n", status);
296     if (status == RPC_S_OK)
297         hr = S_OK;
298     else if (status == RPC_S_CALL_FAILED)
299         hr = *(HRESULT *)olemsg->Buffer;
300     else
301         hr = HRESULT_FROM_WIN32(status);
302
303     TRACE("-- 0x%08lx\n", hr);
304
305     return hr;
306 }
307
308 static HRESULT WINAPI ServerRpcChannelBuffer_FreeBuffer(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE* olemsg)
309 {
310     RPC_MESSAGE *msg = (RPC_MESSAGE *)olemsg;
311     RPC_STATUS status;
312
313     TRACE("(%p)\n", msg);
314
315     status = I_RpcFreeBuffer(msg);
316
317     TRACE("-- %ld\n", status);
318
319     return HRESULT_FROM_WIN32(status);
320 }
321
322 static HRESULT WINAPI ClientRpcChannelBuffer_FreeBuffer(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE* olemsg)
323 {
324     RPC_MESSAGE *msg = (RPC_MESSAGE *)olemsg;
325     RPC_STATUS status;
326
327     TRACE("(%p)\n", msg);
328
329     status = I_RpcFreeBuffer(msg);
330
331     HeapFree(GetProcessHeap(), 0, msg->RpcInterfaceInformation);
332     msg->RpcInterfaceInformation = NULL;
333
334     TRACE("-- %ld\n", status);
335
336     return HRESULT_FROM_WIN32(status);
337 }
338
339 static HRESULT WINAPI RpcChannelBuffer_GetDestCtx(LPRPCCHANNELBUFFER iface, DWORD* pdwDestContext, void** ppvDestContext)
340 {
341     FIXME("(%p,%p), stub!\n", pdwDestContext, ppvDestContext);
342     return E_FAIL;
343 }
344
345 static HRESULT WINAPI RpcChannelBuffer_IsConnected(LPRPCCHANNELBUFFER iface)
346 {
347     TRACE("()\n");
348     /* native does nothing too */
349     return S_OK;
350 }
351
352 static const IRpcChannelBufferVtbl ClientRpcChannelBufferVtbl =
353 {
354     RpcChannelBuffer_QueryInterface,
355     RpcChannelBuffer_AddRef,
356     ClientRpcChannelBuffer_Release,
357     ClientRpcChannelBuffer_GetBuffer,
358     RpcChannelBuffer_SendReceive,
359     ClientRpcChannelBuffer_FreeBuffer,
360     RpcChannelBuffer_GetDestCtx,
361     RpcChannelBuffer_IsConnected
362 };
363
364 static const IRpcChannelBufferVtbl ServerRpcChannelBufferVtbl =
365 {
366     RpcChannelBuffer_QueryInterface,
367     RpcChannelBuffer_AddRef,
368     ServerRpcChannelBuffer_Release,
369     ServerRpcChannelBuffer_GetBuffer,
370     RpcChannelBuffer_SendReceive,
371     ServerRpcChannelBuffer_FreeBuffer,
372     RpcChannelBuffer_GetDestCtx,
373     RpcChannelBuffer_IsConnected
374 };
375
376 /* returns a channel buffer for proxies */
377 HRESULT RPC_CreateClientChannel(const OXID *oxid, const IPID *ipid, IRpcChannelBuffer **chan)
378 {
379     ClientRpcChannelBuffer *This;
380     WCHAR                   endpoint[200];
381     RPC_BINDING_HANDLE      bind;
382     RPC_STATUS              status;
383     LPWSTR                  string_binding;
384
385     /* connect to the apartment listener thread */
386     get_rpc_endpoint(endpoint, oxid);
387
388     TRACE("proxy pipe: connecting to endpoint: %s\n", debugstr_w(endpoint));
389
390     status = RpcStringBindingComposeW(
391         NULL,
392         wszPipeTransport,
393         NULL,
394         endpoint,
395         NULL,
396         &string_binding);
397         
398     if (status == RPC_S_OK)
399     {
400         status = RpcBindingFromStringBindingW(string_binding, &bind);
401
402         if (status == RPC_S_OK)
403         {
404             IPID ipid2 = *ipid; /* why can't RpcBindingSetObject take a const? */
405             status = RpcBindingSetObject(bind, &ipid2);
406             if (status != RPC_S_OK)
407                 RpcBindingFree(&bind);
408         }
409
410         RpcStringFreeW(&string_binding);
411     }
412
413     if (status != RPC_S_OK)
414     {
415         ERR("Couldn't get binding for endpoint %s, status = %ld\n", debugstr_w(endpoint), status);
416         return HRESULT_FROM_WIN32(status);
417     }
418
419     This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
420     if (!This)
421     {
422         RpcBindingFree(&bind);
423         return E_OUTOFMEMORY;
424     }
425
426     This->super.lpVtbl = &ClientRpcChannelBufferVtbl;
427     This->super.refs = 1;
428     This->bind = bind;
429
430     *chan = (IRpcChannelBuffer*)This;
431
432     return S_OK;
433 }
434
435 HRESULT RPC_CreateServerChannel(IRpcChannelBuffer **chan)
436 {
437     RpcChannelBuffer *This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
438     if (!This)
439         return E_OUTOFMEMORY;
440
441     This->lpVtbl = &ServerRpcChannelBufferVtbl;
442     This->refs = 1;
443     
444     *chan = (IRpcChannelBuffer*)This;
445
446     return S_OK;
447 }
448
449
450 void RPC_ExecuteCall(struct dispatch_params *params)
451 {
452     __TRY
453     {
454         params->hr = IRpcStubBuffer_Invoke(params->stub, params->msg, params->chan);
455     }
456     __EXCEPT(ole_filter)
457     {
458         params->hr = GetExceptionCode();
459     }
460     __ENDTRY
461     IRpcStubBuffer_Release(params->stub);
462     if (params->handle) SetEvent(params->handle);
463 }
464
465 static void __RPC_STUB dispatch_rpc(RPC_MESSAGE *msg)
466 {
467     struct dispatch_params *params;
468     IRpcStubBuffer     *stub;
469     APARTMENT          *apt;
470     IPID                ipid;
471
472     RpcBindingInqObject(msg->Handle, &ipid);
473
474     TRACE("ipid = %s, iMethod = %d\n", debugstr_guid(&ipid), msg->ProcNum);
475
476     params = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*params));
477     if (!params) return RpcRaiseException(E_OUTOFMEMORY);
478
479     stub = ipid_to_apt_and_stubbuffer(&ipid, &apt);
480     if (!apt || !stub)
481     {
482         if (apt) apartment_release(apt);
483         ERR("no apartment found for ipid %s\n", debugstr_guid(&ipid));
484         return RpcRaiseException(RPC_E_DISCONNECTED);
485     }
486
487     params->msg = (RPCOLEMESSAGE *)msg;
488     params->stub = stub;
489     params->chan = NULL; /* FIXME: pass server channel */
490     params->status = RPC_S_OK;
491
492     /* Note: this is the important difference between STAs and MTAs - we
493      * always execute RPCs to STAs in the thread that originally created the
494      * apartment (i.e. the one that pumps messages to the window) */
495     if (apt->model & COINIT_APARTMENTTHREADED)
496     {
497         params->handle = CreateEventW(NULL, FALSE, FALSE, NULL);
498
499         TRACE("Calling apartment thread 0x%08lx...\n", apt->tid);
500
501         PostMessageW(apt->win, DM_EXECUTERPC, 0, (LPARAM)params);
502         WaitForSingleObject(params->handle, INFINITE);
503         CloseHandle(params->handle);
504     }
505     else
506         RPC_ExecuteCall(params);
507
508     HeapFree(GetProcessHeap(), 0, params);
509
510     apartment_release(apt);
511 }
512
513 /* stub registration */
514 HRESULT RPC_RegisterInterface(REFIID riid)
515 {
516     struct registered_if *rif;
517     BOOL found = FALSE;
518     HRESULT hr = S_OK;
519     
520     TRACE("(%s)\n", debugstr_guid(riid));
521
522     EnterCriticalSection(&csRegIf);
523     LIST_FOR_EACH_ENTRY(rif, &registered_interfaces, struct registered_if, entry)
524     {
525         if (IsEqualGUID(&rif->If.InterfaceId.SyntaxGUID, riid))
526         {
527             rif->refs++;
528             found = TRUE;
529             break;
530         }
531     }
532     if (!found)
533     {
534         TRACE("Creating new interface\n");
535
536         rif = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*rif));
537         if (rif)
538         {
539             RPC_STATUS status;
540
541             rif->refs = 1;
542             rif->If.Length = sizeof(RPC_SERVER_INTERFACE);
543             /* RPC interface ID = COM interface ID */
544             rif->If.InterfaceId.SyntaxGUID = *riid;
545             rif->If.DispatchTable = &rpc_dispatch;
546             /* all other fields are 0, including the version asCOM objects
547              * always have a version of 0.0 */
548             status = RpcServerRegisterIfEx(
549                 (RPC_IF_HANDLE)&rif->If,
550                 NULL, NULL,
551                 RPC_IF_OLE | RPC_IF_AUTOLISTEN,
552                 RPC_C_LISTEN_MAX_CALLS_DEFAULT,
553                 NULL);
554             if (status == RPC_S_OK)
555                 list_add_tail(&registered_interfaces, &rif->entry);
556             else
557             {
558                 ERR("RpcServerRegisterIfEx failed with error %ld\n", status);
559                 HeapFree(GetProcessHeap(), 0, rif);
560                 hr = HRESULT_FROM_WIN32(status);
561             }
562         }
563         else
564             hr = E_OUTOFMEMORY;
565     }
566     LeaveCriticalSection(&csRegIf);
567     return hr;
568 }
569
570 /* stub unregistration */
571 void RPC_UnregisterInterface(REFIID riid)
572 {
573     struct registered_if *rif;
574     EnterCriticalSection(&csRegIf);
575     LIST_FOR_EACH_ENTRY(rif, &registered_interfaces, struct registered_if, entry)
576     {
577         if (IsEqualGUID(&rif->If.InterfaceId.SyntaxGUID, riid))
578         {
579             if (!--rif->refs)
580             {
581 #if 0 /* this is a stub in builtin and spams the console with FIXME's */
582                 IID iid = *riid; /* RpcServerUnregisterIf doesn't take const IID */
583                 RpcServerUnregisterIf((RPC_IF_HANDLE)&rif->If, &iid, 0);
584                 list_remove(&rif->entry);
585                 HeapFree(GetProcessHeap(), 0, rif);
586 #endif
587             }
588             break;
589         }
590     }
591     LeaveCriticalSection(&csRegIf);
592 }
593
594 /* make the apartment reachable by other threads and processes and create the
595  * IRemUnknown object */
596 void RPC_StartRemoting(struct apartment *apt)
597 {
598     if (!InterlockedExchange(&apt->remoting_started, TRUE))
599     {
600         WCHAR endpoint[200];
601         RPC_STATUS status;
602
603         get_rpc_endpoint(endpoint, &apt->oxid);
604     
605         status = RpcServerUseProtseqEpW(
606             wszPipeTransport,
607             RPC_C_PROTSEQ_MAX_REQS_DEFAULT,
608             endpoint,
609             NULL);
610         if (status != RPC_S_OK)
611             ERR("Couldn't register endpoint %s\n", debugstr_w(endpoint));
612
613         /* FIXME: move remote unknown exporting into this function */
614     }
615     start_apartment_remote_unknown();
616 }
617
618
619 static HRESULT create_server(REFCLSID rclsid)
620 {
621     static const WCHAR  wszLocalServer32[] = { 'L','o','c','a','l','S','e','r','v','e','r','3','2',0 };
622     static const WCHAR  embedding[] = { ' ', '-','E','m','b','e','d','d','i','n','g',0 };
623     HKEY                key;
624     HRESULT             hres;
625     WCHAR               command[MAX_PATH+sizeof(embedding)/sizeof(WCHAR)];
626     DWORD               size = (MAX_PATH+1) * sizeof(WCHAR);
627     STARTUPINFOW        sinfo;
628     PROCESS_INFORMATION pinfo;
629
630     hres = COM_OpenKeyForCLSID(rclsid, wszLocalServer32, KEY_READ, &key);
631     if (FAILED(hres)) {
632         ERR("class %s not registered\n", debugstr_guid(rclsid));
633         return hres;
634     }
635
636     hres = RegQueryValueExW(key, NULL, NULL, NULL, (LPBYTE)command, &size);
637     RegCloseKey(key);
638     if (hres) {
639         WARN("No default value for LocalServer32 key\n");
640         return REGDB_E_CLASSNOTREG; /* FIXME: check retval */
641     }
642
643     memset(&sinfo,0,sizeof(sinfo));
644     sinfo.cb = sizeof(sinfo);
645
646     /* EXE servers are started with the -Embedding switch. */
647
648     strcatW(command, embedding);
649
650     TRACE("activating local server %s for %s\n", debugstr_w(command), debugstr_guid(rclsid));
651
652     /* FIXME: Win2003 supports a ServerExecutable value that is passed into
653      * CreateProcess */
654     if (!CreateProcessW(NULL, command, NULL, NULL, FALSE, 0, NULL, NULL, &sinfo, &pinfo)) {
655         WARN("failed to run local server %s\n", debugstr_w(command));
656         return HRESULT_FROM_WIN32(GetLastError());
657     }
658     CloseHandle(pinfo.hProcess);
659     CloseHandle(pinfo.hThread);
660
661     return S_OK;
662 }
663
664 /*
665  * start_local_service()  - start a service given its name and parameters
666  */
667 static DWORD start_local_service(LPCWSTR name, DWORD num, LPWSTR *params)
668 {
669     SC_HANDLE handle, hsvc;
670     DWORD     r = ERROR_FUNCTION_FAILED;
671
672     TRACE("Starting service %s %ld params\n", debugstr_w(name), num);
673
674     handle = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
675     if (!handle)
676         return r;
677     hsvc = OpenServiceW(handle, name, SC_MANAGER_ALL_ACCESS);
678     if (hsvc)
679     {
680         if(StartServiceW(hsvc, num, (LPCWSTR*)params))
681             r = ERROR_SUCCESS;
682         else
683             r = GetLastError();
684         if (r == ERROR_SERVICE_ALREADY_RUNNING)
685             r = ERROR_SUCCESS;
686         CloseServiceHandle(hsvc);
687     }
688     CloseServiceHandle(handle);
689
690     TRACE("StartService returned error %ld (%s)\n", r, r?"ok":"failed");
691
692     return r;
693 }
694
695 /*
696  * create_local_service()  - start a COM server in a service
697  *
698  *   To start a Local Service, we read the AppID value under
699  * the class's CLSID key, then open the HKCR\\AppId key specified
700  * there and check for a LocalService value.
701  *
702  * Note:  Local Services are not supported under Windows 9x
703  */
704 static HRESULT create_local_service(REFCLSID rclsid)
705 {
706     HRESULT hres;
707     WCHAR buf[CHARS_IN_GUID], keyname[50];
708     static const WCHAR szAppId[] = { 'A','p','p','I','d',0 };
709     static const WCHAR szAppIdKey[] = { 'A','p','p','I','d','\\',0 };
710     static const WCHAR szLocalService[] = { 'L','o','c','a','l','S','e','r','v','i','c','e',0 };
711     static const WCHAR szServiceParams[] = {'S','e','r','v','i','c','e','P','a','r','a','m','s',0};
712     HKEY hkey;
713     LONG r;
714     DWORD type, sz;
715
716     TRACE("Attempting to start Local service for %s\n", debugstr_guid(rclsid));
717
718     /* read the AppID value under the class's key */
719     hres = COM_OpenKeyForCLSID(rclsid, szAppId, KEY_READ, &hkey);
720     if (FAILED(hres))
721         return hres;
722     sz = sizeof buf;
723     r = RegQueryValueExW(hkey, NULL, NULL, &type, (LPBYTE)buf, &sz);
724     RegCloseKey(hkey);
725     if (r!=ERROR_SUCCESS || type!=REG_SZ)
726         return hres;
727
728     /* read the LocalService and ServiceParameters values from the AppID key */
729     strcpyW(keyname, szAppIdKey);
730     strcatW(keyname, buf);
731     r = RegOpenKeyExW(HKEY_CLASSES_ROOT, keyname, 0, KEY_READ, &hkey);
732     if (r!=ERROR_SUCCESS)
733         return hres;
734     sz = sizeof buf;
735     r = RegQueryValueExW(hkey, szLocalService, NULL, &type, (LPBYTE)buf, &sz);
736     if (r==ERROR_SUCCESS && type==REG_SZ)
737     {
738         DWORD num_args = 0;
739         LPWSTR args[1] = { NULL };
740
741         /*
742          * FIXME: I'm not really sure how to deal with the service parameters.
743          *        I suspect that the string returned from RegQueryValueExW
744          *        should be split into a number of arguments by spaces.
745          *        It would make more sense if ServiceParams contained a
746          *        REG_MULTI_SZ here, but it's a REG_SZ for the services
747          *        that I'm interested in for the moment.
748          */
749         r = RegQueryValueExW(hkey, szServiceParams, NULL, &type, NULL, &sz);
750         if (r == ERROR_SUCCESS && type == REG_SZ && sz)
751         {
752             args[0] = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sz);
753             num_args++;
754             RegQueryValueExW(hkey, szServiceParams, NULL, &type, (LPBYTE)args[0], &sz);
755         }
756         r = start_local_service(buf, num_args, args);
757         if (r==ERROR_SUCCESS)
758             hres = S_OK;
759         HeapFree(GetProcessHeap(),0,args[0]);
760     }
761     RegCloseKey(hkey);
762         
763     return hres;
764 }
765
766
767 static void get_localserver_pipe_name(WCHAR *pipefn, REFCLSID rclsid)
768 {
769     static const WCHAR wszPipeRef[] = {'\\','\\','.','\\','p','i','p','e','\\',0};
770     strcpyW(pipefn, wszPipeRef);
771     StringFromGUID2(rclsid, pipefn + sizeof(wszPipeRef)/sizeof(wszPipeRef[0]) - 1, CHARS_IN_GUID);
772 }
773
774 /* FIXME: should call to rpcss instead */
775 HRESULT RPC_GetLocalClassObject(REFCLSID rclsid, REFIID iid, LPVOID *ppv)
776 {
777     HRESULT        hres;
778     HANDLE         hPipe;
779     WCHAR          pipefn[100];
780     DWORD          res, bufferlen;
781     char           marshalbuffer[200];
782     IStream       *pStm;
783     LARGE_INTEGER  seekto;
784     ULARGE_INTEGER newpos;
785     int            tries = 0;
786
787     static const int MAXTRIES = 30; /* 30 seconds */
788
789     TRACE("rclsid=%s, iid=%s\n", debugstr_guid(rclsid), debugstr_guid(iid));
790
791     get_localserver_pipe_name(pipefn, rclsid);
792
793     while (tries++ < MAXTRIES) {
794         TRACE("waiting for %s\n", debugstr_w(pipefn));
795       
796         WaitNamedPipeW( pipefn, NMPWAIT_WAIT_FOREVER );
797         hPipe = CreateFileW(pipefn, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
798         if (hPipe == INVALID_HANDLE_VALUE) {
799             if (tries == 1) {
800                 if ( (hres = create_server(rclsid)) &&
801                      (hres = create_local_service(rclsid)) )
802                     return hres;
803                 Sleep(1000);
804             } else {
805                 WARN("Connecting to %s, no response yet, retrying: le is %lx\n", debugstr_w(pipefn), GetLastError());
806                 Sleep(1000);
807             }
808             continue;
809         }
810         bufferlen = 0;
811         if (!ReadFile(hPipe,marshalbuffer,sizeof(marshalbuffer),&bufferlen,NULL)) {
812             FIXME("Failed to read marshal id from classfactory of %s.\n",debugstr_guid(rclsid));
813             Sleep(1000);
814             continue;
815         }
816         TRACE("read marshal id from pipe\n");
817         CloseHandle(hPipe);
818         break;
819     }
820     
821     if (tries >= MAXTRIES)
822         return E_NOINTERFACE;
823     
824     hres = CreateStreamOnHGlobal(0,TRUE,&pStm);
825     if (hres) return hres;
826     hres = IStream_Write(pStm,marshalbuffer,bufferlen,&res);
827     if (hres) goto out;
828     seekto.u.LowPart = 0;seekto.u.HighPart = 0;
829     hres = IStream_Seek(pStm,seekto,SEEK_SET,&newpos);
830     
831     TRACE("unmarshalling classfactory\n");
832     hres = CoUnmarshalInterface(pStm,&IID_IClassFactory,ppv);
833 out:
834     IStream_Release(pStm);
835     return hres;
836 }
837
838
839 struct local_server_params
840 {
841     CLSID clsid;
842     IStream *stream;
843 };
844
845 /* FIXME: should call to rpcss instead */
846 static DWORD WINAPI local_server_thread(LPVOID param)
847 {
848     struct local_server_params * lsp = (struct local_server_params *)param;
849     HANDLE              hPipe;
850     WCHAR               pipefn[100];
851     HRESULT             hres;
852     IStream             *pStm = lsp->stream;
853     STATSTG             ststg;
854     unsigned char       *buffer;
855     int                 buflen;
856     LARGE_INTEGER       seekto;
857     ULARGE_INTEGER      newpos;
858     ULONG               res;
859
860     TRACE("Starting threader for %s.\n",debugstr_guid(&lsp->clsid));
861
862     get_localserver_pipe_name(pipefn, &lsp->clsid);
863
864     HeapFree(GetProcessHeap(), 0, lsp);
865
866     hPipe = CreateNamedPipeW( pipefn, PIPE_ACCESS_DUPLEX,
867                               PIPE_TYPE_BYTE|PIPE_WAIT, PIPE_UNLIMITED_INSTANCES,
868                               4096, 4096, 500 /* 0.5 second timeout */, NULL );
869     
870     if (hPipe == INVALID_HANDLE_VALUE)
871     {
872         FIXME("pipe creation failed for %s, le is %ld\n", debugstr_w(pipefn), GetLastError());
873         return 1;
874     }
875     
876     while (1) {
877         if (!ConnectNamedPipe(hPipe,NULL)) {
878             ERR("Failure during ConnectNamedPipe %ld, ABORT!\n",GetLastError());
879             break;
880         }
881
882         TRACE("marshalling IClassFactory to client\n");
883         
884         hres = IStream_Stat(pStm,&ststg,0);
885         if (hres) return hres;
886
887         buflen = ststg.cbSize.u.LowPart;
888         buffer = HeapAlloc(GetProcessHeap(),0,buflen);
889         seekto.u.LowPart = 0;
890         seekto.u.HighPart = 0;
891         hres = IStream_Seek(pStm,seekto,SEEK_SET,&newpos);
892         if (hres) {
893             FIXME("IStream_Seek failed, %lx\n",hres);
894             return hres;
895         }
896         
897         hres = IStream_Read(pStm,buffer,buflen,&res);
898         if (hres) {
899             FIXME("Stream Read failed, %lx\n",hres);
900             return hres;
901         }
902         
903         WriteFile(hPipe,buffer,buflen,&res,NULL);
904         FlushFileBuffers(hPipe);
905         DisconnectNamedPipe(hPipe);
906
907         TRACE("done marshalling IClassFactory\n");
908     }
909     CloseHandle(hPipe);
910     IStream_Release(pStm);
911     return 0;
912 }
913
914 void RPC_StartLocalServer(REFCLSID clsid, IStream *stream)
915 {
916     DWORD tid;
917     HANDLE thread;
918     struct local_server_params *lsp = HeapAlloc(GetProcessHeap(), 0, sizeof(*lsp));
919
920     lsp->clsid = *clsid;
921     lsp->stream = stream;
922
923     thread = CreateThread(NULL, 0, local_server_thread, lsp, 0, &tid);
924     CloseHandle(thread);
925     /* FIXME: failure handling */
926 }