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