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