msctf/tests: Test framework for ITfKeyEventSink.
[wine] / dlls / msctf / tests / inputprocessor.c
1 /*
2  * Unit tests for ITfInputProcessor
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 <stdio.h>
22
23 #define COBJMACROS
24 #include "wine/test.h"
25 #include "winuser.h"
26 #include "initguid.h"
27 #include "shlwapi.h"
28 #include "shlguid.h"
29 #include "comcat.h"
30 #include "msctf.h"
31 #include "olectl.h"
32
33 static ITfInputProcessorProfiles* g_ipp;
34 static LANGID gLangid;
35 static ITfCategoryMgr * g_cm = NULL;
36 static ITfThreadMgr* g_tm = NULL;
37 static ITfDocumentMgr *g_dm = NULL;
38 static TfClientId cid = 0;
39 static TfClientId tid = 0;
40
41 #define SINK_UNEXPECTED 0
42 #define SINK_EXPECTED 1
43 #define SINK_FIRED 2
44
45 static BOOL test_ShouldActivate = FALSE;
46 static BOOL test_ShouldDeactivate = FALSE;
47
48 static DWORD tmSinkCookie;
49 static DWORD tmSinkRefCount;
50 static ITfDocumentMgr *test_CurrentFocus = NULL;
51 static ITfDocumentMgr *test_PrevFocus = NULL;
52 static INT  test_OnSetFocus = SINK_UNEXPECTED;
53 static INT  test_OnInitDocumentMgr = SINK_UNEXPECTED;
54 static INT  test_OnPushContext = SINK_UNEXPECTED;
55 static INT  test_OnPopContext = SINK_UNEXPECTED;
56 static INT  test_KEV_OnSetFocus = SINK_UNEXPECTED;
57
58 HRESULT RegisterTextService(REFCLSID rclsid);
59 HRESULT UnregisterTextService();
60 HRESULT ThreadMgrEventSink_Constructor(IUnknown **ppOut);
61 HRESULT TextStoreACP_Constructor(IUnknown **ppOut);
62
63 DEFINE_GUID(CLSID_FakeService, 0xEDE1A7AD,0x66DE,0x47E0,0xB6,0x20,0x3E,0x92,0xF8,0x24,0x6B,0xF3);
64 DEFINE_GUID(CLSID_TF_InputProcessorProfiles, 0x33c53a50,0xf456,0x4884,0xb0,0x49,0x85,0xfd,0x64,0x3e,0xcf,0xed);
65 DEFINE_GUID(CLSID_TF_CategoryMgr,         0xA4B544A1,0x438D,0x4B41,0x93,0x25,0x86,0x95,0x23,0xE2,0xD6,0xC7);
66 DEFINE_GUID(GUID_TFCAT_TIP_KEYBOARD,     0x34745c63,0xb2f0,0x4784,0x8b,0x67,0x5e,0x12,0xc8,0x70,0x1a,0x31);
67 DEFINE_GUID(GUID_TFCAT_TIP_SPEECH,       0xB5A73CD1,0x8355,0x426B,0xA1,0x61,0x25,0x98,0x08,0xF2,0x6B,0x14);
68 DEFINE_GUID(GUID_TFCAT_TIP_HANDWRITING,  0x246ecb87,0xc2f2,0x4abe,0x90,0x5b,0xc8,0xb3,0x8a,0xdd,0x2c,0x43);
69 DEFINE_GUID (GUID_TFCAT_DISPLAYATTRIBUTEPROVIDER,  0x046B8C80,0x1647,0x40F7,0x9B,0x21,0xB9,0x3B,0x81,0xAA,0xBC,0x1B);
70 DEFINE_GUID(GUID_NULL,0,0,0,0,0,0,0,0,0,0,0);
71 DEFINE_GUID(CLSID_TF_ThreadMgr,           0x529a9e6b,0x6587,0x4f23,0xab,0x9e,0x9c,0x7d,0x68,0x3e,0x3c,0x50);
72 DEFINE_GUID(CLSID_PreservedKey,           0xA0ED8E55,0xCD3B,0x4274,0xB2,0x95,0xF6,0xC9,0xBA,0x2B,0x84,0x72);
73
74
75 static HRESULT initialize(void)
76 {
77     HRESULT hr;
78     CoInitialize(NULL);
79     hr = CoCreateInstance (&CLSID_TF_InputProcessorProfiles, NULL,
80           CLSCTX_INPROC_SERVER, &IID_ITfInputProcessorProfiles, (void**)&g_ipp);
81     if (SUCCEEDED(hr))
82         hr = CoCreateInstance (&CLSID_TF_CategoryMgr, NULL,
83           CLSCTX_INPROC_SERVER, &IID_ITfCategoryMgr, (void**)&g_cm);
84     if (SUCCEEDED(hr))
85         hr = CoCreateInstance (&CLSID_TF_ThreadMgr, NULL,
86           CLSCTX_INPROC_SERVER, &IID_ITfThreadMgr, (void**)&g_tm);
87     return hr;
88 }
89
90 static void cleanup(void)
91 {
92     if (g_ipp)
93         ITfInputProcessorProfiles_Release(g_ipp);
94     if (g_cm)
95         ITfCategoryMgr_Release(g_cm);
96     if (g_tm)
97         ITfThreadMgr_Release(g_tm);
98     CoUninitialize();
99 }
100
101 static void test_Register(void)
102 {
103     HRESULT hr;
104
105     static const WCHAR szDesc[] = {'F','a','k','e',' ','W','i','n','e',' ','S','e','r','v','i','c','e',0};
106     static const WCHAR szFile[] = {'F','a','k','e',' ','W','i','n','e',' ','S','e','r','v','i','c','e',' ','F','i','l','e',0};
107
108     hr = RegisterTextService(&CLSID_FakeService);
109     ok(SUCCEEDED(hr),"Unable to register COM for TextService\n");
110     hr = ITfInputProcessorProfiles_Register(g_ipp, &CLSID_FakeService);
111     ok(SUCCEEDED(hr),"Unable to register text service(%x)\n",hr);
112     hr = ITfInputProcessorProfiles_AddLanguageProfile(g_ipp, &CLSID_FakeService, gLangid, &CLSID_FakeService, szDesc, sizeof(szDesc)/sizeof(WCHAR), szFile, sizeof(szFile)/sizeof(WCHAR), 1);
113     ok(SUCCEEDED(hr),"Unable to add Language Profile (%x)\n",hr);
114 }
115
116 static void test_Unregister(void)
117 {
118     HRESULT hr;
119     hr = ITfInputProcessorProfiles_Unregister(g_ipp, &CLSID_FakeService);
120     ok(SUCCEEDED(hr),"Unable to unregister text service(%x)\n",hr);
121     UnregisterTextService();
122 }
123
124 static void test_EnumInputProcessorInfo(void)
125 {
126     IEnumGUID *ppEnum;
127     BOOL found = FALSE;
128
129     if (SUCCEEDED(ITfInputProcessorProfiles_EnumInputProcessorInfo(g_ipp, &ppEnum)))
130     {
131         ULONG fetched;
132         GUID g;
133         while (IEnumGUID_Next(ppEnum, 1, &g, &fetched) == S_OK)
134         {
135             if(IsEqualGUID(&g,&CLSID_FakeService))
136                 found = TRUE;
137         }
138     }
139     ok(found,"Did not find registered text service\n");
140 }
141
142 static void test_EnumLanguageProfiles(void)
143 {
144     BOOL found = FALSE;
145     IEnumTfLanguageProfiles *ppEnum;
146     if (SUCCEEDED(ITfInputProcessorProfiles_EnumLanguageProfiles(g_ipp,gLangid,&ppEnum)))
147     {
148         TF_LANGUAGEPROFILE profile;
149         while (IEnumTfLanguageProfiles_Next(ppEnum,1,&profile,NULL)==S_OK)
150         {
151             if (IsEqualGUID(&profile.clsid,&CLSID_FakeService))
152             {
153                 found = TRUE;
154                 ok(profile.langid == gLangid, "LangId Incorrect\n");
155                 ok(IsEqualGUID(&profile.catid,&GUID_TFCAT_TIP_KEYBOARD), "CatId Incorrect\n");
156                 ok(IsEqualGUID(&profile.guidProfile,&CLSID_FakeService), "guidProfile Incorrect\n");
157             }
158         }
159     }
160     ok(found,"Registered text service not found\n");
161 }
162
163 static void test_RegisterCategory(void)
164 {
165     HRESULT hr;
166     hr = ITfCategoryMgr_RegisterCategory(g_cm, &CLSID_FakeService, &GUID_TFCAT_TIP_KEYBOARD, &CLSID_FakeService);
167     ok(SUCCEEDED(hr),"ITfCategoryMgr_RegisterCategory failed\n");
168     hr = ITfCategoryMgr_RegisterCategory(g_cm, &CLSID_FakeService, &GUID_TFCAT_DISPLAYATTRIBUTEPROVIDER, &CLSID_FakeService);
169     ok(SUCCEEDED(hr),"ITfCategoryMgr_RegisterCategory failed\n");
170 }
171
172 static void test_UnregisterCategory(void)
173 {
174     HRESULT hr;
175     hr = ITfCategoryMgr_UnregisterCategory(g_cm, &CLSID_FakeService, &GUID_TFCAT_TIP_KEYBOARD, &CLSID_FakeService);
176     todo_wine ok(SUCCEEDED(hr),"ITfCategoryMgr_UnregisterCategory failed\n");
177     hr = ITfCategoryMgr_UnregisterCategory(g_cm, &CLSID_FakeService, &GUID_TFCAT_DISPLAYATTRIBUTEPROVIDER, &CLSID_FakeService);
178     todo_wine ok(SUCCEEDED(hr),"ITfCategoryMgr_UnregisterCategory failed\n");
179 }
180
181 static void test_FindClosestCategory(void)
182 {
183     GUID output;
184     HRESULT hr;
185     const GUID *list[3] = {&GUID_TFCAT_TIP_SPEECH, &GUID_TFCAT_TIP_KEYBOARD, &GUID_TFCAT_TIP_HANDWRITING};
186
187     hr = ITfCategoryMgr_FindClosestCategory(g_cm, &CLSID_FakeService, &output, NULL, 0);
188     ok(SUCCEEDED(hr),"ITfCategoryMgr_FindClosestCategory failed (%x)\n",hr);
189     ok(IsEqualGUID(&output,&GUID_TFCAT_DISPLAYATTRIBUTEPROVIDER),"Wrong GUID\n");
190
191     hr = ITfCategoryMgr_FindClosestCategory(g_cm, &CLSID_FakeService, &output, list, 1);
192     ok(SUCCEEDED(hr),"ITfCategoryMgr_FindClosestCategory failed (%x)\n",hr);
193     ok(IsEqualGUID(&output,&GUID_NULL),"Wrong GUID\n");
194
195     hr = ITfCategoryMgr_FindClosestCategory(g_cm, &CLSID_FakeService, &output, list, 3);
196     ok(SUCCEEDED(hr),"ITfCategoryMgr_FindClosestCategory failed (%x)\n",hr);
197     ok(IsEqualGUID(&output,&GUID_TFCAT_TIP_KEYBOARD),"Wrong GUID\n");
198 }
199
200 static void test_Enable(void)
201 {
202     HRESULT hr;
203     BOOL enabled = FALSE;
204
205     hr = ITfInputProcessorProfiles_EnableLanguageProfile(g_ipp,&CLSID_FakeService, gLangid, &CLSID_FakeService, TRUE);
206     ok(SUCCEEDED(hr),"Failed to enable text service\n");
207     hr = ITfInputProcessorProfiles_IsEnabledLanguageProfile(g_ipp,&CLSID_FakeService, gLangid, &CLSID_FakeService, &enabled);
208     ok(SUCCEEDED(hr),"Failed to get enabled state\n");
209     ok(enabled == TRUE,"enabled state incorrect\n");
210 }
211
212 static void test_Disable(void)
213 {
214     HRESULT hr;
215
216     trace("Disabling\n");
217     hr = ITfInputProcessorProfiles_EnableLanguageProfile(g_ipp,&CLSID_FakeService, gLangid, &CLSID_FakeService, FALSE);
218     ok(SUCCEEDED(hr),"Failed to disable text service\n");
219 }
220
221 static void test_ThreadMgrAdviseSinks(void)
222 {
223     ITfSource *source = NULL;
224     HRESULT hr;
225     IUnknown *sink;
226
227     hr = ITfThreadMgr_QueryInterface(g_tm, &IID_ITfSource, (LPVOID*)&source);
228     ok(SUCCEEDED(hr),"Failed to get IID_ITfSource for ThreadMgr\n");
229     if (!source)
230         return;
231
232     ThreadMgrEventSink_Constructor(&sink);
233
234     tmSinkRefCount = 1;
235     tmSinkCookie = 0;
236     hr = ITfSource_AdviseSink(source,&IID_ITfThreadMgrEventSink, sink, &tmSinkCookie);
237     ok(SUCCEEDED(hr),"Failed to Advise Sink\n");
238     ok(tmSinkCookie!=0,"Failed to get sink cookie\n");
239
240     /* Advising the sink adds a ref, Relesing here lets the object be deleted
241        when unadvised */
242     tmSinkRefCount = 2;
243     IUnknown_Release(sink);
244     ITfSource_Release(source);
245 }
246
247 static void test_ThreadMgrUnadviseSinks(void)
248 {
249     ITfSource *source = NULL;
250     HRESULT hr;
251
252     hr = ITfThreadMgr_QueryInterface(g_tm, &IID_ITfSource, (LPVOID*)&source);
253     ok(SUCCEEDED(hr),"Failed to get IID_ITfSource for ThreadMgr\n");
254     if (!source)
255         return;
256
257     tmSinkRefCount = 1;
258     hr = ITfSource_UnadviseSink(source, tmSinkCookie);
259     ok(SUCCEEDED(hr),"Failed to unadvise Sink\n");
260     ITfSource_Release(source);
261 }
262
263 /**********************************************************************
264  * ITfKeyEventSink
265  **********************************************************************/
266 typedef struct tagKeyEventSink
267 {
268     const ITfKeyEventSinkVtbl *KeyEventSinkVtbl;
269     LONG refCount;
270 } KeyEventSink;
271
272 static void KeyEventSink_Destructor(KeyEventSink *This)
273 {
274     HeapFree(GetProcessHeap(),0,This);
275 }
276
277 static HRESULT WINAPI KeyEventSink_QueryInterface(ITfKeyEventSink *iface, REFIID iid, LPVOID *ppvOut)
278 {
279     KeyEventSink *This = (KeyEventSink *)iface;
280     *ppvOut = NULL;
281
282     if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_ITfKeyEventSink))
283     {
284         *ppvOut = This;
285     }
286
287     if (*ppvOut)
288     {
289         IUnknown_AddRef(iface);
290         return S_OK;
291     }
292
293     return E_NOINTERFACE;
294 }
295
296 static ULONG WINAPI KeyEventSink_AddRef(ITfKeyEventSink *iface)
297 {
298     KeyEventSink *This = (KeyEventSink *)iface;
299     return InterlockedIncrement(&This->refCount);
300 }
301
302 static ULONG WINAPI KeyEventSink_Release(ITfKeyEventSink *iface)
303 {
304     KeyEventSink *This = (KeyEventSink *)iface;
305     ULONG ret;
306
307     ret = InterlockedDecrement(&This->refCount);
308     if (ret == 0)
309         KeyEventSink_Destructor(This);
310     return ret;
311 }
312
313 static HRESULT WINAPI KeyEventSink_OnSetFocus(ITfKeyEventSink *iface,
314         BOOL fForeground)
315 {
316     ok(test_KEV_OnSetFocus == SINK_EXPECTED,"Unexpected KeyEventSink_OnSetFocus\n");
317     test_KEV_OnSetFocus = SINK_FIRED;
318     return S_OK;
319 }
320
321 static HRESULT WINAPI KeyEventSink_OnTestKeyDown(ITfKeyEventSink *iface,
322         ITfContext *pic, WPARAM wParam, LPARAM lParam, BOOL *pfEaten)
323 {
324     trace("\n");
325     return S_OK;
326 }
327
328 static HRESULT WINAPI KeyEventSink_OnTestKeyUp(ITfKeyEventSink *iface,
329         ITfContext *pic, WPARAM wParam, LPARAM lParam, BOOL *pfEaten)
330 {
331     trace("\n");
332     return S_OK;
333 }
334
335 static HRESULT WINAPI KeyEventSink_OnKeyDown(ITfKeyEventSink *iface,
336         ITfContext *pic, WPARAM wParam, LPARAM lParam, BOOL *pfEaten)
337 {
338     trace("\n");
339     return S_OK;
340 }
341
342 static HRESULT WINAPI KeyEventSink_OnKeyUp(ITfKeyEventSink *iface,
343         ITfContext *pic, WPARAM wParam, LPARAM lParam, BOOL *pfEaten)
344 {
345     trace("\n");
346     return S_OK;
347 }
348
349 static HRESULT WINAPI KeyEventSink_OnPreservedKey(ITfKeyEventSink *iface,
350     ITfContext *pic, REFGUID rguid, BOOL *pfEaten)
351 {
352     trace("\n");
353     return S_OK;
354 }
355
356 static const ITfKeyEventSinkVtbl KeyEventSink_KeyEventSinkVtbl =
357 {
358     KeyEventSink_QueryInterface,
359     KeyEventSink_AddRef,
360     KeyEventSink_Release,
361
362     KeyEventSink_OnSetFocus,
363     KeyEventSink_OnTestKeyDown,
364     KeyEventSink_OnTestKeyUp,
365     KeyEventSink_OnKeyDown,
366     KeyEventSink_OnKeyUp,
367     KeyEventSink_OnPreservedKey
368 };
369
370 HRESULT KeyEventSink_Constructor(ITfKeyEventSink **ppOut)
371 {
372     KeyEventSink *This;
373
374     This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(KeyEventSink));
375     if (This == NULL)
376         return E_OUTOFMEMORY;
377
378     This->KeyEventSinkVtbl = &KeyEventSink_KeyEventSinkVtbl;
379     This->refCount = 1;
380
381     *ppOut = (ITfKeyEventSink*)This;
382     return S_OK;
383 }
384
385
386 static void test_KeystrokeMgr(void)
387 {
388     ITfKeystrokeMgr *keymgr= NULL;
389     HRESULT hr;
390     TF_PRESERVEDKEY tfpk;
391     BOOL preserved;
392     ITfKeyEventSink *sink;
393
394     KeyEventSink_Constructor(&sink);
395
396     hr = ITfThreadMgr_QueryInterface(g_tm, &IID_ITfKeystrokeMgr, (LPVOID*)&keymgr);
397     ok(SUCCEEDED(hr),"Failed to get IID_ITfKeystrokeMgr for ThreadMgr\n");
398
399     tfpk.uVKey = 'A';
400     tfpk.uModifiers = TF_MOD_SHIFT;
401
402     test_KEV_OnSetFocus = SINK_EXPECTED;
403     hr = ITfKeystrokeMgr_AdviseKeyEventSink(keymgr,tid,sink,TRUE);
404     todo_wine ok(SUCCEEDED(hr),"ITfKeystrokeMgr_AdviseKeyEventSink failed\n");
405     todo_wine ok(test_KEV_OnSetFocus == SINK_FIRED, "KeyEventSink_OnSetFocus not fired as expected\n");
406
407     hr =ITfKeystrokeMgr_PreserveKey(keymgr, 0, &CLSID_PreservedKey, &tfpk, NULL, 0);
408     todo_wine ok(hr==E_INVALIDARG,"ITfKeystrokeMgr_PreserveKey inproperly succeeded\n");
409
410     hr =ITfKeystrokeMgr_PreserveKey(keymgr, tid, &CLSID_PreservedKey, &tfpk, NULL, 0);
411     todo_wine ok(SUCCEEDED(hr),"ITfKeystrokeMgr_PreserveKey failed\n");
412
413     hr =ITfKeystrokeMgr_PreserveKey(keymgr, tid, &CLSID_PreservedKey, &tfpk, NULL, 0);
414     todo_wine ok(hr == TF_E_ALREADY_EXISTS,"ITfKeystrokeMgr_PreserveKey inproperly succeeded \n");
415
416     preserved = FALSE;
417     hr = ITfKeystrokeMgr_IsPreservedKey(keymgr, &CLSID_PreservedKey, &tfpk, &preserved);
418     todo_wine ok(hr == S_OK, "ITfKeystrokeMgr_IsPreservedKey failed\n");
419     if (hr == S_OK) todo_wine ok(preserved == TRUE,"misreporting preserved key\n");
420
421     hr = ITfKeystrokeMgr_UnpreserveKey(keymgr, &CLSID_PreservedKey,&tfpk);
422     todo_wine ok(SUCCEEDED(hr),"ITfKeystrokeMgr_UnpreserveKey failed\n");
423
424     hr = ITfKeystrokeMgr_IsPreservedKey(keymgr, &CLSID_PreservedKey, &tfpk, &preserved);
425     todo_wine ok(hr == S_FALSE, "ITfKeystrokeMgr_IsPreservedKey failed\n");
426     if (hr == S_FALSE) todo_wine ok(preserved == FALSE,"misreporting preserved key\n");
427
428     hr = ITfKeystrokeMgr_UnpreserveKey(keymgr, &CLSID_PreservedKey,&tfpk);
429     todo_wine ok(hr==CONNECT_E_NOCONNECTION,"ITfKeystrokeMgr_UnpreserveKey inproperly succeeded\n");
430
431     hr = ITfKeystrokeMgr_UnadviseKeyEventSink(keymgr,tid);
432     todo_wine ok(SUCCEEDED(hr),"ITfKeystrokeMgr_UnadviseKeyEventSink failed\n");
433
434     ITfKeystrokeMgr_Release(keymgr);
435     ITfKeyEventSink_Release(sink);
436 }
437
438 static void test_Activate(void)
439 {
440     HRESULT hr;
441
442     hr = ITfInputProcessorProfiles_ActivateLanguageProfile(g_ipp,&CLSID_FakeService,gLangid,&CLSID_FakeService);
443     todo_wine ok(SUCCEEDED(hr),"Failed to Activate text service\n");
444 }
445
446 static int inline check_context_refcount(ITfContext *iface)
447 {
448     IUnknown_AddRef(iface);
449     return IUnknown_Release(iface);
450 }
451
452 static void test_startSession(void)
453 {
454     HRESULT hr;
455     DWORD cnt;
456     DWORD editCookie;
457     ITfDocumentMgr *dmtest;
458     ITfContext *cxt,*cxt2,*cxt3,*cxtTest;
459     ITextStoreACP *ts;
460
461     test_ShouldActivate = TRUE;
462     ITfThreadMgr_Activate(g_tm,&cid);
463     todo_wine ok(cid != tid,"TextService id mistakenly matches Client id\n");
464
465     hr = ITfThreadMgr_CreateDocumentMgr(g_tm,&g_dm);
466     ok(SUCCEEDED(hr),"CreateDocumentMgr failed\n");
467
468     hr = ITfThreadMgr_GetFocus(g_tm,&dmtest);
469     ok(SUCCEEDED(hr),"GetFocus Failed\n");
470     ok(dmtest == NULL,"Initial focus not null\n");
471
472     test_CurrentFocus = g_dm;
473     test_PrevFocus = NULL;
474     test_OnSetFocus  = SINK_EXPECTED;
475     hr = ITfThreadMgr_SetFocus(g_tm,g_dm);
476     ok(SUCCEEDED(hr),"SetFocus Failed\n");
477     ok(test_OnSetFocus == SINK_FIRED, "OnSetFocus sink not called\n");
478     test_OnSetFocus  = SINK_UNEXPECTED;
479
480     hr = ITfThreadMgr_GetFocus(g_tm,&dmtest);
481     ok(SUCCEEDED(hr),"GetFocus Failed\n");
482     ok(g_dm == dmtest,"Expected DocumentMgr not focused\n");
483
484     cnt = ITfDocumentMgr_Release(g_dm);
485     ok(cnt == 2,"DocumentMgr refcount not expected (2 vs %i)\n",cnt);
486
487     hr = ITfThreadMgr_GetFocus(g_tm,&dmtest);
488     ok(SUCCEEDED(hr),"GetFocus Failed\n");
489     ok(g_dm == dmtest,"Expected DocumentMgr not focused\n");
490
491     TextStoreACP_Constructor((IUnknown**)&ts);
492
493     hr = ITfDocumentMgr_CreateContext(g_dm, cid, 0, (IUnknown*)ts, &cxt, &editCookie);
494     ok(SUCCEEDED(hr),"CreateContext Failed\n");
495
496     hr = ITfDocumentMgr_CreateContext(g_dm, cid, 0, NULL, &cxt2, &editCookie);
497     ok(SUCCEEDED(hr),"CreateContext Failed\n");
498
499     hr = ITfDocumentMgr_CreateContext(g_dm, cid, 0, NULL, &cxt3, &editCookie);
500     ok(SUCCEEDED(hr),"CreateContext Failed\n");
501
502     cnt = check_context_refcount(cxt);
503     test_OnPushContext = SINK_EXPECTED;
504     test_OnInitDocumentMgr = SINK_EXPECTED;
505     hr = ITfDocumentMgr_Push(g_dm, cxt);
506     ok(SUCCEEDED(hr),"Push Failed\n");
507     ok(check_context_refcount(cxt) > cnt, "Ref count did not increase\n");
508     ok(test_OnPushContext == SINK_FIRED, "OnPushContext sink not fired\n");
509     ok(test_OnInitDocumentMgr == SINK_FIRED, "OnInitDocumentMgr sink not fired\n");
510
511     hr = ITfDocumentMgr_GetTop(g_dm, &cxtTest);
512     ok(SUCCEEDED(hr),"GetTop Failed\n");
513     ok(cxtTest == cxt, "Wrong context on top\n");
514     ok(check_context_refcount(cxt) > cnt, "Ref count did not increase\n");
515     cnt = ITfContext_Release(cxtTest);
516
517     hr = ITfDocumentMgr_GetBase(g_dm, &cxtTest);
518     ok(SUCCEEDED(hr),"GetBase Failed\n");
519     ok(cxtTest == cxt, "Wrong context on Base\n");
520     ok(check_context_refcount(cxt) > cnt, "Ref count did not increase\n");
521     ITfContext_Release(cxtTest);
522
523     check_context_refcount(cxt2);
524     test_OnPushContext = SINK_EXPECTED;
525     hr = ITfDocumentMgr_Push(g_dm, cxt2);
526     ok(SUCCEEDED(hr),"Push Failed\n");
527     ok(test_OnPushContext == SINK_FIRED, "OnPushContext sink not fired\n");
528
529     cnt = check_context_refcount(cxt2);
530     hr = ITfDocumentMgr_GetTop(g_dm, &cxtTest);
531     ok(SUCCEEDED(hr),"GetTop Failed\n");
532     ok(cxtTest == cxt2, "Wrong context on top\n");
533     ok(check_context_refcount(cxt2) > cnt, "Ref count did not increase\n");
534     ITfContext_Release(cxtTest);
535
536     cnt = check_context_refcount(cxt);
537     hr = ITfDocumentMgr_GetBase(g_dm, &cxtTest);
538     ok(SUCCEEDED(hr),"GetBase Failed\n");
539     ok(cxtTest == cxt, "Wrong context on Base\n");
540     ok(check_context_refcount(cxt) > cnt, "Ref count did not increase\n");
541     ITfContext_Release(cxtTest);
542
543     cnt = check_context_refcount(cxt3);
544     hr = ITfDocumentMgr_Push(g_dm, cxt3);
545     ok(!SUCCEEDED(hr),"Push Succeeded\n");
546     ok(check_context_refcount(cxt3) == cnt, "Ref changed\n");
547
548     cnt = check_context_refcount(cxt2);
549     hr = ITfDocumentMgr_GetTop(g_dm, &cxtTest);
550     ok(SUCCEEDED(hr),"GetTop Failed\n");
551     ok(cxtTest == cxt2, "Wrong context on top\n");
552     ok(check_context_refcount(cxt2) > cnt, "Ref count did not increase\n");
553     ITfContext_Release(cxtTest);
554
555     cnt = check_context_refcount(cxt);
556     hr = ITfDocumentMgr_GetBase(g_dm, &cxtTest);
557     ok(SUCCEEDED(hr),"GetBase Failed\n");
558     ok(cxtTest == cxt, "Wrong context on Base\n");
559     ok(check_context_refcount(cxt) > cnt, "Ref count did not increase\n");
560     ITfContext_Release(cxtTest);
561
562     cnt = check_context_refcount(cxt2);
563     test_OnPopContext = SINK_EXPECTED;
564     hr = ITfDocumentMgr_Pop(g_dm, 0);
565     ok(SUCCEEDED(hr),"Pop Failed\n");
566     ok(check_context_refcount(cxt2) < cnt, "Ref count did not decrease\n");
567     ok(test_OnPopContext == SINK_FIRED, "OnPopContext sink not fired\n");
568
569     hr = ITfDocumentMgr_GetTop(g_dm, &cxtTest);
570     ok(SUCCEEDED(hr),"GetTop Failed\n");
571     ok(cxtTest == cxt, "Wrong context on top\n");
572     ITfContext_Release(cxtTest);
573
574     hr = ITfDocumentMgr_GetBase(g_dm, &cxtTest);
575     ok(SUCCEEDED(hr),"GetBase Failed\n");
576     ok(cxtTest == cxt, "Wrong context on base\n");
577     ITfContext_Release(cxtTest);
578
579     hr = ITfDocumentMgr_Pop(g_dm, 0);
580     ok(!SUCCEEDED(hr),"Pop Succeeded\n");
581
582     hr = ITfDocumentMgr_GetTop(g_dm, &cxtTest);
583     ok(SUCCEEDED(hr),"GetTop Failed\n");
584     ok(cxtTest == cxt, "Wrong context on top\n");
585     ITfContext_Release(cxtTest);
586
587     hr = ITfDocumentMgr_GetBase(g_dm, &cxtTest);
588     ok(SUCCEEDED(hr),"GetBase Failed\n");
589     ok(cxtTest == cxt, "Wrong context on base\n");
590     ITfContext_Release(cxtTest);
591
592     ITfContext_Release(cxt);
593     ITfContext_Release(cxt2);
594     ITfContext_Release(cxt3);
595 }
596
597 static void test_endSession(void)
598 {
599     test_ShouldDeactivate = TRUE;
600     test_CurrentFocus = NULL;
601     test_PrevFocus = g_dm;
602     test_OnSetFocus  = SINK_EXPECTED;
603     ITfThreadMgr_Deactivate(g_tm);
604     ok(test_OnSetFocus == SINK_FIRED, "OnSetFocus sink not called\n");
605     test_OnSetFocus  = SINK_UNEXPECTED;
606 }
607
608 START_TEST(inputprocessor)
609 {
610     if (SUCCEEDED(initialize()))
611     {
612         gLangid = GetUserDefaultLCID();
613         test_Register();
614         test_RegisterCategory();
615         test_EnumInputProcessorInfo();
616         test_Enable();
617         test_ThreadMgrAdviseSinks();
618         test_Activate();
619         test_startSession();
620         test_KeystrokeMgr();
621         test_endSession();
622         test_EnumLanguageProfiles();
623         test_FindClosestCategory();
624         test_Disable();
625         test_ThreadMgrUnadviseSinks();
626         test_UnregisterCategory();
627         test_Unregister();
628     }
629     else
630         skip("Unable to create InputProcessor\n");
631     cleanup();
632 }
633
634 /**********************************************************************
635  * ITextStoreACP
636  **********************************************************************/
637 typedef struct tagTextStoreACP
638 {
639     const ITextStoreACPVtbl *TextStoreACPVtbl;
640     LONG refCount;
641 } TextStoreACP;
642
643 static void TextStoreACP_Destructor(TextStoreACP *This)
644 {
645     HeapFree(GetProcessHeap(),0,This);
646 }
647
648 static HRESULT WINAPI TextStoreACP_QueryInterface(ITextStoreACP *iface, REFIID iid, LPVOID *ppvOut)
649 {
650     TextStoreACP *This = (TextStoreACP *)iface;
651     *ppvOut = NULL;
652
653     if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_ITextStoreACP))
654     {
655         *ppvOut = This;
656     }
657
658     if (*ppvOut)
659     {
660         IUnknown_AddRef(iface);
661         return S_OK;
662     }
663
664     return E_NOINTERFACE;
665 }
666
667 static ULONG WINAPI TextStoreACP_AddRef(ITextStoreACP *iface)
668 {
669     TextStoreACP *This = (TextStoreACP *)iface;
670     return InterlockedIncrement(&This->refCount);
671 }
672
673 static ULONG WINAPI TextStoreACP_Release(ITextStoreACP *iface)
674 {
675     TextStoreACP *This = (TextStoreACP *)iface;
676     ULONG ret;
677
678     ret = InterlockedDecrement(&This->refCount);
679     if (ret == 0)
680         TextStoreACP_Destructor(This);
681     return ret;
682 }
683
684 static HRESULT WINAPI TextStoreACP_AdviseSink(ITextStoreACP *iface,
685     REFIID riid, IUnknown *punk, DWORD dwMask)
686 {
687     trace("\n");
688     return S_OK;
689 }
690
691 static HRESULT WINAPI TextStoreACP_UnadviseSink(ITextStoreACP *iface,
692     IUnknown *punk)
693 {
694     trace("\n");
695     return S_OK;
696 }
697 static HRESULT WINAPI TextStoreACP_RequestLock(ITextStoreACP *iface,
698     DWORD dwLockFlags, HRESULT *phrSession)
699 {
700     trace("\n");
701     return S_OK;
702 }
703 static HRESULT WINAPI TextStoreACP_GetStatus(ITextStoreACP *iface,
704     TS_STATUS *pdcs)
705 {
706     trace("\n");
707     return S_OK;
708 }
709 static HRESULT WINAPI TextStoreACP_QueryInsert(ITextStoreACP *iface,
710     LONG acpTestStart, LONG acpTestEnd, ULONG cch, LONG *pacpResultStart,
711     LONG *pacpResultEnd)
712 {
713     trace("\n");
714     return S_OK;
715 }
716 static HRESULT WINAPI TextStoreACP_GetSelection(ITextStoreACP *iface,
717     ULONG ulIndex, ULONG ulCount, TS_SELECTION_ACP *pSelection, ULONG *pcFetched)
718 {
719     trace("\n");
720     return S_OK;
721 }
722 static HRESULT WINAPI TextStoreACP_SetSelection(ITextStoreACP *iface,
723     ULONG ulCount, const TS_SELECTION_ACP *pSelection)
724 {
725     trace("\n");
726     return S_OK;
727 }
728 static HRESULT WINAPI TextStoreACP_GetText(ITextStoreACP *iface,
729     LONG acpStart, LONG acpEnd, WCHAR *pchPlain, ULONG cchPlainReq,
730     ULONG *pcchPlainRet, TS_RUNINFO *prgRunInfo, ULONG cRunInfoReq,
731     ULONG *pcRunInfoRet, LONG *pacpNext)
732 {
733     trace("\n");
734     return S_OK;
735 }
736 static HRESULT WINAPI TextStoreACP_SetText(ITextStoreACP *iface,
737     DWORD dwFlags, LONG acpStart, LONG acpEnd, const WCHAR *pchText,
738     ULONG cch, TS_TEXTCHANGE *pChange)
739 {
740     trace("\n");
741     return S_OK;
742 }
743 static HRESULT WINAPI TextStoreACP_GetFormattedText(ITextStoreACP *iface,
744     LONG acpStart, LONG acpEnd, IDataObject **ppDataObject)
745 {
746     trace("\n");
747     return S_OK;
748 }
749 static HRESULT WINAPI TextStoreACP_GetEmbedded(ITextStoreACP *iface,
750     LONG acpPos, REFGUID rguidService, REFIID riid, IUnknown **ppunk)
751 {
752     trace("\n");
753     return S_OK;
754 }
755 static HRESULT WINAPI TextStoreACP_QueryInsertEmbedded(ITextStoreACP *iface,
756     const GUID *pguidService, const FORMATETC *pFormatEtc, BOOL *pfInsertable)
757 {
758     trace("\n");
759     return S_OK;
760 }
761 static HRESULT WINAPI TextStoreACP_InsertEmbedded(ITextStoreACP *iface,
762     DWORD dwFlags, LONG acpStart, LONG acpEnd, IDataObject *pDataObject,
763     TS_TEXTCHANGE *pChange)
764 {
765     trace("\n");
766     return S_OK;
767 }
768 static HRESULT WINAPI TextStoreACP_InsertTextAtSelection(ITextStoreACP *iface,
769     DWORD dwFlags, const WCHAR *pchText, ULONG cch, LONG *pacpStart,
770     LONG *pacpEnd, TS_TEXTCHANGE *pChange)
771 {
772     trace("\n");
773     return S_OK;
774 }
775 static HRESULT WINAPI TextStoreACP_InsertEmbeddedAtSelection(ITextStoreACP *iface,
776     DWORD dwFlags, IDataObject *pDataObject, LONG *pacpStart, LONG *pacpEnd,
777     TS_TEXTCHANGE *pChange)
778 {
779     trace("\n");
780     return S_OK;
781 }
782 static HRESULT WINAPI TextStoreACP_RequestSupportedAttrs(ITextStoreACP *iface,
783     DWORD dwFlags, ULONG cFilterAttrs, const TS_ATTRID *paFilterAttrs)
784 {
785     trace("\n");
786     return S_OK;
787 }
788 static HRESULT WINAPI TextStoreACP_RequestAttrsAtPosition(ITextStoreACP *iface,
789     LONG acpPos, ULONG cFilterAttrs, const TS_ATTRID *paFilterAttrs,
790     DWORD dwFlags)
791 {
792     trace("\n");
793     return S_OK;
794 }
795 static HRESULT WINAPI TextStoreACP_RequestAttrsTransitioningAtPosition(ITextStoreACP *iface,
796     LONG acpPos, ULONG cFilterAttrs, const TS_ATTRID *paFilterAttrs,
797     DWORD dwFlags)
798 {
799     trace("\n");
800     return S_OK;
801 }
802 static HRESULT WINAPI TextStoreACP_FindNextAttrTransition(ITextStoreACP *iface,
803     LONG acpStart, LONG acpHalt, ULONG cFilterAttrs, const TS_ATTRID *paFilterAttrs,
804     DWORD dwFlags, LONG *pacpNext, BOOL *pfFound, LONG *plFoundOffset)
805 {
806     trace("\n");
807     return S_OK;
808 }
809 static HRESULT WINAPI TextStoreACP_RetrieveRequestedAttrs(ITextStoreACP *iface,
810     ULONG ulCount, TS_ATTRVAL *paAttrVals, ULONG *pcFetched)
811 {
812     trace("\n");
813     return S_OK;
814 }
815 static HRESULT WINAPI TextStoreACP_GetEndACP(ITextStoreACP *iface,
816     LONG *pacp)
817 {
818     trace("\n");
819     return S_OK;
820 }
821 static HRESULT WINAPI TextStoreACP_GetActiveView(ITextStoreACP *iface,
822     TsViewCookie *pvcView)
823 {
824     trace("\n");
825     return S_OK;
826 }
827 static HRESULT WINAPI TextStoreACP_GetACPFromPoint(ITextStoreACP *iface,
828     TsViewCookie vcView, const POINT *ptScreen, DWORD dwFlags,
829     LONG *pacp)
830 {
831     trace("\n");
832     return S_OK;
833 }
834 static HRESULT WINAPI TextStoreACP_GetTextExt(ITextStoreACP *iface,
835     TsViewCookie vcView, LONG acpStart, LONG acpEnd, RECT *prc,
836     BOOL *pfClipped)
837 {
838     trace("\n");
839     return S_OK;
840 }
841 static HRESULT WINAPI TextStoreACP_GetScreenExt(ITextStoreACP *iface,
842     TsViewCookie vcView, RECT *prc)
843 {
844     trace("\n");
845     return S_OK;
846 }
847 static HRESULT WINAPI TextStoreACP_GetWnd(ITextStoreACP *iface,
848     TsViewCookie vcView, HWND *phwnd)
849 {
850     trace("\n");
851     return S_OK;
852 }
853
854 static const ITextStoreACPVtbl TextStoreACP_TextStoreACPVtbl =
855 {
856     TextStoreACP_QueryInterface,
857     TextStoreACP_AddRef,
858     TextStoreACP_Release,
859
860     TextStoreACP_AdviseSink,
861     TextStoreACP_UnadviseSink,
862     TextStoreACP_RequestLock,
863     TextStoreACP_GetStatus,
864     TextStoreACP_QueryInsert,
865     TextStoreACP_GetSelection,
866     TextStoreACP_SetSelection,
867     TextStoreACP_GetText,
868     TextStoreACP_SetText,
869     TextStoreACP_GetFormattedText,
870     TextStoreACP_GetEmbedded,
871     TextStoreACP_QueryInsertEmbedded,
872     TextStoreACP_InsertEmbedded,
873     TextStoreACP_InsertTextAtSelection,
874     TextStoreACP_InsertEmbeddedAtSelection,
875     TextStoreACP_RequestSupportedAttrs,
876     TextStoreACP_RequestAttrsAtPosition,
877     TextStoreACP_RequestAttrsTransitioningAtPosition,
878     TextStoreACP_FindNextAttrTransition,
879     TextStoreACP_RetrieveRequestedAttrs,
880     TextStoreACP_GetEndACP,
881     TextStoreACP_GetActiveView,
882     TextStoreACP_GetACPFromPoint,
883     TextStoreACP_GetTextExt,
884     TextStoreACP_GetScreenExt,
885     TextStoreACP_GetWnd
886 };
887
888 HRESULT TextStoreACP_Constructor(IUnknown **ppOut)
889 {
890     TextStoreACP *This;
891
892     This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(TextStoreACP));
893     if (This == NULL)
894         return E_OUTOFMEMORY;
895
896     This->TextStoreACPVtbl = &TextStoreACP_TextStoreACPVtbl;
897     This->refCount = 1;
898
899     *ppOut = (IUnknown *)This;
900     return S_OK;
901 }
902
903 /**********************************************************************
904  * ITfThreadMgrEventSink
905  **********************************************************************/
906 typedef struct tagThreadMgrEventSink
907 {
908     const ITfThreadMgrEventSinkVtbl *ThreadMgrEventSinkVtbl;
909     LONG refCount;
910 } ThreadMgrEventSink;
911
912 static void ThreadMgrEventSink_Destructor(ThreadMgrEventSink *This)
913 {
914     HeapFree(GetProcessHeap(),0,This);
915 }
916
917 static HRESULT WINAPI ThreadMgrEventSink_QueryInterface(ITfThreadMgrEventSink *iface, REFIID iid, LPVOID *ppvOut)
918 {
919     ThreadMgrEventSink *This = (ThreadMgrEventSink *)iface;
920     *ppvOut = NULL;
921
922     if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_ITfThreadMgrEventSink))
923     {
924         *ppvOut = This;
925     }
926
927     if (*ppvOut)
928     {
929         IUnknown_AddRef(iface);
930         return S_OK;
931     }
932
933     return E_NOINTERFACE;
934 }
935
936 static ULONG WINAPI ThreadMgrEventSink_AddRef(ITfThreadMgrEventSink *iface)
937 {
938     ThreadMgrEventSink *This = (ThreadMgrEventSink *)iface;
939     ok (tmSinkRefCount == This->refCount,"ThreadMgrEventSink refcount off %i vs %i\n",This->refCount,tmSinkRefCount);
940     return InterlockedIncrement(&This->refCount);
941 }
942
943 static ULONG WINAPI ThreadMgrEventSink_Release(ITfThreadMgrEventSink *iface)
944 {
945     ThreadMgrEventSink *This = (ThreadMgrEventSink *)iface;
946     ULONG ret;
947
948     ok (tmSinkRefCount == This->refCount,"ThreadMgrEventSink refcount off %i vs %i\n",This->refCount,tmSinkRefCount);
949     ret = InterlockedDecrement(&This->refCount);
950     if (ret == 0)
951         ThreadMgrEventSink_Destructor(This);
952     return ret;
953 }
954
955 static HRESULT WINAPI ThreadMgrEventSink_OnInitDocumentMgr(ITfThreadMgrEventSink *iface,
956 ITfDocumentMgr *pdim)
957 {
958     ok(test_OnInitDocumentMgr == SINK_EXPECTED, "Unexpected OnInitDocumentMgr sink\n");
959     test_OnInitDocumentMgr = SINK_FIRED;
960     return S_OK;
961 }
962
963 static HRESULT WINAPI ThreadMgrEventSink_OnUninitDocumentMgr(ITfThreadMgrEventSink *iface,
964 ITfDocumentMgr *pdim)
965 {
966     trace("\n");
967     return S_OK;
968 }
969
970 static HRESULT WINAPI ThreadMgrEventSink_OnSetFocus(ITfThreadMgrEventSink *iface,
971 ITfDocumentMgr *pdimFocus, ITfDocumentMgr *pdimPrevFocus)
972 {
973     ok(test_OnSetFocus == SINK_EXPECTED, "Unexpected OnSetFocus sink\n");
974     ok(pdimFocus == test_CurrentFocus,"Sink reports wrong focus\n");
975     ok(pdimPrevFocus == test_PrevFocus,"Sink reports wrong previous focus\n");
976     test_OnSetFocus = SINK_FIRED;
977     return S_OK;
978 }
979
980 static HRESULT WINAPI ThreadMgrEventSink_OnPushContext(ITfThreadMgrEventSink *iface,
981 ITfContext *pic)
982 {
983     ok(test_OnPushContext == SINK_EXPECTED, "Unexpected OnPushContext sink\n");
984     test_OnPushContext = SINK_FIRED;
985     return S_OK;
986 }
987
988 static HRESULT WINAPI ThreadMgrEventSink_OnPopContext(ITfThreadMgrEventSink *iface,
989 ITfContext *pic)
990 {
991     ok(test_OnPopContext == SINK_EXPECTED, "Unexpected OnPopContext sink\n");
992     test_OnPopContext = SINK_FIRED;
993     return S_OK;
994 }
995
996 static const ITfThreadMgrEventSinkVtbl ThreadMgrEventSink_ThreadMgrEventSinkVtbl =
997 {
998     ThreadMgrEventSink_QueryInterface,
999     ThreadMgrEventSink_AddRef,
1000     ThreadMgrEventSink_Release,
1001
1002     ThreadMgrEventSink_OnInitDocumentMgr,
1003     ThreadMgrEventSink_OnUninitDocumentMgr,
1004     ThreadMgrEventSink_OnSetFocus,
1005     ThreadMgrEventSink_OnPushContext,
1006     ThreadMgrEventSink_OnPopContext
1007 };
1008
1009 HRESULT ThreadMgrEventSink_Constructor(IUnknown **ppOut)
1010 {
1011     ThreadMgrEventSink *This;
1012
1013     This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(ThreadMgrEventSink));
1014     if (This == NULL)
1015         return E_OUTOFMEMORY;
1016
1017     This->ThreadMgrEventSinkVtbl = &ThreadMgrEventSink_ThreadMgrEventSinkVtbl;
1018     This->refCount = 1;
1019
1020     *ppOut = (IUnknown *)This;
1021     return S_OK;
1022 }
1023
1024
1025 /********************************************************************************************
1026  * Stub text service for testing
1027  ********************************************************************************************/
1028
1029 static LONG TS_refCount;
1030 static IClassFactory *cf;
1031 static DWORD regid;
1032
1033 typedef HRESULT (*LPFNCONSTRUCTOR)(IUnknown *pUnkOuter, IUnknown **ppvOut);
1034
1035 typedef struct tagClassFactory
1036 {
1037     const IClassFactoryVtbl *vtbl;
1038     LONG   ref;
1039     LPFNCONSTRUCTOR ctor;
1040 } ClassFactory;
1041
1042 typedef struct tagTextService
1043 {
1044     const ITfTextInputProcessorVtbl *TextInputProcessorVtbl;
1045     LONG refCount;
1046 } TextService;
1047
1048 static void ClassFactory_Destructor(ClassFactory *This)
1049 {
1050     HeapFree(GetProcessHeap(),0,This);
1051     TS_refCount--;
1052 }
1053
1054 static HRESULT WINAPI ClassFactory_QueryInterface(IClassFactory *iface, REFIID riid, LPVOID *ppvOut)
1055 {
1056     *ppvOut = NULL;
1057     if (IsEqualIID(riid, &IID_IClassFactory) || IsEqualIID(riid, &IID_IUnknown))
1058     {
1059         IClassFactory_AddRef(iface);
1060         *ppvOut = iface;
1061         return S_OK;
1062     }
1063
1064     return E_NOINTERFACE;
1065 }
1066
1067 static ULONG WINAPI ClassFactory_AddRef(IClassFactory *iface)
1068 {
1069     ClassFactory *This = (ClassFactory *)iface;
1070     return InterlockedIncrement(&This->ref);
1071 }
1072
1073 static ULONG WINAPI ClassFactory_Release(IClassFactory *iface)
1074 {
1075     ClassFactory *This = (ClassFactory *)iface;
1076     ULONG ret = InterlockedDecrement(&This->ref);
1077
1078     if (ret == 0)
1079         ClassFactory_Destructor(This);
1080     return ret;
1081 }
1082
1083 static HRESULT WINAPI ClassFactory_CreateInstance(IClassFactory *iface, IUnknown *punkOuter, REFIID iid, LPVOID *ppvOut)
1084 {
1085     ClassFactory *This = (ClassFactory *)iface;
1086     HRESULT ret;
1087     IUnknown *obj;
1088
1089     ret = This->ctor(punkOuter, &obj);
1090     if (FAILED(ret))
1091         return ret;
1092     ret = IUnknown_QueryInterface(obj, iid, ppvOut);
1093     IUnknown_Release(obj);
1094     return ret;
1095 }
1096
1097 static HRESULT WINAPI ClassFactory_LockServer(IClassFactory *iface, BOOL fLock)
1098 {
1099     if(fLock)
1100         InterlockedIncrement(&TS_refCount);
1101     else
1102         InterlockedDecrement(&TS_refCount);
1103
1104     return S_OK;
1105 }
1106
1107 static const IClassFactoryVtbl ClassFactoryVtbl = {
1108     /* IUnknown */
1109     ClassFactory_QueryInterface,
1110     ClassFactory_AddRef,
1111     ClassFactory_Release,
1112
1113     /* IClassFactory*/
1114     ClassFactory_CreateInstance,
1115     ClassFactory_LockServer
1116 };
1117
1118 static HRESULT ClassFactory_Constructor(LPFNCONSTRUCTOR ctor, LPVOID *ppvOut)
1119 {
1120     ClassFactory *This = HeapAlloc(GetProcessHeap(),0,sizeof(ClassFactory));
1121     This->vtbl = &ClassFactoryVtbl;
1122     This->ref = 1;
1123     This->ctor = ctor;
1124     *ppvOut = (LPVOID)This;
1125     TS_refCount++;
1126     return S_OK;
1127 }
1128
1129 static void TextService_Destructor(TextService *This)
1130 {
1131     HeapFree(GetProcessHeap(),0,This);
1132 }
1133
1134 static HRESULT WINAPI TextService_QueryInterface(ITfTextInputProcessor *iface, REFIID iid, LPVOID *ppvOut)
1135 {
1136     TextService *This = (TextService *)iface;
1137     *ppvOut = NULL;
1138
1139     if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_ITfTextInputProcessor))
1140     {
1141         *ppvOut = This;
1142     }
1143
1144     if (*ppvOut)
1145     {
1146         IUnknown_AddRef(iface);
1147         return S_OK;
1148     }
1149
1150     return E_NOINTERFACE;
1151 }
1152
1153 static ULONG WINAPI TextService_AddRef(ITfTextInputProcessor *iface)
1154 {
1155     TextService *This = (TextService *)iface;
1156     return InterlockedIncrement(&This->refCount);
1157 }
1158
1159 static ULONG WINAPI TextService_Release(ITfTextInputProcessor *iface)
1160 {
1161     TextService *This = (TextService *)iface;
1162     ULONG ret;
1163
1164     ret = InterlockedDecrement(&This->refCount);
1165     if (ret == 0)
1166         TextService_Destructor(This);
1167     return ret;
1168 }
1169
1170 static HRESULT WINAPI TextService_Activate(ITfTextInputProcessor *iface,
1171         ITfThreadMgr *ptim, TfClientId id)
1172 {
1173     trace("TextService_Activate\n");
1174     ok(test_ShouldActivate,"Activation came unexpectedly\n");
1175     tid = id;
1176     return S_OK;
1177 }
1178
1179 static HRESULT WINAPI TextService_Deactivate(ITfTextInputProcessor *iface)
1180 {
1181     trace("TextService_Deactivate\n");
1182     ok(test_ShouldDeactivate,"Deactivation came unexpectedly\n");
1183     return S_OK;
1184 }
1185
1186 static const ITfTextInputProcessorVtbl TextService_TextInputProcessorVtbl=
1187 {
1188     TextService_QueryInterface,
1189     TextService_AddRef,
1190     TextService_Release,
1191
1192     TextService_Activate,
1193     TextService_Deactivate
1194 };
1195
1196 HRESULT TextService_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut)
1197 {
1198     TextService *This;
1199     if (pUnkOuter)
1200         return CLASS_E_NOAGGREGATION;
1201
1202     This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(TextService));
1203     if (This == NULL)
1204         return E_OUTOFMEMORY;
1205
1206     This->TextInputProcessorVtbl= &TextService_TextInputProcessorVtbl;
1207     This->refCount = 1;
1208
1209     *ppOut = (IUnknown *)This;
1210     return S_OK;
1211 }
1212
1213 HRESULT RegisterTextService(REFCLSID rclsid)
1214 {
1215     ClassFactory_Constructor( TextService_Constructor ,(LPVOID*)&cf);
1216     return CoRegisterClassObject(rclsid, (IUnknown*) cf, CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE, &regid);
1217 }
1218
1219 HRESULT UnregisterTextService()
1220 {
1221     return CoRevokeClassObject(regid);
1222 }