- mktime should compute the tm_wday, tm_yday and renormalize the
[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 "objbase.h"
37 #include "ole2.h"
38 #include "ole2ver.h"
39 #include "rpc.h"
40 #include "winerror.h"
41 #include "winreg.h"
42 #include "wownt32.h"
43 #include "wtypes.h"
44 #include "wine/unicode.h"
45 #include "wine/winbase16.h"
46 #include "compobj_private.h"
47 #include "ifs.h"
48
49 #include "compobj_private.h"
50
51 #include "wine/debug.h"
52
53 WINE_DEFAULT_DEBUG_CHANNEL(ole);
54
55 #define REQTYPE_REQUEST         0
56 typedef struct _wine_rpc_request_header {
57     DWORD               reqid;
58     wine_marshal_id     mid;
59     DWORD               iMethod;
60     DWORD               cbBuffer;
61 } wine_rpc_request_header;
62
63 #define REQTYPE_RESPONSE        1
64 typedef struct _wine_rpc_response_header {
65     DWORD               reqid;
66     DWORD               cbBuffer;
67     DWORD               retval;
68 } wine_rpc_response_header;
69
70 /* used when shutting down a pipe, e.g. at the end of a process */
71 #define REQTYPE_DISCONNECT      2
72 typedef struct _wine_rpc_disconnect_header {
73   DWORD reqid;
74   wine_marshal_id mid; /* mid of stub to delete */
75 } wine_rpc_disconnect_header;
76
77
78 #define REQSTATE_START                  0
79 #define REQSTATE_REQ_QUEUED             1
80 #define REQSTATE_REQ_WAITING_FOR_REPLY  2
81 #define REQSTATE_REQ_GOT                3
82 #define REQSTATE_INVOKING               4
83 #define REQSTATE_RESP_QUEUED            5
84 #define REQSTATE_RESP_GOT               6
85 #define REQSTATE_DONE                   6
86
87 typedef struct _wine_rpc_request {
88     int                         state;
89     HANDLE                      hPipe;  /* temp copy of handle */
90     wine_rpc_request_header     reqh;
91     wine_rpc_response_header    resph;
92     LPBYTE                      Buffer;
93 } wine_rpc_request;
94
95 static wine_rpc_request **reqs = NULL;
96 static int nrofreqs = 0;
97
98 /* This pipe is _thread_ based, each thread which talks to a remote
99  * apartment (mid) has its own pipe */
100 typedef struct _wine_pipe {
101     wine_marshal_id     mid;    /* target mid */
102     DWORD               tid;    /* thread which owns this outgoing pipe */
103     HANDLE              hPipe;
104
105     int                 pending;
106     HANDLE              hThread;
107     CRITICAL_SECTION    crit;
108 } wine_pipe;
109
110 static wine_pipe *pipes = NULL;
111 static int nrofpipes = 0;
112
113 typedef struct _PipeBuf {
114     IRpcChannelBufferVtbl       *lpVtbl;
115     DWORD                               ref;
116
117     wine_marshal_id                     mid;
118     wine_pipe                           *pipe;
119 } PipeBuf;
120
121 static HRESULT WINAPI
122 read_pipe(HANDLE hf, LPVOID ptr, DWORD size) {
123     DWORD res;
124     if (!ReadFile(hf,ptr,size,&res,NULL)) {
125         FIXME("Failed to read from %p, le is %lx\n",hf,GetLastError());
126         return E_FAIL;
127     }
128     if (res!=size) {
129         FIXME("Read only %ld of %ld bytes from %p.\n",res,size,hf);
130         return E_FAIL;
131     }
132     return S_OK;
133 }
134
135 static void
136 drs(LPCSTR where) {
137 #if 0
138     static int nrofreaders = 0;
139
140     int i, states[10];
141
142     memset(states,0,sizeof(states));
143     for (i=nrofreqs;i--;)
144         states[reqs[i]->state]++;
145     FIXME("%lx/%s/%d: rq %d, w %d, rg %d, rsq %d, rsg %d, d %d\n",
146             GetCurrentProcessId(),
147             where,
148             nrofreaders,
149             states[REQSTATE_REQ_QUEUED],
150             states[REQSTATE_REQ_WAITING_FOR_REPLY],
151             states[REQSTATE_REQ_GOT],
152             states[REQSTATE_RESP_QUEUED],
153             states[REQSTATE_RESP_GOT],
154             states[REQSTATE_DONE]
155     );
156 #endif
157
158     return ;
159 }
160
161 static HRESULT WINAPI
162 write_pipe(HANDLE hf, LPVOID ptr, DWORD size) {
163     DWORD res;
164     if (!WriteFile(hf,ptr,size,&res,NULL)) {
165         FIXME("Failed to write to %p, le is %lx\n",hf,GetLastError());
166         return E_FAIL;
167     }
168     if (res!=size) {
169         FIXME("Wrote only %ld of %ld bytes to %p.\n",res,size,hf);
170         return E_FAIL;
171     }
172     return S_OK;
173 }
174
175 static DWORD WINAPI _StubReaderThread(LPVOID);
176
177 static HRESULT
178 PIPE_RegisterPipe(wine_marshal_id *mid, HANDLE hPipe, BOOL startreader) {
179   int   i;
180   char  pipefn[100];
181   wine_pipe *new_pipes;
182
183   for (i=0;i<nrofpipes;i++)
184     if (pipes[i].mid.processid==mid->processid)
185       return S_OK;
186   if (pipes)
187     new_pipes=(wine_pipe*)HeapReAlloc(GetProcessHeap(),0,pipes,sizeof(pipes[0])*(nrofpipes+1));
188   else
189     new_pipes=(wine_pipe*)HeapAlloc(GetProcessHeap(),0,sizeof(pipes[0]));
190   if (!new_pipes) return E_OUTOFMEMORY;
191   pipes = new_pipes;
192   sprintf(pipefn,OLESTUBMGR"_%08lx",mid->processid);
193   memcpy(&(pipes[nrofpipes].mid),mid,sizeof(*mid));
194   pipes[nrofpipes].hPipe        = hPipe;
195   InitializeCriticalSection(&(pipes[nrofpipes].crit));
196   nrofpipes++;
197   if (startreader) {
198       pipes[nrofpipes-1].hThread = CreateThread(NULL,0,_StubReaderThread,(LPVOID)(pipes+(nrofpipes-1)),0,&(pipes[nrofpipes-1].tid));
199   } else {
200       pipes[nrofpipes-1].tid     = GetCurrentThreadId();
201   }
202   return S_OK;
203 }
204
205 static HANDLE
206 PIPE_FindByMID(wine_marshal_id *mid) {
207   int i;
208   for (i=0;i<nrofpipes;i++)
209     if ((pipes[i].mid.processid==mid->processid) &&
210         (GetCurrentThreadId()==pipes[i].tid)
211     )
212       return pipes[i].hPipe;
213   return INVALID_HANDLE_VALUE;
214 }
215
216 static wine_pipe*
217 PIPE_GetFromMID(wine_marshal_id *mid) {
218   int i;
219   for (i=0;i<nrofpipes;i++) {
220     if ((pipes[i].mid.processid==mid->processid) &&
221         (GetCurrentThreadId()==pipes[i].tid)
222     )
223       return pipes+i;
224   }
225   return NULL;
226 }
227
228 static HRESULT
229 RPC_GetRequest(wine_rpc_request **req) {
230     static int reqid = 0xdeadbeef;
231     int i;
232
233     for (i=0;i<nrofreqs;i++) { /* try to reuse */
234         if (reqs[i]->state == REQSTATE_DONE) {
235             reqs[i]->reqh.reqid = reqid++;
236             reqs[i]->resph.reqid = reqs[i]->reqh.reqid;
237             reqs[i]->hPipe = INVALID_HANDLE_VALUE;
238             *req = reqs[i];
239             reqs[i]->state = REQSTATE_START;
240             return S_OK;
241         }
242     }
243     /* create new */
244     if (reqs)
245         reqs = (wine_rpc_request**)HeapReAlloc(
246                         GetProcessHeap(),
247                         HEAP_ZERO_MEMORY,
248                         reqs,
249                         sizeof(wine_rpc_request*)*(nrofreqs+1)
250                 );
251     else
252         reqs = (wine_rpc_request**)HeapAlloc(
253                         GetProcessHeap(),
254                         HEAP_ZERO_MEMORY,
255                         sizeof(wine_rpc_request*)
256                 );
257     if (!reqs)
258         return E_OUTOFMEMORY;
259     reqs[nrofreqs] = (wine_rpc_request*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(wine_rpc_request));
260     reqs[nrofreqs]->reqh.reqid = reqid++;
261     reqs[nrofreqs]->resph.reqid = reqs[nrofreqs]->reqh.reqid;
262     reqs[nrofreqs]->hPipe = INVALID_HANDLE_VALUE;
263     *req = reqs[nrofreqs];
264     reqs[nrofreqs]->state = REQSTATE_START;
265     nrofreqs++;
266     return S_OK;
267 }
268
269 static void
270 RPC_FreeRequest(wine_rpc_request *req) {
271     req->state = REQSTATE_DONE; /* Just reuse slot. */
272     return;
273 }
274
275 static HRESULT WINAPI
276 PipeBuf_QueryInterface(
277     LPRPCCHANNELBUFFER iface,REFIID riid,LPVOID *ppv
278 ) {
279     *ppv = NULL;
280     if (IsEqualIID(riid,&IID_IRpcChannelBuffer) || IsEqualIID(riid,&IID_IUnknown)) {
281         *ppv = (LPVOID)iface;
282         IUnknown_AddRef(iface);
283         return S_OK;
284     }
285     return E_NOINTERFACE;
286 }
287
288 static ULONG WINAPI
289 PipeBuf_AddRef(LPRPCCHANNELBUFFER iface) {
290     PipeBuf *This = (PipeBuf *)iface;
291     return InterlockedIncrement(&This->ref);
292 }
293
294 static ULONG WINAPI
295 PipeBuf_Release(LPRPCCHANNELBUFFER iface) {
296     PipeBuf *This = (PipeBuf *)iface;
297     ULONG ref;
298     wine_rpc_disconnect_header header;
299     HANDLE pipe;
300     DWORD reqtype = REQTYPE_DISCONNECT;
301
302     ref = InterlockedDecrement(&This->ref);
303     if (ref)
304         return ref;
305
306     FIXME("Free all stuff\n");
307
308     memcpy(&header.mid, &This->mid, sizeof(wine_marshal_id));
309
310     pipe = PIPE_FindByMID(&This->mid);
311
312     write_pipe(pipe, &reqtype, sizeof(reqtype));
313     write_pipe(pipe, &header, sizeof(wine_rpc_disconnect_header));
314
315     TRACE("written disconnect packet\n");
316
317     HeapFree(GetProcessHeap(),0,This);
318     return 0;
319 }
320
321 static HRESULT WINAPI
322 PipeBuf_GetBuffer(
323     LPRPCCHANNELBUFFER iface,RPCOLEMESSAGE* msg,REFIID riid
324 ) {
325     /*PipeBuf *This = (PipeBuf *)iface;*/
326
327     TRACE("(%p,%s)\n",msg,debugstr_guid(riid));
328     /* probably reuses IID in real. */
329     if (msg->cbBuffer && (msg->Buffer == NULL))
330         msg->Buffer = HeapAlloc(GetProcessHeap(),0,msg->cbBuffer);
331     return S_OK;
332 }
333
334 static HRESULT
335 COM_InvokeAndRpcSend(wine_rpc_request *req) {
336     IRpcStubBuffer      *stub;
337     RPCOLEMESSAGE       msg;
338     HRESULT             hres;
339     DWORD               reqtype;
340
341     hres = MARSHAL_Find_Stub_Buffer(&(req->reqh.mid),&stub);
342     if (hres) {
343         ERR("Stub not found?\n");
344         return hres;
345     }
346     msg.Buffer          = req->Buffer;
347     msg.iMethod         = req->reqh.iMethod;
348     msg.cbBuffer        = req->reqh.cbBuffer;
349     msg.dataRepresentation = NDR_LOCAL_DATA_REPRESENTATION;
350     req->state          = REQSTATE_INVOKING;
351     req->resph.retval   = IRpcStubBuffer_Invoke(stub,&msg,NULL);
352     IUnknown_Release(stub);
353     req->Buffer         = msg.Buffer;
354     req->resph.cbBuffer = msg.cbBuffer;
355     reqtype             = REQTYPE_RESPONSE;
356     hres = write_pipe(req->hPipe,&reqtype,sizeof(reqtype));
357     if (hres) return hres;
358     hres = write_pipe(req->hPipe,&(req->resph),sizeof(req->resph));
359     if (hres) return hres;
360     hres = write_pipe(req->hPipe,req->Buffer,req->resph.cbBuffer);
361     if (hres) return hres;
362     req->state = REQSTATE_DONE;
363     drs("invoke");
364     return S_OK;
365 }
366
367 static HRESULT COM_RpcReceive(wine_pipe *xpipe);
368
369 static HRESULT
370 RPC_QueueRequestAndWait(wine_rpc_request *req) {
371     int                 i;
372     wine_rpc_request    *xreq;
373     HRESULT             hres;
374     DWORD               reqtype;
375     wine_pipe           *xpipe = PIPE_GetFromMID(&(req->reqh.mid));
376
377     if (!xpipe) {
378         FIXME("no pipe found.\n");
379         return E_POINTER;
380     }
381     if (GetCurrentProcessId() == req->reqh.mid.processid) {
382         ERR("In current process?\n");
383         return E_FAIL;
384     }
385     req->hPipe = xpipe->hPipe;
386     req->state = REQSTATE_REQ_WAITING_FOR_REPLY;
387     reqtype = REQTYPE_REQUEST;
388     hres = write_pipe(req->hPipe,&reqtype,sizeof(reqtype));
389     if (hres) return hres;
390     hres = write_pipe(req->hPipe,&(req->reqh),sizeof(req->reqh));
391     if (hres) return hres;
392     hres = write_pipe(req->hPipe,req->Buffer,req->reqh.cbBuffer);
393     if (hres) return hres;
394
395     /* This loop is about allowing re-entrancy. While waiting for the
396      * response to one RPC we may receive a request starting another. */
397     while (!hres) {
398         hres = COM_RpcReceive(xpipe);
399         if (hres) break;
400
401         for (i=0;i<nrofreqs;i++) {
402             xreq = reqs[i];
403             if ((xreq->state==REQSTATE_REQ_GOT) && (xreq->hPipe==req->hPipe)) {
404                 hres = COM_InvokeAndRpcSend(xreq);
405                 if (hres) break;
406             }
407         }
408         if (req->state == REQSTATE_RESP_GOT)
409             return S_OK;
410     }
411     if (FAILED(hres))
412         WARN("-- 0x%08lx\n", hres);
413     return hres;
414 }
415
416 static HRESULT WINAPI
417 PipeBuf_SendReceive(
418     LPRPCCHANNELBUFFER iface,RPCOLEMESSAGE* msg,ULONG *status
419 ) {
420     PipeBuf *This = (PipeBuf *)iface;
421     wine_rpc_request    *req;
422     HRESULT             hres;
423
424     TRACE("()\n");
425
426     if (This->mid.processid == GetCurrentProcessId()) {
427         ERR("Need to call directly!\n");
428         return E_FAIL;
429     }
430
431     hres = RPC_GetRequest(&req);
432     if (hres) return hres;
433     req->reqh.iMethod   = msg->iMethod;
434     req->reqh.cbBuffer  = msg->cbBuffer;
435     memcpy(&(req->reqh.mid),&(This->mid),sizeof(This->mid));
436     req->Buffer = msg->Buffer;
437     hres = RPC_QueueRequestAndWait(req);
438     if (hres) {
439         RPC_FreeRequest(req);
440         return hres;
441     }
442     msg->cbBuffer       = req->resph.cbBuffer;
443     msg->Buffer         = req->Buffer;
444     *status             = req->resph.retval;
445     RPC_FreeRequest(req);
446     return S_OK;
447 }
448
449
450 static HRESULT WINAPI
451 PipeBuf_FreeBuffer(LPRPCCHANNELBUFFER iface,RPCOLEMESSAGE* msg) {
452     FIXME("(%p), stub!\n",msg);
453     return E_FAIL;
454 }
455
456 static HRESULT WINAPI
457 PipeBuf_GetDestCtx(
458     LPRPCCHANNELBUFFER iface,DWORD* pdwDestContext,void** ppvDestContext
459 ) {
460     FIXME("(%p,%p), stub!\n",pdwDestContext,ppvDestContext);
461     return E_FAIL;
462 }
463
464 static HRESULT WINAPI
465 PipeBuf_IsConnected(LPRPCCHANNELBUFFER iface) {
466     FIXME("(), stub!\n");
467     return S_OK;
468 }
469
470 static IRpcChannelBufferVtbl pipebufvt = {
471     PipeBuf_QueryInterface,
472     PipeBuf_AddRef,
473     PipeBuf_Release,
474     PipeBuf_GetBuffer,
475     PipeBuf_SendReceive,
476     PipeBuf_FreeBuffer,
477     PipeBuf_GetDestCtx,
478     PipeBuf_IsConnected
479 };
480
481 HRESULT
482 PIPE_GetNewPipeBuf(wine_marshal_id *mid, IRpcChannelBuffer **pipebuf) {
483   wine_marshal_id       ourid;
484   DWORD                 res;
485   HANDLE                hPipe;
486   HRESULT               hres;
487   PipeBuf               *pbuf;
488
489   hPipe = PIPE_FindByMID(mid);
490   if (hPipe == INVALID_HANDLE_VALUE) {
491       char                      pipefn[200];
492       sprintf(pipefn,OLESTUBMGR"_%08lx",mid->processid);
493       hPipe = CreateFileA(
494               pipefn,
495               GENERIC_READ|GENERIC_WRITE,
496               0,
497               NULL,
498               OPEN_EXISTING,
499               0,
500               0
501       );
502       if (hPipe == INVALID_HANDLE_VALUE) {
503           FIXME("Could not open named pipe %s, le is %lx\n",pipefn,GetLastError());
504           return E_FAIL;
505       }
506       hres = PIPE_RegisterPipe(mid, hPipe, FALSE);
507       if (hres) return hres;
508       memset(&ourid,0,sizeof(ourid));
509       ourid.processid = GetCurrentProcessId();
510       if (!WriteFile(hPipe,&ourid,sizeof(ourid),&res,NULL)||(res!=sizeof(ourid))) {
511           ERR("Failed writing startup mid!\n");
512           return E_FAIL;
513       }
514   }
515   pbuf = (PipeBuf*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(PipeBuf));
516   pbuf->lpVtbl  = &pipebufvt;
517   pbuf->ref     = 1;
518   memcpy(&(pbuf->mid),mid,sizeof(*mid));
519   *pipebuf = (IRpcChannelBuffer*)pbuf;
520   return S_OK;
521 }
522
523 static HRESULT
524 create_server(REFCLSID rclsid) {
525   static const WCHAR embedding[] = { ' ', '-','E','m','b','e','d','d','i','n','g',0 };
526   HKEY          key;
527   char          buf[200];
528   HRESULT       hres = E_UNEXPECTED;
529   char          xclsid[80];
530   WCHAR        exe[MAX_PATH+1];
531   DWORD        exelen = sizeof(exe);
532   WCHAR         command[MAX_PATH+sizeof(embedding)/sizeof(WCHAR)];
533   STARTUPINFOW  sinfo;
534   PROCESS_INFORMATION   pinfo;
535
536   WINE_StringFromCLSID((LPCLSID)rclsid,xclsid);
537
538   sprintf(buf,"CLSID\\%s\\LocalServer32",xclsid);
539   hres = RegOpenKeyExA(HKEY_CLASSES_ROOT, buf, 0, KEY_READ, &key);
540
541   if (hres != ERROR_SUCCESS) {
542       WARN("CLSID %s not registered as LocalServer32\n", xclsid);
543       return REGDB_E_READREGDB; /* Probably */
544   }
545
546   memset(exe,0,sizeof(exe));
547   hres= RegQueryValueExW(key, NULL, NULL, NULL, (LPBYTE)exe, &exelen);
548   RegCloseKey(key);
549   if (hres) {
550       WARN("No default value for LocalServer32 key\n");
551       return REGDB_E_CLASSNOTREG; /* FIXME: check retval */
552   }
553
554   memset(&sinfo,0,sizeof(sinfo));
555   sinfo.cb = sizeof(sinfo);
556
557   /* EXE servers are started with the -Embedding switch. MSDN also claims /Embedding is used,
558      9x does -Embedding, perhaps an 9x/NT difference?  */
559
560   strcpyW(command, exe);
561   strcatW(command, embedding);
562
563   TRACE("activating local server '%s' for %s\n", debugstr_w(command), xclsid);
564
565   if (!CreateProcessW(exe, command, NULL, NULL, FALSE, 0, NULL, NULL, &sinfo, &pinfo)) {
566       WARN("failed to run local server %s\n", debugstr_w(exe));
567       return E_FAIL;
568   }
569
570   return S_OK;
571 }
572 /* http://msdn.microsoft.com/library/en-us/dnmsj99/html/com0199.asp, Figure 4 */
573 HRESULT create_marshalled_proxy(REFCLSID rclsid, REFIID iid, LPVOID *ppv) {
574   HRESULT       hres;
575   HANDLE        hPipe;
576   char          pipefn[200];
577   DWORD         res,bufferlen;
578   char          marshalbuffer[200];
579   IStream       *pStm;
580   LARGE_INTEGER seekto;
581   ULARGE_INTEGER newpos;
582   int           tries = 0;
583 #define MAXTRIES 10000
584
585   TRACE("rclsid=%s, iid=%s\n", debugstr_guid(rclsid), debugstr_guid(iid));
586
587   strcpy(pipefn,PIPEPREF);
588   WINE_StringFromCLSID(rclsid,pipefn+strlen(PIPEPREF));
589
590   while (tries++<MAXTRIES) {
591       WaitNamedPipeA( pipefn, NMPWAIT_WAIT_FOREVER );
592       hPipe     = CreateFileA(
593               pipefn,
594               GENERIC_READ|GENERIC_WRITE,
595               0,
596               NULL,
597               OPEN_EXISTING,
598               0,
599               0
600       );
601       if (hPipe == INVALID_HANDLE_VALUE) {
602           if (tries == 1) {
603               if ((hres = create_server(rclsid)))
604                   return hres;
605               Sleep(1000);
606           } else {
607               WARN("Could not open named pipe to broker %s, le is %lx\n",pipefn,GetLastError());
608               Sleep(1000);
609           }
610           continue;
611       }
612       bufferlen = 0;
613       if (!ReadFile(hPipe,marshalbuffer,sizeof(marshalbuffer),&bufferlen,NULL)) {
614           FIXME("Failed to read marshal id from classfactory of %s.\n",debugstr_guid(rclsid));
615           Sleep(1000);
616           continue;
617       }
618       CloseHandle(hPipe);
619       break;
620   }
621   if (tries>=MAXTRIES)
622       return E_NOINTERFACE;
623   hres = CreateStreamOnHGlobal(0,TRUE,&pStm);
624   if (hres) return hres;
625   hres = IStream_Write(pStm,marshalbuffer,bufferlen,&res);
626   if (hres) goto out;
627   seekto.u.LowPart = 0;seekto.u.HighPart = 0;
628   hres = IStream_Seek(pStm,seekto,SEEK_SET,&newpos);
629   hres = CoUnmarshalInterface(pStm,&IID_IClassFactory,ppv);
630 out:
631   IStream_Release(pStm);
632   return hres;
633 }
634
635
636 static void WINAPI
637 PIPE_StartRequestThread(HANDLE xhPipe) {
638     wine_marshal_id     remoteid;
639     HRESULT             hres;
640
641     hres = read_pipe(xhPipe,&remoteid,sizeof(remoteid));
642     if (hres) {
643         ERR("Failed to read remote mid!\n");
644         return;
645     }
646     PIPE_RegisterPipe(&remoteid,xhPipe, TRUE);
647 }
648
649 static HRESULT
650 COM_RpcReceive(wine_pipe *xpipe) {
651     DWORD       reqtype;
652     HRESULT     hres = S_OK;
653     HANDLE      xhPipe = xpipe->hPipe;
654
655     /*FIXME("%lx %d reading reqtype\n",GetCurrentProcessId(),xhPipe);*/
656     hres = read_pipe(xhPipe,&reqtype,sizeof(reqtype));
657     if (hres) goto end;
658     EnterCriticalSection(&(xpipe->crit));
659     /*FIXME("%lx got reqtype %ld\n",GetCurrentProcessId(),reqtype);*/
660
661     if (reqtype == REQTYPE_DISCONNECT) { /* only received by servers */
662         wine_rpc_disconnect_header header;
663         IRpcStubBuffer *stub;
664         ULONG ret;
665
666         hres = read_pipe(xhPipe, &header, sizeof(header));
667         if (hres) {
668             ERR("could not read disconnect header\n");
669             goto end;
670         }
671
672         TRACE("read disconnect header\n");
673
674         hres = MARSHAL_Find_Stub_Buffer(&header.mid, &stub);
675         if (hres) {
676             ERR("could not locate stub to disconnect, mid.objectid=%p\n", (void*)header.mid.objectid);
677             goto end;
678         }
679
680
681         /* release reference added by MARSHAL_Find_Stub_Buffer call */
682         IRpcStubBuffer_Release(stub);
683         /* release it for real */
684         ret = IRpcStubBuffer_Release(stub);
685         /* FIXME: race */
686         if (ret == 0)
687             MARSHAL_Invalidate_Stub_From_MID(&header.mid);
688         goto end;
689     } else if (reqtype == REQTYPE_REQUEST) {
690         wine_rpc_request        *xreq;
691         RPC_GetRequest(&xreq);
692         xreq->hPipe = xhPipe;
693         hres = read_pipe(xhPipe,&(xreq->reqh),sizeof(xreq->reqh));
694         if (hres) goto end;
695         xreq->resph.reqid = xreq->reqh.reqid;
696         xreq->Buffer = HeapAlloc(GetProcessHeap(),0, xreq->reqh.cbBuffer);
697         hres = read_pipe(xhPipe,xreq->Buffer,xreq->reqh.cbBuffer);
698         if (hres) goto end;
699         xreq->state = REQSTATE_REQ_GOT;
700         goto end;
701     } else if (reqtype == REQTYPE_RESPONSE) {
702         wine_rpc_response_header        resph;
703         int i;
704
705         hres = read_pipe(xhPipe,&resph,sizeof(resph));
706         if (hres) goto end;
707         for (i=nrofreqs;i--;) {
708             wine_rpc_request *xreq = reqs[i];
709             if (xreq->state != REQSTATE_REQ_WAITING_FOR_REPLY)
710                 continue;
711             if (xreq->reqh.reqid == resph.reqid) {
712                 memcpy(&(xreq->resph),&resph,sizeof(resph));
713
714                 if (xreq->Buffer)
715                     xreq->Buffer = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,xreq->Buffer,xreq->resph.cbBuffer);
716                 else
717                     xreq->Buffer = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,xreq->resph.cbBuffer);
718
719                 hres = read_pipe(xhPipe,xreq->Buffer,xreq->resph.cbBuffer);
720                 if (hres) goto end;
721                 xreq->state = REQSTATE_RESP_GOT;
722                 /*PulseEvent(hRpcChanged);*/
723                 goto end;
724             }
725         }
726         ERR("Did not find request for id %lx\n",resph.reqid);
727         hres = S_OK;
728         goto end;
729     }
730     ERR("Unknown reqtype %ld\n",reqtype);
731     hres = E_FAIL;
732 end:
733     LeaveCriticalSection(&(xpipe->crit));
734     return hres;
735 }
736
737 static DWORD WINAPI
738 _StubReaderThread(LPVOID param) {
739     wine_pipe           *xpipe = (wine_pipe*)param;
740     HANDLE              xhPipe = xpipe->hPipe;
741     HRESULT             hres = S_OK;
742
743     TRACE("STUB reader thread %lx\n",GetCurrentProcessId());
744     while (!hres) {
745         int i;
746         hres = COM_RpcReceive(xpipe);
747         if (hres) break;
748
749         for (i=nrofreqs;i--;) {
750             wine_rpc_request *xreq = reqs[i];
751             if ((xreq->state == REQSTATE_REQ_GOT) && (xreq->hPipe == xhPipe)) {
752                 hres = COM_InvokeAndRpcSend(xreq);
753                 if (!hres) break;
754             }
755         }
756     }
757     FIXME("Failed with hres %lx\n",hres);
758     CloseHandle(xhPipe);
759     return 0;
760 }
761
762 static DWORD WINAPI
763 _StubMgrThread(LPVOID param) {
764     char                pipefn[200];
765     HANDLE              listenPipe;
766
767     sprintf(pipefn,OLESTUBMGR"_%08lx",GetCurrentProcessId());
768     TRACE("Stub Manager Thread starting on (%s)\n",pipefn);
769
770     while (1) {
771         listenPipe = CreateNamedPipeA(
772             pipefn,
773             PIPE_ACCESS_DUPLEX,
774             PIPE_TYPE_BYTE|PIPE_WAIT,
775             PIPE_UNLIMITED_INSTANCES,
776             4096,
777             4096,
778             NMPWAIT_USE_DEFAULT_WAIT,
779             NULL
780         );
781         if (listenPipe == INVALID_HANDLE_VALUE) {
782             FIXME("pipe creation failed for %s, le is %lx\n",pipefn,GetLastError());
783             return 1; /* permanent failure, so quit stubmgr thread */
784         }
785         if (!ConnectNamedPipe(listenPipe,NULL)) {
786             ERR("Failure during ConnectNamedPipe %lx!\n",GetLastError());
787             CloseHandle(listenPipe);
788             continue;
789         }
790         PIPE_StartRequestThread(listenPipe);
791     }
792     return 0;
793 }
794
795 void
796 STUBMGR_Start() {
797   static BOOL stubMgrRunning = FALSE;
798   DWORD tid;
799
800   if (!stubMgrRunning) {
801       stubMgrRunning = TRUE;
802       CreateThread(NULL,0,_StubMgrThread,NULL,0,&tid);
803       Sleep(2000); /* actually we just try opening the pipe until it succeeds */
804   }
805 }