Force context switch on chanbuf disconnect to avoid a race in the test
[wine/gsoc_dplay.git] / dlls / ole32 / rpc.c
blobf379d452b752344dbd5f1d57c76876b4e3eea209
1 /*
2 * (Local) RPC Stuff
4 * Copyright 2002 Marcus Meissner
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.
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.
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
21 #include "config.h"
23 #include <stdlib.h>
24 #include <stdarg.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <assert.h>
29 #define COBJMACROS
30 #define NONAMELESSUNION
31 #define NONAMELESSSTRUCT
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"
45 #include "compobj_private.h"
47 #include "wine/debug.h"
49 WINE_DEFAULT_DEBUG_CHANNEL(ole);
51 #define PIPEPREF "\\\\.\\pipe\\"
52 #define OLESTUBMGR PIPEPREF"WINE_OLE_StubMgr"
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;
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;
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;
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
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;
94 static wine_rpc_request **reqs = NULL;
95 static int nrofreqs = 0;
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.
101 typedef struct _wine_pipe {
102 wine_marshal_id mid; /* target mid */
103 DWORD tid; /* thread which owns this pipe */
104 HANDLE hPipe;
106 int pending;
107 HANDLE hThread;
108 CRITICAL_SECTION crit;
110 APARTMENT *apt; /* apartment of the marshalling thread for the stub dispatch case */
111 } wine_pipe;
113 #define MAX_WINE_PIPES 256
115 static wine_pipe pipes[MAX_WINE_PIPES];
116 static int nrofpipes = 0;
118 typedef struct _PipeBuf {
119 IRpcChannelBufferVtbl *lpVtbl;
120 DWORD ref;
122 wine_marshal_id mid;
123 } PipeBuf;
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;
132 if (res!=size) {
133 FIXME("Read only %ld of %ld bytes from %p.\n",res,size,hf);
134 return E_FAIL;
136 return S_OK;
139 static void
140 drs(LPCSTR where) {
141 #if 0
142 static int nrofreaders = 0;
144 int i, states[10];
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]
160 #endif
162 return ;
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;
172 if (res!=size) {
173 FIXME("Wrote only %ld of %ld bytes to %p.\n",res,size,hf);
174 return E_FAIL;
176 return S_OK;
179 static DWORD WINAPI stub_dispatch_thread(LPVOID);
181 static HRESULT
182 PIPE_RegisterPipe(wine_marshal_id *mid, HANDLE hPipe, BOOL startreader) {
183 int i;
184 char pipefn[100];
186 for (i=0;i<nrofpipes;i++)
187 if (pipes[i].mid.oxid==mid->oxid)
188 return S_OK;
189 if (nrofpipes + 1 >= MAX_WINE_PIPES)
191 FIXME("Out of pipes, please increase MAX_WINE_PIPES\n");
192 return E_OUTOFMEMORY;
194 sprintf(pipefn,OLESTUBMGR"_%08lx%08lx",(DWORD)(mid->oxid >> 32),(DWORD)mid->oxid);
195 memcpy(&(pipes[nrofpipes].mid),mid,sizeof(*mid));
196 pipes[nrofpipes].hPipe = hPipe;
197 pipes[nrofpipes].apt = COM_CurrentApt();
198 assert( pipes[nrofpipes].apt );
199 InitializeCriticalSection(&(pipes[nrofpipes].crit));
200 nrofpipes++;
201 if (startreader) {
202 pipes[nrofpipes-1].hThread = CreateThread(NULL,0,stub_dispatch_thread,(LPVOID)(pipes+(nrofpipes-1)),0,&(pipes[nrofpipes-1].tid));
203 } else {
204 pipes[nrofpipes-1].tid = GetCurrentThreadId();
206 return S_OK;
209 static HANDLE
210 PIPE_FindByMID(wine_marshal_id *mid) {
211 int i;
212 for (i=0;i<nrofpipes;i++)
213 if ((pipes[i].mid.oxid==mid->oxid) &&
214 (GetCurrentThreadId()==pipes[i].tid)
216 return pipes[i].hPipe;
217 return INVALID_HANDLE_VALUE;
220 static wine_pipe*
221 PIPE_GetFromMID(wine_marshal_id *mid) {
222 int i;
223 for (i=0;i<nrofpipes;i++) {
224 if ((pipes[i].mid.oxid==mid->oxid) &&
225 (GetCurrentThreadId()==pipes[i].tid)
227 return pipes+i;
229 return NULL;
232 static HRESULT
233 RPC_GetRequest(wine_rpc_request **req) {
234 static int reqid = 0xdeadbeef;
235 int i;
237 for (i=0;i<nrofreqs;i++) { /* try to reuse */
238 if (reqs[i]->state == REQSTATE_DONE) {
239 reqs[i]->reqh.reqid = reqid++;
240 reqs[i]->resph.reqid = reqs[i]->reqh.reqid;
241 reqs[i]->hPipe = INVALID_HANDLE_VALUE;
242 *req = reqs[i];
243 reqs[i]->state = REQSTATE_START;
244 return S_OK;
247 /* create new */
248 if (reqs)
249 reqs = (wine_rpc_request**)HeapReAlloc(
250 GetProcessHeap(),
251 HEAP_ZERO_MEMORY,
252 reqs,
253 sizeof(wine_rpc_request*)*(nrofreqs+1)
255 else
256 reqs = (wine_rpc_request**)HeapAlloc(
257 GetProcessHeap(),
258 HEAP_ZERO_MEMORY,
259 sizeof(wine_rpc_request*)
261 if (!reqs)
262 return E_OUTOFMEMORY;
263 reqs[nrofreqs] = (wine_rpc_request*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(wine_rpc_request));
264 reqs[nrofreqs]->reqh.reqid = reqid++;
265 reqs[nrofreqs]->resph.reqid = reqs[nrofreqs]->reqh.reqid;
266 reqs[nrofreqs]->hPipe = INVALID_HANDLE_VALUE;
267 *req = reqs[nrofreqs];
268 reqs[nrofreqs]->state = REQSTATE_START;
269 nrofreqs++;
270 return S_OK;
273 static void
274 RPC_FreeRequest(wine_rpc_request *req) {
275 req->state = REQSTATE_DONE; /* Just reuse slot. */
276 return;
279 static HRESULT WINAPI
280 PipeBuf_QueryInterface(
281 LPRPCCHANNELBUFFER iface,REFIID riid,LPVOID *ppv
283 *ppv = NULL;
284 if (IsEqualIID(riid,&IID_IRpcChannelBuffer) || IsEqualIID(riid,&IID_IUnknown)) {
285 *ppv = (LPVOID)iface;
286 IUnknown_AddRef(iface);
287 return S_OK;
289 return E_NOINTERFACE;
292 static ULONG WINAPI
293 PipeBuf_AddRef(LPRPCCHANNELBUFFER iface) {
294 PipeBuf *This = (PipeBuf *)iface;
295 return InterlockedIncrement(&This->ref);
298 static ULONG WINAPI
299 PipeBuf_Release(LPRPCCHANNELBUFFER iface) {
300 PipeBuf *This = (PipeBuf *)iface;
301 ULONG ref;
302 wine_rpc_disconnect_header header;
303 HANDLE pipe;
304 DWORD reqtype = REQTYPE_DISCONNECT;
305 DWORD magic;
307 ref = InterlockedDecrement(&This->ref);
308 if (ref)
309 return ref;
311 memcpy(&header.mid, &This->mid, sizeof(wine_marshal_id));
313 pipe = PIPE_FindByMID(&This->mid);
315 write_pipe(pipe, &reqtype, sizeof(reqtype));
316 write_pipe(pipe, &header, sizeof(wine_rpc_disconnect_header));
318 TRACE("written disconnect packet\n");
320 /* prevent a disconnect race with the other side: this isn't
321 * necessary for real dcom but the test suite needs it */
323 read_pipe(pipe, &magic, sizeof(magic));
324 if (magic != 0xcafebabe) ERR("bad disconnection magic: expecting 0xcafebabe but got 0x%lx", magic);
326 HeapFree(GetProcessHeap(),0,This);
327 return 0;
330 static HRESULT WINAPI
331 PipeBuf_GetBuffer(
332 LPRPCCHANNELBUFFER iface,RPCOLEMESSAGE* msg,REFIID riid
334 /*PipeBuf *This = (PipeBuf *)iface;*/
336 TRACE("(%p,%s)\n",msg,debugstr_guid(riid));
337 /* probably reuses IID in real. */
338 if (msg->cbBuffer && (msg->Buffer == NULL))
339 msg->Buffer = HeapAlloc(GetProcessHeap(),0,msg->cbBuffer);
340 return S_OK;
343 static HRESULT
344 COM_InvokeAndRpcSend(wine_rpc_request *req) {
345 IRpcStubBuffer *stub;
346 RPCOLEMESSAGE msg;
347 HRESULT hres;
348 DWORD reqtype;
350 if (!(stub = mid_to_stubbuffer(&(req->reqh.mid))))
352 ERR("Stub not found?\n");
353 return E_FAIL;
356 IUnknown_AddRef(stub);
357 msg.Buffer = req->Buffer;
358 msg.iMethod = req->reqh.iMethod;
359 msg.cbBuffer = req->reqh.cbBuffer;
360 msg.dataRepresentation = NDR_LOCAL_DATA_REPRESENTATION;
361 req->state = REQSTATE_INVOKING;
362 req->resph.retval = IRpcStubBuffer_Invoke(stub,&msg,NULL);
363 IUnknown_Release(stub);
364 req->Buffer = msg.Buffer;
365 req->resph.cbBuffer = msg.cbBuffer;
366 reqtype = REQTYPE_RESPONSE;
367 hres = write_pipe(req->hPipe,&reqtype,sizeof(reqtype));
368 if (hres) return hres;
369 hres = write_pipe(req->hPipe,&(req->resph),sizeof(req->resph));
370 if (hres) return hres;
371 hres = write_pipe(req->hPipe,req->Buffer,req->resph.cbBuffer);
372 if (hres) return hres;
373 req->state = REQSTATE_DONE;
374 drs("invoke");
375 return S_OK;
378 static HRESULT COM_RpcReceive(wine_pipe *xpipe);
380 static HRESULT
381 RPC_QueueRequestAndWait(wine_rpc_request *req) {
382 int i;
383 wine_rpc_request *xreq;
384 HRESULT hres;
385 DWORD reqtype;
386 wine_pipe *xpipe = PIPE_GetFromMID(&(req->reqh.mid));
388 if (!xpipe) {
389 FIXME("no pipe found.\n");
390 return E_POINTER;
392 req->hPipe = xpipe->hPipe;
393 req->state = REQSTATE_REQ_WAITING_FOR_REPLY;
394 reqtype = REQTYPE_REQUEST;
395 hres = write_pipe(req->hPipe,&reqtype,sizeof(reqtype));
396 if (hres) return hres;
397 hres = write_pipe(req->hPipe,&(req->reqh),sizeof(req->reqh));
398 if (hres) return hres;
399 hres = write_pipe(req->hPipe,req->Buffer,req->reqh.cbBuffer);
400 if (hres) return hres;
402 /* This loop is about allowing re-entrancy. While waiting for the
403 * response to one RPC we may receive a request starting another. */
404 while (!hres) {
405 hres = COM_RpcReceive(xpipe);
406 if (hres) break;
408 for (i=0;i<nrofreqs;i++) {
409 xreq = reqs[i];
410 if ((xreq->state==REQSTATE_REQ_GOT) && (xreq->hPipe==req->hPipe)) {
411 hres = COM_InvokeAndRpcSend(xreq);
412 if (hres) break;
415 if (req->state == REQSTATE_RESP_GOT)
416 return S_OK;
418 if (FAILED(hres))
419 WARN("-- 0x%08lx\n", hres);
420 return hres;
423 static HRESULT WINAPI
424 PipeBuf_SendReceive(
425 LPRPCCHANNELBUFFER iface,RPCOLEMESSAGE* msg,ULONG *status
427 PipeBuf *This = (PipeBuf *)iface;
428 wine_rpc_request *req;
429 HRESULT hres;
431 TRACE("()\n");
433 if (This->mid.oxid == COM_CurrentApt()->oxid) {
434 ERR("Need to call directly!\n");
435 return E_FAIL;
438 hres = RPC_GetRequest(&req);
439 if (hres) return hres;
440 req->reqh.iMethod = msg->iMethod;
441 req->reqh.cbBuffer = msg->cbBuffer;
442 memcpy(&(req->reqh.mid),&(This->mid),sizeof(This->mid));
443 req->Buffer = msg->Buffer;
444 hres = RPC_QueueRequestAndWait(req);
445 if (hres) {
446 RPC_FreeRequest(req);
447 return hres;
449 msg->cbBuffer = req->resph.cbBuffer;
450 msg->Buffer = req->Buffer;
451 *status = req->resph.retval;
452 RPC_FreeRequest(req);
453 return S_OK;
457 static HRESULT WINAPI
458 PipeBuf_FreeBuffer(LPRPCCHANNELBUFFER iface,RPCOLEMESSAGE* msg) {
459 FIXME("(%p), stub!\n",msg);
460 return E_FAIL;
463 static HRESULT WINAPI
464 PipeBuf_GetDestCtx(
465 LPRPCCHANNELBUFFER iface,DWORD* pdwDestContext,void** ppvDestContext
467 FIXME("(%p,%p), stub!\n",pdwDestContext,ppvDestContext);
468 return E_FAIL;
471 static HRESULT WINAPI
472 PipeBuf_IsConnected(LPRPCCHANNELBUFFER iface) {
473 FIXME("(), stub!\n");
474 return S_OK;
477 static IRpcChannelBufferVtbl pipebufvt = {
478 PipeBuf_QueryInterface,
479 PipeBuf_AddRef,
480 PipeBuf_Release,
481 PipeBuf_GetBuffer,
482 PipeBuf_SendReceive,
483 PipeBuf_FreeBuffer,
484 PipeBuf_GetDestCtx,
485 PipeBuf_IsConnected
488 HRESULT
489 PIPE_GetNewPipeBuf(wine_marshal_id *mid, IRpcChannelBuffer **pipebuf) {
490 wine_marshal_id ourid;
491 DWORD res;
492 HANDLE hPipe;
493 HRESULT hres;
494 PipeBuf *pbuf;
496 hPipe = PIPE_FindByMID(mid);
497 if (hPipe == INVALID_HANDLE_VALUE) {
498 char pipefn[200];
499 sprintf(pipefn,OLESTUBMGR"_%08lx%08lx",(DWORD)(mid->oxid >> 32),(DWORD)mid->oxid);
500 hPipe = CreateFileA(
501 pipefn,
502 GENERIC_READ|GENERIC_WRITE,
504 NULL,
505 OPEN_EXISTING,
509 if (hPipe == INVALID_HANDLE_VALUE) {
510 FIXME("Could not open named pipe %s, le is %lx\n",pipefn,GetLastError());
511 return E_FAIL;
513 hres = PIPE_RegisterPipe(mid, hPipe, FALSE);
514 if (hres) return hres;
515 memset(&ourid,0,sizeof(ourid));
516 ourid.oxid = COM_CurrentApt()->oxid;
517 if (!WriteFile(hPipe,&ourid,sizeof(ourid),&res,NULL)||(res!=sizeof(ourid))) {
518 ERR("Failed writing startup mid!\n");
519 return E_FAIL;
522 pbuf = (PipeBuf*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(PipeBuf));
523 pbuf->lpVtbl = &pipebufvt;
524 pbuf->ref = 1;
525 memcpy(&(pbuf->mid),mid,sizeof(*mid));
526 *pipebuf = (IRpcChannelBuffer*)pbuf;
527 return S_OK;
530 static HRESULT
531 create_server(REFCLSID rclsid) {
532 static const WCHAR embedding[] = { ' ', '-','E','m','b','e','d','d','i','n','g',0 };
533 HKEY key;
534 char buf[200];
535 HRESULT hres = E_UNEXPECTED;
536 char xclsid[80];
537 WCHAR exe[MAX_PATH+1];
538 DWORD exelen = sizeof(exe);
539 WCHAR command[MAX_PATH+sizeof(embedding)/sizeof(WCHAR)];
540 STARTUPINFOW sinfo;
541 PROCESS_INFORMATION pinfo;
543 WINE_StringFromCLSID((LPCLSID)rclsid,xclsid);
545 sprintf(buf,"CLSID\\%s\\LocalServer32",xclsid);
546 hres = RegOpenKeyExA(HKEY_CLASSES_ROOT, buf, 0, KEY_READ, &key);
548 if (hres != ERROR_SUCCESS) {
549 WARN("CLSID %s not registered as LocalServer32\n", xclsid);
550 return REGDB_E_READREGDB; /* Probably */
553 memset(exe,0,sizeof(exe));
554 hres= RegQueryValueExW(key, NULL, NULL, NULL, (LPBYTE)exe, &exelen);
555 RegCloseKey(key);
556 if (hres) {
557 WARN("No default value for LocalServer32 key\n");
558 return REGDB_E_CLASSNOTREG; /* FIXME: check retval */
561 memset(&sinfo,0,sizeof(sinfo));
562 sinfo.cb = sizeof(sinfo);
564 /* EXE servers are started with the -Embedding switch. MSDN also claims /Embedding is used,
565 9x does -Embedding, perhaps an 9x/NT difference? */
567 strcpyW(command, exe);
568 strcatW(command, embedding);
570 TRACE("activating local server '%s' for %s\n", debugstr_w(command), xclsid);
572 if (!CreateProcessW(exe, command, NULL, NULL, FALSE, 0, NULL, NULL, &sinfo, &pinfo)) {
573 WARN("failed to run local server %s\n", debugstr_w(exe));
574 return E_FAIL;
577 return S_OK;
581 * start_local_service() - start a service given its name and parameters
583 static DWORD
584 start_local_service(LPCWSTR name, DWORD num, LPWSTR *params)
586 SC_HANDLE handle, hsvc;
587 DWORD r = ERROR_FUNCTION_FAILED;
589 TRACE("Starting service %s %ld params\n", debugstr_w(name), num);
591 handle = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
592 if (!handle)
593 return r;
594 hsvc = OpenServiceW(handle, name, SC_MANAGER_ALL_ACCESS);
595 if (hsvc)
597 if(StartServiceW(hsvc, num, (LPCWSTR*)params))
598 r = ERROR_SUCCESS;
599 else
600 r = GetLastError();
601 if (r==ERROR_SERVICE_ALREADY_RUNNING)
602 r = ERROR_SUCCESS;
603 CloseServiceHandle(hsvc);
605 CloseServiceHandle(handle);
607 TRACE("StartService returned error %ld (%s)\n", r, r?"ok":"failed");
609 return r;
613 * create_local_service() - start a COM server in a service
615 * To start a Local Service, we read the AppID value under
616 * the class's CLSID key, then open the HKCR\\AppId key specified
617 * there and check for a LocalService value.
619 * Note: Local Services are not supported under Windows 9x
621 static HRESULT
622 create_local_service(REFCLSID rclsid)
624 HRESULT hres = REGDB_E_READREGDB;
625 WCHAR buf[40], keyname[50];
626 static const WCHAR szClsId[] = { 'C','L','S','I','D','\\',0 };
627 static const WCHAR szAppId[] = { 'A','p','p','I','d',0 };
628 static const WCHAR szAppIdKey[] = { 'A','p','p','I','d','\\',0 };
629 static const WCHAR szLocalService[] = {
630 'L','o','c','a','l','S','e','r','v','i','c','e',0 };
631 static const WCHAR szServiceParams[] = {
632 'S','e','r','v','i','c','e','P','a','r','a','m','s',0};
633 HKEY hkey;
634 LONG r;
635 DWORD type, sz;
637 TRACE("Attempting to start Local service for %s\n", debugstr_guid(rclsid));
639 /* read the AppID value under the class's key */
640 strcpyW(keyname,szClsId);
641 StringFromGUID2(rclsid,&keyname[6],39);
642 r = RegOpenKeyExW(HKEY_CLASSES_ROOT, keyname, 0, KEY_READ, &hkey);
643 if (r!=ERROR_SUCCESS)
644 return hres;
645 sz = sizeof buf;
646 r = RegQueryValueExW(hkey, szAppId, NULL, &type, (LPBYTE)buf, &sz);
647 RegCloseKey(hkey);
648 if (r!=ERROR_SUCCESS || type!=REG_SZ)
649 return hres;
651 /* read the LocalService and ServiceParameters values from the AppID key */
652 strcpyW(keyname, szAppIdKey);
653 strcatW(keyname, buf);
654 r = RegOpenKeyExW(HKEY_CLASSES_ROOT, keyname, 0, KEY_READ, &hkey);
655 if (r!=ERROR_SUCCESS)
656 return hres;
657 sz = sizeof buf;
658 r = RegQueryValueExW(hkey, szLocalService, NULL, &type, (LPBYTE)buf, &sz);
659 if (r==ERROR_SUCCESS && type==REG_SZ)
661 DWORD num_args = 0;
662 LPWSTR args[1] = { NULL };
665 * FIXME: I'm not really sure how to deal with the service parameters.
666 * I suspect that the string returned from RegQueryValueExW
667 * should be split into a number of arguments by spaces.
668 * It would make more sense if ServiceParams contained a
669 * REG_MULTI_SZ here, but it's a REG_SZ for the services
670 * that I'm interested in for the moment.
672 r = RegQueryValueExW(hkey, szServiceParams, NULL, &type, NULL, &sz);
673 if (r == ERROR_SUCCESS && type == REG_SZ && sz)
675 args[0] = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sz);
676 num_args++;
677 RegQueryValueExW(hkey, szServiceParams, NULL, &type, (LPBYTE)args[0], &sz);
679 r = start_local_service(buf, num_args, args);
680 if (r==ERROR_SUCCESS)
681 hres = S_OK;
682 HeapFree(GetProcessHeap(),0,args[0]);
684 RegCloseKey(hkey);
686 return hres;
689 /* http://msdn.microsoft.com/library/en-us/dnmsj99/html/com0199.asp, Figure 4 */
690 HRESULT create_marshalled_proxy(REFCLSID rclsid, REFIID iid, LPVOID *ppv) {
691 HRESULT hres;
692 HANDLE hPipe;
693 char pipefn[200];
694 DWORD res,bufferlen;
695 char marshalbuffer[200];
696 IStream *pStm;
697 LARGE_INTEGER seekto;
698 ULARGE_INTEGER newpos;
699 int tries = 0;
700 #define MAXTRIES 10000
702 TRACE("rclsid=%s, iid=%s\n", debugstr_guid(rclsid), debugstr_guid(iid));
704 strcpy(pipefn,PIPEPREF);
705 WINE_StringFromCLSID(rclsid,pipefn+strlen(PIPEPREF));
707 while (tries++<MAXTRIES) {
708 TRACE("waiting for %s\n", pipefn);
710 WaitNamedPipeA( pipefn, NMPWAIT_WAIT_FOREVER );
711 hPipe = CreateFileA(
712 pipefn,
713 GENERIC_READ|GENERIC_WRITE,
715 NULL,
716 OPEN_EXISTING,
720 if (hPipe == INVALID_HANDLE_VALUE) {
721 if (tries == 1) {
722 if ( (hres = create_server(rclsid)) &&
723 (hres = create_local_service(rclsid)) )
724 return hres;
725 Sleep(1000);
726 } else {
727 WARN("Could not open named pipe to broker %s, le is %lx\n",pipefn,GetLastError());
728 Sleep(1000);
730 continue;
732 bufferlen = 0;
733 if (!ReadFile(hPipe,marshalbuffer,sizeof(marshalbuffer),&bufferlen,NULL)) {
734 FIXME("Failed to read marshal id from classfactory of %s.\n",debugstr_guid(rclsid));
735 Sleep(1000);
736 continue;
738 TRACE("read marshal id from pipe\n");
739 CloseHandle(hPipe);
740 break;
742 if (tries>=MAXTRIES)
743 return E_NOINTERFACE;
744 hres = CreateStreamOnHGlobal(0,TRUE,&pStm);
745 if (hres) return hres;
746 hres = IStream_Write(pStm,marshalbuffer,bufferlen,&res);
747 if (hres) goto out;
748 seekto.u.LowPart = 0;seekto.u.HighPart = 0;
749 hres = IStream_Seek(pStm,seekto,SEEK_SET,&newpos);
750 TRACE("unmarshalling classfactory\n");
751 hres = CoUnmarshalInterface(pStm,&IID_IClassFactory,ppv);
752 out:
753 IStream_Release(pStm);
754 return hres;
758 static void WINAPI
759 PIPE_StartRequestThread(HANDLE xhPipe) {
760 wine_marshal_id remoteid;
761 HRESULT hres;
763 hres = read_pipe(xhPipe,&remoteid,sizeof(remoteid));
764 if (hres) {
765 ERR("Failed to read remote mid!\n");
766 return;
768 PIPE_RegisterPipe(&remoteid,xhPipe, TRUE);
771 static HRESULT
772 COM_RpcReceive(wine_pipe *xpipe) {
773 DWORD reqtype;
774 HRESULT hres = S_OK;
775 HANDLE xhPipe = xpipe->hPipe;
777 /*FIXME("%lx %d reading reqtype\n",GetCurrentProcessId(),xhPipe);*/
778 hres = read_pipe(xhPipe,&reqtype,sizeof(reqtype));
779 if (hres) goto end;
780 EnterCriticalSection(&(xpipe->crit));
781 /*FIXME("%lx got reqtype %ld\n",GetCurrentProcessId(),reqtype);*/
783 if (reqtype == REQTYPE_DISCONNECT) { /* only received by servers */
784 wine_rpc_disconnect_header header;
785 struct stub_manager *stubmgr;
786 DWORD magic = 0xcafebabe;
788 hres = read_pipe(xhPipe, &header, sizeof(header));
789 if (hres) {
790 ERR("could not read disconnect header\n");
791 goto end;
794 TRACE("read disconnect header\n");
796 if (!(stubmgr = get_stub_manager(header.mid.oxid, header.mid.oid)))
798 ERR("could not locate stub to disconnect, mid.oid=%s\n", wine_dbgstr_longlong(header.mid.oid));
799 goto end;
802 stub_manager_ext_release(stubmgr, 1);
804 stub_manager_int_release(stubmgr);
806 write_pipe(xhPipe, &magic, sizeof(magic));
808 goto end;
809 } else if (reqtype == REQTYPE_REQUEST) {
810 wine_rpc_request *xreq;
811 RPC_GetRequest(&xreq);
812 xreq->hPipe = xhPipe;
813 hres = read_pipe(xhPipe,&(xreq->reqh),sizeof(xreq->reqh));
814 if (hres) goto end;
815 xreq->resph.reqid = xreq->reqh.reqid;
816 xreq->Buffer = HeapAlloc(GetProcessHeap(),0, xreq->reqh.cbBuffer);
817 hres = read_pipe(xhPipe,xreq->Buffer,xreq->reqh.cbBuffer);
818 if (hres) goto end;
819 xreq->state = REQSTATE_REQ_GOT;
820 goto end;
821 } else if (reqtype == REQTYPE_RESPONSE) {
822 wine_rpc_response_header resph;
823 int i;
825 hres = read_pipe(xhPipe,&resph,sizeof(resph));
826 if (hres) goto end;
827 for (i=nrofreqs;i--;) {
828 wine_rpc_request *xreq = reqs[i];
829 if (xreq->state != REQSTATE_REQ_WAITING_FOR_REPLY)
830 continue;
831 if (xreq->reqh.reqid == resph.reqid) {
832 memcpy(&(xreq->resph),&resph,sizeof(resph));
834 if (xreq->Buffer)
835 xreq->Buffer = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,xreq->Buffer,xreq->resph.cbBuffer);
836 else
837 xreq->Buffer = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,xreq->resph.cbBuffer);
839 hres = read_pipe(xhPipe,xreq->Buffer,xreq->resph.cbBuffer);
840 if (hres) goto end;
841 xreq->state = REQSTATE_RESP_GOT;
842 /*PulseEvent(hRpcChanged);*/
843 goto end;
846 ERR("Did not find request for id %lx\n",resph.reqid);
847 hres = S_OK;
848 goto end;
850 ERR("Unknown reqtype %ld\n",reqtype);
851 hres = E_FAIL;
852 end:
853 LeaveCriticalSection(&(xpipe->crit));
854 return hres;
857 /* This thread listens on the given pipe for requests to a particular stub manager */
858 static DWORD WINAPI stub_dispatch_thread(LPVOID param)
860 wine_pipe *xpipe = (wine_pipe*)param;
861 HANDLE xhPipe = xpipe->hPipe;
862 HRESULT hres = S_OK;
864 TRACE("starting for apartment OXID %08lx%08lx\n", (DWORD)(xpipe->mid.oxid >> 32), (DWORD)(xpipe->mid.oxid));
866 /* join marshalling apartment. fixme: this stuff is all very wrong, threading needs to work like native */
867 COM_CurrentInfo()->apt = xpipe->apt;
869 while (!hres) {
870 int i;
872 hres = COM_RpcReceive(xpipe);
873 if (hres) break;
875 for (i=nrofreqs;i--;) {
876 wine_rpc_request *xreq = reqs[i];
877 if ((xreq->state == REQSTATE_REQ_GOT) && (xreq->hPipe == xhPipe)) {
878 hres = COM_InvokeAndRpcSend(xreq);
879 if (!hres) break;
884 /* fixme: this thread never quits naturally */
885 WARN("exiting with hres %lx\n",hres);
886 CloseHandle(xhPipe);
887 return 0;
890 struct apartment_listener_params
892 APARTMENT *apt;
893 HANDLE event;
896 /* This thread listens on a named pipe for each apartment that exports
897 * objects. It deals with incoming connection requests. Each time a
898 * client connects a separate thread is spawned for that particular
899 * connection.
901 * This architecture is different in native DCOM.
903 static DWORD WINAPI apartment_listener_thread(LPVOID p)
905 char pipefn[200];
906 HANDLE listenPipe;
907 struct apartment_listener_params * params = (struct apartment_listener_params *)p;
908 APARTMENT *apt = params->apt;
909 HANDLE event = params->event;
911 HeapFree(GetProcessHeap(), 0, params);
913 /* we must join the marshalling threads apartment. we already have a ref here */
914 COM_CurrentInfo()->apt = apt;
916 sprintf(pipefn,OLESTUBMGR"_%08lx%08lx", (DWORD)(apt->oxid >> 32), (DWORD)(apt->oxid));
917 TRACE("Apartment listener thread starting on (%s)\n",pipefn);
919 while (1) {
920 listenPipe = CreateNamedPipeA(
921 pipefn,
922 PIPE_ACCESS_DUPLEX,
923 PIPE_TYPE_BYTE|PIPE_WAIT,
924 PIPE_UNLIMITED_INSTANCES,
925 4096,
926 4096,
927 NMPWAIT_USE_DEFAULT_WAIT,
928 NULL
931 /* tell function that started this thread that we have attempted to created the
932 * named pipe. */
933 if (event) {
934 SetEvent(event);
935 event = NULL;
938 if (listenPipe == INVALID_HANDLE_VALUE) {
939 FIXME("pipe creation failed for %s, error %ld\n",pipefn,GetLastError());
940 return 1; /* permanent failure, so quit stubmgr thread */
943 /* an already connected pipe is not an error */
944 if (!ConnectNamedPipe(listenPipe,NULL) &&
945 (GetLastError() != ERROR_PIPE_CONNECTED)) {
946 ERR("Failure during ConnectNamedPipe %ld!\n",GetLastError());
947 CloseHandle(listenPipe);
948 continue;
951 PIPE_StartRequestThread(listenPipe);
953 return 0;
956 void start_apartment_listener_thread()
958 APARTMENT *apt = COM_CurrentApt();
960 assert( apt );
962 TRACE("apt->listenertid=%ld\n", apt->listenertid);
964 /* apt->listenertid is a hack which needs to die at some point, as
965 * it leaks information into the apartment structure. in fact,
966 * this thread isn't quite correct anyway as native RPC doesn't
967 * use a thread per apartment at all, instead the dispatch thread
968 * either enters the apartment to perform the RPC (for MTAs, RTAs)
969 * or does a context switch into it for STAs.
972 if (!apt->listenertid)
974 HANDLE thread;
975 HANDLE event = CreateEventW(NULL, TRUE, FALSE, NULL);
976 struct apartment_listener_params * params = HeapAlloc(GetProcessHeap(), 0, sizeof(*params));
978 params->apt = apt;
979 params->event = event;
980 thread = CreateThread(NULL, 0, apartment_listener_thread, params, 0, &apt->listenertid);
981 CloseHandle(thread);
982 /* wait for pipe to be created before returning, otherwise we
983 * might try to use it and fail */
984 WaitForSingleObject(event, INFINITE);
985 CloseHandle(event);
989 struct local_server_params
991 CLSID clsid;
992 IStream *stream;
995 static DWORD WINAPI local_server_thread(LPVOID param)
997 struct local_server_params * lsp = (struct local_server_params *)param;
998 HANDLE hPipe;
999 char pipefn[200];
1000 HRESULT hres;
1001 IStream *pStm = lsp->stream;
1002 STATSTG ststg;
1003 unsigned char *buffer;
1004 int buflen;
1005 LARGE_INTEGER seekto;
1006 ULARGE_INTEGER newpos;
1007 ULONG res;
1009 TRACE("Starting threader for %s.\n",debugstr_guid(&lsp->clsid));
1011 strcpy(pipefn,PIPEPREF);
1012 WINE_StringFromCLSID(&lsp->clsid,pipefn+strlen(PIPEPREF));
1014 HeapFree(GetProcessHeap(), 0, lsp);
1016 hPipe = CreateNamedPipeA( pipefn, PIPE_ACCESS_DUPLEX,
1017 PIPE_TYPE_BYTE|PIPE_WAIT, PIPE_UNLIMITED_INSTANCES,
1018 4096, 4096, NMPWAIT_USE_DEFAULT_WAIT, NULL );
1019 if (hPipe == INVALID_HANDLE_VALUE) {
1020 FIXME("pipe creation failed for %s, le is %ld\n",pipefn,GetLastError());
1021 return 1;
1023 while (1) {
1024 if (!ConnectNamedPipe(hPipe,NULL)) {
1025 ERR("Failure during ConnectNamedPipe %ld, ABORT!\n",GetLastError());
1026 break;
1029 TRACE("marshalling IClassFactory to client\n");
1031 hres = IStream_Stat(pStm,&ststg,0);
1032 if (hres) return hres;
1034 buflen = ststg.cbSize.u.LowPart;
1035 buffer = HeapAlloc(GetProcessHeap(),0,buflen);
1036 seekto.u.LowPart = 0;
1037 seekto.u.HighPart = 0;
1038 hres = IStream_Seek(pStm,seekto,SEEK_SET,&newpos);
1039 if (hres) {
1040 FIXME("IStream_Seek failed, %lx\n",hres);
1041 return hres;
1044 hres = IStream_Read(pStm,buffer,buflen,&res);
1045 if (hres) {
1046 FIXME("Stream Read failed, %lx\n",hres);
1047 return hres;
1050 IStream_Release(pStm);
1052 WriteFile(hPipe,buffer,buflen,&res,NULL);
1053 FlushFileBuffers(hPipe);
1054 DisconnectNamedPipe(hPipe);
1056 TRACE("done marshalling IClassFactory\n");
1058 CloseHandle(hPipe);
1059 return 0;
1062 void RPC_StartLocalServer(REFCLSID clsid, IStream *stream)
1064 DWORD tid;
1065 HANDLE thread;
1066 struct local_server_params *lsp = HeapAlloc(GetProcessHeap(), 0, sizeof(*lsp));
1068 lsp->clsid = *clsid;
1069 lsp->stream = stream;
1071 thread = CreateThread(NULL, 0, local_server_thread, lsp, 0, &tid);
1072 CloseHandle(thread);
1073 /* FIXME: failure handling */