ole32: Make RegisteredClass list into a standard Wine list.
[wine] / dlls / ole32 / compobj.c
1 /*
2  *      COMPOBJ library
3  *
4  *      Copyright 1995  Martin von Loewis
5  *      Copyright 1998  Justin Bradford
6  *      Copyright 1999  Francis Beaudet
7  *      Copyright 1999  Sylvain St-Germain
8  *      Copyright 2002  Marcus Meissner
9  *      Copyright 2004  Mike Hearn
10  *      Copyright 2005-2006 Robert Shearman (for CodeWeavers)
11  *
12  * This library is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU Lesser General Public
14  * License as published by the Free Software Foundation; either
15  * version 2.1 of the License, or (at your option) any later version.
16  *
17  * This library is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20  * Lesser General Public License for more details.
21  *
22  * You should have received a copy of the GNU Lesser General Public
23  * 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
25  *
26  * Note
27  * 1. COINIT_MULTITHREADED is 0; it is the lack of COINIT_APARTMENTTHREADED
28  *    Therefore do not test against COINIT_MULTITHREADED
29  *
30  * TODO list:           (items bunched together depend on each other)
31  *
32  *   - Implement the service control manager (in rpcss) to keep track
33  *     of registered class objects: ISCM::ServerRegisterClsid et al
34  *   - Implement the OXID resolver so we don't need magic endpoint names for
35  *     clients and servers to meet up
36  *
37  *   - Make all ole interface marshaling use NDR to be wire compatible with
38  *     native DCOM
39  *
40  */
41
42 #include "config.h"
43
44 #include <stdarg.h>
45 #include <stdio.h>
46 #include <string.h>
47 #include <assert.h>
48
49 #define COBJMACROS
50 #define NONAMELESSUNION
51 #define NONAMELESSSTRUCT
52
53 #include "windef.h"
54 #include "winbase.h"
55 #include "winerror.h"
56 #include "winreg.h"
57 #include "winuser.h"
58 #include "objbase.h"
59 #include "ole2.h"
60 #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 HINSTANCE OLE32_hInstance = 0; /* FIXME: make static ... */
70
71 #define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
72
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 static HRESULT COM_GetRegisteredClassObject(REFCLSID rclsid, DWORD dwClsContext, LPUNKNOWN*  ppUnk);
80 static void COM_RevokeAllClasses(void);
81 static HRESULT get_inproc_class_object(HKEY hkeydll, REFCLSID rclsid, REFIID riid, void **ppv);
82
83 static APARTMENT *MTA; /* protected by csApartment */
84 static APARTMENT *MainApartment; /* the first STA apartment */
85 static struct list apts = LIST_INIT( apts ); /* protected by csApartment */
86
87 static CRITICAL_SECTION csApartment;
88 static CRITICAL_SECTION_DEBUG critsect_debug =
89 {
90     0, 0, &csApartment,
91     { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
92       0, 0, { (DWORD_PTR)(__FILE__ ": csApartment") }
93 };
94 static CRITICAL_SECTION csApartment = { &critsect_debug, -1, 0, 0, 0, 0 };
95
96 struct registered_psclsid
97 {
98     struct list entry;
99     IID iid;
100     CLSID clsid;
101 };
102
103 /*
104  * This lock count counts the number of times CoInitialize is called. It is
105  * decreased every time CoUninitialize is called. When it hits 0, the COM
106  * libraries are freed
107  */
108 static LONG s_COMLockCount = 0;
109
110 /*
111  * This linked list contains the list of registered class objects. These
112  * are mostly used to register the factories for out-of-proc servers of OLE
113  * objects.
114  *
115  * TODO: Make this data structure aware of inter-process communication. This
116  *       means that parts of this will be exported to the Wine Server.
117  */
118 typedef struct tagRegisteredClass
119 {
120   struct list entry;
121   CLSID     classIdentifier;
122   LPUNKNOWN classObject;
123   DWORD     runContext;
124   DWORD     connectFlags;
125   DWORD     dwCookie;
126   LPSTREAM  pMarshaledData; /* FIXME: only really need to store OXID and IPID */
127   void     *RpcRegistration;
128 } RegisteredClass;
129
130 static struct list RegisteredClassList = LIST_INIT(RegisteredClassList);
131
132 static CRITICAL_SECTION csRegisteredClassList;
133 static CRITICAL_SECTION_DEBUG class_cs_debug =
134 {
135     0, 0, &csRegisteredClassList,
136     { &class_cs_debug.ProcessLocksList, &class_cs_debug.ProcessLocksList },
137       0, 0, { (DWORD_PTR)(__FILE__ ": csRegisteredClassList") }
138 };
139 static CRITICAL_SECTION csRegisteredClassList = { &class_cs_debug, -1, 0, 0, 0, 0 };
140
141 /*****************************************************************************
142  * This section contains OpenDllList definitions
143  *
144  * The OpenDllList contains only handles of dll loaded by CoGetClassObject or
145  * other functions that do LoadLibrary _without_ giving back a HMODULE.
146  * Without this list these handles would never be freed.
147  *
148  * FIXME: a DLL that says OK when asked for unloading is unloaded in the
149  * next unload-call but not before 600 sec.
150  */
151
152 typedef struct tagOpenDll {
153   HINSTANCE hLibrary;
154   struct tagOpenDll *next;
155 } OpenDll;
156
157 static OpenDll *openDllList = NULL; /* linked list of open dlls */
158
159 static CRITICAL_SECTION csOpenDllList;
160 static CRITICAL_SECTION_DEBUG dll_cs_debug =
161 {
162     0, 0, &csOpenDllList,
163     { &dll_cs_debug.ProcessLocksList, &dll_cs_debug.ProcessLocksList },
164       0, 0, { (DWORD_PTR)(__FILE__ ": csOpenDllList") }
165 };
166 static CRITICAL_SECTION csOpenDllList = { &dll_cs_debug, -1, 0, 0, 0, 0 };
167
168 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',' ',
169                                        '0','x','#','#','#','#','#','#','#','#',' ',0};
170 static LRESULT CALLBACK apartment_wndproc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
171
172 static void COMPOBJ_DLLList_Add(HANDLE hLibrary);
173 static void COMPOBJ_DllList_FreeUnused(int Timeout);
174
175 static void COMPOBJ_InitProcess( void )
176 {
177     WNDCLASSW wclass;
178
179     /* Dispatching to the correct thread in an apartment is done through
180      * window messages rather than RPC transports. When an interface is
181      * marshalled into another apartment in the same process, a window of the
182      * following class is created. The *caller* of CoMarshalInterface (ie the
183      * application) is responsible for pumping the message loop in that thread.
184      * The WM_USER messages which point to the RPCs are then dispatched to
185      * COM_AptWndProc by the user's code from the apartment in which the interface
186      * was unmarshalled.
187      */
188     memset(&wclass, 0, sizeof(wclass));
189     wclass.lpfnWndProc = apartment_wndproc;
190     wclass.hInstance = OLE32_hInstance;
191     wclass.lpszClassName = wszAptWinClass;
192     RegisterClassW(&wclass);
193 }
194
195 static void COMPOBJ_UninitProcess( void )
196 {
197     UnregisterClassW(wszAptWinClass, OLE32_hInstance);
198 }
199
200 static void COM_TlsDestroy(void)
201 {
202     struct oletls *info = NtCurrentTeb()->ReservedForOle;
203     if (info)
204     {
205         if (info->apt) apartment_release(info->apt);
206         if (info->errorinfo) IErrorInfo_Release(info->errorinfo);
207         if (info->state) IUnknown_Release(info->state);
208         HeapFree(GetProcessHeap(), 0, info);
209         NtCurrentTeb()->ReservedForOle = NULL;
210     }
211 }
212
213 /******************************************************************************
214  * Manage apartments.
215  */
216
217 /* allocates memory and fills in the necessary fields for a new apartment
218  * object. must be called inside apartment cs */
219 static APARTMENT *apartment_construct(DWORD model)
220 {
221     APARTMENT *apt;
222
223     TRACE("creating new apartment, model=%d\n", model);
224
225     apt = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*apt));
226     apt->tid = GetCurrentThreadId();
227
228     list_init(&apt->proxies);
229     list_init(&apt->stubmgrs);
230     list_init(&apt->psclsids);
231     apt->ipidc = 0;
232     apt->refs = 1;
233     apt->remunk_exported = FALSE;
234     apt->oidc = 1;
235     InitializeCriticalSection(&apt->cs);
236     DEBUG_SET_CRITSEC_NAME(&apt->cs, "apartment");
237
238     apt->multi_threaded = !(model & COINIT_APARTMENTTHREADED);
239
240     if (apt->multi_threaded)
241     {
242         /* FIXME: should be randomly generated by in an RPC call to rpcss */
243         apt->oxid = ((OXID)GetCurrentProcessId() << 32) | 0xcafe;
244     }
245     else
246     {
247         /* FIXME: should be randomly generated by in an RPC call to rpcss */
248         apt->oxid = ((OXID)GetCurrentProcessId() << 32) | GetCurrentThreadId();
249     }
250
251     TRACE("Created apartment on OXID %s\n", wine_dbgstr_longlong(apt->oxid));
252
253     list_add_head(&apts, &apt->entry);
254
255     return apt;
256 }
257
258 /* gets and existing apartment if one exists or otherwise creates an apartment
259  * structure which stores OLE apartment-local information and stores a pointer
260  * to it in the thread-local storage */
261 static APARTMENT *apartment_get_or_create(DWORD model)
262 {
263     APARTMENT *apt = COM_CurrentApt();
264
265     if (!apt)
266     {
267         if (model & COINIT_APARTMENTTHREADED)
268         {
269             EnterCriticalSection(&csApartment);
270
271             apt = apartment_construct(model);
272             if (!MainApartment)
273             {
274                 MainApartment = apt;
275                 apt->main = TRUE;
276                 TRACE("Created main-threaded apartment with OXID %s\n", wine_dbgstr_longlong(apt->oxid));
277             }
278
279             LeaveCriticalSection(&csApartment);
280         }
281         else
282         {
283             EnterCriticalSection(&csApartment);
284
285             /* The multi-threaded apartment (MTA) contains zero or more threads interacting
286              * with free threaded (ie thread safe) COM objects. There is only ever one MTA
287              * in a process */
288             if (MTA)
289             {
290                 TRACE("entering the multithreaded apartment %s\n", wine_dbgstr_longlong(MTA->oxid));
291                 apartment_addref(MTA);
292             }
293             else
294                 MTA = apartment_construct(model);
295
296             apt = MTA;
297
298             LeaveCriticalSection(&csApartment);
299         }
300         COM_CurrentInfo()->apt = apt;
301     }
302
303     return apt;
304 }
305
306 static inline BOOL apartment_is_model(APARTMENT *apt, DWORD model)
307 {
308     return (apt->multi_threaded == !(model & COINIT_APARTMENTTHREADED));
309 }
310
311 DWORD apartment_addref(struct apartment *apt)
312 {
313     DWORD refs = InterlockedIncrement(&apt->refs);
314     TRACE("%s: before = %d\n", wine_dbgstr_longlong(apt->oxid), refs - 1);
315     return refs;
316 }
317
318 DWORD apartment_release(struct apartment *apt)
319 {
320     DWORD ret;
321
322     EnterCriticalSection(&csApartment);
323
324     ret = InterlockedDecrement(&apt->refs);
325     TRACE("%s: after = %d\n", wine_dbgstr_longlong(apt->oxid), ret);
326     /* destruction stuff that needs to happen under csApartment CS */
327     if (ret == 0)
328     {
329         if (apt == MTA) MTA = NULL;
330         else if (apt == MainApartment) MainApartment = NULL;
331         list_remove(&apt->entry);
332     }
333
334     LeaveCriticalSection(&csApartment);
335
336     if (ret == 0)
337     {
338         struct list *cursor, *cursor2;
339
340         TRACE("destroying apartment %p, oxid %s\n", apt, wine_dbgstr_longlong(apt->oxid));
341
342         /* no locking is needed for this apartment, because no other thread
343          * can access it at this point */
344
345         apartment_disconnectproxies(apt);
346
347         if (apt->win) DestroyWindow(apt->win);
348
349         LIST_FOR_EACH_SAFE(cursor, cursor2, &apt->stubmgrs)
350         {
351             struct stub_manager *stubmgr = LIST_ENTRY(cursor, struct stub_manager, entry);
352             /* release the implicit reference given by the fact that the
353              * stub has external references (it must do since it is in the
354              * stub manager list in the apartment and all non-apartment users
355              * must have a ref on the apartment and so it cannot be destroyed).
356              */
357             stub_manager_int_release(stubmgr);
358         }
359
360         LIST_FOR_EACH_SAFE(cursor, cursor2, &apt->psclsids)
361         {
362             struct registered_psclsid *registered_psclsid =
363                 LIST_ENTRY(cursor, struct registered_psclsid, entry);
364
365             list_remove(&registered_psclsid->entry);
366             HeapFree(GetProcessHeap(), 0, registered_psclsid);
367         }
368
369         /* if this assert fires, then another thread took a reference to a
370          * stub manager without taking a reference to the containing
371          * apartment, which it must do. */
372         assert(list_empty(&apt->stubmgrs));
373
374         if (apt->filter) IUnknown_Release(apt->filter);
375
376         DEBUG_CLEAR_CRITSEC_NAME(&apt->cs);
377         DeleteCriticalSection(&apt->cs);
378
379         HeapFree(GetProcessHeap(), 0, apt);
380     }
381
382     return ret;
383 }
384
385 /* The given OXID must be local to this process: 
386  *
387  * The ref parameter is here mostly to ensure people remember that
388  * they get one, you should normally take a ref for thread safety.
389  */
390 APARTMENT *apartment_findfromoxid(OXID oxid, BOOL ref)
391 {
392     APARTMENT *result = NULL;
393     struct list *cursor;
394
395     EnterCriticalSection(&csApartment);
396     LIST_FOR_EACH( cursor, &apts )
397     {
398         struct apartment *apt = LIST_ENTRY( cursor, struct apartment, entry );
399         if (apt->oxid == oxid)
400         {
401             result = apt;
402             if (ref) apartment_addref(result);
403             break;
404         }
405     }
406     LeaveCriticalSection(&csApartment);
407
408     return result;
409 }
410
411 /* gets the apartment which has a given creator thread ID. The caller must
412  * release the reference from the apartment as soon as the apartment pointer
413  * is no longer required. */
414 APARTMENT *apartment_findfromtid(DWORD tid)
415 {
416     APARTMENT *result = NULL;
417     struct list *cursor;
418
419     EnterCriticalSection(&csApartment);
420     LIST_FOR_EACH( cursor, &apts )
421     {
422         struct apartment *apt = LIST_ENTRY( cursor, struct apartment, entry );
423         if (apt->tid == tid)
424         {
425             result = apt;
426             apartment_addref(result);
427             break;
428         }
429     }
430     LeaveCriticalSection(&csApartment);
431
432     return result;
433 }
434
435 /* gets an apartment which has a given type. The caller must
436  * release the reference from the apartment as soon as the apartment pointer
437  * is no longer required. */
438 static APARTMENT *apartment_findfromtype(BOOL multi_threaded, BOOL main_apartment)
439 {
440     APARTMENT *result = NULL;
441     struct apartment *apt;
442
443     EnterCriticalSection(&csApartment);
444
445     if (!multi_threaded && main_apartment)
446     {
447         result = MainApartment;
448         if (result) apartment_addref(result);
449         LeaveCriticalSection(&csApartment);
450         return result;
451     }
452
453     LIST_FOR_EACH_ENTRY( apt, &apts, struct apartment, entry )
454     {
455         if (apt->multi_threaded == multi_threaded)
456         {
457             result = apt;
458             apartment_addref(result);
459             break;
460         }
461     }
462     LeaveCriticalSection(&csApartment);
463
464     return result;
465 }
466
467 struct host_object_params
468 {
469     HKEY hkeydll;
470     CLSID clsid; /* clsid of object to marshal */
471     IID iid; /* interface to marshal */
472     IStream *stream; /* stream that the object will be marshaled into */
473 };
474
475 static HRESULT apartment_hostobject(const struct host_object_params *params)
476 {
477     IUnknown *object;
478     HRESULT hr;
479     static const LARGE_INTEGER llZero;
480
481     TRACE("\n");
482
483     hr = get_inproc_class_object(params->hkeydll, &params->clsid, &params->iid, (void **)&object);
484     if (FAILED(hr))
485         return hr;
486
487     hr = CoMarshalInterface(params->stream, &params->iid, object, MSHCTX_INPROC, NULL, MSHLFLAGS_NORMAL);
488     if (FAILED(hr))
489         IUnknown_Release(object);
490     IStream_Seek(params->stream, llZero, STREAM_SEEK_SET, NULL);
491
492     return hr;
493 }
494
495 static LRESULT CALLBACK apartment_wndproc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
496 {
497     switch (msg)
498     {
499     case DM_EXECUTERPC:
500         RPC_ExecuteCall((struct dispatch_params *)lParam);
501         return 0;
502     case DM_HOSTOBJECT:
503         return apartment_hostobject((const struct host_object_params *)lParam);
504     default:
505         return DefWindowProcW(hWnd, msg, wParam, lParam);
506     }
507 }
508
509 HRESULT apartment_createwindowifneeded(struct apartment *apt)
510 {
511     if (apt->multi_threaded)
512         return S_OK;
513
514     if (!apt->win)
515     {
516         HWND hwnd = CreateWindowW(wszAptWinClass, NULL, 0,
517                                   0, 0, 0, 0,
518                                   0, 0, OLE32_hInstance, NULL);
519         if (!hwnd)
520         {
521             ERR("CreateWindow failed with error %d\n", GetLastError());
522             return HRESULT_FROM_WIN32(GetLastError());
523         }
524         if (InterlockedCompareExchangePointer((PVOID *)&apt->win, hwnd, NULL))
525             /* someone beat us to it */
526             DestroyWindow(hwnd);
527     }
528
529     return S_OK;
530 }
531
532 HWND apartment_getwindow(struct apartment *apt)
533 {
534     assert(!apt->multi_threaded);
535     return apt->win;
536 }
537
538 void apartment_joinmta(void)
539 {
540     apartment_addref(MTA);
541     COM_CurrentInfo()->apt = MTA;
542 }
543
544 /*****************************************************************************
545  * This section contains OpenDllList implementation
546  */
547
548 static void COMPOBJ_DLLList_Add(HANDLE hLibrary)
549 {
550     OpenDll *ptr;
551     OpenDll *tmp;
552
553     TRACE("\n");
554
555     EnterCriticalSection( &csOpenDllList );
556
557     if (openDllList == NULL) {
558         /* empty list -- add first node */
559         openDllList = HeapAlloc(GetProcessHeap(),0, sizeof(OpenDll));
560         openDllList->hLibrary=hLibrary;
561         openDllList->next = NULL;
562     } else {
563         /* search for this dll */
564         int found = FALSE;
565         for (ptr = openDllList; ptr->next != NULL; ptr=ptr->next) {
566             if (ptr->hLibrary == hLibrary) {
567                 found = TRUE;
568                 break;
569             }
570         }
571         if (!found) {
572             /* dll not found, add it */
573             tmp = openDllList;
574             openDllList = HeapAlloc(GetProcessHeap(),0, sizeof(OpenDll));
575             openDllList->hLibrary = hLibrary;
576             openDllList->next = tmp;
577         }
578     }
579
580     LeaveCriticalSection( &csOpenDllList );
581 }
582
583 static void COMPOBJ_DllList_FreeUnused(int Timeout)
584 {
585     OpenDll *curr, *next, *prev = NULL;
586     typedef HRESULT (WINAPI *DllCanUnloadNowFunc)(void);
587     DllCanUnloadNowFunc DllCanUnloadNow;
588
589     TRACE("\n");
590
591     EnterCriticalSection( &csOpenDllList );
592
593     for (curr = openDllList; curr != NULL; ) {
594         DllCanUnloadNow = (DllCanUnloadNowFunc) GetProcAddress(curr->hLibrary, "DllCanUnloadNow");
595
596         if ( (DllCanUnloadNow != NULL) && (DllCanUnloadNow() == S_OK) ) {
597             next = curr->next;
598
599             TRACE("freeing %p\n", curr->hLibrary);
600             FreeLibrary(curr->hLibrary);
601
602             HeapFree(GetProcessHeap(), 0, curr);
603             if (curr == openDllList) {
604                 openDllList = next;
605             } else {
606               prev->next = next;
607             }
608
609             curr = next;
610         } else {
611             prev = curr;
612             curr = curr->next;
613         }
614     }
615
616     LeaveCriticalSection( &csOpenDllList );
617 }
618
619 /******************************************************************************
620  *           CoBuildVersion [OLE32.@]
621  *           CoBuildVersion [COMPOBJ.1]
622  *
623  * Gets the build version of the DLL.
624  *
625  * PARAMS
626  *
627  * RETURNS
628  *      Current build version, hiword is majornumber, loword is minornumber
629  */
630 DWORD WINAPI CoBuildVersion(void)
631 {
632     TRACE("Returning version %d, build %d.\n", rmm, rup);
633     return (rmm<<16)+rup;
634 }
635
636 /******************************************************************************
637  *              CoInitialize    [OLE32.@]
638  *
639  * Initializes the COM libraries by calling CoInitializeEx with
640  * COINIT_APARTMENTTHREADED, ie it enters a STA thread.
641  *
642  * PARAMS
643  *  lpReserved [I] Pointer to IMalloc interface (obsolete, should be NULL).
644  *
645  * RETURNS
646  *  Success: S_OK if not already initialized, S_FALSE otherwise.
647  *  Failure: HRESULT code.
648  *
649  * SEE ALSO
650  *   CoInitializeEx
651  */
652 HRESULT WINAPI CoInitialize(LPVOID lpReserved)
653 {
654   /*
655    * Just delegate to the newer method.
656    */
657   return CoInitializeEx(lpReserved, COINIT_APARTMENTTHREADED);
658 }
659
660 /******************************************************************************
661  *              CoInitializeEx  [OLE32.@]
662  *
663  * Initializes the COM libraries.
664  *
665  * PARAMS
666  *  lpReserved [I] Pointer to IMalloc interface (obsolete, should be NULL).
667  *  dwCoInit   [I] One or more flags from the COINIT enumeration. See notes.
668  *
669  * RETURNS
670  *  S_OK               if successful,
671  *  S_FALSE            if this function was called already.
672  *  RPC_E_CHANGED_MODE if a previous call to CoInitializeEx specified another
673  *                     threading model.
674  *
675  * NOTES
676  *
677  * The behavior used to set the IMalloc used for memory management is
678  * obsolete.
679  * The dwCoInit parameter must specify one of the following apartment
680  * threading models:
681  *| COINIT_APARTMENTTHREADED - A single-threaded apartment (STA).
682  *| COINIT_MULTITHREADED - A multi-threaded apartment (MTA).
683  * The parameter may also specify zero or more of the following flags:
684  *| COINIT_DISABLE_OLE1DDE - Don't use DDE for OLE1 support.
685  *| COINIT_SPEED_OVER_MEMORY - Trade memory for speed.
686  *
687  * SEE ALSO
688  *   CoUninitialize
689  */
690 HRESULT WINAPI CoInitializeEx(LPVOID lpReserved, DWORD dwCoInit)
691 {
692   HRESULT hr = S_OK;
693   APARTMENT *apt;
694
695   TRACE("(%p, %x)\n", lpReserved, (int)dwCoInit);
696
697   if (lpReserved!=NULL)
698   {
699     ERR("(%p, %x) - Bad parameter passed-in %p, must be an old Windows Application\n", lpReserved, (int)dwCoInit, lpReserved);
700   }
701
702   /*
703    * Check the lock count. If this is the first time going through the initialize
704    * process, we have to initialize the libraries.
705    *
706    * And crank-up that lock count.
707    */
708   if (InterlockedExchangeAdd(&s_COMLockCount,1)==0)
709   {
710     /*
711      * Initialize the various COM libraries and data structures.
712      */
713     TRACE("() - Initializing the COM libraries\n");
714
715     /* we may need to defer this until after apartment initialisation */
716     RunningObjectTableImpl_Initialize();
717   }
718
719   if (!(apt = COM_CurrentInfo()->apt))
720   {
721     apt = apartment_get_or_create(dwCoInit);
722     if (!apt) return E_OUTOFMEMORY;
723   }
724   else if (!apartment_is_model(apt, dwCoInit))
725   {
726     /* Changing the threading model after it's been set is illegal. If this warning is triggered by Wine
727        code then we are probably using the wrong threading model to implement that API. */
728     ERR("Attempt to change threading model of this apartment from %s to %s\n",
729         apt->multi_threaded ? "multi-threaded" : "apartment threaded",
730         dwCoInit & COINIT_APARTMENTTHREADED ? "apartment threaded" : "multi-threaded");
731     return RPC_E_CHANGED_MODE;
732   }
733   else
734     hr = S_FALSE;
735
736   COM_CurrentInfo()->inits++;
737
738   return hr;
739 }
740
741 /* On COM finalization for a STA thread, the message queue is flushed to ensure no
742    pending RPCs are ignored. Non-COM messages are discarded at this point.
743  */
744 static void COM_FlushMessageQueue(void)
745 {
746     MSG message;
747     APARTMENT *apt = COM_CurrentApt();
748
749     if (!apt || !apt->win) return;
750
751     TRACE("Flushing STA message queue\n");
752
753     while (PeekMessageA(&message, NULL, 0, 0, PM_REMOVE))
754     {
755         if (message.hwnd != apt->win)
756         {
757             WARN("discarding message 0x%x for window %p\n", message.message, message.hwnd);
758             continue;
759         }
760
761         TranslateMessage(&message);
762         DispatchMessageA(&message);
763     }
764 }
765
766 /***********************************************************************
767  *           CoUninitialize   [OLE32.@]
768  *
769  * This method will decrement the refcount on the current apartment, freeing
770  * the resources associated with it if it is the last thread in the apartment.
771  * If the last apartment is freed, the function will additionally release
772  * any COM resources associated with the process.
773  *
774  * PARAMS
775  *
776  * RETURNS
777  *  Nothing.
778  *
779  * SEE ALSO
780  *   CoInitializeEx
781  */
782 void WINAPI CoUninitialize(void)
783 {
784   struct oletls * info = COM_CurrentInfo();
785   LONG lCOMRefCnt;
786
787   TRACE("()\n");
788
789   /* will only happen on OOM */
790   if (!info) return;
791
792   /* sanity check */
793   if (!info->inits)
794   {
795     ERR("Mismatched CoUninitialize\n");
796     return;
797   }
798
799   if (!--info->inits)
800   {
801     apartment_release(info->apt);
802     info->apt = NULL;
803   }
804
805   /*
806    * Decrease the reference count.
807    * If we are back to 0 locks on the COM library, make sure we free
808    * all the associated data structures.
809    */
810   lCOMRefCnt = InterlockedExchangeAdd(&s_COMLockCount,-1);
811   if (lCOMRefCnt==1)
812   {
813     TRACE("() - Releasing the COM libraries\n");
814
815     RunningObjectTableImpl_UnInitialize();
816
817     /* Release the references to the registered class objects */
818     COM_RevokeAllClasses();
819
820     /* This will free the loaded COM Dlls  */
821     CoFreeAllLibraries();
822
823     /* This ensures we deal with any pending RPCs */
824     COM_FlushMessageQueue();
825   }
826   else if (lCOMRefCnt<1) {
827     ERR( "CoUninitialize() - not CoInitialized.\n" );
828     InterlockedExchangeAdd(&s_COMLockCount,1); /* restore the lock count. */
829   }
830 }
831
832 /******************************************************************************
833  *              CoDisconnectObject      [OLE32.@]
834  *              CoDisconnectObject      [COMPOBJ.15]
835  *
836  * Disconnects all connections to this object from remote processes. Dispatches
837  * pending RPCs while blocking new RPCs from occurring, and then calls
838  * IMarshal::DisconnectObject on the given object.
839  *
840  * Typically called when the object server is forced to shut down, for instance by
841  * the user.
842  *
843  * PARAMS
844  *  lpUnk    [I] The object whose stub should be disconnected.
845  *  reserved [I] Reserved. Should be set to 0.
846  *
847  * RETURNS
848  *  Success: S_OK.
849  *  Failure: HRESULT code.
850  *
851  * SEE ALSO
852  *  CoMarshalInterface, CoReleaseMarshalData, CoLockObjectExternal
853  */
854 HRESULT WINAPI CoDisconnectObject( LPUNKNOWN lpUnk, DWORD reserved )
855 {
856     HRESULT hr;
857     IMarshal *marshal;
858     APARTMENT *apt;
859
860     TRACE("(%p, 0x%08x)\n", lpUnk, reserved);
861
862     hr = IUnknown_QueryInterface(lpUnk, &IID_IMarshal, (void **)&marshal);
863     if (hr == S_OK)
864     {
865         hr = IMarshal_DisconnectObject(marshal, reserved);
866         IMarshal_Release(marshal);
867         return hr;
868     }
869
870     apt = COM_CurrentApt();
871     if (!apt)
872         return CO_E_NOTINITIALIZED;
873
874     apartment_disconnectobject(apt, lpUnk);
875
876     /* Note: native is pretty broken here because it just silently
877      * fails, without returning an appropriate error code if the object was
878      * not found, making apps think that the object was disconnected, when
879      * it actually wasn't */
880
881     return S_OK;
882 }
883
884 /******************************************************************************
885  *              CoCreateGuid [OLE32.@]
886  *
887  * Simply forwards to UuidCreate in RPCRT4.
888  *
889  * PARAMS
890  *  pguid [O] Points to the GUID to initialize.
891  *
892  * RETURNS
893  *  Success: S_OK.
894  *  Failure: HRESULT code.
895  *
896  * SEE ALSO
897  *   UuidCreate
898  */
899 HRESULT WINAPI CoCreateGuid(GUID *pguid)
900 {
901     return UuidCreate(pguid);
902 }
903
904 /******************************************************************************
905  *              CLSIDFromString [OLE32.@]
906  *              IIDFromString   [OLE32.@]
907  *
908  * Converts a unique identifier from its string representation into
909  * the GUID struct.
910  *
911  * PARAMS
912  *  idstr [I] The string representation of the GUID.
913  *  id    [O] GUID converted from the string.
914  *
915  * RETURNS
916  *   S_OK on success
917  *   CO_E_CLASSSTRING if idstr is not a valid CLSID
918  *
919  * SEE ALSO
920  *  StringFromCLSID
921  */
922 static HRESULT WINAPI __CLSIDFromString(LPCWSTR s, CLSID *id)
923 {
924   int   i;
925   BYTE table[256];
926
927   if (!s) {
928     memset( id, 0, sizeof (CLSID) );
929     return S_OK;
930   }
931
932   /* validate the CLSID string */
933   if (strlenW(s) != 38)
934     return CO_E_CLASSSTRING;
935
936   if ((s[0]!='{') || (s[9]!='-') || (s[14]!='-') || (s[19]!='-') || (s[24]!='-') || (s[37]!='}'))
937     return CO_E_CLASSSTRING;
938
939   for (i=1; i<37; i++) {
940     if ((i == 9)||(i == 14)||(i == 19)||(i == 24)) continue;
941     if (!(((s[i] >= '0') && (s[i] <= '9'))  ||
942           ((s[i] >= 'a') && (s[i] <= 'f'))  ||
943           ((s[i] >= 'A') && (s[i] <= 'F'))))
944        return CO_E_CLASSSTRING;
945   }
946
947   TRACE("%s -> %p\n", debugstr_w(s), id);
948
949   /* quick lookup table */
950   memset(table, 0, 256);
951
952   for (i = 0; i < 10; i++) {
953     table['0' + i] = i;
954   }
955   for (i = 0; i < 6; i++) {
956     table['A' + i] = i+10;
957     table['a' + i] = i+10;
958   }
959
960   /* in form {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX} */
961
962   id->Data1 = (table[s[1]] << 28 | table[s[2]] << 24 | table[s[3]] << 20 | table[s[4]] << 16 |
963                table[s[5]] << 12 | table[s[6]] << 8  | table[s[7]] << 4  | table[s[8]]);
964   id->Data2 = table[s[10]] << 12 | table[s[11]] << 8 | table[s[12]] << 4 | table[s[13]];
965   id->Data3 = table[s[15]] << 12 | table[s[16]] << 8 | table[s[17]] << 4 | table[s[18]];
966
967   /* these are just sequential bytes */
968   id->Data4[0] = table[s[20]] << 4 | table[s[21]];
969   id->Data4[1] = table[s[22]] << 4 | table[s[23]];
970   id->Data4[2] = table[s[25]] << 4 | table[s[26]];
971   id->Data4[3] = table[s[27]] << 4 | table[s[28]];
972   id->Data4[4] = table[s[29]] << 4 | table[s[30]];
973   id->Data4[5] = table[s[31]] << 4 | table[s[32]];
974   id->Data4[6] = table[s[33]] << 4 | table[s[34]];
975   id->Data4[7] = table[s[35]] << 4 | table[s[36]];
976
977   return S_OK;
978 }
979
980 /*****************************************************************************/
981
982 HRESULT WINAPI CLSIDFromString(LPOLESTR idstr, CLSID *id )
983 {
984     HRESULT ret;
985
986     if (!id)
987         return E_INVALIDARG;
988
989     ret = __CLSIDFromString(idstr, id);
990     if(ret != S_OK) { /* It appears a ProgID is also valid */
991         ret = CLSIDFromProgID(idstr, id);
992     }
993     return ret;
994 }
995
996 /* Converts a GUID into the respective string representation. */
997 HRESULT WINE_StringFromCLSID(
998         const CLSID *id,        /* [in] GUID to be converted */
999         LPSTR idstr             /* [out] pointer to buffer to contain converted guid */
1000 ) {
1001   static const char hex[] = "0123456789ABCDEF";
1002   char *s;
1003   int   i;
1004
1005   if (!id)
1006         { ERR("called with id=Null\n");
1007           *idstr = 0x00;
1008           return E_FAIL;
1009         }
1010
1011   sprintf(idstr, "{%08X-%04X-%04X-%02X%02X-",
1012           id->Data1, id->Data2, id->Data3,
1013           id->Data4[0], id->Data4[1]);
1014   s = &idstr[25];
1015
1016   /* 6 hex bytes */
1017   for (i = 2; i < 8; i++) {
1018     *s++ = hex[id->Data4[i]>>4];
1019     *s++ = hex[id->Data4[i] & 0xf];
1020   }
1021
1022   *s++ = '}';
1023   *s++ = '\0';
1024
1025   TRACE("%p->%s\n", id, idstr);
1026
1027   return S_OK;
1028 }
1029
1030
1031 /******************************************************************************
1032  *              StringFromCLSID [OLE32.@]
1033  *              StringFromIID   [OLE32.@]
1034  *
1035  * Converts a GUID into the respective string representation.
1036  * The target string is allocated using the OLE IMalloc.
1037  *
1038  * PARAMS
1039  *  id    [I] the GUID to be converted.
1040  *  idstr [O] A pointer to a to-be-allocated pointer pointing to the resulting string.
1041  *
1042  * RETURNS
1043  *   S_OK
1044  *   E_FAIL
1045  *
1046  * SEE ALSO
1047  *  StringFromGUID2, CLSIDFromString
1048  */
1049 HRESULT WINAPI StringFromCLSID(REFCLSID id, LPOLESTR *idstr)
1050 {
1051         char            buf[80];
1052         HRESULT       ret;
1053         LPMALLOC        mllc;
1054
1055         if ((ret = CoGetMalloc(0,&mllc)))
1056                 return ret;
1057
1058         ret=WINE_StringFromCLSID(id,buf);
1059         if (!ret) {
1060             DWORD len = MultiByteToWideChar( CP_ACP, 0, buf, -1, NULL, 0 );
1061             *idstr = IMalloc_Alloc( mllc, len * sizeof(WCHAR) );
1062             MultiByteToWideChar( CP_ACP, 0, buf, -1, *idstr, len );
1063         }
1064         return ret;
1065 }
1066
1067 /******************************************************************************
1068  *              StringFromGUID2 [OLE32.@]
1069  *              StringFromGUID2 [COMPOBJ.76]
1070  *
1071  * Modified version of StringFromCLSID that allows you to specify max
1072  * buffer size.
1073  *
1074  * PARAMS
1075  *  id   [I] GUID to convert to string.
1076  *  str  [O] Buffer where the result will be stored.
1077  *  cmax [I] Size of the buffer in characters.
1078  *
1079  * RETURNS
1080  *      Success: The length of the resulting string in characters.
1081  *  Failure: 0.
1082  */
1083 INT WINAPI StringFromGUID2(REFGUID id, LPOLESTR str, INT cmax)
1084 {
1085   char          xguid[80];
1086
1087   if (WINE_StringFromCLSID(id,xguid))
1088         return 0;
1089   return MultiByteToWideChar( CP_ACP, 0, xguid, -1, str, cmax );
1090 }
1091
1092 /* open HKCR\\CLSID\\{string form of clsid}\\{keyname} key */
1093 HRESULT COM_OpenKeyForCLSID(REFCLSID clsid, LPCWSTR keyname, REGSAM access, HKEY *subkey)
1094 {
1095     static const WCHAR wszCLSIDSlash[] = {'C','L','S','I','D','\\',0};
1096     WCHAR path[CHARS_IN_GUID + ARRAYSIZE(wszCLSIDSlash) - 1];
1097     LONG res;
1098     HKEY key;
1099
1100     strcpyW(path, wszCLSIDSlash);
1101     StringFromGUID2(clsid, path + strlenW(wszCLSIDSlash), CHARS_IN_GUID);
1102     res = RegOpenKeyExW(HKEY_CLASSES_ROOT, path, 0, keyname ? KEY_READ : access, &key);
1103     if (res == ERROR_FILE_NOT_FOUND)
1104         return REGDB_E_CLASSNOTREG;
1105     else if (res != ERROR_SUCCESS)
1106         return REGDB_E_READREGDB;
1107
1108     if (!keyname)
1109     {
1110         *subkey = key;
1111         return S_OK;
1112     }
1113
1114     res = RegOpenKeyExW(key, keyname, 0, access, subkey);
1115     RegCloseKey(key);
1116     if (res == ERROR_FILE_NOT_FOUND)
1117         return REGDB_E_KEYMISSING;
1118     else if (res != ERROR_SUCCESS)
1119         return REGDB_E_READREGDB;
1120
1121     return S_OK;
1122 }
1123
1124 /* open HKCR\\AppId\\{string form of appid clsid} key */
1125 HRESULT COM_OpenKeyForAppIdFromCLSID(REFCLSID clsid, REGSAM access, HKEY *subkey)
1126 {
1127     static const WCHAR szAppId[] = { 'A','p','p','I','d',0 };
1128     static const WCHAR szAppIdKey[] = { 'A','p','p','I','d','\\',0 };
1129     DWORD res;
1130     WCHAR buf[CHARS_IN_GUID];
1131     WCHAR keyname[ARRAYSIZE(szAppIdKey) + CHARS_IN_GUID];
1132     DWORD size;
1133     HKEY hkey;
1134     DWORD type;
1135     HRESULT hr;
1136
1137     /* read the AppID value under the class's key */
1138     hr = COM_OpenKeyForCLSID(clsid, NULL, KEY_READ, &hkey);
1139     if (FAILED(hr))
1140         return hr;
1141
1142     size = sizeof(buf);
1143     res = RegQueryValueExW(hkey, szAppId, NULL, &type, (LPBYTE)buf, &size);
1144     RegCloseKey(hkey);
1145     if (res == ERROR_FILE_NOT_FOUND)
1146         return REGDB_E_KEYMISSING;
1147     else if (res != ERROR_SUCCESS || type!=REG_SZ)
1148         return REGDB_E_READREGDB;
1149
1150     strcpyW(keyname, szAppIdKey);
1151     strcatW(keyname, buf);
1152     res = RegOpenKeyExW(HKEY_CLASSES_ROOT, keyname, 0, access, subkey);
1153     if (res == ERROR_FILE_NOT_FOUND)
1154         return REGDB_E_KEYMISSING;
1155     else if (res != ERROR_SUCCESS)
1156         return REGDB_E_READREGDB;
1157
1158     return S_OK;
1159 }
1160
1161 /******************************************************************************
1162  *               ProgIDFromCLSID [OLE32.@]
1163  *
1164  * Converts a class id into the respective program ID.
1165  *
1166  * PARAMS
1167  *  clsid        [I] Class ID, as found in registry.
1168  *  ppszProgID [O] Associated ProgID.
1169  *
1170  * RETURNS
1171  *   S_OK
1172  *   E_OUTOFMEMORY
1173  *   REGDB_E_CLASSNOTREG if the given clsid has no associated ProgID
1174  */
1175 HRESULT WINAPI ProgIDFromCLSID(REFCLSID clsid, LPOLESTR *ppszProgID)
1176 {
1177     static const WCHAR wszProgID[] = {'P','r','o','g','I','D',0};
1178     HKEY     hkey;
1179     HRESULT  ret;
1180     LONG progidlen = 0;
1181
1182     if (!ppszProgID)
1183     {
1184         ERR("ppszProgId isn't optional\n");
1185         return E_INVALIDARG;
1186     }
1187
1188     *ppszProgID = NULL;
1189     ret = COM_OpenKeyForCLSID(clsid, wszProgID, KEY_READ, &hkey);
1190     if (FAILED(ret))
1191         return ret;
1192
1193     if (RegQueryValueW(hkey, NULL, NULL, &progidlen))
1194       ret = REGDB_E_CLASSNOTREG;
1195
1196     if (ret == S_OK)
1197     {
1198       *ppszProgID = CoTaskMemAlloc(progidlen * sizeof(WCHAR));
1199       if (*ppszProgID)
1200       {
1201         if (RegQueryValueW(hkey, NULL, *ppszProgID, &progidlen))
1202           ret = REGDB_E_CLASSNOTREG;
1203       }
1204       else
1205         ret = E_OUTOFMEMORY;
1206     }
1207
1208     RegCloseKey(hkey);
1209     return ret;
1210 }
1211
1212 /******************************************************************************
1213  *              CLSIDFromProgID [OLE32.@]
1214  *
1215  * Converts a program id into the respective GUID.
1216  *
1217  * PARAMS
1218  *  progid [I] Unicode program ID, as found in registry.
1219  *  clsid  [O] Associated CLSID.
1220  *
1221  * RETURNS
1222  *      Success: S_OK
1223  *  Failure: CO_E_CLASSSTRING - the given ProgID cannot be found.
1224  */
1225 HRESULT WINAPI CLSIDFromProgID(LPCOLESTR progid, LPCLSID clsid)
1226 {
1227     static const WCHAR clsidW[] = { '\\','C','L','S','I','D',0 };
1228     WCHAR buf2[CHARS_IN_GUID];
1229     LONG buf2len = sizeof(buf2);
1230     HKEY xhkey;
1231     WCHAR *buf;
1232
1233     if (!progid || !clsid)
1234     {
1235         ERR("neither progid (%p) nor clsid (%p) are optional\n", progid, clsid);
1236         return E_INVALIDARG;
1237     }
1238
1239     /* initialise clsid in case of failure */
1240     memset(clsid, 0, sizeof(*clsid));
1241
1242     buf = HeapAlloc( GetProcessHeap(),0,(strlenW(progid)+8) * sizeof(WCHAR) );
1243     strcpyW( buf, progid );
1244     strcatW( buf, clsidW );
1245     if (RegOpenKeyW(HKEY_CLASSES_ROOT,buf,&xhkey))
1246     {
1247         HeapFree(GetProcessHeap(),0,buf);
1248         return CO_E_CLASSSTRING;
1249     }
1250     HeapFree(GetProcessHeap(),0,buf);
1251
1252     if (RegQueryValueW(xhkey,NULL,buf2,&buf2len))
1253     {
1254         RegCloseKey(xhkey);
1255         return CO_E_CLASSSTRING;
1256     }
1257     RegCloseKey(xhkey);
1258     return CLSIDFromString(buf2,clsid);
1259 }
1260
1261
1262 /*****************************************************************************
1263  *             CoGetPSClsid [OLE32.@]
1264  *
1265  * Retrieves the CLSID of the proxy/stub factory that implements
1266  * IPSFactoryBuffer for the specified interface.
1267  *
1268  * PARAMS
1269  *  riid   [I] Interface whose proxy/stub CLSID is to be returned.
1270  *  pclsid [O] Where to store returned proxy/stub CLSID.
1271  * 
1272  * RETURNS
1273  *   S_OK
1274  *   E_OUTOFMEMORY
1275  *   REGDB_E_IIDNOTREG if no PSFactoryBuffer is associated with the IID, or it could not be parsed
1276  *
1277  * NOTES
1278  *
1279  * The standard marshaller activates the object with the CLSID
1280  * returned and uses the CreateProxy and CreateStub methods on its
1281  * IPSFactoryBuffer interface to construct the proxies and stubs for a
1282  * given object.
1283  *
1284  * CoGetPSClsid determines this CLSID by searching the
1285  * HKEY_CLASSES_ROOT\Interface\{string form of riid}\ProxyStubClsid32
1286  * in the registry and any interface id registered by
1287  * CoRegisterPSClsid within the current process.
1288  *
1289  * BUGS
1290  *
1291  * Native returns S_OK for interfaces with a key in HKCR\Interface, but
1292  * without a ProxyStubClsid32 key and leaves garbage in pclsid. This should be
1293  * considered a bug in native unless an application depends on this (unlikely).
1294  *
1295  * SEE ALSO
1296  *  CoRegisterPSClsid.
1297  */
1298 HRESULT WINAPI CoGetPSClsid(REFIID riid, CLSID *pclsid)
1299 {
1300     static const WCHAR wszInterface[] = {'I','n','t','e','r','f','a','c','e','\\',0};
1301     static const WCHAR wszPSC[] = {'\\','P','r','o','x','y','S','t','u','b','C','l','s','i','d','3','2',0};
1302     WCHAR path[ARRAYSIZE(wszInterface) - 1 + CHARS_IN_GUID - 1 + ARRAYSIZE(wszPSC)];
1303     WCHAR value[CHARS_IN_GUID];
1304     LONG len;
1305     HKEY hkey;
1306     APARTMENT *apt = COM_CurrentApt();
1307     struct registered_psclsid *registered_psclsid;
1308
1309     TRACE("() riid=%s, pclsid=%p\n", debugstr_guid(riid), pclsid);
1310
1311     if (!apt)
1312     {
1313         ERR("apartment not initialised\n");
1314         return CO_E_NOTINITIALIZED;
1315     }
1316
1317     if (!pclsid)
1318     {
1319         ERR("pclsid isn't optional\n");
1320         return E_INVALIDARG;
1321     }
1322
1323     EnterCriticalSection(&apt->cs);
1324
1325     LIST_FOR_EACH_ENTRY(registered_psclsid, &apt->psclsids, struct registered_psclsid, entry)
1326         if (IsEqualIID(&registered_psclsid->iid, riid))
1327         {
1328             *pclsid = registered_psclsid->clsid;
1329             LeaveCriticalSection(&apt->cs);
1330             return S_OK;
1331         }
1332
1333     LeaveCriticalSection(&apt->cs);
1334
1335     /* Interface\\{string form of riid}\\ProxyStubClsid32 */
1336     strcpyW(path, wszInterface);
1337     StringFromGUID2(riid, path + ARRAYSIZE(wszInterface) - 1, CHARS_IN_GUID);
1338     strcpyW(path + ARRAYSIZE(wszInterface) - 1 + CHARS_IN_GUID - 1, wszPSC);
1339
1340     /* Open the key.. */
1341     if (RegOpenKeyExW(HKEY_CLASSES_ROOT, path, 0, KEY_READ, &hkey))
1342     {
1343         WARN("No PSFactoryBuffer object is registered for IID %s\n", debugstr_guid(riid));
1344         return REGDB_E_IIDNOTREG;
1345     }
1346
1347     /* ... Once we have the key, query the registry to get the
1348        value of CLSID as a string, and convert it into a
1349        proper CLSID structure to be passed back to the app */
1350     len = sizeof(value);
1351     if (ERROR_SUCCESS != RegQueryValueW(hkey, NULL, value, &len))
1352     {
1353         RegCloseKey(hkey);
1354         return REGDB_E_IIDNOTREG;
1355     }
1356     RegCloseKey(hkey);
1357
1358     /* We have the CLSid we want back from the registry as a string, so
1359        lets convert it into a CLSID structure */
1360     if (CLSIDFromString(value, pclsid) != NOERROR)
1361         return REGDB_E_IIDNOTREG;
1362
1363     TRACE ("() Returning CLSID=%s\n", debugstr_guid(pclsid));
1364     return S_OK;
1365 }
1366
1367 /*****************************************************************************
1368  *             CoRegisterPSClsid [OLE32.@]
1369  *
1370  * Register a proxy/stub CLSID for the given interface in the current process
1371  * only.
1372  *
1373  * PARAMS
1374  *  riid   [I] Interface whose proxy/stub CLSID is to be registered.
1375  *  rclsid [I] CLSID of the proxy/stub.
1376  * 
1377  * RETURNS
1378  *   Success: S_OK
1379  *   Failure: E_OUTOFMEMORY
1380  *
1381  * NOTES
1382  *
1383  * This function does not add anything to the registry and the effects are
1384  * limited to the lifetime of the current process.
1385  *
1386  * SEE ALSO
1387  *  CoGetPSClsid.
1388  */
1389 HRESULT WINAPI CoRegisterPSClsid(REFIID riid, REFCLSID rclsid)
1390 {
1391     APARTMENT *apt = COM_CurrentApt();
1392     struct registered_psclsid *registered_psclsid;
1393
1394     TRACE("(%s, %s)\n", debugstr_guid(riid), debugstr_guid(rclsid));
1395
1396     if (!apt)
1397     {
1398         ERR("apartment not initialised\n");
1399         return CO_E_NOTINITIALIZED;
1400     }
1401
1402     EnterCriticalSection(&apt->cs);
1403
1404     LIST_FOR_EACH_ENTRY(registered_psclsid, &apt->psclsids, struct registered_psclsid, entry)
1405         if (IsEqualIID(&registered_psclsid->iid, riid))
1406         {
1407             registered_psclsid->clsid = *rclsid;
1408             LeaveCriticalSection(&apt->cs);
1409             return S_OK;
1410         }
1411
1412     registered_psclsid = HeapAlloc(GetProcessHeap(), 0, sizeof(struct registered_psclsid));
1413     if (!registered_psclsid)
1414     {
1415         LeaveCriticalSection(&apt->cs);
1416         return E_OUTOFMEMORY;
1417     }
1418
1419     registered_psclsid->iid = *riid;
1420     registered_psclsid->clsid = *rclsid;
1421     list_add_head(&apt->psclsids, &registered_psclsid->entry);
1422
1423     LeaveCriticalSection(&apt->cs);
1424
1425     return S_OK;
1426 }
1427
1428
1429 /***
1430  * COM_GetRegisteredClassObject
1431  *
1432  * This internal method is used to scan the registered class list to
1433  * find a class object.
1434  *
1435  * Params:
1436  *   rclsid        Class ID of the class to find.
1437  *   dwClsContext  Class context to match.
1438  *   ppv           [out] returns a pointer to the class object. Complying
1439  *                 to normal COM usage, this method will increase the
1440  *                 reference count on this object.
1441  */
1442 static HRESULT COM_GetRegisteredClassObject(
1443         REFCLSID    rclsid,
1444         DWORD       dwClsContext,
1445         LPUNKNOWN*  ppUnk)
1446 {
1447   HRESULT hr = S_FALSE;
1448   RegisteredClass *curClass;
1449
1450   /*
1451    * Sanity check
1452    */
1453   assert(ppUnk!=0);
1454
1455   EnterCriticalSection( &csRegisteredClassList );
1456
1457   LIST_FOR_EACH_ENTRY(curClass, &RegisteredClassList, RegisteredClass, entry)
1458   {
1459     /*
1460      * Check if we have a match on the class ID and context.
1461      */
1462     if ((dwClsContext & curClass->runContext) &&
1463         IsEqualGUID(&(curClass->classIdentifier), rclsid))
1464     {
1465       /*
1466        * We have a match, return the pointer to the class object.
1467        */
1468       *ppUnk = curClass->classObject;
1469
1470       IUnknown_AddRef(curClass->classObject);
1471
1472       hr = S_OK;
1473       break;
1474     }
1475   }
1476
1477   LeaveCriticalSection( &csRegisteredClassList );
1478
1479   return hr;
1480 }
1481
1482 /******************************************************************************
1483  *              CoRegisterClassObject   [OLE32.@]
1484  *
1485  * Registers the class object for a given class ID. Servers housed in EXE
1486  * files use this method instead of exporting DllGetClassObject to allow
1487  * other code to connect to their objects.
1488  *
1489  * PARAMS
1490  *  rclsid       [I] CLSID of the object to register.
1491  *  pUnk         [I] IUnknown of the object.
1492  *  dwClsContext [I] CLSCTX flags indicating the context in which to run the executable.
1493  *  flags        [I] REGCLS flags indicating how connections are made.
1494  *  lpdwRegister [I] A unique cookie that can be passed to CoRevokeClassObject.
1495  *
1496  * RETURNS
1497  *   S_OK on success,
1498  *   E_INVALIDARG if lpdwRegister or pUnk are NULL,
1499  *   CO_E_OBJISREG if the object is already registered. We should not return this.
1500  *
1501  * SEE ALSO
1502  *   CoRevokeClassObject, CoGetClassObject
1503  *
1504  * BUGS
1505  *  MSDN claims that multiple interface registrations are legal, but we
1506  *  can't do that with our current implementation.
1507  */
1508 HRESULT WINAPI CoRegisterClassObject(
1509     REFCLSID rclsid,
1510     LPUNKNOWN pUnk,
1511     DWORD dwClsContext,
1512     DWORD flags,
1513     LPDWORD lpdwRegister)
1514 {
1515   RegisteredClass* newClass;
1516   LPUNKNOWN        foundObject;
1517   HRESULT          hr;
1518
1519   TRACE("(%s,%p,0x%08x,0x%08x,%p)\n",
1520         debugstr_guid(rclsid),pUnk,dwClsContext,flags,lpdwRegister);
1521
1522   if ( (lpdwRegister==0) || (pUnk==0) )
1523     return E_INVALIDARG;
1524
1525   if (!COM_CurrentApt())
1526   {
1527       ERR("COM was not initialized\n");
1528       return CO_E_NOTINITIALIZED;
1529   }
1530
1531   *lpdwRegister = 0;
1532
1533   /* REGCLS_MULTIPLEUSE implies registering as inproc server. This is what
1534    * differentiates the flag from REGCLS_MULTI_SEPARATE. */
1535   if (flags & REGCLS_MULTIPLEUSE)
1536     dwClsContext |= CLSCTX_INPROC_SERVER;
1537
1538   /*
1539    * First, check if the class is already registered.
1540    * If it is, this should cause an error.
1541    */
1542   hr = COM_GetRegisteredClassObject(rclsid, dwClsContext, &foundObject);
1543   if (hr == S_OK) {
1544     if (flags & REGCLS_MULTIPLEUSE) {
1545       if (dwClsContext & CLSCTX_LOCAL_SERVER)
1546         hr = CoLockObjectExternal(foundObject, TRUE, FALSE);
1547       IUnknown_Release(foundObject);
1548       return hr;
1549     }
1550     IUnknown_Release(foundObject);
1551     ERR("object already registered for class %s\n", debugstr_guid(rclsid));
1552     return CO_E_OBJISREG;
1553   }
1554
1555   newClass = HeapAlloc(GetProcessHeap(), 0, sizeof(RegisteredClass));
1556   if ( newClass == NULL )
1557     return E_OUTOFMEMORY;
1558
1559   newClass->classIdentifier = *rclsid;
1560   newClass->runContext      = dwClsContext;
1561   newClass->connectFlags    = flags;
1562   newClass->pMarshaledData  = NULL;
1563   newClass->RpcRegistration = NULL;
1564
1565   /*
1566    * Use the address of the chain node as the cookie since we are sure it's
1567    * unique. FIXME: not on 64-bit platforms.
1568    */
1569   newClass->dwCookie        = (DWORD)newClass;
1570
1571   /*
1572    * Since we're making a copy of the object pointer, we have to increase its
1573    * reference count.
1574    */
1575   newClass->classObject     = pUnk;
1576   IUnknown_AddRef(newClass->classObject);
1577
1578   EnterCriticalSection( &csRegisteredClassList );
1579   list_add_tail(&RegisteredClassList, &newClass->entry);
1580   LeaveCriticalSection( &csRegisteredClassList );
1581
1582   *lpdwRegister = newClass->dwCookie;
1583
1584   if (dwClsContext & CLSCTX_LOCAL_SERVER) {
1585       IClassFactory *classfac;
1586
1587       hr = IUnknown_QueryInterface(newClass->classObject, &IID_IClassFactory,
1588                                    (LPVOID*)&classfac);
1589       if (hr) return hr;
1590
1591       hr = CreateStreamOnHGlobal(0, TRUE, &newClass->pMarshaledData);
1592       if (hr) {
1593           FIXME("Failed to create stream on hglobal, %x\n", hr);
1594           IUnknown_Release(classfac);
1595           return hr;
1596       }
1597       hr = CoMarshalInterface(newClass->pMarshaledData, &IID_IClassFactory,
1598                               (LPVOID)classfac, MSHCTX_LOCAL, NULL,
1599                               MSHLFLAGS_TABLESTRONG);
1600       if (hr) {
1601           FIXME("CoMarshalInterface failed, %x!\n",hr);
1602           IUnknown_Release(classfac);
1603           return hr;
1604       }
1605
1606       IUnknown_Release(classfac);
1607
1608       hr = RPC_StartLocalServer(&newClass->classIdentifier,
1609                                 newClass->pMarshaledData,
1610                                 flags & (REGCLS_MULTIPLEUSE|REGCLS_MULTI_SEPARATE),
1611                                 &newClass->RpcRegistration);
1612   }
1613   return S_OK;
1614 }
1615
1616 /***********************************************************************
1617  *           CoRevokeClassObject [OLE32.@]
1618  *
1619  * Removes a class object from the class registry.
1620  *
1621  * PARAMS
1622  *  dwRegister [I] Cookie returned from CoRegisterClassObject().
1623  *
1624  * RETURNS
1625  *  Success: S_OK.
1626  *  Failure: HRESULT code.
1627  *
1628  * SEE ALSO
1629  *  CoRegisterClassObject
1630  */
1631 HRESULT WINAPI CoRevokeClassObject(
1632         DWORD dwRegister)
1633 {
1634   HRESULT hr = E_INVALIDARG;
1635   RegisteredClass *curClass;
1636
1637   TRACE("(%08x)\n",dwRegister);
1638
1639   EnterCriticalSection( &csRegisteredClassList );
1640
1641   LIST_FOR_EACH_ENTRY(curClass, &RegisteredClassList, RegisteredClass, entry)
1642   {
1643     /*
1644      * Check if we have a match on the cookie.
1645      */
1646     if (curClass->dwCookie == dwRegister)
1647     {
1648       list_remove(&curClass->entry);
1649
1650       if (curClass->runContext & CLSCTX_LOCAL_SERVER)
1651         RPC_StopLocalServer(curClass->RpcRegistration);
1652
1653       /*
1654        * Release the reference to the class object.
1655        */
1656       IUnknown_Release(curClass->classObject);
1657
1658       if (curClass->pMarshaledData)
1659       {
1660         LARGE_INTEGER zero;
1661         memset(&zero, 0, sizeof(zero));
1662         IStream_Seek(curClass->pMarshaledData, zero, STREAM_SEEK_SET, NULL);
1663         CoReleaseMarshalData(curClass->pMarshaledData);
1664       }
1665
1666       /*
1667        * Free the memory used by the chain node.
1668        */
1669       HeapFree(GetProcessHeap(), 0, curClass);
1670
1671       hr = S_OK;
1672       break;
1673     }
1674   }
1675
1676   LeaveCriticalSection( &csRegisteredClassList );
1677
1678   return hr;
1679 }
1680
1681 /***********************************************************************
1682  *      COM_RegReadPath [internal]
1683  *
1684  *      Reads a registry value and expands it when necessary
1685  */
1686 static DWORD COM_RegReadPath(HKEY hkeyroot, const WCHAR *keyname, const WCHAR *valuename, WCHAR * dst, DWORD dstlen)
1687 {
1688         DWORD ret;
1689         HKEY key;
1690         DWORD keytype;
1691         WCHAR src[MAX_PATH];
1692         DWORD dwLength = dstlen * sizeof(WCHAR);
1693
1694         if((ret = RegOpenKeyExW(hkeyroot, keyname, 0, KEY_READ, &key)) == ERROR_SUCCESS) {
1695           if( (ret = RegQueryValueExW(key, NULL, NULL, &keytype, (LPBYTE)src, &dwLength)) == ERROR_SUCCESS ) {
1696             if (keytype == REG_EXPAND_SZ) {
1697               if (dstlen <= ExpandEnvironmentStringsW(src, dst, dstlen)) ret = ERROR_MORE_DATA;
1698             } else {
1699               lstrcpynW(dst, src, dstlen);
1700             }
1701           }
1702           RegCloseKey (key);
1703         }
1704         return ret;
1705 }
1706
1707 static void get_threading_model(HKEY key, LPWSTR value, DWORD len)
1708 {
1709     static const WCHAR wszThreadingModel[] = {'T','h','r','e','a','d','i','n','g','M','o','d','e','l',0};
1710     DWORD keytype;
1711     DWORD ret;
1712     DWORD dwLength = len * sizeof(WCHAR);
1713
1714     ret = RegQueryValueExW(key, wszThreadingModel, NULL, &keytype, (LPBYTE)value, &dwLength);
1715     if ((ret != ERROR_SUCCESS) || (keytype != REG_SZ))
1716         value[0] = '\0';
1717 }
1718
1719 static HRESULT get_inproc_class_object(HKEY hkeydll, REFCLSID rclsid, REFIID riid, void **ppv)
1720 {
1721     static const WCHAR wszApartment[] = {'A','p','a','r','t','m','e','n','t',0};
1722     static const WCHAR wszFree[] = {'F','r','e','e',0};
1723     static const WCHAR wszBoth[] = {'B','o','t','h',0};
1724     HINSTANCE hLibrary;
1725     typedef HRESULT (CALLBACK *DllGetClassObjectFunc)(REFCLSID clsid, REFIID iid, LPVOID *ppv);
1726     DllGetClassObjectFunc DllGetClassObject;
1727     WCHAR dllpath[MAX_PATH+1];
1728     WCHAR threading_model[10 /* strlenW(L"apartment")+1 */];
1729     HRESULT hr;
1730
1731     get_threading_model(hkeydll, threading_model, ARRAYSIZE(threading_model));
1732     /* "Apartment" */
1733     if (!strcmpiW(threading_model, wszApartment))
1734     {
1735         APARTMENT *apt = COM_CurrentApt();
1736         if (apt->multi_threaded)
1737         {
1738             /* try to find an STA */
1739             APARTMENT *host_apt = apartment_findfromtype(FALSE, FALSE);
1740             if (!host_apt)
1741                 FIXME("create a host apartment for apartment-threaded object %s\n", debugstr_guid(rclsid));
1742             if (host_apt)
1743             {
1744                 struct host_object_params params;
1745                 HWND hwnd = apartment_getwindow(host_apt);
1746
1747                 params.hkeydll = hkeydll;
1748                 params.clsid = *rclsid;
1749                 params.iid = *riid;
1750                 hr = CreateStreamOnHGlobal(NULL, TRUE, &params.stream);
1751                 if (FAILED(hr))
1752                     return hr;
1753                 hr = SendMessageW(hwnd, DM_HOSTOBJECT, 0, (LPARAM)&params);
1754                 if (SUCCEEDED(hr))
1755                     hr = CoUnmarshalInterface(params.stream, riid, ppv);
1756                 IStream_Release(params.stream);
1757                 return hr;
1758             }
1759         }
1760     }
1761     /* "Free" */
1762     else if (!strcmpiW(threading_model, wszFree))
1763     {
1764         APARTMENT *apt = COM_CurrentApt();
1765         if (!apt->multi_threaded)
1766         {
1767             FIXME("should create object %s in multi-threaded apartment\n",
1768                 debugstr_guid(rclsid));
1769         }
1770     }
1771     /* everything except "Apartment", "Free" and "Both" */
1772     else if (strcmpiW(threading_model, wszBoth))
1773     {
1774         APARTMENT *apt = COM_CurrentApt();
1775
1776         /* everything else is main-threaded */
1777         if (threading_model[0])
1778             FIXME("unrecognised threading model %s for object %s, should be main-threaded?\n",
1779                 debugstr_w(threading_model), debugstr_guid(rclsid));
1780
1781         if (apt->multi_threaded || !apt->main)
1782         {
1783             /* try to find an STA */
1784             APARTMENT *host_apt = apartment_findfromtype(FALSE, TRUE);
1785             if (!host_apt)
1786                 FIXME("create a host apartment for main-threaded object %s\n", debugstr_guid(rclsid));
1787             if (host_apt)
1788             {
1789                 struct host_object_params params;
1790                 HWND hwnd = apartment_getwindow(host_apt);
1791
1792                 params.hkeydll = hkeydll;
1793                 params.clsid = *rclsid;
1794                 params.iid = *riid;
1795                 hr = CreateStreamOnHGlobal(NULL, TRUE, &params.stream);
1796                 if (FAILED(hr))
1797                     return hr;
1798                 hr = SendMessageW(hwnd, DM_HOSTOBJECT, 0, (LPARAM)&params);
1799                 if (SUCCEEDED(hr))
1800                     hr = CoUnmarshalInterface(params.stream, riid, ppv);
1801                 IStream_Release(params.stream);
1802                 return hr;
1803             }
1804         }
1805     }
1806
1807     if (COM_RegReadPath(hkeydll, NULL, NULL, dllpath, ARRAYSIZE(dllpath)) != ERROR_SUCCESS)
1808     {
1809         /* failure: CLSID is not found in registry */
1810         WARN("class %s not registered inproc\n", debugstr_guid(rclsid));
1811         return REGDB_E_CLASSNOTREG;
1812     }
1813
1814     if ((hLibrary = LoadLibraryExW(dllpath, 0, LOAD_WITH_ALTERED_SEARCH_PATH)) == 0)
1815     {
1816         /* failure: DLL could not be loaded */
1817         ERR("couldn't load in-process dll %s\n", debugstr_w(dllpath));
1818         return E_ACCESSDENIED; /* FIXME: or should this be CO_E_DLLNOTFOUND? */
1819     }
1820
1821     if (!(DllGetClassObject = (DllGetClassObjectFunc)GetProcAddress(hLibrary, "DllGetClassObject")))
1822     {
1823         /* failure: the dll did not export DllGetClassObject */
1824         ERR("couldn't find function DllGetClassObject in %s\n", debugstr_w(dllpath));
1825         FreeLibrary( hLibrary );
1826         return CO_E_DLLNOTFOUND;
1827     }
1828
1829     /* OK: get the ClassObject */
1830     COMPOBJ_DLLList_Add( hLibrary );
1831     hr = DllGetClassObject(rclsid, riid, ppv);
1832
1833     if (hr != S_OK)
1834         ERR("DllGetClassObject returned error 0x%08x\n", hr);
1835
1836     return hr;
1837 }
1838
1839 /***********************************************************************
1840  *           CoGetClassObject [OLE32.@]
1841  *
1842  * FIXME.  If request allows of several options and there is a failure
1843  *         with one (other than not being registered) do we try the
1844  *         others or return failure?  (E.g. inprocess is registered but
1845  *         the DLL is not found but the server version works)
1846  */
1847 HRESULT WINAPI CoGetClassObject(
1848     REFCLSID rclsid, DWORD dwClsContext, COSERVERINFO *pServerInfo,
1849     REFIID iid, LPVOID *ppv)
1850 {
1851     LPUNKNOWN   regClassObject;
1852     HRESULT     hres = E_UNEXPECTED;
1853
1854     TRACE("\n\tCLSID:\t%s,\n\tIID:\t%s\n", debugstr_guid(rclsid), debugstr_guid(iid));
1855
1856     if (!ppv)
1857         return E_INVALIDARG;
1858
1859     *ppv = NULL;
1860
1861     if (!COM_CurrentApt())
1862     {
1863         ERR("apartment not initialised\n");
1864         return CO_E_NOTINITIALIZED;
1865     }
1866
1867     if (pServerInfo) {
1868         FIXME("\tpServerInfo: name=%s\n",debugstr_w(pServerInfo->pwszName));
1869         FIXME("\t\tpAuthInfo=%p\n",pServerInfo->pAuthInfo);
1870     }
1871
1872     /*
1873      * First, try and see if we can't match the class ID with one of the
1874      * registered classes.
1875      */
1876     if (S_OK == COM_GetRegisteredClassObject(rclsid, dwClsContext, &regClassObject))
1877     {
1878       /* Get the required interface from the retrieved pointer. */
1879       hres = IUnknown_QueryInterface(regClassObject, iid, ppv);
1880
1881       /*
1882        * Since QI got another reference on the pointer, we want to release the
1883        * one we already have. If QI was unsuccessful, this will release the object. This
1884        * is good since we are not returning it in the "out" parameter.
1885        */
1886       IUnknown_Release(regClassObject);
1887
1888       return hres;
1889     }
1890
1891     /* First try in-process server */
1892     if (CLSCTX_INPROC_SERVER & dwClsContext)
1893     {
1894         static const WCHAR wszInprocServer32[] = {'I','n','p','r','o','c','S','e','r','v','e','r','3','2',0};
1895         HKEY hkey;
1896
1897         if (IsEqualCLSID(rclsid, &CLSID_InProcFreeMarshaler))
1898             return FTMarshalCF_Create(iid, ppv);
1899
1900         hres = COM_OpenKeyForCLSID(rclsid, wszInprocServer32, KEY_READ, &hkey);
1901         if (FAILED(hres))
1902         {
1903             if (hres == REGDB_E_CLASSNOTREG)
1904                 ERR("class %s not registered\n", debugstr_guid(rclsid));
1905             else
1906                 WARN("class %s not registered as in-proc server\n", debugstr_guid(rclsid));
1907         }
1908
1909         if (SUCCEEDED(hres))
1910         {
1911             hres = get_inproc_class_object(hkey, rclsid, iid, ppv);
1912             RegCloseKey(hkey);
1913         }
1914
1915         /* return if we got a class, otherwise fall through to one of the
1916          * other types */
1917         if (SUCCEEDED(hres))
1918             return hres;
1919     }
1920
1921     /* Next try in-process handler */
1922     if (CLSCTX_INPROC_HANDLER & dwClsContext)
1923     {
1924         static const WCHAR wszInprocHandler32[] = {'I','n','p','r','o','c','H','a','n','d','l','e','r','3','2',0};
1925         HKEY hkey;
1926
1927         hres = COM_OpenKeyForCLSID(rclsid, wszInprocHandler32, KEY_READ, &hkey);
1928         if (FAILED(hres))
1929         {
1930             if (hres == REGDB_E_CLASSNOTREG)
1931                 ERR("class %s not registered\n", debugstr_guid(rclsid));
1932             else
1933                 WARN("class %s not registered in-proc handler\n", debugstr_guid(rclsid));
1934         }
1935
1936         if (SUCCEEDED(hres))
1937         {
1938             hres = get_inproc_class_object(hkey, rclsid, iid, ppv);
1939             RegCloseKey(hkey);
1940         }
1941
1942         /* return if we got a class, otherwise fall through to one of the
1943          * other types */
1944         if (SUCCEEDED(hres))
1945             return hres;
1946     }
1947
1948     /* Next try out of process */
1949     if (CLSCTX_LOCAL_SERVER & dwClsContext)
1950     {
1951         hres = RPC_GetLocalClassObject(rclsid,iid,ppv);
1952         if (SUCCEEDED(hres))
1953             return hres;
1954     }
1955
1956     /* Finally try remote: this requires networked DCOM (a lot of work) */
1957     if (CLSCTX_REMOTE_SERVER & dwClsContext)
1958     {
1959         FIXME ("CLSCTX_REMOTE_SERVER not supported\n");
1960         hres = E_NOINTERFACE;
1961     }
1962
1963     if (FAILED(hres))
1964         ERR("no class object %s could be created for context 0x%x\n",
1965             debugstr_guid(rclsid), dwClsContext);
1966     return hres;
1967 }
1968
1969 /***********************************************************************
1970  *        CoResumeClassObjects (OLE32.@)
1971  *
1972  * Resumes all class objects registered with REGCLS_SUSPENDED.
1973  *
1974  * RETURNS
1975  *  Success: S_OK.
1976  *  Failure: HRESULT code.
1977  */
1978 HRESULT WINAPI CoResumeClassObjects(void)
1979 {
1980        FIXME("stub\n");
1981         return S_OK;
1982 }
1983
1984 /***********************************************************************
1985  *        GetClassFile (OLE32.@)
1986  *
1987  * This function supplies the CLSID associated with the given filename.
1988  */
1989 HRESULT WINAPI GetClassFile(LPCOLESTR filePathName,CLSID *pclsid)
1990 {
1991     IStorage *pstg=0;
1992     HRESULT res;
1993     int nbElm, length, i;
1994     LONG sizeProgId;
1995     LPOLESTR *pathDec=0,absFile=0,progId=0;
1996     LPWSTR extension;
1997     static const WCHAR bkslashW[] = {'\\',0};
1998     static const WCHAR dotW[] = {'.',0};
1999
2000     TRACE("%s, %p\n", debugstr_w(filePathName), pclsid);
2001
2002     /* if the file contain a storage object the return the CLSID written by IStorage_SetClass method*/
2003     if((StgIsStorageFile(filePathName))==S_OK){
2004
2005         res=StgOpenStorage(filePathName,NULL,STGM_READ | STGM_SHARE_DENY_WRITE,NULL,0,&pstg);
2006
2007         if (SUCCEEDED(res))
2008             res=ReadClassStg(pstg,pclsid);
2009
2010         IStorage_Release(pstg);
2011
2012         return res;
2013     }
2014     /* if the file is not a storage object then attemps to match various bits in the file against a
2015        pattern in the registry. this case is not frequently used ! so I present only the psodocode for
2016        this case
2017
2018      for(i=0;i<nFileTypes;i++)
2019
2020         for(i=0;j<nPatternsForType;j++){
2021
2022             PATTERN pat;
2023             HANDLE  hFile;
2024
2025             pat=ReadPatternFromRegistry(i,j);
2026             hFile=CreateFileW(filePathName,,,,,,hFile);
2027             SetFilePosition(hFile,pat.offset);
2028             ReadFile(hFile,buf,pat.size,&r,NULL);
2029             if (memcmp(buf&pat.mask,pat.pattern.pat.size)==0){
2030
2031                 *pclsid=ReadCLSIDFromRegistry(i);
2032                 return S_OK;
2033             }
2034         }
2035      */
2036
2037     /* if the above strategies fail then search for the extension key in the registry */
2038
2039     /* get the last element (absolute file) in the path name */
2040     nbElm=FileMonikerImpl_DecomposePath(filePathName,&pathDec);
2041     absFile=pathDec[nbElm-1];
2042
2043     /* failed if the path represente a directory and not an absolute file name*/
2044     if (!lstrcmpW(absFile, bkslashW))
2045         return MK_E_INVALIDEXTENSION;
2046
2047     /* get the extension of the file */
2048     extension = NULL;
2049     length=lstrlenW(absFile);
2050     for(i = length-1; (i >= 0) && *(extension = &absFile[i]) != '.'; i--)
2051         /* nothing */;
2052
2053     if (!extension || !lstrcmpW(extension, dotW))
2054         return MK_E_INVALIDEXTENSION;
2055
2056     res=RegQueryValueW(HKEY_CLASSES_ROOT, extension, NULL, &sizeProgId);
2057
2058     /* get the progId associated to the extension */
2059     progId = CoTaskMemAlloc(sizeProgId);
2060     res = RegQueryValueW(HKEY_CLASSES_ROOT, extension, progId, &sizeProgId);
2061
2062     if (res==ERROR_SUCCESS)
2063         /* return the clsid associated to the progId */
2064         res= CLSIDFromProgID(progId,pclsid);
2065
2066     for(i=0; pathDec[i]!=NULL;i++)
2067         CoTaskMemFree(pathDec[i]);
2068     CoTaskMemFree(pathDec);
2069
2070     CoTaskMemFree(progId);
2071
2072     if (res==ERROR_SUCCESS)
2073         return res;
2074
2075     return MK_E_INVALIDEXTENSION;
2076 }
2077
2078 /***********************************************************************
2079  *           CoCreateInstance [OLE32.@]
2080  *
2081  * Creates an instance of the specified class.
2082  *
2083  * PARAMS
2084  *  rclsid       [I] Class ID to create an instance of.
2085  *  pUnkOuter    [I] Optional outer unknown to allow aggregation with another object.
2086  *  dwClsContext [I] Flags to restrict the location of the created instance.
2087  *  iid          [I] The ID of the interface of the instance to return.
2088  *  ppv          [O] On returns, contains a pointer to the specified interface of the instance.
2089  *
2090  * RETURNS
2091  *  Success: S_OK
2092  *  Failure: HRESULT code.
2093  *
2094  * NOTES
2095  *  The dwClsContext parameter can be one or more of the following:
2096  *| CLSCTX_INPROC_SERVER - Use an in-process server, such as from a DLL.
2097  *| CLSCTX_INPROC_HANDLER - Use an in-process object which handles certain functions for an object running in another process.
2098  *| CLSCTX_LOCAL_SERVER - Connect to an object running in another process.
2099  *| CLSCTX_REMOTE_SERVER - Connect to an object running on another machine.
2100  *
2101  * Aggregation is the concept of deferring the IUnknown of an object to another
2102  * object. This allows a separate object to behave as though it was part of
2103  * the object and to allow this the pUnkOuter parameter can be set. Note that
2104  * not all objects support having an outer of unknown.
2105  *
2106  * SEE ALSO
2107  *  CoGetClassObject()
2108  */
2109 HRESULT WINAPI CoCreateInstance(
2110         REFCLSID rclsid,
2111         LPUNKNOWN pUnkOuter,
2112         DWORD dwClsContext,
2113         REFIID iid,
2114         LPVOID *ppv)
2115 {
2116   HRESULT hres;
2117   LPCLASSFACTORY lpclf = 0;
2118
2119   TRACE("(rclsid=%s, pUnkOuter=%p, dwClsContext=%08x, riid=%s, ppv=%p)\n", debugstr_guid(rclsid),
2120         pUnkOuter, dwClsContext, debugstr_guid(iid), ppv);
2121
2122   /*
2123    * Sanity check
2124    */
2125   if (ppv==0)
2126     return E_POINTER;
2127
2128   /*
2129    * Initialize the "out" parameter
2130    */
2131   *ppv = 0;
2132
2133   if (!COM_CurrentApt())
2134   {
2135       ERR("apartment not initialised\n");
2136       return CO_E_NOTINITIALIZED;
2137   }
2138
2139   /*
2140    * The Standard Global Interface Table (GIT) object is a process-wide singleton.
2141    * Rather than create a class factory, we can just check for it here
2142    */
2143   if (IsEqualIID(rclsid, &CLSID_StdGlobalInterfaceTable)) {
2144     if (StdGlobalInterfaceTableInstance == NULL)
2145       StdGlobalInterfaceTableInstance = StdGlobalInterfaceTable_Construct();
2146     hres = IGlobalInterfaceTable_QueryInterface( (IGlobalInterfaceTable*) StdGlobalInterfaceTableInstance, iid, ppv);
2147     if (hres) return hres;
2148
2149     TRACE("Retrieved GIT (%p)\n", *ppv);
2150     return S_OK;
2151   }
2152
2153   /*
2154    * Get a class factory to construct the object we want.
2155    */
2156   hres = CoGetClassObject(rclsid,
2157                           dwClsContext,
2158                           NULL,
2159                           &IID_IClassFactory,
2160                           (LPVOID)&lpclf);
2161
2162   if (FAILED(hres))
2163     return hres;
2164
2165   /*
2166    * Create the object and don't forget to release the factory
2167    */
2168         hres = IClassFactory_CreateInstance(lpclf, pUnkOuter, iid, ppv);
2169         IClassFactory_Release(lpclf);
2170         if(FAILED(hres))
2171           FIXME("no instance created for interface %s of class %s, hres is 0x%08x\n",
2172                 debugstr_guid(iid), debugstr_guid(rclsid),hres);
2173
2174         return hres;
2175 }
2176
2177 /***********************************************************************
2178  *           CoCreateInstanceEx [OLE32.@]
2179  */
2180 HRESULT WINAPI CoCreateInstanceEx(
2181   REFCLSID      rclsid,
2182   LPUNKNOWN     pUnkOuter,
2183   DWORD         dwClsContext,
2184   COSERVERINFO* pServerInfo,
2185   ULONG         cmq,
2186   MULTI_QI*     pResults)
2187 {
2188   IUnknown* pUnk = NULL;
2189   HRESULT   hr;
2190   ULONG     index;
2191   ULONG     successCount = 0;
2192
2193   /*
2194    * Sanity check
2195    */
2196   if ( (cmq==0) || (pResults==NULL))
2197     return E_INVALIDARG;
2198
2199   if (pServerInfo!=NULL)
2200     FIXME("() non-NULL pServerInfo not supported!\n");
2201
2202   /*
2203    * Initialize all the "out" parameters.
2204    */
2205   for (index = 0; index < cmq; index++)
2206   {
2207     pResults[index].pItf = NULL;
2208     pResults[index].hr   = E_NOINTERFACE;
2209   }
2210
2211   /*
2212    * Get the object and get its IUnknown pointer.
2213    */
2214   hr = CoCreateInstance(rclsid,
2215                         pUnkOuter,
2216                         dwClsContext,
2217                         &IID_IUnknown,
2218                         (VOID**)&pUnk);
2219
2220   if (hr)
2221     return hr;
2222
2223   /*
2224    * Then, query for all the interfaces requested.
2225    */
2226   for (index = 0; index < cmq; index++)
2227   {
2228     pResults[index].hr = IUnknown_QueryInterface(pUnk,
2229                                                  pResults[index].pIID,
2230                                                  (VOID**)&(pResults[index].pItf));
2231
2232     if (pResults[index].hr == S_OK)
2233       successCount++;
2234   }
2235
2236   /*
2237    * Release our temporary unknown pointer.
2238    */
2239   IUnknown_Release(pUnk);
2240
2241   if (successCount == 0)
2242     return E_NOINTERFACE;
2243
2244   if (successCount!=cmq)
2245     return CO_S_NOTALLINTERFACES;
2246
2247   return S_OK;
2248 }
2249
2250 /***********************************************************************
2251  *           CoLoadLibrary (OLE32.@)
2252  *
2253  * Loads a library.
2254  *
2255  * PARAMS
2256  *  lpszLibName [I] Path to library.
2257  *  bAutoFree   [I] Whether the library should automatically be freed.
2258  *
2259  * RETURNS
2260  *  Success: Handle to loaded library.
2261  *  Failure: NULL.
2262  *
2263  * SEE ALSO
2264  *  CoFreeLibrary, CoFreeAllLibraries, CoFreeUnusedLibraries
2265  */
2266 HINSTANCE WINAPI CoLoadLibrary(LPOLESTR lpszLibName, BOOL bAutoFree)
2267 {
2268     TRACE("(%s, %d)\n", debugstr_w(lpszLibName), bAutoFree);
2269
2270     return LoadLibraryExW(lpszLibName, 0, LOAD_WITH_ALTERED_SEARCH_PATH);
2271 }
2272
2273 /***********************************************************************
2274  *           CoFreeLibrary [OLE32.@]
2275  *
2276  * Unloads a library from memory.
2277  *
2278  * PARAMS
2279  *  hLibrary [I] Handle to library to unload.
2280  *
2281  * RETURNS
2282  *  Nothing
2283  *
2284  * SEE ALSO
2285  *  CoLoadLibrary, CoFreeAllLibraries, CoFreeUnusedLibraries
2286  */
2287 void WINAPI CoFreeLibrary(HINSTANCE hLibrary)
2288 {
2289     FreeLibrary(hLibrary);
2290 }
2291
2292
2293 /***********************************************************************
2294  *           CoFreeAllLibraries [OLE32.@]
2295  *
2296  * Function for backwards compatibility only. Does nothing.
2297  *
2298  * RETURNS
2299  *  Nothing.
2300  *
2301  * SEE ALSO
2302  *  CoLoadLibrary, CoFreeLibrary, CoFreeUnusedLibraries
2303  */
2304 void WINAPI CoFreeAllLibraries(void)
2305 {
2306     /* NOP */
2307 }
2308
2309
2310 /***********************************************************************
2311  *           CoFreeUnusedLibraries [OLE32.@]
2312  *           CoFreeUnusedLibraries [COMPOBJ.17]
2313  *
2314  * Frees any unused libraries. Unused are identified as those that return
2315  * S_OK from their DllCanUnloadNow function.
2316  *
2317  * RETURNS
2318  *  Nothing.
2319  *
2320  * SEE ALSO
2321  *  CoLoadLibrary, CoFreeAllLibraries, CoFreeLibrary
2322  */
2323 void WINAPI CoFreeUnusedLibraries(void)
2324 {
2325     /* FIXME: Calls to CoFreeUnusedLibraries from any thread always route
2326      * through the main apartment's thread to call DllCanUnloadNow */
2327     COMPOBJ_DllList_FreeUnused(0);
2328 }
2329
2330 /***********************************************************************
2331  *           CoFileTimeNow [OLE32.@]
2332  *           CoFileTimeNow [COMPOBJ.82]
2333  *
2334  * Retrieves the current time in FILETIME format.
2335  *
2336  * PARAMS
2337  *  lpFileTime [O] The current time.
2338  *
2339  * RETURNS
2340  *      S_OK.
2341  */
2342 HRESULT WINAPI CoFileTimeNow( FILETIME *lpFileTime )
2343 {
2344     GetSystemTimeAsFileTime( lpFileTime );
2345     return S_OK;
2346 }
2347
2348 static void COM_RevokeAllClasses(void)
2349 {
2350   EnterCriticalSection( &csRegisteredClassList );
2351
2352   while (list_head(&RegisteredClassList))
2353   {
2354     RegisteredClass *curClass = LIST_ENTRY(list_head(&RegisteredClassList),
2355                                            RegisteredClass, entry);
2356     CoRevokeClassObject(curClass->dwCookie);
2357   }
2358
2359   LeaveCriticalSection( &csRegisteredClassList );
2360 }
2361
2362 /******************************************************************************
2363  *              CoLockObjectExternal    [OLE32.@]
2364  *
2365  * Increments or decrements the external reference count of a stub object.
2366  *
2367  * PARAMS
2368  *  pUnk                [I] Stub object.
2369  *  fLock               [I] If TRUE then increments the external ref-count,
2370  *                          otherwise decrements.
2371  *  fLastUnlockReleases [I] If TRUE then the last unlock has the effect of
2372  *                          calling CoDisconnectObject.
2373  *
2374  * RETURNS
2375  *  Success: S_OK.
2376  *  Failure: HRESULT code.
2377  *
2378  * NOTES
2379  *  If fLock is TRUE and an object is passed in that doesn't have a stub
2380  *  manager then a new stub manager is created for the object.
2381  */
2382 HRESULT WINAPI CoLockObjectExternal(
2383     LPUNKNOWN pUnk,
2384     BOOL fLock,
2385     BOOL fLastUnlockReleases)
2386 {
2387     struct stub_manager *stubmgr;
2388     struct apartment *apt;
2389
2390     TRACE("pUnk=%p, fLock=%s, fLastUnlockReleases=%s\n",
2391           pUnk, fLock ? "TRUE" : "FALSE", fLastUnlockReleases ? "TRUE" : "FALSE");
2392
2393     apt = COM_CurrentApt();
2394     if (!apt) return CO_E_NOTINITIALIZED;
2395
2396     stubmgr = get_stub_manager_from_object(apt, pUnk);
2397     
2398     if (stubmgr)
2399     {
2400         if (fLock)
2401             stub_manager_ext_addref(stubmgr, 1);
2402         else
2403             stub_manager_ext_release(stubmgr, 1, fLastUnlockReleases);
2404         
2405         stub_manager_int_release(stubmgr);
2406
2407         return S_OK;
2408     }
2409     else if (fLock)
2410     {
2411         stubmgr = new_stub_manager(apt, pUnk);
2412
2413         if (stubmgr)
2414         {
2415             stub_manager_ext_addref(stubmgr, 1);
2416             stub_manager_int_release(stubmgr);
2417         }
2418
2419         return S_OK;
2420     }
2421     else
2422     {
2423         WARN("stub object not found %p\n", pUnk);
2424         /* Note: native is pretty broken here because it just silently
2425          * fails, without returning an appropriate error code, making apps
2426          * think that the object was disconnected, when it actually wasn't */
2427         return S_OK;
2428     }
2429 }
2430
2431 /***********************************************************************
2432  *           CoInitializeWOW (OLE32.@)
2433  *
2434  * WOW equivalent of CoInitialize?
2435  *
2436  * PARAMS
2437  *  x [I] Unknown.
2438  *  y [I] Unknown.
2439  *
2440  * RETURNS
2441  *  Unknown.
2442  */
2443 HRESULT WINAPI CoInitializeWOW(DWORD x,DWORD y)
2444 {
2445     FIXME("(0x%08x,0x%08x),stub!\n",x,y);
2446     return 0;
2447 }
2448
2449 /***********************************************************************
2450  *           CoGetState [OLE32.@]
2451  *
2452  * Retrieves the thread state object previously stored by CoSetState().
2453  *
2454  * PARAMS
2455  *  ppv [I] Address where pointer to object will be stored.
2456  *
2457  * RETURNS
2458  *  Success: S_OK.
2459  *  Failure: E_OUTOFMEMORY.
2460  *
2461  * NOTES
2462  *  Crashes on all invalid ppv addresses, including NULL.
2463  *  If the function returns a non-NULL object then the caller must release its
2464  *  reference on the object when the object is no longer required.
2465  *
2466  * SEE ALSO
2467  *  CoSetState().
2468  */
2469 HRESULT WINAPI CoGetState(IUnknown ** ppv)
2470 {
2471     struct oletls *info = COM_CurrentInfo();
2472     if (!info) return E_OUTOFMEMORY;
2473
2474     *ppv = NULL;
2475
2476     if (info->state)
2477     {
2478         IUnknown_AddRef(info->state);
2479         *ppv = info->state;
2480         TRACE("apt->state=%p\n", info->state);
2481     }
2482
2483     return S_OK;
2484 }
2485
2486 /***********************************************************************
2487  *           CoSetState [OLE32.@]
2488  *
2489  * Sets the thread state object.
2490  *
2491  * PARAMS
2492  *  pv [I] Pointer to state object to be stored.
2493  *
2494  * NOTES
2495  *  The system keeps a reference on the object while the object stored.
2496  *
2497  * RETURNS
2498  *  Success: S_OK.
2499  *  Failure: E_OUTOFMEMORY.
2500  */
2501 HRESULT WINAPI CoSetState(IUnknown * pv)
2502 {
2503     struct oletls *info = COM_CurrentInfo();
2504     if (!info) return E_OUTOFMEMORY;
2505
2506     if (pv) IUnknown_AddRef(pv);
2507
2508     if (info->state)
2509     {
2510         TRACE("-- release %p now\n", info->state);
2511         IUnknown_Release(info->state);
2512     }
2513
2514     info->state = pv;
2515
2516     return S_OK;
2517 }
2518
2519
2520 /******************************************************************************
2521  *              CoTreatAsClass        [OLE32.@]
2522  *
2523  * Sets the TreatAs value of a class.
2524  *
2525  * PARAMS
2526  *  clsidOld [I] Class to set TreatAs value on.
2527  *  clsidNew [I] The class the clsidOld should be treated as.
2528  *
2529  * RETURNS
2530  *  Success: S_OK.
2531  *  Failure: HRESULT code.
2532  *
2533  * SEE ALSO
2534  *  CoGetTreatAsClass
2535  */
2536 HRESULT WINAPI CoTreatAsClass(REFCLSID clsidOld, REFCLSID clsidNew)
2537 {
2538     static const WCHAR wszAutoTreatAs[] = {'A','u','t','o','T','r','e','a','t','A','s',0};
2539     static const WCHAR wszTreatAs[] = {'T','r','e','a','t','A','s',0};
2540     HKEY hkey = NULL;
2541     WCHAR szClsidNew[CHARS_IN_GUID];
2542     HRESULT res = S_OK;
2543     WCHAR auto_treat_as[CHARS_IN_GUID];
2544     LONG auto_treat_as_size = sizeof(auto_treat_as);
2545     CLSID id;
2546
2547     res = COM_OpenKeyForCLSID(clsidOld, NULL, KEY_READ | KEY_WRITE, &hkey);
2548     if (FAILED(res))
2549         goto done;
2550     if (!memcmp( clsidOld, clsidNew, sizeof(*clsidOld) ))
2551     {
2552        if (!RegQueryValueW(hkey, wszAutoTreatAs, auto_treat_as, &auto_treat_as_size) &&
2553            !CLSIDFromString(auto_treat_as, &id))
2554        {
2555            if (RegSetValueW(hkey, wszTreatAs, REG_SZ, auto_treat_as, sizeof(auto_treat_as)))
2556            {
2557                res = REGDB_E_WRITEREGDB;
2558                goto done;
2559            }
2560        }
2561        else
2562        {
2563            RegDeleteKeyW(hkey, wszTreatAs);
2564            goto done;
2565        }
2566     }
2567     else if (!StringFromGUID2(clsidNew, szClsidNew, ARRAYSIZE(szClsidNew)) &&
2568              !RegSetValueW(hkey, wszTreatAs, REG_SZ, szClsidNew, sizeof(szClsidNew)))
2569     {
2570         res = REGDB_E_WRITEREGDB;
2571         goto done;
2572     }
2573
2574 done:
2575     if (hkey) RegCloseKey(hkey);
2576     return res;
2577 }
2578
2579 /******************************************************************************
2580  *              CoGetTreatAsClass        [OLE32.@]
2581  *
2582  * Gets the TreatAs value of a class.
2583  *
2584  * PARAMS
2585  *  clsidOld [I] Class to get the TreatAs value of.
2586  *  clsidNew [I] The class the clsidOld should be treated as.
2587  *
2588  * RETURNS
2589  *  Success: S_OK.
2590  *  Failure: HRESULT code.
2591  *
2592  * SEE ALSO
2593  *  CoSetTreatAsClass
2594  */
2595 HRESULT WINAPI CoGetTreatAsClass(REFCLSID clsidOld, LPCLSID clsidNew)
2596 {
2597     static const WCHAR wszTreatAs[] = {'T','r','e','a','t','A','s',0};
2598     HKEY hkey = NULL;
2599     WCHAR szClsidNew[CHARS_IN_GUID];
2600     HRESULT res = S_OK;
2601     LONG len = sizeof(szClsidNew);
2602
2603     FIXME("(%s,%p)\n", debugstr_guid(clsidOld), clsidNew);
2604     memcpy(clsidNew,clsidOld,sizeof(CLSID)); /* copy over old value */
2605
2606     res = COM_OpenKeyForCLSID(clsidOld, wszTreatAs, KEY_READ, &hkey);
2607     if (FAILED(res))
2608         goto done;
2609     if (RegQueryValueW(hkey, NULL, szClsidNew, &len))
2610     {
2611         res = S_FALSE;
2612         goto done;
2613     }
2614     res = CLSIDFromString(szClsidNew,clsidNew);
2615     if (FAILED(res))
2616         ERR("Failed CLSIDFromStringA(%s), hres 0x%08x\n", debugstr_w(szClsidNew), res);
2617 done:
2618     if (hkey) RegCloseKey(hkey);
2619     return res;
2620 }
2621
2622 /******************************************************************************
2623  *              CoGetCurrentProcess     [OLE32.@]
2624  *              CoGetCurrentProcess     [COMPOBJ.34]
2625  *
2626  * Gets the current process ID.
2627  *
2628  * RETURNS
2629  *  The current process ID.
2630  *
2631  * NOTES
2632  *   Is DWORD really the correct return type for this function?
2633  */
2634 DWORD WINAPI CoGetCurrentProcess(void)
2635 {
2636         return GetCurrentProcessId();
2637 }
2638
2639 /******************************************************************************
2640  *              CoRegisterMessageFilter [OLE32.@]
2641  *
2642  * Registers a message filter.
2643  *
2644  * PARAMS
2645  *  lpMessageFilter [I] Pointer to interface.
2646  *  lplpMessageFilter [O] Indirect pointer to prior instance if non-NULL.
2647  *
2648  * RETURNS
2649  *  Success: S_OK.
2650  *  Failure: HRESULT code.
2651  *
2652  * NOTES
2653  *  Both lpMessageFilter and lplpMessageFilter are optional. Passing in a NULL
2654  *  lpMessageFilter removes the message filter.
2655  *
2656  *  If lplpMessageFilter is not NULL the previous message filter will be
2657  *  returned in the memory pointer to this parameter and the caller is
2658  *  responsible for releasing the object.
2659  *
2660  *  The current thread be in an apartment otherwise the function will crash.
2661  */
2662 HRESULT WINAPI CoRegisterMessageFilter(
2663     LPMESSAGEFILTER lpMessageFilter,
2664     LPMESSAGEFILTER *lplpMessageFilter)
2665 {
2666     struct apartment *apt;
2667     IMessageFilter *lpOldMessageFilter;
2668
2669     TRACE("(%p, %p)\n", lpMessageFilter, lplpMessageFilter);
2670
2671     apt = COM_CurrentApt();
2672
2673     /* can't set a message filter in a multi-threaded apartment */
2674     if (!apt || apt->multi_threaded)
2675     {
2676         WARN("can't set message filter in MTA or uninitialized apt\n");
2677         return CO_E_NOT_SUPPORTED;
2678     }
2679
2680     if (lpMessageFilter)
2681         IMessageFilter_AddRef(lpMessageFilter);
2682
2683     EnterCriticalSection(&apt->cs);
2684
2685     lpOldMessageFilter = apt->filter;
2686     apt->filter = lpMessageFilter;
2687
2688     LeaveCriticalSection(&apt->cs);
2689
2690     if (lplpMessageFilter)
2691         *lplpMessageFilter = lpOldMessageFilter;
2692     else if (lpOldMessageFilter)
2693         IMessageFilter_Release(lpOldMessageFilter);
2694
2695     return S_OK;
2696 }
2697
2698 /***********************************************************************
2699  *           CoIsOle1Class [OLE32.@]
2700  *
2701  * Determines whether the specified class an OLE v1 class.
2702  *
2703  * PARAMS
2704  *  clsid [I] Class to test.
2705  *
2706  * RETURNS
2707  *  TRUE if the class is an OLE v1 class, or FALSE otherwise.
2708  */
2709 BOOL WINAPI CoIsOle1Class(REFCLSID clsid)
2710 {
2711   FIXME("%s\n", debugstr_guid(clsid));
2712   return FALSE;
2713 }
2714
2715 /***********************************************************************
2716  *           IsEqualGUID [OLE32.@]
2717  *
2718  * Compares two Unique Identifiers.
2719  *
2720  * PARAMS
2721  *  rguid1 [I] The first GUID to compare.
2722  *  rguid2 [I] The other GUID to compare.
2723  *
2724  * RETURNS
2725  *      TRUE if equal
2726  */
2727 #undef IsEqualGUID
2728 BOOL WINAPI IsEqualGUID(
2729      REFGUID rguid1,
2730      REFGUID rguid2)
2731 {
2732     return !memcmp(rguid1,rguid2,sizeof(GUID));
2733 }
2734
2735 /***********************************************************************
2736  *           CoInitializeSecurity [OLE32.@]
2737  */
2738 HRESULT WINAPI CoInitializeSecurity(PSECURITY_DESCRIPTOR pSecDesc, LONG cAuthSvc,
2739                                     SOLE_AUTHENTICATION_SERVICE* asAuthSvc,
2740                                     void* pReserved1, DWORD dwAuthnLevel,
2741                                     DWORD dwImpLevel, void* pReserved2,
2742                                     DWORD dwCapabilities, void* pReserved3)
2743 {
2744   FIXME("(%p,%d,%p,%p,%d,%d,%p,%d,%p) - stub!\n", pSecDesc, cAuthSvc,
2745         asAuthSvc, pReserved1, dwAuthnLevel, dwImpLevel, pReserved2,
2746         dwCapabilities, pReserved3);
2747   return S_OK;
2748 }
2749
2750 /***********************************************************************
2751  *           CoSuspendClassObjects [OLE32.@]
2752  *
2753  * Suspends all registered class objects to prevent further requests coming in
2754  * for those objects.
2755  *
2756  * RETURNS
2757  *  Success: S_OK.
2758  *  Failure: HRESULT code.
2759  */
2760 HRESULT WINAPI CoSuspendClassObjects(void)
2761 {
2762     FIXME("\n");
2763     return S_OK;
2764 }
2765
2766 /***********************************************************************
2767  *           CoAddRefServerProcess [OLE32.@]
2768  *
2769  * Helper function for incrementing the reference count of a local-server
2770  * process.
2771  *
2772  * RETURNS
2773  *  New reference count.
2774  */
2775 ULONG WINAPI CoAddRefServerProcess(void)
2776 {
2777     FIXME("\n");
2778     return 2;
2779 }
2780
2781 /***********************************************************************
2782  *           CoReleaseServerProcess [OLE32.@]
2783  *
2784  * Helper function for decrementing the reference count of a local-server
2785  * process.
2786  *
2787  * RETURNS
2788  *  New reference count.
2789  */
2790 ULONG WINAPI CoReleaseServerProcess(void)
2791 {
2792     FIXME("\n");
2793     return 1;
2794 }
2795
2796 /***********************************************************************
2797  *           CoIsHandlerConnected [OLE32.@]
2798  *
2799  * Determines whether a proxy is connected to a remote stub.
2800  *
2801  * PARAMS
2802  *  pUnk [I] Pointer to object that may or may not be connected.
2803  *
2804  * RETURNS
2805  *  TRUE if pUnk is not a proxy or if pUnk is connected to a remote stub, or
2806  *  FALSE otherwise.
2807  */
2808 BOOL WINAPI CoIsHandlerConnected(IUnknown *pUnk)
2809 {
2810     FIXME("%p\n", pUnk);
2811
2812     return TRUE;
2813 }
2814
2815 /***********************************************************************
2816  *           CoAllowSetForegroundWindow [OLE32.@]
2817  *
2818  */
2819 HRESULT WINAPI CoAllowSetForegroundWindow(IUnknown *pUnk, void *pvReserved)
2820 {
2821     FIXME("(%p, %p): stub\n", pUnk, pvReserved);
2822     return S_OK;
2823 }
2824  
2825 /***********************************************************************
2826  *           CoQueryProxyBlanket [OLE32.@]
2827  *
2828  * Retrieves the security settings being used by a proxy.
2829  *
2830  * PARAMS
2831  *  pProxy        [I] Pointer to the proxy object.
2832  *  pAuthnSvc     [O] The type of authentication service.
2833  *  pAuthzSvc     [O] The type of authorization service.
2834  *  ppServerPrincName [O] Optional. The server prinicple name.
2835  *  pAuthnLevel   [O] The authentication level.
2836  *  pImpLevel     [O] The impersonation level.
2837  *  ppAuthInfo    [O] Information specific to the authorization/authentication service.
2838  *  pCapabilities [O] Flags affecting the security behaviour.
2839  *
2840  * RETURNS
2841  *  Success: S_OK.
2842  *  Failure: HRESULT code.
2843  *
2844  * SEE ALSO
2845  *  CoCopyProxy, CoSetProxyBlanket.
2846  */
2847 HRESULT WINAPI CoQueryProxyBlanket(IUnknown *pProxy, DWORD *pAuthnSvc,
2848     DWORD *pAuthzSvc, OLECHAR **ppServerPrincName, DWORD *pAuthnLevel,
2849     DWORD *pImpLevel, void **ppAuthInfo, DWORD *pCapabilities)
2850 {
2851     IClientSecurity *pCliSec;
2852     HRESULT hr;
2853
2854     TRACE("%p\n", pProxy);
2855
2856     hr = IUnknown_QueryInterface(pProxy, &IID_IClientSecurity, (void **)&pCliSec);
2857     if (SUCCEEDED(hr))
2858     {
2859         hr = IClientSecurity_QueryBlanket(pCliSec, pProxy, pAuthnSvc,
2860                                           pAuthzSvc, ppServerPrincName,
2861                                           pAuthnLevel, pImpLevel, ppAuthInfo,
2862                                           pCapabilities);
2863         IClientSecurity_Release(pCliSec);
2864     }
2865
2866     if (FAILED(hr)) ERR("-- failed with 0x%08x\n", hr);
2867     return hr;
2868 }
2869
2870 /***********************************************************************
2871  *           CoSetProxyBlanket [OLE32.@]
2872  *
2873  * Sets the security settings for a proxy.
2874  *
2875  * PARAMS
2876  *  pProxy       [I] Pointer to the proxy object.
2877  *  AuthnSvc     [I] The type of authentication service.
2878  *  AuthzSvc     [I] The type of authorization service.
2879  *  pServerPrincName [I] The server prinicple name.
2880  *  AuthnLevel   [I] The authentication level.
2881  *  ImpLevel     [I] The impersonation level.
2882  *  pAuthInfo    [I] Information specific to the authorization/authentication service.
2883  *  Capabilities [I] Flags affecting the security behaviour.
2884  *
2885  * RETURNS
2886  *  Success: S_OK.
2887  *  Failure: HRESULT code.
2888  *
2889  * SEE ALSO
2890  *  CoQueryProxyBlanket, CoCopyProxy.
2891  */
2892 HRESULT WINAPI CoSetProxyBlanket(IUnknown *pProxy, DWORD AuthnSvc,
2893     DWORD AuthzSvc, OLECHAR *pServerPrincName, DWORD AuthnLevel,
2894     DWORD ImpLevel, void *pAuthInfo, DWORD Capabilities)
2895 {
2896     IClientSecurity *pCliSec;
2897     HRESULT hr;
2898
2899     TRACE("%p\n", pProxy);
2900
2901     hr = IUnknown_QueryInterface(pProxy, &IID_IClientSecurity, (void **)&pCliSec);
2902     if (SUCCEEDED(hr))
2903     {
2904         hr = IClientSecurity_SetBlanket(pCliSec, pProxy, AuthnSvc,
2905                                         AuthzSvc, pServerPrincName,
2906                                         AuthnLevel, ImpLevel, pAuthInfo,
2907                                         Capabilities);
2908         IClientSecurity_Release(pCliSec);
2909     }
2910
2911     if (FAILED(hr)) ERR("-- failed with 0x%08x\n", hr);
2912     return hr;
2913 }
2914
2915 /***********************************************************************
2916  *           CoCopyProxy [OLE32.@]
2917  *
2918  * Copies a proxy.
2919  *
2920  * PARAMS
2921  *  pProxy [I] Pointer to the proxy object.
2922  *  ppCopy [O] Copy of the proxy.
2923  *
2924  * RETURNS
2925  *  Success: S_OK.
2926  *  Failure: HRESULT code.
2927  *
2928  * SEE ALSO
2929  *  CoQueryProxyBlanket, CoSetProxyBlanket.
2930  */
2931 HRESULT WINAPI CoCopyProxy(IUnknown *pProxy, IUnknown **ppCopy)
2932 {
2933     IClientSecurity *pCliSec;
2934     HRESULT hr;
2935
2936     TRACE("%p\n", pProxy);
2937
2938     hr = IUnknown_QueryInterface(pProxy, &IID_IClientSecurity, (void **)&pCliSec);
2939     if (SUCCEEDED(hr))
2940     {
2941         hr = IClientSecurity_CopyProxy(pCliSec, pProxy, ppCopy);
2942         IClientSecurity_Release(pCliSec);
2943     }
2944
2945     if (FAILED(hr)) ERR("-- failed with 0x%08x\n", hr);
2946     return hr;
2947 }
2948
2949
2950 /***********************************************************************
2951  *           CoGetCallContext [OLE32.@]
2952  *
2953  * Gets the context of the currently executing server call in the current
2954  * thread.
2955  *
2956  * PARAMS
2957  *  riid [I] Context interface to return.
2958  *  ppv  [O] Pointer to memory that will receive the context on return.
2959  *
2960  * RETURNS
2961  *  Success: S_OK.
2962  *  Failure: HRESULT code.
2963  */
2964 HRESULT WINAPI CoGetCallContext(REFIID riid, void **ppv)
2965 {
2966     FIXME("(%s, %p): stub\n", debugstr_guid(riid), ppv);
2967
2968     *ppv = NULL;
2969     return E_NOINTERFACE;
2970 }
2971
2972 /***********************************************************************
2973  *           CoQueryClientBlanket [OLE32.@]
2974  *
2975  * Retrieves the authentication information about the client of the currently
2976  * executing server call in the current thread.
2977  *
2978  * PARAMS
2979  *  pAuthnSvc     [O] Optional. The type of authentication service.
2980  *  pAuthzSvc     [O] Optional. The type of authorization service.
2981  *  pServerPrincName [O] Optional. The server prinicple name.
2982  *  pAuthnLevel   [O] Optional. The authentication level.
2983  *  pImpLevel     [O] Optional. The impersonation level.
2984  *  pPrivs        [O] Optional. Information about the privileges of the client.
2985  *  pCapabilities [IO] Optional. Flags affecting the security behaviour.
2986  *
2987  * RETURNS
2988  *  Success: S_OK.
2989  *  Failure: HRESULT code.
2990  *
2991  * SEE ALSO
2992  *  CoImpersonateClient, CoRevertToSelf, CoGetCallContext.
2993  */
2994 HRESULT WINAPI CoQueryClientBlanket(
2995     DWORD *pAuthnSvc,
2996     DWORD *pAuthzSvc,
2997     OLECHAR **pServerPrincName,
2998     DWORD *pAuthnLevel,
2999     DWORD *pImpLevel,
3000     RPC_AUTHZ_HANDLE *pPrivs,
3001     DWORD *pCapabilities)
3002 {
3003     IServerSecurity *pSrvSec;
3004     HRESULT hr;
3005
3006     TRACE("(%p, %p, %p, %p, %p, %p, %p)\n",
3007         pAuthnSvc, pAuthzSvc, pServerPrincName, pAuthnLevel, pImpLevel,
3008         pPrivs, pCapabilities);
3009
3010     hr = CoGetCallContext(&IID_IServerSecurity, (void **)&pSrvSec);
3011     if (SUCCEEDED(hr))
3012     {
3013         hr = IServerSecurity_QueryBlanket(
3014             pSrvSec, pAuthnSvc, pAuthzSvc, pServerPrincName, pAuthnLevel,
3015             pImpLevel, pPrivs, pCapabilities);
3016         IServerSecurity_Release(pSrvSec);
3017     }
3018
3019     return hr;
3020 }
3021
3022 /***********************************************************************
3023  *           CoImpersonateClient [OLE32.@]
3024  *
3025  * Impersonates the client of the currently executing server call in the
3026  * current thread.
3027  *
3028  * PARAMS
3029  *  None.
3030  *
3031  * RETURNS
3032  *  Success: S_OK.
3033  *  Failure: HRESULT code.
3034  *
3035  * NOTES
3036  *  If this function fails then the current thread will not be impersonating
3037  *  the client and all actions will take place on behalf of the server.
3038  *  Therefore, it is important to check the return value from this function.
3039  *
3040  * SEE ALSO
3041  *  CoRevertToSelf, CoQueryClientBlanket, CoGetCallContext.
3042  */
3043 HRESULT WINAPI CoImpersonateClient(void)
3044 {
3045     IServerSecurity *pSrvSec;
3046     HRESULT hr;
3047
3048     TRACE("\n");
3049
3050     hr = CoGetCallContext(&IID_IServerSecurity, (void **)&pSrvSec);
3051     if (SUCCEEDED(hr))
3052     {
3053         hr = IServerSecurity_ImpersonateClient(pSrvSec);
3054         IServerSecurity_Release(pSrvSec);
3055     }
3056
3057     return hr;
3058 }
3059
3060 /***********************************************************************
3061  *           CoRevertToSelf [OLE32.@]
3062  *
3063  * Ends the impersonation of the client of the currently executing server
3064  * call in the current thread.
3065  *
3066  * PARAMS
3067  *  None.
3068  *
3069  * RETURNS
3070  *  Success: S_OK.
3071  *  Failure: HRESULT code.
3072  *
3073  * SEE ALSO
3074  *  CoImpersonateClient, CoQueryClientBlanket, CoGetCallContext.
3075  */
3076 HRESULT WINAPI CoRevertToSelf(void)
3077 {
3078     IServerSecurity *pSrvSec;
3079     HRESULT hr;
3080
3081     TRACE("\n");
3082
3083     hr = CoGetCallContext(&IID_IServerSecurity, (void **)&pSrvSec);
3084     if (SUCCEEDED(hr))
3085     {
3086         hr = IServerSecurity_RevertToSelf(pSrvSec);
3087         IServerSecurity_Release(pSrvSec);
3088     }
3089
3090     return hr;
3091 }
3092
3093 static BOOL COM_PeekMessage(struct apartment *apt, MSG *msg)
3094 {
3095     /* first try to retrieve messages for incoming COM calls to the apartment window */
3096     return PeekMessageW(msg, apt->win, WM_USER, WM_APP - 1, PM_REMOVE|PM_NOYIELD) ||
3097            /* next retrieve other messages necessary for the app to remain responsive */
3098            PeekMessageW(msg, NULL, 0, WM_USER - 1, PM_REMOVE|PM_NOYIELD);
3099 }
3100
3101 /***********************************************************************
3102  *           CoWaitForMultipleHandles [OLE32.@]
3103  *
3104  * Waits for one or more handles to become signaled.
3105  *
3106  * PARAMS
3107  *  dwFlags   [I] Flags. See notes.
3108  *  dwTimeout [I] Timeout in milliseconds.
3109  *  cHandles  [I] Number of handles pointed to by pHandles.
3110  *  pHandles  [I] Handles to wait for.
3111  *  lpdwindex [O] Index of handle that was signaled.
3112  *
3113  * RETURNS
3114  *  Success: S_OK.
3115  *  Failure: RPC_S_CALLPENDING on timeout.
3116  *
3117  * NOTES
3118  *
3119  * The dwFlags parameter can be zero or more of the following:
3120  *| COWAIT_WAITALL - Wait for all of the handles to become signaled.
3121  *| COWAIT_ALERTABLE - Allows a queued APC to run during the wait.
3122  *
3123  * SEE ALSO
3124  *  MsgWaitForMultipleObjects, WaitForMultipleObjects.
3125  */
3126 HRESULT WINAPI CoWaitForMultipleHandles(DWORD dwFlags, DWORD dwTimeout,
3127     ULONG cHandles, LPHANDLE pHandles, LPDWORD lpdwindex)
3128 {
3129     HRESULT hr = S_OK;
3130     DWORD start_time = GetTickCount();
3131     APARTMENT *apt = COM_CurrentApt();
3132     BOOL message_loop = apt && !apt->multi_threaded;
3133
3134     TRACE("(0x%08x, 0x%08x, %d, %p, %p)\n", dwFlags, dwTimeout, cHandles,
3135         pHandles, lpdwindex);
3136
3137     while (TRUE)
3138     {
3139         DWORD now = GetTickCount();
3140         DWORD res;
3141
3142         if ((dwTimeout != INFINITE) && (start_time + dwTimeout >= now))
3143         {
3144             hr = RPC_S_CALLPENDING;
3145             break;
3146         }
3147
3148         if (message_loop)
3149         {
3150             DWORD wait_flags = (dwFlags & COWAIT_WAITALL) ? MWMO_WAITALL : 0 |
3151                     (dwFlags & COWAIT_ALERTABLE ) ? MWMO_ALERTABLE : 0;
3152
3153             TRACE("waiting for rpc completion or window message\n");
3154
3155             res = MsgWaitForMultipleObjectsEx(cHandles, pHandles,
3156                 (dwTimeout == INFINITE) ? INFINITE : start_time + dwTimeout - now,
3157                 QS_ALLINPUT, wait_flags);
3158
3159             if (res == WAIT_OBJECT_0 + cHandles)  /* messages available */
3160             {
3161                 MSG msg;
3162
3163                 /* call message filter */
3164
3165                 if (COM_CurrentApt()->filter)
3166                 {
3167                     PENDINGTYPE pendingtype =
3168                         COM_CurrentInfo()->pending_call_count_server ?
3169                             PENDINGTYPE_NESTED : PENDINGTYPE_TOPLEVEL;
3170                     DWORD be_handled = IMessageFilter_MessagePending(
3171                         COM_CurrentApt()->filter, 0 /* FIXME */,
3172                         now - start_time, pendingtype);
3173                     TRACE("IMessageFilter_MessagePending returned %d\n", be_handled);
3174                     switch (be_handled)
3175                     {
3176                     case PENDINGMSG_CANCELCALL:
3177                         WARN("call canceled\n");
3178                         hr = RPC_E_CALL_CANCELED;
3179                         break;
3180                     case PENDINGMSG_WAITNOPROCESS:
3181                     case PENDINGMSG_WAITDEFPROCESS:
3182                     default:
3183                         /* FIXME: MSDN is very vague about the difference
3184                          * between WAITNOPROCESS and WAITDEFPROCESS - there
3185                          * appears to be none, so it is possibly a left-over
3186                          * from the 16-bit world. */
3187                         break;
3188                     }
3189                 }
3190
3191                 /* note: using "if" here instead of "while" might seem less
3192                  * efficient, but only if we are optimising for quick delivery
3193                  * of pending messages, rather than quick completion of the
3194                  * COM call */
3195                 if (COM_PeekMessage(apt, &msg))
3196                 {
3197                     TRACE("received message whilst waiting for RPC: 0x%04x\n", msg.message);
3198                     TranslateMessage(&msg);
3199                     DispatchMessageW(&msg);
3200                     if (msg.message == WM_QUIT)
3201                     {
3202                         TRACE("resending WM_QUIT to outer message loop\n");
3203                         PostQuitMessage(msg.wParam);
3204                         /* no longer need to process messages */
3205                         message_loop = FALSE;
3206                     }
3207                 }
3208                 continue;
3209             }
3210         }
3211         else
3212         {
3213             TRACE("waiting for rpc completion\n");
3214
3215             res = WaitForMultipleObjectsEx(cHandles, pHandles,
3216                 (dwFlags & COWAIT_WAITALL) ? TRUE : FALSE,
3217                 (dwTimeout == INFINITE) ? INFINITE : start_time + dwTimeout - now,
3218                 (dwFlags & COWAIT_ALERTABLE) ? TRUE : FALSE);
3219         }
3220
3221         if ((res >= WAIT_OBJECT_0) && (res < WAIT_OBJECT_0 + cHandles))
3222         {
3223             /* handle signaled, store index */
3224             *lpdwindex = (res - WAIT_OBJECT_0);
3225             break;
3226         }
3227         else if (res == WAIT_TIMEOUT)
3228         {
3229             hr = RPC_S_CALLPENDING;
3230             break;
3231         }
3232         else
3233         {
3234             ERR("Unexpected wait termination: %d, %d\n", res, GetLastError());
3235             hr = E_UNEXPECTED;
3236             break;
3237         }
3238     }
3239     TRACE("-- 0x%08x\n", hr);
3240     return hr;
3241 }
3242
3243
3244 /***********************************************************************
3245  *           CoGetObject [OLE32.@]
3246  *
3247  * Gets the object named by coverting the name to a moniker and binding to it.
3248  *
3249  * PARAMS
3250  *  pszName      [I] String representing the object.
3251  *  pBindOptions [I] Parameters affecting the binding to the named object.
3252  *  riid         [I] Interface to bind to on the objecct.
3253  *  ppv          [O] On output, the interface riid of the object represented
3254  *                   by pszName.
3255  *
3256  * RETURNS
3257  *  Success: S_OK.
3258  *  Failure: HRESULT code.
3259  *
3260  * SEE ALSO
3261  *  MkParseDisplayName.
3262  */
3263 HRESULT WINAPI CoGetObject(LPCWSTR pszName, BIND_OPTS *pBindOptions,
3264     REFIID riid, void **ppv)
3265 {
3266     IBindCtx *pbc;
3267     HRESULT hr;
3268
3269     *ppv = NULL;
3270
3271     hr = CreateBindCtx(0, &pbc);
3272     if (SUCCEEDED(hr))
3273     {
3274         if (pBindOptions)
3275             hr = IBindCtx_SetBindOptions(pbc, pBindOptions);
3276
3277         if (SUCCEEDED(hr))
3278         {
3279             ULONG chEaten;
3280             IMoniker *pmk;
3281
3282             hr = MkParseDisplayName(pbc, pszName, &chEaten, &pmk);
3283             if (SUCCEEDED(hr))
3284             {
3285                 hr = IMoniker_BindToObject(pmk, pbc, NULL, riid, ppv);
3286                 IMoniker_Release(pmk);
3287             }
3288         }
3289
3290         IBindCtx_Release(pbc);
3291     }
3292     return hr;
3293 }
3294
3295 /***********************************************************************
3296  *           CoRegisterChannelHook [OLE32.@]
3297  *
3298  * Registers a process-wide hook that is called during ORPC calls.
3299  *
3300  * PARAMS
3301  *  guidExtension [I] GUID of the channel hook to register.
3302  *  pChannelHook  [I] Channel hook object to register.
3303  *
3304  * RETURNS
3305  *  Success: S_OK.
3306  *  Failure: HRESULT code.
3307  */
3308 HRESULT WINAPI CoRegisterChannelHook(REFGUID guidExtension, IChannelHook *pChannelHook)
3309 {
3310     TRACE("(%s, %p)\n", debugstr_guid(guidExtension), pChannelHook);
3311
3312     return RPC_RegisterChannelHook(guidExtension, pChannelHook);
3313 }
3314
3315 /***********************************************************************
3316  *              DllMain (OLE32.@)
3317  */
3318 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID fImpLoad)
3319 {
3320     TRACE("%p 0x%x %p\n", hinstDLL, fdwReason, fImpLoad);
3321
3322     switch(fdwReason) {
3323     case DLL_PROCESS_ATTACH:
3324         OLE32_hInstance = hinstDLL;
3325         COMPOBJ_InitProcess();
3326         if (TRACE_ON(ole)) CoRegisterMallocSpy((LPVOID)-1);
3327         break;
3328
3329     case DLL_PROCESS_DETACH:
3330         if (TRACE_ON(ole)) CoRevokeMallocSpy();
3331         COMPOBJ_UninitProcess();
3332         RPC_UnregisterAllChannelHooks();
3333         OLE32_hInstance = 0;
3334         break;
3335
3336     case DLL_THREAD_DETACH:
3337         COM_TlsDestroy();
3338         break;
3339     }
3340     return TRUE;
3341 }
3342
3343 /* NOTE: DllRegisterServer and DllUnregisterServer are in regsvr.c */