2f4a69aa73a6b93aa27be3baedb12d64518a4b66
[reactos.git] / dll / win32 / 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 * 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
49 #include "ntstatus.h"
50 #define WIN32_NO_STATUS
51 #include "windef.h"
52 #include "winbase.h"
53 #include "winerror.h"
54 #include "winreg.h"
55 #include "winuser.h"
56 #define USE_COM_CONTEXT_DEF
57 #include "objbase.h"
58 #include "ole2.h"
59 #include "ole2ver.h"
60 #include "ctxtcall.h"
61 #include "dde.h"
62 #include "servprov.h"
63 #ifndef __REACTOS__
64 #include "initguid.h"
65 #endif
66 #include "compobj_private.h"
67 #include "moniker.h"
68
69 #include "wine/unicode.h"
70 #include "wine/debug.h"
71
72 WINE_DEFAULT_DEBUG_CHANNEL(ole);
73
74 #undef ARRAYSIZE
75 #define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
76
77 /****************************************************************************
78 * This section defines variables internal to the COM module.
79 */
80
81 static APARTMENT *MTA; /* protected by csApartment */
82 static APARTMENT *MainApartment; /* the first STA apartment */
83 static struct list apts = LIST_INIT( apts ); /* protected by csApartment */
84
85 static CRITICAL_SECTION csApartment;
86 static CRITICAL_SECTION_DEBUG critsect_debug =
87 {
88 0, 0, &csApartment,
89 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
90 0, 0, { (DWORD_PTR)(__FILE__ ": csApartment") }
91 };
92 static CRITICAL_SECTION csApartment = { &critsect_debug, -1, 0, 0, 0, 0 };
93
94 enum comclass_threadingmodel
95 {
96 ThreadingModel_Apartment = 1,
97 ThreadingModel_Free = 2,
98 ThreadingModel_No = 3,
99 ThreadingModel_Both = 4,
100 ThreadingModel_Neutral = 5
101 };
102
103 enum comclass_miscfields
104 {
105 MiscStatus = 1,
106 MiscStatusIcon = 2,
107 MiscStatusContent = 4,
108 MiscStatusThumbnail = 8,
109 MiscStatusDocPrint = 16
110 };
111
112 struct comclassredirect_data
113 {
114 ULONG size;
115 BYTE res;
116 BYTE miscmask;
117 BYTE res1[2];
118 DWORD model;
119 GUID clsid;
120 GUID alias;
121 GUID clsid2;
122 GUID tlbid;
123 ULONG name_len;
124 ULONG name_offset;
125 ULONG progid_len;
126 ULONG progid_offset;
127 ULONG clrdata_len;
128 ULONG clrdata_offset;
129 DWORD miscstatus;
130 DWORD miscstatuscontent;
131 DWORD miscstatusthumbnail;
132 DWORD miscstatusicon;
133 DWORD miscstatusdocprint;
134 };
135
136 struct ifacepsredirect_data
137 {
138 ULONG size;
139 DWORD mask;
140 GUID iid;
141 ULONG nummethods;
142 GUID tlbid;
143 GUID base;
144 ULONG name_len;
145 ULONG name_offset;
146 };
147
148 struct progidredirect_data
149 {
150 ULONG size;
151 DWORD reserved;
152 ULONG clsid_offset;
153 };
154
155 struct class_reg_data
156 {
157 union
158 {
159 struct
160 {
161 struct comclassredirect_data *data;
162 void *section;
163 HANDLE hactctx;
164 } actctx;
165 HKEY hkey;
166 } u;
167 BOOL hkey;
168 };
169
170 struct registered_psclsid
171 {
172 struct list entry;
173 IID iid;
174 CLSID clsid;
175 };
176
177 static struct list registered_psclsid_list = LIST_INIT(registered_psclsid_list);
178
179 static CRITICAL_SECTION cs_registered_psclsid_list;
180 static CRITICAL_SECTION_DEBUG psclsid_cs_debug =
181 {
182 0, 0, &cs_registered_psclsid_list,
183 { &psclsid_cs_debug.ProcessLocksList, &psclsid_cs_debug.ProcessLocksList },
184 0, 0, { (DWORD_PTR)(__FILE__ ": cs_registered_psclsid_list") }
185 };
186 static CRITICAL_SECTION cs_registered_psclsid_list = { &psclsid_cs_debug, -1, 0, 0, 0, 0 };
187
188 /*
189 * This is a marshallable object exposing registered local servers.
190 * IServiceProvider is used only because it happens meet requirements
191 * and already has proxy/stub code. If more functionality is needed,
192 * a custom interface may be used instead.
193 */
194 struct LocalServer
195 {
196 IServiceProvider IServiceProvider_iface;
197 LONG ref;
198 APARTMENT *apt;
199 IStream *marshal_stream;
200 };
201
202 /*
203 * This lock count counts the number of times CoInitialize is called. It is
204 * decreased every time CoUninitialize is called. When it hits 0, the COM
205 * libraries are freed
206 */
207 static LONG s_COMLockCount = 0;
208 /* Reference count used by CoAddRefServerProcess/CoReleaseServerProcess */
209 static LONG s_COMServerProcessReferences = 0;
210
211 /*
212 * This linked list contains the list of registered class objects. These
213 * are mostly used to register the factories for out-of-proc servers of OLE
214 * objects.
215 *
216 * TODO: Make this data structure aware of inter-process communication. This
217 * means that parts of this will be exported to rpcss.
218 */
219 typedef struct tagRegisteredClass
220 {
221 struct list entry;
222 CLSID classIdentifier;
223 OXID apartment_id;
224 LPUNKNOWN classObject;
225 DWORD runContext;
226 DWORD connectFlags;
227 DWORD dwCookie;
228 void *RpcRegistration;
229 } RegisteredClass;
230
231 static struct list RegisteredClassList = LIST_INIT(RegisteredClassList);
232
233 static CRITICAL_SECTION csRegisteredClassList;
234 static CRITICAL_SECTION_DEBUG class_cs_debug =
235 {
236 0, 0, &csRegisteredClassList,
237 { &class_cs_debug.ProcessLocksList, &class_cs_debug.ProcessLocksList },
238 0, 0, { (DWORD_PTR)(__FILE__ ": csRegisteredClassList") }
239 };
240 static CRITICAL_SECTION csRegisteredClassList = { &class_cs_debug, -1, 0, 0, 0, 0 };
241
242 static inline enum comclass_miscfields dvaspect_to_miscfields(DWORD aspect)
243 {
244 switch (aspect)
245 {
246 case DVASPECT_CONTENT:
247 return MiscStatusContent;
248 case DVASPECT_THUMBNAIL:
249 return MiscStatusThumbnail;
250 case DVASPECT_ICON:
251 return MiscStatusIcon;
252 case DVASPECT_DOCPRINT:
253 return MiscStatusDocPrint;
254 default:
255 return MiscStatus;
256 };
257 }
258
259 BOOL actctx_get_miscstatus(const CLSID *clsid, DWORD aspect, DWORD *status)
260 {
261 ACTCTX_SECTION_KEYED_DATA data;
262
263 data.cbSize = sizeof(data);
264 if (FindActCtxSectionGuid(0, NULL, ACTIVATION_CONTEXT_SECTION_COM_SERVER_REDIRECTION,
265 clsid, &data))
266 {
267 struct comclassredirect_data *comclass = (struct comclassredirect_data*)data.lpData;
268 enum comclass_miscfields misc = dvaspect_to_miscfields(aspect);
269
270 if (!(comclass->miscmask & misc))
271 {
272 if (!(comclass->miscmask & MiscStatus))
273 {
274 *status = 0;
275 return TRUE;
276 }
277 misc = MiscStatus;
278 }
279
280 switch (misc)
281 {
282 case MiscStatus:
283 *status = comclass->miscstatus;
284 break;
285 case MiscStatusIcon:
286 *status = comclass->miscstatusicon;
287 break;
288 case MiscStatusContent:
289 *status = comclass->miscstatuscontent;
290 break;
291 case MiscStatusThumbnail:
292 *status = comclass->miscstatusthumbnail;
293 break;
294 case MiscStatusDocPrint:
295 *status = comclass->miscstatusdocprint;
296 break;
297 default:
298 ;
299 };
300
301 return TRUE;
302 }
303 else
304 return FALSE;
305 }
306
307 /* wrapper for NtCreateKey that creates the key recursively if necessary */
308 static NTSTATUS create_key( HKEY *retkey, ACCESS_MASK access, OBJECT_ATTRIBUTES *attr )
309 {
310 NTSTATUS status = NtCreateKey( (HANDLE *)retkey, access, attr, 0, NULL, 0, NULL );
311
312 if (status == STATUS_OBJECT_NAME_NOT_FOUND)
313 {
314 HANDLE subkey, root = attr->RootDirectory;
315 WCHAR *buffer = attr->ObjectName->Buffer;
316 DWORD attrs, pos = 0, i = 0, len = attr->ObjectName->Length / sizeof(WCHAR);
317 UNICODE_STRING str;
318
319 while (i < len && buffer[i] != '\\') i++;
320 if (i == len) return status;
321
322 attrs = attr->Attributes;
323 attr->ObjectName = &str;
324
325 while (i < len)
326 {
327 str.Buffer = buffer + pos;
328 str.Length = (i - pos) * sizeof(WCHAR);
329 status = NtCreateKey( &subkey, access, attr, 0, NULL, 0, NULL );
330 if (attr->RootDirectory != root) NtClose( attr->RootDirectory );
331 if (status) return status;
332 attr->RootDirectory = subkey;
333 while (i < len && buffer[i] == '\\') i++;
334 pos = i;
335 while (i < len && buffer[i] != '\\') i++;
336 }
337 str.Buffer = buffer + pos;
338 str.Length = (i - pos) * sizeof(WCHAR);
339 attr->Attributes = attrs;
340 status = NtCreateKey( (PHANDLE)retkey, access, attr, 0, NULL, 0, NULL );
341 if (attr->RootDirectory != root) NtClose( attr->RootDirectory );
342 }
343 return status;
344 }
345
346 #ifdef __REACTOS__
347 static const WCHAR classes_rootW[] = L"\\REGISTRY\\Machine\\Software\\Classes";
348 #else
349 static const WCHAR classes_rootW[] =
350 {'\\','R','e','g','i','s','t','r','y','\\','M','a','c','h','i','n','e',
351 '\\','S','o','f','t','w','a','r','e','\\','C','l','a','s','s','e','s',0};
352 #endif
353
354 static HKEY classes_root_hkey;
355
356 /* create the special HKEY_CLASSES_ROOT key */
357 static HKEY create_classes_root_hkey(DWORD access)
358 {
359 HKEY hkey, ret = 0;
360 OBJECT_ATTRIBUTES attr;
361 UNICODE_STRING name;
362
363 attr.Length = sizeof(attr);
364 attr.RootDirectory = 0;
365 attr.ObjectName = &name;
366 attr.Attributes = 0;
367 attr.SecurityDescriptor = NULL;
368 attr.SecurityQualityOfService = NULL;
369 RtlInitUnicodeString( &name, classes_rootW );
370 if (create_key( &hkey, access, &attr )) return 0;
371 TRACE( "%s -> %p\n", debugstr_w(attr.ObjectName->Buffer), hkey );
372
373 if (!(access & KEY_WOW64_64KEY))
374 {
375 if (!(ret = InterlockedCompareExchangePointer( (void **)&classes_root_hkey, hkey, 0 )))
376 ret = hkey;
377 else
378 NtClose( hkey ); /* somebody beat us to it */
379 }
380 else
381 ret = hkey;
382 return ret;
383 }
384
385 /* map the hkey from special root to normal key if necessary */
386 static inline HKEY get_classes_root_hkey( HKEY hkey, REGSAM access )
387 {
388 HKEY ret = hkey;
389 const BOOL is_win64 = sizeof(void*) > sizeof(int);
390 const BOOL force_wow32 = is_win64 && (access & KEY_WOW64_32KEY);
391
392 if (hkey == HKEY_CLASSES_ROOT &&
393 ((access & KEY_WOW64_64KEY) || !(ret = classes_root_hkey)))
394 ret = create_classes_root_hkey(MAXIMUM_ALLOWED | (access & KEY_WOW64_64KEY));
395 if (force_wow32 && ret && ret == classes_root_hkey)
396 {
397 static const WCHAR wow6432nodeW[] = {'W','o','w','6','4','3','2','N','o','d','e',0};
398 access &= ~KEY_WOW64_32KEY;
399 if (create_classes_key(classes_root_hkey, wow6432nodeW, access, &hkey))
400 return 0;
401 ret = hkey;
402 }
403
404 return ret;
405 }
406
407 LSTATUS create_classes_key( HKEY hkey, const WCHAR *name, REGSAM access, HKEY *retkey )
408 {
409 OBJECT_ATTRIBUTES attr;
410 UNICODE_STRING nameW;
411
412 if (!(hkey = get_classes_root_hkey( hkey, access ))) return ERROR_INVALID_HANDLE;
413
414 attr.Length = sizeof(attr);
415 attr.RootDirectory = hkey;
416 attr.ObjectName = &nameW;
417 attr.Attributes = 0;
418 attr.SecurityDescriptor = NULL;
419 attr.SecurityQualityOfService = NULL;
420 RtlInitUnicodeString( &nameW, name );
421
422 return RtlNtStatusToDosError( create_key( retkey, access, &attr ) );
423 }
424
425 LSTATUS open_classes_key( HKEY hkey, const WCHAR *name, REGSAM access, HKEY *retkey )
426 {
427 OBJECT_ATTRIBUTES attr;
428 UNICODE_STRING nameW;
429
430 if (!(hkey = get_classes_root_hkey( hkey, access ))) return ERROR_INVALID_HANDLE;
431
432 attr.Length = sizeof(attr);
433 attr.RootDirectory = hkey;
434 attr.ObjectName = &nameW;
435 attr.Attributes = 0;
436 attr.SecurityDescriptor = NULL;
437 attr.SecurityQualityOfService = NULL;
438 RtlInitUnicodeString( &nameW, name );
439
440 return RtlNtStatusToDosError( NtOpenKey( (HANDLE *)retkey, access, &attr ) );
441 }
442
443 /*****************************************************************************
444 * This section contains OpenDllList definitions
445 *
446 * The OpenDllList contains only handles of dll loaded by CoGetClassObject or
447 * other functions that do LoadLibrary _without_ giving back a HMODULE.
448 * Without this list these handles would never be freed.
449 *
450 * FIXME: a DLL that says OK when asked for unloading is unloaded in the
451 * next unload-call but not before 600 sec.
452 */
453
454 typedef HRESULT (CALLBACK *DllGetClassObjectFunc)(REFCLSID clsid, REFIID iid, LPVOID *ppv);
455 typedef HRESULT (WINAPI *DllCanUnloadNowFunc)(void);
456
457 typedef struct tagOpenDll
458 {
459 LONG refs;
460 LPWSTR library_name;
461 HANDLE library;
462 DllGetClassObjectFunc DllGetClassObject;
463 DllCanUnloadNowFunc DllCanUnloadNow;
464 struct list entry;
465 } OpenDll;
466
467 static struct list openDllList = LIST_INIT(openDllList);
468
469 static CRITICAL_SECTION csOpenDllList;
470 static CRITICAL_SECTION_DEBUG dll_cs_debug =
471 {
472 0, 0, &csOpenDllList,
473 { &dll_cs_debug.ProcessLocksList, &dll_cs_debug.ProcessLocksList },
474 0, 0, { (DWORD_PTR)(__FILE__ ": csOpenDllList") }
475 };
476 static CRITICAL_SECTION csOpenDllList = { &dll_cs_debug, -1, 0, 0, 0, 0 };
477
478 struct apartment_loaded_dll
479 {
480 struct list entry;
481 OpenDll *dll;
482 DWORD unload_time;
483 BOOL multi_threaded;
484 };
485
486 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',0};
487
488 /*****************************************************************************
489 * This section contains OpenDllList implementation
490 */
491
492 static OpenDll *COMPOBJ_DllList_Get(LPCWSTR library_name)
493 {
494 OpenDll *ptr;
495 OpenDll *ret = NULL;
496 EnterCriticalSection(&csOpenDllList);
497 LIST_FOR_EACH_ENTRY(ptr, &openDllList, OpenDll, entry)
498 {
499 if (!strcmpiW(library_name, ptr->library_name) &&
500 (InterlockedIncrement(&ptr->refs) != 1) /* entry is being destroy if == 1 */)
501 {
502 ret = ptr;
503 break;
504 }
505 }
506 LeaveCriticalSection(&csOpenDllList);
507 return ret;
508 }
509
510 /* caller must ensure that library_name is not already in the open dll list */
511 static HRESULT COMPOBJ_DllList_Add(LPCWSTR library_name, OpenDll **ret)
512 {
513 OpenDll *entry;
514 int len;
515 HRESULT hr = S_OK;
516 HANDLE hLibrary;
517 DllCanUnloadNowFunc DllCanUnloadNow;
518 DllGetClassObjectFunc DllGetClassObject;
519
520 TRACE("%s\n", debugstr_w(library_name));
521
522 *ret = COMPOBJ_DllList_Get(library_name);
523 if (*ret) return S_OK;
524
525 /* do this outside the csOpenDllList to avoid creating a lock dependency on
526 * the loader lock */
527 hLibrary = LoadLibraryExW(library_name, 0, LOAD_WITH_ALTERED_SEARCH_PATH);
528 if (!hLibrary)
529 {
530 ERR("couldn't load in-process dll %s\n", debugstr_w(library_name));
531 /* failure: DLL could not be loaded */
532 return E_ACCESSDENIED; /* FIXME: or should this be CO_E_DLLNOTFOUND? */
533 }
534
535 DllCanUnloadNow = (void *)GetProcAddress(hLibrary, "DllCanUnloadNow");
536 /* Note: failing to find DllCanUnloadNow is not a failure */
537 DllGetClassObject = (void *)GetProcAddress(hLibrary, "DllGetClassObject");
538 if (!DllGetClassObject)
539 {
540 /* failure: the dll did not export DllGetClassObject */
541 ERR("couldn't find function DllGetClassObject in %s\n", debugstr_w(library_name));
542 FreeLibrary(hLibrary);
543 return CO_E_DLLNOTFOUND;
544 }
545
546 EnterCriticalSection( &csOpenDllList );
547
548 *ret = COMPOBJ_DllList_Get(library_name);
549 if (*ret)
550 {
551 /* another caller to this function already added the dll while we
552 * weren't in the critical section */
553 FreeLibrary(hLibrary);
554 }
555 else
556 {
557 len = strlenW(library_name);
558 entry = HeapAlloc(GetProcessHeap(),0, sizeof(OpenDll));
559 if (entry)
560 entry->library_name = HeapAlloc(GetProcessHeap(), 0, (len + 1)*sizeof(WCHAR));
561 if (entry && entry->library_name)
562 {
563 memcpy(entry->library_name, library_name, (len + 1)*sizeof(WCHAR));
564 entry->library = hLibrary;
565 entry->refs = 1;
566 entry->DllCanUnloadNow = DllCanUnloadNow;
567 entry->DllGetClassObject = DllGetClassObject;
568 list_add_tail(&openDllList, &entry->entry);
569 *ret = entry;
570 }
571 else
572 {
573 HeapFree(GetProcessHeap(), 0, entry);
574 hr = E_OUTOFMEMORY;
575 FreeLibrary(hLibrary);
576 }
577 }
578
579 LeaveCriticalSection( &csOpenDllList );
580
581 return hr;
582 }
583
584 /* pass FALSE for free_entry to release a reference without destroying the
585 * entry if it reaches zero or TRUE otherwise */
586 static void COMPOBJ_DllList_ReleaseRef(OpenDll *entry, BOOL free_entry)
587 {
588 if (!InterlockedDecrement(&entry->refs) && free_entry)
589 {
590 EnterCriticalSection(&csOpenDllList);
591 list_remove(&entry->entry);
592 LeaveCriticalSection(&csOpenDllList);
593
594 TRACE("freeing %p\n", entry->library);
595 FreeLibrary(entry->library);
596
597 HeapFree(GetProcessHeap(), 0, entry->library_name);
598 HeapFree(GetProcessHeap(), 0, entry);
599 }
600 }
601
602 /* frees memory associated with active dll list */
603 static void COMPOBJ_DllList_Free(void)
604 {
605 OpenDll *entry, *cursor2;
606 EnterCriticalSection(&csOpenDllList);
607 LIST_FOR_EACH_ENTRY_SAFE(entry, cursor2, &openDllList, OpenDll, entry)
608 {
609 list_remove(&entry->entry);
610
611 HeapFree(GetProcessHeap(), 0, entry->library_name);
612 HeapFree(GetProcessHeap(), 0, entry);
613 }
614 LeaveCriticalSection(&csOpenDllList);
615 DeleteCriticalSection(&csOpenDllList);
616 }
617
618 /******************************************************************************
619 * Manage apartments.
620 */
621
622 static DWORD apartment_addref(struct apartment *apt)
623 {
624 DWORD refs = InterlockedIncrement(&apt->refs);
625 TRACE("%s: before = %d\n", wine_dbgstr_longlong(apt->oxid), refs - 1);
626 return refs;
627 }
628
629 /* allocates memory and fills in the necessary fields for a new apartment
630 * object. must be called inside apartment cs */
631 static APARTMENT *apartment_construct(DWORD model)
632 {
633 APARTMENT *apt;
634
635 TRACE("creating new apartment, model=%d\n", model);
636
637 apt = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*apt));
638 apt->tid = GetCurrentThreadId();
639
640 list_init(&apt->proxies);
641 list_init(&apt->stubmgrs);
642 list_init(&apt->loaded_dlls);
643 apt->ipidc = 0;
644 apt->refs = 1;
645 apt->remunk_exported = FALSE;
646 apt->oidc = 1;
647 InitializeCriticalSection(&apt->cs);
648 DEBUG_SET_CRITSEC_NAME(&apt->cs, "apartment");
649
650 apt->multi_threaded = !(model & COINIT_APARTMENTTHREADED);
651
652 if (apt->multi_threaded)
653 {
654 /* FIXME: should be randomly generated by in an RPC call to rpcss */
655 apt->oxid = ((OXID)GetCurrentProcessId() << 32) | 0xcafe;
656 }
657 else
658 {
659 /* FIXME: should be randomly generated by in an RPC call to rpcss */
660 apt->oxid = ((OXID)GetCurrentProcessId() << 32) | GetCurrentThreadId();
661 }
662
663 TRACE("Created apartment on OXID %s\n", wine_dbgstr_longlong(apt->oxid));
664
665 list_add_head(&apts, &apt->entry);
666
667 return apt;
668 }
669
670 /* gets and existing apartment if one exists or otherwise creates an apartment
671 * structure which stores OLE apartment-local information and stores a pointer
672 * to it in the thread-local storage */
673 static APARTMENT *apartment_get_or_create(DWORD model)
674 {
675 APARTMENT *apt = COM_CurrentApt();
676
677 if (!apt)
678 {
679 if (model & COINIT_APARTMENTTHREADED)
680 {
681 EnterCriticalSection(&csApartment);
682
683 apt = apartment_construct(model);
684 if (!MainApartment)
685 {
686 MainApartment = apt;
687 apt->main = TRUE;
688 TRACE("Created main-threaded apartment with OXID %s\n", wine_dbgstr_longlong(apt->oxid));
689 }
690
691 LeaveCriticalSection(&csApartment);
692
693 if (apt->main)
694 apartment_createwindowifneeded(apt);
695 }
696 else
697 {
698 EnterCriticalSection(&csApartment);
699
700 /* The multi-threaded apartment (MTA) contains zero or more threads interacting
701 * with free threaded (ie thread safe) COM objects. There is only ever one MTA
702 * in a process */
703 if (MTA)
704 {
705 TRACE("entering the multithreaded apartment %s\n", wine_dbgstr_longlong(MTA->oxid));
706 apartment_addref(MTA);
707 }
708 else
709 MTA = apartment_construct(model);
710
711 apt = MTA;
712
713 LeaveCriticalSection(&csApartment);
714 }
715 COM_CurrentInfo()->apt = apt;
716 }
717
718 return apt;
719 }
720
721 static inline BOOL apartment_is_model(const APARTMENT *apt, DWORD model)
722 {
723 return (apt->multi_threaded == !(model & COINIT_APARTMENTTHREADED));
724 }
725
726 /* gets the multi-threaded apartment if it exists. The caller must
727 * release the reference from the apartment as soon as the apartment pointer
728 * is no longer required. */
729 static APARTMENT *apartment_find_mta(void)
730 {
731 APARTMENT *apt;
732
733 EnterCriticalSection(&csApartment);
734
735 if ((apt = MTA))
736 apartment_addref(apt);
737
738 LeaveCriticalSection(&csApartment);
739
740 return apt;
741 }
742
743 /* Return the current apartment if it exists, or, failing that, the MTA. Caller
744 * must free the returned apartment in either case. */
745 APARTMENT *apartment_get_current_or_mta(void)
746 {
747 APARTMENT *apt = COM_CurrentApt();
748 if (apt)
749 {
750 apartment_addref(apt);
751 return apt;
752 }
753 return apartment_find_mta();
754 }
755
756 static void COM_RevokeRegisteredClassObject(RegisteredClass *curClass)
757 {
758 list_remove(&curClass->entry);
759
760 if (curClass->runContext & CLSCTX_LOCAL_SERVER)
761 RPC_StopLocalServer(curClass->RpcRegistration);
762
763 IUnknown_Release(curClass->classObject);
764 HeapFree(GetProcessHeap(), 0, curClass);
765 }
766
767 static void COM_RevokeAllClasses(const struct apartment *apt)
768 {
769 RegisteredClass *curClass, *cursor;
770
771 EnterCriticalSection( &csRegisteredClassList );
772
773 LIST_FOR_EACH_ENTRY_SAFE(curClass, cursor, &RegisteredClassList, RegisteredClass, entry)
774 {
775 if (curClass->apartment_id == apt->oxid)
776 COM_RevokeRegisteredClassObject(curClass);
777 }
778
779 LeaveCriticalSection( &csRegisteredClassList );
780 }
781
782 static void revoke_registered_psclsids(void)
783 {
784 struct registered_psclsid *psclsid, *psclsid2;
785
786 EnterCriticalSection( &cs_registered_psclsid_list );
787
788 LIST_FOR_EACH_ENTRY_SAFE(psclsid, psclsid2, &registered_psclsid_list, struct registered_psclsid, entry)
789 {
790 list_remove(&psclsid->entry);
791 HeapFree(GetProcessHeap(), 0, psclsid);
792 }
793
794 LeaveCriticalSection( &cs_registered_psclsid_list );
795 }
796
797 /******************************************************************************
798 * Implementation of the manual reset event object. (CLSID_ManualResetEvent)
799 */
800
801 typedef struct ManualResetEvent {
802 ISynchronize ISynchronize_iface;
803 ISynchronizeHandle ISynchronizeHandle_iface;
804 LONG ref;
805 HANDLE event;
806 } MREImpl;
807
808 static inline MREImpl *impl_from_ISynchronize(ISynchronize *iface)
809 {
810 return CONTAINING_RECORD(iface, MREImpl, ISynchronize_iface);
811 }
812
813 static HRESULT WINAPI ISynchronize_fnQueryInterface(ISynchronize *iface, REFIID riid, void **ppv)
814 {
815 MREImpl *This = impl_from_ISynchronize(iface);
816
817 TRACE("%p (%s, %p)\n", This, debugstr_guid(riid), ppv);
818
819 if(IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_ISynchronize)) {
820 *ppv = &This->ISynchronize_iface;
821 }else if(IsEqualGUID(riid, &IID_ISynchronizeHandle)) {
822 *ppv = &This->ISynchronizeHandle_iface;
823 }else {
824 ERR("Unknown interface %s requested.\n", debugstr_guid(riid));
825 *ppv = NULL;
826 return E_NOINTERFACE;
827 }
828
829 IUnknown_AddRef((IUnknown*)*ppv);
830 return S_OK;
831 }
832
833 static ULONG WINAPI ISynchronize_fnAddRef(ISynchronize *iface)
834 {
835 MREImpl *This = impl_from_ISynchronize(iface);
836 LONG ref = InterlockedIncrement(&This->ref);
837 TRACE("%p - ref %d\n", This, ref);
838
839 return ref;
840 }
841
842 static ULONG WINAPI ISynchronize_fnRelease(ISynchronize *iface)
843 {
844 MREImpl *This = impl_from_ISynchronize(iface);
845 LONG ref = InterlockedDecrement(&This->ref);
846 TRACE("%p - ref %d\n", This, ref);
847
848 if(!ref)
849 {
850 CloseHandle(This->event);
851 HeapFree(GetProcessHeap(), 0, This);
852 }
853
854 return ref;
855 }
856
857 static HRESULT WINAPI ISynchronize_fnWait(ISynchronize *iface, DWORD dwFlags, DWORD dwMilliseconds)
858 {
859 MREImpl *This = impl_from_ISynchronize(iface);
860 UINT index;
861 TRACE("%p (%08x, %08x)\n", This, dwFlags, dwMilliseconds);
862 return CoWaitForMultipleHandles(dwFlags, dwMilliseconds, 1, &This->event, &index);
863 }
864
865 static HRESULT WINAPI ISynchronize_fnSignal(ISynchronize *iface)
866 {
867 MREImpl *This = impl_from_ISynchronize(iface);
868 TRACE("%p\n", This);
869 SetEvent(This->event);
870 return S_OK;
871 }
872
873 static HRESULT WINAPI ISynchronize_fnReset(ISynchronize *iface)
874 {
875 MREImpl *This = impl_from_ISynchronize(iface);
876 TRACE("%p\n", This);
877 ResetEvent(This->event);
878 return S_OK;
879 }
880
881 static ISynchronizeVtbl vt_ISynchronize = {
882 ISynchronize_fnQueryInterface,
883 ISynchronize_fnAddRef,
884 ISynchronize_fnRelease,
885 ISynchronize_fnWait,
886 ISynchronize_fnSignal,
887 ISynchronize_fnReset
888 };
889
890 static inline MREImpl *impl_from_ISynchronizeHandle(ISynchronizeHandle *iface)
891 {
892 return CONTAINING_RECORD(iface, MREImpl, ISynchronizeHandle_iface);
893 }
894
895 static HRESULT WINAPI SynchronizeHandle_QueryInterface(ISynchronizeHandle *iface, REFIID riid, void **ppv)
896 {
897 MREImpl *This = impl_from_ISynchronizeHandle(iface);
898 return ISynchronize_QueryInterface(&This->ISynchronize_iface, riid, ppv);
899 }
900
901 static ULONG WINAPI SynchronizeHandle_AddRef(ISynchronizeHandle *iface)
902 {
903 MREImpl *This = impl_from_ISynchronizeHandle(iface);
904 return ISynchronize_AddRef(&This->ISynchronize_iface);
905 }
906
907 static ULONG WINAPI SynchronizeHandle_Release(ISynchronizeHandle *iface)
908 {
909 MREImpl *This = impl_from_ISynchronizeHandle(iface);
910 return ISynchronize_Release(&This->ISynchronize_iface);
911 }
912
913 static HRESULT WINAPI SynchronizeHandle_GetHandle(ISynchronizeHandle *iface, HANDLE *ph)
914 {
915 MREImpl *This = impl_from_ISynchronizeHandle(iface);
916
917 *ph = This->event;
918 return S_OK;
919 }
920
921 static const ISynchronizeHandleVtbl SynchronizeHandleVtbl = {
922 SynchronizeHandle_QueryInterface,
923 SynchronizeHandle_AddRef,
924 SynchronizeHandle_Release,
925 SynchronizeHandle_GetHandle
926 };
927
928 static HRESULT ManualResetEvent_Construct(IUnknown *punkouter, REFIID iid, void **ppv)
929 {
930 MREImpl *This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MREImpl));
931 HRESULT hr;
932
933 if(punkouter)
934 FIXME("Aggregation not implemented.\n");
935
936 This->ref = 1;
937 This->ISynchronize_iface.lpVtbl = &vt_ISynchronize;
938 This->ISynchronizeHandle_iface.lpVtbl = &SynchronizeHandleVtbl;
939 This->event = CreateEventW(NULL, TRUE, FALSE, NULL);
940
941 hr = ISynchronize_QueryInterface(&This->ISynchronize_iface, iid, ppv);
942 ISynchronize_Release(&This->ISynchronize_iface);
943 return hr;
944 }
945
946 static inline LocalServer *impl_from_IServiceProvider(IServiceProvider *iface)
947 {
948 return CONTAINING_RECORD(iface, LocalServer, IServiceProvider_iface);
949 }
950
951 static HRESULT WINAPI LocalServer_QueryInterface(IServiceProvider *iface, REFIID riid, void **ppv)
952 {
953 LocalServer *This = impl_from_IServiceProvider(iface);
954
955 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
956
957 if(IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_IServiceProvider)) {
958 *ppv = &This->IServiceProvider_iface;
959 }else {
960 *ppv = NULL;
961 return E_NOINTERFACE;
962 }
963
964 IUnknown_AddRef((IUnknown*)*ppv);
965 return S_OK;
966 }
967
968 static ULONG WINAPI LocalServer_AddRef(IServiceProvider *iface)
969 {
970 LocalServer *This = impl_from_IServiceProvider(iface);
971 LONG ref = InterlockedIncrement(&This->ref);
972
973 TRACE("(%p) ref=%d\n", This, ref);
974
975 return ref;
976 }
977
978 static ULONG WINAPI LocalServer_Release(IServiceProvider *iface)
979 {
980 LocalServer *This = impl_from_IServiceProvider(iface);
981 LONG ref = InterlockedDecrement(&This->ref);
982
983 TRACE("(%p) ref=%d\n", This, ref);
984
985 if(!ref) {
986 assert(!This->apt);
987 HeapFree(GetProcessHeap(), 0, This);
988 }
989
990 return ref;
991 }
992
993 static HRESULT WINAPI LocalServer_QueryService(IServiceProvider *iface, REFGUID guid, REFIID riid, void **ppv)
994 {
995 LocalServer *This = impl_from_IServiceProvider(iface);
996 APARTMENT *apt = COM_CurrentApt();
997 RegisteredClass *iter;
998 HRESULT hres = E_FAIL;
999
1000 TRACE("(%p)->(%s %s %p)\n", This, debugstr_guid(guid), debugstr_guid(riid), ppv);
1001
1002 if(!This->apt)
1003 return E_UNEXPECTED;
1004
1005 EnterCriticalSection(&csRegisteredClassList);
1006
1007 LIST_FOR_EACH_ENTRY(iter, &RegisteredClassList, RegisteredClass, entry) {
1008 if(iter->apartment_id == apt->oxid
1009 && (iter->runContext & CLSCTX_LOCAL_SERVER)
1010 && IsEqualGUID(&iter->classIdentifier, guid)) {
1011 hres = IUnknown_QueryInterface(iter->classObject, riid, ppv);
1012 break;
1013 }
1014 }
1015
1016 LeaveCriticalSection( &csRegisteredClassList );
1017
1018 return hres;
1019 }
1020
1021 static const IServiceProviderVtbl LocalServerVtbl = {
1022 LocalServer_QueryInterface,
1023 LocalServer_AddRef,
1024 LocalServer_Release,
1025 LocalServer_QueryService
1026 };
1027
1028 static HRESULT get_local_server_stream(APARTMENT *apt, IStream **ret)
1029 {
1030 HRESULT hres = S_OK;
1031
1032 EnterCriticalSection(&apt->cs);
1033
1034 if(!apt->local_server) {
1035 LocalServer *obj;
1036
1037 obj = heap_alloc(sizeof(*obj));
1038 if(obj) {
1039 obj->IServiceProvider_iface.lpVtbl = &LocalServerVtbl;
1040 obj->ref = 1;
1041 obj->apt = apt;
1042
1043 hres = CreateStreamOnHGlobal(0, TRUE, &obj->marshal_stream);
1044 if(SUCCEEDED(hres)) {
1045 hres = CoMarshalInterface(obj->marshal_stream, &IID_IServiceProvider, (IUnknown*)&obj->IServiceProvider_iface,
1046 MSHCTX_LOCAL, NULL, MSHLFLAGS_TABLESTRONG);
1047 if(FAILED(hres))
1048 IStream_Release(obj->marshal_stream);
1049 }
1050
1051 if(SUCCEEDED(hres))
1052 apt->local_server = obj;
1053 else
1054 heap_free(obj);
1055 }else {
1056 hres = E_OUTOFMEMORY;
1057 }
1058 }
1059
1060 if(SUCCEEDED(hres))
1061 hres = IStream_Clone(apt->local_server->marshal_stream, ret);
1062
1063 LeaveCriticalSection(&apt->cs);
1064
1065 if(FAILED(hres))
1066 ERR("Failed: %08x\n", hres);
1067 return hres;
1068 }
1069
1070 /***********************************************************************
1071 * CoRevokeClassObject [OLE32.@]
1072 *
1073 * Removes a class object from the class registry.
1074 *
1075 * PARAMS
1076 * dwRegister [I] Cookie returned from CoRegisterClassObject().
1077 *
1078 * RETURNS
1079 * Success: S_OK.
1080 * Failure: HRESULT code.
1081 *
1082 * NOTES
1083 * Must be called from the same apartment that called CoRegisterClassObject(),
1084 * otherwise it will fail with RPC_E_WRONG_THREAD.
1085 *
1086 * SEE ALSO
1087 * CoRegisterClassObject
1088 */
1089 HRESULT WINAPI DECLSPEC_HOTPATCH CoRevokeClassObject(
1090 DWORD dwRegister)
1091 {
1092 HRESULT hr = E_INVALIDARG;
1093 RegisteredClass *curClass;
1094 APARTMENT *apt;
1095
1096 TRACE("(%08x)\n",dwRegister);
1097
1098 if (!(apt = apartment_get_current_or_mta()))
1099 {
1100 ERR("COM was not initialized\n");
1101 return CO_E_NOTINITIALIZED;
1102 }
1103
1104 EnterCriticalSection( &csRegisteredClassList );
1105
1106 LIST_FOR_EACH_ENTRY(curClass, &RegisteredClassList, RegisteredClass, entry)
1107 {
1108 /*
1109 * Check if we have a match on the cookie.
1110 */
1111 if (curClass->dwCookie == dwRegister)
1112 {
1113 if (curClass->apartment_id == apt->oxid)
1114 {
1115 COM_RevokeRegisteredClassObject(curClass);
1116 hr = S_OK;
1117 }
1118 else
1119 {
1120 ERR("called from wrong apartment, should be called from %s\n",
1121 wine_dbgstr_longlong(curClass->apartment_id));
1122 hr = RPC_E_WRONG_THREAD;
1123 }
1124 break;
1125 }
1126 }
1127
1128 LeaveCriticalSection( &csRegisteredClassList );
1129 apartment_release(apt);
1130 return hr;
1131 }
1132
1133 /* frees unused libraries loaded by apartment_getclassobject by calling the
1134 * DLL's DllCanUnloadNow entry point */
1135 static void apartment_freeunusedlibraries(struct apartment *apt, DWORD delay)
1136 {
1137 struct apartment_loaded_dll *entry, *next;
1138 EnterCriticalSection(&apt->cs);
1139 LIST_FOR_EACH_ENTRY_SAFE(entry, next, &apt->loaded_dlls, struct apartment_loaded_dll, entry)
1140 {
1141 if (entry->dll->DllCanUnloadNow && (entry->dll->DllCanUnloadNow() == S_OK))
1142 {
1143 DWORD real_delay = delay;
1144
1145 if (real_delay == INFINITE)
1146 {
1147 /* DLLs that return multi-threaded objects aren't unloaded
1148 * straight away to cope for programs that have races between
1149 * last object destruction and threads in the DLLs that haven't
1150 * finished, despite DllCanUnloadNow returning S_OK */
1151 if (entry->multi_threaded)
1152 real_delay = 10 * 60 * 1000; /* 10 minutes */
1153 else
1154 real_delay = 0;
1155 }
1156
1157 if (!real_delay || (entry->unload_time && ((int)(GetTickCount() - entry->unload_time) > 0)))
1158 {
1159 list_remove(&entry->entry);
1160 COMPOBJ_DllList_ReleaseRef(entry->dll, TRUE);
1161 HeapFree(GetProcessHeap(), 0, entry);
1162 }
1163 else
1164 {
1165 entry->unload_time = GetTickCount() + real_delay;
1166 if (!entry->unload_time) entry->unload_time = 1;
1167 }
1168 }
1169 else if (entry->unload_time)
1170 entry->unload_time = 0;
1171 }
1172 LeaveCriticalSection(&apt->cs);
1173 }
1174
1175 DWORD apartment_release(struct apartment *apt)
1176 {
1177 DWORD ret;
1178
1179 EnterCriticalSection(&csApartment);
1180
1181 ret = InterlockedDecrement(&apt->refs);
1182 TRACE("%s: after = %d\n", wine_dbgstr_longlong(apt->oxid), ret);
1183
1184 if (apt->being_destroyed)
1185 {
1186 LeaveCriticalSection(&csApartment);
1187 return ret;
1188 }
1189
1190 /* destruction stuff that needs to happen under csApartment CS */
1191 if (ret == 0)
1192 {
1193 apt->being_destroyed = TRUE;
1194 if (apt == MTA) MTA = NULL;
1195 else if (apt == MainApartment) MainApartment = NULL;
1196 list_remove(&apt->entry);
1197 }
1198
1199 LeaveCriticalSection(&csApartment);
1200
1201 if (ret == 0)
1202 {
1203 struct list *cursor, *cursor2;
1204
1205 TRACE("destroying apartment %p, oxid %s\n", apt, wine_dbgstr_longlong(apt->oxid));
1206
1207 if(apt->local_server) {
1208 LocalServer *local_server = apt->local_server;
1209 LARGE_INTEGER zero;
1210
1211 memset(&zero, 0, sizeof(zero));
1212 IStream_Seek(local_server->marshal_stream, zero, STREAM_SEEK_SET, NULL);
1213 CoReleaseMarshalData(local_server->marshal_stream);
1214 IStream_Release(local_server->marshal_stream);
1215 local_server->marshal_stream = NULL;
1216
1217 apt->local_server = NULL;
1218 local_server->apt = NULL;
1219 IServiceProvider_Release(&local_server->IServiceProvider_iface);
1220 }
1221
1222 /* Release the references to the registered class objects */
1223 COM_RevokeAllClasses(apt);
1224
1225 /* no locking is needed for this apartment, because no other thread
1226 * can access it at this point */
1227
1228 apartment_disconnectproxies(apt);
1229
1230 if (apt->win) DestroyWindow(apt->win);
1231 if (apt->host_apt_tid) PostThreadMessageW(apt->host_apt_tid, WM_QUIT, 0, 0);
1232
1233 LIST_FOR_EACH_SAFE(cursor, cursor2, &apt->stubmgrs)
1234 {
1235 struct stub_manager *stubmgr = LIST_ENTRY(cursor, struct stub_manager, entry);
1236 /* release the implicit reference given by the fact that the
1237 * stub has external references (it must do since it is in the
1238 * stub manager list in the apartment and all non-apartment users
1239 * must have a ref on the apartment and so it cannot be destroyed).
1240 */
1241 stub_manager_int_release(stubmgr);
1242 }
1243
1244 /* if this assert fires, then another thread took a reference to a
1245 * stub manager without taking a reference to the containing
1246 * apartment, which it must do. */
1247 assert(list_empty(&apt->stubmgrs));
1248
1249 if (apt->filter) IMessageFilter_Release(apt->filter);
1250
1251 /* free as many unused libraries as possible... */
1252 apartment_freeunusedlibraries(apt, 0);
1253
1254 /* ... and free the memory for the apartment loaded dll entry and
1255 * release the dll list reference without freeing the library for the
1256 * rest */
1257 while ((cursor = list_head(&apt->loaded_dlls)))
1258 {
1259 struct apartment_loaded_dll *apartment_loaded_dll = LIST_ENTRY(cursor, struct apartment_loaded_dll, entry);
1260 COMPOBJ_DllList_ReleaseRef(apartment_loaded_dll->dll, FALSE);
1261 list_remove(cursor);
1262 HeapFree(GetProcessHeap(), 0, apartment_loaded_dll);
1263 }
1264
1265 DEBUG_CLEAR_CRITSEC_NAME(&apt->cs);
1266 DeleteCriticalSection(&apt->cs);
1267
1268 HeapFree(GetProcessHeap(), 0, apt);
1269 }
1270
1271 return ret;
1272 }
1273
1274 /* The given OXID must be local to this process:
1275 *
1276 * The ref parameter is here mostly to ensure people remember that
1277 * they get one, you should normally take a ref for thread safety.
1278 */
1279 APARTMENT *apartment_findfromoxid(OXID oxid, BOOL ref)
1280 {
1281 APARTMENT *result = NULL;
1282 struct list *cursor;
1283
1284 EnterCriticalSection(&csApartment);
1285 LIST_FOR_EACH( cursor, &apts )
1286 {
1287 struct apartment *apt = LIST_ENTRY( cursor, struct apartment, entry );
1288 if (apt->oxid == oxid)
1289 {
1290 result = apt;
1291 if (ref) apartment_addref(result);
1292 break;
1293 }
1294 }
1295 LeaveCriticalSection(&csApartment);
1296
1297 return result;
1298 }
1299
1300 /* gets the apartment which has a given creator thread ID. The caller must
1301 * release the reference from the apartment as soon as the apartment pointer
1302 * is no longer required. */
1303 APARTMENT *apartment_findfromtid(DWORD tid)
1304 {
1305 APARTMENT *result = NULL;
1306 struct list *cursor;
1307
1308 EnterCriticalSection(&csApartment);
1309 LIST_FOR_EACH( cursor, &apts )
1310 {
1311 struct apartment *apt = LIST_ENTRY( cursor, struct apartment, entry );
1312 if (apt->tid == tid)
1313 {
1314 result = apt;
1315 apartment_addref(result);
1316 break;
1317 }
1318 }
1319 LeaveCriticalSection(&csApartment);
1320
1321 return result;
1322 }
1323
1324 /* gets the main apartment if it exists. The caller must
1325 * release the reference from the apartment as soon as the apartment pointer
1326 * is no longer required. */
1327 static APARTMENT *apartment_findmain(void)
1328 {
1329 APARTMENT *result;
1330
1331 EnterCriticalSection(&csApartment);
1332
1333 result = MainApartment;
1334 if (result) apartment_addref(result);
1335
1336 LeaveCriticalSection(&csApartment);
1337
1338 return result;
1339 }
1340
1341 /* gets the specified class object by loading the appropriate DLL, if
1342 * necessary and calls the DllGetClassObject function for the DLL */
1343 static HRESULT apartment_getclassobject(struct apartment *apt, LPCWSTR dllpath,
1344 BOOL apartment_threaded,
1345 REFCLSID rclsid, REFIID riid, void **ppv)
1346 {
1347 static const WCHAR wszOle32[] = {'o','l','e','3','2','.','d','l','l',0};
1348 HRESULT hr = S_OK;
1349 BOOL found = FALSE;
1350 struct apartment_loaded_dll *apartment_loaded_dll;
1351
1352 if (!strcmpiW(dllpath, wszOle32))
1353 {
1354 /* we don't need to control the lifetime of this dll, so use the local
1355 * implementation of DllGetClassObject directly */
1356 TRACE("calling ole32!DllGetClassObject\n");
1357 hr = DllGetClassObject(rclsid, riid, ppv);
1358
1359 if (hr != S_OK)
1360 ERR("DllGetClassObject returned error 0x%08x for dll %s\n", hr, debugstr_w(dllpath));
1361
1362 return hr;
1363 }
1364
1365 EnterCriticalSection(&apt->cs);
1366
1367 LIST_FOR_EACH_ENTRY(apartment_loaded_dll, &apt->loaded_dlls, struct apartment_loaded_dll, entry)
1368 if (!strcmpiW(dllpath, apartment_loaded_dll->dll->library_name))
1369 {
1370 TRACE("found %s already loaded\n", debugstr_w(dllpath));
1371 found = TRUE;
1372 break;
1373 }
1374
1375 if (!found)
1376 {
1377 apartment_loaded_dll = HeapAlloc(GetProcessHeap(), 0, sizeof(*apartment_loaded_dll));
1378 if (!apartment_loaded_dll)
1379 hr = E_OUTOFMEMORY;
1380 if (SUCCEEDED(hr))
1381 {
1382 apartment_loaded_dll->unload_time = 0;
1383 apartment_loaded_dll->multi_threaded = FALSE;
1384 hr = COMPOBJ_DllList_Add( dllpath, &apartment_loaded_dll->dll );
1385 if (FAILED(hr))
1386 HeapFree(GetProcessHeap(), 0, apartment_loaded_dll);
1387 }
1388 if (SUCCEEDED(hr))
1389 {
1390 TRACE("added new loaded dll %s\n", debugstr_w(dllpath));
1391 list_add_tail(&apt->loaded_dlls, &apartment_loaded_dll->entry);
1392 }
1393 }
1394
1395 LeaveCriticalSection(&apt->cs);
1396
1397 if (SUCCEEDED(hr))
1398 {
1399 /* one component being multi-threaded overrides any number of
1400 * apartment-threaded components */
1401 if (!apartment_threaded)
1402 apartment_loaded_dll->multi_threaded = TRUE;
1403
1404 TRACE("calling DllGetClassObject %p\n", apartment_loaded_dll->dll->DllGetClassObject);
1405 /* OK: get the ClassObject */
1406 hr = apartment_loaded_dll->dll->DllGetClassObject(rclsid, riid, ppv);
1407
1408 if (hr != S_OK)
1409 ERR("DllGetClassObject returned error 0x%08x for dll %s\n", hr, debugstr_w(dllpath));
1410 }
1411
1412 return hr;
1413 }
1414
1415 /***********************************************************************
1416 * COM_RegReadPath [internal]
1417 *
1418 * Reads a registry value and expands it when necessary
1419 */
1420 static DWORD COM_RegReadPath(const struct class_reg_data *regdata, WCHAR *dst, DWORD dstlen)
1421 {
1422 DWORD ret;
1423
1424 if (regdata->hkey)
1425 {
1426 DWORD keytype;
1427 WCHAR src[MAX_PATH];
1428 DWORD dwLength = dstlen * sizeof(WCHAR);
1429
1430 if( (ret = RegQueryValueExW(regdata->u.hkey, NULL, NULL, &keytype, (BYTE*)src, &dwLength)) == ERROR_SUCCESS ) {
1431 if (keytype == REG_EXPAND_SZ) {
1432 if (dstlen <= ExpandEnvironmentStringsW(src, dst, dstlen)) ret = ERROR_MORE_DATA;
1433 } else {
1434 const WCHAR *quote_start;
1435 quote_start = strchrW(src, '\"');
1436 if (quote_start) {
1437 const WCHAR *quote_end = strchrW(quote_start + 1, '\"');
1438 if (quote_end) {
1439 memmove(src, quote_start + 1,
1440 (quote_end - quote_start - 1) * sizeof(WCHAR));
1441 src[quote_end - quote_start - 1] = '\0';
1442 }
1443 }
1444 lstrcpynW(dst, src, dstlen);
1445 }
1446 }
1447 return ret;
1448 }
1449 else
1450 {
1451 ULONG_PTR cookie;
1452 WCHAR *nameW;
1453
1454 *dst = 0;
1455 nameW = (WCHAR*)((BYTE*)regdata->u.actctx.section + regdata->u.actctx.data->name_offset);
1456 ActivateActCtx(regdata->u.actctx.hactctx, &cookie);
1457 ret = SearchPathW(NULL, nameW, NULL, dstlen, dst, NULL);
1458 DeactivateActCtx(0, cookie);
1459 return !*dst;
1460 }
1461 }
1462
1463 struct host_object_params
1464 {
1465 struct class_reg_data regdata;
1466 CLSID clsid; /* clsid of object to marshal */
1467 IID iid; /* interface to marshal */
1468 HANDLE event; /* event signalling when ready for multi-threaded case */
1469 HRESULT hr; /* result for multi-threaded case */
1470 IStream *stream; /* stream that the object will be marshaled into */
1471 BOOL apartment_threaded; /* is the component purely apartment-threaded? */
1472 };
1473
1474 static HRESULT apartment_hostobject(struct apartment *apt,
1475 const struct host_object_params *params)
1476 {
1477 IUnknown *object;
1478 HRESULT hr;
1479 static const LARGE_INTEGER llZero;
1480 WCHAR dllpath[MAX_PATH+1];
1481
1482 TRACE("clsid %s, iid %s\n", debugstr_guid(&params->clsid), debugstr_guid(&params->iid));
1483
1484 if (COM_RegReadPath(&params->regdata, dllpath, ARRAYSIZE(dllpath)) != ERROR_SUCCESS)
1485 {
1486 /* failure: CLSID is not found in registry */
1487 WARN("class %s not registered inproc\n", debugstr_guid(&params->clsid));
1488 return REGDB_E_CLASSNOTREG;
1489 }
1490
1491 hr = apartment_getclassobject(apt, dllpath, params->apartment_threaded,
1492 &params->clsid, &params->iid, (void **)&object);
1493 if (FAILED(hr))
1494 return hr;
1495
1496 hr = CoMarshalInterface(params->stream, &params->iid, object, MSHCTX_INPROC, NULL, MSHLFLAGS_NORMAL);
1497 if (FAILED(hr))
1498 IUnknown_Release(object);
1499 IStream_Seek(params->stream, llZero, STREAM_SEEK_SET, NULL);
1500
1501 return hr;
1502 }
1503
1504 static LRESULT CALLBACK apartment_wndproc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1505 {
1506 switch (msg)
1507 {
1508 case DM_EXECUTERPC:
1509 RPC_ExecuteCall((struct dispatch_params *)lParam);
1510 return 0;
1511 case DM_HOSTOBJECT:
1512 return apartment_hostobject(COM_CurrentApt(), (const struct host_object_params *)lParam);
1513 default:
1514 return DefWindowProcW(hWnd, msg, wParam, lParam);
1515 }
1516 }
1517
1518 struct host_thread_params
1519 {
1520 COINIT threading_model;
1521 HANDLE ready_event;
1522 HWND apartment_hwnd;
1523 };
1524
1525 /* thread for hosting an object to allow an object to appear to be created in
1526 * an apartment with an incompatible threading model */
1527 static DWORD CALLBACK apartment_hostobject_thread(LPVOID p)
1528 {
1529 struct host_thread_params *params = p;
1530 MSG msg;
1531 HRESULT hr;
1532 struct apartment *apt;
1533
1534 TRACE("\n");
1535
1536 hr = CoInitializeEx(NULL, params->threading_model);
1537 if (FAILED(hr)) return hr;
1538
1539 apt = COM_CurrentApt();
1540 if (params->threading_model == COINIT_APARTMENTTHREADED)
1541 {
1542 apartment_createwindowifneeded(apt);
1543 params->apartment_hwnd = apartment_getwindow(apt);
1544 }
1545 else
1546 params->apartment_hwnd = NULL;
1547
1548 /* force the message queue to be created before signaling parent thread */
1549 PeekMessageW(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
1550
1551 SetEvent(params->ready_event);
1552 params = NULL; /* can't touch params after here as it may be invalid */
1553
1554 while (GetMessageW(&msg, NULL, 0, 0))
1555 {
1556 if (!msg.hwnd && (msg.message == DM_HOSTOBJECT))
1557 {
1558 struct host_object_params *obj_params = (struct host_object_params *)msg.lParam;
1559 obj_params->hr = apartment_hostobject(apt, obj_params);
1560 SetEvent(obj_params->event);
1561 }
1562 else
1563 {
1564 TranslateMessage(&msg);
1565 DispatchMessageW(&msg);
1566 }
1567 }
1568
1569 TRACE("exiting\n");
1570
1571 CoUninitialize();
1572
1573 return S_OK;
1574 }
1575
1576 /* finds or creates a host apartment, creates the object inside it and returns
1577 * a proxy to it so that the object can be used in the apartment of the
1578 * caller of this function */
1579 static HRESULT apartment_hostobject_in_hostapt(
1580 struct apartment *apt, BOOL multi_threaded, BOOL main_apartment,
1581 const struct class_reg_data *regdata, REFCLSID rclsid, REFIID riid, void **ppv)
1582 {
1583 struct host_object_params params;
1584 HWND apartment_hwnd = NULL;
1585 DWORD apartment_tid = 0;
1586 HRESULT hr;
1587
1588 if (!multi_threaded && main_apartment)
1589 {
1590 APARTMENT *host_apt = apartment_findmain();
1591 if (host_apt)
1592 {
1593 apartment_hwnd = apartment_getwindow(host_apt);
1594 apartment_release(host_apt);
1595 }
1596 }
1597
1598 if (!apartment_hwnd)
1599 {
1600 EnterCriticalSection(&apt->cs);
1601
1602 if (!apt->host_apt_tid)
1603 {
1604 struct host_thread_params thread_params;
1605 HANDLE handles[2];
1606 DWORD wait_value;
1607
1608 thread_params.threading_model = multi_threaded ? COINIT_MULTITHREADED : COINIT_APARTMENTTHREADED;
1609 handles[0] = thread_params.ready_event = CreateEventW(NULL, FALSE, FALSE, NULL);
1610 thread_params.apartment_hwnd = NULL;
1611 handles[1] = CreateThread(NULL, 0, apartment_hostobject_thread, &thread_params, 0, &apt->host_apt_tid);
1612 if (!handles[1])
1613 {
1614 CloseHandle(handles[0]);
1615 LeaveCriticalSection(&apt->cs);
1616 return E_OUTOFMEMORY;
1617 }
1618 wait_value = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
1619 CloseHandle(handles[0]);
1620 CloseHandle(handles[1]);
1621 if (wait_value == WAIT_OBJECT_0)
1622 apt->host_apt_hwnd = thread_params.apartment_hwnd;
1623 else
1624 {
1625 LeaveCriticalSection(&apt->cs);
1626 return E_OUTOFMEMORY;
1627 }
1628 }
1629
1630 if (multi_threaded || !main_apartment)
1631 {
1632 apartment_hwnd = apt->host_apt_hwnd;
1633 apartment_tid = apt->host_apt_tid;
1634 }
1635
1636 LeaveCriticalSection(&apt->cs);
1637 }
1638
1639 /* another thread may have become the main apartment in the time it took
1640 * us to create the thread for the host apartment */
1641 if (!apartment_hwnd && !multi_threaded && main_apartment)
1642 {
1643 APARTMENT *host_apt = apartment_findmain();
1644 if (host_apt)
1645 {
1646 apartment_hwnd = apartment_getwindow(host_apt);
1647 apartment_release(host_apt);
1648 }
1649 }
1650
1651 params.regdata = *regdata;
1652 params.clsid = *rclsid;
1653 params.iid = *riid;
1654 hr = CreateStreamOnHGlobal(NULL, TRUE, &params.stream);
1655 if (FAILED(hr))
1656 return hr;
1657 params.apartment_threaded = !multi_threaded;
1658 if (multi_threaded)
1659 {
1660 params.hr = S_OK;
1661 params.event = CreateEventW(NULL, FALSE, FALSE, NULL);
1662 if (!PostThreadMessageW(apartment_tid, DM_HOSTOBJECT, 0, (LPARAM)&params))
1663 hr = E_OUTOFMEMORY;
1664 else
1665 {
1666 WaitForSingleObject(params.event, INFINITE);
1667 hr = params.hr;
1668 }
1669 CloseHandle(params.event);
1670 }
1671 else
1672 {
1673 if (!apartment_hwnd)
1674 {
1675 ERR("host apartment didn't create window\n");
1676 hr = E_OUTOFMEMORY;
1677 }
1678 else
1679 hr = SendMessageW(apartment_hwnd, DM_HOSTOBJECT, 0, (LPARAM)&params);
1680 }
1681 if (SUCCEEDED(hr))
1682 hr = CoUnmarshalInterface(params.stream, riid, ppv);
1683 IStream_Release(params.stream);
1684 return hr;
1685 }
1686
1687 static BOOL WINAPI register_class( INIT_ONCE *once, void *param, void **context )
1688 {
1689 WNDCLASSW wclass;
1690
1691 /* Dispatching to the correct thread in an apartment is done through
1692 * window messages rather than RPC transports. When an interface is
1693 * marshalled into another apartment in the same process, a window of the
1694 * following class is created. The *caller* of CoMarshalInterface (i.e., the
1695 * application) is responsible for pumping the message loop in that thread.
1696 * The WM_USER messages which point to the RPCs are then dispatched to
1697 * apartment_wndproc by the user's code from the apartment in which the
1698 * interface was unmarshalled.
1699 */
1700 memset(&wclass, 0, sizeof(wclass));
1701 wclass.lpfnWndProc = apartment_wndproc;
1702 wclass.hInstance = hProxyDll;
1703 wclass.lpszClassName = wszAptWinClass;
1704 RegisterClassW(&wclass);
1705 return TRUE;
1706 }
1707
1708 /* create a window for the apartment or return the current one if one has
1709 * already been created */
1710 HRESULT apartment_createwindowifneeded(struct apartment *apt)
1711 {
1712 static INIT_ONCE class_init_once = INIT_ONCE_STATIC_INIT;
1713
1714 if (apt->multi_threaded)
1715 return S_OK;
1716
1717 if (!apt->win)
1718 {
1719 HWND hwnd;
1720
1721 InitOnceExecuteOnce( &class_init_once, register_class, NULL, NULL );
1722
1723 hwnd = CreateWindowW(wszAptWinClass, NULL, 0, 0, 0, 0, 0,
1724 HWND_MESSAGE, 0, hProxyDll, NULL);
1725 if (!hwnd)
1726 {
1727 ERR("CreateWindow failed with error %d\n", GetLastError());
1728 return HRESULT_FROM_WIN32(GetLastError());
1729 }
1730 if (InterlockedCompareExchangePointer((PVOID *)&apt->win, hwnd, NULL))
1731 /* someone beat us to it */
1732 DestroyWindow(hwnd);
1733 }
1734
1735 return S_OK;
1736 }
1737
1738 /* retrieves the window for the main- or apartment-threaded apartment */
1739 HWND apartment_getwindow(const struct apartment *apt)
1740 {
1741 assert(!apt->multi_threaded);
1742 return apt->win;
1743 }
1744
1745 static void COM_TlsDestroy(void)
1746 {
1747 struct oletls *info = NtCurrentTeb()->ReservedForOle;
1748 if (info)
1749 {
1750 if (info->apt) apartment_release(info->apt);
1751 if (info->errorinfo) IErrorInfo_Release(info->errorinfo);
1752 if (info->state) IUnknown_Release(info->state);
1753 if (info->spy) IInitializeSpy_Release(info->spy);
1754 if (info->context_token) IObjContext_Release(info->context_token);
1755 HeapFree(GetProcessHeap(), 0, info);
1756 NtCurrentTeb()->ReservedForOle = NULL;
1757 }
1758 }
1759
1760 /******************************************************************************
1761 * CoBuildVersion [OLE32.@]
1762 *
1763 * Gets the build version of the DLL.
1764 *
1765 * PARAMS
1766 *
1767 * RETURNS
1768 * Current build version, hiword is majornumber, loword is minornumber
1769 */
1770 DWORD WINAPI CoBuildVersion(void)
1771 {
1772 TRACE("Returning version %d, build %d.\n", rmm, rup);
1773 return (rmm<<16)+rup;
1774 }
1775
1776 /******************************************************************************
1777 * CoRegisterInitializeSpy [OLE32.@]
1778 *
1779 * Add a Spy that watches CoInitializeEx calls
1780 *
1781 * PARAMS
1782 * spy [I] Pointer to IUnknown interface that will be QueryInterface'd.
1783 * cookie [II] cookie receiver
1784 *
1785 * RETURNS
1786 * Success: S_OK if not already initialized, S_FALSE otherwise.
1787 * Failure: HRESULT code.
1788 *
1789 * SEE ALSO
1790 * CoInitializeEx
1791 */
1792 HRESULT WINAPI CoRegisterInitializeSpy(IInitializeSpy *spy, ULARGE_INTEGER *cookie)
1793 {
1794 struct oletls *info = COM_CurrentInfo();
1795 HRESULT hr;
1796
1797 TRACE("(%p, %p)\n", spy, cookie);
1798
1799 if (!spy || !cookie || !info)
1800 {
1801 if (!info)
1802 WARN("Could not allocate tls\n");
1803 return E_INVALIDARG;
1804 }
1805
1806 if (info->spy)
1807 {
1808 FIXME("Already registered?\n");
1809 return E_UNEXPECTED;
1810 }
1811
1812 hr = IInitializeSpy_QueryInterface(spy, &IID_IInitializeSpy, (void **) &info->spy);
1813 if (SUCCEEDED(hr))
1814 {
1815 cookie->QuadPart = (DWORD_PTR)spy;
1816 return S_OK;
1817 }
1818 return hr;
1819 }
1820
1821 /******************************************************************************
1822 * CoRevokeInitializeSpy [OLE32.@]
1823 *
1824 * Remove a spy that previously watched CoInitializeEx calls
1825 *
1826 * PARAMS
1827 * cookie [I] The cookie obtained from a previous CoRegisterInitializeSpy call
1828 *
1829 * RETURNS
1830 * Success: S_OK if a spy is removed
1831 * Failure: E_INVALIDARG
1832 *
1833 * SEE ALSO
1834 * CoInitializeEx
1835 */
1836 HRESULT WINAPI CoRevokeInitializeSpy(ULARGE_INTEGER cookie)
1837 {
1838 struct oletls *info = COM_CurrentInfo();
1839 TRACE("(%s)\n", wine_dbgstr_longlong(cookie.QuadPart));
1840
1841 if (!info || !info->spy || cookie.QuadPart != (DWORD_PTR)info->spy)
1842 return E_INVALIDARG;
1843
1844 IInitializeSpy_Release(info->spy);
1845 info->spy = NULL;
1846 return S_OK;
1847 }
1848
1849 HRESULT enter_apartment( struct oletls *info, DWORD model )
1850 {
1851 HRESULT hr = S_OK;
1852
1853 if (!info->apt)
1854 {
1855 if (!apartment_get_or_create( model ))
1856 return E_OUTOFMEMORY;
1857 }
1858 else if (!apartment_is_model( info->apt, model ))
1859 {
1860 WARN( "Attempt to change threading model of this apartment from %s to %s\n",
1861 info->apt->multi_threaded ? "multi-threaded" : "apartment threaded",
1862 model & COINIT_APARTMENTTHREADED ? "apartment threaded" : "multi-threaded" );
1863 return RPC_E_CHANGED_MODE;
1864 }
1865 else
1866 hr = S_FALSE;
1867
1868 info->inits++;
1869
1870 return hr;
1871 }
1872
1873 void leave_apartment( struct oletls *info )
1874 {
1875 if (!--info->inits)
1876 {
1877 if (info->ole_inits)
1878 WARN( "Uninitializing apartment while Ole is still initialized\n" );
1879 apartment_release( info->apt );
1880 info->apt = NULL;
1881 }
1882 }
1883
1884 /******************************************************************************
1885 * CoInitialize [OLE32.@]
1886 *
1887 * Initializes the COM libraries by calling CoInitializeEx with
1888 * COINIT_APARTMENTTHREADED, ie it enters a STA thread.
1889 *
1890 * PARAMS
1891 * lpReserved [I] Pointer to IMalloc interface (obsolete, should be NULL).
1892 *
1893 * RETURNS
1894 * Success: S_OK if not already initialized, S_FALSE otherwise.
1895 * Failure: HRESULT code.
1896 *
1897 * SEE ALSO
1898 * CoInitializeEx
1899 */
1900 HRESULT WINAPI CoInitialize(LPVOID lpReserved)
1901 {
1902 /*
1903 * Just delegate to the newer method.
1904 */
1905 return CoInitializeEx(lpReserved, COINIT_APARTMENTTHREADED);
1906 }
1907
1908 /******************************************************************************
1909 * CoInitializeEx [OLE32.@]
1910 *
1911 * Initializes the COM libraries.
1912 *
1913 * PARAMS
1914 * lpReserved [I] Pointer to IMalloc interface (obsolete, should be NULL).
1915 * dwCoInit [I] One or more flags from the COINIT enumeration. See notes.
1916 *
1917 * RETURNS
1918 * S_OK if successful,
1919 * S_FALSE if this function was called already.
1920 * RPC_E_CHANGED_MODE if a previous call to CoInitializeEx specified another
1921 * threading model.
1922 *
1923 * NOTES
1924 *
1925 * The behavior used to set the IMalloc used for memory management is
1926 * obsolete.
1927 * The dwCoInit parameter must specify one of the following apartment
1928 * threading models:
1929 *| COINIT_APARTMENTTHREADED - A single-threaded apartment (STA).
1930 *| COINIT_MULTITHREADED - A multi-threaded apartment (MTA).
1931 * The parameter may also specify zero or more of the following flags:
1932 *| COINIT_DISABLE_OLE1DDE - Don't use DDE for OLE1 support.
1933 *| COINIT_SPEED_OVER_MEMORY - Trade memory for speed.
1934 *
1935 * SEE ALSO
1936 * CoUninitialize
1937 */
1938 HRESULT WINAPI DECLSPEC_HOTPATCH CoInitializeEx(LPVOID lpReserved, DWORD dwCoInit)
1939 {
1940 struct oletls *info = COM_CurrentInfo();
1941 HRESULT hr;
1942
1943 TRACE("(%p, %x)\n", lpReserved, (int)dwCoInit);
1944
1945 if (lpReserved!=NULL)
1946 {
1947 ERR("(%p, %x) - Bad parameter passed-in %p, must be an old Windows Application\n", lpReserved, (int)dwCoInit, lpReserved);
1948 }
1949
1950 /*
1951 * Check the lock count. If this is the first time going through the initialize
1952 * process, we have to initialize the libraries.
1953 *
1954 * And crank-up that lock count.
1955 */
1956 if (InterlockedExchangeAdd(&s_COMLockCount,1)==0)
1957 {
1958 /*
1959 * Initialize the various COM libraries and data structures.
1960 */
1961 TRACE("() - Initializing the COM libraries\n");
1962
1963 /* we may need to defer this until after apartment initialisation */
1964 RunningObjectTableImpl_Initialize();
1965 }
1966
1967 if (info->spy)
1968 IInitializeSpy_PreInitialize(info->spy, dwCoInit, info->inits);
1969
1970 hr = enter_apartment( info, dwCoInit );
1971
1972 if (info->spy)
1973 IInitializeSpy_PostInitialize(info->spy, hr, dwCoInit, info->inits);
1974
1975 return hr;
1976 }
1977
1978 /***********************************************************************
1979 * CoUninitialize [OLE32.@]
1980 *
1981 * This method will decrement the refcount on the current apartment, freeing
1982 * the resources associated with it if it is the last thread in the apartment.
1983 * If the last apartment is freed, the function will additionally release
1984 * any COM resources associated with the process.
1985 *
1986 * PARAMS
1987 *
1988 * RETURNS
1989 * Nothing.
1990 *
1991 * SEE ALSO
1992 * CoInitializeEx
1993 */
1994 void WINAPI DECLSPEC_HOTPATCH CoUninitialize(void)
1995 {
1996 struct oletls * info = COM_CurrentInfo();
1997 LONG lCOMRefCnt;
1998
1999 TRACE("()\n");
2000
2001 /* will only happen on OOM */
2002 if (!info) return;
2003
2004 if (info->spy)
2005 IInitializeSpy_PreUninitialize(info->spy, info->inits);
2006
2007 /* sanity check */
2008 if (!info->inits)
2009 {
2010 ERR("Mismatched CoUninitialize\n");
2011
2012 if (info->spy)
2013 IInitializeSpy_PostUninitialize(info->spy, info->inits);
2014 return;
2015 }
2016
2017 leave_apartment( info );
2018
2019 /*
2020 * Decrease the reference count.
2021 * If we are back to 0 locks on the COM library, make sure we free
2022 * all the associated data structures.
2023 */
2024 lCOMRefCnt = InterlockedExchangeAdd(&s_COMLockCount,-1);
2025 if (lCOMRefCnt==1)
2026 {
2027 TRACE("() - Releasing the COM libraries\n");
2028
2029 revoke_registered_psclsids();
2030 RunningObjectTableImpl_UnInitialize();
2031 }
2032 else if (lCOMRefCnt<1) {
2033 ERR( "CoUninitialize() - not CoInitialized.\n" );
2034 InterlockedExchangeAdd(&s_COMLockCount,1); /* restore the lock count. */
2035 }
2036 if (info->spy)
2037 IInitializeSpy_PostUninitialize(info->spy, info->inits);
2038 }
2039
2040 /******************************************************************************
2041 * CoDisconnectObject [OLE32.@]
2042 *
2043 * Disconnects all connections to this object from remote processes. Dispatches
2044 * pending RPCs while blocking new RPCs from occurring, and then calls
2045 * IMarshal::DisconnectObject on the given object.
2046 *
2047 * Typically called when the object server is forced to shut down, for instance by
2048 * the user.
2049 *
2050 * PARAMS
2051 * lpUnk [I] The object whose stub should be disconnected.
2052 * reserved [I] Reserved. Should be set to 0.
2053 *
2054 * RETURNS
2055 * Success: S_OK.
2056 * Failure: HRESULT code.
2057 *
2058 * SEE ALSO
2059 * CoMarshalInterface, CoReleaseMarshalData, CoLockObjectExternal
2060 */
2061 HRESULT WINAPI CoDisconnectObject( LPUNKNOWN lpUnk, DWORD reserved )
2062 {
2063 struct stub_manager *manager;
2064 HRESULT hr;
2065 IMarshal *marshal;
2066 APARTMENT *apt;
2067
2068 TRACE("(%p, 0x%08x)\n", lpUnk, reserved);
2069
2070 if (!lpUnk) return E_INVALIDARG;
2071
2072 hr = IUnknown_QueryInterface(lpUnk, &IID_IMarshal, (void **)&marshal);
2073 if (hr == S_OK)
2074 {
2075 hr = IMarshal_DisconnectObject(marshal, reserved);
2076 IMarshal_Release(marshal);
2077 return hr;
2078 }
2079
2080 if (!(apt = apartment_get_current_or_mta()))
2081 {
2082 ERR("apartment not initialised\n");
2083 return CO_E_NOTINITIALIZED;
2084 }
2085
2086 manager = get_stub_manager_from_object(apt, lpUnk, FALSE);
2087 if (manager) {
2088 stub_manager_disconnect(manager);
2089 /* Release stub manager twice, to remove the apartment reference. */
2090 stub_manager_int_release(manager);
2091 stub_manager_int_release(manager);
2092 }
2093
2094 /* Note: native is pretty broken here because it just silently
2095 * fails, without returning an appropriate error code if the object was
2096 * not found, making apps think that the object was disconnected, when
2097 * it actually wasn't */
2098
2099 apartment_release(apt);
2100 return S_OK;
2101 }
2102
2103 /******************************************************************************
2104 * CoCreateGuid [OLE32.@]
2105 *
2106 * Simply forwards to UuidCreate in RPCRT4.
2107 *
2108 * PARAMS
2109 * pguid [O] Points to the GUID to initialize.
2110 *
2111 * RETURNS
2112 * Success: S_OK.
2113 * Failure: HRESULT code.
2114 *
2115 * SEE ALSO
2116 * UuidCreate
2117 */
2118 HRESULT WINAPI CoCreateGuid(GUID *pguid)
2119 {
2120 DWORD status;
2121
2122 if(!pguid) return E_INVALIDARG;
2123
2124 status = UuidCreate(pguid);
2125 if (status == RPC_S_OK || status == RPC_S_UUID_LOCAL_ONLY) return S_OK;
2126 return HRESULT_FROM_WIN32( status );
2127 }
2128
2129 static inline BOOL is_valid_hex(WCHAR c)
2130 {
2131 if (!(((c >= '0') && (c <= '9')) ||
2132 ((c >= 'a') && (c <= 'f')) ||
2133 ((c >= 'A') && (c <= 'F'))))
2134 return FALSE;
2135 return TRUE;
2136 }
2137
2138 static const BYTE guid_conv_table[256] =
2139 {
2140 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00 */
2141 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 */
2142 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20 */
2143 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, /* 0x30 */
2144 0, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x40 */
2145 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x50 */
2146 0, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf /* 0x60 */
2147 };
2148
2149 /* conversion helper for CLSIDFromString/IIDFromString */
2150 static BOOL guid_from_string(LPCWSTR s, GUID *id)
2151 {
2152 int i;
2153
2154 if (!s || s[0]!='{') {
2155 memset( id, 0, sizeof (CLSID) );
2156 if(!s) return TRUE;
2157 return FALSE;
2158 }
2159
2160 TRACE("%s -> %p\n", debugstr_w(s), id);
2161
2162 /* in form {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX} */
2163
2164 id->Data1 = 0;
2165 for (i = 1; i < 9; i++) {
2166 if (!is_valid_hex(s[i])) return FALSE;
2167 id->Data1 = (id->Data1 << 4) | guid_conv_table[s[i]];
2168 }
2169 if (s[9]!='-') return FALSE;
2170
2171 id->Data2 = 0;
2172 for (i = 10; i < 14; i++) {
2173 if (!is_valid_hex(s[i])) return FALSE;
2174 id->Data2 = (id->Data2 << 4) | guid_conv_table[s[i]];
2175 }
2176 if (s[14]!='-') return FALSE;
2177
2178 id->Data3 = 0;
2179 for (i = 15; i < 19; i++) {
2180 if (!is_valid_hex(s[i])) return FALSE;
2181 id->Data3 = (id->Data3 << 4) | guid_conv_table[s[i]];
2182 }
2183 if (s[19]!='-') return FALSE;
2184
2185 for (i = 20; i < 37; i+=2) {
2186 if (i == 24) {
2187 if (s[i]!='-') return FALSE;
2188 i++;
2189 }
2190 if (!is_valid_hex(s[i]) || !is_valid_hex(s[i+1])) return FALSE;
2191 id->Data4[(i-20)/2] = guid_conv_table[s[i]] << 4 | guid_conv_table[s[i+1]];
2192 }
2193
2194 if (s[37] == '}' && s[38] == '\0')
2195 return TRUE;
2196
2197 return FALSE;
2198 }
2199
2200 /*****************************************************************************/
2201
2202 static HRESULT clsid_from_string_reg(LPCOLESTR progid, CLSID *clsid)
2203 {
2204 static const WCHAR clsidW[] = { '\\','C','L','S','I','D',0 };
2205 WCHAR buf2[CHARS_IN_GUID];
2206 LONG buf2len = sizeof(buf2);
2207 HKEY xhkey;
2208 WCHAR *buf;
2209
2210 memset(clsid, 0, sizeof(*clsid));
2211 buf = HeapAlloc( GetProcessHeap(),0,(strlenW(progid)+8) * sizeof(WCHAR) );
2212 if (!buf) return E_OUTOFMEMORY;
2213 strcpyW( buf, progid );
2214 strcatW( buf, clsidW );
2215 if (open_classes_key(HKEY_CLASSES_ROOT, buf, MAXIMUM_ALLOWED, &xhkey))
2216 {
2217 HeapFree(GetProcessHeap(),0,buf);
2218 WARN("couldn't open key for ProgID %s\n", debugstr_w(progid));
2219 return CO_E_CLASSSTRING;
2220 }
2221 HeapFree(GetProcessHeap(),0,buf);
2222
2223 if (RegQueryValueW(xhkey,NULL,buf2,&buf2len))
2224 {
2225 RegCloseKey(xhkey);
2226 WARN("couldn't query clsid value for ProgID %s\n", debugstr_w(progid));
2227 return CO_E_CLASSSTRING;
2228 }
2229 RegCloseKey(xhkey);
2230 return guid_from_string(buf2, clsid) ? S_OK : CO_E_CLASSSTRING;
2231 }
2232
2233 /******************************************************************************
2234 * CLSIDFromString [OLE32.@]
2235 *
2236 * Converts a unique identifier from its string representation into
2237 * the GUID struct.
2238 *
2239 * PARAMS
2240 * idstr [I] The string representation of the GUID.
2241 * id [O] GUID converted from the string.
2242 *
2243 * RETURNS
2244 * S_OK on success
2245 * CO_E_CLASSSTRING if idstr is not a valid CLSID
2246 *
2247 * SEE ALSO
2248 * StringFromCLSID
2249 */
2250 HRESULT WINAPI CLSIDFromString(LPCOLESTR idstr, LPCLSID id )
2251 {
2252 HRESULT ret = CO_E_CLASSSTRING;
2253 CLSID tmp_id;
2254
2255 if (!id)
2256 return E_INVALIDARG;
2257
2258 if (guid_from_string(idstr, id))
2259 return S_OK;
2260
2261 /* It appears a ProgID is also valid */
2262 ret = clsid_from_string_reg(idstr, &tmp_id);
2263 if(SUCCEEDED(ret))
2264 *id = tmp_id;
2265
2266 return ret;
2267 }
2268
2269 /******************************************************************************
2270 * IIDFromString [OLE32.@]
2271 *
2272 * Converts an interface identifier from its string representation to
2273 * the IID struct.
2274 *
2275 * PARAMS
2276 * idstr [I] The string representation of the GUID.
2277 * id [O] IID converted from the string.
2278 *
2279 * RETURNS
2280 * S_OK on success
2281 * CO_E_IIDSTRING if idstr is not a valid IID
2282 *
2283 * SEE ALSO
2284 * StringFromIID
2285 */
2286 HRESULT WINAPI IIDFromString(LPCOLESTR s, IID *iid)
2287 {
2288 TRACE("%s -> %p\n", debugstr_w(s), iid);
2289
2290 if (!s)
2291 {
2292 memset(iid, 0, sizeof(*iid));
2293 return S_OK;
2294 }
2295
2296 /* length mismatch is a special case */
2297 if (strlenW(s) + 1 != CHARS_IN_GUID)
2298 return E_INVALIDARG;
2299
2300 if (s[0] != '{')
2301 return CO_E_IIDSTRING;
2302
2303 return guid_from_string(s, iid) ? S_OK : CO_E_IIDSTRING;
2304 }
2305
2306 /******************************************************************************
2307 * StringFromCLSID [OLE32.@]
2308 * StringFromIID [OLE32.@]
2309 *
2310 * Converts a GUID into the respective string representation.
2311 * The target string is allocated using the OLE IMalloc.
2312 *
2313 * PARAMS
2314 * id [I] the GUID to be converted.
2315 * idstr [O] A pointer to a to-be-allocated pointer pointing to the resulting string.
2316 *
2317 * RETURNS
2318 * S_OK
2319 * E_FAIL
2320 *
2321 * SEE ALSO
2322 * StringFromGUID2, CLSIDFromString
2323 */
2324 HRESULT WINAPI StringFromCLSID(REFCLSID id, LPOLESTR *idstr)
2325 {
2326 if (!(*idstr = CoTaskMemAlloc(CHARS_IN_GUID * sizeof(WCHAR)))) return E_OUTOFMEMORY;
2327 StringFromGUID2( id, *idstr, CHARS_IN_GUID );
2328 return S_OK;
2329 }
2330
2331 /******************************************************************************
2332 * StringFromGUID2 [OLE32.@]
2333 *
2334 * Modified version of StringFromCLSID that allows you to specify max
2335 * buffer size.
2336 *
2337 * PARAMS
2338 * id [I] GUID to convert to string.
2339 * str [O] Buffer where the result will be stored.
2340 * cmax [I] Size of the buffer in characters.
2341 *
2342 * RETURNS
2343 * Success: The length of the resulting string in characters.
2344 * Failure: 0.
2345 */
2346 INT WINAPI StringFromGUID2(REFGUID id, LPOLESTR str, INT cmax)
2347 {
2348 static const WCHAR formatW[] = { '{','%','0','8','X','-','%','0','4','X','-',
2349 '%','0','4','X','-','%','0','2','X','%','0','2','X','-',
2350 '%','0','2','X','%','0','2','X','%','0','2','X','%','0','2','X',
2351 '%','0','2','X','%','0','2','X','}',0 };
2352 if (!id || cmax < CHARS_IN_GUID) return 0;
2353 sprintfW( str, formatW, id->Data1, id->Data2, id->Data3,
2354 id->Data4[0], id->Data4[1], id->Data4[2], id->Data4[3],
2355 id->Data4[4], id->Data4[5], id->Data4[6], id->Data4[7] );
2356 return CHARS_IN_GUID;
2357 }
2358
2359 /* open HKCR\\CLSID\\{string form of clsid}\\{keyname} key */
2360 HRESULT COM_OpenKeyForCLSID(REFCLSID clsid, LPCWSTR keyname, REGSAM access, HKEY *subkey)
2361 {
2362 static const WCHAR wszCLSIDSlash[] = {'C','L','S','I','D','\\',0};
2363 WCHAR path[CHARS_IN_GUID + ARRAYSIZE(wszCLSIDSlash) - 1];
2364 LONG res;
2365 HKEY key;
2366
2367 strcpyW(path, wszCLSIDSlash);
2368 StringFromGUID2(clsid, path + strlenW(wszCLSIDSlash), CHARS_IN_GUID);
2369 res = open_classes_key(HKEY_CLASSES_ROOT, path, keyname ? KEY_READ : access, &key);
2370 if (res == ERROR_FILE_NOT_FOUND)
2371 return REGDB_E_CLASSNOTREG;
2372 else if (res != ERROR_SUCCESS)
2373 return REGDB_E_READREGDB;
2374
2375 if (!keyname)
2376 {
2377 *subkey = key;
2378 return S_OK;
2379 }
2380
2381 res = open_classes_key(key, keyname, access, subkey);
2382 RegCloseKey(key);
2383 if (res == ERROR_FILE_NOT_FOUND)
2384 return REGDB_E_KEYMISSING;
2385 else if (res != ERROR_SUCCESS)
2386 return REGDB_E_READREGDB;
2387
2388 return S_OK;
2389 }
2390
2391 /* open HKCR\\AppId\\{string form of appid clsid} key */
2392 HRESULT COM_OpenKeyForAppIdFromCLSID(REFCLSID clsid, REGSAM access, HKEY *subkey)
2393 {
2394 static const WCHAR szAppId[] = { 'A','p','p','I','d',0 };
2395 static const WCHAR szAppIdKey[] = { 'A','p','p','I','d','\\',0 };
2396 DWORD res;
2397 WCHAR buf[CHARS_IN_GUID];
2398 WCHAR keyname[ARRAYSIZE(szAppIdKey) + CHARS_IN_GUID];
2399 DWORD size;
2400 HKEY hkey;
2401 DWORD type;
2402 HRESULT hr;
2403
2404 /* read the AppID value under the class's key */
2405 hr = COM_OpenKeyForCLSID(clsid, NULL, KEY_READ, &hkey);
2406 if (FAILED(hr))
2407 return hr;
2408
2409 size = sizeof(buf);
2410 res = RegQueryValueExW(hkey, szAppId, NULL, &type, (LPBYTE)buf, &size);
2411 RegCloseKey(hkey);
2412 if (res == ERROR_FILE_NOT_FOUND)
2413 return REGDB_E_KEYMISSING;
2414 else if (res != ERROR_SUCCESS || type!=REG_SZ)
2415 return REGDB_E_READREGDB;
2416
2417 strcpyW(keyname, szAppIdKey);
2418 strcatW(keyname, buf);
2419 res = open_classes_key(HKEY_CLASSES_ROOT, keyname, access, subkey);
2420 if (res == ERROR_FILE_NOT_FOUND)
2421 return REGDB_E_KEYMISSING;
2422 else if (res != ERROR_SUCCESS)
2423 return REGDB_E_READREGDB;
2424
2425 return S_OK;
2426 }
2427
2428 /******************************************************************************
2429 * ProgIDFromCLSID [OLE32.@]
2430 *
2431 * Converts a class id into the respective program ID.
2432 *
2433 * PARAMS
2434 * clsid [I] Class ID, as found in registry.
2435 * ppszProgID [O] Associated ProgID.
2436 *
2437 * RETURNS
2438 * S_OK
2439 * E_OUTOFMEMORY
2440 * REGDB_E_CLASSNOTREG if the given clsid has no associated ProgID
2441 */
2442 HRESULT WINAPI DECLSPEC_HOTPATCH ProgIDFromCLSID(REFCLSID clsid, LPOLESTR *ppszProgID)
2443 {
2444 static const WCHAR wszProgID[] = {'P','r','o','g','I','D',0};
2445 ACTCTX_SECTION_KEYED_DATA data;
2446 HKEY hkey;
2447 HRESULT ret;
2448 LONG progidlen = 0;
2449
2450 if (!ppszProgID)
2451 return E_INVALIDARG;
2452
2453 *ppszProgID = NULL;
2454
2455 data.cbSize = sizeof(data);
2456 if (FindActCtxSectionGuid(0, NULL, ACTIVATION_CONTEXT_SECTION_COM_SERVER_REDIRECTION,
2457 clsid, &data))
2458 {
2459 struct comclassredirect_data *comclass = (struct comclassredirect_data*)data.lpData;
2460 if (comclass->progid_len)
2461 {
2462 WCHAR *ptrW;
2463
2464 *ppszProgID = CoTaskMemAlloc(comclass->progid_len + sizeof(WCHAR));
2465 if (!*ppszProgID) return E_OUTOFMEMORY;
2466
2467 ptrW = (WCHAR*)((BYTE*)comclass + comclass->progid_offset);
2468 memcpy(*ppszProgID, ptrW, comclass->progid_len + sizeof(WCHAR));
2469 return S_OK;
2470 }
2471 else
2472 return REGDB_E_CLASSNOTREG;
2473 }
2474
2475 ret = COM_OpenKeyForCLSID(clsid, wszProgID, KEY_READ, &hkey);
2476 if (FAILED(ret))
2477 return ret;
2478
2479 if (RegQueryValueW(hkey, NULL, NULL, &progidlen))
2480 ret = REGDB_E_CLASSNOTREG;
2481
2482 if (ret == S_OK)
2483 {
2484 *ppszProgID = CoTaskMemAlloc(progidlen * sizeof(WCHAR));
2485 if (*ppszProgID)
2486 {
2487 if (RegQueryValueW(hkey, NULL, *ppszProgID, &progidlen)) {
2488 ret = REGDB_E_CLASSNOTREG;
2489 CoTaskMemFree(*ppszProgID);
2490 *ppszProgID = NULL;
2491 }
2492 }
2493 else
2494 ret = E_OUTOFMEMORY;
2495 }
2496
2497 RegCloseKey(hkey);
2498 return ret;
2499 }
2500
2501 /******************************************************************************
2502 * CLSIDFromProgID [OLE32.@]
2503 *
2504 * Converts a program id into the respective GUID.
2505 *
2506 * PARAMS
2507 * progid [I] Unicode program ID, as found in registry.
2508 * clsid [O] Associated CLSID.
2509 *
2510 * RETURNS
2511 * Success: S_OK
2512 * Failure: CO_E_CLASSSTRING - the given ProgID cannot be found.
2513 */
2514 HRESULT WINAPI DECLSPEC_HOTPATCH CLSIDFromProgID(LPCOLESTR progid, LPCLSID clsid)
2515 {
2516 ACTCTX_SECTION_KEYED_DATA data;
2517
2518 if (!progid || !clsid)
2519 return E_INVALIDARG;
2520
2521 data.cbSize = sizeof(data);
2522 if (FindActCtxSectionStringW(0, NULL, ACTIVATION_CONTEXT_SECTION_COM_PROGID_REDIRECTION,
2523 progid, &data))
2524 {
2525 struct progidredirect_data *progiddata = (struct progidredirect_data*)data.lpData;
2526 CLSID *alias = (CLSID*)((BYTE*)data.lpSectionBase + progiddata->clsid_offset);
2527 *clsid = *alias;
2528 return S_OK;
2529 }
2530
2531 return clsid_from_string_reg(progid, clsid);
2532 }
2533
2534 /******************************************************************************
2535 * CLSIDFromProgIDEx [OLE32.@]
2536 */
2537 HRESULT WINAPI CLSIDFromProgIDEx(LPCOLESTR progid, LPCLSID clsid)
2538 {
2539 FIXME("%s,%p: semi-stub\n", debugstr_w(progid), clsid);
2540
2541 return CLSIDFromProgID(progid, clsid);
2542 }
2543
2544 static HRESULT get_ps_clsid_from_registry(const WCHAR* path, REGSAM access, CLSID *pclsid)
2545 {
2546 HKEY hkey;
2547 WCHAR value[CHARS_IN_GUID];
2548 DWORD len;
2549
2550 access |= KEY_READ;
2551
2552 if (open_classes_key(HKEY_CLASSES_ROOT, path, access, &hkey))
2553 return REGDB_E_IIDNOTREG;
2554
2555 len = sizeof(value);
2556 if (ERROR_SUCCESS != RegQueryValueExW(hkey, NULL, NULL, NULL, (BYTE *)value, &len))
2557 return REGDB_E_IIDNOTREG;
2558 RegCloseKey(hkey);
2559
2560 if (CLSIDFromString(value, pclsid) != NOERROR)
2561 return REGDB_E_IIDNOTREG;
2562
2563 return S_OK;
2564 }
2565
2566 /*****************************************************************************
2567 * CoGetPSClsid [OLE32.@]
2568 *
2569 * Retrieves the CLSID of the proxy/stub factory that implements
2570 * IPSFactoryBuffer for the specified interface.
2571 *
2572 * PARAMS
2573 * riid [I] Interface whose proxy/stub CLSID is to be returned.
2574 * pclsid [O] Where to store returned proxy/stub CLSID.
2575 *
2576 * RETURNS
2577 * S_OK
2578 * E_OUTOFMEMORY
2579 * REGDB_E_IIDNOTREG if no PSFactoryBuffer is associated with the IID, or it could not be parsed
2580 *
2581 * NOTES
2582 *
2583 * The standard marshaller activates the object with the CLSID
2584 * returned and uses the CreateProxy and CreateStub methods on its
2585 * IPSFactoryBuffer interface to construct the proxies and stubs for a
2586 * given object.
2587 *
2588 * CoGetPSClsid determines this CLSID by searching the
2589 * HKEY_CLASSES_ROOT\Interface\{string form of riid}\ProxyStubClsid32
2590 * in the registry and any interface id registered by
2591 * CoRegisterPSClsid within the current process.
2592 *
2593 * BUGS
2594 *
2595 * Native returns S_OK for interfaces with a key in HKCR\Interface, but
2596 * without a ProxyStubClsid32 key and leaves garbage in pclsid. This should be
2597 * considered a bug in native unless an application depends on this (unlikely).
2598 *
2599 * SEE ALSO
2600 * CoRegisterPSClsid.
2601 */
2602 HRESULT WINAPI CoGetPSClsid(REFIID riid, CLSID *pclsid)
2603 {
2604 static const WCHAR wszInterface[] = {'I','n','t','e','r','f','a','c','e','\\',0};
2605 static const WCHAR wszPSC[] = {'\\','P','r','o','x','y','S','t','u','b','C','l','s','i','d','3','2',0};
2606 WCHAR path[ARRAYSIZE(wszInterface) - 1 + CHARS_IN_GUID - 1 + ARRAYSIZE(wszPSC)];
2607 APARTMENT *apt;
2608 struct registered_psclsid *registered_psclsid;
2609 ACTCTX_SECTION_KEYED_DATA data;
2610 HRESULT hr;
2611 REGSAM opposite = (sizeof(void*) > sizeof(int)) ? KEY_WOW64_32KEY : KEY_WOW64_64KEY;
2612 BOOL is_wow64;
2613
2614 TRACE("() riid=%s, pclsid=%p\n", debugstr_guid(riid), pclsid);
2615
2616 if (!(apt = apartment_get_current_or_mta()))
2617 {
2618 ERR("apartment not initialised\n");
2619 return CO_E_NOTINITIALIZED;
2620 }
2621 apartment_release(apt);
2622
2623 if (!pclsid)
2624 return E_INVALIDARG;
2625
2626 EnterCriticalSection(&cs_registered_psclsid_list);
2627
2628 LIST_FOR_EACH_ENTRY(registered_psclsid, &registered_psclsid_list, struct registered_psclsid, entry)
2629 if (IsEqualIID(&registered_psclsid->iid, riid))
2630 {
2631 *pclsid = registered_psclsid->clsid;
2632 LeaveCriticalSection(&cs_registered_psclsid_list);
2633 return S_OK;
2634 }
2635
2636 LeaveCriticalSection(&cs_registered_psclsid_list);
2637
2638 data.cbSize = sizeof(data);
2639 if (FindActCtxSectionGuid(0, NULL, ACTIVATION_CONTEXT_SECTION_COM_INTERFACE_REDIRECTION,
2640 riid, &data))
2641 {
2642 struct ifacepsredirect_data *ifaceps = (struct ifacepsredirect_data*)data.lpData;
2643 *pclsid = ifaceps->iid;
2644 return S_OK;
2645 }
2646
2647 /* Interface\\{string form of riid}\\ProxyStubClsid32 */
2648 strcpyW(path, wszInterface);
2649 StringFromGUID2(riid, path + ARRAYSIZE(wszInterface) - 1, CHARS_IN_GUID);
2650 strcpyW(path + ARRAYSIZE(wszInterface) - 1 + CHARS_IN_GUID - 1, wszPSC);
2651
2652 hr = get_ps_clsid_from_registry(path, 0, pclsid);
2653 if (FAILED(hr) && (opposite == KEY_WOW64_32KEY ||
2654 (IsWow64Process(GetCurrentProcess(), &is_wow64) && is_wow64)))
2655 hr = get_ps_clsid_from_registry(path, opposite, pclsid);
2656
2657 if (hr == S_OK)
2658 TRACE ("() Returning CLSID=%s\n", debugstr_guid(pclsid));
2659 else
2660 WARN("No PSFactoryBuffer object is registered for IID %s\n", debugstr_guid(riid));
2661
2662 return hr;
2663 }
2664
2665 /*****************************************************************************
2666 * CoRegisterPSClsid [OLE32.@]
2667 *
2668 * Register a proxy/stub CLSID for the given interface in the current process
2669 * only.
2670 *
2671 * PARAMS
2672 * riid [I] Interface whose proxy/stub CLSID is to be registered.
2673 * rclsid [I] CLSID of the proxy/stub.
2674 *
2675 * RETURNS
2676 * Success: S_OK
2677 * Failure: E_OUTOFMEMORY
2678 *
2679 * NOTES
2680 *
2681 * Unlike CoRegisterClassObject(), CLSIDs registered with CoRegisterPSClsid()
2682 * will be returned from other apartments in the same process.
2683 *
2684 * This function does not add anything to the registry and the effects are
2685 * limited to the lifetime of the current process.
2686 *
2687 * SEE ALSO
2688 * CoGetPSClsid.
2689 */
2690 HRESULT WINAPI CoRegisterPSClsid(REFIID riid, REFCLSID rclsid)
2691 {
2692 APARTMENT *apt;
2693 struct registered_psclsid *registered_psclsid;
2694
2695 TRACE("(%s, %s)\n", debugstr_guid(riid), debugstr_guid(rclsid));
2696
2697 if (!(apt = apartment_get_current_or_mta()))
2698 {
2699 ERR("apartment not initialised\n");
2700 return CO_E_NOTINITIALIZED;
2701 }
2702 apartment_release(apt);
2703
2704 EnterCriticalSection(&cs_registered_psclsid_list);
2705
2706 LIST_FOR_EACH_ENTRY(registered_psclsid, &registered_psclsid_list, struct registered_psclsid, entry)
2707 if (IsEqualIID(&registered_psclsid->iid, riid))
2708 {
2709 registered_psclsid->clsid = *rclsid;
2710 LeaveCriticalSection(&cs_registered_psclsid_list);
2711 return S_OK;
2712 }
2713
2714 registered_psclsid = HeapAlloc(GetProcessHeap(), 0, sizeof(struct registered_psclsid));
2715 if (!registered_psclsid)
2716 {
2717 LeaveCriticalSection(&cs_registered_psclsid_list);
2718 return E_OUTOFMEMORY;
2719 }
2720
2721 registered_psclsid->iid = *riid;
2722 registered_psclsid->clsid = *rclsid;
2723 list_add_head(&registered_psclsid_list, &registered_psclsid->entry);
2724
2725 LeaveCriticalSection(&cs_registered_psclsid_list);
2726
2727 return S_OK;
2728 }
2729
2730
2731 /***
2732 * COM_GetRegisteredClassObject
2733 *
2734 * This internal method is used to scan the registered class list to
2735 * find a class object.
2736 *
2737 * Params:
2738 * rclsid Class ID of the class to find.
2739 * dwClsContext Class context to match.
2740 * ppv [out] returns a pointer to the class object. Complying
2741 * to normal COM usage, this method will increase the
2742 * reference count on this object.
2743 */
2744 static HRESULT COM_GetRegisteredClassObject(const struct apartment *apt, REFCLSID rclsid,
2745 DWORD dwClsContext, LPUNKNOWN* ppUnk)
2746 {
2747 HRESULT hr = S_FALSE;
2748 RegisteredClass *curClass;
2749
2750 EnterCriticalSection( &csRegisteredClassList );
2751
2752 LIST_FOR_EACH_ENTRY(curClass, &RegisteredClassList, RegisteredClass, entry)
2753 {
2754 /*
2755 * Check if we have a match on the class ID and context.
2756 */
2757 if ((apt->oxid == curClass->apartment_id) &&
2758 (dwClsContext & curClass->runContext) &&
2759 IsEqualGUID(&(curClass->classIdentifier), rclsid))
2760 {
2761 /*
2762 * We have a match, return the pointer to the class object.
2763 */
2764 *ppUnk = curClass->classObject;
2765
2766 IUnknown_AddRef(curClass->classObject);
2767
2768 hr = S_OK;
2769 break;
2770 }
2771 }
2772
2773 LeaveCriticalSection( &csRegisteredClassList );
2774
2775 return hr;
2776 }
2777
2778 /******************************************************************************
2779 * CoRegisterClassObject [OLE32.@]
2780 *
2781 * Registers the class object for a given class ID. Servers housed in EXE
2782 * files use this method instead of exporting DllGetClassObject to allow
2783 * other code to connect to their objects.
2784 *
2785 * PARAMS
2786 * rclsid [I] CLSID of the object to register.
2787 * pUnk [I] IUnknown of the object.
2788 * dwClsContext [I] CLSCTX flags indicating the context in which to run the executable.
2789 * flags [I] REGCLS flags indicating how connections are made.
2790 * lpdwRegister [I] A unique cookie that can be passed to CoRevokeClassObject.
2791 *
2792 * RETURNS
2793 * S_OK on success,
2794 * E_INVALIDARG if lpdwRegister or pUnk are NULL,
2795 * CO_E_OBJISREG if the object is already registered. We should not return this.
2796 *
2797 * SEE ALSO
2798 * CoRevokeClassObject, CoGetClassObject
2799 *
2800 * NOTES
2801 * In-process objects are only registered for the current apartment.
2802 * CoGetClassObject() and CoCreateInstance() will not return objects registered
2803 * in other apartments.
2804 *
2805 * BUGS
2806 * MSDN claims that multiple interface registrations are legal, but we
2807 * can't do that with our current implementation.
2808 */
2809 HRESULT WINAPI CoRegisterClassObject(
2810 REFCLSID rclsid,
2811 LPUNKNOWN pUnk,
2812 DWORD dwClsContext,
2813 DWORD flags,
2814 LPDWORD lpdwRegister)
2815 {
2816 static LONG next_cookie;
2817 RegisteredClass* newClass;
2818 LPUNKNOWN foundObject;
2819 HRESULT hr;
2820 APARTMENT *apt;
2821
2822 TRACE("(%s,%p,0x%08x,0x%08x,%p)\n",
2823 debugstr_guid(rclsid),pUnk,dwClsContext,flags,lpdwRegister);
2824
2825 if ( (lpdwRegister==0) || (pUnk==0) )
2826 return E_INVALIDARG;
2827
2828 if (!(apt = apartment_get_current_or_mta()))
2829 {
2830 ERR("COM was not initialized\n");
2831 return CO_E_NOTINITIALIZED;
2832 }
2833
2834 *lpdwRegister = 0;
2835
2836 /* REGCLS_MULTIPLEUSE implies registering as inproc server. This is what
2837 * differentiates the flag from REGCLS_MULTI_SEPARATE. */
2838 if (flags & REGCLS_MULTIPLEUSE)
2839 dwClsContext |= CLSCTX_INPROC_SERVER;
2840
2841 /*
2842 * First, check if the class is already registered.
2843 * If it is, this should cause an error.
2844 */
2845 hr = COM_GetRegisteredClassObject(apt, rclsid, dwClsContext, &foundObject);
2846 if (hr == S_OK) {
2847 if (flags & REGCLS_MULTIPLEUSE) {
2848 if (dwClsContext & CLSCTX_LOCAL_SERVER)
2849 hr = CoLockObjectExternal(foundObject, TRUE, FALSE);
2850 IUnknown_Release(foundObject);
2851 apartment_release(apt);
2852 return hr;
2853 }
2854 IUnknown_Release(foundObject);
2855 ERR("object already registered for class %s\n", debugstr_guid(rclsid));
2856 apartment_release(apt);
2857 return CO_E_OBJISREG;
2858 }
2859
2860 newClass = HeapAlloc(GetProcessHeap(), 0, sizeof(RegisteredClass));
2861 if ( newClass == NULL )
2862 {
2863 apartment_release(apt);
2864 return E_OUTOFMEMORY;
2865 }
2866
2867 newClass->classIdentifier = *rclsid;
2868 newClass->apartment_id = apt->oxid;
2869 newClass->runContext = dwClsContext;
2870 newClass->connectFlags = flags;
2871 newClass->RpcRegistration = NULL;
2872
2873 if (!(newClass->dwCookie = InterlockedIncrement( &next_cookie )))
2874 newClass->dwCookie = InterlockedIncrement( &next_cookie );
2875
2876 /*
2877 * Since we're making a copy of the object pointer, we have to increase its
2878 * reference count.
2879 */
2880 newClass->classObject = pUnk;
2881 IUnknown_AddRef(newClass->classObject);
2882
2883 EnterCriticalSection( &csRegisteredClassList );
2884 list_add_tail(&RegisteredClassList, &newClass->entry);
2885 LeaveCriticalSection( &csRegisteredClassList );
2886
2887 *lpdwRegister = newClass->dwCookie;
2888
2889 if (dwClsContext & CLSCTX_LOCAL_SERVER) {
2890 IStream *marshal_stream;
2891
2892 hr = get_local_server_stream(apt, &marshal_stream);
2893 if(FAILED(hr))
2894 {
2895 apartment_release(apt);
2896 return hr;
2897 }
2898
2899 hr = RPC_StartLocalServer(&newClass->classIdentifier,
2900 marshal_stream,
2901 flags & (REGCLS_MULTIPLEUSE|REGCLS_MULTI_SEPARATE),
2902 &newClass->RpcRegistration);
2903 IStream_Release(marshal_stream);
2904 }
2905 apartment_release(apt);
2906 return S_OK;
2907 }
2908
2909 static enum comclass_threadingmodel get_threading_model(const struct class_reg_data *data)
2910 {
2911 if (data->hkey)
2912 {
2913 static const WCHAR wszThreadingModel[] = {'T','h','r','e','a','d','i','n','g','M','o','d','e','l',0};
2914 static const WCHAR wszApartment[] = {'A','p','a','r','t','m','e','n','t',0};
2915 static const WCHAR wszFree[] = {'F','r','e','e',0};
2916 static const WCHAR wszBoth[] = {'B','o','t','h',0};
2917 WCHAR threading_model[10 /* strlenW(L"apartment")+1 */];
2918 DWORD dwLength = sizeof(threading_model);
2919 DWORD keytype;
2920 DWORD ret;
2921
2922 ret = RegQueryValueExW(data->u.hkey, wszThreadingModel, NULL, &keytype, (BYTE*)threading_model, &dwLength);
2923 if ((ret != ERROR_SUCCESS) || (keytype != REG_SZ))
2924 threading_model[0] = '\0';
2925
2926 if (!strcmpiW(threading_model, wszApartment)) return ThreadingModel_Apartment;
2927 if (!strcmpiW(threading_model, wszFree)) return ThreadingModel_Free;
2928 if (!strcmpiW(threading_model, wszBoth)) return ThreadingModel_Both;
2929
2930 /* there's not specific handling for this case */
2931 if (threading_model[0]) return ThreadingModel_Neutral;
2932 return ThreadingModel_No;
2933 }
2934 else
2935 return data->u.actctx.data->model;
2936 }
2937
2938 static HRESULT get_inproc_class_object(APARTMENT *apt, const struct class_reg_data *regdata,
2939 REFCLSID rclsid, REFIID riid,
2940 BOOL hostifnecessary, void **ppv)
2941 {
2942 WCHAR dllpath[MAX_PATH+1];
2943 BOOL apartment_threaded;
2944
2945 if (hostifnecessary)
2946 {
2947 enum comclass_threadingmodel model = get_threading_model(regdata);
2948
2949 if (model == ThreadingModel_Apartment)
2950 {
2951 apartment_threaded = TRUE;
2952 if (apt->multi_threaded)
2953 return apartment_hostobject_in_hostapt(apt, FALSE, FALSE, regdata, rclsid, riid, ppv);
2954 }
2955 else if (model == ThreadingModel_Free)
2956 {
2957 apartment_threaded = FALSE;
2958 if (!apt->multi_threaded)
2959 return apartment_hostobject_in_hostapt(apt, TRUE, FALSE, regdata, rclsid, riid, ppv);
2960 }
2961 /* everything except "Apartment", "Free" and "Both" */
2962 else if (model != ThreadingModel_Both)
2963 {
2964 apartment_threaded = TRUE;
2965 /* everything else is main-threaded */
2966 if (model != ThreadingModel_No)
2967 FIXME("unrecognised threading model %d for object %s, should be main-threaded?\n", model, debugstr_guid(rclsid));
2968
2969 if (apt->multi_threaded || !apt->main)
2970 return apartment_hostobject_in_hostapt(apt, FALSE, TRUE, regdata, rclsid, riid, ppv);
2971 }
2972 else
2973 apartment_threaded = FALSE;
2974 }
2975 else
2976 apartment_threaded = !apt->multi_threaded;
2977
2978 if (COM_RegReadPath(regdata, dllpath, ARRAYSIZE(dllpath)) != ERROR_SUCCESS)
2979 {
2980 /* failure: CLSID is not found in registry */
2981 WARN("class %s not registered inproc\n", debugstr_guid(rclsid));
2982 return REGDB_E_CLASSNOTREG;
2983 }
2984
2985 return apartment_getclassobject(apt, dllpath, apartment_threaded,
2986 rclsid, riid, ppv);
2987 }
2988
2989 /***********************************************************************
2990 * CoGetClassObject [OLE32.@]
2991 *
2992 * Creates an object of the specified class.
2993 *
2994 * PARAMS
2995 * rclsid [I] Class ID to create an instance of.
2996 * dwClsContext [I] Flags to restrict the location of the created instance.
2997 * pServerInfo [I] Optional. Details for connecting to a remote server.
2998 * iid [I] The ID of the interface of the instance to return.
2999 * ppv [O] On returns, contains a pointer to the specified interface of the object.
3000 *
3001 * RETURNS
3002 * Success: S_OK
3003 * Failure: HRESULT code.
3004 *
3005 * NOTES
3006 * The dwClsContext parameter can be one or more of the following:
3007 *| CLSCTX_INPROC_SERVER - Use an in-process server, such as from a DLL.
3008 *| CLSCTX_INPROC_HANDLER - Use an in-process object which handles certain functions for an object running in another process.
3009 *| CLSCTX_LOCAL_SERVER - Connect to an object running in another process.
3010 *| CLSCTX_REMOTE_SERVER - Connect to an object running on another machine.
3011 *
3012 * SEE ALSO
3013 * CoCreateInstance()
3014 */
3015 HRESULT WINAPI DECLSPEC_HOTPATCH CoGetClassObject(
3016 REFCLSID rclsid, DWORD dwClsContext, COSERVERINFO *pServerInfo,
3017 REFIID iid, LPVOID *ppv)
3018 {
3019 struct class_reg_data clsreg;
3020 IUnknown *regClassObject;
3021 HRESULT hres = E_UNEXPECTED;
3022 APARTMENT *apt;
3023
3024 TRACE("CLSID: %s,IID: %s\n", debugstr_guid(rclsid), debugstr_guid(iid));
3025
3026 if (!ppv)
3027 return E_INVALIDARG;
3028
3029 *ppv = NULL;
3030
3031 if (!(apt = apartment_get_current_or_mta()))
3032 {
3033 ERR("apartment not initialised\n");
3034 return CO_E_NOTINITIALIZED;
3035 }
3036
3037 if (pServerInfo) {
3038 FIXME("pServerInfo->name=%s pAuthInfo=%p\n",
3039 debugstr_w(pServerInfo->pwszName), pServerInfo->pAuthInfo);
3040 }
3041
3042 if (CLSCTX_INPROC_SERVER & dwClsContext)
3043 {
3044 if (IsEqualCLSID(rclsid, &CLSID_InProcFreeMarshaler))
3045 {
3046 apartment_release(apt);
3047 return FTMarshalCF_Create(iid, ppv);
3048 }
3049 if (IsEqualCLSID(rclsid, &CLSID_GlobalOptions))
3050 return IClassFactory_QueryInterface(&GlobalOptionsCF, iid, ppv);
3051 }
3052
3053 if (CLSCTX_INPROC & dwClsContext)
3054 {
3055 ACTCTX_SECTION_KEYED_DATA data;
3056
3057 data.cbSize = sizeof(data);
3058 /* search activation context first */
3059 if (FindActCtxSectionGuid(FIND_ACTCTX_SECTION_KEY_RETURN_HACTCTX, NULL,
3060 ACTIVATION_CONTEXT_SECTION_COM_SERVER_REDIRECTION,
3061 rclsid, &data))
3062 {
3063 struct comclassredirect_data *comclass = (struct comclassredirect_data*)data.lpData;
3064
3065 clsreg.u.actctx.hactctx = data.hActCtx;
3066 clsreg.u.actctx.data = data.lpData;
3067 clsreg.u.actctx.section = data.lpSectionBase;
3068 clsreg.hkey = FALSE;
3069
3070 hres = get_inproc_class_object(apt, &clsreg, &comclass->clsid, iid, !(dwClsContext & WINE_CLSCTX_DONT_HOST), ppv);
3071 ReleaseActCtx(data.hActCtx);
3072 apartment_release(apt);
3073 return hres;
3074 }
3075 }
3076
3077 /*
3078 * First, try and see if we can't match the class ID with one of the
3079 * registered classes.
3080 */
3081 if (S_OK == COM_GetRegisteredClassObject(apt, rclsid, dwClsContext,
3082 &regClassObject))
3083 {
3084 /* Get the required interface from the retrieved pointer. */
3085 hres = IUnknown_QueryInterface(regClassObject, iid, ppv);
3086
3087 /*
3088 * Since QI got another reference on the pointer, we want to release the
3089 * one we already have. If QI was unsuccessful, this will release the object. This
3090 * is good since we are not returning it in the "out" parameter.
3091 */
3092 IUnknown_Release(regClassObject);
3093 apartment_release(apt);
3094 return hres;
3095 }
3096
3097 /* First try in-process server */
3098 if (CLSCTX_INPROC_SERVER & dwClsContext)
3099 {
3100 static const WCHAR wszInprocServer32[] = {'I','n','p','r','o','c','S','e','r','v','e','r','3','2',0};
3101 HKEY hkey;
3102
3103 hres = COM_OpenKeyForCLSID(rclsid, wszInprocServer32, KEY_READ, &hkey);
3104 if (FAILED(hres))
3105 {
3106 if (hres == REGDB_E_CLASSNOTREG)
3107 ERR("class %s not registered\n", debugstr_guid(rclsid));
3108 else if (hres == REGDB_E_KEYMISSING)
3109 {
3110 WARN("class %s not registered as in-proc server\n", debugstr_guid(rclsid));
3111 hres = REGDB_E_CLASSNOTREG;
3112 }
3113 }
3114
3115 if (SUCCEEDED(hres))
3116 {
3117 clsreg.u.hkey = hkey;
3118 clsreg.hkey = TRUE;
3119
3120 hres = get_inproc_class_object(apt, &clsreg, rclsid, iid, !(dwClsContext & WINE_CLSCTX_DONT_HOST), ppv);
3121 RegCloseKey(hkey);
3122 }
3123
3124 /* return if we got a class, otherwise fall through to one of the
3125 * other types */
3126 if (SUCCEEDED(hres))
3127 {
3128 apartment_release(apt);
3129 return hres;
3130 }
3131 }
3132
3133 /* Next try in-process handler */
3134 if (CLSCTX_INPROC_HANDLER & dwClsContext)
3135 {
3136 static const WCHAR wszInprocHandler32[] = {'I','n','p','r','o','c','H','a','n','d','l','e','r','3','2',0};
3137 HKEY hkey;
3138
3139 hres = COM_OpenKeyForCLSID(rclsid, wszInprocHandler32, KEY_READ, &hkey);
3140 if (FAILED(hres))
3141 {
3142 if (hres == REGDB_E_CLASSNOTREG)
3143 ERR("class %s not registered\n", debugstr_guid(rclsid));
3144 else if (hres == REGDB_E_KEYMISSING)
3145 {
3146 WARN("class %s not registered in-proc handler\n", debugstr_guid(rclsid));
3147 hres = REGDB_E_CLASSNOTREG;
3148 }
3149 }
3150
3151 if (SUCCEEDED(hres))
3152 {
3153 clsreg.u.hkey = hkey;
3154 clsreg.hkey = TRUE;
3155
3156 hres = get_inproc_class_object(apt, &clsreg, rclsid, iid, !(dwClsContext & WINE_CLSCTX_DONT_HOST), ppv);
3157 RegCloseKey(hkey);
3158 }
3159
3160 /* return if we got a class, otherwise fall through to one of the
3161 * other types */
3162 if (SUCCEEDED(hres))
3163 {
3164 apartment_release(apt);
3165 return hres;
3166 }
3167 }
3168 apartment_release(apt);
3169
3170 /* Next try out of process */
3171 if (CLSCTX_LOCAL_SERVER & dwClsContext)
3172 {
3173 hres = RPC_GetLocalClassObject(rclsid,iid,ppv);
3174 if (SUCCEEDED(hres))
3175 return hres;
3176 }
3177
3178 /* Finally try remote: this requires networked DCOM (a lot of work) */
3179 if (CLSCTX_REMOTE_SERVER & dwClsContext)
3180 {
3181 FIXME ("CLSCTX_REMOTE_SERVER not supported\n");
3182 hres = REGDB_E_CLASSNOTREG;
3183 }
3184
3185 if (FAILED(hres))
3186 ERR("no class object %s could be created for context 0x%x\n",
3187 debugstr_guid(rclsid), dwClsContext);
3188 return hres;
3189 }
3190
3191 /***********************************************************************
3192 * CoResumeClassObjects (OLE32.@)
3193 *
3194 * Resumes all class objects registered with REGCLS_SUSPENDED.
3195 *
3196 * RETURNS
3197 * Success: S_OK.
3198 * Failure: HRESULT code.
3199 */
3200 HRESULT WINAPI CoResumeClassObjects(void)
3201 {
3202 FIXME("stub\n");
3203 return S_OK;
3204 }
3205
3206 /***********************************************************************
3207 * CoCreateInstance [OLE32.@]
3208 *
3209 * Creates an instance of the specified class.
3210 *
3211 * PARAMS
3212 * rclsid [I] Class ID to create an instance of.
3213 * pUnkOuter [I] Optional outer unknown to allow aggregation with another object.
3214 * dwClsContext [I] Flags to restrict the location of the created instance.
3215 * iid [I] The ID of the interface of the instance to return.
3216 * ppv [O] On returns, contains a pointer to the specified interface of the instance.
3217 *
3218 * RETURNS
3219 * Success: S_OK
3220 * Failure: HRESULT code.
3221 *
3222 * NOTES
3223 * The dwClsContext parameter can be one or more of the following:
3224 *| CLSCTX_INPROC_SERVER - Use an in-process server, such as from a DLL.
3225 *| CLSCTX_INPROC_HANDLER - Use an in-process object which handles certain functions for an object running in another process.
3226 *| CLSCTX_LOCAL_SERVER - Connect to an object running in another process.
3227 *| CLSCTX_REMOTE_SERVER - Connect to an object running on another machine.
3228 *
3229 * Aggregation is the concept of deferring the IUnknown of an object to another
3230 * object. This allows a separate object to behave as though it was part of
3231 * the object and to allow this the pUnkOuter parameter can be set. Note that
3232 * not all objects support having an outer of unknown.
3233 *
3234 * SEE ALSO
3235 * CoGetClassObject()
3236 */
3237 HRESULT WINAPI DECLSPEC_HOTPATCH CoCreateInstance(
3238 REFCLSID rclsid,
3239 LPUNKNOWN pUnkOuter,
3240 DWORD dwClsContext,
3241 REFIID iid,
3242 LPVOID *ppv)
3243 {
3244 MULTI_QI multi_qi = { iid };
3245 HRESULT hres;
3246
3247 TRACE("(rclsid=%s, pUnkOuter=%p, dwClsContext=%08x, riid=%s, ppv=%p)\n", debugstr_guid(rclsid),
3248 pUnkOuter, dwClsContext, debugstr_guid(iid), ppv);
3249
3250 if (ppv==0)
3251 return E_POINTER;
3252
3253 hres = CoCreateInstanceEx(rclsid, pUnkOuter, dwClsContext, NULL, 1, &multi_qi);
3254 *ppv = multi_qi.pItf;
3255 return hres;
3256 }
3257
3258 static void init_multi_qi(DWORD count, MULTI_QI *mqi, HRESULT hr)
3259 {
3260 ULONG i;
3261
3262 for (i = 0; i < count; i++)
3263 {
3264 mqi[i].pItf = NULL;
3265 mqi[i].hr = hr;
3266 }
3267 }
3268
3269 static HRESULT return_multi_qi(IUnknown *unk, DWORD count, MULTI_QI *mqi, BOOL include_unk)
3270 {
3271 ULONG index = 0, fetched = 0;
3272
3273 if (include_unk)
3274 {
3275 mqi[0].hr = S_OK;
3276 mqi[0].pItf = unk;
3277 index = fetched = 1;
3278 }
3279
3280 for (; index < count; index++)
3281 {
3282 mqi[index].hr = IUnknown_QueryInterface(unk, mqi[index].pIID, (void**)&mqi[index].pItf);
3283 if (mqi[index].hr == S_OK)
3284 fetched++;
3285 }
3286
3287 if (!include_unk)
3288 IUnknown_Release(unk);
3289
3290 if (fetched == 0)
3291 return E_NOINTERFACE;
3292
3293 return fetched == count ? S_OK : CO_S_NOTALLINTERFACES;
3294 }
3295
3296 /***********************************************************************
3297 * CoCreateInstanceEx [OLE32.@]
3298 */
3299 HRESULT WINAPI DECLSPEC_HOTPATCH CoCreateInstanceEx(
3300 REFCLSID rclsid,
3301 LPUNKNOWN pUnkOuter,
3302 DWORD dwClsContext,
3303 COSERVERINFO* pServerInfo,
3304 ULONG cmq,
3305 MULTI_QI* pResults)
3306 {
3307 IUnknown *unk = NULL;
3308 IClassFactory *cf;
3309 APARTMENT *apt;
3310 CLSID clsid;
3311 HRESULT hres;
3312
3313 TRACE("(%s %p %x %p %u %p)\n", debugstr_guid(rclsid), pUnkOuter, dwClsContext, pServerInfo, cmq, pResults);
3314
3315 if (!cmq || !pResults)
3316 return E_INVALIDARG;
3317
3318 if (pServerInfo)
3319 FIXME("() non-NULL pServerInfo not supported!\n");
3320
3321 init_multi_qi(cmq, pResults, E_NOINTERFACE);
3322
3323 hres = CoGetTreatAsClass(rclsid, &clsid);
3324 if(FAILED(hres))
3325 clsid = *rclsid;
3326
3327 if (!(apt = apartment_get_current_or_mta()))
3328 {
3329 ERR("apartment not initialised\n");
3330 return CO_E_NOTINITIALIZED;
3331 }
3332 apartment_release(apt);
3333
3334 /*
3335 * The Standard Global Interface Table (GIT) object is a process-wide singleton.
3336 */
3337 if (IsEqualIID(&clsid, &CLSID_StdGlobalInterfaceTable))
3338 {
3339 IGlobalInterfaceTable *git = get_std_git();
3340 TRACE("Retrieving GIT\n");
3341 return return_multi_qi((IUnknown*)git, cmq, pResults, FALSE);
3342 }
3343
3344 if (IsEqualCLSID(&clsid, &CLSID_ManualResetEvent)) {
3345 hres = ManualResetEvent_Construct(pUnkOuter, pResults[0].pIID, (void**)&unk);
3346 if (FAILED(hres))
3347 return hres;
3348 return return_multi_qi(unk, cmq, pResults, TRUE);
3349 }
3350
3351 /*
3352 * Get a class factory to construct the object we want.
3353 */
3354 hres = CoGetClassObject(&clsid, dwClsContext, NULL, &IID_IClassFactory, (void**)&cf);
3355 if (FAILED(hres))
3356 return hres;
3357
3358 /*
3359 * Create the object and don't forget to release the factory
3360 */
3361 hres = IClassFactory_CreateInstance(cf, pUnkOuter, pResults[0].pIID, (void**)&unk);
3362 IClassFactory_Release(cf);
3363 if (FAILED(hres))
3364 {
3365 if (hres == CLASS_E_NOAGGREGATION && pUnkOuter)
3366 FIXME("Class %s does not support aggregation\n", debugstr_guid(&clsid));
3367 else
3368 FIXME("no instance created for interface %s of class %s, hres is 0x%08x\n",
3369 debugstr_guid(pResults[0].pIID),
3370 debugstr_guid(&clsid),hres);
3371 return hres;
3372 }
3373
3374 return return_multi_qi(unk, cmq, pResults, TRUE);
3375 }
3376
3377 /***********************************************************************
3378 * CoGetInstanceFromFile [OLE32.@]
3379 */
3380 HRESULT WINAPI DECLSPEC_HOTPATCH CoGetInstanceFromFile(
3381 COSERVERINFO *server_info,
3382 CLSID *rclsid,
3383 IUnknown *outer,
3384 DWORD cls_context,
3385 DWORD grfmode,
3386 OLECHAR *filename,
3387 DWORD count,
3388 MULTI_QI *results
3389 )
3390 {
3391 IPersistFile *pf = NULL;
3392 IUnknown* unk = NULL;
3393 CLSID clsid;
3394 HRESULT hr;
3395
3396 if (count == 0 || !results)
3397 return E_INVALIDARG;
3398
3399 if (server_info)
3400 FIXME("() non-NULL server_info not supported\n");
3401
3402 init_multi_qi(count, results, E_NOINTERFACE);
3403
3404 /* optionally get CLSID from a file */
3405 if (!rclsid)
3406 {
3407 hr = GetClassFile(filename, &clsid);
3408 if (FAILED(hr))
3409 {
3410 ERR("failed to get CLSID from a file\n");
3411 return hr;
3412 }
3413
3414 rclsid = &clsid;
3415 }
3416
3417 hr = CoCreateInstance(rclsid,
3418 outer,
3419 cls_context,
3420 &IID_IUnknown,
3421 (void**)&unk);
3422
3423 if (hr != S_OK)
3424 {
3425 init_multi_qi(count, results, hr);
3426 return hr;
3427 }
3428
3429 /* init from file */
3430 hr = IUnknown_QueryInterface(unk, &IID_IPersistFile, (void**)&pf);
3431 if (FAILED(hr))
3432 {
3433 init_multi_qi(count, results, hr);
3434 IUnknown_Release(unk);
3435 return hr;
3436 }
3437
3438 hr = IPersistFile_Load(pf, filename, grfmode);
3439 IPersistFile_Release(pf);
3440 if (SUCCEEDED(hr))
3441 return return_multi_qi(unk, count, results, FALSE);
3442 else
3443 {
3444 init_multi_qi(count, results, hr);
3445 IUnknown_Release(unk);
3446 return hr;
3447 }
3448 }
3449
3450 /***********************************************************************
3451 * CoGetInstanceFromIStorage [OLE32.@]
3452 */
3453 HRESULT WINAPI CoGetInstanceFromIStorage(
3454 COSERVERINFO *server_info,
3455 CLSID *rclsid,
3456 IUnknown *outer,
3457 DWORD cls_context,
3458 IStorage *storage,
3459 DWORD count,
3460 MULTI_QI *results
3461 )
3462 {
3463 IPersistStorage *ps = NULL;
3464 IUnknown* unk = NULL;
3465 STATSTG stat;
3466 HRESULT hr;
3467
3468 if (count == 0 || !results || !storage)
3469 return E_INVALIDARG;
3470
3471 if (server_info)
3472 FIXME("() non-NULL server_info not supported\n");
3473
3474 init_multi_qi(count, results, E_NOINTERFACE);
3475
3476 /* optionally get CLSID from a file */
3477 if (!rclsid)
3478 {
3479 memset(&stat.clsid, 0, sizeof(stat.clsid));
3480 hr = IStorage_Stat(storage, &stat, STATFLAG_NONAME);
3481 if (FAILED(hr))
3482 {
3483 ERR("failed to get CLSID from a file\n");
3484 return hr;
3485 }
3486
3487 rclsid = &stat.clsid;
3488 }
3489
3490 hr = CoCreateInstance(rclsid,
3491 outer,
3492 cls_context,
3493 &IID_IUnknown,
3494 (void**)&unk);
3495
3496 if (hr != S_OK)
3497 return hr;
3498
3499 /* init from IStorage */
3500 hr = IUnknown_QueryInterface(unk, &IID_IPersistStorage, (void**)&ps);
3501 if (FAILED(hr))
3502 ERR("failed to get IPersistStorage\n");
3503
3504 if (ps)
3505 {
3506 IPersistStorage_Load(ps, storage);
3507 IPersistStorage_Release(ps);
3508 }
3509
3510 return return_multi_qi(unk, count, results, FALSE);
3511 }
3512
3513 /***********************************************************************
3514 * CoLoadLibrary (OLE32.@)
3515 *
3516 * Loads a library.
3517 *
3518 * PARAMS
3519 * lpszLibName [I] Path to library.
3520 * bAutoFree [I] Whether the library should automatically be freed.
3521 *
3522 * RETURNS
3523 * Success: Handle to loaded library.
3524 * Failure: NULL.
3525 *
3526 * SEE ALSO
3527 * CoFreeLibrary, CoFreeAllLibraries, CoFreeUnusedLibraries
3528 */
3529 HINSTANCE WINAPI CoLoadLibrary(LPOLESTR lpszLibName, BOOL bAutoFree)
3530 {
3531 TRACE("(%s, %d)\n", debugstr_w(lpszLibName), bAutoFree);
3532
3533 return LoadLibraryExW(lpszLibName, 0, LOAD_WITH_ALTERED_SEARCH_PATH);
3534 }
3535
3536 /***********************************************************************
3537 * CoFreeLibrary [OLE32.@]
3538 *
3539 * Unloads a library from memory.
3540 *
3541 * PARAMS
3542 * hLibrary [I] Handle to library to unload.
3543 *
3544 * RETURNS
3545 * Nothing
3546 *
3547 * SEE ALSO
3548 * CoLoadLibrary, CoFreeAllLibraries, CoFreeUnusedLibraries
3549 */
3550 void WINAPI CoFreeLibrary(HINSTANCE hLibrary)
3551 {
3552 FreeLibrary(hLibrary);
3553 }
3554
3555
3556 /***********************************************************************
3557 * CoFreeAllLibraries [OLE32.@]
3558 *
3559 * Function for backwards compatibility only. Does nothing.
3560 *
3561 * RETURNS
3562 * Nothing.
3563 *
3564 * SEE ALSO
3565 * CoLoadLibrary, CoFreeLibrary, CoFreeUnusedLibraries
3566 */
3567 void WINAPI CoFreeAllLibraries(void)
3568 {
3569 /* NOP */
3570 }
3571
3572 /***********************************************************************
3573 * CoFreeUnusedLibrariesEx [OLE32.@]
3574 *
3575 * Frees any previously unused libraries whose delay has expired and marks
3576 * currently unused libraries for unloading. Unused are identified as those that
3577 * return S_OK from their DllCanUnloadNow function.
3578 *
3579 * PARAMS
3580 * dwUnloadDelay [I] Unload delay in milliseconds.
3581 * dwReserved [I] Reserved. Set to 0.
3582 *
3583 * RETURNS
3584 * Nothing.
3585 *
3586 * SEE ALSO
3587 * CoLoadLibrary, CoFreeAllLibraries, CoFreeLibrary
3588 */
3589 void WINAPI DECLSPEC_HOTPATCH CoFreeUnusedLibrariesEx(DWORD dwUnloadDelay, DWORD dwReserved)
3590 {
3591 struct apartment *apt = COM_CurrentApt();
3592 if (!apt)
3593 {
3594 ERR("apartment not initialised\n");
3595 return;
3596 }
3597
3598 apartment_freeunusedlibraries(apt, dwUnloadDelay);
3599 }
3600
3601 /***********************************************************************
3602 * CoFreeUnusedLibraries [OLE32.@]
3603 *
3604 * Frees any unused libraries. Unused are identified as those that return
3605 * S_OK from their DllCanUnloadNow function.
3606 *
3607 * RETURNS
3608 * Nothing.
3609 *
3610 * SEE ALSO
3611 * CoLoadLibrary, CoFreeAllLibraries, CoFreeLibrary
3612 */
3613 void WINAPI DECLSPEC_HOTPATCH CoFreeUnusedLibraries(void)
3614 {
3615 CoFreeUnusedLibrariesEx(INFINITE, 0);
3616 }
3617
3618 /***********************************************************************
3619 * CoFileTimeNow [OLE32.@]
3620 *
3621 * Retrieves the current time in FILETIME format.
3622 *
3623 * PARAMS
3624 * lpFileTime [O] The current time.
3625 *
3626 * RETURNS
3627 * S_OK.
3628 */
3629 HRESULT WINAPI CoFileTimeNow( FILETIME *lpFileTime )
3630 {
3631 GetSystemTimeAsFileTime( lpFileTime );
3632 return S_OK;
3633 }
3634
3635 /******************************************************************************
3636 * CoLockObjectExternal [OLE32.@]
3637 *
3638 * Increments or decrements the external reference count of a stub object.
3639 *
3640 * PARAMS
3641 * pUnk [I] Stub object.
3642 * fLock [I] If TRUE then increments the external ref-count,
3643 * otherwise decrements.
3644 * fLastUnlockReleases [I] If TRUE then the last unlock has the effect of
3645 * calling CoDisconnectObject.
3646 *
3647 * RETURNS
3648 * Success: S_OK.
3649 * Failure: HRESULT code.
3650 *
3651 * NOTES
3652 * If fLock is TRUE and an object is passed in that doesn't have a stub
3653 * manager then a new stub manager is created for the object.
3654 */
3655 HRESULT WINAPI CoLockObjectExternal(
3656 LPUNKNOWN pUnk,
3657 BOOL fLock,
3658 BOOL fLastUnlockReleases)
3659 {
3660 struct stub_manager *stubmgr;
3661 struct apartment *apt;
3662
3663 TRACE("pUnk=%p, fLock=%s, fLastUnlockReleases=%s\n",
3664 pUnk, fLock ? "TRUE" : "FALSE", fLastUnlockReleases ? "TRUE" : "FALSE");
3665
3666 if (!(apt = apartment_get_current_or_mta()))
3667 {
3668 ERR("apartment not initialised\n");
3669 return CO_E_NOTINITIALIZED;
3670 }
3671
3672 stubmgr = get_stub_manager_from_object(apt, pUnk, fLock);
3673 if (!stubmgr)
3674 {
3675 WARN("stub object not found %p\n", pUnk);
3676 /* Note: native is pretty broken here because it just silently
3677 * fails, without returning an appropriate error code, making apps
3678 * think that the object was disconnected, when it actually wasn't */
3679 apartment_release(apt);
3680 return S_OK;
3681 }
3682
3683 if (fLock)
3684 stub_manager_ext_addref(stubmgr, 1, FALSE);
3685 else
3686 stub_manager_ext_release(stubmgr, 1, FALSE, fLastUnlockReleases);
3687
3688 stub_manager_int_release(stubmgr);
3689 apartment_release(apt);
3690 return S_OK;
3691 }
3692
3693 /***********************************************************************
3694 * CoInitializeWOW (OLE32.@)
3695 *
3696 * WOW equivalent of CoInitialize?
3697 *
3698 * PARAMS
3699 * x [I] Unknown.
3700 * y [I] Unknown.
3701 *
3702 * RETURNS
3703 * Unknown.
3704 */
3705 HRESULT WINAPI CoInitializeWOW(DWORD x,DWORD y)
3706 {
3707 FIXME("(0x%08x,0x%08x),stub!\n",x,y);
3708 return 0;
3709 }
3710
3711 /***********************************************************************
3712 * CoGetState [OLE32.@]
3713 *
3714 * Retrieves the thread state object previously stored by CoSetState().
3715 *
3716 * PARAMS
3717 * ppv [I] Address where pointer to object will be stored.
3718 *
3719 * RETURNS
3720 * Success: S_OK.
3721 * Failure: E_OUTOFMEMORY.
3722 *
3723 * NOTES
3724 * Crashes on all invalid ppv addresses, including NULL.
3725 * If the function returns a non-NULL object then the caller must release its
3726 * reference on the object when the object is no longer required.
3727 *
3728 * SEE ALSO
3729 * CoSetState().
3730 */
3731 HRESULT WINAPI CoGetState(IUnknown ** ppv)
3732 {
3733 struct oletls *info = COM_CurrentInfo();
3734 if (!info) return E_OUTOFMEMORY;
3735
3736 *ppv = NULL;
3737
3738 if (info->state)
3739 {
3740 IUnknown_AddRef(info->state);
3741 *ppv = info->state;
3742 TRACE("apt->state=%p\n", info->state);
3743 }
3744
3745 return S_OK;
3746 }
3747
3748 /***********************************************************************
3749 * CoSetState [OLE32.@]
3750 *
3751 * Sets the thread state object.
3752 *
3753 * PARAMS
3754 * pv [I] Pointer to state object to be stored.
3755 *
3756 * NOTES
3757 * The system keeps a reference on the object while the object stored.
3758 *
3759 * RETURNS
3760 * Success: S_OK.
3761 * Failure: E_OUTOFMEMORY.
3762 */
3763 HRESULT WINAPI CoSetState(IUnknown * pv)
3764 {
3765 struct oletls *info = COM_CurrentInfo();
3766 if (!info) return E_OUTOFMEMORY;
3767
3768 if (pv) IUnknown_AddRef(pv);
3769
3770 if (info->state)
3771 {
3772 TRACE("-- release %p now\n", info->state);
3773 IUnknown_Release(info->state);
3774 }
3775
3776 info->state = pv;
3777
3778 return S_OK;
3779 }
3780
3781
3782 /******************************************************************************
3783 * CoTreatAsClass [OLE32.@]
3784 *
3785 * Sets the TreatAs value of a class.
3786 *
3787 * PARAMS
3788 * clsidOld [I] Class to set TreatAs value on.
3789 * clsidNew [I] The class the clsidOld should be treated as.
3790 *
3791 * RETURNS
3792 * Success: S_OK.
3793 * Failure: HRESULT code.
3794 *
3795 * SEE ALSO
3796 * CoGetTreatAsClass
3797 */
3798 HRESULT WINAPI CoTreatAsClass(REFCLSID clsidOld, REFCLSID clsidNew)
3799 {
3800 static const WCHAR wszAutoTreatAs[] = {'A','u','t','o','T','r','e','a','t','A','s',0};
3801 static const WCHAR wszTreatAs[] = {'T','r','e','a','t','A','s',0};
3802 HKEY hkey = NULL;
3803 WCHAR szClsidNew[CHARS_IN_GUID];
3804 HRESULT res = S_OK;
3805 WCHAR auto_treat_as[CHARS_IN_GUID];
3806 LONG auto_treat_as_size = sizeof(auto_treat_as);
3807 CLSID id;
3808
3809 res = COM_OpenKeyForCLSID(clsidOld, NULL, KEY_READ | KEY_WRITE, &hkey);
3810 if (FAILED(res))
3811 goto done;
3812
3813 if (IsEqualGUID( clsidOld, clsidNew ))
3814 {
3815 if (!RegQueryValueW(hkey, wszAutoTreatAs, auto_treat_as, &auto_treat_as_size) &&
3816 CLSIDFromString(auto_treat_as, &id) == S_OK)
3817 {
3818 if (RegSetValueW(hkey, wszTreatAs, REG_SZ, auto_treat_as, sizeof(auto_treat_as)))
3819 {
3820 res = REGDB_E_WRITEREGDB;
3821 goto done;
3822 }
3823 }
3824 else
3825 {
3826 if(RegDeleteKeyW(hkey, wszTreatAs))
3827 res = REGDB_E_WRITEREGDB;
3828 goto done;
3829 }
3830 }
3831 else
3832 {
3833 if(IsEqualGUID(clsidNew, &CLSID_NULL)){
3834 RegDeleteKeyW(hkey, wszTreatAs);
3835 }else{
3836 if(!StringFromGUID2(clsidNew, szClsidNew, ARRAYSIZE(szClsidNew))){
3837 WARN("StringFromGUID2 failed\n");
3838 res = E_FAIL;
3839 goto done;
3840 }
3841