compobj.c 103 KB
Newer Older
Alexandre Julliard's avatar
Alexandre Julliard committed
1 2 3 4
/*
 *	COMPOBJ library
 *
 *	Copyright 1995	Martin von Loewis
5
 *	Copyright 1998	Justin Bradford
6
 *      Copyright 1999  Francis Beaudet
7 8 9
 *      Copyright 1999  Sylvain St-Germain
 *      Copyright 2002  Marcus Meissner
 *      Copyright 2004  Mike Hearn
10
 *      Copyright 2005-2006 Robert Shearman (for CodeWeavers)
11 12 13 14 15 16 17 18 19 20 21 22 23
 *
 * 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
24
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
Mike Hearn's avatar
Mike Hearn committed
25
 *
26 27 28 29
 * Note
 * 1. COINIT_MULTITHREADED is 0; it is the lack of COINIT_APARTMENTTHREADED
 *    Therefore do not test against COINIT_MULTITHREADED
 *
Mike Hearn's avatar
Mike Hearn committed
30 31 32 33
 * TODO list:           (items bunched together depend on each other)
 *
 *   - Implement the service control manager (in rpcss) to keep track
 *     of registered class objects: ISCM::ServerRegisterClsid et al
34
 *   - Implement the OXID resolver so we don't need magic endpoint names for
Mike Hearn's avatar
Mike Hearn committed
35 36
 *     clients and servers to meet up
 *
37
 *   - Make all ole interface marshaling use NDR to be wire compatible with
Mike Hearn's avatar
Mike Hearn committed
38
 *     native DCOM
39
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
40 41
 */

42
#include "config.h"
43

44
#include <stdarg.h>
45
#include <stdio.h>
Alexandre Julliard's avatar
Alexandre Julliard committed
46
#include <string.h>
47
#include <assert.h>
48

49
#define COBJMACROS
50 51
#define NONAMELESSUNION
#define NONAMELESSSTRUCT
52

53
#include "windef.h"
54
#include "winbase.h"
55 56
#include "winerror.h"
#include "winreg.h"
57
#include "winuser.h"
58 59 60
#include "objbase.h"
#include "ole2.h"
#include "ole2ver.h"
61

62
#include "compobj_private.h"
63

64
#include "wine/unicode.h"
65
#include "wine/debug.h"
66

67
WINE_DEFAULT_DEBUG_CHANNEL(ole);
68

69 70
HINSTANCE OLE32_hInstance = 0; /* FIXME: make static ... */

71 72
#define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))

73
/****************************************************************************
74
 * This section defines variables internal to the COM module.
75
 *
76
 * TODO: Most of these things will have to be made thread-safe.
77 78
 */

79 80 81
static HRESULT COM_GetRegisteredClassObject(const struct apartment *apt, REFCLSID rclsid,
                                            DWORD dwClsContext, LPUNKNOWN*  ppUnk);
static void COM_RevokeAllClasses(const struct apartment *apt);
82
static HRESULT get_inproc_class_object(APARTMENT *apt, HKEY hkeydll, REFCLSID rclsid, REFIID riid, void **ppv);
83

84
static APARTMENT *MTA; /* protected by csApartment */
85
static APARTMENT *MainApartment; /* the first STA apartment */
86
static struct list apts = LIST_INIT( apts ); /* protected by csApartment */
87 88 89 90 91 92

static CRITICAL_SECTION csApartment;
static CRITICAL_SECTION_DEBUG critsect_debug =
{
    0, 0, &csApartment,
    { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
93
      0, 0, { (DWORD_PTR)(__FILE__ ": csApartment") }
94 95
};
static CRITICAL_SECTION csApartment = { &critsect_debug, -1, 0, 0, 0, 0 };
Alexandre Julliard's avatar
Alexandre Julliard committed
96

97 98 99 100 101 102 103
struct registered_psclsid
{
    struct list entry;
    IID iid;
    CLSID clsid;
};

104 105 106 107 108
/*
 * This lock count counts the number of times CoInitialize is called. It is
 * decreased every time CoUninitialize is called. When it hits 0, the COM
 * libraries are freed
 */
109
static LONG s_COMLockCount = 0;
110 111
/* Reference count used by CoAddRefServerProcess/CoReleaseServerProcess */
static LONG s_COMServerProcessReferences = 0;
112 113 114 115 116 117 118 119 120 121 122

/*
 * This linked list contains the list of registered class objects. These
 * are mostly used to register the factories for out-of-proc servers of OLE
 * objects.
 *
 * TODO: Make this data structure aware of inter-process communication. This
 *       means that parts of this will be exported to the Wine Server.
 */
typedef struct tagRegisteredClass
{
123
  struct list entry;
124
  CLSID     classIdentifier;
125
  OXID      apartment_id;
126 127 128 129
  LPUNKNOWN classObject;
  DWORD     runContext;
  DWORD     connectFlags;
  DWORD     dwCookie;
130
  LPSTREAM  pMarshaledData; /* FIXME: only really need to store OXID and IPID */
131
  void     *RpcRegistration;
132 133
} RegisteredClass;

134
static struct list RegisteredClassList = LIST_INIT(RegisteredClassList);
135

136 137 138 139 140
static CRITICAL_SECTION csRegisteredClassList;
static CRITICAL_SECTION_DEBUG class_cs_debug =
{
    0, 0, &csRegisteredClassList,
    { &class_cs_debug.ProcessLocksList, &class_cs_debug.ProcessLocksList },
141
      0, 0, { (DWORD_PTR)(__FILE__ ": csRegisteredClassList") }
142 143 144
};
static CRITICAL_SECTION csRegisteredClassList = { &class_cs_debug, -1, 0, 0, 0, 0 };

145 146 147 148
/*****************************************************************************
 * This section contains OpenDllList definitions
 *
 * The OpenDllList contains only handles of dll loaded by CoGetClassObject or
149 150
 * other functions that do LoadLibrary _without_ giving back a HMODULE.
 * Without this list these handles would never be freed.
151
 *
152
 * FIXME: a DLL that says OK when asked for unloading is unloaded in the
153
 * next unload-call but not before 600 sec.
154
 */
155

156 157 158
typedef HRESULT (CALLBACK *DllGetClassObjectFunc)(REFCLSID clsid, REFIID iid, LPVOID *ppv);
typedef HRESULT (WINAPI *DllCanUnloadNowFunc)(void);

159 160 161 162 163
typedef struct tagOpenDll
{
  LONG refs;
  LPWSTR library_name;
  HANDLE library;
164 165
  DllGetClassObjectFunc DllGetClassObject;
  DllCanUnloadNowFunc DllCanUnloadNow;
166
  struct list entry;
167 168
} OpenDll;

169
static struct list openDllList = LIST_INIT(openDllList);
170

171 172 173 174 175
static CRITICAL_SECTION csOpenDllList;
static CRITICAL_SECTION_DEBUG dll_cs_debug =
{
    0, 0, &csOpenDllList,
    { &dll_cs_debug.ProcessLocksList, &dll_cs_debug.ProcessLocksList },
176
      0, 0, { (DWORD_PTR)(__FILE__ ": csOpenDllList") }
177 178 179
};
static CRITICAL_SECTION csOpenDllList = { &dll_cs_debug, -1, 0, 0, 0, 0 };

180 181 182 183
struct apartment_loaded_dll
{
    struct list entry;
    OpenDll *dll;
184
    DWORD unload_time;
185
    BOOL multi_threaded;
186 187
};

188 189 190
static const WCHAR wszAptWinClass[] = {'O','l','e','M','a','i','n','T','h','r','e','a','d','W','n','d','C','l','a','s','s',' ',
                                       '0','x','#','#','#','#','#','#','#','#',' ',0};
static LRESULT CALLBACK apartment_wndproc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
191
static HRESULT apartment_getclassobject(struct apartment *apt, LPCWSTR dllpath,
192
                                        BOOL apartment_threaded,
193
                                        REFCLSID rclsid, REFIID riid, void **ppv);
194
static void apartment_freeunusedlibraries(struct apartment *apt, DWORD delay);
195

196
static HRESULT COMPOBJ_DllList_Add(LPCWSTR library_name, OpenDll **ret);
197
static OpenDll *COMPOBJ_DllList_Get(LPCWSTR library_name);
198
static void COMPOBJ_DllList_ReleaseRef(OpenDll *entry, BOOL free_entry);
199

200 201
static DWORD COM_RegReadPath(HKEY hkeyroot, const WCHAR *keyname, const WCHAR *valuename, WCHAR * dst, DWORD dstlen);

202
static void COMPOBJ_InitProcess( void )
203
{
204
    WNDCLASSW wclass;
205

206 207 208 209 210 211
    /* Dispatching to the correct thread in an apartment is done through
     * window messages rather than RPC transports. When an interface is
     * marshalled into another apartment in the same process, a window of the
     * following class is created. The *caller* of CoMarshalInterface (ie the
     * application) is responsible for pumping the message loop in that thread.
     * The WM_USER messages which point to the RPCs are then dispatched to
212 213
     * COM_AptWndProc by the user's code from the apartment in which the interface
     * was unmarshalled.
214
     */
215
    memset(&wclass, 0, sizeof(wclass));
216
    wclass.lpfnWndProc = apartment_wndproc;
217
    wclass.hInstance = OLE32_hInstance;
218 219
    wclass.lpszClassName = wszAptWinClass;
    RegisterClassW(&wclass);
220 221
}

222
static void COMPOBJ_UninitProcess( void )
223
{
224
    UnregisterClassW(wszAptWinClass, OLE32_hInstance);
225 226
}

227
static void COM_TlsDestroy(void)
228 229 230 231
{
    struct oletls *info = NtCurrentTeb()->ReservedForOle;
    if (info)
    {
232
        if (info->apt) apartment_release(info->apt);
233 234 235 236 237 238 239
        if (info->errorinfo) IErrorInfo_Release(info->errorinfo);
        if (info->state) IUnknown_Release(info->state);
        HeapFree(GetProcessHeap(), 0, info);
        NtCurrentTeb()->ReservedForOle = NULL;
    }
}

240 241 242
/******************************************************************************
 * Manage apartments.
 */
243

244
/* allocates memory and fills in the necessary fields for a new apartment
245
 * object. must be called inside apartment cs */
246
static APARTMENT *apartment_construct(DWORD model)
247
{
248
    APARTMENT *apt;
249

250
    TRACE("creating new apartment, model=%d\n", model);
251

252 253
    apt = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*apt));
    apt->tid = GetCurrentThreadId();
254

255 256
    list_init(&apt->proxies);
    list_init(&apt->stubmgrs);
257
    list_init(&apt->psclsids);
258
    list_init(&apt->loaded_dlls);
259 260 261 262 263
    apt->ipidc = 0;
    apt->refs = 1;
    apt->remunk_exported = FALSE;
    apt->oidc = 1;
    InitializeCriticalSection(&apt->cs);
264
    DEBUG_SET_CRITSEC_NAME(&apt->cs, "apartment");
265

266
    apt->multi_threaded = !(model & COINIT_APARTMENTTHREADED);
267

268
    if (apt->multi_threaded)
269 270
    {
        /* FIXME: should be randomly generated by in an RPC call to rpcss */
271
        apt->oxid = ((OXID)GetCurrentProcessId() << 32) | 0xcafe;
272 273 274 275
    }
    else
    {
        /* FIXME: should be randomly generated by in an RPC call to rpcss */
276
        apt->oxid = ((OXID)GetCurrentProcessId() << 32) | GetCurrentThreadId();
277
    }
278

279
    TRACE("Created apartment on OXID %s\n", wine_dbgstr_longlong(apt->oxid));
280

281
    list_add_head(&apts, &apt->entry);
282

283 284
    return apt;
}
285

286 287 288
/* gets and existing apartment if one exists or otherwise creates an apartment
 * structure which stores OLE apartment-local information and stores a pointer
 * to it in the thread-local storage */
289
static APARTMENT *apartment_get_or_create(DWORD model)
290 291
{
    APARTMENT *apt = COM_CurrentApt();
292

293 294
    if (!apt)
    {
295 296
        if (model & COINIT_APARTMENTTHREADED)
        {
297 298
            EnterCriticalSection(&csApartment);

299
            apt = apartment_construct(model);
300 301 302 303 304 305 306 307
            if (!MainApartment)
            {
                MainApartment = apt;
                apt->main = TRUE;
                TRACE("Created main-threaded apartment with OXID %s\n", wine_dbgstr_longlong(apt->oxid));
            }

            LeaveCriticalSection(&csApartment);
308
        }
309 310
        else
        {
311 312 313 314 315 316 317 318
            EnterCriticalSection(&csApartment);

            /* The multi-threaded apartment (MTA) contains zero or more threads interacting
             * with free threaded (ie thread safe) COM objects. There is only ever one MTA
             * in a process */
            if (MTA)
            {
                TRACE("entering the multithreaded apartment %s\n", wine_dbgstr_longlong(MTA->oxid));
319
                apartment_addref(MTA);
320 321 322
            }
            else
                MTA = apartment_construct(model);
323

324
            apt = MTA;
325

326 327
            LeaveCriticalSection(&csApartment);
        }
328
        COM_CurrentInfo()->apt = apt;
329
    }
330

331 332 333
    return apt;
}

334
static inline BOOL apartment_is_model(const APARTMENT *apt, DWORD model)
335 336 337 338
{
    return (apt->multi_threaded == !(model & COINIT_APARTMENTTHREADED));
}

339
DWORD apartment_addref(struct apartment *apt)
340
{
341
    DWORD refs = InterlockedIncrement(&apt->refs);
342
    TRACE("%s: before = %d\n", wine_dbgstr_longlong(apt->oxid), refs - 1);
343
    return refs;
344 345
}

346
DWORD apartment_release(struct apartment *apt)
347
{
348 349
    DWORD ret;

350 351
    EnterCriticalSection(&csApartment);

352
    ret = InterlockedDecrement(&apt->refs);
353
    TRACE("%s: after = %d\n", wine_dbgstr_longlong(apt->oxid), ret);
354
    /* destruction stuff that needs to happen under csApartment CS */
355 356
    if (ret == 0)
    {
357
        if (apt == MTA) MTA = NULL;
358
        else if (apt == MainApartment) MainApartment = NULL;
359
        list_remove(&apt->entry);
360 361 362 363 364 365
    }

    LeaveCriticalSection(&csApartment);

    if (ret == 0)
    {
366 367
        struct list *cursor, *cursor2;

368
        TRACE("destroying apartment %p, oxid %s\n", apt, wine_dbgstr_longlong(apt->oxid));
369

370 371 372
        /* Release the references to the registered class objects */
        COM_RevokeAllClasses(apt);

373 374 375
        /* no locking is needed for this apartment, because no other thread
         * can access it at this point */

376
        apartment_disconnectproxies(apt);
377

378
        if (apt->win) DestroyWindow(apt->win);
379
        if (apt->host_apt_tid) PostThreadMessageW(apt->host_apt_tid, WM_QUIT, 0, 0);
380

381
        LIST_FOR_EACH_SAFE(cursor, cursor2, &apt->stubmgrs)
382
        {
383 384 385 386 387 388 389
            struct stub_manager *stubmgr = LIST_ENTRY(cursor, struct stub_manager, entry);
            /* release the implicit reference given by the fact that the
             * stub has external references (it must do since it is in the
             * stub manager list in the apartment and all non-apartment users
             * must have a ref on the apartment and so it cannot be destroyed).
             */
            stub_manager_int_release(stubmgr);
390 391
        }

392 393 394 395 396 397
        LIST_FOR_EACH_SAFE(cursor, cursor2, &apt->psclsids)
        {
            struct registered_psclsid *registered_psclsid =
                LIST_ENTRY(cursor, struct registered_psclsid, entry);

            list_remove(&registered_psclsid->entry);
398
            HeapFree(GetProcessHeap(), 0, registered_psclsid);
399 400
        }

401 402 403 404 405
        /* if this assert fires, then another thread took a reference to a
         * stub manager without taking a reference to the containing
         * apartment, which it must do. */
        assert(list_empty(&apt->stubmgrs));

406 407
        if (apt->filter) IUnknown_Release(apt->filter);

408
        /* free as many unused libraries as possible... */
409
        apartment_freeunusedlibraries(apt, 0);
410 411 412 413

        /* ... and free the memory for the apartment loaded dll entry and
         * release the dll list reference without freeing the library for the
         * rest */
414 415 416
        while ((cursor = list_head(&apt->loaded_dlls)))
        {
            struct apartment_loaded_dll *apartment_loaded_dll = LIST_ENTRY(cursor, struct apartment_loaded_dll, entry);
417
            COMPOBJ_DllList_ReleaseRef(apartment_loaded_dll->dll, FALSE);
418 419 420 421
            list_remove(cursor);
            HeapFree(GetProcessHeap(), 0, apartment_loaded_dll);
        }

422
        DEBUG_CLEAR_CRITSEC_NAME(&apt->cs);
423
        DeleteCriticalSection(&apt->cs);
424

425
        HeapFree(GetProcessHeap(), 0, apt);
426 427
    }

428 429 430
    return ret;
}

431
/* The given OXID must be local to this process: 
432 433 434 435
 *
 * The ref parameter is here mostly to ensure people remember that
 * they get one, you should normally take a ref for thread safety.
 */
436
APARTMENT *apartment_findfromoxid(OXID oxid, BOOL ref)
437
{
438 439
    APARTMENT *result = NULL;
    struct list *cursor;
440 441

    EnterCriticalSection(&csApartment);
442 443 444 445 446 447
    LIST_FOR_EACH( cursor, &apts )
    {
        struct apartment *apt = LIST_ENTRY( cursor, struct apartment, entry );
        if (apt->oxid == oxid)
        {
            result = apt;
448
            if (ref) apartment_addref(result);
449 450 451
            break;
        }
    }
452
    LeaveCriticalSection(&csApartment);
453

454
    return result;
455 456
}

457 458 459
/* gets the apartment which has a given creator thread ID. The caller must
 * release the reference from the apartment as soon as the apartment pointer
 * is no longer required. */
460
APARTMENT *apartment_findfromtid(DWORD tid)
461 462 463 464 465 466 467 468 469 470 471
{
    APARTMENT *result = NULL;
    struct list *cursor;

    EnterCriticalSection(&csApartment);
    LIST_FOR_EACH( cursor, &apts )
    {
        struct apartment *apt = LIST_ENTRY( cursor, struct apartment, entry );
        if (apt->tid == tid)
        {
            result = apt;
472
            apartment_addref(result);
473 474 475 476 477 478 479 480
            break;
        }
    }
    LeaveCriticalSection(&csApartment);

    return result;
}

481
/* gets the main apartment if it exists. The caller must
482 483
 * release the reference from the apartment as soon as the apartment pointer
 * is no longer required. */
484
static APARTMENT *apartment_findmain(void)
485
{
486
    APARTMENT *result;
487 488

    EnterCriticalSection(&csApartment);
489

490 491
    result = MainApartment;
    if (result) apartment_addref(result);
492

493 494 495 496 497 498 499 500 501 502
    LeaveCriticalSection(&csApartment);

    return result;
}

struct host_object_params
{
    HKEY hkeydll;
    CLSID clsid; /* clsid of object to marshal */
    IID iid; /* interface to marshal */
503 504
    HANDLE event; /* event signalling when ready for multi-threaded case */
    HRESULT hr; /* result for multi-threaded case */
505
    IStream *stream; /* stream that the object will be marshaled into */
506
    BOOL apartment_threaded; /* is the component purely apartment-threaded? */
507 508
};

509 510
static HRESULT apartment_hostobject(struct apartment *apt,
                                    const struct host_object_params *params)
511 512 513 514
{
    IUnknown *object;
    HRESULT hr;
    static const LARGE_INTEGER llZero;
515
    WCHAR dllpath[MAX_PATH+1];
516

517
    TRACE("clsid %s, iid %s\n", debugstr_guid(&params->clsid), debugstr_guid(&params->iid));
518

519 520 521 522 523 524 525
    if (COM_RegReadPath(params->hkeydll, NULL, NULL, dllpath, ARRAYSIZE(dllpath)) != ERROR_SUCCESS)
    {
        /* failure: CLSID is not found in registry */
        WARN("class %s not registered inproc\n", debugstr_guid(&params->clsid));
        return REGDB_E_CLASSNOTREG;
    }

526 527
    hr = apartment_getclassobject(apt, dllpath, params->apartment_threaded,
                                  &params->clsid, &params->iid, (void **)&object);
528 529 530 531 532 533 534 535 536 537 538
    if (FAILED(hr))
        return hr;

    hr = CoMarshalInterface(params->stream, &params->iid, object, MSHCTX_INPROC, NULL, MSHLFLAGS_NORMAL);
    if (FAILED(hr))
        IUnknown_Release(object);
    IStream_Seek(params->stream, llZero, STREAM_SEEK_SET, NULL);

    return hr;
}

539
static LRESULT CALLBACK apartment_wndproc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
540
{
541 542 543
    switch (msg)
    {
    case DM_EXECUTERPC:
544 545
        RPC_ExecuteCall((struct dispatch_params *)lParam);
        return 0;
546
    case DM_HOSTOBJECT:
547
        return apartment_hostobject(COM_CurrentApt(), (const struct host_object_params *)lParam);
548 549 550
    default:
        return DefWindowProcW(hWnd, msg, wParam, lParam);
    }
551
}
552

553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617
struct host_thread_params
{
    COINIT threading_model;
    HANDLE ready_event;
    HWND apartment_hwnd;
};

static DWORD CALLBACK apartment_hostobject_thread(LPVOID p)
{
    struct host_thread_params *params = p;
    MSG msg;
    HRESULT hr;
    struct apartment *apt;

    TRACE("\n");

    hr = CoInitializeEx(NULL, params->threading_model);
    if (FAILED(hr)) return hr;

    apt = COM_CurrentApt();
    if (params->threading_model == COINIT_APARTMENTTHREADED)
    {
        apartment_createwindowifneeded(apt);
        params->apartment_hwnd = apartment_getwindow(apt);
    }
    else
        params->apartment_hwnd = NULL;

    /* force the message queue to be created before signaling parent thread */
    PeekMessageW(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);

    SetEvent(params->ready_event);
    params = NULL; /* can't touch params after here as it may be invalid */

    while (GetMessageW(&msg, NULL, 0, 0))
    {
        if (!msg.hwnd && (msg.message == DM_HOSTOBJECT))
        {
            struct host_object_params *params = (struct host_object_params *)msg.lParam;
            params->hr = apartment_hostobject(apt, params);
            SetEvent(params->event);
        }
        else
        {
            TranslateMessage(&msg);
            DispatchMessageW(&msg);
        }
    }

    TRACE("exiting\n");

    CoUninitialize();

    return S_OK;
}

static HRESULT apartment_hostobject_in_hostapt(struct apartment *apt, BOOL multi_threaded, BOOL main_apartment, HKEY hkeydll, REFCLSID rclsid, REFIID riid, void **ppv)
{
    struct host_object_params params;
    HWND apartment_hwnd = NULL;
    DWORD apartment_tid = 0;
    HRESULT hr;

    if (!multi_threaded && main_apartment)
    {
618
        APARTMENT *host_apt = apartment_findmain();
619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670
        if (host_apt)
        {
            apartment_hwnd = apartment_getwindow(host_apt);
            apartment_release(host_apt);
        }
    }

    if (!apartment_hwnd)
    {
        EnterCriticalSection(&apt->cs);

        if (!apt->host_apt_tid)
        {
            struct host_thread_params thread_params;
            HANDLE handles[2];
            DWORD wait_value;

            thread_params.threading_model = multi_threaded ? COINIT_MULTITHREADED : COINIT_APARTMENTTHREADED;
            handles[0] = thread_params.ready_event = CreateEventW(NULL, FALSE, FALSE, NULL);
            thread_params.apartment_hwnd = NULL;
            handles[1] = CreateThread(NULL, 0, apartment_hostobject_thread, &thread_params, 0, &apt->host_apt_tid);
            if (!handles[1])
            {
                CloseHandle(handles[0]);
                LeaveCriticalSection(&apt->cs);
                return E_OUTOFMEMORY;
            }
            wait_value = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
            CloseHandle(handles[0]);
            CloseHandle(handles[1]);
            if (wait_value == WAIT_OBJECT_0)
                apt->host_apt_hwnd = thread_params.apartment_hwnd;
            else
            {
                LeaveCriticalSection(&apt->cs);
                return E_OUTOFMEMORY;
            }
        }

        if (multi_threaded || !main_apartment)
        {
            apartment_hwnd = apt->host_apt_hwnd;
            apartment_tid = apt->host_apt_tid;
        }

        LeaveCriticalSection(&apt->cs);
    }

    /* another thread may have become the main apartment in the time it took
     * us to create the thread for the host apartment */
    if (!apartment_hwnd && !multi_threaded && main_apartment)
    {
671
        APARTMENT *host_apt = apartment_findmain();
672 673 674 675 676 677 678 679 680 681 682 683 684
        if (host_apt)
        {
            apartment_hwnd = apartment_getwindow(host_apt);
            apartment_release(host_apt);
        }
    }

    params.hkeydll = hkeydll;
    params.clsid = *rclsid;
    params.iid = *riid;
    hr = CreateStreamOnHGlobal(NULL, TRUE, &params.stream);
    if (FAILED(hr))
        return hr;
685
    params.apartment_threaded = !multi_threaded;
686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714
    if (multi_threaded)
    {
        params.hr = S_OK;
        params.event = CreateEventW(NULL, FALSE, FALSE, NULL);
        if (!PostThreadMessageW(apartment_tid, DM_HOSTOBJECT, 0, (LPARAM)&params))
            hr = E_OUTOFMEMORY;
        else
        {
            WaitForSingleObject(params.event, INFINITE);
            hr = params.hr;
        }
        CloseHandle(params.event);
    }
    else
    {
        if (!apartment_hwnd)
        {
            ERR("host apartment didn't create window\n");
            hr = E_OUTOFMEMORY;
        }
        else
            hr = SendMessageW(apartment_hwnd, DM_HOSTOBJECT, 0, (LPARAM)&params);
    }
    if (SUCCEEDED(hr))
        hr = CoUnmarshalInterface(params.stream, riid, ppv);
    IStream_Release(params.stream);
    return hr;
}

715 716
HRESULT apartment_createwindowifneeded(struct apartment *apt)
{
717
    if (apt->multi_threaded)
718 719 720 721 722 723 724 725 726
        return S_OK;

    if (!apt->win)
    {
        HWND hwnd = CreateWindowW(wszAptWinClass, NULL, 0,
                                  0, 0, 0, 0,
                                  0, 0, OLE32_hInstance, NULL);
        if (!hwnd)
        {
727
            ERR("CreateWindow failed with error %d\n", GetLastError());
728 729 730 731 732 733 734 735 736 737
            return HRESULT_FROM_WIN32(GetLastError());
        }
        if (InterlockedCompareExchangePointer((PVOID *)&apt->win, hwnd, NULL))
            /* someone beat us to it */
            DestroyWindow(hwnd);
    }

    return S_OK;
}

738
HWND apartment_getwindow(const struct apartment *apt)
739
{
740
    assert(!apt->multi_threaded);
741 742 743
    return apt->win;
}

744 745 746 747 748 749
void apartment_joinmta(void)
{
    apartment_addref(MTA);
    COM_CurrentInfo()->apt = MTA;
}

750
static HRESULT apartment_getclassobject(struct apartment *apt, LPCWSTR dllpath,
751
                                        BOOL apartment_threaded,
752 753
                                        REFCLSID rclsid, REFIID riid, void **ppv)
{
754
    static const WCHAR wszOle32[] = {'o','l','e','3','2','.','d','l','l',0};
755 756 757
    HRESULT hr = S_OK;
    BOOL found = FALSE;
    struct apartment_loaded_dll *apartment_loaded_dll;
758

759 760 761 762 763 764 765 766 767 768 769 770 771
    if (!strcmpiW(dllpath, wszOle32))
    {
        /* we don't need to control the lifetime of this dll, so use the local
         * implementation of DllGetClassObject directly */
        TRACE("calling ole32!DllGetClassObject\n");
        hr = DllGetClassObject(rclsid, riid, ppv);

        if (hr != S_OK)
            ERR("DllGetClassObject returned error 0x%08x\n", hr);

        return hr;
    }

772
    EnterCriticalSection(&apt->cs);
773

774 775 776 777 778 779 780 781 782 783 784 785 786 787 788
    LIST_FOR_EACH_ENTRY(apartment_loaded_dll, &apt->loaded_dlls, struct apartment_loaded_dll, entry)
        if (!strcmpiW(dllpath, apartment_loaded_dll->dll->library_name))
        {
            TRACE("found %s already loaded\n", debugstr_w(dllpath));
            found = TRUE;
            break;
        }

    if (!found)
    {
        apartment_loaded_dll = HeapAlloc(GetProcessHeap(), 0, sizeof(*apartment_loaded_dll));
        if (!apartment_loaded_dll)
            hr = E_OUTOFMEMORY;
        if (SUCCEEDED(hr))
        {
789
            apartment_loaded_dll->unload_time = 0;
790
            apartment_loaded_dll->multi_threaded = FALSE;
791 792 793 794 795 796 797 798 799 800 801 802 803 804 805
            hr = COMPOBJ_DllList_Add( dllpath, &apartment_loaded_dll->dll );
            if (FAILED(hr))
                HeapFree(GetProcessHeap(), 0, apartment_loaded_dll);
        }
        if (SUCCEEDED(hr))
        {
            TRACE("added new loaded dll %s\n", debugstr_w(dllpath));
            list_add_tail(&apt->loaded_dlls, &apartment_loaded_dll->entry);
        }
    }

    LeaveCriticalSection(&apt->cs);

    if (SUCCEEDED(hr))
    {
806 807 808 809 810
        /* one component being multi-threaded overrides any number of
         * apartment-threaded components */
        if (!apartment_threaded)
            apartment_loaded_dll->multi_threaded = TRUE;

811 812 813
        TRACE("calling DllGetClassObject %p\n", apartment_loaded_dll->dll->DllGetClassObject);
        /* OK: get the ClassObject */
        hr = apartment_loaded_dll->dll->DllGetClassObject(rclsid, riid, ppv);
814

815 816 817
        if (hr != S_OK)
            ERR("DllGetClassObject returned error 0x%08x\n", hr);
    }
818 819 820 821

    return hr;
}

822
static void apartment_freeunusedlibraries(struct apartment *apt, DWORD delay)
823 824 825 826 827 828 829
{
    struct apartment_loaded_dll *entry, *next;
    EnterCriticalSection(&apt->cs);
    LIST_FOR_EACH_ENTRY_SAFE(entry, next, &apt->loaded_dlls, struct apartment_loaded_dll, entry)
    {
	if (entry->dll->DllCanUnloadNow && (entry->dll->DllCanUnloadNow() == S_OK))
        {
830 831 832 833 834 835 836 837 838 839 840
            DWORD real_delay = delay;

            if (real_delay == INFINITE)
            {
                if (entry->multi_threaded)
                    real_delay = 10 * 60 * 1000; /* 10 minutes */
                else
                    real_delay = 0;
            }

            if (!real_delay || (entry->unload_time && (entry->unload_time < GetTickCount())))
841 842 843 844 845 846
            {
                list_remove(&entry->entry);
                COMPOBJ_DllList_ReleaseRef(entry->dll, TRUE);
                HeapFree(GetProcessHeap(), 0, entry);
            }
            else
847
                entry->unload_time = GetTickCount() + real_delay;
848
        }
849 850
        else if (entry->unload_time)
            entry->unload_time = 0;
851 852 853 854
    }
    LeaveCriticalSection(&apt->cs);
}

855
/*****************************************************************************
856
 * This section contains OpenDllList implementation
Alexandre Julliard's avatar
Alexandre Julliard committed
857
 */
858

859
/* caller must ensure that library_name is not already in the open dll list */
860
static HRESULT COMPOBJ_DllList_Add(LPCWSTR library_name, OpenDll **ret)
Alexandre Julliard's avatar
Alexandre Julliard committed
861
{
862 863 864
    OpenDll *entry;
    int len;
    HRESULT hr = S_OK;
865
    HANDLE hLibrary;
866 867
    DllCanUnloadNowFunc DllCanUnloadNow;
    DllGetClassObjectFunc DllGetClassObject;
Alexandre Julliard's avatar
Alexandre Julliard committed
868

869
    TRACE("\n");
870

871 872 873 874 875 876 877 878
    *ret = COMPOBJ_DllList_Get(library_name);
    if (*ret) return S_OK;

    /* do this outside the csOpenDllList to avoid creating a lock dependency on
     * the loader lock */
    hLibrary = LoadLibraryExW(library_name, 0, LOAD_WITH_ALTERED_SEARCH_PATH);
    if (!hLibrary)
    {
879
        ERR("couldn't load in-process dll %s\n", debugstr_w(library_name));
880 881 882 883
        /* failure: DLL could not be loaded */
        return E_ACCESSDENIED; /* FIXME: or should this be CO_E_DLLNOTFOUND? */
    }

884
    DllCanUnloadNow = (void *)GetProcAddress(hLibrary, "DllCanUnloadNow");
885
    /* Note: failing to find DllCanUnloadNow is not a failure */
886
    DllGetClassObject = (void *)GetProcAddress(hLibrary, "DllGetClassObject");
887 888 889 890 891 892 893 894
    if (!DllGetClassObject)
    {
        /* failure: the dll did not export DllGetClassObject */
        ERR("couldn't find function DllGetClassObject in %s\n", debugstr_w(library_name));
        FreeLibrary(hLibrary);
        return CO_E_DLLNOTFOUND;
    }

895 896
    EnterCriticalSection( &csOpenDllList );

897
    *ret = COMPOBJ_DllList_Get(library_name);
898 899 900 901 902 903 904
    if (*ret)
    {
        /* another caller to this function already added the dll while we
         * weren't in the critical section */
        FreeLibrary(hLibrary);
    }
    else
905 906 907 908 909 910 911 912 913 914
    {
        len = strlenW(library_name);
        entry = HeapAlloc(GetProcessHeap(),0, sizeof(OpenDll));
        if (entry)
            entry->library_name = HeapAlloc(GetProcessHeap(), 0, (len + 1)*sizeof(WCHAR));
        if (entry && entry->library_name)
        {
            memcpy(entry->library_name, library_name, (len + 1)*sizeof(WCHAR));
            entry->library = hLibrary;
            entry->refs = 1;
915 916
            entry->DllCanUnloadNow = DllCanUnloadNow;
            entry->DllGetClassObject = DllGetClassObject;
917
            list_add_tail(&openDllList, &entry->entry);
918
        }
919
        else
920
        {
921
            hr = E_OUTOFMEMORY;
922 923
            FreeLibrary(hLibrary);
        }
924
        *ret = entry;
925 926 927
    }

    LeaveCriticalSection( &csOpenDllList );
928 929 930 931 932 933 934 935 936 937 938

    return hr;
}

static OpenDll *COMPOBJ_DllList_Get(LPCWSTR library_name)
{
    OpenDll *ptr;
    OpenDll *ret = NULL;
    EnterCriticalSection(&csOpenDllList);
    LIST_FOR_EACH_ENTRY(ptr, &openDllList, OpenDll, entry)
    {
939 940
        if (!strcmpiW(library_name, ptr->library_name) &&
            (InterlockedIncrement(&ptr->refs) != 1) /* entry is being destroy if == 1 */)
941 942 943 944 945 946 947 948 949
        {
            ret = ptr;
            break;
        }
    }
    LeaveCriticalSection(&csOpenDllList);
    return ret;
}

950 951 952
/* pass FALSE for free_entry to release a reference without destroying the
 * entry if it reaches zero or TRUE otherwise */
static void COMPOBJ_DllList_ReleaseRef(OpenDll *entry, BOOL free_entry)
953
{
954
    if (!InterlockedDecrement(&entry->refs) && free_entry)
955 956 957 958 959 960 961 962 963 964 965
    {
        EnterCriticalSection(&csOpenDllList);
        list_remove(&entry->entry);
        LeaveCriticalSection(&csOpenDllList);

        TRACE("freeing %p\n", entry->library);
        FreeLibrary(entry->library);

        HeapFree(GetProcessHeap(), 0, entry->library_name);
        HeapFree(GetProcessHeap(), 0, entry);
    }
966 967
}

968
/******************************************************************************
969
 *           CoBuildVersion [OLE32.@]
970 971 972 973 974
 *           CoBuildVersion [COMPOBJ.1]
 *
 * Gets the build version of the DLL.
 *
 * PARAMS
975 976 977
 *
 * RETURNS
 *	Current build version, hiword is majornumber, loword is minornumber
Alexandre Julliard's avatar
Alexandre Julliard committed
978
 */
979 980 981 982
DWORD WINAPI CoBuildVersion(void)
{
    TRACE("Returning version %d, build %d.\n", rmm, rup);
    return (rmm<<16)+rup;
Alexandre Julliard's avatar
Alexandre Julliard committed
983 984
}

985
/******************************************************************************
986
 *		CoInitialize	[OLE32.@]
987
 *
988 989
 * Initializes the COM libraries by calling CoInitializeEx with
 * COINIT_APARTMENTTHREADED, ie it enters a STA thread.
990
 *
991 992 993 994 995 996 997
 * PARAMS
 *  lpReserved [I] Pointer to IMalloc interface (obsolete, should be NULL).
 *
 * RETURNS
 *  Success: S_OK if not already initialized, S_FALSE otherwise.
 *  Failure: HRESULT code.
 *
998 999
 * SEE ALSO
 *   CoInitializeEx
Alexandre Julliard's avatar
Alexandre Julliard committed
1000
 */
1001
HRESULT WINAPI CoInitialize(LPVOID lpReserved)
1002 1003 1004 1005
{
  /*
   * Just delegate to the newer method.
   */
1006
  return CoInitializeEx(lpReserved, COINIT_APARTMENTTHREADED);
Alexandre Julliard's avatar
Alexandre Julliard committed
1007 1008
}

1009
/******************************************************************************
1010
 *		CoInitializeEx	[OLE32.@]
1011
 *
1012
 * Initializes the COM libraries.
1013
 *
1014 1015 1016
 * PARAMS
 *  lpReserved [I] Pointer to IMalloc interface (obsolete, should be NULL).
 *  dwCoInit   [I] One or more flags from the COINIT enumeration. See notes.
1017 1018
 *
 * RETURNS
1019 1020
 *  S_OK               if successful,
 *  S_FALSE            if this function was called already.
1021 1022
 *  RPC_E_CHANGED_MODE if a previous call to CoInitializeEx specified another
 *                     threading model.
1023
 *
1024 1025 1026 1027
 * NOTES
 *
 * The behavior used to set the IMalloc used for memory management is
 * obsolete.
1028
 * The dwCoInit parameter must specify one of the following apartment
1029 1030 1031 1032 1033 1034 1035
 * threading models:
 *| COINIT_APARTMENTTHREADED - A single-threaded apartment (STA).
 *| COINIT_MULTITHREADED - A multi-threaded apartment (MTA).
 * The parameter may also specify zero or more of the following flags:
 *| COINIT_DISABLE_OLE1DDE - Don't use DDE for OLE1 support.
 *| COINIT_SPEED_OVER_MEMORY - Trade memory for speed.
 *
1036 1037
 * SEE ALSO
 *   CoUninitialize
1038
 */
1039
HRESULT WINAPI CoInitializeEx(LPVOID lpReserved, DWORD dwCoInit)
1040
{
1041 1042
  HRESULT hr = S_OK;
  APARTMENT *apt;
1043

1044
  TRACE("(%p, %x)\n", lpReserved, (int)dwCoInit);
1045 1046 1047

  if (lpReserved!=NULL)
  {
1048
    ERR("(%p, %x) - Bad parameter passed-in %p, must be an old Windows Application\n", lpReserved, (int)dwCoInit, lpReserved);
1049 1050 1051 1052 1053
  }

  /*
   * Check the lock count. If this is the first time going through the initialize
   * process, we have to initialize the libraries.
1054 1055
   *
   * And crank-up that lock count.
1056
   */
1057
  if (InterlockedExchangeAdd(&s_COMLockCount,1)==0)
1058 1059 1060 1061
  {
    /*
     * Initialize the various COM libraries and data structures.
     */
1062
    TRACE("() - Initializing the COM libraries\n");
1063

1064
    /* we may need to defer this until after apartment initialisation */
Noomen Hamza's avatar
Noomen Hamza committed
1065
    RunningObjectTableImpl_Initialize();
1066
  }
1067

1068 1069
  if (!(apt = COM_CurrentInfo()->apt))
  {
1070
    apt = apartment_get_or_create(dwCoInit);
1071
    if (!apt) return E_OUTOFMEMORY;
1072
  }
1073
  else if (!apartment_is_model(apt, dwCoInit))
1074 1075 1076
  {
    /* Changing the threading model after it's been set is illegal. If this warning is triggered by Wine
       code then we are probably using the wrong threading model to implement that API. */
1077 1078 1079
    ERR("Attempt to change threading model of this apartment from %s to %s\n",
        apt->multi_threaded ? "multi-threaded" : "apartment threaded",
        dwCoInit & COINIT_APARTMENTTHREADED ? "apartment threaded" : "multi-threaded");
1080 1081
    return RPC_E_CHANGED_MODE;
  }
1082 1083
  else
    hr = S_FALSE;
1084 1085

  COM_CurrentInfo()->inits++;
1086 1087

  return hr;
1088 1089
}

1090
/***********************************************************************
1091
 *           CoUninitialize   [OLE32.@]
1092
 *
1093 1094 1095 1096
 * This method will decrement the refcount on the current apartment, freeing
 * the resources associated with it if it is the last thread in the apartment.
 * If the last apartment is freed, the function will additionally release
 * any COM resources associated with the process.
1097
 *
1098 1099 1100 1101
 * PARAMS
 *
 * RETURNS
 *  Nothing.
1102 1103 1104
 *
 * SEE ALSO
 *   CoInitializeEx
1105
 */
1106
void WINAPI CoUninitialize(void)
1107
{
1108
  struct oletls * info = COM_CurrentInfo();
1109
  LONG lCOMRefCnt;
1110

1111
  TRACE("()\n");
1112

1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124
  /* will only happen on OOM */
  if (!info) return;

  /* sanity check */
  if (!info->inits)
  {
    ERR("Mismatched CoUninitialize\n");
    return;
  }

  if (!--info->inits)
  {
1125
    apartment_release(info->apt);
1126 1127
    info->apt = NULL;
  }
1128

1129 1130 1131 1132 1133
  /*
   * Decrease the reference count.
   * If we are back to 0 locks on the COM library, make sure we free
   * all the associated data structures.
   */
1134 1135
  lCOMRefCnt = InterlockedExchangeAdd(&s_COMLockCount,-1);
  if (lCOMRefCnt==1)
1136
  {
1137
    TRACE("() - Releasing the COM libraries\n");
1138

Noomen Hamza's avatar
Noomen Hamza committed
1139
    RunningObjectTableImpl_UnInitialize();
1140 1141 1142 1143 1144
  }
  else if (lCOMRefCnt<1) {
    ERR( "CoUninitialize() - not CoInitialized.\n" );
    InterlockedExchangeAdd(&s_COMLockCount,1); /* restore the lock count. */
  }
1145
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1146

1147
/******************************************************************************
1148
 *		CoDisconnectObject	[OLE32.@]
1149 1150
 *
 * Disconnects all connections to this object from remote processes. Dispatches
1151
 * pending RPCs while blocking new RPCs from occurring, and then calls
1152 1153 1154 1155
 * IMarshal::DisconnectObject on the given object.
 *
 * Typically called when the object server is forced to shut down, for instance by
 * the user.
1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166
 *
 * PARAMS
 *  lpUnk    [I] The object whose stub should be disconnected.
 *  reserved [I] Reserved. Should be set to 0.
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: HRESULT code.
 *
 * SEE ALSO
 *  CoMarshalInterface, CoReleaseMarshalData, CoLockObjectExternal
Alexandre Julliard's avatar
Alexandre Julliard committed
1167
 */
1168
HRESULT WINAPI CoDisconnectObject( LPUNKNOWN lpUnk, DWORD reserved )
Alexandre Julliard's avatar
Alexandre Julliard committed
1169
{
1170 1171 1172 1173
    HRESULT hr;
    IMarshal *marshal;
    APARTMENT *apt;

1174
    TRACE("(%p, 0x%08x)\n", lpUnk, reserved);
1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187

    hr = IUnknown_QueryInterface(lpUnk, &IID_IMarshal, (void **)&marshal);
    if (hr == S_OK)
    {
        hr = IMarshal_DisconnectObject(marshal, reserved);
        IMarshal_Release(marshal);
        return hr;
    }

    apt = COM_CurrentApt();
    if (!apt)
        return CO_E_NOTINITIALIZED;

1188
    apartment_disconnectobject(apt, lpUnk);
1189 1190 1191 1192 1193 1194

    /* Note: native is pretty broken here because it just silently
     * fails, without returning an appropriate error code if the object was
     * not found, making apps think that the object was disconnected, when
     * it actually wasn't */

Alexandre Julliard's avatar
Alexandre Julliard committed
1195
    return S_OK;
Alexandre Julliard's avatar
Alexandre Julliard committed
1196 1197
}

1198
/******************************************************************************
1199
 *		CoCreateGuid [OLE32.@]
1200
 *		CoCreateGuid [COMPOBJ.73]
1201
 *
1202 1203
 * Simply forwards to UuidCreate in RPCRT4.
 *
1204 1205 1206 1207 1208 1209 1210
 * PARAMS
 *  pguid [O] Points to the GUID to initialize.
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: HRESULT code.
 *
1211 1212
 * SEE ALSO
 *   UuidCreate
Alexandre Julliard's avatar
Alexandre Julliard committed
1213
 */
1214 1215
HRESULT WINAPI CoCreateGuid(GUID *pguid)
{
1216
    return UuidCreate(pguid);
Alexandre Julliard's avatar
Alexandre Julliard committed
1217
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1218

1219
/******************************************************************************
1220 1221
 *		CLSIDFromString	[OLE32.@]
 *		IIDFromString   [OLE32.@]
1222
 *
1223
 * Converts a unique identifier from its string representation into
Alexandre Julliard's avatar
Alexandre Julliard committed
1224
 * the GUID struct.
1225
 *
1226 1227 1228
 * PARAMS
 *  idstr [I] The string representation of the GUID.
 *  id    [O] GUID converted from the string.
1229
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
1230
 * RETURNS
1231 1232
 *   S_OK on success
 *   CO_E_CLASSSTRING if idstr is not a valid CLSID
1233 1234 1235
 *
 * SEE ALSO
 *  StringFromCLSID
Alexandre Julliard's avatar
Alexandre Julliard committed
1236
 */
1237
static HRESULT WINAPI __CLSIDFromString(LPCWSTR s, CLSID *id)
1238
{
Alexandre Julliard's avatar
Alexandre Julliard committed
1239 1240 1241
  int	i;
  BYTE table[256];

1242
  if (!s) {
1243 1244 1245
    memset( id, 0, sizeof (CLSID) );
    return S_OK;
  }
1246

1247
  /* validate the CLSID string */
1248
  if (strlenW(s) != 38)
1249
    return CO_E_CLASSSTRING;
1250

1251 1252
  if ((s[0]!='{') || (s[9]!='-') || (s[14]!='-') || (s[19]!='-') || (s[24]!='-') || (s[37]!='}'))
    return CO_E_CLASSSTRING;
1253

1254 1255 1256 1257 1258 1259
  for (i=1; i<37; i++) {
    if ((i == 9)||(i == 14)||(i == 19)||(i == 24)) continue;
    if (!(((s[i] >= '0') && (s[i] <= '9'))  ||
          ((s[i] >= 'a') && (s[i] <= 'f'))  ||
          ((s[i] >= 'A') && (s[i] <= 'F'))))
       return CO_E_CLASSSTRING;
1260
  }
1261

1262
  TRACE("%s -> %p\n", debugstr_w(s), id);
Alexandre Julliard's avatar
Alexandre Julliard committed
1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276

  /* quick lookup table */
  memset(table, 0, 256);

  for (i = 0; i < 10; i++) {
    table['0' + i] = i;
  }
  for (i = 0; i < 6; i++) {
    table['A' + i] = i+10;
    table['a' + i] = i+10;
  }

  /* in form {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX} */

1277 1278 1279 1280
  id->Data1 = (table[s[1]] << 28 | table[s[2]] << 24 | table[s[3]] << 20 | table[s[4]] << 16 |
               table[s[5]] << 12 | table[s[6]] << 8  | table[s[7]] << 4  | table[s[8]]);
  id->Data2 = table[s[10]] << 12 | table[s[11]] << 8 | table[s[12]] << 4 | table[s[13]];
  id->Data3 = table[s[15]] << 12 | table[s[16]] << 8 | table[s[17]] << 4 | table[s[18]];
Alexandre Julliard's avatar
Alexandre Julliard committed
1281 1282

  /* these are just sequential bytes */
1283 1284 1285 1286 1287 1288 1289 1290
  id->Data4[0] = table[s[20]] << 4 | table[s[21]];
  id->Data4[1] = table[s[22]] << 4 | table[s[23]];
  id->Data4[2] = table[s[25]] << 4 | table[s[26]];
  id->Data4[3] = table[s[27]] << 4 | table[s[28]];
  id->Data4[4] = table[s[29]] << 4 | table[s[30]];
  id->Data4[5] = table[s[31]] << 4 | table[s[32]];
  id->Data4[6] = table[s[33]] << 4 | table[s[34]];
  id->Data4[7] = table[s[35]] << 4 | table[s[36]];
Alexandre Julliard's avatar
Alexandre Julliard committed
1291

Alexandre Julliard's avatar
Alexandre Julliard committed
1292 1293 1294
  return S_OK;
}

1295 1296
/*****************************************************************************/

1297
HRESULT WINAPI CLSIDFromString(LPOLESTR idstr, CLSID *id )
1298 1299
{
    HRESULT ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
1300

1301 1302 1303
    if (!id)
        return E_INVALIDARG;

1304
    ret = __CLSIDFromString(idstr, id);
1305 1306 1307
    if(ret != S_OK) { /* It appears a ProgID is also valid */
        ret = CLSIDFromProgID(idstr, id);
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1308
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
1309 1310
}

1311
/* Converts a GUID into the respective string representation. */
1312
HRESULT WINE_StringFromCLSID(
Alexandre Julliard's avatar
Alexandre Julliard committed
1313 1314 1315
	const CLSID *id,	/* [in] GUID to be converted */
	LPSTR idstr		/* [out] pointer to buffer to contain converted guid */
) {
1316
  static const char hex[] = "0123456789ABCDEF";
Alexandre Julliard's avatar
Alexandre Julliard committed
1317 1318 1319
  char *s;
  int	i;

Alexandre Julliard's avatar
Alexandre Julliard committed
1320
  if (!id)
1321
	{ ERR("called with id=Null\n");
Alexandre Julliard's avatar
Alexandre Julliard committed
1322 1323 1324
	  *idstr = 0x00;
	  return E_FAIL;
	}
1325

1326
  sprintf(idstr, "{%08X-%04X-%04X-%02X%02X-",
Alexandre Julliard's avatar
Alexandre Julliard committed
1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339
	  id->Data1, id->Data2, id->Data3,
	  id->Data4[0], id->Data4[1]);
  s = &idstr[25];

  /* 6 hex bytes */
  for (i = 2; i < 8; i++) {
    *s++ = hex[id->Data4[i]>>4];
    *s++ = hex[id->Data4[i] & 0xf];
  }

  *s++ = '}';
  *s++ = '\0';

1340
  TRACE("%p->%s\n", id, idstr);
Alexandre Julliard's avatar
Alexandre Julliard committed
1341

1342
  return S_OK;
Alexandre Julliard's avatar
Alexandre Julliard committed
1343 1344
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1345

1346
/******************************************************************************
1347 1348
 *		StringFromCLSID	[OLE32.@]
 *		StringFromIID   [OLE32.@]
1349
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
1350 1351
 * Converts a GUID into the respective string representation.
 * The target string is allocated using the OLE IMalloc.
1352
 *
1353 1354 1355 1356
 * PARAMS
 *  id    [I] the GUID to be converted.
 *  idstr [O] A pointer to a to-be-allocated pointer pointing to the resulting string.
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
1357
 * RETURNS
1358 1359
 *   S_OK
 *   E_FAIL
1360 1361 1362
 *
 * SEE ALSO
 *  StringFromGUID2, CLSIDFromString
Alexandre Julliard's avatar
Alexandre Julliard committed
1363
 */
1364 1365
HRESULT WINAPI StringFromCLSID(REFCLSID id, LPOLESTR *idstr)
{
Alexandre Julliard's avatar
Alexandre Julliard committed
1366
	char            buf[80];
1367
	HRESULT       ret;
1368
	LPMALLOC	mllc;
Alexandre Julliard's avatar
Alexandre Julliard committed
1369

1370
	if ((ret = CoGetMalloc(0,&mllc)))
Alexandre Julliard's avatar
Alexandre Julliard committed
1371 1372 1373 1374
		return ret;

	ret=WINE_StringFromCLSID(id,buf);
	if (!ret) {
1375 1376 1377
            DWORD len = MultiByteToWideChar( CP_ACP, 0, buf, -1, NULL, 0 );
            *idstr = IMalloc_Alloc( mllc, len * sizeof(WCHAR) );
            MultiByteToWideChar( CP_ACP, 0, buf, -1, *idstr, len );
Alexandre Julliard's avatar
Alexandre Julliard committed
1378 1379 1380 1381
	}
	return ret;
}

1382
/******************************************************************************
1383
 *		StringFromGUID2	[OLE32.@]
1384
 *		StringFromGUID2	[COMPOBJ.76]
Alexandre Julliard's avatar
Alexandre Julliard committed
1385
 *
1386 1387
 * Modified version of StringFromCLSID that allows you to specify max
 * buffer size.
Alexandre Julliard's avatar
Alexandre Julliard committed
1388
 *
1389 1390 1391 1392 1393
 * PARAMS
 *  id   [I] GUID to convert to string.
 *  str  [O] Buffer where the result will be stored.
 *  cmax [I] Size of the buffer in characters.
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
1394
 * RETURNS
1395 1396
 *	Success: The length of the resulting string in characters.
 *  Failure: 0.
Alexandre Julliard's avatar
Alexandre Julliard committed
1397
 */
1398
INT WINAPI StringFromGUID2(REFGUID id, LPOLESTR str, INT cmax)
Alexandre Julliard's avatar
Alexandre Julliard committed
1399
{
Alexandre Julliard's avatar
Alexandre Julliard committed
1400 1401 1402 1403
  char		xguid[80];

  if (WINE_StringFromCLSID(id,xguid))
  	return 0;
1404
  return MultiByteToWideChar( CP_ACP, 0, xguid, -1, str, cmax );
Alexandre Julliard's avatar
Alexandre Julliard committed
1405
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1406

1407 1408
/* open HKCR\\CLSID\\{string form of clsid}\\{keyname} key */
HRESULT COM_OpenKeyForCLSID(REFCLSID clsid, LPCWSTR keyname, REGSAM access, HKEY *subkey)
1409 1410 1411
{
    static const WCHAR wszCLSIDSlash[] = {'C','L','S','I','D','\\',0};
    WCHAR path[CHARS_IN_GUID + ARRAYSIZE(wszCLSIDSlash) - 1];
1412 1413 1414
    LONG res;
    HKEY key;

1415 1416
    strcpyW(path, wszCLSIDSlash);
    StringFromGUID2(clsid, path + strlenW(wszCLSIDSlash), CHARS_IN_GUID);
1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430
    res = RegOpenKeyExW(HKEY_CLASSES_ROOT, path, 0, keyname ? KEY_READ : access, &key);
    if (res == ERROR_FILE_NOT_FOUND)
        return REGDB_E_CLASSNOTREG;
    else if (res != ERROR_SUCCESS)
        return REGDB_E_READREGDB;

    if (!keyname)
    {
        *subkey = key;
        return S_OK;
    }

    res = RegOpenKeyExW(key, keyname, 0, access, subkey);
    RegCloseKey(key);
1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452
    if (res == ERROR_FILE_NOT_FOUND)
        return REGDB_E_KEYMISSING;
    else if (res != ERROR_SUCCESS)
        return REGDB_E_READREGDB;

    return S_OK;
}

/* open HKCR\\AppId\\{string form of appid clsid} key */
HRESULT COM_OpenKeyForAppIdFromCLSID(REFCLSID clsid, REGSAM access, HKEY *subkey)
{
    static const WCHAR szAppId[] = { 'A','p','p','I','d',0 };
    static const WCHAR szAppIdKey[] = { 'A','p','p','I','d','\\',0 };
    DWORD res;
    WCHAR buf[CHARS_IN_GUID];
    WCHAR keyname[ARRAYSIZE(szAppIdKey) + CHARS_IN_GUID];
    DWORD size;
    HKEY hkey;
    DWORD type;
    HRESULT hr;

    /* read the AppID value under the class's key */
1453
    hr = COM_OpenKeyForCLSID(clsid, NULL, KEY_READ, &hkey);
1454 1455 1456 1457
    if (FAILED(hr))
        return hr;

    size = sizeof(buf);
1458
    res = RegQueryValueExW(hkey, szAppId, NULL, &type, (LPBYTE)buf, &size);
1459 1460 1461 1462 1463 1464 1465 1466 1467
    RegCloseKey(hkey);
    if (res == ERROR_FILE_NOT_FOUND)
        return REGDB_E_KEYMISSING;
    else if (res != ERROR_SUCCESS || type!=REG_SZ)
        return REGDB_E_READREGDB;

    strcpyW(keyname, szAppIdKey);
    strcatW(keyname, buf);
    res = RegOpenKeyExW(HKEY_CLASSES_ROOT, keyname, 0, access, subkey);
1468 1469 1470 1471 1472 1473
    if (res == ERROR_FILE_NOT_FOUND)
        return REGDB_E_KEYMISSING;
    else if (res != ERROR_SUCCESS)
        return REGDB_E_READREGDB;

    return S_OK;
1474 1475
}

1476
/******************************************************************************
1477 1478
 *               ProgIDFromCLSID [OLE32.@]
 *
1479 1480 1481 1482
 * Converts a class id into the respective program ID.
 *
 * PARAMS
 *  clsid        [I] Class ID, as found in registry.
1483
 *  ppszProgID [O] Associated ProgID.
1484 1485 1486 1487 1488
 *
 * RETURNS
 *   S_OK
 *   E_OUTOFMEMORY
 *   REGDB_E_CLASSNOTREG if the given clsid has no associated ProgID
1489
 */
1490
HRESULT WINAPI ProgIDFromCLSID(REFCLSID clsid, LPOLESTR *ppszProgID)
1491
{
1492 1493 1494
    static const WCHAR wszProgID[] = {'P','r','o','g','I','D',0};
    HKEY     hkey;
    HRESULT  ret;
1495
    LONG progidlen = 0;
1496

1497 1498 1499 1500 1501 1502 1503
    if (!ppszProgID)
    {
        ERR("ppszProgId isn't optional\n");
        return E_INVALIDARG;
    }

    *ppszProgID = NULL;
1504 1505 1506 1507
    ret = COM_OpenKeyForCLSID(clsid, wszProgID, KEY_READ, &hkey);
    if (FAILED(ret))
        return ret;

1508
    if (RegQueryValueW(hkey, NULL, NULL, &progidlen))
1509 1510 1511 1512
      ret = REGDB_E_CLASSNOTREG;

    if (ret == S_OK)
    {
1513 1514
      *ppszProgID = CoTaskMemAlloc(progidlen * sizeof(WCHAR));
      if (*ppszProgID)
1515
      {
1516
        if (RegQueryValueW(hkey, NULL, *ppszProgID, &progidlen))
1517
          ret = REGDB_E_CLASSNOTREG;
1518
      }
1519 1520
      else
        ret = E_OUTOFMEMORY;
1521 1522
    }

1523 1524
    RegCloseKey(hkey);
    return ret;
1525 1526
}

1527
/******************************************************************************
1528
 *		CLSIDFromProgID	[OLE32.@]
1529
 *
1530 1531 1532 1533
 * Converts a program id into the respective GUID.
 *
 * PARAMS
 *  progid [I] Unicode program ID, as found in registry.
1534
 *  clsid  [O] Associated CLSID.
1535
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
1536
 * RETURNS
1537 1538
 *	Success: S_OK
 *  Failure: CO_E_CLASSSTRING - the given ProgID cannot be found.
Alexandre Julliard's avatar
Alexandre Julliard committed
1539
 */
1540
HRESULT WINAPI CLSIDFromProgID(LPCOLESTR progid, LPCLSID clsid)
1541 1542
{
    static const WCHAR clsidW[] = { '\\','C','L','S','I','D',0 };
1543
    WCHAR buf2[CHARS_IN_GUID];
1544
    LONG buf2len = sizeof(buf2);
1545
    HKEY xhkey;
1546
    WCHAR *buf;
1547

1548 1549 1550 1551 1552 1553 1554 1555 1556 1557
    if (!progid || !clsid)
    {
        ERR("neither progid (%p) nor clsid (%p) are optional\n", progid, clsid);
        return E_INVALIDARG;
    }

    /* initialise clsid in case of failure */
    memset(clsid, 0, sizeof(*clsid));

    buf = HeapAlloc( GetProcessHeap(),0,(strlenW(progid)+8) * sizeof(WCHAR) );
1558 1559 1560 1561 1562
    strcpyW( buf, progid );
    strcatW( buf, clsidW );
    if (RegOpenKeyW(HKEY_CLASSES_ROOT,buf,&xhkey))
    {
        HeapFree(GetProcessHeap(),0,buf);
1563
        WARN("couldn't open key for ProgID %s\n", debugstr_w(progid));
1564 1565 1566
        return CO_E_CLASSSTRING;
    }
    HeapFree(GetProcessHeap(),0,buf);
Alexandre Julliard's avatar
Alexandre Julliard committed
1567

1568
    if (RegQueryValueW(xhkey,NULL,buf2,&buf2len))
1569 1570
    {
        RegCloseKey(xhkey);
1571
        WARN("couldn't query clsid value for ProgID %s\n", debugstr_w(progid));
1572 1573 1574
        return CO_E_CLASSSTRING;
    }
    RegCloseKey(xhkey);
1575
    return CLSIDFromString(buf2,clsid);
Alexandre Julliard's avatar
Alexandre Julliard committed
1576 1577
}

1578 1579

/*****************************************************************************
1580
 *             CoGetPSClsid [OLE32.@]
1581
 *
Robert Shearman's avatar
Robert Shearman committed
1582 1583
 * Retrieves the CLSID of the proxy/stub factory that implements
 * IPSFactoryBuffer for the specified interface.
1584
 *
1585 1586 1587 1588 1589 1590 1591
 * PARAMS
 *  riid   [I] Interface whose proxy/stub CLSID is to be returned.
 *  pclsid [O] Where to store returned proxy/stub CLSID.
 * 
 * RETURNS
 *   S_OK
 *   E_OUTOFMEMORY
Robert Shearman's avatar
Robert Shearman committed
1592
 *   REGDB_E_IIDNOTREG if no PSFactoryBuffer is associated with the IID, or it could not be parsed
1593 1594 1595
 *
 * NOTES
 *
1596 1597 1598 1599
 * The standard marshaller activates the object with the CLSID
 * returned and uses the CreateProxy and CreateStub methods on its
 * IPSFactoryBuffer interface to construct the proxies and stubs for a
 * given object.
1600 1601
 *
 * CoGetPSClsid determines this CLSID by searching the
1602 1603 1604
 * HKEY_CLASSES_ROOT\Interface\{string form of riid}\ProxyStubClsid32
 * in the registry and any interface id registered by
 * CoRegisterPSClsid within the current process.
1605
 *
1606
 * BUGS
1607
 *
1608
 * Native returns S_OK for interfaces with a key in HKCR\Interface, but
Robert Shearman's avatar
Robert Shearman committed
1609 1610
 * without a ProxyStubClsid32 key and leaves garbage in pclsid. This should be
 * considered a bug in native unless an application depends on this (unlikely).
1611 1612 1613
 *
 * SEE ALSO
 *  CoRegisterPSClsid.
1614
 */
1615
HRESULT WINAPI CoGetPSClsid(REFIID riid, CLSID *pclsid)
1616
{
1617 1618 1619 1620
    static const WCHAR wszInterface[] = {'I','n','t','e','r','f','a','c','e','\\',0};
    static const WCHAR wszPSC[] = {'\\','P','r','o','x','y','S','t','u','b','C','l','s','i','d','3','2',0};
    WCHAR path[ARRAYSIZE(wszInterface) - 1 + CHARS_IN_GUID - 1 + ARRAYSIZE(wszPSC)];
    WCHAR value[CHARS_IN_GUID];
1621
    LONG len;
1622
    HKEY hkey;
1623 1624
    APARTMENT *apt = COM_CurrentApt();
    struct registered_psclsid *registered_psclsid;
1625 1626 1627

    TRACE("() riid=%s, pclsid=%p\n", debugstr_guid(riid), pclsid);

1628 1629 1630 1631 1632 1633
    if (!apt)
    {
        ERR("apartment not initialised\n");
        return CO_E_NOTINITIALIZED;
    }

1634 1635 1636 1637 1638 1639
    if (!pclsid)
    {
        ERR("pclsid isn't optional\n");
        return E_INVALIDARG;
    }

1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651
    EnterCriticalSection(&apt->cs);

    LIST_FOR_EACH_ENTRY(registered_psclsid, &apt->psclsids, struct registered_psclsid, entry)
        if (IsEqualIID(&registered_psclsid->iid, riid))
        {
            *pclsid = registered_psclsid->clsid;
            LeaveCriticalSection(&apt->cs);
            return S_OK;
        }

    LeaveCriticalSection(&apt->cs);

1652 1653 1654 1655
    /* Interface\\{string form of riid}\\ProxyStubClsid32 */
    strcpyW(path, wszInterface);
    StringFromGUID2(riid, path + ARRAYSIZE(wszInterface) - 1, CHARS_IN_GUID);
    strcpyW(path + ARRAYSIZE(wszInterface) - 1 + CHARS_IN_GUID - 1, wszPSC);
1656 1657

    /* Open the key.. */
1658
    if (RegOpenKeyExW(HKEY_CLASSES_ROOT, path, 0, KEY_READ, &hkey))
1659
    {
Robert Shearman's avatar
Robert Shearman committed
1660 1661
        WARN("No PSFactoryBuffer object is registered for IID %s\n", debugstr_guid(riid));
        return REGDB_E_IIDNOTREG;
1662 1663 1664
    }

    /* ... Once we have the key, query the registry to get the
1665
       value of CLSID as a string, and convert it into a
1666
       proper CLSID structure to be passed back to the app */
1667 1668
    len = sizeof(value);
    if (ERROR_SUCCESS != RegQueryValueW(hkey, NULL, value, &len))
1669
    {
1670
        RegCloseKey(hkey);
Robert Shearman's avatar
Robert Shearman committed
1671
        return REGDB_E_IIDNOTREG;
1672
    }
1673
    RegCloseKey(hkey);
1674 1675 1676

    /* We have the CLSid we want back from the registry as a string, so
       lets convert it into a CLSID structure */
1677
    if (CLSIDFromString(value, pclsid) != NOERROR)
Robert Shearman's avatar
Robert Shearman committed
1678
        return REGDB_E_IIDNOTREG;
1679 1680

    TRACE ("() Returning CLSID=%s\n", debugstr_guid(pclsid));
Robert Shearman's avatar
Robert Shearman committed
1681
    return S_OK;
1682 1683
}

1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744
/*****************************************************************************
 *             CoRegisterPSClsid [OLE32.@]
 *
 * Register a proxy/stub CLSID for the given interface in the current process
 * only.
 *
 * PARAMS
 *  riid   [I] Interface whose proxy/stub CLSID is to be registered.
 *  rclsid [I] CLSID of the proxy/stub.
 * 
 * RETURNS
 *   Success: S_OK
 *   Failure: E_OUTOFMEMORY
 *
 * NOTES
 *
 * This function does not add anything to the registry and the effects are
 * limited to the lifetime of the current process.
 *
 * SEE ALSO
 *  CoGetPSClsid.
 */
HRESULT WINAPI CoRegisterPSClsid(REFIID riid, REFCLSID rclsid)
{
    APARTMENT *apt = COM_CurrentApt();
    struct registered_psclsid *registered_psclsid;

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

    if (!apt)
    {
        ERR("apartment not initialised\n");
        return CO_E_NOTINITIALIZED;
    }

    EnterCriticalSection(&apt->cs);

    LIST_FOR_EACH_ENTRY(registered_psclsid, &apt->psclsids, struct registered_psclsid, entry)
        if (IsEqualIID(&registered_psclsid->iid, riid))
        {
            registered_psclsid->clsid = *rclsid;
            LeaveCriticalSection(&apt->cs);
            return S_OK;
        }

    registered_psclsid = HeapAlloc(GetProcessHeap(), 0, sizeof(struct registered_psclsid));
    if (!registered_psclsid)
    {
        LeaveCriticalSection(&apt->cs);
        return E_OUTOFMEMORY;
    }

    registered_psclsid->iid = *riid;
    registered_psclsid->clsid = *rclsid;
    list_add_head(&apt->psclsids, &registered_psclsid->entry);

    LeaveCriticalSection(&apt->cs);

    return S_OK;
}

1745

1746 1747 1748
/***
 * COM_GetRegisteredClassObject
 *
1749
 * This internal method is used to scan the registered class list to
1750 1751
 * find a class object.
 *
1752
 * Params:
1753 1754 1755 1756 1757 1758
 *   rclsid        Class ID of the class to find.
 *   dwClsContext  Class context to match.
 *   ppv           [out] returns a pointer to the class object. Complying
 *                 to normal COM usage, this method will increase the
 *                 reference count on this object.
 */
1759 1760
static HRESULT COM_GetRegisteredClassObject(const struct apartment *apt, REFCLSID rclsid,
                                            DWORD dwClsContext, LPUNKNOWN* ppUnk)
1761
{
1762
  HRESULT hr = S_FALSE;
1763
  RegisteredClass *curClass;
1764

1765 1766 1767 1768 1769
  /*
   * Sanity check
   */
  assert(ppUnk!=0);

1770
  EnterCriticalSection( &csRegisteredClassList );
1771

1772
  LIST_FOR_EACH_ENTRY(curClass, &RegisteredClassList, RegisteredClass, entry)
1773 1774
  {
    /*
1775
     * Check if we have a match on the class ID and context.
1776
     */
1777 1778
    if ((apt->oxid == curClass->apartment_id) &&
        (dwClsContext & curClass->runContext) &&
1779
        IsEqualGUID(&(curClass->classIdentifier), rclsid))
1780 1781 1782 1783 1784 1785 1786 1787
    {
      /*
       * We have a match, return the pointer to the class object.
       */
      *ppUnk = curClass->classObject;

      IUnknown_AddRef(curClass->classObject);

1788
      hr = S_OK;
1789
      break;
1790 1791 1792
    }
  }

1793
  LeaveCriticalSection( &csRegisteredClassList );
1794

1795
  return hr;
1796 1797
}

1798
/******************************************************************************
1799
 *		CoRegisterClassObject	[OLE32.@]
1800
 *
1801 1802 1803
 * Registers the class object for a given class ID. Servers housed in EXE
 * files use this method instead of exporting DllGetClassObject to allow
 * other code to connect to their objects.
1804
 *
1805 1806 1807 1808 1809 1810 1811
 * PARAMS
 *  rclsid       [I] CLSID of the object to register.
 *  pUnk         [I] IUnknown of the object.
 *  dwClsContext [I] CLSCTX flags indicating the context in which to run the executable.
 *  flags        [I] REGCLS flags indicating how connections are made.
 *  lpdwRegister [I] A unique cookie that can be passed to CoRevokeClassObject.
 *
1812
 * RETURNS
1813 1814
 *   S_OK on success,
 *   E_INVALIDARG if lpdwRegister or pUnk are NULL,
1815 1816 1817 1818
 *   CO_E_OBJISREG if the object is already registered. We should not return this.
 *
 * SEE ALSO
 *   CoRevokeClassObject, CoGetClassObject
1819
 *
1820 1821 1822 1823 1824
 * NOTES
 *  In-process objects are only registered for the current apartment.
 *  CoGetClassObject() and CoCreateInstance() will not return objects registered
 *  in other apartments.
 *
1825 1826 1827
 * BUGS
 *  MSDN claims that multiple interface registrations are legal, but we
 *  can't do that with our current implementation.
Alexandre Julliard's avatar
Alexandre Julliard committed
1828
 */
1829
HRESULT WINAPI CoRegisterClassObject(
1830 1831 1832 1833 1834
    REFCLSID rclsid,
    LPUNKNOWN pUnk,
    DWORD dwClsContext,
    DWORD flags,
    LPDWORD lpdwRegister)
1835 1836 1837 1838
{
  RegisteredClass* newClass;
  LPUNKNOWN        foundObject;
  HRESULT          hr;
1839
  APARTMENT *apt;
Alexandre Julliard's avatar
Alexandre Julliard committed
1840

1841
  TRACE("(%s,%p,0x%08x,0x%08x,%p)\n",
1842
	debugstr_guid(rclsid),pUnk,dwClsContext,flags,lpdwRegister);
1843 1844 1845 1846

  if ( (lpdwRegister==0) || (pUnk==0) )
    return E_INVALIDARG;

1847 1848
  apt = COM_CurrentApt();
  if (!apt)
1849 1850 1851 1852 1853
  {
      ERR("COM was not initialized\n");
      return CO_E_NOTINITIALIZED;
  }

1854 1855
  *lpdwRegister = 0;

1856 1857 1858 1859 1860
  /* REGCLS_MULTIPLEUSE implies registering as inproc server. This is what
   * differentiates the flag from REGCLS_MULTI_SEPARATE. */
  if (flags & REGCLS_MULTIPLEUSE)
    dwClsContext |= CLSCTX_INPROC_SERVER;

1861 1862 1863 1864
  /*
   * First, check if the class is already registered.
   * If it is, this should cause an error.
   */
1865
  hr = COM_GetRegisteredClassObject(apt, rclsid, dwClsContext, &foundObject);
1866
  if (hr == S_OK) {
1867 1868 1869 1870 1871 1872
    if (flags & REGCLS_MULTIPLEUSE) {
      if (dwClsContext & CLSCTX_LOCAL_SERVER)
        hr = CoLockObjectExternal(foundObject, TRUE, FALSE);
      IUnknown_Release(foundObject);
      return hr;
    }
1873
    IUnknown_Release(foundObject);
1874
    ERR("object already registered for class %s\n", debugstr_guid(rclsid));
1875 1876
    return CO_E_OBJISREG;
  }
1877

1878
  newClass = HeapAlloc(GetProcessHeap(), 0, sizeof(RegisteredClass));
1879 1880
  if ( newClass == NULL )
    return E_OUTOFMEMORY;
1881 1882

  newClass->classIdentifier = *rclsid;
1883
  newClass->apartment_id    = apt->oxid;
1884 1885
  newClass->runContext      = dwClsContext;
  newClass->connectFlags    = flags;
1886
  newClass->pMarshaledData  = NULL;
1887
  newClass->RpcRegistration = NULL;
1888

1889 1890
  /*
   * Use the address of the chain node as the cookie since we are sure it's
1891
   * unique. FIXME: not on 64-bit platforms.
1892
   */
1893 1894 1895
  newClass->dwCookie        = (DWORD)newClass;

  /*
1896
   * Since we're making a copy of the object pointer, we have to increase its
1897 1898 1899 1900 1901
   * reference count.
   */
  newClass->classObject     = pUnk;
  IUnknown_AddRef(newClass->classObject);

1902 1903
  EnterCriticalSection( &csRegisteredClassList );
  list_add_tail(&RegisteredClassList, &newClass->entry);
1904 1905
  LeaveCriticalSection( &csRegisteredClassList );

1906
  *lpdwRegister = newClass->dwCookie;
1907

1908
  if (dwClsContext & CLSCTX_LOCAL_SERVER) {
1909 1910
      hr = CreateStreamOnHGlobal(0, TRUE, &newClass->pMarshaledData);
      if (hr) {
1911
          FIXME("Failed to create stream on hglobal, %x\n", hr);
1912 1913 1914
          return hr;
      }
      hr = CoMarshalInterface(newClass->pMarshaledData, &IID_IClassFactory,
1915
                              newClass->classObject, MSHCTX_LOCAL, NULL,
1916 1917
                              MSHLFLAGS_TABLESTRONG);
      if (hr) {
1918
          FIXME("CoMarshalInterface failed, %x!\n",hr);
1919 1920 1921
          return hr;
      }

1922 1923
      hr = RPC_StartLocalServer(&newClass->classIdentifier,
                                newClass->pMarshaledData,
1924
                                flags & (REGCLS_MULTIPLEUSE|REGCLS_MULTI_SEPARATE),
1925
                                &newClass->RpcRegistration);
1926
  }
1927
  return S_OK;
Alexandre Julliard's avatar
Alexandre Julliard committed
1928 1929
}

1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952
static void COM_RevokeRegisteredClassObject(RegisteredClass *curClass)
{
    list_remove(&curClass->entry);

    if (curClass->runContext & CLSCTX_LOCAL_SERVER)
        RPC_StopLocalServer(curClass->RpcRegistration);

    /*
     * Release the reference to the class object.
     */
    IUnknown_Release(curClass->classObject);

    if (curClass->pMarshaledData)
    {
        LARGE_INTEGER zero;
        memset(&zero, 0, sizeof(zero));
        IStream_Seek(curClass->pMarshaledData, zero, STREAM_SEEK_SET, NULL);
        CoReleaseMarshalData(curClass->pMarshaledData);
    }

    HeapFree(GetProcessHeap(), 0, curClass);
}

1953
static void COM_RevokeAllClasses(const struct apartment *apt)
1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967
{
  RegisteredClass *curClass, *cursor;

  EnterCriticalSection( &csRegisteredClassList );

  LIST_FOR_EACH_ENTRY_SAFE(curClass, cursor, &RegisteredClassList, RegisteredClass, entry)
  {
    if (curClass->apartment_id == apt->oxid)
      COM_RevokeRegisteredClassObject(curClass);
  }

  LeaveCriticalSection( &csRegisteredClassList );
}

1968
/***********************************************************************
1969
 *           CoRevokeClassObject [OLE32.@]
1970
 *
1971 1972 1973 1974
 * Removes a class object from the class registry.
 *
 * PARAMS
 *  dwRegister [I] Cookie returned from CoRegisterClassObject().
1975
 *
1976 1977 1978 1979
 * RETURNS
 *  Success: S_OK.
 *  Failure: HRESULT code.
 *
1980 1981 1982 1983
 * NOTES
 *  Must be called from the same apartment that called CoRegisterClassObject(),
 *  otherwise it will fail with RPC_E_WRONG_THREAD.
 *
1984 1985
 * SEE ALSO
 *  CoRegisterClassObject
1986
 */
1987
HRESULT WINAPI CoRevokeClassObject(
1988
        DWORD dwRegister)
1989
{
1990
  HRESULT hr = E_INVALIDARG;
1991
  RegisteredClass *curClass;
1992
  APARTMENT *apt;
1993

1994
  TRACE("(%08x)\n",dwRegister);
1995

1996 1997 1998 1999 2000 2001 2002
  apt = COM_CurrentApt();
  if (!apt)
  {
    ERR("COM was not initialized\n");
    return CO_E_NOTINITIALIZED;
  }

2003 2004
  EnterCriticalSection( &csRegisteredClassList );

2005
  LIST_FOR_EACH_ENTRY(curClass, &RegisteredClassList, RegisteredClass, entry)
2006 2007 2008 2009 2010 2011
  {
    /*
     * Check if we have a match on the cookie.
     */
    if (curClass->dwCookie == dwRegister)
    {
2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022
      if (curClass->apartment_id == apt->oxid)
      {
          COM_RevokeRegisteredClassObject(curClass);
          hr = S_OK;
      }
      else
      {
          ERR("called from wrong apartment, should be called from %s\n",
              wine_dbgstr_longlong(curClass->apartment_id));
          hr = RPC_E_WRONG_THREAD;
      }
2023
      break;
2024
    }
2025 2026
  }

2027
  LeaveCriticalSection( &csRegisteredClassList );
2028

2029
  return hr;
2030 2031
}

2032
/***********************************************************************
2033
 *	COM_RegReadPath	[internal]
2034
 *
2035
 *	Reads a registry value and expands it when necessary
2036
 */
2037
static DWORD COM_RegReadPath(HKEY hkeyroot, const WCHAR *keyname, const WCHAR *valuename, WCHAR * dst, DWORD dstlen)
2038
{
2039
	DWORD ret;
2040 2041
	HKEY key;
	DWORD keytype;
2042 2043
	WCHAR src[MAX_PATH];
	DWORD dwLength = dstlen * sizeof(WCHAR);
2044

2045 2046
	if((ret = RegOpenKeyExW(hkeyroot, keyname, 0, KEY_READ, &key)) == ERROR_SUCCESS) {
          if( (ret = RegQueryValueExW(key, NULL, NULL, &keytype, (LPBYTE)src, &dwLength)) == ERROR_SUCCESS ) {
2047
            if (keytype == REG_EXPAND_SZ) {
2048
              if (dstlen <= ExpandEnvironmentStringsW(src, dst, dstlen)) ret = ERROR_MORE_DATA;
2049
            } else {
2050
              lstrcpynW(dst, src, dstlen);
2051 2052 2053 2054
            }
	  }
          RegCloseKey (key);
	}
2055
	return ret;
2056 2057
}

2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069
static void get_threading_model(HKEY key, LPWSTR value, DWORD len)
{
    static const WCHAR wszThreadingModel[] = {'T','h','r','e','a','d','i','n','g','M','o','d','e','l',0};
    DWORD keytype;
    DWORD ret;
    DWORD dwLength = len * sizeof(WCHAR);

    ret = RegQueryValueExW(key, wszThreadingModel, NULL, &keytype, (LPBYTE)value, &dwLength);
    if ((ret != ERROR_SUCCESS) || (keytype != REG_SZ))
        value[0] = '\0';
}

2070 2071
static HRESULT get_inproc_class_object(APARTMENT *apt, HKEY hkeydll,
                                       REFCLSID rclsid, REFIID riid, void **ppv)
2072
{
2073 2074 2075
    static const WCHAR wszApartment[] = {'A','p','a','r','t','m','e','n','t',0};
    static const WCHAR wszFree[] = {'F','r','e','e',0};
    static const WCHAR wszBoth[] = {'B','o','t','h',0};
2076
    WCHAR dllpath[MAX_PATH+1];
2077
    WCHAR threading_model[10 /* strlenW(L"apartment")+1 */];
2078

2079 2080 2081 2082 2083
    get_threading_model(hkeydll, threading_model, ARRAYSIZE(threading_model));
    /* "Apartment" */
    if (!strcmpiW(threading_model, wszApartment))
    {
        if (apt->multi_threaded)
2084
            return apartment_hostobject_in_hostapt(apt, FALSE, FALSE, hkeydll, rclsid, riid, ppv);
2085 2086 2087 2088 2089
    }
    /* "Free" */
    else if (!strcmpiW(threading_model, wszFree))
    {
        if (!apt->multi_threaded)
2090
            return apartment_hostobject_in_hostapt(apt, TRUE, FALSE, hkeydll, rclsid, riid, ppv);
2091 2092 2093 2094 2095
    }
    /* everything except "Apartment", "Free" and "Both" */
    else if (strcmpiW(threading_model, wszBoth))
    {
        /* everything else is main-threaded */
2096 2097 2098 2099 2100
        if (threading_model[0])
            FIXME("unrecognised threading model %s for object %s, should be main-threaded?\n",
                debugstr_w(threading_model), debugstr_guid(rclsid));

        if (apt->multi_threaded || !apt->main)
2101
            return apartment_hostobject_in_hostapt(apt, FALSE, TRUE, hkeydll, rclsid, riid, ppv);
2102 2103
    }

2104 2105 2106 2107 2108 2109 2110
    if (COM_RegReadPath(hkeydll, NULL, NULL, dllpath, ARRAYSIZE(dllpath)) != ERROR_SUCCESS)
    {
        /* failure: CLSID is not found in registry */
        WARN("class %s not registered inproc\n", debugstr_guid(rclsid));
        return REGDB_E_CLASSNOTREG;
    }

2111 2112 2113
    return apartment_getclassobject(apt, dllpath,
                                    !strcmpiW(threading_model, wszApartment),
                                    rclsid, riid, ppv);
2114 2115
}

Alexandre Julliard's avatar
Alexandre Julliard committed
2116
/***********************************************************************
2117
 *           CoGetClassObject [OLE32.@]
2118
 *
2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140
 * Creates an object of the specified class.
 *
 * PARAMS
 *  rclsid       [I] Class ID to create an instance of.
 *  dwClsContext [I] Flags to restrict the location of the created instance.
 *  pServerInfo  [I] Optional. Details for connecting to a remote server.
 *  iid          [I] The ID of the interface of the instance to return.
 *  ppv          [O] On returns, contains a pointer to the specified interface of the object.
 *
 * RETURNS
 *  Success: S_OK
 *  Failure: HRESULT code.
 *
 * NOTES
 *  The dwClsContext parameter can be one or more of the following:
 *| CLSCTX_INPROC_SERVER - Use an in-process server, such as from a DLL.
 *| CLSCTX_INPROC_HANDLER - Use an in-process object which handles certain functions for an object running in another process.
 *| CLSCTX_LOCAL_SERVER - Connect to an object running in another process.
 *| CLSCTX_REMOTE_SERVER - Connect to an object running on another machine.
 *
 * SEE ALSO
 *  CoCreateInstance()
Alexandre Julliard's avatar
Alexandre Julliard committed
2141
 */
2142 2143
HRESULT WINAPI CoGetClassObject(
    REFCLSID rclsid, DWORD dwClsContext, COSERVERINFO *pServerInfo,
2144 2145
    REFIID iid, LPVOID *ppv)
{
2146 2147
    LPUNKNOWN	regClassObject;
    HRESULT	hres = E_UNEXPECTED;
2148
    APARTMENT  *apt;
2149

2150
    TRACE("\n\tCLSID:\t%s,\n\tIID:\t%s\n", debugstr_guid(rclsid), debugstr_guid(iid));
Alexandre Julliard's avatar
Alexandre Julliard committed
2151

2152 2153 2154 2155 2156
    if (!ppv)
        return E_INVALIDARG;

    *ppv = NULL;

2157 2158
    apt = COM_CurrentApt();
    if (!apt)
2159 2160 2161 2162 2163
    {
        ERR("apartment not initialised\n");
        return CO_E_NOTINITIALIZED;
    }

2164 2165 2166 2167 2168
    if (pServerInfo) {
	FIXME("\tpServerInfo: name=%s\n",debugstr_w(pServerInfo->pwszName));
	FIXME("\t\tpAuthInfo=%p\n",pServerInfo->pAuthInfo);
    }

2169
    /*
2170
     * First, try and see if we can't match the class ID with one of the
2171 2172
     * registered classes.
     */
2173 2174
    if (S_OK == COM_GetRegisteredClassObject(apt, rclsid, dwClsContext,
                                             &regClassObject))
2175
    {
2176
      /* Get the required interface from the retrieved pointer. */
2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188
      hres = IUnknown_QueryInterface(regClassObject, iid, ppv);

      /*
       * Since QI got another reference on the pointer, we want to release the
       * one we already have. If QI was unsuccessful, this will release the object. This
       * is good since we are not returning it in the "out" parameter.
       */
      IUnknown_Release(regClassObject);

      return hres;
    }

2189 2190
    /* First try in-process server */
    if (CLSCTX_INPROC_SERVER & dwClsContext)
2191 2192 2193 2194
    {
        static const WCHAR wszInprocServer32[] = {'I','n','p','r','o','c','S','e','r','v','e','r','3','2',0};
        HKEY hkey;

2195 2196 2197
        if (IsEqualCLSID(rclsid, &CLSID_InProcFreeMarshaler))
            return FTMarshalCF_Create(iid, ppv);

2198 2199
        hres = COM_OpenKeyForCLSID(rclsid, wszInprocServer32, KEY_READ, &hkey);
        if (FAILED(hres))
2200
        {
2201 2202
            if (hres == REGDB_E_CLASSNOTREG)
                ERR("class %s not registered\n", debugstr_guid(rclsid));
2203 2204
            else if (hres == REGDB_E_KEYMISSING)
            {
2205
                WARN("class %s not registered as in-proc server\n", debugstr_guid(rclsid));
2206 2207
                hres = REGDB_E_CLASSNOTREG;
            }
2208 2209 2210 2211
        }

        if (SUCCEEDED(hres))
        {
2212
            hres = get_inproc_class_object(apt, hkey, rclsid, iid, ppv);
2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232
            RegCloseKey(hkey);
        }

        /* return if we got a class, otherwise fall through to one of the
         * other types */
        if (SUCCEEDED(hres))
            return hres;
    }

    /* Next try in-process handler */
    if (CLSCTX_INPROC_HANDLER & dwClsContext)
    {
        static const WCHAR wszInprocHandler32[] = {'I','n','p','r','o','c','H','a','n','d','l','e','r','3','2',0};
        HKEY hkey;

        hres = COM_OpenKeyForCLSID(rclsid, wszInprocHandler32, KEY_READ, &hkey);
        if (FAILED(hres))
        {
            if (hres == REGDB_E_CLASSNOTREG)
                ERR("class %s not registered\n", debugstr_guid(rclsid));
2233 2234
            else if (hres == REGDB_E_KEYMISSING)
            {
2235
                WARN("class %s not registered in-proc handler\n", debugstr_guid(rclsid));
2236 2237
                hres = REGDB_E_CLASSNOTREG;
            }
2238
        }
2239

2240
        if (SUCCEEDED(hres))
2241
        {
2242
            hres = get_inproc_class_object(apt, hkey, rclsid, iid, ppv);
2243
            RegCloseKey(hkey);
2244
        }
2245 2246 2247 2248 2249

        /* return if we got a class, otherwise fall through to one of the
         * other types */
        if (SUCCEEDED(hres))
            return hres;
2250
    }
2251

2252
    /* Next try out of process */
2253 2254
    if (CLSCTX_LOCAL_SERVER & dwClsContext)
    {
2255 2256 2257
        hres = RPC_GetLocalClassObject(rclsid,iid,ppv);
        if (SUCCEEDED(hres))
            return hres;
2258
    }
2259

2260
    /* Finally try remote: this requires networked DCOM (a lot of work) */
2261 2262 2263
    if (CLSCTX_REMOTE_SERVER & dwClsContext)
    {
        FIXME ("CLSCTX_REMOTE_SERVER not supported\n");
2264
        hres = E_NOINTERFACE;
2265 2266
    }

2267
    if (FAILED(hres))
2268
        ERR("no class object %s could be created for context 0x%x\n",
2269
            debugstr_guid(rclsid), dwClsContext);
Alexandre Julliard's avatar
Alexandre Julliard committed
2270 2271
    return hres;
}
2272

2273
/***********************************************************************
2274
 *        CoResumeClassObjects (OLE32.@)
2275
 *
2276 2277 2278 2279 2280
 * Resumes all class objects registered with REGCLS_SUSPENDED.
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: HRESULT code.
2281 2282 2283
 */
HRESULT WINAPI CoResumeClassObjects(void)
{
2284
       FIXME("stub\n");
2285 2286 2287
	return S_OK;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
2288
/***********************************************************************
2289
 *           CoCreateInstance [OLE32.@]
2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317
 *
 * Creates an instance of the specified class.
 *
 * PARAMS
 *  rclsid       [I] Class ID to create an instance of.
 *  pUnkOuter    [I] Optional outer unknown to allow aggregation with another object.
 *  dwClsContext [I] Flags to restrict the location of the created instance.
 *  iid          [I] The ID of the interface of the instance to return.
 *  ppv          [O] On returns, contains a pointer to the specified interface of the instance.
 *
 * RETURNS
 *  Success: S_OK
 *  Failure: HRESULT code.
 *
 * NOTES
 *  The dwClsContext parameter can be one or more of the following:
 *| CLSCTX_INPROC_SERVER - Use an in-process server, such as from a DLL.
 *| CLSCTX_INPROC_HANDLER - Use an in-process object which handles certain functions for an object running in another process.
 *| CLSCTX_LOCAL_SERVER - Connect to an object running in another process.
 *| CLSCTX_REMOTE_SERVER - Connect to an object running on another machine.
 *
 * Aggregation is the concept of deferring the IUnknown of an object to another
 * object. This allows a separate object to behave as though it was part of
 * the object and to allow this the pUnkOuter parameter can be set. Note that
 * not all objects support having an outer of unknown.
 *
 * SEE ALSO
 *  CoGetClassObject()
Alexandre Julliard's avatar
Alexandre Julliard committed
2318
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
2319 2320 2321 2322
HRESULT WINAPI CoCreateInstance(
	REFCLSID rclsid,
	LPUNKNOWN pUnkOuter,
	DWORD dwClsContext,
Alexandre Julliard's avatar
Alexandre Julliard committed
2323
	REFIID iid,
2324
	LPVOID *ppv)
2325
{
2326 2327
  HRESULT hres;
  LPCLASSFACTORY lpclf = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
2328

2329
  TRACE("(rclsid=%s, pUnkOuter=%p, dwClsContext=%08x, riid=%s, ppv=%p)\n", debugstr_guid(rclsid),
2330 2331
        pUnkOuter, dwClsContext, debugstr_guid(iid), ppv);

2332 2333 2334 2335 2336 2337 2338 2339 2340 2341
  /*
   * Sanity check
   */
  if (ppv==0)
    return E_POINTER;

  /*
   * Initialize the "out" parameter
   */
  *ppv = 0;
2342

2343 2344 2345 2346 2347 2348
  if (!COM_CurrentApt())
  {
      ERR("apartment not initialised\n");
      return CO_E_NOTINITIALIZED;
  }

2349 2350 2351 2352 2353
  /*
   * The Standard Global Interface Table (GIT) object is a process-wide singleton.
   * Rather than create a class factory, we can just check for it here
   */
  if (IsEqualIID(rclsid, &CLSID_StdGlobalInterfaceTable)) {
2354
    if (StdGlobalInterfaceTableInstance == NULL)
2355 2356 2357
      StdGlobalInterfaceTableInstance = StdGlobalInterfaceTable_Construct();
    hres = IGlobalInterfaceTable_QueryInterface( (IGlobalInterfaceTable*) StdGlobalInterfaceTableInstance, iid, ppv);
    if (hres) return hres;
2358

2359 2360 2361
    TRACE("Retrieved GIT (%p)\n", *ppv);
    return S_OK;
  }
2362

2363 2364 2365 2366 2367 2368
  /*
   * Get a class factory to construct the object we want.
   */
  hres = CoGetClassObject(rclsid,
			  dwClsContext,
			  NULL,
2369
			  &IID_IClassFactory,
2370 2371
			  (LPVOID)&lpclf);

2372
  if (FAILED(hres))
2373 2374 2375 2376 2377
    return hres;

  /*
   * Create the object and don't forget to release the factory
   */
2378 2379
	hres = IClassFactory_CreateInstance(lpclf, pUnkOuter, iid, ppv);
	IClassFactory_Release(lpclf);
2380
	if(FAILED(hres))
2381
          FIXME("no instance created for interface %s of class %s, hres is 0x%08x\n",
2382
		debugstr_guid(iid), debugstr_guid(rclsid),hres);
2383

Alexandre Julliard's avatar
Alexandre Julliard committed
2384
	return hres;
Alexandre Julliard's avatar
Alexandre Julliard committed
2385 2386
}

2387
/***********************************************************************
2388
 *           CoCreateInstanceEx [OLE32.@]
2389 2390
 */
HRESULT WINAPI CoCreateInstanceEx(
2391
  REFCLSID      rclsid,
2392
  LPUNKNOWN     pUnkOuter,
2393
  DWORD         dwClsContext,
2394 2395 2396 2397 2398 2399 2400
  COSERVERINFO* pServerInfo,
  ULONG         cmq,
  MULTI_QI*     pResults)
{
  IUnknown* pUnk = NULL;
  HRESULT   hr;
  ULONG     index;
2401
  ULONG     successCount = 0;
2402 2403 2404 2405 2406 2407

  /*
   * Sanity check
   */
  if ( (cmq==0) || (pResults==NULL))
    return E_INVALIDARG;
2408

2409
  if (pServerInfo!=NULL)
2410
    FIXME("() non-NULL pServerInfo not supported!\n");
2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421

  /*
   * Initialize all the "out" parameters.
   */
  for (index = 0; index < cmq; index++)
  {
    pResults[index].pItf = NULL;
    pResults[index].hr   = E_NOINTERFACE;
  }

  /*
2422
   * Get the object and get its IUnknown pointer.
2423
   */
2424
  hr = CoCreateInstance(rclsid,
2425 2426 2427
			pUnkOuter,
			dwClsContext,
			&IID_IUnknown,
2428
			(VOID**)&pUnk);
2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458

  if (hr)
    return hr;

  /*
   * Then, query for all the interfaces requested.
   */
  for (index = 0; index < cmq; index++)
  {
    pResults[index].hr = IUnknown_QueryInterface(pUnk,
						 pResults[index].pIID,
						 (VOID**)&(pResults[index].pItf));

    if (pResults[index].hr == S_OK)
      successCount++;
  }

  /*
   * Release our temporary unknown pointer.
   */
  IUnknown_Release(pUnk);

  if (successCount == 0)
    return E_NOINTERFACE;

  if (successCount!=cmq)
    return CO_S_NOTALLINTERFACES;

  return S_OK;
}
2459

Ove Kaaven's avatar
Ove Kaaven committed
2460
/***********************************************************************
2461
 *           CoLoadLibrary (OLE32.@)
2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474
 *
 * Loads a library.
 *
 * PARAMS
 *  lpszLibName [I] Path to library.
 *  bAutoFree   [I] Whether the library should automatically be freed.
 *
 * RETURNS
 *  Success: Handle to loaded library.
 *  Failure: NULL.
 *
 * SEE ALSO
 *  CoFreeLibrary, CoFreeAllLibraries, CoFreeUnusedLibraries
Ove Kaaven's avatar
Ove Kaaven committed
2475
 */
2476
HINSTANCE WINAPI CoLoadLibrary(LPOLESTR lpszLibName, BOOL bAutoFree)
Ove Kaaven's avatar
Ove Kaaven committed
2477
{
2478
    TRACE("(%s, %d)\n", debugstr_w(lpszLibName), bAutoFree);
2479

2480 2481
    return LoadLibraryExW(lpszLibName, 0, LOAD_WITH_ALTERED_SEARCH_PATH);
}
2482

2483
/***********************************************************************
2484
 *           CoFreeLibrary [OLE32.@]
2485
 *
2486 2487 2488 2489 2490 2491 2492 2493 2494 2495
 * Unloads a library from memory.
 *
 * PARAMS
 *  hLibrary [I] Handle to library to unload.
 *
 * RETURNS
 *  Nothing
 *
 * SEE ALSO
 *  CoLoadLibrary, CoFreeAllLibraries, CoFreeUnusedLibraries
2496 2497 2498
 */
void WINAPI CoFreeLibrary(HINSTANCE hLibrary)
{
2499
    FreeLibrary(hLibrary);
2500 2501 2502 2503
}


/***********************************************************************
2504
 *           CoFreeAllLibraries [OLE32.@]
2505
 *
2506 2507 2508 2509 2510 2511 2512
 * Function for backwards compatibility only. Does nothing.
 *
 * RETURNS
 *  Nothing.
 *
 * SEE ALSO
 *  CoLoadLibrary, CoFreeLibrary, CoFreeUnusedLibraries
2513 2514 2515
 */
void WINAPI CoFreeAllLibraries(void)
{
2516
    /* NOP */
Ove Kaaven's avatar
Ove Kaaven committed
2517 2518
}

Alexandre Julliard's avatar
Alexandre Julliard committed
2519
/***********************************************************************
2520
 *           CoFreeUnusedLibrariesEx [OLE32.@]
2521
 *
2522 2523 2524 2525 2526 2527 2528
 * Frees any previously unused libraries whose delay has expired and marks
 * currently unused libraries for unloading. Unused are identified as those that
 * return S_OK from their DllCanUnloadNow function.
 *
 * PARAMS
 *  dwUnloadDelay [I] Unload delay in milliseconds.
 *  dwReserved    [I] Reserved. Set to 0.
2529 2530 2531
 *
 * RETURNS
 *  Nothing.
2532
 *
2533 2534
 * SEE ALSO
 *  CoLoadLibrary, CoFreeAllLibraries, CoFreeLibrary
Alexandre Julliard's avatar
Alexandre Julliard committed
2535
 */
2536
void WINAPI CoFreeUnusedLibrariesEx(DWORD dwUnloadDelay, DWORD dwReserved)
Alexandre Julliard's avatar
Alexandre Julliard committed
2537
{
2538 2539 2540 2541 2542 2543 2544
    struct apartment *apt = COM_CurrentApt();
    if (!apt)
    {
        ERR("apartment not initialised\n");
        return;
    }

2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563
    apartment_freeunusedlibraries(apt, dwUnloadDelay);
}

/***********************************************************************
 *           CoFreeUnusedLibraries [OLE32.@]
 *           CoFreeUnusedLibraries [COMPOBJ.17]
 *
 * Frees any unused libraries. Unused are identified as those that return
 * S_OK from their DllCanUnloadNow function.
 *
 * RETURNS
 *  Nothing.
 *
 * SEE ALSO
 *  CoLoadLibrary, CoFreeAllLibraries, CoFreeLibrary
 */
void WINAPI CoFreeUnusedLibraries(void)
{
    CoFreeUnusedLibrariesEx(INFINITE, 0);
Alexandre Julliard's avatar
Alexandre Julliard committed
2564 2565 2566
}

/***********************************************************************
2567
 *           CoFileTimeNow [OLE32.@]
2568 2569 2570 2571 2572 2573
 *           CoFileTimeNow [COMPOBJ.82]
 *
 * Retrieves the current time in FILETIME format.
 *
 * PARAMS
 *  lpFileTime [O] The current time.
Patrik Stridvall's avatar
Patrik Stridvall committed
2574
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
2575
 * RETURNS
2576
 *	S_OK.
Alexandre Julliard's avatar
Alexandre Julliard committed
2577
 */
2578
HRESULT WINAPI CoFileTimeNow( FILETIME *lpFileTime )
2579 2580 2581
{
    GetSystemTimeAsFileTime( lpFileTime );
    return S_OK;
Alexandre Julliard's avatar
Alexandre Julliard committed
2582
}
Alexandre Julliard's avatar
Alexandre Julliard committed
2583

2584
/******************************************************************************
2585
 *		CoLockObjectExternal	[OLE32.@]
2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598
 *
 * Increments or decrements the external reference count of a stub object.
 *
 * PARAMS
 *  pUnk                [I] Stub object.
 *  fLock               [I] If TRUE then increments the external ref-count,
 *                          otherwise decrements.
 *  fLastUnlockReleases [I] If TRUE then the last unlock has the effect of
 *                          calling CoDisconnectObject.
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: HRESULT code.
2599 2600 2601 2602
 *
 * NOTES
 *  If fLock is TRUE and an object is passed in that doesn't have a stub
 *  manager then a new stub manager is created for the object.
2603
 */
2604
HRESULT WINAPI CoLockObjectExternal(
2605 2606 2607
    LPUNKNOWN pUnk,
    BOOL fLock,
    BOOL fLastUnlockReleases)
2608
{
2609 2610 2611
    struct stub_manager *stubmgr;
    struct apartment *apt;

2612 2613
    TRACE("pUnk=%p, fLock=%s, fLastUnlockReleases=%s\n",
          pUnk, fLock ? "TRUE" : "FALSE", fLastUnlockReleases ? "TRUE" : "FALSE");
2614

2615 2616 2617 2618 2619 2620 2621 2622 2623 2624
    apt = COM_CurrentApt();
    if (!apt) return CO_E_NOTINITIALIZED;

    stubmgr = get_stub_manager_from_object(apt, pUnk);
    
    if (stubmgr)
    {
        if (fLock)
            stub_manager_ext_addref(stubmgr, 1);
        else
2625
            stub_manager_ext_release(stubmgr, 1, fLastUnlockReleases);
2626 2627
        
        stub_manager_int_release(stubmgr);
2628

2629
        return S_OK;
2630
    }
2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642
    else if (fLock)
    {
        stubmgr = new_stub_manager(apt, pUnk);

        if (stubmgr)
        {
            stub_manager_ext_addref(stubmgr, 1);
            stub_manager_int_release(stubmgr);
        }

        return S_OK;
    }
2643 2644 2645 2646 2647 2648 2649 2650
    else
    {
        WARN("stub object not found %p\n", pUnk);
        /* Note: native is pretty broken here because it just silently
         * fails, without returning an appropriate error code, making apps
         * think that the object was disconnected, when it actually wasn't */
        return S_OK;
    }
2651 2652
}

2653
/***********************************************************************
2654
 *           CoInitializeWOW (OLE32.@)
2655 2656 2657 2658 2659 2660 2661 2662 2663
 *
 * WOW equivalent of CoInitialize?
 *
 * PARAMS
 *  x [I] Unknown.
 *  y [I] Unknown.
 *
 * RETURNS
 *  Unknown.
2664
 */
2665 2666
HRESULT WINAPI CoInitializeWOW(DWORD x,DWORD y)
{
2667
    FIXME("(0x%08x,0x%08x),stub!\n",x,y);
2668
    return 0;
2669 2670
}

2671
/***********************************************************************
2672
 *           CoGetState [OLE32.@]
2673
 *
2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689
 * Retrieves the thread state object previously stored by CoSetState().
 *
 * PARAMS
 *  ppv [I] Address where pointer to object will be stored.
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_OUTOFMEMORY.
 *
 * NOTES
 *  Crashes on all invalid ppv addresses, including NULL.
 *  If the function returns a non-NULL object then the caller must release its
 *  reference on the object when the object is no longer required.
 *
 * SEE ALSO
 *  CoSetState().
2690
 */
2691
HRESULT WINAPI CoGetState(IUnknown ** ppv)
2692
{
2693 2694
    struct oletls *info = COM_CurrentInfo();
    if (!info) return E_OUTOFMEMORY;
2695

2696
    *ppv = NULL;
2697

2698
    if (info->state)
2699
    {
2700 2701 2702
        IUnknown_AddRef(info->state);
        *ppv = info->state;
        TRACE("apt->state=%p\n", info->state);
2703
    }
2704

2705
    return S_OK;
2706
}
2707 2708

/***********************************************************************
2709
 *           CoSetState [OLE32.@]
2710
 *
2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721
 * Sets the thread state object.
 *
 * PARAMS
 *  pv [I] Pointer to state object to be stored.
 *
 * NOTES
 *  The system keeps a reference on the object while the object stored.
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_OUTOFMEMORY.
2722
 */
2723
HRESULT WINAPI CoSetState(IUnknown * pv)
2724
{
2725 2726 2727
    struct oletls *info = COM_CurrentInfo();
    if (!info) return E_OUTOFMEMORY;

2728
    if (pv) IUnknown_AddRef(pv);
2729

2730
    if (info->state)
2731
    {
2732 2733
        TRACE("-- release %p now\n", info->state);
        IUnknown_Release(info->state);
2734
    }
2735

2736
    info->state = pv;
2737

2738
    return S_OK;
2739
}
2740

2741

2742
/******************************************************************************
2743
 *              CoTreatAsClass        [OLE32.@]
2744
 *
2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756
 * Sets the TreatAs value of a class.
 *
 * PARAMS
 *  clsidOld [I] Class to set TreatAs value on.
 *  clsidNew [I] The class the clsidOld should be treated as.
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: HRESULT code.
 *
 * SEE ALSO
 *  CoGetTreatAsClass
2757 2758 2759
 */
HRESULT WINAPI CoTreatAsClass(REFCLSID clsidOld, REFCLSID clsidNew)
{
2760 2761 2762 2763
    static const WCHAR wszAutoTreatAs[] = {'A','u','t','o','T','r','e','a','t','A','s',0};
    static const WCHAR wszTreatAs[] = {'T','r','e','a','t','A','s',0};
    HKEY hkey = NULL;
    WCHAR szClsidNew[CHARS_IN_GUID];
2764
    HRESULT res = S_OK;
2765
    WCHAR auto_treat_as[CHARS_IN_GUID];
2766 2767
    LONG auto_treat_as_size = sizeof(auto_treat_as);
    CLSID id;
2768

2769 2770 2771
    res = COM_OpenKeyForCLSID(clsidOld, NULL, KEY_READ | KEY_WRITE, &hkey);
    if (FAILED(res))
        goto done;
2772
    if (!memcmp( clsidOld, clsidNew, sizeof(*clsidOld) ))
2773
    {
2774 2775
       if (!RegQueryValueW(hkey, wszAutoTreatAs, auto_treat_as, &auto_treat_as_size) &&
           !CLSIDFromString(auto_treat_as, &id))
2776
       {
2777
           if (RegSetValueW(hkey, wszTreatAs, REG_SZ, auto_treat_as, sizeof(auto_treat_as)))
2778 2779 2780 2781 2782 2783 2784
           {
               res = REGDB_E_WRITEREGDB;
               goto done;
           }
       }
       else
       {
2785
           RegDeleteKeyW(hkey, wszTreatAs);
2786 2787 2788
           goto done;
       }
    }
2789 2790
    else if (!StringFromGUID2(clsidNew, szClsidNew, ARRAYSIZE(szClsidNew)) &&
             !RegSetValueW(hkey, wszTreatAs, REG_SZ, szClsidNew, sizeof(szClsidNew)))
2791
    {
2792
        res = REGDB_E_WRITEREGDB;
2793 2794 2795 2796 2797 2798
	goto done;
    }

done:
    if (hkey) RegCloseKey(hkey);
    return res;
2799
}
2800

2801
/******************************************************************************
2802
 *              CoGetTreatAsClass        [OLE32.@]
2803
 *
2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815
 * Gets the TreatAs value of a class.
 *
 * PARAMS
 *  clsidOld [I] Class to get the TreatAs value of.
 *  clsidNew [I] The class the clsidOld should be treated as.
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: HRESULT code.
 *
 * SEE ALSO
 *  CoSetTreatAsClass
2816 2817 2818
 */
HRESULT WINAPI CoGetTreatAsClass(REFCLSID clsidOld, LPCLSID clsidNew)
{
2819 2820 2821
    static const WCHAR wszTreatAs[] = {'T','r','e','a','t','A','s',0};
    HKEY hkey = NULL;
    WCHAR szClsidNew[CHARS_IN_GUID];
2822 2823 2824 2825 2826 2827
    HRESULT res = S_OK;
    LONG len = sizeof(szClsidNew);

    FIXME("(%s,%p)\n", debugstr_guid(clsidOld), clsidNew);
    memcpy(clsidNew,clsidOld,sizeof(CLSID)); /* copy over old value */

2828 2829 2830 2831
    res = COM_OpenKeyForCLSID(clsidOld, wszTreatAs, KEY_READ, &hkey);
    if (FAILED(res))
        goto done;
    if (RegQueryValueW(hkey, NULL, szClsidNew, &len))
2832 2833 2834 2835
    {
        res = S_FALSE;
	goto done;
    }
2836
    res = CLSIDFromString(szClsidNew,clsidNew);
2837
    if (FAILED(res))
2838
        ERR("Failed CLSIDFromStringA(%s), hres 0x%08x\n", debugstr_w(szClsidNew), res);
2839 2840 2841 2842
done:
    if (hkey) RegCloseKey(hkey);
    return res;
}
2843

2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872
/******************************************************************************
 *		CoGetCurrentProcess	[OLE32.@]
 *		CoGetCurrentProcess	[COMPOBJ.34]
 *
 * Gets the current process ID.
 *
 * RETURNS
 *  The current process ID.
 *
 * NOTES
 *   Is DWORD really the correct return type for this function?
 */
DWORD WINAPI CoGetCurrentProcess(void)
{
	return GetCurrentProcessId();
}

/******************************************************************************
 *		CoRegisterMessageFilter	[OLE32.@]
 *
 * Registers a message filter.
 *
 * PARAMS
 *  lpMessageFilter [I] Pointer to interface.
 *  lplpMessageFilter [O] Indirect pointer to prior instance if non-NULL.
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: HRESULT code.
2873 2874 2875 2876 2877 2878 2879 2880 2881 2882
 *
 * NOTES
 *  Both lpMessageFilter and lplpMessageFilter are optional. Passing in a NULL
 *  lpMessageFilter removes the message filter.
 *
 *  If lplpMessageFilter is not NULL the previous message filter will be
 *  returned in the memory pointer to this parameter and the caller is
 *  responsible for releasing the object.
 *
 *  The current thread be in an apartment otherwise the function will crash.
2883 2884 2885 2886 2887
 */
HRESULT WINAPI CoRegisterMessageFilter(
    LPMESSAGEFILTER lpMessageFilter,
    LPMESSAGEFILTER *lplpMessageFilter)
{
2888 2889 2890 2891 2892 2893 2894 2895
    struct apartment *apt;
    IMessageFilter *lpOldMessageFilter;

    TRACE("(%p, %p)\n", lpMessageFilter, lplpMessageFilter);

    apt = COM_CurrentApt();

    /* can't set a message filter in a multi-threaded apartment */
2896
    if (!apt || apt->multi_threaded)
2897
    {
2898
        WARN("can't set message filter in MTA or uninitialized apt\n");
2899
        return CO_E_NOT_SUPPORTED;
2900
    }
2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916

    if (lpMessageFilter)
        IMessageFilter_AddRef(lpMessageFilter);

    EnterCriticalSection(&apt->cs);

    lpOldMessageFilter = apt->filter;
    apt->filter = lpMessageFilter;

    LeaveCriticalSection(&apt->cs);

    if (lplpMessageFilter)
        *lplpMessageFilter = lpOldMessageFilter;
    else if (lpOldMessageFilter)
        IMessageFilter_Release(lpOldMessageFilter);

2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936
    return S_OK;
}

/***********************************************************************
 *           CoIsOle1Class [OLE32.@]
 *
 * Determines whether the specified class an OLE v1 class.
 *
 * PARAMS
 *  clsid [I] Class to test.
 *
 * RETURNS
 *  TRUE if the class is an OLE v1 class, or FALSE otherwise.
 */
BOOL WINAPI CoIsOle1Class(REFCLSID clsid)
{
  FIXME("%s\n", debugstr_guid(clsid));
  return FALSE;
}

2937
/***********************************************************************
2938
 *           IsEqualGUID [OLE32.@]
2939 2940 2941
 *
 * Compares two Unique Identifiers.
 *
2942 2943 2944 2945
 * PARAMS
 *  rguid1 [I] The first GUID to compare.
 *  rguid2 [I] The other GUID to compare.
 *
2946 2947 2948 2949 2950
 * RETURNS
 *	TRUE if equal
 */
#undef IsEqualGUID
BOOL WINAPI IsEqualGUID(
2951 2952
     REFGUID rguid1,
     REFGUID rguid2)
2953 2954 2955
{
    return !memcmp(rguid1,rguid2,sizeof(GUID));
}
2956 2957

/***********************************************************************
2958
 *           CoInitializeSecurity [OLE32.@]
2959 2960 2961 2962 2963 2964 2965
 */
HRESULT WINAPI CoInitializeSecurity(PSECURITY_DESCRIPTOR pSecDesc, LONG cAuthSvc,
                                    SOLE_AUTHENTICATION_SERVICE* asAuthSvc,
                                    void* pReserved1, DWORD dwAuthnLevel,
                                    DWORD dwImpLevel, void* pReserved2,
                                    DWORD dwCapabilities, void* pReserved3)
{
2966
  FIXME("(%p,%d,%p,%p,%d,%d,%p,%d,%p) - stub!\n", pSecDesc, cAuthSvc,
2967 2968 2969 2970
        asAuthSvc, pReserved1, dwAuthnLevel, dwImpLevel, pReserved2,
        dwCapabilities, pReserved3);
  return S_OK;
}
2971 2972 2973

/***********************************************************************
 *           CoSuspendClassObjects [OLE32.@]
2974 2975 2976 2977 2978 2979 2980
 *
 * Suspends all registered class objects to prevent further requests coming in
 * for those objects.
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: HRESULT code.
2981 2982 2983 2984 2985 2986
 */
HRESULT WINAPI CoSuspendClassObjects(void)
{
    FIXME("\n");
    return S_OK;
}
2987 2988 2989

/***********************************************************************
 *           CoAddRefServerProcess [OLE32.@]
2990 2991 2992 2993 2994 2995
 *
 * Helper function for incrementing the reference count of a local-server
 * process.
 *
 * RETURNS
 *  New reference count.
2996 2997 2998
 *
 * SEE ALSO
 *  CoReleaseServerProcess().
2999 3000 3001
 */
ULONG WINAPI CoAddRefServerProcess(void)
{
3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012
    ULONG refs;

    TRACE("\n");

    EnterCriticalSection(&csRegisteredClassList);
    refs = ++s_COMServerProcessReferences;
    LeaveCriticalSection(&csRegisteredClassList);

    TRACE("refs before: %d\n", refs - 1);

    return refs;
3013 3014 3015 3016
}

/***********************************************************************
 *           CoReleaseServerProcess [OLE32.@]
3017 3018 3019 3020 3021 3022
 *
 * Helper function for decrementing the reference count of a local-server
 * process.
 *
 * RETURNS
 *  New reference count.
3023 3024 3025 3026 3027 3028 3029
 *
 * NOTES
 *  When reference count reaches 0, this function suspends all registered
 *  classes so no new connections are accepted.
 *
 * SEE ALSO
 *  CoAddRefServerProcess(), CoSuspendClassObjects().
3030 3031 3032
 */
ULONG WINAPI CoReleaseServerProcess(void)
{
3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046
    ULONG refs;

    TRACE("\n");

    EnterCriticalSection(&csRegisteredClassList);

    refs = --s_COMServerProcessReferences;
    /* FIXME: if (!refs) COM_SuspendClassObjects(); */

    LeaveCriticalSection(&csRegisteredClassList);

    TRACE("refs after: %d\n", refs);

    return refs;
3047
}
3048

3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066
/***********************************************************************
 *           CoIsHandlerConnected [OLE32.@]
 *
 * Determines whether a proxy is connected to a remote stub.
 *
 * PARAMS
 *  pUnk [I] Pointer to object that may or may not be connected.
 *
 * RETURNS
 *  TRUE if pUnk is not a proxy or if pUnk is connected to a remote stub, or
 *  FALSE otherwise.
 */
BOOL WINAPI CoIsHandlerConnected(IUnknown *pUnk)
{
    FIXME("%p\n", pUnk);

    return TRUE;
}
3067 3068 3069 3070 3071 3072 3073 3074 3075 3076

/***********************************************************************
 *           CoAllowSetForegroundWindow [OLE32.@]
 *
 */
HRESULT WINAPI CoAllowSetForegroundWindow(IUnknown *pUnk, void *pvReserved)
{
    FIXME("(%p, %p): stub\n", pUnk, pvReserved);
    return S_OK;
}
3077
 
3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110
/***********************************************************************
 *           CoQueryProxyBlanket [OLE32.@]
 *
 * Retrieves the security settings being used by a proxy.
 *
 * PARAMS
 *  pProxy        [I] Pointer to the proxy object.
 *  pAuthnSvc     [O] The type of authentication service.
 *  pAuthzSvc     [O] The type of authorization service.
 *  ppServerPrincName [O] Optional. The server prinicple name.
 *  pAuthnLevel   [O] The authentication level.
 *  pImpLevel     [O] The impersonation level.
 *  ppAuthInfo    [O] Information specific to the authorization/authentication service.
 *  pCapabilities [O] Flags affecting the security behaviour.
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: HRESULT code.
 *
 * SEE ALSO
 *  CoCopyProxy, CoSetProxyBlanket.
 */
HRESULT WINAPI CoQueryProxyBlanket(IUnknown *pProxy, DWORD *pAuthnSvc,
    DWORD *pAuthzSvc, OLECHAR **ppServerPrincName, DWORD *pAuthnLevel,
    DWORD *pImpLevel, void **ppAuthInfo, DWORD *pCapabilities)
{
    IClientSecurity *pCliSec;
    HRESULT hr;

    TRACE("%p\n", pProxy);

    hr = IUnknown_QueryInterface(pProxy, &IID_IClientSecurity, (void **)&pCliSec);
    if (SUCCEEDED(hr))
3111
    {
3112 3113 3114 3115
        hr = IClientSecurity_QueryBlanket(pCliSec, pProxy, pAuthnSvc,
                                          pAuthzSvc, ppServerPrincName,
                                          pAuthnLevel, pImpLevel, ppAuthInfo,
                                          pCapabilities);
3116 3117
        IClientSecurity_Release(pCliSec);
    }
3118

3119
    if (FAILED(hr)) ERR("-- failed with 0x%08x\n", hr);
3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155
    return hr;
}

/***********************************************************************
 *           CoSetProxyBlanket [OLE32.@]
 *
 * Sets the security settings for a proxy.
 *
 * PARAMS
 *  pProxy       [I] Pointer to the proxy object.
 *  AuthnSvc     [I] The type of authentication service.
 *  AuthzSvc     [I] The type of authorization service.
 *  pServerPrincName [I] The server prinicple name.
 *  AuthnLevel   [I] The authentication level.
 *  ImpLevel     [I] The impersonation level.
 *  pAuthInfo    [I] Information specific to the authorization/authentication service.
 *  Capabilities [I] Flags affecting the security behaviour.
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: HRESULT code.
 *
 * SEE ALSO
 *  CoQueryProxyBlanket, CoCopyProxy.
 */
HRESULT WINAPI CoSetProxyBlanket(IUnknown *pProxy, DWORD AuthnSvc,
    DWORD AuthzSvc, OLECHAR *pServerPrincName, DWORD AuthnLevel,
    DWORD ImpLevel, void *pAuthInfo, DWORD Capabilities)
{
    IClientSecurity *pCliSec;
    HRESULT hr;

    TRACE("%p\n", pProxy);

    hr = IUnknown_QueryInterface(pProxy, &IID_IClientSecurity, (void **)&pCliSec);
    if (SUCCEEDED(hr))
3156
    {
3157 3158 3159 3160
        hr = IClientSecurity_SetBlanket(pCliSec, pProxy, AuthnSvc,
                                        AuthzSvc, pServerPrincName,
                                        AuthnLevel, ImpLevel, pAuthInfo,
                                        Capabilities);
3161 3162
        IClientSecurity_Release(pCliSec);
    }
3163

3164
    if (FAILED(hr)) ERR("-- failed with 0x%08x\n", hr);
3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192
    return hr;
}

/***********************************************************************
 *           CoCopyProxy [OLE32.@]
 *
 * Copies a proxy.
 *
 * PARAMS
 *  pProxy [I] Pointer to the proxy object.
 *  ppCopy [O] Copy of the proxy.
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: HRESULT code.
 *
 * SEE ALSO
 *  CoQueryProxyBlanket, CoSetProxyBlanket.
 */
HRESULT WINAPI CoCopyProxy(IUnknown *pProxy, IUnknown **ppCopy)
{
    IClientSecurity *pCliSec;
    HRESULT hr;

    TRACE("%p\n", pProxy);

    hr = IUnknown_QueryInterface(pProxy, &IID_IClientSecurity, (void **)&pCliSec);
    if (SUCCEEDED(hr))
3193
    {
3194
        hr = IClientSecurity_CopyProxy(pCliSec, pProxy, ppCopy);
3195 3196
        IClientSecurity_Release(pCliSec);
    }
3197

3198
    if (FAILED(hr)) ERR("-- failed with 0x%08x\n", hr);
3199 3200
    return hr;
}
3201 3202


3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345
/***********************************************************************
 *           CoGetCallContext [OLE32.@]
 *
 * Gets the context of the currently executing server call in the current
 * thread.
 *
 * PARAMS
 *  riid [I] Context interface to return.
 *  ppv  [O] Pointer to memory that will receive the context on return.
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: HRESULT code.
 */
HRESULT WINAPI CoGetCallContext(REFIID riid, void **ppv)
{
    FIXME("(%s, %p): stub\n", debugstr_guid(riid), ppv);

    *ppv = NULL;
    return E_NOINTERFACE;
}

/***********************************************************************
 *           CoQueryClientBlanket [OLE32.@]
 *
 * Retrieves the authentication information about the client of the currently
 * executing server call in the current thread.
 *
 * PARAMS
 *  pAuthnSvc     [O] Optional. The type of authentication service.
 *  pAuthzSvc     [O] Optional. The type of authorization service.
 *  pServerPrincName [O] Optional. The server prinicple name.
 *  pAuthnLevel   [O] Optional. The authentication level.
 *  pImpLevel     [O] Optional. The impersonation level.
 *  pPrivs        [O] Optional. Information about the privileges of the client.
 *  pCapabilities [IO] Optional. Flags affecting the security behaviour.
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: HRESULT code.
 *
 * SEE ALSO
 *  CoImpersonateClient, CoRevertToSelf, CoGetCallContext.
 */
HRESULT WINAPI CoQueryClientBlanket(
    DWORD *pAuthnSvc,
    DWORD *pAuthzSvc,
    OLECHAR **pServerPrincName,
    DWORD *pAuthnLevel,
    DWORD *pImpLevel,
    RPC_AUTHZ_HANDLE *pPrivs,
    DWORD *pCapabilities)
{
    IServerSecurity *pSrvSec;
    HRESULT hr;

    TRACE("(%p, %p, %p, %p, %p, %p, %p)\n",
        pAuthnSvc, pAuthzSvc, pServerPrincName, pAuthnLevel, pImpLevel,
        pPrivs, pCapabilities);

    hr = CoGetCallContext(&IID_IServerSecurity, (void **)&pSrvSec);
    if (SUCCEEDED(hr))
    {
        hr = IServerSecurity_QueryBlanket(
            pSrvSec, pAuthnSvc, pAuthzSvc, pServerPrincName, pAuthnLevel,
            pImpLevel, pPrivs, pCapabilities);
        IServerSecurity_Release(pSrvSec);
    }

    return hr;
}

/***********************************************************************
 *           CoImpersonateClient [OLE32.@]
 *
 * Impersonates the client of the currently executing server call in the
 * current thread.
 *
 * PARAMS
 *  None.
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: HRESULT code.
 *
 * NOTES
 *  If this function fails then the current thread will not be impersonating
 *  the client and all actions will take place on behalf of the server.
 *  Therefore, it is important to check the return value from this function.
 *
 * SEE ALSO
 *  CoRevertToSelf, CoQueryClientBlanket, CoGetCallContext.
 */
HRESULT WINAPI CoImpersonateClient(void)
{
    IServerSecurity *pSrvSec;
    HRESULT hr;

    TRACE("\n");

    hr = CoGetCallContext(&IID_IServerSecurity, (void **)&pSrvSec);
    if (SUCCEEDED(hr))
    {
        hr = IServerSecurity_ImpersonateClient(pSrvSec);
        IServerSecurity_Release(pSrvSec);
    }

    return hr;
}

/***********************************************************************
 *           CoRevertToSelf [OLE32.@]
 *
 * Ends the impersonation of the client of the currently executing server
 * call in the current thread.
 *
 * PARAMS
 *  None.
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: HRESULT code.
 *
 * SEE ALSO
 *  CoImpersonateClient, CoQueryClientBlanket, CoGetCallContext.
 */
HRESULT WINAPI CoRevertToSelf(void)
{
    IServerSecurity *pSrvSec;
    HRESULT hr;

    TRACE("\n");

    hr = CoGetCallContext(&IID_IServerSecurity, (void **)&pSrvSec);
    if (SUCCEEDED(hr))
    {
        hr = IServerSecurity_RevertToSelf(pSrvSec);
        IServerSecurity_Release(pSrvSec);
    }

    return hr;
}

3346 3347
static BOOL COM_PeekMessage(struct apartment *apt, MSG *msg)
{
3348 3349
    /* first try to retrieve messages for incoming COM calls to the apartment window */
    return PeekMessageW(msg, apt->win, WM_USER, WM_APP - 1, PM_REMOVE|PM_NOYIELD) ||
3350
           /* next retrieve other messages necessary for the app to remain responsive */
3351
           PeekMessageW(msg, NULL, 0, 0, PM_QS_PAINT|PM_QS_POSTMESSAGE|PM_REMOVE|PM_NOYIELD);
3352 3353
}

3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379
/***********************************************************************
 *           CoWaitForMultipleHandles [OLE32.@]
 *
 * Waits for one or more handles to become signaled.
 *
 * PARAMS
 *  dwFlags   [I] Flags. See notes.
 *  dwTimeout [I] Timeout in milliseconds.
 *  cHandles  [I] Number of handles pointed to by pHandles.
 *  pHandles  [I] Handles to wait for.
 *  lpdwindex [O] Index of handle that was signaled.
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: RPC_S_CALLPENDING on timeout.
 *
 * NOTES
 *
 * The dwFlags parameter can be zero or more of the following:
 *| COWAIT_WAITALL - Wait for all of the handles to become signaled.
 *| COWAIT_ALERTABLE - Allows a queued APC to run during the wait.
 *
 * SEE ALSO
 *  MsgWaitForMultipleObjects, WaitForMultipleObjects.
 */
HRESULT WINAPI CoWaitForMultipleHandles(DWORD dwFlags, DWORD dwTimeout,
3380
    ULONG cHandles, LPHANDLE pHandles, LPDWORD lpdwindex)
3381 3382 3383
{
    HRESULT hr = S_OK;
    DWORD start_time = GetTickCount();
3384 3385
    APARTMENT *apt = COM_CurrentApt();
    BOOL message_loop = apt && !apt->multi_threaded;
3386

3387
    TRACE("(0x%08x, 0x%08x, %d, %p, %p)\n", dwFlags, dwTimeout, cHandles,
3388 3389 3390 3391 3392 3393
        pHandles, lpdwindex);

    while (TRUE)
    {
        DWORD now = GetTickCount();
        DWORD res;
3394

3395 3396 3397 3398 3399 3400
        if ((dwTimeout != INFINITE) && (start_time + dwTimeout >= now))
        {
            hr = RPC_S_CALLPENDING;
            break;
        }

3401 3402 3403 3404 3405 3406
        if (message_loop)
        {
            DWORD wait_flags = (dwFlags & COWAIT_WAITALL) ? MWMO_WAITALL : 0 |
                    (dwFlags & COWAIT_ALERTABLE ) ? MWMO_ALERTABLE : 0;

            TRACE("waiting for rpc completion or window message\n");
3407

3408 3409 3410
            res = MsgWaitForMultipleObjectsEx(cHandles, pHandles,
                (dwTimeout == INFINITE) ? INFINITE : start_time + dwTimeout - now,
                QS_ALLINPUT, wait_flags);
3411

3412
            if (res == WAIT_OBJECT_0 + cHandles)  /* messages available */
3413
            {
3414
                MSG msg;
3415

3416 3417 3418 3419
                /* call message filter */

                if (COM_CurrentApt()->filter)
                {
3420 3421 3422
                    PENDINGTYPE pendingtype =
                        COM_CurrentInfo()->pending_call_count_server ?
                            PENDINGTYPE_NESTED : PENDINGTYPE_TOPLEVEL;
3423 3424
                    DWORD be_handled = IMessageFilter_MessagePending(
                        COM_CurrentApt()->filter, 0 /* FIXME */,
3425
                        now - start_time, pendingtype);
3426 3427 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 3443
                    TRACE("IMessageFilter_MessagePending returned %d\n", be_handled);
                    switch (be_handled)
                    {
                    case PENDINGMSG_CANCELCALL:
                        WARN("call canceled\n");
                        hr = RPC_E_CALL_CANCELED;
                        break;
                    case PENDINGMSG_WAITNOPROCESS:
                    case PENDINGMSG_WAITDEFPROCESS:
                    default:
                        /* FIXME: MSDN is very vague about the difference
                         * between WAITNOPROCESS and WAITDEFPROCESS - there
                         * appears to be none, so it is possibly a left-over
                         * from the 16-bit world. */
                        break;
                    }
                }

3444 3445 3446 3447 3448
                /* note: using "if" here instead of "while" might seem less
                 * efficient, but only if we are optimising for quick delivery
                 * of pending messages, rather than quick completion of the
                 * COM call */
                if (COM_PeekMessage(apt, &msg))
3449
                {
3450 3451 3452 3453 3454 3455 3456 3457 3458 3459
                    TRACE("received message whilst waiting for RPC: 0x%04x\n", msg.message);
                    TranslateMessage(&msg);
                    DispatchMessageW(&msg);
                    if (msg.message == WM_QUIT)
                    {
                        TRACE("resending WM_QUIT to outer message loop\n");
                        PostQuitMessage(msg.wParam);
                        /* no longer need to process messages */
                        message_loop = FALSE;
                    }
3460
                }
3461
                continue;
3462 3463
            }
        }
3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474
        else
        {
            TRACE("waiting for rpc completion\n");

            res = WaitForMultipleObjectsEx(cHandles, pHandles,
                (dwFlags & COWAIT_WAITALL) ? TRUE : FALSE,
                (dwTimeout == INFINITE) ? INFINITE : start_time + dwTimeout - now,
                (dwFlags & COWAIT_ALERTABLE) ? TRUE : FALSE);
        }

        if ((res >= WAIT_OBJECT_0) && (res < WAIT_OBJECT_0 + cHandles))
3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486
        {
            /* handle signaled, store index */
            *lpdwindex = (res - WAIT_OBJECT_0);
            break;
        }
        else if (res == WAIT_TIMEOUT)
        {
            hr = RPC_S_CALLPENDING;
            break;
        }
        else
        {
3487
            ERR("Unexpected wait termination: %d, %d\n", res, GetLastError());
3488 3489 3490 3491
            hr = E_UNEXPECTED;
            break;
        }
    }
3492
    TRACE("-- 0x%08x\n", hr);
3493 3494
    return hr;
}
3495

3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537 3538 3539 3540 3541 3542 3543 3544 3545 3546 3547

/***********************************************************************
 *           CoGetObject [OLE32.@]
 *
 * Gets the object named by coverting the name to a moniker and binding to it.
 *
 * PARAMS
 *  pszName      [I] String representing the object.
 *  pBindOptions [I] Parameters affecting the binding to the named object.
 *  riid         [I] Interface to bind to on the objecct.
 *  ppv          [O] On output, the interface riid of the object represented
 *                   by pszName.
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: HRESULT code.
 *
 * SEE ALSO
 *  MkParseDisplayName.
 */
HRESULT WINAPI CoGetObject(LPCWSTR pszName, BIND_OPTS *pBindOptions,
    REFIID riid, void **ppv)
{
    IBindCtx *pbc;
    HRESULT hr;

    *ppv = NULL;

    hr = CreateBindCtx(0, &pbc);
    if (SUCCEEDED(hr))
    {
        if (pBindOptions)
            hr = IBindCtx_SetBindOptions(pbc, pBindOptions);

        if (SUCCEEDED(hr))
        {
            ULONG chEaten;
            IMoniker *pmk;

            hr = MkParseDisplayName(pbc, pszName, &chEaten, &pmk);
            if (SUCCEEDED(hr))
            {
                hr = IMoniker_BindToObject(pmk, pbc, NULL, riid, ppv);
                IMoniker_Release(pmk);
            }
        }

        IBindCtx_Release(pbc);
    }
    return hr;
}

3548 3549 3550 3551 3552 3553 3554 3555 3556 3557 3558 3559 3560 3561 3562 3563 3564 3565 3566 3567
/***********************************************************************
 *           CoRegisterChannelHook [OLE32.@]
 *
 * Registers a process-wide hook that is called during ORPC calls.
 *
 * PARAMS
 *  guidExtension [I] GUID of the channel hook to register.
 *  pChannelHook  [I] Channel hook object to register.
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: HRESULT code.
 */
HRESULT WINAPI CoRegisterChannelHook(REFGUID guidExtension, IChannelHook *pChannelHook)
{
    TRACE("(%s, %p)\n", debugstr_guid(guidExtension), pChannelHook);

    return RPC_RegisterChannelHook(guidExtension, pChannelHook);
}

3568 3569 3570 3571 3572
/***********************************************************************
 *		DllMain (OLE32.@)
 */
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID fImpLoad)
{
3573
    TRACE("%p 0x%x %p\n", hinstDLL, fdwReason, fImpLoad);
3574 3575 3576 3577 3578 3579 3580 3581 3582 3583

    switch(fdwReason) {
    case DLL_PROCESS_ATTACH:
        OLE32_hInstance = hinstDLL;
        COMPOBJ_InitProcess();
	if (TRACE_ON(ole)) CoRegisterMallocSpy((LPVOID)-1);
	break;

    case DLL_PROCESS_DETACH:
        if (TRACE_ON(ole)) CoRevokeMallocSpy();
3584
        OLEDD_UnInitialize();
3585
        COMPOBJ_UninitProcess();
3586
        RPC_UnregisterAllChannelHooks();
3587 3588 3589 3590 3591 3592 3593 3594 3595 3596 3597
        OLE32_hInstance = 0;
	break;

    case DLL_THREAD_DETACH:
        COM_TlsDestroy();
        break;
    }
    return TRUE;
}

/* NOTE: DllRegisterServer and DllUnregisterServer are in regsvr.c */