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