xref: /reactos/dll/win32/ole32/git.c (revision 5ddbd373)
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