📄 rpc.c
字号:
/*
* RPC Manager
*
* Copyright 2001 Ove K鍁en, TransGaming Technologies
* 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 <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#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 "excpt.h"
#include "wine/unicode.h"
#include "wine/exception.h"
#include "compobj_private.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(ole);
static void __RPC_STUB dispatch_rpc(RPC_MESSAGE *msg);
/* we only use one function to dispatch calls for all methods - we use the
* RPC_IF_OLE flag to tell the RPC runtime that this is the case */
static RPC_DISPATCH_FUNCTION rpc_dispatch_table[1] = { dispatch_rpc }; /* (RO) */
static RPC_DISPATCH_TABLE rpc_dispatch = { 1, rpc_dispatch_table }; /* (RO) */
static struct list registered_interfaces = LIST_INIT(registered_interfaces); /* (CS csRegIf) */
static CRITICAL_SECTION csRegIf;
static CRITICAL_SECTION_DEBUG csRegIf_debug =
{
0, 0, &csRegIf,
{ &csRegIf_debug.ProcessLocksList, &csRegIf_debug.ProcessLocksList },
0, 0, { (DWORD_PTR)(__FILE__ ": dcom registered server interfaces") }
};
static CRITICAL_SECTION csRegIf = { &csRegIf_debug, -1, 0, 0, 0, 0 };
static WCHAR wszPipeTransport[] = {'n','c','a','c','n','_','n','p',0};
struct registered_if
{
struct list entry;
DWORD refs; /* ref count */
RPC_SERVER_INTERFACE If; /* interface registered with the RPC runtime */
};
/* get the pipe endpoint specified of the specified apartment */
static inline void get_rpc_endpoint(LPWSTR endpoint, const OXID *oxid)
{
/* FIXME: should get endpoint from rpcss */
static const WCHAR wszEndpointFormat[] = {'\\','p','i','p','e','\\','O','L','E','_','%','0','8','l','x','%','0','8','l','x',0};
wsprintfW(endpoint, wszEndpointFormat, (DWORD)(*oxid >> 32),(DWORD)*oxid);
}
typedef struct
{
const IRpcChannelBufferVtbl *lpVtbl;
LONG refs;
} RpcChannelBuffer;
typedef struct
{
RpcChannelBuffer super; /* superclass */
RPC_BINDING_HANDLE bind; /* handle to the remote server */
} ClientRpcChannelBuffer;
struct dispatch_params
{
RPCOLEMESSAGE *msg; /* message */
IRpcStubBuffer *stub; /* stub buffer, if applicable */
IRpcChannelBuffer *chan; /* server channel buffer, if applicable */
HANDLE handle; /* handle that will become signaled when call finishes */
RPC_STATUS status; /* status (out) */
HRESULT hr; /* hresult (out) */
};
static WINE_EXCEPTION_FILTER(ole_filter)
{
if (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION)
return EXCEPTION_CONTINUE_SEARCH;
return EXCEPTION_EXECUTE_HANDLER;
}
static HRESULT WINAPI RpcChannelBuffer_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 RpcChannelBuffer_AddRef(LPRPCCHANNELBUFFER iface)
{
RpcChannelBuffer *This = (RpcChannelBuffer *)iface;
return InterlockedIncrement(&This->refs);
}
static ULONG WINAPI ServerRpcChannelBuffer_Release(LPRPCCHANNELBUFFER iface)
{
RpcChannelBuffer *This = (RpcChannelBuffer *)iface;
ULONG ref;
ref = InterlockedDecrement(&This->refs);
if (ref)
return ref;
HeapFree(GetProcessHeap(), 0, This);
return 0;
}
static ULONG WINAPI ClientRpcChannelBuffer_Release(LPRPCCHANNELBUFFER iface)
{
ClientRpcChannelBuffer *This = (ClientRpcChannelBuffer *)iface;
ULONG ref;
ref = InterlockedDecrement(&This->super.refs);
if (ref)
return ref;
RpcBindingFree(&This->bind);
HeapFree(GetProcessHeap(), 0, This);
return 0;
}
static HRESULT WINAPI ServerRpcChannelBuffer_GetBuffer(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE* olemsg, REFIID riid)
{
RpcChannelBuffer *This = (RpcChannelBuffer *)iface;
RPC_MESSAGE *msg = (RPC_MESSAGE *)olemsg;
RPC_STATUS status;
TRACE("(%p)->(%p,%s)\n", This, olemsg, debugstr_guid(riid));
status = I_RpcGetBuffer(msg);
TRACE("-- %ld\n", status);
return HRESULT_FROM_WIN32(status);
}
static HRESULT WINAPI ClientRpcChannelBuffer_GetBuffer(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE* olemsg, REFIID riid)
{
ClientRpcChannelBuffer *This = (ClientRpcChannelBuffer *)iface;
RPC_MESSAGE *msg = (RPC_MESSAGE *)olemsg;
RPC_CLIENT_INTERFACE *cif;
RPC_STATUS status;
TRACE("(%p)->(%p,%s)\n", This, olemsg, debugstr_guid(riid));
cif = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(RPC_CLIENT_INTERFACE));
if (!cif)
return E_OUTOFMEMORY;
cif->Length = sizeof(RPC_CLIENT_INTERFACE);
/* RPC interface ID = COM interface ID */
cif->InterfaceId.SyntaxGUID = *riid;
/* COM objects always have a version of 0.0 */
cif->InterfaceId.SyntaxVersion.MajorVersion = 0;
cif->InterfaceId.SyntaxVersion.MinorVersion = 0;
msg->RpcInterfaceInformation = cif;
msg->Handle = This->bind;
status = I_RpcGetBuffer(msg);
TRACE("-- %ld\n", status);
return HRESULT_FROM_WIN32(status);
}
/* this thread runs an outgoing RPC */
static DWORD WINAPI rpc_sendreceive_thread(LPVOID param)
{
struct dispatch_params *data = (struct dispatch_params *) param;
/* FIXME: trap and rethrow RPC exceptions in app thread */
data->status = I_RpcSendReceive((RPC_MESSAGE *)data->msg);
TRACE("completed with status 0x%lx\n", data->status);
return 0;
}
static HRESULT WINAPI RpcChannelBuffer_SendReceive(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE *olemsg, ULONG *pstatus)
{
HRESULT hr = S_OK;
RPC_MESSAGE *msg = (RPC_MESSAGE *)olemsg;
RPC_STATUS status;
DWORD index;
struct dispatch_params *params;
DWORD tid;
IRpcStubBuffer *stub;
APARTMENT *apt;
IPID ipid;
TRACE("(%p) iMethod=%ld\n", olemsg, olemsg->iMethod);
params = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*params));
if (!params) return E_OUTOFMEMORY;
params->msg = olemsg;
params->status = RPC_S_OK;
params->hr = S_OK;
/* Note: this is an optimization in the Microsoft OLE runtime that we need
* to copy, as shown by the test_no_couninitialize_client test. without
* short-circuiting the RPC runtime in the case below, the test will
* deadlock on the loader lock due to the RPC runtime needing to create
* a thread to process the RPC when this function is called indirectly
* from DllMain */
RpcBindingInqObject(msg->Handle, &ipid);
stub = ipid_to_apt_and_stubbuffer(&ipid, &apt);
if (apt && (apt->model & COINIT_APARTMENTTHREADED))
{
params->stub = stub;
params->chan = NULL; /* FIXME: pass server channel */
params->handle = CreateEventW(NULL, FALSE, FALSE, NULL);
TRACE("Calling apartment thread 0x%08lx...\n", apt->tid);
PostMessageW(apt->win, DM_EXECUTERPC, 0, (LPARAM)params);
}
else
{
if (stub) IRpcStubBuffer_Release(stub);
/* we use a separate thread here because we need to be able to
* pump the message loop in the application thread: if we do not,
* any windows created by this thread will hang and RPCs that try
* and re-enter this STA from an incoming server thread will
* deadlock. InstallShield is an example of that.
*/
params->handle = CreateThread(NULL, 0, rpc_sendreceive_thread, params, 0, &tid);
if (!params->handle)
{
ERR("Could not create RpcSendReceive thread, error %lx\n", GetLastError());
hr = E_UNEXPECTED;
}
}
if (apt) apartment_release(apt);
if (hr == S_OK)
hr = CoWaitForMultipleHandles(0, INFINITE, 1, ¶ms->handle, &index);
CloseHandle(params->handle);
if (hr == S_OK) hr = params->hr;
status = params->status;
HeapFree(GetProcessHeap(), 0, params);
params = NULL;
if (hr) return hr;
if (pstatus) *pstatus = status;
TRACE("RPC call status: 0x%lx\n", status);
if (status == RPC_S_OK)
hr = S_OK;
else if (status == RPC_S_CALL_FAILED)
hr = *(HRESULT *)olemsg->Buffer;
else
hr = HRESULT_FROM_WIN32(status);
TRACE("-- 0x%08lx\n", hr);
return hr;
}
static HRESULT WINAPI ServerRpcChannelBuffer_FreeBuffer(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE* olemsg)
{
RPC_MESSAGE *msg = (RPC_MESSAGE *)olemsg;
RPC_STATUS status;
TRACE("(%p)\n", msg);
status = I_RpcFreeBuffer(msg);
TRACE("-- %ld\n", status);
return HRESULT_FROM_WIN32(status);
}
static HRESULT WINAPI ClientRpcChannelBuffer_FreeBuffer(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE* olemsg)
{
RPC_MESSAGE *msg = (RPC_MESSAGE *)olemsg;
RPC_STATUS status;
TRACE("(%p)\n", msg);
status = I_RpcFreeBuffer(msg);
HeapFree(GetProcessHeap(), 0, msg->RpcInterfaceInformation);
msg->RpcInterfaceInformation = NULL;
TRACE("-- %ld\n", status);
return HRESULT_FROM_WIN32(status);
}
static HRESULT WINAPI RpcChannelBuffer_GetDestCtx(LPRPCCHANNELBUFFER iface, DWORD* pdwDestContext, void** ppvDestContext)
{
FIXME("(%p,%p), stub!\n", pdwDestContext, ppvDestContext);
return E_FAIL;
}
static HRESULT WINAPI RpcChannelBuffer_IsConnected(LPRPCCHANNELBUFFER iface)
{
TRACE("()\n");
/* native does nothing too */
return S_OK;
}
static const IRpcChannelBufferVtbl ClientRpcChannelBufferVtbl =
{
RpcChannelBuffer_QueryInterface,
RpcChannelBuffer_AddRef,
ClientRpcChannelBuffer_Release,
ClientRpcChannelBuffer_GetBuffer,
RpcChannelBuffer_SendReceive,
ClientRpcChannelBuffer_FreeBuffer,
RpcChannelBuffer_GetDestCtx,
RpcChannelBuffer_IsConnected
};
static const IRpcChannelBufferVtbl ServerRpcChannelBufferVtbl =
{
RpcChannelBuffer_QueryInterface,
RpcChannelBuffer_AddRef,
ServerRpcChannelBuffer_Release,
ServerRpcChannelBuffer_GetBuffer,
RpcChannelBuffer_SendReceive,
ServerRpcChannelBuffer_FreeBuffer,
RpcChannelBuffer_GetDestCtx,
RpcChannelBuffer_IsConnected
};
/* returns a channel buffer for proxies */
HRESULT RPC_CreateClientChannel(const OXID *oxid, const IPID *ipid, IRpcChannelBuffer **chan)
{
ClientRpcChannelBuffer *This;
WCHAR endpoint[200];
RPC_BINDING_HANDLE bind;
RPC_STATUS status;
LPWSTR string_binding;
/* connect to the apartment listener thread */
get_rpc_endpoint(endpoint, oxid);
TRACE("proxy pipe: connecting to endpoint: %s\n", debugstr_w(endpoint));
status = RpcStringBindingComposeW(
NULL,
wszPipeTransport,
NULL,
endpoint,
NULL,
&string_binding);
if (status == RPC_S_OK)
{
status = RpcBindingFromStringBindingW(string_binding, &bind);
if (status == RPC_S_OK)
{
IPID ipid2 = *ipid; /* why can't RpcBindingSetObject take a const? */
status = RpcBindingSetObject(bind, &ipid2);
if (status != RPC_S_OK)
RpcBindingFree(&bind);
}
RpcStringFreeW(&string_binding);
}
if (status != RPC_S_OK)
{
ERR("Couldn't get binding for endpoint %s, status = %ld\n", debugstr_w(endpoint), status);
return HRESULT_FROM_WIN32(status);
}
This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
if (!This)
{
RpcBindingFree(&bind);
return E_OUTOFMEMORY;
}
This->super.lpVtbl = &ClientRpcChannelBufferVtbl;
This->super.refs = 1;
This->bind = bind;
*chan = (IRpcChannelBuffer*)This;
return S_OK;
}
HRESULT RPC_CreateServerChannel(IRpcChannelBuffer **chan)
{
RpcChannelBuffer *This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
if (!This)
return E_OUTOFMEMORY;
This->lpVtbl = &ServerRpcChannelBufferVtbl;
This->refs = 1;
*chan = (IRpcChannelBuffer*)This;
return S_OK;
}
void RPC_ExecuteCall(struct dispatch_params *params)
{
__TRY
{
params->hr = IRpcStubBuffer_Invoke(params->stub, params->msg, params->chan);
}
__EXCEPT(ole_filter)
{
params->hr = GetExceptionCode();
}
__ENDTRY
IRpcStubBuffer_Release(params->stub);
if (params->handle) SetEvent(params->handle);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -