/* * (Local) RPC Stuff * * Copyright 2002 Marcus Meissner * Copyright 2005 Mike Hearn, Rob Shearman for CodeWeavers * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include #include #include #define COBJMACROS #define NONAMELESSUNION #define NONAMELESSSTRUCT #include "windef.h" #include "winbase.h" #include "winuser.h" #include "winsvc.h" #include "objbase.h" #include "ole2.h" #include "rpc.h" #include "winerror.h" #include "winreg.h" #include "wtypes.h" #include "wine/unicode.h" #include "compobj_private.h" #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(ole); #define PIPEPREF "\\\\.\\pipe\\" #define OLESTUBMGR PIPEPREF"WINE_OLE_StubMgr" #define REQTYPE_REQUEST 0 #define REQTYPE_RESPONSE 1 struct request_header { DWORD reqid; IPID ipid; DWORD iMethod; DWORD cbBuffer; }; struct response_header { DWORD reqid; DWORD cbBuffer; DWORD retval; }; #define REQSTATE_START 0 #define REQSTATE_REQ_QUEUED 1 #define REQSTATE_REQ_WAITING_FOR_REPLY 2 #define REQSTATE_REQ_GOT 3 #define REQSTATE_INVOKING 4 #define REQSTATE_RESP_QUEUED 5 #define REQSTATE_RESP_GOT 6 #define REQSTATE_DONE 6 struct rpc { int state; HANDLE hPipe; /* temp copy of handle */ struct request_header reqh; struct response_header resph; LPBYTE Buffer; }; /* fixme: this should have a lock */ static struct rpc **reqs = NULL; static int nrofreqs = 0; /* This pipe is _thread_ based, each thread which talks to a remote * apartment (oxid) has its own pipe. The same structure is used both * for outgoing and incoming RPCs. */ struct pipe { wine_marshal_id mid; /* target mid */ DWORD tid; /* thread which owns this pipe */ HANDLE hPipe; int pending; HANDLE hThread; CRITICAL_SECTION crit; APARTMENT *apt; /* apartment of the marshalling thread for the stub dispatch case */ }; typedef struct _PipeBuf { IRpcChannelBufferVtbl *lpVtbl; DWORD ref; wine_marshal_id mid; HANDLE pipe; } PipeBuf; /* some helper functions */ static HRESULT WINAPI read_pipe(HANDLE hf, LPVOID ptr, DWORD size) { DWORD res; if (!ReadFile(hf,ptr,size,&res,NULL)) { ERR("Failed to read from %p, le is %ld\n",hf,GetLastError()); return E_FAIL; } if (res != size) { if (!res) { WARN("%p disconnected\n", hf); return RPC_E_DISCONNECTED; } ERR("Read only %ld of %ld bytes from %p.\n",res,size,hf); return E_FAIL; } return S_OK; } static HRESULT WINAPI write_pipe(HANDLE hf, LPVOID ptr, DWORD size) { DWORD res; if (!WriteFile(hf,ptr,size,&res,NULL)) { FIXME("Failed to write to %p, le is %ld\n",hf,GetLastError()); return E_FAIL; } if (res!=size) { FIXME("Wrote only %ld of %ld bytes to %p.\n",res,size,hf); return E_FAIL; } return S_OK; } static HANDLE dupe_handle(HANDLE h) { HANDLE h2; if (!DuplicateHandle(GetCurrentProcess(), h, GetCurrentProcess(), &h2, 0, FALSE, DUPLICATE_SAME_ACCESS)) { ERR("could not duplicate handle: %ld\n", GetLastError()); return INVALID_HANDLE_VALUE; } return h2; } static DWORD WINAPI client_dispatch_thread(LPVOID); /* FIXME: this all needs to be made thread safe */ static HRESULT RPC_GetRequest(struct rpc **req) { static int reqid = 0; int i; /* try to reuse */ for (i = 0; i < nrofreqs; i++) { if (reqs[i]->state == REQSTATE_DONE) { TRACE("reusing reqs[%d]\n", i); reqs[i]->reqh.reqid = reqid++; reqs[i]->resph.reqid = reqs[i]->reqh.reqid; reqs[i]->hPipe = INVALID_HANDLE_VALUE; reqs[i]->state = REQSTATE_START; *req = reqs[i]; return S_OK; } } TRACE("creating new struct rpc (request)\n"); if (reqs) reqs = (struct rpc**)HeapReAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, reqs, sizeof(struct rpc*)*(nrofreqs+1) ); else reqs = (struct rpc**)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct rpc*) ); if (!reqs) return E_OUTOFMEMORY; reqs[nrofreqs] = (struct rpc*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(struct rpc)); reqs[nrofreqs]->reqh.reqid = reqid++; reqs[nrofreqs]->resph.reqid = reqs[nrofreqs]->reqh.reqid; reqs[nrofreqs]->hPipe = INVALID_HANDLE_VALUE; reqs[nrofreqs]->state = REQSTATE_START; *req = reqs[nrofreqs]; nrofreqs++; return S_OK; } static HRESULT WINAPI PipeBuf_QueryInterface( LPRPCCHANNELBUFFER iface,REFIID riid,LPVOID *ppv ) { *ppv = NULL; if (IsEqualIID(riid,&IID_IRpcChannelBuffer) || IsEqualIID(riid,&IID_IUnknown)) { *ppv = (LPVOID)iface; IUnknown_AddRef(iface); return S_OK; } return E_NOINTERFACE; } static ULONG WINAPI PipeBuf_AddRef(LPRPCCHANNELBUFFER iface) { PipeBuf *This = (PipeBuf *)iface; return InterlockedIncrement(&This->ref); } static ULONG WINAPI PipeBuf_Release(LPRPCCHANNELBUFFER iface) { PipeBuf *This = (PipeBuf *)iface; ULONG ref; ref = InterlockedDecrement(&This->ref); if (ref) return ref; CloseHandle(This->pipe); HeapFree(GetProcessHeap(),0,This); return 0; } static HRESULT WINAPI PipeBuf_GetBuffer(LPRPCCHANNELBUFFER iface,RPCOLEMESSAGE* msg,REFIID riid) { TRACE("(%p,%s)\n",msg,debugstr_guid(riid)); /* probably reuses IID in real. */ if (msg->cbBuffer && (msg->Buffer == NULL)) msg->Buffer = HeapAlloc(GetProcessHeap(),0,msg->cbBuffer); return S_OK; } static HRESULT COM_InvokeAndRpcSend(struct rpc *req) { IRpcStubBuffer *stub; RPCOLEMESSAGE msg; HRESULT hres; DWORD reqtype; if (!(stub = ipid_to_stubbuffer(&(req->reqh.ipid)))) /* ipid_to_stubbuffer will already have logged the error */ return RPC_E_DISCONNECTED; IUnknown_AddRef(stub); msg.Buffer = req->Buffer; msg.iMethod = req->reqh.iMethod; msg.cbBuffer = req->reqh.cbBuffer; msg.dataRepresentation = NDR_LOCAL_DATA_REPRESENTATION; req->state = REQSTATE_INVOKING; req->resph.retval = IRpcStubBuffer_Invoke(stub,&msg,NULL); IUnknown_Release(stub); req->Buffer = msg.Buffer; req->resph.cbBuffer = msg.cbBuffer; reqtype = REQTYPE_RESPONSE; hres = write_pipe(req->hPipe,&reqtype,sizeof(reqtype)); if (hres) return hres; hres = write_pipe(req->hPipe,&(req->resph),sizeof(req->resph)); if (hres) return hres; hres = write_pipe(req->hPipe,req->Buffer,req->resph.cbBuffer); if (hres) return hres; req->state = REQSTATE_DONE; return S_OK; } static HRESULT process_incoming_rpc(HANDLE pipe); static HRESULT RPC_QueueRequestAndWait(struct rpc *req, HANDLE pipe) { int i; struct rpc *xreq; HRESULT hres; DWORD reqtype; req->hPipe = pipe; req->state = REQSTATE_REQ_WAITING_FOR_REPLY; reqtype = REQTYPE_REQUEST; hres = write_pipe(req->hPipe,&reqtype,sizeof(reqtype)); if (hres) return hres; hres = write_pipe(req->hPipe,&(req->reqh),sizeof(req->reqh)); if (hres) return hres; hres = write_pipe(req->hPipe,req->Buffer,req->reqh.cbBuffer); if (hres) return hres; /* This loop is about allowing re-entrancy. While waiting for the * response to one RPC we may receive a request starting another. */ while (!hres) { hres = process_incoming_rpc(pipe); if (hres) break; for (i=0;istate==REQSTATE_REQ_GOT) && (xreq->hPipe==req->hPipe)) { hres = COM_InvokeAndRpcSend(xreq); if (hres) break; } } if (req->state == REQSTATE_RESP_GOT) return S_OK; } if (FAILED(hres)) WARN("-- 0x%08lx\n", hres); return hres; } static HRESULT WINAPI PipeBuf_SendReceive(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE *msg, ULONG *status) { PipeBuf *This = (PipeBuf *)iface; struct rpc *req; HRESULT hres; if (This->mid.oxid == COM_CurrentApt()->oxid) { ERR("Need to call directly!\n"); return E_FAIL; } hres = RPC_GetRequest(&req); if (hres) return hres; req->reqh.iMethod = msg->iMethod; req->reqh.cbBuffer = msg->cbBuffer; req->reqh.ipid = This->mid.ipid; req->Buffer = msg->Buffer; TRACE(" -> rpc ->\n"); hres = RPC_QueueRequestAndWait(req, This->pipe); TRACE(" <- response <-\n"); if (hres) { req->state = REQSTATE_DONE; return hres; } msg->cbBuffer = req->resph.cbBuffer; msg->Buffer = req->Buffer; *status = req->resph.retval; req->state = REQSTATE_DONE; return S_OK; } static HRESULT WINAPI PipeBuf_FreeBuffer(LPRPCCHANNELBUFFER iface,RPCOLEMESSAGE* msg) { TRACE("(%p)\n",msg); HeapFree(GetProcessHeap(), 0, msg->Buffer); return S_OK; } static HRESULT WINAPI PipeBuf_GetDestCtx(LPRPCCHANNELBUFFER iface,DWORD* pdwDestContext,void** ppvDestContext) { FIXME("(%p,%p), stub!\n",pdwDestContext,ppvDestContext); return E_FAIL; } static HRESULT WINAPI PipeBuf_IsConnected(LPRPCCHANNELBUFFER iface) { FIXME("(), stub!\n"); return S_OK; } static IRpcChannelBufferVtbl pipebufvt = { PipeBuf_QueryInterface, PipeBuf_AddRef, PipeBuf_Release, PipeBuf_GetBuffer, PipeBuf_SendReceive, PipeBuf_FreeBuffer, PipeBuf_GetDestCtx, PipeBuf_IsConnected }; /* returns a pipebuf for proxies */ HRESULT PIPE_GetNewPipeBuf(wine_marshal_id *mid, IRpcChannelBuffer **pipebuf) { wine_marshal_id ourid; HANDLE handle; PipeBuf *pbuf; char pipefn[200]; /* connect to the apartment listener thread */ sprintf(pipefn,OLESTUBMGR"_%08lx%08lx",(DWORD)(mid->oxid >> 32),(DWORD)mid->oxid); TRACE("proxy pipe: connecting to apartment listener thread: %s\n", pipefn); while (TRUE) { BOOL ret = WaitNamedPipeA(pipefn, NMPWAIT_USE_DEFAULT_WAIT); if (!ret) { ERR("Could not open named pipe %s, error %ld\n", pipefn, GetLastError()); return RPC_E_SERVER_DIED; } handle = CreateFileA(pipefn, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0); if (handle == INVALID_HANDLE_VALUE) { if (GetLastError() == ERROR_PIPE_BUSY) continue; ERR("Could not open named pipe %s, error %ld\n", pipefn, GetLastError()); return RPC_E_SERVER_DIED; } break; } memset(&ourid,0,sizeof(ourid)); ourid.oxid = COM_CurrentApt()->oxid; TRACE("constructing new pipebuf for proxy\n"); pbuf = (PipeBuf*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(PipeBuf)); pbuf->lpVtbl = &pipebufvt; pbuf->ref = 1; memcpy(&(pbuf->mid),mid,sizeof(*mid)); pbuf->pipe = dupe_handle(handle); *pipebuf = (IRpcChannelBuffer*)pbuf; return S_OK; } static HRESULT create_server(REFCLSID rclsid) { static const WCHAR embedding[] = { ' ', '-','E','m','b','e','d','d','i','n','g',0 }; HKEY key; char buf[200]; HRESULT hres = E_UNEXPECTED; char xclsid[80]; WCHAR exe[MAX_PATH+1]; DWORD exelen = sizeof(exe); WCHAR command[MAX_PATH+sizeof(embedding)/sizeof(WCHAR)]; STARTUPINFOW sinfo; PROCESS_INFORMATION pinfo; WINE_StringFromCLSID((LPCLSID)rclsid,xclsid); sprintf(buf,"CLSID\\%s\\LocalServer32",xclsid); hres = RegOpenKeyExA(HKEY_CLASSES_ROOT, buf, 0, KEY_READ, &key); if (hres != ERROR_SUCCESS) { WARN("CLSID %s not registered as LocalServer32\n", xclsid); return REGDB_E_READREGDB; /* Probably */ } memset(exe,0,sizeof(exe)); hres= RegQueryValueExW(key, NULL, NULL, NULL, (LPBYTE)exe, &exelen); RegCloseKey(key); if (hres) { WARN("No default value for LocalServer32 key\n"); return REGDB_E_CLASSNOTREG; /* FIXME: check retval */ } memset(&sinfo,0,sizeof(sinfo)); sinfo.cb = sizeof(sinfo); /* EXE servers are started with the -Embedding switch. MSDN also claims /Embedding is used, * 9x does -Embedding, perhaps an 9x/NT difference? */ strcpyW(command, exe); strcatW(command, embedding); TRACE("activating local server '%s' for %s\n", debugstr_w(command), xclsid); if (!CreateProcessW(exe, command, NULL, NULL, FALSE, 0, NULL, NULL, &sinfo, &pinfo)) { WARN("failed to run local server %s\n", debugstr_w(exe)); return E_FAIL; } return S_OK; } /* * start_local_service() - start a service given its name and parameters */ static DWORD start_local_service(LPCWSTR name, DWORD num, LPWSTR *params) { SC_HANDLE handle, hsvc; DWORD r = ERROR_FUNCTION_FAILED; TRACE("Starting service %s %ld params\n", debugstr_w(name), num); handle = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS); if (!handle) return r; hsvc = OpenServiceW(handle, name, SC_MANAGER_ALL_ACCESS); if (hsvc) { if(StartServiceW(hsvc, num, (LPCWSTR*)params)) r = ERROR_SUCCESS; else r = GetLastError(); if (r == ERROR_SERVICE_ALREADY_RUNNING) r = ERROR_SUCCESS; CloseServiceHandle(hsvc); } CloseServiceHandle(handle); TRACE("StartService returned error %ld (%s)\n", r, r?"ok":"failed"); return r; } /* * create_local_service() - start a COM server in a service * * To start a Local Service, we read the AppID value under * the class's CLSID key, then open the HKCR\\AppId key specified * there and check for a LocalService value. * * Note: Local Services are not supported under Windows 9x */ static HRESULT create_local_service(REFCLSID rclsid) { HRESULT hres = REGDB_E_READREGDB; WCHAR buf[40], keyname[50]; static const WCHAR szClsId[] = { 'C','L','S','I','D','\\',0 }; static const WCHAR szAppId[] = { 'A','p','p','I','d',0 }; static const WCHAR szAppIdKey[] = { 'A','p','p','I','d','\\',0 }; static const WCHAR szLocalService[] = { 'L','o','c','a','l','S','e','r','v','i','c','e',0 }; static const WCHAR szServiceParams[] = {'S','e','r','v','i','c','e','P','a','r','a','m','s',0}; HKEY hkey; LONG r; DWORD type, sz; TRACE("Attempting to start Local service for %s\n", debugstr_guid(rclsid)); /* read the AppID value under the class's key */ strcpyW(keyname,szClsId); StringFromGUID2(rclsid,&keyname[6],39); r = RegOpenKeyExW(HKEY_CLASSES_ROOT, keyname, 0, KEY_READ, &hkey); if (r!=ERROR_SUCCESS) return hres; sz = sizeof buf; r = RegQueryValueExW(hkey, szAppId, NULL, &type, (LPBYTE)buf, &sz); RegCloseKey(hkey); if (r!=ERROR_SUCCESS || type!=REG_SZ) return hres; /* read the LocalService and ServiceParameters values from the AppID key */ strcpyW(keyname, szAppIdKey); strcatW(keyname, buf); r = RegOpenKeyExW(HKEY_CLASSES_ROOT, keyname, 0, KEY_READ, &hkey); if (r!=ERROR_SUCCESS) return hres; sz = sizeof buf; r = RegQueryValueExW(hkey, szLocalService, NULL, &type, (LPBYTE)buf, &sz); if (r==ERROR_SUCCESS && type==REG_SZ) { DWORD num_args = 0; LPWSTR args[1] = { NULL }; /* * FIXME: I'm not really sure how to deal with the service parameters. * I suspect that the string returned from RegQueryValueExW * should be split into a number of arguments by spaces. * It would make more sense if ServiceParams contained a * REG_MULTI_SZ here, but it's a REG_SZ for the services * that I'm interested in for the moment. */ r = RegQueryValueExW(hkey, szServiceParams, NULL, &type, NULL, &sz); if (r == ERROR_SUCCESS && type == REG_SZ && sz) { args[0] = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sz); num_args++; RegQueryValueExW(hkey, szServiceParams, NULL, &type, (LPBYTE)args[0], &sz); } r = start_local_service(buf, num_args, args); if (r==ERROR_SUCCESS) hres = S_OK; HeapFree(GetProcessHeap(),0,args[0]); } RegCloseKey(hkey); return hres; } /* http://msdn.microsoft.com/library/en-us/dnmsj99/html/com0199.asp, Figure 4 */ HRESULT create_marshalled_proxy(REFCLSID rclsid, REFIID iid, LPVOID *ppv) { HRESULT hres; HANDLE hPipe; char pipefn[200]; DWORD res, bufferlen; char marshalbuffer[200]; IStream *pStm; LARGE_INTEGER seekto; ULARGE_INTEGER newpos; int tries = 0; static const int MAXTRIES = 10000; TRACE("rclsid=%s, iid=%s\n", debugstr_guid(rclsid), debugstr_guid(iid)); strcpy(pipefn,PIPEPREF); WINE_StringFromCLSID(rclsid,pipefn+strlen(PIPEPREF)); while (tries++ < MAXTRIES) { TRACE("waiting for %s\n", pipefn); WaitNamedPipeA( pipefn, NMPWAIT_WAIT_FOREVER ); hPipe = CreateFileA(pipefn, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0); if (hPipe == INVALID_HANDLE_VALUE) { if (tries == 1) { if ( (hres = create_server(rclsid)) && (hres = create_local_service(rclsid)) ) return hres; Sleep(1000); } else { WARN("Connecting to %s, no response yet, retrying: le is %lx\n",pipefn,GetLastError()); Sleep(1000); } continue; } bufferlen = 0; if (!ReadFile(hPipe,marshalbuffer,sizeof(marshalbuffer),&bufferlen,NULL)) { FIXME("Failed to read marshal id from classfactory of %s.\n",debugstr_guid(rclsid)); Sleep(1000); continue; } TRACE("read marshal id from pipe\n"); CloseHandle(hPipe); break; } if (tries >= MAXTRIES) return E_NOINTERFACE; hres = CreateStreamOnHGlobal(0,TRUE,&pStm); if (hres) return hres; hres = IStream_Write(pStm,marshalbuffer,bufferlen,&res); if (hres) goto out; seekto.u.LowPart = 0;seekto.u.HighPart = 0; hres = IStream_Seek(pStm,seekto,SEEK_SET,&newpos); TRACE("unmarshalling classfactory\n"); hres = CoUnmarshalInterface(pStm,&IID_IClassFactory,ppv); out: IStream_Release(pStm); return hres; } /* this reads an RPC from the given pipe and places it in the global reqs array */ static HRESULT process_incoming_rpc(HANDLE pipe) { DWORD reqtype; HRESULT hres = S_OK; hres = read_pipe(pipe,&reqtype,sizeof(reqtype)); if (hres) return hres; /* only received by servers */ if (reqtype == REQTYPE_REQUEST) { struct rpc *xreq; RPC_GetRequest(&xreq); xreq->hPipe = pipe; hres = read_pipe(pipe,&(xreq->reqh),sizeof(xreq->reqh)); if (hres) { xreq->state = REQSTATE_DONE; return hres; } xreq->resph.reqid = xreq->reqh.reqid; xreq->Buffer = HeapAlloc(GetProcessHeap(),0, xreq->reqh.cbBuffer); hres = read_pipe(pipe,xreq->Buffer,xreq->reqh.cbBuffer); if (hres) goto end; TRACE("received RPC for IPID %s\n", debugstr_guid(&xreq->reqh.ipid)); xreq->state = REQSTATE_REQ_GOT; goto end; } else if (reqtype == REQTYPE_RESPONSE) { struct response_header resph; int i; hres = read_pipe(pipe,&resph,sizeof(resph)); if (hres) goto end; TRACE("read RPC response\n"); for (i = nrofreqs; i--;) { struct rpc *xreq = reqs[i]; if (xreq->state != REQSTATE_REQ_WAITING_FOR_REPLY) continue; if (xreq->reqh.reqid == resph.reqid) { memcpy(&(xreq->resph),&resph,sizeof(resph)); if (xreq->Buffer) xreq->Buffer = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,xreq->Buffer,xreq->resph.cbBuffer); else xreq->Buffer = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,xreq->resph.cbBuffer); hres = read_pipe(pipe,xreq->Buffer,xreq->resph.cbBuffer); if (hres) goto end; TRACE("received response for reqid 0x%lx\n", xreq->reqh.reqid); xreq->state = REQSTATE_RESP_GOT; goto end; } } ERR("protocol error: did not find request for id %lx\n",resph.reqid); hres = E_FAIL; goto end; } ERR("protocol error: unknown reqtype %ld\n",reqtype); hres = E_FAIL; end: return hres; } struct stub_dispatch_params { struct apartment *apt; HANDLE pipe; }; /* This thread listens on the given pipe for requests to any stub manager */ static DWORD WINAPI client_dispatch_thread(LPVOID param) { HANDLE pipe = ((struct stub_dispatch_params *)param)->pipe; struct apartment *apt = ((struct stub_dispatch_params *)param)->apt; HRESULT hres = S_OK; HANDLE shutdown_event = dupe_handle(apt->shutdown_event); HeapFree(GetProcessHeap(), 0, param); /* join marshalling apartment. fixme: this stuff is all very wrong, threading needs to work like native */ COM_CurrentInfo()->apt = apt; while (TRUE) { int i; TRACE("waiting for RPC on OXID: %08lx%08lx\n", (DWORD)(apt->oxid >> 32), (DWORD)(apt->oxid)); /* read a new request into the global array, block if no requests have been sent */ hres = process_incoming_rpc(pipe); if (hres) break; /* do you expect me to talk? */ if (WaitForSingleObject(shutdown_event, 0) == WAIT_OBJECT_0) { /* no mr bond, i expect you to die! bwahaha */ CloseHandle(shutdown_event); break; } TRACE("received RPC on OXID: %08lx%08lx\n", (DWORD)(apt->oxid >> 32), (DWORD)(apt->oxid)); /* now scan the array looking for the RPC just loaded */ for (i=nrofreqs;i--;) { struct rpc *req = reqs[i]; if ((req->state == REQSTATE_REQ_GOT) && (req->hPipe == pipe)) { hres = COM_InvokeAndRpcSend(req); if (!hres) break; } } } TRACE("exiting with hres %lx\n",hres); /* leave marshalling apartment. fixme: this stuff is all very wrong, threading needs to work like native */ COM_CurrentInfo()->apt = NULL; DisconnectNamedPipe(pipe); CloseHandle(pipe); return 0; } struct apartment_listener_params { APARTMENT *apt; HANDLE event; }; /* This thread listens on a named pipe for each apartment that exports * objects. It deals with incoming connection requests. Each time a * client connects a separate thread is spawned for that particular * connection. * * This architecture is different in native DCOM. */ static DWORD WINAPI apartment_listener_thread(LPVOID p) { char pipefn[200]; HANDLE listenPipe, thread_handle; OVERLAPPED overlapped; HANDLE wait[2]; struct apartment_listener_params * params = (struct apartment_listener_params *)p; struct apartment *apt = params->apt; HANDLE event = params->event; HANDLE apt_shutdown_event = dupe_handle(apt->shutdown_event); OXID this_oxid = apt->oxid; /* copy here so we can print it when we shut down */ HeapFree(GetProcessHeap(), 0, params); overlapped.hEvent = CreateEventA(NULL, TRUE, FALSE, NULL); /* we must join the marshalling threads apartment. we already have a ref here */ COM_CurrentInfo()->apt = apt; sprintf(pipefn,OLESTUBMGR"_%08lx%08lx", (DWORD)(apt->oxid >> 32), (DWORD)(apt->oxid)); TRACE("Apartment listener thread starting on (%s)\n",pipefn); while (TRUE) { struct stub_dispatch_params *params; DWORD res; listenPipe = CreateNamedPipeA( pipefn, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE|PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, 4096, 4096, 500 /* 0.5 seconds */, NULL ); /* tell function that started this thread that we have attempted to created the * named pipe. */ if (event) { SetEvent(event); event = NULL; } if (listenPipe == INVALID_HANDLE_VALUE) { FIXME("pipe creation failed for %s, error %ld\n",pipefn,GetLastError()); break; /* permanent failure, so quit stubmgr thread */ } TRACE("waiting for a client ...\n"); /* an already connected pipe is not an error */ if (!ConnectNamedPipe(listenPipe, &overlapped)) { DWORD le = GetLastError(); if ((le != ERROR_IO_PENDING) && (le != ERROR_PIPE_CONNECTED)) { ERR("Failure during ConnectNamedPipe %ld!\n",GetLastError()); CloseHandle(listenPipe); continue; } } /* wait for action */ wait[0] = apt_shutdown_event; wait[1] = overlapped.hEvent; res = WaitForMultipleObjectsEx(2, wait, FALSE, INFINITE, FALSE); if (res == WAIT_OBJECT_0) break; ResetEvent(overlapped.hEvent); /* start the stub dispatch thread for this connection */ TRACE("starting stub dispatch thread for OXID %08lx%08lx\n", (DWORD)(apt->oxid >> 32), (DWORD)(apt->oxid)); params = HeapAlloc(GetProcessHeap(), 0, sizeof(struct stub_dispatch_params)); if (!params) { ERR("out of memory, dropping this client\n"); CloseHandle(listenPipe); continue; } params->apt = apt; params->pipe = listenPipe; thread_handle = CreateThread(NULL, 0, &client_dispatch_thread, params, 0, NULL); CloseHandle(thread_handle); } TRACE("shutting down: %s\n", wine_dbgstr_longlong(this_oxid)); /* we must leave the marshalling threads apartment. we don't have a ref here */ COM_CurrentInfo()->apt = NULL; DisconnectNamedPipe(listenPipe); CloseHandle(listenPipe); CloseHandle(overlapped.hEvent); CloseHandle(apt_shutdown_event); return 0; } void start_apartment_listener_thread() { APARTMENT *apt = COM_CurrentApt(); assert( apt ); TRACE("apt->listenertid=%ld\n", apt->listenertid); /* apt->listenertid is a hack which needs to die at some point, as * it leaks information into the apartment structure. in fact, * this thread isn't quite correct anyway as native RPC doesn't * use a thread per apartment at all, instead the dispatch thread * either enters the apartment to perform the RPC (for MTAs, RTAs) * or does a context switch into it for STAs. */ if (!apt->listenertid) { HANDLE thread; HANDLE event = CreateEventW(NULL, TRUE, FALSE, NULL); struct apartment_listener_params * params = HeapAlloc(GetProcessHeap(), 0, sizeof(*params)); params->apt = apt; params->event = event; thread = CreateThread(NULL, 0, apartment_listener_thread, params, 0, &apt->listenertid); CloseHandle(thread); /* wait for pipe to be created before returning, otherwise we * might try to use it and fail */ WaitForSingleObject(event, INFINITE); CloseHandle(event); } } struct local_server_params { CLSID clsid; IStream *stream; }; static DWORD WINAPI local_server_thread(LPVOID param) { struct local_server_params * lsp = (struct local_server_params *)param; HANDLE hPipe; char pipefn[200]; HRESULT hres; IStream *pStm = lsp->stream; STATSTG ststg; unsigned char *buffer; int buflen; LARGE_INTEGER seekto; ULARGE_INTEGER newpos; ULONG res; TRACE("Starting threader for %s.\n",debugstr_guid(&lsp->clsid)); strcpy(pipefn,PIPEPREF); WINE_StringFromCLSID(&lsp->clsid,pipefn+strlen(PIPEPREF)); HeapFree(GetProcessHeap(), 0, lsp); hPipe = CreateNamedPipeA( pipefn, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE|PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, 4096, 4096, 500 /* 0.5 second timeout */, NULL ); if (hPipe == INVALID_HANDLE_VALUE) { FIXME("pipe creation failed for %s, le is %ld\n",pipefn,GetLastError()); return 1; } while (1) { if (!ConnectNamedPipe(hPipe,NULL)) { ERR("Failure during ConnectNamedPipe %ld, ABORT!\n",GetLastError()); break; } TRACE("marshalling IClassFactory to client\n"); hres = IStream_Stat(pStm,&ststg,0); if (hres) return hres; buflen = ststg.cbSize.u.LowPart; buffer = HeapAlloc(GetProcessHeap(),0,buflen); seekto.u.LowPart = 0; seekto.u.HighPart = 0; hres = IStream_Seek(pStm,seekto,SEEK_SET,&newpos); if (hres) { FIXME("IStream_Seek failed, %lx\n",hres); return hres; } hres = IStream_Read(pStm,buffer,buflen,&res); if (hres) { FIXME("Stream Read failed, %lx\n",hres); return hres; } WriteFile(hPipe,buffer,buflen,&res,NULL); FlushFileBuffers(hPipe); DisconnectNamedPipe(hPipe); TRACE("done marshalling IClassFactory\n"); } CloseHandle(hPipe); IStream_Release(pStm); return 0; } void RPC_StartLocalServer(REFCLSID clsid, IStream *stream) { DWORD tid; HANDLE thread; struct local_server_params *lsp = HeapAlloc(GetProcessHeap(), 0, sizeof(*lsp)); lsp->clsid = *clsid; lsp->stream = stream; thread = CreateThread(NULL, 0, local_server_thread, lsp, 0, &tid); CloseHandle(thread); /* FIXME: failure handling */ }