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