xmllite: Fail to set input for external IXmlReaderInput.
[wine] / dlls / imm32 / tests / imm32.c
1 /*
2  * Unit tests for imm32
3  *
4  * Copyright (c) 2008 Michael Jung
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 #include "wine/test.h"
24 #include "winuser.h"
25 #include "wingdi.h"
26 #include "imm.h"
27
28 #define NUMELEMS(array) (sizeof((array))/sizeof((array)[0]))
29
30 static BOOL (WINAPI *pImmAssociateContextEx)(HWND,HIMC,DWORD);
31 static BOOL (WINAPI *pImmIsUIMessageA)(HWND,UINT,WPARAM,LPARAM);
32
33 /*
34  * msgspy - record and analyse message traces sent to a certain window
35  */
36 typedef struct _msgs {
37     CWPSTRUCT    msg;
38     BOOL         post;
39 } imm_msgs;
40
41 static struct _msg_spy {
42     HWND         hwnd;
43     HHOOK        get_msg_hook;
44     HHOOK        call_wnd_proc_hook;
45     imm_msgs     msgs[32];
46     unsigned int i_msg;
47 } msg_spy;
48
49 static LRESULT CALLBACK get_msg_filter(int nCode, WPARAM wParam, LPARAM lParam)
50 {
51     if (HC_ACTION == nCode) {
52         MSG *msg = (MSG*)lParam;
53
54         if ((msg->hwnd == msg_spy.hwnd || msg_spy.hwnd == NULL) &&
55             (msg_spy.i_msg < NUMELEMS(msg_spy.msgs)))
56         {
57             msg_spy.msgs[msg_spy.i_msg].msg.hwnd    = msg->hwnd;
58             msg_spy.msgs[msg_spy.i_msg].msg.message = msg->message;
59             msg_spy.msgs[msg_spy.i_msg].msg.wParam  = msg->wParam;
60             msg_spy.msgs[msg_spy.i_msg].msg.lParam  = msg->lParam;
61             msg_spy.msgs[msg_spy.i_msg].post = TRUE;
62             msg_spy.i_msg++;
63         }
64     }
65
66     return CallNextHookEx(msg_spy.get_msg_hook, nCode, wParam, lParam);
67 }
68
69 static LRESULT CALLBACK call_wnd_proc_filter(int nCode, WPARAM wParam,
70                                              LPARAM lParam)
71 {
72     if (HC_ACTION == nCode) {
73         CWPSTRUCT *cwp = (CWPSTRUCT*)lParam;
74
75         if (((cwp->hwnd == msg_spy.hwnd || msg_spy.hwnd == NULL)) &&
76             (msg_spy.i_msg < NUMELEMS(msg_spy.msgs)))
77         {
78             memcpy(&msg_spy.msgs[msg_spy.i_msg].msg, cwp, sizeof(msg_spy.msgs[0].msg));
79             msg_spy.msgs[msg_spy.i_msg].post = FALSE;
80             msg_spy.i_msg++;
81         }
82     }
83
84     return CallNextHookEx(msg_spy.call_wnd_proc_hook, nCode, wParam, lParam);
85 }
86
87 static void msg_spy_pump_msg_queue(void) {
88     MSG msg;
89
90     while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
91         TranslateMessage(&msg);
92         DispatchMessage(&msg);
93     }
94
95     return;
96 }
97
98 static void msg_spy_flush_msgs(void) {
99     msg_spy_pump_msg_queue();
100     msg_spy.i_msg = 0;
101 }
102
103 static imm_msgs* msg_spy_find_next_msg(UINT message, UINT *start) {
104     UINT i;
105
106     msg_spy_pump_msg_queue();
107
108     if (msg_spy.i_msg >= NUMELEMS(msg_spy.msgs))
109         fprintf(stdout, "%s:%d: msg_spy: message buffer overflow!\n",
110                 __FILE__, __LINE__);
111
112     for (i = *start; i < msg_spy.i_msg; i++)
113         if (msg_spy.msgs[i].msg.message == message)
114         {
115             *start = i+1;
116             return &msg_spy.msgs[i];
117         }
118
119     return NULL;
120 }
121
122 static imm_msgs* msg_spy_find_msg(UINT message) {
123     UINT i = 0;
124
125     return msg_spy_find_next_msg(message, &i);
126 }
127
128 static void msg_spy_init(HWND hwnd) {
129     msg_spy.hwnd = hwnd;
130     msg_spy.get_msg_hook =
131             SetWindowsHookEx(WH_GETMESSAGE, get_msg_filter, GetModuleHandle(0),
132                              GetCurrentThreadId());
133     msg_spy.call_wnd_proc_hook =
134             SetWindowsHookEx(WH_CALLWNDPROC, call_wnd_proc_filter,
135                              GetModuleHandle(0), GetCurrentThreadId());
136     msg_spy.i_msg = 0;
137
138     msg_spy_flush_msgs();
139 }
140
141 static void msg_spy_cleanup(void) {
142     if (msg_spy.get_msg_hook)
143         UnhookWindowsHookEx(msg_spy.get_msg_hook);
144     if (msg_spy.call_wnd_proc_hook)
145         UnhookWindowsHookEx(msg_spy.call_wnd_proc_hook);
146     memset(&msg_spy, 0, sizeof(msg_spy));
147 }
148
149 /*
150  * imm32 test cases - Issue some IMM commands on a dummy window and analyse the
151  * messages being sent to this window in response.
152  */
153 static const char wndcls[] = "winetest_imm32_wndcls";
154 static HWND hwnd;
155
156 static LRESULT WINAPI wndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
157 {
158     switch (msg)
159     {
160         case WM_IME_SETCONTEXT:
161         case WM_NCCREATE:
162         case WM_CREATE:
163             return TRUE;
164     }
165
166     return DefWindowProcA(hwnd,msg,wParam,lParam);
167 }
168
169 static BOOL init(void) {
170     WNDCLASSEX wc;
171     HIMC imc;
172     HMODULE hmod;
173
174     hmod = GetModuleHandleA("imm32.dll");
175     pImmAssociateContextEx = (void*)GetProcAddress(hmod, "ImmAssociateContextEx");
176     pImmIsUIMessageA = (void*)GetProcAddress(hmod, "ImmIsUIMessageA");
177
178     wc.cbSize        = sizeof(WNDCLASSEX);
179     wc.style         = 0;
180     wc.lpfnWndProc   = wndProc;
181     wc.cbClsExtra    = 0;
182     wc.cbWndExtra    = 0;
183     wc.hInstance     = GetModuleHandle(0);
184     wc.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
185     wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
186     wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
187     wc.lpszMenuName  = NULL;
188     wc.lpszClassName = wndcls;
189     wc.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);
190
191     if (!RegisterClassExA(&wc))
192         return FALSE;
193
194     hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, wndcls, "Wine imm32.dll test",
195                           WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
196                           240, 120, NULL, NULL, GetModuleHandle(0), NULL);
197     if (!hwnd)
198         return FALSE;
199
200     imc = ImmGetContext(hwnd);
201     if (!imc)
202     {
203         win_skip("IME support not implemented\n");
204         return FALSE;
205     }
206     ImmReleaseContext(hwnd, imc);
207
208     ShowWindow(hwnd, SW_SHOWNORMAL);
209     UpdateWindow(hwnd);
210
211     msg_spy_init(hwnd);
212
213     return TRUE;
214 }
215
216 static void cleanup(void) {
217     msg_spy_cleanup();
218     if (hwnd)
219         DestroyWindow(hwnd);
220     UnregisterClass(wndcls, GetModuleHandle(0));
221 }
222
223 static void test_ImmNotifyIME(void) {
224     static const char string[] = "wine";
225     char resstr[16] = "";
226     HIMC imc;
227     BOOL ret;
228
229     imc = ImmGetContext(hwnd);
230     msg_spy_flush_msgs();
231
232     ret = ImmNotifyIME(imc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
233     ok(broken(!ret) ||
234        ret, /* Vista+ */
235        "Canceling an empty composition string should succeed.\n");
236     ok(!msg_spy_find_msg(WM_IME_COMPOSITION), "Windows does not post "
237        "WM_IME_COMPOSITION in response to NI_COMPOSITIONSTR / CPS_CANCEL, if "
238        "the composition string being canceled is empty.\n");
239
240     ImmSetCompositionString(imc, SCS_SETSTR, string, sizeof(string), NULL, 0);
241     msg_spy_flush_msgs();
242
243     ImmNotifyIME(imc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
244     msg_spy_flush_msgs();
245
246     /* behavior differs between win9x and NT */
247     ret = ImmGetCompositionString(imc, GCS_COMPSTR, resstr, sizeof(resstr));
248     ok(!ret, "After being cancelled the composition string is empty.\n");
249
250     msg_spy_flush_msgs();
251
252     ret = ImmNotifyIME(imc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
253     ok(broken(!ret) ||
254        ret, /* Vista+ */
255        "Canceling an empty composition string should succeed.\n");
256     ok(!msg_spy_find_msg(WM_IME_COMPOSITION), "Windows does not post "
257        "WM_IME_COMPOSITION in response to NI_COMPOSITIONSTR / CPS_CANCEL, if "
258        "the composition string being canceled is empty.\n");
259
260     msg_spy_flush_msgs();
261     ImmReleaseContext(hwnd, imc);
262 }
263
264 static void test_ImmGetCompositionString(void)
265 {
266     HIMC imc;
267     static const WCHAR string[] = {'w','i','n','e',0x65e5,0x672c,0x8a9e};
268     char cstring[20];
269     WCHAR wstring[20];
270     DWORD len;
271     DWORD alen,wlen;
272
273     imc = ImmGetContext(hwnd);
274     ImmSetCompositionStringW(imc, SCS_SETSTR, string, sizeof(string), NULL,0);
275     alen = ImmGetCompositionStringA(imc, GCS_COMPSTR, cstring, 20);
276     wlen = ImmGetCompositionStringW(imc, GCS_COMPSTR, wstring, 20);
277     /* windows machines without any IME installed just return 0 above */
278     if( alen && wlen)
279     {
280         len = ImmGetCompositionStringW(imc, GCS_COMPATTR, NULL, 0);
281         ok(len*sizeof(WCHAR)==wlen,"GCS_COMPATTR(W) not returning correct count\n");
282         len = ImmGetCompositionStringA(imc, GCS_COMPATTR, NULL, 0);
283         ok(len==alen,"GCS_COMPATTR(A) not returning correct count\n");
284     }
285     ImmReleaseContext(hwnd, imc);
286 }
287
288 static void test_ImmSetCompositionString(void)
289 {
290     HIMC imc;
291     BOOL ret;
292
293     SetLastError(0xdeadbeef);
294     imc = ImmGetContext(hwnd);
295     ok(imc != 0, "ImmGetContext() failed. Last error: %u\n", GetLastError());
296     if (!imc)
297         return;
298
299     ret = ImmSetCompositionStringW(imc, SCS_SETSTR, NULL, 0, NULL, 0);
300     ok(broken(!ret) ||
301        ret, /* Vista+ */
302        "ImmSetCompositionStringW() failed.\n");
303
304     ret = ImmSetCompositionStringW(imc, SCS_SETSTR | SCS_CHANGEATTR,
305         NULL, 0, NULL, 0);
306     ok(!ret, "ImmSetCompositionStringW() succeeded.\n");
307
308     ret = ImmSetCompositionStringW(imc, SCS_SETSTR | SCS_CHANGECLAUSE,
309         NULL, 0, NULL, 0);
310     ok(!ret, "ImmSetCompositionStringW() succeeded.\n");
311
312     ret = ImmSetCompositionStringW(imc, SCS_CHANGEATTR | SCS_CHANGECLAUSE,
313         NULL, 0, NULL, 0);
314     ok(!ret, "ImmSetCompositionStringW() succeeded.\n");
315
316     ret = ImmSetCompositionStringW(imc, SCS_SETSTR | SCS_CHANGEATTR | SCS_CHANGECLAUSE,
317         NULL, 0, NULL, 0);
318     ok(!ret, "ImmSetCompositionStringW() succeeded.\n");
319
320     ImmReleaseContext(hwnd, imc);
321 }
322
323 static void test_ImmIME(void)
324 {
325     HIMC imc;
326
327     imc = ImmGetContext(hwnd);
328     if (imc)
329     {
330         BOOL rc;
331         rc = ImmConfigureIMEA(imc, NULL, IME_CONFIG_REGISTERWORD, NULL);
332         ok (rc == 0, "ImmConfigureIMEA did not fail\n");
333         rc = ImmConfigureIMEW(imc, NULL, IME_CONFIG_REGISTERWORD, NULL);
334         ok (rc == 0, "ImmConfigureIMEW did not fail\n");
335     }
336     ImmReleaseContext(hwnd,imc);
337 }
338
339 static void test_ImmAssociateContextEx(void)
340 {
341     HIMC imc;
342     BOOL rc;
343
344     if (!pImmAssociateContextEx) return;
345
346     imc = ImmGetContext(hwnd);
347     if (imc)
348     {
349         HIMC retimc, newimc;
350
351         newimc = ImmCreateContext();
352         ok(newimc != imc, "handles should not be the same\n");
353         rc = pImmAssociateContextEx(NULL, NULL, 0);
354         ok(!rc, "ImmAssociateContextEx succeeded\n");
355         rc = pImmAssociateContextEx(hwnd, NULL, 0);
356         ok(rc, "ImmAssociateContextEx failed\n");
357         rc = pImmAssociateContextEx(NULL, imc, 0);
358         ok(!rc, "ImmAssociateContextEx succeeded\n");
359
360         rc = pImmAssociateContextEx(hwnd, imc, 0);
361         ok(rc, "ImmAssociateContextEx failed\n");
362         retimc = ImmGetContext(hwnd);
363         ok(retimc == imc, "handles should be the same\n");
364         ImmReleaseContext(hwnd,retimc);
365
366         rc = pImmAssociateContextEx(hwnd, newimc, 0);
367         ok(rc, "ImmAssociateContextEx failed\n");
368         retimc = ImmGetContext(hwnd);
369         ok(retimc == newimc, "handles should be the same\n");
370         ImmReleaseContext(hwnd,retimc);
371
372         rc = pImmAssociateContextEx(hwnd, NULL, IACE_DEFAULT);
373         ok(rc, "ImmAssociateContextEx failed\n");
374     }
375     ImmReleaseContext(hwnd,imc);
376 }
377
378 typedef struct _igc_threadinfo {
379     HWND hwnd;
380     HANDLE event;
381     HIMC himc;
382 } igc_threadinfo;
383
384
385 static DWORD WINAPI ImmGetContextThreadFunc( LPVOID lpParam)
386 {
387     HIMC h1,h2;
388     HWND hwnd2;
389     COMPOSITIONFORM cf;
390     POINT pt;
391     igc_threadinfo *info= (igc_threadinfo*)lpParam;
392     info->hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, wndcls, "Wine imm32.dll test",
393                           WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
394                           240, 120, NULL, NULL, GetModuleHandle(0), NULL);
395
396     h1 = ImmGetContext(hwnd);
397     todo_wine ok(info->himc == h1, "hwnd context changed in new thread\n");
398     h2 = ImmGetContext(info->hwnd);
399     todo_wine ok(h2 != h1, "new hwnd in new thread should have different context\n");
400     info->himc = h2;
401     ImmReleaseContext(hwnd,h1);
402
403     hwnd2 = CreateWindowEx(WS_EX_CLIENTEDGE, wndcls, "Wine imm32.dll test",
404                           WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
405                           240, 120, NULL, NULL, GetModuleHandle(0), NULL);
406     h1 = ImmGetContext(hwnd2);
407
408     ok(h1 == h2, "Windows in same thread should have same default context\n");
409     ImmReleaseContext(hwnd2,h1);
410     ImmReleaseContext(info->hwnd,h2);
411     DestroyWindow(hwnd2);
412
413     /* priming for later tests */
414     ImmSetCompositionWindow(h1, &cf);
415     ImmSetStatusWindowPos(h1, &pt);
416
417     SetEvent(info->event);
418     Sleep(INFINITE);
419     return 1;
420 }
421
422 static void test_ImmThreads(void)
423 {
424     HIMC himc, otherHimc, h1;
425     igc_threadinfo threadinfo;
426     HANDLE hThread;
427     DWORD dwThreadId;
428     BOOL rc;
429     LOGFONT lf;
430     COMPOSITIONFORM cf;
431     DWORD status, sentence;
432     POINT pt;
433
434     himc = ImmGetContext(hwnd);
435     threadinfo.event = CreateEvent(NULL, TRUE, FALSE, NULL);
436     threadinfo.himc = himc;
437     hThread = CreateThread(NULL, 0, ImmGetContextThreadFunc, &threadinfo, 0, &dwThreadId );
438     WaitForSingleObject(threadinfo.event, INFINITE);
439
440     otherHimc = ImmGetContext(threadinfo.hwnd);
441
442     todo_wine ok(himc != otherHimc, "Windows from other threads should have different himc\n");
443     todo_wine ok(otherHimc == threadinfo.himc, "Context from other thread should not change in main thread\n");
444
445     if (0) /* FIXME: Causes wine to hang */
446     {
447     h1 = ImmAssociateContext(hwnd,otherHimc);
448     ok(h1 == NULL, "Should fail to be able to Associate a default context from a different thread\n");
449     h1 = ImmGetContext(hwnd);
450     ok(h1 == himc, "Context for window should remain unchanged\n");
451     ImmReleaseContext(hwnd,h1);
452     }
453
454
455     /* OpenStatus */
456     rc = ImmSetOpenStatus(himc, TRUE);
457     ok(rc != 0, "ImmSetOpenStatus failed\n");
458     rc = ImmGetOpenStatus(himc);
459     ok(rc != 0, "ImmGetOpenStatus failed\n");
460     rc = ImmSetOpenStatus(himc, FALSE);
461     ok(rc != 0, "ImmSetOpenStatus failed\n");
462     rc = ImmGetOpenStatus(himc);
463     ok(rc == 0, "ImmGetOpenStatus failed\n");
464
465     rc = ImmSetOpenStatus(otherHimc, TRUE);
466     todo_wine ok(rc == 0, "ImmSetOpenStatus should fail\n");
467     rc = ImmGetOpenStatus(otherHimc);
468     todo_wine ok(rc == 0, "ImmGetOpenStatus failed\n");
469     rc = ImmSetOpenStatus(otherHimc, FALSE);
470     todo_wine ok(rc == 0, "ImmSetOpenStatus should fail\n");
471     rc = ImmGetOpenStatus(otherHimc);
472     ok(rc == 0, "ImmGetOpenStatus failed\n");
473
474     /* CompositionFont */
475     rc = ImmGetCompositionFont(himc, &lf);
476     ok(rc != 0, "ImmGetCompositionFont failed\n");
477     rc = ImmSetCompositionFont(himc, &lf);
478     ok(rc != 0, "ImmSetCompositionFont failed\n");
479
480     rc = ImmGetCompositionFont(otherHimc, &lf);
481     ok(rc != 0 || broken(rc == 0), "ImmGetCompositionFont failed\n");
482     rc = ImmSetCompositionFont(otherHimc, &lf);
483     todo_wine ok(rc == 0, "ImmSetCompositionFont should fail\n");
484
485     /* CompositionWindow */
486     rc = ImmSetCompositionWindow(himc, &cf);
487     ok(rc != 0, "ImmSetCompositionWindow failed\n");
488     rc = ImmGetCompositionWindow(himc, &cf);
489     ok(rc != 0, "ImmGetCompositionWindow failed\n");
490
491     rc = ImmSetCompositionWindow(otherHimc, &cf);
492     todo_wine ok(rc == 0, "ImmSetCompositionWindow should fail\n");
493     rc = ImmGetCompositionWindow(otherHimc, &cf);
494     ok(rc != 0 || broken(rc == 0), "ImmGetCompositionWindow failed\n");
495
496     /* ConversionStatus */
497     rc = ImmGetConversionStatus(himc, &status, &sentence);
498     ok(rc != 0, "ImmGetConversionStatus failed\n");
499     rc = ImmSetConversionStatus(himc, status, sentence);
500     ok(rc != 0, "ImmSetConversionStatus failed\n");
501
502     rc = ImmGetConversionStatus(otherHimc, &status, &sentence);
503     ok(rc != 0 || broken(rc == 0), "ImmGetConversionStatus failed\n");
504     rc = ImmSetConversionStatus(otherHimc, status, sentence);
505     todo_wine ok(rc == 0, "ImmSetConversionStatus should fail\n");
506
507     /* StatusWindowPos */
508     rc = ImmSetStatusWindowPos(himc, &pt);
509     ok(rc != 0, "ImmSetStatusWindowPos failed\n");
510     rc = ImmGetStatusWindowPos(himc, &pt);
511     ok(rc != 0, "ImmGetStatusWindowPos failed\n");
512
513     rc = ImmSetStatusWindowPos(otherHimc, &pt);
514     todo_wine ok(rc == 0, "ImmSetStatusWindowPos should fail\n");
515     rc = ImmGetStatusWindowPos(otherHimc, &pt);
516     ok(rc != 0 || broken(rc == 0), "ImmGetStatusWindowPos failed\n");
517
518     ImmReleaseContext(threadinfo.hwnd,otherHimc);
519     ImmReleaseContext(hwnd,himc);
520
521     DestroyWindow(threadinfo.hwnd);
522     TerminateThread(hThread, 1);
523
524     himc = ImmGetContext(GetDesktopWindow());
525     todo_wine ok(himc == NULL, "Should not be able to get himc from other process window\n");
526 }
527
528 static void test_ImmIsUIMessage(void)
529 {
530     struct test
531     {
532         UINT msg;
533         BOOL ret;
534     };
535
536     static const struct test tests[] =
537     {
538         { WM_MOUSEMOVE,            FALSE },
539         { WM_IME_STARTCOMPOSITION, TRUE  },
540         { WM_IME_ENDCOMPOSITION,   TRUE  },
541         { WM_IME_COMPOSITION,      TRUE  },
542         { WM_IME_SETCONTEXT,       TRUE  },
543         { WM_IME_NOTIFY,           TRUE  },
544         { WM_IME_CONTROL,          FALSE },
545         { WM_IME_COMPOSITIONFULL,  TRUE  },
546         { WM_IME_SELECT,           TRUE  },
547         { WM_IME_CHAR,             FALSE },
548         { 0x287 /* FIXME */,       TRUE  },
549         { WM_IME_REQUEST,          FALSE },
550         { WM_IME_KEYDOWN,          FALSE },
551         { WM_IME_KEYUP,            FALSE },
552         { 0, FALSE } /* mark the end */
553     };
554
555     const struct test *test;
556     BOOL ret;
557
558     if (!pImmIsUIMessageA) return;
559
560     for (test = tests; test->msg; test++)
561     {
562         msg_spy_flush_msgs();
563         ret = pImmIsUIMessageA(NULL, test->msg, 0, 0);
564         ok(ret == test->ret, "ImmIsUIMessageA returned %x for %x\n", ret, test->msg);
565         ok(!msg_spy_find_msg(test->msg), "Windows does not send 0x%x for NULL hwnd\n", test->msg);
566
567         ret = pImmIsUIMessageA(hwnd, test->msg, 0, 0);
568         ok(ret == test->ret, "ImmIsUIMessageA returned %x for %x\n", ret, test->msg);
569         if (ret)
570             ok(msg_spy_find_msg(test->msg) != NULL, "Windows does send 0x%x\n", test->msg);
571         else
572             ok(!msg_spy_find_msg(test->msg), "Windows does not send 0x%x\n", test->msg);
573     }
574 }
575
576 static void test_ImmGetContext(void)
577 {
578     HIMC himc;
579     DWORD err;
580
581     SetLastError(0xdeadbeef);
582     himc = ImmGetContext((HWND)0xffffffff);
583     err = GetLastError();
584     ok(himc == NULL, "ImmGetContext succeeded\n");
585     ok(err == ERROR_INVALID_WINDOW_HANDLE, "got %u\n", err);
586
587     himc = ImmGetContext(hwnd);
588     ok(himc != NULL, "ImmGetContext failed\n");
589     ok(ImmReleaseContext(hwnd, himc), "ImmReleaseContext failed\n");
590 }
591
592 static void test_ImmGetDescription(void)
593 {
594     HKL hkl;
595     WCHAR japime[] = { 'E', '0', '0', '1', '0', '4', '1', '1', 0 };
596     WCHAR descW[100];
597     CHAR descA[100];
598     UINT ret, lret;
599
600     /* FIXME: invalid keyboard layouts should not pass */
601     ret = ImmGetDescriptionW(NULL, NULL, 0);
602     todo_wine ok(!ret, "ImmGetDescriptionW failed, expected 0 received %d.\n", ret);
603
604     /* load a language with valid IMM descriptions */
605     hkl = LoadKeyboardLayoutW(japime, KLF_ACTIVATE);
606     todo_wine ok(hkl != 0, "LoadKeyboardLayoutW failed, expected != 0.\n");
607
608     ret = ImmGetDescriptionW(hkl, NULL, 0);
609     if(!ret)
610     {
611         win_skip("ImmGetDescriptionW is not working for current loaded keyboard.\n");
612         return;
613     }
614
615     ret = ImmGetDescriptionW(hkl, descW, 0);
616     ok(ret, "ImmGetDescriptionW failed, expected != 0 received 0.\n");
617
618     lret = ImmGetDescriptionW(hkl, descW, ret + 1);
619     ok(lret, "ImmGetDescriptionW failed, expected != 0 received 0.\n");
620     ok(lret == ret, "ImmGetDescriptionW failed to return the correct amount of data. Expected %d, got %d.\n", ret, lret);
621
622     lret = ImmGetDescriptionA(hkl, descA, ret + 1);
623     ok(lret, "ImmGetDescriptionA failed, expected != 0 received 0.\n");
624     todo_wine ok(lret == ret, "ImmGetDescriptionA failed to return the correct amount of data. Expected %d, got %d.\n", ret, lret);
625
626     ret /= 2; /* try to copy partially */
627     lret = ImmGetDescriptionW(hkl, descW, ret + 1);
628     ok(lret, "ImmGetDescriptionW failed, expected != 0 received 0.\n");
629     ok(lret == ret, "ImmGetDescriptionW failed to return the correct amount of data. Expected %d, got %d.\n", ret, lret);
630
631     ret = ImmGetDescriptionW(hkl, descW, 1);
632     ok(!ret, "ImmGetDescriptionW failed, expected 0 received %d.\n", ret);
633
634     UnloadKeyboardLayout(hkl);
635 }
636
637 static void test_ImmDefaultHwnd(void)
638 {
639     HIMC imc1, imc2, imc3;
640     HWND def1, def3;
641     HWND hwnd;
642
643     hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", "Wine imm32.dll test",
644                           WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
645                           240, 120, NULL, NULL, GetModuleHandle(0), NULL);
646
647     ShowWindow(hwnd, SW_SHOWNORMAL);
648
649     imc1 = ImmGetContext(hwnd);
650     if (!imc1)
651     {
652         win_skip("IME support not implemented\n");
653         return;
654     }
655
656     def1 = ImmGetDefaultIMEWnd(hwnd);
657
658     imc2 = ImmCreateContext();
659     ImmSetOpenStatus(imc2, TRUE);
660
661     imc3 = ImmGetContext(hwnd);
662     def3 = ImmGetDefaultIMEWnd(hwnd);
663
664     ok(def3 == def1, "Default IME window should not change\n");
665     ok(imc1 == imc3, "IME context should not change\n");
666     ImmSetOpenStatus(imc2, FALSE);
667
668     ImmReleaseContext(hwnd, imc1);
669     ImmReleaseContext(hwnd, imc3);
670     ImmDestroyContext(imc2);
671     DestroyWindow(hwnd);
672 }
673
674 static void test_ImmMessages(void)
675 {
676     CANDIDATEFORM cf;
677     imm_msgs *msg;
678     HWND defwnd;
679     HIMC imc;
680     UINT idx = 0;
681
682     HWND hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", "Wine imm32.dll test",
683                           WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
684                           240, 120, NULL, NULL, GetModuleHandle(0), NULL);
685
686     ShowWindow(hwnd, SW_SHOWNORMAL);
687     defwnd = ImmGetDefaultIMEWnd(hwnd);
688     imc = ImmGetContext(hwnd);
689
690     ImmSetOpenStatus(imc, TRUE);
691     msg_spy_flush_msgs();
692     SendMessage(defwnd, WM_IME_CONTROL, IMC_GETCANDIDATEPOS, (LPARAM)&cf );
693     do
694     {
695         msg = msg_spy_find_next_msg(WM_IME_CONTROL,&idx);
696         if (msg) ok(!msg->post, "Message should not be posted\n");
697     } while (msg);
698     msg_spy_flush_msgs();
699     ImmSetOpenStatus(imc, FALSE);
700     ImmReleaseContext(hwnd, imc);
701     DestroyWindow(hwnd);
702 }
703
704 START_TEST(imm32) {
705     if (init())
706     {
707         test_ImmNotifyIME();
708         test_ImmGetCompositionString();
709         test_ImmSetCompositionString();
710         test_ImmIME();
711         test_ImmAssociateContextEx();
712         test_ImmThreads();
713         test_ImmIsUIMessage();
714         test_ImmGetContext();
715         test_ImmGetDescription();
716         test_ImmDefaultHwnd();
717         msg_spy_cleanup();
718         /* Reinitialize the hooks to capture all windows */
719         msg_spy_init(NULL);
720         test_ImmMessages();
721         msg_spy_cleanup();
722     }
723     cleanup();
724 }