📄 rpc.c
字号:
static void __RPC_STUB dispatch_rpc(RPC_MESSAGE *msg)
{
struct dispatch_params *params;
IRpcStubBuffer *stub;
APARTMENT *apt;
IPID ipid;
RpcBindingInqObject(msg->Handle, &ipid);
TRACE("ipid = %s, iMethod = %d\n", debugstr_guid(&ipid), msg->ProcNum);
params = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*params));
if (!params) return RpcRaiseException(E_OUTOFMEMORY);
stub = ipid_to_apt_and_stubbuffer(&ipid, &apt);
if (!apt || !stub)
{
if (apt) apartment_release(apt);
ERR("no apartment found for ipid %s\n", debugstr_guid(&ipid));
return RpcRaiseException(RPC_E_DISCONNECTED);
}
params->msg = (RPCOLEMESSAGE *)msg;
params->stub = stub;
params->chan = NULL; /* FIXME: pass server channel */
params->status = RPC_S_OK;
/* Note: this is the important difference between STAs and MTAs - we
* always execute RPCs to STAs in the thread that originally created the
* apartment (i.e. the one that pumps messages to the window) */
if (apt->model & COINIT_APARTMENTTHREADED)
{
params->handle = CreateEventW(NULL, FALSE, FALSE, NULL);
TRACE("Calling apartment thread 0x%08lx...\n", apt->tid);
PostMessageW(apt->win, DM_EXECUTERPC, 0, (LPARAM)params);
WaitForSingleObject(params->handle, INFINITE);
CloseHandle(params->handle);
}
else
RPC_ExecuteCall(params);
HeapFree(GetProcessHeap(), 0, params);
apartment_release(apt);
}
/* stub registration */
HRESULT RPC_RegisterInterface(REFIID riid)
{
struct registered_if *rif;
BOOL found = FALSE;
HRESULT hr = S_OK;
TRACE("(%s)\n", debugstr_guid(riid));
EnterCriticalSection(&csRegIf);
LIST_FOR_EACH_ENTRY(rif, ®istered_interfaces, struct registered_if, entry)
{
if (IsEqualGUID(&rif->If.InterfaceId.SyntaxGUID, riid))
{
rif->refs++;
found = TRUE;
break;
}
}
if (!found)
{
TRACE("Creating new interface\n");
rif = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*rif));
if (rif)
{
RPC_STATUS status;
rif->refs = 1;
rif->If.Length = sizeof(RPC_SERVER_INTERFACE);
/* RPC interface ID = COM interface ID */
rif->If.InterfaceId.SyntaxGUID = *riid;
rif->If.DispatchTable = &rpc_dispatch;
/* all other fields are 0, including the version asCOM objects
* always have a version of 0.0 */
status = RpcServerRegisterIfEx(
(RPC_IF_HANDLE)&rif->If,
NULL, NULL,
RPC_IF_OLE | RPC_IF_AUTOLISTEN,
RPC_C_LISTEN_MAX_CALLS_DEFAULT,
NULL);
if (status == RPC_S_OK)
list_add_tail(®istered_interfaces, &rif->entry);
else
{
ERR("RpcServerRegisterIfEx failed with error %ld\n", status);
HeapFree(GetProcessHeap(), 0, rif);
hr = HRESULT_FROM_WIN32(status);
}
}
else
hr = E_OUTOFMEMORY;
}
LeaveCriticalSection(&csRegIf);
return hr;
}
/* stub unregistration */
void RPC_UnregisterInterface(REFIID riid)
{
struct registered_if *rif;
EnterCriticalSection(&csRegIf);
LIST_FOR_EACH_ENTRY(rif, ®istered_interfaces, struct registered_if, entry)
{
if (IsEqualGUID(&rif->If.InterfaceId.SyntaxGUID, riid))
{
if (!--rif->refs)
{
#if 0 /* this is a stub in builtin and spams the console with FIXME's */
IID iid = *riid; /* RpcServerUnregisterIf doesn't take const IID */
RpcServerUnregisterIf((RPC_IF_HANDLE)&rif->If, &iid, 0);
list_remove(&rif->entry);
HeapFree(GetProcessHeap(), 0, rif);
#endif
}
break;
}
}
LeaveCriticalSection(&csRegIf);
}
/* make the apartment reachable by other threads and processes and create the
* IRemUnknown object */
void RPC_StartRemoting(struct apartment *apt)
{
if (!InterlockedExchange(&apt->remoting_started, TRUE))
{
WCHAR endpoint[200];
RPC_STATUS status;
get_rpc_endpoint(endpoint, &apt->oxid);
status = RpcServerUseProtseqEpW(
wszPipeTransport,
RPC_C_PROTSEQ_MAX_REQS_DEFAULT,
endpoint,
NULL);
if (status != RPC_S_OK)
ERR("Couldn't register endpoint %s\n", debugstr_w(endpoint));
/* FIXME: move remote unknown exporting into this function */
}
start_apartment_remote_unknown();
}
static HRESULT create_server(REFCLSID rclsid)
{
static const WCHAR wszLocalServer32[] = { 'L','o','c','a','l','S','e','r','v','e','r','3','2',0 };
static const WCHAR embedding[] = { ' ', '-','E','m','b','e','d','d','i','n','g',0 };
HKEY key;
HRESULT hres;
WCHAR command[MAX_PATH+sizeof(embedding)/sizeof(WCHAR)];
DWORD size = (MAX_PATH+1) * sizeof(WCHAR);
STARTUPINFOW sinfo;
PROCESS_INFORMATION pinfo;
hres = COM_OpenKeyForCLSID(rclsid, wszLocalServer32, KEY_READ, &key);
if (FAILED(hres)) {
ERR("class %s not registered\n", debugstr_guid(rclsid));
return hres;
}
hres = RegQueryValueExW(key, NULL, NULL, NULL, (LPBYTE)command, &size);
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. */
strcatW(command, embedding);
TRACE("activating local server %s for %s\n", debugstr_w(command), debugstr_guid(rclsid));
/* FIXME: Win2003 supports a ServerExecutable value that is passed into
* CreateProcess */
if (!CreateProcessW(NULL, command, NULL, NULL, FALSE, 0, NULL, NULL, &sinfo, &pinfo)) {
WARN("failed to run local server %s\n", debugstr_w(command));
return HRESULT_FROM_WIN32(GetLastError());
}
CloseHandle(pinfo.hProcess);
CloseHandle(pinfo.hThread);
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;
WCHAR buf[CHARS_IN_GUID], keyname[50];
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 */
hres = COM_OpenKeyForCLSID(rclsid, szAppId, KEY_READ, &hkey);
if (FAILED(hres))
return hres;
sz = sizeof buf;
r = RegQueryValueExW(hkey, NULL, 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;
}
static void get_localserver_pipe_name(WCHAR *pipefn, REFCLSID rclsid)
{
static const WCHAR wszPipeRef[] = {'\\','\\','.','\\','p','i','p','e','\\',0};
strcpyW(pipefn, wszPipeRef);
StringFromGUID2(rclsid, pipefn + sizeof(wszPipeRef)/sizeof(wszPipeRef[0]) - 1, CHARS_IN_GUID);
}
/* FIXME: should call to rpcss instead */
HRESULT RPC_GetLocalClassObject(REFCLSID rclsid, REFIID iid, LPVOID *ppv)
{
HRESULT hres;
HANDLE hPipe;
WCHAR pipefn[100];
DWORD res, bufferlen;
char marshalbuffer[200];
IStream *pStm;
LARGE_INTEGER seekto;
ULARGE_INTEGER newpos;
int tries = 0;
static const int MAXTRIES = 30; /* 30 seconds */
TRACE("rclsid=%s, iid=%s\n", debugstr_guid(rclsid), debugstr_guid(iid));
get_localserver_pipe_name(pipefn, rclsid);
while (tries++ < MAXTRIES) {
TRACE("waiting for %s\n", debugstr_w(pipefn));
WaitNamedPipeW( pipefn, NMPWAIT_WAIT_FOREVER );
hPipe = CreateFileW(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", debugstr_w(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;
}
struct local_server_params
{
CLSID clsid;
IStream *stream;
};
/* FIXME: should call to rpcss instead */
static DWORD WINAPI local_server_thread(LPVOID param)
{
struct local_server_params * lsp = (struct local_server_params *)param;
HANDLE hPipe;
WCHAR pipefn[100];
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));
get_localserver_pipe_name(pipefn, &lsp->clsid);
HeapFree(GetProcessHeap(), 0, lsp);
hPipe = CreateNamedPipeW( 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", debugstr_w(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 */
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -