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