rpc.c 30.1 KB
Newer Older
1
/*
2
 *	RPC Manager
3
 *
4
 * Copyright 2001  Ove Kven, TransGaming Technologies
5 6
 * Copyright 2002  Marcus Meissner
 * Copyright 2005  Mike Hearn, Rob Shearman for CodeWeavers
7 8 9 10 11 12 13 14 15 16 17 18 19 20
 *
 * 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
21 22 23
 */

#include "config.h"
24
#include "wine/port.h"
25 26

#include <stdlib.h>
27
#include <stdarg.h>
28 29 30 31
#include <stdio.h>
#include <string.h>
#include <assert.h>

32
#define COBJMACROS
33 34
#define NONAMELESSUNION
#define NONAMELESSSTRUCT
35

36
#include "windef.h"
37
#include "winbase.h"
38
#include "winuser.h"
39
#include "winsvc.h"
40 41 42 43 44 45
#include "objbase.h"
#include "ole2.h"
#include "rpc.h"
#include "winerror.h"
#include "winreg.h"
#include "wtypes.h"
46
#include "excpt.h"
47
#include "wine/unicode.h"
48
#include "wine/exception.h"
49 50 51

#include "compobj_private.h"

52
#include "wine/debug.h"
53

54
WINE_DEFAULT_DEBUG_CHANNEL(ole);
55

56
static void __RPC_STUB dispatch_rpc(RPC_MESSAGE *msg);
57

58 59 60 61
/* 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) */
62

63 64 65
static struct list registered_interfaces = LIST_INIT(registered_interfaces); /* (CS csRegIf) */
static CRITICAL_SECTION csRegIf;
static CRITICAL_SECTION_DEBUG csRegIf_debug =
66
{
67 68
    0, 0, &csRegIf,
    { &csRegIf_debug.ProcessLocksList, &csRegIf_debug.ProcessLocksList },
69
      0, 0, { (DWORD_PTR)(__FILE__ ": dcom registered server interfaces") }
70
};
71 72
static CRITICAL_SECTION csRegIf = { &csRegIf_debug, -1, 0, 0, 0, 0 };

73
static WCHAR wszRpcTransport[] = {'n','c','a','l','r','p','c',0};
74

75

76
struct registered_if
77
{
78 79 80
    struct list entry;
    DWORD refs; /* ref count */
    RPC_SERVER_INTERFACE If; /* interface registered with the RPC runtime */
81
};
82

83 84 85 86 87 88 89
/* 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);
}
90

91 92 93
typedef struct
{
    const IRpcChannelBufferVtbl *lpVtbl;
94
    LONG                  refs;
95
} RpcChannelBuffer;
96

97
typedef struct
98
{
99
    RpcChannelBuffer       super; /* superclass */
100

101
    RPC_BINDING_HANDLE     bind; /* handle to the remote server */
102
    OXID                   oxid; /* apartment in which the channel is valid */
103 104
    DWORD                  dest_context; /* returned from GetDestCtx */
    LPVOID                 dest_context_data; /* returned from GetDestCtx */
105
} ClientRpcChannelBuffer;
106

107 108 109 110 111 112 113
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) */
114
    HRESULT            hr; /* hresult (out) */
115 116
};

117 118 119 120 121 122 123
static WINE_EXCEPTION_FILTER(ole_filter)
{
    if (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION)
        return EXCEPTION_CONTINUE_SEARCH;
    return EXCEPTION_EXECUTE_HANDLER;
}

124
static HRESULT WINAPI RpcChannelBuffer_QueryInterface(LPRPCCHANNELBUFFER iface, REFIID riid, LPVOID *ppv)
125
{
126 127 128 129 130 131 132 133 134
    *ppv = NULL;
    if (IsEqualIID(riid,&IID_IRpcChannelBuffer) || IsEqualIID(riid,&IID_IUnknown))
    {
        *ppv = (LPVOID)iface;
        IUnknown_AddRef(iface);
        return S_OK;
    }
    return E_NOINTERFACE;
}
135

136 137 138 139 140
static ULONG WINAPI RpcChannelBuffer_AddRef(LPRPCCHANNELBUFFER iface)
{
    RpcChannelBuffer *This = (RpcChannelBuffer *)iface;
    return InterlockedIncrement(&This->refs);
}
141

142 143 144 145
static ULONG WINAPI ServerRpcChannelBuffer_Release(LPRPCCHANNELBUFFER iface)
{
    RpcChannelBuffer *This = (RpcChannelBuffer *)iface;
    ULONG ref;
146

147 148 149
    ref = InterlockedDecrement(&This->refs);
    if (ref)
        return ref;
150

151 152 153
    HeapFree(GetProcessHeap(), 0, This);
    return 0;
}
154

155 156 157 158
static ULONG WINAPI ClientRpcChannelBuffer_Release(LPRPCCHANNELBUFFER iface)
{
    ClientRpcChannelBuffer *This = (ClientRpcChannelBuffer *)iface;
    ULONG ref;
159

160 161 162
    ref = InterlockedDecrement(&This->super.refs);
    if (ref)
        return ref;
163

164 165 166 167
    RpcBindingFree(&This->bind);
    HeapFree(GetProcessHeap(), 0, This);
    return 0;
}
168

169
static HRESULT WINAPI ServerRpcChannelBuffer_GetBuffer(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE* olemsg, REFIID riid)
170
{
171 172 173 174
    RpcChannelBuffer *This = (RpcChannelBuffer *)iface;
    RPC_MESSAGE *msg = (RPC_MESSAGE *)olemsg;
    RPC_STATUS status;

175
    TRACE("(%p)->(%p,%s)\n", This, olemsg, debugstr_guid(riid));
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190

    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;

191
    TRACE("(%p)->(%p,%s)\n", This, olemsg, debugstr_guid(riid));
192 193 194 195 196 197 198 199 200 201 202 203 204

    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;
205
    
206 207 208 209 210
    status = I_RpcGetBuffer(msg);

    TRACE("-- %ld\n", status);

    return HRESULT_FROM_WIN32(status);
211 212
}

213 214 215 216 217 218
static HRESULT WINAPI ServerRpcChannelBuffer_SendReceive(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE *olemsg, ULONG *pstatus)
{
    FIXME("stub\n");
    return E_NOTIMPL;
}

219 220 221
/* this thread runs an outgoing RPC */
static DWORD WINAPI rpc_sendreceive_thread(LPVOID param)
{
222
    struct dispatch_params *data = (struct dispatch_params *) param;
223

224
    /* FIXME: trap and rethrow RPC exceptions in app thread */
225
    data->status = I_RpcSendReceive((RPC_MESSAGE *)data->msg);
226 227

    TRACE("completed with status 0x%lx\n", data->status);
228 229 230

    SetEvent(data->handle);

231 232 233
    return 0;
}

234 235 236 237 238 239 240 241 242 243 244 245 246
static inline HRESULT ClientRpcChannelBuffer_IsCorrectApartment(ClientRpcChannelBuffer *This, APARTMENT *apt)
{
    OXID oxid;
    if (!apt)
        return S_FALSE;
    if (apartment_getoxid(apt, &oxid) != S_OK)
        return S_FALSE;
    if (This->oxid != oxid)
        return S_FALSE;
    return S_OK;
}

static HRESULT WINAPI ClientRpcChannelBuffer_SendReceive(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE *olemsg, ULONG *pstatus)
247
{
248 249
    ClientRpcChannelBuffer *This = (ClientRpcChannelBuffer *)iface;
    HRESULT hr;
250
    RPC_MESSAGE *msg = (RPC_MESSAGE *)olemsg;
251
    RPC_STATUS status;
252 253
    DWORD index;
    struct dispatch_params *params;
254
    APARTMENT *apt = NULL;
255 256
    IPID ipid;

257
    TRACE("(%p) iMethod=%ld\n", olemsg, olemsg->iMethod);
258

259 260 261 262 263 264 265 266
    hr = ClientRpcChannelBuffer_IsCorrectApartment(This, COM_CurrentApt());
    if (hr != S_OK)
    {
        ERR("called from wrong apartment, should have been 0x%s\n",
            wine_dbgstr_longlong(This->oxid));
        return RPC_E_WRONG_THREAD;
    }

267
    params = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*params));
268
    if (!params) return E_OUTOFMEMORY;
269

270 271
    params->msg = olemsg;
    params->status = RPC_S_OK;
272
    params->hr = S_OK;
273

274 275 276 277 278 279 280 281
    /* 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);
282
    hr = ipid_get_dispatch_params(&ipid, &apt, &params->stub, &params->chan);
283
    params->handle = CreateEventW(NULL, FALSE, FALSE, NULL);
284
    if ((hr == S_OK) && !apt->multi_threaded)
285 286 287
    {
        TRACE("Calling apartment thread 0x%08lx...\n", apt->tid);

288
        if (!PostMessageW(apartment_getwindow(apt), DM_EXECUTERPC, 0, (LPARAM)params))
289 290 291 292
        {
            ERR("PostMessage failed with error %ld\n", GetLastError());
            hr = HRESULT_FROM_WIN32(GetLastError());
        }
293 294
    }
    else
295
    {
296 297 298 299 300 301 302 303 304
        if (hr == S_OK)
        {
            /* otherwise, we go via RPC runtime so the stub and channel aren't
             * needed here */
            IRpcStubBuffer_Release(params->stub);
            params->stub = NULL;
            IRpcChannelBuffer_Release(params->chan);
            params->chan = NULL;
        }
305 306 307 308 309 310 311

        /* 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.
         */
312
        if (!QueueUserWorkItem(rpc_sendreceive_thread, params, WT_EXECUTEDEFAULT))
313
        {
314
            ERR("QueueUserWorkItem failed with error %lx\n", GetLastError());
315 316
            hr = E_UNEXPECTED;
        }
317 318
        else
            hr = S_OK;
319
    }
320
    if (apt) apartment_release(apt);
321

322
    if (hr == S_OK)
323 324 325 326
    {
        if (WaitForSingleObject(params->handle, 0))
            hr = CoWaitForMultipleHandles(0, INFINITE, 1, &params->handle, &index);
    }
327
    CloseHandle(params->handle);
328

329 330
    if (hr == S_OK) hr = params->hr;

331 332 333
    status = params->status;
    HeapFree(GetProcessHeap(), 0, params);
    params = NULL;
334

335 336
    if (hr) return hr;
    
337 338
    if (pstatus) *pstatus = status;

339
    TRACE("RPC call status: 0x%lx\n", status);
340 341 342
    if (status == RPC_S_OK)
        hr = S_OK;
    else if (status == RPC_S_CALL_FAILED)
343
        hr = *(HRESULT *)olemsg->Buffer;
344 345 346 347 348 349
    else
        hr = HRESULT_FROM_WIN32(status);

    TRACE("-- 0x%08lx\n", hr);

    return hr;
350 351
}

352
static HRESULT WINAPI ServerRpcChannelBuffer_FreeBuffer(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE* olemsg)
353
{
354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380
    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);
381 382
}

383 384 385 386 387 388 389 390 391 392 393 394 395
static HRESULT WINAPI ClientRpcChannelBuffer_GetDestCtx(LPRPCCHANNELBUFFER iface, DWORD* pdwDestContext, void** ppvDestContext)
{
    ClientRpcChannelBuffer *This = (ClientRpcChannelBuffer *)iface;

    TRACE("(%p,%p)\n", pdwDestContext, ppvDestContext);

    *pdwDestContext = This->dest_context;
    *ppvDestContext = This->dest_context_data;

    return S_OK;
}

static HRESULT WINAPI ServerRpcChannelBuffer_GetDestCtx(LPRPCCHANNELBUFFER iface, DWORD* pdwDestContext, void** ppvDestContext)
396 397 398 399
{
    FIXME("(%p,%p), stub!\n", pdwDestContext, ppvDestContext);
    return E_FAIL;
}
400

401 402 403 404 405 406
static HRESULT WINAPI RpcChannelBuffer_IsConnected(LPRPCCHANNELBUFFER iface)
{
    TRACE("()\n");
    /* native does nothing too */
    return S_OK;
}
407

408 409 410 411 412 413
static const IRpcChannelBufferVtbl ClientRpcChannelBufferVtbl =
{
    RpcChannelBuffer_QueryInterface,
    RpcChannelBuffer_AddRef,
    ClientRpcChannelBuffer_Release,
    ClientRpcChannelBuffer_GetBuffer,
414
    ClientRpcChannelBuffer_SendReceive,
415
    ClientRpcChannelBuffer_FreeBuffer,
416
    ClientRpcChannelBuffer_GetDestCtx,
417 418
    RpcChannelBuffer_IsConnected
};
419

420 421 422 423 424 425
static const IRpcChannelBufferVtbl ServerRpcChannelBufferVtbl =
{
    RpcChannelBuffer_QueryInterface,
    RpcChannelBuffer_AddRef,
    ServerRpcChannelBuffer_Release,
    ServerRpcChannelBuffer_GetBuffer,
426
    ServerRpcChannelBuffer_SendReceive,
427
    ServerRpcChannelBuffer_FreeBuffer,
428
    ServerRpcChannelBuffer_GetDestCtx,
429 430
    RpcChannelBuffer_IsConnected
};
431

432
/* returns a channel buffer for proxies */
433 434 435
HRESULT RPC_CreateClientChannel(const OXID *oxid, const IPID *ipid,
                                DWORD dest_context, void *dest_context_data,
                                IRpcChannelBuffer **chan)
436
{
437 438 439 440 441 442 443
    ClientRpcChannelBuffer *This;
    WCHAR                   endpoint[200];
    RPC_BINDING_HANDLE      bind;
    RPC_STATUS              status;
    LPWSTR                  string_binding;

    /* connect to the apartment listener thread */
444
    get_rpc_endpoint(endpoint, oxid);
445

446 447 448 449
    TRACE("proxy pipe: connecting to endpoint: %s\n", debugstr_w(endpoint));

    status = RpcStringBindingComposeW(
        NULL,
450
        wszRpcTransport,
451 452 453 454 455 456
        NULL,
        endpoint,
        NULL,
        &string_binding);
        
    if (status == RPC_S_OK)
457
    {
458 459 460
        status = RpcBindingFromStringBindingW(string_binding, &bind);

        if (status == RPC_S_OK)
461
        {
462 463
            IPID ipid2 = *ipid; /* why can't RpcBindingSetObject take a const? */
            status = RpcBindingSetObject(bind, &ipid2);
464 465
            if (status != RPC_S_OK)
                RpcBindingFree(&bind);
466
        }
467 468

        RpcStringFreeW(&string_binding);
469 470
    }

471 472 473 474
    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);
475 476
    }

477 478 479 480 481 482
    This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
    if (!This)
    {
        RpcBindingFree(&bind);
        return E_OUTOFMEMORY;
    }
483

484 485 486
    This->super.lpVtbl = &ClientRpcChannelBufferVtbl;
    This->super.refs = 1;
    This->bind = bind;
487
    apartment_getoxid(COM_CurrentApt(), &This->oxid);
488 489
    This->dest_context = dest_context;
    This->dest_context_data = dest_context_data;
490

491
    *chan = (IRpcChannelBuffer*)This;
492

493
    return S_OK;
494 495
}

496
HRESULT RPC_CreateServerChannel(IRpcChannelBuffer **chan)
497
{
498 499 500 501 502 503 504 505 506
    RpcChannelBuffer *This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
    if (!This)
        return E_OUTOFMEMORY;

    This->lpVtbl = &ServerRpcChannelBufferVtbl;
    This->refs = 1;
    
    *chan = (IRpcChannelBuffer*)This;

507 508 509
    return S_OK;
}

510

511
void RPC_ExecuteCall(struct dispatch_params *params)
512
{
513 514 515 516
    __TRY
    {
        params->hr = IRpcStubBuffer_Invoke(params->stub, params->msg, params->chan);
    }
517
    __EXCEPT(ole_filter)
518 519 520 521
    {
        params->hr = GetExceptionCode();
    }
    __ENDTRY
522
    IRpcStubBuffer_Release(params->stub);
523
    IRpcChannelBuffer_Release(params->chan);
524
    if (params->handle) SetEvent(params->handle);
525 526
}

527 528
static void __RPC_STUB dispatch_rpc(RPC_MESSAGE *msg)
{
529
    struct dispatch_params *params;
530 531 532
    APARTMENT *apt;
    IPID ipid;
    HRESULT hr;
533

534
    RpcBindingInqObject(msg->Handle, &ipid);
535

536 537
    TRACE("ipid = %s, iMethod = %d\n", debugstr_guid(&ipid), msg->ProcNum);

538
    params = HeapAlloc(GetProcessHeap(), 0, sizeof(*params));
539 540
    if (!params) return RpcRaiseException(E_OUTOFMEMORY);

541 542
    hr = ipid_get_dispatch_params(&ipid, &apt, &params->stub, &params->chan);
    if (hr != S_OK)
543
    {
544
        ERR("no apartment found for ipid %s\n", debugstr_guid(&ipid));
545
        return RpcRaiseException(hr);
546 547
    }

548 549
    params->msg = (RPCOLEMESSAGE *)msg;
    params->status = RPC_S_OK;
550 551
    params->hr = S_OK;
    params->handle = NULL;
552

553 554 555
    /* 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) */
556
    if (!apt->multi_threaded)
557 558 559 560 561
    {
        params->handle = CreateEventW(NULL, FALSE, FALSE, NULL);

        TRACE("Calling apartment thread 0x%08lx...\n", apt->tid);

562
        if (PostMessageW(apartment_getwindow(apt), DM_EXECUTERPC, 0, (LPARAM)params))
563 564 565 566 567 568 569
            WaitForSingleObject(params->handle, INFINITE);
        else
        {
            ERR("PostMessage failed with error %ld\n", GetLastError());
            IRpcChannelBuffer_Release(params->chan);
            IRpcStubBuffer_Release(params->stub);
        }
570 571
        CloseHandle(params->handle);
    }
572
    else
573 574 575 576 577 578 579
    {
        BOOL joined = FALSE;
        if (!COM_CurrentInfo()->apt)
        {
            apartment_joinmta();
            joined = TRUE;
        }
580
        RPC_ExecuteCall(params);
581 582 583 584 585 586
        if (joined)
        {
            apartment_release(COM_CurrentInfo()->apt);
            COM_CurrentInfo()->apt = NULL;
        }
    }
587 588

    HeapFree(GetProcessHeap(), 0, params);
589

590
    apartment_release(apt);
591 592
}

593 594
/* stub registration */
HRESULT RPC_RegisterInterface(REFIID riid)
595
{
596 597 598 599 600
    struct registered_if *rif;
    BOOL found = FALSE;
    HRESULT hr = S_OK;
    
    TRACE("(%s)\n", debugstr_guid(riid));
601

602 603
    EnterCriticalSection(&csRegIf);
    LIST_FOR_EACH_ENTRY(rif, &registered_interfaces, struct registered_if, entry)
604
    {
605 606 607 608 609 610
        if (IsEqualGUID(&rif->If.InterfaceId.SyntaxGUID, riid))
        {
            rif->refs++;
            found = TRUE;
            break;
        }
611
    }
612 613 614
    if (!found)
    {
        TRACE("Creating new interface\n");
615

616
        rif = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*rif));
617 618 619 620 621 622 623 624 625
        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;
626 627
            /* all other fields are 0, including the version asCOM objects
             * always have a version of 0.0 */
628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647
            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(&registered_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;
648 649
}

650 651
/* stub unregistration */
void RPC_UnregisterInterface(REFIID riid)
652
{
653 654 655
    struct registered_if *rif;
    EnterCriticalSection(&csRegIf);
    LIST_FOR_EACH_ENTRY(rif, &registered_interfaces, struct registered_if, entry)
656
    {
657
        if (IsEqualGUID(&rif->If.InterfaceId.SyntaxGUID, riid))
658
        {
659 660 661 662 663 664 665
            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);
666
#endif
667 668
            }
            break;
669
        }
670 671 672
    }
    LeaveCriticalSection(&csRegIf);
}
673

674 675 676
/* make the apartment reachable by other threads and processes and create the
 * IRemUnknown object */
void RPC_StartRemoting(struct apartment *apt)
677
{
678
    if (!InterlockedExchange(&apt->remoting_started, TRUE))
679 680 681
    {
        WCHAR endpoint[200];
        RPC_STATUS status;
682

683
        get_rpc_endpoint(endpoint, &apt->oxid);
684
    
685
        status = RpcServerUseProtseqEpW(
686
            wszRpcTransport,
687 688 689 690 691
            RPC_C_PROTSEQ_MAX_REQS_DEFAULT,
            endpoint,
            NULL);
        if (status != RPC_S_OK)
            ERR("Couldn't register endpoint %s\n", debugstr_w(endpoint));
692 693

        /* FIXME: move remote unknown exporting into this function */
694
    }
695
    start_apartment_remote_unknown();
696 697
}

698

699
static HRESULT create_server(REFCLSID rclsid)
700
{
701
    static const WCHAR  wszLocalServer32[] = { 'L','o','c','a','l','S','e','r','v','e','r','3','2',0 };
702 703
    static const WCHAR  embedding[] = { ' ', '-','E','m','b','e','d','d','i','n','g',0 };
    HKEY                key;
704
    HRESULT             hres;
705
    WCHAR               command[MAX_PATH+sizeof(embedding)/sizeof(WCHAR)];
706
    DWORD               size = (MAX_PATH+1) * sizeof(WCHAR);
707 708 709
    STARTUPINFOW        sinfo;
    PROCESS_INFORMATION pinfo;

710 711
    hres = COM_OpenKeyForCLSID(rclsid, wszLocalServer32, KEY_READ, &key);
    if (FAILED(hres)) {
712
        ERR("class %s not registered\n", debugstr_guid(rclsid));
713
        return hres;
714
    }
715

716
    hres = RegQueryValueExW(key, NULL, NULL, NULL, (LPBYTE)command, &size);
717 718 719 720 721
    RegCloseKey(key);
    if (hres) {
        WARN("No default value for LocalServer32 key\n");
        return REGDB_E_CLASSNOTREG; /* FIXME: check retval */
    }
722

723 724
    memset(&sinfo,0,sizeof(sinfo));
    sinfo.cb = sizeof(sinfo);
725

726
    /* EXE servers are started with the -Embedding switch. */
727

728
    strcatW(command, embedding);
729

730
    TRACE("activating local server %s for %s\n", debugstr_w(command), debugstr_guid(rclsid));
731

732 733 734 735
    /* 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));
736
        return HRESULT_FROM_WIN32(GetLastError());
737
    }
738 739
    CloseHandle(pinfo.hProcess);
    CloseHandle(pinfo.hThread);
740

741
    return S_OK;
742
}
743 744 745 746

/*
 * start_local_service()  - start a service given its name and parameters
 */
747
static DWORD start_local_service(LPCWSTR name, DWORD num, LPCWSTR *params)
748 749
{
    SC_HANDLE handle, hsvc;
750
    DWORD     r = ERROR_FUNCTION_FAILED;
751 752 753

    TRACE("Starting service %s %ld params\n", debugstr_w(name), num);

754
    handle = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
755 756
    if (!handle)
        return r;
757
    hsvc = OpenServiceW(handle, name, SERVICE_START);
758 759
    if (hsvc)
    {
760
        if(StartServiceW(hsvc, num, params))
761 762 763
            r = ERROR_SUCCESS;
        else
            r = GetLastError();
764
        if (r == ERROR_SERVICE_ALREADY_RUNNING)
765 766 767
            r = ERROR_SUCCESS;
        CloseServiceHandle(hsvc);
    }
768 769
    else
        r = GetLastError();
770 771
    CloseServiceHandle(handle);

772
    TRACE("StartService returned error %ld (%s)\n", r, (r == ERROR_SUCCESS) ? "ok":"failed");
773 774 775 776 777 778 779 780 781 782 783 784 785

    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
 */
786
static HRESULT create_local_service(REFCLSID rclsid)
787
{
788
    HRESULT hres;
789
    WCHAR buf[CHARS_IN_GUID], keyname[50];
790 791
    static const WCHAR szAppId[] = { 'A','p','p','I','d',0 };
    static const WCHAR szAppIdKey[] = { 'A','p','p','I','d','\\',0 };
792 793
    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};
794 795 796 797 798 799 800
    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 */
801 802
    hres = COM_OpenKeyForCLSID(rclsid, szAppId, KEY_READ, &hkey);
    if (FAILED(hres))
803 804
        return hres;
    sz = sizeof buf;
805
    r = RegQueryValueExW(hkey, NULL, NULL, &type, (LPBYTE)buf, &sz);
806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837
    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);
        }
838
        r = start_local_service(buf, num_args, (LPCWSTR *)args);
839 840 841 842 843 844 845 846 847
        if (r==ERROR_SUCCESS)
            hres = S_OK;
        HeapFree(GetProcessHeap(),0,args[0]);
    }
    RegCloseKey(hkey);
        
    return hres;
}

848 849 850 851 852 853 854

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);
}
855 856

/* FIXME: should call to rpcss instead */
857
HRESULT RPC_GetLocalClassObject(REFCLSID rclsid, REFIID iid, LPVOID *ppv)
858 859 860
{
    HRESULT        hres;
    HANDLE         hPipe;
861
    WCHAR          pipefn[100];
862 863 864 865 866 867
    DWORD          res, bufferlen;
    char           marshalbuffer[200];
    IStream       *pStm;
    LARGE_INTEGER  seekto;
    ULARGE_INTEGER newpos;
    int            tries = 0;
868

869
    static const int MAXTRIES = 30; /* 30 seconds */
870 871 872

    TRACE("rclsid=%s, iid=%s\n", debugstr_guid(rclsid), debugstr_guid(iid));

873
    get_localserver_pipe_name(pipefn, rclsid);
874 875

    while (tries++ < MAXTRIES) {
876
        TRACE("waiting for %s\n", debugstr_w(pipefn));
877
      
878 879
        WaitNamedPipeW( pipefn, NMPWAIT_WAIT_FOREVER );
        hPipe = CreateFileW(pipefn, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
880 881
        if (hPipe == INVALID_HANDLE_VALUE) {
            if (tries == 1) {
882 883
                if ( (hres = create_local_service(rclsid)) &&
                     (hres = create_server(rclsid)) )
884 885 886
                    return hres;
                Sleep(1000);
            } else {
887
                WARN("Connecting to %s, no response yet, retrying: le is %lx\n", debugstr_w(pipefn), GetLastError());
888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914
                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);
915
out:
916 917
    IStream_Release(pStm);
    return hres;
918 919 920
}


921 922 923 924 925 926
struct local_server_params
{
    CLSID clsid;
    IStream *stream;
};

927
/* FIXME: should call to rpcss instead */
928 929 930 931
static DWORD WINAPI local_server_thread(LPVOID param)
{
    struct local_server_params * lsp = (struct local_server_params *)param;
    HANDLE		hPipe;
932
    WCHAR 		pipefn[100];
933 934 935 936 937 938 939 940 941 942 943
    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));

944
    get_localserver_pipe_name(pipefn, &lsp->clsid);
945 946 947

    HeapFree(GetProcessHeap(), 0, lsp);

948
    hPipe = CreateNamedPipeW( pipefn, PIPE_ACCESS_DUPLEX,
949 950 951 952 953
                              PIPE_TYPE_BYTE|PIPE_WAIT, PIPE_UNLIMITED_INSTANCES,
                              4096, 4096, 500 /* 0.5 second timeout */, NULL );
    
    if (hPipe == INVALID_HANDLE_VALUE)
    {
954
        FIXME("pipe creation failed for %s, le is %ld\n", debugstr_w(pipefn), GetLastError());
955 956
        return 1;
    }
957
    
958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991
    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);
Robert Shearman's avatar
Robert Shearman committed
992
    IStream_Release(pStm);
993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008
    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 */
}