Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
wine-mirror
GitHub Repository: wine-mirror/wine
Path: blob/master/dlls/combase/apartment.c
4389 views
1
/*
2
* Copyright 1995 Martin von Loewis
3
* Copyright 1998 Justin Bradford
4
* Copyright 1999 Francis Beaudet
5
* Copyright 1999 Sylvain St-Germain
6
* Copyright 2002 Marcus Meissner
7
* Copyright 2004 Mike Hearn
8
* Copyright 2005-2006 Robert Shearman (for CodeWeavers)
9
*
10
* This library is free software; you can redistribute it and/or
11
* modify it under the terms of the GNU Lesser General Public
12
* License as published by the Free Software Foundation; either
13
* version 2.1 of the License, or (at your option) any later version.
14
*
15
* This library is distributed in the hope that it will be useful,
16
* but WITHOUT ANY WARRANTY; without even the implied warranty of
17
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18
* Lesser General Public License for more details.
19
*
20
* You should have received a copy of the GNU Lesser General Public
21
* License along with this library; if not, write to the Free Software
22
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23
*
24
*/
25
26
#include <stdarg.h>
27
#include <assert.h>
28
29
#define COBJMACROS
30
#include "windef.h"
31
#include "winbase.h"
32
#include "servprov.h"
33
34
#include "combase_private.h"
35
36
#include "wine/debug.h"
37
#include "wine/list.h"
38
39
WINE_DEFAULT_DEBUG_CHANNEL(ole);
40
41
enum comclass_threadingmodel
42
{
43
ThreadingModel_Apartment = 1,
44
ThreadingModel_Free = 2,
45
ThreadingModel_No = 3,
46
ThreadingModel_Both = 4,
47
ThreadingModel_Neutral = 5
48
};
49
50
static struct apartment *mta;
51
static struct apartment *main_sta; /* the first STA */
52
static struct list apts = LIST_INIT(apts);
53
54
static CRITICAL_SECTION apt_cs;
55
static CRITICAL_SECTION_DEBUG apt_cs_debug =
56
{
57
0, 0, &apt_cs,
58
{ &apt_cs_debug.ProcessLocksList, &apt_cs_debug.ProcessLocksList },
59
0, 0, { (DWORD_PTR)(__FILE__ ": apt_cs") }
60
};
61
static CRITICAL_SECTION apt_cs = { &apt_cs_debug, -1, 0, 0, 0, 0 };
62
63
static struct list dlls = LIST_INIT(dlls);
64
65
static CRITICAL_SECTION dlls_cs;
66
static CRITICAL_SECTION_DEBUG dlls_cs_debug =
67
{
68
0, 0, &dlls_cs,
69
{ &dlls_cs_debug.ProcessLocksList, &dlls_cs_debug.ProcessLocksList },
70
0, 0, { (DWORD_PTR)(__FILE__ ": dlls_cs") }
71
};
72
static CRITICAL_SECTION dlls_cs = { &dlls_cs_debug, -1, 0, 0, 0, 0 };
73
74
typedef HRESULT (WINAPI *DllGetClassObjectFunc)(REFCLSID clsid, REFIID iid, void **obj);
75
typedef HRESULT (WINAPI *DllCanUnloadNowFunc)(void);
76
77
struct opendll
78
{
79
LONG refs;
80
LPWSTR library_name;
81
HANDLE library;
82
DllGetClassObjectFunc DllGetClassObject;
83
DllCanUnloadNowFunc DllCanUnloadNow;
84
struct list entry;
85
};
86
87
struct apartment_loaded_dll
88
{
89
struct list entry;
90
struct opendll *dll;
91
DWORD unload_time;
92
BOOL multi_threaded;
93
};
94
95
static struct opendll *apartment_get_dll(const WCHAR *library_name)
96
{
97
struct opendll *ptr, *ret = NULL;
98
99
EnterCriticalSection(&dlls_cs);
100
LIST_FOR_EACH_ENTRY(ptr, &dlls, struct opendll, entry)
101
{
102
if (!wcsicmp(library_name, ptr->library_name) &&
103
(InterlockedIncrement(&ptr->refs) != 1) /* entry is being destroyed if == 1 */)
104
{
105
ret = ptr;
106
break;
107
}
108
}
109
LeaveCriticalSection(&dlls_cs);
110
111
return ret;
112
}
113
114
/* caller must ensure that library_name is not already in the open dll list */
115
static HRESULT apartment_add_dll(const WCHAR *library_name, struct opendll **ret)
116
{
117
struct opendll *entry;
118
int len;
119
HRESULT hr = S_OK;
120
HANDLE hLibrary;
121
DllCanUnloadNowFunc DllCanUnloadNow;
122
DllGetClassObjectFunc DllGetClassObject;
123
124
TRACE("%s\n", debugstr_w(library_name));
125
126
*ret = apartment_get_dll(library_name);
127
if (*ret) return S_OK;
128
129
/* Load outside of dlls lock to avoid dependency on the loader lock */
130
hLibrary = LoadLibraryExW(library_name, 0, LOAD_WITH_ALTERED_SEARCH_PATH);
131
if (!hLibrary)
132
{
133
ERR("couldn't load in-process dll %s\n", debugstr_w(library_name));
134
/* failure: DLL could not be loaded */
135
return E_ACCESSDENIED; /* FIXME: or should this be CO_E_DLLNOTFOUND? */
136
}
137
138
/* DllCanUnloadNow is optional */
139
DllCanUnloadNow = (void *)GetProcAddress(hLibrary, "DllCanUnloadNow");
140
DllGetClassObject = (void *)GetProcAddress(hLibrary, "DllGetClassObject");
141
if (!DllGetClassObject)
142
{
143
/* failure: the dll did not export DllGetClassObject */
144
ERR("couldn't find function DllGetClassObject in %s\n", debugstr_w(library_name));
145
FreeLibrary(hLibrary);
146
return CO_E_DLLNOTFOUND;
147
}
148
149
EnterCriticalSection(&dlls_cs);
150
151
*ret = apartment_get_dll(library_name);
152
if (*ret)
153
{
154
/* another caller to this function already added the dll while we
155
* weren't in the critical section */
156
FreeLibrary(hLibrary);
157
}
158
else
159
{
160
len = lstrlenW(library_name);
161
entry = malloc(sizeof(*entry));
162
if (entry)
163
entry->library_name = malloc((len + 1) * sizeof(WCHAR));
164
if (entry && entry->library_name)
165
{
166
memcpy(entry->library_name, library_name, (len + 1)*sizeof(WCHAR));
167
entry->library = hLibrary;
168
entry->refs = 1;
169
entry->DllCanUnloadNow = DllCanUnloadNow;
170
entry->DllGetClassObject = DllGetClassObject;
171
list_add_tail(&dlls, &entry->entry);
172
*ret = entry;
173
}
174
else
175
{
176
free(entry);
177
hr = E_OUTOFMEMORY;
178
FreeLibrary(hLibrary);
179
}
180
}
181
182
LeaveCriticalSection(&dlls_cs);
183
184
return hr;
185
}
186
187
/* pass FALSE for free_entry to release a reference without destroying the
188
* entry if it reaches zero or TRUE otherwise */
189
static void apartment_release_dll(struct opendll *entry, BOOL free_entry)
190
{
191
if (!InterlockedDecrement(&entry->refs) && free_entry)
192
{
193
EnterCriticalSection(&dlls_cs);
194
list_remove(&entry->entry);
195
LeaveCriticalSection(&dlls_cs);
196
197
TRACE("freeing %p\n", entry->library);
198
FreeLibrary(entry->library);
199
200
free(entry->library_name);
201
free(entry);
202
}
203
}
204
205
/* frees memory associated with active dll list */
206
static void apartment_release_dlls(void)
207
{
208
struct opendll *entry, *cursor2;
209
EnterCriticalSection(&dlls_cs);
210
LIST_FOR_EACH_ENTRY_SAFE(entry, cursor2, &dlls, struct opendll, entry)
211
{
212
list_remove(&entry->entry);
213
free(entry->library_name);
214
free(entry);
215
}
216
LeaveCriticalSection(&dlls_cs);
217
DeleteCriticalSection(&dlls_cs);
218
}
219
220
/*
221
* This is a marshallable object exposing registered local servers.
222
* IServiceProvider is used only because it happens meet requirements
223
* and already has proxy/stub code. If more functionality is needed,
224
* a custom interface may be used instead.
225
*/
226
struct local_server
227
{
228
IServiceProvider IServiceProvider_iface;
229
LONG refcount;
230
struct apartment *apt;
231
IStream *marshal_stream;
232
};
233
234
static inline struct local_server *impl_from_IServiceProvider(IServiceProvider *iface)
235
{
236
return CONTAINING_RECORD(iface, struct local_server, IServiceProvider_iface);
237
}
238
239
static HRESULT WINAPI local_server_QueryInterface(IServiceProvider *iface, REFIID riid, void **obj)
240
{
241
struct local_server *local_server = impl_from_IServiceProvider(iface);
242
243
TRACE("%p, %s, %p\n", iface, debugstr_guid(riid), obj);
244
245
if (IsEqualGUID(riid, &IID_IUnknown) ||
246
IsEqualGUID(riid, &IID_IServiceProvider))
247
{
248
*obj = &local_server->IServiceProvider_iface;
249
}
250
else
251
{
252
*obj = NULL;
253
return E_NOINTERFACE;
254
}
255
256
IUnknown_AddRef((IUnknown *)*obj);
257
return S_OK;
258
}
259
260
static ULONG WINAPI local_server_AddRef(IServiceProvider *iface)
261
{
262
struct local_server *local_server = impl_from_IServiceProvider(iface);
263
LONG refcount = InterlockedIncrement(&local_server->refcount);
264
265
TRACE("%p, refcount %ld\n", iface, refcount);
266
267
return refcount;
268
}
269
270
static ULONG WINAPI local_server_Release(IServiceProvider *iface)
271
{
272
struct local_server *local_server = impl_from_IServiceProvider(iface);
273
LONG refcount = InterlockedDecrement(&local_server->refcount);
274
275
TRACE("%p, refcount %ld\n", iface, refcount);
276
277
if (!refcount)
278
{
279
assert(!local_server->apt);
280
free(local_server);
281
}
282
283
return refcount;
284
}
285
286
static HRESULT WINAPI local_server_QueryService(IServiceProvider *iface, REFGUID guid, REFIID riid, void **obj)
287
{
288
struct local_server *local_server = impl_from_IServiceProvider(iface);
289
struct apartment *apt = com_get_current_apt();
290
HRESULT hr = E_FAIL;
291
IUnknown *unk;
292
293
TRACE("%p, %s, %s, %p\n", iface, debugstr_guid(guid), debugstr_guid(riid), obj);
294
295
if (!local_server->apt)
296
return E_UNEXPECTED;
297
298
if ((unk = com_get_registered_class_object(apt, guid, CLSCTX_LOCAL_SERVER)))
299
{
300
hr = IUnknown_QueryInterface(unk, riid, obj);
301
IUnknown_Release(unk);
302
}
303
304
return hr;
305
}
306
307
static const IServiceProviderVtbl local_server_vtbl =
308
{
309
local_server_QueryInterface,
310
local_server_AddRef,
311
local_server_Release,
312
local_server_QueryService
313
};
314
315
HRESULT apartment_get_local_server_stream(struct apartment *apt, IStream **ret)
316
{
317
HRESULT hr = S_OK;
318
319
EnterCriticalSection(&apt->cs);
320
321
if (!apt->local_server)
322
{
323
struct local_server *obj;
324
325
obj = malloc(sizeof(*obj));
326
if (obj)
327
{
328
obj->IServiceProvider_iface.lpVtbl = &local_server_vtbl;
329
obj->refcount = 1;
330
obj->apt = apt;
331
332
hr = CreateStreamOnHGlobal(0, TRUE, &obj->marshal_stream);
333
if (SUCCEEDED(hr))
334
{
335
hr = CoMarshalInterface(obj->marshal_stream, &IID_IServiceProvider, (IUnknown *)&obj->IServiceProvider_iface,
336
MSHCTX_LOCAL, NULL, MSHLFLAGS_TABLESTRONG);
337
if (FAILED(hr))
338
IStream_Release(obj->marshal_stream);
339
}
340
341
if (SUCCEEDED(hr))
342
apt->local_server = obj;
343
else
344
free(obj);
345
}
346
else
347
hr = E_OUTOFMEMORY;
348
}
349
350
if (SUCCEEDED(hr))
351
hr = IStream_Clone(apt->local_server->marshal_stream, ret);
352
353
LeaveCriticalSection(&apt->cs);
354
355
if (FAILED(hr))
356
ERR("Failed: %#lx\n", hr);
357
358
return hr;
359
}
360
361
/* Creates new apartment for given model */
362
static struct apartment *apartment_construct(DWORD model)
363
{
364
struct apartment *apt;
365
366
TRACE("creating new apartment, model %ld\n", model);
367
368
apt = calloc(1, sizeof(*apt));
369
apt->tid = GetCurrentThreadId();
370
371
list_init(&apt->proxies);
372
list_init(&apt->stubmgrs);
373
list_init(&apt->loaded_dlls);
374
list_init(&apt->usage_cookies);
375
apt->ipidc = 0;
376
apt->refs = 1;
377
apt->remunk_exported = FALSE;
378
apt->oidc = 1;
379
InitializeCriticalSectionEx(&apt->cs, 0, RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO);
380
apt->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": apartment");
381
382
apt->multi_threaded = !(model & COINIT_APARTMENTTHREADED);
383
384
if (apt->multi_threaded)
385
{
386
/* FIXME: should be randomly generated by in an RPC call to rpcss */
387
apt->oxid = ((OXID)GetCurrentProcessId() << 32) | 0xcafe;
388
}
389
else
390
{
391
/* FIXME: should be randomly generated by in an RPC call to rpcss */
392
apt->oxid = ((OXID)GetCurrentProcessId() << 32) | GetCurrentThreadId();
393
}
394
395
TRACE("Created apartment on OXID %s\n", wine_dbgstr_longlong(apt->oxid));
396
397
list_add_head(&apts, &apt->entry);
398
399
return apt;
400
}
401
402
/* Frees unused libraries loaded into apartment */
403
void apartment_freeunusedlibraries(struct apartment *apt, DWORD delay)
404
{
405
struct apartment_loaded_dll *entry, *next;
406
407
EnterCriticalSection(&apt->cs);
408
LIST_FOR_EACH_ENTRY_SAFE(entry, next, &apt->loaded_dlls, struct apartment_loaded_dll, entry)
409
{
410
if (entry->dll->DllCanUnloadNow && (entry->dll->DllCanUnloadNow() == S_OK))
411
{
412
DWORD real_delay = delay;
413
414
if (real_delay == INFINITE)
415
{
416
/* DLLs that return multi-threaded objects aren't unloaded
417
* straight away to cope for programs that have races between
418
* last object destruction and threads in the DLLs that haven't
419
* finished, despite DllCanUnloadNow returning S_OK */
420
if (entry->multi_threaded)
421
real_delay = 10 * 60 * 1000; /* 10 minutes */
422
else
423
real_delay = 0;
424
}
425
426
if (!real_delay || (entry->unload_time && ((int)(GetTickCount() - entry->unload_time) > 0)))
427
{
428
list_remove(&entry->entry);
429
apartment_release_dll(entry->dll, TRUE);
430
free(entry);
431
}
432
else
433
{
434
entry->unload_time = GetTickCount() + real_delay;
435
if (!entry->unload_time) entry->unload_time = 1;
436
}
437
}
438
else if (entry->unload_time)
439
entry->unload_time = 0;
440
}
441
LeaveCriticalSection(&apt->cs);
442
}
443
444
void apartment_release(struct apartment *apt)
445
{
446
DWORD refcount;
447
448
EnterCriticalSection(&apt_cs);
449
450
refcount = InterlockedDecrement(&apt->refs);
451
TRACE("%s: after = %ld\n", wine_dbgstr_longlong(apt->oxid), refcount);
452
453
if (apt->being_destroyed)
454
{
455
LeaveCriticalSection(&apt_cs);
456
return;
457
}
458
459
/* destruction stuff that needs to happen under global */
460
if (!refcount)
461
{
462
apt->being_destroyed = TRUE;
463
if (apt == mta) mta = NULL;
464
else if (apt == main_sta) main_sta = NULL;
465
list_remove(&apt->entry);
466
}
467
468
LeaveCriticalSection(&apt_cs);
469
470
if (!refcount)
471
{
472
struct list *cursor, *cursor2;
473
474
TRACE("destroying apartment %p, oxid %s\n", apt, wine_dbgstr_longlong(apt->oxid));
475
476
if (apt->local_server)
477
{
478
struct local_server *local_server = apt->local_server;
479
LARGE_INTEGER zero;
480
481
memset(&zero, 0, sizeof(zero));
482
IStream_Seek(local_server->marshal_stream, zero, STREAM_SEEK_SET, NULL);
483
CoReleaseMarshalData(local_server->marshal_stream);
484
IStream_Release(local_server->marshal_stream);
485
local_server->marshal_stream = NULL;
486
487
apt->local_server = NULL;
488
local_server->apt = NULL;
489
IServiceProvider_Release(&local_server->IServiceProvider_iface);
490
}
491
492
/* Release the references to the registered class objects */
493
apartment_revoke_all_classes(apt);
494
495
/* no locking is needed for this apartment, because no other thread
496
* can access it at this point */
497
498
apartment_disconnectproxies(apt);
499
500
if (apt->win) DestroyWindow(apt->win);
501
if (apt->host_apt_tid) PostThreadMessageW(apt->host_apt_tid, WM_QUIT, 0, 0);
502
503
LIST_FOR_EACH_SAFE(cursor, cursor2, &apt->stubmgrs)
504
{
505
struct stub_manager *stubmgr = LIST_ENTRY(cursor, struct stub_manager, entry);
506
/* release the implicit reference given by the fact that the
507
* stub has external references (it must do since it is in the
508
* stub manager list in the apartment and all non-apartment users
509
* must have a ref on the apartment and so it cannot be destroyed).
510
*/
511
stub_manager_int_release(stubmgr);
512
}
513
514
/* if this assert fires, then another thread took a reference to a
515
* stub manager without taking a reference to the containing
516
* apartment, which it must do. */
517
assert(list_empty(&apt->stubmgrs));
518
519
if (apt->filter) IMessageFilter_Release(apt->filter);
520
521
/* free as many unused libraries as possible... */
522
apartment_freeunusedlibraries(apt, 0);
523
524
/* ... and free the memory for the apartment loaded dll entry and
525
* release the dll list reference without freeing the library for the
526
* rest */
527
while ((cursor = list_head(&apt->loaded_dlls)))
528
{
529
struct apartment_loaded_dll *apartment_loaded_dll = LIST_ENTRY(cursor, struct apartment_loaded_dll, entry);
530
apartment_release_dll(apartment_loaded_dll->dll, FALSE);
531
list_remove(cursor);
532
free(apartment_loaded_dll);
533
}
534
535
apt->cs.DebugInfo->Spare[0] = 0;
536
DeleteCriticalSection(&apt->cs);
537
538
free(apt);
539
}
540
}
541
542
static DWORD apartment_addref(struct apartment *apt)
543
{
544
DWORD refs = InterlockedIncrement(&apt->refs);
545
TRACE("%s: before = %ld\n", wine_dbgstr_longlong(apt->oxid), refs - 1);
546
return refs;
547
}
548
549
/* Gets existing apartment or creates a new one and enters it */
550
static struct apartment *apartment_get_or_create(DWORD model)
551
{
552
struct apartment *apt = com_get_current_apt();
553
struct tlsdata *data;
554
555
if (!apt)
556
{
557
com_get_tlsdata(&data);
558
559
if (model & COINIT_APARTMENTTHREADED)
560
{
561
EnterCriticalSection(&apt_cs);
562
563
apt = apartment_construct(model);
564
if (!main_sta)
565
{
566
main_sta = apt;
567
apt->main = TRUE;
568
TRACE("Created main-threaded apartment with OXID %s\n", wine_dbgstr_longlong(apt->oxid));
569
}
570
571
data->flags |= OLETLS_APARTMENTTHREADED;
572
if (model & COINIT_DISABLE_OLE1DDE)
573
data->flags |= OLETLS_DISABLE_OLE1DDE;
574
575
LeaveCriticalSection(&apt_cs);
576
577
if (apt->main)
578
apartment_createwindowifneeded(apt);
579
}
580
else
581
{
582
EnterCriticalSection(&apt_cs);
583
584
/* The multi-threaded apartment (MTA) contains zero or more threads interacting
585
* with free threaded (ie thread safe) COM objects. There is only ever one MTA
586
* in a process */
587
if (mta)
588
{
589
TRACE("entering the multithreaded apartment %s\n", wine_dbgstr_longlong(mta->oxid));
590
apartment_addref(mta);
591
}
592
else
593
mta = apartment_construct(model);
594
595
data->flags |= OLETLS_MULTITHREADED | OLETLS_DISABLE_OLE1DDE;
596
597
apt = mta;
598
599
LeaveCriticalSection(&apt_cs);
600
}
601
data->apt = apt;
602
}
603
604
return apt;
605
}
606
607
struct apartment * apartment_get_mta(void)
608
{
609
struct apartment *apt;
610
611
EnterCriticalSection(&apt_cs);
612
613
if ((apt = mta))
614
apartment_addref(apt);
615
616
LeaveCriticalSection(&apt_cs);
617
618
return apt;
619
}
620
621
/* Return the current apartment if it exists, or, failing that, the MTA. Caller
622
* must free the returned apartment in either case. */
623
struct apartment * apartment_get_current_or_mta(void)
624
{
625
struct apartment *apt = com_get_current_apt();
626
if (apt)
627
{
628
apartment_addref(apt);
629
return apt;
630
}
631
return apartment_get_mta();
632
}
633
634
/* The given OXID must be local to this process */
635
struct apartment * apartment_findfromoxid(OXID oxid)
636
{
637
struct apartment *result = NULL, *apt;
638
639
EnterCriticalSection(&apt_cs);
640
LIST_FOR_EACH_ENTRY(apt, &apts, struct apartment, entry)
641
{
642
if (apt->oxid == oxid)
643
{
644
result = apt;
645
apartment_addref(result);
646
break;
647
}
648
}
649
LeaveCriticalSection(&apt_cs);
650
651
return result;
652
}
653
654
/* gets the apartment which has a given creator thread ID. The caller must
655
* release the reference from the apartment as soon as the apartment pointer
656
* is no longer required. */
657
struct apartment * apartment_findfromtid(DWORD tid)
658
{
659
struct apartment *result = NULL, *apt;
660
661
EnterCriticalSection(&apt_cs);
662
LIST_FOR_EACH_ENTRY(apt, &apts, struct apartment, entry)
663
{
664
if (apt != mta && apt->tid == tid)
665
{
666
result = apt;
667
apartment_addref(result);
668
break;
669
}
670
}
671
672
if (!result && mta && mta->tid == tid)
673
{
674
result = mta;
675
apartment_addref(result);
676
}
677
678
LeaveCriticalSection(&apt_cs);
679
680
return result;
681
}
682
683
/* gets the main apartment if it exists. The caller must
684
* release the reference from the apartment as soon as the apartment pointer
685
* is no longer required. */
686
static struct apartment *apartment_findmain(void)
687
{
688
struct apartment *result;
689
690
EnterCriticalSection(&apt_cs);
691
692
result = main_sta;
693
if (result) apartment_addref(result);
694
695
LeaveCriticalSection(&apt_cs);
696
697
return result;
698
}
699
700
struct host_object_params
701
{
702
struct class_reg_data regdata;
703
CLSID clsid; /* clsid of object to marshal */
704
IID iid; /* interface to marshal */
705
HANDLE event; /* event signalling when ready for multi-threaded case */
706
HRESULT hr; /* result for multi-threaded case */
707
IStream *stream; /* stream that the object will be marshaled into */
708
BOOL apartment_threaded; /* is the component purely apartment-threaded? */
709
};
710
711
/* Returns expanded dll path from the registry or activation context. */
712
static BOOL get_object_dll_path(const struct class_reg_data *regdata, WCHAR *dst, DWORD dstlen)
713
{
714
DWORD ret;
715
716
if (regdata->origin == CLASS_REG_REGISTRY)
717
{
718
DWORD keytype;
719
WCHAR src[MAX_PATH];
720
DWORD dwLength = dstlen * sizeof(WCHAR);
721
722
if ((ret = RegQueryValueExW(regdata->u.hkey, NULL, NULL, &keytype, (BYTE*)src, &dwLength)) == ERROR_SUCCESS)
723
{
724
if (keytype == REG_EXPAND_SZ)
725
{
726
if (dstlen <= ExpandEnvironmentStringsW(src, dst, dstlen)) ret = ERROR_MORE_DATA;
727
}
728
else
729
{
730
const WCHAR *quote_start;
731
quote_start = wcschr(src, '\"');
732
if (quote_start)
733
{
734
const WCHAR *quote_end = wcschr(quote_start + 1, '\"');
735
if (quote_end)
736
{
737
memmove(src, quote_start + 1, (quote_end - quote_start - 1) * sizeof(WCHAR));
738
src[quote_end - quote_start - 1] = '\0';
739
}
740
}
741
lstrcpynW(dst, src, dstlen);
742
}
743
}
744
return !ret;
745
}
746
else
747
{
748
ULONG_PTR cookie;
749
750
*dst = 0;
751
ActivateActCtx(regdata->u.actctx.hactctx, &cookie);
752
ret = SearchPathW(NULL, regdata->u.actctx.module_name, L".dll", dstlen, dst, NULL);
753
DeactivateActCtx(0, cookie);
754
return *dst != 0;
755
}
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
HRESULT hr = S_OK;
765
BOOL found = FALSE;
766
struct apartment_loaded_dll *apartment_loaded_dll;
767
768
if (!wcsicmp(dllpath, L"ole32.dll"))
769
{
770
HRESULT (WINAPI *p_ole32_DllGetClassObject)(REFCLSID clsid, REFIID riid, void **obj);
771
772
p_ole32_DllGetClassObject = (void *)GetProcAddress(GetModuleHandleW(L"ole32.dll"), "DllGetClassObject");
773
774
/* we don't need to control the lifetime of this dll, so use the local
775
* implementation of DllGetClassObject directly */
776
TRACE("calling ole32!DllGetClassObject\n");
777
hr = p_ole32_DllGetClassObject(rclsid, riid, ppv);
778
779
if (hr != S_OK)
780
ERR("DllGetClassObject returned error %#lx for dll %s\n", hr, debugstr_w(dllpath));
781
782
return hr;
783
}
784
785
EnterCriticalSection(&apt->cs);
786
787
LIST_FOR_EACH_ENTRY(apartment_loaded_dll, &apt->loaded_dlls, struct apartment_loaded_dll, entry)
788
if (!wcsicmp(dllpath, apartment_loaded_dll->dll->library_name))
789
{
790
TRACE("found %s already loaded\n", debugstr_w(dllpath));
791
found = TRUE;
792
break;
793
}
794
795
if (!found)
796
{
797
apartment_loaded_dll = malloc(sizeof(*apartment_loaded_dll));
798
if (!apartment_loaded_dll)
799
hr = E_OUTOFMEMORY;
800
if (SUCCEEDED(hr))
801
{
802
apartment_loaded_dll->unload_time = 0;
803
apartment_loaded_dll->multi_threaded = FALSE;
804
hr = apartment_add_dll(dllpath, &apartment_loaded_dll->dll);
805
if (FAILED(hr))
806
free(apartment_loaded_dll);
807
}
808
if (SUCCEEDED(hr))
809
{
810
TRACE("added new loaded dll %s\n", debugstr_w(dllpath));
811
list_add_tail(&apt->loaded_dlls, &apartment_loaded_dll->entry);
812
}
813
}
814
815
LeaveCriticalSection(&apt->cs);
816
817
if (SUCCEEDED(hr))
818
{
819
/* one component being multi-threaded overrides any number of
820
* apartment-threaded components */
821
if (!apartment_threaded)
822
apartment_loaded_dll->multi_threaded = TRUE;
823
824
TRACE("calling DllGetClassObject %p\n", apartment_loaded_dll->dll->DllGetClassObject);
825
/* OK: get the ClassObject */
826
hr = apartment_loaded_dll->dll->DllGetClassObject(rclsid, riid, ppv);
827
828
if (hr != S_OK)
829
ERR("DllGetClassObject returned error %#lx for dll %s\n", hr, debugstr_w(dllpath));
830
}
831
832
return hr;
833
}
834
835
static HRESULT apartment_hostobject(struct apartment *apt,
836
const struct host_object_params *params);
837
838
struct host_thread_params
839
{
840
COINIT threading_model;
841
HANDLE ready_event;
842
HWND apartment_hwnd;
843
};
844
845
/* thread for hosting an object to allow an object to appear to be created in
846
* an apartment with an incompatible threading model */
847
static DWORD CALLBACK apartment_hostobject_thread(void *p)
848
{
849
struct host_thread_params *params = p;
850
MSG msg;
851
HRESULT hr;
852
struct apartment *apt;
853
854
TRACE("\n");
855
856
hr = CoInitializeEx(NULL, params->threading_model);
857
if (FAILED(hr)) return hr;
858
859
apt = com_get_current_apt();
860
if (params->threading_model == COINIT_APARTMENTTHREADED)
861
{
862
apartment_createwindowifneeded(apt);
863
params->apartment_hwnd = apartment_getwindow(apt);
864
}
865
else
866
params->apartment_hwnd = NULL;
867
868
/* force the message queue to be created before signaling parent thread */
869
PeekMessageW(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
870
871
SetEvent(params->ready_event);
872
params = NULL; /* can't touch params after here as it may be invalid */
873
874
while (GetMessageW(&msg, NULL, 0, 0))
875
{
876
if (!msg.hwnd && (msg.message == DM_HOSTOBJECT))
877
{
878
struct host_object_params *obj_params = (struct host_object_params *)msg.lParam;
879
obj_params->hr = apartment_hostobject(apt, obj_params);
880
SetEvent(obj_params->event);
881
}
882
else
883
{
884
TranslateMessage(&msg);
885
DispatchMessageW(&msg);
886
}
887
}
888
889
TRACE("exiting\n");
890
891
CoUninitialize();
892
893
return S_OK;
894
}
895
896
/* finds or creates a host apartment, creates the object inside it and returns
897
* a proxy to it so that the object can be used in the apartment of the
898
* caller of this function */
899
static HRESULT apartment_hostobject_in_hostapt(struct apartment *apt, BOOL multi_threaded,
900
BOOL main_apartment, const struct class_reg_data *regdata, REFCLSID rclsid, REFIID riid, void **ppv)
901
{
902
struct host_object_params params;
903
HWND apartment_hwnd = NULL;
904
DWORD apartment_tid = 0;
905
HRESULT hr;
906
907
if (!multi_threaded && main_apartment)
908
{
909
struct apartment *host_apt = apartment_findmain();
910
if (host_apt)
911
{
912
apartment_hwnd = apartment_getwindow(host_apt);
913
apartment_release(host_apt);
914
}
915
}
916
917
if (!apartment_hwnd)
918
{
919
EnterCriticalSection(&apt->cs);
920
921
if (!apt->host_apt_tid)
922
{
923
struct host_thread_params thread_params;
924
HANDLE handles[2];
925
DWORD wait_value;
926
927
thread_params.threading_model = multi_threaded ? COINIT_MULTITHREADED : COINIT_APARTMENTTHREADED;
928
handles[0] = thread_params.ready_event = CreateEventW(NULL, FALSE, FALSE, NULL);
929
thread_params.apartment_hwnd = NULL;
930
handles[1] = CreateThread(NULL, 0, apartment_hostobject_thread, &thread_params, 0, &apt->host_apt_tid);
931
if (!handles[1])
932
{
933
CloseHandle(handles[0]);
934
LeaveCriticalSection(&apt->cs);
935
return E_OUTOFMEMORY;
936
}
937
wait_value = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
938
CloseHandle(handles[0]);
939
CloseHandle(handles[1]);
940
if (wait_value == WAIT_OBJECT_0)
941
apt->host_apt_hwnd = thread_params.apartment_hwnd;
942
else
943
{
944
LeaveCriticalSection(&apt->cs);
945
return E_OUTOFMEMORY;
946
}
947
}
948
949
if (multi_threaded || !main_apartment)
950
{
951
apartment_hwnd = apt->host_apt_hwnd;
952
apartment_tid = apt->host_apt_tid;
953
}
954
955
LeaveCriticalSection(&apt->cs);
956
}
957
958
/* another thread may have become the main apartment in the time it took
959
* us to create the thread for the host apartment */
960
if (!apartment_hwnd && !multi_threaded && main_apartment)
961
{
962
struct apartment *host_apt = apartment_findmain();
963
if (host_apt)
964
{
965
apartment_hwnd = apartment_getwindow(host_apt);
966
apartment_release(host_apt);
967
}
968
}
969
970
params.regdata = *regdata;
971
params.clsid = *rclsid;
972
params.iid = *riid;
973
hr = CreateStreamOnHGlobal(NULL, TRUE, &params.stream);
974
if (FAILED(hr))
975
return hr;
976
params.apartment_threaded = !multi_threaded;
977
if (multi_threaded)
978
{
979
params.hr = S_OK;
980
params.event = CreateEventW(NULL, FALSE, FALSE, NULL);
981
if (!PostThreadMessageW(apartment_tid, DM_HOSTOBJECT, 0, (LPARAM)&params))
982
hr = E_OUTOFMEMORY;
983
else
984
{
985
WaitForSingleObject(params.event, INFINITE);
986
hr = params.hr;
987
}
988
CloseHandle(params.event);
989
}
990
else
991
{
992
if (!apartment_hwnd)
993
{
994
ERR("host apartment didn't create window\n");
995
hr = E_OUTOFMEMORY;
996
}
997
else
998
hr = SendMessageW(apartment_hwnd, DM_HOSTOBJECT, 0, (LPARAM)&params);
999
}
1000
if (SUCCEEDED(hr))
1001
hr = CoUnmarshalInterface(params.stream, riid, ppv);
1002
IStream_Release(params.stream);
1003
return hr;
1004
}
1005
1006
static enum comclass_threadingmodel get_threading_model(const struct class_reg_data *data)
1007
{
1008
if (data->origin == CLASS_REG_REGISTRY)
1009
{
1010
WCHAR threading_model[10 /* lstrlenW(L"apartment")+1 */];
1011
DWORD dwLength = sizeof(threading_model);
1012
DWORD keytype;
1013
DWORD ret;
1014
1015
ret = RegQueryValueExW(data->u.hkey, L"ThreadingModel", NULL, &keytype, (BYTE*)threading_model, &dwLength);
1016
if ((ret != ERROR_SUCCESS) || (keytype != REG_SZ))
1017
threading_model[0] = '\0';
1018
1019
if (!wcsicmp(threading_model, L"Apartment")) return ThreadingModel_Apartment;
1020
if (!wcsicmp(threading_model, L"Free")) return ThreadingModel_Free;
1021
if (!wcsicmp(threading_model, L"Both")) return ThreadingModel_Both;
1022
1023
/* there's not specific handling for this case */
1024
if (threading_model[0]) return ThreadingModel_Neutral;
1025
return ThreadingModel_No;
1026
}
1027
else
1028
return data->u.actctx.threading_model;
1029
}
1030
1031
HRESULT apartment_get_inproc_class_object(struct apartment *apt, const struct class_reg_data *regdata,
1032
REFCLSID rclsid, REFIID riid, DWORD class_context, void **ppv)
1033
{
1034
WCHAR dllpath[MAX_PATH+1];
1035
BOOL apartment_threaded;
1036
1037
if (!(class_context & CLSCTX_PS_DLL))
1038
{
1039
enum comclass_threadingmodel model = get_threading_model(regdata);
1040
1041
if (model == ThreadingModel_Apartment)
1042
{
1043
apartment_threaded = TRUE;
1044
if (apt->multi_threaded)
1045
return apartment_hostobject_in_hostapt(apt, FALSE, FALSE, regdata, rclsid, riid, ppv);
1046
}
1047
else if (model == ThreadingModel_Free)
1048
{
1049
apartment_threaded = FALSE;
1050
if (!apt->multi_threaded)
1051
return apartment_hostobject_in_hostapt(apt, TRUE, FALSE, regdata, rclsid, riid, ppv);
1052
}
1053
/* everything except "Apartment", "Free" and "Both" */
1054
else if (model != ThreadingModel_Both)
1055
{
1056
apartment_threaded = TRUE;
1057
/* everything else is main-threaded */
1058
if (model != ThreadingModel_No)
1059
FIXME("unrecognised threading model %d for object %s, should be main-threaded?\n", model, debugstr_guid(rclsid));
1060
1061
if (apt->multi_threaded || !apt->main)
1062
return apartment_hostobject_in_hostapt(apt, FALSE, TRUE, regdata, rclsid, riid, ppv);
1063
}
1064
else
1065
apartment_threaded = FALSE;
1066
}
1067
else
1068
apartment_threaded = !apt->multi_threaded;
1069
1070
if (!get_object_dll_path(regdata, dllpath, ARRAY_SIZE(dllpath)))
1071
{
1072
/* failure: CLSID is not found in registry */
1073
WARN("class %s not registered inproc\n", debugstr_guid(rclsid));
1074
return REGDB_E_CLASSNOTREG;
1075
}
1076
1077
return apartment_getclassobject(apt, dllpath, apartment_threaded, rclsid, riid, ppv);
1078
}
1079
1080
static HRESULT apartment_hostobject(struct apartment *apt, const struct host_object_params *params)
1081
{
1082
static const LARGE_INTEGER llZero;
1083
WCHAR dllpath[MAX_PATH+1];
1084
IUnknown *object;
1085
HRESULT hr;
1086
1087
TRACE("clsid %s, iid %s\n", debugstr_guid(&params->clsid), debugstr_guid(&params->iid));
1088
1089
if (!get_object_dll_path(&params->regdata, dllpath, ARRAY_SIZE(dllpath)))
1090
{
1091
/* failure: CLSID is not found in registry */
1092
WARN("class %s not registered inproc\n", debugstr_guid(&params->clsid));
1093
return REGDB_E_CLASSNOTREG;
1094
}
1095
1096
hr = apartment_getclassobject(apt, dllpath, params->apartment_threaded, &params->clsid, &params->iid, (void **)&object);
1097
if (FAILED(hr))
1098
return hr;
1099
1100
hr = CoMarshalInterface(params->stream, &params->iid, object, MSHCTX_INPROC, NULL, MSHLFLAGS_NORMAL);
1101
if (FAILED(hr))
1102
IUnknown_Release(object);
1103
IStream_Seek(params->stream, llZero, STREAM_SEEK_SET, NULL);
1104
1105
return hr;
1106
}
1107
1108
struct dispatch_params;
1109
1110
static LRESULT CALLBACK apartment_wndproc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1111
{
1112
switch (msg)
1113
{
1114
case DM_EXECUTERPC:
1115
rpc_execute_call((struct dispatch_params *)lParam);
1116
return 0;
1117
case DM_HOSTOBJECT:
1118
return apartment_hostobject(com_get_current_apt(), (const struct host_object_params *)lParam);
1119
default:
1120
return DefWindowProcW(hWnd, msg, wParam, lParam);
1121
}
1122
}
1123
1124
static BOOL apartment_is_model(const struct apartment *apt, DWORD model)
1125
{
1126
return (apt->multi_threaded == !(model & COINIT_APARTMENTTHREADED));
1127
}
1128
1129
HRESULT enter_apartment(struct tlsdata *data, DWORD model)
1130
{
1131
HRESULT hr = S_OK;
1132
1133
if (!data->apt)
1134
{
1135
if (!apartment_get_or_create(model))
1136
return E_OUTOFMEMORY;
1137
}
1138
else if (!apartment_is_model(data->apt, model))
1139
{
1140
WARN("Attempt to change threading model of this apartment from %s to %s\n",
1141
data->apt->multi_threaded ? "multi-threaded" : "apartment threaded",
1142
model & COINIT_APARTMENTTHREADED ? "apartment threaded" : "multi-threaded" );
1143
return RPC_E_CHANGED_MODE;
1144
}
1145
else
1146
hr = S_FALSE;
1147
1148
data->inits++;
1149
1150
return hr;
1151
}
1152
1153
void leave_apartment(struct tlsdata *data)
1154
{
1155
if (!--data->inits)
1156
{
1157
if (data->ole_inits)
1158
WARN( "Uninitializing apartment while Ole is still initialized\n" );
1159
apartment_release(data->apt);
1160
if (data->implicit_mta_cookie)
1161
{
1162
apartment_decrement_mta_usage(data->implicit_mta_cookie);
1163
data->implicit_mta_cookie = NULL;
1164
}
1165
data->apt = NULL;
1166
data->flags &= ~(OLETLS_DISABLE_OLE1DDE | OLETLS_APARTMENTTHREADED | OLETLS_MULTITHREADED);
1167
}
1168
}
1169
1170
struct mta_cookie
1171
{
1172
struct list entry;
1173
};
1174
1175
HRESULT apartment_increment_mta_usage(CO_MTA_USAGE_COOKIE *cookie)
1176
{
1177
struct mta_cookie *mta_cookie;
1178
1179
*cookie = NULL;
1180
1181
if (!(mta_cookie = malloc(sizeof(*mta_cookie))))
1182
return E_OUTOFMEMORY;
1183
1184
EnterCriticalSection(&apt_cs);
1185
1186
if (mta)
1187
apartment_addref(mta);
1188
else
1189
mta = apartment_construct(COINIT_MULTITHREADED);
1190
list_add_head(&mta->usage_cookies, &mta_cookie->entry);
1191
1192
LeaveCriticalSection(&apt_cs);
1193
1194
*cookie = (CO_MTA_USAGE_COOKIE)mta_cookie;
1195
1196
return S_OK;
1197
}
1198
1199
void apartment_decrement_mta_usage(CO_MTA_USAGE_COOKIE cookie)
1200
{
1201
struct mta_cookie *mta_cookie = (struct mta_cookie *)cookie;
1202
1203
EnterCriticalSection(&apt_cs);
1204
1205
if (mta)
1206
{
1207
struct mta_cookie *cur;
1208
1209
LIST_FOR_EACH_ENTRY(cur, &mta->usage_cookies, struct mta_cookie, entry)
1210
{
1211
if (mta_cookie == cur)
1212
{
1213
list_remove(&cur->entry);
1214
free(cur);
1215
apartment_release(mta);
1216
break;
1217
}
1218
}
1219
}
1220
1221
LeaveCriticalSection(&apt_cs);
1222
}
1223
1224
static const WCHAR aptwinclassW[] = L"OleMainThreadWndClass";
1225
static ATOM apt_win_class;
1226
1227
static BOOL WINAPI register_class( INIT_ONCE *once, void *param, void **context )
1228
{
1229
WNDCLASSW wclass;
1230
1231
/* Dispatching to the correct thread in an apartment is done through
1232
* window messages rather than RPC transports. When an interface is
1233
* marshalled into another apartment in the same process, a window of the
1234
* following class is created. The *caller* of CoMarshalInterface (i.e., the
1235
* application) is responsible for pumping the message loop in that thread.
1236
* The WM_USER messages which point to the RPCs are then dispatched to
1237
* apartment_wndproc by the user's code from the apartment in which the
1238
* interface was unmarshalled.
1239
*/
1240
memset(&wclass, 0, sizeof(wclass));
1241
wclass.lpfnWndProc = apartment_wndproc;
1242
wclass.hInstance = hProxyDll;
1243
wclass.lpszClassName = aptwinclassW;
1244
apt_win_class = RegisterClassW(&wclass);
1245
return TRUE;
1246
}
1247
1248
/* create a window for the apartment or return the current one if one has
1249
* already been created */
1250
HRESULT apartment_createwindowifneeded(struct apartment *apt)
1251
{
1252
static INIT_ONCE class_init_once = INIT_ONCE_STATIC_INIT;
1253
1254
if (apt->multi_threaded)
1255
return S_OK;
1256
1257
if (!apt->win)
1258
{
1259
HWND hwnd;
1260
1261
InitOnceExecuteOnce( &class_init_once, register_class, NULL, NULL );
1262
1263
hwnd = CreateWindowW(aptwinclassW, NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, 0, hProxyDll, NULL);
1264
if (!hwnd)
1265
{
1266
ERR("CreateWindow failed with error %ld\n", GetLastError());
1267
return HRESULT_FROM_WIN32(GetLastError());
1268
}
1269
if (InterlockedCompareExchangePointer((void **)&apt->win, hwnd, NULL))
1270
/* someone beat us to it */
1271
DestroyWindow(hwnd);
1272
}
1273
1274
return S_OK;
1275
}
1276
1277
/* retrieves the window for the main- or apartment-threaded apartment */
1278
HWND apartment_getwindow(const struct apartment *apt)
1279
{
1280
assert(!apt->multi_threaded);
1281
return apt->win;
1282
}
1283
1284
OXID apartment_getoxid(const struct apartment *apt)
1285
{
1286
return apt->oxid;
1287
}
1288
1289
void apartment_global_cleanup(void)
1290
{
1291
if (apt_win_class)
1292
UnregisterClassW((const WCHAR *)MAKEINTATOM(apt_win_class), hProxyDll);
1293
apartment_release_dlls();
1294
DeleteCriticalSection(&apt_cs);
1295
}
1296
1297
HRESULT ensure_mta(void)
1298
{
1299
struct apartment *apt;
1300
struct tlsdata *data;
1301
HRESULT hr;
1302
1303
if (FAILED(hr = com_get_tlsdata(&data)))
1304
return hr;
1305
if ((apt = data->apt) && (data->implicit_mta_cookie || apt->multi_threaded))
1306
return S_OK;
1307
1308
EnterCriticalSection(&apt_cs);
1309
if (apt || mta)
1310
hr = apartment_increment_mta_usage(&data->implicit_mta_cookie);
1311
else
1312
hr = CO_E_NOTINITIALIZED;
1313
LeaveCriticalSection(&apt_cs);
1314
1315
if (FAILED(hr))
1316
{
1317
ERR("Failed, hr %#lx.\n", hr);
1318
return hr;
1319
}
1320
return S_OK;
1321
}
1322
1323