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 { 126 *ppvObject = iface; 127 } 128 else 129 { 130 FIXME("(%s), not supported.\n", debugstr_guid(riid)); 131 return E_NOINTERFACE; 132 } 133 134 /* Now inc the refcount */ 135 IGlobalInterfaceTable_AddRef(iface); 136 return S_OK; 137 } 138 139 static ULONG WINAPI 140 StdGlobalInterfaceTable_AddRef(IGlobalInterfaceTable* iface) 141 { 142 return 1; 143 } 144 145 static ULONG WINAPI 146 StdGlobalInterfaceTable_Release(IGlobalInterfaceTable* iface) 147 { 148 return 1; 149 } 150 151 /*** 152 * Now implement the actual IGlobalInterfaceTable interface 153 */ 154 155 static HRESULT WINAPI 156 StdGlobalInterfaceTable_RegisterInterfaceInGlobal( 157 IGlobalInterfaceTable* iface, IUnknown* pUnk, 158 REFIID riid, DWORD* pdwCookie) 159 { 160 StdGlobalInterfaceTableImpl* const This = impl_from_IGlobalInterfaceTable(iface); 161 IStream* stream = NULL; 162 HRESULT hres; 163 StdGITEntry* entry; 164 LARGE_INTEGER zero; 165 166 TRACE("iface=%p, pUnk=%p, riid=%s, pdwCookie=0x%p\n", iface, pUnk, debugstr_guid(riid), pdwCookie); 167 168 if (pUnk == NULL) return E_INVALIDARG; 169 170 /* marshal the interface */ 171 TRACE("About to marshal the interface\n"); 172 173 hres = CreateStreamOnHGlobal(0, TRUE, &stream); 174 if (hres != S_OK) return hres; 175 hres = CoMarshalInterface(stream, riid, pUnk, MSHCTX_INPROC, NULL, MSHLFLAGS_TABLESTRONG); 176 if (hres != S_OK) 177 { 178 IStream_Release(stream); 179 return hres; 180 } 181 182 zero.QuadPart = 0; 183 IStream_Seek(stream, zero, STREAM_SEEK_SET, NULL); 184 185 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(StdGITEntry)); 186 if (!entry) 187 { 188 CoReleaseMarshalData(stream); 189 IStream_Release(stream); 190 return E_OUTOFMEMORY; 191 } 192 193 EnterCriticalSection(&git_section); 194 195 entry->iid = *riid; 196 entry->stream = stream; 197 entry->cookie = This->nextCookie; 198 This->nextCookie++; /* inc the cookie count */ 199 200 /* insert the new entry at the end of the list */ 201 list_add_tail(&This->list, &entry->entry); 202 203 /* and return the cookie */ 204 *pdwCookie = entry->cookie; 205 206 LeaveCriticalSection(&git_section); 207 208 TRACE("Cookie is 0x%x\n", entry->cookie); 209 return S_OK; 210 } 211 212 static HRESULT WINAPI 213 StdGlobalInterfaceTable_RevokeInterfaceFromGlobal( 214 IGlobalInterfaceTable* iface, DWORD dwCookie) 215 { 216 StdGlobalInterfaceTableImpl* This = impl_from_IGlobalInterfaceTable(iface); 217 StdGITEntry* entry; 218 HRESULT hr; 219 220 TRACE("iface=%p, dwCookie=0x%x\n", iface, dwCookie); 221 222 EnterCriticalSection(&git_section); 223 224 entry = StdGlobalInterfaceTable_FindEntry(This, dwCookie); 225 if (entry == NULL) { 226 TRACE("Entry not found\n"); 227 LeaveCriticalSection(&git_section); 228 return E_INVALIDARG; /* not found */ 229 } 230 231 list_remove(&entry->entry); 232 233 LeaveCriticalSection(&git_section); 234 235 /* Free the stream */ 236 hr = CoReleaseMarshalData(entry->stream); 237 if (hr != S_OK) 238 { 239 WARN("Failed to release marshal data, hr = 0x%08x\n", hr); 240 return hr; 241 } 242 IStream_Release(entry->stream); 243 244 HeapFree(GetProcessHeap(), 0, entry); 245 return S_OK; 246 } 247 248 static HRESULT WINAPI 249 StdGlobalInterfaceTable_GetInterfaceFromGlobal( 250 IGlobalInterfaceTable* iface, DWORD dwCookie, 251 REFIID riid, void **ppv) 252 { 253 StdGlobalInterfaceTableImpl* This = impl_from_IGlobalInterfaceTable(iface); 254 StdGITEntry* entry; 255 HRESULT hres; 256 IStream *stream; 257 258 TRACE("dwCookie=0x%x, riid=%s, ppv=%p\n", dwCookie, debugstr_guid(riid), ppv); 259 260 EnterCriticalSection(&git_section); 261 262 entry = StdGlobalInterfaceTable_FindEntry(This, dwCookie); 263 if (entry == NULL) { 264 WARN("Entry for cookie 0x%x not found\n", dwCookie); 265 LeaveCriticalSection(&git_section); 266 return E_INVALIDARG; 267 } 268 269 TRACE("entry=%p\n", entry); 270 271 hres = IStream_Clone(entry->stream, &stream); 272 273 LeaveCriticalSection(&git_section); 274 275 if (hres != S_OK) { 276 WARN("Failed to clone stream with error 0x%08x\n", hres); 277 return hres; 278 } 279 280 /* unmarshal the interface */ 281 hres = CoUnmarshalInterface(stream, riid, ppv); 282 IStream_Release(stream); 283 284 if (hres != S_OK) { 285 WARN("Failed to unmarshal stream\n"); 286 return hres; 287 } 288 289 TRACE("ppv=%p\n", *ppv); 290 return S_OK; 291 } 292 293 /* Classfactory definition - despite what MSDN says, some programs need this */ 294 295 static HRESULT WINAPI 296 GITCF_QueryInterface(LPCLASSFACTORY iface,REFIID riid, LPVOID *ppv) 297 { 298 *ppv = NULL; 299 if (IsEqualIID(riid, &IID_IUnknown) || 300 IsEqualIID(riid, &IID_IClassFactory)) 301 { 302 *ppv = iface; 303 return S_OK; 304 } 305 return E_NOINTERFACE; 306 } 307 308 static ULONG WINAPI GITCF_AddRef(LPCLASSFACTORY iface) 309 { 310 return 2; 311 } 312 313 static ULONG WINAPI GITCF_Release(LPCLASSFACTORY iface) 314 { 315 return 1; 316 } 317 318 static HRESULT WINAPI 319 GITCF_CreateInstance(LPCLASSFACTORY iface, LPUNKNOWN pUnk, 320 REFIID riid, LPVOID *ppv) 321 { 322 IGlobalInterfaceTable *git = get_std_git(); 323 HRESULT hr = IGlobalInterfaceTable_QueryInterface(git, riid, ppv); 324 IGlobalInterfaceTable_Release(git); 325 return hr; 326 } 327 328 static HRESULT WINAPI GITCF_LockServer(LPCLASSFACTORY iface, BOOL fLock) 329 { 330 FIXME("(%d), stub!\n",fLock); 331 return S_OK; 332 } 333 334 static const IClassFactoryVtbl GITClassFactoryVtbl = { 335 GITCF_QueryInterface, 336 GITCF_AddRef, 337 GITCF_Release, 338 GITCF_CreateInstance, 339 GITCF_LockServer 340 }; 341 342 static IClassFactory git_classfactory = { &GITClassFactoryVtbl }; 343 344 HRESULT StdGlobalInterfaceTable_GetFactory(LPVOID *ppv) 345 { 346 *ppv = &git_classfactory; 347 TRACE("Returning GIT classfactory\n"); 348 return S_OK; 349 } 350 351 /* Virtual function table */ 352 static const IGlobalInterfaceTableVtbl StdGlobalInterfaceTableImpl_Vtbl = 353 { 354 StdGlobalInterfaceTable_QueryInterface, 355 StdGlobalInterfaceTable_AddRef, 356 StdGlobalInterfaceTable_Release, 357 StdGlobalInterfaceTable_RegisterInterfaceInGlobal, 358 StdGlobalInterfaceTable_RevokeInterfaceFromGlobal, 359 StdGlobalInterfaceTable_GetInterfaceFromGlobal 360 }; 361 362 IGlobalInterfaceTable* get_std_git(void) 363 { 364 if (!std_git) 365 { 366 StdGlobalInterfaceTableImpl* newGIT; 367 368 newGIT = HeapAlloc(GetProcessHeap(), 0, sizeof(StdGlobalInterfaceTableImpl)); 369 if (!newGIT) return NULL; 370 371 newGIT->IGlobalInterfaceTable_iface.lpVtbl = &StdGlobalInterfaceTableImpl_Vtbl; 372 list_init(&newGIT->list); 373 newGIT->nextCookie = 0xf100; /* that's where windows starts, so that's where we start */ 374 375 if (InterlockedCompareExchangePointer((void**)&std_git, &newGIT->IGlobalInterfaceTable_iface, NULL)) 376 { 377 HeapFree(GetProcessHeap(), 0, newGIT); 378 } 379 else 380 TRACE("Created the GIT at %p\n", newGIT); 381 } 382 383 return std_git; 384 } 385 386 void release_std_git(void) 387 { 388 StdGlobalInterfaceTableImpl *git; 389 StdGITEntry *entry, *entry2; 390 391 if (!std_git) return; 392 393 git = impl_from_IGlobalInterfaceTable(std_git); 394 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &git->list, StdGITEntry, entry) 395 { 396 list_remove(&entry->entry); 397 398 CoReleaseMarshalData(entry->stream); 399 IStream_Release(entry->stream); 400 HeapFree(GetProcessHeap(), 0, entry); 401 } 402 403 HeapFree(GetProcessHeap(), 0, git); 404 } 405