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
30 #define NONAMELESSUNION
31 #define NONAMELESSSTRUCT
44 #include "wine/unicode.h"
45 #include "wine/winbase16.h"
46 #include "compobj_private.h"
49 #include "compobj_private.h"
51 #include "wine/debug.h"
53 WINE_DEFAULT_DEBUG_CHANNEL(ole);
55 #define REQTYPE_REQUEST 0
56 typedef struct _wine_rpc_request_header {
61 } wine_rpc_request_header;
63 #define REQTYPE_RESPONSE 1
64 typedef struct _wine_rpc_response_header {
68 } wine_rpc_response_header;
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 {
74 wine_marshal_id mid; /* mid of stub to delete */
75 } wine_rpc_disconnect_header;
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
87 typedef struct _wine_rpc_request {
89 HANDLE hPipe; /* temp copy of handle */
90 wine_rpc_request_header reqh;
91 wine_rpc_response_header resph;
95 static wine_rpc_request **reqs = NULL;
96 static int nrofreqs = 0;
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 */
107 CRITICAL_SECTION crit;
110 static wine_pipe *pipes = NULL;
111 static int nrofpipes = 0;
113 typedef struct _PipeBuf {
114 IRpcChannelBufferVtbl *lpVtbl;
121 static HRESULT WINAPI
122 read_pipe(HANDLE hf, LPVOID ptr, DWORD size) {
124 if (!ReadFile(hf,ptr,size,&res,NULL)) {
125 FIXME("Failed to read from %p, le is %lx\n",hf,GetLastError());
129 FIXME("Read only %ld of %ld bytes from %p.\n",res,size,hf);
138 static int nrofreaders = 0;
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(),
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]
161 static HRESULT WINAPI
162 write_pipe(HANDLE hf, LPVOID ptr, DWORD size) {
164 if (!WriteFile(hf,ptr,size,&res,NULL)) {
165 FIXME("Failed to write to %p, le is %lx\n",hf,GetLastError());
169 FIXME("Wrote only %ld of %ld bytes to %p.\n",res,size,hf);
175 static DWORD WINAPI _StubReaderThread(LPVOID);
178 PIPE_RegisterPipe(wine_marshal_id *mid, HANDLE hPipe, BOOL startreader) {
181 wine_pipe *new_pipes;
183 for (i=0;i<nrofpipes;i++)
184 if (pipes[i].mid.oxid==mid->oxid)
187 new_pipes=(wine_pipe*)HeapReAlloc(GetProcessHeap(),0,pipes,sizeof(pipes[0])*(nrofpipes+1));
189 new_pipes=(wine_pipe*)HeapAlloc(GetProcessHeap(),0,sizeof(pipes[0]));
190 if (!new_pipes) return E_OUTOFMEMORY;
192 sprintf(pipefn,OLESTUBMGR"_%08lx%08lx",(DWORD)(mid->oxid >> 32),(DWORD)mid->oxid);
193 memcpy(&(pipes[nrofpipes].mid),mid,sizeof(*mid));
194 pipes[nrofpipes].hPipe = hPipe;
195 InitializeCriticalSection(&(pipes[nrofpipes].crit));
198 pipes[nrofpipes-1].hThread = CreateThread(NULL,0,_StubReaderThread,(LPVOID)(pipes+(nrofpipes-1)),0,&(pipes[nrofpipes-1].tid));
200 pipes[nrofpipes-1].tid = GetCurrentThreadId();
206 PIPE_FindByMID(wine_marshal_id *mid) {
208 for (i=0;i<nrofpipes;i++)
209 if ((pipes[i].mid.oxid==mid->oxid) &&
210 (GetCurrentThreadId()==pipes[i].tid)
212 return pipes[i].hPipe;
213 return INVALID_HANDLE_VALUE;
217 PIPE_GetFromMID(wine_marshal_id *mid) {
219 for (i=0;i<nrofpipes;i++) {
220 if ((pipes[i].mid.oxid==mid->oxid) &&
221 (GetCurrentThreadId()==pipes[i].tid)
229 RPC_GetRequest(wine_rpc_request **req) {
230 static int reqid = 0xdeadbeef;
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;
239 reqs[i]->state = REQSTATE_START;
245 reqs = (wine_rpc_request**)HeapReAlloc(
249 sizeof(wine_rpc_request*)*(nrofreqs+1)
252 reqs = (wine_rpc_request**)HeapAlloc(
255 sizeof(wine_rpc_request*)
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;
270 RPC_FreeRequest(wine_rpc_request *req) {
271 req->state = REQSTATE_DONE; /* Just reuse slot. */
275 static HRESULT WINAPI
276 PipeBuf_QueryInterface(
277 LPRPCCHANNELBUFFER iface,REFIID riid,LPVOID *ppv
280 if (IsEqualIID(riid,&IID_IRpcChannelBuffer) || IsEqualIID(riid,&IID_IUnknown)) {
281 *ppv = (LPVOID)iface;
282 IUnknown_AddRef(iface);
285 return E_NOINTERFACE;
289 PipeBuf_AddRef(LPRPCCHANNELBUFFER iface) {
290 PipeBuf *This = (PipeBuf *)iface;
291 return InterlockedIncrement(&This->ref);
295 PipeBuf_Release(LPRPCCHANNELBUFFER iface) {
296 PipeBuf *This = (PipeBuf *)iface;
298 wine_rpc_disconnect_header header;
300 DWORD reqtype = REQTYPE_DISCONNECT;
302 ref = InterlockedDecrement(&This->ref);
306 FIXME("Free all stuff\n");
308 memcpy(&header.mid, &This->mid, sizeof(wine_marshal_id));
310 pipe = PIPE_FindByMID(&This->mid);
312 write_pipe(pipe, &reqtype, sizeof(reqtype));
313 write_pipe(pipe, &header, sizeof(wine_rpc_disconnect_header));
315 TRACE("written disconnect packet\n");
317 HeapFree(GetProcessHeap(),0,This);
321 static HRESULT WINAPI
323 LPRPCCHANNELBUFFER iface,RPCOLEMESSAGE* msg,REFIID riid
325 /*PipeBuf *This = (PipeBuf *)iface;*/
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);
335 COM_InvokeAndRpcSend(wine_rpc_request *req) {
336 IRpcStubBuffer *stub;
341 hres = MARSHAL_Find_Stub_Buffer(&(req->reqh.mid),&stub);
343 ERR("Stub not found?\n");
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;
367 static HRESULT COM_RpcReceive(wine_pipe *xpipe);
370 RPC_QueueRequestAndWait(wine_rpc_request *req) {
372 wine_rpc_request *xreq;
375 wine_pipe *xpipe = PIPE_GetFromMID(&(req->reqh.mid));
378 FIXME("no pipe found.\n");
381 req->hPipe = xpipe->hPipe;
382 req->state = REQSTATE_REQ_WAITING_FOR_REPLY;
383 reqtype = REQTYPE_REQUEST;
384 hres = write_pipe(req->hPipe,&reqtype,sizeof(reqtype));
385 if (hres) return hres;
386 hres = write_pipe(req->hPipe,&(req->reqh),sizeof(req->reqh));
387 if (hres) return hres;
388 hres = write_pipe(req->hPipe,req->Buffer,req->reqh.cbBuffer);
389 if (hres) return hres;
391 /* This loop is about allowing re-entrancy. While waiting for the
392 * response to one RPC we may receive a request starting another. */
394 hres = COM_RpcReceive(xpipe);
397 for (i=0;i<nrofreqs;i++) {
399 if ((xreq->state==REQSTATE_REQ_GOT) && (xreq->hPipe==req->hPipe)) {
400 hres = COM_InvokeAndRpcSend(xreq);
404 if (req->state == REQSTATE_RESP_GOT)
408 WARN("-- 0x%08lx\n", hres);
412 static HRESULT WINAPI
414 LPRPCCHANNELBUFFER iface,RPCOLEMESSAGE* msg,ULONG *status
416 PipeBuf *This = (PipeBuf *)iface;
417 wine_rpc_request *req;
422 if (This->mid.oxid == COM_CurrentApt()->oxid) {
423 ERR("Need to call directly!\n");
427 hres = RPC_GetRequest(&req);
428 if (hres) return hres;
429 req->reqh.iMethod = msg->iMethod;
430 req->reqh.cbBuffer = msg->cbBuffer;
431 memcpy(&(req->reqh.mid),&(This->mid),sizeof(This->mid));
432 req->Buffer = msg->Buffer;
433 hres = RPC_QueueRequestAndWait(req);
435 RPC_FreeRequest(req);
438 msg->cbBuffer = req->resph.cbBuffer;
439 msg->Buffer = req->Buffer;
440 *status = req->resph.retval;
441 RPC_FreeRequest(req);
446 static HRESULT WINAPI
447 PipeBuf_FreeBuffer(LPRPCCHANNELBUFFER iface,RPCOLEMESSAGE* msg) {
448 FIXME("(%p), stub!\n",msg);
452 static HRESULT WINAPI
454 LPRPCCHANNELBUFFER iface,DWORD* pdwDestContext,void** ppvDestContext
456 FIXME("(%p,%p), stub!\n",pdwDestContext,ppvDestContext);
460 static HRESULT WINAPI
461 PipeBuf_IsConnected(LPRPCCHANNELBUFFER iface) {
462 FIXME("(), stub!\n");
466 static IRpcChannelBufferVtbl pipebufvt = {
467 PipeBuf_QueryInterface,
478 PIPE_GetNewPipeBuf(wine_marshal_id *mid, IRpcChannelBuffer **pipebuf) {
479 wine_marshal_id ourid;
485 hPipe = PIPE_FindByMID(mid);
486 if (hPipe == INVALID_HANDLE_VALUE) {
488 sprintf(pipefn,OLESTUBMGR"_%08lx%08lx",(DWORD)(mid->oxid >> 32),(DWORD)mid->oxid);
491 GENERIC_READ|GENERIC_WRITE,
498 if (hPipe == INVALID_HANDLE_VALUE) {
499 FIXME("Could not open named pipe %s, le is %lx\n",pipefn,GetLastError());
502 hres = PIPE_RegisterPipe(mid, hPipe, FALSE);
503 if (hres) return hres;
504 memset(&ourid,0,sizeof(ourid));
505 ourid.oxid = COM_CurrentApt()->oxid;
506 if (!WriteFile(hPipe,&ourid,sizeof(ourid),&res,NULL)||(res!=sizeof(ourid))) {
507 ERR("Failed writing startup mid!\n");
511 pbuf = (PipeBuf*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(PipeBuf));
512 pbuf->lpVtbl = &pipebufvt;
514 memcpy(&(pbuf->mid),mid,sizeof(*mid));
515 *pipebuf = (IRpcChannelBuffer*)pbuf;
520 create_server(REFCLSID rclsid) {
521 static const WCHAR embedding[] = { ' ', '-','E','m','b','e','d','d','i','n','g',0 };
524 HRESULT hres = E_UNEXPECTED;
526 WCHAR exe[MAX_PATH+1];
527 DWORD exelen = sizeof(exe);
528 WCHAR command[MAX_PATH+sizeof(embedding)/sizeof(WCHAR)];
530 PROCESS_INFORMATION pinfo;
532 WINE_StringFromCLSID((LPCLSID)rclsid,xclsid);
534 sprintf(buf,"CLSID\\%s\\LocalServer32",xclsid);
535 hres = RegOpenKeyExA(HKEY_CLASSES_ROOT, buf, 0, KEY_READ, &key);
537 if (hres != ERROR_SUCCESS) {
538 WARN("CLSID %s not registered as LocalServer32\n", xclsid);
539 return REGDB_E_READREGDB; /* Probably */
542 memset(exe,0,sizeof(exe));
543 hres= RegQueryValueExW(key, NULL, NULL, NULL, (LPBYTE)exe, &exelen);
546 WARN("No default value for LocalServer32 key\n");
547 return REGDB_E_CLASSNOTREG; /* FIXME: check retval */
550 memset(&sinfo,0,sizeof(sinfo));
551 sinfo.cb = sizeof(sinfo);
553 /* EXE servers are started with the -Embedding switch. MSDN also claims /Embedding is used,
554 9x does -Embedding, perhaps an 9x/NT difference? */
556 strcpyW(command, exe);
557 strcatW(command, embedding);
559 TRACE("activating local server '%s' for %s\n", debugstr_w(command), xclsid);
561 if (!CreateProcessW(exe, command, NULL, NULL, FALSE, 0, NULL, NULL, &sinfo, &pinfo)) {
562 WARN("failed to run local server %s\n", debugstr_w(exe));
568 /* http://msdn.microsoft.com/library/en-us/dnmsj99/html/com0199.asp, Figure 4 */
569 HRESULT create_marshalled_proxy(REFCLSID rclsid, REFIID iid, LPVOID *ppv) {
574 char marshalbuffer[200];
576 LARGE_INTEGER seekto;
577 ULARGE_INTEGER newpos;
579 #define MAXTRIES 10000
581 TRACE("rclsid=%s, iid=%s\n", debugstr_guid(rclsid), debugstr_guid(iid));
583 strcpy(pipefn,PIPEPREF);
584 WINE_StringFromCLSID(rclsid,pipefn+strlen(PIPEPREF));
586 while (tries++<MAXTRIES) {
587 WaitNamedPipeA( pipefn, NMPWAIT_WAIT_FOREVER );
590 GENERIC_READ|GENERIC_WRITE,
597 if (hPipe == INVALID_HANDLE_VALUE) {
599 if ((hres = create_server(rclsid)))
603 WARN("Could not open named pipe to broker %s, le is %lx\n",pipefn,GetLastError());
609 if (!ReadFile(hPipe,marshalbuffer,sizeof(marshalbuffer),&bufferlen,NULL)) {
610 FIXME("Failed to read marshal id from classfactory of %s.\n",debugstr_guid(rclsid));
618 return E_NOINTERFACE;
619 hres = CreateStreamOnHGlobal(0,TRUE,&pStm);
620 if (hres) return hres;
621 hres = IStream_Write(pStm,marshalbuffer,bufferlen,&res);
623 seekto.u.LowPart = 0;seekto.u.HighPart = 0;
624 hres = IStream_Seek(pStm,seekto,SEEK_SET,&newpos);
625 hres = CoUnmarshalInterface(pStm,&IID_IClassFactory,ppv);
627 IStream_Release(pStm);
633 PIPE_StartRequestThread(HANDLE xhPipe) {
634 wine_marshal_id remoteid;
637 hres = read_pipe(xhPipe,&remoteid,sizeof(remoteid));
639 ERR("Failed to read remote mid!\n");
642 PIPE_RegisterPipe(&remoteid,xhPipe, TRUE);
646 COM_RpcReceive(wine_pipe *xpipe) {
649 HANDLE xhPipe = xpipe->hPipe;
651 /*FIXME("%lx %d reading reqtype\n",GetCurrentProcessId(),xhPipe);*/
652 hres = read_pipe(xhPipe,&reqtype,sizeof(reqtype));
654 EnterCriticalSection(&(xpipe->crit));
655 /*FIXME("%lx got reqtype %ld\n",GetCurrentProcessId(),reqtype);*/
657 if (reqtype == REQTYPE_DISCONNECT) { /* only received by servers */
658 wine_rpc_disconnect_header header;
659 IRpcStubBuffer *stub;
662 hres = read_pipe(xhPipe, &header, sizeof(header));
664 ERR("could not read disconnect header\n");
668 TRACE("read disconnect header\n");
670 hres = MARSHAL_Find_Stub_Buffer(&header.mid, &stub);
672 ERR("could not locate stub to disconnect, mid.oid=%s\n",
673 wine_dbgstr_longlong(header.mid.oid));
678 /* release reference added by MARSHAL_Find_Stub_Buffer call */
679 IRpcStubBuffer_Release(stub);
680 /* release it for real */
681 ret = IRpcStubBuffer_Release(stub);
684 MARSHAL_Invalidate_Stub_From_MID(&header.mid);
686 } else if (reqtype == REQTYPE_REQUEST) {
687 wine_rpc_request *xreq;
688 RPC_GetRequest(&xreq);
689 xreq->hPipe = xhPipe;
690 hres = read_pipe(xhPipe,&(xreq->reqh),sizeof(xreq->reqh));
692 xreq->resph.reqid = xreq->reqh.reqid;
693 xreq->Buffer = HeapAlloc(GetProcessHeap(),0, xreq->reqh.cbBuffer);
694 hres = read_pipe(xhPipe,xreq->Buffer,xreq->reqh.cbBuffer);
696 xreq->state = REQSTATE_REQ_GOT;
698 } else if (reqtype == REQTYPE_RESPONSE) {
699 wine_rpc_response_header resph;
702 hres = read_pipe(xhPipe,&resph,sizeof(resph));
704 for (i=nrofreqs;i--;) {
705 wine_rpc_request *xreq = reqs[i];
706 if (xreq->state != REQSTATE_REQ_WAITING_FOR_REPLY)
708 if (xreq->reqh.reqid == resph.reqid) {
709 memcpy(&(xreq->resph),&resph,sizeof(resph));
712 xreq->Buffer = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,xreq->Buffer,xreq->resph.cbBuffer);
714 xreq->Buffer = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,xreq->resph.cbBuffer);
716 hres = read_pipe(xhPipe,xreq->Buffer,xreq->resph.cbBuffer);
718 xreq->state = REQSTATE_RESP_GOT;
719 /*PulseEvent(hRpcChanged);*/
723 ERR("Did not find request for id %lx\n",resph.reqid);
727 ERR("Unknown reqtype %ld\n",reqtype);
730 LeaveCriticalSection(&(xpipe->crit));
735 _StubReaderThread(LPVOID param) {
736 wine_pipe *xpipe = (wine_pipe*)param;
737 HANDLE xhPipe = xpipe->hPipe;
740 TRACE("STUB reader thread %lx\n",GetCurrentProcessId());
743 hres = COM_RpcReceive(xpipe);
746 for (i=nrofreqs;i--;) {
747 wine_rpc_request *xreq = reqs[i];
748 if ((xreq->state == REQSTATE_REQ_GOT) && (xreq->hPipe == xhPipe)) {
749 hres = COM_InvokeAndRpcSend(xreq);
754 FIXME("Failed with hres %lx\n",hres);
759 /* This thread listens on a named pipe for the entire process. It
760 * deals with incoming connection requests to objects.
762 static DWORD WINAPI listener_thread(LPVOID param)
767 sprintf(pipefn,OLESTUBMGR"_%08lx",GetCurrentProcessId());
768 TRACE("Process listener thread starting on (%s)\n",pipefn);
771 listenPipe = CreateNamedPipeA(
774 PIPE_TYPE_BYTE|PIPE_WAIT,
775 PIPE_UNLIMITED_INSTANCES,
778 NMPWAIT_USE_DEFAULT_WAIT,
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 */
785 if (!ConnectNamedPipe(listenPipe,NULL)) {
786 ERR("Failure during ConnectNamedPipe %lx!\n",GetLastError());
787 CloseHandle(listenPipe);
790 PIPE_StartRequestThread(listenPipe);
795 void start_listener_thread()
797 static BOOL running = FALSE;
803 CreateThread(NULL, 0, listener_thread, NULL, 0, &tid);
804 Sleep(2000); /* actually we just try opening the pipe until it succeeds */