xref: /reactos/dll/win32/msctf/compartmentmgr.c (revision 8540ab04)
1 /*
2  *  ITfCompartmentMgr implementation
3  *
4  *  Copyright 2009 Aric Stewart, CodeWeavers
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 
21 #include "config.h"
22 
23 #include <stdarg.h>
24 
25 #define COBJMACROS
26 
27 #include "wine/debug.h"
28 #include "windef.h"
29 #include "winbase.h"
30 #include "winreg.h"
31 #include "winuser.h"
32 #include "shlwapi.h"
33 #include "winerror.h"
34 #include "objbase.h"
35 #include "oleauto.h"
36 #include "olectl.h"
37 
38 #include "wine/unicode.h"
39 
40 #include "msctf.h"
41 #include "msctf_internal.h"
42 
43 WINE_DEFAULT_DEBUG_CHANNEL(msctf);
44 
45 typedef struct tagCompartmentValue {
46     struct list entry;
47     GUID guid;
48     TfClientId owner;
49     ITfCompartment *compartment;
50 } CompartmentValue;
51 
52 typedef struct tagCompartmentMgr {
53     ITfCompartmentMgr ITfCompartmentMgr_iface;
54     LONG refCount;
55 
56     IUnknown *pUnkOuter;
57 
58     struct list values;
59 } CompartmentMgr;
60 
61 typedef struct tagCompartmentEnumGuid {
62     IEnumGUID IEnumGUID_iface;
63     LONG refCount;
64 
65     struct list *values;
66     struct list *cursor;
67 } CompartmentEnumGuid;
68 
69 typedef struct tagCompartment {
70     ITfCompartment ITfCompartment_iface;
71     ITfSource ITfSource_iface;
72     LONG refCount;
73 
74     /* Only VT_I4, VT_UNKNOWN and VT_BSTR data types are allowed */
75     VARIANT variant;
76     CompartmentValue *valueData;
77     struct list CompartmentEventSink;
78 } Compartment;
79 
80 static HRESULT CompartmentEnumGuid_Constructor(struct list* values, IEnumGUID **ppOut);
81 static HRESULT Compartment_Constructor(CompartmentValue *value, ITfCompartment **ppOut);
82 
83 static inline CompartmentMgr *impl_from_ITfCompartmentMgr(ITfCompartmentMgr *iface)
84 {
85     return CONTAINING_RECORD(iface, CompartmentMgr, ITfCompartmentMgr_iface);
86 }
87 
88 static inline Compartment *impl_from_ITfCompartment(ITfCompartment *iface)
89 {
90     return CONTAINING_RECORD(iface, Compartment, ITfCompartment_iface);
91 }
92 
93 static inline Compartment *impl_from_ITfSource(ITfSource *iface)
94 {
95     return CONTAINING_RECORD(iface, Compartment, ITfSource_iface);
96 }
97 
98 static inline CompartmentEnumGuid *impl_from_IEnumGUID(IEnumGUID *iface)
99 {
100     return CONTAINING_RECORD(iface, CompartmentEnumGuid, IEnumGUID_iface);
101 }
102 
103 HRESULT CompartmentMgr_Destructor(ITfCompartmentMgr *iface)
104 {
105     CompartmentMgr *This = impl_from_ITfCompartmentMgr(iface);
106     struct list *cursor, *cursor2;
107 
108     LIST_FOR_EACH_SAFE(cursor, cursor2, &This->values)
109     {
110         CompartmentValue* value = LIST_ENTRY(cursor,CompartmentValue,entry);
111         list_remove(cursor);
112         ITfCompartment_Release(value->compartment);
113         HeapFree(GetProcessHeap(),0,value);
114     }
115 
116     HeapFree(GetProcessHeap(),0,This);
117     return S_OK;
118 }
119 
120 /*****************************************************
121  * ITfCompartmentMgr functions
122  *****************************************************/
123 static HRESULT WINAPI CompartmentMgr_QueryInterface(ITfCompartmentMgr *iface, REFIID iid, LPVOID *ppvOut)
124 {
125     CompartmentMgr *This = impl_from_ITfCompartmentMgr(iface);
126     if (This->pUnkOuter)
127         return IUnknown_QueryInterface(This->pUnkOuter, iid, ppvOut);
128     else
129     {
130         *ppvOut = NULL;
131 
132         if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_ITfCompartmentMgr))
133         {
134             *ppvOut = &This->ITfCompartmentMgr_iface;
135         }
136 
137         if (*ppvOut)
138         {
139             ITfCompartmentMgr_AddRef(iface);
140             return S_OK;
141         }
142 
143         WARN("unsupported interface: %s\n", debugstr_guid(iid));
144         return E_NOINTERFACE;
145     }
146 }
147 
148 static ULONG WINAPI CompartmentMgr_AddRef(ITfCompartmentMgr *iface)
149 {
150     CompartmentMgr *This = impl_from_ITfCompartmentMgr(iface);
151     if (This->pUnkOuter)
152         return IUnknown_AddRef(This->pUnkOuter);
153     else
154         return InterlockedIncrement(&This->refCount);
155 }
156 
157 static ULONG WINAPI CompartmentMgr_Release(ITfCompartmentMgr *iface)
158 {
159     CompartmentMgr *This = impl_from_ITfCompartmentMgr(iface);
160     if (This->pUnkOuter)
161         return IUnknown_Release(This->pUnkOuter);
162     else
163     {
164         ULONG ret;
165 
166         ret = InterlockedDecrement(&This->refCount);
167         if (ret == 0)
168             CompartmentMgr_Destructor(iface);
169         return ret;
170     }
171 }
172 
173 static HRESULT WINAPI CompartmentMgr_GetCompartment(ITfCompartmentMgr *iface,
174         REFGUID rguid, ITfCompartment **ppcomp)
175 {
176     CompartmentMgr *This = impl_from_ITfCompartmentMgr(iface);
177     CompartmentValue* value;
178     struct list *cursor;
179     HRESULT hr;
180 
181     TRACE("(%p) %s  %p\n",This,debugstr_guid(rguid),ppcomp);
182 
183     LIST_FOR_EACH(cursor, &This->values)
184     {
185         value = LIST_ENTRY(cursor,CompartmentValue,entry);
186         if (IsEqualGUID(rguid,&value->guid))
187         {
188             ITfCompartment_AddRef(value->compartment);
189             *ppcomp = value->compartment;
190             return S_OK;
191         }
192     }
193 
194     value = HeapAlloc(GetProcessHeap(),0,sizeof(CompartmentValue));
195     value->guid = *rguid;
196     value->owner = 0;
197     hr = Compartment_Constructor(value,&value->compartment);
198     if (SUCCEEDED(hr))
199     {
200         list_add_head(&This->values,&value->entry);
201         ITfCompartment_AddRef(value->compartment);
202         *ppcomp = value->compartment;
203     }
204     else
205     {
206         HeapFree(GetProcessHeap(),0,value);
207         *ppcomp = NULL;
208     }
209     return hr;
210 }
211 
212 static HRESULT WINAPI CompartmentMgr_ClearCompartment(ITfCompartmentMgr *iface,
213     TfClientId tid, REFGUID rguid)
214 {
215     CompartmentMgr *This = impl_from_ITfCompartmentMgr(iface);
216     struct list *cursor;
217 
218     TRACE("(%p) %i %s\n",This,tid,debugstr_guid(rguid));
219 
220     LIST_FOR_EACH(cursor, &This->values)
221     {
222         CompartmentValue* value = LIST_ENTRY(cursor,CompartmentValue,entry);
223         if (IsEqualGUID(rguid,&value->guid))
224         {
225             if (value->owner && tid != value->owner)
226                 return E_UNEXPECTED;
227             list_remove(cursor);
228             ITfCompartment_Release(value->compartment);
229             HeapFree(GetProcessHeap(),0,value);
230             return S_OK;
231         }
232     }
233 
234     return CONNECT_E_NOCONNECTION;
235 }
236 
237 static HRESULT WINAPI CompartmentMgr_EnumCompartments(ITfCompartmentMgr *iface,
238  IEnumGUID **ppEnum)
239 {
240     CompartmentMgr *This = impl_from_ITfCompartmentMgr(iface);
241 
242     TRACE("(%p) %p\n",This,ppEnum);
243     if (!ppEnum)
244         return E_INVALIDARG;
245     return CompartmentEnumGuid_Constructor(&This->values, ppEnum);
246 }
247 
248 static const ITfCompartmentMgrVtbl CompartmentMgrVtbl =
249 {
250     CompartmentMgr_QueryInterface,
251     CompartmentMgr_AddRef,
252     CompartmentMgr_Release,
253     CompartmentMgr_GetCompartment,
254     CompartmentMgr_ClearCompartment,
255     CompartmentMgr_EnumCompartments
256 };
257 
258 HRESULT CompartmentMgr_Constructor(IUnknown *pUnkOuter, REFIID riid, IUnknown **ppOut)
259 {
260     CompartmentMgr *This;
261 
262     if (!ppOut)
263         return E_POINTER;
264 
265     if (pUnkOuter && !IsEqualIID (riid, &IID_IUnknown))
266         return CLASS_E_NOAGGREGATION;
267 
268     This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(CompartmentMgr));
269     if (This == NULL)
270         return E_OUTOFMEMORY;
271 
272     This->ITfCompartmentMgr_iface.lpVtbl = &CompartmentMgrVtbl;
273     This->pUnkOuter = pUnkOuter;
274     list_init(&This->values);
275 
276     if (pUnkOuter)
277     {
278         *ppOut = (IUnknown*)&This->ITfCompartmentMgr_iface;
279         TRACE("returning %p\n", *ppOut);
280         return S_OK;
281     }
282     else
283     {
284         HRESULT hr;
285         hr = ITfCompartmentMgr_QueryInterface(&This->ITfCompartmentMgr_iface, riid, (void**)ppOut);
286         if (FAILED(hr))
287             HeapFree(GetProcessHeap(),0,This);
288         return hr;
289     }
290 }
291 
292 /**************************************************
293  * IEnumGUID implementation for ITfCompartmentMgr::EnumCompartments
294  **************************************************/
295 static void CompartmentEnumGuid_Destructor(CompartmentEnumGuid *This)
296 {
297     TRACE("destroying %p\n", This);
298     HeapFree(GetProcessHeap(),0,This);
299 }
300 
301 static HRESULT WINAPI CompartmentEnumGuid_QueryInterface(IEnumGUID *iface, REFIID iid, LPVOID *ppvOut)
302 {
303     CompartmentEnumGuid *This = impl_from_IEnumGUID(iface);
304     *ppvOut = NULL;
305 
306     if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_IEnumGUID))
307     {
308         *ppvOut = &This->IEnumGUID_iface;
309     }
310 
311     if (*ppvOut)
312     {
313         IEnumGUID_AddRef(iface);
314         return S_OK;
315     }
316 
317     WARN("unsupported interface: %s\n", debugstr_guid(iid));
318     return E_NOINTERFACE;
319 }
320 
321 static ULONG WINAPI CompartmentEnumGuid_AddRef(IEnumGUID *iface)
322 {
323     CompartmentEnumGuid *This = impl_from_IEnumGUID(iface);
324     return InterlockedIncrement(&This->refCount);
325 }
326 
327 static ULONG WINAPI CompartmentEnumGuid_Release(IEnumGUID *iface)
328 {
329     CompartmentEnumGuid *This = impl_from_IEnumGUID(iface);
330     ULONG ret;
331 
332     ret = InterlockedDecrement(&This->refCount);
333     if (ret == 0)
334         CompartmentEnumGuid_Destructor(This);
335     return ret;
336 }
337 
338 /*****************************************************
339  * IEnumGuid functions
340  *****************************************************/
341 static HRESULT WINAPI CompartmentEnumGuid_Next(IEnumGUID *iface,
342     ULONG celt, GUID *rgelt, ULONG *pceltFetched)
343 {
344     CompartmentEnumGuid *This = impl_from_IEnumGUID(iface);
345     ULONG fetched = 0;
346 
347     TRACE("(%p)\n",This);
348 
349     if (rgelt == NULL) return E_POINTER;
350 
351     while (fetched < celt && This->cursor)
352     {
353         CompartmentValue* value = LIST_ENTRY(This->cursor,CompartmentValue,entry);
354         if (!value)
355             break;
356 
357         This->cursor = list_next(This->values,This->cursor);
358         *rgelt = value->guid;
359 
360         ++fetched;
361         ++rgelt;
362     }
363 
364     if (pceltFetched) *pceltFetched = fetched;
365     return fetched == celt ? S_OK : S_FALSE;
366 }
367 
368 static HRESULT WINAPI CompartmentEnumGuid_Skip(IEnumGUID *iface, ULONG celt)
369 {
370     CompartmentEnumGuid *This = impl_from_IEnumGUID(iface);
371     TRACE("(%p)\n",This);
372 
373     This->cursor = list_next(This->values,This->cursor);
374     return S_OK;
375 }
376 
377 static HRESULT WINAPI CompartmentEnumGuid_Reset(IEnumGUID *iface)
378 {
379     CompartmentEnumGuid *This = impl_from_IEnumGUID(iface);
380     TRACE("(%p)\n",This);
381     This->cursor = list_head(This->values);
382     return S_OK;
383 }
384 
385 static HRESULT WINAPI CompartmentEnumGuid_Clone(IEnumGUID *iface,
386     IEnumGUID **ppenum)
387 {
388     CompartmentEnumGuid *This = impl_from_IEnumGUID(iface);
389     HRESULT res;
390 
391     TRACE("(%p)\n",This);
392 
393     if (ppenum == NULL) return E_POINTER;
394 
395     res = CompartmentEnumGuid_Constructor(This->values, ppenum);
396     if (SUCCEEDED(res))
397     {
398         CompartmentEnumGuid *new_This = impl_from_IEnumGUID(*ppenum);
399         new_This->cursor = This->cursor;
400     }
401     return res;
402 }
403 
404 static const IEnumGUIDVtbl EnumGUIDVtbl =
405 {
406     CompartmentEnumGuid_QueryInterface,
407     CompartmentEnumGuid_AddRef,
408     CompartmentEnumGuid_Release,
409     CompartmentEnumGuid_Next,
410     CompartmentEnumGuid_Skip,
411     CompartmentEnumGuid_Reset,
412     CompartmentEnumGuid_Clone
413 };
414 
415 static HRESULT CompartmentEnumGuid_Constructor(struct list *values, IEnumGUID **ppOut)
416 {
417     CompartmentEnumGuid *This;
418 
419     This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(CompartmentEnumGuid));
420     if (This == NULL)
421         return E_OUTOFMEMORY;
422 
423     This->IEnumGUID_iface.lpVtbl= &EnumGUIDVtbl;
424     This->refCount = 1;
425 
426     This->values = values;
427     This->cursor = list_head(values);
428 
429     *ppOut = &This->IEnumGUID_iface;
430     TRACE("returning %p\n", *ppOut);
431     return S_OK;
432 }
433 
434 /**************************************************
435  * ITfCompartment
436  **************************************************/
437 static void Compartment_Destructor(Compartment *This)
438 {
439     TRACE("destroying %p\n", This);
440     VariantClear(&This->variant);
441     free_sinks(&This->CompartmentEventSink);
442     HeapFree(GetProcessHeap(),0,This);
443 }
444 
445 static HRESULT WINAPI Compartment_QueryInterface(ITfCompartment *iface, REFIID iid, LPVOID *ppvOut)
446 {
447     Compartment *This = impl_from_ITfCompartment(iface);
448 
449     *ppvOut = NULL;
450 
451     if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_ITfCompartment))
452     {
453         *ppvOut = &This->ITfCompartment_iface;
454     }
455     else if (IsEqualIID(iid, &IID_ITfSource))
456     {
457         *ppvOut = &This->ITfSource_iface;
458     }
459 
460     if (*ppvOut)
461     {
462         ITfCompartment_AddRef(iface);
463         return S_OK;
464     }
465 
466     WARN("unsupported interface: %s\n", debugstr_guid(iid));
467     return E_NOINTERFACE;
468 }
469 
470 static ULONG WINAPI Compartment_AddRef(ITfCompartment *iface)
471 {
472     Compartment *This = impl_from_ITfCompartment(iface);
473     return InterlockedIncrement(&This->refCount);
474 }
475 
476 static ULONG WINAPI Compartment_Release(ITfCompartment *iface)
477 {
478     Compartment *This = impl_from_ITfCompartment(iface);
479     ULONG ret;
480 
481     ret = InterlockedDecrement(&This->refCount);
482     if (ret == 0)
483         Compartment_Destructor(This);
484     return ret;
485 }
486 
487 static HRESULT WINAPI Compartment_SetValue(ITfCompartment *iface,
488     TfClientId tid, const VARIANT *pvarValue)
489 {
490     Compartment *This = impl_from_ITfCompartment(iface);
491     ITfCompartmentEventSink *sink;
492     struct list *cursor;
493 
494     TRACE("(%p) %i %p\n",This,tid,pvarValue);
495 
496     if (!pvarValue)
497         return E_INVALIDARG;
498 
499     if (!(V_VT(pvarValue) == VT_BSTR || V_VT(pvarValue) == VT_I4 ||
500           V_VT(pvarValue) == VT_UNKNOWN))
501         return E_INVALIDARG;
502 
503     if (!This->valueData->owner)
504         This->valueData->owner = tid;
505 
506     VariantClear(&This->variant);
507 
508     /* Shallow copy of value and type */
509     This->variant = *pvarValue;
510 
511     if (V_VT(pvarValue) == VT_BSTR)
512         V_BSTR(&This->variant) = SysAllocStringByteLen((char*)V_BSTR(pvarValue),
513                 SysStringByteLen(V_BSTR(pvarValue)));
514     else if (V_VT(pvarValue) == VT_UNKNOWN)
515         IUnknown_AddRef(V_UNKNOWN(&This->variant));
516 
517     SINK_FOR_EACH(cursor, &This->CompartmentEventSink, ITfCompartmentEventSink, sink)
518     {
519         ITfCompartmentEventSink_OnChange(sink, &This->valueData->guid);
520     }
521 
522     return S_OK;
523 }
524 
525 static HRESULT WINAPI Compartment_GetValue(ITfCompartment *iface,
526     VARIANT *pvarValue)
527 {
528     Compartment *This = impl_from_ITfCompartment(iface);
529     TRACE("(%p) %p\n",This, pvarValue);
530 
531     if (!pvarValue)
532         return E_INVALIDARG;
533 
534     VariantInit(pvarValue);
535     if (V_VT(&This->variant) == VT_EMPTY) return S_FALSE;
536     return VariantCopy(pvarValue,&This->variant);
537 }
538 
539 static const ITfCompartmentVtbl CompartmentVtbl =
540 {
541     Compartment_QueryInterface,
542     Compartment_AddRef,
543     Compartment_Release,
544     Compartment_SetValue,
545     Compartment_GetValue
546 };
547 
548 /*****************************************************
549  * ITfSource functions
550  *****************************************************/
551 
552 static HRESULT WINAPI CompartmentSource_QueryInterface(ITfSource *iface, REFIID iid, LPVOID *ppvOut)
553 {
554     Compartment *This = impl_from_ITfSource(iface);
555     return ITfCompartment_QueryInterface(&This->ITfCompartment_iface, iid, ppvOut);
556 }
557 
558 static ULONG WINAPI CompartmentSource_AddRef(ITfSource *iface)
559 {
560     Compartment *This = impl_from_ITfSource(iface);
561     return ITfCompartment_AddRef(&This->ITfCompartment_iface);
562 }
563 
564 static ULONG WINAPI CompartmentSource_Release(ITfSource *iface)
565 {
566     Compartment *This = impl_from_ITfSource(iface);
567     return ITfCompartment_Release(&This->ITfCompartment_iface);
568 }
569 
570 static HRESULT WINAPI CompartmentSource_AdviseSink(ITfSource *iface,
571         REFIID riid, IUnknown *punk, DWORD *pdwCookie)
572 {
573     Compartment *This = impl_from_ITfSource(iface);
574 
575     TRACE("(%p) %s %p %p\n",This,debugstr_guid(riid),punk,pdwCookie);
576 
577     if (!riid || !punk || !pdwCookie)
578         return E_INVALIDARG;
579 
580     if (IsEqualIID(riid, &IID_ITfCompartmentEventSink))
581         return advise_sink(&This->CompartmentEventSink, &IID_ITfCompartmentEventSink,
582                            COOKIE_MAGIC_COMPARTMENTSINK, punk, pdwCookie);
583 
584     FIXME("(%p) Unhandled Sink: %s\n",This,debugstr_guid(riid));
585     return E_NOTIMPL;
586 }
587 
588 static HRESULT WINAPI CompartmentSource_UnadviseSink(ITfSource *iface, DWORD pdwCookie)
589 {
590     Compartment *This = impl_from_ITfSource(iface);
591 
592     TRACE("(%p) %x\n",This,pdwCookie);
593 
594     if (get_Cookie_magic(pdwCookie)!=COOKIE_MAGIC_COMPARTMENTSINK)
595         return E_INVALIDARG;
596 
597     return unadvise_sink(pdwCookie);
598 }
599 
600 static const ITfSourceVtbl CompartmentSourceVtbl =
601 {
602     CompartmentSource_QueryInterface,
603     CompartmentSource_AddRef,
604     CompartmentSource_Release,
605     CompartmentSource_AdviseSink,
606     CompartmentSource_UnadviseSink,
607 };
608 
609 static HRESULT Compartment_Constructor(CompartmentValue *valueData, ITfCompartment **ppOut)
610 {
611     Compartment *This;
612 
613     This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(Compartment));
614     if (This == NULL)
615         return E_OUTOFMEMORY;
616 
617     This->ITfCompartment_iface.lpVtbl= &CompartmentVtbl;
618     This->ITfSource_iface.lpVtbl = &CompartmentSourceVtbl;
619     This->refCount = 1;
620 
621     This->valueData = valueData;
622     VariantInit(&This->variant);
623 
624     list_init(&This->CompartmentEventSink);
625 
626     *ppOut = &This->ITfCompartment_iface;
627     TRACE("returning %p\n", *ppOut);
628     return S_OK;
629 }
630