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