1 /* 2 * Implementation of the StdGlobalInterfaceTable object 3 * 4 * The GlobalInterfaceTable (GIT) object is used to marshal interfaces between 5 * threading apartments (contexts). When you want to pass an interface but not 6 * as a parameter, it wouldn't get marshalled automatically, so you can use this 7 * object to insert the interface into a table, and you get back a cookie. 8 * Then when it's retrieved, it'll be unmarshalled into the right apartment. 9 * 10 * Copyright 2003 Mike Hearn <mike@theoretic.com> 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 27 #include <stdarg.h> 28 29 #define COBJMACROS 30 31 #include "windef.h" 32 #include "winbase.h" 33 #include "winuser.h" 34 #include "objbase.h" 35 #include "ole2.h" 36 #include "winerror.h" 37 38 #include "compobj_private.h" 39 40 #include "wine/list.h" 41 #include "wine/debug.h" 42 43 WINE_DEFAULT_DEBUG_CHANNEL(ole); 44 45 /**************************************************************************** 46 * StdGlobalInterfaceTable definition 47 * 48 * This class implements IGlobalInterfaceTable and is a process-wide singleton 49 * used for marshalling interfaces between threading apartments using cookies. 50 */ 51 52 /* Each entry in the linked list of GIT entries */ 53 typedef struct StdGITEntry 54 { 55 DWORD cookie; 56 IID iid; /* IID of the interface */ 57 IStream* stream; /* Holds the marshalled interface */ 58 59 struct list entry; 60 } StdGITEntry; 61 62 /* Class data */ 63 typedef struct StdGlobalInterfaceTableImpl 64 { 65 IGlobalInterfaceTable IGlobalInterfaceTable_iface; 66 67 struct list list; 68 ULONG nextCookie; 69 70 } StdGlobalInterfaceTableImpl; 71 72 static IGlobalInterfaceTable *std_git; 73 74 static CRITICAL_SECTION git_section; 75 static CRITICAL_SECTION_DEBUG critsect_debug = 76 { 77 0, 0, &git_section, 78 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList }, 79 0, 0, { (DWORD_PTR)(__FILE__ ": global interface table") } 80 }; 81 static CRITICAL_SECTION git_section = { &critsect_debug, -1, 0, 0, 0, 0 }; 82 83 84 static inline StdGlobalInterfaceTableImpl *impl_from_IGlobalInterfaceTable(IGlobalInterfaceTable *iface) 85 { 86 return CONTAINING_RECORD(iface, StdGlobalInterfaceTableImpl, IGlobalInterfaceTable_iface); 87 } 88 89 /*** 90 * A helper function to traverse the list and find the entry that matches the cookie. 91 * Returns NULL if not found. Must be called inside git_section critical section. 92 */ 93 static StdGITEntry* StdGlobalInterfaceTable_FindEntry(StdGlobalInterfaceTableImpl* This, 94 DWORD cookie) 95 { 96 StdGITEntry* e; 97 98 TRACE("This=%p, cookie=0x%x\n", This, cookie); 99 100 LIST_FOR_EACH_ENTRY(e, &This->list, StdGITEntry, entry) { 101 if (e->cookie == cookie) 102 return e; 103 } 104 105 TRACE("Entry not found\n"); 106 return NULL; 107 } 108 109 /*** 110 * Here's the boring boilerplate stuff for IUnknown 111 */ 112 113 static HRESULT WINAPI 114 StdGlobalInterfaceTable_QueryInterface(IGlobalInterfaceTable* iface, 115 REFIID riid, void** ppvObject) 116 { 117 /* Make sure silly coders can't crash us */ 118 if (ppvObject == 0) return E_INVALIDARG; 119 120 *ppvObject = 0; /* assume we don't have the interface */ 121 122 /* Do we implement that interface? */ 123 if (IsEqualIID(&IID_IUnknown, riid) || 124 IsEqualIID(&IID_IGlobalInterfaceTable, riid)) 125 *ppvObject = iface; 126 else 127 return E_NOINTERFACE; 128 129 /* Now inc the refcount */ 130 IGlobalInterfaceTable_AddRef(iface); 131 return S_OK; 132 } 133 134 static ULONG WINAPI 135 StdGlobalInterfaceTable_AddRef(IGlobalInterfaceTable* iface) 136 { 137 return 1; 138 } 139 140 static ULONG WINAPI 141 StdGlobalInterfaceTable_Release(IGlobalInterfaceTable* iface) 142 { 143 return 1; 144 } 145 146 /*** 147 * Now implement the actual IGlobalInterfaceTable interface 148 */ 149 150 static HRESULT WINAPI 151 StdGlobalInterfaceTable_RegisterInterfaceInGlobal( 152 IGlobalInterfaceTable* iface, IUnknown* pUnk, 153 REFIID riid, DWORD* pdwCookie) 154 { 155 StdGlobalInterfaceTableImpl* const This = impl_from_IGlobalInterfaceTable(iface); 156 IStream* stream = NULL; 157 HRESULT hres; 158 StdGITEntry* entry; 159 LARGE_INTEGER zero; 160 161 TRACE("iface=%p, pUnk=%p, riid=%s, pdwCookie=0x%p\n", iface, pUnk, debugstr_guid(riid), pdwCookie); 162 163 if (pUnk == NULL) return E_INVALIDARG; 164 165 /* marshal the interface */ 166 TRACE("About to marshal the interface\n"); 167 168 hres = CreateStreamOnHGlobal(0, TRUE, &stream); 169 if (hres != S_OK) return hres; 170 hres = CoMarshalInterface(stream, riid, pUnk, MSHCTX_INPROC, NULL, MSHLFLAGS_TABLESTRONG); 171 if (hres != S_OK) 172 { 173 IStream_Release(stream); 174 return hres; 175 } 176 177 zero.QuadPart = 0; 178 IStream_Seek(stream, zero, STREAM_SEEK_SET, NULL); 179 180 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(StdGITEntry)); 181 if (!entry) 182 { 183 CoReleaseMarshalData(stream); 184 IStream_Release(stream); 185 return E_OUTOFMEMORY; 186 } 187 188 EnterCriticalSection(&git_section); 189 190 entry->iid = *riid; 191 entry->stream = stream; 192 entry->cookie = This->nextCookie; 193 This->nextCookie++; /* inc the cookie count */ 194 195 /* insert the new entry at the end of the list */ 196 list_add_tail(&This->list, &entry->entry); 197 198 /* and return the cookie */ 199 *pdwCookie = entry->cookie; 200 201 LeaveCriticalSection(&git_section); 202 203 TRACE("Cookie is 0x%x\n", entry->cookie); 204 return S_OK; 205 } 206 207 static HRESULT WINAPI 208 StdGlobalInterfaceTable_RevokeInterfaceFromGlobal( 209 IGlobalInterfaceTable* iface, DWORD dwCookie) 210 { 211 StdGlobalInterfaceTableImpl* This = impl_from_IGlobalInterfaceTable(iface); 212 StdGITEntry* entry; 213 HRESULT hr; 214 215 TRACE("iface=%p, dwCookie=0x%x\n", iface, dwCookie); 216 217 EnterCriticalSection(&git_section); 218 219 entry = StdGlobalInterfaceTable_FindEntry(This, dwCookie); 220 if (entry == NULL) { 221 TRACE("Entry not found\n"); 222 LeaveCriticalSection(&git_section); 223 return E_INVALIDARG; /* not found */ 224 } 225 226 list_remove(&entry->entry); 227 228 LeaveCriticalSection(&git_section); 229 230 /* Free the stream */ 231 hr = CoReleaseMarshalData(entry->stream); 232 if (hr != S_OK) 233 { 234 WARN("Failed to release marshal data, hr = 0x%08x\n", hr); 235 return hr; 236 } 237 IStream_Release(entry->stream); 238 239 HeapFree(GetProcessHeap(), 0, entry); 240 return S_OK; 241 } 242 243 static HRESULT WINAPI 244 StdGlobalInterfaceTable_GetInterfaceFromGlobal( 245 IGlobalInterfaceTable* iface, DWORD dwCookie, 246 REFIID riid, void **ppv) 247 { 248 StdGlobalInterfaceTableImpl* This = impl_from_IGlobalInterfaceTable(iface); 249 StdGITEntry* entry; 250 HRESULT hres; 251 IStream *stream; 252 253 TRACE("dwCookie=0x%x, riid=%s, ppv=%p\n", dwCookie, debugstr_guid(riid), ppv); 254 255 EnterCriticalSection(&git_section); 256 257 entry = StdGlobalInterfaceTable_FindEntry(This, dwCookie); 258 if (entry == NULL) { 259 WARN("Entry for cookie 0x%x not found\n", dwCookie); 260 LeaveCriticalSection(&git_section); 261 return E_INVALIDARG; 262 } 263 264 TRACE("entry=%p\n", entry); 265 266 hres = IStream_Clone(entry->stream, &stream); 267 268 LeaveCriticalSection(&git_section); 269 270 if (hres != S_OK) { 271 WARN("Failed to clone stream with error 0x%08x\n", hres); 272 return hres; 273 } 274 275 /* unmarshal the interface */ 276 hres = CoUnmarshalInterface(stream, riid, ppv); 277 IStream_Release(stream); 278 279 if (hres != S_OK) { 280 WARN("Failed to unmarshal stream\n"); 281 return hres; 282 } 283 284 TRACE("ppv=%p\n", *ppv); 285 return S_OK; 286 } 287 288 /* Classfactory definition - despite what MSDN says, some programs need this */ 289 290 static HRESULT WINAPI 291 GITCF_QueryInterface(LPCLASSFACTORY iface,REFIID riid, LPVOID *ppv) 292 { 293 *ppv = NULL; 294 if (IsEqualIID(riid, &IID_IUnknown) || 295 IsEqualIID(riid, &IID_IClassFactory)) 296 { 297 *ppv = iface; 298 return S_OK; 299 } 300 return E_NOINTERFACE; 301 } 302 303 static ULONG WINAPI GITCF_AddRef(LPCLASSFACTORY iface) 304 { 305 return 2; 306 } 307 308 static ULONG WINAPI GITCF_Release(LPCLASSFACTORY iface) 309 { 310 return 1; 311 } 312 313 static HRESULT WINAPI 314 GITCF_CreateInstance(LPCLASSFACTORY iface, LPUNKNOWN pUnk, 315 REFIID riid, LPVOID *ppv) 316 { 317 if (IsEqualIID(riid,&IID_IGlobalInterfaceTable)) { 318 IGlobalInterfaceTable *git = get_std_git(); 319 return IGlobalInterfaceTable_QueryInterface(git, riid, ppv); 320 } 321 322 FIXME("(%s), not supported.\n",debugstr_guid(riid)); 323 return E_NOINTERFACE; 324 } 325 326 static HRESULT WINAPI GITCF_LockServer(LPCLASSFACTORY iface, BOOL fLock) 327 { 328 FIXME("(%d), stub!\n",fLock); 329 return S_OK; 330 } 331 332 static const IClassFactoryVtbl GITClassFactoryVtbl = { 333 GITCF_QueryInterface, 334 GITCF_AddRef, 335 GITCF_Release, 336 GITCF_CreateInstance, 337 GITCF_LockServer 338 }; 339 340 static IClassFactory git_classfactory = { &GITClassFactoryVtbl }; 341 342 HRESULT StdGlobalInterfaceTable_GetFactory(LPVOID *ppv) 343 { 344 *ppv = &git_classfactory; 345 TRACE("Returning GIT classfactory\n"); 346 return S_OK; 347 } 348 349 /* Virtual function table */ 350 static const IGlobalInterfaceTableVtbl StdGlobalInterfaceTableImpl_Vtbl = 351 { 352 StdGlobalInterfaceTable_QueryInterface, 353 StdGlobalInterfaceTable_AddRef, 354 StdGlobalInterfaceTable_Release, 355 StdGlobalInterfaceTable_RegisterInterfaceInGlobal, 356 StdGlobalInterfaceTable_RevokeInterfaceFromGlobal, 357 StdGlobalInterfaceTable_GetInterfaceFromGlobal 358 }; 359 360 IGlobalInterfaceTable* get_std_git(void) 361 { 362 if (!std_git) 363 { 364 StdGlobalInterfaceTableImpl* newGIT; 365 366 newGIT = HeapAlloc(GetProcessHeap(), 0, sizeof(StdGlobalInterfaceTableImpl)); 367 if (!newGIT) return NULL; 368 369 newGIT->IGlobalInterfaceTable_iface.lpVtbl = &StdGlobalInterfaceTableImpl_Vtbl; 370 list_init(&newGIT->list); 371 newGIT->nextCookie = 0xf100; /* that's where windows starts, so that's where we start */ 372 373 if (InterlockedCompareExchangePointer((void**)&std_git, &newGIT->IGlobalInterfaceTable_iface, NULL)) 374 { 375 HeapFree(GetProcessHeap(), 0, newGIT); 376 } 377 else 378 TRACE("Created the GIT at %p\n", newGIT); 379 } 380 381 return std_git; 382 } 383 384 void release_std_git(void) 385 { 386 StdGlobalInterfaceTableImpl *git; 387 StdGITEntry *entry, *entry2; 388 389 if (!std_git) return; 390 391 git = impl_from_IGlobalInterfaceTable(std_git); 392 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &git->list, StdGITEntry, entry) 393 { 394 list_remove(&entry->entry); 395 396 CoReleaseMarshalData(entry->stream); 397 IStream_Release(entry->stream); 398 HeapFree(GetProcessHeap(), 0, entry); 399 } 400 401 HeapFree(GetProcessHeap(), 0, git); 402 } 403