winex11: Keep the result string in the IME UI window procedure.
[wine.git] / dlls / imm32 / tests / imm32.c
blob12e8f99f66cdab02a3496e5dd2de40f62f0bafe0
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"
27 #include "ddk/imm.h"
29 #define NUMELEMS(array) (sizeof((array))/sizeof((array)[0]))
31 static BOOL (WINAPI *pImmAssociateContextEx)(HWND,HIMC,DWORD);
32 static BOOL (WINAPI *pImmIsUIMessageA)(HWND,UINT,WPARAM,LPARAM);
33 static UINT (WINAPI *pSendInput) (UINT, INPUT*, size_t);
36 * msgspy - record and analyse message traces sent to a certain window
38 typedef struct _msgs {
39 CWPSTRUCT msg;
40 BOOL post;
41 } imm_msgs;
43 static struct _msg_spy {
44 HWND hwnd;
45 HHOOK get_msg_hook;
46 HHOOK call_wnd_proc_hook;
47 imm_msgs msgs[64];
48 unsigned int i_msg;
49 } msg_spy;
51 typedef struct
53 DWORD type;
54 union
56 MOUSEINPUT mi;
57 KEYBDINPUT ki;
58 HARDWAREINPUT hi;
59 } u;
60 } TEST_INPUT;
62 typedef struct _tagTRANSMSG {
63 UINT message;
64 WPARAM wParam;
65 LPARAM lParam;
66 } TRANSMSG, *LPTRANSMSG;
68 static UINT (WINAPI *pSendInput) (UINT, INPUT*, size_t);
70 static LRESULT CALLBACK get_msg_filter(int nCode, WPARAM wParam, LPARAM lParam)
72 if (HC_ACTION == nCode) {
73 MSG *msg = (MSG*)lParam;
75 if ((msg->hwnd == msg_spy.hwnd || msg_spy.hwnd == NULL) &&
76 (msg_spy.i_msg < NUMELEMS(msg_spy.msgs)))
78 msg_spy.msgs[msg_spy.i_msg].msg.hwnd = msg->hwnd;
79 msg_spy.msgs[msg_spy.i_msg].msg.message = msg->message;
80 msg_spy.msgs[msg_spy.i_msg].msg.wParam = msg->wParam;
81 msg_spy.msgs[msg_spy.i_msg].msg.lParam = msg->lParam;
82 msg_spy.msgs[msg_spy.i_msg].post = TRUE;
83 msg_spy.i_msg++;
87 return CallNextHookEx(msg_spy.get_msg_hook, nCode, wParam, lParam);
90 static LRESULT CALLBACK call_wnd_proc_filter(int nCode, WPARAM wParam,
91 LPARAM lParam)
93 if (HC_ACTION == nCode) {
94 CWPSTRUCT *cwp = (CWPSTRUCT*)lParam;
96 if (((cwp->hwnd == msg_spy.hwnd || msg_spy.hwnd == NULL)) &&
97 (msg_spy.i_msg < NUMELEMS(msg_spy.msgs)))
99 memcpy(&msg_spy.msgs[msg_spy.i_msg].msg, cwp, sizeof(msg_spy.msgs[0].msg));
100 msg_spy.msgs[msg_spy.i_msg].post = FALSE;
101 msg_spy.i_msg++;
105 return CallNextHookEx(msg_spy.call_wnd_proc_hook, nCode, wParam, lParam);
108 static void msg_spy_pump_msg_queue(void) {
109 MSG msg;
111 while(PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) {
112 TranslateMessage(&msg);
113 DispatchMessageW(&msg);
116 return;
119 static void msg_spy_flush_msgs(void) {
120 msg_spy_pump_msg_queue();
121 msg_spy.i_msg = 0;
124 static imm_msgs* msg_spy_find_next_msg(UINT message, UINT *start) {
125 UINT i;
127 msg_spy_pump_msg_queue();
129 if (msg_spy.i_msg >= NUMELEMS(msg_spy.msgs))
130 fprintf(stdout, "%s:%d: msg_spy: message buffer overflow!\n",
131 __FILE__, __LINE__);
133 for (i = *start; i < msg_spy.i_msg; i++)
134 if (msg_spy.msgs[i].msg.message == message)
136 *start = i+1;
137 return &msg_spy.msgs[i];
140 return NULL;
143 static imm_msgs* msg_spy_find_msg(UINT message) {
144 UINT i = 0;
146 return msg_spy_find_next_msg(message, &i);
149 static void msg_spy_init(HWND hwnd) {
150 msg_spy.hwnd = hwnd;
151 msg_spy.get_msg_hook =
152 SetWindowsHookExW(WH_GETMESSAGE, get_msg_filter, GetModuleHandleW(NULL),
153 GetCurrentThreadId());
154 msg_spy.call_wnd_proc_hook =
155 SetWindowsHookExW(WH_CALLWNDPROC, call_wnd_proc_filter,
156 GetModuleHandleW(NULL), GetCurrentThreadId());
157 msg_spy.i_msg = 0;
159 msg_spy_flush_msgs();
162 static void msg_spy_cleanup(void) {
163 if (msg_spy.get_msg_hook)
164 UnhookWindowsHookEx(msg_spy.get_msg_hook);
165 if (msg_spy.call_wnd_proc_hook)
166 UnhookWindowsHookEx(msg_spy.call_wnd_proc_hook);
167 memset(&msg_spy, 0, sizeof(msg_spy));
171 * imm32 test cases - Issue some IMM commands on a dummy window and analyse the
172 * messages being sent to this window in response.
174 static const char wndcls[] = "winetest_imm32_wndcls";
175 static enum { PHASE_UNKNOWN, FIRST_WINDOW, SECOND_WINDOW,
176 CREATE_CANCEL, NCCREATE_CANCEL, IME_DISABLED } test_phase;
177 static HWND hwnd;
179 static HWND get_ime_window(void);
181 static LRESULT WINAPI wndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
183 HWND default_ime_wnd;
184 switch (msg)
186 case WM_IME_SETCONTEXT:
187 return TRUE;
188 case WM_NCCREATE:
189 default_ime_wnd = get_ime_window();
190 switch(test_phase) {
191 case FIRST_WINDOW:
192 case IME_DISABLED:
193 ok(!default_ime_wnd, "expected no IME windows\n");
194 break;
195 case SECOND_WINDOW:
196 ok(default_ime_wnd != NULL, "expected IME window existence\n");
197 break;
198 default:
199 break; /* do nothing */
201 if (test_phase == NCCREATE_CANCEL)
202 return FALSE;
203 return TRUE;
204 case WM_NCCALCSIZE:
205 default_ime_wnd = get_ime_window();
206 switch(test_phase) {
207 case FIRST_WINDOW:
208 case SECOND_WINDOW:
209 case CREATE_CANCEL:
210 ok(default_ime_wnd != NULL, "expected IME window existence\n");
211 break;
212 case IME_DISABLED:
213 ok(!default_ime_wnd, "expected no IME windows\n");
214 break;
215 default:
216 break; /* do nothing */
218 break;
219 case WM_CREATE:
220 default_ime_wnd = get_ime_window();
221 switch(test_phase) {
222 case FIRST_WINDOW:
223 case SECOND_WINDOW:
224 case CREATE_CANCEL:
225 ok(default_ime_wnd != NULL, "expected IME window existence\n");
226 break;
227 case IME_DISABLED:
228 ok(!default_ime_wnd, "expected no IME windows\n");
229 break;
230 default:
231 break; /* do nothing */
233 if (test_phase == CREATE_CANCEL)
234 return -1;
235 return TRUE;
238 return DefWindowProcA(hWnd,msg,wParam,lParam);
241 static BOOL init(void) {
242 WNDCLASSEXA wc;
243 HIMC imc;
244 HMODULE hmod,huser;
246 hmod = GetModuleHandleA("imm32.dll");
247 huser = GetModuleHandleA("user32");
248 pImmAssociateContextEx = (void*)GetProcAddress(hmod, "ImmAssociateContextEx");
249 pImmIsUIMessageA = (void*)GetProcAddress(hmod, "ImmIsUIMessageA");
250 pSendInput = (void*)GetProcAddress(huser, "SendInput");
252 wc.cbSize = sizeof(WNDCLASSEXA);
253 wc.style = 0;
254 wc.lpfnWndProc = wndProc;
255 wc.cbClsExtra = 0;
256 wc.cbWndExtra = 0;
257 wc.hInstance = GetModuleHandleA(NULL);
258 wc.hIcon = LoadIconA(NULL, (LPCSTR)IDI_APPLICATION);
259 wc.hCursor = LoadCursorA(NULL, (LPCSTR)IDC_ARROW);
260 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
261 wc.lpszMenuName = NULL;
262 wc.lpszClassName = wndcls;
263 wc.hIconSm = LoadIconA(NULL, (LPCSTR)IDI_APPLICATION);
265 if (!RegisterClassExA(&wc))
266 return FALSE;
268 hwnd = CreateWindowExA(WS_EX_CLIENTEDGE, wndcls, "Wine imm32.dll test",
269 WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
270 240, 120, NULL, NULL, GetModuleHandleW(NULL), NULL);
271 if (!hwnd)
272 return FALSE;
274 imc = ImmGetContext(hwnd);
275 if (!imc)
277 win_skip("IME support not implemented\n");
278 return FALSE;
280 ImmReleaseContext(hwnd, imc);
282 ShowWindow(hwnd, SW_SHOWNORMAL);
283 UpdateWindow(hwnd);
285 msg_spy_init(hwnd);
287 return TRUE;
290 static void cleanup(void) {
291 msg_spy_cleanup();
292 if (hwnd)
293 DestroyWindow(hwnd);
294 UnregisterClassA(wndcls, GetModuleHandleW(NULL));
297 static void test_ImmNotifyIME(void) {
298 static const char string[] = "wine";
299 char resstr[16] = "";
300 HIMC imc;
301 BOOL ret;
303 imc = ImmGetContext(hwnd);
304 msg_spy_flush_msgs();
306 ret = ImmNotifyIME(imc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
307 ok(broken(!ret) ||
308 ret, /* Vista+ */
309 "Canceling an empty composition string should succeed.\n");
310 ok(!msg_spy_find_msg(WM_IME_COMPOSITION), "Windows does not post "
311 "WM_IME_COMPOSITION in response to NI_COMPOSITIONSTR / CPS_CANCEL, if "
312 "the composition string being canceled is empty.\n");
314 ImmSetCompositionStringA(imc, SCS_SETSTR, string, sizeof(string), NULL, 0);
315 msg_spy_flush_msgs();
317 ImmNotifyIME(imc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
318 msg_spy_flush_msgs();
320 /* behavior differs between win9x and NT */
321 ret = ImmGetCompositionStringA(imc, GCS_COMPSTR, resstr, sizeof(resstr));
322 ok(!ret, "After being cancelled the composition string is empty.\n");
324 msg_spy_flush_msgs();
326 ret = ImmNotifyIME(imc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
327 ok(broken(!ret) ||
328 ret, /* Vista+ */
329 "Canceling an empty composition string should succeed.\n");
330 ok(!msg_spy_find_msg(WM_IME_COMPOSITION), "Windows does not post "
331 "WM_IME_COMPOSITION in response to NI_COMPOSITIONSTR / CPS_CANCEL, if "
332 "the composition string being canceled is empty.\n");
334 msg_spy_flush_msgs();
335 ImmReleaseContext(hwnd, imc);
337 imc = ImmCreateContext();
338 ImmDestroyContext(imc);
340 SetLastError(0xdeadbeef);
341 ret = ImmNotifyIME((HIMC)0xdeadcafe, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
342 ok (ret == 0, "Bad IME should return 0\n");
343 ret = GetLastError();
344 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret);
345 SetLastError(0xdeadbeef);
346 ret = ImmNotifyIME(0x00000000, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
347 ok (ret == 0, "NULL IME should return 0\n");
348 ret = GetLastError();
349 ok(ret == ERROR_SUCCESS, "wrong last error %08x!\n", ret);
350 SetLastError(0xdeadbeef);
351 ret = ImmNotifyIME(imc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
352 ok (ret == 0, "Destroyed IME should return 0\n");
353 ret = GetLastError();
354 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret);
358 static struct {
359 WNDPROC old_wnd_proc;
360 BOOL catch_result_str;
361 BOOL catch_ime_char;
362 DWORD start;
363 DWORD timer_id;
364 } ime_composition_test;
366 static LRESULT WINAPI test_ime_wnd_proc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
368 switch (msg)
370 case WM_IME_COMPOSITION:
371 if ((lParam & GCS_RESULTSTR) && !ime_composition_test.catch_result_str) {
372 WCHAR wstring[20];
373 HIMC imc;
374 LONG size;
375 LRESULT ret;
377 ret = CallWindowProcA(ime_composition_test.old_wnd_proc,
378 hWnd, msg, wParam, lParam);
380 imc = ImmGetContext(hWnd);
381 size = ImmGetCompositionStringW(imc, GCS_RESULTSTR,
382 wstring, sizeof(wstring));
383 ok(size > 0, "ImmGetCompositionString(GCS_RESULTSTR) is %d\n", size);
384 ImmReleaseContext(hwnd, imc);
386 ime_composition_test.catch_result_str = TRUE;
387 return ret;
389 break;
390 case WM_IME_CHAR:
391 if (!ime_composition_test.catch_ime_char)
392 ime_composition_test.catch_ime_char = TRUE;
393 break;
394 case WM_TIMER:
395 if (wParam == ime_composition_test.timer_id) {
396 HWND parent = GetParent(hWnd);
397 char title[64];
398 int left = 20 - (GetTickCount() - ime_composition_test.start) / 1000;
399 wsprintfA(title, "%sLeft %d sec. - IME composition test",
400 ime_composition_test.catch_result_str ? "[*] " : "", left);
401 SetWindowTextA(parent, title);
402 if (left <= 0)
403 DestroyWindow(parent);
404 else
405 SetTimer(hWnd, wParam, 100, NULL);
406 return TRUE;
408 break;
410 return CallWindowProcA(ime_composition_test.old_wnd_proc,
411 hWnd, msg, wParam, lParam);
414 static void test_ImmGetCompositionString(void)
416 HIMC imc;
417 static const WCHAR string[] = {'w','i','n','e',0x65e5,0x672c,0x8a9e};
418 char cstring[20];
419 WCHAR wstring[20];
420 LONG len;
421 LONG alen,wlen;
422 BOOL ret;
423 DWORD prop;
425 imc = ImmGetContext(hwnd);
426 ret = ImmSetCompositionStringW(imc, SCS_SETSTR, string, sizeof(string), NULL,0);
427 if (!ret) {
428 win_skip("Composition isn't supported\n");
429 ImmReleaseContext(hwnd, imc);
430 return;
432 msg_spy_flush_msgs();
434 alen = ImmGetCompositionStringA(imc, GCS_COMPSTR, cstring, 20);
435 wlen = ImmGetCompositionStringW(imc, GCS_COMPSTR, wstring, 20);
436 /* windows machines without any IME installed just return 0 above */
437 if( alen && wlen)
439 len = ImmGetCompositionStringW(imc, GCS_COMPATTR, NULL, 0);
440 ok(len*sizeof(WCHAR)==wlen,"GCS_COMPATTR(W) not returning correct count\n");
441 len = ImmGetCompositionStringA(imc, GCS_COMPATTR, NULL, 0);
442 ok(len==alen,"GCS_COMPATTR(A) not returning correct count\n");
444 else
445 win_skip("Composition string isn't available\n");
447 ImmReleaseContext(hwnd, imc);
449 /* Test composition results input by IMM API */
450 prop = ImmGetProperty(GetKeyboardLayout(0), IGP_SETCOMPSTR);
451 if (!(prop & SCS_CAP_COMPSTR)) {
452 /* Wine's IME doesn't support SCS_SETSTR in ImmSetCompositionString */
453 skip("This IME doesn't support SCS_SETSTR\n");
455 else {
456 ime_composition_test.old_wnd_proc =
457 (WNDPROC)SetWindowLongPtrA(hwnd, GWLP_WNDPROC,
458 (LONG_PTR)test_ime_wnd_proc);
459 imc = ImmGetContext(hwnd);
460 msg_spy_flush_msgs();
462 ret = ImmSetCompositionStringW(imc, SCS_SETSTR,
463 string, sizeof(string), NULL,0);
464 ok(ret, "ImmSetCompositionStringW failed\n");
465 wlen = ImmGetCompositionStringW(imc, GCS_COMPSTR,
466 wstring, sizeof(wstring));
467 if (wlen > 0) {
468 ret = ImmNotifyIME(imc, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
469 ok(ret, "ImmNotifyIME(CPS_COMPLETE) failed\n");
470 msg_spy_flush_msgs();
471 ok(ime_composition_test.catch_result_str,
472 "WM_IME_COMPOSITION(GCS_RESULTSTR) isn't sent\n");
473 ok(ime_composition_test.catch_ime_char,
474 "WM_IME_CHAR isn't sent\n");
476 else
477 win_skip("Composition string isn't available\n");
478 ImmReleaseContext(hwnd, imc);
479 SetWindowLongPtrA(hwnd, GWLP_WNDPROC,
480 (LONG_PTR)ime_composition_test.old_wnd_proc);
481 msg_spy_flush_msgs();
484 /* Test composition results input by hand */
485 memset(&ime_composition_test, 0, sizeof(ime_composition_test));
486 if (winetest_interactive) {
487 HWND hwndMain, hwndChild;
488 MSG msg;
489 const DWORD MY_TIMER = 0xcaffe;
491 hwndMain = CreateWindowExA(WS_EX_CLIENTEDGE, wndcls,
492 "IME composition test",
493 WS_OVERLAPPEDWINDOW | WS_VISIBLE,
494 CW_USEDEFAULT, CW_USEDEFAULT, 320, 160,
495 NULL, NULL, GetModuleHandleA(NULL), NULL);
496 hwndChild = CreateWindowExA(0, "static",
497 "Input a DBCS character here using IME.",
498 WS_CHILD | WS_VISIBLE | 0
499 /* ES_AUTOHSCROLL | ES_MULTILINE */,
500 0, 0, 320, 100, hwndMain, NULL,
501 GetModuleHandleA(NULL), NULL);
503 ime_composition_test.old_wnd_proc =
504 (WNDPROC)SetWindowLongPtrA(hwndChild, GWLP_WNDPROC,
505 (LONG_PTR)test_ime_wnd_proc);
507 SetFocus(hwndChild);
509 /* FIXME: In wine, IME UI window is created in ImmSetOpenStatus().
510 So, we prepare it here to generate WM_IME_CHAR messages. */
511 imc = ImmGetContext(hwndChild);
512 ImmSetOpenStatus(imc, FALSE);
513 ImmReleaseContext(hwndChild, imc);
515 ime_composition_test.timer_id = MY_TIMER;
516 ime_composition_test.start = GetTickCount();
517 SetTimer(hwndChild, ime_composition_test.timer_id, 100, NULL);
518 while (GetMessageA(&msg, NULL, 0, 0)) {
519 TranslateMessage(&msg);
520 DispatchMessageA(&msg);
521 if (!IsWindow(hwndMain))
522 break;
524 if (!ime_composition_test.catch_result_str)
525 skip("WM_IME_COMPOSITION(GCS_RESULTSTR) isn't tested\n");
526 else
527 ok(ime_composition_test.catch_ime_char,
528 "WM_IME_CHAR isn't sent\n");
529 msg_spy_flush_msgs();
533 static void test_ImmSetCompositionString(void)
535 HIMC imc;
536 BOOL ret;
538 SetLastError(0xdeadbeef);
539 imc = ImmGetContext(hwnd);
540 ok(imc != 0, "ImmGetContext() failed. Last error: %u\n", GetLastError());
541 if (!imc)
542 return;
544 ret = ImmSetCompositionStringW(imc, SCS_SETSTR, NULL, 0, NULL, 0);
545 ok(broken(!ret) ||
546 ret, /* Vista+ */
547 "ImmSetCompositionStringW() failed.\n");
549 ret = ImmSetCompositionStringW(imc, SCS_SETSTR | SCS_CHANGEATTR,
550 NULL, 0, NULL, 0);
551 ok(!ret, "ImmSetCompositionStringW() succeeded.\n");
553 ret = ImmSetCompositionStringW(imc, SCS_SETSTR | SCS_CHANGECLAUSE,
554 NULL, 0, NULL, 0);
555 ok(!ret, "ImmSetCompositionStringW() succeeded.\n");
557 ret = ImmSetCompositionStringW(imc, SCS_CHANGEATTR | SCS_CHANGECLAUSE,
558 NULL, 0, NULL, 0);
559 ok(!ret, "ImmSetCompositionStringW() succeeded.\n");
561 ret = ImmSetCompositionStringW(imc, SCS_SETSTR | SCS_CHANGEATTR | SCS_CHANGECLAUSE,
562 NULL, 0, NULL, 0);
563 ok(!ret, "ImmSetCompositionStringW() succeeded.\n");
565 ImmReleaseContext(hwnd, imc);
568 static void test_ImmIME(void)
570 HIMC imc;
572 imc = ImmGetContext(hwnd);
573 if (imc)
575 BOOL rc;
576 rc = ImmConfigureIMEA(imc, NULL, IME_CONFIG_REGISTERWORD, NULL);
577 ok (rc == 0, "ImmConfigureIMEA did not fail\n");
578 rc = ImmConfigureIMEW(imc, NULL, IME_CONFIG_REGISTERWORD, NULL);
579 ok (rc == 0, "ImmConfigureIMEW did not fail\n");
581 ImmReleaseContext(hwnd,imc);
584 static void test_ImmAssociateContextEx(void)
586 HIMC imc;
587 BOOL rc;
589 if (!pImmAssociateContextEx) return;
591 imc = ImmGetContext(hwnd);
592 if (imc)
594 HIMC retimc, newimc;
596 newimc = ImmCreateContext();
597 ok(newimc != imc, "handles should not be the same\n");
598 rc = pImmAssociateContextEx(NULL, NULL, 0);
599 ok(!rc, "ImmAssociateContextEx succeeded\n");
600 rc = pImmAssociateContextEx(hwnd, NULL, 0);
601 ok(rc, "ImmAssociateContextEx failed\n");
602 rc = pImmAssociateContextEx(NULL, imc, 0);
603 ok(!rc, "ImmAssociateContextEx succeeded\n");
605 rc = pImmAssociateContextEx(hwnd, imc, 0);
606 ok(rc, "ImmAssociateContextEx failed\n");
607 retimc = ImmGetContext(hwnd);
608 ok(retimc == imc, "handles should be the same\n");
609 ImmReleaseContext(hwnd,retimc);
611 rc = pImmAssociateContextEx(hwnd, newimc, 0);
612 ok(rc, "ImmAssociateContextEx failed\n");
613 retimc = ImmGetContext(hwnd);
614 ok(retimc == newimc, "handles should be the same\n");
615 ImmReleaseContext(hwnd,retimc);
617 rc = pImmAssociateContextEx(hwnd, NULL, IACE_DEFAULT);
618 ok(rc, "ImmAssociateContextEx failed\n");
620 ImmReleaseContext(hwnd,imc);
623 typedef struct _igc_threadinfo {
624 HWND hwnd;
625 HANDLE event;
626 HIMC himc;
627 HIMC u_himc;
628 } igc_threadinfo;
631 static DWORD WINAPI ImmGetContextThreadFunc( LPVOID lpParam)
633 HIMC h1,h2;
634 HWND hwnd2;
635 COMPOSITIONFORM cf;
636 CANDIDATEFORM cdf;
637 POINT pt;
638 MSG msg;
640 igc_threadinfo *info= (igc_threadinfo*)lpParam;
641 info->hwnd = CreateWindowExA(WS_EX_CLIENTEDGE, wndcls, "Wine imm32.dll test",
642 WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
643 240, 120, NULL, NULL, GetModuleHandleW(NULL), NULL);
645 h1 = ImmGetContext(hwnd);
646 ok(info->himc == h1, "hwnd context changed in new thread\n");
647 h2 = ImmGetContext(info->hwnd);
648 ok(h2 != h1, "new hwnd in new thread should have different context\n");
649 info->himc = h2;
650 ImmReleaseContext(hwnd,h1);
652 hwnd2 = CreateWindowExA(WS_EX_CLIENTEDGE, wndcls, "Wine imm32.dll test",
653 WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
654 240, 120, NULL, NULL, GetModuleHandleW(NULL), NULL);
655 h1 = ImmGetContext(hwnd2);
657 ok(h1 == h2, "Windows in same thread should have same default context\n");
658 ImmReleaseContext(hwnd2,h1);
659 ImmReleaseContext(info->hwnd,h2);
660 DestroyWindow(hwnd2);
662 /* priming for later tests */
663 ImmSetCompositionWindow(h1, &cf);
664 ImmSetStatusWindowPos(h1, &pt);
665 info->u_himc = ImmCreateContext();
666 ImmSetOpenStatus(info->u_himc, TRUE);
667 cdf.dwIndex = 0;
668 cdf.dwStyle = CFS_CANDIDATEPOS;
669 cdf.ptCurrentPos.x = 0;
670 cdf.ptCurrentPos.y = 0;
671 ImmSetCandidateWindow(info->u_himc, &cdf);
673 SetEvent(info->event);
675 while(GetMessageW(&msg, 0, 0, 0))
677 TranslateMessage(&msg);
678 DispatchMessageW(&msg);
680 return 1;
683 static void test_ImmThreads(void)
685 HIMC himc, otherHimc, h1;
686 igc_threadinfo threadinfo;
687 HANDLE hThread;
688 DWORD dwThreadId;
689 BOOL rc;
690 LOGFONTA lf;
691 COMPOSITIONFORM cf;
692 CANDIDATEFORM cdf;
693 DWORD status, sentence;
694 POINT pt;
696 himc = ImmGetContext(hwnd);
697 threadinfo.event = CreateEventA(NULL, TRUE, FALSE, NULL);
698 threadinfo.himc = himc;
699 hThread = CreateThread(NULL, 0, ImmGetContextThreadFunc, &threadinfo, 0, &dwThreadId );
700 WaitForSingleObject(threadinfo.event, INFINITE);
702 otherHimc = ImmGetContext(threadinfo.hwnd);
704 ok(himc != otherHimc, "Windows from other threads should have different himc\n");
705 ok(otherHimc == threadinfo.himc, "Context from other thread should not change in main thread\n");
707 h1 = ImmAssociateContext(hwnd,otherHimc);
708 ok(h1 == NULL, "Should fail to be able to Associate a default context from a different thread\n");
709 h1 = ImmGetContext(hwnd);
710 ok(h1 == himc, "Context for window should remain unchanged\n");
711 ImmReleaseContext(hwnd,h1);
713 h1 = ImmAssociateContext(hwnd, threadinfo.u_himc);
714 ok (h1 == NULL, "Should fail to associate a context from a different thread\n");
715 h1 = ImmGetContext(hwnd);
716 ok(h1 == himc, "Context for window should remain unchanged\n");
717 ImmReleaseContext(hwnd,h1);
719 h1 = ImmAssociateContext(threadinfo.hwnd, threadinfo.u_himc);
720 ok (h1 == NULL, "Should fail to associate a context from a different thread into a window from that thread.\n");
721 h1 = ImmGetContext(threadinfo.hwnd);
722 ok(h1 == threadinfo.himc, "Context for window should remain unchanged\n");
723 ImmReleaseContext(threadinfo.hwnd,h1);
725 /* OpenStatus */
726 rc = ImmSetOpenStatus(himc, TRUE);
727 ok(rc != 0, "ImmSetOpenStatus failed\n");
728 rc = ImmGetOpenStatus(himc);
729 ok(rc != 0, "ImmGetOpenStatus failed\n");
730 rc = ImmSetOpenStatus(himc, FALSE);
731 ok(rc != 0, "ImmSetOpenStatus failed\n");
732 rc = ImmGetOpenStatus(himc);
733 ok(rc == 0, "ImmGetOpenStatus failed\n");
735 rc = ImmSetOpenStatus(otherHimc, TRUE);
736 ok(rc == 0, "ImmSetOpenStatus should fail\n");
737 rc = ImmSetOpenStatus(threadinfo.u_himc, TRUE);
738 ok(rc == 0, "ImmSetOpenStatus should fail\n");
739 rc = ImmGetOpenStatus(otherHimc);
740 ok(rc == 0, "ImmGetOpenStatus failed\n");
741 rc = ImmGetOpenStatus(threadinfo.u_himc);
742 ok (rc == 1 || broken(rc == 0), "ImmGetOpenStatus should return 1\n");
743 rc = ImmSetOpenStatus(otherHimc, FALSE);
744 ok(rc == 0, "ImmSetOpenStatus should fail\n");
745 rc = ImmGetOpenStatus(otherHimc);
746 ok(rc == 0, "ImmGetOpenStatus failed\n");
748 /* CompositionFont */
749 rc = ImmGetCompositionFontA(himc, &lf);
750 ok(rc != 0, "ImmGetCompositionFont failed\n");
751 rc = ImmSetCompositionFontA(himc, &lf);
752 ok(rc != 0, "ImmSetCompositionFont failed\n");
754 rc = ImmGetCompositionFontA(otherHimc, &lf);
755 ok(rc != 0 || broken(rc == 0), "ImmGetCompositionFont failed\n");
756 rc = ImmGetCompositionFontA(threadinfo.u_himc, &lf);
757 ok(rc != 0 || broken(rc == 0), "ImmGetCompositionFont user himc failed\n");
758 rc = ImmSetCompositionFontA(otherHimc, &lf);
759 ok(rc == 0, "ImmSetCompositionFont should fail\n");
760 rc = ImmSetCompositionFontA(threadinfo.u_himc, &lf);
761 ok(rc == 0, "ImmSetCompositionFont should fail\n");
763 /* CompositionWindow */
764 rc = ImmSetCompositionWindow(himc, &cf);
765 ok(rc != 0, "ImmSetCompositionWindow failed\n");
766 rc = ImmGetCompositionWindow(himc, &cf);
767 ok(rc != 0, "ImmGetCompositionWindow failed\n");
769 rc = ImmSetCompositionWindow(otherHimc, &cf);
770 ok(rc == 0, "ImmSetCompositionWindow should fail\n");
771 rc = ImmSetCompositionWindow(threadinfo.u_himc, &cf);
772 ok(rc == 0, "ImmSetCompositionWindow should fail\n");
773 rc = ImmGetCompositionWindow(otherHimc, &cf);
774 ok(rc != 0 || broken(rc == 0), "ImmGetCompositionWindow failed\n");
775 rc = ImmGetCompositionWindow(threadinfo.u_himc, &cf);
776 ok(rc != 0 || broken(rc == 0), "ImmGetCompositionWindow failed\n");
778 /* ConversionStatus */
779 rc = ImmGetConversionStatus(himc, &status, &sentence);
780 ok(rc != 0, "ImmGetConversionStatus failed\n");
781 rc = ImmSetConversionStatus(himc, status, sentence);
782 ok(rc != 0, "ImmSetConversionStatus failed\n");
784 rc = ImmGetConversionStatus(otherHimc, &status, &sentence);
785 ok(rc != 0 || broken(rc == 0), "ImmGetConversionStatus failed\n");
786 rc = ImmGetConversionStatus(threadinfo.u_himc, &status, &sentence);
787 ok(rc != 0 || broken(rc == 0), "ImmGetConversionStatus failed\n");
788 rc = ImmSetConversionStatus(otherHimc, status, sentence);
789 ok(rc == 0, "ImmSetConversionStatus should fail\n");
790 rc = ImmSetConversionStatus(threadinfo.u_himc, status, sentence);
791 ok(rc == 0, "ImmSetConversionStatus should fail\n");
793 /* StatusWindowPos */
794 rc = ImmSetStatusWindowPos(himc, &pt);
795 ok(rc != 0, "ImmSetStatusWindowPos failed\n");
796 rc = ImmGetStatusWindowPos(himc, &pt);
797 ok(rc != 0, "ImmGetStatusWindowPos failed\n");
799 rc = ImmSetStatusWindowPos(otherHimc, &pt);
800 ok(rc == 0, "ImmSetStatusWindowPos should fail\n");
801 rc = ImmSetStatusWindowPos(threadinfo.u_himc, &pt);
802 ok(rc == 0, "ImmSetStatusWindowPos should fail\n");
803 rc = ImmGetStatusWindowPos(otherHimc, &pt);
804 ok(rc != 0 || broken(rc == 0), "ImmGetStatusWindowPos failed\n");
805 rc = ImmGetStatusWindowPos(threadinfo.u_himc, &pt);
806 ok(rc != 0 || broken(rc == 0), "ImmGetStatusWindowPos failed\n");
808 h1 = ImmAssociateContext(threadinfo.hwnd, NULL);
809 ok (h1 == otherHimc, "ImmAssociateContext cross thread with NULL should work\n");
810 h1 = ImmGetContext(threadinfo.hwnd);
811 ok (h1 == NULL, "CrossThread window context should be NULL\n");
812 h1 = ImmAssociateContext(threadinfo.hwnd, h1);
813 ok (h1 == NULL, "Resetting cross thread context should fail\n");
814 h1 = ImmGetContext(threadinfo.hwnd);
815 ok (h1 == NULL, "CrossThread window context should still be NULL\n");
817 rc = ImmDestroyContext(threadinfo.u_himc);
818 ok (rc == 0, "ImmDestroyContext Cross Thread should fail\n");
820 /* Candidate Window */
821 rc = ImmGetCandidateWindow(himc, 0, &cdf);
822 ok (rc == 0, "ImmGetCandidateWindow should fail\n");
823 cdf.dwIndex = 0;
824 cdf.dwStyle = CFS_CANDIDATEPOS;
825 cdf.ptCurrentPos.x = 0;
826 cdf.ptCurrentPos.y = 0;
827 rc = ImmSetCandidateWindow(himc, &cdf);
828 ok (rc == 1, "ImmSetCandidateWindow should succeed\n");
829 rc = ImmGetCandidateWindow(himc, 0, &cdf);
830 ok (rc == 1, "ImmGetCandidateWindow should succeed\n");
832 rc = ImmGetCandidateWindow(otherHimc, 0, &cdf);
833 ok (rc == 0, "ImmGetCandidateWindow should fail\n");
834 rc = ImmSetCandidateWindow(otherHimc, &cdf);
835 ok (rc == 0, "ImmSetCandidateWindow should fail\n");
836 rc = ImmGetCandidateWindow(threadinfo.u_himc, 0, &cdf);
837 ok (rc == 1 || broken( rc == 0), "ImmGetCandidateWindow should succeed\n");
838 rc = ImmSetCandidateWindow(threadinfo.u_himc, &cdf);
839 ok (rc == 0, "ImmSetCandidateWindow should fail\n");
841 ImmReleaseContext(threadinfo.hwnd,otherHimc);
842 ImmReleaseContext(hwnd,himc);
844 SendMessageA(threadinfo.hwnd, WM_CLOSE, 0, 0);
845 rc = PostThreadMessageA(dwThreadId, WM_QUIT, 1, 0);
846 ok(rc == 1, "PostThreadMessage should succeed\n");
847 WaitForSingleObject(hThread, INFINITE);
848 CloseHandle(hThread);
850 himc = ImmGetContext(GetDesktopWindow());
851 ok(himc == NULL, "Should not be able to get himc from other process window\n");
854 static void test_ImmIsUIMessage(void)
856 struct test
858 UINT msg;
859 BOOL ret;
862 static const struct test tests[] =
864 { WM_MOUSEMOVE, FALSE },
865 { WM_IME_STARTCOMPOSITION, TRUE },
866 { WM_IME_ENDCOMPOSITION, TRUE },
867 { WM_IME_COMPOSITION, TRUE },
868 { WM_IME_SETCONTEXT, TRUE },
869 { WM_IME_NOTIFY, TRUE },
870 { WM_IME_CONTROL, FALSE },
871 { WM_IME_COMPOSITIONFULL, TRUE },
872 { WM_IME_SELECT, TRUE },
873 { WM_IME_CHAR, FALSE },
874 { 0x287 /* FIXME */, TRUE },
875 { WM_IME_REQUEST, FALSE },
876 { WM_IME_KEYDOWN, FALSE },
877 { WM_IME_KEYUP, FALSE },
878 { 0, FALSE } /* mark the end */
881 UINT WM_MSIME_SERVICE = RegisterWindowMessageA("MSIMEService");
882 UINT WM_MSIME_RECONVERTOPTIONS = RegisterWindowMessageA("MSIMEReconvertOptions");
883 UINT WM_MSIME_MOUSE = RegisterWindowMessageA("MSIMEMouseOperation");
884 UINT WM_MSIME_RECONVERTREQUEST = RegisterWindowMessageA("MSIMEReconvertRequest");
885 UINT WM_MSIME_RECONVERT = RegisterWindowMessageA("MSIMEReconvert");
886 UINT WM_MSIME_QUERYPOSITION = RegisterWindowMessageA("MSIMEQueryPosition");
887 UINT WM_MSIME_DOCUMENTFEED = RegisterWindowMessageA("MSIMEDocumentFeed");
889 const struct test *test;
890 BOOL ret;
892 if (!pImmIsUIMessageA) return;
894 for (test = tests; test->msg; test++)
896 msg_spy_flush_msgs();
897 ret = pImmIsUIMessageA(NULL, test->msg, 0, 0);
898 ok(ret == test->ret, "ImmIsUIMessageA returned %x for %x\n", ret, test->msg);
899 ok(!msg_spy_find_msg(test->msg), "Windows does not send 0x%x for NULL hwnd\n", test->msg);
901 ret = pImmIsUIMessageA(hwnd, test->msg, 0, 0);
902 ok(ret == test->ret, "ImmIsUIMessageA returned %x for %x\n", ret, test->msg);
903 if (ret)
904 ok(msg_spy_find_msg(test->msg) != NULL, "Windows does send 0x%x\n", test->msg);
905 else
906 ok(!msg_spy_find_msg(test->msg), "Windows does not send 0x%x\n", test->msg);
909 ret = pImmIsUIMessageA(NULL, WM_MSIME_SERVICE, 0, 0);
910 ok(!ret, "ImmIsUIMessageA returned TRUE for WM_MSIME_SERVICE\n");
911 ret = pImmIsUIMessageA(NULL, WM_MSIME_RECONVERTOPTIONS, 0, 0);
912 ok(!ret, "ImmIsUIMessageA returned TRUE for WM_MSIME_RECONVERTOPTIONS\n");
913 ret = pImmIsUIMessageA(NULL, WM_MSIME_MOUSE, 0, 0);
914 ok(!ret, "ImmIsUIMessageA returned TRUE for WM_MSIME_MOUSE\n");
915 ret = pImmIsUIMessageA(NULL, WM_MSIME_RECONVERTREQUEST, 0, 0);
916 ok(!ret, "ImmIsUIMessageA returned TRUE for WM_MSIME_RECONVERTREQUEST\n");
917 ret = pImmIsUIMessageA(NULL, WM_MSIME_RECONVERT, 0, 0);
918 ok(!ret, "ImmIsUIMessageA returned TRUE for WM_MSIME_RECONVERT\n");
919 ret = pImmIsUIMessageA(NULL, WM_MSIME_QUERYPOSITION, 0, 0);
920 ok(!ret, "ImmIsUIMessageA returned TRUE for WM_MSIME_QUERYPOSITION\n");
921 ret = pImmIsUIMessageA(NULL, WM_MSIME_DOCUMENTFEED, 0, 0);
922 ok(!ret, "ImmIsUIMessageA returned TRUE for WM_MSIME_DOCUMENTFEED\n");
925 static void test_ImmGetContext(void)
927 HIMC himc;
928 DWORD err;
930 SetLastError(0xdeadbeef);
931 himc = ImmGetContext((HWND)0xffffffff);
932 err = GetLastError();
933 ok(himc == NULL, "ImmGetContext succeeded\n");
934 ok(err == ERROR_INVALID_WINDOW_HANDLE, "got %u\n", err);
936 himc = ImmGetContext(hwnd);
937 ok(himc != NULL, "ImmGetContext failed\n");
938 ok(ImmReleaseContext(hwnd, himc), "ImmReleaseContext failed\n");
941 static void test_ImmGetDescription(void)
943 HKL hkl;
944 WCHAR descW[100];
945 CHAR descA[100];
946 UINT ret, lret;
948 /* FIXME: invalid keyboard layouts should not pass */
949 ret = ImmGetDescriptionW(NULL, NULL, 0);
950 ok(!ret, "ImmGetDescriptionW failed, expected 0 received %d.\n", ret);
951 ret = ImmGetDescriptionA(NULL, NULL, 0);
952 ok(!ret, "ImmGetDescriptionA failed, expected 0 received %d.\n", ret);
954 /* load a language with valid IMM descriptions */
955 hkl = GetKeyboardLayout(0);
956 ok(hkl != 0, "GetKeyboardLayout failed, expected != 0.\n");
958 ret = ImmGetDescriptionW(hkl, NULL, 0);
959 if(!ret)
961 win_skip("ImmGetDescriptionW is not working for current loaded keyboard.\n");
962 return;
965 SetLastError(0xdeadcafe);
966 ret = ImmGetDescriptionW(0, NULL, 100);
967 ok (ret == 0, "ImmGetDescriptionW with 0 hkl should return 0\n");
968 ret = GetLastError();
969 ok (ret == 0xdeadcafe, "Last Error should remain unchanged\n");
971 ret = ImmGetDescriptionW(hkl, descW, 0);
972 ok(ret, "ImmGetDescriptionW failed, expected != 0 received 0.\n");
974 lret = ImmGetDescriptionW(hkl, descW, ret + 1);
975 ok(lret, "ImmGetDescriptionW failed, expected != 0 received 0.\n");
976 ok(lret == ret, "ImmGetDescriptionW failed to return the correct amount of data. Expected %d, got %d.\n", ret, lret);
978 lret = ImmGetDescriptionA(hkl, descA, ret + 1);
979 ok(lret, "ImmGetDescriptionA failed, expected != 0 received 0.\n");
980 ok(lret == ret, "ImmGetDescriptionA failed to return the correct amount of data. Expected %d, got %d.\n", ret, lret);
982 ret /= 2; /* try to copy partially */
983 lret = ImmGetDescriptionW(hkl, descW, ret + 1);
984 ok(lret, "ImmGetDescriptionW failed, expected != 0 received 0.\n");
985 ok(lret == ret, "ImmGetDescriptionW failed to return the correct amount of data. Expected %d, got %d.\n", ret, lret);
987 lret = ImmGetDescriptionA(hkl, descA, ret + 1);
988 ok(!lret, "ImmGetDescriptionA should fail\n");
990 ret = ImmGetDescriptionW(hkl, descW, 1);
991 ok(!ret, "ImmGetDescriptionW failed, expected 0 received %d.\n", ret);
993 UnloadKeyboardLayout(hkl);
996 static LRESULT (WINAPI *old_imm_wnd_proc)(HWND, UINT, WPARAM, LPARAM);
997 static LRESULT WINAPI imm_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
999 ok(msg != WM_DESTROY, "got WM_DESTROY message\n");
1000 return old_imm_wnd_proc(hwnd, msg, wparam, lparam);
1003 static HWND thread_ime_wnd;
1004 static DWORD WINAPI test_ImmGetDefaultIMEWnd_thread(void *arg)
1006 CreateWindowA("static", "static", WS_POPUP, 0, 0, 1, 1, NULL, NULL, NULL, NULL);
1008 thread_ime_wnd = ImmGetDefaultIMEWnd(0);
1009 ok(thread_ime_wnd != 0, "ImmGetDefaultIMEWnd returned NULL\n");
1010 old_imm_wnd_proc = (void*)SetWindowLongPtrW(thread_ime_wnd, GWLP_WNDPROC, (LONG_PTR)imm_wnd_proc);
1011 return 0;
1014 static void test_ImmDefaultHwnd(void)
1016 HIMC imc1, imc2, imc3;
1017 HWND def1, def3;
1018 HANDLE thread;
1019 HWND hwnd;
1020 char title[16];
1021 LONG style;
1023 hwnd = CreateWindowExA(WS_EX_CLIENTEDGE, "EDIT", "Wine imm32.dll test",
1024 WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
1025 240, 120, NULL, NULL, GetModuleHandleW(NULL), NULL);
1027 ShowWindow(hwnd, SW_SHOWNORMAL);
1029 imc1 = ImmGetContext(hwnd);
1030 if (!imc1)
1032 win_skip("IME support not implemented\n");
1033 return;
1036 def1 = ImmGetDefaultIMEWnd(hwnd);
1038 GetWindowTextA(def1, title, sizeof(title));
1039 ok(!strcmp(title, "Default IME"), "got %s\n", title);
1040 style = GetWindowLongA(def1, GWL_STYLE);
1041 ok(style == (WS_DISABLED | WS_POPUP | WS_CLIPSIBLINGS), "got %08x\n", style);
1042 style = GetWindowLongA(def1, GWL_EXSTYLE);
1043 ok(style == 0, "got %08x\n", style);
1045 imc2 = ImmCreateContext();
1046 ImmSetOpenStatus(imc2, TRUE);
1048 imc3 = ImmGetContext(hwnd);
1049 def3 = ImmGetDefaultIMEWnd(hwnd);
1051 ok(def3 == def1, "Default IME window should not change\n");
1052 ok(imc1 == imc3, "IME context should not change\n");
1053 ImmSetOpenStatus(imc2, FALSE);
1055 thread = CreateThread(NULL, 0, test_ImmGetDefaultIMEWnd_thread, NULL, 0, NULL);
1056 WaitForSingleObject(thread, INFINITE);
1057 ok(thread_ime_wnd != def1, "thread_ime_wnd == def1\n");
1058 ok(!IsWindow(thread_ime_wnd), "thread_ime_wnd was not destroyed\n");
1059 CloseHandle(thread);
1061 ImmReleaseContext(hwnd, imc1);
1062 ImmReleaseContext(hwnd, imc3);
1063 ImmDestroyContext(imc2);
1064 DestroyWindow(hwnd);
1067 static BOOL CALLBACK is_ime_window_proc(HWND hWnd, LPARAM param)
1069 static const WCHAR imeW[] = {'I','M','E',0};
1070 WCHAR class_nameW[16];
1071 HWND *ime_window = (HWND *)param;
1072 if (GetClassNameW(hWnd, class_nameW, sizeof(class_nameW)/sizeof(class_nameW[0])) &&
1073 !lstrcmpW(class_nameW, imeW)) {
1074 *ime_window = hWnd;
1075 return FALSE;
1077 return TRUE;
1080 static HWND get_ime_window(void)
1082 HWND ime_window = NULL;
1083 EnumThreadWindows(GetCurrentThreadId(), is_ime_window_proc, (LPARAM)&ime_window);
1084 return ime_window;
1087 struct testcase_ime_window {
1088 BOOL visible;
1089 BOOL top_level_window;
1092 static DWORD WINAPI test_default_ime_window_cb(void *arg)
1094 struct testcase_ime_window *testcase = (struct testcase_ime_window *)arg;
1095 DWORD visible = testcase->visible ? WS_VISIBLE : 0;
1096 HWND hwnd1, hwnd2, default_ime_wnd, ime_wnd;
1098 ok(!get_ime_window(), "Expected no IME windows\n");
1099 if (testcase->top_level_window) {
1100 test_phase = FIRST_WINDOW;
1101 hwnd1 = CreateWindowExA(WS_EX_CLIENTEDGE, wndcls, "Wine imm32.dll test",
1102 WS_OVERLAPPEDWINDOW | visible,
1103 CW_USEDEFAULT, CW_USEDEFAULT,
1104 240, 120, NULL, NULL, GetModuleHandleW(NULL), NULL);
1106 else {
1107 hwnd1 = CreateWindowExA(WS_EX_CLIENTEDGE, "EDIT", "Wine imm32.dll test",
1108 WS_CHILD | visible,
1109 CW_USEDEFAULT, CW_USEDEFAULT,
1110 240, 24, hwnd, NULL, GetModuleHandleW(NULL), NULL);
1112 ime_wnd = get_ime_window();
1113 ok(ime_wnd != NULL, "Expected IME window existence\n");
1114 default_ime_wnd = ImmGetDefaultIMEWnd(hwnd1);
1115 ok(ime_wnd == default_ime_wnd, "Expected %p, got %p\n", ime_wnd, default_ime_wnd);
1117 test_phase = SECOND_WINDOW;
1118 hwnd2 = CreateWindowExA(WS_EX_CLIENTEDGE, wndcls, "Wine imm32.dll test",
1119 WS_OVERLAPPEDWINDOW | visible,
1120 CW_USEDEFAULT, CW_USEDEFAULT,
1121 240, 120, NULL, NULL, GetModuleHandleW(NULL), NULL);
1122 DestroyWindow(hwnd2);
1123 ok(IsWindow(ime_wnd) ||
1124 broken(!testcase->visible /* Vista */) ||
1125 broken(!testcase->top_level_window /* Vista */) ,
1126 "Expected IME window existence\n");
1127 DestroyWindow(hwnd1);
1128 ok(!IsWindow(ime_wnd), "Expected no IME windows\n");
1129 return 1;
1132 static DWORD WINAPI test_default_ime_window_cancel_cb(void *arg)
1134 struct testcase_ime_window *testcase = (struct testcase_ime_window *)arg;
1135 DWORD visible = testcase->visible ? WS_VISIBLE : 0;
1136 HWND hwnd1, hwnd2, default_ime_wnd, ime_wnd;
1138 ok(!get_ime_window(), "Expected no IME windows\n");
1139 test_phase = NCCREATE_CANCEL;
1140 hwnd1 = CreateWindowExA(WS_EX_CLIENTEDGE, wndcls, "Wine imm32.dll test",
1141 WS_OVERLAPPEDWINDOW | visible,
1142 CW_USEDEFAULT, CW_USEDEFAULT,
1143 240, 120, NULL, NULL, GetModuleHandleW(NULL), NULL);
1144 ok(hwnd1 == NULL, "creation succeeded, got %p\n", hwnd1);
1145 ok(!get_ime_window(), "Expected no IME windows\n");
1147 test_phase = CREATE_CANCEL;
1148 hwnd1 = CreateWindowExA(WS_EX_CLIENTEDGE, wndcls, "Wine imm32.dll test",
1149 WS_OVERLAPPEDWINDOW | visible,
1150 CW_USEDEFAULT, CW_USEDEFAULT,
1151 240, 120, NULL, NULL, GetModuleHandleW(NULL), NULL);
1152 ok(hwnd1 == NULL, "creation succeeded, got %p\n", hwnd1);
1153 ok(!get_ime_window(), "Expected no IME windows\n");
1155 test_phase = FIRST_WINDOW;
1156 hwnd2 = CreateWindowExA(WS_EX_CLIENTEDGE, wndcls, "Wine imm32.dll test",
1157 WS_OVERLAPPEDWINDOW | visible,
1158 CW_USEDEFAULT, CW_USEDEFAULT,
1159 240, 120, NULL, NULL, GetModuleHandleW(NULL), NULL);
1160 ime_wnd = get_ime_window();
1161 ok(ime_wnd != NULL, "Expected IME window existence\n");
1162 default_ime_wnd = ImmGetDefaultIMEWnd(hwnd2);
1163 ok(ime_wnd == default_ime_wnd, "Expected %p, got %p\n", ime_wnd, default_ime_wnd);
1165 DestroyWindow(hwnd2);
1166 ok(!IsWindow(ime_wnd), "Expected no IME windows\n");
1167 return 1;
1170 static DWORD WINAPI test_default_ime_disabled_cb(void *arg)
1172 HWND hWnd, default_ime_wnd;
1174 ok(!get_ime_window(), "Expected no IME windows\n");
1175 ImmDisableIME(GetCurrentThreadId());
1176 test_phase = IME_DISABLED;
1177 hWnd = CreateWindowExA(WS_EX_CLIENTEDGE, wndcls, "Wine imm32.dll test",
1178 WS_OVERLAPPEDWINDOW | WS_VISIBLE,
1179 CW_USEDEFAULT, CW_USEDEFAULT,
1180 240, 120, NULL, NULL, GetModuleHandleW(NULL), NULL);
1181 default_ime_wnd = ImmGetDefaultIMEWnd(hWnd);
1182 ok(!default_ime_wnd, "Expected no IME windows\n");
1183 DestroyWindow(hWnd);
1184 return 1;
1187 static DWORD WINAPI test_default_ime_with_message_only_window_cb(void *arg)
1189 HWND hwnd1, hwnd2, default_ime_wnd;
1191 test_phase = PHASE_UNKNOWN;
1192 hwnd1 = CreateWindowA(wndcls, "Wine imm32.dll test",
1193 WS_OVERLAPPEDWINDOW,
1194 CW_USEDEFAULT, CW_USEDEFAULT,
1195 240, 120, HWND_MESSAGE, NULL, GetModuleHandleW(NULL), NULL);
1196 default_ime_wnd = ImmGetDefaultIMEWnd(hwnd1);
1197 ok(!IsWindow(default_ime_wnd), "Expected no IME windows, got %p\n", default_ime_wnd);
1199 hwnd2 = CreateWindowA(wndcls, "Wine imm32.dll test",
1200 WS_OVERLAPPEDWINDOW,
1201 CW_USEDEFAULT, CW_USEDEFAULT,
1202 240, 120, hwnd1, NULL, GetModuleHandleW(NULL), NULL);
1203 default_ime_wnd = ImmGetDefaultIMEWnd(hwnd2);
1204 ok(IsWindow(default_ime_wnd), "Expected IME window existence\n");
1206 DestroyWindow(hwnd2);
1207 DestroyWindow(hwnd1);
1209 hwnd1 = CreateWindowA(wndcls, "Wine imm32.dll test",
1210 WS_OVERLAPPEDWINDOW,
1211 CW_USEDEFAULT, CW_USEDEFAULT,
1212 240, 120, NULL, NULL, GetModuleHandleW(NULL), NULL);
1213 default_ime_wnd = ImmGetDefaultIMEWnd(hwnd1);
1214 ok(IsWindow(default_ime_wnd), "Expected IME window existence\n");
1215 SetParent(hwnd1, HWND_MESSAGE);
1216 default_ime_wnd = ImmGetDefaultIMEWnd(hwnd1);
1217 ok(IsWindow(default_ime_wnd), "Expected IME window existence\n");
1218 DestroyWindow(hwnd1);
1219 return 1;
1222 static void test_default_ime_window_creation(void)
1224 HANDLE thread;
1225 size_t i;
1226 struct testcase_ime_window testcases[] = {
1227 /* visible, top-level window */
1228 { TRUE, TRUE },
1229 { FALSE, TRUE },
1230 { TRUE, FALSE },
1231 { FALSE, FALSE }
1234 for (i = 0; i < sizeof(testcases)/sizeof(testcases[0]); i++)
1236 thread = CreateThread(NULL, 0, test_default_ime_window_cb, &testcases[i], 0, NULL);
1237 ok(thread != NULL, "CreateThread failed with error %u\n", GetLastError());
1238 while (MsgWaitForMultipleObjects(1, &thread, FALSE, INFINITE, QS_ALLINPUT) == WAIT_OBJECT_0 + 1)
1240 MSG msg;
1241 while (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE))
1243 TranslateMessage(&msg);
1244 DispatchMessageA(&msg);
1247 CloseHandle(thread);
1249 if (testcases[i].top_level_window)
1251 thread = CreateThread(NULL, 0, test_default_ime_window_cancel_cb, &testcases[i], 0, NULL);
1252 ok(thread != NULL, "CreateThread failed with error %u\n", GetLastError());
1253 WaitForSingleObject(thread, INFINITE);
1254 CloseHandle(thread);
1258 thread = CreateThread(NULL, 0, test_default_ime_disabled_cb, NULL, 0, NULL);
1259 WaitForSingleObject(thread, INFINITE);
1260 CloseHandle(thread);
1262 thread = CreateThread(NULL, 0, test_default_ime_with_message_only_window_cb, NULL, 0, NULL);
1263 WaitForSingleObject(thread, INFINITE);
1264 CloseHandle(thread);
1266 test_phase = PHASE_UNKNOWN;
1269 static void test_ImmGetIMCLockCount(void)
1271 HIMC imc;
1272 DWORD count, ret, i;
1273 INPUTCONTEXT *ic;
1275 imc = ImmCreateContext();
1276 ImmDestroyContext(imc);
1277 SetLastError(0xdeadbeef);
1278 count = ImmGetIMCLockCount((HIMC)0xdeadcafe);
1279 ok(count == 0, "Invalid IMC should return 0\n");
1280 ret = GetLastError();
1281 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret);
1282 SetLastError(0xdeadbeef);
1283 count = ImmGetIMCLockCount(0x00000000);
1284 ok(count == 0, "NULL IMC should return 0\n");
1285 ret = GetLastError();
1286 ok(ret == 0xdeadbeef, "Last Error should remain unchanged: %08x\n",ret);
1287 count = ImmGetIMCLockCount(imc);
1288 ok(count == 0, "Destroyed IMC should return 0\n");
1289 ret = GetLastError();
1290 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret);
1292 imc = ImmCreateContext();
1293 count = ImmGetIMCLockCount(imc);
1294 ok(count == 0, "expect 0, returned %d\n", count);
1295 ic = ImmLockIMC(imc);
1296 ok(ic != NULL, "ImmLockIMC failed!\n");
1297 count = ImmGetIMCLockCount(imc);
1298 ok(count == 1, "expect 1, returned %d\n", count);
1299 ret = ImmUnlockIMC(imc);
1300 ok(ret == TRUE, "expect TRUE, ret %d\n", ret);
1301 count = ImmGetIMCLockCount(imc);
1302 ok(count == 0, "expect 0, returned %d\n", count);
1303 ret = ImmUnlockIMC(imc);
1304 ok(ret == TRUE, "expect TRUE, ret %d\n", ret);
1305 count = ImmGetIMCLockCount(imc);
1306 ok(count == 0, "expect 0, returned %d\n", count);
1308 for (i = 0; i < GMEM_LOCKCOUNT * 2; i++)
1310 ic = ImmLockIMC(imc);
1311 ok(ic != NULL, "ImmLockIMC failed!\n");
1313 count = ImmGetIMCLockCount(imc);
1314 todo_wine ok(count == GMEM_LOCKCOUNT, "expect GMEM_LOCKCOUNT, returned %d\n", count);
1316 for (i = 0; i < GMEM_LOCKCOUNT - 1; i++)
1317 ImmUnlockIMC(imc);
1318 count = ImmGetIMCLockCount(imc);
1319 todo_wine ok(count == 1, "expect 1, returned %d\n", count);
1320 ImmUnlockIMC(imc);
1321 count = ImmGetIMCLockCount(imc);
1322 todo_wine ok(count == 0, "expect 0, returned %d\n", count);
1324 ImmDestroyContext(imc);
1327 static void test_ImmGetIMCCLockCount(void)
1329 HIMCC imcc;
1330 DWORD count, g_count, i;
1331 BOOL ret;
1332 VOID *p;
1334 imcc = ImmCreateIMCC(sizeof(CANDIDATEINFO));
1335 count = ImmGetIMCCLockCount(imcc);
1336 ok(count == 0, "expect 0, returned %d\n", count);
1337 ImmLockIMCC(imcc);
1338 count = ImmGetIMCCLockCount(imcc);
1339 ok(count == 1, "expect 1, returned %d\n", count);
1340 ret = ImmUnlockIMCC(imcc);
1341 ok(ret == FALSE, "expect FALSE, ret %d\n", ret);
1342 count = ImmGetIMCCLockCount(imcc);
1343 ok(count == 0, "expect 0, returned %d\n", count);
1344 ret = ImmUnlockIMCC(imcc);
1345 ok(ret == FALSE, "expect FALSE, ret %d\n", ret);
1346 count = ImmGetIMCCLockCount(imcc);
1347 ok(count == 0, "expect 0, returned %d\n", count);
1349 p = ImmLockIMCC(imcc);
1350 ok(GlobalHandle(p) == imcc, "expect %p, returned %p\n", imcc, GlobalHandle(p));
1352 for (i = 0; i < GMEM_LOCKCOUNT * 2; i++)
1354 ImmLockIMCC(imcc);
1355 count = ImmGetIMCCLockCount(imcc);
1356 g_count = GlobalFlags(imcc) & GMEM_LOCKCOUNT;
1357 ok(count == g_count, "count %d, g_count %d\n", count, g_count);
1359 count = ImmGetIMCCLockCount(imcc);
1360 ok(count == GMEM_LOCKCOUNT, "expect GMEM_LOCKCOUNT, returned %d\n", count);
1362 for (i = 0; i < GMEM_LOCKCOUNT - 1; i++)
1363 GlobalUnlock(imcc);
1364 count = ImmGetIMCCLockCount(imcc);
1365 ok(count == 1, "expect 1, returned %d\n", count);
1366 GlobalUnlock(imcc);
1367 count = ImmGetIMCCLockCount(imcc);
1368 ok(count == 0, "expect 0, returned %d\n", count);
1370 ImmDestroyIMCC(imcc);
1373 static void test_ImmDestroyContext(void)
1375 HIMC imc;
1376 DWORD ret, count;
1377 INPUTCONTEXT *ic;
1379 imc = ImmCreateContext();
1380 count = ImmGetIMCLockCount(imc);
1381 ok(count == 0, "expect 0, returned %d\n", count);
1382 ic = ImmLockIMC(imc);
1383 ok(ic != NULL, "ImmLockIMC failed!\n");
1384 count = ImmGetIMCLockCount(imc);
1385 ok(count == 1, "expect 1, returned %d\n", count);
1386 ret = ImmDestroyContext(imc);
1387 ok(ret == TRUE, "Destroy a locked IMC should success!\n");
1388 ic = ImmLockIMC(imc);
1389 ok(ic == NULL, "Lock a destroyed IMC should fail!\n");
1390 ret = ImmUnlockIMC(imc);
1391 ok(ret == FALSE, "Unlock a destroyed IMC should fail!\n");
1392 count = ImmGetIMCLockCount(imc);
1393 ok(count == 0, "Get lock count of a destroyed IMC should return 0!\n");
1394 SetLastError(0xdeadbeef);
1395 ret = ImmDestroyContext(imc);
1396 ok(ret == FALSE, "Destroy a destroyed IMC should fail!\n");
1397 ret = GetLastError();
1398 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret);
1401 static void test_ImmDestroyIMCC(void)
1403 HIMCC imcc;
1404 DWORD ret, count, size;
1405 VOID *p;
1407 imcc = ImmCreateIMCC(sizeof(CANDIDATEINFO));
1408 count = ImmGetIMCCLockCount(imcc);
1409 ok(count == 0, "expect 0, returned %d\n", count);
1410 p = ImmLockIMCC(imcc);
1411 ok(p != NULL, "ImmLockIMCC failed!\n");
1412 count = ImmGetIMCCLockCount(imcc);
1413 ok(count == 1, "expect 1, returned %d\n", count);
1414 size = ImmGetIMCCSize(imcc);
1415 ok(size == sizeof(CANDIDATEINFO), "returned %d\n", size);
1416 p = ImmDestroyIMCC(imcc);
1417 ok(p == NULL, "Destroy a locked IMCC should success!\n");
1418 p = ImmLockIMCC(imcc);
1419 ok(p == NULL, "Lock a destroyed IMCC should fail!\n");
1420 ret = ImmUnlockIMCC(imcc);
1421 ok(ret == FALSE, "Unlock a destroyed IMCC should return FALSE!\n");
1422 count = ImmGetIMCCLockCount(imcc);
1423 ok(count == 0, "Get lock count of a destroyed IMCC should return 0!\n");
1424 size = ImmGetIMCCSize(imcc);
1425 ok(size == 0, "Get size of a destroyed IMCC should return 0!\n");
1426 SetLastError(0xdeadbeef);
1427 p = ImmDestroyIMCC(imcc);
1428 ok(p != NULL, "returned NULL\n");
1429 ret = GetLastError();
1430 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret);
1433 static void test_ImmMessages(void)
1435 CANDIDATEFORM cf;
1436 imm_msgs *msg;
1437 HWND defwnd;
1438 HIMC imc;
1439 UINT idx = 0;
1441 LPINPUTCONTEXT lpIMC;
1442 LPTRANSMSG lpTransMsg;
1444 HWND hwnd = CreateWindowExA(WS_EX_CLIENTEDGE, "EDIT", "Wine imm32.dll test",
1445 WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
1446 240, 120, NULL, NULL, GetModuleHandleA(NULL), NULL);
1448 ShowWindow(hwnd, SW_SHOWNORMAL);
1449 defwnd = ImmGetDefaultIMEWnd(hwnd);
1450 imc = ImmGetContext(hwnd);
1452 ImmSetOpenStatus(imc, TRUE);
1453 msg_spy_flush_msgs();
1454 SendMessageA(defwnd, WM_IME_CONTROL, IMC_GETCANDIDATEPOS, (LPARAM)&cf );
1457 msg = msg_spy_find_next_msg(WM_IME_CONTROL,&idx);
1458 if (msg) ok(!msg->post, "Message should not be posted\n");
1459 } while (msg);
1460 msg_spy_flush_msgs();
1462 lpIMC = ImmLockIMC(imc);
1463 lpIMC->hMsgBuf = ImmReSizeIMCC(lpIMC->hMsgBuf, (lpIMC->dwNumMsgBuf + 1) * sizeof(TRANSMSG));
1464 lpTransMsg = ImmLockIMCC(lpIMC->hMsgBuf);
1465 lpTransMsg += lpIMC->dwNumMsgBuf;
1466 lpTransMsg->message = WM_IME_STARTCOMPOSITION;
1467 lpTransMsg->wParam = 0;
1468 lpTransMsg->lParam = 0;
1469 ImmUnlockIMCC(lpIMC->hMsgBuf);
1470 lpIMC->dwNumMsgBuf++;
1471 ImmUnlockIMC(imc);
1472 ImmGenerateMessage(imc);
1473 idx = 0;
1476 msg = msg_spy_find_next_msg(WM_IME_STARTCOMPOSITION, &idx);
1477 if (msg) ok(!msg->post, "Message should not be posted\n");
1478 } while (msg);
1479 msg_spy_flush_msgs();
1481 lpIMC = ImmLockIMC(imc);
1482 lpIMC->hMsgBuf = ImmReSizeIMCC(lpIMC->hMsgBuf, (lpIMC->dwNumMsgBuf + 1) * sizeof(TRANSMSG));
1483 lpTransMsg = ImmLockIMCC(lpIMC->hMsgBuf);
1484 lpTransMsg += lpIMC->dwNumMsgBuf;
1485 lpTransMsg->message = WM_IME_COMPOSITION;
1486 lpTransMsg->wParam = 0;
1487 lpTransMsg->lParam = 0;
1488 ImmUnlockIMCC(lpIMC->hMsgBuf);
1489 lpIMC->dwNumMsgBuf++;
1490 ImmUnlockIMC(imc);
1491 ImmGenerateMessage(imc);
1492 idx = 0;
1495 msg = msg_spy_find_next_msg(WM_IME_COMPOSITION, &idx);
1496 if (msg) ok(!msg->post, "Message should not be posted\n");
1497 } while (msg);
1498 msg_spy_flush_msgs();
1500 lpIMC = ImmLockIMC(imc);
1501 lpIMC->hMsgBuf = ImmReSizeIMCC(lpIMC->hMsgBuf, (lpIMC->dwNumMsgBuf + 1) * sizeof(TRANSMSG));
1502 lpTransMsg = ImmLockIMCC(lpIMC->hMsgBuf);
1503 lpTransMsg += lpIMC->dwNumMsgBuf;
1504 lpTransMsg->message = WM_IME_ENDCOMPOSITION;
1505 lpTransMsg->wParam = 0;
1506 lpTransMsg->lParam = 0;
1507 ImmUnlockIMCC(lpIMC->hMsgBuf);
1508 lpIMC->dwNumMsgBuf++;
1509 ImmUnlockIMC(imc);
1510 ImmGenerateMessage(imc);
1511 idx = 0;
1514 msg = msg_spy_find_next_msg(WM_IME_ENDCOMPOSITION, &idx);
1515 if (msg) ok(!msg->post, "Message should not be posted\n");
1516 } while (msg);
1517 msg_spy_flush_msgs();
1519 ImmSetOpenStatus(imc, FALSE);
1520 ImmReleaseContext(hwnd, imc);
1521 DestroyWindow(hwnd);
1524 static LRESULT CALLBACK processkey_wnd_proc( HWND hWnd, UINT msg, WPARAM wParam,
1525 LPARAM lParam )
1527 return DefWindowProcW(hWnd, msg, wParam, lParam);
1530 static void test_ime_processkey(void)
1532 WCHAR classNameW[] = {'P','r','o','c','e','s','s', 'K','e','y','T','e','s','t','C','l','a','s','s',0};
1533 WCHAR windowNameW[] = {'P','r','o','c','e','s','s', 'K','e','y',0};
1535 MSG msg;
1536 WNDCLASSW wclass;
1537 HANDLE hInstance = GetModuleHandleW(NULL);
1538 TEST_INPUT inputs[2];
1539 HIMC imc;
1540 INT rc;
1541 HWND hWndTest;
1543 wclass.lpszClassName = classNameW;
1544 wclass.style = CS_HREDRAW | CS_VREDRAW;
1545 wclass.lpfnWndProc = processkey_wnd_proc;
1546 wclass.hInstance = hInstance;
1547 wclass.hIcon = LoadIconW(0, (LPCWSTR)IDI_APPLICATION);
1548 wclass.hCursor = LoadCursorW( NULL, (LPCWSTR)IDC_ARROW);
1549 wclass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
1550 wclass.lpszMenuName = 0;
1551 wclass.cbClsExtra = 0;
1552 wclass.cbWndExtra = 0;
1553 if(!RegisterClassW(&wclass)){
1554 win_skip("Failed to register window.\n");
1555 return;
1558 /* create the test window that will receive the keystrokes */
1559 hWndTest = CreateWindowW(wclass.lpszClassName, windowNameW,
1560 WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, 100, 100,
1561 NULL, NULL, hInstance, NULL);
1563 ShowWindow(hWndTest, SW_SHOW);
1564 SetWindowPos(hWndTest, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE);
1565 SetForegroundWindow(hWndTest);
1566 UpdateWindow(hWndTest);
1568 imc = ImmGetContext(hWndTest);
1569 if (!imc)
1571 win_skip("IME not supported\n");
1572 DestroyWindow(hWndTest);
1573 return;
1576 rc = ImmSetOpenStatus(imc, TRUE);
1577 if (rc != TRUE)
1579 win_skip("Unable to open IME\n");
1580 ImmReleaseContext(hWndTest, imc);
1581 DestroyWindow(hWndTest);
1582 return;
1585 /* flush pending messages */
1586 while (PeekMessageW(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageW(&msg);
1588 SetFocus(hWndTest);
1590 /* init input data that never changes */
1591 inputs[1].type = inputs[0].type = INPUT_KEYBOARD;
1592 inputs[1].u.ki.dwExtraInfo = inputs[0].u.ki.dwExtraInfo = 0;
1593 inputs[1].u.ki.time = inputs[0].u.ki.time = 0;
1595 /* Pressing a key */
1596 inputs[0].u.ki.wVk = 0x41;
1597 inputs[0].u.ki.wScan = 0x1e;
1598 inputs[0].u.ki.dwFlags = 0x0;
1600 pSendInput(1, (INPUT*)inputs, sizeof(INPUT));
1602 while(PeekMessageW(&msg, hWndTest, 0, 0, PM_NOREMOVE)) {
1603 if(msg.message != WM_KEYDOWN)
1604 PeekMessageW(&msg, hWndTest, 0, 0, PM_REMOVE);
1605 else
1607 ok(msg.wParam != VK_PROCESSKEY,"Incorrect ProcessKey Found\n");
1608 PeekMessageW(&msg, hWndTest, 0, 0, PM_REMOVE);
1609 if(msg.wParam == VK_PROCESSKEY)
1610 trace("ProcessKey was correctly found\n");
1612 TranslateMessage(&msg);
1613 DispatchMessageW(&msg);
1616 inputs[0].u.ki.wVk = 0x41;
1617 inputs[0].u.ki.wScan = 0x1e;
1618 inputs[0].u.ki.dwFlags = KEYEVENTF_KEYUP;
1620 pSendInput(1, (INPUT*)inputs, sizeof(INPUT));
1622 while(PeekMessageW(&msg, hWndTest, 0, 0, PM_NOREMOVE)) {
1623 if(msg.message != WM_KEYUP)
1624 PeekMessageW(&msg, hWndTest, 0, 0, PM_REMOVE);
1625 else
1627 ok(msg.wParam != VK_PROCESSKEY,"Incorrect ProcessKey Found\n");
1628 PeekMessageW(&msg, hWndTest, 0, 0, PM_REMOVE);
1629 ok(msg.wParam != VK_PROCESSKEY,"ProcessKey should still not be Found\n");
1631 TranslateMessage(&msg);
1632 DispatchMessageW(&msg);
1635 ImmReleaseContext(hWndTest, imc);
1636 ImmSetOpenStatus(imc, FALSE);
1637 DestroyWindow(hWndTest);
1640 static void test_InvalidIMC(void)
1642 HIMC imc_destroy;
1643 HIMC imc_null = 0x00000000;
1644 HIMC imc_bad = (HIMC)0xdeadcafe;
1646 HIMC imc1, imc2, oldimc;
1647 DWORD ret;
1648 DWORD count;
1649 CHAR buffer[1000];
1650 INPUTCONTEXT *ic;
1651 LOGFONTA lf;
1653 memset(&lf, 0, sizeof(lf));
1655 imc_destroy = ImmCreateContext();
1656 ret = ImmDestroyContext(imc_destroy);
1657 ok(ret == TRUE, "Destroy an IMC should success!\n");
1659 /* Test associating destroyed imc */
1660 imc1 = ImmGetContext(hwnd);
1661 SetLastError(0xdeadbeef);
1662 oldimc = ImmAssociateContext(hwnd, imc_destroy);
1663 ok(!oldimc, "Associating to a destroyed imc should fail!\n");
1664 ret = GetLastError();
1665 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret);
1666 imc2 = ImmGetContext(hwnd);
1667 ok(imc1 == imc2, "imc should not changed! imc1 %p, imc2 %p\n", imc1, imc2);
1669 /* Test associating NULL imc, which is different from an invalid imc */
1670 oldimc = ImmAssociateContext(hwnd, imc_null);
1671 ok(oldimc != NULL, "Associating to NULL imc should success!\n");
1672 imc2 = ImmGetContext(hwnd);
1673 ok(!imc2, "expect NULL, returned %p\n", imc2);
1674 oldimc = ImmAssociateContext(hwnd, imc1);
1675 ok(!oldimc, "expect NULL, returned %p\n", oldimc);
1676 imc2 = ImmGetContext(hwnd);
1677 ok(imc2 == imc1, "imc should not changed! imc2 %p, imc1 %p\n", imc2, imc1);
1679 /* Test associating invalid imc */
1680 imc1 = ImmGetContext(hwnd);
1681 SetLastError(0xdeadbeef);
1682 oldimc = ImmAssociateContext(hwnd, imc_bad);
1683 ok(!oldimc, "Associating to a destroyed imc should fail!\n");
1684 ret = GetLastError();
1685 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret);
1686 imc2 = ImmGetContext(hwnd);
1687 ok(imc1 == imc2, "imc should not changed! imc1 %p, imc2 %p\n", imc1, imc2);
1690 /* Test ImmGetCandidateListA */
1691 SetLastError(0xdeadbeef);
1692 ret = ImmGetCandidateListA(imc_bad, 0, NULL, 0);
1693 ok(ret == 0, "Bad IME should return 0\n");
1694 ret = GetLastError();
1695 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret);
1696 SetLastError(0xdeadbeef);
1697 ret = ImmGetCandidateListA(imc_null, 0, NULL, 0);
1698 ok(ret == 0, "NULL IME should return 0\n");
1699 ret = GetLastError();
1700 ok(ret == 0xdeadbeef, "last error should remain unchanged %08x!\n", ret);
1701 SetLastError(0xdeadbeef);
1702 ret = ImmGetCandidateListA(imc_destroy, 0, NULL, 0);
1703 ok(ret == 0, "Destroyed IME should return 0\n");
1704 ret = GetLastError();
1705 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret);
1707 /* Test ImmGetCandidateListCountA*/
1708 SetLastError(0xdeadbeef);
1709 ret = ImmGetCandidateListCountA(imc_bad,&count);
1710 ok(ret == 0, "Bad IME should return 0\n");
1711 ret = GetLastError();
1712 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret);
1713 SetLastError(0xdeadbeef);
1714 ret = ImmGetCandidateListCountA(imc_null,&count);
1715 ok(ret == 0, "NULL IME should return 0\n");
1716 ret = GetLastError();
1717 ok(ret == 0xdeadbeef, "last error should remain unchanged %08x!\n", ret);
1718 SetLastError(0xdeadbeef);
1719 ret = ImmGetCandidateListCountA(imc_destroy,&count);
1720 ok(ret == 0, "Destroyed IME should return 0\n");
1721 ret = GetLastError();
1722 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret);
1724 /* Test ImmGetCandidateWindow */
1725 SetLastError(0xdeadbeef);
1726 ret = ImmGetCandidateWindow(imc_bad, 0, (LPCANDIDATEFORM)buffer);
1727 ok(ret == 0, "Bad IME should return 0\n");
1728 ret = GetLastError();
1729 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret);
1730 SetLastError(0xdeadbeef);
1731 ret = ImmGetCandidateWindow(imc_null, 0, (LPCANDIDATEFORM)buffer);
1732 ok(ret == 0, "NULL IME should return 0\n");
1733 ret = GetLastError();
1734 ok(ret == 0xdeadbeef, "last error should remain unchanged %08x!\n", ret);
1735 SetLastError(0xdeadbeef);
1736 ret = ImmGetCandidateWindow(imc_destroy, 0, (LPCANDIDATEFORM)buffer);
1737 ok(ret == 0, "Destroyed IME should return 0\n");
1738 ret = GetLastError();
1739 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret);
1741 /* Test ImmGetCompositionFontA */
1742 SetLastError(0xdeadbeef);
1743 ret = ImmGetCompositionFontA(imc_bad, (LPLOGFONTA)buffer);
1744 ok(ret == 0, "Bad IME should return 0\n");
1745 ret = GetLastError();
1746 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret);
1747 SetLastError(0xdeadbeef);
1748 ret = ImmGetCompositionFontA(imc_null, (LPLOGFONTA)buffer);
1749 ok(ret == 0, "NULL IME should return 0\n");
1750 ret = GetLastError();
1751 ok(ret == 0xdeadbeef, "last error should remain unchanged %08x!\n", ret);
1752 SetLastError(0xdeadbeef);
1753 ret = ImmGetCompositionFontA(imc_destroy, (LPLOGFONTA)buffer);
1754 ok(ret == 0, "Destroyed IME should return 0\n");
1755 ret = GetLastError();
1756 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret);
1758 /* Test ImmGetCompositionWindow */
1759 SetLastError(0xdeadbeef);
1760 ret = ImmGetCompositionWindow(imc_bad, (LPCOMPOSITIONFORM)buffer);
1761 ok(ret == 0, "Bad IME should return 0\n");
1762 ret = GetLastError();
1763 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret);
1764 SetLastError(0xdeadbeef);
1765 ret = ImmGetCompositionWindow(imc_null, (LPCOMPOSITIONFORM)buffer);
1766 ok(ret == 0, "NULL IME should return 0\n");
1767 ret = GetLastError();
1768 ok(ret == 0xdeadbeef, "last error should remain unchanged %08x!\n", ret);
1769 SetLastError(0xdeadbeef);
1770 ret = ImmGetCompositionWindow(imc_destroy, (LPCOMPOSITIONFORM)buffer);
1771 ok(ret == 0, "Destroyed IME should return 0\n");
1772 ret = GetLastError();
1773 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret);
1775 /* Test ImmGetCompositionStringA */
1776 SetLastError(0xdeadbeef);
1777 ret = ImmGetCompositionStringA(imc_bad, GCS_COMPSTR, NULL, 0);
1778 ok(ret == 0, "Bad IME should return 0\n");
1779 ret = GetLastError();
1780 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret);
1781 SetLastError(0xdeadbeef);
1782 ret = ImmGetCompositionStringA(imc_null, GCS_COMPSTR, NULL, 0);
1783 ok(ret == 0, "NULL IME should return 0\n");
1784 ret = GetLastError();
1785 ok(ret == 0xdeadbeef, "last error should remain unchanged %08x!\n", ret);
1786 SetLastError(0xdeadbeef);
1787 ret = ImmGetCompositionStringA(imc_destroy, GCS_COMPSTR, NULL, 0);
1788 ok(ret == 0, "Destroyed IME should return 0\n");
1789 ret = GetLastError();
1790 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret);
1792 /* Test ImmSetOpenStatus */
1793 SetLastError(0xdeadbeef);
1794 ret = ImmSetOpenStatus(imc_bad, 1);
1795 ok(ret == 0, "Bad IME should return 0\n");
1796 ret = GetLastError();
1797 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret);
1798 SetLastError(0xdeadbeef);
1799 ret = ImmSetOpenStatus(imc_null, 1);
1800 ok(ret == 0, "NULL IME should return 0\n");
1801 ret = GetLastError();
1802 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret);
1803 SetLastError(0xdeadbeef);
1804 ret = ImmSetOpenStatus(imc_destroy, 1);
1805 ok(ret == 0, "Destroyed IME should return 0\n");
1806 ret = GetLastError();
1807 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret);
1809 /* Test ImmGetOpenStatus */
1810 SetLastError(0xdeadbeef);
1811 ret = ImmGetOpenStatus(imc_bad);
1812 ok(ret == 0, "Bad IME should return 0\n");
1813 ret = GetLastError();
1814 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret);
1815 SetLastError(0xdeadbeef);
1816 ret = ImmGetOpenStatus(imc_null);
1817 ok(ret == 0, "NULL IME should return 0\n");
1818 ret = GetLastError();
1819 ok(ret == 0xdeadbeef, "last error should remain unchanged %08x!\n", ret);
1820 SetLastError(0xdeadbeef);
1821 ret = ImmGetOpenStatus(imc_destroy);
1822 ok(ret == 0, "Destroyed IME should return 0\n");
1823 ret = GetLastError();
1824 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret);
1826 /* Test ImmGetStatusWindowPos */
1827 SetLastError(0xdeadbeef);
1828 ret = ImmGetStatusWindowPos(imc_bad, NULL);
1829 ok(ret == 0, "Bad IME should return 0\n");
1830 ret = GetLastError();
1831 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret);
1832 SetLastError(0xdeadbeef);
1833 ret = ImmGetStatusWindowPos(imc_null, NULL);
1834 ok(ret == 0, "NULL IME should return 0\n");
1835 ret = GetLastError();
1836 ok(ret == 0xdeadbeef, "last error should remain unchanged %08x!\n", ret);
1837 SetLastError(0xdeadbeef);
1838 ret = ImmGetStatusWindowPos(imc_destroy, NULL);
1839 ok(ret == 0, "Destroyed IME should return 0\n");
1840 ret = GetLastError();
1841 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret);
1843 /* Test ImmRequestMessageA */
1844 SetLastError(0xdeadbeef);
1845 ret = ImmRequestMessageA(imc_bad, WM_CHAR, 0);
1846 ok(ret == 0, "Bad IME should return 0\n");
1847 ret = GetLastError();
1848 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret);
1849 SetLastError(0xdeadbeef);
1850 ret = ImmRequestMessageA(imc_null, WM_CHAR, 0);
1851 ok(ret == 0, "NULL IME should return 0\n");
1852 ret = GetLastError();
1853 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret);
1854 SetLastError(0xdeadbeef);
1855 ret = ImmRequestMessageA(imc_destroy, WM_CHAR, 0);
1856 ok(ret == 0, "Destroyed IME should return 0\n");
1857 ret = GetLastError();
1858 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret);
1860 /* Test ImmSetCompositionFontA */
1861 SetLastError(0xdeadbeef);
1862 ret = ImmSetCompositionFontA(imc_bad, &lf);
1863 ok(ret == 0, "Bad IME should return 0\n");
1864 ret = GetLastError();
1865 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret);
1866 SetLastError(0xdeadbeef);
1867 ret = ImmSetCompositionFontA(imc_null, &lf);
1868 ok(ret == 0, "NULL IME should return 0\n");
1869 ret = GetLastError();
1870 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret);
1871 SetLastError(0xdeadbeef);
1872 ret = ImmSetCompositionFontA(imc_destroy, &lf);
1873 ok(ret == 0, "Destroyed IME should return 0\n");
1874 ret = GetLastError();
1875 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret);
1877 /* Test ImmSetCompositionWindow */
1878 SetLastError(0xdeadbeef);
1879 ret = ImmSetCompositionWindow(imc_bad, NULL);
1880 ok(ret == 0, "Bad IME should return 0\n");
1881 ret = GetLastError();
1882 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret);
1883 SetLastError(0xdeadbeef);
1884 ret = ImmSetCompositionWindow(imc_null, NULL);
1885 ok(ret == 0, "NULL IME should return 0\n");
1886 ret = GetLastError();
1887 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret);
1888 SetLastError(0xdeadbeef);
1889 ret = ImmSetCompositionWindow(imc_destroy, NULL);
1890 ok(ret == 0, "Destroyed IME should return 0\n");
1891 ret = GetLastError();
1892 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret);
1894 /* Test ImmSetConversionStatus */
1895 SetLastError(0xdeadbeef);
1896 ret = ImmSetConversionStatus(imc_bad, 0, 0);
1897 ok(ret == 0, "Bad IME should return 0\n");
1898 ret = GetLastError();
1899 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret);
1900 SetLastError(0xdeadbeef);
1901 ret = ImmSetConversionStatus(imc_null, 0, 0);
1902 ok(ret == 0, "NULL IME should return 0\n");
1903 ret = GetLastError();
1904 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret);
1905 SetLastError(0xdeadbeef);
1906 ret = ImmSetConversionStatus(imc_destroy, 0, 0);
1907 ok(ret == 0, "Destroyed IME should return 0\n");
1908 ret = GetLastError();
1909 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret);
1911 /* Test ImmSetStatusWindowPos */
1912 SetLastError(0xdeadbeef);
1913 ret = ImmSetStatusWindowPos(imc_bad, 0);
1914 ok(ret == 0, "Bad IME should return 0\n");
1915 ret = GetLastError();
1916 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret);
1917 SetLastError(0xdeadbeef);
1918 ret = ImmSetStatusWindowPos(imc_null, 0);
1919 ok(ret == 0, "NULL IME should return 0\n");
1920 ret = GetLastError();
1921 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret);
1922 SetLastError(0xdeadbeef);
1923 ret = ImmSetStatusWindowPos(imc_destroy, 0);
1924 ok(ret == 0, "Destroyed IME should return 0\n");
1925 ret = GetLastError();
1926 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret);
1928 /* Test ImmGetImeMenuItemsA */
1929 SetLastError(0xdeadbeef);
1930 ret = ImmGetImeMenuItemsA(imc_bad, 0, 0, NULL, NULL, 0);
1931 ok(ret == 0, "Bad IME should return 0\n");
1932 ret = GetLastError();
1933 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret);
1934 SetLastError(0xdeadbeef);
1935 ret = ImmGetImeMenuItemsA(imc_null, 0, 0, NULL, NULL, 0);
1936 ok(ret == 0, "NULL IME should return 0\n");
1937 ret = GetLastError();
1938 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret);
1939 SetLastError(0xdeadbeef);
1940 ret = ImmGetImeMenuItemsA(imc_destroy, 0, 0, NULL, NULL, 0);
1941 ok(ret == 0, "Destroyed IME should return 0\n");
1942 ret = GetLastError();
1943 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret);
1945 /* Test ImmLockIMC */
1946 SetLastError(0xdeadbeef);
1947 ic = ImmLockIMC(imc_bad);
1948 ok(ic == 0, "Bad IME should return 0\n");
1949 ret = GetLastError();
1950 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret);
1951 SetLastError(0xdeadbeef);
1952 ic = ImmLockIMC(imc_null);
1953 ok(ic == 0, "NULL IME should return 0\n");
1954 ret = GetLastError();
1955 ok(ret == 0xdeadbeef, "last error should remain unchanged %08x!\n", ret);
1956 SetLastError(0xdeadbeef);
1957 ic = ImmLockIMC(imc_destroy);
1958 ok(ic == 0, "Destroyed IME should return 0\n");
1959 ret = GetLastError();
1960 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret);
1962 /* Test ImmUnlockIMC */
1963 SetLastError(0xdeadbeef);
1964 ret = ImmUnlockIMC(imc_bad);
1965 ok(ret == 0, "Bad IME should return 0\n");
1966 ret = GetLastError();
1967 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret);
1968 SetLastError(0xdeadbeef);
1969 ret = ImmUnlockIMC(imc_null);
1970 ok(ret == 0, "NULL IME should return 0\n");
1971 ret = GetLastError();
1972 ok(ret == 0xdeadbeef, "last error should remain unchanged %08x!\n", ret);
1973 SetLastError(0xdeadbeef);
1974 ret = ImmUnlockIMC(imc_destroy);
1975 ok(ret == 0, "Destroyed IME should return 0\n");
1976 ret = GetLastError();
1977 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret);
1979 /* Test ImmGenerateMessage */
1980 SetLastError(0xdeadbeef);
1981 ret = ImmGenerateMessage(imc_bad);
1982 ok(ret == 0, "Bad IME should return 0\n");
1983 ret = GetLastError();
1984 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret);
1985 SetLastError(0xdeadbeef);
1986 ret = ImmGenerateMessage(imc_null);
1987 ok(ret == 0, "NULL IME should return 0\n");
1988 ret = GetLastError();
1989 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret);
1990 SetLastError(0xdeadbeef);
1991 ret = ImmGenerateMessage(imc_destroy);
1992 ok(ret == 0, "Destroyed IME should return 0\n");
1993 ret = GetLastError();
1994 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret);
1997 START_TEST(imm32) {
1998 if (init())
2000 test_ImmNotifyIME();
2001 test_ImmGetCompositionString();
2002 test_ImmSetCompositionString();
2003 test_ImmIME();
2004 test_ImmAssociateContextEx();
2005 test_ImmThreads();
2006 test_ImmIsUIMessage();
2007 test_ImmGetContext();
2008 test_ImmGetDescription();
2009 test_ImmDefaultHwnd();
2010 test_default_ime_window_creation();
2011 test_ImmGetIMCLockCount();
2012 test_ImmGetIMCCLockCount();
2013 test_ImmDestroyContext();
2014 test_ImmDestroyIMCC();
2015 test_InvalidIMC();
2016 msg_spy_cleanup();
2017 /* Reinitialize the hooks to capture all windows */
2018 msg_spy_init(NULL);
2019 test_ImmMessages();
2020 msg_spy_cleanup();
2021 if (pSendInput)
2022 test_ime_processkey();
2023 else win_skip("SendInput is not available\n");
2025 cleanup();