iphlpapi: Clean up memory allocation.
[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()) return CO_E_NOTINITIALIZED;
1833
1834   /*
1835    * Sanity check
1836    */
1837   if (ppv==0)
1838     return E_POINTER;
1839
1840   /*
1841    * Initialize the "out" parameter
1842    */
1843   *ppv = 0;
1844
1845   /*
1846    * The Standard Global Interface Table (GIT) object is a process-wide singleton.
1847    * Rather than create a class factory, we can just check for it here
1848    */
1849   if (IsEqualIID(rclsid, &CLSID_StdGlobalInterfaceTable)) {
1850     if (StdGlobalInterfaceTableInstance == NULL)
1851       StdGlobalInterfaceTableInstance = StdGlobalInterfaceTable_Construct();
1852     hres = IGlobalInterfaceTable_QueryInterface( (IGlobalInterfaceTable*) StdGlobalInterfaceTableInstance, iid, ppv);
1853     if (hres) return hres;
1854
1855     TRACE("Retrieved GIT (%p)\n", *ppv);
1856     return S_OK;
1857   }
1858
1859   /*
1860    * Get a class factory to construct the object we want.
1861    */
1862   hres = CoGetClassObject(rclsid,
1863                           dwClsContext,
1864                           NULL,
1865                           &IID_IClassFactory,
1866                           (LPVOID)&lpclf);
1867
1868   if (FAILED(hres)) {
1869     FIXME("no classfactory created for CLSID %s, hres is 0x%08lx\n",
1870           debugstr_guid(rclsid),hres);
1871     return hres;
1872   }
1873
1874   /*
1875    * Create the object and don't forget to release the factory
1876    */
1877         hres = IClassFactory_CreateInstance(lpclf, pUnkOuter, iid, ppv);
1878         IClassFactory_Release(lpclf);
1879         if(FAILED(hres))
1880           FIXME("no instance created for interface %s of class %s, hres is 0x%08lx\n",
1881                 debugstr_guid(iid), debugstr_guid(rclsid),hres);
1882
1883         return hres;
1884 }
1885
1886 /***********************************************************************
1887  *           CoCreateInstanceEx [OLE32.@]
1888  */
1889 HRESULT WINAPI CoCreateInstanceEx(
1890   REFCLSID      rclsid,
1891   LPUNKNOWN     pUnkOuter,
1892   DWORD         dwClsContext,
1893   COSERVERINFO* pServerInfo,
1894   ULONG         cmq,
1895   MULTI_QI*     pResults)
1896 {
1897   IUnknown* pUnk = NULL;
1898   HRESULT   hr;
1899   ULONG     index;
1900   ULONG     successCount = 0;
1901
1902   /*
1903    * Sanity check
1904    */
1905   if ( (cmq==0) || (pResults==NULL))
1906     return E_INVALIDARG;
1907
1908   if (pServerInfo!=NULL)
1909     FIXME("() non-NULL pServerInfo not supported!\n");
1910
1911   /*
1912    * Initialize all the "out" parameters.
1913    */
1914   for (index = 0; index < cmq; index++)
1915   {
1916     pResults[index].pItf = NULL;
1917     pResults[index].hr   = E_NOINTERFACE;
1918   }
1919
1920   /*
1921    * Get the object and get its IUnknown pointer.
1922    */
1923   hr = CoCreateInstance(rclsid,
1924                         pUnkOuter,
1925                         dwClsContext,
1926                         &IID_IUnknown,
1927                         (VOID**)&pUnk);
1928
1929   if (hr)
1930     return hr;
1931
1932   /*
1933    * Then, query for all the interfaces requested.
1934    */
1935   for (index = 0; index < cmq; index++)
1936   {
1937     pResults[index].hr = IUnknown_QueryInterface(pUnk,
1938                                                  pResults[index].pIID,
1939                                                  (VOID**)&(pResults[index].pItf));
1940
1941     if (pResults[index].hr == S_OK)
1942       successCount++;
1943   }
1944
1945   /*
1946    * Release our temporary unknown pointer.
1947    */
1948   IUnknown_Release(pUnk);
1949
1950   if (successCount == 0)
1951     return E_NOINTERFACE;
1952
1953   if (successCount!=cmq)
1954     return CO_S_NOTALLINTERFACES;
1955
1956   return S_OK;
1957 }
1958
1959 /***********************************************************************
1960  *           CoLoadLibrary (OLE32.@)
1961  *
1962  * Loads a library.
1963  *
1964  * PARAMS
1965  *  lpszLibName [I] Path to library.
1966  *  bAutoFree   [I] Whether the library should automatically be freed.
1967  *
1968  * RETURNS
1969  *  Success: Handle to loaded library.
1970  *  Failure: NULL.
1971  *
1972  * SEE ALSO
1973  *  CoFreeLibrary, CoFreeAllLibraries, CoFreeUnusedLibraries
1974  */
1975 HINSTANCE WINAPI CoLoadLibrary(LPOLESTR lpszLibName, BOOL bAutoFree)
1976 {
1977     TRACE("(%s, %d)\n", debugstr_w(lpszLibName), bAutoFree);
1978
1979     return LoadLibraryExW(lpszLibName, 0, LOAD_WITH_ALTERED_SEARCH_PATH);
1980 }
1981
1982 /***********************************************************************
1983  *           CoFreeLibrary [OLE32.@]
1984  *
1985  * Unloads a library from memory.
1986  *
1987  * PARAMS
1988  *  hLibrary [I] Handle to library to unload.
1989  *
1990  * RETURNS
1991  *  Nothing
1992  *
1993  * SEE ALSO
1994  *  CoLoadLibrary, CoFreeAllLibraries, CoFreeUnusedLibraries
1995  */
1996 void WINAPI CoFreeLibrary(HINSTANCE hLibrary)
1997 {
1998     FreeLibrary(hLibrary);
1999 }
2000
2001
2002 /***********************************************************************
2003  *           CoFreeAllLibraries [OLE32.@]
2004  *
2005  * Function for backwards compatibility only. Does nothing.
2006  *
2007  * RETURNS
2008  *  Nothing.
2009  *
2010  * SEE ALSO
2011  *  CoLoadLibrary, CoFreeLibrary, CoFreeUnusedLibraries
2012  */
2013 void WINAPI CoFreeAllLibraries(void)
2014 {
2015     /* NOP */
2016 }
2017
2018
2019 /***********************************************************************
2020  *           CoFreeUnusedLibraries [OLE32.@]
2021  *           CoFreeUnusedLibraries [COMPOBJ.17]
2022  *
2023  * Frees any unused libraries. Unused are identified as those that return
2024  * S_OK from their DllCanUnloadNow function.
2025  *
2026  * RETURNS
2027  *  Nothing.
2028  *
2029  * SEE ALSO
2030  *  CoLoadLibrary, CoFreeAllLibraries, CoFreeLibrary
2031  */
2032 void WINAPI CoFreeUnusedLibraries(void)
2033 {
2034     /* FIXME: Calls to CoFreeUnusedLibraries from any thread always route
2035      * through the main apartment's thread to call DllCanUnloadNow */
2036     COMPOBJ_DllList_FreeUnused(0);
2037 }
2038
2039 /***********************************************************************
2040  *           CoFileTimeNow [OLE32.@]
2041  *           CoFileTimeNow [COMPOBJ.82]
2042  *
2043  * Retrieves the current time in FILETIME format.
2044  *
2045  * PARAMS
2046  *  lpFileTime [O] The current time.
2047  *
2048  * RETURNS
2049  *      S_OK.
2050  */
2051 HRESULT WINAPI CoFileTimeNow( FILETIME *lpFileTime )
2052 {
2053     GetSystemTimeAsFileTime( lpFileTime );
2054     return S_OK;
2055 }
2056
2057 static void COM_RevokeAllClasses()
2058 {
2059   EnterCriticalSection( &csRegisteredClassList );
2060
2061   while (firstRegisteredClass!=0)
2062   {
2063     CoRevokeClassObject(firstRegisteredClass->dwCookie);
2064   }
2065
2066   LeaveCriticalSection( &csRegisteredClassList );
2067 }
2068
2069 /******************************************************************************
2070  *              CoLockObjectExternal    [OLE32.@]
2071  *
2072  * Increments or decrements the external reference count of a stub object.
2073  *
2074  * PARAMS
2075  *  pUnk                [I] Stub object.
2076  *  fLock               [I] If TRUE then increments the external ref-count,
2077  *                          otherwise decrements.
2078  *  fLastUnlockReleases [I] If TRUE then the last unlock has the effect of
2079  *                          calling CoDisconnectObject.
2080  *
2081  * RETURNS
2082  *  Success: S_OK.
2083  *  Failure: HRESULT code.
2084  */
2085 HRESULT WINAPI CoLockObjectExternal(
2086     LPUNKNOWN pUnk,
2087     BOOL fLock,
2088     BOOL fLastUnlockReleases)
2089 {
2090     struct stub_manager *stubmgr;
2091     struct apartment *apt;
2092
2093     TRACE("pUnk=%p, fLock=%s, fLastUnlockReleases=%s\n",
2094           pUnk, fLock ? "TRUE" : "FALSE", fLastUnlockReleases ? "TRUE" : "FALSE");
2095
2096     apt = COM_CurrentApt();
2097     if (!apt) return CO_E_NOTINITIALIZED;
2098
2099     stubmgr = get_stub_manager_from_object(apt, pUnk);
2100     
2101     if (stubmgr)
2102     {
2103         if (fLock)
2104             stub_manager_ext_addref(stubmgr, 1);
2105         else
2106             stub_manager_ext_release(stubmgr, 1);
2107         
2108         stub_manager_int_release(stubmgr);
2109
2110         return S_OK;
2111     }
2112     else
2113     {
2114         WARN("stub object not found %p\n", pUnk);
2115         /* Note: native is pretty broken here because it just silently
2116          * fails, without returning an appropriate error code, making apps
2117          * think that the object was disconnected, when it actually wasn't */
2118         return S_OK;
2119     }
2120 }
2121
2122 /***********************************************************************
2123  *           CoInitializeWOW (OLE32.@)
2124  *
2125  * WOW equivalent of CoInitialize?
2126  *
2127  * PARAMS
2128  *  x [I] Unknown.
2129  *  y [I] Unknown.
2130  *
2131  * RETURNS
2132  *  Unknown.
2133  */
2134 HRESULT WINAPI CoInitializeWOW(DWORD x,DWORD y)
2135 {
2136     FIXME("(0x%08lx,0x%08lx),stub!\n",x,y);
2137     return 0;
2138 }
2139
2140 /***********************************************************************
2141  *           CoGetState [OLE32.@]
2142  *
2143  * Retrieves the thread state object previously stored by CoSetState().
2144  *
2145  * PARAMS
2146  *  ppv [I] Address where pointer to object will be stored.
2147  *
2148  * RETURNS
2149  *  Success: S_OK.
2150  *  Failure: E_OUTOFMEMORY.
2151  *
2152  * NOTES
2153  *  Crashes on all invalid ppv addresses, including NULL.
2154  *  If the function returns a non-NULL object then the caller must release its
2155  *  reference on the object when the object is no longer required.
2156  *
2157  * SEE ALSO
2158  *  CoSetState().
2159  */
2160 HRESULT WINAPI CoGetState(IUnknown ** ppv)
2161 {
2162     struct oletls *info = COM_CurrentInfo();
2163     if (!info) return E_OUTOFMEMORY;
2164
2165     *ppv = NULL;
2166
2167     if (info->state)
2168     {
2169         IUnknown_AddRef(info->state);
2170         *ppv = info->state;
2171         TRACE("apt->state=%p\n", info->state);
2172     }
2173
2174     return S_OK;
2175 }
2176
2177 /***********************************************************************
2178  *           CoSetState [OLE32.@]
2179  *
2180  * Sets the thread state object.
2181  *
2182  * PARAMS
2183  *  pv [I] Pointer to state object to be stored.
2184  *
2185  * NOTES
2186  *  The system keeps a reference on the object while the object stored.
2187  *
2188  * RETURNS
2189  *  Success: S_OK.
2190  *  Failure: E_OUTOFMEMORY.
2191  */
2192 HRESULT WINAPI CoSetState(IUnknown * pv)
2193 {
2194     struct oletls *info = COM_CurrentInfo();
2195     if (!info) return E_OUTOFMEMORY;
2196
2197     if (pv) IUnknown_AddRef(pv);
2198
2199     if (info->state)
2200     {
2201         TRACE("-- release %p now\n", info->state);
2202         IUnknown_Release(info->state);
2203     }
2204
2205     info->state = pv;
2206
2207     return S_OK;
2208 }
2209
2210
2211 /******************************************************************************
2212  *              OleGetAutoConvert        [OLE32.@]
2213  */
2214 HRESULT WINAPI OleGetAutoConvert(REFCLSID clsidOld, LPCLSID pClsidNew)
2215 {
2216     static const WCHAR wszAutoConvertTo[] = {'A','u','t','o','C','o','n','v','e','r','t','T','o',0};
2217     HKEY hkey = NULL;
2218     WCHAR buf[CHARS_IN_GUID];
2219     LONG len;
2220     HRESULT res = S_OK;
2221
2222     res = COM_OpenKeyForCLSID(clsidOld, wszAutoConvertTo, KEY_READ, &hkey);
2223     if (FAILED(res))
2224         goto done;
2225
2226     len = sizeof(buf);
2227     if (RegQueryValueW(hkey, NULL, buf, &len))
2228     {
2229         res = REGDB_E_KEYMISSING;
2230         goto done;
2231     }
2232     res = CLSIDFromString(buf, pClsidNew);
2233 done:
2234     if (hkey) RegCloseKey(hkey);
2235     return res;
2236 }
2237
2238 /******************************************************************************
2239  *              CoTreatAsClass        [OLE32.@]
2240  *
2241  * Sets the TreatAs value of a class.
2242  *
2243  * PARAMS
2244  *  clsidOld [I] Class to set TreatAs value on.
2245  *  clsidNew [I] The class the clsidOld should be treated as.
2246  *
2247  * RETURNS
2248  *  Success: S_OK.
2249  *  Failure: HRESULT code.
2250  *
2251  * SEE ALSO
2252  *  CoGetTreatAsClass
2253  */
2254 HRESULT WINAPI CoTreatAsClass(REFCLSID clsidOld, REFCLSID clsidNew)
2255 {
2256     static const WCHAR wszAutoTreatAs[] = {'A','u','t','o','T','r','e','a','t','A','s',0};
2257     static const WCHAR wszTreatAs[] = {'T','r','e','a','t','A','s',0};
2258     HKEY hkey = NULL;
2259     WCHAR szClsidNew[CHARS_IN_GUID];
2260     HRESULT res = S_OK;
2261     WCHAR auto_treat_as[CHARS_IN_GUID];
2262     LONG auto_treat_as_size = sizeof(auto_treat_as);
2263     CLSID id;
2264
2265     res = COM_OpenKeyForCLSID(clsidOld, NULL, KEY_READ | KEY_WRITE, &hkey);
2266     if (FAILED(res))
2267         goto done;
2268     if (!memcmp( clsidOld, clsidNew, sizeof(*clsidOld) ))
2269     {
2270        if (!RegQueryValueW(hkey, wszAutoTreatAs, auto_treat_as, &auto_treat_as_size) &&
2271            !CLSIDFromString(auto_treat_as, &id))
2272        {
2273            if (RegSetValueW(hkey, wszTreatAs, REG_SZ, auto_treat_as, sizeof(auto_treat_as)))
2274            {
2275                res = REGDB_E_WRITEREGDB;
2276                goto done;
2277            }
2278        }
2279        else
2280        {
2281            RegDeleteKeyW(hkey, wszTreatAs);
2282            goto done;
2283        }
2284     }
2285     else if (!StringFromGUID2(clsidNew, szClsidNew, ARRAYSIZE(szClsidNew)) &&
2286              !RegSetValueW(hkey, wszTreatAs, REG_SZ, szClsidNew, sizeof(szClsidNew)))
2287     {
2288         res = REGDB_E_WRITEREGDB;
2289         goto done;
2290     }
2291
2292 done:
2293     if (hkey) RegCloseKey(hkey);
2294     return res;
2295 }
2296
2297 /******************************************************************************
2298  *              CoGetTreatAsClass        [OLE32.@]
2299  *
2300  * Gets the TreatAs value of a class.
2301  *
2302  * PARAMS
2303  *  clsidOld [I] Class to get the TreatAs value of.
2304  *  clsidNew [I] The class the clsidOld should be treated as.
2305  *
2306  * RETURNS
2307  *  Success: S_OK.
2308  *  Failure: HRESULT code.
2309  *
2310  * SEE ALSO
2311  *  CoSetTreatAsClass
2312  */
2313 HRESULT WINAPI CoGetTreatAsClass(REFCLSID clsidOld, LPCLSID clsidNew)
2314 {
2315     static const WCHAR wszTreatAs[] = {'T','r','e','a','t','A','s',0};
2316     HKEY hkey = NULL;
2317     WCHAR szClsidNew[CHARS_IN_GUID];
2318     HRESULT res = S_OK;
2319     LONG len = sizeof(szClsidNew);
2320
2321     FIXME("(%s,%p)\n", debugstr_guid(clsidOld), clsidNew);
2322     memcpy(clsidNew,clsidOld,sizeof(CLSID)); /* copy over old value */
2323
2324     res = COM_OpenKeyForCLSID(clsidOld, wszTreatAs, KEY_READ, &hkey);
2325     if (FAILED(res))
2326         goto done;
2327     if (RegQueryValueW(hkey, NULL, szClsidNew, &len))
2328     {
2329         res = S_FALSE;
2330         goto done;
2331     }
2332     res = CLSIDFromString(szClsidNew,clsidNew);
2333     if (FAILED(res))
2334         ERR("Failed CLSIDFromStringA(%s), hres 0x%08lx\n", debugstr_w(szClsidNew), res);
2335 done:
2336     if (hkey) RegCloseKey(hkey);
2337     return res;
2338 }
2339
2340 /******************************************************************************
2341  *              CoGetCurrentProcess     [OLE32.@]
2342  *              CoGetCurrentProcess     [COMPOBJ.34]
2343  *
2344  * Gets the current process ID.
2345  *
2346  * RETURNS
2347  *  The current process ID.
2348  *
2349  * NOTES
2350  *   Is DWORD really the correct return type for this function?
2351  */
2352 DWORD WINAPI CoGetCurrentProcess(void)
2353 {
2354         return GetCurrentProcessId();
2355 }
2356
2357 /******************************************************************************
2358  *              CoRegisterMessageFilter [OLE32.@]
2359  *
2360  * Registers a message filter.
2361  *
2362  * PARAMS
2363  *  lpMessageFilter [I] Pointer to interface.
2364  *  lplpMessageFilter [O] Indirect pointer to prior instance if non-NULL.
2365  *
2366  * RETURNS
2367  *  Success: S_OK.
2368  *  Failure: HRESULT code.
2369  */
2370 HRESULT WINAPI CoRegisterMessageFilter(
2371     LPMESSAGEFILTER lpMessageFilter,
2372     LPMESSAGEFILTER *lplpMessageFilter)
2373 {
2374     FIXME("stub\n");
2375     if (lplpMessageFilter) {
2376         *lplpMessageFilter = NULL;
2377     }
2378     return S_OK;
2379 }
2380
2381 /***********************************************************************
2382  *           CoIsOle1Class [OLE32.@]
2383  *
2384  * Determines whether the specified class an OLE v1 class.
2385  *
2386  * PARAMS
2387  *  clsid [I] Class to test.
2388  *
2389  * RETURNS
2390  *  TRUE if the class is an OLE v1 class, or FALSE otherwise.
2391  */
2392 BOOL WINAPI CoIsOle1Class(REFCLSID clsid)
2393 {
2394   FIXME("%s\n", debugstr_guid(clsid));
2395   return FALSE;
2396 }
2397
2398 /***********************************************************************
2399  *           IsEqualGUID [OLE32.@]
2400  *
2401  * Compares two Unique Identifiers.
2402  *
2403  * PARAMS
2404  *  rguid1 [I] The first GUID to compare.
2405  *  rguid2 [I] The other GUID to compare.
2406  *
2407  * RETURNS
2408  *      TRUE if equal
2409  */
2410 #undef IsEqualGUID
2411 BOOL WINAPI IsEqualGUID(
2412      REFGUID rguid1,
2413      REFGUID rguid2)
2414 {
2415     return !memcmp(rguid1,rguid2,sizeof(GUID));
2416 }
2417
2418 /***********************************************************************
2419  *           CoInitializeSecurity [OLE32.@]
2420  */
2421 HRESULT WINAPI CoInitializeSecurity(PSECURITY_DESCRIPTOR pSecDesc, LONG cAuthSvc,
2422                                     SOLE_AUTHENTICATION_SERVICE* asAuthSvc,
2423                                     void* pReserved1, DWORD dwAuthnLevel,
2424                                     DWORD dwImpLevel, void* pReserved2,
2425                                     DWORD dwCapabilities, void* pReserved3)
2426 {
2427   FIXME("(%p,%ld,%p,%p,%ld,%ld,%p,%ld,%p) - stub!\n", pSecDesc, cAuthSvc,
2428         asAuthSvc, pReserved1, dwAuthnLevel, dwImpLevel, pReserved2,
2429         dwCapabilities, pReserved3);
2430   return S_OK;
2431 }
2432
2433 /***********************************************************************
2434  *           CoSuspendClassObjects [OLE32.@]
2435  *
2436  * Suspends all registered class objects to prevent further requests coming in
2437  * for those objects.
2438  *
2439  * RETURNS
2440  *  Success: S_OK.
2441  *  Failure: HRESULT code.
2442  */
2443 HRESULT WINAPI CoSuspendClassObjects(void)
2444 {
2445     FIXME("\n");
2446     return S_OK;
2447 }
2448
2449 /***********************************************************************
2450  *           CoAddRefServerProcess [OLE32.@]
2451  *
2452  * Helper function for incrementing the reference count of a local-server
2453  * process.
2454  *
2455  * RETURNS
2456  *  New reference count.
2457  */
2458 ULONG WINAPI CoAddRefServerProcess(void)
2459 {
2460     FIXME("\n");
2461     return 2;
2462 }
2463
2464 /***********************************************************************
2465  *           CoReleaseServerProcess [OLE32.@]
2466  *
2467  * Helper function for decrementing the reference count of a local-server
2468  * process.
2469  *
2470  * RETURNS
2471  *  New reference count.
2472  */
2473 ULONG WINAPI CoReleaseServerProcess(void)
2474 {
2475     FIXME("\n");
2476     return 1;
2477 }
2478
2479 /***********************************************************************
2480  *           CoIsHandlerConnected [OLE32.@]
2481  *
2482  * Determines whether a proxy is connected to a remote stub.
2483  *
2484  * PARAMS
2485  *  pUnk [I] Pointer to object that may or may not be connected.
2486  *
2487  * RETURNS
2488  *  TRUE if pUnk is not a proxy or if pUnk is connected to a remote stub, or
2489  *  FALSE otherwise.
2490  */
2491 BOOL WINAPI CoIsHandlerConnected(IUnknown *pUnk)
2492 {
2493     FIXME("%p\n", pUnk);
2494
2495     return TRUE;
2496 }
2497
2498 /***********************************************************************
2499  *           CoAllowSetForegroundWindow [OLE32.@]
2500  *
2501  */
2502 HRESULT WINAPI CoAllowSetForegroundWindow(IUnknown *pUnk, void *pvReserved)
2503 {
2504     FIXME("(%p, %p): stub\n", pUnk, pvReserved);
2505     return S_OK;
2506 }
2507  
2508 /***********************************************************************
2509  *           CoQueryProxyBlanket [OLE32.@]
2510  *
2511  * Retrieves the security settings being used by a proxy.
2512  *
2513  * PARAMS
2514  *  pProxy        [I] Pointer to the proxy object.
2515  *  pAuthnSvc     [O] The type of authentication service.
2516  *  pAuthzSvc     [O] The type of authorization service.
2517  *  ppServerPrincName [O] Optional. The server prinicple name.
2518  *  pAuthnLevel   [O] The authentication level.
2519  *  pImpLevel     [O] The impersonation level.
2520  *  ppAuthInfo    [O] Information specific to the authorization/authentication service.
2521  *  pCapabilities [O] Flags affecting the security behaviour.
2522  *
2523  * RETURNS
2524  *  Success: S_OK.
2525  *  Failure: HRESULT code.
2526  *
2527  * SEE ALSO
2528  *  CoCopyProxy, CoSetProxyBlanket.
2529  */
2530 HRESULT WINAPI CoQueryProxyBlanket(IUnknown *pProxy, DWORD *pAuthnSvc,
2531     DWORD *pAuthzSvc, OLECHAR **ppServerPrincName, DWORD *pAuthnLevel,
2532     DWORD *pImpLevel, void **ppAuthInfo, DWORD *pCapabilities)
2533 {
2534     IClientSecurity *pCliSec;
2535     HRESULT hr;
2536
2537     TRACE("%p\n", pProxy);
2538
2539     hr = IUnknown_QueryInterface(pProxy, &IID_IClientSecurity, (void **)&pCliSec);
2540     if (SUCCEEDED(hr))
2541     {
2542         hr = IClientSecurity_QueryBlanket(pCliSec, pProxy, pAuthnSvc,
2543                                           pAuthzSvc, ppServerPrincName,
2544                                           pAuthnLevel, pImpLevel, ppAuthInfo,
2545                                           pCapabilities);
2546         IClientSecurity_Release(pCliSec);
2547     }
2548
2549     if (FAILED(hr)) ERR("-- failed with 0x%08lx\n", hr);
2550     return hr;
2551 }
2552
2553 /***********************************************************************
2554  *           CoSetProxyBlanket [OLE32.@]
2555  *
2556  * Sets the security settings for a proxy.
2557  *
2558  * PARAMS
2559  *  pProxy       [I] Pointer to the proxy object.
2560  *  AuthnSvc     [I] The type of authentication service.
2561  *  AuthzSvc     [I] The type of authorization service.
2562  *  pServerPrincName [I] The server prinicple name.
2563  *  AuthnLevel   [I] The authentication level.
2564  *  ImpLevel     [I] The impersonation level.
2565  *  pAuthInfo    [I] Information specific to the authorization/authentication service.
2566  *  Capabilities [I] Flags affecting the security behaviour.
2567  *
2568  * RETURNS
2569  *  Success: S_OK.
2570  *  Failure: HRESULT code.
2571  *
2572  * SEE ALSO
2573  *  CoQueryProxyBlanket, CoCopyProxy.
2574  */
2575 HRESULT WINAPI CoSetProxyBlanket(IUnknown *pProxy, DWORD AuthnSvc,
2576     DWORD AuthzSvc, OLECHAR *pServerPrincName, DWORD AuthnLevel,
2577     DWORD ImpLevel, void *pAuthInfo, DWORD Capabilities)
2578 {
2579     IClientSecurity *pCliSec;
2580     HRESULT hr;
2581
2582     TRACE("%p\n", pProxy);
2583
2584     hr = IUnknown_QueryInterface(pProxy, &IID_IClientSecurity, (void **)&pCliSec);
2585     if (SUCCEEDED(hr))
2586     {
2587         hr = IClientSecurity_SetBlanket(pCliSec, pProxy, AuthnSvc,
2588                                         AuthzSvc, pServerPrincName,
2589                                         AuthnLevel, ImpLevel, pAuthInfo,
2590                                         Capabilities);
2591         IClientSecurity_Release(pCliSec);
2592     }
2593
2594     if (FAILED(hr)) ERR("-- failed with 0x%08lx\n", hr);
2595     return hr;
2596 }
2597
2598 /***********************************************************************
2599  *           CoCopyProxy [OLE32.@]
2600  *
2601  * Copies a proxy.
2602  *
2603  * PARAMS
2604  *  pProxy [I] Pointer to the proxy object.
2605  *  ppCopy [O] Copy of the proxy.
2606  *
2607  * RETURNS
2608  *  Success: S_OK.
2609  *  Failure: HRESULT code.
2610  *
2611  * SEE ALSO
2612  *  CoQueryProxyBlanket, CoSetProxyBlanket.
2613  */
2614 HRESULT WINAPI CoCopyProxy(IUnknown *pProxy, IUnknown **ppCopy)
2615 {
2616     IClientSecurity *pCliSec;
2617     HRESULT hr;
2618
2619     TRACE("%p\n", pProxy);
2620
2621     hr = IUnknown_QueryInterface(pProxy, &IID_IClientSecurity, (void **)&pCliSec);
2622     if (SUCCEEDED(hr))
2623     {
2624         hr = IClientSecurity_CopyProxy(pCliSec, pProxy, ppCopy);
2625         IClientSecurity_Release(pCliSec);
2626     }
2627
2628     if (FAILED(hr)) ERR("-- failed with 0x%08lx\n", hr);
2629     return hr;
2630 }
2631
2632
2633 /***********************************************************************
2634  *           CoWaitForMultipleHandles [OLE32.@]
2635  *
2636  * Waits for one or more handles to become signaled.
2637  *
2638  * PARAMS
2639  *  dwFlags   [I] Flags. See notes.
2640  *  dwTimeout [I] Timeout in milliseconds.
2641  *  cHandles  [I] Number of handles pointed to by pHandles.
2642  *  pHandles  [I] Handles to wait for.
2643  *  lpdwindex [O] Index of handle that was signaled.
2644  *
2645  * RETURNS
2646  *  Success: S_OK.
2647  *  Failure: RPC_S_CALLPENDING on timeout.
2648  *
2649  * NOTES
2650  *
2651  * The dwFlags parameter can be zero or more of the following:
2652  *| COWAIT_WAITALL - Wait for all of the handles to become signaled.
2653  *| COWAIT_ALERTABLE - Allows a queued APC to run during the wait.
2654  *
2655  * SEE ALSO
2656  *  MsgWaitForMultipleObjects, WaitForMultipleObjects.
2657  */
2658 HRESULT WINAPI CoWaitForMultipleHandles(DWORD dwFlags, DWORD dwTimeout,
2659     ULONG cHandles, const HANDLE* pHandles, LPDWORD lpdwindex)
2660 {
2661     HRESULT hr = S_OK;
2662     DWORD start_time = GetTickCount();
2663     BOOL message_loop = TRUE;
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         if (message_loop)
2680         {
2681             DWORD wait_flags = (dwFlags & COWAIT_WAITALL) ? MWMO_WAITALL : 0 |
2682                     (dwFlags & COWAIT_ALERTABLE ) ? MWMO_ALERTABLE : 0;
2683
2684             TRACE("waiting for rpc completion or window message\n");
2685
2686             res = MsgWaitForMultipleObjectsEx(cHandles, pHandles,
2687                 (dwTimeout == INFINITE) ? INFINITE : start_time + dwTimeout - now,
2688                 QS_ALLINPUT, wait_flags);
2689
2690             if (res == WAIT_OBJECT_0 + cHandles)  /* messages available */
2691             {
2692                 MSG msg;
2693                 while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
2694                 {
2695                     /* FIXME: filter the messages here */
2696                     TRACE("received message whilst waiting for RPC: 0x%04x\n", msg.message);
2697                     TranslateMessage(&msg);
2698                     DispatchMessageW(&msg);
2699                     if (msg.message == WM_QUIT)
2700                     {
2701                         TRACE("resending WM_QUIT to outer message loop\n");
2702                         PostQuitMessage(msg.wParam);
2703                         /* no longer need to process messages */
2704                         message_loop = FALSE;
2705                         break;
2706                     }
2707                 }
2708                 continue;
2709             }
2710         }
2711         else
2712         {
2713             TRACE("waiting for rpc completion\n");
2714
2715             res = WaitForMultipleObjectsEx(cHandles, pHandles,
2716                 (dwFlags & COWAIT_WAITALL) ? TRUE : FALSE,
2717                 (dwTimeout == INFINITE) ? INFINITE : start_time + dwTimeout - now,
2718                 (dwFlags & COWAIT_ALERTABLE) ? TRUE : FALSE);
2719         }
2720
2721         if ((res >= WAIT_OBJECT_0) && (res < WAIT_OBJECT_0 + cHandles))
2722         {
2723             /* handle signaled, store index */
2724             *lpdwindex = (res - WAIT_OBJECT_0);
2725             break;
2726         }
2727         else if (res == WAIT_TIMEOUT)
2728         {
2729             hr = RPC_S_CALLPENDING;
2730             break;
2731         }
2732         else
2733         {
2734             ERR("Unexpected wait termination: %ld, %ld\n", res, GetLastError());
2735             hr = E_UNEXPECTED;
2736             break;
2737         }
2738     }
2739     TRACE("-- 0x%08lx\n", hr);
2740     return hr;
2741 }
2742
2743 /***********************************************************************
2744  *              DllMain (OLE32.@)
2745  */
2746 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID fImpLoad)
2747 {
2748     TRACE("%p 0x%lx %p\n", hinstDLL, fdwReason, fImpLoad);
2749
2750     switch(fdwReason) {
2751     case DLL_PROCESS_ATTACH:
2752         OLE32_hInstance = hinstDLL;
2753         COMPOBJ_InitProcess();
2754         if (TRACE_ON(ole)) CoRegisterMallocSpy((LPVOID)-1);
2755         break;
2756
2757     case DLL_PROCESS_DETACH:
2758         if (TRACE_ON(ole)) CoRevokeMallocSpy();
2759         COMPOBJ_UninitProcess();
2760         OLE32_hInstance = 0;
2761         break;
2762
2763     case DLL_THREAD_DETACH:
2764         COM_TlsDestroy();
2765         break;
2766     }
2767     return TRUE;
2768 }
2769
2770 /* NOTE: DllRegisterServer and DllUnregisterServer are in regsvr.c */