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 * Copyright 2005-2006 Robert Shearman (for CodeWeavers)
11 *
12 * This library is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Lesser General Public
14 * License as published by the Free Software Foundation; either
15 * version 2.1 of the License, or (at your option) any later version.
16 *
17 * This library is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Lesser General Public License for more details.
21 *
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with this library; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
25 *
26 * Note
27 * 1. COINIT_MULTITHREADED is 0; it is the lack of COINIT_APARTMENTTHREADED
28 * Therefore do not test against COINIT_MULTITHREADED
29 *
30 * TODO list: (items bunched together depend on each other)
31 *
32 * - Implement the service control manager (in rpcss) to keep track
33 * of registered class objects: ISCM::ServerRegisterClsid et al
34 * - Implement the OXID resolver so we don't need magic endpoint names for
35 * clients and servers to meet up
36 *
37 */
38
39 #include "config.h"
40
41 #include <stdarg.h>
42 #include <stdio.h>
43 #include <string.h>
44 #include <assert.h>
45
46 #define COBJMACROS
47 #define NONAMELESSUNION
48 #define NONAMELESSSTRUCT
49
50 #include "windef.h"
51 #include "winbase.h"
52 #include "winerror.h"
53 #include "winreg.h"
54 #include "winuser.h"
55 #include "objbase.h"
56 #include "ole2.h"
57 #include "ole2ver.h"
58
59 #include "compobj_private.h"
60
61 #include "wine/unicode.h"
62 #include "wine/debug.h"
63
64 WINE_DEFAULT_DEBUG_CHANNEL(ole);
65
66 HINSTANCE OLE32_hInstance = 0;
67
68 #define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
69
70 /****************************************************************************
71 * This section defines variables internal to the COM module.
72 */
73
74 static HRESULT COM_GetRegisteredClassObject(const struct apartment *apt, REFCLSID rclsid,
75 DWORD dwClsContext, LPUNKNOWN* ppUnk);
76 static void COM_RevokeAllClasses(const struct apartment *apt);
77 static HRESULT get_inproc_class_object(APARTMENT *apt, HKEY hkeydll, REFCLSID rclsid, REFIID riid, BOOL hostifnecessary, void **ppv);
78
79 static APARTMENT *MTA; /* protected by csApartment */
80 static APARTMENT *MainApartment; /* the first STA apartment */
81 static struct list apts = LIST_INIT( apts ); /* protected by csApartment */
82
83 static CRITICAL_SECTION csApartment;
84 static CRITICAL_SECTION_DEBUG critsect_debug =
85 {
86 0, 0, &csApartment,
87 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
88 0, 0, { (DWORD_PTR)(__FILE__ ": csApartment") }
89 };
90 static CRITICAL_SECTION csApartment = { &critsect_debug, -1, 0, 0, 0, 0 };
91
92 struct registered_psclsid
93 {
94 struct list entry;
95 IID iid;
96 CLSID clsid;
97 };
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 /* Reference count used by CoAddRefServerProcess/CoReleaseServerProcess */
106 static LONG s_COMServerProcessReferences = 0;
107
108 /*
109 * This linked list contains the list of registered class objects. These
110 * are mostly used to register the factories for out-of-proc servers of OLE
111 * objects.
112 *
113 * TODO: Make this data structure aware of inter-process communication. This
114 * means that parts of this will be exported to rpcss.
115 */
116 typedef struct tagRegisteredClass
117 {
118 struct list entry;
119 CLSID classIdentifier;
120 OXID apartment_id;
121 LPUNKNOWN classObject;
122 DWORD runContext;
123 DWORD connectFlags;
124 DWORD dwCookie;
125 LPSTREAM pMarshaledData; /* FIXME: only really need to store OXID and IPID */
126 void *RpcRegistration;
127 } RegisteredClass;
128
129 static struct list RegisteredClassList = LIST_INIT(RegisteredClassList);
130
131 static CRITICAL_SECTION csRegisteredClassList;
132 static CRITICAL_SECTION_DEBUG class_cs_debug =
133 {
134 0, 0, &csRegisteredClassList,
135 { &class_cs_debug.ProcessLocksList, &class_cs_debug.ProcessLocksList },
136 0, 0, { (DWORD_PTR)(__FILE__ ": csRegisteredClassList") }
137 };
138 static CRITICAL_SECTION csRegisteredClassList = { &class_cs_debug, -1, 0, 0, 0, 0 };
139
140 /*****************************************************************************
141 * This section contains OpenDllList definitions
142 *
143 * The OpenDllList contains only handles of dll loaded by CoGetClassObject or
144 * other functions that do LoadLibrary _without_ giving back a HMODULE.
145 * Without this list these handles would never be freed.
146 *
147 * FIXME: a DLL that says OK when asked for unloading is unloaded in the
148 * next unload-call but not before 600 sec.
149 */
150
151 typedef HRESULT (CALLBACK *DllGetClassObjectFunc)(REFCLSID clsid, REFIID iid, LPVOID *ppv);
152 typedef HRESULT (WINAPI *DllCanUnloadNowFunc)(void);
153
154 typedef struct tagOpenDll
155 {
156 LONG refs;
157 LPWSTR library_name;
158 HANDLE library;
159 DllGetClassObjectFunc DllGetClassObject;
160 DllCanUnloadNowFunc DllCanUnloadNow;
161 struct list entry;
162 } OpenDll;
163
164 static struct list openDllList = LIST_INIT(openDllList);
165
166 static CRITICAL_SECTION csOpenDllList;
167 static CRITICAL_SECTION_DEBUG dll_cs_debug =
168 {
169 0, 0, &csOpenDllList,
170 { &dll_cs_debug.ProcessLocksList, &dll_cs_debug.ProcessLocksList },
171 0, 0, { (DWORD_PTR)(__FILE__ ": csOpenDllList") }
172 };
173 static CRITICAL_SECTION csOpenDllList = { &dll_cs_debug, -1, 0, 0, 0, 0 };
174
175 struct apartment_loaded_dll
176 {
177 struct list entry;
178 OpenDll *dll;
179 DWORD unload_time;
180 BOOL multi_threaded;
181 };
182
183 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',' ',
184 '','x','#','#','#','#','#','#','#','#',' ',0};
185 static LRESULT CALLBACK apartment_wndproc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
186 static HRESULT apartment_getclassobject(struct apartment *apt, LPCWSTR dllpath,
187 BOOL apartment_threaded,
188 REFCLSID rclsid, REFIID riid, void **ppv);
189 static void apartment_freeunusedlibraries(struct apartment *apt, DWORD delay);
190
191 static HRESULT COMPOBJ_DllList_Add(LPCWSTR library_name, OpenDll **ret);
192 static OpenDll *COMPOBJ_DllList_Get(LPCWSTR library_name);
193 static void COMPOBJ_DllList_ReleaseRef(OpenDll *entry, BOOL free_entry);
194
195 static DWORD COM_RegReadPath(HKEY hkeyroot, const WCHAR *keyname, const WCHAR *valuename, WCHAR * dst, DWORD dstlen);
196
197 static void COMPOBJ_InitProcess( void )
198 {
199 WNDCLASSW wclass;
200
201 /* Dispatching to the correct thread in an apartment is done through
202 * window messages rather than RPC transports. When an interface is
203 * marshalled into another apartment in the same process, a window of the
204 * following class is created. The *caller* of CoMarshalInterface (i.e., the
205 * application) is responsible for pumping the message loop in that thread.
206 * The WM_USER messages which point to the RPCs are then dispatched to
207 * apartment_wndproc by the user's code from the apartment in which the
208 * interface was unmarshalled.
209 */
210 memset(&wclass, 0, sizeof(wclass));
211 wclass.lpfnWndProc = apartment_wndproc;
212 wclass.hInstance = OLE32_hInstance;
213 wclass.lpszClassName = wszAptWinClass;
214 RegisterClassW(&wclass);
215 }
216
217 static void COMPOBJ_UninitProcess( void )
218 {
219 UnregisterClassW(wszAptWinClass, OLE32_hInstance);
220 }
221
222 static void COM_TlsDestroy(void)
223 {
224 struct oletls *info = NtCurrentTeb()->ReservedForOle;
225 if (info)
226 {
227 if (info->apt) apartment_release(info->apt);
228 if (info->errorinfo) IErrorInfo_Release(info->errorinfo);
229 if (info->state) IUnknown_Release(info->state);
230 HeapFree(GetProcessHeap(), 0, info);
231 NtCurrentTeb()->ReservedForOle = NULL;
232 }
233 }
234
235 /******************************************************************************
236 * Manage apartments.
237 */
238
239 /* allocates memory and fills in the necessary fields for a new apartment
240 * object. must be called inside apartment cs */
241 static APARTMENT *apartment_construct(DWORD model)
242 {
243 APARTMENT *apt;
244
245 TRACE("creating new apartment, model=%d\n", model);
246
247 apt = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*apt));
248 apt->tid = GetCurrentThreadId();
249
250 list_init(&apt->proxies);
251 list_init(&apt->stubmgrs);
252 list_init(&apt->psclsids);
253 list_init(&apt->loaded_dlls);
254 apt->ipidc = 0;
255 apt->refs = 1;
256 apt->remunk_exported = FALSE;
257 apt->oidc = 1;
258 InitializeCriticalSection(&apt->cs);
259 DEBUG_SET_CRITSEC_NAME(&apt->cs, "apartment");
260
261 apt->multi_threaded = !(model & COINIT_APARTMENTTHREADED);
262
263 if (apt->multi_threaded)
264 {
265 /* FIXME: should be randomly generated by in an RPC call to rpcss */
266 apt->oxid = ((OXID)GetCurrentProcessId() << 32) | 0xcafe;
267 }
268 else
269 {
270 /* FIXME: should be randomly generated by in an RPC call to rpcss */
271 apt->oxid = ((OXID)GetCurrentProcessId() << 32) | GetCurrentThreadId();
272 }
273
274 TRACE("Created apartment on OXID %s\n", wine_dbgstr_longlong(apt->oxid));
275
276 list_add_head(&apts, &apt->entry);
277
278 return apt;
279 }
280
281 /* gets and existing apartment if one exists or otherwise creates an apartment
282 * structure which stores OLE apartment-local information and stores a pointer
283 * to it in the thread-local storage */
284 static APARTMENT *apartment_get_or_create(DWORD model)
285 {
286 APARTMENT *apt = COM_CurrentApt();
287
288 if (!apt)
289 {
290 if (model & COINIT_APARTMENTTHREADED)
291 {
292 EnterCriticalSection(&csApartment);
293
294 apt = apartment_construct(model);
295 if (!MainApartment)
296 {
297 MainApartment = apt;
298 apt->main = TRUE;
299 TRACE("Created main-threaded apartment with OXID %s\n", wine_dbgstr_longlong(apt->oxid));
300 }
301
302 LeaveCriticalSection(&csApartment);
303
304 if (apt->main)
305 apartment_createwindowifneeded(apt);
306 }
307 else
308 {
309 EnterCriticalSection(&csApartment);
310
311 /* The multi-threaded apartment (MTA) contains zero or more threads interacting
312 * with free threaded (ie thread safe) COM objects. There is only ever one MTA
313 * in a process */
314 if (MTA)
315 {
316 TRACE("entering the multithreaded apartment %s\n", wine_dbgstr_longlong(MTA->oxid));
317 apartment_addref(MTA);
318 }
319 else
320 MTA = apartment_construct(model);
321
322 apt = MTA;
323
324 LeaveCriticalSection(&csApartment);
325 }
326 COM_CurrentInfo()->apt = apt;
327 }
328
329 return apt;
330 }
331
332 static inline BOOL apartment_is_model(const APARTMENT *apt, DWORD model)
333 {
334 return (apt->multi_threaded == !(model & COINIT_APARTMENTTHREADED));
335 }
336
337 DWORD apartment_addref(struct apartment *apt)
338 {
339 DWORD refs = InterlockedIncrement(&apt->refs);
340 TRACE("%s: before = %d\n", wine_dbgstr_longlong(apt->oxid), refs - 1);
341 return refs;
342 }
343
344 DWORD apartment_release(struct apartment *apt)
345 {
346 DWORD ret;
347
348 EnterCriticalSection(&csApartment);
349
350 ret = InterlockedDecrement(&apt->refs);
351 TRACE("%s: after = %d\n", wine_dbgstr_longlong(apt->oxid), ret);
352 /* destruction stuff that needs to happen under csApartment CS */
353 if (ret == 0)
354 {
355 if (apt == MTA) MTA = NULL;
356 else if (apt == MainApartment) MainApartment = NULL;
357 list_remove(&apt->entry);
358 }
359
360 LeaveCriticalSection(&csApartment);
361
362 if (ret == 0)
363 {
364 struct list *cursor, *cursor2;
365
366 TRACE("destroying apartment %p, oxid %s\n", apt, wine_dbgstr_longlong(apt->oxid));
367
368 /* Release the references to the registered class objects */
369 COM_RevokeAllClasses(apt);
370
371 /* no locking is needed for this apartment, because no other thread
372 * can access it at this point */
373
374 apartment_disconnectproxies(apt);
375
376 if (apt->win) DestroyWindow(apt->win);
377 if (apt->host_apt_tid) PostThreadMessageW(apt->host_apt_tid, WM_QUIT, 0, 0);
378
379 LIST_FOR_EACH_SAFE(cursor, cursor2, &apt->stubmgrs)
380 {
381 struct stub_manager *stubmgr = LIST_ENTRY(cursor, struct stub_manager, entry);
382 /* release the implicit reference given by the fact that the
383 * stub has external references (it must do since it is in the
384 * stub manager list in the apartment and all non-apartment users
385 * must have a ref on the apartment and so it cannot be destroyed).
386 */
387 stub_manager_int_release(stubmgr);
388 }
389
390 LIST_FOR_EACH_SAFE(cursor, cursor2, &apt->psclsids)
391 {
392 struct registered_psclsid *registered_psclsid =
393 LIST_ENTRY(cursor, struct registered_psclsid, entry);
394
395 list_remove(®istered_psclsid->entry);
396 HeapFree(GetProcessHeap(), 0, registered_psclsid);
397 }
398
399 /* if this assert fires, then another thread took a reference to a
400 * stub manager without taking a reference to the containing
401 * apartment, which it must do. */
402 assert(list_empty(&apt->stubmgrs));
403
404 if (apt->filter) IUnknown_Release(apt->filter);
405
406 /* free as many unused libraries as possible... */
407 apartment_freeunusedlibraries(apt, 0);
408
409 /* ... and free the memory for the apartment loaded dll entry and
410 * release the dll list reference without freeing the library for the
411 * rest */
412 while ((cursor = list_head(&apt->loaded_dlls)))
413 {
414 struct apartment_loaded_dll *apartment_loaded_dll = LIST_ENTRY(cursor, struct apartment_loaded_dll, entry);
415 COMPOBJ_DllList_ReleaseRef(apartment_loaded_dll->dll, FALSE);
416 list_remove(cursor);
417 HeapFree(GetProcessHeap(), 0, apartment_loaded_dll);
418 }
419
420 DEBUG_CLEAR_CRITSEC_NAME(&apt->cs);
421 DeleteCriticalSection(&apt->cs);
422
423 HeapFree(GetProcessHeap(), 0, apt);
424 }
425
426 return ret;
427 }
428
429 /* The given OXID must be local to this process:
430 *
431 * The ref parameter is here mostly to ensure people remember that
432 * they get one, you should normally take a ref for thread safety.
433 */
434 APARTMENT *apartment_findfromoxid(OXID oxid, BOOL ref)
435 {
436 APARTMENT *result = NULL;
437 struct list *cursor;
438
439 EnterCriticalSection(&csApartment);
440 LIST_FOR_EACH( cursor, &apts )
441 {
442 struct apartment *apt = LIST_ENTRY( cursor, struct apartment, entry );
443 if (apt->oxid == oxid)
444 {
445 result = apt;
446 if (ref) apartment_addref(result);
447 break;
448 }
449 }
450 LeaveCriticalSection(&csApartment);
451
452 return result;
453 }
454
455 /* gets the apartment which has a given creator thread ID. The caller must
456 * release the reference from the apartment as soon as the apartment pointer
457 * is no longer required. */
458 APARTMENT *apartment_findfromtid(DWORD tid)
459 {
460 APARTMENT *result = NULL;
461 struct list *cursor;
462
463 EnterCriticalSection(&csApartment);
464 LIST_FOR_EACH( cursor, &apts )
465 {
466 struct apartment *apt = LIST_ENTRY( cursor, struct apartment, entry );
467 if (apt->tid == tid)
468 {
469 result = apt;
470 apartment_addref(result);
471 break;
472 }
473 }
474 LeaveCriticalSection(&csApartment);
475
476 return result;
477 }
478
479 /* gets the main apartment if it exists. The caller must
480 * release the reference from the apartment as soon as the apartment pointer
481 * is no longer required. */
482 static APARTMENT *apartment_findmain(void)
483 {
484 APARTMENT *result;
485
486 EnterCriticalSection(&csApartment);
487
488 result = MainApartment;
489 if (result) apartment_addref(result);
490
491 LeaveCriticalSection(&csApartment);
492
493 return result;
494 }
495
496 struct host_object_params
497 {
498 HKEY hkeydll;
499 CLSID clsid; /* clsid of object to marshal */
500 IID iid; /* interface to marshal */
501 HANDLE event; /* event signalling when ready for multi-threaded case */
502 HRESULT hr; /* result for multi-threaded case */
503 IStream *stream; /* stream that the object will be marshaled into */
504 BOOL apartment_threaded; /* is the component purely apartment-threaded? */
505 };
506
507 static HRESULT apartment_hostobject(struct apartment *apt,
508 const struct host_object_params *params)
509 {
510 IUnknown *object;
511 HRESULT hr;
512 static const LARGE_INTEGER llZero;
513 WCHAR dllpath[MAX_PATH+1];
514
515 TRACE("clsid %s, iid %s\n", debugstr_guid(¶ms->clsid), debugstr_guid(¶ms->iid));
516
517 if (COM_RegReadPath(params->hkeydll, NULL, NULL, dllpath, ARRAYSIZE(dllpath)) != ERROR_SUCCESS)
518 {
519 /* failure: CLSID is not found in registry */
520 WARN("class %s not registered inproc\n", debugstr_guid(¶ms->clsid));
521 return REGDB_E_CLASSNOTREG;
522 }
523
524 hr = apartment_getclassobject(apt, dllpath, params->apartment_threaded,
525 ¶ms->clsid, ¶ms->iid, (void **)&object);
526 if (FAILED(hr))
527 return hr;
528
529 hr = CoMarshalInterface(params->stream, ¶ms->iid, object, MSHCTX_INPROC, NULL, MSHLFLAGS_NORMAL);
530 if (FAILED(hr))
531 IUnknown_Release(object);
532 IStream_Seek(params->stream, llZero, STREAM_SEEK_SET, NULL);
533
534 return hr;
535 }
536
537 static LRESULT CALLBACK apartment_wndproc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
538 {
539 switch (msg)
540 {
541 case DM_EXECUTERPC:
542 RPC_ExecuteCall((struct dispatch_params *)lParam);
543 return 0;
544 case DM_HOSTOBJECT:
545 return apartment_hostobject(COM_CurrentApt(), (const struct host_object_params *)lParam);
546 default:
547 return DefWindowProcW(hWnd, msg, wParam, lParam);
548 }
549 }
550
551 struct host_thread_params
552 {
553 COINIT threading_model;
554 HANDLE ready_event;
555 HWND apartment_hwnd;
556 };
557
558 /* thread for hosting an object to allow an object to appear to be created in
559 * an apartment with an incompatible threading model */
560 static DWORD CALLBACK apartment_hostobject_thread(LPVOID p)
561 {
562 struct host_thread_params *params = p;
563 MSG msg;
564 HRESULT hr;
565 struct apartment *apt;
566
567 TRACE("\n");
568
569 hr = CoInitializeEx(NULL, params->threading_model);
570 if (FAILED(hr)) return hr;
571
572 apt = COM_CurrentApt();
573 if (params->threading_model == COINIT_APARTMENTTHREADED)
574 {
575 apartment_createwindowifneeded(apt);
576 params->apartment_hwnd = apartment_getwindow(apt);
577 }
578 else
579 params->apartment_hwnd = NULL;
580
581 /* force the message queue to be created before signaling parent thread */
582 PeekMessageW(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
583
584 SetEvent(params->ready_event);
585 params = NULL; /* can't touch params after here as it may be invalid */
586
587 while (GetMessageW(&msg, NULL, 0, 0))
588 {
589 if (!msg.hwnd && (msg.message == DM_HOSTOBJECT))
590 {
591 struct host_object_params *obj_params = (struct host_object_params *)msg.lParam;
592 obj_params->hr = apartment_hostobject(apt, obj_params);
593 SetEvent(obj_params->event);
594 }
595 else
596 {
597 TranslateMessage(&msg);
598 DispatchMessageW(&msg);
599 }
600 }
601
602 TRACE("exiting\n");
603
604 CoUninitialize();
605
606 return S_OK;
607 }
608
609 /* finds or creates a host apartment, creates the object inside it and returns
610 * a proxy to it so that the object can be used in the apartment of the
611 * caller of this function */
612 static HRESULT apartment_hostobject_in_hostapt(
613 struct apartment *apt, BOOL multi_threaded, BOOL main_apartment,
614 HKEY hkeydll, REFCLSID rclsid, REFIID riid, void **ppv)
615 {
616 struct host_object_params params;
617 HWND apartment_hwnd = NULL;
618 DWORD apartment_tid = 0;
619 HRESULT hr;
620
621 if (!multi_threaded && main_apartment)
622 {
623 APARTMENT *host_apt = apartment_findmain();
624 if (host_apt)
625 {
626 apartment_hwnd = apartment_getwindow(host_apt);
627 apartment_release(host_apt);
628 }
629 }
630
631 if (!apartment_hwnd)
632 {
633 EnterCriticalSection(&apt->cs);
634
635 if (!apt->host_apt_tid)
636 {
637 struct host_thread_params thread_params;
638 HANDLE handles[2];
639 DWORD wait_value;
640
641 thread_params.threading_model = multi_threaded ? COINIT_MULTITHREADED : COINIT_APARTMENTTHREADED;
642 handles[0] = thread_params.ready_event = CreateEventW(NULL, FALSE, FALSE, NULL);
643 thread_params.apartment_hwnd = NULL;
644 handles[1] = CreateThread(NULL, 0, apartment_hostobject_thread, &thread_params, 0, &apt->host_apt_tid);
645 if (!handles[1])
646 {
647 CloseHandle(handles[0]);
648 LeaveCriticalSection(&apt->cs);
649 return E_OUTOFMEMORY;
650 }
651 wait_value = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
652 CloseHandle(handles[0]);
653 CloseHandle(handles[1]);
654 if (wait_value == WAIT_OBJECT_0)
655 apt->host_apt_hwnd = thread_params.apartment_hwnd;
656 else
657 {
658 LeaveCriticalSection(&apt->cs);
659 return E_OUTOFMEMORY;
660 }
661 }
662
663 if (multi_threaded || !main_apartment)
664 {
665 apartment_hwnd = apt->host_apt_hwnd;
666 apartment_tid = apt->host_apt_tid;
667 }
668
669 LeaveCriticalSection(&apt->cs);
670 }
671
672 /* another thread may have become the main apartment in the time it took
673 * us to create the thread for the host apartment */
674 if (!apartment_hwnd && !multi_threaded && main_apartment)
675 {
676 APARTMENT *host_apt = apartment_findmain();
677 if (host_apt)
678 {
679 apartment_hwnd = apartment_getwindow(host_apt);
680 apartment_release(host_apt);
681 }
682 }
683
684 params.hkeydll = hkeydll;
685 params.clsid = *rclsid;
686 params.iid = *riid;
687 hr = CreateStreamOnHGlobal(NULL, TRUE, ¶ms.stream);
688 if (FAILED(hr))
689 return hr;
690 params.apartment_threaded = !multi_threaded;
691 if (multi_threaded)
692 {
693 params.hr = S_OK;
694 params.event = CreateEventW(NULL, FALSE, FALSE, NULL);
695 if (!PostThreadMessageW(apartment_tid, DM_HOSTOBJECT, 0, (LPARAM)¶ms))
696 hr = E_OUTOFMEMORY;
697 else
698 {
699 WaitForSingleObject(params.event, INFINITE);
700 hr = params.hr;
701 }
702 CloseHandle(params.event);
703 }
704 else
705 {
706 if (!apartment_hwnd)
707 {
708 ERR("host apartment didn't create window\n");
709 hr = E_OUTOFMEMORY;
710 }
711 else
712 hr = SendMessageW(apartment_hwnd, DM_HOSTOBJECT, 0, (LPARAM)¶ms);
713 }
714 if (SUCCEEDED(hr))
715 hr = CoUnmarshalInterface(params.stream, riid, ppv);
716 IStream_Release(params.stream);
717 return hr;
718 }
719
720 /* create a window for the apartment or return the current one if one has
721 * already been created */
722 HRESULT apartment_createwindowifneeded(struct apartment *apt)
723 {
724 if (apt->multi_threaded)
725 return S_OK;
726
727 if (!apt->win)
728 {
729 HWND hwnd = CreateWindowW(wszAptWinClass, NULL, 0,
730 0, 0, 0, 0,
731 HWND_MESSAGE, 0, OLE32_hInstance, NULL);
732 if (!hwnd)
733 {
734 ERR("CreateWindow failed with error %d\n", GetLastError());
735 return HRESULT_FROM_WIN32(GetLastError());
736 }
737 if (InterlockedCompareExchangePointer((PVOID *)&apt->win, hwnd, NULL))
738 /* someone beat us to it */
739 DestroyWindow(hwnd);
740 }
741
742 return S_OK;
743 }
744
745 /* retrieves the window for the main- or apartment-threaded apartment */
746 HWND apartment_getwindow(const struct apartment *apt)
747 {
748 assert(!apt->multi_threaded);
749 return apt->win;
750 }
751
752 void apartment_joinmta(void)
753 {
754 apartment_addref(MTA);
755 COM_CurrentInfo()->apt = MTA;
756 }
757
758 /* gets the specified class object by loading the appropriate DLL, if
759 * necessary and calls the DllGetClassObject function for the DLL */
760 static HRESULT apartment_getclassobject(struct apartment *apt, LPCWSTR dllpath,
761 BOOL apartment_threaded,
762 REFCLSID rclsid, REFIID riid, void **ppv)
763 {
764 static const WCHAR wszOle32[] = {'o','l','e','3','2','.','d','l','l',0};
765 HRESULT hr = S_OK;
766 BOOL found = FALSE;
767 struct apartment_loaded_dll *apartment_loaded_dll;
768
769 if (!strcmpiW(dllpath, wszOle32))
770 {
771 /* we don't need to control the lifetime of this dll, so use the local
772 * implementation of DllGetClassObject directly */
773 TRACE("calling ole32!DllGetClassObject\n");
774 hr = DllGetClassObject(rclsid, riid, ppv);
775
776 if (hr != S_OK)
777 ERR("DllGetClassObject returned error 0x%08x\n", hr);
778
779 return hr;
780 }
781
782 EnterCriticalSection(&apt->cs);
783
784 LIST_FOR_EACH_ENTRY(apartment_loaded_dll, &apt->loaded_dlls, struct apartment_loaded_dll, entry)
785 if (!strcmpiW(dllpath, apartment_loaded_dll->dll->library_name))
786 {
787 TRACE("found %s already loaded\n", debugstr_w(dllpath));
788 found = TRUE;
789 break;
790 }
791
792 if (!found)
793 {
794 apartment_loaded_dll = HeapAlloc(GetProcessHeap(), 0, sizeof(*apartment_loaded_dll));
795 if (!apartment_loaded_dll)
796 hr = E_OUTOFMEMORY;
797 if (SUCCEEDED(hr))
798 {
799 apartment_loaded_dll->unload_time = 0;
800 apartment_loaded_dll->multi_threaded = FALSE;
801 hr = COMPOBJ_DllList_Add( dllpath, &apartment_loaded_dll->dll );
802 if (FAILED(hr))
803 HeapFree(GetProcessHeap(), 0, apartment_loaded_dll);
804 }
805 if (SUCCEEDED(hr))
806 {
807 TRACE("added new loaded dll %s\n", debugstr_w(dllpath));
808 list_add_tail(&apt->loaded_dlls, &apartment_loaded_dll->entry);
809 }
810 }
811
812 LeaveCriticalSection(&apt->cs);
813
814 if (SUCCEEDED(hr))
815 {
816 /* one component being multi-threaded overrides any number of
817 * apartment-threaded components */
818 if (!apartment_threaded)
819 apartment_loaded_dll->multi_threaded = TRUE;
820
821 TRACE("calling DllGetClassObject %p\n", apartment_loaded_dll->dll->DllGetClassObject);
822 /* OK: get the ClassObject */
823 hr = apartment_loaded_dll->dll->DllGetClassObject(rclsid, riid, ppv);
824
825 if (hr != S_OK)
826 ERR("DllGetClassObject returned error 0x%08x\n", hr);
827 }
828
829 return hr;
830 }
831
832 /* frees unused libraries loaded by apartment_getclassobject by calling the
833 * DLL's DllCanUnloadNow entry point */
834 static void apartment_freeunusedlibraries(struct apartment *apt, DWORD delay)
835 {
836 struct apartment_loaded_dll *entry, *next;
837 EnterCriticalSection(&apt->cs);
838 LIST_FOR_EACH_ENTRY_SAFE(entry, next, &apt->loaded_dlls, struct apartment_loaded_dll, entry)
839 {
840 if (entry->dll->DllCanUnloadNow && (entry->dll->DllCanUnloadNow() == S_OK))
841 {
842 DWORD real_delay = delay;
843
844 if (real_delay == INFINITE)
845 {
846 /* DLLs that return multi-threaded objects aren't unloaded
847 * straight away to cope for programs that have races between
848 * last object destruction and threads in the DLLs that haven't
849 * finished, despite DllCanUnloadNow returning S_OK */
850 if (entry->multi_threaded)
851 real_delay = 10 * 60 * 1000; /* 10 minutes */
852 else
853 real_delay = 0;
854 }
855
856 if (!real_delay || (entry->unload_time && (entry->unload_time < GetTickCount())))
857 {
858 list_remove(&entry->entry);
859 COMPOBJ_DllList_ReleaseRef(entry->dll, TRUE);
860 HeapFree(GetProcessHeap(), 0, entry);
861 }
862 else
863 entry->unload_time = GetTickCount() + real_delay;
864 }
865 else if (entry->unload_time)
866 entry->unload_time = 0;
867 }
868 LeaveCriticalSection(&apt->cs);
869 }
870
871 /*****************************************************************************
872 * This section contains OpenDllList implementation
873 */
874
875 /* caller must ensure that library_name is not already in the open dll list */
876 static HRESULT COMPOBJ_DllList_Add(LPCWSTR library_name, OpenDll **ret)
877 {
878 OpenDll *entry;
879 int len;
880 HRESULT hr = S_OK;
881 HANDLE hLibrary;
882 DllCanUnloadNowFunc DllCanUnloadNow;
883 DllGetClassObjectFunc DllGetClassObject;
884
885 TRACE("\n");
886
887 *ret = COMPOBJ_DllList_Get(library_name);
888 if (*ret) return S_OK;
889
890 /* do this outside the csOpenDllList to avoid creating a lock dependency on
891 * the loader lock */
892 hLibrary = LoadLibraryExW(library_name, 0, LOAD_WITH_ALTERED_SEARCH_PATH);
893 if (!hLibrary)
894 {
895 ERR("couldn't load in-process dll %s\n", debugstr_w(library_name));
896 /* failure: DLL could not be loaded */
897 return E_ACCESSDENIED; /* FIXME: or should this be CO_E_DLLNOTFOUND? */
898 }
899
900 DllCanUnloadNow = (void *)GetProcAddress(hLibrary, "DllCanUnloadNow");
901 /* Note: failing to find DllCanUnloadNow is not a failure */
902 DllGetClassObject = (void *)GetProcAddress(hLibrary, "DllGetClassObject");
903 if (!DllGetClassObject)
904 {
905 /* failure: the dll did not export DllGetClassObject */
906 ERR("couldn't find function DllGetClassObject in %s\n", debugstr_w(library_name));
907 FreeLibrary(hLibrary);
908 return CO_E_DLLNOTFOUND;
909 }
910
911 EnterCriticalSection( &csOpenDllList );
912
913 *ret = COMPOBJ_DllList_Get(library_name);
914 if (*ret)
915 {
916 /* another caller to this function already added the dll while we
917 * weren't in the critical section */
918 FreeLibrary(hLibrary);
919 }
920 else
921 {
922 len = strlenW(library_name);
923 entry = HeapAlloc(