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