wine/dlls/ole32/rpc.c
2004-01-23 01:51:33 +00:00

701 lines
18 KiB
C

/*
* (Local) RPC Stuff
*
* Copyright 2002 Marcus Meissner
*
* 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 <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#define NONAMELESSUNION
#define NONAMELESSSTRUCT
#include "windef.h"
#include "winbase.h"
#include "winuser.h"
#include "objbase.h"
#include "ole2.h"
#include "ole2ver.h"
#include "rpc.h"
#include "winerror.h"
#include "winreg.h"
#include "wownt32.h"
#include "wtypes.h"
#include "wine/unicode.h"
#include "wine/winbase16.h"
#include "compobj_private.h"
#include "ifs.h"
#include "compobj_private.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(ole);
typedef struct _wine_rpc_request {
int state;
HANDLE hPipe; /* temp copy of handle */
wine_rpc_request_header reqh;
wine_rpc_response_header resph;
LPBYTE Buffer;
} wine_rpc_request;
static wine_rpc_request **reqs = NULL;
static int nrofreqs = 0;
/* This pipe is _thread_ based */
typedef struct _wine_pipe {
wine_marshal_id mid; /* target mid */
DWORD tid; /* thread in which we execute */
HANDLE hPipe;
int pending;
HANDLE hThread;
CRITICAL_SECTION crit;
} wine_pipe;
static wine_pipe *pipes = NULL;
static int nrofpipes = 0;
typedef struct _PipeBuf {
ICOM_VTABLE(IRpcChannelBuffer) *lpVtbl;
DWORD ref;
wine_marshal_id mid;
wine_pipe *pipe;
} PipeBuf;
static HRESULT WINAPI
_xread(HANDLE hf, LPVOID ptr, DWORD size) {
DWORD res;
if (!ReadFile(hf,ptr,size,&res,NULL)) {
FIXME("Failed to read from %p, le is %lx\n",hf,GetLastError());
return E_FAIL;
}
if (res!=size) {
FIXME("Read only %ld of %ld bytes from %p.\n",res,size,hf);
return E_FAIL;
}
return S_OK;
}
static void
drs(LPCSTR where) {
#if 0
static int nrofreaders = 0;
int i, states[10];
memset(states,0,sizeof(states));
for (i=nrofreqs;i--;)
states[reqs[i]->state]++;
FIXME("%lx/%s/%d: rq %d, w %d, rg %d, rsq %d, rsg %d, d %d\n",
GetCurrentProcessId(),
where,
nrofreaders,
states[REQSTATE_REQ_QUEUED],
states[REQSTATE_REQ_WAITING_FOR_REPLY],
states[REQSTATE_REQ_GOT],
states[REQSTATE_RESP_QUEUED],
states[REQSTATE_RESP_GOT],
states[REQSTATE_DONE]
);
#endif
return ;
}
static HRESULT WINAPI
_xwrite(HANDLE hf, LPVOID ptr, DWORD size) {
DWORD res;
if (!WriteFile(hf,ptr,size,&res,NULL)) {
FIXME("Failed to write to %p, le is %lx\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 DWORD WINAPI _StubReaderThread(LPVOID);
static HRESULT
PIPE_RegisterPipe(wine_marshal_id *mid, HANDLE hPipe, BOOL startreader) {
int i;
char pipefn[100];
wine_pipe *new_pipes;
for (i=0;i<nrofpipes;i++)
if (pipes[i].mid.processid==mid->processid)
return S_OK;
if (pipes)
new_pipes=(wine_pipe*)HeapReAlloc(GetProcessHeap(),0,pipes,sizeof(pipes[0])*(nrofpipes+1));
else
new_pipes=(wine_pipe*)HeapAlloc(GetProcessHeap(),0,sizeof(pipes[0]));
if (!new_pipes) return E_OUTOFMEMORY;
pipes = new_pipes;
sprintf(pipefn,OLESTUBMGR"_%08lx",mid->processid);
memcpy(&(pipes[nrofpipes].mid),mid,sizeof(*mid));
pipes[nrofpipes].hPipe = hPipe;
InitializeCriticalSection(&(pipes[nrofpipes].crit));
nrofpipes++;
if (startreader) {
pipes[nrofpipes-1].hThread = CreateThread(NULL,0,_StubReaderThread,(LPVOID)(pipes+(nrofpipes-1)),0,&(pipes[nrofpipes-1].tid));
} else {
pipes[nrofpipes-1].tid = GetCurrentThreadId();
}
return S_OK;
}
static HANDLE
PIPE_FindByMID(wine_marshal_id *mid) {
int i;
for (i=0;i<nrofpipes;i++)
if ((pipes[i].mid.processid==mid->processid) &&
(GetCurrentThreadId()==pipes[i].tid)
)
return pipes[i].hPipe;
return INVALID_HANDLE_VALUE;
}
static wine_pipe*
PIPE_GetFromMID(wine_marshal_id *mid) {
int i;
for (i=0;i<nrofpipes;i++) {
if ((pipes[i].mid.processid==mid->processid) &&
(GetCurrentThreadId()==pipes[i].tid)
)
return pipes+i;
}
return NULL;
}
static HRESULT
RPC_GetRequest(wine_rpc_request **req) {
static int reqid = 0xdeadbeef;
int i;
for (i=0;i<nrofreqs;i++) { /* try to reuse */
if (reqs[i]->state == REQSTATE_DONE) {
reqs[i]->reqh.reqid = reqid++;
reqs[i]->resph.reqid = reqs[i]->reqh.reqid;
reqs[i]->hPipe = INVALID_HANDLE_VALUE;
*req = reqs[i];
reqs[i]->state = REQSTATE_START;
return S_OK;
}
}
/* create new */
if (reqs)
reqs = (wine_rpc_request**)HeapReAlloc(
GetProcessHeap(),
HEAP_ZERO_MEMORY,
reqs,
sizeof(wine_rpc_request*)*(nrofreqs+1)
);
else
reqs = (wine_rpc_request**)HeapAlloc(
GetProcessHeap(),
HEAP_ZERO_MEMORY,
sizeof(wine_rpc_request*)
);
if (!reqs)
return E_OUTOFMEMORY;
reqs[nrofreqs] = (wine_rpc_request*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(wine_rpc_request));
reqs[nrofreqs]->reqh.reqid = reqid++;
reqs[nrofreqs]->resph.reqid = reqs[nrofreqs]->reqh.reqid;
reqs[nrofreqs]->hPipe = INVALID_HANDLE_VALUE;
*req = reqs[nrofreqs];
reqs[nrofreqs]->state = REQSTATE_START;
nrofreqs++;
return S_OK;
}
static void
RPC_FreeRequest(wine_rpc_request *req) {
req->state = REQSTATE_DONE; /* Just reuse slot. */
return;
}
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) {
ICOM_THIS(PipeBuf,iface);
This->ref++;
return This->ref;
}
static ULONG WINAPI
PipeBuf_Release(LPRPCCHANNELBUFFER iface) {
ICOM_THIS(PipeBuf,iface);
This->ref--;
if (This->ref)
return This->ref;
ERR("Free all stuff.\n");
HeapFree(GetProcessHeap(),0,This);
return 0;
}
static HRESULT WINAPI
PipeBuf_GetBuffer(
LPRPCCHANNELBUFFER iface,RPCOLEMESSAGE* msg,REFIID riid
) {
/*ICOM_THIS(PipeBuf,iface);*/
TRACE("(%p,%s), slightly wrong.\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
_invoke_onereq(wine_rpc_request *req) {
IRpcStubBuffer *stub;
RPCOLEMESSAGE msg;
HRESULT hres;
DWORD reqtype;
hres = MARSHAL_Find_Stub_Buffer(&(req->reqh.mid),&stub);
if (hres) {
ERR("Stub not found?\n");
return hres;
}
msg.Buffer = req->Buffer;
msg.iMethod = req->reqh.iMethod;
msg.cbBuffer = req->reqh.cbBuffer;
req->state = REQSTATE_INVOKING;
req->resph.retval = IRpcStubBuffer_Invoke(stub,&msg,NULL);
req->Buffer = msg.Buffer;
req->resph.cbBuffer = msg.cbBuffer;
reqtype = REQTYPE_RESPONSE;
hres = _xwrite(req->hPipe,&reqtype,sizeof(reqtype));
if (hres) return hres;
hres = _xwrite(req->hPipe,&(req->resph),sizeof(req->resph));
if (hres) return hres;
hres = _xwrite(req->hPipe,req->Buffer,req->resph.cbBuffer);
if (hres) return hres;
req->state = REQSTATE_DONE;
drs("invoke");
return S_OK;
}
static HRESULT _read_one(wine_pipe *xpipe);
static HRESULT
RPC_QueueRequestAndWait(wine_rpc_request *req) {
int i;
wine_rpc_request *xreq;
HRESULT hres;
DWORD reqtype;
wine_pipe *xpipe = PIPE_GetFromMID(&(req->reqh.mid));
if (!xpipe) {
FIXME("no pipe found.\n");
return E_POINTER;
}
if (GetCurrentProcessId() == req->reqh.mid.processid) {
ERR("In current process?\n");
return E_FAIL;
}
req->hPipe = xpipe->hPipe;
req->state = REQSTATE_REQ_WAITING_FOR_REPLY;
reqtype = REQTYPE_REQUEST;
hres = _xwrite(req->hPipe,&reqtype,sizeof(reqtype));
if (hres) return hres;
hres = _xwrite(req->hPipe,&(req->reqh),sizeof(req->reqh));
if (hres) return hres;
hres = _xwrite(req->hPipe,req->Buffer,req->reqh.cbBuffer);
if (hres) return hres;
while (1) {
/*WaitForSingleObject(hRpcChanged,INFINITE);*/
hres = _read_one(xpipe);
if (hres) break;
for (i=0;i<nrofreqs;i++) {
xreq = reqs[i];
if ((xreq->state==REQSTATE_REQ_GOT) && (xreq->hPipe==req->hPipe)) {
_invoke_onereq(xreq);
}
}
if (req->state == REQSTATE_RESP_GOT)
return S_OK;
}
return hres;
}
static HRESULT WINAPI
PipeBuf_SendReceive(
LPRPCCHANNELBUFFER iface,RPCOLEMESSAGE* msg,ULONG *status
) {
ICOM_THIS(PipeBuf,iface);
wine_rpc_request *req;
HRESULT hres;
TRACE("()\n");
if (This->mid.processid == GetCurrentProcessId()) {
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;
memcpy(&(req->reqh.mid),&(This->mid),sizeof(This->mid));
req->Buffer = msg->Buffer;
hres = RPC_QueueRequestAndWait(req);
if (hres) {
RPC_FreeRequest(req);
return hres;
}
msg->cbBuffer = req->resph.cbBuffer;
msg->Buffer = req->Buffer;
*status = req->resph.retval;
RPC_FreeRequest(req);
return S_OK;
}
static HRESULT WINAPI
PipeBuf_FreeBuffer(LPRPCCHANNELBUFFER iface,RPCOLEMESSAGE* msg) {
FIXME("(%p), stub!\n",msg);
return E_FAIL;
}
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 ICOM_VTABLE(IRpcChannelBuffer) pipebufvt = {
ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
PipeBuf_QueryInterface,
PipeBuf_AddRef,
PipeBuf_Release,
PipeBuf_GetBuffer,
PipeBuf_SendReceive,
PipeBuf_FreeBuffer,
PipeBuf_GetDestCtx,
PipeBuf_IsConnected
};
HRESULT
PIPE_GetNewPipeBuf(wine_marshal_id *mid, IRpcChannelBuffer **pipebuf) {
wine_marshal_id ourid;
DWORD res;
HANDLE hPipe;
HRESULT hres;
PipeBuf *pbuf;
hPipe = PIPE_FindByMID(mid);
if (hPipe == INVALID_HANDLE_VALUE) {
char pipefn[200];
sprintf(pipefn,OLESTUBMGR"_%08lx",mid->processid);
hPipe = CreateFileA(
pipefn,
GENERIC_READ|GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
0,
0
);
if (hPipe == INVALID_HANDLE_VALUE) {
FIXME("Could not open named pipe %s, le is %lx\n",pipefn,GetLastError());
return E_FAIL;
}
hres = PIPE_RegisterPipe(mid, hPipe, FALSE);
if (hres) return hres;
memset(&ourid,0,sizeof(ourid));
ourid.processid = GetCurrentProcessId();
if (!WriteFile(hPipe,&ourid,sizeof(ourid),&res,NULL)||(res!=sizeof(ourid))) {
ERR("Failed writing startup mid!\n");
return E_FAIL;
}
}
pbuf = (PipeBuf*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(PipeBuf));
pbuf->lpVtbl = &pipebufvt;
pbuf->ref = 1;
memcpy(&(pbuf->mid),mid,sizeof(*mid));
*pipebuf = (IRpcChannelBuffer*)pbuf;
return S_OK;
}
static HRESULT
create_server(REFCLSID rclsid) {
HKEY key;
char buf[200];
HRESULT hres = E_UNEXPECTED;
char xclsid[80];
WCHAR dllName[MAX_PATH+1];
DWORD dllNameLen = sizeof(dllName);
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)
return REGDB_E_READREGDB; /* Probably */
memset(dllName,0,sizeof(dllName));
hres= RegQueryValueExW(key,NULL,NULL,NULL,(LPBYTE)dllName,&dllNameLen);
RegCloseKey(key);
if (hres)
return REGDB_E_CLASSNOTREG; /* FIXME: check retval */
memset(&sinfo,0,sizeof(sinfo));
sinfo.cb = sizeof(sinfo);
if (!CreateProcessW(NULL,dllName,NULL,NULL,FALSE,0,NULL,NULL,&sinfo,&pinfo))
return E_FAIL;
return S_OK;
}
/* 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;
#define MAXTRIES 10000
strcpy(pipefn,PIPEPREF);
WINE_StringFromCLSID(rclsid,pipefn+strlen(PIPEPREF));
while (tries++<MAXTRIES) {
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)))
return hres;
Sleep(1000);
} else {
WARN("Could not open named pipe to broker %s, 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;
}
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);
hres = CoUnmarshalInterface(pStm,&IID_IClassFactory,ppv);
out:
IStream_Release(pStm);
return hres;
}
static void WINAPI
PIPE_StartRequestThread(HANDLE xhPipe) {
wine_marshal_id remoteid;
HRESULT hres;
hres = _xread(xhPipe,&remoteid,sizeof(remoteid));
if (hres) {
ERR("Failed to read remote mid!\n");
return;
}
PIPE_RegisterPipe(&remoteid,xhPipe, TRUE);
}
static HRESULT
_read_one(wine_pipe *xpipe) {
DWORD reqtype;
HRESULT hres = S_OK;
HANDLE xhPipe = xpipe->hPipe;
/*FIXME("%lx %d reading reqtype\n",GetCurrentProcessId(),xhPipe);*/
hres = _xread(xhPipe,&reqtype,sizeof(reqtype));
if (hres) goto end;
EnterCriticalSection(&(xpipe->crit));
/*FIXME("%lx got reqtype %ld\n",GetCurrentProcessId(),reqtype);*/
if (reqtype == REQTYPE_REQUEST) {
wine_rpc_request *xreq;
RPC_GetRequest(&xreq);
xreq->hPipe = xhPipe;
hres = _xread(xhPipe,&(xreq->reqh),sizeof(xreq->reqh));
if (hres) goto end;
xreq->resph.reqid = xreq->reqh.reqid;
xreq->Buffer = HeapAlloc(GetProcessHeap(),0, xreq->reqh.cbBuffer);
hres = _xread(xhPipe,xreq->Buffer,xreq->reqh.cbBuffer);
if (hres) goto end;
xreq->state = REQSTATE_REQ_GOT;
goto end;
}
if (reqtype == REQTYPE_RESPONSE) {
wine_rpc_response_header resph;
int i;
hres = _xread(xhPipe,&resph,sizeof(resph));
if (hres) goto end;
for (i=nrofreqs;i--;) {
wine_rpc_request *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 = _xread(xhPipe,xreq->Buffer,xreq->resph.cbBuffer);
if (hres) goto end;
xreq->state = REQSTATE_RESP_GOT;
/*PulseEvent(hRpcChanged);*/
goto end;
}
}
ERR("Did not find request for id %lx\n",resph.reqid);
hres = S_OK;
goto end;
}
ERR("Unknown reqtype %ld\n",reqtype);
hres = E_FAIL;
end:
LeaveCriticalSection(&(xpipe->crit));
return hres;
}
static DWORD WINAPI
_StubReaderThread(LPVOID param) {
wine_pipe *xpipe = (wine_pipe*)param;
HANDLE xhPipe = xpipe->hPipe;
HRESULT hres;
TRACE("STUB reader thread %lx\n",GetCurrentProcessId());
while (1) {
int i;
hres = _read_one(xpipe);
if (hres) break;
for (i=nrofreqs;i--;) {
wine_rpc_request *xreq = reqs[i];
if ((xreq->state == REQSTATE_REQ_GOT) && (xreq->hPipe == xhPipe)) {
_invoke_onereq(xreq);
}
}
}
FIXME("Failed with hres %lx\n",hres);
CloseHandle(xhPipe);
return 0;
}
static DWORD WINAPI
_StubMgrThread(LPVOID param) {
char pipefn[200];
HANDLE listenPipe;
sprintf(pipefn,OLESTUBMGR"_%08lx",GetCurrentProcessId());
TRACE("Stub Manager Thread starting on (%s)\n",pipefn);
while (1) {
listenPipe = CreateNamedPipeA(
pipefn,
PIPE_ACCESS_DUPLEX,
PIPE_TYPE_BYTE|PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES,
4096,
4096,
NMPWAIT_USE_DEFAULT_WAIT,
NULL
);
if (listenPipe == INVALID_HANDLE_VALUE) {
FIXME("pipe creation failed for %s, le is %lx\n",pipefn,GetLastError());
return 1; /* permanent failure, so quit stubmgr thread */
}
if (!ConnectNamedPipe(listenPipe,NULL)) {
ERR("Failure during ConnectNamedPipe %lx!\n",GetLastError());
CloseHandle(listenPipe);
continue;
}
PIPE_StartRequestThread(listenPipe);
}
return 0;
}
void
STUBMGR_Start() {
static BOOL stubMgrRunning = FALSE;
DWORD tid;
if (!stubMgrRunning) {
stubMgrRunning = TRUE;
CreateThread(NULL,0,_StubMgrThread,NULL,0,&tid);
Sleep(2000); /* actually we just try opening the pipe until it succeeds */
}
}