Return the correct error code from IConnectionPointContainer::FindConnectionPoint.
[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     *lplpszProgID = NULL;
1053     ret = COM_OpenKeyForCLSID(clsid, wszProgID, KEY_READ, &hkey);
1054     if (FAILED(ret))
1055         return ret;
1056
1057     if (RegQueryValueW(hkey, NULL, NULL, &progidlen))
1058       ret = REGDB_E_CLASSNOTREG;
1059
1060     if (ret == S_OK)
1061     {
1062       *lplpszProgID = CoTaskMemAlloc(progidlen * sizeof(WCHAR));
1063       if (*lplpszProgID)
1064       {
1065         if (RegQueryValueW(hkey, NULL, *lplpszProgID, &progidlen))
1066           ret = REGDB_E_CLASSNOTREG;
1067       }
1068       else
1069         ret = E_OUTOFMEMORY;
1070     }
1071
1072     RegCloseKey(hkey);
1073     return ret;
1074 }
1075
1076 /******************************************************************************
1077  *              CLSIDFromProgID [OLE32.@]
1078  *
1079  * Converts a program id into the respective GUID.
1080  *
1081  * PARAMS
1082  *  progid [I] Unicode program ID, as found in registry.
1083  *  riid   [O] Associated CLSID.
1084  *
1085  * RETURNS
1086  *      Success: S_OK
1087  *  Failure: CO_E_CLASSSTRING - the given ProgID cannot be found.
1088  */
1089 HRESULT WINAPI CLSIDFromProgID(LPCOLESTR progid, LPCLSID riid)
1090 {
1091     static const WCHAR clsidW[] = { '\\','C','L','S','I','D',0 };
1092     WCHAR buf2[CHARS_IN_GUID];
1093     LONG buf2len = sizeof(buf2);
1094     HKEY xhkey;
1095
1096     WCHAR *buf = HeapAlloc( GetProcessHeap(),0,(strlenW(progid)+8) * sizeof(WCHAR) );
1097     strcpyW( buf, progid );
1098     strcatW( buf, clsidW );
1099     if (RegOpenKeyW(HKEY_CLASSES_ROOT,buf,&xhkey))
1100     {
1101         HeapFree(GetProcessHeap(),0,buf);
1102         return CO_E_CLASSSTRING;
1103     }
1104     HeapFree(GetProcessHeap(),0,buf);
1105
1106     if (RegQueryValueW(xhkey,NULL,buf2,&buf2len))
1107     {
1108         RegCloseKey(xhkey);
1109         return CO_E_CLASSSTRING;
1110     }
1111     RegCloseKey(xhkey);
1112     return CLSIDFromString(buf2,riid);
1113 }
1114
1115
1116 /*****************************************************************************
1117  *             CoGetPSClsid [OLE32.@]
1118  *
1119  * Retrieves the CLSID of the proxy/stub factory that implements
1120  * IPSFactoryBuffer for the specified interface.
1121  *
1122  * PARAMS
1123  *  riid   [I] Interface whose proxy/stub CLSID is to be returned.
1124  *  pclsid [O] Where to store returned proxy/stub CLSID.
1125  * 
1126  * RETURNS
1127  *   S_OK
1128  *   E_OUTOFMEMORY
1129  *   REGDB_E_IIDNOTREG if no PSFactoryBuffer is associated with the IID, or it could not be parsed
1130  *
1131  * NOTES
1132  *
1133  * The standard marshaller activates the object with the CLSID
1134  * returned and uses the CreateProxy and CreateStub methods on its
1135  * IPSFactoryBuffer interface to construct the proxies and stubs for a
1136  * given object.
1137  *
1138  * CoGetPSClsid determines this CLSID by searching the
1139  * HKEY_CLASSES_ROOT\Interface\{string form of riid}\ProxyStubClsid32
1140  * in the registry and any interface id registered by
1141  * CoRegisterPSClsid within the current process.
1142  *
1143  * BUGS
1144  *
1145  * We only search the registry, not ids registered with
1146  * CoRegisterPSClsid.
1147  * Also, native returns S_OK for interfaces with a key in HKCR\Interface, but
1148  * without a ProxyStubClsid32 key and leaves garbage in pclsid. This should be
1149  * considered a bug in native unless an application depends on this (unlikely).
1150  */
1151 HRESULT WINAPI CoGetPSClsid(REFIID riid, CLSID *pclsid)
1152 {
1153     static const WCHAR wszInterface[] = {'I','n','t','e','r','f','a','c','e','\\',0};
1154     static const WCHAR wszPSC[] = {'\\','P','r','o','x','y','S','t','u','b','C','l','s','i','d','3','2',0};
1155     WCHAR path[ARRAYSIZE(wszInterface) - 1 + CHARS_IN_GUID - 1 + ARRAYSIZE(wszPSC)];
1156     WCHAR value[CHARS_IN_GUID];
1157     LONG len;
1158     HKEY hkey;
1159
1160     TRACE("() riid=%s, pclsid=%p\n", debugstr_guid(riid), pclsid);
1161
1162     /* Interface\\{string form of riid}\\ProxyStubClsid32 */
1163     strcpyW(path, wszInterface);
1164     StringFromGUID2(riid, path + ARRAYSIZE(wszInterface) - 1, CHARS_IN_GUID);
1165     strcpyW(path + ARRAYSIZE(wszInterface) - 1 + CHARS_IN_GUID - 1, wszPSC);
1166
1167     /* Open the key.. */
1168     if (RegOpenKeyExW(HKEY_CLASSES_ROOT, path, 0, KEY_READ, &hkey))
1169     {
1170         WARN("No PSFactoryBuffer object is registered for IID %s\n", debugstr_guid(riid));
1171         return REGDB_E_IIDNOTREG;
1172     }
1173
1174     /* ... Once we have the key, query the registry to get the
1175        value of CLSID as a string, and convert it into a
1176        proper CLSID structure to be passed back to the app */
1177     len = sizeof(value);
1178     if (ERROR_SUCCESS != RegQueryValueW(hkey, NULL, value, &len))
1179     {
1180         RegCloseKey(hkey);
1181         return REGDB_E_IIDNOTREG;
1182     }
1183     RegCloseKey(hkey);
1184
1185     /* We have the CLSid we want back from the registry as a string, so
1186        lets convert it into a CLSID structure */
1187     if (CLSIDFromString(value, pclsid) != NOERROR)
1188         return REGDB_E_IIDNOTREG;
1189
1190     TRACE ("() Returning CLSID=%s\n", debugstr_guid(pclsid));
1191     return S_OK;
1192 }
1193
1194
1195
1196 /***********************************************************************
1197  *              WriteClassStm (OLE32.@)
1198  *
1199  * Writes a CLSID to a stream.
1200  *
1201  * PARAMS
1202  *  pStm   [I] Stream to write to.
1203  *  rclsid [I] CLSID to write.
1204  *
1205  * RETURNS
1206  *  Success: S_OK.
1207  *  Failure: HRESULT code.
1208  */
1209 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
1210 {
1211     TRACE("(%p,%p)\n",pStm,rclsid);
1212
1213     if (rclsid==NULL)
1214         return E_INVALIDARG;
1215
1216     return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
1217 }
1218
1219 /***********************************************************************
1220  *              ReadClassStm (OLE32.@)
1221  *
1222  * Reads a CLSID from a stream.
1223  *
1224  * PARAMS
1225  *  pStm   [I] Stream to read from.
1226  *  rclsid [O] CLSID to read.
1227  *
1228  * RETURNS
1229  *  Success: S_OK.
1230  *  Failure: HRESULT code.
1231  */
1232 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
1233 {
1234     ULONG nbByte;
1235     HRESULT res;
1236
1237     TRACE("(%p,%p)\n",pStm,pclsid);
1238
1239     if (pclsid==NULL)
1240         return E_INVALIDARG;
1241
1242     res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
1243
1244     if (FAILED(res))
1245         return res;
1246
1247     if (nbByte != sizeof(CLSID))
1248         return S_FALSE;
1249     else
1250         return S_OK;
1251 }
1252
1253
1254 /***
1255  * COM_GetRegisteredClassObject
1256  *
1257  * This internal method is used to scan the registered class list to
1258  * find a class object.
1259  *
1260  * Params:
1261  *   rclsid        Class ID of the class to find.
1262  *   dwClsContext  Class context to match.
1263  *   ppv           [out] returns a pointer to the class object. Complying
1264  *                 to normal COM usage, this method will increase the
1265  *                 reference count on this object.
1266  */
1267 static HRESULT COM_GetRegisteredClassObject(
1268         REFCLSID    rclsid,
1269         DWORD       dwClsContext,
1270         LPUNKNOWN*  ppUnk)
1271 {
1272   HRESULT hr = S_FALSE;
1273   RegisteredClass* curClass;
1274
1275   EnterCriticalSection( &csRegisteredClassList );
1276
1277   /*
1278    * Sanity check
1279    */
1280   assert(ppUnk!=0);
1281
1282   /*
1283    * Iterate through the whole list and try to match the class ID.
1284    */
1285   curClass = firstRegisteredClass;
1286
1287   while (curClass != 0)
1288   {
1289     /*
1290      * Check if we have a match on the class ID.
1291      */
1292     if (IsEqualGUID(&(curClass->classIdentifier), rclsid))
1293     {
1294       /*
1295        * Since we don't do out-of process or DCOM just right away, let's ignore the
1296        * class context.
1297        */
1298
1299       /*
1300        * We have a match, return the pointer to the class object.
1301        */
1302       *ppUnk = curClass->classObject;
1303
1304       IUnknown_AddRef(curClass->classObject);
1305
1306       hr = S_OK;
1307       goto end;
1308     }
1309
1310     /*
1311      * Step to the next class in the list.
1312      */
1313     curClass = curClass->nextClass;
1314   }
1315
1316 end:
1317   LeaveCriticalSection( &csRegisteredClassList );
1318   /*
1319    * If we get to here, we haven't found our class.
1320    */
1321   return hr;
1322 }
1323
1324 /******************************************************************************
1325  *              CoRegisterClassObject   [OLE32.@]
1326  *
1327  * Registers the class object for a given class ID. Servers housed in EXE
1328  * files use this method instead of exporting DllGetClassObject to allow
1329  * other code to connect to their objects.
1330  *
1331  * PARAMS
1332  *  rclsid       [I] CLSID of the object to register.
1333  *  pUnk         [I] IUnknown of the object.
1334  *  dwClsContext [I] CLSCTX flags indicating the context in which to run the executable.
1335  *  flags        [I] REGCLS flags indicating how connections are made.
1336  *  lpdwRegister [I] A unique cookie that can be passed to CoRevokeClassObject.
1337  *
1338  * RETURNS
1339  *   S_OK on success,
1340  *   E_INVALIDARG if lpdwRegister or pUnk are NULL,
1341  *   CO_E_OBJISREG if the object is already registered. We should not return this.
1342  *
1343  * SEE ALSO
1344  *   CoRevokeClassObject, CoGetClassObject
1345  *
1346  * BUGS
1347  *  MSDN claims that multiple interface registrations are legal, but we
1348  *  can't do that with our current implementation.
1349  */
1350 HRESULT WINAPI CoRegisterClassObject(
1351     REFCLSID rclsid,
1352     LPUNKNOWN pUnk,
1353     DWORD dwClsContext,
1354     DWORD flags,
1355     LPDWORD lpdwRegister)
1356 {
1357   RegisteredClass* newClass;
1358   LPUNKNOWN        foundObject;
1359   HRESULT          hr;
1360
1361   TRACE("(%s,%p,0x%08lx,0x%08lx,%p)\n",
1362         debugstr_guid(rclsid),pUnk,dwClsContext,flags,lpdwRegister);
1363
1364   if ( (lpdwRegister==0) || (pUnk==0) )
1365     return E_INVALIDARG;
1366
1367   if (!COM_CurrentApt())
1368   {
1369       ERR("COM was not initialized\n");
1370       return CO_E_NOTINITIALIZED;
1371   }
1372
1373   *lpdwRegister = 0;
1374
1375   /*
1376    * First, check if the class is already registered.
1377    * If it is, this should cause an error.
1378    */
1379   hr = COM_GetRegisteredClassObject(rclsid, dwClsContext, &foundObject);
1380   if (hr == S_OK) {
1381     if (flags & REGCLS_MULTIPLEUSE) {
1382       if (dwClsContext & CLSCTX_LOCAL_SERVER)
1383         hr = CoLockObjectExternal(foundObject, TRUE, FALSE);
1384       IUnknown_Release(foundObject);
1385       return hr;
1386     }
1387     IUnknown_Release(foundObject);
1388     ERR("object already registered for class %s\n", debugstr_guid(rclsid));
1389     return CO_E_OBJISREG;
1390   }
1391
1392   newClass = HeapAlloc(GetProcessHeap(), 0, sizeof(RegisteredClass));
1393   if ( newClass == NULL )
1394     return E_OUTOFMEMORY;
1395
1396   EnterCriticalSection( &csRegisteredClassList );
1397
1398   newClass->classIdentifier = *rclsid;
1399   newClass->runContext      = dwClsContext;
1400   newClass->connectFlags    = flags;
1401   newClass->pMarshaledData  = NULL;
1402
1403   /*
1404    * Use the address of the chain node as the cookie since we are sure it's
1405    * unique. FIXME: not on 64-bit platforms.
1406    */
1407   newClass->dwCookie        = (DWORD)newClass;
1408   newClass->nextClass       = firstRegisteredClass;
1409
1410   /*
1411    * Since we're making a copy of the object pointer, we have to increase its
1412    * reference count.
1413    */
1414   newClass->classObject     = pUnk;
1415   IUnknown_AddRef(newClass->classObject);
1416
1417   firstRegisteredClass = newClass;
1418   LeaveCriticalSection( &csRegisteredClassList );
1419
1420   *lpdwRegister = newClass->dwCookie;
1421
1422   if (dwClsContext & CLSCTX_LOCAL_SERVER) {
1423       IClassFactory *classfac;
1424
1425       hr = IUnknown_QueryInterface(newClass->classObject, &IID_IClassFactory,
1426                                    (LPVOID*)&classfac);
1427       if (hr) return hr;
1428
1429       hr = CreateStreamOnHGlobal(0, TRUE, &newClass->pMarshaledData);
1430       if (hr) {
1431           FIXME("Failed to create stream on hglobal, %lx\n", hr);
1432           IUnknown_Release(classfac);
1433           return hr;
1434       }
1435       hr = CoMarshalInterface(newClass->pMarshaledData, &IID_IClassFactory,
1436                               (LPVOID)classfac, MSHCTX_LOCAL, NULL,
1437                               MSHLFLAGS_TABLESTRONG);
1438       if (hr) {
1439           FIXME("CoMarshalInterface failed, %lx!\n",hr);
1440           IUnknown_Release(classfac);
1441           return hr;
1442       }
1443
1444       IUnknown_Release(classfac);
1445
1446       RPC_StartLocalServer(&newClass->classIdentifier, newClass->pMarshaledData);
1447   }
1448   return S_OK;
1449 }
1450
1451 /***********************************************************************
1452  *           CoRevokeClassObject [OLE32.@]
1453  *
1454  * Removes a class object from the class registry.
1455  *
1456  * PARAMS
1457  *  dwRegister [I] Cookie returned from CoRegisterClassObject().
1458  *
1459  * RETURNS
1460  *  Success: S_OK.
1461  *  Failure: HRESULT code.
1462  *
1463  * SEE ALSO
1464  *  CoRegisterClassObject
1465  */
1466 HRESULT WINAPI CoRevokeClassObject(
1467         DWORD dwRegister)
1468 {
1469   HRESULT hr = E_INVALIDARG;
1470   RegisteredClass** prevClassLink;
1471   RegisteredClass*  curClass;
1472
1473   TRACE("(%08lx)\n",dwRegister);
1474
1475   EnterCriticalSection( &csRegisteredClassList );
1476
1477   /*
1478    * Iterate through the whole list and try to match the cookie.
1479    */
1480   curClass      = firstRegisteredClass;
1481   prevClassLink = &firstRegisteredClass;
1482
1483   while (curClass != 0)
1484   {
1485     /*
1486      * Check if we have a match on the cookie.
1487      */
1488     if (curClass->dwCookie == dwRegister)
1489     {
1490       /*
1491        * Remove the class from the chain.
1492        */
1493       *prevClassLink = curClass->nextClass;
1494
1495       /*
1496        * Release the reference to the class object.
1497        */
1498       IUnknown_Release(curClass->classObject);
1499
1500       if (curClass->pMarshaledData)
1501       {
1502         LARGE_INTEGER zero;
1503         memset(&zero, 0, sizeof(zero));
1504         /* FIXME: stop local server thread */
1505         IStream_Seek(curClass->pMarshaledData, zero, SEEK_SET, NULL);
1506         CoReleaseMarshalData(curClass->pMarshaledData);
1507       }
1508
1509       /*
1510        * Free the memory used by the chain node.
1511        */
1512       HeapFree(GetProcessHeap(), 0, curClass);
1513
1514       hr = S_OK;
1515       goto end;
1516     }
1517
1518     /*
1519      * Step to the next class in the list.
1520      */
1521     prevClassLink = &(curClass->nextClass);
1522     curClass      = curClass->nextClass;
1523   }
1524
1525 end:
1526   LeaveCriticalSection( &csRegisteredClassList );
1527   /*
1528    * If we get to here, we haven't found our class.
1529    */
1530   return hr;
1531 }
1532
1533 /***********************************************************************
1534  *      COM_RegReadPath [internal]
1535  *
1536  *      Reads a registry value and expands it when necessary
1537  */
1538 HRESULT COM_RegReadPath(HKEY hkeyroot, const WCHAR *keyname, const WCHAR *valuename, WCHAR * dst, DWORD dstlen)
1539 {
1540         HRESULT hres;
1541         HKEY key;
1542         DWORD keytype;
1543         WCHAR src[MAX_PATH];
1544         DWORD dwLength = dstlen * sizeof(WCHAR);
1545
1546         if((hres = RegOpenKeyExW(hkeyroot, keyname, 0, KEY_READ, &key)) == ERROR_SUCCESS) {
1547           if( (hres = RegQueryValueExW(key, NULL, NULL, &keytype, (LPBYTE)src, &dwLength)) == ERROR_SUCCESS ) {
1548             if (keytype == REG_EXPAND_SZ) {
1549               if (dstlen <= ExpandEnvironmentStringsW(src, dst, dstlen)) hres = ERROR_MORE_DATA;
1550             } else {
1551               lstrcpynW(dst, src, dstlen);
1552             }
1553           }
1554           RegCloseKey (key);
1555         }
1556         return hres;
1557 }
1558
1559 static HRESULT get_inproc_class_object(HKEY hkeydll, REFCLSID rclsid, REFIID riid, void **ppv)
1560 {
1561     HINSTANCE hLibrary;
1562     typedef HRESULT (CALLBACK *DllGetClassObjectFunc)(REFCLSID clsid, REFIID iid, LPVOID *ppv);
1563     DllGetClassObjectFunc DllGetClassObject;
1564     WCHAR dllpath[MAX_PATH+1];
1565
1566     if (COM_RegReadPath(hkeydll, NULL, NULL, dllpath, ARRAYSIZE(dllpath)) != ERROR_SUCCESS)
1567     {
1568         /* failure: CLSID is not found in registry */
1569         WARN("class %s not registered inproc\n", debugstr_guid(rclsid));
1570         return REGDB_E_CLASSNOTREG;
1571     }
1572
1573     if ((hLibrary = LoadLibraryExW(dllpath, 0, LOAD_WITH_ALTERED_SEARCH_PATH)) == 0)
1574     {
1575         /* failure: DLL could not be loaded */
1576         ERR("couldn't load in-process dll %s\n", debugstr_w(dllpath));
1577         return E_ACCESSDENIED; /* FIXME: or should this be CO_E_DLLNOTFOUND? */
1578     }
1579
1580     if (!(DllGetClassObject = (DllGetClassObjectFunc)GetProcAddress(hLibrary, "DllGetClassObject")))
1581     {
1582         /* failure: the dll did not export DllGetClassObject */
1583         ERR("couldn't find function DllGetClassObject in %s\n", debugstr_w(dllpath));
1584         FreeLibrary( hLibrary );
1585         return CO_E_DLLNOTFOUND;
1586     }
1587
1588     /* OK: get the ClassObject */
1589     COMPOBJ_DLLList_Add( hLibrary );
1590     return DllGetClassObject(rclsid, riid, ppv);
1591 }
1592
1593 /***********************************************************************
1594  *           CoGetClassObject [OLE32.@]
1595  *
1596  * FIXME.  If request allows of several options and there is a failure
1597  *         with one (other than not being registered) do we try the
1598  *         others or return failure?  (E.g. inprocess is registered but
1599  *         the DLL is not found but the server version works)
1600  */
1601 HRESULT WINAPI CoGetClassObject(
1602     REFCLSID rclsid, DWORD dwClsContext, COSERVERINFO *pServerInfo,
1603     REFIID iid, LPVOID *ppv)
1604 {
1605     LPUNKNOWN   regClassObject;
1606     HRESULT     hres = E_UNEXPECTED;
1607
1608     TRACE("\n\tCLSID:\t%s,\n\tIID:\t%s\n", debugstr_guid(rclsid), debugstr_guid(iid));
1609
1610     if (pServerInfo) {
1611         FIXME("\tpServerInfo: name=%s\n",debugstr_w(pServerInfo->pwszName));
1612         FIXME("\t\tpAuthInfo=%p\n",pServerInfo->pAuthInfo);
1613     }
1614
1615     /*
1616      * First, try and see if we can't match the class ID with one of the
1617      * registered classes.
1618      */
1619     if (S_OK == COM_GetRegisteredClassObject(rclsid, dwClsContext, &regClassObject))
1620     {
1621       /* Get the required interface from the retrieved pointer. */
1622       hres = IUnknown_QueryInterface(regClassObject, iid, ppv);
1623
1624       /*
1625        * Since QI got another reference on the pointer, we want to release the
1626        * one we already have. If QI was unsuccessful, this will release the object. This
1627        * is good since we are not returning it in the "out" parameter.
1628        */
1629       IUnknown_Release(regClassObject);
1630
1631       return hres;
1632     }
1633
1634     /* First try in-process server */
1635     if (CLSCTX_INPROC_SERVER & dwClsContext)
1636     {
1637         static const WCHAR wszInprocServer32[] = {'I','n','p','r','o','c','S','e','r','v','e','r','3','2',0};
1638         HKEY hkey;
1639
1640         hres = COM_OpenKeyForCLSID(rclsid, wszInprocServer32, KEY_READ, &hkey);
1641         if (FAILED(hres))
1642         {
1643             if (hres == REGDB_E_CLASSNOTREG)
1644                 ERR("class %s not registered\n", debugstr_guid(rclsid));
1645             else
1646                 WARN("class %s not registered as in-proc server\n", debugstr_guid(rclsid));
1647         }
1648
1649         if (SUCCEEDED(hres))
1650         {
1651             hres = get_inproc_class_object(hkey, rclsid, iid, ppv);
1652             RegCloseKey(hkey);
1653         }
1654
1655         /* return if we got a class, otherwise fall through to one of the
1656          * other types */
1657         if (SUCCEEDED(hres))
1658             return hres;
1659     }
1660
1661     /* Next try in-process handler */
1662     if (CLSCTX_INPROC_HANDLER & dwClsContext)
1663     {
1664         static const WCHAR wszInprocHandler32[] = {'I','n','p','r','o','c','H','a','n','d','l','e','r','3','2',0};
1665         HKEY hkey;
1666
1667         hres = COM_OpenKeyForCLSID(rclsid, wszInprocHandler32, KEY_READ, &hkey);
1668         if (FAILED(hres))
1669         {
1670             if (hres == REGDB_E_CLASSNOTREG)
1671                 ERR("class %s not registered\n", debugstr_guid(rclsid));
1672             else
1673                 WARN("class %s not registered in-proc handler\n", debugstr_guid(rclsid));
1674         }
1675
1676         if (SUCCEEDED(hres))
1677         {
1678             hres = get_inproc_class_object(hkey, rclsid, iid, ppv);
1679             RegCloseKey(hkey);
1680         }
1681
1682         /* return if we got a class, otherwise fall through to one of the
1683          * other types */
1684         if (SUCCEEDED(hres))
1685             return hres;
1686     }
1687
1688     /* Next try out of process */
1689     if (CLSCTX_LOCAL_SERVER & dwClsContext)
1690     {
1691         return RPC_GetLocalClassObject(rclsid,iid,ppv);
1692     }
1693
1694     /* Finally try remote: this requires networked DCOM (a lot of work) */
1695     if (CLSCTX_REMOTE_SERVER & dwClsContext)
1696     {
1697         FIXME ("CLSCTX_REMOTE_SERVER not supported\n");
1698         hres = E_NOINTERFACE;
1699     }
1700
1701     if (FAILED(hres))
1702         ERR("no class object %s could be created for for context 0x%lx\n",
1703             debugstr_guid(rclsid), dwClsContext);
1704     return hres;
1705 }
1706
1707 /***********************************************************************
1708  *        CoResumeClassObjects (OLE32.@)
1709  *
1710  * Resumes all class objects registered with REGCLS_SUSPENDED.
1711  *
1712  * RETURNS
1713  *  Success: S_OK.
1714  *  Failure: HRESULT code.
1715  */
1716 HRESULT WINAPI CoResumeClassObjects(void)
1717 {
1718        FIXME("stub\n");
1719         return S_OK;
1720 }
1721
1722 /***********************************************************************
1723  *        GetClassFile (OLE32.@)
1724  *
1725  * This function supplies the CLSID associated with the given filename.
1726  */
1727 HRESULT WINAPI GetClassFile(LPCOLESTR filePathName,CLSID *pclsid)
1728 {
1729     IStorage *pstg=0;
1730     HRESULT res;
1731     int nbElm, length, i;
1732     LONG sizeProgId;
1733     LPOLESTR *pathDec=0,absFile=0,progId=0;
1734     LPWSTR extension;
1735     static const WCHAR bkslashW[] = {'\\',0};
1736     static const WCHAR dotW[] = {'.',0};
1737
1738     TRACE("%s, %p\n", debugstr_w(filePathName), pclsid);
1739
1740     /* if the file contain a storage object the return the CLSID written by IStorage_SetClass method*/
1741     if((StgIsStorageFile(filePathName))==S_OK){
1742
1743         res=StgOpenStorage(filePathName,NULL,STGM_READ | STGM_SHARE_DENY_WRITE,NULL,0,&pstg);
1744
1745         if (SUCCEEDED(res))
1746             res=ReadClassStg(pstg,pclsid);
1747
1748         IStorage_Release(pstg);
1749
1750         return res;
1751     }
1752     /* if the file is not a storage object then attemps to match various bits in the file against a
1753        pattern in the registry. this case is not frequently used ! so I present only the psodocode for
1754        this case
1755
1756      for(i=0;i<nFileTypes;i++)
1757
1758         for(i=0;j<nPatternsForType;j++){
1759
1760             PATTERN pat;
1761             HANDLE  hFile;
1762
1763             pat=ReadPatternFromRegistry(i,j);
1764             hFile=CreateFileW(filePathName,,,,,,hFile);
1765             SetFilePosition(hFile,pat.offset);
1766             ReadFile(hFile,buf,pat.size,&r,NULL);
1767             if (memcmp(buf&pat.mask,pat.pattern.pat.size)==0){
1768
1769                 *pclsid=ReadCLSIDFromRegistry(i);
1770                 return S_OK;
1771             }
1772         }
1773      */
1774
1775     /* if the above strategies fail then search for the extension key in the registry */
1776
1777     /* get the last element (absolute file) in the path name */
1778     nbElm=FileMonikerImpl_DecomposePath(filePathName,&pathDec);
1779     absFile=pathDec[nbElm-1];
1780
1781     /* failed if the path represente a directory and not an absolute file name*/
1782     if (!lstrcmpW(absFile, bkslashW))
1783         return MK_E_INVALIDEXTENSION;
1784
1785     /* get the extension of the file */
1786     extension = NULL;
1787     length=lstrlenW(absFile);
1788     for(i = length-1; (i >= 0) && *(extension = &absFile[i]) != '.'; i--)
1789         /* nothing */;
1790
1791     if (!extension || !lstrcmpW(extension, dotW))
1792         return MK_E_INVALIDEXTENSION;
1793
1794     res=RegQueryValueW(HKEY_CLASSES_ROOT, extension, NULL, &sizeProgId);
1795
1796     /* get the progId associated to the extension */
1797     progId = CoTaskMemAlloc(sizeProgId);
1798     res = RegQueryValueW(HKEY_CLASSES_ROOT, extension, progId, &sizeProgId);
1799
1800     if (res==ERROR_SUCCESS)
1801         /* return the clsid associated to the progId */
1802         res= CLSIDFromProgID(progId,pclsid);
1803
1804     for(i=0; pathDec[i]!=NULL;i++)
1805         CoTaskMemFree(pathDec[i]);
1806     CoTaskMemFree(pathDec);
1807
1808     CoTaskMemFree(progId);
1809
1810     if (res==ERROR_SUCCESS)
1811         return res;
1812
1813     return MK_E_INVALIDEXTENSION;
1814 }
1815
1816 /***********************************************************************
1817  *           CoCreateInstance [OLE32.@]
1818  */
1819 HRESULT WINAPI CoCreateInstance(
1820         REFCLSID rclsid,
1821         LPUNKNOWN pUnkOuter,
1822         DWORD dwClsContext,
1823         REFIID iid,
1824         LPVOID *ppv)
1825 {
1826   HRESULT hres;
1827   LPCLASSFACTORY lpclf = 0;
1828
1829   TRACE("(rclsid=%s, pUnkOuter=%p, dwClsContext=%08lx, riid=%s, ppv=%p)\n", debugstr_guid(rclsid),
1830         pUnkOuter, dwClsContext, debugstr_guid(iid), ppv);
1831
1832   if (!COM_CurrentApt())
1833   {
1834       ERR("apartment not initialised\n");
1835       return CO_E_NOTINITIALIZED;
1836   }
1837
1838   /*
1839    * Sanity check
1840    */
1841   if (ppv==0)
1842     return E_POINTER;
1843
1844   /*
1845    * Initialize the "out" parameter
1846    */
1847   *ppv = 0;
1848
1849   /*
1850    * The Standard Global Interface Table (GIT) object is a process-wide singleton.
1851    * Rather than create a class factory, we can just check for it here
1852    */
1853   if (IsEqualIID(rclsid, &CLSID_StdGlobalInterfaceTable)) {
1854     if (StdGlobalInterfaceTableInstance == NULL)
1855       StdGlobalInterfaceTableInstance = StdGlobalInterfaceTable_Construct();
1856     hres = IGlobalInterfaceTable_QueryInterface( (IGlobalInterfaceTable*) StdGlobalInterfaceTableInstance, iid, ppv);
1857     if (hres) return hres;
1858
1859     TRACE("Retrieved GIT (%p)\n", *ppv);
1860     return S_OK;
1861   }
1862
1863   /*
1864    * Get a class factory to construct the object we want.
1865    */
1866   hres = CoGetClassObject(rclsid,
1867                           dwClsContext,
1868                           NULL,
1869                           &IID_IClassFactory,
1870                           (LPVOID)&lpclf);
1871
1872   if (FAILED(hres)) {
1873     FIXME("no classfactory created for CLSID %s, hres is 0x%08lx\n",
1874           debugstr_guid(rclsid),hres);
1875     return hres;
1876   }
1877
1878   /*
1879    * Create the object and don't forget to release the factory
1880    */
1881         hres = IClassFactory_CreateInstance(lpclf, pUnkOuter, iid, ppv);
1882         IClassFactory_Release(lpclf);
1883         if(FAILED(hres))
1884           FIXME("no instance created for interface %s of class %s, hres is 0x%08lx\n",
1885                 debugstr_guid(iid), debugstr_guid(rclsid),hres);
1886
1887         return hres;
1888 }
1889
1890 /***********************************************************************
1891  *           CoCreateInstanceEx [OLE32.@]
1892  */
1893 HRESULT WINAPI CoCreateInstanceEx(
1894   REFCLSID      rclsid,
1895   LPUNKNOWN     pUnkOuter,
1896   DWORD         dwClsContext,
1897   COSERVERINFO* pServerInfo,
1898   ULONG         cmq,
1899   MULTI_QI*     pResults)
1900 {
1901   IUnknown* pUnk = NULL;
1902   HRESULT   hr;
1903   ULONG     index;
1904   ULONG     successCount = 0;
1905
1906   /*
1907    * Sanity check
1908    */
1909   if ( (cmq==0) || (pResults==NULL))
1910     return E_INVALIDARG;
1911
1912   if (pServerInfo!=NULL)
1913     FIXME("() non-NULL pServerInfo not supported!\n");
1914
1915   /*
1916    * Initialize all the "out" parameters.
1917    */
1918   for (index = 0; index < cmq; index++)
1919   {
1920     pResults[index].pItf = NULL;
1921     pResults[index].hr   = E_NOINTERFACE;
1922   }
1923
1924   /*
1925    * Get the object and get its IUnknown pointer.
1926    */
1927   hr = CoCreateInstance(rclsid,
1928                         pUnkOuter,
1929                         dwClsContext,
1930                         &IID_IUnknown,
1931                         (VOID**)&pUnk);
1932
1933   if (hr)
1934     return hr;
1935
1936   /*
1937    * Then, query for all the interfaces requested.
1938    */
1939   for (index = 0; index < cmq; index++)
1940   {
1941     pResults[index].hr = IUnknown_QueryInterface(pUnk,
1942                                                  pResults[index].pIID,
1943                                                  (VOID**)&(pResults[index].pItf));
1944
1945     if (pResults[index].hr == S_OK)
1946       successCount++;
1947   }
1948
1949   /*
1950    * Release our temporary unknown pointer.
1951    */
1952   IUnknown_Release(pUnk);
1953
1954   if (successCount == 0)
1955     return E_NOINTERFACE;
1956
1957   if (successCount!=cmq)
1958     return CO_S_NOTALLINTERFACES;
1959
1960   return S_OK;
1961 }
1962
1963 /***********************************************************************
1964  *           CoLoadLibrary (OLE32.@)
1965  *
1966  * Loads a library.
1967  *
1968  * PARAMS
1969  *  lpszLibName [I] Path to library.
1970  *  bAutoFree   [I] Whether the library should automatically be freed.
1971  *
1972  * RETURNS
1973  *  Success: Handle to loaded library.
1974  *  Failure: NULL.
1975  *
1976  * SEE ALSO
1977  *  CoFreeLibrary, CoFreeAllLibraries, CoFreeUnusedLibraries
1978  */
1979 HINSTANCE WINAPI CoLoadLibrary(LPOLESTR lpszLibName, BOOL bAutoFree)
1980 {
1981     TRACE("(%s, %d)\n", debugstr_w(lpszLibName), bAutoFree);
1982
1983     return LoadLibraryExW(lpszLibName, 0, LOAD_WITH_ALTERED_SEARCH_PATH);
1984 }
1985
1986 /***********************************************************************
1987  *           CoFreeLibrary [OLE32.@]
1988  *
1989  * Unloads a library from memory.
1990  *
1991  * PARAMS
1992  *  hLibrary [I] Handle to library to unload.
1993  *
1994  * RETURNS
1995  *  Nothing
1996  *
1997  * SEE ALSO
1998  *  CoLoadLibrary, CoFreeAllLibraries, CoFreeUnusedLibraries
1999  */
2000 void WINAPI CoFreeLibrary(HINSTANCE hLibrary)
2001 {
2002     FreeLibrary(hLibrary);
2003 }
2004
2005
2006 /***********************************************************************
2007  *           CoFreeAllLibraries [OLE32.@]
2008  *
2009  * Function for backwards compatibility only. Does nothing.
2010  *
2011  * RETURNS
2012  *  Nothing.
2013  *
2014  * SEE ALSO
2015  *  CoLoadLibrary, CoFreeLibrary, CoFreeUnusedLibraries
2016  */
2017 void WINAPI CoFreeAllLibraries(void)
2018 {
2019     /* NOP */
2020 }
2021
2022
2023 /***********************************************************************
2024  *           CoFreeUnusedLibraries [OLE32.@]
2025  *           CoFreeUnusedLibraries [COMPOBJ.17]
2026  *
2027  * Frees any unused libraries. Unused are identified as those that return
2028  * S_OK from their DllCanUnloadNow function.
2029  *
2030  * RETURNS
2031  *  Nothing.
2032  *
2033  * SEE ALSO
2034  *  CoLoadLibrary, CoFreeAllLibraries, CoFreeLibrary
2035  */
2036 void WINAPI CoFreeUnusedLibraries(void)
2037 {
2038     /* FIXME: Calls to CoFreeUnusedLibraries from any thread always route
2039      * through the main apartment's thread to call DllCanUnloadNow */
2040     COMPOBJ_DllList_FreeUnused(0);
2041 }
2042
2043 /***********************************************************************
2044  *           CoFileTimeNow [OLE32.@]
2045  *           CoFileTimeNow [COMPOBJ.82]
2046  *
2047  * Retrieves the current time in FILETIME format.
2048  *
2049  * PARAMS
2050  *  lpFileTime [O] The current time.
2051  *
2052  * RETURNS
2053  *      S_OK.
2054  */
2055 HRESULT WINAPI CoFileTimeNow( FILETIME *lpFileTime )
2056 {
2057     GetSystemTimeAsFileTime( lpFileTime );
2058     return S_OK;
2059 }
2060
2061 static void COM_RevokeAllClasses()
2062 {
2063   EnterCriticalSection( &csRegisteredClassList );
2064
2065   while (firstRegisteredClass!=0)
2066   {
2067     CoRevokeClassObject(firstRegisteredClass->dwCookie);
2068   }
2069
2070   LeaveCriticalSection( &csRegisteredClassList );
2071 }
2072
2073 /******************************************************************************
2074  *              CoLockObjectExternal    [OLE32.@]
2075  *
2076  * Increments or decrements the external reference count of a stub object.
2077  *
2078  * PARAMS
2079  *  pUnk                [I] Stub object.
2080  *  fLock               [I] If TRUE then increments the external ref-count,
2081  *                          otherwise decrements.
2082  *  fLastUnlockReleases [I] If TRUE then the last unlock has the effect of
2083  *                          calling CoDisconnectObject.
2084  *
2085  * RETURNS
2086  *  Success: S_OK.
2087  *  Failure: HRESULT code.
2088  */
2089 HRESULT WINAPI CoLockObjectExternal(
2090     LPUNKNOWN pUnk,
2091     BOOL fLock,
2092     BOOL fLastUnlockReleases)
2093 {
2094     struct stub_manager *stubmgr;
2095     struct apartment *apt;
2096
2097     TRACE("pUnk=%p, fLock=%s, fLastUnlockReleases=%s\n",
2098           pUnk, fLock ? "TRUE" : "FALSE", fLastUnlockReleases ? "TRUE" : "FALSE");
2099
2100     apt = COM_CurrentApt();
2101     if (!apt) return CO_E_NOTINITIALIZED;
2102
2103     stubmgr = get_stub_manager_from_object(apt, pUnk);
2104     
2105     if (stubmgr)
2106     {
2107         if (fLock)
2108             stub_manager_ext_addref(stubmgr, 1);
2109         else
2110             stub_manager_ext_release(stubmgr, 1);
2111         
2112         stub_manager_int_release(stubmgr);
2113
2114         return S_OK;
2115     }
2116     else
2117     {
2118         WARN("stub object not found %p\n", pUnk);
2119         /* Note: native is pretty broken here because it just silently
2120          * fails, without returning an appropriate error code, making apps
2121          * think that the object was disconnected, when it actually wasn't */
2122         return S_OK;
2123     }
2124 }
2125
2126 /***********************************************************************
2127  *           CoInitializeWOW (OLE32.@)
2128  *
2129  * WOW equivalent of CoInitialize?
2130  *
2131  * PARAMS
2132  *  x [I] Unknown.
2133  *  y [I] Unknown.
2134  *
2135  * RETURNS
2136  *  Unknown.
2137  */
2138 HRESULT WINAPI CoInitializeWOW(DWORD x,DWORD y)
2139 {
2140     FIXME("(0x%08lx,0x%08lx),stub!\n",x,y);
2141     return 0;
2142 }
2143
2144 /***********************************************************************
2145  *           CoGetState [OLE32.@]
2146  *
2147  * Retrieves the thread state object previously stored by CoSetState().
2148  *
2149  * PARAMS
2150  *  ppv [I] Address where pointer to object will be stored.
2151  *
2152  * RETURNS
2153  *  Success: S_OK.
2154  *  Failure: E_OUTOFMEMORY.
2155  *
2156  * NOTES
2157  *  Crashes on all invalid ppv addresses, including NULL.
2158  *  If the function returns a non-NULL object then the caller must release its
2159  *  reference on the object when the object is no longer required.
2160  *
2161  * SEE ALSO
2162  *  CoSetState().
2163  */
2164 HRESULT WINAPI CoGetState(IUnknown ** ppv)
2165 {
2166     struct oletls *info = COM_CurrentInfo();
2167     if (!info) return E_OUTOFMEMORY;
2168
2169     *ppv = NULL;
2170
2171     if (info->state)
2172     {
2173         IUnknown_AddRef(info->state);
2174         *ppv = info->state;
2175         TRACE("apt->state=%p\n", info->state);
2176     }
2177
2178     return S_OK;
2179 }
2180
2181 /***********************************************************************
2182  *           CoSetState [OLE32.@]
2183  *
2184  * Sets the thread state object.
2185  *
2186  * PARAMS
2187  *  pv [I] Pointer to state object to be stored.
2188  *
2189  * NOTES
2190  *  The system keeps a reference on the object while the object stored.
2191  *
2192  * RETURNS
2193  *  Success: S_OK.
2194  *  Failure: E_OUTOFMEMORY.
2195  */
2196 HRESULT WINAPI CoSetState(IUnknown * pv)
2197 {
2198     struct oletls *info = COM_CurrentInfo();
2199     if (!info) return E_OUTOFMEMORY;
2200
2201     if (pv) IUnknown_AddRef(pv);
2202
2203     if (info->state)
2204     {
2205         TRACE("-- release %p now\n", info->state);
2206         IUnknown_Release(info->state);
2207     }
2208
2209     info->state = pv;
2210
2211     return S_OK;
2212 }
2213
2214
2215 /******************************************************************************
2216  *              OleGetAutoConvert        [OLE32.@]
2217  */
2218 HRESULT WINAPI OleGetAutoConvert(REFCLSID clsidOld, LPCLSID pClsidNew)
2219 {
2220     static const WCHAR wszAutoConvertTo[] = {'A','u','t','o','C','o','n','v','e','r','t','T','o',0};
2221     HKEY hkey = NULL;
2222     WCHAR buf[CHARS_IN_GUID];
2223     LONG len;
2224     HRESULT res = S_OK;
2225
2226     res = COM_OpenKeyForCLSID(clsidOld, wszAutoConvertTo, KEY_READ, &hkey);
2227     if (FAILED(res))
2228         goto done;
2229
2230     len = sizeof(buf);
2231     if (RegQueryValueW(hkey, NULL, buf, &len))
2232     {
2233         res = REGDB_E_KEYMISSING;
2234         goto done;
2235     }
2236     res = CLSIDFromString(buf, pClsidNew);
2237 done:
2238     if (hkey) RegCloseKey(hkey);
2239     return res;
2240 }
2241
2242 /******************************************************************************
2243  *              CoTreatAsClass        [OLE32.@]
2244  *
2245  * Sets the TreatAs value of a class.
2246  *
2247  * PARAMS
2248  *  clsidOld [I] Class to set TreatAs value on.
2249  *  clsidNew [I] The class the clsidOld should be treated as.
2250  *
2251  * RETURNS
2252  *  Success: S_OK.
2253  *  Failure: HRESULT code.
2254  *
2255  * SEE ALSO
2256  *  CoGetTreatAsClass
2257  */
2258 HRESULT WINAPI CoTreatAsClass(REFCLSID clsidOld, REFCLSID clsidNew)
2259 {
2260     static const WCHAR wszAutoTreatAs[] = {'A','u','t','o','T','r','e','a','t','A','s',0};
2261     static const WCHAR wszTreatAs[] = {'T','r','e','a','t','A','s',0};
2262     HKEY hkey = NULL;
2263     WCHAR szClsidNew[CHARS_IN_GUID];
2264     HRESULT res = S_OK;
2265     WCHAR auto_treat_as[CHARS_IN_GUID];
2266     LONG auto_treat_as_size = sizeof(auto_treat_as);
2267     CLSID id;
2268
2269     res = COM_OpenKeyForCLSID(clsidOld, NULL, KEY_READ | KEY_WRITE, &hkey);
2270     if (FAILED(res))
2271         goto done;
2272     if (!memcmp( clsidOld, clsidNew, sizeof(*clsidOld) ))
2273     {
2274        if (!RegQueryValueW(hkey, wszAutoTreatAs, auto_treat_as, &auto_treat_as_size) &&
2275            !CLSIDFromString(auto_treat_as, &id))
2276        {
2277            if (RegSetValueW(hkey, wszTreatAs, REG_SZ, auto_treat_as, sizeof(auto_treat_as)))
2278            {
2279                res = REGDB_E_WRITEREGDB;
2280                goto done;
2281            }
2282        }
2283        else
2284        {
2285            RegDeleteKeyW(hkey, wszTreatAs);
2286            goto done;
2287        }
2288     }
2289     else if (!StringFromGUID2(clsidNew, szClsidNew, ARRAYSIZE(szClsidNew)) &&
2290              !RegSetValueW(hkey, wszTreatAs, REG_SZ, szClsidNew, sizeof(szClsidNew)))
2291     {
2292         res = REGDB_E_WRITEREGDB;
2293         goto done;
2294     }
2295
2296 done:
2297     if (hkey) RegCloseKey(hkey);
2298     return res;
2299 }
2300
2301 /******************************************************************************
2302  *              CoGetTreatAsClass        [OLE32.@]
2303  *
2304  * Gets the TreatAs value of a class.
2305  *
2306  * PARAMS
2307  *  clsidOld [I] Class to get the TreatAs value of.
2308  *  clsidNew [I] The class the clsidOld should be treated as.
2309  *
2310  * RETURNS
2311  *  Success: S_OK.
2312  *  Failure: HRESULT code.
2313  *
2314  * SEE ALSO
2315  *  CoSetTreatAsClass
2316  */
2317 HRESULT WINAPI CoGetTreatAsClass(REFCLSID clsidOld, LPCLSID clsidNew)
2318 {
2319     static const WCHAR wszTreatAs[] = {'T','r','e','a','t','A','s',0};
2320     HKEY hkey = NULL;
2321     WCHAR szClsidNew[CHARS_IN_GUID];
2322     HRESULT res = S_OK;
2323     LONG len = sizeof(szClsidNew);
2324
2325     FIXME("(%s,%p)\n", debugstr_guid(clsidOld), clsidNew);
2326     memcpy(clsidNew,clsidOld,sizeof(CLSID)); /* copy over old value */
2327
2328     res = COM_OpenKeyForCLSID(clsidOld, wszTreatAs, KEY_READ, &hkey);
2329     if (FAILED(res))
2330         goto done;
2331     if (RegQueryValueW(hkey, NULL, szClsidNew, &len))
2332     {
2333         res = S_FALSE;
2334         goto done;
2335     }
2336     res = CLSIDFromString(szClsidNew,clsidNew);
2337     if (FAILED(res))
2338         ERR("Failed CLSIDFromStringA(%s), hres 0x%08lx\n", debugstr_w(szClsidNew), res);
2339 done:
2340     if (hkey) RegCloseKey(hkey);
2341     return res;
2342 }
2343
2344 /******************************************************************************
2345  *              CoGetCurrentProcess     [OLE32.@]
2346  *              CoGetCurrentProcess     [COMPOBJ.34]
2347  *
2348  * Gets the current process ID.
2349  *
2350  * RETURNS
2351  *  The current process ID.
2352  *
2353  * NOTES
2354  *   Is DWORD really the correct return type for this function?
2355  */
2356 DWORD WINAPI CoGetCurrentProcess(void)
2357 {
2358         return GetCurrentProcessId();
2359 }
2360
2361 /******************************************************************************
2362  *              CoRegisterMessageFilter [OLE32.@]
2363  *
2364  * Registers a message filter.
2365  *
2366  * PARAMS
2367  *  lpMessageFilter [I] Pointer to interface.
2368  *  lplpMessageFilter [O] Indirect pointer to prior instance if non-NULL.
2369  *
2370  * RETURNS
2371  *  Success: S_OK.
2372  *  Failure: HRESULT code.
2373  */
2374 HRESULT WINAPI CoRegisterMessageFilter(
2375     LPMESSAGEFILTER lpMessageFilter,
2376     LPMESSAGEFILTER *lplpMessageFilter)
2377 {
2378     FIXME("stub\n");
2379     if (lplpMessageFilter) {
2380         *lplpMessageFilter = NULL;
2381     }
2382     return S_OK;
2383 }
2384
2385 /***********************************************************************
2386  *           CoIsOle1Class [OLE32.@]
2387  *
2388  * Determines whether the specified class an OLE v1 class.
2389  *
2390  * PARAMS
2391  *  clsid [I] Class to test.
2392  *
2393  * RETURNS
2394  *  TRUE if the class is an OLE v1 class, or FALSE otherwise.
2395  */
2396 BOOL WINAPI CoIsOle1Class(REFCLSID clsid)
2397 {
2398   FIXME("%s\n", debugstr_guid(clsid));
2399   return FALSE;
2400 }
2401
2402 /***********************************************************************
2403  *           IsEqualGUID [OLE32.@]
2404  *
2405  * Compares two Unique Identifiers.
2406  *
2407  * PARAMS
2408  *  rguid1 [I] The first GUID to compare.
2409  *  rguid2 [I] The other GUID to compare.
2410  *
2411  * RETURNS
2412  *      TRUE if equal
2413  */
2414 #undef IsEqualGUID
2415 BOOL WINAPI IsEqualGUID(
2416      REFGUID rguid1,
2417      REFGUID rguid2)
2418 {
2419     return !memcmp(rguid1,rguid2,sizeof(GUID));
2420 }
2421
2422 /***********************************************************************
2423  *           CoInitializeSecurity [OLE32.@]
2424  */
2425 HRESULT WINAPI CoInitializeSecurity(PSECURITY_DESCRIPTOR pSecDesc, LONG cAuthSvc,
2426                                     SOLE_AUTHENTICATION_SERVICE* asAuthSvc,
2427                                     void* pReserved1, DWORD dwAuthnLevel,
2428                                     DWORD dwImpLevel, void* pReserved2,
2429                                     DWORD dwCapabilities, void* pReserved3)
2430 {
2431   FIXME("(%p,%ld,%p,%p,%ld,%ld,%p,%ld,%p) - stub!\n", pSecDesc, cAuthSvc,
2432         asAuthSvc, pReserved1, dwAuthnLevel, dwImpLevel, pReserved2,
2433         dwCapabilities, pReserved3);
2434   return S_OK;
2435 }
2436
2437 /***********************************************************************
2438  *           CoSuspendClassObjects [OLE32.@]
2439  *
2440  * Suspends all registered class objects to prevent further requests coming in
2441  * for those objects.
2442  *
2443  * RETURNS
2444  *  Success: S_OK.
2445  *  Failure: HRESULT code.
2446  */
2447 HRESULT WINAPI CoSuspendClassObjects(void)
2448 {
2449     FIXME("\n");
2450     return S_OK;
2451 }
2452
2453 /***********************************************************************
2454  *           CoAddRefServerProcess [OLE32.@]
2455  *
2456  * Helper function for incrementing the reference count of a local-server
2457  * process.
2458  *
2459  * RETURNS
2460  *  New reference count.
2461  */
2462 ULONG WINAPI CoAddRefServerProcess(void)
2463 {
2464     FIXME("\n");
2465     return 2;
2466 }
2467
2468 /***********************************************************************
2469  *           CoReleaseServerProcess [OLE32.@]
2470  *
2471  * Helper function for decrementing the reference count of a local-server
2472  * process.
2473  *
2474  * RETURNS
2475  *  New reference count.
2476  */
2477 ULONG WINAPI CoReleaseServerProcess(void)
2478 {
2479     FIXME("\n");
2480     return 1;
2481 }
2482
2483 /***********************************************************************
2484  *           CoIsHandlerConnected [OLE32.@]
2485  *
2486  * Determines whether a proxy is connected to a remote stub.
2487  *
2488  * PARAMS
2489  *  pUnk [I] Pointer to object that may or may not be connected.
2490  *
2491  * RETURNS
2492  *  TRUE if pUnk is not a proxy or if pUnk is connected to a remote stub, or
2493  *  FALSE otherwise.
2494  */
2495 BOOL WINAPI CoIsHandlerConnected(IUnknown *pUnk)
2496 {
2497     FIXME("%p\n", pUnk);
2498
2499     return TRUE;
2500 }
2501
2502 /***********************************************************************
2503  *           CoAllowSetForegroundWindow [OLE32.@]
2504  *
2505  */
2506 HRESULT WINAPI CoAllowSetForegroundWindow(IUnknown *pUnk, void *pvReserved)
2507 {
2508     FIXME("(%p, %p): stub\n", pUnk, pvReserved);
2509     return S_OK;
2510 }
2511  
2512 /***********************************************************************
2513  *           CoQueryProxyBlanket [OLE32.@]
2514  *
2515  * Retrieves the security settings being used by a proxy.
2516  *
2517  * PARAMS
2518  *  pProxy        [I] Pointer to the proxy object.
2519  *  pAuthnSvc     [O] The type of authentication service.
2520  *  pAuthzSvc     [O] The type of authorization service.
2521  *  ppServerPrincName [O] Optional. The server prinicple name.
2522  *  pAuthnLevel   [O] The authentication level.
2523  *  pImpLevel     [O] The impersonation level.
2524  *  ppAuthInfo    [O] Information specific to the authorization/authentication service.
2525  *  pCapabilities [O] Flags affecting the security behaviour.
2526  *
2527  * RETURNS
2528  *  Success: S_OK.
2529  *  Failure: HRESULT code.
2530  *
2531  * SEE ALSO
2532  *  CoCopyProxy, CoSetProxyBlanket.
2533  */
2534 HRESULT WINAPI CoQueryProxyBlanket(IUnknown *pProxy, DWORD *pAuthnSvc,
2535     DWORD *pAuthzSvc, OLECHAR **ppServerPrincName, DWORD *pAuthnLevel,
2536     DWORD *pImpLevel, void **ppAuthInfo, DWORD *pCapabilities)
2537 {
2538     IClientSecurity *pCliSec;
2539     HRESULT hr;
2540
2541     TRACE("%p\n", pProxy);
2542
2543     hr = IUnknown_QueryInterface(pProxy, &IID_IClientSecurity, (void **)&pCliSec);
2544     if (SUCCEEDED(hr))
2545     {
2546         hr = IClientSecurity_QueryBlanket(pCliSec, pProxy, pAuthnSvc,
2547                                           pAuthzSvc, ppServerPrincName,
2548                                           pAuthnLevel, pImpLevel, ppAuthInfo,
2549                                           pCapabilities);
2550         IClientSecurity_Release(pCliSec);
2551     }
2552
2553     if (FAILED(hr)) ERR("-- failed with 0x%08lx\n", hr);
2554     return hr;
2555 }
2556
2557 /***********************************************************************
2558  *           CoSetProxyBlanket [OLE32.@]
2559  *
2560  * Sets the security settings for a proxy.
2561  *
2562  * PARAMS
2563  *  pProxy       [I] Pointer to the proxy object.
2564  *  AuthnSvc     [I] The type of authentication service.
2565  *  AuthzSvc     [I] The type of authorization service.
2566  *  pServerPrincName [I] The server prinicple name.
2567  *  AuthnLevel   [I] The authentication level.
2568  *  ImpLevel     [I] The impersonation level.
2569  *  pAuthInfo    [I] Information specific to the authorization/authentication service.
2570  *  Capabilities [I] Flags affecting the security behaviour.
2571  *
2572  * RETURNS
2573  *  Success: S_OK.
2574  *  Failure: HRESULT code.
2575  *
2576  * SEE ALSO
2577  *  CoQueryProxyBlanket, CoCopyProxy.
2578  */
2579 HRESULT WINAPI CoSetProxyBlanket(IUnknown *pProxy, DWORD AuthnSvc,
2580     DWORD AuthzSvc, OLECHAR *pServerPrincName, DWORD AuthnLevel,
2581     DWORD ImpLevel, void *pAuthInfo, DWORD Capabilities)
2582 {
2583     IClientSecurity *pCliSec;
2584     HRESULT hr;
2585
2586     TRACE("%p\n", pProxy);
2587
2588     hr = IUnknown_QueryInterface(pProxy, &IID_IClientSecurity, (void **)&pCliSec);
2589     if (SUCCEEDED(hr))
2590     {
2591         hr = IClientSecurity_SetBlanket(pCliSec, pProxy, AuthnSvc,
2592                                         AuthzSvc, pServerPrincName,
2593                                         AuthnLevel, ImpLevel, pAuthInfo,
2594                                         Capabilities);
2595         IClientSecurity_Release(pCliSec);
2596     }
2597
2598     if (FAILED(hr)) ERR("-- failed with 0x%08lx\n", hr);
2599     return hr;
2600 }
2601
2602 /***********************************************************************
2603  *           CoCopyProxy [OLE32.@]
2604  *
2605  * Copies a proxy.
2606  *
2607  * PARAMS
2608  *  pProxy [I] Pointer to the proxy object.
2609  *  ppCopy [O] Copy of the proxy.
2610  *
2611  * RETURNS
2612  *  Success: S_OK.
2613  *  Failure: HRESULT code.
2614  *
2615  * SEE ALSO
2616  *  CoQueryProxyBlanket, CoSetProxyBlanket.
2617  */
2618 HRESULT WINAPI CoCopyProxy(IUnknown *pProxy, IUnknown **ppCopy)
2619 {
2620     IClientSecurity *pCliSec;
2621     HRESULT hr;
2622
2623     TRACE("%p\n", pProxy);
2624
2625     hr = IUnknown_QueryInterface(pProxy, &IID_IClientSecurity, (void **)&pCliSec);
2626     if (SUCCEEDED(hr))
2627     {
2628         hr = IClientSecurity_CopyProxy(pCliSec, pProxy, ppCopy);
2629         IClientSecurity_Release(pCliSec);
2630     }
2631
2632     if (FAILED(hr)) ERR("-- failed with 0x%08lx\n", hr);
2633     return hr;
2634 }
2635
2636
2637 /***********************************************************************
2638  *           CoWaitForMultipleHandles [OLE32.@]
2639  *
2640  * Waits for one or more handles to become signaled.
2641  *
2642  * PARAMS
2643  *  dwFlags   [I] Flags. See notes.
2644  *  dwTimeout [I] Timeout in milliseconds.
2645  *  cHandles  [I] Number of handles pointed to by pHandles.
2646  *  pHandles  [I] Handles to wait for.
2647  *  lpdwindex [O] Index of handle that was signaled.
2648  *
2649  * RETURNS
2650  *  Success: S_OK.
2651  *  Failure: RPC_S_CALLPENDING on timeout.
2652  *
2653  * NOTES
2654  *
2655  * The dwFlags parameter can be zero or more of the following:
2656  *| COWAIT_WAITALL - Wait for all of the handles to become signaled.
2657  *| COWAIT_ALERTABLE - Allows a queued APC to run during the wait.
2658  *
2659  * SEE ALSO
2660  *  MsgWaitForMultipleObjects, WaitForMultipleObjects.
2661  */
2662 HRESULT WINAPI CoWaitForMultipleHandles(DWORD dwFlags, DWORD dwTimeout,
2663     ULONG cHandles, const HANDLE* pHandles, LPDWORD lpdwindex)
2664 {
2665     HRESULT hr = S_OK;
2666     DWORD start_time = GetTickCount();
2667     BOOL message_loop = TRUE;
2668
2669     TRACE("(0x%08lx, 0x%08lx, %ld, %p, %p)\n", dwFlags, dwTimeout, cHandles,
2670         pHandles, lpdwindex);
2671
2672     while (TRUE)
2673     {
2674         DWORD now = GetTickCount();
2675         DWORD res;
2676
2677         if ((dwTimeout != INFINITE) && (start_time + dwTimeout >= now))
2678         {
2679             hr = RPC_S_CALLPENDING;
2680             break;
2681         }
2682
2683         if (message_loop)
2684         {
2685             DWORD wait_flags = (dwFlags & COWAIT_WAITALL) ? MWMO_WAITALL : 0 |
2686                     (dwFlags & COWAIT_ALERTABLE ) ? MWMO_ALERTABLE : 0;
2687
2688             TRACE("waiting for rpc completion or window message\n");
2689
2690             res = MsgWaitForMultipleObjectsEx(cHandles, pHandles,
2691                 (dwTimeout == INFINITE) ? INFINITE : start_time + dwTimeout - now,
2692                 QS_ALLINPUT, wait_flags);
2693
2694             if (res == WAIT_OBJECT_0 + cHandles)  /* messages available */
2695             {
2696                 MSG msg;
2697                 while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
2698                 {
2699                     /* FIXME: filter the messages here */
2700                     TRACE("received message whilst waiting for RPC: 0x%04x\n", msg.message);
2701                     TranslateMessage(&msg);
2702                     DispatchMessageW(&msg);
2703                     if (msg.message == WM_QUIT)
2704                     {
2705                         TRACE("resending WM_QUIT to outer message loop\n");
2706                         PostQuitMessage(msg.wParam);
2707                         /* no longer need to process messages */
2708                         message_loop = FALSE;
2709                         break;
2710                     }
2711                 }
2712                 continue;
2713             }
2714         }
2715         else
2716         {
2717             TRACE("waiting for rpc completion\n");
2718
2719             res = WaitForMultipleObjectsEx(cHandles, pHandles,
2720                 (dwFlags & COWAIT_WAITALL) ? TRUE : FALSE,
2721                 (dwTimeout == INFINITE) ? INFINITE : start_time + dwTimeout - now,
2722                 (dwFlags & COWAIT_ALERTABLE) ? TRUE : FALSE);
2723         }
2724
2725         if ((res >= WAIT_OBJECT_0) && (res < WAIT_OBJECT_0 + cHandles))
2726         {
2727             /* handle signaled, store index */
2728             *lpdwindex = (res - WAIT_OBJECT_0);
2729             break;
2730         }
2731         else if (res == WAIT_TIMEOUT)
2732         {
2733             hr = RPC_S_CALLPENDING;
2734             break;
2735         }
2736         else
2737         {
2738             ERR("Unexpected wait termination: %ld, %ld\n", res, GetLastError());
2739             hr = E_UNEXPECTED;
2740             break;
2741         }
2742     }
2743     TRACE("-- 0x%08lx\n", hr);
2744     return hr;
2745 }
2746
2747 /***********************************************************************
2748  *              DllMain (OLE32.@)
2749  */
2750 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID fImpLoad)
2751 {
2752     TRACE("%p 0x%lx %p\n", hinstDLL, fdwReason, fImpLoad);
2753
2754     switch(fdwReason) {
2755     case DLL_PROCESS_ATTACH:
2756         OLE32_hInstance = hinstDLL;
2757         COMPOBJ_InitProcess();
2758         if (TRACE_ON(ole)) CoRegisterMallocSpy((LPVOID)-1);
2759         break;
2760
2761     case DLL_PROCESS_DETACH:
2762         if (TRACE_ON(ole)) CoRevokeMallocSpy();
2763         COMPOBJ_UninitProcess();
2764         OLE32_hInstance = 0;
2765         break;
2766
2767     case DLL_THREAD_DETACH:
2768         COM_TlsDestroy();
2769         break;
2770     }
2771     return TRUE;
2772 }
2773
2774 /* NOTE: DllRegisterServer and DllUnregisterServer are in regsvr.c */