imm32: Send WM_IME_SELECT messages when IME is activated.
[wine.git] / dlls / imm32 / tests / imm32.c
blob713d72a2f816ba33ae17269fceec393f81ff3cd4
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 <stdarg.h>
22 #include <stddef.h>
24 #include "ntstatus.h"
25 #define WIN32_NO_STATUS
26 #include "windef.h"
27 #include "winbase.h"
29 #include "wine/test.h"
30 #include "objbase.h"
31 #include "winuser.h"
32 #include "wingdi.h"
33 #include "imm.h"
34 #include "immdev.h"
36 #include "ime_test.h"
38 static const char *debugstr_ok( const char *cond )
40 int c, n = 0;
41 /* skip possible casts */
42 while ((c = *cond++))
44 if (c == '(') n++;
45 if (!n) break;
46 if (c == ')') n--;
48 if (!strchr( cond - 1, '(' )) return wine_dbg_sprintf( "got %s", cond - 1 );
49 return wine_dbg_sprintf( "%.*s returned", (int)strcspn( cond - 1, "( " ), cond - 1 );
52 #define ok_eq( e, r, t, f, ... ) \
53 do \
54 { \
55 t v = (r); \
56 ok( v == (e), "%s " f "\n", debugstr_ok( #r ), v, ##__VA_ARGS__ ); \
57 } while (0)
58 #define ok_ne( e, r, t, f, ... ) \
59 do \
60 { \
61 t v = (r); \
62 ok( v != (e), "%s " f "\n", debugstr_ok( #r ), v, ##__VA_ARGS__ ); \
63 } while (0)
64 #define ok_wcs( e, r ) \
65 do \
66 { \
67 const WCHAR *v = (r); \
68 ok( !wcscmp( v, (e) ), "%s %s\n", debugstr_ok(#r), debugstr_w(v) ); \
69 } while (0)
70 #define ok_str( e, r ) \
71 do \
72 { \
73 const char *v = (r); \
74 ok( !strcmp( v, (e) ), "%s %s\n", debugstr_ok(#r), debugstr_a(v) ); \
75 } while (0)
76 #define ok_ret( e, r ) ok_eq( e, r, UINT_PTR, "%Iu, error %ld", GetLastError() )
78 BOOL WINAPI ImmSetActiveContext(HWND, HIMC, BOOL);
80 static BOOL (WINAPI *pImmAssociateContextEx)(HWND,HIMC,DWORD);
81 static UINT (WINAPI *pNtUserAssociateInputContext)(HWND,HIMC,ULONG);
82 static BOOL (WINAPI *pImmIsUIMessageA)(HWND,UINT,WPARAM,LPARAM);
83 static UINT (WINAPI *pSendInput) (UINT, INPUT*, size_t);
85 extern BOOL WINAPI ImmFreeLayout(HKL);
86 extern BOOL WINAPI ImmLoadIME(HKL);
87 extern BOOL WINAPI ImmActivateLayout(HKL);
89 #define DEFINE_EXPECT(func) \
90 static BOOL expect_ ## func = FALSE, called_ ## func = FALSE, enabled_ ## func = FALSE
92 #define SET_EXPECT(func) \
93 expect_ ## func = TRUE
95 #define CHECK_EXPECT2(func) \
96 do { \
97 if (enabled_ ## func) {\
98 ok(expect_ ##func, "unexpected call " #func "\n"); \
99 called_ ## func = TRUE; \
101 }while(0)
103 #define CHECK_EXPECT(func) \
104 do { \
105 CHECK_EXPECT2(func); \
106 expect_ ## func = FALSE; \
107 }while(0)
109 #define CHECK_CALLED(func) \
110 do { \
111 ok(called_ ## func, "expected " #func "\n"); \
112 expect_ ## func = called_ ## func = FALSE; \
113 }while(0)
115 #define SET_ENABLE(func, val) \
116 enabled_ ## func = (val)
118 DEFINE_EXPECT(WM_IME_SETCONTEXT_DEACTIVATE);
119 DEFINE_EXPECT(WM_IME_SETCONTEXT_ACTIVATE);
121 static void process_messages(void)
123 MSG msg;
125 while (PeekMessageA( &msg, 0, 0, 0, PM_REMOVE ))
127 TranslateMessage( &msg );
128 DispatchMessageA( &msg );
133 * msgspy - record and analyse message traces sent to a certain window
135 typedef struct _msgs {
136 CWPSTRUCT msg;
137 BOOL post;
138 } imm_msgs;
140 static struct _msg_spy {
141 HWND hwnd;
142 HHOOK get_msg_hook;
143 HHOOK call_wnd_proc_hook;
144 imm_msgs msgs[64];
145 unsigned int i_msg;
146 } msg_spy;
148 typedef struct
150 DWORD type;
151 union
153 MOUSEINPUT mi;
154 KEYBDINPUT ki;
155 HARDWAREINPUT hi;
156 } u;
157 } TEST_INPUT;
159 static UINT (WINAPI *pSendInput) (UINT, INPUT*, size_t);
161 static LRESULT CALLBACK get_msg_filter(int nCode, WPARAM wParam, LPARAM lParam)
163 if (HC_ACTION == nCode) {
164 MSG *msg = (MSG*)lParam;
166 if ((msg->hwnd == msg_spy.hwnd || msg_spy.hwnd == NULL) &&
167 (msg_spy.i_msg < ARRAY_SIZE(msg_spy.msgs)))
169 msg_spy.msgs[msg_spy.i_msg].msg.hwnd = msg->hwnd;
170 msg_spy.msgs[msg_spy.i_msg].msg.message = msg->message;
171 msg_spy.msgs[msg_spy.i_msg].msg.wParam = msg->wParam;
172 msg_spy.msgs[msg_spy.i_msg].msg.lParam = msg->lParam;
173 msg_spy.msgs[msg_spy.i_msg].post = TRUE;
174 msg_spy.i_msg++;
178 return CallNextHookEx(msg_spy.get_msg_hook, nCode, wParam, lParam);
181 static LRESULT CALLBACK call_wnd_proc_filter(int nCode, WPARAM wParam,
182 LPARAM lParam)
184 if (HC_ACTION == nCode) {
185 CWPSTRUCT *cwp = (CWPSTRUCT*)lParam;
187 if (((cwp->hwnd == msg_spy.hwnd || msg_spy.hwnd == NULL)) &&
188 (msg_spy.i_msg < ARRAY_SIZE(msg_spy.msgs)))
190 memcpy(&msg_spy.msgs[msg_spy.i_msg].msg, cwp, sizeof(msg_spy.msgs[0].msg));
191 msg_spy.msgs[msg_spy.i_msg].post = FALSE;
192 msg_spy.i_msg++;
196 return CallNextHookEx(msg_spy.call_wnd_proc_hook, nCode, wParam, lParam);
199 static void msg_spy_pump_msg_queue(void) {
200 MSG msg;
202 while(PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) {
203 TranslateMessage(&msg);
204 DispatchMessageW(&msg);
207 return;
210 static void msg_spy_flush_msgs(void) {
211 msg_spy_pump_msg_queue();
212 msg_spy.i_msg = 0;
215 static imm_msgs* msg_spy_find_next_msg(UINT message, UINT *start) {
216 UINT i;
218 msg_spy_pump_msg_queue();
220 if (msg_spy.i_msg >= ARRAY_SIZE(msg_spy.msgs))
221 fprintf(stdout, "%s:%d: msg_spy: message buffer overflow!\n",
222 __FILE__, __LINE__);
224 for (i = *start; i < msg_spy.i_msg; i++)
225 if (msg_spy.msgs[i].msg.message == message)
227 *start = i+1;
228 return &msg_spy.msgs[i];
231 return NULL;
234 static imm_msgs* msg_spy_find_msg(UINT message) {
235 UINT i = 0;
237 return msg_spy_find_next_msg(message, &i);
240 static void msg_spy_init(HWND hwnd) {
241 msg_spy.hwnd = hwnd;
242 msg_spy.get_msg_hook =
243 SetWindowsHookExW(WH_GETMESSAGE, get_msg_filter, GetModuleHandleW(NULL),
244 GetCurrentThreadId());
245 msg_spy.call_wnd_proc_hook =
246 SetWindowsHookExW(WH_CALLWNDPROC, call_wnd_proc_filter,
247 GetModuleHandleW(NULL), GetCurrentThreadId());
248 msg_spy.i_msg = 0;
250 msg_spy_flush_msgs();
253 static void msg_spy_cleanup(void) {
254 if (msg_spy.get_msg_hook)
255 UnhookWindowsHookEx(msg_spy.get_msg_hook);
256 if (msg_spy.call_wnd_proc_hook)
257 UnhookWindowsHookEx(msg_spy.call_wnd_proc_hook);
258 memset(&msg_spy, 0, sizeof(msg_spy));
262 * imm32 test cases - Issue some IMM commands on a dummy window and analyse the
263 * messages being sent to this window in response.
265 static const char wndcls[] = "winetest_imm32_wndcls";
266 static enum { PHASE_UNKNOWN, FIRST_WINDOW, SECOND_WINDOW,
267 CREATE_CANCEL, NCCREATE_CANCEL, IME_DISABLED } test_phase;
268 static HWND hwnd, child;
270 static HWND get_ime_window(void);
272 static void load_resource( const WCHAR *name, WCHAR *filename )
274 static WCHAR path[MAX_PATH];
275 DWORD written;
276 HANDLE file;
277 HRSRC res;
278 void *ptr;
280 GetTempPathW( ARRAY_SIZE(path), path );
281 GetTempFileNameW( path, name, 0, filename );
283 file = CreateFileW( filename, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0 );
284 ok( file != INVALID_HANDLE_VALUE, "failed to create %s, error %lu\n", debugstr_w(filename), GetLastError() );
286 res = FindResourceW( NULL, name, L"TESTDLL" );
287 ok( res != 0, "couldn't find resource\n" );
288 ptr = LockResource( LoadResource( GetModuleHandleW( NULL ), res ) );
289 WriteFile( file, ptr, SizeofResource( GetModuleHandleW( NULL ), res ), &written, NULL );
290 ok( written == SizeofResource( GetModuleHandleW( NULL ), res ), "couldn't write resource\n" );
291 CloseHandle( file );
294 static LRESULT WINAPI wndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
296 HWND default_ime_wnd;
297 switch (msg)
299 case WM_IME_SETCONTEXT:
300 if (wParam) CHECK_EXPECT(WM_IME_SETCONTEXT_ACTIVATE);
301 else CHECK_EXPECT(WM_IME_SETCONTEXT_DEACTIVATE);
302 ok(lParam == ISC_SHOWUIALL || !lParam, "lParam = %Ix\n", lParam);
303 return TRUE;
304 case WM_NCCREATE:
305 default_ime_wnd = get_ime_window();
306 switch(test_phase) {
307 case FIRST_WINDOW:
308 case IME_DISABLED:
309 ok(!default_ime_wnd, "expected no IME windows\n");
310 break;
311 case SECOND_WINDOW:
312 ok(default_ime_wnd != NULL, "expected IME window existence\n");
313 break;
314 default:
315 break; /* do nothing */
317 if (test_phase == NCCREATE_CANCEL)
318 return FALSE;
319 return TRUE;
320 case WM_NCCALCSIZE:
321 default_ime_wnd = get_ime_window();
322 switch(test_phase) {
323 case FIRST_WINDOW:
324 case SECOND_WINDOW:
325 case CREATE_CANCEL:
326 ok(default_ime_wnd != NULL, "expected IME window existence\n");
327 break;
328 case IME_DISABLED:
329 ok(!default_ime_wnd, "expected no IME windows\n");
330 break;
331 default:
332 break; /* do nothing */
334 break;
335 case WM_CREATE:
336 default_ime_wnd = get_ime_window();
337 switch(test_phase) {
338 case FIRST_WINDOW:
339 case SECOND_WINDOW:
340 case CREATE_CANCEL:
341 ok(default_ime_wnd != NULL, "expected IME window existence\n");
342 break;
343 case IME_DISABLED:
344 ok(!default_ime_wnd, "expected no IME windows\n");
345 break;
346 default:
347 break; /* do nothing */
349 if (test_phase == CREATE_CANCEL)
350 return -1;
351 return TRUE;
354 return DefWindowProcA(hWnd,msg,wParam,lParam);
357 static BOOL is_ime_enabled(void)
359 HIMC himc;
360 HWND wnd;
362 wnd = CreateWindowA("static", "static", 0, 0, 0, 0, 0, 0, 0, 0, 0);
363 ok(wnd != NULL, "CreateWindow failed\n");
365 himc = ImmGetContext(wnd);
366 if (!himc)
368 DestroyWindow(wnd);
369 return FALSE;
372 ImmReleaseContext(wnd, himc);
373 DestroyWindow(wnd);
374 return TRUE;
377 static BOOL init(void) {
378 WNDCLASSEXA wc;
379 HMODULE hmod,huser;
381 hmod = GetModuleHandleA("imm32.dll");
382 huser = GetModuleHandleA("user32");
383 pImmAssociateContextEx = (void*)GetProcAddress(hmod, "ImmAssociateContextEx");
384 pImmIsUIMessageA = (void*)GetProcAddress(hmod, "ImmIsUIMessageA");
385 pSendInput = (void*)GetProcAddress(huser, "SendInput");
386 pNtUserAssociateInputContext = (void*)GetProcAddress(GetModuleHandleW(L"win32u.dll"),
387 "NtUserAssociateInputContext");
389 wc.cbSize = sizeof(WNDCLASSEXA);
390 wc.style = 0;
391 wc.lpfnWndProc = wndProc;
392 wc.cbClsExtra = 0;
393 wc.cbWndExtra = 0;
394 wc.hInstance = GetModuleHandleA(NULL);
395 wc.hIcon = LoadIconA(NULL, (LPCSTR)IDI_APPLICATION);
396 wc.hCursor = LoadCursorA(NULL, (LPCSTR)IDC_ARROW);
397 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
398 wc.lpszMenuName = NULL;
399 wc.lpszClassName = wndcls;
400 wc.hIconSm = LoadIconA(NULL, (LPCSTR)IDI_APPLICATION);
402 if (!RegisterClassExA(&wc))
403 return FALSE;
405 hwnd = CreateWindowExA(WS_EX_CLIENTEDGE, wndcls, "Wine imm32.dll test",
406 WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
407 240, 120, NULL, NULL, GetModuleHandleW(NULL), NULL);
408 if (!hwnd)
409 return FALSE;
411 child = CreateWindowA("edit", "edit", WS_CHILD | WS_VISIBLE, 0, 0, 50, 50, hwnd, 0, 0, 0);
412 if (!child)
413 return FALSE;
415 ShowWindow(hwnd, SW_SHOWNORMAL);
416 UpdateWindow(hwnd);
418 msg_spy_init(hwnd);
420 return TRUE;
423 static void cleanup(void) {
424 msg_spy_cleanup();
425 if (hwnd)
426 DestroyWindow(hwnd);
427 UnregisterClassA(wndcls, GetModuleHandleW(NULL));
430 static void test_ImmNotifyIME(void) {
431 static const char string[] = "wine";
432 char resstr[16] = "";
433 HIMC imc;
434 BOOL ret;
436 imc = ImmGetContext(hwnd);
437 msg_spy_flush_msgs();
439 ret = ImmNotifyIME(imc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
440 ok(broken(!ret) ||
441 ret, /* Vista+ */
442 "Canceling an empty composition string should succeed.\n");
443 ok(!msg_spy_find_msg(WM_IME_COMPOSITION), "Windows does not post "
444 "WM_IME_COMPOSITION in response to NI_COMPOSITIONSTR / CPS_CANCEL, if "
445 "the composition string being canceled is empty.\n");
447 ImmSetCompositionStringA(imc, SCS_SETSTR, string, sizeof(string), NULL, 0);
448 msg_spy_flush_msgs();
450 ImmNotifyIME(imc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
451 msg_spy_flush_msgs();
453 /* behavior differs between win9x and NT */
454 ret = ImmGetCompositionStringA(imc, GCS_COMPSTR, resstr, sizeof(resstr));
455 ok(!ret, "After being cancelled the composition string is empty.\n");
457 msg_spy_flush_msgs();
459 ret = ImmNotifyIME(imc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
460 ok(broken(!ret) ||
461 ret, /* Vista+ */
462 "Canceling an empty composition string should succeed.\n");
463 ok(!msg_spy_find_msg(WM_IME_COMPOSITION), "Windows does not post "
464 "WM_IME_COMPOSITION in response to NI_COMPOSITIONSTR / CPS_CANCEL, if "
465 "the composition string being canceled is empty.\n");
467 msg_spy_flush_msgs();
468 ImmReleaseContext(hwnd, imc);
470 imc = ImmCreateContext();
471 ImmDestroyContext(imc);
473 SetLastError(0xdeadbeef);
474 ret = ImmNotifyIME((HIMC)0xdeadcafe, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
475 ok (ret == 0, "Bad IME should return 0\n");
476 ret = GetLastError();
477 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret);
478 SetLastError(0xdeadbeef);
479 ret = ImmNotifyIME(0x00000000, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
480 ok (ret == 0, "NULL IME should return 0\n");
481 ret = GetLastError();
482 ok(ret == ERROR_SUCCESS, "wrong last error %08x!\n", ret);
483 SetLastError(0xdeadbeef);
484 ret = ImmNotifyIME(imc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
485 ok (ret == 0, "Destroyed IME should return 0\n");
486 ret = GetLastError();
487 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret);
491 static struct {
492 WNDPROC old_wnd_proc;
493 BOOL catch_result_str;
494 BOOL catch_ime_char;
495 DWORD start;
496 DWORD timer_id;
497 } ime_composition_test;
499 static LRESULT WINAPI test_ime_wnd_proc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
501 switch (msg)
503 case WM_IME_COMPOSITION:
504 if ((lParam & GCS_RESULTSTR) && !ime_composition_test.catch_result_str) {
505 HWND hwndIme;
506 WCHAR wstring[20];
507 HIMC imc;
508 LONG size;
509 LRESULT ret;
511 hwndIme = ImmGetDefaultIMEWnd(hWnd);
512 ok(hwndIme != NULL, "expected IME window existence\n");
514 ok(!ime_composition_test.catch_ime_char, "WM_IME_CHAR is sent\n");
515 ret = CallWindowProcA(ime_composition_test.old_wnd_proc,
516 hWnd, msg, wParam, lParam);
517 ok(ime_composition_test.catch_ime_char, "WM_IME_CHAR isn't sent\n");
519 ime_composition_test.catch_ime_char = FALSE;
520 SendMessageA(hwndIme, msg, wParam, lParam);
521 ok(!ime_composition_test.catch_ime_char, "WM_IME_CHAR is sent\n");
523 imc = ImmGetContext(hWnd);
524 size = ImmGetCompositionStringW(imc, GCS_RESULTSTR,
525 wstring, sizeof(wstring));
526 ok(size > 0, "ImmGetCompositionString(GCS_RESULTSTR) is %ld\n", size);
527 ImmReleaseContext(hwnd, imc);
529 ime_composition_test.catch_result_str = TRUE;
530 return ret;
532 break;
533 case WM_IME_CHAR:
534 if (!ime_composition_test.catch_result_str)
535 ime_composition_test.catch_ime_char = TRUE;
536 break;
537 case WM_TIMER:
538 if (wParam == ime_composition_test.timer_id) {
539 HWND parent = GetParent(hWnd);
540 char title[64];
541 int left = 20 - (GetTickCount() - ime_composition_test.start) / 1000;
542 wsprintfA(title, "%sLeft %d sec. - IME composition test",
543 ime_composition_test.catch_result_str ? "[*] " : "", left);
544 SetWindowTextA(parent, title);
545 if (left <= 0)
546 DestroyWindow(parent);
547 else
548 SetTimer(hWnd, wParam, 100, NULL);
549 return TRUE;
551 break;
553 return CallWindowProcA(ime_composition_test.old_wnd_proc,
554 hWnd, msg, wParam, lParam);
557 static void test_ImmGetCompositionString(void)
559 HIMC imc;
560 static const WCHAR string[] = {'w','i','n','e',0x65e5,0x672c,0x8a9e};
561 char cstring[20];
562 WCHAR wstring[20];
563 LONG len;
564 LONG alen,wlen;
565 BOOL ret;
566 DWORD prop;
568 imc = ImmGetContext(hwnd);
569 ret = ImmSetCompositionStringW(imc, SCS_SETSTR, string, sizeof(string), NULL,0);
570 if (!ret) {
571 win_skip("Composition isn't supported\n");
572 ImmReleaseContext(hwnd, imc);
573 return;
575 msg_spy_flush_msgs();
577 alen = ImmGetCompositionStringA(imc, GCS_COMPSTR, cstring, 20);
578 wlen = ImmGetCompositionStringW(imc, GCS_COMPSTR, wstring, 20);
579 /* windows machines without any IME installed just return 0 above */
580 if( alen && wlen)
582 len = ImmGetCompositionStringW(imc, GCS_COMPATTR, NULL, 0);
583 ok(len*sizeof(WCHAR)==wlen,"GCS_COMPATTR(W) not returning correct count\n");
584 len = ImmGetCompositionStringA(imc, GCS_COMPATTR, NULL, 0);
585 ok(len==alen,"GCS_COMPATTR(A) not returning correct count\n");
587 /* Get strings with exactly matching buffer sizes. */
588 memset(wstring, 0x1a, sizeof(wstring));
589 memset(cstring, 0x1a, sizeof(cstring));
591 len = ImmGetCompositionStringA(imc, GCS_COMPSTR, cstring, alen);
592 ok(len == alen, "Unexpected length %ld.\n", len);
593 ok(cstring[alen] == 0x1a, "Unexpected buffer contents.\n");
595 len = ImmGetCompositionStringW(imc, GCS_COMPSTR, wstring, wlen);
596 ok(len == wlen, "Unexpected length %ld.\n", len);
597 ok(wstring[wlen/sizeof(WCHAR)] == 0x1a1a, "Unexpected buffer contents.\n");
599 /* Get strings with exactly smaller buffer sizes. */
600 memset(wstring, 0x1a, sizeof(wstring));
601 memset(cstring, 0x1a, sizeof(cstring));
603 /* Returns 0 but still fills buffer. */
604 len = ImmGetCompositionStringA(imc, GCS_COMPSTR, cstring, alen - 1);
605 ok(!len, "Unexpected length %ld.\n", len);
606 ok(cstring[0] == 'w', "Unexpected buffer contents %s.\n", cstring);
608 len = ImmGetCompositionStringW(imc, GCS_COMPSTR, wstring, wlen - 1);
609 ok(len == wlen - 1, "Unexpected length %ld.\n", len);
610 ok(!memcmp(wstring, string, wlen - 1), "Unexpected buffer contents.\n");
612 /* Get the size of the required output buffer. */
613 memset(wstring, 0x1a, sizeof(wstring));
614 memset(cstring, 0x1a, sizeof(cstring));
616 len = ImmGetCompositionStringA(imc, GCS_COMPSTR, cstring, 0);
617 ok(len == alen, "Unexpected length %ld.\n", len);
618 ok(cstring[0] == 0x1a, "Unexpected buffer contents %s.\n", cstring);
620 len = ImmGetCompositionStringW(imc, GCS_COMPSTR, wstring, 0);
621 ok(len == wlen, "Unexpected length %ld.\n", len);
622 ok(wstring[0] == 0x1a1a, "Unexpected buffer contents.\n");
624 else
625 win_skip("Composition string isn't available\n");
627 ImmReleaseContext(hwnd, imc);
629 /* Test composition results input by IMM API */
630 prop = ImmGetProperty(GetKeyboardLayout(0), IGP_SETCOMPSTR);
631 if (!(prop & SCS_CAP_COMPSTR)) {
632 /* Wine's IME doesn't support SCS_SETSTR in ImmSetCompositionString */
633 skip("This IME doesn't support SCS_SETSTR\n");
635 else {
636 ime_composition_test.old_wnd_proc =
637 (WNDPROC)SetWindowLongPtrA(hwnd, GWLP_WNDPROC,
638 (LONG_PTR)test_ime_wnd_proc);
639 imc = ImmGetContext(hwnd);
640 msg_spy_flush_msgs();
642 ret = ImmSetCompositionStringW(imc, SCS_SETSTR,
643 string, sizeof(string), NULL,0);
644 ok(ret, "ImmSetCompositionStringW failed\n");
645 wlen = ImmGetCompositionStringW(imc, GCS_COMPSTR,
646 wstring, sizeof(wstring));
647 if (wlen > 0) {
648 ret = ImmNotifyIME(imc, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
649 ok(ret, "ImmNotifyIME(CPS_COMPLETE) failed\n");
650 msg_spy_flush_msgs();
651 ok(ime_composition_test.catch_result_str,
652 "WM_IME_COMPOSITION(GCS_RESULTSTR) isn't sent\n");
654 else
655 win_skip("Composition string isn't available\n");
656 ImmReleaseContext(hwnd, imc);
657 SetWindowLongPtrA(hwnd, GWLP_WNDPROC,
658 (LONG_PTR)ime_composition_test.old_wnd_proc);
659 msg_spy_flush_msgs();
662 /* Test composition results input by hand */
663 memset(&ime_composition_test, 0, sizeof(ime_composition_test));
664 if (winetest_interactive) {
665 HWND hwndMain, hwndChild;
666 MSG msg;
667 const DWORD MY_TIMER = 0xcaffe;
669 hwndMain = CreateWindowExA(WS_EX_CLIENTEDGE, wndcls,
670 "IME composition test",
671 WS_OVERLAPPEDWINDOW | WS_VISIBLE,
672 CW_USEDEFAULT, CW_USEDEFAULT, 320, 160,
673 NULL, NULL, GetModuleHandleA(NULL), NULL);
674 hwndChild = CreateWindowExA(0, "static",
675 "Input a DBCS character here using IME.",
676 WS_CHILD | WS_VISIBLE,
677 0, 0, 320, 100, hwndMain, NULL,
678 GetModuleHandleA(NULL), NULL);
680 ime_composition_test.old_wnd_proc =
681 (WNDPROC)SetWindowLongPtrA(hwndChild, GWLP_WNDPROC,
682 (LONG_PTR)test_ime_wnd_proc);
684 SetFocus(hwndChild);
686 ime_composition_test.timer_id = MY_TIMER;
687 ime_composition_test.start = GetTickCount();
688 SetTimer(hwndChild, ime_composition_test.timer_id, 100, NULL);
689 while (GetMessageA(&msg, NULL, 0, 0)) {
690 TranslateMessage(&msg);
691 DispatchMessageA(&msg);
692 if (!IsWindow(hwndMain))
693 break;
695 if (!ime_composition_test.catch_result_str)
696 skip("WM_IME_COMPOSITION(GCS_RESULTSTR) isn't tested\n");
697 msg_spy_flush_msgs();
701 static void test_ImmSetCompositionString(void)
703 HIMC imc;
704 BOOL ret;
706 SetLastError(0xdeadbeef);
707 imc = ImmGetContext(hwnd);
708 ok(imc != 0, "ImmGetContext() failed. Last error: %lu\n", GetLastError());
709 if (!imc)
710 return;
712 ret = ImmSetCompositionStringW(imc, SCS_SETSTR, NULL, 0, NULL, 0);
713 ok(broken(!ret) ||
714 ret, /* Vista+ */
715 "ImmSetCompositionStringW() failed.\n");
717 ret = ImmSetCompositionStringW(imc, SCS_SETSTR | SCS_CHANGEATTR,
718 NULL, 0, NULL, 0);
719 ok(!ret, "ImmSetCompositionStringW() succeeded.\n");
721 ret = ImmSetCompositionStringW(imc, SCS_SETSTR | SCS_CHANGECLAUSE,
722 NULL, 0, NULL, 0);
723 ok(!ret, "ImmSetCompositionStringW() succeeded.\n");
725 ret = ImmSetCompositionStringW(imc, SCS_CHANGEATTR | SCS_CHANGECLAUSE,
726 NULL, 0, NULL, 0);
727 ok(!ret, "ImmSetCompositionStringW() succeeded.\n");
729 ret = ImmSetCompositionStringW(imc, SCS_SETSTR | SCS_CHANGEATTR | SCS_CHANGECLAUSE,
730 NULL, 0, NULL, 0);
731 ok(!ret, "ImmSetCompositionStringW() succeeded.\n");
733 ImmReleaseContext(hwnd, imc);
736 static void test_ImmIME(void)
738 HIMC imc;
740 imc = ImmGetContext(hwnd);
741 if (imc)
743 BOOL rc;
744 rc = ImmConfigureIMEA(imc, NULL, IME_CONFIG_REGISTERWORD, NULL);
745 ok (rc == 0, "ImmConfigureIMEA did not fail\n");
746 rc = ImmConfigureIMEW(imc, NULL, IME_CONFIG_REGISTERWORD, NULL);
747 ok (rc == 0, "ImmConfigureIMEW did not fail\n");
749 ImmReleaseContext(hwnd,imc);
752 static void test_ImmAssociateContextEx(void)
754 HIMC imc;
755 BOOL rc;
757 if (!pImmAssociateContextEx) return;
759 imc = ImmGetContext(hwnd);
760 if (imc)
762 HIMC retimc, newimc;
763 HWND focus;
765 SET_ENABLE(WM_IME_SETCONTEXT_ACTIVATE, TRUE);
766 SET_ENABLE(WM_IME_SETCONTEXT_DEACTIVATE, TRUE);
768 ok(GetActiveWindow() == hwnd, "hwnd is not active\n");
769 newimc = ImmCreateContext();
770 ok(newimc != imc, "handles should not be the same\n");
771 rc = pImmAssociateContextEx(NULL, NULL, 0);
772 ok(!rc, "ImmAssociateContextEx succeeded\n");
773 SET_EXPECT(WM_IME_SETCONTEXT_DEACTIVATE);
774 SET_EXPECT(WM_IME_SETCONTEXT_ACTIVATE);
775 rc = pImmAssociateContextEx(hwnd, NULL, 0);
776 CHECK_CALLED(WM_IME_SETCONTEXT_DEACTIVATE);
777 CHECK_CALLED(WM_IME_SETCONTEXT_ACTIVATE);
778 ok(rc, "ImmAssociateContextEx failed\n");
779 rc = pImmAssociateContextEx(NULL, imc, 0);
780 ok(!rc, "ImmAssociateContextEx succeeded\n");
782 SET_EXPECT(WM_IME_SETCONTEXT_DEACTIVATE);
783 SET_EXPECT(WM_IME_SETCONTEXT_ACTIVATE);
784 rc = pImmAssociateContextEx(hwnd, imc, 0);
785 CHECK_CALLED(WM_IME_SETCONTEXT_DEACTIVATE);
786 CHECK_CALLED(WM_IME_SETCONTEXT_ACTIVATE);
787 ok(rc, "ImmAssociateContextEx failed\n");
788 retimc = ImmGetContext(hwnd);
789 ok(retimc == imc, "handles should be the same\n");
790 ImmReleaseContext(hwnd,retimc);
792 rc = pImmAssociateContextEx(hwnd, imc, 0);
793 ok(rc, "ImmAssociateContextEx failed\n");
795 SET_EXPECT(WM_IME_SETCONTEXT_DEACTIVATE);
796 SET_EXPECT(WM_IME_SETCONTEXT_ACTIVATE);
797 rc = pImmAssociateContextEx(hwnd, newimc, 0);
798 CHECK_CALLED(WM_IME_SETCONTEXT_DEACTIVATE);
799 CHECK_CALLED(WM_IME_SETCONTEXT_ACTIVATE);
800 ok(rc, "ImmAssociateContextEx failed\n");
801 retimc = ImmGetContext(hwnd);
802 ok(retimc == newimc, "handles should be the same\n");
803 ImmReleaseContext(hwnd,retimc);
805 focus = CreateWindowA("button", "button", 0, 0, 0, 0, 0, 0, 0, 0, 0);
806 ok(focus != NULL, "CreateWindow failed\n");
807 SET_EXPECT(WM_IME_SETCONTEXT_DEACTIVATE);
808 SetFocus(focus);
809 CHECK_CALLED(WM_IME_SETCONTEXT_DEACTIVATE);
810 rc = pImmAssociateContextEx(hwnd, imc, 0);
811 ok(rc, "ImmAssociateContextEx failed\n");
812 SET_EXPECT(WM_IME_SETCONTEXT_ACTIVATE);
813 DestroyWindow(focus);
814 CHECK_CALLED(WM_IME_SETCONTEXT_ACTIVATE);
816 SET_EXPECT(WM_IME_SETCONTEXT_DEACTIVATE);
817 SetFocus(child);
818 CHECK_CALLED(WM_IME_SETCONTEXT_DEACTIVATE);
819 rc = pImmAssociateContextEx(hwnd, newimc, 0);
820 ok(rc, "ImmAssociateContextEx failed\n");
821 SET_EXPECT(WM_IME_SETCONTEXT_ACTIVATE);
822 SetFocus(hwnd);
823 CHECK_CALLED(WM_IME_SETCONTEXT_ACTIVATE);
825 SET_EXPECT(WM_IME_SETCONTEXT_DEACTIVATE);
826 SET_EXPECT(WM_IME_SETCONTEXT_ACTIVATE);
827 rc = pImmAssociateContextEx(hwnd, NULL, IACE_DEFAULT);
828 CHECK_CALLED(WM_IME_SETCONTEXT_DEACTIVATE);
829 CHECK_CALLED(WM_IME_SETCONTEXT_ACTIVATE);
830 ok(rc, "ImmAssociateContextEx failed\n");
832 SET_ENABLE(WM_IME_SETCONTEXT_ACTIVATE, FALSE);
833 SET_ENABLE(WM_IME_SETCONTEXT_DEACTIVATE, FALSE);
835 ImmReleaseContext(hwnd,imc);
838 /* similar to above, but using NtUserAssociateInputContext */
839 static void test_NtUserAssociateInputContext(void)
841 HIMC imc;
842 UINT rc;
844 if (!pNtUserAssociateInputContext)
846 win_skip("NtUserAssociateInputContext not available\n");
847 return;
850 imc = ImmGetContext(hwnd);
851 if (imc)
853 HIMC retimc, newimc;
854 HWND focus;
856 SET_ENABLE(WM_IME_SETCONTEXT_ACTIVATE, TRUE);
857 SET_ENABLE(WM_IME_SETCONTEXT_DEACTIVATE, TRUE);
859 ok(GetActiveWindow() == hwnd, "hwnd is not active\n");
860 newimc = ImmCreateContext();
861 ok(newimc != imc, "handles should not be the same\n");
862 rc = pNtUserAssociateInputContext(NULL, NULL, 0);
863 ok(rc == 2, "NtUserAssociateInputContext returned %x\n", rc);
864 rc = pNtUserAssociateInputContext(hwnd, NULL, 0);
865 ok(rc == 1, "NtUserAssociateInputContext returned %x\n", rc);
866 rc = pNtUserAssociateInputContext(NULL, imc, 0);
867 ok(rc == 2, "NtUserAssociateInputContext returned %x\n", rc);
869 rc = pNtUserAssociateInputContext(hwnd, imc, 0);
870 ok(rc == 1, "NtUserAssociateInputContext returned %x\n", rc);
871 retimc = ImmGetContext(hwnd);
872 ok(retimc == imc, "handles should be the same\n");
873 ImmReleaseContext(hwnd,retimc);
875 rc = pNtUserAssociateInputContext(hwnd, imc, 0);
876 ok(rc == 0, "NtUserAssociateInputContext returned %x\n", rc);
878 rc = pNtUserAssociateInputContext(hwnd, newimc, 0);
879 ok(rc == 1, "NtUserAssociateInputContext returned %x\n", rc);
880 retimc = ImmGetContext(hwnd);
881 ok(retimc == newimc, "handles should be the same\n");
882 ImmReleaseContext(hwnd,retimc);
884 focus = CreateWindowA("button", "button", 0, 0, 0, 0, 0, 0, 0, 0, 0);
885 ok(focus != NULL, "CreateWindow failed\n");
886 SET_EXPECT(WM_IME_SETCONTEXT_DEACTIVATE);
887 SetFocus(focus);
888 CHECK_CALLED(WM_IME_SETCONTEXT_DEACTIVATE);
889 rc = pNtUserAssociateInputContext(hwnd, imc, 0);
890 ok(rc == 0, "NtUserAssociateInputContext returned %x\n", rc);
891 SET_EXPECT(WM_IME_SETCONTEXT_ACTIVATE);
892 DestroyWindow(focus);
893 CHECK_CALLED(WM_IME_SETCONTEXT_ACTIVATE);
895 SET_EXPECT(WM_IME_SETCONTEXT_DEACTIVATE);
896 SetFocus(child);
897 CHECK_CALLED(WM_IME_SETCONTEXT_DEACTIVATE);
898 rc = pNtUserAssociateInputContext(hwnd, newimc, 0);
899 ok(rc == 0, "NtUserAssociateInputContext returned %x\n", rc);
900 SET_EXPECT(WM_IME_SETCONTEXT_ACTIVATE);
901 SetFocus(hwnd);
902 CHECK_CALLED(WM_IME_SETCONTEXT_ACTIVATE);
904 rc = pNtUserAssociateInputContext(hwnd, NULL, IACE_DEFAULT);
905 ok(rc == 1, "NtUserAssociateInputContext returned %x\n", rc);
907 SET_ENABLE(WM_IME_SETCONTEXT_ACTIVATE, FALSE);
908 SET_ENABLE(WM_IME_SETCONTEXT_DEACTIVATE, FALSE);
910 ImmReleaseContext(hwnd,imc);
913 typedef struct _igc_threadinfo {
914 HWND hwnd;
915 HANDLE event;
916 HIMC himc;
917 HIMC u_himc;
918 } igc_threadinfo;
921 static DWORD WINAPI ImmGetContextThreadFunc( LPVOID lpParam)
923 HIMC h1,h2;
924 HWND hwnd2;
925 COMPOSITIONFORM cf;
926 CANDIDATEFORM cdf;
927 POINT pt;
928 MSG msg;
930 igc_threadinfo *info= (igc_threadinfo*)lpParam;
931 info->hwnd = CreateWindowExA(WS_EX_CLIENTEDGE, wndcls, "Wine imm32.dll test",
932 WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
933 240, 120, NULL, NULL, GetModuleHandleW(NULL), NULL);
935 h1 = ImmGetContext(hwnd);
936 ok(info->himc == h1, "hwnd context changed in new thread\n");
937 h2 = ImmGetContext(info->hwnd);
938 ok(h2 != h1, "new hwnd in new thread should have different context\n");
939 info->himc = h2;
940 ImmReleaseContext(hwnd,h1);
942 hwnd2 = CreateWindowExA(WS_EX_CLIENTEDGE, wndcls, "Wine imm32.dll test",
943 WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
944 240, 120, NULL, NULL, GetModuleHandleW(NULL), NULL);
945 h1 = ImmGetContext(hwnd2);
947 ok(h1 == h2, "Windows in same thread should have same default context\n");
948 ImmReleaseContext(hwnd2,h1);
949 ImmReleaseContext(info->hwnd,h2);
950 DestroyWindow(hwnd2);
952 /* priming for later tests */
953 ImmSetCompositionWindow(h1, &cf);
954 ImmSetStatusWindowPos(h1, &pt);
955 info->u_himc = ImmCreateContext();
956 ImmSetOpenStatus(info->u_himc, TRUE);
957 cdf.dwIndex = 0;
958 cdf.dwStyle = CFS_CANDIDATEPOS;
959 cdf.ptCurrentPos.x = 0;
960 cdf.ptCurrentPos.y = 0;
961 ImmSetCandidateWindow(info->u_himc, &cdf);
963 SetEvent(info->event);
965 while(GetMessageW(&msg, 0, 0, 0))
967 TranslateMessage(&msg);
968 DispatchMessageW(&msg);
970 return 1;
973 static void test_ImmThreads(void)
975 HIMC himc, otherHimc, h1;
976 igc_threadinfo threadinfo;
977 HANDLE hThread;
978 DWORD dwThreadId;
979 BOOL rc;
980 LOGFONTA lf;
981 COMPOSITIONFORM cf;
982 CANDIDATEFORM cdf;
983 DWORD status, sentence;
984 POINT pt;
986 himc = ImmGetContext(hwnd);
987 threadinfo.event = CreateEventA(NULL, TRUE, FALSE, NULL);
988 threadinfo.himc = himc;
989 hThread = CreateThread(NULL, 0, ImmGetContextThreadFunc, &threadinfo, 0, &dwThreadId );
990 WaitForSingleObject(threadinfo.event, INFINITE);
992 otherHimc = ImmGetContext(threadinfo.hwnd);
994 ok(himc != otherHimc, "Windows from other threads should have different himc\n");
995 ok(otherHimc == threadinfo.himc, "Context from other thread should not change in main thread\n");
997 SET_ENABLE(WM_IME_SETCONTEXT_DEACTIVATE, TRUE);
998 SET_ENABLE(WM_IME_SETCONTEXT_ACTIVATE, TRUE);
999 SET_EXPECT(WM_IME_SETCONTEXT_ACTIVATE);
1000 rc = ImmSetActiveContext(hwnd, otherHimc, TRUE);
1001 ok(rc, "ImmSetActiveContext failed\n");
1002 CHECK_CALLED(WM_IME_SETCONTEXT_ACTIVATE);
1003 SET_EXPECT(WM_IME_SETCONTEXT_DEACTIVATE);
1004 rc = ImmSetActiveContext(hwnd, otherHimc, FALSE);
1005 ok(rc, "ImmSetActiveContext failed\n");
1006 CHECK_CALLED(WM_IME_SETCONTEXT_DEACTIVATE);
1007 SET_ENABLE(WM_IME_SETCONTEXT_DEACTIVATE, FALSE);
1008 SET_ENABLE(WM_IME_SETCONTEXT_ACTIVATE, FALSE);
1010 h1 = ImmAssociateContext(hwnd,otherHimc);
1011 ok(h1 == NULL, "Should fail to be able to Associate a default context from a different thread\n");
1012 h1 = ImmGetContext(hwnd);
1013 ok(h1 == himc, "Context for window should remain unchanged\n");
1014 ImmReleaseContext(hwnd,h1);
1016 h1 = ImmAssociateContext(hwnd, threadinfo.u_himc);
1017 ok (h1 == NULL, "Should fail to associate a context from a different thread\n");
1018 h1 = ImmGetContext(hwnd);
1019 ok(h1 == himc, "Context for window should remain unchanged\n");
1020 ImmReleaseContext(hwnd,h1);
1022 h1 = ImmAssociateContext(threadinfo.hwnd, threadinfo.u_himc);
1023 ok (h1 == NULL, "Should fail to associate a context from a different thread into a window from that thread.\n");
1024 h1 = ImmGetContext(threadinfo.hwnd);
1025 ok(h1 == threadinfo.himc, "Context for window should remain unchanged\n");
1026 ImmReleaseContext(threadinfo.hwnd,h1);
1028 /* OpenStatus */
1029 rc = ImmSetOpenStatus(himc, TRUE);
1030 ok(rc != 0, "ImmSetOpenStatus failed\n");
1031 rc = ImmGetOpenStatus(himc);
1032 ok(rc != 0, "ImmGetOpenStatus failed\n");
1033 rc = ImmSetOpenStatus(himc, FALSE);
1034 ok(rc != 0, "ImmSetOpenStatus failed\n");
1035 rc = ImmGetOpenStatus(himc);
1036 ok(rc == 0, "ImmGetOpenStatus failed\n");
1038 rc = ImmSetOpenStatus(otherHimc, TRUE);
1039 ok(rc == 0, "ImmSetOpenStatus should fail\n");
1040 rc = ImmSetOpenStatus(threadinfo.u_himc, TRUE);
1041 ok(rc == 0, "ImmSetOpenStatus should fail\n");
1042 rc = ImmGetOpenStatus(otherHimc);
1043 ok(rc == 0, "ImmGetOpenStatus failed\n");
1044 rc = ImmGetOpenStatus(threadinfo.u_himc);
1045 ok (rc == 1 || broken(rc == 0), "ImmGetOpenStatus should return 1\n");
1046 rc = ImmSetOpenStatus(otherHimc, FALSE);
1047 ok(rc == 0, "ImmSetOpenStatus should fail\n");
1048 rc = ImmGetOpenStatus(otherHimc);
1049 ok(rc == 0, "ImmGetOpenStatus failed\n");
1051 /* CompositionFont */
1052 rc = ImmGetCompositionFontA(himc, &lf);
1053 ok(rc != 0, "ImmGetCompositionFont failed\n");
1054 rc = ImmSetCompositionFontA(himc, &lf);
1055 ok(rc != 0, "ImmSetCompositionFont failed\n");
1057 rc = ImmGetCompositionFontA(otherHimc, &lf);
1058 ok(rc != 0 || broken(rc == 0), "ImmGetCompositionFont failed\n");
1059 rc = ImmGetCompositionFontA(threadinfo.u_himc, &lf);
1060 ok(rc != 0 || broken(rc == 0), "ImmGetCompositionFont user himc failed\n");
1061 rc = ImmSetCompositionFontA(otherHimc, &lf);
1062 ok(rc == 0, "ImmSetCompositionFont should fail\n");
1063 rc = ImmSetCompositionFontA(threadinfo.u_himc, &lf);
1064 ok(rc == 0, "ImmSetCompositionFont should fail\n");
1066 /* CompositionString */
1067 rc = ImmSetCompositionStringA(himc, SCS_SETSTR, "a", 2, NULL, 0);
1068 ok(rc, "failed.\n");
1069 rc = ImmSetCompositionStringA(otherHimc, SCS_SETSTR, "a", 2, NULL, 0);
1070 ok(!rc, "should fail.\n");
1071 rc = ImmSetCompositionStringA(threadinfo.u_himc, SCS_SETSTR, "a", 2, NULL, 0);
1072 ok(!rc, "should fail.\n");
1074 /* CompositionWindow */
1075 rc = ImmSetCompositionWindow(himc, &cf);
1076 ok(rc != 0, "ImmSetCompositionWindow failed\n");
1077 rc = ImmGetCompositionWindow(himc, &cf);
1078 ok(rc != 0, "ImmGetCompositionWindow failed\n");
1080 rc = ImmSetCompositionWindow(otherHimc, &cf);
1081 ok(rc == 0, "ImmSetCompositionWindow should fail\n");
1082 rc = ImmSetCompositionWindow(threadinfo.u_himc, &cf);
1083 ok(rc == 0, "ImmSetCompositionWindow should fail\n");
1084 rc = ImmGetCompositionWindow(otherHimc, &cf);
1085 ok(rc != 0 || broken(rc == 0), "ImmGetCompositionWindow failed\n");
1086 rc = ImmGetCompositionWindow(threadinfo.u_himc, &cf);
1087 ok(rc != 0 || broken(rc == 0), "ImmGetCompositionWindow failed\n");
1089 /* ConversionStatus */
1090 rc = ImmGetConversionStatus(himc, &status, &sentence);
1091 ok(rc != 0, "ImmGetConversionStatus failed\n");
1092 rc = ImmSetConversionStatus(himc, status, sentence);
1093 ok(rc != 0, "ImmSetConversionStatus failed\n");
1095 rc = ImmGetConversionStatus(otherHimc, &status, &sentence);
1096 ok(rc != 0 || broken(rc == 0), "ImmGetConversionStatus failed\n");
1097 rc = ImmGetConversionStatus(threadinfo.u_himc, &status, &sentence);
1098 ok(rc != 0 || broken(rc == 0), "ImmGetConversionStatus failed\n");
1099 rc = ImmSetConversionStatus(otherHimc, status, sentence);
1100 ok(rc == 0, "ImmSetConversionStatus should fail\n");
1101 rc = ImmSetConversionStatus(threadinfo.u_himc, status, sentence);
1102 ok(rc == 0, "ImmSetConversionStatus should fail\n");
1104 /* StatusWindowPos */
1105 rc = ImmSetStatusWindowPos(himc, &pt);
1106 ok(rc != 0, "ImmSetStatusWindowPos failed\n");
1107 rc = ImmGetStatusWindowPos(himc, &pt);
1108 ok(rc != 0, "ImmGetStatusWindowPos failed\n");
1110 rc = ImmSetStatusWindowPos(otherHimc, &pt);
1111 ok(rc == 0, "ImmSetStatusWindowPos should fail\n");
1112 rc = ImmSetStatusWindowPos(threadinfo.u_himc, &pt);
1113 ok(rc == 0, "ImmSetStatusWindowPos should fail\n");
1114 rc = ImmGetStatusWindowPos(otherHimc, &pt);
1115 ok(rc != 0 || broken(rc == 0), "ImmGetStatusWindowPos failed\n");
1116 rc = ImmGetStatusWindowPos(threadinfo.u_himc, &pt);
1117 ok(rc != 0 || broken(rc == 0), "ImmGetStatusWindowPos failed\n");
1119 h1 = ImmAssociateContext(threadinfo.hwnd, NULL);
1120 ok (h1 == otherHimc, "ImmAssociateContext cross thread with NULL should work\n");
1121 h1 = ImmGetContext(threadinfo.hwnd);
1122 ok (h1 == NULL, "CrossThread window context should be NULL\n");
1123 h1 = ImmAssociateContext(threadinfo.hwnd, h1);
1124 ok (h1 == NULL, "Resetting cross thread context should fail\n");
1125 h1 = ImmGetContext(threadinfo.hwnd);
1126 ok (h1 == NULL, "CrossThread window context should still be NULL\n");
1128 rc = ImmDestroyContext(threadinfo.u_himc);
1129 ok (rc == 0, "ImmDestroyContext Cross Thread should fail\n");
1131 /* Candidate Window */
1132 rc = ImmGetCandidateWindow(himc, 0, &cdf);
1133 ok (rc == 0, "ImmGetCandidateWindow should fail\n");
1134 cdf.dwIndex = 0;
1135 cdf.dwStyle = CFS_CANDIDATEPOS;
1136 cdf.ptCurrentPos.x = 0;
1137 cdf.ptCurrentPos.y = 0;
1138 rc = ImmSetCandidateWindow(himc, &cdf);
1139 ok (rc == 1, "ImmSetCandidateWindow should succeed\n");
1140 rc = ImmGetCandidateWindow(himc, 0, &cdf);
1141 ok (rc == 1, "ImmGetCandidateWindow should succeed\n");
1143 rc = ImmGetCandidateWindow(otherHimc, 0, &cdf);
1144 ok (rc == 0, "ImmGetCandidateWindow should fail\n");
1145 rc = ImmSetCandidateWindow(otherHimc, &cdf);
1146 ok (rc == 0, "ImmSetCandidateWindow should fail\n");
1147 rc = ImmGetCandidateWindow(threadinfo.u_himc, 0, &cdf);
1148 ok (rc == 1 || broken( rc == 0), "ImmGetCandidateWindow should succeed\n");
1149 rc = ImmSetCandidateWindow(threadinfo.u_himc, &cdf);
1150 ok (rc == 0, "ImmSetCandidateWindow should fail\n");
1152 ImmReleaseContext(threadinfo.hwnd,otherHimc);
1153 ImmReleaseContext(hwnd,himc);
1155 SendMessageA(threadinfo.hwnd, WM_CLOSE, 0, 0);
1156 rc = PostThreadMessageA(dwThreadId, WM_QUIT, 1, 0);
1157 ok(rc == 1, "PostThreadMessage should succeed\n");
1158 WaitForSingleObject(hThread, INFINITE);
1159 CloseHandle(hThread);
1161 himc = ImmGetContext(GetDesktopWindow());
1162 ok(himc == NULL, "Should not be able to get himc from other process window\n");
1165 static void test_ImmIsUIMessage(void)
1167 struct test
1169 UINT msg;
1170 BOOL ret;
1173 static const struct test tests[] =
1175 { WM_MOUSEMOVE, FALSE },
1176 { WM_IME_STARTCOMPOSITION, TRUE },
1177 { WM_IME_ENDCOMPOSITION, TRUE },
1178 { WM_IME_COMPOSITION, TRUE },
1179 { WM_IME_SETCONTEXT, TRUE },
1180 { WM_IME_NOTIFY, TRUE },
1181 { WM_IME_CONTROL, FALSE },
1182 { WM_IME_COMPOSITIONFULL, TRUE },
1183 { WM_IME_SELECT, TRUE },
1184 { WM_IME_CHAR, FALSE },
1185 { 0x287 /* FIXME */, TRUE },
1186 { WM_IME_REQUEST, FALSE },
1187 { WM_IME_KEYDOWN, FALSE },
1188 { WM_IME_KEYUP, FALSE },
1189 { 0, FALSE } /* mark the end */
1192 UINT WM_MSIME_SERVICE = RegisterWindowMessageA("MSIMEService");
1193 UINT WM_MSIME_RECONVERTOPTIONS = RegisterWindowMessageA("MSIMEReconvertOptions");
1194 UINT WM_MSIME_MOUSE = RegisterWindowMessageA("MSIMEMouseOperation");
1195 UINT WM_MSIME_RECONVERTREQUEST = RegisterWindowMessageA("MSIMEReconvertRequest");
1196 UINT WM_MSIME_RECONVERT = RegisterWindowMessageA("MSIMEReconvert");
1197 UINT WM_MSIME_QUERYPOSITION = RegisterWindowMessageA("MSIMEQueryPosition");
1198 UINT WM_MSIME_DOCUMENTFEED = RegisterWindowMessageA("MSIMEDocumentFeed");
1200 const struct test *test;
1201 BOOL ret;
1203 if (!pImmIsUIMessageA) return;
1205 for (test = tests; test->msg; test++)
1207 msg_spy_flush_msgs();
1208 ret = pImmIsUIMessageA(NULL, test->msg, 0, 0);
1209 ok(ret == test->ret, "ImmIsUIMessageA returned %x for %x\n", ret, test->msg);
1210 ok(!msg_spy_find_msg(test->msg), "Windows does not send 0x%x for NULL hwnd\n", test->msg);
1212 ret = pImmIsUIMessageA(hwnd, test->msg, 0, 0);
1213 ok(ret == test->ret, "ImmIsUIMessageA returned %x for %x\n", ret, test->msg);
1214 if (ret)
1215 ok(msg_spy_find_msg(test->msg) != NULL, "Windows does send 0x%x\n", test->msg);
1216 else
1217 ok(!msg_spy_find_msg(test->msg), "Windows does not send 0x%x\n", test->msg);
1220 ret = pImmIsUIMessageA(NULL, WM_MSIME_SERVICE, 0, 0);
1221 ok(!ret, "ImmIsUIMessageA returned TRUE for WM_MSIME_SERVICE\n");
1222 ret = pImmIsUIMessageA(NULL, WM_MSIME_RECONVERTOPTIONS, 0, 0);
1223 ok(!ret, "ImmIsUIMessageA returned TRUE for WM_MSIME_RECONVERTOPTIONS\n");
1224 ret = pImmIsUIMessageA(NULL, WM_MSIME_MOUSE, 0, 0);
1225 ok(!ret, "ImmIsUIMessageA returned TRUE for WM_MSIME_MOUSE\n");
1226 ret = pImmIsUIMessageA(NULL, WM_MSIME_RECONVERTREQUEST, 0, 0);
1227 ok(!ret, "ImmIsUIMessageA returned TRUE for WM_MSIME_RECONVERTREQUEST\n");
1228 ret = pImmIsUIMessageA(NULL, WM_MSIME_RECONVERT, 0, 0);
1229 ok(!ret, "ImmIsUIMessageA returned TRUE for WM_MSIME_RECONVERT\n");
1230 ret = pImmIsUIMessageA(NULL, WM_MSIME_QUERYPOSITION, 0, 0);
1231 ok(!ret, "ImmIsUIMessageA returned TRUE for WM_MSIME_QUERYPOSITION\n");
1232 ret = pImmIsUIMessageA(NULL, WM_MSIME_DOCUMENTFEED, 0, 0);
1233 ok(!ret, "ImmIsUIMessageA returned TRUE for WM_MSIME_DOCUMENTFEED\n");
1236 static void test_ImmGetContext(void)
1238 HIMC himc;
1239 DWORD err;
1241 SetLastError(0xdeadbeef);
1242 himc = ImmGetContext((HWND)0xffffffff);
1243 err = GetLastError();
1244 ok(himc == NULL, "ImmGetContext succeeded\n");
1245 ok(err == ERROR_INVALID_WINDOW_HANDLE, "got %lu\n", err);
1247 himc = ImmGetContext(hwnd);
1248 ok(himc != NULL, "ImmGetContext failed\n");
1249 ok(ImmReleaseContext(hwnd, himc), "ImmReleaseContext failed\n");
1252 static LRESULT (WINAPI *old_imm_wnd_proc)(HWND, UINT, WPARAM, LPARAM);
1253 static LRESULT WINAPI imm_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
1255 ok(msg != WM_DESTROY, "got WM_DESTROY message\n");
1256 return old_imm_wnd_proc(hwnd, msg, wparam, lparam);
1259 static HWND thread_ime_wnd;
1260 static DWORD WINAPI test_ImmGetDefaultIMEWnd_thread(void *arg)
1262 CreateWindowA("static", "static", WS_POPUP, 0, 0, 1, 1, NULL, NULL, NULL, NULL);
1264 thread_ime_wnd = ImmGetDefaultIMEWnd(0);
1265 ok(thread_ime_wnd != 0, "ImmGetDefaultIMEWnd returned NULL\n");
1266 old_imm_wnd_proc = (void*)SetWindowLongPtrW(thread_ime_wnd, GWLP_WNDPROC, (LONG_PTR)imm_wnd_proc);
1267 return 0;
1270 static void test_ImmDefaultHwnd(void)
1272 HIMC imc1, imc2, imc3;
1273 HWND def1, def3;
1274 HANDLE thread;
1275 HWND hwnd;
1276 char title[16];
1277 LONG style;
1279 hwnd = CreateWindowExA(WS_EX_CLIENTEDGE, "EDIT", "Wine imm32.dll test",
1280 WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
1281 240, 120, NULL, NULL, GetModuleHandleW(NULL), NULL);
1283 ShowWindow(hwnd, SW_SHOWNORMAL);
1285 imc1 = ImmGetContext(hwnd);
1286 if (!imc1)
1288 win_skip("IME support not implemented\n");
1289 return;
1292 def1 = ImmGetDefaultIMEWnd(hwnd);
1294 GetWindowTextA(def1, title, sizeof(title));
1295 ok(!strcmp(title, "Default IME"), "got %s\n", title);
1296 style = GetWindowLongA(def1, GWL_STYLE);
1297 ok(style == (WS_DISABLED | WS_POPUP | WS_CLIPSIBLINGS), "got %08lx\n", style);
1298 style = GetWindowLongA(def1, GWL_EXSTYLE);
1299 ok(style == 0, "got %08lx\n", style);
1301 imc2 = ImmCreateContext();
1302 ImmSetOpenStatus(imc2, TRUE);
1304 imc3 = ImmGetContext(hwnd);
1305 def3 = ImmGetDefaultIMEWnd(hwnd);
1307 ok(def3 == def1, "Default IME window should not change\n");
1308 ok(imc1 == imc3, "IME context should not change\n");
1309 ImmSetOpenStatus(imc2, FALSE);
1311 thread = CreateThread(NULL, 0, test_ImmGetDefaultIMEWnd_thread, NULL, 0, NULL);
1312 WaitForSingleObject(thread, INFINITE);
1313 ok(thread_ime_wnd != def1, "thread_ime_wnd == def1\n");
1314 ok(!IsWindow(thread_ime_wnd), "thread_ime_wnd was not destroyed\n");
1315 CloseHandle(thread);
1317 ImmReleaseContext(hwnd, imc1);
1318 ImmReleaseContext(hwnd, imc3);
1319 ImmDestroyContext(imc2);
1320 DestroyWindow(hwnd);
1323 static BOOL CALLBACK is_ime_window_proc(HWND hWnd, LPARAM param)
1325 WCHAR class_nameW[16];
1326 HWND *ime_window = (HWND *)param;
1327 if (GetClassNameW(hWnd, class_nameW, ARRAY_SIZE(class_nameW)) && !lstrcmpW(class_nameW, L"IME"))
1329 *ime_window = hWnd;
1330 return FALSE;
1332 return TRUE;
1335 static HWND get_ime_window(void)
1337 HWND ime_window = NULL;
1338 EnumThreadWindows(GetCurrentThreadId(), is_ime_window_proc, (LPARAM)&ime_window);
1339 return ime_window;
1342 struct testcase_ime_window {
1343 BOOL visible;
1344 BOOL top_level_window;
1347 static DWORD WINAPI test_default_ime_window_cb(void *arg)
1349 struct testcase_ime_window *testcase = (struct testcase_ime_window *)arg;
1350 DWORD visible = testcase->visible ? WS_VISIBLE : 0;
1351 HWND hwnd1, hwnd2, default_ime_wnd, ime_wnd;
1353 ok(!get_ime_window(), "Expected no IME windows\n");
1354 if (testcase->top_level_window) {
1355 test_phase = FIRST_WINDOW;
1356 hwnd1 = CreateWindowExA(WS_EX_CLIENTEDGE, wndcls, "Wine imm32.dll test",
1357 WS_OVERLAPPEDWINDOW | visible,
1358 CW_USEDEFAULT, CW_USEDEFAULT,
1359 240, 120, NULL, NULL, GetModuleHandleW(NULL), NULL);
1361 else {
1362 hwnd1 = CreateWindowExA(WS_EX_CLIENTEDGE, "EDIT", "Wine imm32.dll test",
1363 WS_CHILD | visible,
1364 CW_USEDEFAULT, CW_USEDEFAULT,
1365 240, 24, hwnd, NULL, GetModuleHandleW(NULL), NULL);
1367 ime_wnd = get_ime_window();
1368 ok(ime_wnd != NULL, "Expected IME window existence\n");
1369 default_ime_wnd = ImmGetDefaultIMEWnd(hwnd1);
1370 ok(ime_wnd == default_ime_wnd, "Expected %p, got %p\n", ime_wnd, default_ime_wnd);
1372 test_phase = SECOND_WINDOW;
1373 hwnd2 = CreateWindowExA(WS_EX_CLIENTEDGE, wndcls, "Wine imm32.dll test",
1374 WS_OVERLAPPEDWINDOW | visible,
1375 CW_USEDEFAULT, CW_USEDEFAULT,
1376 240, 120, NULL, NULL, GetModuleHandleW(NULL), NULL);
1377 DestroyWindow(hwnd2);
1378 ok(IsWindow(ime_wnd) ||
1379 broken(!testcase->visible /* Vista */) ||
1380 broken(!testcase->top_level_window /* Vista */) ,
1381 "Expected IME window existence\n");
1382 DestroyWindow(hwnd1);
1383 flaky
1384 ok(!IsWindow(ime_wnd), "Expected no IME windows\n");
1385 return 1;
1388 static DWORD WINAPI test_default_ime_window_cancel_cb(void *arg)
1390 struct testcase_ime_window *testcase = (struct testcase_ime_window *)arg;
1391 DWORD visible = testcase->visible ? WS_VISIBLE : 0;
1392 HWND hwnd1, hwnd2, default_ime_wnd, ime_wnd;
1394 ok(!get_ime_window(), "Expected no IME windows\n");
1395 test_phase = NCCREATE_CANCEL;
1396 hwnd1 = CreateWindowExA(WS_EX_CLIENTEDGE, wndcls, "Wine imm32.dll test",
1397 WS_OVERLAPPEDWINDOW | visible,
1398 CW_USEDEFAULT, CW_USEDEFAULT,
1399 240, 120, NULL, NULL, GetModuleHandleW(NULL), NULL);
1400 ok(hwnd1 == NULL, "creation succeeded, got %p\n", hwnd1);
1401 ok(!get_ime_window(), "Expected no IME windows\n");
1403 test_phase = CREATE_CANCEL;
1404 hwnd1 = CreateWindowExA(WS_EX_CLIENTEDGE, wndcls, "Wine imm32.dll test",
1405 WS_OVERLAPPEDWINDOW | visible,
1406 CW_USEDEFAULT, CW_USEDEFAULT,
1407 240, 120, NULL, NULL, GetModuleHandleW(NULL), NULL);
1408 ok(hwnd1 == NULL, "creation succeeded, got %p\n", hwnd1);
1409 ok(!get_ime_window(), "Expected no IME windows\n");
1411 test_phase = FIRST_WINDOW;
1412 hwnd2 = CreateWindowExA(WS_EX_CLIENTEDGE, wndcls, "Wine imm32.dll test",
1413 WS_OVERLAPPEDWINDOW | visible,
1414 CW_USEDEFAULT, CW_USEDEFAULT,
1415 240, 120, NULL, NULL, GetModuleHandleW(NULL), NULL);
1416 ime_wnd = get_ime_window();
1417 ok(ime_wnd != NULL, "Expected IME window existence\n");
1418 default_ime_wnd = ImmGetDefaultIMEWnd(hwnd2);
1419 ok(ime_wnd == default_ime_wnd, "Expected %p, got %p\n", ime_wnd, default_ime_wnd);
1421 DestroyWindow(hwnd2);
1422 ok(!IsWindow(ime_wnd), "Expected no IME windows\n");
1423 return 1;
1426 static DWORD WINAPI test_default_ime_disabled_cb(void *arg)
1428 HWND hWnd, default_ime_wnd;
1430 ok(!get_ime_window(), "Expected no IME windows\n");
1431 ImmDisableIME(GetCurrentThreadId());
1432 test_phase = IME_DISABLED;
1433 hWnd = CreateWindowExA(WS_EX_CLIENTEDGE, wndcls, "Wine imm32.dll test",
1434 WS_OVERLAPPEDWINDOW | WS_VISIBLE,
1435 CW_USEDEFAULT, CW_USEDEFAULT,
1436 240, 120, NULL, NULL, GetModuleHandleW(NULL), NULL);
1437 default_ime_wnd = ImmGetDefaultIMEWnd(hWnd);
1438 ok(!default_ime_wnd, "Expected no IME windows\n");
1439 DestroyWindow(hWnd);
1440 return 1;
1443 static DWORD WINAPI test_default_ime_with_message_only_window_cb(void *arg)
1445 HWND hwnd1, hwnd2, default_ime_wnd;
1447 /* Message-only window doesn't create associated IME window. */
1448 test_phase = PHASE_UNKNOWN;
1449 hwnd1 = CreateWindowA(wndcls, "Wine imm32.dll test",
1450 WS_OVERLAPPEDWINDOW,
1451 CW_USEDEFAULT, CW_USEDEFAULT,
1452 240, 120, HWND_MESSAGE, NULL, GetModuleHandleW(NULL), NULL);
1453 default_ime_wnd = ImmGetDefaultIMEWnd(hwnd1);
1454 ok(!default_ime_wnd, "Expected no IME windows, got %p\n", default_ime_wnd);
1456 /* Setting message-only window as owner at creation,
1457 doesn't create associated IME window. */
1458 hwnd2 = CreateWindowA(wndcls, "Wine imm32.dll test",
1459 WS_OVERLAPPEDWINDOW,
1460 CW_USEDEFAULT, CW_USEDEFAULT,
1461 240, 120, hwnd1, NULL, GetModuleHandleW(NULL), NULL);
1462 default_ime_wnd = ImmGetDefaultIMEWnd(hwnd2);
1463 todo_wine ok(!default_ime_wnd || broken(IsWindow(default_ime_wnd)), "Expected no IME windows, got %p\n", default_ime_wnd);
1465 DestroyWindow(hwnd2);
1466 DestroyWindow(hwnd1);
1468 /* Making window message-only after creation,
1469 doesn't disassociate IME window. */
1470 hwnd1 = CreateWindowA(wndcls, "Wine imm32.dll test",
1471 WS_OVERLAPPEDWINDOW,
1472 CW_USEDEFAULT, CW_USEDEFAULT,
1473 240, 120, NULL, NULL, GetModuleHandleW(NULL), NULL);
1474 default_ime_wnd = ImmGetDefaultIMEWnd(hwnd1);
1475 ok(IsWindow(default_ime_wnd), "Expected IME window existence\n");
1476 SetParent(hwnd1, HWND_MESSAGE);
1477 default_ime_wnd = ImmGetDefaultIMEWnd(hwnd1);
1478 ok(IsWindow(default_ime_wnd), "Expected IME window existence\n");
1479 DestroyWindow(hwnd1);
1480 return 1;
1483 static void test_default_ime_window_creation(void)
1485 HANDLE thread;
1486 size_t i;
1487 struct testcase_ime_window testcases[] = {
1488 /* visible, top-level window */
1489 { TRUE, TRUE },
1490 { FALSE, TRUE },
1491 { TRUE, FALSE },
1492 { FALSE, FALSE }
1495 for (i = 0; i < ARRAY_SIZE(testcases); i++)
1497 thread = CreateThread(NULL, 0, test_default_ime_window_cb, &testcases[i], 0, NULL);
1498 ok(thread != NULL, "CreateThread failed with error %lu\n", GetLastError());
1499 while (MsgWaitForMultipleObjects(1, &thread, FALSE, INFINITE, QS_ALLINPUT) == WAIT_OBJECT_0 + 1)
1501 MSG msg;
1502 while (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE))
1504 TranslateMessage(&msg);
1505 DispatchMessageA(&msg);
1508 CloseHandle(thread);
1510 if (testcases[i].top_level_window)
1512 thread = CreateThread(NULL, 0, test_default_ime_window_cancel_cb, &testcases[i], 0, NULL);
1513 ok(thread != NULL, "CreateThread failed with error %lu\n", GetLastError());
1514 WaitForSingleObject(thread, INFINITE);
1515 CloseHandle(thread);
1519 thread = CreateThread(NULL, 0, test_default_ime_disabled_cb, NULL, 0, NULL);
1520 WaitForSingleObject(thread, INFINITE);
1521 CloseHandle(thread);
1523 thread = CreateThread(NULL, 0, test_default_ime_with_message_only_window_cb, NULL, 0, NULL);
1524 WaitForSingleObject(thread, INFINITE);
1525 CloseHandle(thread);
1527 test_phase = PHASE_UNKNOWN;
1530 static void test_ImmGetIMCLockCount(void)
1532 HIMC imc;
1533 DWORD count, ret, i;
1534 INPUTCONTEXT *ic;
1536 imc = ImmCreateContext();
1537 ImmDestroyContext(imc);
1538 SetLastError(0xdeadbeef);
1539 count = ImmGetIMCLockCount((HIMC)0xdeadcafe);
1540 ok(count == 0, "Invalid IMC should return 0\n");
1541 ret = GetLastError();
1542 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
1543 SetLastError(0xdeadbeef);
1544 count = ImmGetIMCLockCount(0x00000000);
1545 ok(count == 0, "NULL IMC should return 0\n");
1546 ret = GetLastError();
1547 ok(ret == 0xdeadbeef, "Last Error should remain unchanged: %08lx\n",ret);
1548 count = ImmGetIMCLockCount(imc);
1549 ok(count == 0, "Destroyed IMC should return 0\n");
1550 ret = GetLastError();
1551 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
1553 imc = ImmCreateContext();
1554 count = ImmGetIMCLockCount(imc);
1555 ok(count == 0, "expect 0, returned %ld\n", count);
1556 ic = ImmLockIMC(imc);
1557 ok(ic != NULL, "ImmLockIMC failed!\n");
1558 count = ImmGetIMCLockCount(imc);
1559 ok(count == 1, "expect 1, returned %ld\n", count);
1560 ret = ImmUnlockIMC(imc);
1561 ok(ret == TRUE, "expect TRUE, ret %ld\n", ret);
1562 count = ImmGetIMCLockCount(imc);
1563 ok(count == 0, "expect 0, returned %ld\n", count);
1564 ret = ImmUnlockIMC(imc);
1565 ok(ret == TRUE, "expect TRUE, ret %ld\n", ret);
1566 count = ImmGetIMCLockCount(imc);
1567 ok(count == 0, "expect 0, returned %ld\n", count);
1569 for (i = 0; i < GMEM_LOCKCOUNT * 2; i++)
1571 ic = ImmLockIMC(imc);
1572 ok(ic != NULL, "ImmLockIMC failed!\n");
1574 count = ImmGetIMCLockCount(imc);
1575 todo_wine ok(count == GMEM_LOCKCOUNT, "expect GMEM_LOCKCOUNT, returned %ld\n", count);
1577 for (i = 0; i < GMEM_LOCKCOUNT - 1; i++)
1578 ImmUnlockIMC(imc);
1579 count = ImmGetIMCLockCount(imc);
1580 todo_wine ok(count == 1, "expect 1, returned %ld\n", count);
1581 ImmUnlockIMC(imc);
1582 count = ImmGetIMCLockCount(imc);
1583 todo_wine ok(count == 0, "expect 0, returned %ld\n", count);
1585 ImmDestroyContext(imc);
1588 static void test_ImmGetIMCCLockCount(void)
1590 HIMCC imcc;
1591 DWORD count, g_count, i;
1592 BOOL ret;
1593 VOID *p;
1595 imcc = ImmCreateIMCC(sizeof(CANDIDATEINFO));
1596 count = ImmGetIMCCLockCount(imcc);
1597 ok(count == 0, "expect 0, returned %ld\n", count);
1598 ImmLockIMCC(imcc);
1599 count = ImmGetIMCCLockCount(imcc);
1600 ok(count == 1, "expect 1, returned %ld\n", count);
1601 ret = ImmUnlockIMCC(imcc);
1602 ok(ret == FALSE, "expect FALSE, ret %d\n", ret);
1603 count = ImmGetIMCCLockCount(imcc);
1604 ok(count == 0, "expect 0, returned %ld\n", count);
1605 ret = ImmUnlockIMCC(imcc);
1606 ok(ret == FALSE, "expect FALSE, ret %d\n", ret);
1607 count = ImmGetIMCCLockCount(imcc);
1608 ok(count == 0, "expect 0, returned %ld\n", count);
1610 p = ImmLockIMCC(imcc);
1611 ok(GlobalHandle(p) == imcc, "expect %p, returned %p\n", imcc, GlobalHandle(p));
1613 for (i = 0; i < GMEM_LOCKCOUNT * 2; i++)
1615 ImmLockIMCC(imcc);
1616 count = ImmGetIMCCLockCount(imcc);
1617 g_count = GlobalFlags(imcc) & GMEM_LOCKCOUNT;
1618 ok(count == g_count, "count %ld, g_count %ld\n", count, g_count);
1620 count = ImmGetIMCCLockCount(imcc);
1621 ok(count == GMEM_LOCKCOUNT, "expect GMEM_LOCKCOUNT, returned %ld\n", count);
1623 for (i = 0; i < GMEM_LOCKCOUNT - 1; i++)
1624 GlobalUnlock(imcc);
1625 count = ImmGetIMCCLockCount(imcc);
1626 ok(count == 1, "expect 1, returned %ld\n", count);
1627 GlobalUnlock(imcc);
1628 count = ImmGetIMCCLockCount(imcc);
1629 ok(count == 0, "expect 0, returned %ld\n", count);
1631 ImmDestroyIMCC(imcc);
1634 static void test_ImmDestroyContext(void)
1636 HIMC imc;
1637 DWORD ret, count;
1638 INPUTCONTEXT *ic;
1640 imc = ImmCreateContext();
1641 count = ImmGetIMCLockCount(imc);
1642 ok(count == 0, "expect 0, returned %ld\n", count);
1643 ic = ImmLockIMC(imc);
1644 ok(ic != NULL, "ImmLockIMC failed!\n");
1645 count = ImmGetIMCLockCount(imc);
1646 ok(count == 1, "expect 1, returned %ld\n", count);
1647 ret = ImmDestroyContext(imc);
1648 ok(ret == TRUE, "Destroy a locked IMC should success!\n");
1649 ic = ImmLockIMC(imc);
1650 ok(ic == NULL, "Lock a destroyed IMC should fail!\n");
1651 ret = ImmUnlockIMC(imc);
1652 ok(ret == FALSE, "Unlock a destroyed IMC should fail!\n");
1653 count = ImmGetIMCLockCount(imc);
1654 ok(count == 0, "Get lock count of a destroyed IMC should return 0!\n");
1655 SetLastError(0xdeadbeef);
1656 ret = ImmDestroyContext(imc);
1657 ok(ret == FALSE, "Destroy a destroyed IMC should fail!\n");
1658 ret = GetLastError();
1659 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
1662 static void test_ImmDestroyIMCC(void)
1664 HIMCC imcc;
1665 DWORD ret, count, size;
1666 VOID *p;
1668 imcc = ImmCreateIMCC(sizeof(CANDIDATEINFO));
1669 count = ImmGetIMCCLockCount(imcc);
1670 ok(count == 0, "expect 0, returned %ld\n", count);
1671 p = ImmLockIMCC(imcc);
1672 ok(p != NULL, "ImmLockIMCC failed!\n");
1673 count = ImmGetIMCCLockCount(imcc);
1674 ok(count == 1, "expect 1, returned %ld\n", count);
1675 size = ImmGetIMCCSize(imcc);
1676 ok(size == sizeof(CANDIDATEINFO), "returned %ld\n", size);
1677 p = ImmDestroyIMCC(imcc);
1678 ok(p == NULL, "Destroy a locked IMCC should success!\n");
1679 p = ImmLockIMCC(imcc);
1680 ok(p == NULL, "Lock a destroyed IMCC should fail!\n");
1681 ret = ImmUnlockIMCC(imcc);
1682 ok(ret == FALSE, "Unlock a destroyed IMCC should return FALSE!\n");
1683 count = ImmGetIMCCLockCount(imcc);
1684 ok(count == 0, "Get lock count of a destroyed IMCC should return 0!\n");
1685 size = ImmGetIMCCSize(imcc);
1686 ok(size == 0, "Get size of a destroyed IMCC should return 0!\n");
1687 SetLastError(0xdeadbeef);
1688 p = ImmDestroyIMCC(imcc);
1689 ok(p != NULL, "returned NULL\n");
1690 ret = GetLastError();
1691 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
1694 static void test_ImmMessages(void)
1696 CANDIDATEFORM cf;
1697 imm_msgs *msg;
1698 HWND defwnd;
1699 HIMC imc;
1700 UINT idx = 0;
1702 LPINPUTCONTEXT lpIMC;
1703 LPTRANSMSG lpTransMsg;
1705 HWND hwnd = CreateWindowExA(WS_EX_CLIENTEDGE, "EDIT", "Wine imm32.dll test",
1706 WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
1707 240, 120, NULL, NULL, GetModuleHandleA(NULL), NULL);
1709 ShowWindow(hwnd, SW_SHOWNORMAL);
1710 defwnd = ImmGetDefaultIMEWnd(hwnd);
1711 imc = ImmGetContext(hwnd);
1713 ImmSetOpenStatus(imc, TRUE);
1714 msg_spy_flush_msgs();
1715 SendMessageA(defwnd, WM_IME_CONTROL, IMC_GETCANDIDATEPOS, (LPARAM)&cf );
1718 msg = msg_spy_find_next_msg(WM_IME_CONTROL,&idx);
1719 if (msg) ok(!msg->post, "Message should not be posted\n");
1720 } while (msg);
1721 msg_spy_flush_msgs();
1723 lpIMC = ImmLockIMC(imc);
1724 lpIMC->hMsgBuf = ImmReSizeIMCC(lpIMC->hMsgBuf, (lpIMC->dwNumMsgBuf + 1) * sizeof(TRANSMSG));
1725 lpTransMsg = ImmLockIMCC(lpIMC->hMsgBuf);
1726 lpTransMsg += lpIMC->dwNumMsgBuf;
1727 lpTransMsg->message = WM_IME_STARTCOMPOSITION;
1728 lpTransMsg->wParam = 0;
1729 lpTransMsg->lParam = 0;
1730 ImmUnlockIMCC(lpIMC->hMsgBuf);
1731 lpIMC->dwNumMsgBuf++;
1732 ImmUnlockIMC(imc);
1733 ImmGenerateMessage(imc);
1734 idx = 0;
1737 msg = msg_spy_find_next_msg(WM_IME_STARTCOMPOSITION, &idx);
1738 if (msg) ok(!msg->post, "Message should not be posted\n");
1739 } while (msg);
1740 msg_spy_flush_msgs();
1742 lpIMC = ImmLockIMC(imc);
1743 lpIMC->hMsgBuf = ImmReSizeIMCC(lpIMC->hMsgBuf, (lpIMC->dwNumMsgBuf + 1) * sizeof(TRANSMSG));
1744 lpTransMsg = ImmLockIMCC(lpIMC->hMsgBuf);
1745 lpTransMsg += lpIMC->dwNumMsgBuf;
1746 lpTransMsg->message = WM_IME_COMPOSITION;
1747 lpTransMsg->wParam = 0;
1748 lpTransMsg->lParam = 0;
1749 ImmUnlockIMCC(lpIMC->hMsgBuf);
1750 lpIMC->dwNumMsgBuf++;
1751 ImmUnlockIMC(imc);
1752 ImmGenerateMessage(imc);
1753 idx = 0;
1756 msg = msg_spy_find_next_msg(WM_IME_COMPOSITION, &idx);
1757 if (msg) ok(!msg->post, "Message should not be posted\n");
1758 } while (msg);
1759 msg_spy_flush_msgs();
1761 lpIMC = ImmLockIMC(imc);
1762 lpIMC->hMsgBuf = ImmReSizeIMCC(lpIMC->hMsgBuf, (lpIMC->dwNumMsgBuf + 1) * sizeof(TRANSMSG));
1763 lpTransMsg = ImmLockIMCC(lpIMC->hMsgBuf);
1764 lpTransMsg += lpIMC->dwNumMsgBuf;
1765 lpTransMsg->message = WM_IME_ENDCOMPOSITION;
1766 lpTransMsg->wParam = 0;
1767 lpTransMsg->lParam = 0;
1768 ImmUnlockIMCC(lpIMC->hMsgBuf);
1769 lpIMC->dwNumMsgBuf++;
1770 ImmUnlockIMC(imc);
1771 ImmGenerateMessage(imc);
1772 idx = 0;
1775 msg = msg_spy_find_next_msg(WM_IME_ENDCOMPOSITION, &idx);
1776 if (msg) ok(!msg->post, "Message should not be posted\n");
1777 } while (msg);
1778 msg_spy_flush_msgs();
1780 ImmSetOpenStatus(imc, FALSE);
1781 ImmReleaseContext(hwnd, imc);
1782 DestroyWindow(hwnd);
1785 static LRESULT CALLBACK processkey_wnd_proc( HWND hWnd, UINT msg, WPARAM wParam,
1786 LPARAM lParam )
1788 return DefWindowProcW(hWnd, msg, wParam, lParam);
1791 static void test_ime_processkey(void)
1793 MSG msg;
1794 WNDCLASSW wclass;
1795 HANDLE hInstance = GetModuleHandleW(NULL);
1796 TEST_INPUT inputs[2];
1797 HIMC imc;
1798 INT rc;
1799 HWND hWndTest;
1801 wclass.lpszClassName = L"ProcessKeyTestClass";
1802 wclass.style = CS_HREDRAW | CS_VREDRAW;
1803 wclass.lpfnWndProc = processkey_wnd_proc;
1804 wclass.hInstance = hInstance;
1805 wclass.hIcon = LoadIconW(0, (LPCWSTR)IDI_APPLICATION);
1806 wclass.hCursor = LoadCursorW( NULL, (LPCWSTR)IDC_ARROW);
1807 wclass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
1808 wclass.lpszMenuName = 0;
1809 wclass.cbClsExtra = 0;
1810 wclass.cbWndExtra = 0;
1811 if(!RegisterClassW(&wclass)){
1812 win_skip("Failed to register window.\n");
1813 return;
1816 /* create the test window that will receive the keystrokes */
1817 hWndTest = CreateWindowW(wclass.lpszClassName, L"ProcessKey",
1818 WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, 100, 100,
1819 NULL, NULL, hInstance, NULL);
1821 ShowWindow(hWndTest, SW_SHOW);
1822 SetWindowPos(hWndTest, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE);
1823 SetForegroundWindow(hWndTest);
1824 UpdateWindow(hWndTest);
1826 imc = ImmGetContext(hWndTest);
1827 if (!imc)
1829 win_skip("IME not supported\n");
1830 DestroyWindow(hWndTest);
1831 return;
1834 rc = ImmSetOpenStatus(imc, TRUE);
1835 if (rc != TRUE)
1837 win_skip("Unable to open IME\n");
1838 ImmReleaseContext(hWndTest, imc);
1839 DestroyWindow(hWndTest);
1840 return;
1843 /* flush pending messages */
1844 while (PeekMessageW(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageW(&msg);
1846 SetFocus(hWndTest);
1848 /* init input data that never changes */
1849 inputs[1].type = inputs[0].type = INPUT_KEYBOARD;
1850 inputs[1].u.ki.dwExtraInfo = inputs[0].u.ki.dwExtraInfo = 0;
1851 inputs[1].u.ki.time = inputs[0].u.ki.time = 0;
1853 /* Pressing a key */
1854 inputs[0].u.ki.wVk = 0x41;
1855 inputs[0].u.ki.wScan = 0x1e;
1856 inputs[0].u.ki.dwFlags = 0x0;
1858 pSendInput(1, (INPUT*)inputs, sizeof(INPUT));
1860 while(PeekMessageW(&msg, hWndTest, 0, 0, PM_NOREMOVE)) {
1861 if(msg.message != WM_KEYDOWN)
1862 PeekMessageW(&msg, hWndTest, 0, 0, PM_REMOVE);
1863 else
1865 ok(msg.wParam != VK_PROCESSKEY,"Incorrect ProcessKey Found\n");
1866 PeekMessageW(&msg, hWndTest, 0, 0, PM_REMOVE);
1867 if(msg.wParam == VK_PROCESSKEY)
1868 trace("ProcessKey was correctly found\n");
1870 TranslateMessage(&msg);
1871 /* test calling TranslateMessage multiple times */
1872 TranslateMessage(&msg);
1873 DispatchMessageW(&msg);
1876 inputs[0].u.ki.wVk = 0x41;
1877 inputs[0].u.ki.wScan = 0x1e;
1878 inputs[0].u.ki.dwFlags = KEYEVENTF_KEYUP;
1880 pSendInput(1, (INPUT*)inputs, sizeof(INPUT));
1882 while(PeekMessageW(&msg, hWndTest, 0, 0, PM_NOREMOVE)) {
1883 if(msg.message != WM_KEYUP)
1884 PeekMessageW(&msg, hWndTest, 0, 0, PM_REMOVE);
1885 else
1887 ok(msg.wParam != VK_PROCESSKEY,"Incorrect ProcessKey Found\n");
1888 PeekMessageW(&msg, hWndTest, 0, 0, PM_REMOVE);
1889 ok(msg.wParam != VK_PROCESSKEY,"ProcessKey should still not be Found\n");
1891 TranslateMessage(&msg);
1892 DispatchMessageW(&msg);
1895 ImmReleaseContext(hWndTest, imc);
1896 ImmSetOpenStatus(imc, FALSE);
1897 DestroyWindow(hWndTest);
1900 static void test_InvalidIMC(void)
1902 HIMC imc_destroy;
1903 HIMC imc_null = 0x00000000;
1904 HIMC imc_bad = (HIMC)0xdeadcafe;
1906 HIMC imc1, imc2, oldimc;
1907 DWORD ret;
1908 DWORD count;
1909 CHAR buffer[1000];
1910 INPUTCONTEXT *ic;
1911 LOGFONTA lf;
1912 BOOL r;
1914 memset(&lf, 0, sizeof(lf));
1916 imc_destroy = ImmCreateContext();
1917 ret = ImmDestroyContext(imc_destroy);
1918 ok(ret == TRUE, "Destroy an IMC should success!\n");
1920 /* Test associating destroyed imc */
1921 imc1 = ImmGetContext(hwnd);
1922 SetLastError(0xdeadbeef);
1923 oldimc = ImmAssociateContext(hwnd, imc_destroy);
1924 ok(!oldimc, "Associating to a destroyed imc should fail!\n");
1925 ret = GetLastError();
1926 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
1927 imc2 = ImmGetContext(hwnd);
1928 ok(imc1 == imc2, "imc should not changed! imc1 %p, imc2 %p\n", imc1, imc2);
1930 SET_ENABLE(WM_IME_SETCONTEXT_DEACTIVATE, TRUE);
1931 SET_ENABLE(WM_IME_SETCONTEXT_ACTIVATE, TRUE);
1932 r = ImmSetActiveContext(hwnd, imc_destroy, TRUE);
1933 ok(!r, "ImmSetActiveContext succeeded\n");
1934 SET_EXPECT(WM_IME_SETCONTEXT_DEACTIVATE);
1935 r = ImmSetActiveContext(hwnd, imc_destroy, FALSE);
1936 ok(r, "ImmSetActiveContext failed\n");
1937 CHECK_CALLED(WM_IME_SETCONTEXT_DEACTIVATE);
1938 SET_ENABLE(WM_IME_SETCONTEXT_ACTIVATE, FALSE);
1939 SET_ENABLE(WM_IME_SETCONTEXT_DEACTIVATE, FALSE);
1941 /* Test associating NULL imc, which is different from an invalid imc */
1942 oldimc = ImmAssociateContext(hwnd, imc_null);
1943 ok(oldimc != NULL, "Associating to NULL imc should success!\n");
1944 imc2 = ImmGetContext(hwnd);
1945 ok(!imc2, "expect NULL, returned %p\n", imc2);
1946 oldimc = ImmAssociateContext(hwnd, imc1);
1947 ok(!oldimc, "expect NULL, returned %p\n", oldimc);
1948 imc2 = ImmGetContext(hwnd);
1949 ok(imc2 == imc1, "imc should not changed! imc2 %p, imc1 %p\n", imc2, imc1);
1951 /* Test associating invalid imc */
1952 imc1 = ImmGetContext(hwnd);
1953 SetLastError(0xdeadbeef);
1954 oldimc = ImmAssociateContext(hwnd, imc_bad);
1955 ok(!oldimc, "Associating to a destroyed imc should fail!\n");
1956 ret = GetLastError();
1957 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
1958 imc2 = ImmGetContext(hwnd);
1959 ok(imc1 == imc2, "imc should not changed! imc1 %p, imc2 %p\n", imc1, imc2);
1962 /* Test ImmGetCandidateListA */
1963 SetLastError(0xdeadbeef);
1964 ret = ImmGetCandidateListA(imc_bad, 0, NULL, 0);
1965 ok(ret == 0, "Bad IME should return 0\n");
1966 ret = GetLastError();
1967 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
1968 SetLastError(0xdeadbeef);
1969 ret = ImmGetCandidateListA(imc_null, 0, NULL, 0);
1970 ok(ret == 0, "NULL IME should return 0\n");
1971 ret = GetLastError();
1972 ok(ret == 0xdeadbeef, "last error should remain unchanged %08lx!\n", ret);
1973 SetLastError(0xdeadbeef);
1974 ret = ImmGetCandidateListA(imc_destroy, 0, NULL, 0);
1975 ok(ret == 0, "Destroyed IME should return 0\n");
1976 ret = GetLastError();
1977 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
1979 /* Test ImmGetCandidateListCountA*/
1980 SetLastError(0xdeadbeef);
1981 ret = ImmGetCandidateListCountA(imc_bad,&count);
1982 ok(ret == 0, "Bad IME should return 0\n");
1983 ret = GetLastError();
1984 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
1985 SetLastError(0xdeadbeef);
1986 ret = ImmGetCandidateListCountA(imc_null,&count);
1987 ok(ret == 0, "NULL IME should return 0\n");
1988 ret = GetLastError();
1989 ok(ret == 0xdeadbeef, "last error should remain unchanged %08lx!\n", ret);
1990 SetLastError(0xdeadbeef);
1991 ret = ImmGetCandidateListCountA(imc_destroy,&count);
1992 ok(ret == 0, "Destroyed IME should return 0\n");
1993 ret = GetLastError();
1994 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
1996 /* Test ImmGetCandidateWindow */
1997 SetLastError(0xdeadbeef);
1998 ret = ImmGetCandidateWindow(imc_bad, 0, (LPCANDIDATEFORM)buffer);
1999 ok(ret == 0, "Bad IME should return 0\n");
2000 ret = GetLastError();
2001 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2002 SetLastError(0xdeadbeef);
2003 ret = ImmGetCandidateWindow(imc_null, 0, (LPCANDIDATEFORM)buffer);
2004 ok(ret == 0, "NULL IME should return 0\n");
2005 ret = GetLastError();
2006 ok(ret == 0xdeadbeef, "last error should remain unchanged %08lx!\n", ret);
2007 SetLastError(0xdeadbeef);
2008 ret = ImmGetCandidateWindow(imc_destroy, 0, (LPCANDIDATEFORM)buffer);
2009 ok(ret == 0, "Destroyed IME should return 0\n");
2010 ret = GetLastError();
2011 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2013 /* Test ImmGetCompositionFontA */
2014 SetLastError(0xdeadbeef);
2015 ret = ImmGetCompositionFontA(imc_bad, (LPLOGFONTA)buffer);
2016 ok(ret == 0, "Bad IME should return 0\n");
2017 ret = GetLastError();
2018 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2019 SetLastError(0xdeadbeef);
2020 ret = ImmGetCompositionFontA(imc_null, (LPLOGFONTA)buffer);
2021 ok(ret == 0, "NULL IME should return 0\n");
2022 ret = GetLastError();
2023 ok(ret == 0xdeadbeef, "last error should remain unchanged %08lx!\n", ret);
2024 SetLastError(0xdeadbeef);
2025 ret = ImmGetCompositionFontA(imc_destroy, (LPLOGFONTA)buffer);
2026 ok(ret == 0, "Destroyed IME should return 0\n");
2027 ret = GetLastError();
2028 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2030 /* Test ImmGetCompositionWindow */
2031 SetLastError(0xdeadbeef);
2032 ret = ImmGetCompositionWindow(imc_bad, (LPCOMPOSITIONFORM)buffer);
2033 ok(ret == 0, "Bad IME should return 0\n");
2034 ret = GetLastError();
2035 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2036 SetLastError(0xdeadbeef);
2037 ret = ImmGetCompositionWindow(imc_null, (LPCOMPOSITIONFORM)buffer);
2038 ok(ret == 0, "NULL IME should return 0\n");
2039 ret = GetLastError();
2040 ok(ret == 0xdeadbeef, "last error should remain unchanged %08lx!\n", ret);
2041 SetLastError(0xdeadbeef);
2042 ret = ImmGetCompositionWindow(imc_destroy, (LPCOMPOSITIONFORM)buffer);
2043 ok(ret == 0, "Destroyed IME should return 0\n");
2044 ret = GetLastError();
2045 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2047 /* Test ImmGetCompositionStringA */
2048 SetLastError(0xdeadbeef);
2049 ret = ImmGetCompositionStringA(imc_bad, GCS_COMPSTR, NULL, 0);
2050 ok(ret == 0, "Bad IME should return 0\n");
2051 ret = GetLastError();
2052 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2053 SetLastError(0xdeadbeef);
2054 ret = ImmGetCompositionStringA(imc_null, GCS_COMPSTR, NULL, 0);
2055 ok(ret == 0, "NULL IME should return 0\n");
2056 ret = GetLastError();
2057 ok(ret == 0xdeadbeef, "last error should remain unchanged %08lx!\n", ret);
2058 SetLastError(0xdeadbeef);
2059 ret = ImmGetCompositionStringA(imc_destroy, GCS_COMPSTR, NULL, 0);
2060 ok(ret == 0, "Destroyed IME should return 0\n");
2061 ret = GetLastError();
2062 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2064 /* Test ImmSetOpenStatus */
2065 SetLastError(0xdeadbeef);
2066 ret = ImmSetOpenStatus(imc_bad, 1);
2067 ok(ret == 0, "Bad IME should return 0\n");
2068 ret = GetLastError();
2069 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2070 SetLastError(0xdeadbeef);
2071 ret = ImmSetOpenStatus(imc_null, 1);
2072 ok(ret == 0, "NULL IME should return 0\n");
2073 ret = GetLastError();
2074 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2075 SetLastError(0xdeadbeef);
2076 ret = ImmSetOpenStatus(imc_destroy, 1);
2077 ok(ret == 0, "Destroyed IME should return 0\n");
2078 ret = GetLastError();
2079 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2081 /* Test ImmGetOpenStatus */
2082 SetLastError(0xdeadbeef);
2083 ret = ImmGetOpenStatus(imc_bad);
2084 ok(ret == 0, "Bad IME should return 0\n");
2085 ret = GetLastError();
2086 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2087 SetLastError(0xdeadbeef);
2088 ret = ImmGetOpenStatus(imc_null);
2089 ok(ret == 0, "NULL IME should return 0\n");
2090 ret = GetLastError();
2091 ok(ret == 0xdeadbeef, "last error should remain unchanged %08lx!\n", ret);
2092 SetLastError(0xdeadbeef);
2093 ret = ImmGetOpenStatus(imc_destroy);
2094 ok(ret == 0, "Destroyed IME should return 0\n");
2095 ret = GetLastError();
2096 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2098 /* Test ImmGetStatusWindowPos */
2099 SetLastError(0xdeadbeef);
2100 ret = ImmGetStatusWindowPos(imc_bad, NULL);
2101 ok(ret == 0, "Bad IME should return 0\n");
2102 ret = GetLastError();
2103 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2104 SetLastError(0xdeadbeef);
2105 ret = ImmGetStatusWindowPos(imc_null, NULL);
2106 ok(ret == 0, "NULL IME should return 0\n");
2107 ret = GetLastError();
2108 ok(ret == 0xdeadbeef, "last error should remain unchanged %08lx!\n", ret);
2109 SetLastError(0xdeadbeef);
2110 ret = ImmGetStatusWindowPos(imc_destroy, NULL);
2111 ok(ret == 0, "Destroyed IME should return 0\n");
2112 ret = GetLastError();
2113 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2115 /* Test ImmRequestMessageA */
2116 SetLastError(0xdeadbeef);
2117 ret = ImmRequestMessageA(imc_bad, WM_CHAR, 0);
2118 ok(ret == 0, "Bad IME should return 0\n");
2119 ret = GetLastError();
2120 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2121 SetLastError(0xdeadbeef);
2122 ret = ImmRequestMessageA(imc_null, WM_CHAR, 0);
2123 ok(ret == 0, "NULL IME should return 0\n");
2124 ret = GetLastError();
2125 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2126 SetLastError(0xdeadbeef);
2127 ret = ImmRequestMessageA(imc_destroy, WM_CHAR, 0);
2128 ok(ret == 0, "Destroyed IME should return 0\n");
2129 ret = GetLastError();
2130 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2132 /* Test ImmSetCompositionFontA */
2133 SetLastError(0xdeadbeef);
2134 ret = ImmSetCompositionFontA(imc_bad, &lf);
2135 ok(ret == 0, "Bad IME should return 0\n");
2136 ret = GetLastError();
2137 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2138 SetLastError(0xdeadbeef);
2139 ret = ImmSetCompositionFontA(imc_null, &lf);
2140 ok(ret == 0, "NULL IME should return 0\n");
2141 ret = GetLastError();
2142 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2143 SetLastError(0xdeadbeef);
2144 ret = ImmSetCompositionFontA(imc_destroy, &lf);
2145 ok(ret == 0, "Destroyed IME should return 0\n");
2146 ret = GetLastError();
2147 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2149 /* Test ImmSetCompositionWindow */
2150 SetLastError(0xdeadbeef);
2151 ret = ImmSetCompositionWindow(imc_bad, NULL);
2152 ok(ret == 0, "Bad IME should return 0\n");
2153 ret = GetLastError();
2154 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2155 SetLastError(0xdeadbeef);
2156 ret = ImmSetCompositionWindow(imc_null, NULL);
2157 ok(ret == 0, "NULL IME should return 0\n");
2158 ret = GetLastError();
2159 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2160 SetLastError(0xdeadbeef);
2161 ret = ImmSetCompositionWindow(imc_destroy, NULL);
2162 ok(ret == 0, "Destroyed IME should return 0\n");
2163 ret = GetLastError();
2164 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2166 /* Test ImmSetConversionStatus */
2167 SetLastError(0xdeadbeef);
2168 ret = ImmSetConversionStatus(imc_bad, 0, 0);
2169 ok(ret == 0, "Bad IME should return 0\n");
2170 ret = GetLastError();
2171 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2172 SetLastError(0xdeadbeef);
2173 ret = ImmSetConversionStatus(imc_null, 0, 0);
2174 ok(ret == 0, "NULL IME should return 0\n");
2175 ret = GetLastError();
2176 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2177 SetLastError(0xdeadbeef);
2178 ret = ImmSetConversionStatus(imc_destroy, 0, 0);
2179 ok(ret == 0, "Destroyed IME should return 0\n");
2180 ret = GetLastError();
2181 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2183 /* Test ImmSetStatusWindowPos */
2184 SetLastError(0xdeadbeef);
2185 ret = ImmSetStatusWindowPos(imc_bad, 0);
2186 ok(ret == 0, "Bad IME should return 0\n");
2187 ret = GetLastError();
2188 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2189 SetLastError(0xdeadbeef);
2190 ret = ImmSetStatusWindowPos(imc_null, 0);
2191 ok(ret == 0, "NULL IME should return 0\n");
2192 ret = GetLastError();
2193 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2194 SetLastError(0xdeadbeef);
2195 ret = ImmSetStatusWindowPos(imc_destroy, 0);
2196 ok(ret == 0, "Destroyed IME should return 0\n");
2197 ret = GetLastError();
2198 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2200 /* Test ImmGetImeMenuItemsA */
2201 SetLastError(0xdeadbeef);
2202 ret = ImmGetImeMenuItemsA(imc_bad, 0, 0, NULL, NULL, 0);
2203 ok(ret == 0, "Bad IME should return 0\n");
2204 ret = GetLastError();
2205 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2206 SetLastError(0xdeadbeef);
2207 ret = ImmGetImeMenuItemsA(imc_null, 0, 0, NULL, NULL, 0);
2208 ok(ret == 0, "NULL IME should return 0\n");
2209 ret = GetLastError();
2210 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2211 SetLastError(0xdeadbeef);
2212 ret = ImmGetImeMenuItemsA(imc_destroy, 0, 0, NULL, NULL, 0);
2213 ok(ret == 0, "Destroyed IME should return 0\n");
2214 ret = GetLastError();
2215 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2217 /* Test ImmLockIMC */
2218 SetLastError(0xdeadbeef);
2219 ic = ImmLockIMC(imc_bad);
2220 ok(ic == 0, "Bad IME should return 0\n");
2221 ret = GetLastError();
2222 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2223 SetLastError(0xdeadbeef);
2224 ic = ImmLockIMC(imc_null);
2225 ok(ic == 0, "NULL IME should return 0\n");
2226 ret = GetLastError();
2227 ok(ret == 0xdeadbeef, "last error should remain unchanged %08lx!\n", ret);
2228 SetLastError(0xdeadbeef);
2229 ic = ImmLockIMC(imc_destroy);
2230 ok(ic == 0, "Destroyed IME should return 0\n");
2231 ret = GetLastError();
2232 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2234 /* Test ImmUnlockIMC */
2235 SetLastError(0xdeadbeef);
2236 ret = ImmUnlockIMC(imc_bad);
2237 ok(ret == 0, "Bad IME should return 0\n");
2238 ret = GetLastError();
2239 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2240 SetLastError(0xdeadbeef);
2241 ret = ImmUnlockIMC(imc_null);
2242 ok(ret == 0, "NULL IME should return 0\n");
2243 ret = GetLastError();
2244 ok(ret == 0xdeadbeef, "last error should remain unchanged %08lx!\n", ret);
2245 SetLastError(0xdeadbeef);
2246 ret = ImmUnlockIMC(imc_destroy);
2247 ok(ret == 0, "Destroyed IME should return 0\n");
2248 ret = GetLastError();
2249 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2251 /* Test ImmGenerateMessage */
2252 SetLastError(0xdeadbeef);
2253 ret = ImmGenerateMessage(imc_bad);
2254 ok(ret == 0, "Bad IME should return 0\n");
2255 ret = GetLastError();
2256 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2257 SetLastError(0xdeadbeef);
2258 ret = ImmGenerateMessage(imc_null);
2259 ok(ret == 0, "NULL IME should return 0\n");
2260 ret = GetLastError();
2261 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2262 SetLastError(0xdeadbeef);
2263 ret = ImmGenerateMessage(imc_destroy);
2264 ok(ret == 0, "Destroyed IME should return 0\n");
2265 ret = GetLastError();
2266 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2269 #define test_apttype(apttype) _test_apttype(apttype, __LINE__)
2270 static void _test_apttype(APTTYPE apttype, unsigned int line)
2272 APTTYPEQUALIFIER qualifier;
2273 HRESULT hr, hr_expected;
2274 APTTYPE type;
2276 hr = CoGetApartmentType(&type, &qualifier);
2277 hr_expected = (apttype == -1 ? CO_E_NOTINITIALIZED : S_OK);
2278 ok_(__FILE__, line)(hr == hr_expected, "CoGetApartmentType returned %lx\n", hr);
2279 if (FAILED(hr))
2280 return;
2282 ok_(__FILE__, line)(type == apttype, "type %x\n", type);
2283 ok_(__FILE__, line)(!qualifier, "qualifier %x\n", qualifier);
2286 static DWORD WINAPI com_initialization_thread(void *arg)
2288 HRESULT hr;
2289 BOOL r;
2291 test_apttype(-1);
2292 ImmDisableIME(GetCurrentThreadId());
2293 r = ImmSetActiveContext(NULL, NULL, TRUE);
2294 ok(r, "ImmSetActiveContext failed\n");
2295 test_apttype(APTTYPE_MAINSTA);
2296 hr = CoInitialize(NULL);
2297 ok(hr == S_OK, "CoInitialize returned %lx\n", hr);
2298 CoUninitialize();
2299 test_apttype(-1);
2301 /* Changes IMM behavior so it no longer initialized COM */
2302 r = ImmSetActiveContext(NULL, NULL, TRUE);
2303 ok(r, "ImmSetActiveContext failed\n");
2304 test_apttype(APTTYPE_MAINSTA);
2305 hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
2306 ok(hr == S_OK, "CoInitialize returned %lx\n", hr);
2307 test_apttype(APTTYPE_MTA);
2308 CoUninitialize();
2309 test_apttype(-1);
2310 r = ImmSetActiveContext(NULL, NULL, TRUE);
2311 ok(r, "ImmSetActiveContext failed\n");
2312 test_apttype(-1);
2313 return 0;
2316 static void test_com_initialization(void)
2318 APTTYPEQUALIFIER qualifier;
2319 HANDLE thread;
2320 APTTYPE type;
2321 HRESULT hr;
2322 HWND wnd;
2323 BOOL r;
2325 thread = CreateThread(NULL, 0, com_initialization_thread, NULL, 0, NULL);
2326 ok(thread != NULL, "CreateThread failed\n");
2327 WaitForSingleObject(thread, INFINITE);
2328 CloseHandle(thread);
2330 test_apttype(-1);
2331 r = ImmSetActiveContext(NULL, (HIMC)0xdeadbeef, TRUE);
2332 ok(!r, "ImmSetActiveContext succeeded\n");
2333 test_apttype(-1);
2335 r = ImmSetActiveContext(NULL, NULL, TRUE);
2336 ok(r, "ImmSetActiveContext failed\n");
2337 test_apttype(APTTYPE_MAINSTA);
2339 /* Force default IME window destruction */
2340 wnd = CreateWindowA("static", "static", 0, 0, 0, 0, 0, 0, 0, 0, 0);
2341 ok(wnd != NULL, "CreateWindow failed\n");
2342 DestroyWindow(wnd);
2343 test_apttype(-1);
2345 r = ImmSetActiveContext(NULL, NULL, TRUE);
2346 ok(r, "ImmSetActiveContext failed\n");
2347 test_apttype(APTTYPE_MAINSTA);
2348 hr = CoInitialize(NULL);
2349 ok(hr == S_OK, "CoInitialize returned %lx\n", hr);
2350 CoUninitialize();
2351 test_apttype(-1);
2353 /* Test with default IME window created */
2354 wnd = CreateWindowA("static", "static", 0, 0, 0, 0, 0, 0, 0, 0, 0);
2355 ok(wnd != NULL, "CreateWindow failed\n");
2356 test_apttype(-1);
2357 r = ImmSetActiveContext(NULL, NULL, TRUE);
2358 ok(r, "ImmSetActiveContext failed\n");
2359 test_apttype(APTTYPE_MAINSTA);
2360 hr = CoInitialize(NULL);
2361 ok(hr == S_OK, "CoInitialize returned %lx\n", hr);
2362 CoUninitialize();
2363 test_apttype(APTTYPE_MAINSTA);
2364 DestroyWindow(wnd);
2365 test_apttype(-1);
2367 wnd = CreateWindowA("static", "static", 0, 0, 0, 0, 0, 0, 0, 0, 0);
2368 ok(wnd != NULL, "CreateWindow failed\n");
2369 r = ImmSetActiveContext(NULL, NULL, TRUE);
2370 CoUninitialize();
2371 hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
2372 ok(hr == S_OK, "CoInitialize returned %lx\n", hr);
2373 test_apttype(APTTYPE_MTA);
2374 DestroyWindow(wnd);
2376 hr = CoGetApartmentType(&type, &qualifier);
2377 ok(hr == CO_E_NOTINITIALIZED || broken(hr == S_OK) /* w10v22H2 */,
2378 "CoGetApartmentType returned %#lx\n", hr);
2379 test_apttype(hr == S_OK ? APTTYPE_MTA : -1);
2381 wnd = CreateWindowA("static", "static", WS_POPUP, 0, 0, 100, 100, 0, 0, 0, 0);
2382 ok(wnd != NULL, "CreateWindow failed\n");
2383 test_apttype(hr == S_OK ? APTTYPE_MTA : -1);
2384 ShowWindow(wnd, SW_SHOW);
2385 test_apttype(hr == S_OK ? APTTYPE_MTA : APTTYPE_MAINSTA);
2386 DestroyWindow(wnd);
2387 test_apttype(-1);
2390 static DWORD WINAPI disable_ime_thread(void *arg)
2392 HWND h, def;
2393 MSG msg;
2394 BOOL r;
2396 h = CreateWindowA("static", "static", 0, 0, 0, 0, 0, 0, 0, 0, 0);
2397 ok(h != NULL, "CreateWindow failed\n");
2398 def = ImmGetDefaultIMEWnd(h);
2399 ok(def != NULL, "ImmGetDefaultIMEWnd returned NULL\n");
2401 r = ImmDisableIME(arg ? GetCurrentThreadId() : 0);
2402 ok(r, "ImmDisableIME failed\n");
2404 if (arg)
2406 def = ImmGetDefaultIMEWnd(h);
2407 todo_wine ok(def != NULL, "ImmGetDefaultIMEWnd returned NULL\n");
2408 while(PeekMessageA(&msg, 0, 0, 0, PM_REMOVE))
2409 DispatchMessageA(&msg);
2411 def = ImmGetDefaultIMEWnd(h);
2412 ok(!def, "ImmGetDefaultIMEWnd returned %p\n", def);
2413 return 0;
2416 static DWORD WINAPI check_not_disabled_ime_thread(void *arg)
2418 HWND def, hwnd;
2420 WaitForSingleObject(arg, INFINITE);
2421 hwnd = CreateWindowA("static", "static", 0, 0, 0, 0, 0, 0, 0, 0, 0);
2422 ok(hwnd != NULL, "CreateWindow failed\n");
2423 def = ImmGetDefaultIMEWnd(hwnd);
2424 ok(def != NULL, "ImmGetDefaultIMEWnd returned %p\n", def);
2425 return 0;
2428 static DWORD WINAPI disable_ime_process(void *arg)
2430 BOOL r = ImmDisableIME(-1);
2431 ok(r, "ImmDisableIME failed\n");
2432 return 0;
2435 static void test_ImmDisableIME(void)
2437 HANDLE thread, event;
2438 DWORD tid;
2439 HWND def, def2;
2440 BOOL r;
2442 def = ImmGetDefaultIMEWnd(hwnd);
2443 ok(def != NULL, "ImmGetDefaultIMEWnd(hwnd) returned NULL\n");
2445 event = CreateEventW(NULL, TRUE, FALSE, FALSE);
2446 thread = CreateThread(NULL, 0, check_not_disabled_ime_thread, event, 0, &tid);
2447 ok(thread != NULL, "CreateThread failed\n");
2448 r = ImmDisableIME(tid);
2449 ok(!r, "ImmDisableIME(tid) succeeded\n");
2450 SetEvent(event);
2451 WaitForSingleObject(thread, INFINITE);
2452 CloseHandle(thread);
2453 CloseHandle(event);
2455 thread = CreateThread(NULL, 0, disable_ime_thread, 0, 0, NULL);
2456 ok(thread != NULL, "CreateThread failed\n");
2457 WaitForSingleObject(thread, INFINITE);
2458 CloseHandle(thread);
2460 thread = CreateThread(NULL, 0, disable_ime_thread, (void*)1, 0, NULL);
2461 ok(thread != NULL, "CreateThread failed\n");
2462 WaitForSingleObject(thread, INFINITE);
2463 CloseHandle(thread);
2465 msg_spy_pump_msg_queue();
2466 thread = CreateThread(NULL, 0, disable_ime_process, 0, 0, NULL);
2467 ok(thread != NULL, "CreateThread failed\n");
2468 WaitForSingleObject(thread, INFINITE);
2469 CloseHandle(thread);
2471 ok(IsWindow(def), "not a window\n");
2472 def2 = ImmGetDefaultIMEWnd(hwnd);
2473 ok(def2 == def, "ImmGetDefaultIMEWnd(hwnd) returned %p\n", def2);
2474 ok(IsWindow(def), "not a window\n");
2475 msg_spy_pump_msg_queue();
2476 ok(!IsWindow(def), "window is still valid\n");
2477 def = ImmGetDefaultIMEWnd(hwnd);
2478 ok(!def, "ImmGetDefaultIMEWnd(hwnd) returned %p\n", def);
2480 r = ImmDisableIME(-1);
2481 ok(r, "ImmDisableIME(-1) failed\n");
2482 def = ImmGetDefaultIMEWnd(hwnd);
2483 ok(!def, "ImmGetDefaultIMEWnd(hwnd) returned %p\n", def);
2486 #define ime_trace( msg, ... ) if (winetest_debug > 1) trace( "%04lx:%s " msg, GetCurrentThreadId(), __func__, ## __VA_ARGS__ )
2488 static BOOL todo_ImeInquire;
2489 DEFINE_EXPECT( ImeInquire );
2490 static BOOL todo_ImeDestroy;
2491 DEFINE_EXPECT( ImeDestroy );
2492 DEFINE_EXPECT( ImeEscape );
2493 DEFINE_EXPECT( ImeEnumRegisterWord );
2494 DEFINE_EXPECT( ImeRegisterWord );
2495 DEFINE_EXPECT( ImeGetRegisterWordStyle );
2496 DEFINE_EXPECT( ImeUnregisterWord );
2497 static BOOL todo_IME_DLL_PROCESS_ATTACH;
2498 DEFINE_EXPECT( IME_DLL_PROCESS_ATTACH );
2499 static BOOL todo_IME_DLL_PROCESS_DETACH;
2500 DEFINE_EXPECT( IME_DLL_PROCESS_DETACH );
2502 static IMEINFO ime_info;
2503 static UINT ime_count;
2504 static WCHAR ime_path[MAX_PATH];
2505 static HIMC default_himc;
2506 static HKL default_hkl;
2507 static HKL expect_ime = (HKL)(int)0xe020047f;
2509 enum ime_function
2511 IME_SELECT = 1,
2512 IME_NOTIFY,
2513 IME_PROCESS_KEY,
2514 MSG_IME_UI,
2517 struct ime_call
2519 HKL hkl;
2520 HIMC himc;
2521 enum ime_function func;
2523 union
2525 int select;
2526 struct
2528 int action;
2529 int index;
2530 int value;
2531 } notify;
2532 struct
2534 WORD vkey;
2535 LPARAM key_data;
2536 } process_key;
2537 struct
2539 UINT msg;
2540 WPARAM wparam;
2541 LPARAM lparam;
2542 } message;
2545 BOOL todo;
2546 BOOL broken;
2547 BOOL flaky_himc;
2550 struct ime_call empty_sequence[] = {{0}};
2551 static struct ime_call ime_calls[1024];
2552 static ULONG ime_call_count;
2554 #define ok_call( a, b ) ok_call_( __FILE__, __LINE__, a, b )
2555 static int ok_call_( const char *file, int line, const struct ime_call *expected, const struct ime_call *received )
2557 int ret;
2559 if ((ret = expected->func - received->func)) goto done;
2560 /* Wine doesn't allocate HIMC in a deterministic order, ignore them when they are enumerated */
2561 if (expected->flaky_himc && (ret = !!(UINT_PTR)expected->himc - !!(UINT_PTR)received->himc)) goto done;
2562 if (!expected->flaky_himc && (ret = (UINT_PTR)expected->himc - (UINT_PTR)received->himc)) goto done;
2563 if ((ret = (UINT)(UINT_PTR)expected->hkl - (UINT)(UINT_PTR)received->hkl)) goto done;
2564 switch (expected->func)
2566 case IME_SELECT:
2567 if ((ret = expected->select - received->select)) goto done;
2568 break;
2569 case IME_NOTIFY:
2570 if ((ret = expected->notify.action - received->notify.action)) goto done;
2571 if ((ret = expected->notify.index - received->notify.index)) goto done;
2572 if ((ret = expected->notify.value - received->notify.value)) goto done;
2573 break;
2574 case IME_PROCESS_KEY:
2575 if ((ret = expected->process_key.vkey - received->process_key.vkey)) goto done;
2576 if ((ret = expected->process_key.key_data - received->process_key.key_data)) goto done;
2577 break;
2578 case MSG_IME_UI:
2579 if ((ret = expected->message.msg - received->message.msg)) goto done;
2580 if ((ret = (expected->message.wparam - received->message.wparam))) goto done;
2581 if ((ret = (expected->message.lparam - received->message.lparam))) goto done;
2582 break;
2585 done:
2586 if (ret && broken( expected->broken )) return ret;
2588 switch (received->func)
2590 case IME_SELECT:
2591 todo_wine_if( expected->todo )
2592 ok_(file, line)( !ret, "got hkl %p, himc %p, IME_SELECT select %u\n", received->hkl, received->himc, received->select );
2593 return ret;
2594 case IME_NOTIFY:
2595 todo_wine_if( expected->todo )
2596 ok_(file, line)( !ret, "got hkl %p, himc %p, IME_NOTIFY action %#x, index %#x, value %#x\n",
2597 received->hkl, received->himc, received->notify.action, received->notify.index,
2598 received->notify.value );
2599 return ret;
2600 case IME_PROCESS_KEY:
2601 todo_wine_if( expected->todo )
2602 ok_(file, line)( !ret, "got hkl %p, himc %p, IME_PROCESS_KEY vkey %#x, key_data %#Ix\n",
2603 received->hkl, received->himc, received->process_key.vkey, received->process_key.key_data );
2604 return ret;
2605 case MSG_IME_UI:
2606 todo_wine_if( expected->todo )
2607 ok_(file, line)( !ret, "got hkl %p, himc %p, MSG_IME_UI msg %#x, wparam %#Ix, lparam %#Ix\n", received->hkl,
2608 received->himc, received->message.msg, received->message.wparam, received->message.lparam );
2609 return ret;
2612 switch (expected->func)
2614 case IME_SELECT:
2615 todo_wine_if( expected->todo )
2616 ok_(file, line)( !ret, "hkl %p, himc %p, IME_SELECT select %u\n", expected->hkl, expected->himc, expected->select );
2617 break;
2618 case IME_NOTIFY:
2619 todo_wine_if( expected->todo )
2620 ok_(file, line)( !ret, "hkl %p, himc %p, IME_NOTIFY action %#x, index %#x, value %#x\n",
2621 expected->hkl, expected->himc, expected->notify.action, expected->notify.index,
2622 expected->notify.value );
2623 break;
2624 case IME_PROCESS_KEY:
2625 todo_wine_if( expected->todo )
2626 ok_(file, line)( !ret, "hkl %p, himc %p, IME_PROCESS_KEY vkey %#x, key_data %#Ix\n",
2627 expected->hkl, expected->himc, expected->process_key.vkey, expected->process_key.key_data );
2628 break;
2629 case MSG_IME_UI:
2630 todo_wine_if( expected->todo )
2631 ok_(file, line)( !ret, "hkl %p, himc %p, MSG_IME_UI msg %#x, wparam %#Ix, lparam %#Ix\n", expected->hkl,
2632 expected->himc, expected->message.msg, expected->message.wparam, expected->message.lparam );
2633 break;
2636 return 0;
2639 #define ok_seq( a ) ok_seq_( __FILE__, __LINE__, a, #a )
2640 static void ok_seq_( const char *file, int line, const struct ime_call *expected, const char *context )
2642 const struct ime_call *received = ime_calls;
2643 UINT i = 0, ret;
2645 while (expected->func || received->func)
2647 winetest_push_context( "%u%s%s", i++, !expected->func ? " (spurious)" : "",
2648 !received->func ? " (missing)" : "" );
2649 ret = ok_call_( file, line, expected, received );
2650 if (ret && expected->todo && !strcmp( winetest_platform, "wine" ))
2651 expected++;
2652 else if (ret && broken(expected->broken))
2653 expected++;
2654 else
2656 if (expected->func) expected++;
2657 if (received->func) received++;
2659 winetest_pop_context();
2662 memset( ime_calls, 0, sizeof(ime_calls) );
2663 ime_call_count = 0;
2666 static BOOL ignore_message( UINT msg )
2668 switch (msg)
2670 case WM_IME_STARTCOMPOSITION:
2671 case WM_IME_ENDCOMPOSITION:
2672 case WM_IME_COMPOSITION:
2673 case WM_IME_SETCONTEXT:
2674 case WM_IME_NOTIFY:
2675 case WM_IME_CONTROL:
2676 case WM_IME_COMPOSITIONFULL:
2677 case WM_IME_SELECT:
2678 case WM_IME_CHAR:
2679 case 0x287:
2680 case WM_IME_REQUEST:
2681 case WM_IME_KEYDOWN:
2682 case WM_IME_KEYUP:
2683 return FALSE;
2684 default:
2685 return TRUE;
2689 static LRESULT CALLBACK ime_ui_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
2691 struct ime_call call =
2693 .hkl = GetKeyboardLayout( 0 ), .himc = (HIMC)GetWindowLongPtrW( hwnd, IMMGWL_IMC ),
2694 .func = MSG_IME_UI, .message = {.msg = msg, .wparam = wparam, .lparam = lparam}
2696 LONG_PTR ptr;
2698 ime_trace( "hwnd %p, msg %#x, wparam %#Ix, lparam %#Ix\n", hwnd, msg, wparam, lparam );
2700 if (ignore_message( msg )) return DefWindowProcW( hwnd, msg, wparam, lparam );
2702 ptr = GetWindowLongPtrW( hwnd, IMMGWL_PRIVATE );
2703 ok( !ptr, "got IMMGWL_PRIVATE %#Ix\n", ptr );
2705 ime_calls[ime_call_count++] = call;
2706 return DefWindowProcW( hwnd, msg, wparam, lparam );
2709 static WNDCLASSEXW ime_ui_class =
2711 .cbSize = sizeof(WNDCLASSEXW),
2712 .style = CS_IME,
2713 .lpfnWndProc = ime_ui_window_proc,
2714 .cbWndExtra = 2 * sizeof(LONG_PTR),
2715 .lpszClassName = L"WineTestIME",
2718 static BOOL WINAPI ime_ImeConfigure( HKL hkl, HWND hwnd, DWORD mode, void *data )
2720 ime_trace( "hkl %p, hwnd %p, mode %lu, data %p\n", hkl, hwnd, mode, data );
2721 ok( 0, "unexpected call\n" );
2722 return FALSE;
2725 static DWORD WINAPI ime_ImeConversionList( HIMC himc, const WCHAR *source, CANDIDATELIST *dest,
2726 DWORD dest_len, UINT flag )
2728 ime_trace( "himc %p, source %s, dest %p, dest_len %lu, flag %#x\n",
2729 himc, debugstr_w(source), dest, dest_len, flag );
2730 ok( 0, "unexpected call\n" );
2731 return 0;
2734 static BOOL WINAPI ime_ImeDestroy( UINT force )
2736 ime_trace( "force %u\n", force );
2738 todo_wine_if( todo_ImeDestroy )
2739 CHECK_EXPECT( ImeDestroy );
2741 ok( !force, "got force %u\n", force );
2743 return TRUE;
2746 static UINT WINAPI ime_ImeEnumRegisterWord( REGISTERWORDENUMPROCW proc, const WCHAR *reading, DWORD style,
2747 const WCHAR *string, void *data )
2749 ime_trace( "proc %p, reading %s, style %lu, string %s, data %p\n",
2750 proc, debugstr_w(reading), style, debugstr_w(string), data );
2752 ok_eq( default_hkl, GetKeyboardLayout( 0 ), HKL, "%p" );
2753 CHECK_EXPECT( ImeEnumRegisterWord );
2755 if (!style)
2757 ok_eq( 0, reading, const void *, "%p" );
2758 ok_eq( 0, string, const void *, "%p" );
2760 else if (ime_info.fdwProperty & IME_PROP_UNICODE)
2762 ok_eq( 0xdeadbeef, style, UINT, "%#x" );
2763 ok_wcs( L"Reading", reading );
2764 ok_wcs( L"String", string );
2766 else
2768 ok_eq( 0xdeadbeef, style, UINT, "%#x" );
2769 ok_str( "Reading", (char *)reading );
2770 ok_str( "String", (char *)string );
2773 if (style) return proc( reading, style, string, data );
2774 return 0;
2777 static LRESULT WINAPI ime_ImeEscape( HIMC himc, UINT escape, void *data )
2779 ime_trace( "himc %p, escape %#x, data %p\n", himc, escape, data );
2781 ok_eq( default_hkl, GetKeyboardLayout( 0 ), HKL, "%p" );
2782 CHECK_EXPECT( ImeEscape );
2784 switch (escape)
2786 case IME_ESC_SET_EUDC_DICTIONARY:
2787 if (!data) return 4;
2788 if (ime_info.fdwProperty & IME_PROP_UNICODE)
2789 ok_wcs( L"EscapeIme", data );
2790 else
2791 ok_str( "EscapeIme", data );
2792 /* fallthrough */
2793 case IME_ESC_QUERY_SUPPORT:
2794 case IME_ESC_SEQUENCE_TO_INTERNAL:
2795 case IME_ESC_GET_EUDC_DICTIONARY:
2796 case IME_ESC_MAX_KEY:
2797 case IME_ESC_IME_NAME:
2798 case IME_ESC_HANJA_MODE:
2799 case IME_ESC_GETHELPFILENAME:
2800 if (!data) return 4;
2801 if (ime_info.fdwProperty & IME_PROP_UNICODE) wcscpy( data, L"ImeEscape" );
2802 else strcpy( data, "ImeEscape" );
2803 return 4;
2806 ok_eq( 0xdeadbeef, escape, UINT, "%#x" );
2807 ok_eq( NULL, data, void *, "%p" );
2809 return TRUE;
2812 static DWORD WINAPI ime_ImeGetImeMenuItems( HIMC himc, DWORD flags, DWORD type, IMEMENUITEMINFOW *parent,
2813 IMEMENUITEMINFOW *menu, DWORD size )
2815 ime_trace( "himc %p, flags %#lx, type %lu, parent %p, menu %p, size %#lx\n",
2816 himc, flags, type, parent, menu, size );
2817 ok( 0, "unexpected call\n" );
2818 return 0;
2821 static UINT WINAPI ime_ImeGetRegisterWordStyle( UINT item, STYLEBUFW *style )
2823 ime_trace( "item %u, style %p\n", item, style );
2825 ok_eq( default_hkl, GetKeyboardLayout( 0 ), HKL, "%p" );
2826 CHECK_EXPECT( ImeGetRegisterWordStyle );
2828 if (!style)
2829 ok_eq( 16, item, UINT, "%u" );
2830 else if (ime_info.fdwProperty & IME_PROP_UNICODE)
2832 STYLEBUFW *styleW = style;
2833 styleW->dwStyle = 0xdeadbeef;
2834 wcscpy( styleW->szDescription, L"StyleDescription" );
2836 else
2838 STYLEBUFA *styleA = (STYLEBUFA *)style;
2839 styleA->dwStyle = 0xdeadbeef;
2840 strcpy( styleA->szDescription, "StyleDescription" );
2843 return 0xdeadbeef;
2846 static BOOL WINAPI ime_ImeInquire( IMEINFO *info, WCHAR *ui_class, DWORD flags )
2848 ime_trace( "info %p, ui_class %p, flags %#lx\n", info, ui_class, flags );
2850 todo_wine_if( todo_ImeInquire )
2851 CHECK_EXPECT( ImeInquire );
2853 ok( !!info, "got info %p\n", info );
2854 ok( !!ui_class, "got ui_class %p\n", ui_class );
2855 ok( !flags, "got flags %#lx\n", flags );
2857 *info = ime_info;
2859 if (ime_info.fdwProperty & IME_PROP_UNICODE)
2860 wcscpy( ui_class, ime_ui_class.lpszClassName );
2861 else
2862 WideCharToMultiByte( CP_ACP, 0, ime_ui_class.lpszClassName, -1,
2863 (char *)ui_class, 17, NULL, NULL );
2865 return TRUE;
2868 static BOOL WINAPI ime_ImeProcessKey( HIMC himc, UINT vkey, LPARAM key_data, BYTE *key_state )
2870 struct ime_call call =
2872 .hkl = GetKeyboardLayout( 0 ), .himc = himc,
2873 .func = IME_PROCESS_KEY, .process_key = {.vkey = vkey, .key_data = key_data}
2875 ime_trace( "himc %p, vkey %u, key_data %#Ix, key_state %p\n",
2876 himc, vkey, key_data, key_state );
2877 ime_calls[ime_call_count++] = call;
2878 return TRUE;
2881 static BOOL WINAPI ime_ImeRegisterWord( const WCHAR *reading, DWORD style, const WCHAR *string )
2883 ime_trace( "reading %s, style %lu, string %s\n", debugstr_w(reading), style, debugstr_w(string) );
2885 ok_eq( default_hkl, GetKeyboardLayout( 0 ), HKL, "%p" );
2886 CHECK_EXPECT( ImeRegisterWord );
2888 if (style) ok_eq( 0xdeadbeef, style, UINT, "%#x" );
2889 if (ime_info.fdwProperty & IME_PROP_UNICODE)
2891 if (reading) ok_wcs( L"Reading", reading );
2892 if (string) ok_wcs( L"String", string );
2894 else
2896 if (reading) ok_str( "Reading", (char *)reading );
2897 if (string) ok_str( "String", (char *)string );
2900 return FALSE;
2903 static BOOL WINAPI ime_ImeSelect( HIMC himc, BOOL select )
2905 struct ime_call call =
2907 .hkl = GetKeyboardLayout( 0 ), .himc = himc,
2908 .func = IME_SELECT, .select = select
2910 ime_trace( "himc %p, select %d\n", himc, select );
2911 ime_calls[ime_call_count++] = call;
2912 return TRUE;
2915 static BOOL WINAPI ime_ImeSetActiveContext( HIMC himc, BOOL flag )
2917 ime_trace( "himc %p, flag %#x\n", himc, flag );
2918 ok( 0, "unexpected call\n" );
2919 return FALSE;
2922 static BOOL WINAPI ime_ImeSetCompositionString( HIMC himc, DWORD index, const void *comp, DWORD comp_len,
2923 const void *read, DWORD read_len )
2925 ime_trace( "himc %p, index %lu, comp %p, comp_len %lu, read %p, read_len %lu\n",
2926 himc, index, comp, comp_len, read, read_len );
2927 ok( 0, "unexpected call\n" );
2928 return FALSE;
2931 static UINT WINAPI ime_ImeToAsciiEx( UINT vkey, UINT scan_code, BYTE *key_state, TRANSMSGLIST *msgs, UINT state, HIMC himc )
2933 ime_trace( "vkey %u, scan_code %u, key_state %p, msgs %p, state %u, himc %p\n",
2934 vkey, scan_code, key_state, msgs, state, himc );
2935 return 0;
2938 static BOOL WINAPI ime_ImeUnregisterWord( const WCHAR *reading, DWORD style, const WCHAR *string )
2940 ime_trace( "reading %s, style %lu, string %s\n", debugstr_w(reading), style, debugstr_w(string) );
2942 ok_eq( default_hkl, GetKeyboardLayout( 0 ), HKL, "%p" );
2943 CHECK_EXPECT( ImeUnregisterWord );
2945 if (style) ok_eq( 0xdeadbeef, style, UINT, "%#x" );
2946 if (ime_info.fdwProperty & IME_PROP_UNICODE)
2948 if (reading) ok_wcs( L"Reading", reading );
2949 if (string) ok_wcs( L"String", string );
2951 else
2953 if (reading) ok_str( "Reading", (char *)reading );
2954 if (string) ok_str( "String", (char *)string );
2957 return FALSE;
2960 static BOOL WINAPI ime_NotifyIME( HIMC himc, DWORD action, DWORD index, DWORD value )
2962 struct ime_call call =
2964 .hkl = GetKeyboardLayout( 0 ), .himc = himc,
2965 .func = IME_NOTIFY, .notify = {.action = action, .index = index, .value = value}
2967 ime_trace( "himc %p, action %#lx, index %lu, value %lu\n", himc, action, index, value );
2968 ime_calls[ime_call_count++] = call;
2969 return FALSE;
2972 static BOOL WINAPI ime_DllMain( HINSTANCE instance, DWORD reason, LPVOID reserved )
2974 ime_trace( "instance %p, reason %lu, reserved %p.\n", instance, reason, reserved );
2976 switch (reason)
2978 case DLL_PROCESS_ATTACH:
2979 DisableThreadLibraryCalls( instance );
2980 ime_ui_class.hInstance = instance;
2981 RegisterClassExW( &ime_ui_class );
2982 todo_wine_if(todo_IME_DLL_PROCESS_ATTACH)
2983 CHECK_EXPECT( IME_DLL_PROCESS_ATTACH );
2984 break;
2986 case DLL_PROCESS_DETACH:
2987 UnregisterClassW( ime_ui_class.lpszClassName, instance );
2988 todo_wine_if(todo_IME_DLL_PROCESS_DETACH)
2989 CHECK_EXPECT( IME_DLL_PROCESS_DETACH );
2990 break;
2993 return TRUE;
2996 static struct ime_functions ime_functions =
2998 ime_ImeConfigure,
2999 ime_ImeConversionList,
3000 ime_ImeDestroy,
3001 ime_ImeEnumRegisterWord,
3002 ime_ImeEscape,
3003 ime_ImeGetImeMenuItems,
3004 ime_ImeGetRegisterWordStyle,
3005 ime_ImeInquire,
3006 ime_ImeProcessKey,
3007 ime_ImeRegisterWord,
3008 ime_ImeSelect,
3009 ime_ImeSetActiveContext,
3010 ime_ImeSetCompositionString,
3011 ime_ImeToAsciiEx,
3012 ime_ImeUnregisterWord,
3013 ime_NotifyIME,
3014 ime_DllMain,
3017 static HKL ime_install(void)
3019 WCHAR buffer[MAX_PATH];
3020 HMODULE module;
3021 DWORD len, ret;
3022 HKEY hkey;
3023 HKL hkl;
3025 /* IME module gets cached and won't reload from disk as soon as a window has
3026 * loaded it. To workaround the issue we load the module first as a DLL,
3027 * set its function pointers from the test, and later when the cached IME
3028 * gets loaded, read the function pointers from the separately loaded DLL.
3031 load_resource( L"ime_wrapper.dll", buffer );
3033 SetLastError( 0xdeadbeef );
3034 ret = MoveFileW( buffer, L"c:\\windows\\system32\\winetest_ime.dll" );
3035 if (!ret)
3037 ok( GetLastError() == ERROR_ACCESS_DENIED, "got error %lu\n", GetLastError() );
3038 win_skip( "Failed to copy DLL to system directory\n" );
3039 return 0;
3042 module = LoadLibraryW( L"c:\\windows\\system32\\winetest_ime.dll" );
3043 ok( !!module, "LoadLibraryW failed, error %lu\n", GetLastError() );
3044 *(struct ime_functions *)GetProcAddress( module, "ime_functions" ) = ime_functions;
3046 /* install the actual IME module, it will lookup the functions from the DLL */
3047 load_resource( L"ime_wrapper.dll", buffer );
3049 SetLastError( 0xdeadbeef );
3050 swprintf( ime_path, ARRAY_SIZE(ime_path), L"c:\\windows\\system32\\wine%04x.ime", ime_count++ );
3051 ret = MoveFileW( buffer, ime_path );
3052 todo_wine_if( GetLastError() == ERROR_ALREADY_EXISTS )
3053 ok( ret || broken( !ret ) /* sometimes still in use */,
3054 "MoveFileW failed, error %lu\n", GetLastError() );
3056 hkl = ImmInstallIMEW( ime_path, L"WineTest IME" );
3057 ok( hkl == expect_ime, "ImmInstallIMEW returned %p, error %lu\n", hkl, GetLastError() );
3059 swprintf( buffer, ARRAY_SIZE(buffer), L"System\\CurrentControlSet\\Control\\Keyboard Layouts\\%08x", hkl );
3060 ret = RegOpenKeyW( HKEY_LOCAL_MACHINE, buffer, &hkey );
3061 ok( !ret, "RegOpenKeyW returned %#lx, error %lu\n", ret, GetLastError() );
3063 len = sizeof(buffer);
3064 memset( buffer, 0xcd, sizeof(buffer) );
3065 ret = RegQueryValueExW( hkey, L"Ime File", NULL, NULL, (BYTE *)buffer, &len );
3066 ok( !ret, "RegQueryValueExW returned %#lx, error %lu\n", ret, GetLastError() );
3067 ok( !wcsicmp( buffer, wcsrchr( ime_path, '\\' ) + 1 ), "got Ime File %s\n", debugstr_w(buffer) );
3069 len = sizeof(buffer);
3070 memset( buffer, 0xcd, sizeof(buffer) );
3071 ret = RegQueryValueExW( hkey, L"Layout Text", NULL, NULL, (BYTE *)buffer, &len );
3072 ok( !ret, "RegQueryValueExW returned %#lx, error %lu\n", ret, GetLastError() );
3073 ok( !wcscmp( buffer, L"WineTest IME" ), "got Layout Text %s\n", debugstr_w(buffer) );
3075 len = sizeof(buffer);
3076 memset( buffer, 0, sizeof(buffer) );
3077 ret = RegQueryValueExW( hkey, L"Layout File", NULL, NULL, (BYTE *)buffer, &len );
3078 todo_wine
3079 ok( !ret, "RegQueryValueExW returned %#lx, error %lu\n", ret, GetLastError() );
3080 todo_wine
3081 ok( !wcscmp( buffer, L"kbdus.dll" ), "got Layout File %s\n", debugstr_w(buffer) );
3083 ret = RegCloseKey( hkey );
3084 ok( !ret, "RegCloseKey returned %#lx, error %lu\n", ret, GetLastError() );
3086 return hkl;
3089 static void ime_cleanup( HKL hkl, BOOL free )
3091 HMODULE module = GetModuleHandleW( L"winetest_ime.dll" );
3092 WCHAR buffer[MAX_PATH], value[MAX_PATH];
3093 DWORD i, buffer_len, value_len, ret;
3094 HKEY hkey;
3096 ret = UnloadKeyboardLayout( hkl );
3097 todo_wine
3098 ok( ret, "UnloadKeyboardLayout failed, error %lu\n", GetLastError() );
3100 if (free) ok_ret( 1, ImmFreeLayout( hkl ) );
3102 swprintf( buffer, ARRAY_SIZE(buffer), L"System\\CurrentControlSet\\Control\\Keyboard Layouts\\%08x", hkl );
3103 ret = RegDeleteKeyW( HKEY_LOCAL_MACHINE, buffer );
3104 ok( !ret, "RegDeleteKeyW returned %#lx, error %lu\n", ret, GetLastError() );
3106 ret = RegOpenKeyW( HKEY_CURRENT_USER, L"Keyboard Layout\\Preload", &hkey );
3107 ok( !ret, "RegOpenKeyW returned %#lx, error %lu\n", ret, GetLastError() );
3109 value_len = ARRAY_SIZE(value);
3110 buffer_len = sizeof(buffer);
3111 for (i = 0; !RegEnumValueW( hkey, i, value, &value_len, NULL, NULL, (void *)buffer, &buffer_len ); i++)
3113 value_len = ARRAY_SIZE(value);
3114 buffer_len = sizeof(buffer);
3115 if (hkl != UlongToHandle( wcstoul( buffer, NULL, 16 ) )) continue;
3116 ret = RegDeleteValueW( hkey, value );
3117 ok( !ret, "RegDeleteValueW returned %#lx, error %lu\n", ret, GetLastError() );
3120 ret = RegCloseKey( hkey );
3121 ok( !ret, "RegCloseKey returned %#lx, error %lu\n", ret, GetLastError() );
3123 ret = DeleteFileW( ime_path );
3124 todo_wine_if( GetLastError() == ERROR_ACCESS_DENIED )
3125 ok( ret || broken( !ret ) /* sometimes still in use */,
3126 "DeleteFileW failed, error %lu\n", GetLastError() );
3128 ret = FreeLibrary( module );
3129 ok( ret, "FreeLibrary failed, error %lu\n", GetLastError() );
3131 ret = DeleteFileW( L"c:\\windows\\system32\\winetest_ime.dll" );
3132 ok( ret, "DeleteFileW failed, error %lu\n", GetLastError() );
3135 static BOOL CALLBACK enum_get_context( HIMC himc, LPARAM lparam )
3137 ime_trace( "himc %p\n", himc );
3138 *(HIMC *)lparam = himc;
3139 return TRUE;
3142 static BOOL CALLBACK enum_find_context( HIMC himc, LPARAM lparam )
3144 ime_trace( "himc %p\n", himc );
3145 if (lparam && lparam == (LPARAM)himc) return FALSE;
3146 return TRUE;
3149 static void test_ImmEnumInputContext(void)
3151 HIMC himc;
3153 ok_ret( 1, ImmEnumInputContext( 0, enum_get_context, (LPARAM)&default_himc ) );
3154 ok_ret( 1, ImmEnumInputContext( -1, enum_find_context, 0 ) );
3155 ok_ret( 1, ImmEnumInputContext( GetCurrentThreadId(), enum_find_context, 0 ) );
3157 todo_wine
3158 ok_ret( 0, ImmEnumInputContext( 1, enum_find_context, 0 ) );
3159 todo_wine
3160 ok_ret( 0, ImmEnumInputContext( GetCurrentProcessId(), enum_find_context, 0 ) );
3162 himc = ImmCreateContext();
3163 ok_ne( NULL, himc, HIMC, "%p" );
3164 ok_ret( 0, ImmEnumInputContext( GetCurrentThreadId(), enum_find_context, (LPARAM)himc ) );
3165 ok_ret( 1, ImmDestroyContext( himc ) );
3166 ok_ret( 1, ImmEnumInputContext( GetCurrentThreadId(), enum_find_context, (LPARAM)himc ) );
3169 static void test_ImmInstallIME(void)
3171 UINT ret;
3172 HKL hkl;
3174 SET_ENABLE( IME_DLL_PROCESS_ATTACH, TRUE );
3175 SET_ENABLE( ImeInquire, TRUE );
3176 SET_ENABLE( ImeDestroy, TRUE );
3177 SET_ENABLE( IME_DLL_PROCESS_DETACH, TRUE );
3179 /* IME_PROP_END_UNLOAD for the IME to unload / reload. */
3180 ime_info.fdwProperty = IME_PROP_END_UNLOAD;
3182 if (!(hkl = ime_install())) goto cleanup;
3184 SET_EXPECT( IME_DLL_PROCESS_ATTACH );
3185 SET_EXPECT( ImeInquire );
3186 ret = ImmLoadIME( hkl );
3187 ok( ret, "ImmLoadIME returned %#x\n", ret );
3188 CHECK_CALLED( IME_DLL_PROCESS_ATTACH );
3189 CHECK_CALLED( ImeInquire );
3191 ret = ImmLoadIME( hkl );
3192 ok( ret, "ImmLoadIME returned %#x\n", ret );
3194 SET_EXPECT( ImeDestroy );
3195 SET_EXPECT( IME_DLL_PROCESS_DETACH );
3196 ret = ImmFreeLayout( hkl );
3197 ok( ret, "ImmFreeLayout returned %#x\n", ret );
3198 CHECK_CALLED( ImeDestroy );
3199 CHECK_CALLED( IME_DLL_PROCESS_DETACH );
3201 ret = ImmFreeLayout( hkl );
3202 ok( ret, "ImmFreeLayout returned %#x\n", ret );
3204 ime_cleanup( hkl, FALSE );
3206 ime_info.fdwProperty = 0;
3208 if (!(hkl = ime_install())) goto cleanup;
3210 SET_EXPECT( IME_DLL_PROCESS_ATTACH );
3211 SET_EXPECT( ImeInquire );
3212 ret = ImmLoadIME( hkl );
3213 ok( ret, "ImmLoadIME returned %#x\n", ret );
3214 CHECK_CALLED( IME_DLL_PROCESS_ATTACH );
3215 CHECK_CALLED( ImeInquire );
3217 ret = ImmLoadIME( hkl );
3218 ok( ret, "ImmLoadIME returned %#x\n", ret );
3220 SET_EXPECT( ImeDestroy );
3221 SET_EXPECT( IME_DLL_PROCESS_DETACH );
3222 ret = ImmFreeLayout( hkl );
3223 ok( ret, "ImmFreeLayout returned %#x\n", ret );
3224 CHECK_CALLED( ImeDestroy );
3225 CHECK_CALLED( IME_DLL_PROCESS_DETACH );
3227 ret = ImmFreeLayout( hkl );
3228 ok( ret, "ImmFreeLayout returned %#x\n", ret );
3230 ime_cleanup( hkl, FALSE );
3232 cleanup:
3233 SET_ENABLE( IME_DLL_PROCESS_ATTACH, FALSE );
3234 SET_ENABLE( ImeInquire, FALSE );
3235 SET_ENABLE( ImeDestroy, FALSE );
3236 SET_ENABLE( IME_DLL_PROCESS_DETACH, FALSE );
3239 static void test_ImmIsIME(void)
3241 HKL hkl = GetKeyboardLayout( 0 );
3243 SET_ENABLE( IME_DLL_PROCESS_ATTACH, TRUE );
3244 SET_ENABLE( ImeInquire, TRUE );
3245 SET_ENABLE( ImeDestroy, TRUE );
3246 SET_ENABLE( IME_DLL_PROCESS_DETACH, TRUE );
3248 SetLastError( 0xdeadbeef );
3249 todo_wine
3250 ok_ret( 0, ImmIsIME( 0 ) );
3251 ok_ret( 0xdeadbeef, GetLastError() );
3252 ok_ret( 1, ImmIsIME( hkl ) );
3254 /* IME_PROP_END_UNLOAD for the IME to unload / reload. */
3255 ime_info.fdwProperty = IME_PROP_END_UNLOAD;
3257 if (!(hkl = ime_install())) goto cleanup;
3259 todo_ImeInquire = TRUE;
3260 todo_ImeDestroy = TRUE;
3261 todo_IME_DLL_PROCESS_ATTACH = TRUE;
3262 todo_IME_DLL_PROCESS_DETACH = TRUE;
3263 ok_ret( 1, ImmIsIME( hkl ) );
3264 todo_IME_DLL_PROCESS_ATTACH = FALSE;
3265 todo_IME_DLL_PROCESS_DETACH = FALSE;
3266 todo_ImeInquire = FALSE;
3267 todo_ImeDestroy = FALSE;
3269 ime_cleanup( hkl, FALSE );
3271 cleanup:
3272 SET_ENABLE( IME_DLL_PROCESS_ATTACH, FALSE );
3273 SET_ENABLE( ImeInquire, FALSE );
3274 SET_ENABLE( ImeDestroy, FALSE );
3275 SET_ENABLE( IME_DLL_PROCESS_DETACH, FALSE );
3278 static void test_ImmGetProperty(void)
3280 static const IMEINFO expect_ime_info =
3282 .fdwProperty = IME_PROP_UNICODE | IME_PROP_AT_CARET,
3284 static const IMEINFO expect_ime_info_0411 = /* MS Japanese IME */
3286 .fdwProperty = IME_PROP_CANDLIST_START_FROM_1 | IME_PROP_UNICODE | IME_PROP_AT_CARET | 0xa,
3287 .fdwConversionCaps = IME_CMODE_NATIVE | IME_CMODE_FULLSHAPE | IME_CMODE_KATAKANA,
3288 .fdwSentenceCaps = IME_SMODE_PLAURALCLAUSE | IME_SMODE_CONVERSATION,
3289 .fdwSCSCaps = SCS_CAP_COMPSTR | SCS_CAP_SETRECONVERTSTRING | SCS_CAP_MAKEREAD,
3290 .fdwSelectCaps = SELECT_CAP_CONVERSION | SELECT_CAP_SENTENCE,
3291 .fdwUICaps = UI_CAP_ROT90,
3293 static const IMEINFO expect_ime_info_0412 = /* MS Korean IME */
3295 .fdwProperty = IME_PROP_CANDLIST_START_FROM_1 | IME_PROP_UNICODE | IME_PROP_AT_CARET | 0xa,
3296 .fdwConversionCaps = IME_CMODE_NATIVE | IME_CMODE_FULLSHAPE,
3297 .fdwSentenceCaps = IME_SMODE_NONE,
3298 .fdwSCSCaps = SCS_CAP_COMPSTR | SCS_CAP_SETRECONVERTSTRING,
3299 .fdwSelectCaps = SELECT_CAP_CONVERSION,
3300 .fdwUICaps = UI_CAP_ROT90,
3302 static const IMEINFO expect_ime_info_0804 = /* MS Chinese IME */
3304 .fdwProperty = IME_PROP_CANDLIST_START_FROM_1 | IME_PROP_UNICODE | IME_PROP_AT_CARET | 0xa,
3305 .fdwConversionCaps = IME_CMODE_NATIVE | IME_CMODE_FULLSHAPE,
3306 .fdwSentenceCaps = IME_SMODE_PLAURALCLAUSE,
3307 .fdwSCSCaps = SCS_CAP_COMPSTR | SCS_CAP_SETRECONVERTSTRING | SCS_CAP_MAKEREAD,
3308 .fdwUICaps = UI_CAP_ROT90,
3310 HKL hkl = GetKeyboardLayout( 0 );
3311 const IMEINFO *expect;
3313 SET_ENABLE( ImeInquire, TRUE );
3314 SET_ENABLE( ImeDestroy, TRUE );
3316 SetLastError( 0xdeadbeef );
3317 ok_ret( 0, ImmGetProperty( 0, 0 ) );
3318 ok_ret( 0, ImmGetProperty( hkl, 0 ) );
3320 if (hkl == (HKL)0x04110411) expect = &expect_ime_info_0411;
3321 else if (hkl == (HKL)0x04120412) expect = &expect_ime_info_0412;
3322 else if (hkl == (HKL)0x08040804) expect = &expect_ime_info_0804;
3323 else expect = &expect_ime_info;
3325 /* IME_PROP_COMPLETE_ON_UNSELECT seems to be somtimes set on CJK locales IMEs, sometimes not */
3326 ok_ret( expect->fdwProperty, ImmGetProperty( hkl, IGP_PROPERTY ) & ~IME_PROP_COMPLETE_ON_UNSELECT );
3327 todo_wine
3328 ok_ret( expect->fdwConversionCaps, ImmGetProperty( hkl, IGP_CONVERSION ) );
3329 todo_wine
3330 ok_ret( expect->fdwSentenceCaps, ImmGetProperty( hkl, IGP_SENTENCE ) );
3331 ok_ret( expect->fdwSCSCaps, ImmGetProperty( hkl, IGP_SETCOMPSTR ) );
3332 todo_wine
3333 ok_ret( expect->fdwSelectCaps, ImmGetProperty( hkl, IGP_SELECT ) );
3334 ok_ret( IMEVER_0400, ImmGetProperty( hkl, IGP_GETIMEVERSION ) );
3335 ok_ret( expect->fdwUICaps, ImmGetProperty( hkl, IGP_UI ) );
3336 ok_ret( 0xdeadbeef, GetLastError() );
3338 /* IME_PROP_END_UNLOAD for the IME to unload / reload. */
3339 ime_info.fdwProperty = IME_PROP_END_UNLOAD;
3341 if (!(hkl = ime_install())) goto cleanup;
3343 SET_EXPECT( ImeInquire );
3344 SET_EXPECT( ImeDestroy );
3345 ok_ret( 0, ImmGetProperty( hkl, 0 ) );
3346 CHECK_CALLED( ImeInquire );
3347 CHECK_CALLED( ImeDestroy );
3349 expect = &ime_info;
3350 todo_ImeInquire = TRUE;
3351 todo_ImeDestroy = TRUE;
3352 ok_ret( expect->fdwProperty, ImmGetProperty( hkl, IGP_PROPERTY ) );
3353 ok_ret( expect->fdwConversionCaps, ImmGetProperty( hkl, IGP_CONVERSION ) );
3354 ok_ret( expect->fdwSentenceCaps, ImmGetProperty( hkl, IGP_SENTENCE ) );
3355 ok_ret( expect->fdwSCSCaps, ImmGetProperty( hkl, IGP_SETCOMPSTR ) );
3356 ok_ret( expect->fdwSelectCaps, ImmGetProperty( hkl, IGP_SELECT ) );
3357 ok_ret( IMEVER_0400, ImmGetProperty( hkl, IGP_GETIMEVERSION ) );
3358 ok_ret( expect->fdwUICaps, ImmGetProperty( hkl, IGP_UI ) );
3359 todo_ImeInquire = FALSE;
3360 called_ImeInquire = FALSE;
3361 todo_ImeDestroy = FALSE;
3362 called_ImeDestroy = FALSE;
3364 ime_cleanup( hkl, FALSE );
3366 cleanup:
3367 SET_ENABLE( ImeInquire, FALSE );
3368 SET_ENABLE( ImeDestroy, FALSE );
3371 static void test_ImmGetDescription(void)
3373 HKL hkl = GetKeyboardLayout( 0 );
3374 WCHAR bufferW[MAX_PATH];
3375 char bufferA[MAX_PATH];
3376 DWORD ret;
3378 SET_ENABLE( IME_DLL_PROCESS_ATTACH, TRUE );
3379 SET_ENABLE( ImeInquire, TRUE );
3380 SET_ENABLE( ImeDestroy, TRUE );
3381 SET_ENABLE( IME_DLL_PROCESS_DETACH, TRUE );
3383 SetLastError( 0xdeadbeef );
3384 ret = ImmGetDescriptionW( NULL, NULL, 0 );
3385 ok( !ret, "ImmGetDescriptionW returned %lu\n", ret );
3386 ret = ImmGetDescriptionA( NULL, NULL, 0 );
3387 ok( !ret, "ImmGetDescriptionA returned %lu\n", ret );
3388 ret = ImmGetDescriptionW( NULL, NULL, 100 );
3389 ok( !ret, "ImmGetDescriptionW returned %lu\n", ret );
3390 ret = ImmGetDescriptionA( NULL, NULL, 100 );
3391 ok( !ret, "ImmGetDescriptionA returned %lu\n", ret );
3392 ret = ImmGetDescriptionW( hkl, bufferW, 100 );
3393 ok( !ret, "ImmGetDescriptionW returned %lu\n", ret );
3394 ret = ImmGetDescriptionA( hkl, bufferA, 100 );
3395 ok( !ret, "ImmGetDescriptionA returned %lu\n", ret );
3396 ret = GetLastError();
3397 ok( ret == 0xdeadbeef, "got error %lu\n", ret );
3399 if (!(hkl = ime_install())) goto cleanup;
3401 memset( bufferW, 0xcd, sizeof(bufferW) );
3402 ret = ImmGetDescriptionW( hkl, bufferW, 2 );
3403 ok( ret == 1, "ImmGetDescriptionW returned %lu\n", ret );
3404 ok( !wcscmp( bufferW, L"W" ), "got bufferW %s\n", debugstr_w(bufferW) );
3405 memset( bufferA, 0xcd, sizeof(bufferA) );
3406 ret = ImmGetDescriptionA( hkl, bufferA, 2 );
3407 ok( ret == 0, "ImmGetDescriptionA returned %lu\n", ret );
3408 ok( !strcmp( bufferA, "" ), "got bufferA %s\n", debugstr_a(bufferA) );
3410 memset( bufferW, 0xcd, sizeof(bufferW) );
3411 ret = ImmGetDescriptionW( hkl, bufferW, 11 );
3412 ok( ret == 10, "ImmGetDescriptionW returned %lu\n", ret );
3413 ok( !wcscmp( bufferW, L"WineTest I" ), "got bufferW %s\n", debugstr_w(bufferW) );
3414 memset( bufferA, 0xcd, sizeof(bufferA) );
3415 ret = ImmGetDescriptionA( hkl, bufferA, 11 );
3416 ok( ret == 0, "ImmGetDescriptionA returned %lu\n", ret );
3417 ok( !strcmp( bufferA, "" ), "got bufferA %s\n", debugstr_a(bufferA) );
3419 memset( bufferW, 0xcd, sizeof(bufferW) );
3420 ret = ImmGetDescriptionW( hkl, bufferW, 12 );
3421 ok( ret == 11, "ImmGetDescriptionW returned %lu\n", ret );
3422 ok( !wcscmp( bufferW, L"WineTest IM" ), "got bufferW %s\n", debugstr_w(bufferW) );
3423 memset( bufferA, 0xcd, sizeof(bufferA) );
3424 ret = ImmGetDescriptionA( hkl, bufferA, 12 );
3425 ok( ret == 12, "ImmGetDescriptionA returned %lu\n", ret );
3426 ok( !strcmp( bufferA, "WineTest IME" ), "got bufferA %s\n", debugstr_a(bufferA) );
3428 memset( bufferW, 0xcd, sizeof(bufferW) );
3429 ret = ImmGetDescriptionW( hkl, bufferW, 13 );
3430 ok( ret == 12, "ImmGetDescriptionW returned %lu\n", ret );
3431 ok( !wcscmp( bufferW, L"WineTest IME" ), "got bufferW %s\n", debugstr_w(bufferW) );
3432 memset( bufferA, 0xcd, sizeof(bufferA) );
3433 ret = ImmGetDescriptionA( hkl, bufferA, 13 );
3434 ok( ret == 12, "ImmGetDescriptionA returned %lu\n", ret );
3435 ok( !strcmp( bufferA, "WineTest IME" ), "got bufferA %s\n", debugstr_a(bufferA) );
3437 ime_cleanup( hkl, FALSE );
3439 cleanup:
3440 SET_ENABLE( IME_DLL_PROCESS_ATTACH, FALSE );
3441 SET_ENABLE( ImeInquire, FALSE );
3442 SET_ENABLE( ImeDestroy, FALSE );
3443 SET_ENABLE( IME_DLL_PROCESS_DETACH, FALSE );
3446 static void test_ImmGetIMEFileName(void)
3448 HKL hkl = GetKeyboardLayout( 0 );
3449 WCHAR bufferW[MAX_PATH], expectW[16];
3450 char bufferA[MAX_PATH], expectA[16];
3451 DWORD ret;
3453 SET_ENABLE( IME_DLL_PROCESS_ATTACH, TRUE );
3454 SET_ENABLE( ImeInquire, TRUE );
3455 SET_ENABLE( ImeDestroy, TRUE );
3456 SET_ENABLE( IME_DLL_PROCESS_DETACH, TRUE );
3458 SetLastError( 0xdeadbeef );
3459 ret = ImmGetIMEFileNameW( NULL, NULL, 0 );
3460 ok( !ret, "ImmGetIMEFileNameW returned %lu\n", ret );
3461 ret = ImmGetIMEFileNameA( NULL, NULL, 0 );
3462 ok( !ret, "ImmGetIMEFileNameA returned %lu\n", ret );
3463 ret = ImmGetIMEFileNameW( NULL, NULL, 100 );
3464 ok( !ret, "ImmGetIMEFileNameW returned %lu\n", ret );
3465 ret = ImmGetIMEFileNameA( NULL, NULL, 100 );
3466 ok( !ret, "ImmGetIMEFileNameA returned %lu\n", ret );
3467 ret = ImmGetIMEFileNameW( hkl, bufferW, 100 );
3468 ok( !ret, "ImmGetIMEFileNameW returned %lu\n", ret );
3469 ret = ImmGetIMEFileNameA( hkl, bufferA, 100 );
3470 ok( !ret, "ImmGetIMEFileNameA returned %lu\n", ret );
3471 ret = GetLastError();
3472 ok( ret == 0xdeadbeef, "got error %lu\n", ret );
3474 if (!(hkl = ime_install())) goto cleanup;
3476 memset( bufferW, 0xcd, sizeof(bufferW) );
3477 ret = ImmGetIMEFileNameW( hkl, bufferW, 2 );
3478 ok( ret == 1, "ImmGetIMEFileNameW returned %lu\n", ret );
3479 ok( !wcscmp( bufferW, L"W" ), "got bufferW %s\n", debugstr_w(bufferW) );
3480 memset( bufferA, 0xcd, sizeof(bufferA) );
3481 ret = ImmGetIMEFileNameA( hkl, bufferA, 2 );
3482 ok( ret == 0, "ImmGetIMEFileNameA returned %lu\n", ret );
3483 ok( !strcmp( bufferA, "" ), "got bufferA %s\n", debugstr_a(bufferA) );
3485 swprintf( expectW, ARRAY_SIZE(expectW), L"WINE%04X.I", ime_count - 1 );
3486 memset( bufferW, 0xcd, sizeof(bufferW) );
3487 ret = ImmGetIMEFileNameW( hkl, bufferW, 11 );
3488 ok( ret == 10, "ImmGetIMEFileNameW returned %lu\n", ret );
3489 ok( !wcscmp( bufferW, expectW ), "got bufferW %s\n", debugstr_w(bufferW) );
3490 memset( bufferA, 0xcd, sizeof(bufferA) );
3491 ret = ImmGetIMEFileNameA( hkl, bufferA, 11 );
3492 ok( ret == 0, "ImmGetIMEFileNameA returned %lu\n", ret );
3493 ok( !strcmp( bufferA, "" ), "got bufferA %s\n", debugstr_a(bufferA) );
3495 swprintf( expectW, ARRAY_SIZE(expectW), L"WINE%04X.IM", ime_count - 1 );
3496 memset( bufferW, 0xcd, sizeof(bufferW) );
3497 ret = ImmGetIMEFileNameW( hkl, bufferW, 12 );
3498 ok( ret == 11, "ImmGetIMEFileNameW returned %lu\n", ret );
3499 ok( !wcscmp( bufferW, expectW ), "got bufferW %s\n", debugstr_w(bufferW) );
3500 snprintf( expectA, ARRAY_SIZE(expectA), "WINE%04X.IME", ime_count - 1 );
3501 memset( bufferA, 0xcd, sizeof(bufferA) );
3502 ret = ImmGetIMEFileNameA( hkl, bufferA, 12 );
3503 ok( ret == 12, "ImmGetIMEFileNameA returned %lu\n", ret );
3504 ok( !strcmp( bufferA, expectA ), "got bufferA %s\n", debugstr_a(bufferA) );
3506 swprintf( expectW, ARRAY_SIZE(expectW), L"WINE%04X.IME", ime_count - 1 );
3507 memset( bufferW, 0xcd, sizeof(bufferW) );
3508 ret = ImmGetIMEFileNameW( hkl, bufferW, 13 );
3509 ok( ret == 12, "ImmGetIMEFileNameW returned %lu\n", ret );
3510 ok( !wcscmp( bufferW, expectW ), "got bufferW %s\n", debugstr_w(bufferW) );
3511 memset( bufferA, 0xcd, sizeof(bufferA) );
3512 ret = ImmGetIMEFileNameA( hkl, bufferA, 13 );
3513 ok( ret == 12, "ImmGetIMEFileNameA returned %lu\n", ret );
3514 ok( !strcmp( bufferA, expectA ), "got bufferA %s\n", debugstr_a(bufferA) );
3516 ime_cleanup( hkl, FALSE );
3518 cleanup:
3519 SET_ENABLE( IME_DLL_PROCESS_ATTACH, FALSE );
3520 SET_ENABLE( ImeInquire, FALSE );
3521 SET_ENABLE( ImeDestroy, FALSE );
3522 SET_ENABLE( IME_DLL_PROCESS_DETACH, FALSE );
3525 static void test_ImmEscape( BOOL unicode )
3527 HKL hkl = GetKeyboardLayout( 0 );
3528 DWORD i, codes[] =
3530 IME_ESC_QUERY_SUPPORT,
3531 IME_ESC_SEQUENCE_TO_INTERNAL,
3532 IME_ESC_GET_EUDC_DICTIONARY,
3533 IME_ESC_SET_EUDC_DICTIONARY,
3534 IME_ESC_MAX_KEY,
3535 IME_ESC_IME_NAME,
3536 IME_ESC_HANJA_MODE,
3537 IME_ESC_GETHELPFILENAME,
3539 WCHAR bufferW[512];
3540 char bufferA[512];
3542 SET_ENABLE( ImeEscape, TRUE );
3544 winetest_push_context( unicode ? "unicode" : "ansi" );
3546 ok_ret( 0, ImmEscapeW( hkl, 0, 0, NULL ) );
3547 ok_ret( 0, ImmEscapeA( hkl, 0, 0, NULL ) );
3549 /* IME_PROP_END_UNLOAD for the IME to unload / reload. */
3550 ime_info.fdwProperty = IME_PROP_END_UNLOAD;
3551 if (unicode) ime_info.fdwProperty |= IME_PROP_UNICODE;
3553 if (!(hkl = ime_install())) goto cleanup;
3555 for (i = 0; i < ARRAY_SIZE(codes); ++i)
3557 winetest_push_context( "esc %#lx", codes[i] );
3559 SET_EXPECT( ImeEscape );
3560 ok_ret( 4, ImmEscapeW( hkl, 0, codes[i], NULL ) );
3561 CHECK_CALLED( ImeEscape );
3563 SET_EXPECT( ImeEscape );
3564 memset( bufferW, 0xcd, sizeof(bufferW) );
3565 if (codes[i] == IME_ESC_SET_EUDC_DICTIONARY) wcscpy( bufferW, L"EscapeIme" );
3566 ok_ret( 4, ImmEscapeW( hkl, 0, codes[i], bufferW ) );
3567 if (unicode || codes[i] == IME_ESC_GET_EUDC_DICTIONARY || codes[i] == IME_ESC_IME_NAME ||
3568 codes[i] == IME_ESC_GETHELPFILENAME)
3570 ok_wcs( L"ImeEscape", bufferW );
3571 ok_eq( 0xcdcd, bufferW[10], WORD, "%#x" );
3573 else if (codes[i] == IME_ESC_SET_EUDC_DICTIONARY)
3575 ok_wcs( L"EscapeIme", bufferW );
3576 ok_eq( 0xcdcd, bufferW[10], WORD, "%#x" );
3578 else if (codes[i] == IME_ESC_HANJA_MODE)
3580 todo_wine
3581 ok_eq( 0xcdcd, bufferW[0], WORD, "%#x" );
3583 else
3585 ok( !memcmp( bufferW, "ImeEscape", 10 ), "got bufferW %s\n", debugstr_w(bufferW) );
3586 ok_eq( 0xcdcd, bufferW[5], WORD, "%#x" );
3588 CHECK_CALLED( ImeEscape );
3590 SET_EXPECT( ImeEscape );
3591 ok_ret( 4, ImmEscapeA( hkl, 0, codes[i], NULL ) );
3592 CHECK_CALLED( ImeEscape );
3594 SET_EXPECT( ImeEscape );
3595 memset( bufferA, 0xcd, sizeof(bufferA) );
3596 if (codes[i] == IME_ESC_SET_EUDC_DICTIONARY) strcpy( bufferA, "EscapeIme" );
3597 ok_ret( 4, ImmEscapeA( hkl, 0, codes[i], bufferA ) );
3598 if (!unicode || codes[i] == IME_ESC_GET_EUDC_DICTIONARY || codes[i] == IME_ESC_IME_NAME ||
3599 codes[i] == IME_ESC_GETHELPFILENAME)
3601 ok_str( "ImeEscape", bufferA );
3602 ok_eq( 0xcd, bufferA[10], BYTE, "%#x" );
3604 else if (codes[i] == IME_ESC_SET_EUDC_DICTIONARY)
3606 ok_str( "EscapeIme", bufferA );
3607 ok_eq( 0xcd, bufferA[10], BYTE, "%#x" );
3609 else if (codes[i] == IME_ESC_HANJA_MODE)
3611 todo_wine
3612 ok_eq( 0xcd, bufferA[0], BYTE, "%#x" );
3614 else
3616 ok( !memcmp( bufferA, L"ImeEscape", 10 * sizeof(WCHAR) ), "got bufferA %s\n", debugstr_a(bufferA) );
3617 ok_eq( 0xcd, bufferA[20], BYTE, "%#x" );
3619 CHECK_CALLED( ImeEscape );
3621 winetest_pop_context();
3624 ime_cleanup( hkl, FALSE );
3626 cleanup:
3627 SET_ENABLE( ImeEscape, FALSE );
3629 winetest_pop_context();
3632 static int CALLBACK enum_register_wordA( const char *reading, DWORD style, const char *string, void *user )
3634 ime_trace( "reading %s, style %#lx, string %s, user %p\n", debugstr_a(reading), style, debugstr_a(string), user );
3636 ok_eq( 0xdeadbeef, style, UINT, "%#x" );
3637 ok_str( "Reading", reading );
3638 ok_str( "String", string );
3640 return 0xdeadbeef;
3643 static int CALLBACK enum_register_wordW( const WCHAR *reading, DWORD style, const WCHAR *string, void *user )
3645 ime_trace( "reading %s, style %#lx, string %s, user %p\n", debugstr_w(reading), style, debugstr_w(string), user );
3647 ok_eq( 0xdeadbeef, style, UINT, "%#x" );
3648 ok_wcs( L"Reading", reading );
3649 ok_wcs( L"String", string );
3651 return 0xdeadbeef;
3654 static void test_ImmEnumRegisterWord( BOOL unicode )
3656 HKL hkl = GetKeyboardLayout( 0 );
3658 winetest_push_context( unicode ? "unicode" : "ansi" );
3660 SET_ENABLE( ImeEnumRegisterWord, TRUE );
3662 SetLastError( 0xdeadbeef );
3663 ok_ret( 0, ImmEnumRegisterWordW( NULL, enum_register_wordW, NULL, 0, NULL, NULL ) );
3664 ok_ret( 0, ImmEnumRegisterWordA( NULL, enum_register_wordA, NULL, 0, NULL, NULL ) );
3665 ok_ret( 0, ImmEnumRegisterWordW( hkl, enum_register_wordW, NULL, 0, NULL, NULL ) );
3666 ok_ret( 0, ImmEnumRegisterWordA( hkl, enum_register_wordA, NULL, 0, NULL, NULL ) );
3667 todo_wine
3668 ok_ret( 0xdeadbeef, GetLastError() );
3670 /* IME_PROP_END_UNLOAD for the IME to unload / reload. */
3671 ime_info.fdwProperty = IME_PROP_END_UNLOAD;
3672 if (unicode) ime_info.fdwProperty |= IME_PROP_UNICODE;
3674 if (!(hkl = ime_install())) goto cleanup;
3676 SET_EXPECT( ImeEnumRegisterWord );
3677 ok_ret( 0, ImmEnumRegisterWordW( hkl, enum_register_wordW, NULL, 0, NULL, NULL ) );
3678 CHECK_CALLED( ImeEnumRegisterWord );
3680 SET_EXPECT( ImeEnumRegisterWord );
3681 ok_ret( 0, ImmEnumRegisterWordA( hkl, enum_register_wordA, NULL, 0, NULL, NULL ) );
3682 CHECK_CALLED( ImeEnumRegisterWord );
3684 SET_EXPECT( ImeEnumRegisterWord );
3685 ok_ret( 0xdeadbeef, ImmEnumRegisterWordW( hkl, enum_register_wordW, L"Reading", 0xdeadbeef, L"String", NULL ) );
3686 CHECK_CALLED( ImeEnumRegisterWord );
3688 SET_EXPECT( ImeEnumRegisterWord );
3689 ok_ret( 0xdeadbeef, ImmEnumRegisterWordA( hkl, enum_register_wordA, "Reading", 0xdeadbeef, "String", NULL ) );
3690 CHECK_CALLED( ImeEnumRegisterWord );
3692 ime_cleanup( hkl, FALSE );
3694 cleanup:
3695 SET_ENABLE( ImeEnumRegisterWord, FALSE );
3697 winetest_pop_context();
3700 static void test_ImmRegisterWord( BOOL unicode )
3702 HKL hkl = GetKeyboardLayout( 0 );
3704 SET_ENABLE( ImeRegisterWord, TRUE );
3706 winetest_push_context( unicode ? "unicode" : "ansi" );
3708 SetLastError( 0xdeadbeef );
3709 ok_ret( 0, ImmRegisterWordW( NULL, NULL, 0, NULL ) );
3710 ok_ret( 0, ImmRegisterWordA( NULL, NULL, 0, NULL ) );
3711 ok_ret( 0, ImmRegisterWordW( hkl, NULL, 0, NULL ) );
3712 ok_ret( 0, ImmRegisterWordA( hkl, NULL, 0, NULL ) );
3713 todo_wine
3714 ok_ret( 0xdeadbeef, GetLastError() );
3716 /* IME_PROP_END_UNLOAD for the IME to unload / reload. */
3717 ime_info.fdwProperty = IME_PROP_END_UNLOAD;
3718 if (unicode) ime_info.fdwProperty |= IME_PROP_UNICODE;
3720 if (!(hkl = ime_install())) goto cleanup;
3722 SET_EXPECT( ImeRegisterWord );
3723 ok_ret( 0, ImmRegisterWordW( hkl, NULL, 0, NULL ) );
3724 CHECK_CALLED( ImeRegisterWord );
3726 SET_EXPECT( ImeRegisterWord );
3727 ok_ret( 0, ImmRegisterWordA( hkl, NULL, 0, NULL ) );
3728 CHECK_CALLED( ImeRegisterWord );
3730 SET_EXPECT( ImeRegisterWord );
3731 ok_ret( 0, ImmRegisterWordW( hkl, L"Reading", 0, NULL ) );
3732 CHECK_CALLED( ImeRegisterWord );
3734 SET_EXPECT( ImeRegisterWord );
3735 ok_ret( 0, ImmRegisterWordA( hkl, "Reading", 0, NULL ) );
3736 CHECK_CALLED( ImeRegisterWord );
3738 SET_EXPECT( ImeRegisterWord );
3739 ok_ret( 0, ImmRegisterWordW( hkl, NULL, 0xdeadbeef, NULL ) );
3740 CHECK_CALLED( ImeRegisterWord );
3742 SET_EXPECT( ImeRegisterWord );
3743 ok_ret( 0, ImmRegisterWordA( hkl, NULL, 0xdeadbeef, NULL ) );
3744 CHECK_CALLED( ImeRegisterWord );
3746 SET_EXPECT( ImeRegisterWord );
3747 ok_ret( 0, ImmRegisterWordW( hkl, NULL, 0, L"String" ) );
3748 CHECK_CALLED( ImeRegisterWord );
3750 SET_EXPECT( ImeRegisterWord );
3751 ok_ret( 0, ImmRegisterWordA( hkl, NULL, 0, "String" ) );
3752 CHECK_CALLED( ImeRegisterWord );
3754 ime_cleanup( hkl, FALSE );
3756 cleanup:
3757 SET_ENABLE( ImeRegisterWord, FALSE );
3759 winetest_pop_context();
3762 static void test_ImmGetRegisterWordStyle( BOOL unicode )
3764 HKL hkl = GetKeyboardLayout( 0 );
3765 STYLEBUFW styleW;
3766 STYLEBUFA styleA;
3768 winetest_push_context( unicode ? "unicode" : "ansi" );
3770 SET_ENABLE( ImeGetRegisterWordStyle, TRUE );
3772 SetLastError( 0xdeadbeef );
3773 ok_ret( 0, ImmGetRegisterWordStyleW( NULL, 0, &styleW ) );
3774 ok_ret( 0, ImmGetRegisterWordStyleA( NULL, 0, &styleA ) );
3775 ok_ret( 0, ImmGetRegisterWordStyleW( hkl, 0, &styleW ) );
3776 ok_ret( 0, ImmGetRegisterWordStyleA( hkl, 0, &styleA ) );
3777 todo_wine
3778 ok_ret( 0xdeadbeef, GetLastError() );
3780 /* IME_PROP_END_UNLOAD for the IME to unload / reload. */
3781 ime_info.fdwProperty = IME_PROP_END_UNLOAD;
3782 if (unicode) ime_info.fdwProperty |= IME_PROP_UNICODE;
3784 if (!(hkl = ime_install())) goto cleanup;
3786 if (!strcmp( winetest_platform, "wine" )) goto skip_null;
3788 SET_EXPECT( ImeGetRegisterWordStyle );
3789 ok_ret( 0xdeadbeef, ImmGetRegisterWordStyleW( hkl, 16, NULL ) );
3790 CHECK_CALLED( ImeGetRegisterWordStyle );
3792 SET_EXPECT( ImeGetRegisterWordStyle );
3793 ok_ret( 0xdeadbeef, ImmGetRegisterWordStyleA( hkl, 16, NULL ) );
3794 CHECK_CALLED( ImeGetRegisterWordStyle );
3796 skip_null:
3797 SET_EXPECT( ImeGetRegisterWordStyle );
3798 memset( &styleW, 0xcd, sizeof(styleW) );
3799 ok_ret( 0xdeadbeef, ImmGetRegisterWordStyleW( hkl, 1, &styleW ) );
3800 if (ime_info.fdwProperty & IME_PROP_UNICODE)
3802 ok_eq( 0xdeadbeef, styleW.dwStyle, UINT, "%#x" );
3803 ok_wcs( L"StyleDescription", styleW.szDescription );
3805 else
3807 todo_wine
3808 ok_eq( 0xcdcdcdcd, styleW.dwStyle, UINT, "%#x" );
3809 todo_wine
3810 ok_eq( 0xcdcd, styleW.szDescription[0], WORD, "%#x" );
3812 CHECK_CALLED( ImeGetRegisterWordStyle );
3814 SET_EXPECT( ImeGetRegisterWordStyle );
3815 memset( &styleA, 0xcd, sizeof(styleA) );
3816 ok_ret( 0xdeadbeef, ImmGetRegisterWordStyleA( hkl, 1, &styleA ) );
3817 if (ime_info.fdwProperty & IME_PROP_UNICODE)
3819 todo_wine
3820 ok_eq( 0xcdcdcdcd, styleA.dwStyle, UINT, "%#x" );
3821 todo_wine
3822 ok_eq( 0xcd, styleA.szDescription[0], BYTE, "%#x" );
3824 else
3826 ok_eq( 0xdeadbeef, styleA.dwStyle, UINT, "%#x" );
3827 ok_str( "StyleDescription", styleA.szDescription );
3829 CHECK_CALLED( ImeGetRegisterWordStyle );
3831 ime_cleanup( hkl, FALSE );
3833 cleanup:
3834 SET_ENABLE( ImeGetRegisterWordStyle, FALSE );
3836 winetest_pop_context();
3839 static void test_ImmUnregisterWord( BOOL unicode )
3841 HKL hkl = GetKeyboardLayout( 0 );
3843 winetest_push_context( unicode ? "unicode" : "ansi" );
3845 SET_ENABLE( ImeUnregisterWord, TRUE );
3847 SetLastError( 0xdeadbeef );
3848 ok_ret( 0, ImmUnregisterWordW( NULL, NULL, 0, NULL ) );
3849 ok_ret( 0, ImmUnregisterWordA( NULL, NULL, 0, NULL ) );
3850 ok_ret( 0, ImmUnregisterWordW( hkl, NULL, 0, NULL ) );
3851 ok_ret( 0, ImmUnregisterWordA( hkl, NULL, 0, NULL ) );
3852 todo_wine
3853 ok_ret( 0xdeadbeef, GetLastError() );
3855 /* IME_PROP_END_UNLOAD for the IME to unload / reload. */
3856 ime_info.fdwProperty = IME_PROP_END_UNLOAD;
3857 if (unicode) ime_info.fdwProperty |= IME_PROP_UNICODE;
3859 if (!(hkl = ime_install())) goto cleanup;
3861 SET_EXPECT( ImeUnregisterWord );
3862 ok_ret( 0, ImmUnregisterWordW( hkl, NULL, 0, NULL ) );
3863 CHECK_CALLED( ImeUnregisterWord );
3865 SET_EXPECT( ImeUnregisterWord );
3866 ok_ret( 0, ImmUnregisterWordA( hkl, NULL, 0, NULL ) );
3867 CHECK_CALLED( ImeUnregisterWord );
3869 SET_EXPECT( ImeUnregisterWord );
3870 ok_ret( 0, ImmUnregisterWordW( hkl, L"Reading", 0, NULL ) );
3871 CHECK_CALLED( ImeUnregisterWord );
3873 SET_EXPECT( ImeUnregisterWord );
3874 ok_ret( 0, ImmUnregisterWordA( hkl, "Reading", 0, NULL ) );
3875 CHECK_CALLED( ImeUnregisterWord );
3877 SET_EXPECT( ImeUnregisterWord );
3878 ok_ret( 0, ImmUnregisterWordW( hkl, NULL, 0xdeadbeef, NULL ) );
3879 CHECK_CALLED( ImeUnregisterWord );
3881 SET_EXPECT( ImeUnregisterWord );
3882 ok_ret( 0, ImmUnregisterWordA( hkl, NULL, 0xdeadbeef, NULL ) );
3883 CHECK_CALLED( ImeUnregisterWord );
3885 SET_EXPECT( ImeUnregisterWord );
3886 ok_ret( 0, ImmUnregisterWordW( hkl, NULL, 0, L"String" ) );
3887 CHECK_CALLED( ImeUnregisterWord );
3889 SET_EXPECT( ImeUnregisterWord );
3890 ok_ret( 0, ImmUnregisterWordA( hkl, NULL, 0, "String" ) );
3891 CHECK_CALLED( ImeUnregisterWord );
3893 ime_cleanup( hkl, FALSE );
3895 cleanup:
3896 SET_ENABLE( ImeUnregisterWord, FALSE );
3898 winetest_pop_context();
3901 static void test_ImmProcessKey(void)
3903 const struct ime_call process_key_seq[] =
3906 .hkl = expect_ime, .himc = default_himc,
3907 .func = IME_PROCESS_KEY, .process_key = {.vkey = 'A', .key_data = 0},
3909 {0},
3911 HKL hkl, old_hkl = GetKeyboardLayout( 0 );
3912 UINT_PTR ret;
3914 hwnd = CreateWindowW( L"static", NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
3915 100, 100, 100, 100, NULL, NULL, NULL, NULL );
3916 ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() );
3917 process_messages();
3919 ok_ret( 0, ImmProcessKey( hwnd, old_hkl, 'A', 0, 0 ) );
3920 ok_seq( empty_sequence );
3922 ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE;
3924 if (!(hkl = ime_install())) goto cleanup;
3926 ok_ret( 1, ImmActivateLayout( hkl ) );
3927 ok_ret( 1, ImmLoadIME( hkl ) );
3928 process_messages();
3929 memset( ime_calls, 0, sizeof(ime_calls) );
3930 ime_call_count = 0;
3932 ok_ret( 0, ImmProcessKey( 0, hkl, 'A', 0, 0 ) );
3933 ok_seq( empty_sequence );
3935 ret = ImmProcessKey( hwnd, hkl, 'A', 0, 0 );
3936 todo_wine
3937 ok_ret( 2, ret );
3938 ok_seq( process_key_seq );
3940 ok_eq( hkl, GetKeyboardLayout( 0 ), HKL, "%p" );
3941 ok_ret( 0, ImmProcessKey( hwnd, old_hkl, 'A', 0, 0 ) );
3942 todo_wine
3943 ok_seq( empty_sequence );
3944 ok_eq( hkl, GetKeyboardLayout( 0 ), HKL, "%p" );
3946 ok_ret( 1, ImmActivateLayout( old_hkl ) );
3948 ime_cleanup( hkl, TRUE );
3949 process_messages();
3950 memset( ime_calls, 0, sizeof(ime_calls) );
3951 ime_call_count = 0;
3953 cleanup:
3954 ok_ret( 1, DestroyWindow( hwnd ) );
3957 struct ime_windows
3959 HWND ime_hwnd;
3960 HWND ime_ui_hwnd;
3963 static BOOL CALLBACK enum_thread_ime_windows( HWND hwnd, LPARAM lparam )
3965 struct ime_windows *params = (void *)lparam;
3966 WCHAR buffer[256];
3967 UINT ret;
3969 ime_trace( "hwnd %p, lparam %#Ix\n", hwnd, lparam );
3971 ret = RealGetWindowClassW( hwnd, buffer, ARRAY_SIZE(buffer) );
3972 ok( ret, "RealGetWindowClassW returned %#x\n", ret );
3974 if (!wcscmp( buffer, L"IME" ))
3976 ok( !params->ime_hwnd, "Found extra IME window %p\n", hwnd );
3977 params->ime_hwnd = hwnd;
3979 if (!wcscmp( buffer, ime_ui_class.lpszClassName ))
3981 ok( !params->ime_ui_hwnd, "Found extra IME UI window %p\n", hwnd );
3982 params->ime_ui_hwnd = hwnd;
3985 return TRUE;
3988 static void test_ImmActivateLayout(void)
3990 const struct ime_call activate_seq[] =
3993 .hkl = expect_ime, .himc = default_himc,
3994 .func = IME_SELECT, .select = 1,
3996 {0},
3998 const struct ime_call deactivate_seq[] =
4001 .hkl = expect_ime, .himc = default_himc,
4002 .func = IME_NOTIFY, .notify = {.action = NI_COMPOSITIONSTR, .index = CPS_CANCEL, .value = 0},
4003 .todo = TRUE,
4006 .hkl = default_hkl, .himc = default_himc,
4007 .func = IME_SELECT, .select = 0,
4009 {0},
4011 struct ime_call activate_with_window_seq[] =
4014 .hkl = expect_ime, .himc = default_himc,
4015 .func = IME_SELECT, .select = 1,
4016 .flaky_himc = TRUE,
4019 .hkl = expect_ime, .himc = 0/*himc*/,
4020 .func = IME_SELECT, .select = 1,
4021 .flaky_himc = TRUE,
4024 .hkl = expect_ime, .himc = default_himc,
4025 .func = MSG_IME_UI, .message = {.msg = WM_IME_SELECT, .wparam = 1, .lparam = (LPARAM)expect_ime},
4028 .hkl = expect_ime, .himc = default_himc,
4029 .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_OPENSTATUSWINDOW},
4030 .todo = TRUE,
4033 .hkl = expect_ime, .himc = default_himc,
4034 .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETCONVERSIONMODE},
4035 .todo = TRUE, .broken = (default_hkl == (HKL)0x04120412),
4038 .hkl = expect_ime, .himc = default_himc,
4039 .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETSENTENCEMODE},
4040 .todo = TRUE,
4042 {0},
4044 struct ime_call deactivate_with_window_seq[] =
4047 .hkl = expect_ime, .himc = default_himc,
4048 .func = IME_NOTIFY, .notify = {.action = NI_COMPOSITIONSTR, .index = CPS_CANCEL, .value = 0},
4049 .todo = TRUE, .flaky_himc = TRUE,
4052 .hkl = expect_ime, .himc = 0/*himc*/,
4053 .func = IME_NOTIFY, .notify = {.action = NI_COMPOSITIONSTR, .index = CPS_CANCEL, .value = 0},
4054 .todo = TRUE, .flaky_himc = TRUE,
4057 .hkl = expect_ime, .himc = default_himc,
4058 .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_CLOSESTATUSWINDOW},
4059 .todo = TRUE,
4062 .hkl = expect_ime, .himc = default_himc,
4063 .func = MSG_IME_UI, .message = {.msg = WM_IME_SELECT, .wparam = 0, .lparam = (LPARAM)expect_ime},
4066 .hkl = default_hkl, .himc = default_himc,
4067 .func = IME_SELECT, .select = 0,
4068 .flaky_himc = TRUE,
4071 .hkl = default_hkl, .himc = 0/*himc*/,
4072 .func = IME_SELECT, .select = 0,
4073 .flaky_himc = TRUE,
4075 {0},
4077 HKL hkl, old_hkl = GetKeyboardLayout( 0 );
4078 struct ime_windows ime_windows = {0};
4079 HIMC himc;
4080 UINT ret;
4082 SET_ENABLE( ImeInquire, TRUE );
4083 SET_ENABLE( ImeDestroy, TRUE );
4085 ok_ret( 1, ImmActivateLayout( old_hkl ) );
4087 ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE;
4089 if (!(hkl = ime_install())) goto cleanup;
4091 /* ActivateKeyboardLayout doesn't call ImeInquire / ImeDestroy */
4093 ok_seq( empty_sequence );
4094 ok_eq( old_hkl, ActivateKeyboardLayout( hkl, 0 ), HKL, "%p" );
4095 ok_eq( hkl, GetKeyboardLayout( 0 ), HKL, "%p" );
4096 ok_eq( hkl, ActivateKeyboardLayout( old_hkl, 0 ), HKL, "%p" );
4097 ok_seq( empty_sequence );
4098 ok_eq( old_hkl, GetKeyboardLayout( 0 ), HKL, "%p" );
4101 /* ImmActivateLayout changes active HKL */
4103 SET_EXPECT( ImeInquire );
4104 ok_ret( 1, ImmActivateLayout( hkl ) );
4105 ok_seq( activate_seq );
4106 CHECK_CALLED( ImeInquire );
4107 ok_ret( 1, ImmLoadIME( hkl ) );
4109 ok_eq( hkl, GetKeyboardLayout( 0 ), HKL, "%p" );
4111 ok_ret( 1, ImmActivateLayout( hkl ) );
4112 ok_seq( empty_sequence );
4114 todo_ImeDestroy = TRUE; /* Wine doesn't leak the IME */
4115 ok_ret( 1, ImmActivateLayout( old_hkl ) );
4116 ok_seq( deactivate_seq );
4117 todo_ImeDestroy = FALSE;
4119 ok_eq( old_hkl, GetKeyboardLayout( 0 ), HKL, "%p" );
4121 ime_cleanup( hkl, FALSE );
4122 ok_seq( empty_sequence );
4125 /* ImmActivateLayout leaks the IME, we need to free it manually */
4127 SET_EXPECT( ImeDestroy );
4128 ret = ImmFreeLayout( hkl );
4129 ok( ret, "ImmFreeLayout returned %u\n", ret );
4130 CHECK_CALLED( ImeDestroy );
4131 ok_seq( empty_sequence );
4134 /* when there's a window, ActivateKeyboardLayout calls ImeInquire */
4136 if (!(hkl = ime_install())) goto cleanup;
4138 hwnd = CreateWindowW( L"static", NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
4139 100, 100, 100, 100, NULL, NULL, NULL, NULL );
4140 ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() );
4141 process_messages();
4142 ok_seq( empty_sequence );
4144 himc = ImmCreateContext();
4145 ok( !!himc, "got himc %p\n", himc );
4146 ok_seq( empty_sequence );
4148 SET_EXPECT( ImeInquire );
4149 ok_eq( old_hkl, ActivateKeyboardLayout( hkl, 0 ), HKL, "%p" );
4150 CHECK_CALLED( ImeInquire );
4151 activate_with_window_seq[1].himc = himc;
4152 ok_seq( activate_with_window_seq );
4153 todo_ImeInquire = TRUE;
4154 ok_ret( 1, ImmLoadIME( hkl ) );
4155 todo_ImeInquire = FALSE;
4157 ok_eq( hkl, GetKeyboardLayout( 0 ), HKL, "%p" );
4159 /* FIXME: ignore spurious VK_CONTROL ImeProcessKey / ImeToAsciiEx calls */
4160 process_messages();
4161 memset( ime_calls, 0, sizeof(ime_calls) );
4162 ime_call_count = 0;
4164 todo_ImeDestroy = TRUE; /* Wine doesn't leak the IME */
4165 ok_eq( hkl, ActivateKeyboardLayout( old_hkl, 0 ), HKL, "%p" );
4166 todo_ImeDestroy = FALSE;
4167 deactivate_with_window_seq[1].himc = himc;
4168 deactivate_with_window_seq[5].himc = himc;
4169 ok_seq( deactivate_with_window_seq );
4171 ok_eq( old_hkl, GetKeyboardLayout( 0 ), HKL, "%p" );
4173 ok_ret( 1, ImmDestroyContext( himc ) );
4174 ok_seq( empty_sequence );
4177 todo_ImeInquire = TRUE;
4178 ok_eq( old_hkl, ActivateKeyboardLayout( hkl, 0 ), HKL, "%p" );
4179 todo_ImeInquire = FALSE;
4180 ok_eq( hkl, GetKeyboardLayout( 0 ), HKL, "%p" );
4182 ok_ret( 1, EnumThreadWindows( GetCurrentThreadId(), enum_thread_ime_windows, (LPARAM)&ime_windows ) );
4183 ok( !!ime_windows.ime_hwnd, "missing IME window\n" );
4184 ok( !!ime_windows.ime_ui_hwnd, "missing IME UI window\n" );
4185 ok_ret( (UINT_PTR)ime_windows.ime_hwnd, (UINT_PTR)GetParent( ime_windows.ime_ui_hwnd ) );
4187 todo_ImeDestroy = TRUE; /* Wine doesn't leak the IME */
4188 ok_eq( hkl, ActivateKeyboardLayout( old_hkl, 0 ), HKL, "%p" );
4189 todo_ImeDestroy = FALSE;
4190 ok_eq( old_hkl, GetKeyboardLayout( 0 ), HKL, "%p" );
4193 SET_EXPECT( ImeDestroy );
4194 ime_cleanup( hkl, TRUE );
4195 CHECK_CALLED( ImeDestroy );
4197 ok_ret( 1, DestroyWindow( hwnd ) );
4198 process_messages();
4199 memset( ime_calls, 0, sizeof(ime_calls) );
4200 ime_call_count = 0;
4203 cleanup:
4204 SET_ENABLE( ImeInquire, FALSE );
4205 SET_ENABLE( ImeDestroy, FALSE );
4208 static void test_ImmCreateInputContext(void)
4210 struct ime_call activate_seq[] =
4213 .hkl = expect_ime, .himc = default_himc,
4214 .func = IME_SELECT, .select = 1,
4215 .flaky_himc = TRUE,
4218 .hkl = expect_ime, .himc = 0/*himc[0]*/,
4219 .func = IME_SELECT, .select = 1,
4220 .flaky_himc = TRUE,
4223 .hkl = expect_ime, .himc = default_himc,
4224 .func = MSG_IME_UI, .message = {.msg = WM_IME_SELECT, .wparam = 1, .lparam = (LPARAM)expect_ime},
4227 .hkl = expect_ime, .himc = default_himc,
4228 .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_OPENSTATUSWINDOW},
4229 .todo = TRUE,
4231 {0},
4233 struct ime_call select1_seq[] =
4236 .hkl = expect_ime, .himc = 0/*himc[1]*/,
4237 .func = IME_SELECT, .select = 1,
4238 .todo = TRUE,
4240 {0},
4242 struct ime_call select0_seq[] =
4245 .hkl = expect_ime, .himc = 0/*himc[1]*/,
4246 .func = IME_SELECT, .select = 0,
4248 {0},
4250 struct ime_call deactivate_seq[] =
4253 .hkl = expect_ime, .himc = default_himc,
4254 .func = IME_NOTIFY, .notify = {.action = NI_COMPOSITIONSTR, .index = CPS_CANCEL, .value = 0},
4255 .todo = TRUE, .flaky_himc = TRUE,
4258 .hkl = expect_ime, .himc = 0/*himc[0]*/,
4259 .func = IME_NOTIFY, .notify = {.action = NI_COMPOSITIONSTR, .index = CPS_CANCEL, .value = 0},
4260 .todo = TRUE, .flaky_himc = TRUE,
4263 .hkl = expect_ime, .himc = default_himc,
4264 .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_CLOSESTATUSWINDOW},
4265 .todo = TRUE,
4268 .hkl = expect_ime, .himc = default_himc,
4269 .func = MSG_IME_UI, .message = {.msg = WM_IME_SELECT, .wparam = 0, .lparam = (LPARAM)expect_ime},
4272 .hkl = default_hkl, .himc = default_himc,
4273 .func = IME_SELECT, .select = 0,
4274 .flaky_himc = TRUE,
4277 .hkl = default_hkl, .himc = 0/*himc[0]*/,
4278 .func = IME_SELECT, .select = 0,
4279 .flaky_himc = TRUE,
4281 {0},
4283 HKL hkl, old_hkl = GetKeyboardLayout( 0 );
4284 INPUTCONTEXT *ctx;
4285 HIMC himc[2];
4286 HWND hwnd;
4288 ctx = ImmLockIMC( default_himc );
4289 ok( !!ctx, "ImmLockIMC failed, error %lu\n", GetLastError() );
4290 ok_ret( 0, IsWindow( ctx->hWnd ) );
4291 ok_ret( 1, ImmUnlockIMC( default_himc ) );
4294 /* new input contexts cannot be locked before IME window has been created */
4296 himc[0] = ImmCreateContext();
4297 ok( !!himc[0], "ImmCreateContext failed, error %lu\n", GetLastError() );
4298 ctx = ImmLockIMC( himc[0] );
4299 todo_wine
4300 ok( !ctx, "ImmLockIMC failed, error %lu\n", GetLastError() );
4301 if (ctx) ImmUnlockIMCC( himc[0] );
4303 hwnd = CreateWindowW( L"static", NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
4304 100, 100, 100, 100, NULL, NULL, NULL, NULL );
4305 ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() );
4306 process_messages();
4308 ctx = ImmLockIMC( default_himc );
4309 ok( !!ctx, "ImmLockIMC failed, error %lu\n", GetLastError() );
4310 ok_ret( 1, ImmUnlockIMC( default_himc ) );
4312 ctx = ImmLockIMC( himc[0] );
4313 ok( !!ctx, "ImmLockIMC failed, error %lu\n", GetLastError() );
4314 ok_ret( 1, ImmUnlockIMC( himc[0] ) );
4317 ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE;
4318 ime_info.dwPrivateDataSize = 123;
4320 if (!(hkl = ime_install())) goto cleanup;
4322 ok_ret( 1, ImmLoadIME( hkl ) );
4324 /* Activating the layout calls ImeSelect 1 on existing HIMC */
4326 ok_seq( empty_sequence );
4327 ok_ret( 1, ImmActivateLayout( hkl ) );
4328 activate_seq[1].himc = himc[0];
4329 ok_seq( activate_seq );
4331 ok_eq( hkl, GetKeyboardLayout( 0 ), HKL, "%p" );
4333 ctx = ImmLockIMC( default_himc );
4334 ok( !!ctx, "ImmLockIMC failed, error %lu\n", GetLastError() );
4335 ok_ret( 1, ImmUnlockIMC( default_himc ) );
4337 ctx = ImmLockIMC( himc[0] );
4338 ok( !!ctx, "ImmLockIMC failed, error %lu\n", GetLastError() );
4339 ok_ret( 1, ImmUnlockIMC( himc[0] ) );
4342 /* ImmLockIMC triggers the ImeSelect call, to allocate private data */
4344 himc[1] = ImmCreateContext();
4345 ok( !!himc[1], "ImmCreateContext failed, error %lu\n", GetLastError() );
4347 todo_wine
4348 ok_seq( empty_sequence );
4349 ctx = ImmLockIMC( himc[1] );
4350 ok( !!ctx, "ImmLockIMC failed, error %lu\n", GetLastError() );
4351 select1_seq[0].himc = himc[1];
4352 ok_seq( select1_seq );
4354 ok_ret( 1, ImmUnlockIMC( himc[1] ) );
4356 ok_seq( empty_sequence );
4357 ok_ret( 1, ImmDestroyContext( himc[1] ) );
4358 select0_seq[0].himc = himc[1];
4359 ok_seq( select0_seq );
4362 /* Deactivating the layout calls ImeSelect 0 on existing HIMC */
4364 ok_ret( 1, ImmActivateLayout( old_hkl ) );
4365 deactivate_seq[1].himc = himc[0];
4366 deactivate_seq[5].himc = himc[0];
4367 ok_seq( deactivate_seq );
4369 ok_eq( old_hkl, GetKeyboardLayout( 0 ), HKL, "%p" );
4371 ime_cleanup( hkl, TRUE );
4372 ok_seq( empty_sequence );
4374 cleanup:
4375 ok_ret( 1, ImmDestroyContext( himc[0] ) );
4376 ok_ret( 1, DestroyWindow( hwnd ) );
4377 process_messages();
4378 memset( ime_calls, 0, sizeof(ime_calls) );
4379 ime_call_count = 0;
4381 ime_info.dwPrivateDataSize = 0;
4384 static void test_DefWindowProc(void)
4386 const struct ime_call start_composition_seq[] =
4388 {.hkl = expect_ime, .himc = default_himc, .func = MSG_IME_UI, .message = {.msg = WM_IME_STARTCOMPOSITION}},
4389 {0},
4391 const struct ime_call end_composition_seq[] =
4393 {.hkl = expect_ime, .himc = default_himc, .func = MSG_IME_UI, .message = {.msg = WM_IME_ENDCOMPOSITION}},
4394 {0},
4396 const struct ime_call composition_seq[] =
4398 {.hkl = expect_ime, .himc = default_himc, .func = MSG_IME_UI, .message = {.msg = WM_IME_COMPOSITION}},
4399 {0},
4401 const struct ime_call set_context_seq[] =
4403 {.hkl = expect_ime, .himc = default_himc, .func = MSG_IME_UI, .message = {.msg = WM_IME_SETCONTEXT}},
4404 {0},
4406 const struct ime_call notify_seq[] =
4408 {.hkl = expect_ime, .himc = default_himc, .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY}},
4409 {0},
4411 HKL hkl, old_hkl = GetKeyboardLayout( 0 );
4412 UINT_PTR ret;
4414 ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE;
4416 if (!(hkl = ime_install())) return;
4418 hwnd = CreateWindowW( L"static", NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
4419 100, 100, 100, 100, NULL, NULL, NULL, NULL );
4420 ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() );
4422 ok_ret( 1, ImmActivateLayout( hkl ) );
4423 ok_ret( 1, ImmLoadIME( hkl ) );
4424 process_messages();
4425 memset( ime_calls, 0, sizeof(ime_calls) );
4426 ime_call_count = 0;
4428 ok_ret( 0, DefWindowProcW( hwnd, WM_IME_STARTCOMPOSITION, 0, 0 ) );
4429 ok_seq( start_composition_seq );
4430 ok_ret( 0, DefWindowProcW( hwnd, WM_IME_ENDCOMPOSITION, 0, 0 ) );
4431 ok_seq( end_composition_seq );
4432 ok_ret( 0, DefWindowProcW( hwnd, WM_IME_COMPOSITION, 0, 0 ) );
4433 ok_seq( composition_seq );
4434 ok_ret( 0, DefWindowProcW( hwnd, WM_IME_SETCONTEXT, 0, 0 ) );
4435 ok_seq( set_context_seq );
4436 ok_ret( 0, DefWindowProcW( hwnd, WM_IME_NOTIFY, 0, 0 ) );
4437 ok_seq( notify_seq );
4438 ok_ret( 0, DefWindowProcW( hwnd, WM_IME_CONTROL, 0, 0 ) );
4439 ok_seq( empty_sequence );
4440 ok_ret( 0, DefWindowProcW( hwnd, WM_IME_COMPOSITIONFULL, 0, 0 ) );
4441 ok_seq( empty_sequence );
4442 ok_ret( 0, DefWindowProcW( hwnd, WM_IME_SELECT, 0, 0 ) );
4443 ok_seq( empty_sequence );
4444 ok_ret( 0, DefWindowProcW( hwnd, WM_IME_CHAR, 0, 0 ) );
4445 ok_seq( empty_sequence );
4446 ok_ret( 0, DefWindowProcW( hwnd, 0x287, 0, 0 ) );
4447 ok_seq( empty_sequence );
4448 ok_ret( 0, DefWindowProcW( hwnd, WM_IME_REQUEST, 0, 0 ) );
4449 ok_seq( empty_sequence );
4450 ret = DefWindowProcW( hwnd, WM_IME_KEYDOWN, 0, 0 );
4451 todo_wine
4452 ok_ret( 0, ret );
4453 ok_seq( empty_sequence );
4454 ret = DefWindowProcW( hwnd, WM_IME_KEYUP, 0, 0 );
4455 todo_wine
4456 ok_ret( 0, ret );
4457 ok_seq( empty_sequence );
4459 ok_ret( 1, ImmActivateLayout( old_hkl ) );
4460 ok_ret( 1, DestroyWindow( hwnd ) );
4461 process_messages();
4463 ime_cleanup( hkl, TRUE );
4464 memset( ime_calls, 0, sizeof(ime_calls) );
4465 ime_call_count = 0;
4468 START_TEST(imm32)
4470 default_hkl = GetKeyboardLayout( 0 );
4472 if (!is_ime_enabled())
4474 win_skip("IME support not implemented\n");
4475 return;
4478 test_com_initialization();
4480 test_ImmEnumInputContext();
4482 test_ImmInstallIME();
4483 test_ImmGetDescription();
4484 test_ImmGetIMEFileName();
4485 test_ImmIsIME();
4486 test_ImmGetProperty();
4488 test_ImmEscape( FALSE );
4489 test_ImmEscape( TRUE );
4490 test_ImmEnumRegisterWord( FALSE );
4491 test_ImmEnumRegisterWord( TRUE );
4492 test_ImmRegisterWord( FALSE );
4493 test_ImmRegisterWord( TRUE );
4494 test_ImmGetRegisterWordStyle( FALSE );
4495 test_ImmGetRegisterWordStyle( TRUE );
4496 test_ImmUnregisterWord( FALSE );
4497 test_ImmUnregisterWord( TRUE );
4499 test_ImmActivateLayout();
4500 test_ImmCreateInputContext();
4501 test_ImmProcessKey();
4502 test_DefWindowProc();
4504 if (init())
4506 test_ImmNotifyIME();
4507 test_ImmGetCompositionString();
4508 test_ImmSetCompositionString();
4509 test_ImmIME();
4510 test_ImmAssociateContextEx();
4511 test_NtUserAssociateInputContext();
4512 test_ImmThreads();
4513 test_ImmIsUIMessage();
4514 test_ImmGetContext();
4515 test_ImmDefaultHwnd();
4516 test_default_ime_window_creation();
4517 test_ImmGetIMCLockCount();
4518 test_ImmGetIMCCLockCount();
4519 test_ImmDestroyContext();
4520 test_ImmDestroyIMCC();
4521 test_InvalidIMC();
4522 msg_spy_cleanup();
4523 /* Reinitialize the hooks to capture all windows */
4524 msg_spy_init(NULL);
4525 test_ImmMessages();
4526 msg_spy_cleanup();
4527 if (pSendInput)
4528 test_ime_processkey();
4529 else win_skip("SendInput is not available\n");
4531 /* there's no way of enabling IME - keep the test last */
4532 test_ImmDisableIME();
4534 cleanup();