- The apartment reference should be held while the stub manager
[wine] / dlls / ole32 / rpc.c
1 /*
2  *      (Local) RPC Stuff
3  *
4  *  Copyright 2002  Marcus Meissner
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "config.h"
22
23 #include <stdlib.h>
24 #include <stdarg.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <assert.h>
28
29 #define COBJMACROS
30 #define NONAMELESSUNION
31 #define NONAMELESSSTRUCT
32
33 #include "windef.h"
34 #include "winbase.h"
35 #include "winuser.h"
36 #include "winsvc.h"
37 #include "objbase.h"
38 #include "ole2.h"
39 #include "rpc.h"
40 #include "winerror.h"
41 #include "winreg.h"
42 #include "wtypes.h"
43 #include "wine/unicode.h"
44
45 #include "compobj_private.h"
46
47 #include "wine/debug.h"
48
49 WINE_DEFAULT_DEBUG_CHANNEL(ole);
50
51 #define PIPEPREF "\\\\.\\pipe\\"
52 #define OLESTUBMGR PIPEPREF"WINE_OLE_StubMgr"
53
54 #define REQTYPE_REQUEST         0
55 typedef struct _wine_rpc_request_header {
56     DWORD               reqid;
57     wine_marshal_id     mid;
58     DWORD               iMethod;
59     DWORD               cbBuffer;
60 } wine_rpc_request_header;
61
62 #define REQTYPE_RESPONSE        1
63 typedef struct _wine_rpc_response_header {
64     DWORD               reqid;
65     DWORD               cbBuffer;
66     DWORD               retval;
67 } wine_rpc_response_header;
68
69 /* used when shutting down a pipe, e.g. at the end of a process */
70 #define REQTYPE_DISCONNECT      2
71 typedef struct _wine_rpc_disconnect_header {
72   DWORD reqid;
73   wine_marshal_id mid; /* mid of stub to delete */
74 } wine_rpc_disconnect_header;
75
76
77 #define REQSTATE_START                  0
78 #define REQSTATE_REQ_QUEUED             1
79 #define REQSTATE_REQ_WAITING_FOR_REPLY  2
80 #define REQSTATE_REQ_GOT                3
81 #define REQSTATE_INVOKING               4
82 #define REQSTATE_RESP_QUEUED            5
83 #define REQSTATE_RESP_GOT               6
84 #define REQSTATE_DONE                   6
85
86 typedef struct _wine_rpc_request {
87     int                         state;
88     HANDLE                      hPipe;  /* temp copy of handle */
89     wine_rpc_request_header     reqh;
90     wine_rpc_response_header    resph;
91     LPBYTE                      Buffer;
92 } wine_rpc_request;
93
94 static wine_rpc_request **reqs = NULL;
95 static int nrofreqs = 0;
96
97 /* This pipe is _thread_ based, each thread which talks to a remote
98  * apartment (mid) has its own pipe. The same structure is used both
99  * for outgoing and incoming RPCs.
100  */
101 typedef struct _wine_pipe {
102     wine_marshal_id     mid;    /* target mid */
103     DWORD               tid;    /* thread which owns this pipe */
104     HANDLE              hPipe;
105
106     int                 pending;
107     HANDLE              hThread;
108     CRITICAL_SECTION    crit;
109
110     APARTMENT          *apt;    /* apartment of the marshalling thread for the stub dispatch case */
111 } wine_pipe;
112
113 #define MAX_WINE_PIPES 256
114
115 static wine_pipe pipes[MAX_WINE_PIPES];
116 static int nrofpipes = 0;
117
118 typedef struct _PipeBuf {
119     IRpcChannelBufferVtbl       *lpVtbl;
120     DWORD                               ref;
121
122     wine_marshal_id                     mid;
123 } PipeBuf;
124
125 static HRESULT WINAPI
126 read_pipe(HANDLE hf, LPVOID ptr, DWORD size) {
127     DWORD res;
128     if (!ReadFile(hf,ptr,size,&res,NULL)) {
129         FIXME("Failed to read from %p, le is %ld\n",hf,GetLastError());
130         return E_FAIL;
131     }
132     if (res!=size) {
133         FIXME("Read only %ld of %ld bytes from %p.\n",res,size,hf);
134         return E_FAIL;
135     }
136     return S_OK;
137 }
138
139 static void
140 drs(LPCSTR where) {
141 #if 0
142     static int nrofreaders = 0;
143
144     int i, states[10];
145
146     memset(states,0,sizeof(states));
147     for (i=nrofreqs;i--;)
148         states[reqs[i]->state]++;
149     FIXME("%lx/%s/%d: rq %d, w %d, rg %d, rsq %d, rsg %d, d %d\n",
150             GetCurrentProcessId(),
151             where,
152             nrofreaders,
153             states[REQSTATE_REQ_QUEUED],
154             states[REQSTATE_REQ_WAITING_FOR_REPLY],
155             states[REQSTATE_REQ_GOT],
156             states[REQSTATE_RESP_QUEUED],
157             states[REQSTATE_RESP_GOT],
158             states[REQSTATE_DONE]
159     );
160 #endif
161
162     return ;
163 }
164
165 static HRESULT WINAPI
166 write_pipe(HANDLE hf, LPVOID ptr, DWORD size) {
167     DWORD res;
168     if (!WriteFile(hf,ptr,size,&res,NULL)) {
169         FIXME("Failed to write to %p, le is %ld\n",hf,GetLastError());
170         return E_FAIL;
171     }
172     if (res!=size) {
173         FIXME("Wrote only %ld of %ld bytes to %p.\n",res,size,hf);
174         return E_FAIL;
175     }
176     return S_OK;
177 }
178
179 static DWORD WINAPI stub_dispatch_thread(LPVOID);
180
181 static HRESULT
182 PIPE_RegisterPipe(wine_marshal_id *mid, HANDLE hPipe, BOOL startreader) {
183
184 /* FIXME: this pipe caching code is commented out because it is breaks the
185  * tests, causing them hang due to writing to or reading from the wrong pipe.
186  */
187 #if 0
188   int   i;
189
190   for (i=0;i<nrofpipes;i++)
191     if (pipes[i].mid.oxid==mid->oxid)
192       return S_OK;
193 #endif
194
195   if (nrofpipes + 1 >= MAX_WINE_PIPES)
196   {
197     FIXME("Out of pipes, please increase MAX_WINE_PIPES\n");
198     return E_OUTOFMEMORY;
199   }
200   memcpy(&(pipes[nrofpipes].mid),mid,sizeof(*mid));
201   pipes[nrofpipes].hPipe        = hPipe;
202   pipes[nrofpipes].apt          = COM_CurrentApt();
203   assert( pipes[nrofpipes].apt );
204   InitializeCriticalSection(&(pipes[nrofpipes].crit));
205   nrofpipes++;
206   if (startreader) {
207       pipes[nrofpipes-1].hThread = CreateThread(NULL,0,stub_dispatch_thread,(LPVOID)(pipes+(nrofpipes-1)),0,&(pipes[nrofpipes-1].tid));
208   } else {
209       pipes[nrofpipes-1].tid     = GetCurrentThreadId();
210   }
211   return S_OK;
212 }
213
214 static HANDLE
215 PIPE_FindByMID(wine_marshal_id *mid) {
216   int i;
217   for (i=0;i<nrofpipes;i++)
218     if ((pipes[i].mid.oxid==mid->oxid) &&
219         (GetCurrentThreadId()==pipes[i].tid)
220     )
221       return pipes[i].hPipe;
222   return INVALID_HANDLE_VALUE;
223 }
224
225 static wine_pipe*
226 PIPE_GetFromMID(wine_marshal_id *mid) {
227   int i;
228   for (i=0;i<nrofpipes;i++) {
229     if ((pipes[i].mid.oxid==mid->oxid) &&
230         (GetCurrentThreadId()==pipes[i].tid)
231     )
232       return pipes+i;
233   }
234   return NULL;
235 }
236
237 static HRESULT
238 RPC_GetRequest(wine_rpc_request **req) {
239     static int reqid = 0xdeadbeef;
240     int i;
241
242     for (i=0;i<nrofreqs;i++) { /* try to reuse */
243         if (reqs[i]->state == REQSTATE_DONE) {
244             reqs[i]->reqh.reqid = reqid++;
245             reqs[i]->resph.reqid = reqs[i]->reqh.reqid;
246             reqs[i]->hPipe = INVALID_HANDLE_VALUE;
247             *req = reqs[i];
248             reqs[i]->state = REQSTATE_START;
249             return S_OK;
250         }
251     }
252     /* create new */
253     if (reqs)
254         reqs = (wine_rpc_request**)HeapReAlloc(
255                         GetProcessHeap(),
256                         HEAP_ZERO_MEMORY,
257                         reqs,
258                         sizeof(wine_rpc_request*)*(nrofreqs+1)
259                 );
260     else
261         reqs = (wine_rpc_request**)HeapAlloc(
262                         GetProcessHeap(),
263                         HEAP_ZERO_MEMORY,
264                         sizeof(wine_rpc_request*)
265                 );
266     if (!reqs)
267         return E_OUTOFMEMORY;
268     reqs[nrofreqs] = (wine_rpc_request*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(wine_rpc_request));
269     reqs[nrofreqs]->reqh.reqid = reqid++;
270     reqs[nrofreqs]->resph.reqid = reqs[nrofreqs]->reqh.reqid;
271     reqs[nrofreqs]->hPipe = INVALID_HANDLE_VALUE;
272     *req = reqs[nrofreqs];
273     reqs[nrofreqs]->state = REQSTATE_START;
274     nrofreqs++;
275     return S_OK;
276 }
277
278 static void
279 RPC_FreeRequest(wine_rpc_request *req) {
280     req->state = REQSTATE_DONE; /* Just reuse slot. */
281     return;
282 }
283
284 static HRESULT WINAPI
285 PipeBuf_QueryInterface(
286     LPRPCCHANNELBUFFER iface,REFIID riid,LPVOID *ppv
287 ) {
288     *ppv = NULL;
289     if (IsEqualIID(riid,&IID_IRpcChannelBuffer) || IsEqualIID(riid,&IID_IUnknown)) {
290         *ppv = (LPVOID)iface;
291         IUnknown_AddRef(iface);
292         return S_OK;
293     }
294     return E_NOINTERFACE;
295 }
296
297 static ULONG WINAPI
298 PipeBuf_AddRef(LPRPCCHANNELBUFFER iface) {
299     PipeBuf *This = (PipeBuf *)iface;
300     return InterlockedIncrement(&This->ref);
301 }
302
303 static ULONG WINAPI
304 PipeBuf_Release(LPRPCCHANNELBUFFER iface) {
305     PipeBuf *This = (PipeBuf *)iface;
306     ULONG ref;
307     wine_rpc_disconnect_header header;
308     HANDLE pipe;
309     DWORD reqtype = REQTYPE_DISCONNECT;
310     DWORD magic;
311
312     ref = InterlockedDecrement(&This->ref);
313     if (ref)
314         return ref;
315
316     memcpy(&header.mid, &This->mid, sizeof(wine_marshal_id));
317
318     pipe = PIPE_FindByMID(&This->mid);
319
320     write_pipe(pipe, &reqtype, sizeof(reqtype));
321     write_pipe(pipe, &header, sizeof(wine_rpc_disconnect_header));
322
323     TRACE("written disconnect packet\n");
324
325     /* prevent a disconnect race with the other side: this isn't
326      * necessary for real dcom but the test suite needs it */
327     
328     read_pipe(pipe, &magic, sizeof(magic));
329     if (magic != 0xcafebabe) ERR("bad disconnection magic: expecting 0xcafebabe but got 0x%lx", magic);
330
331     HeapFree(GetProcessHeap(),0,This);
332     return 0;
333 }
334
335 static HRESULT WINAPI
336 PipeBuf_GetBuffer(
337     LPRPCCHANNELBUFFER iface,RPCOLEMESSAGE* msg,REFIID riid
338 ) {
339     /*PipeBuf *This = (PipeBuf *)iface;*/
340
341     TRACE("(%p,%s)\n",msg,debugstr_guid(riid));
342     /* probably reuses IID in real. */
343     if (msg->cbBuffer && (msg->Buffer == NULL))
344         msg->Buffer = HeapAlloc(GetProcessHeap(),0,msg->cbBuffer);
345     return S_OK;
346 }
347
348 static HRESULT
349 COM_InvokeAndRpcSend(wine_rpc_request *req) {
350     IRpcStubBuffer     *stub;
351     RPCOLEMESSAGE       msg;
352     HRESULT             hres;
353     DWORD               reqtype;
354
355     if (!(stub = mid_to_stubbuffer(&(req->reqh.mid))))
356     {
357         ERR("Stub not found?\n");
358         return E_FAIL;
359     }
360
361     IUnknown_AddRef(stub);
362     msg.Buffer          = req->Buffer;
363     msg.iMethod         = req->reqh.iMethod;
364     msg.cbBuffer        = req->reqh.cbBuffer;
365     msg.dataRepresentation = NDR_LOCAL_DATA_REPRESENTATION;
366     req->state          = REQSTATE_INVOKING;
367     req->resph.retval   = IRpcStubBuffer_Invoke(stub,&msg,NULL);
368     IUnknown_Release(stub);
369     req->Buffer         = msg.Buffer;
370     req->resph.cbBuffer = msg.cbBuffer;
371     reqtype             = REQTYPE_RESPONSE;
372     hres = write_pipe(req->hPipe,&reqtype,sizeof(reqtype));
373     if (hres) return hres;
374     hres = write_pipe(req->hPipe,&(req->resph),sizeof(req->resph));
375     if (hres) return hres;
376     hres = write_pipe(req->hPipe,req->Buffer,req->resph.cbBuffer);
377     if (hres) return hres;
378     req->state = REQSTATE_DONE;
379     drs("invoke");
380     return S_OK;
381 }
382
383 static HRESULT COM_RpcReceive(wine_pipe *xpipe);
384
385 static HRESULT
386 RPC_QueueRequestAndWait(wine_rpc_request *req) {
387     int                 i;
388     wine_rpc_request    *xreq;
389     HRESULT             hres;
390     DWORD               reqtype;
391     wine_pipe           *xpipe = PIPE_GetFromMID(&(req->reqh.mid));
392
393     if (!xpipe) {
394         FIXME("no pipe found.\n");
395         return E_POINTER;
396     }
397     req->hPipe = xpipe->hPipe;
398     req->state = REQSTATE_REQ_WAITING_FOR_REPLY;
399     reqtype = REQTYPE_REQUEST;
400     hres = write_pipe(req->hPipe,&reqtype,sizeof(reqtype));
401     if (hres) return hres;
402     hres = write_pipe(req->hPipe,&(req->reqh),sizeof(req->reqh));
403     if (hres) return hres;
404     hres = write_pipe(req->hPipe,req->Buffer,req->reqh.cbBuffer);
405     if (hres) return hres;
406
407     /* This loop is about allowing re-entrancy. While waiting for the
408      * response to one RPC we may receive a request starting another. */
409     while (!hres) {
410         hres = COM_RpcReceive(xpipe);
411         if (hres) break;
412
413         for (i=0;i<nrofreqs;i++) {
414             xreq = reqs[i];
415             if ((xreq->state==REQSTATE_REQ_GOT) && (xreq->hPipe==req->hPipe)) {
416                 hres = COM_InvokeAndRpcSend(xreq);
417                 if (hres) break;
418             }
419         }
420         if (req->state == REQSTATE_RESP_GOT)
421             return S_OK;
422     }
423     if (FAILED(hres))
424         WARN("-- 0x%08lx\n", hres);
425     return hres;
426 }
427
428 static HRESULT WINAPI
429 PipeBuf_SendReceive(
430     LPRPCCHANNELBUFFER iface,RPCOLEMESSAGE* msg,ULONG *status
431 ) {
432     PipeBuf *This = (PipeBuf *)iface;
433     wine_rpc_request    *req;
434     HRESULT             hres;
435
436     TRACE("()\n");
437
438     if (This->mid.oxid == COM_CurrentApt()->oxid) {
439         ERR("Need to call directly!\n");
440         return E_FAIL;
441     }
442
443     hres = RPC_GetRequest(&req);
444     if (hres) return hres;
445     req->reqh.iMethod   = msg->iMethod;
446     req->reqh.cbBuffer  = msg->cbBuffer;
447     memcpy(&(req->reqh.mid),&(This->mid),sizeof(This->mid));
448     req->Buffer = msg->Buffer;
449     hres = RPC_QueueRequestAndWait(req);
450     if (hres) {
451         RPC_FreeRequest(req);
452         return hres;
453     }
454     msg->cbBuffer       = req->resph.cbBuffer;
455     msg->Buffer         = req->Buffer;
456     *status             = req->resph.retval;
457     RPC_FreeRequest(req);
458     return S_OK;
459 }
460
461
462 static HRESULT WINAPI
463 PipeBuf_FreeBuffer(LPRPCCHANNELBUFFER iface,RPCOLEMESSAGE* msg) {
464     FIXME("(%p), stub!\n",msg);
465     return E_FAIL;
466 }
467
468 static HRESULT WINAPI
469 PipeBuf_GetDestCtx(
470     LPRPCCHANNELBUFFER iface,DWORD* pdwDestContext,void** ppvDestContext
471 ) {
472     FIXME("(%p,%p), stub!\n",pdwDestContext,ppvDestContext);
473     return E_FAIL;
474 }
475
476 static HRESULT WINAPI
477 PipeBuf_IsConnected(LPRPCCHANNELBUFFER iface) {
478     FIXME("(), stub!\n");
479     return S_OK;
480 }
481
482 static IRpcChannelBufferVtbl pipebufvt = {
483     PipeBuf_QueryInterface,
484     PipeBuf_AddRef,
485     PipeBuf_Release,
486     PipeBuf_GetBuffer,
487     PipeBuf_SendReceive,
488     PipeBuf_FreeBuffer,
489     PipeBuf_GetDestCtx,
490     PipeBuf_IsConnected
491 };
492
493 HRESULT
494 PIPE_GetNewPipeBuf(wine_marshal_id *mid, IRpcChannelBuffer **pipebuf) {
495   wine_marshal_id       ourid;
496   DWORD                 res;
497   HANDLE                hPipe;
498   HRESULT               hres;
499   PipeBuf               *pbuf;
500
501   hPipe = PIPE_FindByMID(mid);
502   if (hPipe == INVALID_HANDLE_VALUE) {
503       char                      pipefn[200];
504       sprintf(pipefn,OLESTUBMGR"_%08lx%08lx",(DWORD)(mid->oxid >> 32),(DWORD)mid->oxid);
505       hPipe = CreateFileA(
506               pipefn,
507               GENERIC_READ|GENERIC_WRITE,
508               0,
509               NULL,
510               OPEN_EXISTING,
511               0,
512               0
513       );
514       if (hPipe == INVALID_HANDLE_VALUE) {
515           FIXME("Could not open named pipe %s, le is %lx\n",pipefn,GetLastError());
516           return E_FAIL;
517       }
518       hres = PIPE_RegisterPipe(mid, hPipe, FALSE);
519       if (hres) return hres;
520       memset(&ourid,0,sizeof(ourid));
521       ourid.oxid = COM_CurrentApt()->oxid;
522       if (!WriteFile(hPipe,&ourid,sizeof(ourid),&res,NULL)||(res!=sizeof(ourid))) {
523           ERR("Failed writing startup mid!\n");
524           return E_FAIL;
525       }
526   }
527   pbuf = (PipeBuf*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(PipeBuf));
528   pbuf->lpVtbl  = &pipebufvt;
529   pbuf->ref     = 1;
530   memcpy(&(pbuf->mid),mid,sizeof(*mid));
531   *pipebuf = (IRpcChannelBuffer*)pbuf;
532   return S_OK;
533 }
534
535 static HRESULT
536 create_server(REFCLSID rclsid) {
537   static const WCHAR embedding[] = { ' ', '-','E','m','b','e','d','d','i','n','g',0 };
538   HKEY          key;
539   char          buf[200];
540   HRESULT       hres = E_UNEXPECTED;
541   char          xclsid[80];
542   WCHAR        exe[MAX_PATH+1];
543   DWORD        exelen = sizeof(exe);
544   WCHAR         command[MAX_PATH+sizeof(embedding)/sizeof(WCHAR)];
545   STARTUPINFOW  sinfo;
546   PROCESS_INFORMATION   pinfo;
547
548   WINE_StringFromCLSID((LPCLSID)rclsid,xclsid);
549
550   sprintf(buf,"CLSID\\%s\\LocalServer32",xclsid);
551   hres = RegOpenKeyExA(HKEY_CLASSES_ROOT, buf, 0, KEY_READ, &key);
552
553   if (hres != ERROR_SUCCESS) {
554       WARN("CLSID %s not registered as LocalServer32\n", xclsid);
555       return REGDB_E_READREGDB; /* Probably */
556   }
557
558   memset(exe,0,sizeof(exe));
559   hres= RegQueryValueExW(key, NULL, NULL, NULL, (LPBYTE)exe, &exelen);
560   RegCloseKey(key);
561   if (hres) {
562       WARN("No default value for LocalServer32 key\n");
563       return REGDB_E_CLASSNOTREG; /* FIXME: check retval */
564   }
565
566   memset(&sinfo,0,sizeof(sinfo));
567   sinfo.cb = sizeof(sinfo);
568
569   /* EXE servers are started with the -Embedding switch. MSDN also claims /Embedding is used,
570      9x does -Embedding, perhaps an 9x/NT difference?  */
571
572   strcpyW(command, exe);
573   strcatW(command, embedding);
574
575   TRACE("activating local server '%s' for %s\n", debugstr_w(command), xclsid);
576
577   if (!CreateProcessW(exe, command, NULL, NULL, FALSE, 0, NULL, NULL, &sinfo, &pinfo)) {
578       WARN("failed to run local server %s\n", debugstr_w(exe));
579       return E_FAIL;
580   }
581
582   return S_OK;
583 }
584
585 /*
586  * start_local_service()  - start a service given its name and parameters
587  */
588 static DWORD
589 start_local_service(LPCWSTR name, DWORD num, LPWSTR *params)
590 {
591     SC_HANDLE handle, hsvc;
592     DWORD r = ERROR_FUNCTION_FAILED;
593
594     TRACE("Starting service %s %ld params\n", debugstr_w(name), num);
595
596     handle = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
597     if (!handle)
598         return r;
599     hsvc = OpenServiceW(handle, name, SC_MANAGER_ALL_ACCESS);
600     if (hsvc)
601     {
602         if(StartServiceW(hsvc, num, (LPCWSTR*)params))
603             r = ERROR_SUCCESS;
604         else
605             r = GetLastError();
606         if (r==ERROR_SERVICE_ALREADY_RUNNING)
607             r = ERROR_SUCCESS;
608         CloseServiceHandle(hsvc);
609     }
610     CloseServiceHandle(handle);
611
612     TRACE("StartService returned error %ld (%s)\n", r, r?"ok":"failed");
613
614     return r;
615 }
616
617 /*
618  * create_local_service()  - start a COM server in a service
619  *
620  *   To start a Local Service, we read the AppID value under
621  * the class's CLSID key, then open the HKCR\\AppId key specified
622  * there and check for a LocalService value.
623  *
624  * Note:  Local Services are not supported under Windows 9x
625  */
626 static HRESULT
627 create_local_service(REFCLSID rclsid)
628 {
629     HRESULT hres = REGDB_E_READREGDB;
630     WCHAR buf[40], keyname[50];
631     static const WCHAR szClsId[] = { 'C','L','S','I','D','\\',0 };
632     static const WCHAR szAppId[] = { 'A','p','p','I','d',0 };
633     static const WCHAR szAppIdKey[] = { 'A','p','p','I','d','\\',0 };
634     static const WCHAR szLocalService[] = { 
635                  'L','o','c','a','l','S','e','r','v','i','c','e',0 };
636     static const WCHAR szServiceParams[] = {
637                  'S','e','r','v','i','c','e','P','a','r','a','m','s',0};
638     HKEY hkey;
639     LONG r;
640     DWORD type, sz;
641
642     TRACE("Attempting to start Local service for %s\n", debugstr_guid(rclsid));
643
644     /* read the AppID value under the class's key */
645     strcpyW(keyname,szClsId);
646     StringFromGUID2(rclsid,&keyname[6],39);
647     r = RegOpenKeyExW(HKEY_CLASSES_ROOT, keyname, 0, KEY_READ, &hkey);
648     if (r!=ERROR_SUCCESS)
649         return hres;
650     sz = sizeof buf;
651     r = RegQueryValueExW(hkey, szAppId, NULL, &type, (LPBYTE)buf, &sz);
652     RegCloseKey(hkey);
653     if (r!=ERROR_SUCCESS || type!=REG_SZ)
654         return hres;
655
656     /* read the LocalService and ServiceParameters values from the AppID key */
657     strcpyW(keyname, szAppIdKey);
658     strcatW(keyname, buf);
659     r = RegOpenKeyExW(HKEY_CLASSES_ROOT, keyname, 0, KEY_READ, &hkey);
660     if (r!=ERROR_SUCCESS)
661         return hres;
662     sz = sizeof buf;
663     r = RegQueryValueExW(hkey, szLocalService, NULL, &type, (LPBYTE)buf, &sz);
664     if (r==ERROR_SUCCESS && type==REG_SZ)
665     {
666         DWORD num_args = 0;
667         LPWSTR args[1] = { NULL };
668
669         /*
670          * FIXME: I'm not really sure how to deal with the service parameters.
671          *        I suspect that the string returned from RegQueryValueExW
672          *        should be split into a number of arguments by spaces.
673          *        It would make more sense if ServiceParams contained a
674          *        REG_MULTI_SZ here, but it's a REG_SZ for the services
675          *        that I'm interested in for the moment.
676          */
677         r = RegQueryValueExW(hkey, szServiceParams, NULL, &type, NULL, &sz);
678         if (r == ERROR_SUCCESS && type == REG_SZ && sz)
679         {
680             args[0] = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sz);
681             num_args++;
682             RegQueryValueExW(hkey, szServiceParams, NULL, &type, (LPBYTE)args[0], &sz);
683         }
684         r = start_local_service(buf, num_args, args);
685         if (r==ERROR_SUCCESS)
686             hres = S_OK;
687         HeapFree(GetProcessHeap(),0,args[0]);
688     }
689     RegCloseKey(hkey);
690         
691     return hres;
692 }
693
694 /* http://msdn.microsoft.com/library/en-us/dnmsj99/html/com0199.asp, Figure 4 */
695 HRESULT create_marshalled_proxy(REFCLSID rclsid, REFIID iid, LPVOID *ppv) {
696   HRESULT       hres;
697   HANDLE        hPipe;
698   char          pipefn[200];
699   DWORD         res,bufferlen;
700   char          marshalbuffer[200];
701   IStream       *pStm;
702   LARGE_INTEGER seekto;
703   ULARGE_INTEGER newpos;
704   int           tries = 0;
705 #define MAXTRIES 10000
706
707   TRACE("rclsid=%s, iid=%s\n", debugstr_guid(rclsid), debugstr_guid(iid));
708
709   strcpy(pipefn,PIPEPREF);
710   WINE_StringFromCLSID(rclsid,pipefn+strlen(PIPEPREF));
711
712   while (tries++<MAXTRIES) {
713       TRACE("waiting for %s\n", pipefn);
714       
715       WaitNamedPipeA( pipefn, NMPWAIT_WAIT_FOREVER );
716       hPipe     = CreateFileA(
717               pipefn,
718               GENERIC_READ|GENERIC_WRITE,
719               0,
720               NULL,
721               OPEN_EXISTING,
722               0,
723               0
724       );
725       if (hPipe == INVALID_HANDLE_VALUE) {
726           if (tries == 1) {
727               if ( (hres = create_server(rclsid)) &&
728                    (hres = create_local_service(rclsid)) )
729                   return hres;
730               Sleep(1000);
731           } else {
732               WARN("Could not open named pipe to broker %s, le is %lx\n",pipefn,GetLastError());
733               Sleep(1000);
734           }
735           continue;
736       }
737       bufferlen = 0;
738       if (!ReadFile(hPipe,marshalbuffer,sizeof(marshalbuffer),&bufferlen,NULL)) {
739           FIXME("Failed to read marshal id from classfactory of %s.\n",debugstr_guid(rclsid));
740           Sleep(1000);
741           continue;
742       }
743       TRACE("read marshal id from pipe\n");
744       CloseHandle(hPipe);
745       break;
746   }
747   if (tries>=MAXTRIES)
748       return E_NOINTERFACE;
749   hres = CreateStreamOnHGlobal(0,TRUE,&pStm);
750   if (hres) return hres;
751   hres = IStream_Write(pStm,marshalbuffer,bufferlen,&res);
752   if (hres) goto out;
753   seekto.u.LowPart = 0;seekto.u.HighPart = 0;
754   hres = IStream_Seek(pStm,seekto,SEEK_SET,&newpos);
755   TRACE("unmarshalling classfactory\n");
756   hres = CoUnmarshalInterface(pStm,&IID_IClassFactory,ppv);
757 out:
758   IStream_Release(pStm);
759   return hres;
760 }
761
762
763 static void WINAPI
764 PIPE_StartRequestThread(HANDLE xhPipe) {
765     wine_marshal_id     remoteid;
766     HRESULT             hres;
767
768     hres = read_pipe(xhPipe,&remoteid,sizeof(remoteid));
769     if (hres) {
770         ERR("Failed to read remote mid!\n");
771         return;
772     }
773     PIPE_RegisterPipe(&remoteid,xhPipe, TRUE);
774 }
775
776 static HRESULT
777 COM_RpcReceive(wine_pipe *xpipe) {
778     DWORD       reqtype;
779     HRESULT     hres = S_OK;
780     HANDLE      xhPipe = xpipe->hPipe;
781
782     /*FIXME("%lx %d reading reqtype\n",GetCurrentProcessId(),xhPipe);*/
783     hres = read_pipe(xhPipe,&reqtype,sizeof(reqtype));
784     if (hres) goto end;
785     EnterCriticalSection(&(xpipe->crit));
786     /*FIXME("%lx got reqtype %ld\n",GetCurrentProcessId(),reqtype);*/
787
788     if (reqtype == REQTYPE_DISCONNECT) { /* only received by servers */
789         wine_rpc_disconnect_header header;
790         struct stub_manager *stubmgr;
791         DWORD magic = 0xcafebabe;
792         APARTMENT *apt;
793
794         hres = read_pipe(xhPipe, &header, sizeof(header));
795         if (hres) {
796             ERR("could not read disconnect header\n");
797             goto disconnect_end;
798         }
799
800         TRACE("read disconnect header\n");
801
802         if (!(apt = COM_ApartmentFromOXID(header.mid.oxid, TRUE)))
803         {
804             ERR("Could not map OXID %s to apartment object in disconnect\n", wine_dbgstr_longlong(header.mid.oxid));
805             goto disconnect_end;
806         }
807
808         if (!(stubmgr = get_stub_manager(apt, header.mid.oid)))
809         {
810             ERR("could not locate stub to disconnect, mid.oid=%s\n", wine_dbgstr_longlong(header.mid.oid));
811             COM_ApartmentRelease(apt);
812             goto disconnect_end;
813         }
814
815         stub_manager_ext_release(stubmgr, 1);
816
817         stub_manager_int_release(stubmgr);
818         COM_ApartmentRelease(apt);
819
820 disconnect_end:
821         write_pipe(xhPipe, &magic, sizeof(magic));
822         goto end;
823     } else if (reqtype == REQTYPE_REQUEST) {
824         wine_rpc_request        *xreq;
825         RPC_GetRequest(&xreq);
826         xreq->hPipe = xhPipe;
827         hres = read_pipe(xhPipe,&(xreq->reqh),sizeof(xreq->reqh));
828         if (hres) goto end;
829         xreq->resph.reqid = xreq->reqh.reqid;
830         xreq->Buffer = HeapAlloc(GetProcessHeap(),0, xreq->reqh.cbBuffer);
831         hres = read_pipe(xhPipe,xreq->Buffer,xreq->reqh.cbBuffer);
832         if (hres) goto end;
833         xreq->state = REQSTATE_REQ_GOT;
834         goto end;
835     } else if (reqtype == REQTYPE_RESPONSE) {
836         wine_rpc_response_header        resph;
837         int i;
838
839         hres = read_pipe(xhPipe,&resph,sizeof(resph));
840         if (hres) goto end;
841         for (i=nrofreqs;i--;) {
842             wine_rpc_request *xreq = reqs[i];
843             if (xreq->state != REQSTATE_REQ_WAITING_FOR_REPLY)
844                 continue;
845             if (xreq->reqh.reqid == resph.reqid) {
846                 memcpy(&(xreq->resph),&resph,sizeof(resph));
847
848                 if (xreq->Buffer)
849                     xreq->Buffer = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,xreq->Buffer,xreq->resph.cbBuffer);
850                 else
851                     xreq->Buffer = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,xreq->resph.cbBuffer);
852
853                 hres = read_pipe(xhPipe,xreq->Buffer,xreq->resph.cbBuffer);
854                 if (hres) goto end;
855                 xreq->state = REQSTATE_RESP_GOT;
856                 /*PulseEvent(hRpcChanged);*/
857                 goto end;
858             }
859         }
860         ERR("Did not find request for id %lx\n",resph.reqid);
861         hres = S_OK;
862         goto end;
863     }
864     ERR("Unknown reqtype %ld\n",reqtype);
865     hres = E_FAIL;
866 end:
867     LeaveCriticalSection(&(xpipe->crit));
868     return hres;
869 }
870
871 /* This thread listens on the given pipe for requests to a particular stub manager */
872 static DWORD WINAPI stub_dispatch_thread(LPVOID param)
873 {
874     wine_pipe           *xpipe = (wine_pipe*)param;
875     HANDLE              xhPipe = xpipe->hPipe;
876     HRESULT             hres = S_OK;
877
878     TRACE("starting for apartment OXID %08lx%08lx\n", (DWORD)(xpipe->mid.oxid >> 32), (DWORD)(xpipe->mid.oxid));
879
880     /* join marshalling apartment. fixme: this stuff is all very wrong, threading needs to work like native */
881     COM_CurrentInfo()->apt = xpipe->apt;
882     
883     while (!hres) {
884         int i;
885         
886         hres = COM_RpcReceive(xpipe);
887         if (hres) break;
888
889         for (i=nrofreqs;i--;) {
890             wine_rpc_request *xreq = reqs[i];
891             if ((xreq->state == REQSTATE_REQ_GOT) && (xreq->hPipe == xhPipe)) {
892                 hres = COM_InvokeAndRpcSend(xreq);
893                 if (!hres) break;
894             }
895         }
896     }
897
898     /* fixme: this thread never quits naturally */
899     WARN("exiting with hres %lx\n",hres);
900     CloseHandle(xhPipe);
901     return 0;
902 }
903
904 struct apartment_listener_params
905 {
906     APARTMENT *apt;
907     HANDLE event;
908 };
909
910 /* This thread listens on a named pipe for each apartment that exports
911  * objects. It deals with incoming connection requests. Each time a
912  * client connects a separate thread is spawned for that particular
913  * connection.
914  *
915  * This architecture is different in native DCOM.
916  */
917 static DWORD WINAPI apartment_listener_thread(LPVOID p)
918 {
919     char                pipefn[200];
920     HANDLE              listenPipe;
921     struct apartment_listener_params * params = (struct apartment_listener_params *)p;
922     APARTMENT *apt = params->apt;
923     HANDLE event = params->event;
924
925     HeapFree(GetProcessHeap(), 0, params);
926
927     /* we must join the marshalling threads apartment. we already have a ref here */
928     COM_CurrentInfo()->apt = apt;
929
930     sprintf(pipefn,OLESTUBMGR"_%08lx%08lx", (DWORD)(apt->oxid >> 32), (DWORD)(apt->oxid));
931     TRACE("Apartment listener thread starting on (%s)\n",pipefn);
932
933     while (1) {
934         listenPipe = CreateNamedPipeA(
935             pipefn,
936             PIPE_ACCESS_DUPLEX,
937             PIPE_TYPE_BYTE|PIPE_WAIT,
938             PIPE_UNLIMITED_INSTANCES,
939             4096,
940             4096,
941             NMPWAIT_USE_DEFAULT_WAIT,
942             NULL
943         );
944
945         /* tell function that started this thread that we have attempted to created the
946          * named pipe. */
947         if (event) {
948             SetEvent(event);
949             event = NULL;
950         }
951
952         if (listenPipe == INVALID_HANDLE_VALUE) {
953             FIXME("pipe creation failed for %s, error %ld\n",pipefn,GetLastError());
954             return 1; /* permanent failure, so quit stubmgr thread */
955         }
956
957         /* an already connected pipe is not an error */
958         if (!ConnectNamedPipe(listenPipe,NULL) &&
959             (GetLastError() != ERROR_PIPE_CONNECTED)) {
960             ERR("Failure during ConnectNamedPipe %ld!\n",GetLastError());
961             CloseHandle(listenPipe);
962             continue;
963         }
964
965         PIPE_StartRequestThread(listenPipe);
966     }
967     return 0;
968 }
969
970 void start_apartment_listener_thread()
971 {
972     APARTMENT *apt = COM_CurrentApt();
973     
974     assert( apt );
975     
976     TRACE("apt->listenertid=%ld\n", apt->listenertid);
977
978     /* apt->listenertid is a hack which needs to die at some point, as
979      * it leaks information into the apartment structure. in fact,
980      * this thread isn't quite correct anyway as native RPC doesn't
981      * use a thread per apartment at all, instead the dispatch thread
982      * either enters the apartment to perform the RPC (for MTAs, RTAs)
983      * or does a context switch into it for STAs.
984      */
985     
986     if (!apt->listenertid)
987     {
988         HANDLE thread;
989         HANDLE event = CreateEventW(NULL, TRUE, FALSE, NULL);
990         struct apartment_listener_params * params = HeapAlloc(GetProcessHeap(), 0, sizeof(*params));
991
992         params->apt = apt;
993         params->event = event;
994         thread = CreateThread(NULL, 0, apartment_listener_thread, params, 0, &apt->listenertid);
995         CloseHandle(thread);
996         /* wait for pipe to be created before returning, otherwise we
997          * might try to use it and fail */
998         WaitForSingleObject(event, INFINITE);
999         CloseHandle(event);
1000     }
1001 }
1002
1003 struct local_server_params
1004 {
1005     CLSID clsid;
1006     IStream *stream;
1007 };
1008
1009 static DWORD WINAPI local_server_thread(LPVOID param)
1010 {
1011     struct local_server_params * lsp = (struct local_server_params *)param;
1012     HANDLE              hPipe;
1013     char                pipefn[200];
1014     HRESULT             hres;
1015     IStream             *pStm = lsp->stream;
1016     STATSTG             ststg;
1017     unsigned char       *buffer;
1018     int                 buflen;
1019     LARGE_INTEGER       seekto;
1020     ULARGE_INTEGER      newpos;
1021     ULONG               res;
1022
1023     TRACE("Starting threader for %s.\n",debugstr_guid(&lsp->clsid));
1024
1025     strcpy(pipefn,PIPEPREF);
1026     WINE_StringFromCLSID(&lsp->clsid,pipefn+strlen(PIPEPREF));
1027
1028     HeapFree(GetProcessHeap(), 0, lsp);
1029
1030     hPipe = CreateNamedPipeA( pipefn, PIPE_ACCESS_DUPLEX,
1031                PIPE_TYPE_BYTE|PIPE_WAIT, PIPE_UNLIMITED_INSTANCES,
1032                4096, 4096, NMPWAIT_USE_DEFAULT_WAIT, NULL );
1033     if (hPipe == INVALID_HANDLE_VALUE) {
1034         FIXME("pipe creation failed for %s, le is %ld\n",pipefn,GetLastError());
1035         return 1;
1036     }
1037     while (1) {
1038         if (!ConnectNamedPipe(hPipe,NULL)) {
1039             ERR("Failure during ConnectNamedPipe %ld, ABORT!\n",GetLastError());
1040             break;
1041         }
1042
1043         TRACE("marshalling IClassFactory to client\n");
1044         
1045         hres = IStream_Stat(pStm,&ststg,0);
1046         if (hres) return hres;
1047
1048         buflen = ststg.cbSize.u.LowPart;
1049         buffer = HeapAlloc(GetProcessHeap(),0,buflen);
1050         seekto.u.LowPart = 0;
1051         seekto.u.HighPart = 0;
1052         hres = IStream_Seek(pStm,seekto,SEEK_SET,&newpos);
1053         if (hres) {
1054             FIXME("IStream_Seek failed, %lx\n",hres);
1055             return hres;
1056         }
1057         
1058         hres = IStream_Read(pStm,buffer,buflen,&res);
1059         if (hres) {
1060             FIXME("Stream Read failed, %lx\n",hres);
1061             return hres;
1062         }
1063         
1064         IStream_Release(pStm);
1065
1066         WriteFile(hPipe,buffer,buflen,&res,NULL);
1067         FlushFileBuffers(hPipe);
1068         DisconnectNamedPipe(hPipe);
1069
1070         TRACE("done marshalling IClassFactory\n");
1071     }
1072     CloseHandle(hPipe);
1073     return 0;
1074 }
1075
1076 void RPC_StartLocalServer(REFCLSID clsid, IStream *stream)
1077 {
1078     DWORD tid;
1079     HANDLE thread;
1080     struct local_server_params *lsp = HeapAlloc(GetProcessHeap(), 0, sizeof(*lsp));
1081
1082     lsp->clsid = *clsid;
1083     lsp->stream = stream;
1084
1085     thread = CreateThread(NULL, 0, local_server_thread, lsp, 0, &tid);
1086     CloseHandle(thread);
1087     /* FIXME: failure handling */
1088 }