msctf/tests: Add ITextStoreACP interface for testing.
[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_Activate(void)
261 {
262     HRESULT hr;
263
264     hr = ITfInputProcessorProfiles_ActivateLanguageProfile(g_ipp,&CLSID_FakeService,gLangid,&CLSID_FakeService);
265     todo_wine ok(SUCCEEDED(hr),"Failed to Activate text service\n");
266 }
267
268 static int inline check_context_refcount(ITfContext *iface)
269 {
270     IUnknown_AddRef(iface);
271     return IUnknown_Release(iface);
272 }
273
274 static void test_startSession(void)
275 {
276     HRESULT hr;
277     DWORD cnt;
278     DWORD editCookie;
279     ITfDocumentMgr *dmtest;
280     ITfContext *cxt,*cxt2,*cxt3,*cxtTest;
281     ITextStoreACP *ts;
282
283     test_ShouldActivate = TRUE;
284     ITfThreadMgr_Activate(g_tm,&cid);
285     todo_wine ok(cid != tid,"TextService id mistakenly matches Client id\n");
286
287     hr = ITfThreadMgr_CreateDocumentMgr(g_tm,&g_dm);
288     ok(SUCCEEDED(hr),"CreateDocumentMgr failed\n");
289
290     hr = ITfThreadMgr_GetFocus(g_tm,&dmtest);
291     ok(SUCCEEDED(hr),"GetFocus Failed\n");
292     ok(dmtest == NULL,"Initial focus not null\n");
293
294     test_CurrentFocus = g_dm;
295     test_PrevFocus = NULL;
296     test_OnSetFocus  = SINK_EXPECTED;
297     hr = ITfThreadMgr_SetFocus(g_tm,g_dm);
298     ok(SUCCEEDED(hr),"SetFocus Failed\n");
299     ok(test_OnSetFocus == SINK_FIRED, "OnSetFocus sink not called\n");
300     test_OnSetFocus  = SINK_UNEXPECTED;
301
302     hr = ITfThreadMgr_GetFocus(g_tm,&dmtest);
303     ok(SUCCEEDED(hr),"GetFocus Failed\n");
304     ok(g_dm == dmtest,"Expected DocumentMgr not focused\n");
305
306     cnt = ITfDocumentMgr_Release(g_dm);
307     ok(cnt == 2,"DocumentMgr refcount not expected (2 vs %i)\n",cnt);
308
309     hr = ITfThreadMgr_GetFocus(g_tm,&dmtest);
310     ok(SUCCEEDED(hr),"GetFocus Failed\n");
311     ok(g_dm == dmtest,"Expected DocumentMgr not focused\n");
312
313     TextStoreACP_Constructor((IUnknown**)&ts);
314
315     hr = ITfDocumentMgr_CreateContext(g_dm, cid, 0, (IUnknown*)ts, &cxt, &editCookie);
316     ok(SUCCEEDED(hr),"CreateContext Failed\n");
317
318     hr = ITfDocumentMgr_CreateContext(g_dm, cid, 0, NULL, &cxt2, &editCookie);
319     ok(SUCCEEDED(hr),"CreateContext Failed\n");
320
321     hr = ITfDocumentMgr_CreateContext(g_dm, cid, 0, NULL, &cxt3, &editCookie);
322     ok(SUCCEEDED(hr),"CreateContext Failed\n");
323
324     cnt = check_context_refcount(cxt);
325     test_OnPushContext = SINK_EXPECTED;
326     test_OnInitDocumentMgr = SINK_EXPECTED;
327     hr = ITfDocumentMgr_Push(g_dm, cxt);
328     ok(SUCCEEDED(hr),"Push Failed\n");
329     ok(check_context_refcount(cxt) > cnt, "Ref count did not increase\n");
330     ok(test_OnPushContext == SINK_FIRED, "OnPushContext sink not fired\n");
331     ok(test_OnInitDocumentMgr == SINK_FIRED, "OnInitDocumentMgr sink not fired\n");
332
333     hr = ITfDocumentMgr_GetTop(g_dm, &cxtTest);
334     ok(SUCCEEDED(hr),"GetTop Failed\n");
335     ok(cxtTest == cxt, "Wrong context on top\n");
336     ok(check_context_refcount(cxt) > cnt, "Ref count did not increase\n");
337     cnt = ITfContext_Release(cxtTest);
338
339     hr = ITfDocumentMgr_GetBase(g_dm, &cxtTest);
340     ok(SUCCEEDED(hr),"GetBase Failed\n");
341     ok(cxtTest == cxt, "Wrong context on Base\n");
342     ok(check_context_refcount(cxt) > cnt, "Ref count did not increase\n");
343     ITfContext_Release(cxtTest);
344
345     check_context_refcount(cxt2);
346     test_OnPushContext = SINK_EXPECTED;
347     hr = ITfDocumentMgr_Push(g_dm, cxt2);
348     ok(SUCCEEDED(hr),"Push Failed\n");
349     ok(test_OnPushContext == SINK_FIRED, "OnPushContext sink not fired\n");
350
351     cnt = check_context_refcount(cxt2);
352     hr = ITfDocumentMgr_GetTop(g_dm, &cxtTest);
353     ok(SUCCEEDED(hr),"GetTop Failed\n");
354     ok(cxtTest == cxt2, "Wrong context on top\n");
355     ok(check_context_refcount(cxt2) > cnt, "Ref count did not increase\n");
356     ITfContext_Release(cxtTest);
357
358     cnt = check_context_refcount(cxt);
359     hr = ITfDocumentMgr_GetBase(g_dm, &cxtTest);
360     ok(SUCCEEDED(hr),"GetBase Failed\n");
361     ok(cxtTest == cxt, "Wrong context on Base\n");
362     ok(check_context_refcount(cxt) > cnt, "Ref count did not increase\n");
363     ITfContext_Release(cxtTest);
364
365     cnt = check_context_refcount(cxt3);
366     hr = ITfDocumentMgr_Push(g_dm, cxt3);
367     ok(!SUCCEEDED(hr),"Push Succeeded\n");
368     ok(check_context_refcount(cxt3) == cnt, "Ref changed\n");
369
370     cnt = check_context_refcount(cxt2);
371     hr = ITfDocumentMgr_GetTop(g_dm, &cxtTest);
372     ok(SUCCEEDED(hr),"GetTop Failed\n");
373     ok(cxtTest == cxt2, "Wrong context on top\n");
374     ok(check_context_refcount(cxt2) > cnt, "Ref count did not increase\n");
375     ITfContext_Release(cxtTest);
376
377     cnt = check_context_refcount(cxt);
378     hr = ITfDocumentMgr_GetBase(g_dm, &cxtTest);
379     ok(SUCCEEDED(hr),"GetBase Failed\n");
380     ok(cxtTest == cxt, "Wrong context on Base\n");
381     ok(check_context_refcount(cxt) > cnt, "Ref count did not increase\n");
382     ITfContext_Release(cxtTest);
383
384     cnt = check_context_refcount(cxt2);
385     test_OnPopContext = SINK_EXPECTED;
386     hr = ITfDocumentMgr_Pop(g_dm, 0);
387     ok(SUCCEEDED(hr),"Pop Failed\n");
388     ok(check_context_refcount(cxt2) < cnt, "Ref count did not decrease\n");
389     ok(test_OnPopContext == SINK_FIRED, "OnPopContext sink not fired\n");
390
391     hr = ITfDocumentMgr_GetTop(g_dm, &cxtTest);
392     ok(SUCCEEDED(hr),"GetTop Failed\n");
393     ok(cxtTest == cxt, "Wrong context on top\n");
394     ITfContext_Release(cxtTest);
395
396     hr = ITfDocumentMgr_GetBase(g_dm, &cxtTest);
397     ok(SUCCEEDED(hr),"GetBase Failed\n");
398     ok(cxtTest == cxt, "Wrong context on base\n");
399     ITfContext_Release(cxtTest);
400
401     hr = ITfDocumentMgr_Pop(g_dm, 0);
402     ok(!SUCCEEDED(hr),"Pop Succeeded\n");
403
404     hr = ITfDocumentMgr_GetTop(g_dm, &cxtTest);
405     ok(SUCCEEDED(hr),"GetTop Failed\n");
406     ok(cxtTest == cxt, "Wrong context on top\n");
407     ITfContext_Release(cxtTest);
408
409     hr = ITfDocumentMgr_GetBase(g_dm, &cxtTest);
410     ok(SUCCEEDED(hr),"GetBase Failed\n");
411     ok(cxtTest == cxt, "Wrong context on base\n");
412     ITfContext_Release(cxtTest);
413
414     ITfContext_Release(cxt);
415     ITfContext_Release(cxt2);
416     ITfContext_Release(cxt3);
417 }
418
419 static void test_endSession(void)
420 {
421     test_ShouldDeactivate = TRUE;
422     test_CurrentFocus = NULL;
423     test_PrevFocus = g_dm;
424     test_OnSetFocus  = SINK_EXPECTED;
425     ITfThreadMgr_Deactivate(g_tm);
426     ok(test_OnSetFocus == SINK_FIRED, "OnSetFocus sink not called\n");
427     test_OnSetFocus  = SINK_UNEXPECTED;
428 }
429
430 START_TEST(inputprocessor)
431 {
432     if (SUCCEEDED(initialize()))
433     {
434         gLangid = GetUserDefaultLCID();
435         test_Register();
436         test_RegisterCategory();
437         test_EnumInputProcessorInfo();
438         test_Enable();
439         test_ThreadMgrAdviseSinks();
440         test_Activate();
441         test_startSession();
442         test_endSession();
443         test_EnumLanguageProfiles();
444         test_FindClosestCategory();
445         test_Disable();
446         test_ThreadMgrUnadviseSinks();
447         test_UnregisterCategory();
448         test_Unregister();
449     }
450     else
451         skip("Unable to create InputProcessor\n");
452     cleanup();
453 }
454
455 /**********************************************************************
456  * ITextStoreACP
457  **********************************************************************/
458 typedef struct tagTextStoreACP
459 {
460     const ITextStoreACPVtbl *TextStoreACPVtbl;
461     LONG refCount;
462 } TextStoreACP;
463
464 static void TextStoreACP_Destructor(TextStoreACP *This)
465 {
466     HeapFree(GetProcessHeap(),0,This);
467 }
468
469 static HRESULT WINAPI TextStoreACP_QueryInterface(ITextStoreACP *iface, REFIID iid, LPVOID *ppvOut)
470 {
471     TextStoreACP *This = (TextStoreACP *)iface;
472     *ppvOut = NULL;
473
474     if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_ITextStoreACP))
475     {
476         *ppvOut = This;
477     }
478
479     if (*ppvOut)
480     {
481         IUnknown_AddRef(iface);
482         return S_OK;
483     }
484
485     return E_NOINTERFACE;
486 }
487
488 static ULONG WINAPI TextStoreACP_AddRef(ITextStoreACP *iface)
489 {
490     TextStoreACP *This = (TextStoreACP *)iface;
491     return InterlockedIncrement(&This->refCount);
492 }
493
494 static ULONG WINAPI TextStoreACP_Release(ITextStoreACP *iface)
495 {
496     TextStoreACP *This = (TextStoreACP *)iface;
497     ULONG ret;
498
499     ret = InterlockedDecrement(&This->refCount);
500     if (ret == 0)
501         TextStoreACP_Destructor(This);
502     return ret;
503 }
504
505 static HRESULT WINAPI TextStoreACP_AdviseSink(ITextStoreACP *iface,
506     REFIID riid, IUnknown *punk, DWORD dwMask)
507 {
508     trace("\n");
509     return S_OK;
510 }
511
512 static HRESULT WINAPI TextStoreACP_UnadviseSink(ITextStoreACP *iface,
513     IUnknown *punk)
514 {
515     trace("\n");
516     return S_OK;
517 }
518 static HRESULT WINAPI TextStoreACP_RequestLock(ITextStoreACP *iface,
519     DWORD dwLockFlags, HRESULT *phrSession)
520 {
521     trace("\n");
522     return S_OK;
523 }
524 static HRESULT WINAPI TextStoreACP_GetStatus(ITextStoreACP *iface,
525     TS_STATUS *pdcs)
526 {
527     trace("\n");
528     return S_OK;
529 }
530 static HRESULT WINAPI TextStoreACP_QueryInsert(ITextStoreACP *iface,
531     LONG acpTestStart, LONG acpTestEnd, ULONG cch, LONG *pacpResultStart,
532     LONG *pacpResultEnd)
533 {
534     trace("\n");
535     return S_OK;
536 }
537 static HRESULT WINAPI TextStoreACP_GetSelection(ITextStoreACP *iface,
538     ULONG ulIndex, ULONG ulCount, TS_SELECTION_ACP *pSelection, ULONG *pcFetched)
539 {
540     trace("\n");
541     return S_OK;
542 }
543 static HRESULT WINAPI TextStoreACP_SetSelection(ITextStoreACP *iface,
544     ULONG ulCount, const TS_SELECTION_ACP *pSelection)
545 {
546     trace("\n");
547     return S_OK;
548 }
549 static HRESULT WINAPI TextStoreACP_GetText(ITextStoreACP *iface,
550     LONG acpStart, LONG acpEnd, WCHAR *pchPlain, ULONG cchPlainReq,
551     ULONG *pcchPlainRet, TS_RUNINFO *prgRunInfo, ULONG cRunInfoReq,
552     ULONG *pcRunInfoRet, LONG *pacpNext)
553 {
554     trace("\n");
555     return S_OK;
556 }
557 static HRESULT WINAPI TextStoreACP_SetText(ITextStoreACP *iface,
558     DWORD dwFlags, LONG acpStart, LONG acpEnd, const WCHAR *pchText,
559     ULONG cch, TS_TEXTCHANGE *pChange)
560 {
561     trace("\n");
562     return S_OK;
563 }
564 static HRESULT WINAPI TextStoreACP_GetFormattedText(ITextStoreACP *iface,
565     LONG acpStart, LONG acpEnd, IDataObject **ppDataObject)
566 {
567     trace("\n");
568     return S_OK;
569 }
570 static HRESULT WINAPI TextStoreACP_GetEmbedded(ITextStoreACP *iface,
571     LONG acpPos, REFGUID rguidService, REFIID riid, IUnknown **ppunk)
572 {
573     trace("\n");
574     return S_OK;
575 }
576 static HRESULT WINAPI TextStoreACP_QueryInsertEmbedded(ITextStoreACP *iface,
577     const GUID *pguidService, const FORMATETC *pFormatEtc, BOOL *pfInsertable)
578 {
579     trace("\n");
580     return S_OK;
581 }
582 static HRESULT WINAPI TextStoreACP_InsertEmbedded(ITextStoreACP *iface,
583     DWORD dwFlags, LONG acpStart, LONG acpEnd, IDataObject *pDataObject,
584     TS_TEXTCHANGE *pChange)
585 {
586     trace("\n");
587     return S_OK;
588 }
589 static HRESULT WINAPI TextStoreACP_InsertTextAtSelection(ITextStoreACP *iface,
590     DWORD dwFlags, const WCHAR *pchText, ULONG cch, LONG *pacpStart,
591     LONG *pacpEnd, TS_TEXTCHANGE *pChange)
592 {
593     trace("\n");
594     return S_OK;
595 }
596 static HRESULT WINAPI TextStoreACP_InsertEmbeddedAtSelection(ITextStoreACP *iface,
597     DWORD dwFlags, IDataObject *pDataObject, LONG *pacpStart, LONG *pacpEnd,
598     TS_TEXTCHANGE *pChange)
599 {
600     trace("\n");
601     return S_OK;
602 }
603 static HRESULT WINAPI TextStoreACP_RequestSupportedAttrs(ITextStoreACP *iface,
604     DWORD dwFlags, ULONG cFilterAttrs, const TS_ATTRID *paFilterAttrs)
605 {
606     trace("\n");
607     return S_OK;
608 }
609 static HRESULT WINAPI TextStoreACP_RequestAttrsAtPosition(ITextStoreACP *iface,
610     LONG acpPos, ULONG cFilterAttrs, const TS_ATTRID *paFilterAttrs,
611     DWORD dwFlags)
612 {
613     trace("\n");
614     return S_OK;
615 }
616 static HRESULT WINAPI TextStoreACP_RequestAttrsTransitioningAtPosition(ITextStoreACP *iface,
617     LONG acpPos, ULONG cFilterAttrs, const TS_ATTRID *paFilterAttrs,
618     DWORD dwFlags)
619 {
620     trace("\n");
621     return S_OK;
622 }
623 static HRESULT WINAPI TextStoreACP_FindNextAttrTransition(ITextStoreACP *iface,
624     LONG acpStart, LONG acpHalt, ULONG cFilterAttrs, const TS_ATTRID *paFilterAttrs,
625     DWORD dwFlags, LONG *pacpNext, BOOL *pfFound, LONG *plFoundOffset)
626 {
627     trace("\n");
628     return S_OK;
629 }
630 static HRESULT WINAPI TextStoreACP_RetrieveRequestedAttrs(ITextStoreACP *iface,
631     ULONG ulCount, TS_ATTRVAL *paAttrVals, ULONG *pcFetched)
632 {
633     trace("\n");
634     return S_OK;
635 }
636 static HRESULT WINAPI TextStoreACP_GetEndACP(ITextStoreACP *iface,
637     LONG *pacp)
638 {
639     trace("\n");
640     return S_OK;
641 }
642 static HRESULT WINAPI TextStoreACP_GetActiveView(ITextStoreACP *iface,
643     TsViewCookie *pvcView)
644 {
645     trace("\n");
646     return S_OK;
647 }
648 static HRESULT WINAPI TextStoreACP_GetACPFromPoint(ITextStoreACP *iface,
649     TsViewCookie vcView, const POINT *ptScreen, DWORD dwFlags,
650     LONG *pacp)
651 {
652     trace("\n");
653     return S_OK;
654 }
655 static HRESULT WINAPI TextStoreACP_GetTextExt(ITextStoreACP *iface,
656     TsViewCookie vcView, LONG acpStart, LONG acpEnd, RECT *prc,
657     BOOL *pfClipped)
658 {
659     trace("\n");
660     return S_OK;
661 }
662 static HRESULT WINAPI TextStoreACP_GetScreenExt(ITextStoreACP *iface,
663     TsViewCookie vcView, RECT *prc)
664 {
665     trace("\n");
666     return S_OK;
667 }
668 static HRESULT WINAPI TextStoreACP_GetWnd(ITextStoreACP *iface,
669     TsViewCookie vcView, HWND *phwnd)
670 {
671     trace("\n");
672     return S_OK;
673 }
674
675 static const ITextStoreACPVtbl TextStoreACP_TextStoreACPVtbl =
676 {
677     TextStoreACP_QueryInterface,
678     TextStoreACP_AddRef,
679     TextStoreACP_Release,
680
681     TextStoreACP_AdviseSink,
682     TextStoreACP_UnadviseSink,
683     TextStoreACP_RequestLock,
684     TextStoreACP_GetStatus,
685     TextStoreACP_QueryInsert,
686     TextStoreACP_GetSelection,
687     TextStoreACP_SetSelection,
688     TextStoreACP_GetText,
689     TextStoreACP_SetText,
690     TextStoreACP_GetFormattedText,
691     TextStoreACP_GetEmbedded,
692     TextStoreACP_QueryInsertEmbedded,
693     TextStoreACP_InsertEmbedded,
694     TextStoreACP_InsertTextAtSelection,
695     TextStoreACP_InsertEmbeddedAtSelection,
696     TextStoreACP_RequestSupportedAttrs,
697     TextStoreACP_RequestAttrsAtPosition,
698     TextStoreACP_RequestAttrsTransitioningAtPosition,
699     TextStoreACP_FindNextAttrTransition,
700     TextStoreACP_RetrieveRequestedAttrs,
701     TextStoreACP_GetEndACP,
702     TextStoreACP_GetActiveView,
703     TextStoreACP_GetACPFromPoint,
704     TextStoreACP_GetTextExt,
705     TextStoreACP_GetScreenExt,
706     TextStoreACP_GetWnd
707 };
708
709 HRESULT TextStoreACP_Constructor(IUnknown **ppOut)
710 {
711     TextStoreACP *This;
712
713     This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(TextStoreACP));
714     if (This == NULL)
715         return E_OUTOFMEMORY;
716
717     This->TextStoreACPVtbl = &TextStoreACP_TextStoreACPVtbl;
718     This->refCount = 1;
719
720     *ppOut = (IUnknown *)This;
721     return S_OK;
722 }
723
724 /**********************************************************************
725  * ITfThreadMgrEventSink
726  **********************************************************************/
727 typedef struct tagThreadMgrEventSink
728 {
729     const ITfThreadMgrEventSinkVtbl *ThreadMgrEventSinkVtbl;
730     LONG refCount;
731 } ThreadMgrEventSink;
732
733 static void ThreadMgrEventSink_Destructor(ThreadMgrEventSink *This)
734 {
735     HeapFree(GetProcessHeap(),0,This);
736 }
737
738 static HRESULT WINAPI ThreadMgrEventSink_QueryInterface(ITfThreadMgrEventSink *iface, REFIID iid, LPVOID *ppvOut)
739 {
740     ThreadMgrEventSink *This = (ThreadMgrEventSink *)iface;
741     *ppvOut = NULL;
742
743     if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_ITfThreadMgrEventSink))
744     {
745         *ppvOut = This;
746     }
747
748     if (*ppvOut)
749     {
750         IUnknown_AddRef(iface);
751         return S_OK;
752     }
753
754     return E_NOINTERFACE;
755 }
756
757 static ULONG WINAPI ThreadMgrEventSink_AddRef(ITfThreadMgrEventSink *iface)
758 {
759     ThreadMgrEventSink *This = (ThreadMgrEventSink *)iface;
760     ok (tmSinkRefCount == This->refCount,"ThreadMgrEventSink refcount off %i vs %i\n",This->refCount,tmSinkRefCount);
761     return InterlockedIncrement(&This->refCount);
762 }
763
764 static ULONG WINAPI ThreadMgrEventSink_Release(ITfThreadMgrEventSink *iface)
765 {
766     ThreadMgrEventSink *This = (ThreadMgrEventSink *)iface;
767     ULONG ret;
768
769     ok (tmSinkRefCount == This->refCount,"ThreadMgrEventSink refcount off %i vs %i\n",This->refCount,tmSinkRefCount);
770     ret = InterlockedDecrement(&This->refCount);
771     if (ret == 0)
772         ThreadMgrEventSink_Destructor(This);
773     return ret;
774 }
775
776 static HRESULT WINAPI ThreadMgrEventSink_OnInitDocumentMgr(ITfThreadMgrEventSink *iface,
777 ITfDocumentMgr *pdim)
778 {
779     ok(test_OnInitDocumentMgr == SINK_EXPECTED, "Unexpected OnInitDocumentMgr sink\n");
780     test_OnInitDocumentMgr = SINK_FIRED;
781     return S_OK;
782 }
783
784 static HRESULT WINAPI ThreadMgrEventSink_OnUninitDocumentMgr(ITfThreadMgrEventSink *iface,
785 ITfDocumentMgr *pdim)
786 {
787     trace("\n");
788     return S_OK;
789 }
790
791 static HRESULT WINAPI ThreadMgrEventSink_OnSetFocus(ITfThreadMgrEventSink *iface,
792 ITfDocumentMgr *pdimFocus, ITfDocumentMgr *pdimPrevFocus)
793 {
794     ok(test_OnSetFocus == SINK_EXPECTED, "Unexpected OnSetFocus sink\n");
795     ok(pdimFocus == test_CurrentFocus,"Sink reports wrong focus\n");
796     ok(pdimPrevFocus == test_PrevFocus,"Sink reports wrong previous focus\n");
797     test_OnSetFocus = SINK_FIRED;
798     return S_OK;
799 }
800
801 static HRESULT WINAPI ThreadMgrEventSink_OnPushContext(ITfThreadMgrEventSink *iface,
802 ITfContext *pic)
803 {
804     ok(test_OnPushContext == SINK_EXPECTED, "Unexpected OnPushContext sink\n");
805     test_OnPushContext = SINK_FIRED;
806     return S_OK;
807 }
808
809 static HRESULT WINAPI ThreadMgrEventSink_OnPopContext(ITfThreadMgrEventSink *iface,
810 ITfContext *pic)
811 {
812     ok(test_OnPopContext == SINK_EXPECTED, "Unexpected OnPopContext sink\n");
813     test_OnPopContext = SINK_FIRED;
814     return S_OK;
815 }
816
817 static const ITfThreadMgrEventSinkVtbl ThreadMgrEventSink_ThreadMgrEventSinkVtbl =
818 {
819     ThreadMgrEventSink_QueryInterface,
820     ThreadMgrEventSink_AddRef,
821     ThreadMgrEventSink_Release,
822
823     ThreadMgrEventSink_OnInitDocumentMgr,
824     ThreadMgrEventSink_OnUninitDocumentMgr,
825     ThreadMgrEventSink_OnSetFocus,
826     ThreadMgrEventSink_OnPushContext,
827     ThreadMgrEventSink_OnPopContext
828 };
829
830 HRESULT ThreadMgrEventSink_Constructor(IUnknown **ppOut)
831 {
832     ThreadMgrEventSink *This;
833
834     This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(ThreadMgrEventSink));
835     if (This == NULL)
836         return E_OUTOFMEMORY;
837
838     This->ThreadMgrEventSinkVtbl = &ThreadMgrEventSink_ThreadMgrEventSinkVtbl;
839     This->refCount = 1;
840
841     *ppOut = (IUnknown *)This;
842     return S_OK;
843 }
844
845
846 /********************************************************************************************
847  * Stub text service for testing
848  ********************************************************************************************/
849
850 static LONG TS_refCount;
851 static IClassFactory *cf;
852 static DWORD regid;
853
854 typedef HRESULT (*LPFNCONSTRUCTOR)(IUnknown *pUnkOuter, IUnknown **ppvOut);
855
856 typedef struct tagClassFactory
857 {
858     const IClassFactoryVtbl *vtbl;
859     LONG   ref;
860     LPFNCONSTRUCTOR ctor;
861 } ClassFactory;
862
863 typedef struct tagTextService
864 {
865     const ITfTextInputProcessorVtbl *TextInputProcessorVtbl;
866     LONG refCount;
867 } TextService;
868
869 static void ClassFactory_Destructor(ClassFactory *This)
870 {
871     HeapFree(GetProcessHeap(),0,This);
872     TS_refCount--;
873 }
874
875 static HRESULT WINAPI ClassFactory_QueryInterface(IClassFactory *iface, REFIID riid, LPVOID *ppvOut)
876 {
877     *ppvOut = NULL;
878     if (IsEqualIID(riid, &IID_IClassFactory) || IsEqualIID(riid, &IID_IUnknown))
879     {
880         IClassFactory_AddRef(iface);
881         *ppvOut = iface;
882         return S_OK;
883     }
884
885     return E_NOINTERFACE;
886 }
887
888 static ULONG WINAPI ClassFactory_AddRef(IClassFactory *iface)
889 {
890     ClassFactory *This = (ClassFactory *)iface;
891     return InterlockedIncrement(&This->ref);
892 }
893
894 static ULONG WINAPI ClassFactory_Release(IClassFactory *iface)
895 {
896     ClassFactory *This = (ClassFactory *)iface;
897     ULONG ret = InterlockedDecrement(&This->ref);
898
899     if (ret == 0)
900         ClassFactory_Destructor(This);
901     return ret;
902 }
903
904 static HRESULT WINAPI ClassFactory_CreateInstance(IClassFactory *iface, IUnknown *punkOuter, REFIID iid, LPVOID *ppvOut)
905 {
906     ClassFactory *This = (ClassFactory *)iface;
907     HRESULT ret;
908     IUnknown *obj;
909
910     ret = This->ctor(punkOuter, &obj);
911     if (FAILED(ret))
912         return ret;
913     ret = IUnknown_QueryInterface(obj, iid, ppvOut);
914     IUnknown_Release(obj);
915     return ret;
916 }
917
918 static HRESULT WINAPI ClassFactory_LockServer(IClassFactory *iface, BOOL fLock)
919 {
920     if(fLock)
921         InterlockedIncrement(&TS_refCount);
922     else
923         InterlockedDecrement(&TS_refCount);
924
925     return S_OK;
926 }
927
928 static const IClassFactoryVtbl ClassFactoryVtbl = {
929     /* IUnknown */
930     ClassFactory_QueryInterface,
931     ClassFactory_AddRef,
932     ClassFactory_Release,
933
934     /* IClassFactory*/
935     ClassFactory_CreateInstance,
936     ClassFactory_LockServer
937 };
938
939 static HRESULT ClassFactory_Constructor(LPFNCONSTRUCTOR ctor, LPVOID *ppvOut)
940 {
941     ClassFactory *This = HeapAlloc(GetProcessHeap(),0,sizeof(ClassFactory));
942     This->vtbl = &ClassFactoryVtbl;
943     This->ref = 1;
944     This->ctor = ctor;
945     *ppvOut = (LPVOID)This;
946     TS_refCount++;
947     return S_OK;
948 }
949
950 static void TextService_Destructor(TextService *This)
951 {
952     HeapFree(GetProcessHeap(),0,This);
953 }
954
955 static HRESULT WINAPI TextService_QueryInterface(ITfTextInputProcessor *iface, REFIID iid, LPVOID *ppvOut)
956 {
957     TextService *This = (TextService *)iface;
958     *ppvOut = NULL;
959
960     if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_ITfTextInputProcessor))
961     {
962         *ppvOut = This;
963     }
964
965     if (*ppvOut)
966     {
967         IUnknown_AddRef(iface);
968         return S_OK;
969     }
970
971     return E_NOINTERFACE;
972 }
973
974 static ULONG WINAPI TextService_AddRef(ITfTextInputProcessor *iface)
975 {
976     TextService *This = (TextService *)iface;
977     return InterlockedIncrement(&This->refCount);
978 }
979
980 static ULONG WINAPI TextService_Release(ITfTextInputProcessor *iface)
981 {
982     TextService *This = (TextService *)iface;
983     ULONG ret;
984
985     ret = InterlockedDecrement(&This->refCount);
986     if (ret == 0)
987         TextService_Destructor(This);
988     return ret;
989 }
990
991 static HRESULT WINAPI TextService_Activate(ITfTextInputProcessor *iface,
992         ITfThreadMgr *ptim, TfClientId id)
993 {
994     trace("TextService_Activate\n");
995     ok(test_ShouldActivate,"Activation came unexpectedly\n");
996     tid = id;
997     return S_OK;
998 }
999
1000 static HRESULT WINAPI TextService_Deactivate(ITfTextInputProcessor *iface)
1001 {
1002     trace("TextService_Deactivate\n");
1003     ok(test_ShouldDeactivate,"Deactivation came unexpectedly\n");
1004     return S_OK;
1005 }
1006
1007 static const ITfTextInputProcessorVtbl TextService_TextInputProcessorVtbl=
1008 {
1009     TextService_QueryInterface,
1010     TextService_AddRef,
1011     TextService_Release,
1012
1013     TextService_Activate,
1014     TextService_Deactivate
1015 };
1016
1017 HRESULT TextService_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut)
1018 {
1019     TextService *This;
1020     if (pUnkOuter)
1021         return CLASS_E_NOAGGREGATION;
1022
1023     This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(TextService));
1024     if (This == NULL)
1025         return E_OUTOFMEMORY;
1026
1027     This->TextInputProcessorVtbl= &TextService_TextInputProcessorVtbl;
1028     This->refCount = 1;
1029
1030     *ppOut = (IUnknown *)This;
1031     return S_OK;
1032 }
1033
1034 HRESULT RegisterTextService(REFCLSID rclsid)
1035 {
1036     ClassFactory_Constructor( TextService_Constructor ,(LPVOID*)&cf);
1037     return CoRegisterClassObject(rclsid, (IUnknown*) cf, CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE, &regid);
1038 }
1039
1040 HRESULT UnregisterTextService()
1041 {
1042     return CoRevokeClassObject(regid);
1043 }