xref: /reactos/dll/win32/msctf/inputprocessor.c (revision 02e84521)
1 /*
2  *  ITfInputProcessorProfiles 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 "olectl.h"
36 
37 #include "wine/unicode.h"
38 
39 #include "msctf.h"
40 #include "msctf_internal.h"
41 
42 WINE_DEFAULT_DEBUG_CHANNEL(msctf);
43 
44 static const WCHAR szwLngp[] = {'L','a','n','g','u','a','g','e','P','r','o','f','i','l','e',0};
45 static const WCHAR szwEnable[] = {'E','n','a','b','l','e',0};
46 static const WCHAR szwTipfmt[] = {'%','s','\\','%','s',0};
47 static const WCHAR szwFullLangfmt[] = {'%','s','\\','%','s','\\','%','s','\\','0','x','%','0','8','x','\\','%','s',0};
48 
49 static const WCHAR szwAssemblies[] = {'A','s','s','e','m','b','l','i','e','s',0};
50 static const WCHAR szwDefault[] = {'D','e','f','a','u','l','t',0};
51 static const WCHAR szwProfile[] = {'P','r','o','f','i','l','e',0};
52 static const WCHAR szwDefaultFmt[] = {'%','s','\\','%','s','\\','0','x','%','0','8','x','\\','%','s',0};
53 
54 typedef struct tagInputProcessorProfiles {
55     ITfInputProcessorProfiles ITfInputProcessorProfiles_iface;
56     ITfSource ITfSource_iface;
57     ITfInputProcessorProfileMgr ITfInputProcessorProfileMgr_iface;
58     /* const ITfInputProcessorProfilesExVtbl *InputProcessorProfilesExVtbl; */
59     /* const ITfInputProcessorProfileSubstituteLayoutVtbl *InputProcessorProfileSubstituteLayoutVtbl; */
60     LONG refCount;
61 
62     LANGID  currentLanguage;
63 
64     struct list     LanguageProfileNotifySink;
65 } InputProcessorProfiles;
66 
67 typedef struct tagProfilesEnumGuid {
68     IEnumGUID IEnumGUID_iface;
69     LONG refCount;
70 
71     HKEY key;
72     DWORD next_index;
73 } ProfilesEnumGuid;
74 
75 typedef struct tagEnumTfLanguageProfiles {
76     IEnumTfLanguageProfiles IEnumTfLanguageProfiles_iface;
77     LONG refCount;
78 
79     HKEY    tipkey;
80     DWORD   tip_index;
81     WCHAR   szwCurrentClsid[39];
82 
83     HKEY    langkey;
84     DWORD   lang_index;
85 
86     LANGID  langid;
87     ITfCategoryMgr *catmgr;
88 } EnumTfLanguageProfiles;
89 
90 typedef struct {
91     IEnumTfInputProcessorProfiles IEnumTfInputProcessorProfiles_iface;
92     LONG ref;
93 } EnumTfInputProcessorProfiles;
94 
95 static HRESULT ProfilesEnumGuid_Constructor(IEnumGUID **ppOut);
96 static HRESULT EnumTfLanguageProfiles_Constructor(LANGID langid, EnumTfLanguageProfiles **out);
97 
98 static inline EnumTfInputProcessorProfiles *impl_from_IEnumTfInputProcessorProfiles(IEnumTfInputProcessorProfiles *iface)
99 {
100     return CONTAINING_RECORD(iface, EnumTfInputProcessorProfiles, IEnumTfInputProcessorProfiles_iface);
101 }
102 
103 static HRESULT WINAPI EnumTfInputProcessorProfiles_QueryInterface(IEnumTfInputProcessorProfiles *iface,
104         REFIID riid, void **ppv)
105 {
106     EnumTfInputProcessorProfiles *This = impl_from_IEnumTfInputProcessorProfiles(iface);
107 
108     if(IsEqualGUID(riid, &IID_IUnknown)) {
109         TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
110         *ppv = &This->IEnumTfInputProcessorProfiles_iface;
111     }else if(IsEqualGUID(riid, &IID_IEnumTfInputProcessorProfiles)) {
112         TRACE("(%p)->(IID_IEnumTfInputProcessorProfiles %p)\n", This, ppv);
113         *ppv = &This->IEnumTfInputProcessorProfiles_iface;
114     }else {
115         *ppv = NULL;
116         WARN("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
117         return E_NOINTERFACE;
118     }
119 
120     IUnknown_AddRef((IUnknown*)*ppv);
121     return S_OK;
122 }
123 
124 static ULONG WINAPI EnumTfInputProcessorProfiles_AddRef(IEnumTfInputProcessorProfiles *iface)
125 {
126     EnumTfInputProcessorProfiles *This = impl_from_IEnumTfInputProcessorProfiles(iface);
127     LONG ref = InterlockedIncrement(&This->ref);
128 
129     TRACE("(%p) ref=%d\n", This, ref);
130 
131     return ref;
132 }
133 
134 static ULONG WINAPI EnumTfInputProcessorProfiles_Release(IEnumTfInputProcessorProfiles *iface)
135 {
136     EnumTfInputProcessorProfiles *This = impl_from_IEnumTfInputProcessorProfiles(iface);
137     LONG ref = InterlockedDecrement(&This->ref);
138 
139     TRACE("(%p) ref=%d\n", This, ref);
140 
141     if(!ref)
142         HeapFree(GetProcessHeap(), 0, This);
143 
144     return ref;
145 }
146 
147 static HRESULT WINAPI EnumTfInputProcessorProfiles_Clone(IEnumTfInputProcessorProfiles *iface,
148         IEnumTfInputProcessorProfiles **ret)
149 {
150     EnumTfInputProcessorProfiles *This = impl_from_IEnumTfInputProcessorProfiles(iface);
151     FIXME("(%p)->(%p)\n", This, ret);
152     return E_NOTIMPL;
153 }
154 
155 static HRESULT WINAPI EnumTfInputProcessorProfiles_Next(IEnumTfInputProcessorProfiles *iface, ULONG count,
156         TF_INPUTPROCESSORPROFILE *profile, ULONG *fetch)
157 {
158     EnumTfInputProcessorProfiles *This = impl_from_IEnumTfInputProcessorProfiles(iface);
159 
160     FIXME("(%p)->(%u %p %p)\n", This, count, profile, fetch);
161 
162     if(fetch)
163         *fetch = 0;
164     return S_FALSE;
165 }
166 
167 static HRESULT WINAPI EnumTfInputProcessorProfiles_Reset(IEnumTfInputProcessorProfiles *iface)
168 {
169     EnumTfInputProcessorProfiles *This = impl_from_IEnumTfInputProcessorProfiles(iface);
170     FIXME("(%p)\n", This);
171     return E_NOTIMPL;
172 }
173 
174 static HRESULT WINAPI EnumTfInputProcessorProfiles_Skip(IEnumTfInputProcessorProfiles *iface, ULONG count)
175 {
176     EnumTfInputProcessorProfiles *This = impl_from_IEnumTfInputProcessorProfiles(iface);
177     FIXME("(%p)->(%u)\n", This, count);
178     return E_NOTIMPL;
179 }
180 
181 static const IEnumTfInputProcessorProfilesVtbl EnumTfInputProcessorProfilesVtbl = {
182     EnumTfInputProcessorProfiles_QueryInterface,
183     EnumTfInputProcessorProfiles_AddRef,
184     EnumTfInputProcessorProfiles_Release,
185     EnumTfInputProcessorProfiles_Clone,
186     EnumTfInputProcessorProfiles_Next,
187     EnumTfInputProcessorProfiles_Reset,
188     EnumTfInputProcessorProfiles_Skip
189 };
190 
191 static inline InputProcessorProfiles *impl_from_ITfInputProcessorProfiles(ITfInputProcessorProfiles *iface)
192 {
193     return CONTAINING_RECORD(iface, InputProcessorProfiles, ITfInputProcessorProfiles_iface);
194 }
195 
196 static inline InputProcessorProfiles *impl_from_ITfSource(ITfSource *iface)
197 {
198     return CONTAINING_RECORD(iface, InputProcessorProfiles, ITfSource_iface);
199 }
200 
201 static inline ProfilesEnumGuid *impl_from_IEnumGUID(IEnumGUID *iface)
202 {
203     return CONTAINING_RECORD(iface, ProfilesEnumGuid, IEnumGUID_iface);
204 }
205 
206 static inline EnumTfLanguageProfiles *impl_from_IEnumTfLanguageProfiles(IEnumTfLanguageProfiles *iface)
207 {
208     return CONTAINING_RECORD(iface, EnumTfLanguageProfiles, IEnumTfLanguageProfiles_iface);
209 }
210 
211 static void InputProcessorProfiles_Destructor(InputProcessorProfiles *This)
212 {
213     TRACE("destroying %p\n", This);
214 
215     free_sinks(&This->LanguageProfileNotifySink);
216     HeapFree(GetProcessHeap(),0,This);
217 }
218 
219 static void add_userkey( REFCLSID rclsid, LANGID langid,
220                                 REFGUID guidProfile)
221 {
222     HKEY key;
223     WCHAR buf[39];
224     WCHAR buf2[39];
225     WCHAR fullkey[168];
226     DWORD disposition = 0;
227     ULONG res;
228 
229     TRACE("\n");
230 
231     StringFromGUID2(rclsid, buf, 39);
232     StringFromGUID2(guidProfile, buf2, 39);
233     sprintfW(fullkey,szwFullLangfmt,szwSystemTIPKey,buf,szwLngp,langid,buf2);
234 
235     res = RegCreateKeyExW(HKEY_CURRENT_USER,fullkey, 0, NULL, 0,
236                    KEY_READ | KEY_WRITE, NULL, &key, &disposition);
237 
238     if (!res && disposition == REG_CREATED_NEW_KEY)
239     {
240         DWORD zero = 0x0;
241         RegSetValueExW(key, szwEnable, 0, REG_DWORD, (LPBYTE)&zero, sizeof(DWORD));
242     }
243 
244     if (!res)
245         RegCloseKey(key);
246 }
247 
248 static HRESULT WINAPI InputProcessorProfiles_QueryInterface(ITfInputProcessorProfiles *iface, REFIID iid, void **ppv)
249 {
250     InputProcessorProfiles *This = impl_from_ITfInputProcessorProfiles(iface);
251 
252     if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_ITfInputProcessorProfiles))
253     {
254         *ppv = &This->ITfInputProcessorProfiles_iface;
255     }
256     else if (IsEqualIID(iid, &IID_ITfInputProcessorProfileMgr))
257     {
258         *ppv = &This->ITfInputProcessorProfileMgr_iface;
259     }
260     else if (IsEqualIID(iid, &IID_ITfSource))
261     {
262         *ppv = &This->ITfSource_iface;
263     }
264     else
265     {
266         *ppv = NULL;
267         WARN("unsupported interface: %s\n", debugstr_guid(iid));
268         return E_NOINTERFACE;
269     }
270 
271     ITfInputProcessorProfiles_AddRef(iface);
272     return S_OK;
273 }
274 
275 static ULONG WINAPI InputProcessorProfiles_AddRef(ITfInputProcessorProfiles *iface)
276 {
277     InputProcessorProfiles *This = impl_from_ITfInputProcessorProfiles(iface);
278     return InterlockedIncrement(&This->refCount);
279 }
280 
281 static ULONG WINAPI InputProcessorProfiles_Release(ITfInputProcessorProfiles *iface)
282 {
283     InputProcessorProfiles *This = impl_from_ITfInputProcessorProfiles(iface);
284     ULONG ret;
285 
286     ret = InterlockedDecrement(&This->refCount);
287     if (ret == 0)
288         InputProcessorProfiles_Destructor(This);
289     return ret;
290 }
291 
292 /*****************************************************
293  * ITfInputProcessorProfiles functions
294  *****************************************************/
295 static HRESULT WINAPI InputProcessorProfiles_Register(
296         ITfInputProcessorProfiles *iface, REFCLSID rclsid)
297 {
298     InputProcessorProfiles *This = impl_from_ITfInputProcessorProfiles(iface);
299     HKEY tipkey;
300     WCHAR buf[39];
301     WCHAR fullkey[68];
302 
303     TRACE("(%p) %s\n",This,debugstr_guid(rclsid));
304 
305     StringFromGUID2(rclsid, buf, 39);
306     sprintfW(fullkey,szwTipfmt,szwSystemTIPKey,buf);
307 
308     if (RegCreateKeyExW(HKEY_LOCAL_MACHINE,fullkey, 0, NULL, 0,
309                     KEY_READ | KEY_WRITE, NULL, &tipkey, NULL) != ERROR_SUCCESS)
310         return E_FAIL;
311 
312     RegCloseKey(tipkey);
313 
314     return S_OK;
315 }
316 
317 static HRESULT WINAPI InputProcessorProfiles_Unregister(
318         ITfInputProcessorProfiles *iface, REFCLSID rclsid)
319 {
320     InputProcessorProfiles *This = impl_from_ITfInputProcessorProfiles(iface);
321     WCHAR buf[39];
322     WCHAR fullkey[68];
323 
324     TRACE("(%p) %s\n",This,debugstr_guid(rclsid));
325 
326     StringFromGUID2(rclsid, buf, 39);
327     sprintfW(fullkey,szwTipfmt,szwSystemTIPKey,buf);
328 
329     RegDeleteTreeW(HKEY_LOCAL_MACHINE, fullkey);
330     RegDeleteTreeW(HKEY_CURRENT_USER, fullkey);
331 
332     return S_OK;
333 }
334 
335 static HRESULT WINAPI InputProcessorProfiles_AddLanguageProfile(
336         ITfInputProcessorProfiles *iface, REFCLSID rclsid,
337         LANGID langid, REFGUID guidProfile, const WCHAR *pchDesc,
338         ULONG cchDesc, const WCHAR *pchIconFile, ULONG cchFile,
339         ULONG uIconIndex)
340 {
341     InputProcessorProfiles *This = impl_from_ITfInputProcessorProfiles(iface);
342     HKEY tipkey,fmtkey;
343     WCHAR buf[39];
344     WCHAR fullkey[100];
345     ULONG res;
346     DWORD disposition = 0;
347 
348     static const WCHAR fmt2[] = {'%','s','\\','0','x','%','0','8','x','\\','%','s',0};
349     static const WCHAR desc[] = {'D','e','s','c','r','i','p','t','i','o','n',0};
350     static const WCHAR icnf[] = {'I','c','o','n','F','i','l','e',0};
351     static const WCHAR icni[] = {'I','c','o','n','I','n','d','e','x',0};
352 
353     TRACE("(%p) %s %x %s %s %s %i\n",This,debugstr_guid(rclsid), langid,
354             debugstr_guid(guidProfile), debugstr_wn(pchDesc,cchDesc),
355             debugstr_wn(pchIconFile,cchFile),uIconIndex);
356 
357     StringFromGUID2(rclsid, buf, 39);
358     sprintfW(fullkey,szwTipfmt,szwSystemTIPKey,buf);
359 
360     if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,fullkey, 0, KEY_READ | KEY_WRITE,
361                 &tipkey ) != ERROR_SUCCESS)
362         return E_FAIL;
363 
364     StringFromGUID2(guidProfile, buf, 39);
365     sprintfW(fullkey,fmt2,szwLngp,langid,buf);
366 
367     res = RegCreateKeyExW(tipkey,fullkey, 0, NULL, 0, KEY_READ | KEY_WRITE,
368             NULL, &fmtkey, &disposition);
369 
370     if (!res)
371     {
372         DWORD zero = 0x0;
373         RegSetValueExW(fmtkey, desc, 0, REG_SZ, (const BYTE*)pchDesc, cchDesc * sizeof(WCHAR));
374         RegSetValueExW(fmtkey, icnf, 0, REG_SZ, (const BYTE*)pchIconFile, cchFile * sizeof(WCHAR));
375         RegSetValueExW(fmtkey, icni, 0, REG_DWORD, (LPBYTE)&uIconIndex, sizeof(DWORD));
376         if (disposition == REG_CREATED_NEW_KEY)
377             RegSetValueExW(fmtkey, szwEnable, 0, REG_DWORD, (LPBYTE)&zero, sizeof(DWORD));
378         RegCloseKey(fmtkey);
379 
380         add_userkey(rclsid, langid, guidProfile);
381     }
382     RegCloseKey(tipkey);
383 
384     if (!res)
385         return S_OK;
386     else
387         return E_FAIL;
388 }
389 
390 static HRESULT WINAPI InputProcessorProfiles_RemoveLanguageProfile(
391         ITfInputProcessorProfiles *iface, REFCLSID rclsid, LANGID langid,
392         REFGUID guidProfile)
393 {
394     InputProcessorProfiles *This = impl_from_ITfInputProcessorProfiles(iface);
395     FIXME("STUB:(%p)\n",This);
396     return E_NOTIMPL;
397 }
398 
399 static HRESULT WINAPI InputProcessorProfiles_EnumInputProcessorInfo(
400         ITfInputProcessorProfiles *iface, IEnumGUID **ppEnum)
401 {
402     InputProcessorProfiles *This = impl_from_ITfInputProcessorProfiles(iface);
403     TRACE("(%p) %p\n",This,ppEnum);
404     return ProfilesEnumGuid_Constructor(ppEnum);
405 }
406 
407 static HRESULT WINAPI InputProcessorProfiles_GetDefaultLanguageProfile(
408         ITfInputProcessorProfiles *iface, LANGID langid, REFGUID catid,
409         CLSID *pclsid, GUID *pguidProfile)
410 {
411     InputProcessorProfiles *This = impl_from_ITfInputProcessorProfiles(iface);
412     WCHAR fullkey[168];
413     WCHAR buf[39];
414     HKEY hkey;
415     DWORD count;
416     ULONG res;
417 
418     TRACE("%p) %x %s %p %p\n",This, langid, debugstr_guid(catid),pclsid,pguidProfile);
419 
420     if (!catid || !pclsid || !pguidProfile)
421         return E_INVALIDARG;
422 
423     StringFromGUID2(catid, buf, 39);
424     sprintfW(fullkey, szwDefaultFmt, szwSystemCTFKey, szwAssemblies, langid, buf);
425 
426     if (RegOpenKeyExW(HKEY_CURRENT_USER, fullkey, 0, KEY_READ | KEY_WRITE,
427                 &hkey ) != ERROR_SUCCESS)
428         return S_FALSE;
429 
430     count = sizeof(buf);
431     res = RegQueryValueExW(hkey, szwDefault, 0, NULL, (LPBYTE)buf, &count);
432     if (res != ERROR_SUCCESS)
433     {
434         RegCloseKey(hkey);
435         return S_FALSE;
436     }
437     CLSIDFromString(buf,pclsid);
438 
439     res = RegQueryValueExW(hkey, szwProfile, 0, NULL, (LPBYTE)buf, &count);
440     if (res == ERROR_SUCCESS)
441         CLSIDFromString(buf,pguidProfile);
442 
443     RegCloseKey(hkey);
444 
445     return S_OK;
446 }
447 
448 static HRESULT WINAPI InputProcessorProfiles_SetDefaultLanguageProfile(
449         ITfInputProcessorProfiles *iface, LANGID langid, REFCLSID rclsid,
450         REFGUID guidProfiles)
451 {
452     InputProcessorProfiles *This = impl_from_ITfInputProcessorProfiles(iface);
453     WCHAR fullkey[168];
454     WCHAR buf[39];
455     HKEY hkey;
456     GUID catid;
457     HRESULT hr;
458     ITfCategoryMgr *catmgr;
459     static const GUID * tipcats[3] = { &GUID_TFCAT_TIP_KEYBOARD,
460                                        &GUID_TFCAT_TIP_SPEECH,
461                                        &GUID_TFCAT_TIP_HANDWRITING };
462 
463     TRACE("%p) %x %s %s\n",This, langid, debugstr_guid(rclsid),debugstr_guid(guidProfiles));
464 
465     if (!rclsid || !guidProfiles)
466         return E_INVALIDARG;
467 
468     hr = CategoryMgr_Constructor(NULL,(IUnknown**)&catmgr);
469 
470     if (FAILED(hr))
471         return hr;
472 
473     if (ITfCategoryMgr_FindClosestCategory(catmgr, rclsid,
474             &catid, tipcats, 3) != S_OK)
475         hr = ITfCategoryMgr_FindClosestCategory(catmgr, rclsid,
476                 &catid, NULL, 0);
477     ITfCategoryMgr_Release(catmgr);
478 
479     if (FAILED(hr))
480         return E_FAIL;
481 
482     StringFromGUID2(&catid, buf, 39);
483     sprintfW(fullkey, szwDefaultFmt, szwSystemCTFKey, szwAssemblies, langid, buf);
484 
485     if (RegCreateKeyExW(HKEY_CURRENT_USER, fullkey, 0, NULL, 0, KEY_READ | KEY_WRITE,
486                 NULL, &hkey, NULL ) != ERROR_SUCCESS)
487         return E_FAIL;
488 
489     StringFromGUID2(rclsid, buf, 39);
490     RegSetValueExW(hkey, szwDefault, 0, REG_SZ, (LPBYTE)buf, sizeof(buf));
491     StringFromGUID2(guidProfiles, buf, 39);
492     RegSetValueExW(hkey, szwProfile, 0, REG_SZ, (LPBYTE)buf, sizeof(buf));
493     RegCloseKey(hkey);
494 
495     return S_OK;
496 }
497 
498 static HRESULT WINAPI InputProcessorProfiles_ActivateLanguageProfile(
499         ITfInputProcessorProfiles *iface, REFCLSID rclsid, LANGID langid,
500         REFGUID guidProfiles)
501 {
502     InputProcessorProfiles *This = impl_from_ITfInputProcessorProfiles(iface);
503     HRESULT hr;
504     BOOL enabled;
505     TF_LANGUAGEPROFILE LanguageProfile;
506 
507     TRACE("(%p) %s %x %s\n",This,debugstr_guid(rclsid),langid,debugstr_guid(guidProfiles));
508 
509     if (langid != This->currentLanguage) return E_INVALIDARG;
510 
511     if (get_active_textservice(rclsid,NULL))
512     {
513         TRACE("Already Active\n");
514         return E_FAIL;
515     }
516 
517     hr = ITfInputProcessorProfiles_IsEnabledLanguageProfile(iface, rclsid,
518             langid, guidProfiles, &enabled);
519     if (FAILED(hr) || !enabled)
520     {
521         TRACE("Not Enabled\n");
522         return E_FAIL;
523     }
524 
525     LanguageProfile.clsid = *rclsid;
526     LanguageProfile.langid = langid;
527     LanguageProfile.guidProfile = *guidProfiles;
528     LanguageProfile.fActive = TRUE;
529 
530     return add_active_textservice(&LanguageProfile);
531 }
532 
533 static HRESULT WINAPI InputProcessorProfiles_GetActiveLanguageProfile(
534         ITfInputProcessorProfiles *iface, REFCLSID rclsid, LANGID *plangid,
535         GUID *pguidProfile)
536 {
537     InputProcessorProfiles *This = impl_from_ITfInputProcessorProfiles(iface);
538     TF_LANGUAGEPROFILE profile;
539 
540     TRACE("(%p) %s %p %p\n",This,debugstr_guid(rclsid),plangid,pguidProfile);
541 
542     if (!rclsid || !plangid || !pguidProfile)
543         return E_INVALIDARG;
544 
545     if (get_active_textservice(rclsid, &profile))
546     {
547         *plangid = profile.langid;
548         *pguidProfile = profile.guidProfile;
549         return S_OK;
550     }
551     else
552     {
553         *pguidProfile = GUID_NULL;
554         return S_FALSE;
555     }
556 }
557 
558 static HRESULT WINAPI InputProcessorProfiles_GetLanguageProfileDescription(
559         ITfInputProcessorProfiles *iface, REFCLSID rclsid, LANGID langid,
560         REFGUID guidProfile, BSTR *pbstrProfile)
561 {
562     InputProcessorProfiles *This = impl_from_ITfInputProcessorProfiles(iface);
563     FIXME("STUB:(%p)\n",This);
564     return E_NOTIMPL;
565 }
566 
567 static HRESULT WINAPI InputProcessorProfiles_GetCurrentLanguage(
568         ITfInputProcessorProfiles *iface, LANGID *plangid)
569 {
570     InputProcessorProfiles *This = impl_from_ITfInputProcessorProfiles(iface);
571     TRACE("(%p) 0x%x\n",This,This->currentLanguage);
572 
573     if (!plangid)
574         return E_INVALIDARG;
575 
576     *plangid = This->currentLanguage;
577 
578     return S_OK;
579 }
580 
581 static HRESULT WINAPI InputProcessorProfiles_ChangeCurrentLanguage(
582         ITfInputProcessorProfiles *iface, LANGID langid)
583 {
584     InputProcessorProfiles *This = impl_from_ITfInputProcessorProfiles(iface);
585     ITfLanguageProfileNotifySink *sink;
586     struct list *cursor;
587     BOOL accept;
588 
589     FIXME("STUB:(%p)\n",This);
590 
591     SINK_FOR_EACH(cursor, &This->LanguageProfileNotifySink, ITfLanguageProfileNotifySink, sink)
592     {
593         accept = TRUE;
594         ITfLanguageProfileNotifySink_OnLanguageChange(sink, langid, &accept);
595         if (!accept)
596             return E_FAIL;
597     }
598 
599     /* TODO:  On successful language change call OnLanguageChanged sink */
600     return E_NOTIMPL;
601 }
602 
603 static HRESULT WINAPI InputProcessorProfiles_GetLanguageList(
604         ITfInputProcessorProfiles *iface, LANGID **ppLangId, ULONG *pulCount)
605 {
606     InputProcessorProfiles *This = impl_from_ITfInputProcessorProfiles(iface);
607     FIXME("Semi-STUB:(%p)\n",This);
608     *ppLangId = CoTaskMemAlloc(sizeof(LANGID));
609     **ppLangId = This->currentLanguage;
610     *pulCount = 1;
611     return S_OK;
612 }
613 
614 static HRESULT WINAPI InputProcessorProfiles_EnumLanguageProfiles(
615         ITfInputProcessorProfiles *iface, LANGID langid,
616         IEnumTfLanguageProfiles **ppEnum)
617 {
618     InputProcessorProfiles *This = impl_from_ITfInputProcessorProfiles(iface);
619     EnumTfLanguageProfiles *profenum;
620     HRESULT hr;
621 
622     TRACE("(%p) %x %p\n",This,langid,ppEnum);
623 
624     if (!ppEnum)
625         return E_INVALIDARG;
626     hr = EnumTfLanguageProfiles_Constructor(langid, &profenum);
627     *ppEnum = &profenum->IEnumTfLanguageProfiles_iface;
628 
629     return hr;
630 }
631 
632 static HRESULT WINAPI InputProcessorProfiles_EnableLanguageProfile(
633         ITfInputProcessorProfiles *iface, REFCLSID rclsid, LANGID langid,
634         REFGUID guidProfile, BOOL fEnable)
635 {
636     InputProcessorProfiles *This = impl_from_ITfInputProcessorProfiles(iface);
637     HKEY key;
638     WCHAR buf[39];
639     WCHAR buf2[39];
640     WCHAR fullkey[168];
641     ULONG res;
642 
643     TRACE("(%p) %s %x %s %i\n",This, debugstr_guid(rclsid), langid, debugstr_guid(guidProfile), fEnable);
644 
645     StringFromGUID2(rclsid, buf, 39);
646     StringFromGUID2(guidProfile, buf2, 39);
647     sprintfW(fullkey,szwFullLangfmt,szwSystemTIPKey,buf,szwLngp,langid,buf2);
648 
649     res = RegOpenKeyExW(HKEY_CURRENT_USER, fullkey, 0, KEY_READ | KEY_WRITE, &key);
650 
651     if (!res)
652     {
653         RegSetValueExW(key, szwEnable, 0, REG_DWORD, (LPBYTE)&fEnable, sizeof(DWORD));
654         RegCloseKey(key);
655     }
656     else
657         return E_FAIL;
658 
659     return S_OK;
660 }
661 
662 static HRESULT WINAPI InputProcessorProfiles_IsEnabledLanguageProfile(
663         ITfInputProcessorProfiles *iface, REFCLSID rclsid, LANGID langid,
664         REFGUID guidProfile, BOOL *pfEnable)
665 {
666     InputProcessorProfiles *This = impl_from_ITfInputProcessorProfiles(iface);
667     HKEY key;
668     WCHAR buf[39];
669     WCHAR buf2[39];
670     WCHAR fullkey[168];
671     ULONG res;
672 
673     TRACE("(%p) %s, %i, %s, %p\n",This,debugstr_guid(rclsid),langid,debugstr_guid(guidProfile),pfEnable);
674 
675     if (!pfEnable)
676         return E_INVALIDARG;
677 
678     StringFromGUID2(rclsid, buf, 39);
679     StringFromGUID2(guidProfile, buf2, 39);
680     sprintfW(fullkey,szwFullLangfmt,szwSystemTIPKey,buf,szwLngp,langid,buf2);
681 
682     res = RegOpenKeyExW(HKEY_CURRENT_USER, fullkey, 0, KEY_READ | KEY_WRITE, &key);
683 
684     if (!res)
685     {
686         DWORD count = sizeof(DWORD);
687         res = RegQueryValueExW(key, szwEnable, 0, NULL, (LPBYTE)pfEnable, &count);
688         RegCloseKey(key);
689     }
690 
691     if (res)  /* Try Default */
692     {
693         res = RegOpenKeyExW(HKEY_LOCAL_MACHINE, fullkey, 0, KEY_READ | KEY_WRITE, &key);
694 
695         if (!res)
696         {
697             DWORD count = sizeof(DWORD);
698             res = RegQueryValueExW(key, szwEnable, 0, NULL, (LPBYTE)pfEnable, &count);
699             RegCloseKey(key);
700         }
701     }
702 
703     if (!res)
704         return S_OK;
705     else
706         return E_FAIL;
707 }
708 
709 static HRESULT WINAPI InputProcessorProfiles_EnableLanguageProfileByDefault(
710         ITfInputProcessorProfiles *iface, REFCLSID rclsid, LANGID langid,
711         REFGUID guidProfile, BOOL fEnable)
712 {
713     InputProcessorProfiles *This = impl_from_ITfInputProcessorProfiles(iface);
714     HKEY key;
715     WCHAR buf[39];
716     WCHAR buf2[39];
717     WCHAR fullkey[168];
718     ULONG res;
719 
720     TRACE("(%p) %s %x %s %i\n",This,debugstr_guid(rclsid),langid,debugstr_guid(guidProfile),fEnable);
721 
722     StringFromGUID2(rclsid, buf, 39);
723     StringFromGUID2(guidProfile, buf2, 39);
724     sprintfW(fullkey,szwFullLangfmt,szwSystemTIPKey,buf,szwLngp,langid,buf2);
725 
726     res = RegOpenKeyExW(HKEY_LOCAL_MACHINE, fullkey, 0, KEY_READ | KEY_WRITE, &key);
727 
728     if (!res)
729     {
730         RegSetValueExW(key, szwEnable, 0, REG_DWORD, (LPBYTE)&fEnable, sizeof(DWORD));
731         RegCloseKey(key);
732     }
733     else
734         return E_FAIL;
735 
736     return S_OK;
737 }
738 
739 static HRESULT WINAPI InputProcessorProfiles_SubstituteKeyboardLayout(
740         ITfInputProcessorProfiles *iface, REFCLSID rclsid, LANGID langid,
741         REFGUID guidProfile, HKL hKL)
742 {
743     InputProcessorProfiles *This = impl_from_ITfInputProcessorProfiles(iface);
744     FIXME("STUB:(%p)\n",This);
745     return E_NOTIMPL;
746 }
747 
748 static const ITfInputProcessorProfilesVtbl InputProcessorProfilesVtbl =
749 {
750     InputProcessorProfiles_QueryInterface,
751     InputProcessorProfiles_AddRef,
752     InputProcessorProfiles_Release,
753     InputProcessorProfiles_Register,
754     InputProcessorProfiles_Unregister,
755     InputProcessorProfiles_AddLanguageProfile,
756     InputProcessorProfiles_RemoveLanguageProfile,
757     InputProcessorProfiles_EnumInputProcessorInfo,
758     InputProcessorProfiles_GetDefaultLanguageProfile,
759     InputProcessorProfiles_SetDefaultLanguageProfile,
760     InputProcessorProfiles_ActivateLanguageProfile,
761     InputProcessorProfiles_GetActiveLanguageProfile,
762     InputProcessorProfiles_GetLanguageProfileDescription,
763     InputProcessorProfiles_GetCurrentLanguage,
764     InputProcessorProfiles_ChangeCurrentLanguage,
765     InputProcessorProfiles_GetLanguageList,
766     InputProcessorProfiles_EnumLanguageProfiles,
767     InputProcessorProfiles_EnableLanguageProfile,
768     InputProcessorProfiles_IsEnabledLanguageProfile,
769     InputProcessorProfiles_EnableLanguageProfileByDefault,
770     InputProcessorProfiles_SubstituteKeyboardLayout
771 };
772 
773 static inline InputProcessorProfiles *impl_from_ITfInputProcessorProfileMgr(ITfInputProcessorProfileMgr *iface)
774 {
775     return CONTAINING_RECORD(iface, InputProcessorProfiles, ITfInputProcessorProfileMgr_iface);
776 }
777 
778 static HRESULT WINAPI InputProcessorProfileMgr_QueryInterface(ITfInputProcessorProfileMgr *iface, REFIID riid, void **ppv)
779 {
780     InputProcessorProfiles *This = impl_from_ITfInputProcessorProfileMgr(iface);
781     return ITfInputProcessorProfiles_QueryInterface(&This->ITfInputProcessorProfiles_iface, riid, ppv);
782 }
783 
784 static ULONG WINAPI InputProcessorProfileMgr_AddRef(ITfInputProcessorProfileMgr *iface)
785 {
786     InputProcessorProfiles *This = impl_from_ITfInputProcessorProfileMgr(iface);
787     return ITfInputProcessorProfiles_AddRef(&This->ITfInputProcessorProfiles_iface);
788 }
789 
790 static ULONG WINAPI InputProcessorProfileMgr_Release(ITfInputProcessorProfileMgr *iface)
791 {
792     InputProcessorProfiles *This = impl_from_ITfInputProcessorProfileMgr(iface);
793     return ITfInputProcessorProfiles_Release(&This->ITfInputProcessorProfiles_iface);
794 }
795 
796 static HRESULT WINAPI InputProcessorProfileMgr_ActivateProfile(ITfInputProcessorProfileMgr *iface, DWORD dwProfileType,
797         LANGID langid, REFCLSID clsid, REFGUID guidProfile, HKL hkl, DWORD dwFlags)
798 {
799     InputProcessorProfiles *This = impl_from_ITfInputProcessorProfileMgr(iface);
800     FIXME("(%p)->(%d %x %s %s %p %x)\n", This, dwProfileType, langid, debugstr_guid(clsid),
801           debugstr_guid(guidProfile), hkl, dwFlags);
802     return E_NOTIMPL;
803 }
804 
805 static HRESULT WINAPI InputProcessorProfileMgr_DeactivateProfile(ITfInputProcessorProfileMgr *iface, DWORD dwProfileType,
806         LANGID langid, REFCLSID clsid, REFGUID guidProfile, HKL hkl, DWORD dwFlags)
807 {
808     InputProcessorProfiles *This = impl_from_ITfInputProcessorProfileMgr(iface);
809     FIXME("(%p)->(%d %x %s %s %p %x)\n", This, dwProfileType, langid, debugstr_guid(clsid),
810           debugstr_guid(guidProfile), hkl, dwFlags);
811     return E_NOTIMPL;
812 }
813 
814 static HRESULT WINAPI InputProcessorProfileMgr_GetProfile(ITfInputProcessorProfileMgr *iface, DWORD dwProfileType,
815         LANGID langid, REFCLSID clsid, REFGUID guidProfile, HKL hkl, TF_INPUTPROCESSORPROFILE *pProfile)
816 {
817     InputProcessorProfiles *This = impl_from_ITfInputProcessorProfileMgr(iface);
818     FIXME("(%p)->(%d %x %s %s %p %p)\n", This, dwProfileType, langid, debugstr_guid(clsid),
819           debugstr_guid(guidProfile), hkl, pProfile);
820     return E_NOTIMPL;
821 }
822 
823 static HRESULT WINAPI InputProcessorProfileMgr_EnumProfiles(ITfInputProcessorProfileMgr *iface, LANGID langid,
824         IEnumTfInputProcessorProfiles **ppEnum)
825 {
826     InputProcessorProfiles *This = impl_from_ITfInputProcessorProfileMgr(iface);
827     EnumTfInputProcessorProfiles *enum_profiles;
828 
829     TRACE("(%p)->(%x %p)\n", This, langid, ppEnum);
830 
831     enum_profiles = HeapAlloc(GetProcessHeap(), 0, sizeof(*enum_profiles));
832     if(!enum_profiles)
833         return E_OUTOFMEMORY;
834 
835     enum_profiles->IEnumTfInputProcessorProfiles_iface.lpVtbl = &EnumTfInputProcessorProfilesVtbl;
836     enum_profiles->ref = 1;
837 
838     *ppEnum = &enum_profiles->IEnumTfInputProcessorProfiles_iface;
839     return S_OK;
840 }
841 
842 static HRESULT WINAPI InputProcessorProfileMgr_ReleaseInputProcessor(ITfInputProcessorProfileMgr *iface, REFCLSID rclsid,
843         DWORD dwFlags)
844 {
845     InputProcessorProfiles *This = impl_from_ITfInputProcessorProfileMgr(iface);
846     FIXME("(%p)->(%s %x)\n", This, debugstr_guid(rclsid), dwFlags);
847     return E_NOTIMPL;
848 }
849 
850 static HRESULT WINAPI InputProcessorProfileMgr_RegisterProfile(ITfInputProcessorProfileMgr *iface, REFCLSID rclsid,
851         LANGID langid, REFGUID guidProfile, const WCHAR *pchDesc, ULONG cchDesc, const WCHAR *pchIconFile,
852         ULONG cchFile, ULONG uIconIndex, HKL hklsubstitute, DWORD dwPreferredLayout, BOOL bEnabledByDefault,
853         DWORD dwFlags)
854 {
855     InputProcessorProfiles *This = impl_from_ITfInputProcessorProfileMgr(iface);
856     FIXME("(%p)->(%s %x %s %s %d %s %u %u %p %x %x %x)\n", This, debugstr_guid(rclsid), langid, debugstr_guid(guidProfile),
857           debugstr_w(pchDesc), cchDesc, debugstr_w(pchIconFile), cchFile, uIconIndex, hklsubstitute, dwPreferredLayout,
858           bEnabledByDefault, dwFlags);
859     return E_NOTIMPL;
860 }
861 
862 static HRESULT WINAPI InputProcessorProfileMgr_UnregisterProfile(ITfInputProcessorProfileMgr *iface, REFCLSID rclsid,
863         LANGID langid, REFGUID guidProfile, DWORD dwFlags)
864 {
865     InputProcessorProfiles *This = impl_from_ITfInputProcessorProfileMgr(iface);
866     FIXME("(%p)->(%s %x %s %x)\n", This, debugstr_guid(rclsid), langid, debugstr_guid(guidProfile), dwFlags);
867     return E_NOTIMPL;
868 }
869 
870 static HRESULT WINAPI InputProcessorProfileMgr_GetActiveProfile(ITfInputProcessorProfileMgr *iface, REFGUID catid,
871         TF_INPUTPROCESSORPROFILE *pProfile)
872 {
873     InputProcessorProfiles *This = impl_from_ITfInputProcessorProfileMgr(iface);
874     FIXME("(%p)->(%s %p)\n", This, debugstr_guid(catid), pProfile);
875     return E_NOTIMPL;
876 }
877 
878 static const ITfInputProcessorProfileMgrVtbl InputProcessorProfileMgrVtbl = {
879     InputProcessorProfileMgr_QueryInterface,
880     InputProcessorProfileMgr_AddRef,
881     InputProcessorProfileMgr_Release,
882     InputProcessorProfileMgr_ActivateProfile,
883     InputProcessorProfileMgr_DeactivateProfile,
884     InputProcessorProfileMgr_GetProfile,
885     InputProcessorProfileMgr_EnumProfiles,
886     InputProcessorProfileMgr_ReleaseInputProcessor,
887     InputProcessorProfileMgr_RegisterProfile,
888     InputProcessorProfileMgr_UnregisterProfile,
889     InputProcessorProfileMgr_GetActiveProfile
890 };
891 
892 /*****************************************************
893  * ITfSource functions
894  *****************************************************/
895 static HRESULT WINAPI IPPSource_QueryInterface(ITfSource *iface, REFIID iid, LPVOID *ppvOut)
896 {
897     InputProcessorProfiles *This = impl_from_ITfSource(iface);
898     return ITfInputProcessorProfiles_QueryInterface(&This->ITfInputProcessorProfiles_iface, iid, ppvOut);
899 }
900 
901 static ULONG WINAPI IPPSource_AddRef(ITfSource *iface)
902 {
903     InputProcessorProfiles *This = impl_from_ITfSource(iface);
904     return ITfInputProcessorProfiles_AddRef(&This->ITfInputProcessorProfiles_iface);
905 }
906 
907 static ULONG WINAPI IPPSource_Release(ITfSource *iface)
908 {
909     InputProcessorProfiles *This = impl_from_ITfSource(iface);
910     return ITfInputProcessorProfiles_Release(&This->ITfInputProcessorProfiles_iface);
911 }
912 
913 static HRESULT WINAPI IPPSource_AdviseSink(ITfSource *iface,
914         REFIID riid, IUnknown *punk, DWORD *pdwCookie)
915 {
916     InputProcessorProfiles *This = impl_from_ITfSource(iface);
917 
918     TRACE("(%p) %s %p %p\n",This,debugstr_guid(riid),punk,pdwCookie);
919 
920     if (!riid || !punk || !pdwCookie)
921         return E_INVALIDARG;
922 
923     if (IsEqualIID(riid, &IID_ITfLanguageProfileNotifySink))
924         return advise_sink(&This->LanguageProfileNotifySink, &IID_ITfLanguageProfileNotifySink,
925                            COOKIE_MAGIC_IPPSINK, punk, pdwCookie);
926 
927     FIXME("(%p) Unhandled Sink: %s\n",This,debugstr_guid(riid));
928     return E_NOTIMPL;
929 }
930 
931 static HRESULT WINAPI IPPSource_UnadviseSink(ITfSource *iface, DWORD pdwCookie)
932 {
933     InputProcessorProfiles *This = impl_from_ITfSource(iface);
934 
935     TRACE("(%p) %x\n",This,pdwCookie);
936 
937     if (get_Cookie_magic(pdwCookie)!=COOKIE_MAGIC_IPPSINK)
938         return E_INVALIDARG;
939 
940     return unadvise_sink(pdwCookie);
941 }
942 
943 static const ITfSourceVtbl InputProcessorProfilesSourceVtbl =
944 {
945     IPPSource_QueryInterface,
946     IPPSource_AddRef,
947     IPPSource_Release,
948     IPPSource_AdviseSink,
949     IPPSource_UnadviseSink,
950 };
951 
952 HRESULT InputProcessorProfiles_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut)
953 {
954     InputProcessorProfiles *This;
955     if (pUnkOuter)
956         return CLASS_E_NOAGGREGATION;
957 
958     This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(InputProcessorProfiles));
959     if (This == NULL)
960         return E_OUTOFMEMORY;
961 
962     This->ITfInputProcessorProfiles_iface.lpVtbl= &InputProcessorProfilesVtbl;
963     This->ITfSource_iface.lpVtbl = &InputProcessorProfilesSourceVtbl;
964     This->ITfInputProcessorProfileMgr_iface.lpVtbl = &InputProcessorProfileMgrVtbl;
965     This->refCount = 1;
966     This->currentLanguage = GetUserDefaultLCID();
967 
968     list_init(&This->LanguageProfileNotifySink);
969 
970     *ppOut = (IUnknown *)&This->ITfInputProcessorProfiles_iface;
971     TRACE("returning %p\n", *ppOut);
972     return S_OK;
973 }
974 
975 /**************************************************
976  * IEnumGUID implementation for ITfInputProcessorProfiles::EnumInputProcessorInfo
977  **************************************************/
978 static void ProfilesEnumGuid_Destructor(ProfilesEnumGuid *This)
979 {
980     TRACE("destroying %p\n", This);
981     RegCloseKey(This->key);
982     HeapFree(GetProcessHeap(),0,This);
983 }
984 
985 static HRESULT WINAPI ProfilesEnumGuid_QueryInterface(IEnumGUID *iface, REFIID iid, LPVOID *ppvOut)
986 {
987     ProfilesEnumGuid *This = impl_from_IEnumGUID(iface);
988     *ppvOut = NULL;
989 
990     if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_IEnumGUID))
991     {
992         *ppvOut = &This->IEnumGUID_iface;
993     }
994 
995     if (*ppvOut)
996     {
997         IEnumGUID_AddRef(iface);
998         return S_OK;
999     }
1000 
1001     WARN("unsupported interface: %s\n", debugstr_guid(iid));
1002     return E_NOINTERFACE;
1003 }
1004 
1005 static ULONG WINAPI ProfilesEnumGuid_AddRef(IEnumGUID *iface)
1006 {
1007     ProfilesEnumGuid *This = impl_from_IEnumGUID(iface);
1008     return InterlockedIncrement(&This->refCount);
1009 }
1010 
1011 static ULONG WINAPI ProfilesEnumGuid_Release(IEnumGUID *iface)
1012 {
1013     ProfilesEnumGuid *This = impl_from_IEnumGUID(iface);
1014     ULONG ret;
1015 
1016     ret = InterlockedDecrement(&This->refCount);
1017     if (ret == 0)
1018         ProfilesEnumGuid_Destructor(This);
1019     return ret;
1020 }
1021 
1022 /*****************************************************
1023  * IEnumGuid functions
1024  *****************************************************/
1025 static HRESULT WINAPI ProfilesEnumGuid_Next( LPENUMGUID iface,
1026     ULONG celt, GUID *rgelt, ULONG *pceltFetched)
1027 {
1028     ProfilesEnumGuid *This = impl_from_IEnumGUID(iface);
1029     ULONG fetched = 0;
1030 
1031     TRACE("(%p)\n",This);
1032 
1033     if (rgelt == NULL) return E_POINTER;
1034 
1035     if (This->key) while (fetched < celt)
1036     {
1037         LSTATUS res;
1038         HRESULT hr;
1039         WCHAR catid[39];
1040         DWORD cName = 39;
1041 
1042         res = RegEnumKeyExW(This->key, This->next_index, catid, &cName,
1043                     NULL, NULL, NULL, NULL);
1044         if (res != ERROR_SUCCESS && res != ERROR_MORE_DATA) break;
1045         ++(This->next_index);
1046 
1047         hr = CLSIDFromString(catid, rgelt);
1048         if (FAILED(hr)) continue;
1049 
1050         ++fetched;
1051         ++rgelt;
1052     }
1053 
1054     if (pceltFetched) *pceltFetched = fetched;
1055     return fetched == celt ? S_OK : S_FALSE;
1056 }
1057 
1058 static HRESULT WINAPI ProfilesEnumGuid_Skip( LPENUMGUID iface, ULONG celt)
1059 {
1060     ProfilesEnumGuid *This = impl_from_IEnumGUID(iface);
1061     TRACE("(%p)\n",This);
1062 
1063     This->next_index += celt;
1064     return S_OK;
1065 }
1066 
1067 static HRESULT WINAPI ProfilesEnumGuid_Reset( LPENUMGUID iface)
1068 {
1069     ProfilesEnumGuid *This = impl_from_IEnumGUID(iface);
1070     TRACE("(%p)\n",This);
1071     This->next_index = 0;
1072     return S_OK;
1073 }
1074 
1075 static HRESULT WINAPI ProfilesEnumGuid_Clone( LPENUMGUID iface,
1076     IEnumGUID **ppenum)
1077 {
1078     ProfilesEnumGuid *This = impl_from_IEnumGUID(iface);
1079     HRESULT res;
1080 
1081     TRACE("(%p)\n",This);
1082 
1083     if (ppenum == NULL) return E_POINTER;
1084 
1085     res = ProfilesEnumGuid_Constructor(ppenum);
1086     if (SUCCEEDED(res))
1087     {
1088         ProfilesEnumGuid *new_This = impl_from_IEnumGUID(*ppenum);
1089         new_This->next_index = This->next_index;
1090     }
1091     return res;
1092 }
1093 
1094 static const IEnumGUIDVtbl EnumGUIDVtbl =
1095 {
1096     ProfilesEnumGuid_QueryInterface,
1097     ProfilesEnumGuid_AddRef,
1098     ProfilesEnumGuid_Release,
1099     ProfilesEnumGuid_Next,
1100     ProfilesEnumGuid_Skip,
1101     ProfilesEnumGuid_Reset,
1102     ProfilesEnumGuid_Clone
1103 };
1104 
1105 static HRESULT ProfilesEnumGuid_Constructor(IEnumGUID **ppOut)
1106 {
1107     ProfilesEnumGuid *This;
1108 
1109     This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(ProfilesEnumGuid));
1110     if (This == NULL)
1111         return E_OUTOFMEMORY;
1112 
1113     This->IEnumGUID_iface.lpVtbl= &EnumGUIDVtbl;
1114     This->refCount = 1;
1115 
1116     if (RegCreateKeyExW(HKEY_LOCAL_MACHINE, szwSystemTIPKey, 0, NULL, 0,
1117                     KEY_READ | KEY_WRITE, NULL, &This->key, NULL) != ERROR_SUCCESS)
1118     {
1119         HeapFree(GetProcessHeap(), 0, This);
1120         return E_FAIL;
1121     }
1122 
1123     *ppOut = &This->IEnumGUID_iface;
1124     TRACE("returning %p\n", *ppOut);
1125     return S_OK;
1126 }
1127 
1128 /**************************************************
1129  * IEnumTfLanguageProfiles implementation
1130  **************************************************/
1131 static void EnumTfLanguageProfiles_Destructor(EnumTfLanguageProfiles *This)
1132 {
1133     TRACE("destroying %p\n", This);
1134     RegCloseKey(This->tipkey);
1135     if (This->langkey)
1136         RegCloseKey(This->langkey);
1137     ITfCategoryMgr_Release(This->catmgr);
1138     HeapFree(GetProcessHeap(),0,This);
1139 }
1140 
1141 static HRESULT WINAPI EnumTfLanguageProfiles_QueryInterface(IEnumTfLanguageProfiles *iface, REFIID iid, LPVOID *ppvOut)
1142 {
1143     EnumTfLanguageProfiles *This = impl_from_IEnumTfLanguageProfiles(iface);
1144 
1145     *ppvOut = NULL;
1146 
1147     if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_IEnumTfLanguageProfiles))
1148     {
1149         *ppvOut = &This->IEnumTfLanguageProfiles_iface;
1150     }
1151 
1152     if (*ppvOut)
1153     {
1154         IEnumTfLanguageProfiles_AddRef(iface);
1155         return S_OK;
1156     }
1157 
1158     WARN("unsupported interface: %s\n", debugstr_guid(iid));
1159     return E_NOINTERFACE;
1160 }
1161 
1162 static ULONG WINAPI EnumTfLanguageProfiles_AddRef(IEnumTfLanguageProfiles *iface)
1163 {
1164     EnumTfLanguageProfiles *This = impl_from_IEnumTfLanguageProfiles(iface);
1165     return InterlockedIncrement(&This->refCount);
1166 }
1167 
1168 static ULONG WINAPI EnumTfLanguageProfiles_Release(IEnumTfLanguageProfiles *iface)
1169 {
1170     EnumTfLanguageProfiles *This = impl_from_IEnumTfLanguageProfiles(iface);
1171     ULONG ret;
1172 
1173     ret = InterlockedDecrement(&This->refCount);
1174     if (ret == 0)
1175         EnumTfLanguageProfiles_Destructor(This);
1176     return ret;
1177 }
1178 
1179 /*****************************************************
1180  * IEnumGuid functions
1181  *****************************************************/
1182 static INT next_LanguageProfile(EnumTfLanguageProfiles *This, CLSID clsid, TF_LANGUAGEPROFILE *tflp)
1183 {
1184     WCHAR fullkey[168];
1185     ULONG res;
1186     WCHAR profileid[39];
1187     DWORD cName = 39;
1188     GUID  profile;
1189 
1190     static const WCHAR fmt[] = {'%','s','\\','%','s','\\','0','x','%','0','8','x',0};
1191 
1192     if (This->langkey == NULL)
1193     {
1194         sprintfW(fullkey,fmt,This->szwCurrentClsid,szwLngp,This->langid);
1195         res = RegOpenKeyExW(This->tipkey, fullkey, 0, KEY_READ | KEY_WRITE, &This->langkey);
1196         if (res)
1197         {
1198             This->langkey = NULL;
1199             return -1;
1200         }
1201         This->lang_index = 0;
1202     }
1203     res = RegEnumKeyExW(This->langkey, This->lang_index, profileid, &cName,
1204                 NULL, NULL, NULL, NULL);
1205     if (res != ERROR_SUCCESS && res != ERROR_MORE_DATA)
1206     {
1207         RegCloseKey(This->langkey);
1208         This->langkey = NULL;
1209         return -1;
1210     }
1211     ++(This->lang_index);
1212 
1213     if (tflp)
1214     {
1215         static const GUID * tipcats[3] = { &GUID_TFCAT_TIP_KEYBOARD,
1216                                            &GUID_TFCAT_TIP_SPEECH,
1217                                            &GUID_TFCAT_TIP_HANDWRITING };
1218         res = CLSIDFromString(profileid, &profile);
1219         if (FAILED(res)) return 0;
1220 
1221         tflp->clsid = clsid;
1222         tflp->langid = This->langid;
1223         tflp->fActive = get_active_textservice(&clsid, NULL);
1224         tflp->guidProfile = profile;
1225         if (ITfCategoryMgr_FindClosestCategory(This->catmgr, &clsid,
1226                 &tflp->catid, tipcats, 3) != S_OK)
1227             ITfCategoryMgr_FindClosestCategory(This->catmgr, &clsid,
1228                     &tflp->catid, NULL, 0);
1229     }
1230 
1231     return 1;
1232 }
1233 
1234 static HRESULT WINAPI EnumTfLanguageProfiles_Next(IEnumTfLanguageProfiles *iface,
1235     ULONG ulCount, TF_LANGUAGEPROFILE *pProfile, ULONG *pcFetch)
1236 {
1237     EnumTfLanguageProfiles *This = impl_from_IEnumTfLanguageProfiles(iface);
1238     ULONG fetched = 0;
1239 
1240     TRACE("(%p)\n",This);
1241 
1242     if (pProfile == NULL) return E_POINTER;
1243 
1244     if (This->tipkey) while (fetched < ulCount)
1245     {
1246         LSTATUS res;
1247         HRESULT hr;
1248         DWORD cName = 39;
1249         GUID clsid;
1250 
1251         res = RegEnumKeyExW(This->tipkey, This->tip_index,
1252                     This->szwCurrentClsid, &cName, NULL, NULL, NULL, NULL);
1253         if (res != ERROR_SUCCESS && res != ERROR_MORE_DATA) break;
1254         ++(This->tip_index);
1255         hr = CLSIDFromString(This->szwCurrentClsid, &clsid);
1256         if (FAILED(hr)) continue;
1257 
1258         while ( fetched < ulCount)
1259         {
1260             INT res = next_LanguageProfile(This, clsid, pProfile);
1261             if (res == 1)
1262             {
1263                 ++fetched;
1264                 ++pProfile;
1265             }
1266             else if (res == -1)
1267                 break;
1268             else
1269                 continue;
1270         }
1271     }
1272 
1273     if (pcFetch) *pcFetch = fetched;
1274     return fetched == ulCount ? S_OK : S_FALSE;
1275 }
1276 
1277 static HRESULT WINAPI EnumTfLanguageProfiles_Skip( IEnumTfLanguageProfiles* iface, ULONG celt)
1278 {
1279     EnumTfLanguageProfiles *This = impl_from_IEnumTfLanguageProfiles(iface);
1280     FIXME("STUB (%p)\n",This);
1281     return E_NOTIMPL;
1282 }
1283 
1284 static HRESULT WINAPI EnumTfLanguageProfiles_Reset( IEnumTfLanguageProfiles* iface)
1285 {
1286     EnumTfLanguageProfiles *This = impl_from_IEnumTfLanguageProfiles(iface);
1287     TRACE("(%p)\n",This);
1288     This->tip_index = 0;
1289     if (This->langkey)
1290         RegCloseKey(This->langkey);
1291     This->langkey = NULL;
1292     This->lang_index = 0;
1293     return S_OK;
1294 }
1295 
1296 static HRESULT WINAPI EnumTfLanguageProfiles_Clone( IEnumTfLanguageProfiles *iface,
1297     IEnumTfLanguageProfiles **ppenum)
1298 {
1299     EnumTfLanguageProfiles *This = impl_from_IEnumTfLanguageProfiles(iface);
1300     EnumTfLanguageProfiles *new_This;
1301     HRESULT res;
1302 
1303     TRACE("(%p)\n",This);
1304 
1305     if (ppenum == NULL) return E_POINTER;
1306 
1307     res = EnumTfLanguageProfiles_Constructor(This->langid, &new_This);
1308     if (SUCCEEDED(res))
1309     {
1310         new_This->tip_index = This->tip_index;
1311         lstrcpynW(new_This->szwCurrentClsid,This->szwCurrentClsid,39);
1312 
1313         if (This->langkey)
1314         {
1315             WCHAR fullkey[168];
1316             static const WCHAR fmt[] = {'%','s','\\','%','s','\\','0','x','%','0','8','x',0};
1317 
1318             sprintfW(fullkey,fmt,This->szwCurrentClsid,szwLngp,This->langid);
1319             res = RegOpenKeyExW(new_This->tipkey, fullkey, 0, KEY_READ | KEY_WRITE, &This->langkey);
1320             new_This->lang_index = This->lang_index;
1321         }
1322         *ppenum = &new_This->IEnumTfLanguageProfiles_iface;
1323     }
1324     return res;
1325 }
1326 
1327 static const IEnumTfLanguageProfilesVtbl EnumTfLanguageProfilesVtbl =
1328 {
1329     EnumTfLanguageProfiles_QueryInterface,
1330     EnumTfLanguageProfiles_AddRef,
1331     EnumTfLanguageProfiles_Release,
1332     EnumTfLanguageProfiles_Clone,
1333     EnumTfLanguageProfiles_Next,
1334     EnumTfLanguageProfiles_Reset,
1335     EnumTfLanguageProfiles_Skip
1336 };
1337 
1338 static HRESULT EnumTfLanguageProfiles_Constructor(LANGID langid, EnumTfLanguageProfiles **out)
1339 {
1340     HRESULT hr;
1341     EnumTfLanguageProfiles *This;
1342 
1343     This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(EnumTfLanguageProfiles));
1344     if (This == NULL)
1345         return E_OUTOFMEMORY;
1346 
1347     This->IEnumTfLanguageProfiles_iface.lpVtbl= &EnumTfLanguageProfilesVtbl;
1348     This->refCount = 1;
1349     This->langid = langid;
1350 
1351     hr = CategoryMgr_Constructor(NULL,(IUnknown**)&This->catmgr);
1352     if (FAILED(hr))
1353     {
1354         HeapFree(GetProcessHeap(),0,This);
1355         return hr;
1356     }
1357 
1358     if (RegCreateKeyExW(HKEY_LOCAL_MACHINE, szwSystemTIPKey, 0, NULL, 0,
1359                     KEY_READ | KEY_WRITE, NULL, &This->tipkey, NULL) != ERROR_SUCCESS)
1360     {
1361         HeapFree(GetProcessHeap(), 0, This);
1362         return E_FAIL;
1363     }
1364 
1365     *out = This;
1366     TRACE("returning %p\n", *out);
1367     return S_OK;
1368 }
1369