imm32/tests: Test showing that communication to the IME window is done via SendMessage.
[wine/multimedia.git] / dlls / imm32 / tests / imm32.c
blob5aff7f79eec2defd18af23b2aa16c624a978dab1
1 /*
2 * Unit tests for imm32
4 * Copyright (c) 2008 Michael Jung
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.
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.
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
21 #include <stdio.h>
23 #include "wine/test.h"
24 #include "winuser.h"
25 #include "wingdi.h"
26 #include "imm.h"
28 #define NUMELEMS(array) (sizeof((array))/sizeof((array)[0]))
30 static BOOL (WINAPI *pImmAssociateContextEx)(HWND,HIMC,DWORD);
31 static BOOL (WINAPI *pImmIsUIMessageA)(HWND,UINT,WPARAM,LPARAM);
34 * msgspy - record and analyse message traces sent to a certain window
36 typedef struct _msgs {
37 CWPSTRUCT msg;
38 BOOL post;
39 } imm_msgs;
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;
49 static LRESULT CALLBACK get_msg_filter(int nCode, WPARAM wParam, LPARAM lParam)
51 if (HC_ACTION == nCode) {
52 MSG *msg = (MSG*)lParam;
54 if ((msg->hwnd == msg_spy.hwnd || msg_spy.hwnd == NULL) &&
55 (msg_spy.i_msg < NUMELEMS(msg_spy.msgs)))
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++;
66 return CallNextHookEx(msg_spy.get_msg_hook, nCode, wParam, lParam);
69 static LRESULT CALLBACK call_wnd_proc_filter(int nCode, WPARAM wParam,
70 LPARAM lParam)
72 if (HC_ACTION == nCode) {
73 CWPSTRUCT *cwp = (CWPSTRUCT*)lParam;
75 if (((cwp->hwnd == msg_spy.hwnd || msg_spy.hwnd == NULL)) &&
76 (msg_spy.i_msg < NUMELEMS(msg_spy.msgs)))
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++;
84 return CallNextHookEx(msg_spy.call_wnd_proc_hook, nCode, wParam, lParam);
87 static void msg_spy_pump_msg_queue(void) {
88 MSG msg;
90 while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
91 TranslateMessage(&msg);
92 DispatchMessage(&msg);
95 return;
98 static void msg_spy_flush_msgs(void) {
99 msg_spy_pump_msg_queue();
100 msg_spy.i_msg = 0;
103 static imm_msgs* msg_spy_find_next_msg(UINT message, UINT *start) {
104 UINT i;
106 msg_spy_pump_msg_queue();
108 if (msg_spy.i_msg >= NUMELEMS(msg_spy.msgs))
109 fprintf(stdout, "%s:%d: msg_spy: message buffer overflow!\n",
110 __FILE__, __LINE__);
112 for (i = *start; i < msg_spy.i_msg; i++)
113 if (msg_spy.msgs[i].msg.message == message)
115 *start = i+1;
116 return &msg_spy.msgs[i];
119 return NULL;
122 static imm_msgs* msg_spy_find_msg(UINT message) {
123 UINT i = 0;
125 return msg_spy_find_next_msg(message, &i);
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;
138 msg_spy_flush_msgs();
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));
150 * imm32 test cases - Issue some IMM commands on a dummy window and analyse the
151 * messages being sent to this window in response.
153 static const char wndcls[] = "winetest_imm32_wndcls";
154 static HWND hwnd;
156 static LRESULT WINAPI wndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
158 switch (msg)
160 case WM_IME_SETCONTEXT:
161 case WM_NCCREATE:
162 case WM_CREATE:
163 return TRUE;
166 return DefWindowProcA(hwnd,msg,wParam,lParam);
169 static BOOL init(void) {
170 WNDCLASSEX wc;
171 HIMC imc;
172 HMODULE hmod;
174 hmod = GetModuleHandleA("imm32.dll");
175 pImmAssociateContextEx = (void*)GetProcAddress(hmod, "ImmAssociateContextEx");
176 pImmIsUIMessageA = (void*)GetProcAddress(hmod, "ImmIsUIMessageA");
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);
191 if (!RegisterClassExA(&wc))
192 return FALSE;
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;
200 imc = ImmGetContext(hwnd);
201 if (!imc)
203 win_skip("IME support not implemented\n");
204 return FALSE;
206 ImmReleaseContext(hwnd, imc);
208 ShowWindow(hwnd, SW_SHOWNORMAL);
209 UpdateWindow(hwnd);
211 msg_spy_init(hwnd);
213 return TRUE;
216 static void cleanup(void) {
217 msg_spy_cleanup();
218 if (hwnd)
219 DestroyWindow(hwnd);
220 UnregisterClass(wndcls, GetModuleHandle(0));
223 static void test_ImmNotifyIME(void) {
224 static const char string[] = "wine";
225 char resstr[16] = "";
226 HIMC imc;
227 BOOL ret;
229 imc = ImmGetContext(hwnd);
230 msg_spy_flush_msgs();
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");
240 ImmSetCompositionString(imc, SCS_SETSTR, string, sizeof(string), NULL, 0);
241 msg_spy_flush_msgs();
243 ImmNotifyIME(imc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
244 msg_spy_flush_msgs();
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");
250 msg_spy_flush_msgs();
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");
260 msg_spy_flush_msgs();
261 ImmReleaseContext(hwnd, imc);
264 static void test_ImmGetCompositionString(void)
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;
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)
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");
285 ImmReleaseContext(hwnd, imc);
288 static void test_ImmSetCompositionString(void)
290 HIMC imc;
291 BOOL ret;
293 SetLastError(0xdeadbeef);
294 imc = ImmGetContext(hwnd);
295 ok(imc != 0, "ImmGetContext() failed. Last error: %u\n", GetLastError());
296 if (!imc)
297 return;
299 ret = ImmSetCompositionStringW(imc, SCS_SETSTR, NULL, 0, NULL, 0);
300 ok(broken(!ret) ||
301 ret, /* Vista+ */
302 "ImmSetCompositionStringW() failed.\n");
304 ret = ImmSetCompositionStringW(imc, SCS_SETSTR | SCS_CHANGEATTR,
305 NULL, 0, NULL, 0);
306 ok(!ret, "ImmSetCompositionStringW() succeeded.\n");
308 ret = ImmSetCompositionStringW(imc, SCS_SETSTR | SCS_CHANGECLAUSE,
309 NULL, 0, NULL, 0);
310 ok(!ret, "ImmSetCompositionStringW() succeeded.\n");
312 ret = ImmSetCompositionStringW(imc, SCS_CHANGEATTR | SCS_CHANGECLAUSE,
313 NULL, 0, NULL, 0);
314 ok(!ret, "ImmSetCompositionStringW() succeeded.\n");
316 ret = ImmSetCompositionStringW(imc, SCS_SETSTR | SCS_CHANGEATTR | SCS_CHANGECLAUSE,
317 NULL, 0, NULL, 0);
318 ok(!ret, "ImmSetCompositionStringW() succeeded.\n");
320 ImmReleaseContext(hwnd, imc);
323 static void test_ImmIME(void)
325 HIMC imc;
327 imc = ImmGetContext(hwnd);
328 if (imc)
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");
336 ImmReleaseContext(hwnd,imc);
339 static void test_ImmAssociateContextEx(void)
341 HIMC imc;
342 BOOL rc;
344 if (!pImmAssociateContextEx) return;
346 imc = ImmGetContext(hwnd);
347 if (imc)
349 HIMC retimc, newimc;
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");
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);
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);
372 rc = pImmAssociateContextEx(hwnd, NULL, IACE_DEFAULT);
373 ok(rc, "ImmAssociateContextEx failed\n");
375 ImmReleaseContext(hwnd,imc);
378 typedef struct _igc_threadinfo {
379 HWND hwnd;
380 HANDLE event;
381 HIMC himc;
382 } igc_threadinfo;
385 static DWORD WINAPI ImmGetContextThreadFunc( LPVOID lpParam)
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);
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);
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);
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);
413 /* priming for later tests */
414 ImmSetCompositionWindow(h1, &cf);
415 ImmSetStatusWindowPos(h1, &pt);
417 SetEvent(info->event);
418 Sleep(INFINITE);
419 return 1;
422 static void test_ImmThreads(void)
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;
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);
440 otherHimc = ImmGetContext(threadinfo.hwnd);
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");
445 if (0) /* FIXME: Causes wine to hang */
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);
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");
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");
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");
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");
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");
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");
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");
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");
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");
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");
518 ImmReleaseContext(threadinfo.hwnd,otherHimc);
519 ImmReleaseContext(hwnd,himc);
521 DestroyWindow(threadinfo.hwnd);
522 TerminateThread(hThread, 1);
524 himc = ImmGetContext(GetDesktopWindow());
525 todo_wine ok(himc == NULL, "Should not be able to get himc from other process window\n");
528 static void test_ImmIsUIMessage(void)
530 struct test
532 UINT msg;
533 BOOL ret;
536 static const struct test tests[] =
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 */
555 const struct test *test;
556 BOOL ret;
558 if (!pImmIsUIMessageA) return;
560 for (test = tests; test->msg; test++)
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);
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);
576 static void test_ImmGetContext(void)
578 HIMC himc;
579 DWORD err;
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);
587 himc = ImmGetContext(hwnd);
588 ok(himc != NULL, "ImmGetContext failed\n");
589 ok(ImmReleaseContext(hwnd, himc), "ImmReleaseContext failed\n");
592 static void test_ImmGetDescription(void)
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;
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);
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");
608 ret = ImmGetDescriptionW(hkl, NULL, 0);
609 if(!ret)
611 win_skip("ImmGetDescriptionW is not working for current loaded keyboard.\n");
612 return;
615 ret = ImmGetDescriptionW(hkl, descW, 0);
616 ok(ret, "ImmGetDescriptionW failed, expected != 0 received 0.\n");
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);
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);
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);
631 ret = ImmGetDescriptionW(hkl, descW, 1);
632 ok(!ret, "ImmGetDescriptionW failed, expected 0 received %d.\n", ret);
634 UnloadKeyboardLayout(hkl);
637 static void test_ImmDefaultHwnd(void)
639 HIMC imc1, imc2, imc3;
640 HWND def1, def3;
641 HWND hwnd;
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);
647 ShowWindow(hwnd, SW_SHOWNORMAL);
649 imc1 = ImmGetContext(hwnd);
650 if (!imc1)
652 win_skip("IME support not implemented\n");
653 return;
656 def1 = ImmGetDefaultIMEWnd(hwnd);
658 imc2 = ImmCreateContext();
659 ImmSetOpenStatus(imc2, TRUE);
661 imc3 = ImmGetContext(hwnd);
662 def3 = ImmGetDefaultIMEWnd(hwnd);
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);
668 ImmReleaseContext(hwnd, imc1);
669 ImmReleaseContext(hwnd, imc3);
670 ImmDestroyContext(imc2);
671 DestroyWindow(hwnd);
674 static void test_ImmMessages(void)
676 CANDIDATEFORM cf;
677 imm_msgs *msg;
678 HWND defwnd;
679 HIMC imc;
680 UINT idx = 0;
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);
686 ShowWindow(hwnd, SW_SHOWNORMAL);
687 defwnd = ImmGetDefaultIMEWnd(hwnd);
688 imc = ImmGetContext(hwnd);
690 ImmSetOpenStatus(imc, TRUE);
691 msg_spy_flush_msgs();
692 SendMessage(defwnd, WM_IME_CONTROL, IMC_GETCANDIDATEPOS, (LPARAM)&cf );
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);
704 START_TEST(imm32) {
705 if (init())
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();
723 cleanup();