imm32: Ignore some messages in ImmTranslateMessage.
[wine.git] / dlls / imm32 / tests / imm32.c
blob3f8736483d856d037a3a0576ba5ea18c7b2cc4a2
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 check_member_( file, line, val, exp, fmt, member ) \
90 ok_(file, line)( (val).member == (exp).member, "got " #member " " fmt "\n", (val).member )
91 #define check_member( val, exp, fmt, member ) \
92 check_member_( __FILE__, __LINE__, val, exp, fmt, member )
94 #define check_member_wstr_( file, line, val, exp, member ) \
95 ok_(file, line)( !wcscmp( (val).member, (exp).member ), "got " #member " %s\n", \
96 debugstr_w((val).member) )
97 #define check_member_wstr( val, exp, member ) \
98 check_member_wstr_( __FILE__, __LINE__, val, exp, member )
100 #define check_member_str_( file, line, val, exp, member ) \
101 ok_(file, line)( !strcmp( (val).member, (exp).member ), "got " #member " %s\n", \
102 debugstr_a((val).member) )
103 #define check_member_str( val, exp, member ) \
104 check_member_str_( __FILE__, __LINE__, val, exp, member )
106 #define check_member_point_( file, line, val, exp, member ) \
107 ok_(file, line)( !memcmp( &(val).member, &(exp).member, sizeof(POINT) ), \
108 "got " #member " %s\n", wine_dbgstr_point( &(val).member ) )
109 #define check_member_point( val, exp, member ) \
110 check_member_point_( __FILE__, __LINE__, val, exp, member )
112 #define check_member_rect_( file, line, val, exp, member ) \
113 ok_(file, line)( !memcmp( &(val).member, &(exp).member, sizeof(RECT) ), \
114 "got " #member " %s\n", wine_dbgstr_rect( &(val).member ) )
115 #define check_member_rect( val, exp, member ) \
116 check_member_rect_( __FILE__, __LINE__, val, exp, member )
118 #define check_composition_string( a, b ) check_composition_string_( __LINE__, a, b )
119 static void check_composition_string_( int line, COMPOSITIONSTRING *string, const COMPOSITIONSTRING *expect )
121 check_member_( __FILE__, line, *string, *expect, "%lu", dwSize );
122 check_member_( __FILE__, line, *string, *expect, "%lu", dwCompReadAttrLen );
123 check_member_( __FILE__, line, *string, *expect, "%lu", dwCompReadAttrOffset );
124 check_member_( __FILE__, line, *string, *expect, "%lu", dwCompReadClauseLen );
125 check_member_( __FILE__, line, *string, *expect, "%lu", dwCompReadClauseOffset );
126 check_member_( __FILE__, line, *string, *expect, "%lu", dwCompReadStrLen );
127 check_member_( __FILE__, line, *string, *expect, "%lu", dwCompReadStrOffset );
128 check_member_( __FILE__, line, *string, *expect, "%lu", dwCompAttrLen );
129 check_member_( __FILE__, line, *string, *expect, "%lu", dwCompAttrOffset );
130 check_member_( __FILE__, line, *string, *expect, "%lu", dwCompClauseLen );
131 check_member_( __FILE__, line, *string, *expect, "%lu", dwCompClauseOffset );
132 check_member_( __FILE__, line, *string, *expect, "%lu", dwCompStrLen );
133 check_member_( __FILE__, line, *string, *expect, "%lu", dwCompStrOffset );
134 check_member_( __FILE__, line, *string, *expect, "%lu", dwCursorPos );
135 check_member_( __FILE__, line, *string, *expect, "%lu", dwDeltaStart );
136 check_member_( __FILE__, line, *string, *expect, "%lu", dwResultReadClauseLen );
137 check_member_( __FILE__, line, *string, *expect, "%lu", dwResultReadClauseOffset );
138 check_member_( __FILE__, line, *string, *expect, "%lu", dwResultReadStrLen );
139 check_member_( __FILE__, line, *string, *expect, "%lu", dwResultReadStrOffset );
140 check_member_( __FILE__, line, *string, *expect, "%lu", dwResultClauseLen );
141 check_member_( __FILE__, line, *string, *expect, "%lu", dwResultClauseOffset );
142 check_member_( __FILE__, line, *string, *expect, "%lu", dwResultStrLen );
143 check_member_( __FILE__, line, *string, *expect, "%lu", dwResultStrOffset );
144 check_member_( __FILE__, line, *string, *expect, "%lu", dwPrivateSize );
145 check_member_( __FILE__, line, *string, *expect, "%lu", dwPrivateOffset );
148 #define check_candidate_list( a, b ) check_candidate_list_( __LINE__, a, b, TRUE )
149 static void check_candidate_list_( int line, CANDIDATELIST *list, const CANDIDATELIST *expect, BOOL unicode )
151 UINT i;
153 check_member_( __FILE__, line, *list, *expect, "%lu", dwSize );
154 check_member_( __FILE__, line, *list, *expect, "%lu", dwStyle );
155 check_member_( __FILE__, line, *list, *expect, "%lu", dwCount );
156 check_member_( __FILE__, line, *list, *expect, "%lu", dwSelection );
157 check_member_( __FILE__, line, *list, *expect, "%lu", dwPageStart );
158 check_member_( __FILE__, line, *list, *expect, "%lu", dwPageSize );
159 for (i = 0; i < list->dwCount && i < expect->dwCount; ++i)
161 void *list_str = (BYTE *)list + list->dwOffset[i], *expect_str = (BYTE *)expect + expect->dwOffset[i];
162 check_member_( __FILE__, line, *list, *expect, "%lu", dwOffset[i] );
163 if (unicode) ok_( __FILE__, line )( !wcscmp( list_str, expect_str ), "got %s\n", debugstr_w(list_str) );
164 else ok_( __FILE__, line )( !strcmp( list_str, expect_str ), "got %s\n", debugstr_a(list_str) );
168 #define check_candidate_form( a, b ) check_candidate_form_( __LINE__, a, b )
169 static void check_candidate_form_( int line, CANDIDATEFORM *form, const CANDIDATEFORM *expect )
171 check_member_( __FILE__, line, *form, *expect, "%#lx", dwIndex );
172 check_member_( __FILE__, line, *form, *expect, "%#lx", dwStyle );
173 check_member_point_( __FILE__, line, *form, *expect, ptCurrentPos );
174 check_member_rect_( __FILE__, line, *form, *expect, rcArea );
177 #define check_composition_form( a, b ) check_composition_form_( __LINE__, a, b )
178 static void check_composition_form_( int line, COMPOSITIONFORM *form, const COMPOSITIONFORM *expect )
180 check_member_( __FILE__, line, *form, *expect, "%#lx", dwStyle );
181 check_member_point_( __FILE__, line, *form, *expect, ptCurrentPos );
182 check_member_rect_( __FILE__, line, *form, *expect, rcArea );
185 #define check_logfont_w( a, b ) check_logfont_w_( __LINE__, a, b )
186 static void check_logfont_w_( int line, LOGFONTW *font, const LOGFONTW *expect )
188 check_member_( __FILE__, line, *font, *expect, "%lu", lfHeight );
189 check_member_( __FILE__, line, *font, *expect, "%lu", lfWidth );
190 check_member_( __FILE__, line, *font, *expect, "%lu", lfEscapement );
191 check_member_( __FILE__, line, *font, *expect, "%lu", lfOrientation );
192 check_member_( __FILE__, line, *font, *expect, "%lu", lfWeight );
193 check_member_( __FILE__, line, *font, *expect, "%u", lfItalic );
194 check_member_( __FILE__, line, *font, *expect, "%u", lfUnderline );
195 check_member_( __FILE__, line, *font, *expect, "%u", lfStrikeOut );
196 check_member_( __FILE__, line, *font, *expect, "%u", lfCharSet );
197 check_member_( __FILE__, line, *font, *expect, "%u", lfOutPrecision );
198 check_member_( __FILE__, line, *font, *expect, "%u", lfClipPrecision );
199 check_member_( __FILE__, line, *font, *expect, "%u", lfQuality );
200 check_member_( __FILE__, line, *font, *expect, "%u", lfPitchAndFamily );
201 check_member_wstr_( __FILE__, line, *font, *expect, lfFaceName );
204 #define check_logfont_a( a, b ) check_logfont_a_( __LINE__, a, b )
205 static void check_logfont_a_( int line, LOGFONTA *font, const LOGFONTA *expect )
207 check_member_( __FILE__, line, *font, *expect, "%lu", lfHeight );
208 check_member_( __FILE__, line, *font, *expect, "%lu", lfWidth );
209 check_member_( __FILE__, line, *font, *expect, "%lu", lfEscapement );
210 check_member_( __FILE__, line, *font, *expect, "%lu", lfOrientation );
211 check_member_( __FILE__, line, *font, *expect, "%lu", lfWeight );
212 check_member_( __FILE__, line, *font, *expect, "%u", lfItalic );
213 check_member_( __FILE__, line, *font, *expect, "%u", lfUnderline );
214 check_member_( __FILE__, line, *font, *expect, "%u", lfStrikeOut );
215 check_member_( __FILE__, line, *font, *expect, "%u", lfCharSet );
216 check_member_( __FILE__, line, *font, *expect, "%u", lfOutPrecision );
217 check_member_( __FILE__, line, *font, *expect, "%u", lfClipPrecision );
218 check_member_( __FILE__, line, *font, *expect, "%u", lfQuality );
219 check_member_( __FILE__, line, *font, *expect, "%u", lfPitchAndFamily );
220 check_member_str_( __FILE__, line, *font, *expect, lfFaceName );
223 #define DEFINE_EXPECT(func) \
224 static BOOL expect_ ## func = FALSE, called_ ## func = FALSE, enabled_ ## func = FALSE
226 #define SET_EXPECT(func) \
227 expect_ ## func = TRUE
229 #define CHECK_EXPECT2(func) \
230 do { \
231 if (enabled_ ## func) {\
232 ok(expect_ ##func, "unexpected call " #func "\n"); \
233 called_ ## func = TRUE; \
235 }while(0)
237 #define CHECK_EXPECT(func) \
238 do { \
239 CHECK_EXPECT2(func); \
240 expect_ ## func = FALSE; \
241 }while(0)
243 #define CHECK_CALLED(func) \
244 do { \
245 ok(called_ ## func, "expected " #func "\n"); \
246 expect_ ## func = called_ ## func = FALSE; \
247 }while(0)
249 #define SET_ENABLE(func, val) \
250 enabled_ ## func = (val)
252 DEFINE_EXPECT(WM_IME_SETCONTEXT_DEACTIVATE);
253 DEFINE_EXPECT(WM_IME_SETCONTEXT_ACTIVATE);
255 #define process_messages() process_messages_(0)
256 static void process_messages_(HWND hwnd)
258 MSG msg;
260 while (PeekMessageA( &msg, hwnd, 0, 0, PM_REMOVE ))
262 TranslateMessage( &msg );
263 DispatchMessageA( &msg );
267 /* try to make sure pending X events have been processed before continuing */
268 #define flush_events() flush_events_( 100, 200 )
269 static void flush_events_( int min_timeout, int max_timeout )
271 DWORD time = GetTickCount() + max_timeout;
272 MSG msg;
274 while (max_timeout > 0)
276 if (MsgWaitForMultipleObjects( 0, NULL, FALSE, min_timeout, QS_ALLINPUT ) == WAIT_TIMEOUT) break;
277 while (PeekMessageA( &msg, 0, 0, 0, PM_REMOVE ))
279 TranslateMessage( &msg );
280 DispatchMessageA( &msg );
282 max_timeout = time - GetTickCount();
286 #define ime_trace( msg, ... ) if (winetest_debug > 1) trace( "%04lx:%s " msg, GetCurrentThreadId(), __func__, ## __VA_ARGS__ )
288 static BOOL ImeSelect_init_status;
289 static BOOL todo_ImeInquire;
290 DEFINE_EXPECT( ImeInquire );
291 static BOOL todo_ImeDestroy;
292 DEFINE_EXPECT( ImeDestroy );
293 DEFINE_EXPECT( ImeEscape );
294 DEFINE_EXPECT( ImeEnumRegisterWord );
295 DEFINE_EXPECT( ImeRegisterWord );
296 DEFINE_EXPECT( ImeGetRegisterWordStyle );
297 DEFINE_EXPECT( ImeUnregisterWord );
298 static BOOL todo_ImeSetCompositionString;
299 DEFINE_EXPECT( ImeSetCompositionString );
300 static BOOL todo_IME_DLL_PROCESS_ATTACH;
301 DEFINE_EXPECT( IME_DLL_PROCESS_ATTACH );
302 static BOOL todo_IME_DLL_PROCESS_DETACH;
303 DEFINE_EXPECT( IME_DLL_PROCESS_DETACH );
305 static IMEINFO ime_info;
306 static UINT ime_count;
307 static WCHAR ime_path[MAX_PATH];
308 static HIMC default_himc;
309 static HKL default_hkl, wineime_hkl;
310 static HKL expect_ime = (HKL)(int)0xe020047f;
312 enum ime_function
314 IME_SELECT = 1,
315 IME_NOTIFY,
316 IME_PROCESS_KEY,
317 IME_TO_ASCII_EX,
318 IME_SET_ACTIVE_CONTEXT,
319 MSG_IME_UI,
320 MSG_TEST_WIN,
323 struct ime_call
325 HKL hkl;
326 HIMC himc;
327 enum ime_function func;
329 union
331 int select;
332 struct
334 int action;
335 int index;
336 int value;
337 } notify;
338 struct
340 WORD vkey;
341 LPARAM lparam;
342 } process_key;
343 struct
345 UINT vkey;
346 UINT vsc;
347 UINT flags;
348 } to_ascii_ex;
349 struct
351 int flag;
352 } set_active_context;
353 struct
355 UINT msg;
356 WPARAM wparam;
357 LPARAM lparam;
358 } message;
361 BOOL todo;
362 BOOL broken;
363 BOOL flaky_himc;
364 BOOL todo_value;
367 struct ime_call empty_sequence[] = {{0}};
368 static struct ime_call ime_calls[1024];
369 static ULONG ime_call_count;
371 #define ok_call( a, b ) ok_call_( __FILE__, __LINE__, a, b )
372 static int ok_call_( const char *file, int line, const struct ime_call *expected, const struct ime_call *received )
374 int ret;
376 if ((ret = expected->func - received->func)) goto done;
377 /* Wine doesn't allocate HIMC in a deterministic order, ignore them when they are enumerated */
378 if (expected->flaky_himc && (ret = !!(UINT_PTR)expected->himc - !!(UINT_PTR)received->himc)) goto done;
379 if (!expected->flaky_himc && (ret = (UINT_PTR)expected->himc - (UINT_PTR)received->himc)) goto done;
380 if ((ret = (UINT)(UINT_PTR)expected->hkl - (UINT)(UINT_PTR)received->hkl)) goto done;
381 switch (expected->func)
383 case IME_SELECT:
384 if ((ret = expected->select - received->select)) goto done;
385 break;
386 case IME_NOTIFY:
387 if ((ret = expected->notify.action - received->notify.action)) goto done;
388 if ((ret = expected->notify.index - received->notify.index)) goto done;
389 if ((ret = expected->notify.value - received->notify.value)) goto done;
390 break;
391 case IME_PROCESS_KEY:
392 if ((ret = expected->process_key.vkey - received->process_key.vkey)) goto done;
393 if ((ret = expected->process_key.lparam - received->process_key.lparam)) goto done;
394 break;
395 case IME_TO_ASCII_EX:
396 if ((ret = expected->to_ascii_ex.vkey - received->to_ascii_ex.vkey)) goto done;
397 if ((ret = expected->to_ascii_ex.vsc - received->to_ascii_ex.vsc)) goto done;
398 if ((ret = expected->to_ascii_ex.flags - received->to_ascii_ex.flags)) goto done;
399 break;
400 case IME_SET_ACTIVE_CONTEXT:
401 if ((ret = expected->set_active_context.flag - received->set_active_context.flag)) goto done;
402 break;
403 case MSG_IME_UI:
404 case MSG_TEST_WIN:
405 if ((ret = expected->message.msg - received->message.msg)) goto done;
406 if ((ret = (expected->message.wparam - received->message.wparam))) goto done;
407 if ((ret = (expected->message.lparam - received->message.lparam))) goto done;
408 break;
411 done:
412 if (ret && broken( expected->broken )) return ret;
414 switch (received->func)
416 case IME_SELECT:
417 todo_wine_if( expected->todo || expected->todo_value )
418 ok_(file, line)( !ret, "got hkl %p, himc %p, IME_SELECT select %u\n", received->hkl, received->himc, received->select );
419 return ret;
420 case IME_NOTIFY:
421 todo_wine_if( expected->todo || expected->todo_value )
422 ok_(file, line)( !ret, "got hkl %p, himc %p, IME_NOTIFY action %#x, index %#x, value %#x\n",
423 received->hkl, received->himc, received->notify.action, received->notify.index,
424 received->notify.value );
425 return ret;
426 case IME_PROCESS_KEY:
427 todo_wine_if( expected->todo || expected->todo_value )
428 ok_(file, line)( !ret, "got hkl %p, himc %p, IME_PROCESS_KEY vkey %#x, lparam %#Ix\n",
429 received->hkl, received->himc, received->process_key.vkey, received->process_key.lparam );
430 return ret;
431 case IME_TO_ASCII_EX:
432 todo_wine_if( expected->todo || expected->todo_value )
433 ok_(file, line)( !ret, "got hkl %p, himc %p, IME_TO_ASCII_EX vkey %#x, vsc %#x, flags %#x\n",
434 received->hkl, received->himc, received->to_ascii_ex.vkey, received->to_ascii_ex.vsc,
435 received->to_ascii_ex.flags );
436 return ret;
437 case IME_SET_ACTIVE_CONTEXT:
438 todo_wine_if( expected->todo || expected->todo_value )
439 ok_(file, line)( !ret, "got hkl %p, himc %p, IME_SET_ACTIVE_CONTEXT flag %u\n", received->hkl, received->himc,
440 received->set_active_context.flag );
441 return ret;
442 case MSG_IME_UI:
443 todo_wine_if( expected->todo || expected->todo_value )
444 ok_(file, line)( !ret, "got hkl %p, himc %p, MSG_IME_UI msg %#x, wparam %#Ix, lparam %#Ix\n", received->hkl,
445 received->himc, received->message.msg, received->message.wparam, received->message.lparam );
446 return ret;
447 case MSG_TEST_WIN:
448 todo_wine_if( expected->todo || expected->todo_value )
449 ok_(file, line)( !ret, "got hkl %p, himc %p, MSG_TEST_WIN msg %#x, wparam %#Ix, lparam %#Ix\n", received->hkl,
450 received->himc, received->message.msg, received->message.wparam, received->message.lparam );
451 return ret;
454 switch (expected->func)
456 case IME_SELECT:
457 todo_wine_if( expected->todo || expected->todo_value )
458 ok_(file, line)( !ret, "hkl %p, himc %p, IME_SELECT select %u\n", expected->hkl, expected->himc, expected->select );
459 break;
460 case IME_NOTIFY:
461 todo_wine_if( expected->todo || expected->todo_value )
462 ok_(file, line)( !ret, "hkl %p, himc %p, IME_NOTIFY action %#x, index %#x, value %#x\n",
463 expected->hkl, expected->himc, expected->notify.action, expected->notify.index,
464 expected->notify.value );
465 break;
466 case IME_PROCESS_KEY:
467 todo_wine_if( expected->todo || expected->todo_value )
468 ok_(file, line)( !ret, "hkl %p, himc %p, IME_PROCESS_KEY vkey %#x, lparam %#Ix\n",
469 expected->hkl, expected->himc, expected->process_key.vkey, expected->process_key.lparam );
470 break;
471 case IME_TO_ASCII_EX:
472 todo_wine_if( expected->todo || expected->todo_value )
473 ok_(file, line)( !ret, "hkl %p, himc %p, IME_TO_ASCII_EX vkey %#x, vsc %#x, flags %#x\n",
474 expected->hkl, expected->himc, expected->to_ascii_ex.vkey, expected->to_ascii_ex.vsc,
475 expected->to_ascii_ex.flags );
476 break;
477 case IME_SET_ACTIVE_CONTEXT:
478 todo_wine_if( expected->todo || expected->todo_value )
479 ok_(file, line)( !ret, "hkl %p, himc %p, IME_SET_ACTIVE_CONTEXT flag %u\n", expected->hkl, expected->himc,
480 expected->set_active_context.flag );
481 break;
482 case MSG_IME_UI:
483 todo_wine_if( expected->todo || expected->todo_value )
484 ok_(file, line)( !ret, "hkl %p, himc %p, MSG_IME_UI msg %#x, wparam %#Ix, lparam %#Ix\n", expected->hkl,
485 expected->himc, expected->message.msg, expected->message.wparam, expected->message.lparam );
486 break;
487 case MSG_TEST_WIN:
488 todo_wine_if( expected->todo || expected->todo_value )
489 ok_(file, line)( !ret, "hkl %p, himc %p, MSG_TEST_WIN msg %#x, wparam %#Ix, lparam %#Ix\n", expected->hkl,
490 expected->himc, expected->message.msg, expected->message.wparam, expected->message.lparam );
491 break;
494 return 0;
497 #define ok_seq( a ) ok_seq_( __FILE__, __LINE__, a, #a )
498 static void ok_seq_( const char *file, int line, const struct ime_call *expected, const char *context )
500 const struct ime_call *received = ime_calls;
501 UINT i = 0, ret;
503 while (expected->func || received->func)
505 winetest_push_context( "%u%s%s", i++, !expected->func ? " (spurious)" : "",
506 !received->func ? " (missing)" : "" );
507 ret = ok_call_( file, line, expected, received );
508 if (ret && expected->todo && expected->func &&
509 !strcmp( winetest_platform, "wine" ))
510 expected++;
511 else if (ret && broken(expected->broken))
512 expected++;
513 else
515 if (expected->func) expected++;
516 if (received->func) received++;
518 winetest_pop_context();
521 memset( ime_calls, 0, sizeof(ime_calls) );
522 ime_call_count = 0;
525 static BOOL check_WM_SHOWWINDOW;
527 static BOOL ignore_message( UINT msg )
529 switch (msg)
531 case WM_IME_STARTCOMPOSITION:
532 case WM_IME_ENDCOMPOSITION:
533 case WM_IME_COMPOSITION:
534 case WM_IME_SETCONTEXT:
535 case WM_IME_NOTIFY:
536 case WM_IME_CONTROL:
537 case WM_IME_COMPOSITIONFULL:
538 case WM_IME_SELECT:
539 case WM_IME_CHAR:
540 case 0x287:
541 case WM_IME_REQUEST:
542 case WM_IME_KEYDOWN:
543 case WM_IME_KEYUP:
544 return FALSE;
545 case WM_SHOWWINDOW:
546 return !check_WM_SHOWWINDOW;
547 default:
548 return TRUE;
552 static LRESULT CALLBACK ime_ui_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
554 struct ime_call call =
556 .hkl = GetKeyboardLayout( 0 ), .himc = (HIMC)GetWindowLongPtrW( hwnd, IMMGWL_IMC ),
557 .func = MSG_IME_UI, .message = {.msg = msg, .wparam = wparam, .lparam = lparam}
559 LONG_PTR ptr;
561 ime_trace( "hwnd %p, msg %#x, wparam %#Ix, lparam %#Ix\n", hwnd, msg, wparam, lparam );
563 if (ignore_message( msg )) return DefWindowProcW( hwnd, msg, wparam, lparam );
565 ptr = GetWindowLongPtrW( hwnd, IMMGWL_PRIVATE );
566 ok( !ptr, "got IMMGWL_PRIVATE %#Ix\n", ptr );
568 ime_calls[ime_call_count++] = call;
569 return DefWindowProcW( hwnd, msg, wparam, lparam );
572 static LRESULT CALLBACK test_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
574 struct ime_call call =
576 .hkl = GetKeyboardLayout( 0 ), .himc = ImmGetContext( hwnd ),
577 .func = MSG_TEST_WIN, .message = {.msg = msg, .wparam = wparam, .lparam = lparam}
580 ime_trace( "hwnd %p, msg %#x, wparam %#Ix, lparam %#Ix\n", hwnd, msg, wparam, lparam );
582 if (ignore_message( msg )) return DefWindowProcW( hwnd, msg, wparam, lparam );
584 ime_calls[ime_call_count++] = call;
585 return DefWindowProcW( hwnd, msg, wparam, lparam );
588 static WNDCLASSEXW ime_ui_class =
590 .cbSize = sizeof(WNDCLASSEXW),
591 .style = CS_IME,
592 .lpfnWndProc = ime_ui_window_proc,
593 .cbWndExtra = 2 * sizeof(LONG_PTR),
594 .lpszClassName = L"WineTestIME",
597 static WNDCLASSEXW test_class =
599 .cbSize = sizeof(WNDCLASSEXW),
600 .lpfnWndProc = test_window_proc,
601 .lpszClassName = L"WineTest",
605 * msgspy - record and analyse message traces sent to a certain window
607 typedef struct _msgs {
608 CWPSTRUCT msg;
609 BOOL post;
610 } imm_msgs;
612 static struct _msg_spy {
613 HWND hwnd;
614 HHOOK get_msg_hook;
615 HHOOK call_wnd_proc_hook;
616 imm_msgs msgs[64];
617 unsigned int i_msg;
618 } msg_spy;
620 typedef struct
622 DWORD type;
623 union
625 MOUSEINPUT mi;
626 KEYBDINPUT ki;
627 HARDWAREINPUT hi;
628 } u;
629 } TEST_INPUT;
631 static UINT (WINAPI *pSendInput) (UINT, INPUT*, size_t);
633 static LRESULT CALLBACK get_msg_filter(int nCode, WPARAM wParam, LPARAM lParam)
635 if (HC_ACTION == nCode) {
636 MSG *msg = (MSG*)lParam;
638 if ((msg->hwnd == msg_spy.hwnd || msg_spy.hwnd == NULL) &&
639 (msg_spy.i_msg < ARRAY_SIZE(msg_spy.msgs)))
641 msg_spy.msgs[msg_spy.i_msg].msg.hwnd = msg->hwnd;
642 msg_spy.msgs[msg_spy.i_msg].msg.message = msg->message;
643 msg_spy.msgs[msg_spy.i_msg].msg.wParam = msg->wParam;
644 msg_spy.msgs[msg_spy.i_msg].msg.lParam = msg->lParam;
645 msg_spy.msgs[msg_spy.i_msg].post = TRUE;
646 msg_spy.i_msg++;
650 return CallNextHookEx(msg_spy.get_msg_hook, nCode, wParam, lParam);
653 static LRESULT CALLBACK call_wnd_proc_filter(int nCode, WPARAM wParam,
654 LPARAM lParam)
656 if (HC_ACTION == nCode) {
657 CWPSTRUCT *cwp = (CWPSTRUCT*)lParam;
659 if (((cwp->hwnd == msg_spy.hwnd || msg_spy.hwnd == NULL)) &&
660 (msg_spy.i_msg < ARRAY_SIZE(msg_spy.msgs)))
662 memcpy(&msg_spy.msgs[msg_spy.i_msg].msg, cwp, sizeof(msg_spy.msgs[0].msg));
663 msg_spy.msgs[msg_spy.i_msg].post = FALSE;
664 msg_spy.i_msg++;
668 return CallNextHookEx(msg_spy.call_wnd_proc_hook, nCode, wParam, lParam);
671 static void msg_spy_pump_msg_queue(void) {
672 MSG msg;
674 while(PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) {
675 TranslateMessage(&msg);
676 DispatchMessageW(&msg);
679 return;
682 static void msg_spy_flush_msgs(void) {
683 msg_spy_pump_msg_queue();
684 msg_spy.i_msg = 0;
687 static imm_msgs* msg_spy_find_next_msg(UINT message, UINT *start) {
688 UINT i;
690 msg_spy_pump_msg_queue();
692 if (msg_spy.i_msg >= ARRAY_SIZE(msg_spy.msgs))
693 fprintf(stdout, "%s:%d: msg_spy: message buffer overflow!\n",
694 __FILE__, __LINE__);
696 for (i = *start; i < msg_spy.i_msg; i++)
697 if (msg_spy.msgs[i].msg.message == message)
699 *start = i+1;
700 return &msg_spy.msgs[i];
703 return NULL;
706 static imm_msgs* msg_spy_find_msg(UINT message) {
707 UINT i = 0;
709 return msg_spy_find_next_msg(message, &i);
712 static void msg_spy_init(HWND hwnd) {
713 msg_spy.hwnd = hwnd;
714 msg_spy.get_msg_hook =
715 SetWindowsHookExW(WH_GETMESSAGE, get_msg_filter, GetModuleHandleW(NULL),
716 GetCurrentThreadId());
717 msg_spy.call_wnd_proc_hook =
718 SetWindowsHookExW(WH_CALLWNDPROC, call_wnd_proc_filter,
719 GetModuleHandleW(NULL), GetCurrentThreadId());
720 msg_spy.i_msg = 0;
722 msg_spy_flush_msgs();
725 static void msg_spy_cleanup(void) {
726 if (msg_spy.get_msg_hook)
727 UnhookWindowsHookEx(msg_spy.get_msg_hook);
728 if (msg_spy.call_wnd_proc_hook)
729 UnhookWindowsHookEx(msg_spy.call_wnd_proc_hook);
730 memset(&msg_spy, 0, sizeof(msg_spy));
734 * imm32 test cases - Issue some IMM commands on a dummy window and analyse the
735 * messages being sent to this window in response.
737 static const char wndcls[] = "winetest_imm32_wndcls";
738 static enum { PHASE_UNKNOWN, FIRST_WINDOW, SECOND_WINDOW,
739 CREATE_CANCEL, NCCREATE_CANCEL, IME_DISABLED } test_phase;
740 static HWND hwnd, child;
742 static HWND get_ime_window(void);
744 static void load_resource( const WCHAR *name, WCHAR *filename )
746 static WCHAR path[MAX_PATH];
747 DWORD written;
748 HANDLE file;
749 HRSRC res;
750 void *ptr;
752 GetTempPathW( ARRAY_SIZE(path), path );
753 GetTempFileNameW( path, name, 0, filename );
755 file = CreateFileW( filename, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0 );
756 ok( file != INVALID_HANDLE_VALUE, "failed to create %s, error %lu\n", debugstr_w(filename), GetLastError() );
758 res = FindResourceW( NULL, name, L"TESTDLL" );
759 ok( res != 0, "couldn't find resource\n" );
760 ptr = LockResource( LoadResource( GetModuleHandleW( NULL ), res ) );
761 WriteFile( file, ptr, SizeofResource( GetModuleHandleW( NULL ), res ), &written, NULL );
762 ok( written == SizeofResource( GetModuleHandleW( NULL ), res ), "couldn't write resource\n" );
763 CloseHandle( file );
766 static LRESULT WINAPI wndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
768 HWND default_ime_wnd;
769 switch (msg)
771 case WM_IME_SETCONTEXT:
772 if (wParam) CHECK_EXPECT(WM_IME_SETCONTEXT_ACTIVATE);
773 else CHECK_EXPECT(WM_IME_SETCONTEXT_DEACTIVATE);
774 ok(lParam == ISC_SHOWUIALL || !lParam, "lParam = %Ix\n", lParam);
775 return TRUE;
776 case WM_NCCREATE:
777 default_ime_wnd = get_ime_window();
778 switch(test_phase) {
779 case FIRST_WINDOW:
780 case IME_DISABLED:
781 ok(!default_ime_wnd, "expected no IME windows\n");
782 break;
783 case SECOND_WINDOW:
784 ok(default_ime_wnd != NULL, "expected IME window existence\n");
785 break;
786 default:
787 break; /* do nothing */
789 if (test_phase == NCCREATE_CANCEL)
790 return FALSE;
791 return TRUE;
792 case WM_NCCALCSIZE:
793 default_ime_wnd = get_ime_window();
794 switch(test_phase) {
795 case FIRST_WINDOW:
796 case SECOND_WINDOW:
797 case CREATE_CANCEL:
798 ok(default_ime_wnd != NULL, "expected IME window existence\n");
799 break;
800 case IME_DISABLED:
801 ok(!default_ime_wnd, "expected no IME windows\n");
802 break;
803 default:
804 break; /* do nothing */
806 break;
807 case WM_CREATE:
808 default_ime_wnd = get_ime_window();
809 switch(test_phase) {
810 case FIRST_WINDOW:
811 case SECOND_WINDOW:
812 case CREATE_CANCEL:
813 ok(default_ime_wnd != NULL, "expected IME window existence\n");
814 break;
815 case IME_DISABLED:
816 ok(!default_ime_wnd, "expected no IME windows\n");
817 break;
818 default:
819 break; /* do nothing */
821 if (test_phase == CREATE_CANCEL)
822 return -1;
823 return TRUE;
826 return DefWindowProcA(hWnd,msg,wParam,lParam);
829 static BOOL is_ime_enabled(void)
831 HIMC himc;
832 HWND wnd;
834 wnd = CreateWindowA("static", "static", 0, 0, 0, 0, 0, 0, 0, 0, 0);
835 ok(wnd != NULL, "CreateWindow failed\n");
837 himc = ImmGetContext(wnd);
838 if (!himc)
840 DestroyWindow(wnd);
841 return FALSE;
844 ImmReleaseContext(wnd, himc);
845 DestroyWindow(wnd);
846 return TRUE;
849 static BOOL init(void) {
850 WNDCLASSEXA wc;
851 HMODULE hmod,huser;
853 hmod = GetModuleHandleA("imm32.dll");
854 huser = GetModuleHandleA("user32");
855 pImmAssociateContextEx = (void*)GetProcAddress(hmod, "ImmAssociateContextEx");
856 pImmIsUIMessageA = (void*)GetProcAddress(hmod, "ImmIsUIMessageA");
857 pSendInput = (void*)GetProcAddress(huser, "SendInput");
858 pNtUserAssociateInputContext = (void*)GetProcAddress(GetModuleHandleW(L"win32u.dll"),
859 "NtUserAssociateInputContext");
861 wc.cbSize = sizeof(WNDCLASSEXA);
862 wc.style = 0;
863 wc.lpfnWndProc = wndProc;
864 wc.cbClsExtra = 0;
865 wc.cbWndExtra = 0;
866 wc.hInstance = GetModuleHandleA(NULL);
867 wc.hIcon = LoadIconA(NULL, (LPCSTR)IDI_APPLICATION);
868 wc.hCursor = LoadCursorA(NULL, (LPCSTR)IDC_ARROW);
869 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
870 wc.lpszMenuName = NULL;
871 wc.lpszClassName = wndcls;
872 wc.hIconSm = LoadIconA(NULL, (LPCSTR)IDI_APPLICATION);
874 if (!RegisterClassExA(&wc))
875 return FALSE;
877 hwnd = CreateWindowExA(WS_EX_CLIENTEDGE, wndcls, "Wine imm32.dll test",
878 WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
879 240, 120, NULL, NULL, GetModuleHandleW(NULL), NULL);
880 if (!hwnd)
881 return FALSE;
883 child = CreateWindowA("edit", "edit", WS_CHILD | WS_VISIBLE, 0, 0, 50, 50, hwnd, 0, 0, 0);
884 if (!child)
885 return FALSE;
887 ShowWindow(hwnd, SW_SHOWNORMAL);
888 UpdateWindow(hwnd);
890 msg_spy_init(hwnd);
892 return TRUE;
895 static void cleanup(void) {
896 msg_spy_cleanup();
897 if (hwnd)
898 DestroyWindow(hwnd);
899 UnregisterClassA(wndcls, GetModuleHandleW(NULL));
902 static void test_ImmNotifyIME(void) {
903 static const char string[] = "wine";
904 char resstr[16] = "";
905 HIMC imc;
906 BOOL ret;
908 imc = ImmGetContext(hwnd);
909 msg_spy_flush_msgs();
911 ret = ImmNotifyIME(imc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
912 ok(broken(!ret) ||
913 ret, /* Vista+ */
914 "Canceling an empty composition string should succeed.\n");
915 ok(!msg_spy_find_msg(WM_IME_COMPOSITION), "Windows does not post "
916 "WM_IME_COMPOSITION in response to NI_COMPOSITIONSTR / CPS_CANCEL, if "
917 "the composition string being canceled is empty.\n");
919 ImmSetCompositionStringA(imc, SCS_SETSTR, string, sizeof(string), NULL, 0);
920 msg_spy_flush_msgs();
922 ImmNotifyIME(imc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
923 msg_spy_flush_msgs();
925 /* behavior differs between win9x and NT */
926 ret = ImmGetCompositionStringA(imc, GCS_COMPSTR, resstr, sizeof(resstr));
927 ok(!ret, "After being cancelled the composition string is empty.\n");
929 msg_spy_flush_msgs();
931 ret = ImmNotifyIME(imc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
932 ok(broken(!ret) ||
933 ret, /* Vista+ */
934 "Canceling an empty composition string should succeed.\n");
935 ok(!msg_spy_find_msg(WM_IME_COMPOSITION), "Windows does not post "
936 "WM_IME_COMPOSITION in response to NI_COMPOSITIONSTR / CPS_CANCEL, if "
937 "the composition string being canceled is empty.\n");
939 msg_spy_flush_msgs();
940 ImmReleaseContext(hwnd, imc);
942 imc = ImmCreateContext();
943 ImmDestroyContext(imc);
945 SetLastError(0xdeadbeef);
946 ret = ImmNotifyIME((HIMC)0xdeadcafe, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
947 ok (ret == 0, "Bad IME should return 0\n");
948 ret = GetLastError();
949 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret);
950 SetLastError(0xdeadbeef);
951 ret = ImmNotifyIME(0x00000000, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
952 ok (ret == 0, "NULL IME should return 0\n");
953 ret = GetLastError();
954 ok(ret == ERROR_SUCCESS, "wrong last error %08x!\n", ret);
955 SetLastError(0xdeadbeef);
956 ret = ImmNotifyIME(imc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
957 ok (ret == 0, "Destroyed IME should return 0\n");
958 ret = GetLastError();
959 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret);
963 static struct {
964 WNDPROC old_wnd_proc;
965 BOOL catch_result_str;
966 BOOL catch_ime_char;
967 DWORD start;
968 DWORD timer_id;
969 } ime_composition_test;
971 static LRESULT WINAPI test_ime_wnd_proc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
973 switch (msg)
975 case WM_IME_COMPOSITION:
976 if ((lParam & GCS_RESULTSTR) && !ime_composition_test.catch_result_str) {
977 HWND hwndIme;
978 WCHAR wstring[20];
979 HIMC imc;
980 LONG size;
981 LRESULT ret;
983 hwndIme = ImmGetDefaultIMEWnd(hWnd);
984 ok(hwndIme != NULL, "expected IME window existence\n");
986 ok(!ime_composition_test.catch_ime_char, "WM_IME_CHAR is sent\n");
987 ret = CallWindowProcA(ime_composition_test.old_wnd_proc,
988 hWnd, msg, wParam, lParam);
989 ok(ime_composition_test.catch_ime_char, "WM_IME_CHAR isn't sent\n");
991 ime_composition_test.catch_ime_char = FALSE;
992 SendMessageA(hwndIme, msg, wParam, lParam);
993 ok(!ime_composition_test.catch_ime_char, "WM_IME_CHAR is sent\n");
995 imc = ImmGetContext(hWnd);
996 size = ImmGetCompositionStringW(imc, GCS_RESULTSTR,
997 wstring, sizeof(wstring));
998 ok(size > 0, "ImmGetCompositionString(GCS_RESULTSTR) is %ld\n", size);
999 ImmReleaseContext(hwnd, imc);
1001 ime_composition_test.catch_result_str = TRUE;
1002 return ret;
1004 break;
1005 case WM_IME_CHAR:
1006 if (!ime_composition_test.catch_result_str)
1007 ime_composition_test.catch_ime_char = TRUE;
1008 break;
1009 case WM_TIMER:
1010 if (wParam == ime_composition_test.timer_id) {
1011 HWND parent = GetParent(hWnd);
1012 char title[64];
1013 int left = 20 - (GetTickCount() - ime_composition_test.start) / 1000;
1014 wsprintfA(title, "%sLeft %d sec. - IME composition test",
1015 ime_composition_test.catch_result_str ? "[*] " : "", left);
1016 SetWindowTextA(parent, title);
1017 if (left <= 0)
1018 DestroyWindow(parent);
1019 else
1020 SetTimer(hWnd, wParam, 100, NULL);
1021 return TRUE;
1023 break;
1025 return CallWindowProcA(ime_composition_test.old_wnd_proc,
1026 hWnd, msg, wParam, lParam);
1029 static void test_SCS_SETSTR(void)
1031 HIMC imc;
1032 static const WCHAR string[] = {'w','i','n','e',0x65e5,0x672c,0x8a9e};
1033 char cstring[20];
1034 WCHAR wstring[20];
1035 LONG len;
1036 LONG alen,wlen;
1037 BOOL ret;
1038 DWORD prop;
1040 imc = ImmGetContext(hwnd);
1041 ret = ImmSetCompositionStringW(imc, SCS_SETSTR, string, sizeof(string), NULL,0);
1042 if (!ret) {
1043 win_skip("Composition isn't supported\n");
1044 ImmReleaseContext(hwnd, imc);
1045 return;
1047 msg_spy_flush_msgs();
1049 alen = ImmGetCompositionStringA(imc, GCS_COMPSTR, cstring, 20);
1050 wlen = ImmGetCompositionStringW(imc, GCS_COMPSTR, wstring, 20);
1051 /* windows machines without any IME installed just return 0 above */
1052 if( alen && wlen)
1054 len = ImmGetCompositionStringW(imc, GCS_COMPATTR, NULL, 0);
1055 ok(len*sizeof(WCHAR)==wlen,"GCS_COMPATTR(W) not returning correct count\n");
1056 len = ImmGetCompositionStringA(imc, GCS_COMPATTR, NULL, 0);
1057 ok(len==alen,"GCS_COMPATTR(A) not returning correct count\n");
1059 /* Get strings with exactly matching buffer sizes. */
1060 memset(wstring, 0x1a, sizeof(wstring));
1061 memset(cstring, 0x1a, sizeof(cstring));
1063 len = ImmGetCompositionStringA(imc, GCS_COMPSTR, cstring, alen);
1064 ok(len == alen, "Unexpected length %ld.\n", len);
1065 ok(cstring[alen] == 0x1a, "Unexpected buffer contents.\n");
1067 len = ImmGetCompositionStringW(imc, GCS_COMPSTR, wstring, wlen);
1068 ok(len == wlen, "Unexpected length %ld.\n", len);
1069 ok(wstring[wlen/sizeof(WCHAR)] == 0x1a1a, "Unexpected buffer contents.\n");
1071 /* Get strings with exactly smaller buffer sizes. */
1072 memset(wstring, 0x1a, sizeof(wstring));
1073 memset(cstring, 0x1a, sizeof(cstring));
1075 /* Returns 0 but still fills buffer. */
1076 len = ImmGetCompositionStringA(imc, GCS_COMPSTR, cstring, alen - 1);
1077 ok(!len, "Unexpected length %ld.\n", len);
1078 ok(cstring[0] == 'w', "Unexpected buffer contents %s.\n", cstring);
1080 len = ImmGetCompositionStringW(imc, GCS_COMPSTR, wstring, wlen - 1);
1081 ok(len == wlen - 1, "Unexpected length %ld.\n", len);
1082 ok(!memcmp(wstring, string, wlen - 1), "Unexpected buffer contents.\n");
1084 /* Get the size of the required output buffer. */
1085 memset(wstring, 0x1a, sizeof(wstring));
1086 memset(cstring, 0x1a, sizeof(cstring));
1088 len = ImmGetCompositionStringA(imc, GCS_COMPSTR, cstring, 0);
1089 ok(len == alen, "Unexpected length %ld.\n", len);
1090 ok(cstring[0] == 0x1a, "Unexpected buffer contents %s.\n", cstring);
1092 len = ImmGetCompositionStringW(imc, GCS_COMPSTR, wstring, 0);
1093 ok(len == wlen, "Unexpected length %ld.\n", len);
1094 ok(wstring[0] == 0x1a1a, "Unexpected buffer contents.\n");
1096 else
1097 win_skip("Composition string isn't available\n");
1099 ImmReleaseContext(hwnd, imc);
1101 /* Test composition results input by IMM API */
1102 prop = ImmGetProperty(GetKeyboardLayout(0), IGP_SETCOMPSTR);
1103 if (!(prop & SCS_CAP_COMPSTR)) {
1104 /* Wine's IME doesn't support SCS_SETSTR in ImmSetCompositionString */
1105 skip("This IME doesn't support SCS_SETSTR\n");
1107 else {
1108 ime_composition_test.old_wnd_proc =
1109 (WNDPROC)SetWindowLongPtrA(hwnd, GWLP_WNDPROC,
1110 (LONG_PTR)test_ime_wnd_proc);
1111 imc = ImmGetContext(hwnd);
1112 msg_spy_flush_msgs();
1114 ret = ImmSetCompositionStringW(imc, SCS_SETSTR,
1115 string, sizeof(string), NULL,0);
1116 ok(ret, "ImmSetCompositionStringW failed\n");
1117 wlen = ImmGetCompositionStringW(imc, GCS_COMPSTR,
1118 wstring, sizeof(wstring));
1119 if (wlen > 0) {
1120 ret = ImmNotifyIME(imc, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
1121 ok(ret, "ImmNotifyIME(CPS_COMPLETE) failed\n");
1122 msg_spy_flush_msgs();
1123 ok(ime_composition_test.catch_result_str,
1124 "WM_IME_COMPOSITION(GCS_RESULTSTR) isn't sent\n");
1126 else
1127 win_skip("Composition string isn't available\n");
1128 ImmReleaseContext(hwnd, imc);
1129 SetWindowLongPtrA(hwnd, GWLP_WNDPROC,
1130 (LONG_PTR)ime_composition_test.old_wnd_proc);
1131 msg_spy_flush_msgs();
1134 /* Test composition results input by hand */
1135 memset(&ime_composition_test, 0, sizeof(ime_composition_test));
1136 if (winetest_interactive) {
1137 HWND hwndMain, hwndChild;
1138 MSG msg;
1139 const DWORD MY_TIMER = 0xcaffe;
1141 hwndMain = CreateWindowExA(WS_EX_CLIENTEDGE, wndcls,
1142 "IME composition test",
1143 WS_OVERLAPPEDWINDOW | WS_VISIBLE,
1144 CW_USEDEFAULT, CW_USEDEFAULT, 320, 160,
1145 NULL, NULL, GetModuleHandleA(NULL), NULL);
1146 hwndChild = CreateWindowExA(0, "static",
1147 "Input a DBCS character here using IME.",
1148 WS_CHILD | WS_VISIBLE,
1149 0, 0, 320, 100, hwndMain, NULL,
1150 GetModuleHandleA(NULL), NULL);
1152 ime_composition_test.old_wnd_proc =
1153 (WNDPROC)SetWindowLongPtrA(hwndChild, GWLP_WNDPROC,
1154 (LONG_PTR)test_ime_wnd_proc);
1156 SetFocus(hwndChild);
1158 ime_composition_test.timer_id = MY_TIMER;
1159 ime_composition_test.start = GetTickCount();
1160 SetTimer(hwndChild, ime_composition_test.timer_id, 100, NULL);
1161 while (GetMessageA(&msg, NULL, 0, 0)) {
1162 TranslateMessage(&msg);
1163 DispatchMessageA(&msg);
1164 if (!IsWindow(hwndMain))
1165 break;
1167 if (!ime_composition_test.catch_result_str)
1168 skip("WM_IME_COMPOSITION(GCS_RESULTSTR) isn't tested\n");
1169 msg_spy_flush_msgs();
1172 SetLastError(0xdeadbeef);
1173 imc = ImmGetContext(hwnd);
1174 ok(imc != 0, "ImmGetContext() failed. Last error: %lu\n", GetLastError());
1175 if (!imc)
1176 return;
1178 ret = ImmSetCompositionStringW(imc, SCS_SETSTR, NULL, 0, NULL, 0);
1179 ok(broken(!ret) ||
1180 ret, /* Vista+ */
1181 "ImmSetCompositionStringW() failed.\n");
1183 ret = ImmSetCompositionStringW(imc, SCS_SETSTR | SCS_CHANGEATTR,
1184 NULL, 0, NULL, 0);
1185 ok(!ret, "ImmSetCompositionStringW() succeeded.\n");
1187 ret = ImmSetCompositionStringW(imc, SCS_SETSTR | SCS_CHANGECLAUSE,
1188 NULL, 0, NULL, 0);
1189 ok(!ret, "ImmSetCompositionStringW() succeeded.\n");
1191 ret = ImmSetCompositionStringW(imc, SCS_CHANGEATTR | SCS_CHANGECLAUSE,
1192 NULL, 0, NULL, 0);
1193 ok(!ret, "ImmSetCompositionStringW() succeeded.\n");
1195 ret = ImmSetCompositionStringW(imc, SCS_SETSTR | SCS_CHANGEATTR | SCS_CHANGECLAUSE,
1196 NULL, 0, NULL, 0);
1197 ok(!ret, "ImmSetCompositionStringW() succeeded.\n");
1199 ImmReleaseContext(hwnd, imc);
1202 static void test_ImmIME(void)
1204 HIMC imc;
1206 imc = ImmGetContext(hwnd);
1207 if (imc)
1209 BOOL rc;
1210 rc = ImmConfigureIMEA(imc, NULL, IME_CONFIG_REGISTERWORD, NULL);
1211 ok (rc == 0, "ImmConfigureIMEA did not fail\n");
1212 rc = ImmConfigureIMEW(imc, NULL, IME_CONFIG_REGISTERWORD, NULL);
1213 ok (rc == 0, "ImmConfigureIMEW did not fail\n");
1215 ImmReleaseContext(hwnd,imc);
1218 static void test_ImmAssociateContextEx(void)
1220 HIMC imc;
1221 BOOL rc;
1223 if (!pImmAssociateContextEx) return;
1225 imc = ImmGetContext(hwnd);
1226 if (imc)
1228 HIMC retimc, newimc;
1229 HWND focus;
1231 SET_ENABLE(WM_IME_SETCONTEXT_ACTIVATE, TRUE);
1232 SET_ENABLE(WM_IME_SETCONTEXT_DEACTIVATE, TRUE);
1234 ok(GetActiveWindow() == hwnd, "hwnd is not active\n");
1235 newimc = ImmCreateContext();
1236 ok(newimc != imc, "handles should not be the same\n");
1237 rc = pImmAssociateContextEx(NULL, NULL, 0);
1238 ok(!rc, "ImmAssociateContextEx succeeded\n");
1239 SET_EXPECT(WM_IME_SETCONTEXT_DEACTIVATE);
1240 SET_EXPECT(WM_IME_SETCONTEXT_ACTIVATE);
1241 rc = pImmAssociateContextEx(hwnd, NULL, 0);
1242 CHECK_CALLED(WM_IME_SETCONTEXT_DEACTIVATE);
1243 CHECK_CALLED(WM_IME_SETCONTEXT_ACTIVATE);
1244 ok(rc, "ImmAssociateContextEx failed\n");
1245 rc = pImmAssociateContextEx(NULL, imc, 0);
1246 ok(!rc, "ImmAssociateContextEx succeeded\n");
1248 SET_EXPECT(WM_IME_SETCONTEXT_DEACTIVATE);
1249 SET_EXPECT(WM_IME_SETCONTEXT_ACTIVATE);
1250 rc = pImmAssociateContextEx(hwnd, imc, 0);
1251 CHECK_CALLED(WM_IME_SETCONTEXT_DEACTIVATE);
1252 CHECK_CALLED(WM_IME_SETCONTEXT_ACTIVATE);
1253 ok(rc, "ImmAssociateContextEx failed\n");
1254 retimc = ImmGetContext(hwnd);
1255 ok(retimc == imc, "handles should be the same\n");
1256 ImmReleaseContext(hwnd,retimc);
1258 rc = pImmAssociateContextEx(hwnd, imc, 0);
1259 ok(rc, "ImmAssociateContextEx failed\n");
1261 SET_EXPECT(WM_IME_SETCONTEXT_DEACTIVATE);
1262 SET_EXPECT(WM_IME_SETCONTEXT_ACTIVATE);
1263 rc = pImmAssociateContextEx(hwnd, newimc, 0);
1264 CHECK_CALLED(WM_IME_SETCONTEXT_DEACTIVATE);
1265 CHECK_CALLED(WM_IME_SETCONTEXT_ACTIVATE);
1266 ok(rc, "ImmAssociateContextEx failed\n");
1267 retimc = ImmGetContext(hwnd);
1268 ok(retimc == newimc, "handles should be the same\n");
1269 ImmReleaseContext(hwnd,retimc);
1271 focus = CreateWindowA("button", "button", 0, 0, 0, 0, 0, 0, 0, 0, 0);
1272 ok(focus != NULL, "CreateWindow failed\n");
1273 SET_EXPECT(WM_IME_SETCONTEXT_DEACTIVATE);
1274 SetFocus(focus);
1275 CHECK_CALLED(WM_IME_SETCONTEXT_DEACTIVATE);
1276 rc = pImmAssociateContextEx(hwnd, imc, 0);
1277 ok(rc, "ImmAssociateContextEx failed\n");
1278 SET_EXPECT(WM_IME_SETCONTEXT_ACTIVATE);
1279 DestroyWindow(focus);
1280 CHECK_CALLED(WM_IME_SETCONTEXT_ACTIVATE);
1282 SET_EXPECT(WM_IME_SETCONTEXT_DEACTIVATE);
1283 SetFocus(child);
1284 CHECK_CALLED(WM_IME_SETCONTEXT_DEACTIVATE);
1285 rc = pImmAssociateContextEx(hwnd, newimc, 0);
1286 ok(rc, "ImmAssociateContextEx failed\n");
1287 SET_EXPECT(WM_IME_SETCONTEXT_ACTIVATE);
1288 SetFocus(hwnd);
1289 CHECK_CALLED(WM_IME_SETCONTEXT_ACTIVATE);
1291 SET_EXPECT(WM_IME_SETCONTEXT_DEACTIVATE);
1292 SET_EXPECT(WM_IME_SETCONTEXT_ACTIVATE);
1293 rc = pImmAssociateContextEx(hwnd, NULL, IACE_DEFAULT);
1294 CHECK_CALLED(WM_IME_SETCONTEXT_DEACTIVATE);
1295 CHECK_CALLED(WM_IME_SETCONTEXT_ACTIVATE);
1296 ok(rc, "ImmAssociateContextEx failed\n");
1298 SET_ENABLE(WM_IME_SETCONTEXT_ACTIVATE, FALSE);
1299 SET_ENABLE(WM_IME_SETCONTEXT_DEACTIVATE, FALSE);
1301 ImmReleaseContext(hwnd,imc);
1304 /* similar to above, but using NtUserAssociateInputContext */
1305 static void test_NtUserAssociateInputContext(void)
1307 HIMC imc;
1308 UINT rc;
1310 if (!pNtUserAssociateInputContext)
1312 win_skip("NtUserAssociateInputContext not available\n");
1313 return;
1316 imc = ImmGetContext(hwnd);
1317 if (imc)
1319 HIMC retimc, newimc;
1320 HWND focus;
1322 SET_ENABLE(WM_IME_SETCONTEXT_ACTIVATE, TRUE);
1323 SET_ENABLE(WM_IME_SETCONTEXT_DEACTIVATE, TRUE);
1325 ok(GetActiveWindow() == hwnd, "hwnd is not active\n");
1326 newimc = ImmCreateContext();
1327 ok(newimc != imc, "handles should not be the same\n");
1328 rc = pNtUserAssociateInputContext(NULL, NULL, 0);
1329 ok(rc == 2, "NtUserAssociateInputContext returned %x\n", rc);
1330 rc = pNtUserAssociateInputContext(hwnd, NULL, 0);
1331 ok(rc == 1, "NtUserAssociateInputContext returned %x\n", rc);
1332 rc = pNtUserAssociateInputContext(NULL, imc, 0);
1333 ok(rc == 2, "NtUserAssociateInputContext returned %x\n", rc);
1335 rc = pNtUserAssociateInputContext(hwnd, imc, 0);
1336 ok(rc == 1, "NtUserAssociateInputContext returned %x\n", rc);
1337 retimc = ImmGetContext(hwnd);
1338 ok(retimc == imc, "handles should be the same\n");
1339 ImmReleaseContext(hwnd,retimc);
1341 rc = pNtUserAssociateInputContext(hwnd, imc, 0);
1342 ok(rc == 0, "NtUserAssociateInputContext returned %x\n", rc);
1344 rc = pNtUserAssociateInputContext(hwnd, newimc, 0);
1345 ok(rc == 1, "NtUserAssociateInputContext returned %x\n", rc);
1346 retimc = ImmGetContext(hwnd);
1347 ok(retimc == newimc, "handles should be the same\n");
1348 ImmReleaseContext(hwnd,retimc);
1350 focus = CreateWindowA("button", "button", 0, 0, 0, 0, 0, 0, 0, 0, 0);
1351 ok(focus != NULL, "CreateWindow failed\n");
1352 SET_EXPECT(WM_IME_SETCONTEXT_DEACTIVATE);
1353 SetFocus(focus);
1354 CHECK_CALLED(WM_IME_SETCONTEXT_DEACTIVATE);
1355 rc = pNtUserAssociateInputContext(hwnd, imc, 0);
1356 ok(rc == 0, "NtUserAssociateInputContext returned %x\n", rc);
1357 SET_EXPECT(WM_IME_SETCONTEXT_ACTIVATE);
1358 DestroyWindow(focus);
1359 CHECK_CALLED(WM_IME_SETCONTEXT_ACTIVATE);
1361 SET_EXPECT(WM_IME_SETCONTEXT_DEACTIVATE);
1362 SetFocus(child);
1363 CHECK_CALLED(WM_IME_SETCONTEXT_DEACTIVATE);
1364 rc = pNtUserAssociateInputContext(hwnd, newimc, 0);
1365 ok(rc == 0, "NtUserAssociateInputContext returned %x\n", rc);
1366 SET_EXPECT(WM_IME_SETCONTEXT_ACTIVATE);
1367 SetFocus(hwnd);
1368 CHECK_CALLED(WM_IME_SETCONTEXT_ACTIVATE);
1370 rc = pNtUserAssociateInputContext(hwnd, NULL, IACE_DEFAULT);
1371 ok(rc == 1, "NtUserAssociateInputContext returned %x\n", rc);
1373 SET_ENABLE(WM_IME_SETCONTEXT_ACTIVATE, FALSE);
1374 SET_ENABLE(WM_IME_SETCONTEXT_DEACTIVATE, FALSE);
1376 ImmReleaseContext(hwnd,imc);
1379 struct test_cross_thread_himc_params
1381 HWND hwnd;
1382 HANDLE event;
1383 HIMC himc[2];
1384 INPUTCONTEXT *contexts[2];
1387 static DWORD WINAPI test_cross_thread_himc_thread( void *arg )
1389 CANDIDATEFORM candidate = {.dwIndex = 1, .dwStyle = CFS_CANDIDATEPOS};
1390 struct test_cross_thread_himc_params *params = arg;
1391 COMPOSITIONFORM composition = {0};
1392 INPUTCONTEXT *contexts[2];
1393 HIMC himc[2], tmp_himc;
1394 LOGFONTW fontW = {0};
1395 HWND hwnd, tmp_hwnd;
1396 POINT pos = {0};
1397 MSG msg;
1399 hwnd = CreateWindowW( test_class.lpszClassName, NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
1400 100, 100, 100, 100, NULL, NULL, NULL, NULL );
1401 ok_ne( NULL, hwnd, HWND, "%p" );
1402 himc[0] = ImmGetContext( hwnd );
1403 ok_ne( NULL, himc[0], HIMC, "%p" );
1404 contexts[0] = ImmLockIMC( himc[0] );
1405 ok_ne( NULL, contexts[0], INPUTCONTEXT *, "%p" );
1406 contexts[0]->hWnd = hwnd;
1408 tmp_hwnd = CreateWindowW( L"static", NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
1409 100, 100, 100, 100, NULL, NULL, NULL, NULL );
1410 tmp_himc = ImmGetContext( tmp_hwnd );
1411 ok_eq( himc[0], tmp_himc, HIMC, "%p" );
1412 ok_ret( 1, ImmReleaseContext( tmp_hwnd, tmp_himc ) );
1413 ok_ret( 1, DestroyWindow( tmp_hwnd ) );
1415 himc[1] = ImmCreateContext();
1416 ok_ne( NULL, himc[1], HIMC, "%p" );
1417 contexts[1] = ImmLockIMC( himc[1] );
1418 ok_ne( NULL, contexts[1], INPUTCONTEXT *, "%p" );
1419 contexts[1]->hWnd = hwnd;
1421 ok_ret( 1, ImmSetOpenStatus( himc[0], 0xdeadbeef ) );
1422 ok_ret( 1, ImmSetOpenStatus( himc[1], 0xfeedcafe ) );
1423 ok_ret( 1, ImmSetCompositionWindow( himc[0], &composition ) );
1424 ok_ret( 1, ImmSetCompositionWindow( himc[1], &composition ) );
1425 ok_ret( 1, ImmSetCandidateWindow( himc[0], &candidate ) );
1426 ok_ret( 1, ImmSetCandidateWindow( himc[1], &candidate ) );
1427 ok_ret( 1, ImmSetStatusWindowPos( himc[0], &pos ) );
1428 ok_ret( 1, ImmSetStatusWindowPos( himc[1], &pos ) );
1429 ok_ret( 1, ImmSetCompositionFontW( himc[0], &fontW ) );
1430 ok_ret( 1, ImmSetCompositionFontW( himc[1], &fontW ) );
1432 params->hwnd = hwnd;
1433 params->himc[0] = himc[0];
1434 params->himc[1] = himc[1];
1435 params->contexts[0] = contexts[0];
1436 params->contexts[1] = contexts[1];
1437 SetEvent( params->event );
1439 while (GetMessageW( &msg, 0, 0, 0 ))
1441 TranslateMessage( &msg );
1442 DispatchMessageW( &msg );
1445 ok_ret( 1, ImmUnlockIMC( himc[0] ) );
1446 ok_ret( 1, ImmUnlockIMC( himc[1] ) );
1448 ok_ret( 1, ImmDestroyContext( himc[1] ) );
1449 ok_ret( 1, ImmReleaseContext( hwnd, himc[0] ) );
1450 ok_ret( 0, DestroyWindow( hwnd ) );
1452 return 1;
1455 static void test_cross_thread_himc(void)
1457 static const WCHAR comp_string[] = L"CompString";
1458 RECONVERTSTRING reconv = {.dwSize = sizeof(RECONVERTSTRING)};
1459 struct test_cross_thread_himc_params params;
1460 COMPOSITIONFORM composition = {0};
1461 DWORD tid, conversion, sentence;
1462 IMECHARPOSITION char_pos = {0};
1463 CANDIDATEFORM candidate = {0};
1464 COMPOSITIONSTRING *string;
1465 HIMC himc[2], tmp_himc;
1466 INPUTCONTEXT *tmp_ctx;
1467 LOGFONTW fontW = {0};
1468 LOGFONTA fontA = {0};
1469 char buffer[512];
1470 POINT pos = {0};
1471 HANDLE thread;
1472 BYTE *dst;
1473 UINT ret;
1475 himc[0] = ImmGetContext( hwnd );
1476 ok_ne( NULL, himc[0], HIMC, "%p" );
1477 ok_ne( NULL, ImmLockIMC( himc[0] ), INPUTCONTEXT *, "%p" );
1478 ok_ret( 1, ImmUnlockIMC( himc[0] ) );
1480 params.event = CreateEventW(NULL, TRUE, FALSE, NULL);
1481 ok_ne( NULL, params.event, HANDLE, "%p" );
1482 thread = CreateThread( NULL, 0, test_cross_thread_himc_thread, &params, 0, &tid );
1483 ok_ne( NULL, thread, HANDLE, "%p" );
1484 WaitForSingleObject( params.event, INFINITE );
1486 memset( ime_calls, 0, sizeof(ime_calls) );
1487 ime_call_count = 0;
1489 tmp_himc = ImmGetContext( params.hwnd );
1490 ok_ne( himc[0], tmp_himc, HIMC, "%p" );
1491 ok_eq( params.himc[0], tmp_himc, HIMC, "%p" );
1492 ok_ret( 1, ImmReleaseContext( params.hwnd, tmp_himc ) );
1494 himc[1] = ImmCreateContext();
1495 ok_ne( NULL, himc[1], HIMC, "%p" );
1496 tmp_ctx = ImmLockIMC( himc[1] );
1497 ok_ne( NULL, tmp_ctx, INPUTCONTEXT *, "%p" );
1499 tmp_ctx->hCompStr = ImmReSizeIMCC( tmp_ctx->hCompStr, 512 );
1500 ok_ne( NULL, tmp_ctx->hCompStr, HIMCC, "%p" );
1501 string = ImmLockIMCC( tmp_ctx->hCompStr );
1502 ok_ne( NULL, string, COMPOSITIONSTRING *, "%p" );
1503 string->dwSize = sizeof(COMPOSITIONSTRING);
1504 string->dwCompStrLen = wcslen( comp_string );
1505 string->dwCompStrOffset = string->dwSize;
1506 dst = (BYTE *)string + string->dwCompStrOffset;
1507 memcpy( dst, comp_string, string->dwCompStrLen * sizeof(WCHAR) );
1508 string->dwSize += string->dwCompStrLen * sizeof(WCHAR);
1510 string->dwCompClauseLen = 2 * sizeof(DWORD);
1511 string->dwCompClauseOffset = string->dwSize;
1512 dst = (BYTE *)string + string->dwCompClauseOffset;
1513 *(DWORD *)(dst + 0 * sizeof(DWORD)) = 0;
1514 *(DWORD *)(dst + 1 * sizeof(DWORD)) = string->dwCompStrLen;
1515 string->dwSize += 2 * sizeof(DWORD);
1517 string->dwCompAttrLen = string->dwCompStrLen;
1518 string->dwCompAttrOffset = string->dwSize;
1519 dst = (BYTE *)string + string->dwCompAttrOffset;
1520 memset( dst, ATTR_INPUT, string->dwCompStrLen );
1521 string->dwSize += string->dwCompStrLen;
1522 ok_ret( 0, ImmUnlockIMCC( tmp_ctx->hCompStr ) );
1524 ok_ret( 1, ImmUnlockIMC( himc[1] ) );
1526 /* ImmLockIMC should succeed with cross thread HIMC */
1528 tmp_ctx = ImmLockIMC( params.himc[0] );
1529 ok_eq( params.contexts[0], tmp_ctx, INPUTCONTEXT *, "%p" );
1530 ret = ImmGetIMCLockCount( params.himc[0] );
1531 ok( ret >= 2, "got ret %u\n", ret );
1533 tmp_ctx->hCompStr = ImmReSizeIMCC( tmp_ctx->hCompStr, 512 );
1534 ok_ne( NULL, tmp_ctx->hCompStr, HIMCC, "%p" );
1535 string = ImmLockIMCC( tmp_ctx->hCompStr );
1536 ok_ne( NULL, string, COMPOSITIONSTRING *, "%p" );
1537 string->dwSize = sizeof(COMPOSITIONSTRING);
1538 string->dwCompStrLen = wcslen( comp_string );
1539 string->dwCompStrOffset = string->dwSize;
1540 dst = (BYTE *)string + string->dwCompStrOffset;
1541 memcpy( dst, comp_string, string->dwCompStrLen * sizeof(WCHAR) );
1542 string->dwSize += string->dwCompStrLen * sizeof(WCHAR);
1544 string->dwCompClauseLen = 2 * sizeof(DWORD);
1545 string->dwCompClauseOffset = string->dwSize;
1546 dst = (BYTE *)string + string->dwCompClauseOffset;
1547 *(DWORD *)(dst + 0 * sizeof(DWORD)) = 0;
1548 *(DWORD *)(dst + 1 * sizeof(DWORD)) = string->dwCompStrLen;
1549 string->dwSize += 2 * sizeof(DWORD);
1551 string->dwCompAttrLen = string->dwCompStrLen;
1552 string->dwCompAttrOffset = string->dwSize;
1553 dst = (BYTE *)string + string->dwCompAttrOffset;
1554 memset( dst, ATTR_INPUT, string->dwCompStrLen );
1555 string->dwSize += string->dwCompStrLen;
1556 ok_ret( 0, ImmUnlockIMCC( tmp_ctx->hCompStr ) );
1558 ok_ret( 1, ImmUnlockIMC( params.himc[0] ) );
1560 tmp_ctx = ImmLockIMC( params.himc[1] );
1561 ok_eq( params.contexts[1], tmp_ctx, INPUTCONTEXT *, "%p" );
1562 ret = ImmGetIMCLockCount( params.himc[1] );
1563 ok( ret >= 2, "got ret %u\n", ret );
1564 ok_ret( 1, ImmUnlockIMC( params.himc[1] ) );
1566 /* ImmSetActiveContext should succeed with cross thread HIMC */
1568 SET_ENABLE( WM_IME_SETCONTEXT_DEACTIVATE, TRUE );
1569 SET_ENABLE( WM_IME_SETCONTEXT_ACTIVATE, TRUE );
1571 SET_EXPECT( WM_IME_SETCONTEXT_ACTIVATE );
1572 ok_ret( 1, ImmSetActiveContext( hwnd, params.himc[0], TRUE ) );
1573 CHECK_CALLED( WM_IME_SETCONTEXT_ACTIVATE );
1575 SET_EXPECT( WM_IME_SETCONTEXT_DEACTIVATE );
1576 ok_ret( 1, ImmSetActiveContext( hwnd, params.himc[0], FALSE ) );
1577 CHECK_CALLED( WM_IME_SETCONTEXT_DEACTIVATE );
1579 SET_ENABLE( WM_IME_SETCONTEXT_DEACTIVATE, FALSE );
1580 SET_ENABLE( WM_IME_SETCONTEXT_ACTIVATE, FALSE );
1582 /* ImmSetOpenStatus should fail with cross thread HIMC */
1584 ok_ret( 1, ImmSetOpenStatus( himc[1], 0xdeadbeef ) );
1585 ok_ret( (int)0xdeadbeef, ImmGetOpenStatus( himc[1] ) );
1587 ok_ret( 0, ImmSetOpenStatus( params.himc[0], TRUE ) );
1588 ok_ret( 0, ImmSetOpenStatus( params.himc[1], TRUE ) );
1589 ok_ret( (int)0xdeadbeef, ImmGetOpenStatus( params.himc[0] ) );
1590 ok_ret( (int)0xfeedcafe, ImmGetOpenStatus( params.himc[1] ) );
1591 ok_ret( 0, ImmSetOpenStatus( params.himc[0], FALSE ) );
1592 ok_ret( (int)0xdeadbeef, ImmGetOpenStatus( params.himc[0] ) );
1594 /* ImmSetConversionStatus should fail with cross thread HIMC */
1596 ok_ret( 1, ImmGetConversionStatus( himc[1], &conversion, &sentence ) );
1597 ok_ret( 1, ImmSetConversionStatus( himc[1], conversion, sentence ) );
1599 ok_ret( 1, ImmGetConversionStatus( params.himc[0], &conversion, &sentence ) );
1600 ok_ret( 1, ImmGetConversionStatus( params.himc[1], &conversion, &sentence ) );
1601 ok_ret( 0, ImmSetConversionStatus( params.himc[0], conversion, sentence ) );
1602 ok_ret( 0, ImmSetConversionStatus( params.himc[1], conversion, sentence ) );
1604 /* ImmSetCompositionFont(W|A) should fail with cross thread HIMC */
1606 ok_ret( 1, ImmSetCompositionFontA( himc[1], &fontA ) );
1607 ok_ret( 1, ImmGetCompositionFontA( himc[1], &fontA ) );
1608 ok_ret( 1, ImmSetCompositionFontW( himc[1], &fontW ) );
1609 ok_ret( 1, ImmGetCompositionFontW( himc[1], &fontW ) );
1611 ok_ret( 0, ImmSetCompositionFontA( params.himc[0], &fontA ) );
1612 ok_ret( 0, ImmSetCompositionFontA( params.himc[1], &fontA ) );
1613 ok_ret( 1, ImmGetCompositionFontA( params.himc[0], &fontA ) );
1614 ok_ret( 1, ImmGetCompositionFontA( params.himc[1], &fontA ) );
1615 ok_ret( 0, ImmSetCompositionFontW( params.himc[0], &fontW ) );
1616 ok_ret( 0, ImmSetCompositionFontW( params.himc[1], &fontW ) );
1617 ok_ret( 1, ImmGetCompositionFontW( params.himc[0], &fontW ) );
1618 ok_ret( 1, ImmGetCompositionFontW( params.himc[1], &fontW ) );
1620 /* ImmRequestMessage(W|A) should fail with cross thread HIMC */
1622 ok_ret( 0, ImmRequestMessageW( himc[1], IMR_COMPOSITIONFONT, (LPARAM)&fontW ) );
1623 ok_ret( 0, ImmRequestMessageA( himc[1], IMR_COMPOSITIONFONT, (LPARAM)&fontA ) );
1625 ok_ret( 0, ImmRequestMessageW( params.himc[0], IMR_COMPOSITIONFONT, (LPARAM)&fontW ) );
1626 ok_ret( 0, ImmRequestMessageA( params.himc[0], IMR_COMPOSITIONFONT, (LPARAM)&fontA ) );
1627 ok_ret( 0, ImmRequestMessageW( params.himc[1], IMR_COMPOSITIONFONT, (LPARAM)&fontW ) );
1628 ok_ret( 0, ImmRequestMessageA( params.himc[1], IMR_COMPOSITIONFONT, (LPARAM)&fontA ) );
1630 ok_seq( empty_sequence );
1632 /* ImmSetCompositionString(W|A) should fail with cross thread HIMC */
1634 ok_ret( 10, ImmGetCompositionStringA( himc[1], GCS_COMPSTR, buffer, sizeof(buffer) ) );
1635 ok_ret( 20, ImmGetCompositionStringW( himc[1], GCS_COMPSTR, buffer, sizeof(buffer) ) );
1636 ok_ret( 1, ImmSetCompositionStringA( himc[1], SCS_SETSTR, "a", 2, NULL, 0 ) );
1637 ok_ret( 1, ImmSetCompositionStringW( himc[1], SCS_SETSTR, L"a", 4, NULL, 0 ) );
1639 ok_ret( 0, ImmSetCompositionStringA( params.himc[0], SCS_SETSTR, "a", 2, NULL, 0 ) );
1640 ok_ret( 0, ImmSetCompositionStringA( params.himc[1], SCS_SETSTR, "a", 2, NULL, 0 ) );
1641 ok_ret( 0, ImmSetCompositionStringW( params.himc[0], SCS_SETSTR, L"a", 4, NULL, 0 ) );
1642 ok_ret( 0, ImmSetCompositionStringW( params.himc[1], SCS_SETSTR, L"a", 4, NULL, 0 ) );
1643 ok_ret( 10, ImmGetCompositionStringA( params.himc[0], GCS_COMPSTR, buffer, sizeof(buffer) ) );
1644 ok_ret( 0, ImmGetCompositionStringA( params.himc[1], GCS_COMPSTR, buffer, sizeof(buffer) ) );
1645 ok_ret( 20, ImmGetCompositionStringW( params.himc[0], GCS_COMPSTR, buffer, sizeof(buffer) ) );
1646 ok_ret( 0, ImmGetCompositionStringW( params.himc[1], GCS_COMPSTR, buffer, sizeof(buffer) ) );
1648 /* ImmRequestMessage(W|A) should fail with cross thread HIMC */
1650 ok_ret( 0, ImmRequestMessageW( himc[1], IMR_RECONVERTSTRING, 0 ) );
1651 ok_ret( 0, ImmRequestMessageW( himc[1], IMR_RECONVERTSTRING, (LPARAM)&reconv ) );
1652 ok_ret( 0, ImmRequestMessageA( himc[1], IMR_RECONVERTSTRING, 0 ) );
1653 ok_ret( 0, ImmRequestMessageA( himc[1], IMR_RECONVERTSTRING, (LPARAM)&reconv ) );
1655 ok_ret( 0, ImmRequestMessageW( params.himc[0], IMR_RECONVERTSTRING, 0 ) );
1656 ok_ret( 0, ImmRequestMessageW( params.himc[0], IMR_RECONVERTSTRING, (LPARAM)&reconv ) );
1657 ok_ret( 0, ImmRequestMessageA( params.himc[0], IMR_RECONVERTSTRING, 0 ) );
1658 ok_ret( 0, ImmRequestMessageA( params.himc[0], IMR_RECONVERTSTRING, (LPARAM)&reconv ) );
1659 ok_ret( 0, ImmRequestMessageW( params.himc[1], IMR_RECONVERTSTRING, 0 ) );
1660 ok_ret( 0, ImmRequestMessageW( params.himc[1], IMR_RECONVERTSTRING, (LPARAM)&reconv ) );
1661 ok_ret( 0, ImmRequestMessageA( params.himc[1], IMR_RECONVERTSTRING, 0 ) );
1662 ok_ret( 0, ImmRequestMessageA( params.himc[1], IMR_RECONVERTSTRING, (LPARAM)&reconv ) );
1664 ok_ret( 0, ImmRequestMessageW( himc[1], IMR_DOCUMENTFEED, 0 ) );
1665 ok_ret( 0, ImmRequestMessageW( himc[1], IMR_DOCUMENTFEED, (LPARAM)&reconv ) );
1666 ok_ret( 0, ImmRequestMessageA( himc[1], IMR_DOCUMENTFEED, 0 ) );
1667 ok_ret( 0, ImmRequestMessageA( himc[1], IMR_DOCUMENTFEED, (LPARAM)&reconv ) );
1669 ok_ret( 0, ImmRequestMessageW( params.himc[0], IMR_DOCUMENTFEED, 0 ) );
1670 ok_ret( 0, ImmRequestMessageW( params.himc[0], IMR_DOCUMENTFEED, (LPARAM)&reconv ) );
1671 ok_ret( 0, ImmRequestMessageA( params.himc[0], IMR_DOCUMENTFEED, 0 ) );
1672 ok_ret( 0, ImmRequestMessageA( params.himc[0], IMR_DOCUMENTFEED, (LPARAM)&reconv ) );
1673 ok_ret( 0, ImmRequestMessageW( params.himc[1], IMR_DOCUMENTFEED, 0 ) );
1674 ok_ret( 0, ImmRequestMessageW( params.himc[1], IMR_DOCUMENTFEED, (LPARAM)&reconv ) );
1675 ok_ret( 0, ImmRequestMessageA( params.himc[1], IMR_DOCUMENTFEED, 0 ) );
1676 ok_ret( 0, ImmRequestMessageA( params.himc[1], IMR_DOCUMENTFEED, (LPARAM)&reconv ) );
1678 ok_ret( 0, ImmRequestMessageW( himc[1], IMR_CONFIRMRECONVERTSTRING, (LPARAM)&reconv ) );
1679 ok_ret( 0, ImmRequestMessageA( himc[1], IMR_CONFIRMRECONVERTSTRING, (LPARAM)&reconv ) );
1681 ok_ret( 0, ImmRequestMessageW( params.himc[0], IMR_CONFIRMRECONVERTSTRING, (LPARAM)&reconv ) );
1682 ok_ret( 0, ImmRequestMessageA( params.himc[0], IMR_CONFIRMRECONVERTSTRING, (LPARAM)&reconv ) );
1683 ok_ret( 0, ImmRequestMessageW( params.himc[1], IMR_CONFIRMRECONVERTSTRING, (LPARAM)&reconv ) );
1684 ok_ret( 0, ImmRequestMessageA( params.himc[1], IMR_CONFIRMRECONVERTSTRING, (LPARAM)&reconv ) );
1686 ok_seq( empty_sequence );
1688 /* ImmSetCompositionWindow should fail with cross thread HIMC */
1690 ok_ret( 1, ImmSetCompositionWindow( himc[1], &composition ) );
1691 ok_ret( 1, ImmGetCompositionWindow( himc[1], &composition ) );
1693 ok_ret( 0, ImmSetCompositionWindow( params.himc[0], &composition ) );
1694 ok_ret( 0, ImmSetCompositionWindow( params.himc[1], &composition ) );
1695 ok_ret( 1, ImmGetCompositionWindow( params.himc[0], &composition ) );
1696 ok_ret( 1, ImmGetCompositionWindow( params.himc[1], &composition ) );
1698 /* ImmRequestMessage(W|A) should fail with cross thread HIMC */
1700 ok_ret( 0, ImmRequestMessageW( himc[1], IMR_COMPOSITIONWINDOW, (LPARAM)&composition ) );
1701 ok_ret( 0, ImmRequestMessageA( himc[1], IMR_COMPOSITIONWINDOW, (LPARAM)&composition ) );
1703 ok_ret( 0, ImmRequestMessageW( params.himc[0], IMR_COMPOSITIONWINDOW, (LPARAM)&composition ) );
1704 ok_ret( 0, ImmRequestMessageA( params.himc[0], IMR_COMPOSITIONWINDOW, (LPARAM)&composition ) );
1705 ok_ret( 0, ImmRequestMessageW( params.himc[1], IMR_COMPOSITIONWINDOW, (LPARAM)&composition ) );
1706 ok_ret( 0, ImmRequestMessageA( params.himc[1], IMR_COMPOSITIONWINDOW, (LPARAM)&composition ) );
1708 ok_seq( empty_sequence );
1710 /* ImmSetCandidateWindow should fail with cross thread HIMC */
1712 ok_ret( 1, ImmSetCandidateWindow( himc[1], &candidate ) );
1713 ok_ret( 1, ImmGetCandidateWindow( himc[1], 0, &candidate ) );
1715 ok_ret( 1, ImmGetCandidateWindow( params.himc[0], 1, &candidate ) );
1716 ok_ret( 1, ImmGetCandidateWindow( params.himc[1], 1, &candidate ) );
1717 ok_ret( 0, ImmSetCandidateWindow( params.himc[0], &candidate ) );
1718 ok_ret( 0, ImmSetCandidateWindow( params.himc[1], &candidate ) );
1720 /* ImmRequestMessage(W|A) should fail with cross thread HIMC */
1722 candidate.dwIndex = -1;
1723 ok_ret( 0, ImmRequestMessageW( himc[1], IMR_CANDIDATEWINDOW, (LPARAM)&candidate ) );
1724 ok_ret( 0, ImmRequestMessageA( himc[1], IMR_CANDIDATEWINDOW, (LPARAM)&candidate ) );
1726 candidate.dwIndex = 0;
1727 ok_ret( 0, ImmRequestMessageW( himc[1], IMR_CANDIDATEWINDOW, (LPARAM)&candidate ) );
1728 ok_ret( 0, ImmRequestMessageA( himc[1], IMR_CANDIDATEWINDOW, (LPARAM)&candidate ) );
1730 ok_ret( 0, ImmRequestMessageW( params.himc[0], IMR_CANDIDATEWINDOW, (LPARAM)&candidate ) );
1731 ok_ret( 0, ImmRequestMessageA( params.himc[0], IMR_CANDIDATEWINDOW, (LPARAM)&candidate ) );
1732 ok_ret( 0, ImmRequestMessageW( params.himc[1], IMR_CANDIDATEWINDOW, (LPARAM)&candidate ) );
1733 ok_ret( 0, ImmRequestMessageA( params.himc[1], IMR_CANDIDATEWINDOW, (LPARAM)&candidate ) );
1735 ok_seq( empty_sequence );
1737 /* ImmSetStatusWindowPos should fail with cross thread HIMC */
1739 ok_ret( 1, ImmSetStatusWindowPos( himc[1], &pos ) );
1740 ok_ret( 1, ImmGetStatusWindowPos( himc[1], &pos ) );
1742 ok_ret( 0, ImmSetStatusWindowPos( params.himc[0], &pos ) );
1743 ok_ret( 0, ImmSetStatusWindowPos( params.himc[1], &pos ) );
1744 ok_ret( 1, ImmGetStatusWindowPos( params.himc[0], &pos ) );
1745 ok_ret( 1, ImmGetStatusWindowPos( params.himc[1], &pos ) );
1747 /* ImmRequestMessage(W|A) should fail with cross thread HIMC */
1749 ok_ret( 0, ImmRequestMessageW( himc[1], IMR_QUERYCHARPOSITION, (LPARAM)&char_pos ) );
1750 ok_ret( 0, ImmRequestMessageA( himc[1], IMR_QUERYCHARPOSITION, (LPARAM)&char_pos ) );
1752 ok_ret( 0, ImmRequestMessageW( params.himc[0], IMR_QUERYCHARPOSITION, (LPARAM)&char_pos ) );
1753 ok_ret( 0, ImmRequestMessageA( params.himc[0], IMR_QUERYCHARPOSITION, (LPARAM)&char_pos ) );
1754 ok_ret( 0, ImmRequestMessageW( params.himc[1], IMR_QUERYCHARPOSITION, (LPARAM)&char_pos ) );
1755 ok_ret( 0, ImmRequestMessageA( params.himc[1], IMR_QUERYCHARPOSITION, (LPARAM)&char_pos ) );
1757 ok_seq( empty_sequence );
1759 /* ImmGenerateMessage should fail with cross thread HIMC */
1761 ok_ret( 1, ImmGenerateMessage( himc[1] ) );
1763 ok_ret( 0, ImmGenerateMessage( params.himc[0] ) );
1764 ok_ret( 0, ImmGenerateMessage( params.himc[1] ) );
1766 /* ImmAssociateContext should fail with cross thread HWND or HIMC */
1768 tmp_himc = ImmAssociateContext( hwnd, params.himc[0] );
1769 ok_eq( NULL, tmp_himc, HIMC, "%p" );
1770 tmp_himc = ImmGetContext( hwnd );
1771 ok_eq( himc[0], tmp_himc, HIMC, "%p" );
1772 ok_ret( 1, ImmReleaseContext( hwnd, tmp_himc ) );
1774 tmp_himc = ImmAssociateContext( hwnd, params.himc[1] );
1775 ok_eq( NULL, tmp_himc, HIMC, "%p" );
1776 tmp_himc = ImmGetContext( hwnd );
1777 ok_eq( himc[0], tmp_himc, HIMC, "%p" );
1778 ok_ret( 1, ImmReleaseContext( hwnd, tmp_himc ) );
1780 tmp_himc = ImmAssociateContext( params.hwnd, params.himc[1] );
1781 ok_eq( NULL, tmp_himc, HIMC, "%p" );
1782 tmp_himc = ImmGetContext( params.hwnd );
1783 ok_eq( params.himc[0], tmp_himc, HIMC, "%p" );
1784 ok_ret( 1, ImmReleaseContext( params.hwnd, tmp_himc ) );
1786 /* ImmAssociateContext should succeed with cross thread HWND and NULL HIMC */
1788 tmp_himc = ImmAssociateContext( params.hwnd, NULL );
1789 ok_eq( params.himc[0], tmp_himc, HIMC, "%p" );
1790 tmp_himc = ImmGetContext( params.hwnd );
1791 ok_eq( NULL, tmp_himc, HIMC, "%p" );
1793 /* ImmReleaseContext / ImmDestroyContext should fail with cross thread HIMC */
1795 ok_ret( 1, ImmReleaseContext( params.hwnd, params.himc[0] ) );
1796 ok_ret( 0, ImmDestroyContext( params.himc[1] ) );
1798 /* ImmGetContext should fail with another process HWND */
1800 tmp_himc = ImmGetContext( GetDesktopWindow() );
1801 ok_eq( NULL, tmp_himc, HIMC, "%p" );
1803 ok_ret( 0, SendMessageW( params.hwnd, WM_CLOSE, 0, 0 ) );
1804 ok_ret( 1, PostThreadMessageW( tid, WM_QUIT, 1, 0 ) );
1805 ok_ret( 0, WaitForSingleObject( thread, 5000 ) );
1806 ok_ret( 1, CloseHandle( thread ) );
1807 ok_ret( 1, CloseHandle( params.event ) );
1809 ok_ret( 1, ImmReleaseContext( hwnd, himc[0] ) );
1810 ok_ret( 1, ImmDestroyContext( himc[1] ) );
1813 static void test_ImmIsUIMessage(void)
1815 struct test
1817 UINT msg;
1818 BOOL ret;
1821 static const struct test tests[] =
1823 { WM_MOUSEMOVE, FALSE },
1824 { WM_IME_STARTCOMPOSITION, TRUE },
1825 { WM_IME_ENDCOMPOSITION, TRUE },
1826 { WM_IME_COMPOSITION, TRUE },
1827 { WM_IME_SETCONTEXT, TRUE },
1828 { WM_IME_NOTIFY, TRUE },
1829 { WM_IME_CONTROL, FALSE },
1830 { WM_IME_COMPOSITIONFULL, TRUE },
1831 { WM_IME_SELECT, TRUE },
1832 { WM_IME_CHAR, FALSE },
1833 { 0x287 /* FIXME */, TRUE },
1834 { WM_IME_REQUEST, FALSE },
1835 { WM_IME_KEYDOWN, FALSE },
1836 { WM_IME_KEYUP, FALSE },
1837 { 0, FALSE } /* mark the end */
1840 UINT WM_MSIME_SERVICE = RegisterWindowMessageA("MSIMEService");
1841 UINT WM_MSIME_RECONVERTOPTIONS = RegisterWindowMessageA("MSIMEReconvertOptions");
1842 UINT WM_MSIME_MOUSE = RegisterWindowMessageA("MSIMEMouseOperation");
1843 UINT WM_MSIME_RECONVERTREQUEST = RegisterWindowMessageA("MSIMEReconvertRequest");
1844 UINT WM_MSIME_RECONVERT = RegisterWindowMessageA("MSIMEReconvert");
1845 UINT WM_MSIME_QUERYPOSITION = RegisterWindowMessageA("MSIMEQueryPosition");
1846 UINT WM_MSIME_DOCUMENTFEED = RegisterWindowMessageA("MSIMEDocumentFeed");
1848 const struct test *test;
1849 BOOL ret;
1851 if (!pImmIsUIMessageA) return;
1853 for (test = tests; test->msg; test++)
1855 msg_spy_flush_msgs();
1856 ret = pImmIsUIMessageA(NULL, test->msg, 0, 0);
1857 ok(ret == test->ret, "ImmIsUIMessageA returned %x for %x\n", ret, test->msg);
1858 ok(!msg_spy_find_msg(test->msg), "Windows does not send 0x%x for NULL hwnd\n", test->msg);
1860 ret = pImmIsUIMessageA(hwnd, test->msg, 0, 0);
1861 ok(ret == test->ret, "ImmIsUIMessageA returned %x for %x\n", ret, test->msg);
1862 if (ret)
1863 ok(msg_spy_find_msg(test->msg) != NULL, "Windows does send 0x%x\n", test->msg);
1864 else
1865 ok(!msg_spy_find_msg(test->msg), "Windows does not send 0x%x\n", test->msg);
1868 ret = pImmIsUIMessageA(NULL, WM_MSIME_SERVICE, 0, 0);
1869 ok(!ret, "ImmIsUIMessageA returned TRUE for WM_MSIME_SERVICE\n");
1870 ret = pImmIsUIMessageA(NULL, WM_MSIME_RECONVERTOPTIONS, 0, 0);
1871 ok(!ret, "ImmIsUIMessageA returned TRUE for WM_MSIME_RECONVERTOPTIONS\n");
1872 ret = pImmIsUIMessageA(NULL, WM_MSIME_MOUSE, 0, 0);
1873 ok(!ret, "ImmIsUIMessageA returned TRUE for WM_MSIME_MOUSE\n");
1874 ret = pImmIsUIMessageA(NULL, WM_MSIME_RECONVERTREQUEST, 0, 0);
1875 ok(!ret, "ImmIsUIMessageA returned TRUE for WM_MSIME_RECONVERTREQUEST\n");
1876 ret = pImmIsUIMessageA(NULL, WM_MSIME_RECONVERT, 0, 0);
1877 ok(!ret, "ImmIsUIMessageA returned TRUE for WM_MSIME_RECONVERT\n");
1878 ret = pImmIsUIMessageA(NULL, WM_MSIME_QUERYPOSITION, 0, 0);
1879 ok(!ret, "ImmIsUIMessageA returned TRUE for WM_MSIME_QUERYPOSITION\n");
1880 ret = pImmIsUIMessageA(NULL, WM_MSIME_DOCUMENTFEED, 0, 0);
1881 ok(!ret, "ImmIsUIMessageA returned TRUE for WM_MSIME_DOCUMENTFEED\n");
1884 static void test_ImmGetContext(void)
1886 HIMC himc;
1887 DWORD err;
1889 SetLastError(0xdeadbeef);
1890 himc = ImmGetContext((HWND)0xffffffff);
1891 err = GetLastError();
1892 ok(himc == NULL, "ImmGetContext succeeded\n");
1893 ok(err == ERROR_INVALID_WINDOW_HANDLE, "got %lu\n", err);
1895 himc = ImmGetContext(hwnd);
1896 ok(himc != NULL, "ImmGetContext failed\n");
1897 ok(ImmReleaseContext(hwnd, himc), "ImmReleaseContext failed\n");
1900 static LRESULT (WINAPI *old_imm_wnd_proc)(HWND, UINT, WPARAM, LPARAM);
1901 static LRESULT WINAPI imm_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
1903 ok(msg != WM_DESTROY, "got WM_DESTROY message\n");
1904 return old_imm_wnd_proc(hwnd, msg, wparam, lparam);
1907 static HWND thread_ime_wnd;
1908 static DWORD WINAPI test_ImmGetDefaultIMEWnd_thread(void *arg)
1910 CreateWindowA("static", "static", WS_POPUP, 0, 0, 1, 1, NULL, NULL, NULL, NULL);
1912 thread_ime_wnd = ImmGetDefaultIMEWnd(0);
1913 ok(thread_ime_wnd != 0, "ImmGetDefaultIMEWnd returned NULL\n");
1914 old_imm_wnd_proc = (void*)SetWindowLongPtrW(thread_ime_wnd, GWLP_WNDPROC, (LONG_PTR)imm_wnd_proc);
1915 return 0;
1918 static void test_ImmDefaultHwnd(void)
1920 HIMC imc1, imc2, imc3;
1921 HWND def1, def3;
1922 HANDLE thread;
1923 HWND hwnd;
1924 char title[16];
1925 LONG style;
1927 hwnd = CreateWindowExA(WS_EX_CLIENTEDGE, "EDIT", "Wine imm32.dll test",
1928 WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
1929 240, 120, NULL, NULL, GetModuleHandleW(NULL), NULL);
1931 ShowWindow(hwnd, SW_SHOWNORMAL);
1933 imc1 = ImmGetContext(hwnd);
1934 if (!imc1)
1936 win_skip("IME support not implemented\n");
1937 return;
1940 def1 = ImmGetDefaultIMEWnd(hwnd);
1942 GetWindowTextA(def1, title, sizeof(title));
1943 ok(!strcmp(title, "Default IME"), "got %s\n", title);
1944 style = GetWindowLongA(def1, GWL_STYLE);
1945 ok(style == (WS_DISABLED | WS_POPUP | WS_CLIPSIBLINGS), "got %08lx\n", style);
1946 style = GetWindowLongA(def1, GWL_EXSTYLE);
1947 ok(style == 0, "got %08lx\n", style);
1949 imc2 = ImmCreateContext();
1950 ImmSetOpenStatus(imc2, TRUE);
1952 imc3 = ImmGetContext(hwnd);
1953 def3 = ImmGetDefaultIMEWnd(hwnd);
1955 ok(def3 == def1, "Default IME window should not change\n");
1956 ok(imc1 == imc3, "IME context should not change\n");
1957 ImmSetOpenStatus(imc2, FALSE);
1959 thread = CreateThread(NULL, 0, test_ImmGetDefaultIMEWnd_thread, NULL, 0, NULL);
1960 WaitForSingleObject(thread, INFINITE);
1961 ok(thread_ime_wnd != def1, "thread_ime_wnd == def1\n");
1962 ok(!IsWindow(thread_ime_wnd), "thread_ime_wnd was not destroyed\n");
1963 CloseHandle(thread);
1965 ImmReleaseContext(hwnd, imc1);
1966 ImmReleaseContext(hwnd, imc3);
1967 ImmDestroyContext(imc2);
1968 DestroyWindow(hwnd);
1971 static BOOL CALLBACK is_ime_window_proc(HWND hWnd, LPARAM param)
1973 WCHAR class_nameW[16];
1974 HWND *ime_window = (HWND *)param;
1975 if (GetClassNameW(hWnd, class_nameW, ARRAY_SIZE(class_nameW)) && !lstrcmpW(class_nameW, L"IME"))
1977 *ime_window = hWnd;
1978 return FALSE;
1980 return TRUE;
1983 static HWND get_ime_window(void)
1985 HWND ime_window = NULL;
1986 EnumThreadWindows(GetCurrentThreadId(), is_ime_window_proc, (LPARAM)&ime_window);
1987 return ime_window;
1990 struct testcase_ime_window {
1991 BOOL visible;
1992 BOOL top_level_window;
1995 static DWORD WINAPI test_default_ime_window_cb(void *arg)
1997 struct testcase_ime_window *testcase = (struct testcase_ime_window *)arg;
1998 DWORD visible = testcase->visible ? WS_VISIBLE : 0;
1999 HWND hwnd1, hwnd2, default_ime_wnd, ime_wnd;
2001 ok(!get_ime_window(), "Expected no IME windows\n");
2002 if (testcase->top_level_window) {
2003 test_phase = FIRST_WINDOW;
2004 hwnd1 = CreateWindowExA(WS_EX_CLIENTEDGE, wndcls, "Wine imm32.dll test",
2005 WS_OVERLAPPEDWINDOW | visible,
2006 CW_USEDEFAULT, CW_USEDEFAULT,
2007 240, 120, NULL, NULL, GetModuleHandleW(NULL), NULL);
2009 else {
2010 hwnd1 = CreateWindowExA(WS_EX_CLIENTEDGE, "EDIT", "Wine imm32.dll test",
2011 WS_CHILD | visible,
2012 CW_USEDEFAULT, CW_USEDEFAULT,
2013 240, 24, hwnd, NULL, GetModuleHandleW(NULL), NULL);
2015 ime_wnd = get_ime_window();
2016 ok(ime_wnd != NULL, "Expected IME window existence\n");
2017 default_ime_wnd = ImmGetDefaultIMEWnd(hwnd1);
2018 ok(ime_wnd == default_ime_wnd, "Expected %p, got %p\n", ime_wnd, default_ime_wnd);
2020 test_phase = SECOND_WINDOW;
2021 hwnd2 = CreateWindowExA(WS_EX_CLIENTEDGE, wndcls, "Wine imm32.dll test",
2022 WS_OVERLAPPEDWINDOW | visible,
2023 CW_USEDEFAULT, CW_USEDEFAULT,
2024 240, 120, NULL, NULL, GetModuleHandleW(NULL), NULL);
2025 DestroyWindow(hwnd2);
2026 ok(IsWindow(ime_wnd) ||
2027 broken(!testcase->visible /* Vista */) ||
2028 broken(!testcase->top_level_window /* Vista */) ,
2029 "Expected IME window existence\n");
2030 DestroyWindow(hwnd1);
2031 flaky
2032 ok(!IsWindow(ime_wnd), "Expected no IME windows\n");
2033 return 1;
2036 static DWORD WINAPI test_default_ime_window_cancel_cb(void *arg)
2038 struct testcase_ime_window *testcase = (struct testcase_ime_window *)arg;
2039 DWORD visible = testcase->visible ? WS_VISIBLE : 0;
2040 HWND hwnd1, hwnd2, default_ime_wnd, ime_wnd;
2042 ok(!get_ime_window(), "Expected no IME windows\n");
2043 test_phase = NCCREATE_CANCEL;
2044 hwnd1 = CreateWindowExA(WS_EX_CLIENTEDGE, wndcls, "Wine imm32.dll test",
2045 WS_OVERLAPPEDWINDOW | visible,
2046 CW_USEDEFAULT, CW_USEDEFAULT,
2047 240, 120, NULL, NULL, GetModuleHandleW(NULL), NULL);
2048 ok(hwnd1 == NULL, "creation succeeded, got %p\n", hwnd1);
2049 ok(!get_ime_window(), "Expected no IME windows\n");
2051 test_phase = CREATE_CANCEL;
2052 hwnd1 = CreateWindowExA(WS_EX_CLIENTEDGE, wndcls, "Wine imm32.dll test",
2053 WS_OVERLAPPEDWINDOW | visible,
2054 CW_USEDEFAULT, CW_USEDEFAULT,
2055 240, 120, NULL, NULL, GetModuleHandleW(NULL), NULL);
2056 ok(hwnd1 == NULL, "creation succeeded, got %p\n", hwnd1);
2057 ok(!get_ime_window(), "Expected no IME windows\n");
2059 test_phase = FIRST_WINDOW;
2060 hwnd2 = CreateWindowExA(WS_EX_CLIENTEDGE, wndcls, "Wine imm32.dll test",
2061 WS_OVERLAPPEDWINDOW | visible,
2062 CW_USEDEFAULT, CW_USEDEFAULT,
2063 240, 120, NULL, NULL, GetModuleHandleW(NULL), NULL);
2064 ime_wnd = get_ime_window();
2065 ok(ime_wnd != NULL, "Expected IME window existence\n");
2066 default_ime_wnd = ImmGetDefaultIMEWnd(hwnd2);
2067 ok(ime_wnd == default_ime_wnd, "Expected %p, got %p\n", ime_wnd, default_ime_wnd);
2069 DestroyWindow(hwnd2);
2070 ok(!IsWindow(ime_wnd), "Expected no IME windows\n");
2071 return 1;
2074 static DWORD WINAPI test_default_ime_disabled_cb(void *arg)
2076 HWND hWnd, default_ime_wnd;
2078 ok(!get_ime_window(), "Expected no IME windows\n");
2079 ImmDisableIME(GetCurrentThreadId());
2080 test_phase = IME_DISABLED;
2081 hWnd = CreateWindowExA(WS_EX_CLIENTEDGE, wndcls, "Wine imm32.dll test",
2082 WS_OVERLAPPEDWINDOW | WS_VISIBLE,
2083 CW_USEDEFAULT, CW_USEDEFAULT,
2084 240, 120, NULL, NULL, GetModuleHandleW(NULL), NULL);
2085 default_ime_wnd = ImmGetDefaultIMEWnd(hWnd);
2086 ok(!default_ime_wnd, "Expected no IME windows\n");
2087 DestroyWindow(hWnd);
2088 return 1;
2091 static DWORD WINAPI test_default_ime_with_message_only_window_cb(void *arg)
2093 HWND hwnd1, hwnd2, default_ime_wnd;
2095 /* Message-only window doesn't create associated IME window. */
2096 test_phase = PHASE_UNKNOWN;
2097 hwnd1 = CreateWindowA(wndcls, "Wine imm32.dll test",
2098 WS_OVERLAPPEDWINDOW,
2099 CW_USEDEFAULT, CW_USEDEFAULT,
2100 240, 120, HWND_MESSAGE, NULL, GetModuleHandleW(NULL), NULL);
2101 default_ime_wnd = ImmGetDefaultIMEWnd(hwnd1);
2102 ok(!default_ime_wnd, "Expected no IME windows, got %p\n", default_ime_wnd);
2104 /* Setting message-only window as owner at creation,
2105 doesn't create associated IME window. */
2106 hwnd2 = CreateWindowA(wndcls, "Wine imm32.dll test",
2107 WS_OVERLAPPEDWINDOW,
2108 CW_USEDEFAULT, CW_USEDEFAULT,
2109 240, 120, hwnd1, NULL, GetModuleHandleW(NULL), NULL);
2110 default_ime_wnd = ImmGetDefaultIMEWnd(hwnd2);
2111 todo_wine ok(!default_ime_wnd || broken(IsWindow(default_ime_wnd)), "Expected no IME windows, got %p\n", default_ime_wnd);
2113 DestroyWindow(hwnd2);
2114 DestroyWindow(hwnd1);
2116 /* Making window message-only after creation,
2117 doesn't disassociate IME window. */
2118 hwnd1 = CreateWindowA(wndcls, "Wine imm32.dll test",
2119 WS_OVERLAPPEDWINDOW,
2120 CW_USEDEFAULT, CW_USEDEFAULT,
2121 240, 120, NULL, NULL, GetModuleHandleW(NULL), NULL);
2122 default_ime_wnd = ImmGetDefaultIMEWnd(hwnd1);
2123 ok(IsWindow(default_ime_wnd), "Expected IME window existence\n");
2124 SetParent(hwnd1, HWND_MESSAGE);
2125 default_ime_wnd = ImmGetDefaultIMEWnd(hwnd1);
2126 ok(IsWindow(default_ime_wnd), "Expected IME window existence\n");
2127 DestroyWindow(hwnd1);
2128 return 1;
2131 static void test_default_ime_window_creation(void)
2133 HANDLE thread;
2134 size_t i;
2135 struct testcase_ime_window testcases[] = {
2136 /* visible, top-level window */
2137 { TRUE, TRUE },
2138 { FALSE, TRUE },
2139 { TRUE, FALSE },
2140 { FALSE, FALSE }
2143 for (i = 0; i < ARRAY_SIZE(testcases); i++)
2145 thread = CreateThread(NULL, 0, test_default_ime_window_cb, &testcases[i], 0, NULL);
2146 ok(thread != NULL, "CreateThread failed with error %lu\n", GetLastError());
2147 while (MsgWaitForMultipleObjects(1, &thread, FALSE, INFINITE, QS_ALLINPUT) == WAIT_OBJECT_0 + 1)
2149 MSG msg;
2150 while (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE))
2152 TranslateMessage(&msg);
2153 DispatchMessageA(&msg);
2156 CloseHandle(thread);
2158 if (testcases[i].top_level_window)
2160 thread = CreateThread(NULL, 0, test_default_ime_window_cancel_cb, &testcases[i], 0, NULL);
2161 ok(thread != NULL, "CreateThread failed with error %lu\n", GetLastError());
2162 WaitForSingleObject(thread, INFINITE);
2163 CloseHandle(thread);
2167 thread = CreateThread(NULL, 0, test_default_ime_disabled_cb, NULL, 0, NULL);
2168 WaitForSingleObject(thread, INFINITE);
2169 CloseHandle(thread);
2171 thread = CreateThread(NULL, 0, test_default_ime_with_message_only_window_cb, NULL, 0, NULL);
2172 WaitForSingleObject(thread, INFINITE);
2173 CloseHandle(thread);
2175 test_phase = PHASE_UNKNOWN;
2178 static void test_ImmGetIMCLockCount(void)
2180 HIMC imc;
2181 DWORD count, ret, i;
2182 INPUTCONTEXT *ic;
2184 imc = ImmCreateContext();
2185 ImmDestroyContext(imc);
2186 SetLastError(0xdeadbeef);
2187 count = ImmGetIMCLockCount((HIMC)0xdeadcafe);
2188 ok(count == 0, "Invalid IMC should return 0\n");
2189 ret = GetLastError();
2190 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2191 SetLastError(0xdeadbeef);
2192 count = ImmGetIMCLockCount(0x00000000);
2193 ok(count == 0, "NULL IMC should return 0\n");
2194 ret = GetLastError();
2195 ok(ret == 0xdeadbeef, "Last Error should remain unchanged: %08lx\n",ret);
2196 count = ImmGetIMCLockCount(imc);
2197 ok(count == 0, "Destroyed IMC should return 0\n");
2198 ret = GetLastError();
2199 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2201 imc = ImmCreateContext();
2202 count = ImmGetIMCLockCount(imc);
2203 ok(count == 0, "expect 0, returned %ld\n", count);
2204 ic = ImmLockIMC(imc);
2205 ok(ic != NULL, "ImmLockIMC failed!\n");
2206 count = ImmGetIMCLockCount(imc);
2207 ok(count == 1, "expect 1, returned %ld\n", count);
2208 ret = ImmUnlockIMC(imc);
2209 ok(ret == TRUE, "expect TRUE, ret %ld\n", ret);
2210 count = ImmGetIMCLockCount(imc);
2211 ok(count == 0, "expect 0, returned %ld\n", count);
2212 ret = ImmUnlockIMC(imc);
2213 ok(ret == TRUE, "expect TRUE, ret %ld\n", ret);
2214 count = ImmGetIMCLockCount(imc);
2215 ok(count == 0, "expect 0, returned %ld\n", count);
2217 for (i = 0; i < GMEM_LOCKCOUNT * 2; i++)
2219 ic = ImmLockIMC(imc);
2220 ok(ic != NULL, "ImmLockIMC failed!\n");
2222 count = ImmGetIMCLockCount(imc);
2223 todo_wine ok(count == GMEM_LOCKCOUNT, "expect GMEM_LOCKCOUNT, returned %ld\n", count);
2225 for (i = 0; i < GMEM_LOCKCOUNT - 1; i++)
2226 ImmUnlockIMC(imc);
2227 count = ImmGetIMCLockCount(imc);
2228 todo_wine ok(count == 1, "expect 1, returned %ld\n", count);
2229 ImmUnlockIMC(imc);
2230 count = ImmGetIMCLockCount(imc);
2231 todo_wine ok(count == 0, "expect 0, returned %ld\n", count);
2233 ImmDestroyContext(imc);
2236 static void test_ImmGetIMCCLockCount(void)
2238 HIMCC imcc;
2239 DWORD count, g_count, i;
2240 BOOL ret;
2241 VOID *p;
2243 imcc = ImmCreateIMCC(sizeof(CANDIDATEINFO));
2244 count = ImmGetIMCCLockCount(imcc);
2245 ok(count == 0, "expect 0, returned %ld\n", count);
2246 ImmLockIMCC(imcc);
2247 count = ImmGetIMCCLockCount(imcc);
2248 ok(count == 1, "expect 1, returned %ld\n", count);
2249 ret = ImmUnlockIMCC(imcc);
2250 ok(ret == FALSE, "expect FALSE, ret %d\n", ret);
2251 count = ImmGetIMCCLockCount(imcc);
2252 ok(count == 0, "expect 0, returned %ld\n", count);
2253 ret = ImmUnlockIMCC(imcc);
2254 ok(ret == FALSE, "expect FALSE, ret %d\n", ret);
2255 count = ImmGetIMCCLockCount(imcc);
2256 ok(count == 0, "expect 0, returned %ld\n", count);
2258 p = ImmLockIMCC(imcc);
2259 ok(GlobalHandle(p) == imcc, "expect %p, returned %p\n", imcc, GlobalHandle(p));
2261 for (i = 0; i < GMEM_LOCKCOUNT * 2; i++)
2263 ImmLockIMCC(imcc);
2264 count = ImmGetIMCCLockCount(imcc);
2265 g_count = GlobalFlags(imcc) & GMEM_LOCKCOUNT;
2266 ok(count == g_count, "count %ld, g_count %ld\n", count, g_count);
2268 count = ImmGetIMCCLockCount(imcc);
2269 ok(count == GMEM_LOCKCOUNT, "expect GMEM_LOCKCOUNT, returned %ld\n", count);
2271 for (i = 0; i < GMEM_LOCKCOUNT - 1; i++)
2272 GlobalUnlock(imcc);
2273 count = ImmGetIMCCLockCount(imcc);
2274 ok(count == 1, "expect 1, returned %ld\n", count);
2275 GlobalUnlock(imcc);
2276 count = ImmGetIMCCLockCount(imcc);
2277 ok(count == 0, "expect 0, returned %ld\n", count);
2279 ImmDestroyIMCC(imcc);
2282 static void test_ImmDestroyContext(void)
2284 HIMC imc;
2285 DWORD ret, count;
2286 INPUTCONTEXT *ic;
2288 imc = ImmCreateContext();
2289 count = ImmGetIMCLockCount(imc);
2290 ok(count == 0, "expect 0, returned %ld\n", count);
2291 ic = ImmLockIMC(imc);
2292 ok(ic != NULL, "ImmLockIMC failed!\n");
2293 count = ImmGetIMCLockCount(imc);
2294 ok(count == 1, "expect 1, returned %ld\n", count);
2295 ret = ImmDestroyContext(imc);
2296 ok(ret == TRUE, "Destroy a locked IMC should success!\n");
2297 ic = ImmLockIMC(imc);
2298 ok(ic == NULL, "Lock a destroyed IMC should fail!\n");
2299 ret = ImmUnlockIMC(imc);
2300 ok(ret == FALSE, "Unlock a destroyed IMC should fail!\n");
2301 count = ImmGetIMCLockCount(imc);
2302 ok(count == 0, "Get lock count of a destroyed IMC should return 0!\n");
2303 SetLastError(0xdeadbeef);
2304 ret = ImmDestroyContext(imc);
2305 ok(ret == FALSE, "Destroy a destroyed IMC should fail!\n");
2306 ret = GetLastError();
2307 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2310 static void test_ImmDestroyIMCC(void)
2312 HIMCC imcc;
2313 DWORD ret, count, size;
2314 VOID *p;
2316 imcc = ImmCreateIMCC(sizeof(CANDIDATEINFO));
2317 count = ImmGetIMCCLockCount(imcc);
2318 ok(count == 0, "expect 0, returned %ld\n", count);
2319 p = ImmLockIMCC(imcc);
2320 ok(p != NULL, "ImmLockIMCC failed!\n");
2321 count = ImmGetIMCCLockCount(imcc);
2322 ok(count == 1, "expect 1, returned %ld\n", count);
2323 size = ImmGetIMCCSize(imcc);
2324 ok(size == sizeof(CANDIDATEINFO), "returned %ld\n", size);
2325 p = ImmDestroyIMCC(imcc);
2326 ok(p == NULL, "Destroy a locked IMCC should success!\n");
2327 p = ImmLockIMCC(imcc);
2328 ok(p == NULL, "Lock a destroyed IMCC should fail!\n");
2329 ret = ImmUnlockIMCC(imcc);
2330 ok(ret == FALSE, "Unlock a destroyed IMCC should return FALSE!\n");
2331 count = ImmGetIMCCLockCount(imcc);
2332 ok(count == 0, "Get lock count of a destroyed IMCC should return 0!\n");
2333 size = ImmGetIMCCSize(imcc);
2334 ok(size == 0, "Get size of a destroyed IMCC should return 0!\n");
2335 SetLastError(0xdeadbeef);
2336 p = ImmDestroyIMCC(imcc);
2337 ok(p != NULL, "returned NULL\n");
2338 ret = GetLastError();
2339 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2342 static void test_ImmMessages(void)
2344 CANDIDATEFORM cf;
2345 imm_msgs *msg;
2346 HWND defwnd;
2347 HIMC imc;
2348 UINT idx = 0;
2350 LPINPUTCONTEXT lpIMC;
2351 LPTRANSMSG lpTransMsg;
2353 HWND hwnd = CreateWindowExA(WS_EX_CLIENTEDGE, "EDIT", "Wine imm32.dll test",
2354 WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
2355 240, 120, NULL, NULL, GetModuleHandleA(NULL), NULL);
2357 ShowWindow(hwnd, SW_SHOWNORMAL);
2358 defwnd = ImmGetDefaultIMEWnd(hwnd);
2359 imc = ImmGetContext(hwnd);
2361 ImmSetOpenStatus(imc, TRUE);
2362 msg_spy_flush_msgs();
2363 SendMessageA(defwnd, WM_IME_CONTROL, IMC_GETCANDIDATEPOS, (LPARAM)&cf );
2366 msg = msg_spy_find_next_msg(WM_IME_CONTROL,&idx);
2367 if (msg) ok(!msg->post, "Message should not be posted\n");
2368 } while (msg);
2369 msg_spy_flush_msgs();
2371 lpIMC = ImmLockIMC(imc);
2372 lpIMC->hMsgBuf = ImmReSizeIMCC(lpIMC->hMsgBuf, (lpIMC->dwNumMsgBuf + 1) * sizeof(TRANSMSG));
2373 lpTransMsg = ImmLockIMCC(lpIMC->hMsgBuf);
2374 lpTransMsg += lpIMC->dwNumMsgBuf;
2375 lpTransMsg->message = WM_IME_STARTCOMPOSITION;
2376 lpTransMsg->wParam = 0;
2377 lpTransMsg->lParam = 0;
2378 ImmUnlockIMCC(lpIMC->hMsgBuf);
2379 lpIMC->dwNumMsgBuf++;
2380 ImmUnlockIMC(imc);
2381 ImmGenerateMessage(imc);
2382 idx = 0;
2385 msg = msg_spy_find_next_msg(WM_IME_STARTCOMPOSITION, &idx);
2386 if (msg) ok(!msg->post, "Message should not be posted\n");
2387 } while (msg);
2388 msg_spy_flush_msgs();
2390 lpIMC = ImmLockIMC(imc);
2391 lpIMC->hMsgBuf = ImmReSizeIMCC(lpIMC->hMsgBuf, (lpIMC->dwNumMsgBuf + 1) * sizeof(TRANSMSG));
2392 lpTransMsg = ImmLockIMCC(lpIMC->hMsgBuf);
2393 lpTransMsg += lpIMC->dwNumMsgBuf;
2394 lpTransMsg->message = WM_IME_COMPOSITION;
2395 lpTransMsg->wParam = 0;
2396 lpTransMsg->lParam = 0;
2397 ImmUnlockIMCC(lpIMC->hMsgBuf);
2398 lpIMC->dwNumMsgBuf++;
2399 ImmUnlockIMC(imc);
2400 ImmGenerateMessage(imc);
2401 idx = 0;
2404 msg = msg_spy_find_next_msg(WM_IME_COMPOSITION, &idx);
2405 if (msg) ok(!msg->post, "Message should not be posted\n");
2406 } while (msg);
2407 msg_spy_flush_msgs();
2409 lpIMC = ImmLockIMC(imc);
2410 lpIMC->hMsgBuf = ImmReSizeIMCC(lpIMC->hMsgBuf, (lpIMC->dwNumMsgBuf + 1) * sizeof(TRANSMSG));
2411 lpTransMsg = ImmLockIMCC(lpIMC->hMsgBuf);
2412 lpTransMsg += lpIMC->dwNumMsgBuf;
2413 lpTransMsg->message = WM_IME_ENDCOMPOSITION;
2414 lpTransMsg->wParam = 0;
2415 lpTransMsg->lParam = 0;
2416 ImmUnlockIMCC(lpIMC->hMsgBuf);
2417 lpIMC->dwNumMsgBuf++;
2418 ImmUnlockIMC(imc);
2419 ImmGenerateMessage(imc);
2420 idx = 0;
2423 msg = msg_spy_find_next_msg(WM_IME_ENDCOMPOSITION, &idx);
2424 if (msg) ok(!msg->post, "Message should not be posted\n");
2425 } while (msg);
2426 msg_spy_flush_msgs();
2428 ImmSetOpenStatus(imc, FALSE);
2429 ImmReleaseContext(hwnd, imc);
2430 DestroyWindow(hwnd);
2433 static LRESULT CALLBACK processkey_wnd_proc( HWND hWnd, UINT msg, WPARAM wParam,
2434 LPARAM lParam )
2436 return DefWindowProcW(hWnd, msg, wParam, lParam);
2439 static void test_ime_processkey(void)
2441 MSG msg;
2442 WNDCLASSW wclass;
2443 HANDLE hInstance = GetModuleHandleW(NULL);
2444 TEST_INPUT inputs[2];
2445 HIMC imc;
2446 INT rc;
2447 HWND hWndTest;
2449 wclass.lpszClassName = L"ProcessKeyTestClass";
2450 wclass.style = CS_HREDRAW | CS_VREDRAW;
2451 wclass.lpfnWndProc = processkey_wnd_proc;
2452 wclass.hInstance = hInstance;
2453 wclass.hIcon = LoadIconW(0, (LPCWSTR)IDI_APPLICATION);
2454 wclass.hCursor = LoadCursorW( NULL, (LPCWSTR)IDC_ARROW);
2455 wclass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
2456 wclass.lpszMenuName = 0;
2457 wclass.cbClsExtra = 0;
2458 wclass.cbWndExtra = 0;
2459 if(!RegisterClassW(&wclass)){
2460 win_skip("Failed to register window.\n");
2461 return;
2464 /* create the test window that will receive the keystrokes */
2465 hWndTest = CreateWindowW(wclass.lpszClassName, L"ProcessKey",
2466 WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, 100, 100,
2467 NULL, NULL, hInstance, NULL);
2469 ShowWindow(hWndTest, SW_SHOW);
2470 SetWindowPos(hWndTest, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE);
2471 SetForegroundWindow(hWndTest);
2472 UpdateWindow(hWndTest);
2474 imc = ImmGetContext(hWndTest);
2475 if (!imc)
2477 win_skip("IME not supported\n");
2478 DestroyWindow(hWndTest);
2479 return;
2482 rc = ImmSetOpenStatus(imc, TRUE);
2483 if (rc != TRUE)
2485 win_skip("Unable to open IME\n");
2486 ImmReleaseContext(hWndTest, imc);
2487 DestroyWindow(hWndTest);
2488 return;
2491 /* flush pending messages */
2492 while (PeekMessageW(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageW(&msg);
2494 SetFocus(hWndTest);
2496 /* init input data that never changes */
2497 inputs[1].type = inputs[0].type = INPUT_KEYBOARD;
2498 inputs[1].u.ki.dwExtraInfo = inputs[0].u.ki.dwExtraInfo = 0;
2499 inputs[1].u.ki.time = inputs[0].u.ki.time = 0;
2501 /* Pressing a key */
2502 inputs[0].u.ki.wVk = 0x41;
2503 inputs[0].u.ki.wScan = 0x1e;
2504 inputs[0].u.ki.dwFlags = 0x0;
2506 pSendInput(1, (INPUT*)inputs, sizeof(INPUT));
2508 while(PeekMessageW(&msg, hWndTest, 0, 0, PM_NOREMOVE)) {
2509 if(msg.message != WM_KEYDOWN)
2510 PeekMessageW(&msg, hWndTest, 0, 0, PM_REMOVE);
2511 else
2513 ok(msg.wParam != VK_PROCESSKEY,"Incorrect ProcessKey Found\n");
2514 PeekMessageW(&msg, hWndTest, 0, 0, PM_REMOVE);
2515 if(msg.wParam == VK_PROCESSKEY)
2516 trace("ProcessKey was correctly found\n");
2518 TranslateMessage(&msg);
2519 /* test calling TranslateMessage multiple times */
2520 TranslateMessage(&msg);
2521 DispatchMessageW(&msg);
2524 inputs[0].u.ki.wVk = 0x41;
2525 inputs[0].u.ki.wScan = 0x1e;
2526 inputs[0].u.ki.dwFlags = KEYEVENTF_KEYUP;
2528 pSendInput(1, (INPUT*)inputs, sizeof(INPUT));
2530 while(PeekMessageW(&msg, hWndTest, 0, 0, PM_NOREMOVE)) {
2531 if(msg.message != WM_KEYUP)
2532 PeekMessageW(&msg, hWndTest, 0, 0, PM_REMOVE);
2533 else
2535 ok(msg.wParam != VK_PROCESSKEY,"Incorrect ProcessKey Found\n");
2536 PeekMessageW(&msg, hWndTest, 0, 0, PM_REMOVE);
2537 ok(msg.wParam != VK_PROCESSKEY,"ProcessKey should still not be Found\n");
2539 TranslateMessage(&msg);
2540 DispatchMessageW(&msg);
2543 ImmReleaseContext(hWndTest, imc);
2544 ImmSetOpenStatus(imc, FALSE);
2545 DestroyWindow(hWndTest);
2548 static void test_InvalidIMC(void)
2550 HIMC imc_destroy;
2551 HIMC imc_null = 0x00000000;
2552 HIMC imc_bad = (HIMC)0xdeadcafe;
2554 HIMC imc1, imc2, oldimc;
2555 DWORD ret;
2556 DWORD count;
2557 CHAR buffer[1000];
2558 INPUTCONTEXT *ic;
2559 LOGFONTA lf;
2560 BOOL r;
2562 memset(&lf, 0, sizeof(lf));
2564 imc_destroy = ImmCreateContext();
2565 ret = ImmDestroyContext(imc_destroy);
2566 ok(ret == TRUE, "Destroy an IMC should success!\n");
2568 /* Test associating destroyed imc */
2569 imc1 = ImmGetContext(hwnd);
2570 SetLastError(0xdeadbeef);
2571 oldimc = ImmAssociateContext(hwnd, imc_destroy);
2572 ok(!oldimc, "Associating to a destroyed imc should fail!\n");
2573 ret = GetLastError();
2574 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2575 imc2 = ImmGetContext(hwnd);
2576 ok(imc1 == imc2, "imc should not changed! imc1 %p, imc2 %p\n", imc1, imc2);
2578 SET_ENABLE(WM_IME_SETCONTEXT_DEACTIVATE, TRUE);
2579 SET_ENABLE(WM_IME_SETCONTEXT_ACTIVATE, TRUE);
2580 r = ImmSetActiveContext(hwnd, imc_destroy, TRUE);
2581 ok(!r, "ImmSetActiveContext succeeded\n");
2582 SET_EXPECT(WM_IME_SETCONTEXT_DEACTIVATE);
2583 r = ImmSetActiveContext(hwnd, imc_destroy, FALSE);
2584 ok(r, "ImmSetActiveContext failed\n");
2585 CHECK_CALLED(WM_IME_SETCONTEXT_DEACTIVATE);
2586 SET_ENABLE(WM_IME_SETCONTEXT_ACTIVATE, FALSE);
2587 SET_ENABLE(WM_IME_SETCONTEXT_DEACTIVATE, FALSE);
2589 /* Test associating NULL imc, which is different from an invalid imc */
2590 oldimc = ImmAssociateContext(hwnd, imc_null);
2591 ok(oldimc != NULL, "Associating to NULL imc should success!\n");
2592 imc2 = ImmGetContext(hwnd);
2593 ok(!imc2, "expect NULL, returned %p\n", imc2);
2594 oldimc = ImmAssociateContext(hwnd, imc1);
2595 ok(!oldimc, "expect NULL, returned %p\n", oldimc);
2596 imc2 = ImmGetContext(hwnd);
2597 ok(imc2 == imc1, "imc should not changed! imc2 %p, imc1 %p\n", imc2, imc1);
2599 /* Test associating invalid imc */
2600 imc1 = ImmGetContext(hwnd);
2601 SetLastError(0xdeadbeef);
2602 oldimc = ImmAssociateContext(hwnd, imc_bad);
2603 ok(!oldimc, "Associating to a destroyed imc should fail!\n");
2604 ret = GetLastError();
2605 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2606 imc2 = ImmGetContext(hwnd);
2607 ok(imc1 == imc2, "imc should not changed! imc1 %p, imc2 %p\n", imc1, imc2);
2610 /* Test ImmGetCandidateListA */
2611 SetLastError(0xdeadbeef);
2612 ret = ImmGetCandidateListA(imc_bad, 0, NULL, 0);
2613 ok(ret == 0, "Bad IME should return 0\n");
2614 ret = GetLastError();
2615 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2616 SetLastError(0xdeadbeef);
2617 ret = ImmGetCandidateListA(imc_null, 0, NULL, 0);
2618 ok(ret == 0, "NULL IME should return 0\n");
2619 ret = GetLastError();
2620 ok(ret == 0xdeadbeef, "last error should remain unchanged %08lx!\n", ret);
2621 SetLastError(0xdeadbeef);
2622 ret = ImmGetCandidateListA(imc_destroy, 0, NULL, 0);
2623 ok(ret == 0, "Destroyed IME should return 0\n");
2624 ret = GetLastError();
2625 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2627 /* Test ImmGetCandidateListCountA*/
2628 SetLastError(0xdeadbeef);
2629 ret = ImmGetCandidateListCountA(imc_bad,&count);
2630 ok(ret == 0, "Bad IME should return 0\n");
2631 ret = GetLastError();
2632 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2633 SetLastError(0xdeadbeef);
2634 ret = ImmGetCandidateListCountA(imc_null,&count);
2635 ok(ret == 0, "NULL IME should return 0\n");
2636 ret = GetLastError();
2637 ok(ret == 0xdeadbeef, "last error should remain unchanged %08lx!\n", ret);
2638 SetLastError(0xdeadbeef);
2639 ret = ImmGetCandidateListCountA(imc_destroy,&count);
2640 ok(ret == 0, "Destroyed IME should return 0\n");
2641 ret = GetLastError();
2642 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2644 /* Test ImmGetCandidateWindow */
2645 SetLastError(0xdeadbeef);
2646 ret = ImmGetCandidateWindow(imc_bad, 0, (LPCANDIDATEFORM)buffer);
2647 ok(ret == 0, "Bad IME should return 0\n");
2648 ret = GetLastError();
2649 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2650 SetLastError(0xdeadbeef);
2651 ret = ImmGetCandidateWindow(imc_null, 0, (LPCANDIDATEFORM)buffer);
2652 ok(ret == 0, "NULL IME should return 0\n");
2653 ret = GetLastError();
2654 ok(ret == 0xdeadbeef, "last error should remain unchanged %08lx!\n", ret);
2655 SetLastError(0xdeadbeef);
2656 ret = ImmGetCandidateWindow(imc_destroy, 0, (LPCANDIDATEFORM)buffer);
2657 ok(ret == 0, "Destroyed IME should return 0\n");
2658 ret = GetLastError();
2659 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2661 /* Test ImmGetCompositionFontA */
2662 SetLastError(0xdeadbeef);
2663 ret = ImmGetCompositionFontA(imc_bad, (LPLOGFONTA)buffer);
2664 ok(ret == 0, "Bad IME should return 0\n");
2665 ret = GetLastError();
2666 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2667 SetLastError(0xdeadbeef);
2668 ret = ImmGetCompositionFontA(imc_null, (LPLOGFONTA)buffer);
2669 ok(ret == 0, "NULL IME should return 0\n");
2670 ret = GetLastError();
2671 ok(ret == 0xdeadbeef, "last error should remain unchanged %08lx!\n", ret);
2672 SetLastError(0xdeadbeef);
2673 ret = ImmGetCompositionFontA(imc_destroy, (LPLOGFONTA)buffer);
2674 ok(ret == 0, "Destroyed IME should return 0\n");
2675 ret = GetLastError();
2676 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2678 /* Test ImmGetCompositionWindow */
2679 SetLastError(0xdeadbeef);
2680 ret = ImmGetCompositionWindow(imc_bad, (LPCOMPOSITIONFORM)buffer);
2681 ok(ret == 0, "Bad IME should return 0\n");
2682 ret = GetLastError();
2683 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2684 SetLastError(0xdeadbeef);
2685 ret = ImmGetCompositionWindow(imc_null, (LPCOMPOSITIONFORM)buffer);
2686 ok(ret == 0, "NULL IME should return 0\n");
2687 ret = GetLastError();
2688 ok(ret == 0xdeadbeef, "last error should remain unchanged %08lx!\n", ret);
2689 SetLastError(0xdeadbeef);
2690 ret = ImmGetCompositionWindow(imc_destroy, (LPCOMPOSITIONFORM)buffer);
2691 ok(ret == 0, "Destroyed IME should return 0\n");
2692 ret = GetLastError();
2693 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2695 /* Test ImmGetCompositionStringA */
2696 SetLastError(0xdeadbeef);
2697 ret = ImmGetCompositionStringA(imc_bad, GCS_COMPSTR, NULL, 0);
2698 ok(ret == 0, "Bad IME should return 0\n");
2699 ret = GetLastError();
2700 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2701 SetLastError(0xdeadbeef);
2702 ret = ImmGetCompositionStringA(imc_null, GCS_COMPSTR, NULL, 0);
2703 ok(ret == 0, "NULL IME should return 0\n");
2704 ret = GetLastError();
2705 ok(ret == 0xdeadbeef, "last error should remain unchanged %08lx!\n", ret);
2706 SetLastError(0xdeadbeef);
2707 ret = ImmGetCompositionStringA(imc_destroy, GCS_COMPSTR, NULL, 0);
2708 ok(ret == 0, "Destroyed IME should return 0\n");
2709 ret = GetLastError();
2710 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2712 /* Test ImmSetOpenStatus */
2713 SetLastError(0xdeadbeef);
2714 ret = ImmSetOpenStatus(imc_bad, 1);
2715 ok(ret == 0, "Bad IME should return 0\n");
2716 ret = GetLastError();
2717 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2718 SetLastError(0xdeadbeef);
2719 ret = ImmSetOpenStatus(imc_null, 1);
2720 ok(ret == 0, "NULL IME should return 0\n");
2721 ret = GetLastError();
2722 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2723 SetLastError(0xdeadbeef);
2724 ret = ImmSetOpenStatus(imc_destroy, 1);
2725 ok(ret == 0, "Destroyed IME should return 0\n");
2726 ret = GetLastError();
2727 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2729 /* Test ImmGetOpenStatus */
2730 SetLastError(0xdeadbeef);
2731 ret = ImmGetOpenStatus(imc_bad);
2732 ok(ret == 0, "Bad IME should return 0\n");
2733 ret = GetLastError();
2734 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2735 SetLastError(0xdeadbeef);
2736 ret = ImmGetOpenStatus(imc_null);
2737 ok(ret == 0, "NULL IME should return 0\n");
2738 ret = GetLastError();
2739 ok(ret == 0xdeadbeef, "last error should remain unchanged %08lx!\n", ret);
2740 SetLastError(0xdeadbeef);
2741 ret = ImmGetOpenStatus(imc_destroy);
2742 ok(ret == 0, "Destroyed IME should return 0\n");
2743 ret = GetLastError();
2744 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2746 /* Test ImmGetStatusWindowPos */
2747 SetLastError(0xdeadbeef);
2748 ret = ImmGetStatusWindowPos(imc_bad, NULL);
2749 ok(ret == 0, "Bad IME should return 0\n");
2750 ret = GetLastError();
2751 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2752 SetLastError(0xdeadbeef);
2753 ret = ImmGetStatusWindowPos(imc_null, NULL);
2754 ok(ret == 0, "NULL IME should return 0\n");
2755 ret = GetLastError();
2756 ok(ret == 0xdeadbeef, "last error should remain unchanged %08lx!\n", ret);
2757 SetLastError(0xdeadbeef);
2758 ret = ImmGetStatusWindowPos(imc_destroy, NULL);
2759 ok(ret == 0, "Destroyed IME should return 0\n");
2760 ret = GetLastError();
2761 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2763 /* Test ImmRequestMessageA */
2764 SetLastError(0xdeadbeef);
2765 ret = ImmRequestMessageA(imc_bad, WM_CHAR, 0);
2766 ok(ret == 0, "Bad IME should return 0\n");
2767 ret = GetLastError();
2768 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2769 SetLastError(0xdeadbeef);
2770 ret = ImmRequestMessageA(imc_null, WM_CHAR, 0);
2771 ok(ret == 0, "NULL IME should return 0\n");
2772 ret = GetLastError();
2773 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2774 SetLastError(0xdeadbeef);
2775 ret = ImmRequestMessageA(imc_destroy, WM_CHAR, 0);
2776 ok(ret == 0, "Destroyed IME should return 0\n");
2777 ret = GetLastError();
2778 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2780 /* Test ImmSetCompositionFontA */
2781 SetLastError(0xdeadbeef);
2782 ret = ImmSetCompositionFontA(imc_bad, &lf);
2783 ok(ret == 0, "Bad IME should return 0\n");
2784 ret = GetLastError();
2785 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2786 SetLastError(0xdeadbeef);
2787 ret = ImmSetCompositionFontA(imc_null, &lf);
2788 ok(ret == 0, "NULL IME should return 0\n");
2789 ret = GetLastError();
2790 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2791 SetLastError(0xdeadbeef);
2792 ret = ImmSetCompositionFontA(imc_destroy, &lf);
2793 ok(ret == 0, "Destroyed IME should return 0\n");
2794 ret = GetLastError();
2795 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2797 /* Test ImmSetCompositionWindow */
2798 SetLastError(0xdeadbeef);
2799 ret = ImmSetCompositionWindow(imc_bad, NULL);
2800 ok(ret == 0, "Bad IME should return 0\n");
2801 ret = GetLastError();
2802 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2803 SetLastError(0xdeadbeef);
2804 ret = ImmSetCompositionWindow(imc_null, NULL);
2805 ok(ret == 0, "NULL IME should return 0\n");
2806 ret = GetLastError();
2807 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2808 SetLastError(0xdeadbeef);
2809 ret = ImmSetCompositionWindow(imc_destroy, NULL);
2810 ok(ret == 0, "Destroyed IME should return 0\n");
2811 ret = GetLastError();
2812 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2814 /* Test ImmSetConversionStatus */
2815 SetLastError(0xdeadbeef);
2816 ret = ImmSetConversionStatus(imc_bad, 0, 0);
2817 ok(ret == 0, "Bad IME should return 0\n");
2818 ret = GetLastError();
2819 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2820 SetLastError(0xdeadbeef);
2821 ret = ImmSetConversionStatus(imc_null, 0, 0);
2822 ok(ret == 0, "NULL IME should return 0\n");
2823 ret = GetLastError();
2824 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2825 SetLastError(0xdeadbeef);
2826 ret = ImmSetConversionStatus(imc_destroy, 0, 0);
2827 ok(ret == 0, "Destroyed IME should return 0\n");
2828 ret = GetLastError();
2829 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2831 /* Test ImmSetStatusWindowPos */
2832 SetLastError(0xdeadbeef);
2833 ret = ImmSetStatusWindowPos(imc_bad, 0);
2834 ok(ret == 0, "Bad IME should return 0\n");
2835 ret = GetLastError();
2836 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2837 SetLastError(0xdeadbeef);
2838 ret = ImmSetStatusWindowPos(imc_null, 0);
2839 ok(ret == 0, "NULL IME should return 0\n");
2840 ret = GetLastError();
2841 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2842 SetLastError(0xdeadbeef);
2843 ret = ImmSetStatusWindowPos(imc_destroy, 0);
2844 ok(ret == 0, "Destroyed IME should return 0\n");
2845 ret = GetLastError();
2846 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2848 /* Test ImmGetImeMenuItemsA */
2849 SetLastError(0xdeadbeef);
2850 ret = ImmGetImeMenuItemsA(imc_bad, 0, 0, NULL, NULL, 0);
2851 ok(ret == 0, "Bad IME should return 0\n");
2852 ret = GetLastError();
2853 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2854 SetLastError(0xdeadbeef);
2855 ret = ImmGetImeMenuItemsA(imc_null, 0, 0, NULL, NULL, 0);
2856 ok(ret == 0, "NULL IME should return 0\n");
2857 ret = GetLastError();
2858 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2859 SetLastError(0xdeadbeef);
2860 ret = ImmGetImeMenuItemsA(imc_destroy, 0, 0, NULL, NULL, 0);
2861 ok(ret == 0, "Destroyed IME should return 0\n");
2862 ret = GetLastError();
2863 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2865 /* Test ImmLockIMC */
2866 SetLastError(0xdeadbeef);
2867 ic = ImmLockIMC(imc_bad);
2868 ok(ic == 0, "Bad IME should return 0\n");
2869 ret = GetLastError();
2870 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2871 SetLastError(0xdeadbeef);
2872 ic = ImmLockIMC(imc_null);
2873 ok(ic == 0, "NULL IME should return 0\n");
2874 ret = GetLastError();
2875 ok(ret == 0xdeadbeef, "last error should remain unchanged %08lx!\n", ret);
2876 SetLastError(0xdeadbeef);
2877 ic = ImmLockIMC(imc_destroy);
2878 ok(ic == 0, "Destroyed IME should return 0\n");
2879 ret = GetLastError();
2880 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2882 /* Test ImmUnlockIMC */
2883 SetLastError(0xdeadbeef);
2884 ret = ImmUnlockIMC(imc_bad);
2885 ok(ret == 0, "Bad IME should return 0\n");
2886 ret = GetLastError();
2887 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2888 SetLastError(0xdeadbeef);
2889 ret = ImmUnlockIMC(imc_null);
2890 ok(ret == 0, "NULL IME should return 0\n");
2891 ret = GetLastError();
2892 ok(ret == 0xdeadbeef, "last error should remain unchanged %08lx!\n", ret);
2893 SetLastError(0xdeadbeef);
2894 ret = ImmUnlockIMC(imc_destroy);
2895 ok(ret == 0, "Destroyed IME should return 0\n");
2896 ret = GetLastError();
2897 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2899 /* Test ImmGenerateMessage */
2900 SetLastError(0xdeadbeef);
2901 ret = ImmGenerateMessage(imc_bad);
2902 ok(ret == 0, "Bad IME should return 0\n");
2903 ret = GetLastError();
2904 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2905 SetLastError(0xdeadbeef);
2906 ret = ImmGenerateMessage(imc_null);
2907 ok(ret == 0, "NULL IME should return 0\n");
2908 ret = GetLastError();
2909 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2910 SetLastError(0xdeadbeef);
2911 ret = ImmGenerateMessage(imc_destroy);
2912 ok(ret == 0, "Destroyed IME should return 0\n");
2913 ret = GetLastError();
2914 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
2917 #define test_apttype(apttype) _test_apttype(apttype, __LINE__)
2918 static void _test_apttype(APTTYPE apttype, unsigned int line)
2920 APTTYPEQUALIFIER qualifier;
2921 HRESULT hr, hr_expected;
2922 APTTYPE type;
2924 hr = CoGetApartmentType(&type, &qualifier);
2925 hr_expected = (apttype == -1 ? CO_E_NOTINITIALIZED : S_OK);
2926 ok_(__FILE__, line)(hr == hr_expected, "CoGetApartmentType returned %lx\n", hr);
2927 if (FAILED(hr))
2928 return;
2930 ok_(__FILE__, line)(type == apttype, "type %x\n", type);
2931 ok_(__FILE__, line)(!qualifier, "qualifier %x\n", qualifier);
2934 static DWORD WINAPI com_initialization_thread(void *arg)
2936 HRESULT hr;
2937 BOOL r;
2939 test_apttype(-1);
2940 ImmDisableIME(GetCurrentThreadId());
2941 r = ImmSetActiveContext(NULL, NULL, TRUE);
2942 ok(r, "ImmSetActiveContext failed\n");
2943 test_apttype(APTTYPE_MAINSTA);
2944 hr = CoInitialize(NULL);
2945 ok(hr == S_OK, "CoInitialize returned %lx\n", hr);
2946 CoUninitialize();
2947 test_apttype(-1);
2949 /* Changes IMM behavior so it no longer initialized COM */
2950 r = ImmSetActiveContext(NULL, NULL, TRUE);
2951 ok(r, "ImmSetActiveContext failed\n");
2952 test_apttype(APTTYPE_MAINSTA);
2953 hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
2954 ok(hr == S_OK, "CoInitialize returned %lx\n", hr);
2955 test_apttype(APTTYPE_MTA);
2956 CoUninitialize();
2957 test_apttype(-1);
2958 r = ImmSetActiveContext(NULL, NULL, TRUE);
2959 ok(r, "ImmSetActiveContext failed\n");
2960 test_apttype(-1);
2961 return 0;
2964 static void test_com_initialization(void)
2966 APTTYPEQUALIFIER qualifier;
2967 HANDLE thread;
2968 APTTYPE type;
2969 HRESULT hr;
2970 HWND wnd;
2971 BOOL r;
2973 thread = CreateThread(NULL, 0, com_initialization_thread, NULL, 0, NULL);
2974 ok(thread != NULL, "CreateThread failed\n");
2975 WaitForSingleObject(thread, INFINITE);
2976 CloseHandle(thread);
2978 test_apttype(-1);
2979 r = ImmSetActiveContext(NULL, (HIMC)0xdeadbeef, TRUE);
2980 ok(!r, "ImmSetActiveContext succeeded\n");
2981 test_apttype(-1);
2983 r = ImmSetActiveContext(NULL, NULL, TRUE);
2984 ok(r, "ImmSetActiveContext failed\n");
2985 test_apttype(APTTYPE_MAINSTA);
2987 /* Force default IME window destruction */
2988 wnd = CreateWindowA("static", "static", 0, 0, 0, 0, 0, 0, 0, 0, 0);
2989 ok(wnd != NULL, "CreateWindow failed\n");
2990 DestroyWindow(wnd);
2991 test_apttype(-1);
2993 r = ImmSetActiveContext(NULL, NULL, TRUE);
2994 ok(r, "ImmSetActiveContext failed\n");
2995 test_apttype(APTTYPE_MAINSTA);
2996 hr = CoInitialize(NULL);
2997 ok(hr == S_OK, "CoInitialize returned %lx\n", hr);
2998 CoUninitialize();
2999 test_apttype(-1);
3001 /* Test with default IME window created */
3002 wnd = CreateWindowA("static", "static", 0, 0, 0, 0, 0, 0, 0, 0, 0);
3003 ok(wnd != NULL, "CreateWindow failed\n");
3004 test_apttype(-1);
3005 r = ImmSetActiveContext(NULL, NULL, TRUE);
3006 ok(r, "ImmSetActiveContext failed\n");
3007 test_apttype(APTTYPE_MAINSTA);
3008 hr = CoInitialize(NULL);
3009 ok(hr == S_OK, "CoInitialize returned %lx\n", hr);
3010 CoUninitialize();
3011 test_apttype(APTTYPE_MAINSTA);
3012 DestroyWindow(wnd);
3013 test_apttype(-1);
3015 wnd = CreateWindowA("static", "static", 0, 0, 0, 0, 0, 0, 0, 0, 0);
3016 ok(wnd != NULL, "CreateWindow failed\n");
3017 r = ImmSetActiveContext(NULL, NULL, TRUE);
3018 CoUninitialize();
3019 hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
3020 ok(hr == S_OK, "CoInitialize returned %lx\n", hr);
3021 test_apttype(APTTYPE_MTA);
3022 DestroyWindow(wnd);
3024 hr = CoGetApartmentType(&type, &qualifier);
3025 ok(hr == CO_E_NOTINITIALIZED || broken(hr == S_OK) /* w10v22H2 */,
3026 "CoGetApartmentType returned %#lx\n", hr);
3027 test_apttype(hr == S_OK ? APTTYPE_MTA : -1);
3029 wnd = CreateWindowA("static", "static", WS_POPUP, 0, 0, 100, 100, 0, 0, 0, 0);
3030 ok(wnd != NULL, "CreateWindow failed\n");
3031 test_apttype(hr == S_OK ? APTTYPE_MTA : -1);
3032 ShowWindow(wnd, SW_SHOW);
3033 test_apttype(hr == S_OK ? APTTYPE_MTA : APTTYPE_MAINSTA);
3034 DestroyWindow(wnd);
3035 test_apttype(-1);
3038 static DWORD WINAPI disable_ime_thread(void *arg)
3040 HWND h, def;
3041 MSG msg;
3042 BOOL r;
3044 h = CreateWindowA("static", "static", 0, 0, 0, 0, 0, 0, 0, 0, 0);
3045 ok(h != NULL, "CreateWindow failed\n");
3046 def = ImmGetDefaultIMEWnd(h);
3047 ok(def != NULL, "ImmGetDefaultIMEWnd returned NULL\n");
3049 r = ImmDisableIME(arg ? GetCurrentThreadId() : 0);
3050 ok(r, "ImmDisableIME failed\n");
3052 if (arg)
3054 def = ImmGetDefaultIMEWnd(h);
3055 todo_wine ok(def != NULL, "ImmGetDefaultIMEWnd returned NULL\n");
3056 while(PeekMessageA(&msg, 0, 0, 0, PM_REMOVE))
3057 DispatchMessageA(&msg);
3059 def = ImmGetDefaultIMEWnd(h);
3060 ok(!def, "ImmGetDefaultIMEWnd returned %p\n", def);
3061 return 0;
3064 static DWORD WINAPI check_not_disabled_ime_thread(void *arg)
3066 HWND def, hwnd;
3068 WaitForSingleObject(arg, INFINITE);
3069 hwnd = CreateWindowA("static", "static", 0, 0, 0, 0, 0, 0, 0, 0, 0);
3070 ok(hwnd != NULL, "CreateWindow failed\n");
3071 def = ImmGetDefaultIMEWnd(hwnd);
3072 ok(def != NULL, "ImmGetDefaultIMEWnd returned %p\n", def);
3073 return 0;
3076 static DWORD WINAPI disable_ime_process(void *arg)
3078 BOOL r = ImmDisableIME(-1);
3079 ok(r, "ImmDisableIME failed\n");
3080 return 0;
3083 static void test_ImmDisableIME(void)
3085 HANDLE thread, event;
3086 DWORD tid;
3087 HWND def, def2;
3088 BOOL r;
3090 def = ImmGetDefaultIMEWnd(hwnd);
3091 ok(def != NULL, "ImmGetDefaultIMEWnd(hwnd) returned NULL\n");
3093 event = CreateEventW(NULL, TRUE, FALSE, FALSE);
3094 thread = CreateThread(NULL, 0, check_not_disabled_ime_thread, event, 0, &tid);
3095 ok(thread != NULL, "CreateThread failed\n");
3096 r = ImmDisableIME(tid);
3097 ok(!r, "ImmDisableIME(tid) succeeded\n");
3098 SetEvent(event);
3099 WaitForSingleObject(thread, INFINITE);
3100 CloseHandle(thread);
3101 CloseHandle(event);
3103 thread = CreateThread(NULL, 0, disable_ime_thread, 0, 0, NULL);
3104 ok(thread != NULL, "CreateThread failed\n");
3105 WaitForSingleObject(thread, INFINITE);
3106 CloseHandle(thread);
3108 thread = CreateThread(NULL, 0, disable_ime_thread, (void*)1, 0, NULL);
3109 ok(thread != NULL, "CreateThread failed\n");
3110 WaitForSingleObject(thread, INFINITE);
3111 CloseHandle(thread);
3113 msg_spy_pump_msg_queue();
3114 thread = CreateThread(NULL, 0, disable_ime_process, 0, 0, NULL);
3115 ok(thread != NULL, "CreateThread failed\n");
3116 WaitForSingleObject(thread, INFINITE);
3117 CloseHandle(thread);
3119 ok(IsWindow(def), "not a window\n");
3120 def2 = ImmGetDefaultIMEWnd(hwnd);
3121 ok(def2 == def, "ImmGetDefaultIMEWnd(hwnd) returned %p\n", def2);
3122 ok(IsWindow(def), "not a window\n");
3123 msg_spy_pump_msg_queue();
3124 ok(!IsWindow(def), "window is still valid\n");
3125 def = ImmGetDefaultIMEWnd(hwnd);
3126 ok(!def, "ImmGetDefaultIMEWnd(hwnd) returned %p\n", def);
3128 r = ImmDisableIME(-1);
3129 ok(r, "ImmDisableIME(-1) failed\n");
3130 def = ImmGetDefaultIMEWnd(hwnd);
3131 ok(!def, "ImmGetDefaultIMEWnd(hwnd) returned %p\n", def);
3134 static BOOL WINAPI ime_ImeConfigure( HKL hkl, HWND hwnd, DWORD mode, void *data )
3136 ime_trace( "hkl %p, hwnd %p, mode %lu, data %p\n", hkl, hwnd, mode, data );
3137 ok( 0, "unexpected call\n" );
3138 return FALSE;
3141 static DWORD WINAPI ime_ImeConversionList( HIMC himc, const WCHAR *source, CANDIDATELIST *dest,
3142 DWORD dest_len, UINT flag )
3144 ime_trace( "himc %p, source %s, dest %p, dest_len %lu, flag %#x\n",
3145 himc, debugstr_w(source), dest, dest_len, flag );
3146 ok( 0, "unexpected call\n" );
3147 return 0;
3150 static BOOL WINAPI ime_ImeDestroy( UINT force )
3152 ime_trace( "force %u\n", force );
3154 todo_wine_if( todo_ImeDestroy )
3155 CHECK_EXPECT( ImeDestroy );
3157 ok( !force, "got force %u\n", force );
3159 return TRUE;
3162 static UINT WINAPI ime_ImeEnumRegisterWord( REGISTERWORDENUMPROCW proc, const WCHAR *reading, DWORD style,
3163 const WCHAR *string, void *data )
3165 ime_trace( "proc %p, reading %s, style %lu, string %s, data %p\n",
3166 proc, debugstr_w(reading), style, debugstr_w(string), data );
3168 ok_eq( default_hkl, GetKeyboardLayout( 0 ), HKL, "%p" );
3169 CHECK_EXPECT( ImeEnumRegisterWord );
3171 if (!style)
3173 ok_eq( 0, reading, const void *, "%p" );
3174 ok_eq( 0, string, const void *, "%p" );
3176 else if (ime_info.fdwProperty & IME_PROP_UNICODE)
3178 ok_eq( 0xdeadbeef, style, UINT, "%#x" );
3179 ok_wcs( L"Reading", reading );
3180 ok_wcs( L"String", string );
3182 else
3184 ok_eq( 0xdeadbeef, style, UINT, "%#x" );
3185 ok_str( "Reading", (char *)reading );
3186 ok_str( "String", (char *)string );
3189 if (style) return proc( reading, style, string, data );
3190 return 0;
3193 static LRESULT WINAPI ime_ImeEscape( HIMC himc, UINT escape, void *data )
3195 ime_trace( "himc %p, escape %#x, data %p\n", himc, escape, data );
3197 ok_eq( default_hkl, GetKeyboardLayout( 0 ), HKL, "%p" );
3198 CHECK_EXPECT( ImeEscape );
3200 switch (escape)
3202 case IME_ESC_SET_EUDC_DICTIONARY:
3203 if (!data) return 4;
3204 if (ime_info.fdwProperty & IME_PROP_UNICODE)
3205 ok_wcs( L"EscapeIme", data );
3206 else
3207 ok_str( "EscapeIme", data );
3208 /* fallthrough */
3209 case IME_ESC_QUERY_SUPPORT:
3210 case IME_ESC_SEQUENCE_TO_INTERNAL:
3211 case IME_ESC_GET_EUDC_DICTIONARY:
3212 case IME_ESC_MAX_KEY:
3213 case IME_ESC_IME_NAME:
3214 case IME_ESC_HANJA_MODE:
3215 case IME_ESC_GETHELPFILENAME:
3216 if (!data) return 4;
3217 if (ime_info.fdwProperty & IME_PROP_UNICODE) wcscpy( data, L"ImeEscape" );
3218 else strcpy( data, "ImeEscape" );
3219 return 4;
3222 ok_eq( 0xdeadbeef, escape, UINT, "%#x" );
3223 ok_eq( NULL, data, void *, "%p" );
3225 return TRUE;
3228 static DWORD WINAPI ime_ImeGetImeMenuItems( HIMC himc, DWORD flags, DWORD type, IMEMENUITEMINFOW *parent,
3229 IMEMENUITEMINFOW *menu, DWORD size )
3231 ime_trace( "himc %p, flags %#lx, type %lu, parent %p, menu %p, size %#lx\n",
3232 himc, flags, type, parent, menu, size );
3233 ok( 0, "unexpected call\n" );
3234 return 0;
3237 static UINT WINAPI ime_ImeGetRegisterWordStyle( UINT item, STYLEBUFW *style )
3239 ime_trace( "item %u, style %p\n", item, style );
3241 ok_eq( default_hkl, GetKeyboardLayout( 0 ), HKL, "%p" );
3242 CHECK_EXPECT( ImeGetRegisterWordStyle );
3244 if (!style)
3245 ok_eq( 16, item, UINT, "%u" );
3246 else if (ime_info.fdwProperty & IME_PROP_UNICODE)
3248 STYLEBUFW *styleW = style;
3249 styleW->dwStyle = 0xdeadbeef;
3250 wcscpy( styleW->szDescription, L"StyleDescription" );
3252 else
3254 STYLEBUFA *styleA = (STYLEBUFA *)style;
3255 styleA->dwStyle = 0xdeadbeef;
3256 strcpy( styleA->szDescription, "StyleDescription" );
3259 return 0xdeadbeef;
3262 static BOOL WINAPI ime_ImeInquire( IMEINFO *info, WCHAR *ui_class, DWORD flags )
3264 ime_trace( "info %p, ui_class %p, flags %#lx\n", info, ui_class, flags );
3266 todo_wine_if( todo_ImeInquire )
3267 CHECK_EXPECT( ImeInquire );
3269 ok( !!info, "got info %p\n", info );
3270 ok( !!ui_class, "got ui_class %p\n", ui_class );
3271 ok( !flags, "got flags %#lx\n", flags );
3273 *info = ime_info;
3275 if (ime_info.fdwProperty & IME_PROP_UNICODE)
3276 wcscpy( ui_class, ime_ui_class.lpszClassName );
3277 else
3278 WideCharToMultiByte( CP_ACP, 0, ime_ui_class.lpszClassName, -1,
3279 (char *)ui_class, 17, NULL, NULL );
3281 return TRUE;
3284 static BOOL WINAPI ime_ImeProcessKey( HIMC himc, UINT vkey, LPARAM lparam, BYTE *state )
3286 struct ime_call call =
3288 .hkl = GetKeyboardLayout( 0 ), .himc = himc,
3289 .func = IME_PROCESS_KEY, .process_key = {.vkey = vkey, .lparam = lparam}
3291 ime_trace( "himc %p, vkey %u, lparam %#Ix, state %p\n",
3292 himc, vkey, lparam, state );
3293 ime_calls[ime_call_count++] = call;
3294 return LOWORD(lparam);
3297 static BOOL WINAPI ime_ImeRegisterWord( const WCHAR *reading, DWORD style, const WCHAR *string )
3299 ime_trace( "reading %s, style %lu, string %s\n", debugstr_w(reading), style, debugstr_w(string) );
3301 ok_eq( default_hkl, GetKeyboardLayout( 0 ), HKL, "%p" );
3302 CHECK_EXPECT( ImeRegisterWord );
3304 if (style) ok_eq( 0xdeadbeef, style, UINT, "%#x" );
3305 if (ime_info.fdwProperty & IME_PROP_UNICODE)
3307 if (reading) ok_wcs( L"Reading", reading );
3308 if (string) ok_wcs( L"String", string );
3310 else
3312 if (reading) ok_str( "Reading", (char *)reading );
3313 if (string) ok_str( "String", (char *)string );
3316 return FALSE;
3319 static BOOL WINAPI ime_ImeSelect( HIMC himc, BOOL select )
3321 struct ime_call call =
3323 .hkl = GetKeyboardLayout( 0 ), .himc = himc,
3324 .func = IME_SELECT, .select = select
3326 INPUTCONTEXT *ctx;
3328 ime_trace( "himc %p, select %d\n", himc, select );
3329 ime_calls[ime_call_count++] = call;
3331 if (ImeSelect_init_status && select)
3333 ctx = ImmLockIMC( himc );
3334 ok_ne( NULL, ctx, INPUTCONTEXT *, "%p" );
3335 ctx->fOpen = ~0;
3336 ctx->fdwConversion = ~0;
3337 ctx->fdwSentence = ~0;
3338 ImmUnlockIMC( himc );
3341 return TRUE;
3344 static BOOL WINAPI ime_ImeSetActiveContext( HIMC himc, BOOL flag )
3346 struct ime_call call =
3348 .hkl = GetKeyboardLayout( 0 ), .himc = himc,
3349 .func = IME_SET_ACTIVE_CONTEXT, .set_active_context = {.flag = flag}
3351 ime_trace( "himc %p, flag %#x\n", himc, flag );
3352 ime_calls[ime_call_count++] = call;
3353 return TRUE;
3356 static BOOL WINAPI ime_ImeSetCompositionString( HIMC himc, DWORD index, const void *comp, DWORD comp_len,
3357 const void *read, DWORD read_len )
3359 ime_trace( "himc %p, index %lu, comp %p, comp_len %lu, read %p, read_len %lu\n",
3360 himc, index, comp, comp_len, read, read_len );
3361 CHECK_EXPECT( ImeSetCompositionString );
3363 ok_eq( expect_ime, GetKeyboardLayout( 0 ), HKL, "%p" );
3364 ok_ne( default_himc, himc, HIMC, "%p" );
3366 if (ime_info.fdwProperty & IME_PROP_UNICODE)
3368 switch (index)
3370 case SCS_SETSTR:
3371 todo_wine_if( todo_ImeSetCompositionString )
3372 ok_eq( 22, comp_len, UINT, "%#x" );
3373 ok_wcs( L"CompString", comp );
3374 break;
3375 case SCS_CHANGECLAUSE:
3377 const UINT *clause = comp;
3378 ok_eq( 8, comp_len, UINT, "%#x" );
3379 ok_eq( 0, clause[0], UINT, "%#x" );
3380 todo_wine_if( todo_ImeSetCompositionString )
3381 ok_eq( 1, clause[1], UINT, "%#x");
3382 break;
3384 case SCS_CHANGEATTR:
3386 const BYTE *attr = comp;
3387 todo_wine_if( todo_ImeSetCompositionString && comp_len != 4 )
3388 ok_eq( 4, comp_len, UINT, "%#x" );
3389 todo_wine_if( todo_ImeSetCompositionString && attr[0] != 0xcd )
3390 ok_eq( 0xcd, attr[0], UINT, "%#x" );
3391 todo_wine_if( todo_ImeSetCompositionString )
3392 ok_eq( 0xcd, attr[1], UINT, "%#x" );
3393 break;
3395 default:
3396 ok( 0, "unexpected index %#lx\n", index );
3397 break;
3400 else
3402 switch (index)
3404 case SCS_SETSTR:
3405 todo_wine_if( todo_ImeSetCompositionString )
3406 ok_eq( 11, comp_len, UINT, "%#x" );
3407 ok_str( "CompString", comp );
3408 break;
3409 case SCS_CHANGECLAUSE:
3411 const UINT *clause = comp;
3412 ok_eq( 8, comp_len, UINT, "%#x" );
3413 todo_wine_if( todo_ImeSetCompositionString )
3414 ok_eq( 0, clause[0], UINT, "%#x" );
3415 todo_wine_if( todo_ImeSetCompositionString )
3416 ok_eq( 1, clause[1], UINT, "%#x");
3417 break;
3419 case SCS_CHANGEATTR:
3421 const BYTE *attr = comp;
3422 todo_wine_if( todo_ImeSetCompositionString && comp_len != 4 )
3423 ok_eq( 4, comp_len, UINT, "%#x" );
3424 todo_wine_if( todo_ImeSetCompositionString )
3425 ok_eq( 0xcd, attr[0], UINT, "%#x" );
3426 todo_wine_if( todo_ImeSetCompositionString )
3427 ok_eq( 0xcd, attr[1], UINT, "%#x" );
3428 break;
3430 default:
3431 ok( 0, "unexpected index %#lx\n", index );
3432 break;
3436 ok_eq( NULL, read, const void *, "%p" );
3437 ok_eq( 0, read_len, UINT, "%#x" );
3439 return TRUE;
3442 static UINT WINAPI ime_ImeToAsciiEx( UINT vkey, UINT vsc, BYTE *state, TRANSMSGLIST *msgs, UINT flags, HIMC himc )
3444 struct ime_call call =
3446 .hkl = GetKeyboardLayout( 0 ), .himc = himc,
3447 .func = IME_TO_ASCII_EX, .to_ascii_ex = {.vkey = vkey, .vsc = vsc, .flags = flags}
3449 INPUTCONTEXT *ctx;
3450 UINT count = 0;
3452 ime_trace( "vkey %#x, vsc %#x, state %p, msgs %p, flags %#x, himc %p\n",
3453 vkey, vsc, state, msgs, flags, himc );
3454 ime_calls[ime_call_count++] = call;
3456 ok_ne( NULL, msgs, TRANSMSGLIST *, "%p" );
3457 todo_wine ok_eq( 256, msgs->uMsgCount, UINT, "%u" );
3459 ctx = ImmLockIMC( himc );
3460 todo_wine ok_ret( VK_PROCESSKEY, ImmGetVirtualKey( ctx->hWnd ) );
3462 if (vsc & 0x200)
3464 msgs->TransMsg[0].message = WM_IME_STARTCOMPOSITION;
3465 msgs->TransMsg[0].wParam = 1;
3466 msgs->TransMsg[0].lParam = 0;
3467 count++;
3468 msgs->TransMsg[1].message = WM_IME_ENDCOMPOSITION;
3469 msgs->TransMsg[1].wParam = 1;
3470 msgs->TransMsg[1].lParam = 0;
3471 count++;
3474 if (vsc & 0x400)
3476 TRANSMSG *msgs;
3478 ctx = ImmLockIMC( himc );
3479 ok_ne( NULL, ctx, INPUTCONTEXT *, "%p" );
3481 ok_ne( NULL, ctx->hMsgBuf, HIMCC, "%p" );
3482 ok_eq( 0, ctx->dwNumMsgBuf, UINT, "%u" );
3484 ctx->hMsgBuf = ImmReSizeIMCC( ctx->hMsgBuf, 64 * sizeof(*msgs) );
3485 ok_ne( NULL, ctx->hMsgBuf, HIMCC, "%p" );
3487 msgs = ImmLockIMCC( ctx->hMsgBuf );
3488 ok_ne( NULL, msgs, TRANSMSG *, "%p" );
3490 msgs[ctx->dwNumMsgBuf].message = WM_IME_STARTCOMPOSITION;
3491 msgs[ctx->dwNumMsgBuf].wParam = 2;
3492 msgs[ctx->dwNumMsgBuf].lParam = 0;
3493 ctx->dwNumMsgBuf++;
3494 msgs[ctx->dwNumMsgBuf].message = WM_IME_ENDCOMPOSITION;
3495 msgs[ctx->dwNumMsgBuf].wParam = 2;
3496 msgs[ctx->dwNumMsgBuf].lParam = 0;
3497 ctx->dwNumMsgBuf++;
3499 ok_ret( 0, ImmUnlockIMCC( ctx->hMsgBuf ) );
3502 ok_ret( 1, ImmUnlockIMC( himc ) );
3504 if (vsc & 0x800) count = ~0;
3505 return count;
3508 static BOOL WINAPI ime_ImeUnregisterWord( const WCHAR *reading, DWORD style, const WCHAR *string )
3510 ime_trace( "reading %s, style %lu, string %s\n", debugstr_w(reading), style, debugstr_w(string) );
3512 ok_eq( default_hkl, GetKeyboardLayout( 0 ), HKL, "%p" );
3513 CHECK_EXPECT( ImeUnregisterWord );
3515 if (style) ok_eq( 0xdeadbeef, style, UINT, "%#x" );
3516 if (ime_info.fdwProperty & IME_PROP_UNICODE)
3518 if (reading) ok_wcs( L"Reading", reading );
3519 if (string) ok_wcs( L"String", string );
3521 else
3523 if (reading) ok_str( "Reading", (char *)reading );
3524 if (string) ok_str( "String", (char *)string );
3527 return FALSE;
3530 static BOOL WINAPI ime_NotifyIME( HIMC himc, DWORD action, DWORD index, DWORD value )
3532 struct ime_call call =
3534 .hkl = GetKeyboardLayout( 0 ), .himc = himc,
3535 .func = IME_NOTIFY, .notify = {.action = action, .index = index, .value = value}
3537 ime_trace( "himc %p, action %#lx, index %lu, value %lu\n", himc, action, index, value );
3538 ime_calls[ime_call_count++] = call;
3539 return FALSE;
3542 static BOOL WINAPI ime_DllMain( HINSTANCE instance, DWORD reason, LPVOID reserved )
3544 ime_trace( "instance %p, reason %lu, reserved %p.\n", instance, reason, reserved );
3546 switch (reason)
3548 case DLL_PROCESS_ATTACH:
3549 DisableThreadLibraryCalls( instance );
3550 ime_ui_class.hInstance = instance;
3551 RegisterClassExW( &ime_ui_class );
3552 todo_wine_if(todo_IME_DLL_PROCESS_ATTACH)
3553 CHECK_EXPECT( IME_DLL_PROCESS_ATTACH );
3554 break;
3556 case DLL_PROCESS_DETACH:
3557 UnregisterClassW( ime_ui_class.lpszClassName, instance );
3558 todo_wine_if(todo_IME_DLL_PROCESS_DETACH)
3559 CHECK_EXPECT( IME_DLL_PROCESS_DETACH );
3560 break;
3563 return TRUE;
3566 static struct ime_functions ime_functions =
3568 ime_ImeConfigure,
3569 ime_ImeConversionList,
3570 ime_ImeDestroy,
3571 ime_ImeEnumRegisterWord,
3572 ime_ImeEscape,
3573 ime_ImeGetImeMenuItems,
3574 ime_ImeGetRegisterWordStyle,
3575 ime_ImeInquire,
3576 ime_ImeProcessKey,
3577 ime_ImeRegisterWord,
3578 ime_ImeSelect,
3579 ime_ImeSetActiveContext,
3580 ime_ImeSetCompositionString,
3581 ime_ImeToAsciiEx,
3582 ime_ImeUnregisterWord,
3583 ime_NotifyIME,
3584 ime_DllMain,
3587 static HKL ime_install(void)
3589 WCHAR buffer[MAX_PATH];
3590 HMODULE module;
3591 DWORD len, ret;
3592 HKEY hkey;
3593 HKL hkl;
3595 /* IME module gets cached and won't reload from disk as soon as a window has
3596 * loaded it. To workaround the issue we load the module first as a DLL,
3597 * set its function pointers from the test, and later when the cached IME
3598 * gets loaded, read the function pointers from the separately loaded DLL.
3601 load_resource( L"ime_wrapper.dll", buffer );
3603 SetLastError( 0xdeadbeef );
3604 ret = MoveFileW( buffer, L"c:\\windows\\system32\\winetest_ime.dll" );
3605 if (!ret)
3607 ok( GetLastError() == ERROR_ACCESS_DENIED, "got error %lu\n", GetLastError() );
3608 win_skip( "Failed to copy DLL to system directory\n" );
3609 return 0;
3612 module = LoadLibraryW( L"c:\\windows\\system32\\winetest_ime.dll" );
3613 ok( !!module, "LoadLibraryW failed, error %lu\n", GetLastError() );
3614 *(struct ime_functions *)GetProcAddress( module, "ime_functions" ) = ime_functions;
3616 /* install the actual IME module, it will lookup the functions from the DLL */
3617 load_resource( L"ime_wrapper.dll", buffer );
3619 SetLastError( 0xdeadbeef );
3620 swprintf( ime_path, ARRAY_SIZE(ime_path), L"c:\\windows\\system32\\wine%04x.ime", ime_count++ );
3621 ret = MoveFileW( buffer, ime_path );
3622 todo_wine_if( GetLastError() == ERROR_ALREADY_EXISTS )
3623 ok( ret || broken( !ret ) /* sometimes still in use */,
3624 "MoveFileW failed, error %lu\n", GetLastError() );
3626 hkl = ImmInstallIMEW( ime_path, L"WineTest IME" );
3627 ok( hkl == expect_ime, "ImmInstallIMEW returned %p, error %lu\n", hkl, GetLastError() );
3629 swprintf( buffer, ARRAY_SIZE(buffer), L"System\\CurrentControlSet\\Control\\Keyboard Layouts\\%08x", hkl );
3630 ret = RegOpenKeyW( HKEY_LOCAL_MACHINE, buffer, &hkey );
3631 ok( !ret, "RegOpenKeyW returned %#lx, error %lu\n", ret, GetLastError() );
3633 len = sizeof(buffer);
3634 memset( buffer, 0xcd, sizeof(buffer) );
3635 ret = RegQueryValueExW( hkey, L"Ime File", NULL, NULL, (BYTE *)buffer, &len );
3636 ok( !ret, "RegQueryValueExW returned %#lx, error %lu\n", ret, GetLastError() );
3637 ok( !wcsicmp( buffer, wcsrchr( ime_path, '\\' ) + 1 ), "got Ime File %s\n", debugstr_w(buffer) );
3639 len = sizeof(buffer);
3640 memset( buffer, 0xcd, sizeof(buffer) );
3641 ret = RegQueryValueExW( hkey, L"Layout Text", NULL, NULL, (BYTE *)buffer, &len );
3642 ok( !ret, "RegQueryValueExW returned %#lx, error %lu\n", ret, GetLastError() );
3643 ok( !wcscmp( buffer, L"WineTest IME" ), "got Layout Text %s\n", debugstr_w(buffer) );
3645 len = sizeof(buffer);
3646 memset( buffer, 0, sizeof(buffer) );
3647 ret = RegQueryValueExW( hkey, L"Layout File", NULL, NULL, (BYTE *)buffer, &len );
3648 todo_wine
3649 ok( !ret, "RegQueryValueExW returned %#lx, error %lu\n", ret, GetLastError() );
3650 todo_wine
3651 ok( !wcscmp( buffer, L"kbdus.dll" ), "got Layout File %s\n", debugstr_w(buffer) );
3653 ret = RegCloseKey( hkey );
3654 ok( !ret, "RegCloseKey returned %#lx, error %lu\n", ret, GetLastError() );
3656 return hkl;
3659 static void ime_cleanup( HKL hkl, BOOL free )
3661 HMODULE module = GetModuleHandleW( L"winetest_ime.dll" );
3662 WCHAR buffer[MAX_PATH], value[MAX_PATH];
3663 DWORD i, buffer_len, value_len, ret;
3664 HKEY hkey;
3666 ret = UnloadKeyboardLayout( hkl );
3667 todo_wine
3668 ok( ret, "UnloadKeyboardLayout failed, error %lu\n", GetLastError() );
3670 if (free) ok_ret( 1, ImmFreeLayout( hkl ) );
3672 swprintf( buffer, ARRAY_SIZE(buffer), L"System\\CurrentControlSet\\Control\\Keyboard Layouts\\%08x", hkl );
3673 ret = RegDeleteKeyW( HKEY_LOCAL_MACHINE, buffer );
3674 ok( !ret, "RegDeleteKeyW returned %#lx, error %lu\n", ret, GetLastError() );
3676 ret = RegOpenKeyW( HKEY_CURRENT_USER, L"Keyboard Layout\\Preload", &hkey );
3677 ok( !ret, "RegOpenKeyW returned %#lx, error %lu\n", ret, GetLastError() );
3679 value_len = ARRAY_SIZE(value);
3680 buffer_len = sizeof(buffer);
3681 for (i = 0; !RegEnumValueW( hkey, i, value, &value_len, NULL, NULL, (void *)buffer, &buffer_len ); i++)
3683 value_len = ARRAY_SIZE(value);
3684 buffer_len = sizeof(buffer);
3685 if (hkl != UlongToHandle( wcstoul( buffer, NULL, 16 ) )) continue;
3686 ret = RegDeleteValueW( hkey, value );
3687 ok( !ret, "RegDeleteValueW returned %#lx, error %lu\n", ret, GetLastError() );
3690 ret = RegCloseKey( hkey );
3691 ok( !ret, "RegCloseKey returned %#lx, error %lu\n", ret, GetLastError() );
3693 ret = DeleteFileW( ime_path );
3694 todo_wine_if( GetLastError() == ERROR_ACCESS_DENIED )
3695 ok( ret || broken( !ret ) /* sometimes still in use */,
3696 "DeleteFileW failed, error %lu\n", GetLastError() );
3698 ret = FreeLibrary( module );
3699 ok( ret, "FreeLibrary failed, error %lu\n", GetLastError() );
3701 ret = DeleteFileW( L"c:\\windows\\system32\\winetest_ime.dll" );
3702 ok( ret, "DeleteFileW failed, error %lu\n", GetLastError() );
3705 static BOOL CALLBACK enum_get_context( HIMC himc, LPARAM lparam )
3707 ime_trace( "himc %p\n", himc );
3708 *(HIMC *)lparam = himc;
3709 return TRUE;
3712 static BOOL CALLBACK enum_find_context( HIMC himc, LPARAM lparam )
3714 ime_trace( "himc %p\n", himc );
3715 if (lparam && lparam == (LPARAM)himc) return FALSE;
3716 return TRUE;
3719 static void test_ImmEnumInputContext(void)
3721 HIMC himc;
3723 ok_ret( 1, ImmEnumInputContext( 0, enum_get_context, (LPARAM)&default_himc ) );
3724 ok_ret( 1, ImmEnumInputContext( -1, enum_find_context, 0 ) );
3725 ok_ret( 1, ImmEnumInputContext( GetCurrentThreadId(), enum_find_context, 0 ) );
3727 todo_wine
3728 ok_ret( 0, ImmEnumInputContext( 1, enum_find_context, 0 ) );
3729 todo_wine
3730 ok_ret( 0, ImmEnumInputContext( GetCurrentProcessId(), enum_find_context, 0 ) );
3732 himc = ImmCreateContext();
3733 ok_ne( NULL, himc, HIMC, "%p" );
3734 ok_ret( 0, ImmEnumInputContext( GetCurrentThreadId(), enum_find_context, (LPARAM)himc ) );
3735 ok_ret( 1, ImmDestroyContext( himc ) );
3736 ok_ret( 1, ImmEnumInputContext( GetCurrentThreadId(), enum_find_context, (LPARAM)himc ) );
3739 static void test_ImmInstallIME(void)
3741 UINT ret;
3742 HKL hkl;
3744 SET_ENABLE( IME_DLL_PROCESS_ATTACH, TRUE );
3745 SET_ENABLE( ImeInquire, TRUE );
3746 SET_ENABLE( ImeDestroy, TRUE );
3747 SET_ENABLE( IME_DLL_PROCESS_DETACH, TRUE );
3749 /* IME_PROP_END_UNLOAD for the IME to unload / reload. */
3750 ime_info.fdwProperty = IME_PROP_END_UNLOAD;
3752 if (!(hkl = ime_install())) goto cleanup;
3754 SET_EXPECT( IME_DLL_PROCESS_ATTACH );
3755 SET_EXPECT( ImeInquire );
3756 ret = ImmLoadIME( hkl );
3757 ok( ret, "ImmLoadIME returned %#x\n", ret );
3758 CHECK_CALLED( IME_DLL_PROCESS_ATTACH );
3759 CHECK_CALLED( ImeInquire );
3761 ret = ImmLoadIME( hkl );
3762 ok( ret, "ImmLoadIME returned %#x\n", ret );
3764 SET_EXPECT( ImeDestroy );
3765 SET_EXPECT( IME_DLL_PROCESS_DETACH );
3766 ret = ImmFreeLayout( hkl );
3767 ok( ret, "ImmFreeLayout returned %#x\n", ret );
3768 CHECK_CALLED( ImeDestroy );
3769 CHECK_CALLED( IME_DLL_PROCESS_DETACH );
3771 ret = ImmFreeLayout( hkl );
3772 ok( ret, "ImmFreeLayout returned %#x\n", ret );
3774 ime_info.fdwProperty = 0;
3776 SET_EXPECT( IME_DLL_PROCESS_ATTACH );
3777 SET_EXPECT( ImeInquire );
3778 ret = ImmLoadIME( hkl );
3779 ok( ret, "ImmLoadIME returned %#x\n", ret );
3780 CHECK_CALLED( IME_DLL_PROCESS_ATTACH );
3781 CHECK_CALLED( ImeInquire );
3783 ret = ImmLoadIME( hkl );
3784 ok( ret, "ImmLoadIME returned %#x\n", ret );
3786 SET_EXPECT( ImeDestroy );
3787 SET_EXPECT( IME_DLL_PROCESS_DETACH );
3788 ret = ImmFreeLayout( hkl );
3789 ok( ret, "ImmFreeLayout returned %#x\n", ret );
3790 CHECK_CALLED( ImeDestroy );
3791 CHECK_CALLED( IME_DLL_PROCESS_DETACH );
3793 ret = ImmFreeLayout( hkl );
3794 ok( ret, "ImmFreeLayout returned %#x\n", ret );
3796 ime_cleanup( hkl, FALSE );
3798 cleanup:
3799 SET_ENABLE( IME_DLL_PROCESS_ATTACH, FALSE );
3800 SET_ENABLE( ImeInquire, FALSE );
3801 SET_ENABLE( ImeDestroy, FALSE );
3802 SET_ENABLE( IME_DLL_PROCESS_DETACH, FALSE );
3805 static void test_ImmIsIME(void)
3807 HKL hkl = GetKeyboardLayout( 0 );
3809 SET_ENABLE( IME_DLL_PROCESS_ATTACH, TRUE );
3810 SET_ENABLE( ImeInquire, TRUE );
3811 SET_ENABLE( ImeDestroy, TRUE );
3812 SET_ENABLE( IME_DLL_PROCESS_DETACH, TRUE );
3814 SetLastError( 0xdeadbeef );
3815 ok_ret( 0, ImmIsIME( 0 ) );
3816 ok_ret( 0xdeadbeef, GetLastError() );
3817 ok_ret( 1, ImmIsIME( hkl ) );
3819 /* IME_PROP_END_UNLOAD for the IME to unload / reload. */
3820 ime_info.fdwProperty = IME_PROP_END_UNLOAD;
3822 if (!(hkl = wineime_hkl)) goto cleanup;
3824 todo_ImeInquire = TRUE;
3825 todo_ImeDestroy = TRUE;
3826 todo_IME_DLL_PROCESS_ATTACH = TRUE;
3827 todo_IME_DLL_PROCESS_DETACH = TRUE;
3828 ok_ret( 1, ImmIsIME( hkl ) );
3829 todo_IME_DLL_PROCESS_ATTACH = FALSE;
3830 todo_IME_DLL_PROCESS_DETACH = FALSE;
3831 todo_ImeInquire = FALSE;
3832 todo_ImeDestroy = FALSE;
3834 cleanup:
3835 SET_ENABLE( IME_DLL_PROCESS_ATTACH, FALSE );
3836 SET_ENABLE( ImeInquire, FALSE );
3837 SET_ENABLE( ImeDestroy, FALSE );
3838 SET_ENABLE( IME_DLL_PROCESS_DETACH, FALSE );
3841 static void test_ImmGetProperty(void)
3843 static const IMEINFO expect_ime_info =
3845 .fdwProperty = IME_PROP_UNICODE | IME_PROP_AT_CARET,
3847 static const IMEINFO expect_ime_info_0411 = /* MS Japanese IME */
3849 .fdwProperty = IME_PROP_CANDLIST_START_FROM_1 | IME_PROP_UNICODE | IME_PROP_AT_CARET | 0xa,
3850 .fdwConversionCaps = IME_CMODE_NATIVE | IME_CMODE_FULLSHAPE | IME_CMODE_KATAKANA,
3851 .fdwSentenceCaps = IME_SMODE_PLAURALCLAUSE | IME_SMODE_CONVERSATION,
3852 .fdwSCSCaps = SCS_CAP_COMPSTR | SCS_CAP_SETRECONVERTSTRING | SCS_CAP_MAKEREAD,
3853 .fdwSelectCaps = SELECT_CAP_CONVERSION | SELECT_CAP_SENTENCE,
3854 .fdwUICaps = UI_CAP_ROT90,
3856 static const IMEINFO expect_ime_info_0412 = /* MS Korean IME */
3858 .fdwProperty = IME_PROP_CANDLIST_START_FROM_1 | IME_PROP_UNICODE | IME_PROP_AT_CARET | 0xa,
3859 .fdwConversionCaps = IME_CMODE_NATIVE | IME_CMODE_FULLSHAPE,
3860 .fdwSentenceCaps = IME_SMODE_NONE,
3861 .fdwSCSCaps = SCS_CAP_COMPSTR | SCS_CAP_SETRECONVERTSTRING,
3862 .fdwSelectCaps = SELECT_CAP_CONVERSION,
3863 .fdwUICaps = UI_CAP_ROT90,
3865 static const IMEINFO expect_ime_info_0804 = /* MS Chinese IME */
3867 .fdwProperty = IME_PROP_CANDLIST_START_FROM_1 | IME_PROP_UNICODE | IME_PROP_AT_CARET | 0xa,
3868 .fdwConversionCaps = IME_CMODE_NATIVE | IME_CMODE_FULLSHAPE,
3869 .fdwSentenceCaps = IME_SMODE_PLAURALCLAUSE,
3870 .fdwSCSCaps = SCS_CAP_COMPSTR | SCS_CAP_SETRECONVERTSTRING | SCS_CAP_MAKEREAD,
3871 .fdwUICaps = UI_CAP_ROT90,
3873 HKL hkl = GetKeyboardLayout( 0 );
3874 const IMEINFO *expect;
3876 SET_ENABLE( ImeInquire, TRUE );
3877 SET_ENABLE( ImeDestroy, TRUE );
3879 SetLastError( 0xdeadbeef );
3880 ok_ret( 0, ImmGetProperty( 0, 0 ) );
3881 ok_ret( 0, ImmGetProperty( hkl, 0 ) );
3883 if (hkl == (HKL)0x04110411) expect = &expect_ime_info_0411;
3884 else if (hkl == (HKL)0x04120412) expect = &expect_ime_info_0412;
3885 else if (hkl == (HKL)0x08040804) expect = &expect_ime_info_0804;
3886 else expect = &expect_ime_info;
3888 /* IME_PROP_COMPLETE_ON_UNSELECT seems to be somtimes set on CJK locales IMEs, sometimes not */
3889 ok_ret( expect->fdwProperty, ImmGetProperty( hkl, IGP_PROPERTY ) & ~IME_PROP_COMPLETE_ON_UNSELECT );
3890 todo_wine
3891 ok_ret( expect->fdwConversionCaps, ImmGetProperty( hkl, IGP_CONVERSION ) );
3892 todo_wine
3893 ok_ret( expect->fdwSentenceCaps, ImmGetProperty( hkl, IGP_SENTENCE ) );
3894 ok_ret( expect->fdwSCSCaps, ImmGetProperty( hkl, IGP_SETCOMPSTR ) );
3895 todo_wine
3896 ok_ret( expect->fdwSelectCaps, ImmGetProperty( hkl, IGP_SELECT ) );
3897 ok_ret( IMEVER_0400, ImmGetProperty( hkl, IGP_GETIMEVERSION ) );
3898 ok_ret( expect->fdwUICaps, ImmGetProperty( hkl, IGP_UI ) );
3899 todo_wine
3900 ok_ret( 0xdeadbeef, GetLastError() );
3902 /* IME_PROP_END_UNLOAD for the IME to unload / reload. */
3903 ime_info.fdwProperty = IME_PROP_END_UNLOAD;
3905 if (!(hkl = wineime_hkl)) goto cleanup;
3907 SET_EXPECT( ImeInquire );
3908 SET_EXPECT( ImeDestroy );
3909 ok_ret( 0, ImmGetProperty( hkl, 0 ) );
3910 CHECK_CALLED( ImeInquire );
3911 CHECK_CALLED( ImeDestroy );
3913 expect = &ime_info;
3914 todo_ImeInquire = TRUE;
3915 todo_ImeDestroy = TRUE;
3916 ok_ret( expect->fdwProperty, ImmGetProperty( hkl, IGP_PROPERTY ) );
3917 ok_ret( expect->fdwConversionCaps, ImmGetProperty( hkl, IGP_CONVERSION ) );
3918 ok_ret( expect->fdwSentenceCaps, ImmGetProperty( hkl, IGP_SENTENCE ) );
3919 ok_ret( expect->fdwSCSCaps, ImmGetProperty( hkl, IGP_SETCOMPSTR ) );
3920 ok_ret( expect->fdwSelectCaps, ImmGetProperty( hkl, IGP_SELECT ) );
3921 ok_ret( IMEVER_0400, ImmGetProperty( hkl, IGP_GETIMEVERSION ) );
3922 ok_ret( expect->fdwUICaps, ImmGetProperty( hkl, IGP_UI ) );
3923 todo_ImeInquire = FALSE;
3924 called_ImeInquire = FALSE;
3925 todo_ImeDestroy = FALSE;
3926 called_ImeDestroy = FALSE;
3928 cleanup:
3929 SET_ENABLE( ImeInquire, FALSE );
3930 SET_ENABLE( ImeDestroy, FALSE );
3933 static void test_ImmGetDescription(void)
3935 HKL hkl = GetKeyboardLayout( 0 );
3936 WCHAR bufferW[MAX_PATH];
3937 char bufferA[MAX_PATH];
3938 DWORD ret;
3940 SET_ENABLE( IME_DLL_PROCESS_ATTACH, TRUE );
3941 SET_ENABLE( ImeInquire, TRUE );
3942 SET_ENABLE( ImeDestroy, TRUE );
3943 SET_ENABLE( IME_DLL_PROCESS_DETACH, TRUE );
3945 SetLastError( 0xdeadbeef );
3946 ret = ImmGetDescriptionW( NULL, NULL, 0 );
3947 ok( !ret, "ImmGetDescriptionW returned %lu\n", ret );
3948 ret = ImmGetDescriptionA( NULL, NULL, 0 );
3949 ok( !ret, "ImmGetDescriptionA returned %lu\n", ret );
3950 ret = ImmGetDescriptionW( NULL, NULL, 100 );
3951 ok( !ret, "ImmGetDescriptionW returned %lu\n", ret );
3952 ret = ImmGetDescriptionA( NULL, NULL, 100 );
3953 ok( !ret, "ImmGetDescriptionA returned %lu\n", ret );
3954 ret = ImmGetDescriptionW( hkl, bufferW, 100 );
3955 ok( !ret, "ImmGetDescriptionW returned %lu\n", ret );
3956 ret = ImmGetDescriptionA( hkl, bufferA, 100 );
3957 ok( !ret, "ImmGetDescriptionA returned %lu\n", ret );
3958 ret = GetLastError();
3959 ok( ret == 0xdeadbeef, "got error %lu\n", ret );
3961 if (!(hkl = wineime_hkl)) goto cleanup;
3963 memset( bufferW, 0xcd, sizeof(bufferW) );
3964 ret = ImmGetDescriptionW( hkl, bufferW, 2 );
3965 ok( ret == 1, "ImmGetDescriptionW returned %lu\n", ret );
3966 ok( !wcscmp( bufferW, L"W" ), "got bufferW %s\n", debugstr_w(bufferW) );
3967 memset( bufferA, 0xcd, sizeof(bufferA) );
3968 ret = ImmGetDescriptionA( hkl, bufferA, 2 );
3969 ok( ret == 0, "ImmGetDescriptionA returned %lu\n", ret );
3970 ok( !strcmp( bufferA, "" ), "got bufferA %s\n", debugstr_a(bufferA) );
3972 memset( bufferW, 0xcd, sizeof(bufferW) );
3973 ret = ImmGetDescriptionW( hkl, bufferW, 11 );
3974 ok( ret == 10, "ImmGetDescriptionW returned %lu\n", ret );
3975 ok( !wcscmp( bufferW, L"WineTest I" ), "got bufferW %s\n", debugstr_w(bufferW) );
3976 memset( bufferA, 0xcd, sizeof(bufferA) );
3977 ret = ImmGetDescriptionA( hkl, bufferA, 11 );
3978 ok( ret == 0, "ImmGetDescriptionA returned %lu\n", ret );
3979 ok( !strcmp( bufferA, "" ), "got bufferA %s\n", debugstr_a(bufferA) );
3981 memset( bufferW, 0xcd, sizeof(bufferW) );
3982 ret = ImmGetDescriptionW( hkl, bufferW, 12 );
3983 ok( ret == 11, "ImmGetDescriptionW returned %lu\n", ret );
3984 ok( !wcscmp( bufferW, L"WineTest IM" ), "got bufferW %s\n", debugstr_w(bufferW) );
3985 memset( bufferA, 0xcd, sizeof(bufferA) );
3986 ret = ImmGetDescriptionA( hkl, bufferA, 12 );
3987 ok( ret == 12, "ImmGetDescriptionA returned %lu\n", ret );
3988 ok( !strcmp( bufferA, "WineTest IME" ), "got bufferA %s\n", debugstr_a(bufferA) );
3990 memset( bufferW, 0xcd, sizeof(bufferW) );
3991 ret = ImmGetDescriptionW( hkl, bufferW, 13 );
3992 ok( ret == 12, "ImmGetDescriptionW returned %lu\n", ret );
3993 ok( !wcscmp( bufferW, L"WineTest IME" ), "got bufferW %s\n", debugstr_w(bufferW) );
3994 memset( bufferA, 0xcd, sizeof(bufferA) );
3995 ret = ImmGetDescriptionA( hkl, bufferA, 13 );
3996 ok( ret == 12, "ImmGetDescriptionA returned %lu\n", ret );
3997 ok( !strcmp( bufferA, "WineTest IME" ), "got bufferA %s\n", debugstr_a(bufferA) );
3999 cleanup:
4000 SET_ENABLE( IME_DLL_PROCESS_ATTACH, FALSE );
4001 SET_ENABLE( ImeInquire, FALSE );
4002 SET_ENABLE( ImeDestroy, FALSE );
4003 SET_ENABLE( IME_DLL_PROCESS_DETACH, FALSE );
4006 static void test_ImmGetIMEFileName(void)
4008 HKL hkl = GetKeyboardLayout( 0 );
4009 WCHAR bufferW[MAX_PATH], expectW[16];
4010 char bufferA[MAX_PATH], expectA[16];
4011 DWORD ret;
4013 SET_ENABLE( IME_DLL_PROCESS_ATTACH, TRUE );
4014 SET_ENABLE( ImeInquire, TRUE );
4015 SET_ENABLE( ImeDestroy, TRUE );
4016 SET_ENABLE( IME_DLL_PROCESS_DETACH, TRUE );
4018 SetLastError( 0xdeadbeef );
4019 ret = ImmGetIMEFileNameW( NULL, NULL, 0 );
4020 ok( !ret, "ImmGetIMEFileNameW returned %lu\n", ret );
4021 ret = ImmGetIMEFileNameA( NULL, NULL, 0 );
4022 ok( !ret, "ImmGetIMEFileNameA returned %lu\n", ret );
4023 ret = ImmGetIMEFileNameW( NULL, NULL, 100 );
4024 ok( !ret, "ImmGetIMEFileNameW returned %lu\n", ret );
4025 ret = ImmGetIMEFileNameA( NULL, NULL, 100 );
4026 ok( !ret, "ImmGetIMEFileNameA returned %lu\n", ret );
4027 ret = ImmGetIMEFileNameW( hkl, bufferW, 100 );
4028 ok( !ret, "ImmGetIMEFileNameW returned %lu\n", ret );
4029 ret = ImmGetIMEFileNameA( hkl, bufferA, 100 );
4030 ok( !ret, "ImmGetIMEFileNameA returned %lu\n", ret );
4031 ret = GetLastError();
4032 ok( ret == 0xdeadbeef, "got error %lu\n", ret );
4034 if (!(hkl = wineime_hkl)) goto cleanup;
4036 memset( bufferW, 0xcd, sizeof(bufferW) );
4037 ret = ImmGetIMEFileNameW( hkl, bufferW, 2 );
4038 ok( ret == 1, "ImmGetIMEFileNameW returned %lu\n", ret );
4039 ok( !wcscmp( bufferW, L"W" ), "got bufferW %s\n", debugstr_w(bufferW) );
4040 memset( bufferA, 0xcd, sizeof(bufferA) );
4041 ret = ImmGetIMEFileNameA( hkl, bufferA, 2 );
4042 ok( ret == 0, "ImmGetIMEFileNameA returned %lu\n", ret );
4043 ok( !strcmp( bufferA, "" ), "got bufferA %s\n", debugstr_a(bufferA) );
4045 swprintf( expectW, ARRAY_SIZE(expectW), L"WINE%04X.I", ime_count - 1 );
4046 memset( bufferW, 0xcd, sizeof(bufferW) );
4047 ret = ImmGetIMEFileNameW( hkl, bufferW, 11 );
4048 ok( ret == 10, "ImmGetIMEFileNameW returned %lu\n", ret );
4049 ok( !wcscmp( bufferW, expectW ), "got bufferW %s\n", debugstr_w(bufferW) );
4050 memset( bufferA, 0xcd, sizeof(bufferA) );
4051 ret = ImmGetIMEFileNameA( hkl, bufferA, 11 );
4052 ok( ret == 0, "ImmGetIMEFileNameA returned %lu\n", ret );
4053 ok( !strcmp( bufferA, "" ), "got bufferA %s\n", debugstr_a(bufferA) );
4055 swprintf( expectW, ARRAY_SIZE(expectW), L"WINE%04X.IM", ime_count - 1 );
4056 memset( bufferW, 0xcd, sizeof(bufferW) );
4057 ret = ImmGetIMEFileNameW( hkl, bufferW, 12 );
4058 ok( ret == 11, "ImmGetIMEFileNameW returned %lu\n", ret );
4059 ok( !wcscmp( bufferW, expectW ), "got bufferW %s\n", debugstr_w(bufferW) );
4060 snprintf( expectA, ARRAY_SIZE(expectA), "WINE%04X.IME", ime_count - 1 );
4061 memset( bufferA, 0xcd, sizeof(bufferA) );
4062 ret = ImmGetIMEFileNameA( hkl, bufferA, 12 );
4063 ok( ret == 12, "ImmGetIMEFileNameA returned %lu\n", ret );
4064 ok( !strcmp( bufferA, expectA ), "got bufferA %s\n", debugstr_a(bufferA) );
4066 swprintf( expectW, ARRAY_SIZE(expectW), L"WINE%04X.IME", ime_count - 1 );
4067 memset( bufferW, 0xcd, sizeof(bufferW) );
4068 ret = ImmGetIMEFileNameW( hkl, bufferW, 13 );
4069 ok( ret == 12, "ImmGetIMEFileNameW returned %lu\n", ret );
4070 ok( !wcscmp( bufferW, expectW ), "got bufferW %s\n", debugstr_w(bufferW) );
4071 memset( bufferA, 0xcd, sizeof(bufferA) );
4072 ret = ImmGetIMEFileNameA( hkl, bufferA, 13 );
4073 ok( ret == 12, "ImmGetIMEFileNameA returned %lu\n", ret );
4074 ok( !strcmp( bufferA, expectA ), "got bufferA %s\n", debugstr_a(bufferA) );
4076 cleanup:
4077 SET_ENABLE( IME_DLL_PROCESS_ATTACH, FALSE );
4078 SET_ENABLE( ImeInquire, FALSE );
4079 SET_ENABLE( ImeDestroy, FALSE );
4080 SET_ENABLE( IME_DLL_PROCESS_DETACH, FALSE );
4083 static void test_ImmEscape( BOOL unicode )
4085 HKL hkl = GetKeyboardLayout( 0 );
4086 DWORD i, codes[] =
4088 IME_ESC_QUERY_SUPPORT,
4089 IME_ESC_SEQUENCE_TO_INTERNAL,
4090 IME_ESC_GET_EUDC_DICTIONARY,
4091 IME_ESC_SET_EUDC_DICTIONARY,
4092 IME_ESC_MAX_KEY,
4093 IME_ESC_IME_NAME,
4094 IME_ESC_HANJA_MODE,
4095 IME_ESC_GETHELPFILENAME,
4097 WCHAR bufferW[512];
4098 char bufferA[512];
4100 SET_ENABLE( ImeEscape, TRUE );
4102 winetest_push_context( unicode ? "unicode" : "ansi" );
4104 ok_ret( 0, ImmEscapeW( hkl, 0, 0, NULL ) );
4105 ok_ret( 0, ImmEscapeA( hkl, 0, 0, NULL ) );
4107 /* IME_PROP_END_UNLOAD for the IME to unload / reload. */
4108 ime_info.fdwProperty = IME_PROP_END_UNLOAD;
4109 if (unicode) ime_info.fdwProperty |= IME_PROP_UNICODE;
4111 if (!(hkl = wineime_hkl)) goto cleanup;
4113 for (i = 0; i < ARRAY_SIZE(codes); ++i)
4115 winetest_push_context( "esc %#lx", codes[i] );
4117 SET_EXPECT( ImeEscape );
4118 ok_ret( 4, ImmEscapeW( hkl, 0, codes[i], NULL ) );
4119 CHECK_CALLED( ImeEscape );
4121 SET_EXPECT( ImeEscape );
4122 memset( bufferW, 0xcd, sizeof(bufferW) );
4123 if (codes[i] == IME_ESC_SET_EUDC_DICTIONARY) wcscpy( bufferW, L"EscapeIme" );
4124 ok_ret( 4, ImmEscapeW( hkl, 0, codes[i], bufferW ) );
4125 if (unicode || codes[i] == IME_ESC_GET_EUDC_DICTIONARY || codes[i] == IME_ESC_IME_NAME ||
4126 codes[i] == IME_ESC_GETHELPFILENAME)
4128 ok_wcs( L"ImeEscape", bufferW );
4129 ok_eq( 0xcdcd, bufferW[10], WORD, "%#x" );
4131 else if (codes[i] == IME_ESC_SET_EUDC_DICTIONARY)
4133 ok_wcs( L"EscapeIme", bufferW );
4134 ok_eq( 0xcdcd, bufferW[10], WORD, "%#x" );
4136 else if (codes[i] == IME_ESC_HANJA_MODE)
4138 todo_wine
4139 ok_eq( 0xcdcd, bufferW[0], WORD, "%#x" );
4141 else
4143 ok( !memcmp( bufferW, "ImeEscape", 10 ), "got bufferW %s\n", debugstr_w(bufferW) );
4144 ok_eq( 0xcdcd, bufferW[5], WORD, "%#x" );
4146 CHECK_CALLED( ImeEscape );
4148 SET_EXPECT( ImeEscape );
4149 ok_ret( 4, ImmEscapeA( hkl, 0, codes[i], NULL ) );
4150 CHECK_CALLED( ImeEscape );
4152 SET_EXPECT( ImeEscape );
4153 memset( bufferA, 0xcd, sizeof(bufferA) );
4154 if (codes[i] == IME_ESC_SET_EUDC_DICTIONARY) strcpy( bufferA, "EscapeIme" );
4155 ok_ret( 4, ImmEscapeA( hkl, 0, codes[i], bufferA ) );
4156 if (!unicode || codes[i] == IME_ESC_GET_EUDC_DICTIONARY || codes[i] == IME_ESC_IME_NAME ||
4157 codes[i] == IME_ESC_GETHELPFILENAME)
4159 ok_str( "ImeEscape", bufferA );
4160 ok_eq( 0xcd, bufferA[10], BYTE, "%#x" );
4162 else if (codes[i] == IME_ESC_SET_EUDC_DICTIONARY)
4164 ok_str( "EscapeIme", bufferA );
4165 ok_eq( 0xcd, bufferA[10], BYTE, "%#x" );
4167 else if (codes[i] == IME_ESC_HANJA_MODE)
4169 todo_wine
4170 ok_eq( 0xcd, bufferA[0], BYTE, "%#x" );
4172 else
4174 ok( !memcmp( bufferA, L"ImeEscape", 10 * sizeof(WCHAR) ), "got bufferA %s\n", debugstr_a(bufferA) );
4175 ok_eq( 0xcd, bufferA[20], BYTE, "%#x" );
4177 CHECK_CALLED( ImeEscape );
4179 winetest_pop_context();
4182 cleanup:
4183 SET_ENABLE( ImeEscape, FALSE );
4185 winetest_pop_context();
4188 static int CALLBACK enum_register_wordA( const char *reading, DWORD style, const char *string, void *user )
4190 ime_trace( "reading %s, style %#lx, string %s, user %p\n", debugstr_a(reading), style, debugstr_a(string), user );
4192 ok_eq( 0xdeadbeef, style, UINT, "%#x" );
4193 ok_str( "Reading", reading );
4194 ok_str( "String", string );
4196 return 0xdeadbeef;
4199 static int CALLBACK enum_register_wordW( const WCHAR *reading, DWORD style, const WCHAR *string, void *user )
4201 ime_trace( "reading %s, style %#lx, string %s, user %p\n", debugstr_w(reading), style, debugstr_w(string), user );
4203 ok_eq( 0xdeadbeef, style, UINT, "%#x" );
4204 ok_wcs( L"Reading", reading );
4205 ok_wcs( L"String", string );
4207 return 0xdeadbeef;
4210 static void test_ImmEnumRegisterWord( BOOL unicode )
4212 HKL hkl = GetKeyboardLayout( 0 );
4214 winetest_push_context( unicode ? "unicode" : "ansi" );
4216 SET_ENABLE( ImeEnumRegisterWord, TRUE );
4218 SetLastError( 0xdeadbeef );
4219 ok_ret( 0, ImmEnumRegisterWordW( NULL, enum_register_wordW, NULL, 0, NULL, NULL ) );
4220 ok_ret( 0, ImmEnumRegisterWordA( NULL, enum_register_wordA, NULL, 0, NULL, NULL ) );
4221 ok_ret( 0, ImmEnumRegisterWordW( hkl, enum_register_wordW, NULL, 0, NULL, NULL ) );
4222 ok_ret( 0, ImmEnumRegisterWordA( hkl, enum_register_wordA, NULL, 0, NULL, NULL ) );
4223 todo_wine
4224 ok_ret( 0xdeadbeef, GetLastError() );
4226 /* IME_PROP_END_UNLOAD for the IME to unload / reload. */
4227 ime_info.fdwProperty = IME_PROP_END_UNLOAD;
4228 if (unicode) ime_info.fdwProperty |= IME_PROP_UNICODE;
4230 if (!(hkl = wineime_hkl)) goto cleanup;
4232 SET_EXPECT( ImeEnumRegisterWord );
4233 ok_ret( 0, ImmEnumRegisterWordW( hkl, enum_register_wordW, NULL, 0, NULL, NULL ) );
4234 CHECK_CALLED( ImeEnumRegisterWord );
4236 SET_EXPECT( ImeEnumRegisterWord );
4237 ok_ret( 0, ImmEnumRegisterWordA( hkl, enum_register_wordA, NULL, 0, NULL, NULL ) );
4238 CHECK_CALLED( ImeEnumRegisterWord );
4240 SET_EXPECT( ImeEnumRegisterWord );
4241 ok_ret( 0xdeadbeef, ImmEnumRegisterWordW( hkl, enum_register_wordW, L"Reading", 0xdeadbeef, L"String", NULL ) );
4242 CHECK_CALLED( ImeEnumRegisterWord );
4244 SET_EXPECT( ImeEnumRegisterWord );
4245 ok_ret( 0xdeadbeef, ImmEnumRegisterWordA( hkl, enum_register_wordA, "Reading", 0xdeadbeef, "String", NULL ) );
4246 CHECK_CALLED( ImeEnumRegisterWord );
4248 cleanup:
4249 SET_ENABLE( ImeEnumRegisterWord, FALSE );
4251 winetest_pop_context();
4254 static void test_ImmRegisterWord( BOOL unicode )
4256 HKL hkl = GetKeyboardLayout( 0 );
4258 SET_ENABLE( ImeRegisterWord, TRUE );
4260 winetest_push_context( unicode ? "unicode" : "ansi" );
4262 SetLastError( 0xdeadbeef );
4263 ok_ret( 0, ImmRegisterWordW( NULL, NULL, 0, NULL ) );
4264 ok_ret( 0, ImmRegisterWordA( NULL, NULL, 0, NULL ) );
4265 ok_ret( 0, ImmRegisterWordW( hkl, NULL, 0, NULL ) );
4266 ok_ret( 0, ImmRegisterWordA( hkl, NULL, 0, NULL ) );
4267 todo_wine
4268 ok_ret( 0xdeadbeef, GetLastError() );
4270 /* IME_PROP_END_UNLOAD for the IME to unload / reload. */
4271 ime_info.fdwProperty = IME_PROP_END_UNLOAD;
4272 if (unicode) ime_info.fdwProperty |= IME_PROP_UNICODE;
4274 if (!(hkl = wineime_hkl)) goto cleanup;
4276 SET_EXPECT( ImeRegisterWord );
4277 ok_ret( 0, ImmRegisterWordW( hkl, NULL, 0, NULL ) );
4278 CHECK_CALLED( ImeRegisterWord );
4280 SET_EXPECT( ImeRegisterWord );
4281 ok_ret( 0, ImmRegisterWordA( hkl, NULL, 0, NULL ) );
4282 CHECK_CALLED( ImeRegisterWord );
4284 SET_EXPECT( ImeRegisterWord );
4285 ok_ret( 0, ImmRegisterWordW( hkl, L"Reading", 0, NULL ) );
4286 CHECK_CALLED( ImeRegisterWord );
4288 SET_EXPECT( ImeRegisterWord );
4289 ok_ret( 0, ImmRegisterWordA( hkl, "Reading", 0, NULL ) );
4290 CHECK_CALLED( ImeRegisterWord );
4292 SET_EXPECT( ImeRegisterWord );
4293 ok_ret( 0, ImmRegisterWordW( hkl, NULL, 0xdeadbeef, NULL ) );
4294 CHECK_CALLED( ImeRegisterWord );
4296 SET_EXPECT( ImeRegisterWord );
4297 ok_ret( 0, ImmRegisterWordA( hkl, NULL, 0xdeadbeef, NULL ) );
4298 CHECK_CALLED( ImeRegisterWord );
4300 SET_EXPECT( ImeRegisterWord );
4301 ok_ret( 0, ImmRegisterWordW( hkl, NULL, 0, L"String" ) );
4302 CHECK_CALLED( ImeRegisterWord );
4304 SET_EXPECT( ImeRegisterWord );
4305 ok_ret( 0, ImmRegisterWordA( hkl, NULL, 0, "String" ) );
4306 CHECK_CALLED( ImeRegisterWord );
4308 cleanup:
4309 SET_ENABLE( ImeRegisterWord, FALSE );
4311 winetest_pop_context();
4314 static void test_ImmGetRegisterWordStyle( BOOL unicode )
4316 HKL hkl = GetKeyboardLayout( 0 );
4317 STYLEBUFW styleW;
4318 STYLEBUFA styleA;
4320 winetest_push_context( unicode ? "unicode" : "ansi" );
4322 SET_ENABLE( ImeGetRegisterWordStyle, TRUE );
4324 SetLastError( 0xdeadbeef );
4325 ok_ret( 0, ImmGetRegisterWordStyleW( NULL, 0, &styleW ) );
4326 ok_ret( 0, ImmGetRegisterWordStyleA( NULL, 0, &styleA ) );
4327 ok_ret( 0, ImmGetRegisterWordStyleW( hkl, 0, &styleW ) );
4328 ok_ret( 0, ImmGetRegisterWordStyleA( hkl, 0, &styleA ) );
4329 todo_wine
4330 ok_ret( 0xdeadbeef, GetLastError() );
4332 /* IME_PROP_END_UNLOAD for the IME to unload / reload. */
4333 ime_info.fdwProperty = IME_PROP_END_UNLOAD;
4334 if (unicode) ime_info.fdwProperty |= IME_PROP_UNICODE;
4336 if (!(hkl = wineime_hkl)) goto cleanup;
4338 if (!strcmp( winetest_platform, "wine" )) goto skip_null;
4340 SET_EXPECT( ImeGetRegisterWordStyle );
4341 ok_ret( 0xdeadbeef, ImmGetRegisterWordStyleW( hkl, 16, NULL ) );
4342 CHECK_CALLED( ImeGetRegisterWordStyle );
4344 SET_EXPECT( ImeGetRegisterWordStyle );
4345 ok_ret( 0xdeadbeef, ImmGetRegisterWordStyleA( hkl, 16, NULL ) );
4346 CHECK_CALLED( ImeGetRegisterWordStyle );
4348 skip_null:
4349 SET_EXPECT( ImeGetRegisterWordStyle );
4350 memset( &styleW, 0xcd, sizeof(styleW) );
4351 ok_ret( 0xdeadbeef, ImmGetRegisterWordStyleW( hkl, 1, &styleW ) );
4352 if (ime_info.fdwProperty & IME_PROP_UNICODE)
4354 ok_eq( 0xdeadbeef, styleW.dwStyle, UINT, "%#x" );
4355 ok_wcs( L"StyleDescription", styleW.szDescription );
4357 else
4359 todo_wine
4360 ok_eq( 0xcdcdcdcd, styleW.dwStyle, UINT, "%#x" );
4361 todo_wine
4362 ok_eq( 0xcdcd, styleW.szDescription[0], WORD, "%#x" );
4364 CHECK_CALLED( ImeGetRegisterWordStyle );
4366 SET_EXPECT( ImeGetRegisterWordStyle );
4367 memset( &styleA, 0xcd, sizeof(styleA) );
4368 ok_ret( 0xdeadbeef, ImmGetRegisterWordStyleA( hkl, 1, &styleA ) );
4369 if (ime_info.fdwProperty & IME_PROP_UNICODE)
4371 todo_wine
4372 ok_eq( 0xcdcdcdcd, styleA.dwStyle, UINT, "%#x" );
4373 todo_wine
4374 ok_eq( 0xcd, styleA.szDescription[0], BYTE, "%#x" );
4376 else
4378 ok_eq( 0xdeadbeef, styleA.dwStyle, UINT, "%#x" );
4379 ok_str( "StyleDescription", styleA.szDescription );
4381 CHECK_CALLED( ImeGetRegisterWordStyle );
4383 cleanup:
4384 SET_ENABLE( ImeGetRegisterWordStyle, FALSE );
4386 winetest_pop_context();
4389 static void test_ImmUnregisterWord( BOOL unicode )
4391 HKL hkl = GetKeyboardLayout( 0 );
4393 winetest_push_context( unicode ? "unicode" : "ansi" );
4395 SET_ENABLE( ImeUnregisterWord, TRUE );
4397 SetLastError( 0xdeadbeef );
4398 ok_ret( 0, ImmUnregisterWordW( NULL, NULL, 0, NULL ) );
4399 ok_ret( 0, ImmUnregisterWordA( NULL, NULL, 0, NULL ) );
4400 ok_ret( 0, ImmUnregisterWordW( hkl, NULL, 0, NULL ) );
4401 ok_ret( 0, ImmUnregisterWordA( hkl, NULL, 0, NULL ) );
4402 todo_wine
4403 ok_ret( 0xdeadbeef, GetLastError() );
4405 /* IME_PROP_END_UNLOAD for the IME to unload / reload. */
4406 ime_info.fdwProperty = IME_PROP_END_UNLOAD;
4407 if (unicode) ime_info.fdwProperty |= IME_PROP_UNICODE;
4409 if (!(hkl = wineime_hkl)) goto cleanup;
4411 SET_EXPECT( ImeUnregisterWord );
4412 ok_ret( 0, ImmUnregisterWordW( hkl, NULL, 0, NULL ) );
4413 CHECK_CALLED( ImeUnregisterWord );
4415 SET_EXPECT( ImeUnregisterWord );
4416 ok_ret( 0, ImmUnregisterWordA( hkl, NULL, 0, NULL ) );
4417 CHECK_CALLED( ImeUnregisterWord );
4419 SET_EXPECT( ImeUnregisterWord );
4420 ok_ret( 0, ImmUnregisterWordW( hkl, L"Reading", 0, NULL ) );
4421 CHECK_CALLED( ImeUnregisterWord );
4423 SET_EXPECT( ImeUnregisterWord );
4424 ok_ret( 0, ImmUnregisterWordA( hkl, "Reading", 0, NULL ) );
4425 CHECK_CALLED( ImeUnregisterWord );
4427 SET_EXPECT( ImeUnregisterWord );
4428 ok_ret( 0, ImmUnregisterWordW( hkl, NULL, 0xdeadbeef, NULL ) );
4429 CHECK_CALLED( ImeUnregisterWord );
4431 SET_EXPECT( ImeUnregisterWord );
4432 ok_ret( 0, ImmUnregisterWordA( hkl, NULL, 0xdeadbeef, NULL ) );
4433 CHECK_CALLED( ImeUnregisterWord );
4435 SET_EXPECT( ImeUnregisterWord );
4436 ok_ret( 0, ImmUnregisterWordW( hkl, NULL, 0, L"String" ) );
4437 CHECK_CALLED( ImeUnregisterWord );
4439 SET_EXPECT( ImeUnregisterWord );
4440 ok_ret( 0, ImmUnregisterWordA( hkl, NULL, 0, "String" ) );
4441 CHECK_CALLED( ImeUnregisterWord );
4443 cleanup:
4444 SET_ENABLE( ImeUnregisterWord, FALSE );
4446 winetest_pop_context();
4449 struct ime_windows
4451 HWND ime_hwnd;
4452 HWND ime_ui_hwnd;
4455 static BOOL CALLBACK enum_thread_ime_windows( HWND hwnd, LPARAM lparam )
4457 struct ime_windows *params = (void *)lparam;
4458 WCHAR buffer[256];
4459 UINT ret;
4461 ime_trace( "hwnd %p, lparam %#Ix\n", hwnd, lparam );
4463 ret = RealGetWindowClassW( hwnd, buffer, ARRAY_SIZE(buffer) );
4464 ok( ret, "RealGetWindowClassW returned %#x\n", ret );
4466 if (!wcscmp( buffer, L"IME" ))
4468 ok( !params->ime_hwnd, "Found extra IME window %p\n", hwnd );
4469 params->ime_hwnd = hwnd;
4471 if (!wcscmp( buffer, ime_ui_class.lpszClassName ))
4473 ok( !params->ime_ui_hwnd, "Found extra IME UI window %p\n", hwnd );
4474 params->ime_ui_hwnd = hwnd;
4477 return TRUE;
4480 static void test_ImmSetConversionStatus(void)
4482 const struct ime_call set_conversion_status_0_seq[] =
4485 .hkl = expect_ime, .himc = default_himc,
4486 .func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETCONVERSIONMODE},
4489 .hkl = expect_ime, .himc = default_himc,
4490 .func = MSG_TEST_WIN, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETCONVERSIONMODE},
4493 .hkl = expect_ime, .himc = default_himc,
4494 .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETCONVERSIONMODE},
4497 .hkl = expect_ime, .himc = default_himc,
4498 .func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETSENTENCEMODE},
4501 .hkl = expect_ime, .himc = default_himc,
4502 .func = MSG_TEST_WIN, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETSENTENCEMODE},
4505 .hkl = expect_ime, .himc = default_himc,
4506 .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETSENTENCEMODE},
4508 {0},
4510 const struct ime_call set_conversion_status_1_seq[] =
4513 .hkl = expect_ime, .himc = default_himc,
4514 .func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0xdeadbeef, .value = IMC_SETCONVERSIONMODE},
4516 {0},
4518 const struct ime_call set_conversion_status_2_seq[] =
4521 .hkl = expect_ime, .himc = default_himc,
4522 .func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETCONVERSIONMODE},
4525 .hkl = expect_ime, .himc = default_himc,
4526 .func = MSG_TEST_WIN, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETCONVERSIONMODE},
4529 .hkl = expect_ime, .himc = default_himc,
4530 .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETCONVERSIONMODE},
4533 .hkl = expect_ime, .himc = default_himc,
4534 .func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0xfeedcafe, .value = IMC_SETSENTENCEMODE},
4537 .hkl = expect_ime, .himc = default_himc,
4538 .func = MSG_TEST_WIN, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETSENTENCEMODE},
4541 .hkl = expect_ime, .himc = default_himc,
4542 .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETSENTENCEMODE},
4544 {0},
4546 DWORD old_conversion, old_sentence, conversion, sentence;
4547 HKL hkl;
4548 INPUTCONTEXT *ctx;
4550 ok_ret( 0, ImmGetConversionStatus( 0, &old_conversion, &old_sentence ) );
4551 ok_ret( 1, ImmGetConversionStatus( default_himc, &old_conversion, &old_sentence ) );
4553 ctx = ImmLockIMC( default_himc );
4554 ok_ne( NULL, ctx, INPUTCONTEXT *, "%p" );
4555 ok_eq( old_conversion, ctx->fdwConversion, UINT, "%#x" );
4556 ok_eq( old_sentence, ctx->fdwSentence, UINT, "%#x" );
4558 hwnd = CreateWindowW( test_class.lpszClassName, NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
4559 100, 100, 100, 100, NULL, NULL, NULL, NULL );
4560 ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() );
4561 process_messages();
4563 ok_ret( 1, ImmGetConversionStatus( default_himc, &conversion, &sentence ) );
4564 ok_eq( old_conversion, conversion, UINT, "%#x" );
4565 ok_eq( old_sentence, sentence, UINT, "%#x" );
4566 ok_eq( old_conversion, ctx->fdwConversion, UINT, "%#x" );
4567 ok_eq( old_sentence, ctx->fdwSentence, UINT, "%#x" );
4569 ok_ret( 1, ImmSetConversionStatus( default_himc, 0, 0 ) );
4570 ok_ret( 1, ImmGetConversionStatus( default_himc, &conversion, &sentence ) );
4571 ok_eq( 0, conversion, UINT, "%#x" );
4572 ok_eq( 0, sentence, UINT, "%#x" );
4573 ok_eq( 0, ctx->fdwConversion, UINT, "%#x" );
4574 ok_eq( 0, ctx->fdwSentence, UINT, "%#x" );
4576 ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE;
4578 if (!(hkl = wineime_hkl)) goto cleanup;
4580 ok_ret( 1, ImmActivateLayout( hkl ) );
4581 ok_ret( 1, ImmLoadIME( hkl ) );
4582 process_messages();
4583 /* initial values are dependent on both old and new IME */
4584 ok_ret( 1, ImmSetConversionStatus( default_himc, 0, 0 ) );
4585 memset( ime_calls, 0, sizeof(ime_calls) );
4586 ime_call_count = 0;
4588 ok_ret( 1, ImmGetConversionStatus( default_himc, &conversion, &sentence ) );
4589 ok_eq( 0, conversion, UINT, "%#x" );
4590 ok_eq( 0, sentence, UINT, "%#x" );
4591 ok_eq( 0, ctx->fdwConversion, UINT, "%#x" );
4592 ok_eq( 0, ctx->fdwSentence, UINT, "%#x" );
4594 ok_seq( empty_sequence );
4595 ok_ret( 1, ImmSetConversionStatus( default_himc, 0xdeadbeef, 0xfeedcafe ) );
4596 ok_seq( set_conversion_status_0_seq );
4598 ok_ret( 1, ImmGetConversionStatus( default_himc, &conversion, &sentence ) );
4599 ok_eq( 0xdeadbeef, conversion, UINT, "%#x" );
4600 ok_eq( 0xfeedcafe, sentence, UINT, "%#x" );
4601 ok_eq( 0xdeadbeef, ctx->fdwConversion, UINT, "%#x" );
4602 ok_eq( 0xfeedcafe, ctx->fdwSentence, UINT, "%#x" );
4604 ok_ret( 1, ImmSetConversionStatus( default_himc, 0xdeadbeef, 0xfeedcafe ) );
4605 ok_seq( empty_sequence );
4607 ok_ret( 1, ImmGetConversionStatus( default_himc, &conversion, NULL ) );
4608 ok_eq( 0xdeadbeef, conversion, UINT, "%#x" );
4609 ok_eq( 0xdeadbeef, ctx->fdwConversion, UINT, "%#x" );
4610 ok_eq( 0xfeedcafe, ctx->fdwSentence, UINT, "%#x" );
4612 ctx->hWnd = 0;
4613 ok_seq( empty_sequence );
4614 ok_ret( 1, ImmSetConversionStatus( default_himc, 0, 0xfeedcafe ) );
4615 ok_seq( set_conversion_status_1_seq );
4617 ok_ret( 1, ImmGetConversionStatus( default_himc, &conversion, &sentence ) );
4618 ok_eq( 0, conversion, UINT, "%#x" );
4619 ok_eq( 0xfeedcafe, sentence, UINT, "%#x" );
4620 ok_eq( 0, ctx->fdwConversion, UINT, "%#x" );
4621 ok_eq( 0xfeedcafe, ctx->fdwSentence, UINT, "%#x" );
4623 ctx->hWnd = hwnd;
4624 ok_seq( empty_sequence );
4625 ok_ret( 1, ImmSetConversionStatus( default_himc, ~0, ~0 ) );
4626 ok_seq( set_conversion_status_2_seq );
4628 ok_ret( 1, ImmGetConversionStatus( default_himc, NULL, &sentence ) );
4629 ok_eq( ~0, sentence, UINT, "%#x" );
4630 ok_eq( ~0, ctx->fdwConversion, UINT, "%#x" );
4631 ok_eq( ~0, ctx->fdwSentence, UINT, "%#x" );
4633 ok_ret( 1, ImmSetConversionStatus( default_himc, ~0, ~0 ) );
4634 ok_seq( empty_sequence );
4636 ok_ret( 1, ImmGetConversionStatus( default_himc, &conversion, &sentence ) );
4637 ok_eq( ~0, conversion, UINT, "%#x" );
4638 ok_eq( ~0, sentence, UINT, "%#x" );
4639 ok_eq( ~0, ctx->fdwConversion, UINT, "%#x" );
4640 ok_eq( ~0, ctx->fdwSentence, UINT, "%#x" );
4642 /* status is cached and some bits are kept from the previous active IME */
4643 ok_ret( 1, ImmActivateLayout( default_hkl ) );
4644 todo_wine ok_eq( 0x200, ctx->fdwConversion, UINT, "%#x" );
4645 ok_eq( old_sentence, ctx->fdwSentence, UINT, "%#x" );
4646 ok_ret( 1, ImmActivateLayout( hkl ) );
4647 ok_eq( ~0, ctx->fdwConversion, UINT, "%#x" );
4648 ok_eq( ~0, ctx->fdwSentence, UINT, "%#x" );
4649 ok_ret( 1, ImmActivateLayout( default_hkl ) );
4650 todo_wine ok_eq( 0x200, ctx->fdwConversion, UINT, "%#x" );
4651 ok_eq( old_sentence, ctx->fdwSentence, UINT, "%#x" );
4653 ok_ret( 1, ImmFreeLayout( hkl ) );
4655 cleanup:
4656 /* sanitize conversion status to some sane default */
4657 ok_ret( 1, ImmSetConversionStatus( default_himc, 0, 0 ) );
4658 ok_ret( 1, ImmGetConversionStatus( default_himc, &conversion, &sentence ) );
4659 ok_eq( 0, conversion, UINT, "%#x" );
4660 ok_eq( 0, sentence, UINT, "%#x" );
4661 ok_eq( 0, ctx->fdwConversion, UINT, "%#x" );
4662 ok_eq( 0, ctx->fdwSentence, UINT, "%#x" );
4664 ok_ret( 1, DestroyWindow( hwnd ) );
4665 process_messages();
4666 memset( ime_calls, 0, sizeof(ime_calls) );
4667 ime_call_count = 0;
4669 ok_ret( 1, ImmUnlockIMC( default_himc ) );
4672 static void test_ImmSetOpenStatus(void)
4674 const struct ime_call set_open_status_0_seq[] =
4677 .hkl = expect_ime, .himc = default_himc,
4678 .func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETOPENSTATUS},
4681 .hkl = expect_ime, .himc = default_himc,
4682 .func = MSG_TEST_WIN, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETOPENSTATUS},
4685 .hkl = expect_ime, .himc = default_himc,
4686 .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETOPENSTATUS},
4688 {0},
4690 const struct ime_call set_open_status_1_seq[] =
4693 .hkl = expect_ime, .himc = default_himc,
4694 .func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETOPENSTATUS},
4696 {0},
4698 const struct ime_call set_open_status_2_seq[] =
4701 .hkl = expect_ime, .himc = default_himc,
4702 .func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETOPENSTATUS},
4705 .hkl = expect_ime, .himc = default_himc,
4706 .func = MSG_TEST_WIN, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETOPENSTATUS},
4709 .hkl = expect_ime, .himc = default_himc,
4710 .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETOPENSTATUS},
4712 {0},
4714 HKL hkl;
4715 struct ime_windows ime_windows = {0};
4716 DWORD old_status, status;
4717 INPUTCONTEXT *ctx;
4718 HIMC himc;
4720 ok_ret( 0, ImmGetOpenStatus( 0 ) );
4721 old_status = ImmGetOpenStatus( default_himc );
4723 ctx = ImmLockIMC( default_himc );
4724 ok_ne( NULL, ctx, INPUTCONTEXT *, "%p" );
4725 ok_eq( old_status, ctx->fOpen, UINT, "%#x" );
4727 hwnd = CreateWindowW( test_class.lpszClassName, NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
4728 100, 100, 100, 100, NULL, NULL, NULL, NULL );
4729 ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() );
4730 process_messages();
4732 status = ImmGetOpenStatus( default_himc );
4733 ok_eq( old_status, status, UINT, "%#x" );
4734 ok_eq( old_status, ctx->fOpen, UINT, "%#x" );
4736 ok_ret( 1, ImmSetOpenStatus( default_himc, 0 ) );
4737 status = ImmGetOpenStatus( default_himc );
4738 ok_eq( 0, status, UINT, "%#x" );
4739 ok_eq( 0, ctx->fOpen, UINT, "%#x" );
4741 ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE;
4743 if (!(hkl = wineime_hkl)) goto cleanup;
4745 ok_ret( 1, ImmActivateLayout( hkl ) );
4746 ok_ret( 1, ImmLoadIME( hkl ) );
4747 process_messages();
4748 /* initial values are dependent on both old and new IME */
4749 ok_ret( 1, ImmSetOpenStatus( default_himc, 0 ) );
4750 memset( ime_calls, 0, sizeof(ime_calls) );
4751 ime_call_count = 0;
4753 status = ImmGetOpenStatus( default_himc );
4754 ok_eq( 0, status, UINT, "%#x" );
4755 ok_eq( 0, ctx->fOpen, UINT, "%#x" );
4757 ok_seq( empty_sequence );
4758 ok_ret( 1, ImmSetOpenStatus( default_himc, 0xdeadbeef ) );
4759 ok_seq( set_open_status_0_seq );
4761 status = ImmGetOpenStatus( default_himc );
4762 ok_eq( 0xdeadbeef, status, UINT, "%#x" );
4763 ok_eq( 0xdeadbeef, ctx->fOpen, UINT, "%#x" );
4766 himc = ImmCreateContext();
4767 ok_ne( NULL, himc, HIMC, "%p" );
4768 ok_ret( 1, EnumThreadWindows( GetCurrentThreadId(), enum_thread_ime_windows, (LPARAM)&ime_windows ) );
4769 ok_ne( NULL, ime_windows.ime_hwnd, HWND, "%p" );
4770 ok_ne( NULL, ime_windows.ime_ui_hwnd, HWND, "%p" );
4771 ok_eq( default_himc, (HIMC)GetWindowLongPtrW( ime_windows.ime_ui_hwnd, IMMGWL_IMC ), HIMC, "%p" );
4772 ok_ret( 1, ImmSetOpenStatus( himc, TRUE ) );
4773 ok_eq( default_himc, (HIMC)GetWindowLongPtrW( ime_windows.ime_ui_hwnd, IMMGWL_IMC ), HIMC, "%p" );
4774 ok_ret( 1, ImmSetOpenStatus( himc, FALSE ) );
4775 ok_eq( default_himc, (HIMC)GetWindowLongPtrW( ime_windows.ime_ui_hwnd, IMMGWL_IMC ), HIMC, "%p" );
4776 ok_ret( 1, ImmDestroyContext( himc ) );
4777 memset( ime_calls, 0, sizeof(ime_calls) );
4778 ime_call_count = 0;
4781 ok_ret( 1, ImmSetOpenStatus( default_himc, 0xdeadbeef ) );
4782 ok_seq( empty_sequence );
4784 status = ImmGetOpenStatus( default_himc );
4785 ok_eq( 0xdeadbeef, status, UINT, "%#x" );
4786 ok_eq( 0xdeadbeef, ctx->fOpen, UINT, "%#x" );
4788 ctx->hWnd = 0;
4789 ok_ret( 1, ImmSetOpenStatus( default_himc, 0xfeedcafe ) );
4790 ok_seq( set_open_status_1_seq );
4792 status = ImmGetOpenStatus( default_himc );
4793 ok_eq( 0xfeedcafe, status, UINT, "%#x" );
4794 ok_eq( 0xfeedcafe, ctx->fOpen, UINT, "%#x" );
4796 ctx->hWnd = hwnd;
4797 ok_seq( empty_sequence );
4798 ok_ret( 1, ImmSetOpenStatus( default_himc, ~0 ) );
4799 ok_seq( set_open_status_2_seq );
4801 status = ImmGetOpenStatus( default_himc );
4802 ok_eq( ~0, status, UINT, "%#x" );
4803 ok_eq( ~0, ctx->fOpen, UINT, "%#x" );
4805 ok_ret( 1, ImmSetOpenStatus( default_himc, ~0 ) );
4806 ok_seq( empty_sequence );
4808 status = ImmGetOpenStatus( default_himc );
4809 ok_eq( ~0, status, UINT, "%#x" );
4810 ok_eq( ~0, ctx->fOpen, UINT, "%#x" );
4812 /* status is cached between IME activations */
4814 ok_ret( 1, ImmActivateLayout( default_hkl ) );
4815 status = ImmGetOpenStatus( default_himc );
4816 ok_eq( old_status, status, UINT, "%#x" );
4817 ok_eq( old_status, ctx->fOpen, UINT, "%#x" );
4818 ok_ret( 1, ImmActivateLayout( hkl ) );
4819 status = ImmGetOpenStatus( default_himc );
4820 todo_wine ok_eq( 1, status, UINT, "%#x" );
4821 todo_wine ok_eq( 1, ctx->fOpen, UINT, "%#x" );
4822 ok_ret( 1, ImmSetOpenStatus( default_himc, 0 ) );
4823 ok_ret( 1, ImmActivateLayout( default_hkl ) );
4824 status = ImmGetOpenStatus( default_himc );
4825 ok_eq( old_status, status, UINT, "%#x" );
4826 ok_eq( old_status, ctx->fOpen, UINT, "%#x" );
4828 ok_ret( 1, ImmFreeLayout( hkl ) );
4830 cleanup:
4831 /* sanitize open status to some sane default */
4832 ok_ret( 1, ImmSetOpenStatus( default_himc, 0 ) );
4833 status = ImmGetOpenStatus( default_himc );
4834 ok_eq( 0, status, UINT, "%#x" );
4835 ok_eq( 0, ctx->fOpen, UINT, "%#x" );
4837 ok_ret( 1, DestroyWindow( hwnd ) );
4838 process_messages();
4839 memset( ime_calls, 0, sizeof(ime_calls) );
4840 ime_call_count = 0;
4842 ok_ret( 1, ImmUnlockIMC( default_himc ) );
4845 static void test_ImmProcessKey(void)
4847 const struct ime_call process_key_0[] =
4850 .hkl = expect_ime, .himc = default_himc,
4851 .func = IME_PROCESS_KEY, .process_key = {.vkey = 'A', .lparam = MAKELONG(0, 0x1e)},
4853 {0},
4855 const struct ime_call process_key_1[] =
4858 .hkl = expect_ime, .himc = default_himc,
4859 .func = IME_PROCESS_KEY, .process_key = {.vkey = 'A', .lparam = MAKELONG(1, 0x1e)},
4861 {0},
4863 const struct ime_call process_key_2[] =
4866 .hkl = expect_ime, .himc = default_himc,
4867 .func = IME_PROCESS_KEY, .process_key = {.vkey = 'A', .lparam = MAKELONG(2, 0x1e)},
4869 {0},
4871 HKL hkl;
4872 UINT_PTR ret;
4873 HIMC himc;
4875 hwnd = CreateWindowW( L"static", NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
4876 100, 100, 100, 100, NULL, NULL, NULL, NULL );
4877 ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() );
4878 process_messages();
4880 ok_ret( 0, ImmProcessKey( hwnd, default_hkl, 'A', MAKELONG(1, 0x1e), 0 ) );
4881 ok_seq( empty_sequence );
4883 ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE;
4885 if (!(hkl = wineime_hkl)) goto cleanup;
4887 ok_ret( 1, ImmActivateLayout( hkl ) );
4888 ok_ret( 1, ImmLoadIME( hkl ) );
4889 process_messages();
4890 memset( ime_calls, 0, sizeof(ime_calls) );
4891 ime_call_count = 0;
4893 ok_ret( 0, ImmProcessKey( 0, hkl, 'A', MAKELONG(1, 0x1e), 0 ) );
4894 ok_seq( empty_sequence );
4896 ok_ret( 0, ImmProcessKey( hwnd, hkl, 'A', MAKELONG(0, 0x1e), 0 ) );
4897 ok_seq( process_key_0 );
4898 ret = ImmProcessKey( hwnd, hkl, 'A', MAKELONG(1, 0x1e), 0 );
4899 todo_wine
4900 ok_ret( 2, ret );
4901 ok_seq( process_key_1 );
4902 ok_ret( 2, ImmProcessKey( hwnd, hkl, 'A', MAKELONG(2, 0x1e), 0 ) );
4903 ok_seq( process_key_2 );
4905 ok_eq( hkl, GetKeyboardLayout( 0 ), HKL, "%p" );
4906 ok_ret( 0, ImmProcessKey( hwnd, default_hkl, 'A', MAKELONG(1, 0x1e), 0 ) );
4907 ok_seq( empty_sequence );
4908 ok_eq( hkl, GetKeyboardLayout( 0 ), HKL, "%p" );
4910 himc = ImmCreateContext();
4911 ok_ne( NULL, himc, HIMC, "%p" );
4912 ok_ret( 'A', ImmGetVirtualKey( hwnd ) );
4913 ok_eq( default_himc, ImmAssociateContext( hwnd, himc ), HIMC, "%p" );
4914 todo_wine ok_ret( VK_PROCESSKEY, ImmGetVirtualKey( hwnd ) );
4915 ok_eq( himc, ImmAssociateContext( hwnd, default_himc ), HIMC, "%p" );
4916 ok_ret( 'A', ImmGetVirtualKey( hwnd ) );
4917 ImmDestroyContext( himc );
4919 ok_ret( 0, ImmTranslateMessage( hwnd, WM_KEYUP, 'A', 0 ) );
4920 ok_ret( VK_PROCESSKEY, ImmGetVirtualKey( hwnd ) );
4922 ok_ret( 1, ImmActivateLayout( default_hkl ) );
4924 ok_ret( 1, ImmFreeLayout( hkl ) );
4925 process_messages();
4926 memset( ime_calls, 0, sizeof(ime_calls) );
4927 ime_call_count = 0;
4929 cleanup:
4930 ok_ret( 1, DestroyWindow( hwnd ) );
4933 static void test_ImmActivateLayout(void)
4935 const struct ime_call activate_seq[] =
4938 .hkl = expect_ime, .himc = default_himc,
4939 .func = IME_SELECT, .select = 1,
4941 {0},
4943 const struct ime_call deactivate_seq[] =
4946 .hkl = expect_ime, .himc = default_himc,
4947 .func = IME_NOTIFY, .notify = {.action = NI_COMPOSITIONSTR, .index = CPS_CANCEL, .value = 0},
4948 .todo = TRUE,
4951 .hkl = default_hkl, .himc = default_himc,
4952 .func = IME_SELECT, .select = 0,
4954 {0},
4956 struct ime_call activate_with_window_seq[] =
4959 .hkl = expect_ime, .himc = default_himc,
4960 .func = IME_SELECT, .select = 1,
4961 .flaky_himc = TRUE,
4964 .hkl = expect_ime, .himc = 0/*himc*/,
4965 .func = IME_SELECT, .select = 1,
4966 .flaky_himc = TRUE,
4969 .hkl = expect_ime, .himc = default_himc,
4970 .func = MSG_IME_UI, .message = {.msg = WM_IME_SELECT, .wparam = 1, .lparam = (LPARAM)expect_ime},
4973 .hkl = expect_ime, .himc = default_himc,
4974 .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_OPENSTATUSWINDOW},
4975 .todo = TRUE,
4978 .hkl = expect_ime, .himc = default_himc,
4979 .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETOPENSTATUS},
4980 .todo = TRUE,
4983 .hkl = expect_ime, .himc = default_himc,
4984 .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETCONVERSIONMODE},
4985 .todo = TRUE,
4988 .hkl = expect_ime, .himc = default_himc,
4989 .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETSENTENCEMODE},
4990 .todo = TRUE,
4992 {0},
4994 struct ime_call deactivate_with_window_seq[] =
4997 .hkl = expect_ime, .himc = default_himc,
4998 .func = IME_NOTIFY, .notify = {.action = NI_COMPOSITIONSTR, .index = CPS_CANCEL, .value = 0},
4999 .todo = TRUE, .flaky_himc = TRUE,
5002 .hkl = expect_ime, .himc = 0/*himc*/,
5003 .func = IME_NOTIFY, .notify = {.action = NI_COMPOSITIONSTR, .index = CPS_CANCEL, .value = 0},
5004 .todo = TRUE, .flaky_himc = TRUE,
5007 .hkl = expect_ime, .himc = default_himc,
5008 .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_CLOSESTATUSWINDOW},
5009 .todo = TRUE,
5012 .hkl = expect_ime, .himc = default_himc,
5013 .func = MSG_IME_UI, .message = {.msg = WM_IME_SELECT, .wparam = 0, .lparam = (LPARAM)expect_ime},
5016 .hkl = default_hkl, .himc = default_himc,
5017 .func = IME_SELECT, .select = 0,
5018 .flaky_himc = TRUE,
5021 .hkl = default_hkl, .himc = 0/*himc*/,
5022 .func = IME_SELECT, .select = 0,
5023 .flaky_himc = TRUE,
5025 {0},
5027 HKL hkl;
5028 struct ime_windows ime_windows = {0};
5029 HIMC himc;
5030 UINT ret;
5032 SET_ENABLE( ImeInquire, TRUE );
5033 SET_ENABLE( ImeDestroy, TRUE );
5035 ok_ret( 1, ImmActivateLayout( default_hkl ) );
5037 ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE;
5039 if (!(hkl = wineime_hkl)) goto cleanup;
5041 /* ActivateKeyboardLayout doesn't call ImeInquire / ImeDestroy */
5043 ok_seq( empty_sequence );
5044 ok_eq( default_hkl, ActivateKeyboardLayout( hkl, 0 ), HKL, "%p" );
5045 ok_eq( hkl, GetKeyboardLayout( 0 ), HKL, "%p" );
5046 ok_eq( hkl, ActivateKeyboardLayout( default_hkl, 0 ), HKL, "%p" );
5047 ok_seq( empty_sequence );
5048 ok_eq( default_hkl, GetKeyboardLayout( 0 ), HKL, "%p" );
5051 /* ImmActivateLayout changes active HKL */
5053 SET_EXPECT( ImeInquire );
5054 ok_ret( 1, ImmActivateLayout( hkl ) );
5055 ok_seq( activate_seq );
5056 CHECK_CALLED( ImeInquire );
5057 ok_ret( 1, ImmLoadIME( hkl ) );
5059 ok_eq( hkl, GetKeyboardLayout( 0 ), HKL, "%p" );
5061 ok_ret( 1, ImmActivateLayout( hkl ) );
5062 ok_seq( empty_sequence );
5064 ok_ret( 1, ImmActivateLayout( default_hkl ) );
5065 ok_seq( deactivate_seq );
5067 ok_eq( default_hkl, GetKeyboardLayout( 0 ), HKL, "%p" );
5070 /* ImmActivateLayout leaks the IME, we need to free it manually */
5072 SET_EXPECT( ImeDestroy );
5073 ret = ImmFreeLayout( hkl );
5074 ok( ret, "ImmFreeLayout returned %u\n", ret );
5075 CHECK_CALLED( ImeDestroy );
5076 ok_seq( empty_sequence );
5079 /* when there's a window, ActivateKeyboardLayout calls ImeInquire */
5081 hwnd = CreateWindowW( L"static", NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
5082 100, 100, 100, 100, NULL, NULL, NULL, NULL );
5083 ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() );
5084 process_messages();
5085 ok_seq( empty_sequence );
5087 himc = ImmCreateContext();
5088 ok( !!himc, "got himc %p\n", himc );
5089 ok_seq( empty_sequence );
5091 SET_EXPECT( ImeInquire );
5092 ok_eq( default_hkl, ActivateKeyboardLayout( hkl, 0 ), HKL, "%p" );
5093 CHECK_CALLED( ImeInquire );
5094 activate_with_window_seq[1].himc = himc;
5095 ok_seq( activate_with_window_seq );
5096 ok_ret( 1, ImmLoadIME( hkl ) );
5098 ok_eq( hkl, GetKeyboardLayout( 0 ), HKL, "%p" );
5100 /* FIXME: ignore spurious VK_CONTROL ImeProcessKey / ImeToAsciiEx calls */
5101 process_messages();
5102 memset( ime_calls, 0, sizeof(ime_calls) );
5103 ime_call_count = 0;
5105 ok_eq( hkl, ActivateKeyboardLayout( default_hkl, 0 ), HKL, "%p" );
5106 deactivate_with_window_seq[1].himc = himc;
5107 deactivate_with_window_seq[5].himc = himc;
5108 ok_seq( deactivate_with_window_seq );
5110 ok_eq( default_hkl, GetKeyboardLayout( 0 ), HKL, "%p" );
5112 ok_ret( 1, ImmDestroyContext( himc ) );
5113 ok_seq( empty_sequence );
5116 ok_eq( default_hkl, ActivateKeyboardLayout( hkl, 0 ), HKL, "%p" );
5117 ok_eq( hkl, GetKeyboardLayout( 0 ), HKL, "%p" );
5119 ok_ret( 1, EnumThreadWindows( GetCurrentThreadId(), enum_thread_ime_windows, (LPARAM)&ime_windows ) );
5120 ok( !!ime_windows.ime_hwnd, "missing IME window\n" );
5121 ok( !!ime_windows.ime_ui_hwnd, "missing IME UI window\n" );
5122 ok_ret( (UINT_PTR)ime_windows.ime_hwnd, (UINT_PTR)GetParent( ime_windows.ime_ui_hwnd ) );
5124 ok_eq( hkl, ActivateKeyboardLayout( default_hkl, 0 ), HKL, "%p" );
5125 ok_eq( default_hkl, GetKeyboardLayout( 0 ), HKL, "%p" );
5126 process_messages();
5129 SET_EXPECT( ImeDestroy );
5130 ok_ret( 1, ImmFreeLayout( hkl ) );
5131 CHECK_CALLED( ImeDestroy );
5133 ok_ret( 1, DestroyWindow( hwnd ) );
5134 process_messages();
5135 memset( ime_calls, 0, sizeof(ime_calls) );
5136 ime_call_count = 0;
5139 cleanup:
5140 SET_ENABLE( ImeInquire, FALSE );
5141 SET_ENABLE( ImeDestroy, FALSE );
5144 static void test_ImmCreateInputContext(void)
5146 struct ime_call activate_seq[] =
5149 .hkl = expect_ime, .himc = default_himc,
5150 .func = IME_SELECT, .select = 1,
5151 .flaky_himc = TRUE,
5154 .hkl = expect_ime, .himc = 0/*himc[0]*/,
5155 .func = IME_SELECT, .select = 1,
5156 .flaky_himc = TRUE,
5159 .hkl = expect_ime, .himc = default_himc,
5160 .func = MSG_IME_UI, .message = {.msg = WM_IME_SELECT, .wparam = 1, .lparam = (LPARAM)expect_ime},
5163 .hkl = expect_ime, .himc = default_himc,
5164 .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_OPENSTATUSWINDOW},
5165 .todo = TRUE,
5167 {0},
5169 struct ime_call select1_seq[] =
5172 .hkl = expect_ime, .himc = 0/*himc[0]*/,
5173 .func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETOPENSTATUS},
5174 .todo = TRUE, .flaky_himc = TRUE, .broken = TRUE /* sometimes */,
5177 .hkl = expect_ime, .himc = default_himc,
5178 .func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETOPENSTATUS},
5179 .todo = TRUE, .flaky_himc = TRUE, .broken = TRUE /* sometimes */,
5182 .hkl = expect_ime, .himc = default_himc,
5183 .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETOPENSTATUS},
5184 .todo = TRUE, .broken = TRUE /* sometimes */,
5187 .hkl = expect_ime, .himc = default_himc,
5188 .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETCONVERSIONMODE},
5189 .todo = TRUE, .broken = TRUE /* sometimes */,
5192 .hkl = expect_ime, .himc = 0/*himc[1]*/,
5193 .func = IME_SELECT, .select = 1,
5195 {0},
5197 struct ime_call select0_seq[] =
5200 .hkl = expect_ime, .himc = 0/*himc[1]*/,
5201 .func = IME_SELECT, .select = 0,
5203 {0},
5205 struct ime_call deactivate_seq[] =
5208 .hkl = expect_ime, .himc = default_himc,
5209 .func = IME_NOTIFY, .notify = {.action = NI_COMPOSITIONSTR, .index = CPS_CANCEL, .value = 0},
5210 .todo = TRUE, .flaky_himc = TRUE,
5213 .hkl = expect_ime, .himc = 0/*himc[0]*/,
5214 .func = IME_NOTIFY, .notify = {.action = NI_COMPOSITIONSTR, .index = CPS_CANCEL, .value = 0},
5215 .todo = TRUE, .flaky_himc = TRUE,
5218 .hkl = expect_ime, .himc = default_himc,
5219 .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_CLOSESTATUSWINDOW},
5220 .todo = TRUE,
5223 .hkl = expect_ime, .himc = default_himc,
5224 .func = MSG_IME_UI, .message = {.msg = WM_IME_SELECT, .wparam = 0, .lparam = (LPARAM)expect_ime},
5227 .hkl = default_hkl, .himc = default_himc,
5228 .func = IME_SELECT, .select = 0,
5229 .flaky_himc = TRUE,
5232 .hkl = default_hkl, .himc = 0/*himc[0]*/,
5233 .func = IME_SELECT, .select = 0,
5234 .flaky_himc = TRUE,
5236 {0},
5238 HKL hkl;
5239 INPUTCONTEXT *ctx;
5240 HIMC himc[2];
5241 HWND hwnd;
5243 ctx = ImmLockIMC( default_himc );
5244 ok( !!ctx, "ImmLockIMC failed, error %lu\n", GetLastError() );
5245 ok_ret( 0, IsWindow( ctx->hWnd ) );
5246 ok_ret( 1, ImmUnlockIMC( default_himc ) );
5249 /* new input contexts cannot be locked before IME window has been created */
5251 himc[0] = ImmCreateContext();
5252 ok( !!himc[0], "ImmCreateContext failed, error %lu\n", GetLastError() );
5253 ctx = ImmLockIMC( himc[0] );
5254 todo_wine
5255 ok( !ctx, "ImmLockIMC failed, error %lu\n", GetLastError() );
5256 if (ctx) ImmUnlockIMCC( himc[0] );
5258 hwnd = CreateWindowW( L"static", NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
5259 100, 100, 100, 100, NULL, NULL, NULL, NULL );
5260 ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() );
5261 process_messages();
5263 ctx = ImmLockIMC( default_himc );
5264 ok( !!ctx, "ImmLockIMC failed, error %lu\n", GetLastError() );
5265 ok_ret( 1, ImmUnlockIMC( default_himc ) );
5267 ctx = ImmLockIMC( himc[0] );
5268 ok( !!ctx, "ImmLockIMC failed, error %lu\n", GetLastError() );
5269 ok_ret( 1, ImmUnlockIMC( himc[0] ) );
5272 ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE;
5273 ime_info.dwPrivateDataSize = 123;
5275 if (!(hkl = wineime_hkl)) goto cleanup;
5277 ok_ret( 1, ImmLoadIME( hkl ) );
5279 /* Activating the layout calls ImeSelect 1 on existing HIMC */
5281 ok_seq( empty_sequence );
5282 ok_ret( 1, ImmActivateLayout( hkl ) );
5283 activate_seq[1].himc = himc[0];
5284 ok_seq( activate_seq );
5286 ok_eq( hkl, GetKeyboardLayout( 0 ), HKL, "%p" );
5288 ctx = ImmLockIMC( default_himc );
5289 ok( !!ctx, "ImmLockIMC failed, error %lu\n", GetLastError() );
5290 ok_ret( 1, ImmUnlockIMC( default_himc ) );
5292 ctx = ImmLockIMC( himc[0] );
5293 ok( !!ctx, "ImmLockIMC failed, error %lu\n", GetLastError() );
5294 ok_ret( 1, ImmUnlockIMC( himc[0] ) );
5297 /* ImmLockIMC triggers the ImeSelect call, to allocate private data */
5299 himc[1] = ImmCreateContext();
5300 ok( !!himc[1], "ImmCreateContext failed, error %lu\n", GetLastError() );
5302 ok_seq( empty_sequence );
5303 ctx = ImmLockIMC( himc[1] );
5304 ok( !!ctx, "ImmLockIMC failed, error %lu\n", GetLastError() );
5305 select1_seq[0].himc = himc[0];
5306 select1_seq[4].himc = himc[1];
5307 ok_seq( select1_seq );
5309 ok_ret( 1, ImmUnlockIMC( himc[1] ) );
5311 ok_seq( empty_sequence );
5312 ok_ret( 1, ImmDestroyContext( himc[1] ) );
5313 select0_seq[0].himc = himc[1];
5314 ok_seq( select0_seq );
5317 /* Deactivating the layout calls ImeSelect 0 on existing HIMC */
5319 ok_ret( 1, ImmActivateLayout( default_hkl ) );
5320 deactivate_seq[1].himc = himc[0];
5321 deactivate_seq[5].himc = himc[0];
5322 ok_seq( deactivate_seq );
5324 ok_eq( default_hkl, GetKeyboardLayout( 0 ), HKL, "%p" );
5326 ok_ret( 1, ImmFreeLayout( hkl ) );
5327 ok_seq( empty_sequence );
5329 cleanup:
5330 ok_ret( 1, ImmDestroyContext( himc[0] ) );
5331 ok_ret( 1, DestroyWindow( hwnd ) );
5332 process_messages();
5333 memset( ime_calls, 0, sizeof(ime_calls) );
5334 ime_call_count = 0;
5336 ime_info.dwPrivateDataSize = 0;
5339 static void test_DefWindowProc(void)
5341 const struct ime_call start_composition_seq[] =
5343 {.hkl = expect_ime, .himc = default_himc, .func = MSG_IME_UI, .message = {.msg = WM_IME_STARTCOMPOSITION}},
5344 {0},
5346 const struct ime_call end_composition_seq[] =
5348 {.hkl = expect_ime, .himc = default_himc, .func = MSG_IME_UI, .message = {.msg = WM_IME_ENDCOMPOSITION}},
5349 {0},
5351 const struct ime_call composition_seq[] =
5353 {.hkl = expect_ime, .himc = default_himc, .func = MSG_IME_UI, .message = {.msg = WM_IME_COMPOSITION}},
5354 {0},
5356 const struct ime_call set_context_seq[] =
5358 {.hkl = expect_ime, .himc = default_himc, .func = MSG_IME_UI, .message = {.msg = WM_IME_SETCONTEXT}},
5359 {0},
5361 const struct ime_call notify_seq[] =
5363 {.hkl = expect_ime, .himc = default_himc, .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY}},
5364 {0},
5366 HKL hkl;
5367 UINT_PTR ret;
5369 ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE;
5371 if (!(hkl = wineime_hkl)) return;
5373 hwnd = CreateWindowW( L"static", NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
5374 100, 100, 100, 100, NULL, NULL, NULL, NULL );
5375 ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() );
5377 ok_ret( 1, ImmActivateLayout( hkl ) );
5378 ok_ret( 1, ImmLoadIME( hkl ) );
5379 process_messages();
5380 memset( ime_calls, 0, sizeof(ime_calls) );
5381 ime_call_count = 0;
5383 ok_ret( 0, DefWindowProcW( hwnd, WM_IME_STARTCOMPOSITION, 0, 0 ) );
5384 ok_seq( start_composition_seq );
5385 ok_ret( 0, DefWindowProcW( hwnd, WM_IME_ENDCOMPOSITION, 0, 0 ) );
5386 ok_seq( end_composition_seq );
5387 ok_ret( 0, DefWindowProcW( hwnd, WM_IME_COMPOSITION, 0, 0 ) );
5388 ok_seq( composition_seq );
5389 ok_ret( 0, DefWindowProcW( hwnd, WM_IME_SETCONTEXT, 0, 0 ) );
5390 ok_seq( set_context_seq );
5391 ok_ret( 0, DefWindowProcW( hwnd, WM_IME_NOTIFY, 0, 0 ) );
5392 ok_seq( notify_seq );
5393 ok_ret( 0, DefWindowProcW( hwnd, WM_IME_CONTROL, 0, 0 ) );
5394 ok_seq( empty_sequence );
5395 ok_ret( 0, DefWindowProcW( hwnd, WM_IME_COMPOSITIONFULL, 0, 0 ) );
5396 ok_seq( empty_sequence );
5397 ok_ret( 0, DefWindowProcW( hwnd, WM_IME_SELECT, 0, 0 ) );
5398 ok_seq( empty_sequence );
5399 ok_ret( 0, DefWindowProcW( hwnd, WM_IME_CHAR, 0, 0 ) );
5400 ok_seq( empty_sequence );
5401 ok_ret( 0, DefWindowProcW( hwnd, 0x287, 0, 0 ) );
5402 ok_seq( empty_sequence );
5403 ok_ret( 0, DefWindowProcW( hwnd, WM_IME_REQUEST, 0, 0 ) );
5404 ok_seq( empty_sequence );
5405 ret = DefWindowProcW( hwnd, WM_IME_KEYDOWN, 0, 0 );
5406 todo_wine
5407 ok_ret( 0, ret );
5408 ok_seq( empty_sequence );
5409 ret = DefWindowProcW( hwnd, WM_IME_KEYUP, 0, 0 );
5410 todo_wine
5411 ok_ret( 0, ret );
5412 ok_seq( empty_sequence );
5414 ok_ret( 1, ImmActivateLayout( default_hkl ) );
5415 ok_ret( 1, DestroyWindow( hwnd ) );
5416 process_messages();
5418 ok_ret( 1, ImmFreeLayout( hkl ) );
5419 memset( ime_calls, 0, sizeof(ime_calls) );
5420 ime_call_count = 0;
5423 static void test_ImmSetActiveContext(void)
5425 const struct ime_call activate_0_seq[] =
5428 .hkl = expect_ime, .himc = default_himc,
5429 .func = IME_SET_ACTIVE_CONTEXT, .set_active_context = {.flag = 1}
5432 .hkl = expect_ime, .himc = default_himc,
5433 .func = MSG_IME_UI, .message = {.msg = WM_IME_SETCONTEXT, .wparam = 1, .lparam = ISC_SHOWUIALL}
5435 {0},
5437 const struct ime_call deactivate_0_seq[] =
5440 .hkl = expect_ime, .himc = default_himc,
5441 .func = IME_SET_ACTIVE_CONTEXT, .set_active_context = {.flag = 0}
5444 .hkl = expect_ime, .himc = default_himc,
5445 .func = MSG_IME_UI, .message = {.msg = WM_IME_SETCONTEXT, .wparam = 0, .lparam = ISC_SHOWUIALL}
5447 {0},
5449 struct ime_call deactivate_1_seq[] =
5452 .hkl = expect_ime, .himc = default_himc,
5453 .func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETOPENSTATUS},
5454 .todo = TRUE, .flaky_himc = TRUE, .broken = TRUE /* sometimes */,
5457 .hkl = expect_ime, .himc = default_himc,
5458 .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETOPENSTATUS},
5459 .todo = TRUE, .broken = TRUE /* sometimes */,
5462 .hkl = expect_ime, .himc = default_himc,
5463 .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETCONVERSIONMODE},
5464 .todo = TRUE, .broken = TRUE /* sometimes */,
5467 .hkl = expect_ime, .himc = 0/*himc*/,
5468 .func = IME_SELECT, .select = 1,
5471 .hkl = expect_ime, .himc = 0/*himc*/,
5472 .func = IME_SET_ACTIVE_CONTEXT, .set_active_context = {.flag = 0}
5475 .hkl = expect_ime, .himc = default_himc,
5476 .func = MSG_IME_UI, .message = {.msg = WM_IME_SETCONTEXT, .wparam = 0, .lparam = ISC_SHOWUIALL}
5478 {0},
5480 struct ime_call deactivate_2_seq[] =
5483 .hkl = expect_ime, .himc = 0/*himc*/,
5484 .func = IME_SET_ACTIVE_CONTEXT, .set_active_context = {.flag = 0}
5487 .hkl = expect_ime, .himc = default_himc,
5488 .func = MSG_IME_UI, .message = {.msg = WM_IME_SETCONTEXT, .wparam = 0, .lparam = ISC_SHOWUIALL}
5490 {0},
5492 struct ime_call activate_1_seq[] =
5495 .hkl = expect_ime, .himc = 0/*himc*/,
5496 .func = IME_SET_ACTIVE_CONTEXT, .set_active_context = {.flag = 1}
5499 .hkl = expect_ime, .himc = default_himc,
5500 .func = MSG_IME_UI, .message = {.msg = WM_IME_SETCONTEXT, .wparam = 1, .lparam = ISC_SHOWUIALL}
5502 {0},
5504 HKL hkl;
5505 struct ime_windows ime_windows = {0};
5506 INPUTCONTEXT *ctx;
5507 HIMC himc;
5509 ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE;
5511 if (!(hkl = wineime_hkl)) return;
5513 hwnd = CreateWindowW( L"static", NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
5514 100, 100, 100, 100, NULL, NULL, NULL, NULL );
5515 ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() );
5517 ok_ret( 1, ImmActivateLayout( hkl ) );
5518 ok_ret( 1, ImmLoadIME( hkl ) );
5519 process_messages();
5520 memset( ime_calls, 0, sizeof(ime_calls) );
5521 ime_call_count = 0;
5523 ok_ret( 1, EnumThreadWindows( GetCurrentThreadId(), enum_thread_ime_windows, (LPARAM)&ime_windows ) );
5524 ok_ne( NULL, ime_windows.ime_hwnd, HWND, "%p" );
5525 ok_ne( NULL, ime_windows.ime_ui_hwnd, HWND, "%p" );
5526 ok_ret( 0, IsWindowVisible( ime_windows.ime_ui_hwnd ) );
5528 SetLastError( 0xdeadbeef );
5529 ok_ret( 1, ImmSetActiveContext( hwnd, default_himc, TRUE ) );
5530 ok_seq( activate_0_seq );
5531 ok_ret( 0, GetLastError() );
5532 ok_ret( 0, IsWindowVisible( ime_windows.ime_ui_hwnd ) );
5533 ok_ret( 1, ImmSetActiveContext( hwnd, default_himc, TRUE ) );
5534 ok_seq( activate_0_seq );
5535 ok_ret( 1, ImmSetActiveContext( hwnd, default_himc, FALSE ) );
5536 ok_seq( deactivate_0_seq );
5538 himc = ImmCreateContext();
5539 ok_ne( NULL, himc, HIMC, "%p" );
5540 ctx = ImmLockIMC( himc );
5541 ok_ne( NULL, ctx, INPUTCONTEXT *, "%p" );
5542 ok_eq( 0, ctx->hWnd, HWND, "%p" );
5544 ok_ret( 1, ImmSetActiveContext( hwnd, himc, FALSE ) );
5545 deactivate_1_seq[3].himc = himc;
5546 deactivate_1_seq[4].himc = himc;
5547 ok_seq( deactivate_1_seq );
5548 ok_ret( 1, ImmSetActiveContext( hwnd, himc, TRUE ) );
5549 activate_1_seq[0].himc = himc;
5550 ok_seq( activate_1_seq );
5552 ctx->hWnd = (HWND)0xdeadbeef;
5553 ok_ret( 1, ImmSetActiveContext( hwnd, himc, FALSE ) );
5554 ok_eq( (HWND)0xdeadbeef, ctx->hWnd, HWND, "%p" );
5555 deactivate_2_seq[0].himc = himc;
5556 ok_seq( deactivate_2_seq );
5558 ctx->hWnd = 0;
5559 ok_ret( 1, ImmSetActiveContext( hwnd, himc, TRUE ) );
5560 ok_eq( hwnd, ctx->hWnd, HWND, "%p" );
5561 activate_1_seq[0].himc = himc;
5562 ok_seq( activate_1_seq );
5564 ok_eq( default_himc, (HIMC)GetWindowLongPtrW( ime_windows.ime_ui_hwnd, IMMGWL_IMC ), HIMC, "%p" );
5565 ok_ret( 1, ImmSetActiveContext( hwnd, himc, TRUE ) );
5566 ok_ret( 0, IsWindowVisible( ime_windows.ime_ui_hwnd ) );
5567 ok_eq( default_himc, (HIMC)GetWindowLongPtrW( ime_windows.ime_ui_hwnd, IMMGWL_IMC ), HIMC, "%p" );
5569 ctx->hWnd = 0;
5570 ok_eq( default_himc, ImmAssociateContext( hwnd, himc ), HIMC, "%p" );
5571 ok_eq( himc, (HIMC)GetWindowLongPtrW( ime_windows.ime_ui_hwnd, IMMGWL_IMC ), HIMC, "%p" );
5572 ok_ret( 0, IsWindowVisible( ime_windows.ime_ui_hwnd ) );
5573 ok_eq( hwnd, ctx->hWnd, HWND, "%p" );
5575 ctx->hWnd = (HWND)0xdeadbeef;
5576 ok_eq( himc, ImmGetContext( hwnd ), HIMC, "%p" );
5577 ok_eq( (HWND)0xdeadbeef, ctx->hWnd, HWND, "%p" );
5578 ok_ret( 1, ImmReleaseContext( hwnd, himc ) );
5580 ok_ret( 1, ImmUnlockIMC( himc ) );
5581 ok_ret( 1, ImmDestroyContext( himc ) );
5583 ok_ret( 1, ImmActivateLayout( default_hkl ) );
5584 ok_ret( 1, DestroyWindow( hwnd ) );
5585 process_messages();
5587 ok_ret( 1, ImmFreeLayout( hkl ) );
5588 memset( ime_calls, 0, sizeof(ime_calls) );
5589 ime_call_count = 0;
5592 static void test_ImmRequestMessage(void)
5594 struct ime_call composition_window_seq[] =
5597 .hkl = expect_ime, .himc = default_himc,
5598 .func = MSG_TEST_WIN, .message = {.msg = WM_IME_REQUEST, .wparam = IMR_COMPOSITIONWINDOW, .lparam = 0/*&comp_form*/}
5600 {0},
5602 struct ime_call candidate_window_seq[] =
5605 .hkl = expect_ime, .himc = default_himc,
5606 .func = MSG_TEST_WIN, .message = {.msg = WM_IME_REQUEST, .wparam = IMR_CANDIDATEWINDOW, .lparam = 0/*&cand_form*/}
5608 {0},
5610 struct ime_call composition_font_seq[] =
5613 .hkl = expect_ime, .himc = default_himc,
5614 .func = MSG_TEST_WIN, .message = {.msg = WM_IME_REQUEST, .wparam = IMR_COMPOSITIONFONT, .lparam = 0/*&log_font*/}
5616 {0},
5618 struct ime_call reconvert_string_seq[] =
5621 .hkl = expect_ime, .himc = default_himc,
5622 .func = MSG_TEST_WIN, .message = {.msg = WM_IME_REQUEST, .wparam = IMR_RECONVERTSTRING, .lparam = 0/*&reconv*/}
5624 {0},
5626 struct ime_call confirm_reconvert_string_seq[] =
5629 .hkl = expect_ime, .himc = default_himc,
5630 .func = MSG_TEST_WIN, .message = {.msg = WM_IME_REQUEST, .wparam = IMR_CONFIRMRECONVERTSTRING, .lparam = 0/*&reconv*/}
5632 {0},
5634 struct ime_call query_char_position_seq[] =
5637 .hkl = expect_ime, .himc = default_himc,
5638 .func = MSG_TEST_WIN, .message = {.msg = WM_IME_REQUEST, .wparam = IMR_QUERYCHARPOSITION, .lparam = 0/*&char_pos*/}
5640 {0},
5642 struct ime_call document_feed_seq[] =
5645 .hkl = expect_ime, .himc = default_himc,
5646 .func = MSG_TEST_WIN, .message = {.msg = WM_IME_REQUEST, .wparam = IMR_DOCUMENTFEED, .lparam = 0/*&reconv*/}
5648 {0},
5650 HKL hkl;
5651 COMPOSITIONFORM comp_form = {0};
5652 IMECHARPOSITION char_pos = {0};
5653 RECONVERTSTRING reconv = {0};
5654 CANDIDATEFORM cand_form = {0};
5655 LOGFONTW log_font = {0};
5656 INPUTCONTEXT *ctx;
5657 HIMC himc;
5659 if (!(hkl = wineime_hkl)) return;
5661 hwnd = CreateWindowW( test_class.lpszClassName, NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
5662 100, 100, 100, 100, NULL, NULL, NULL, NULL );
5663 ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() );
5665 ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE;
5667 ok_ret( 1, ImmActivateLayout( hkl ) );
5668 ok_ret( 1, ImmLoadIME( hkl ) );
5669 himc = ImmCreateContext();
5670 ok_ne( NULL, himc, HIMC, "%p" );
5671 ctx = ImmLockIMC( himc );
5672 ok_ne( NULL, ctx, INPUTCONTEXT *, "%p" );
5673 process_messages();
5674 memset( ime_calls, 0, sizeof(ime_calls) );
5675 ime_call_count = 0;
5677 ok_ret( 0, ImmRequestMessageW( default_himc, 0xdeadbeef, 0 ) );
5678 todo_wine ok_seq( empty_sequence );
5679 ok_ret( 0, ImmRequestMessageW( default_himc, IMR_COMPOSITIONWINDOW, (LPARAM)&comp_form ) );
5680 composition_window_seq[0].message.lparam = (LPARAM)&comp_form;
5681 ok_seq( composition_window_seq );
5682 ok_ret( 0, ImmRequestMessageW( default_himc, IMR_CANDIDATEWINDOW, (LPARAM)&cand_form ) );
5683 candidate_window_seq[0].message.lparam = (LPARAM)&cand_form;
5684 ok_seq( candidate_window_seq );
5685 ok_ret( 0, ImmRequestMessageW( default_himc, IMR_COMPOSITIONFONT, (LPARAM)&log_font ) );
5686 composition_font_seq[0].message.lparam = (LPARAM)&log_font;
5687 ok_seq( composition_font_seq );
5688 ok_ret( 0, ImmRequestMessageW( default_himc, IMR_RECONVERTSTRING, (LPARAM)&reconv ) );
5689 todo_wine ok_seq( empty_sequence );
5690 reconv.dwSize = sizeof(RECONVERTSTRING);
5691 ok_ret( 0, ImmRequestMessageW( default_himc, IMR_RECONVERTSTRING, (LPARAM)&reconv ) );
5692 reconvert_string_seq[0].message.lparam = (LPARAM)&reconv;
5693 ok_seq( reconvert_string_seq );
5694 ok_ret( 0, ImmRequestMessageW( default_himc, IMR_CONFIRMRECONVERTSTRING, (LPARAM)&reconv ) );
5695 confirm_reconvert_string_seq[0].message.lparam = (LPARAM)&reconv;
5696 ok_seq( confirm_reconvert_string_seq );
5697 ok_ret( 0, ImmRequestMessageW( default_himc, IMR_QUERYCHARPOSITION, (LPARAM)&char_pos ) );
5698 query_char_position_seq[0].message.lparam = (LPARAM)&char_pos;
5699 ok_seq( query_char_position_seq );
5700 ok_ret( 0, ImmRequestMessageW( default_himc, IMR_DOCUMENTFEED, (LPARAM)&reconv ) );
5701 document_feed_seq[0].message.lparam = (LPARAM)&reconv;
5702 ok_seq( document_feed_seq );
5704 ok_ret( 0, ImmRequestMessageW( himc, IMR_CANDIDATEWINDOW, (LPARAM)&cand_form ) );
5705 ok_seq( empty_sequence );
5706 ok_ret( 1, ImmSetActiveContext( hwnd, himc, TRUE ) );
5707 memset( ime_calls, 0, sizeof(ime_calls) );
5708 ime_call_count = 0;
5709 ok_ret( 0, ImmRequestMessageW( himc, IMR_CANDIDATEWINDOW, (LPARAM)&cand_form ) );
5710 candidate_window_seq[0].message.lparam = (LPARAM)&cand_form;
5711 ok_seq( candidate_window_seq );
5712 ok_ret( 0, ImmRequestMessageW( default_himc, IMR_CANDIDATEWINDOW, (LPARAM)&cand_form ) );
5713 ok_seq( candidate_window_seq );
5715 ok_ret( 1, ImmUnlockIMC( himc ) );
5716 ok_ret( 1, ImmDestroyContext( himc ) );
5718 ok_ret( 1, ImmActivateLayout( default_hkl ) );
5719 ok_ret( 1, DestroyWindow( hwnd ) );
5720 process_messages();
5722 ok_ret( 1, ImmFreeLayout( hkl ) );
5723 memset( ime_calls, 0, sizeof(ime_calls) );
5724 ime_call_count = 0;
5727 static void test_ImmGetCandidateList( BOOL unicode )
5729 char buffer[512], expect_bufW[512] = {0}, expect_bufA[512] = {0};
5730 CANDIDATELIST *cand_list = (CANDIDATELIST *)buffer, *expect_listW, *expect_listA;
5731 HKL hkl;
5732 CANDIDATEINFO *cand_info;
5733 INPUTCONTEXT *ctx;
5734 HIMC himc;
5736 expect_listW = (CANDIDATELIST *)expect_bufW;
5737 expect_listW->dwSize = offsetof(CANDIDATELIST, dwOffset[2]) + 32 * sizeof(WCHAR);
5738 expect_listW->dwStyle = 0xdeadbeef;
5739 expect_listW->dwCount = 2;
5740 expect_listW->dwSelection = 3;
5741 expect_listW->dwPageStart = 4;
5742 expect_listW->dwPageSize = 5;
5743 expect_listW->dwOffset[0] = offsetof(CANDIDATELIST, dwOffset[2]) + 2 * sizeof(WCHAR);
5744 expect_listW->dwOffset[1] = offsetof(CANDIDATELIST, dwOffset[2]) + 16 * sizeof(WCHAR);
5745 wcscpy( (WCHAR *)(expect_bufW + expect_listW->dwOffset[0]), L"Candidate 1" );
5746 wcscpy( (WCHAR *)(expect_bufW + expect_listW->dwOffset[1]), L"Candidate 2" );
5748 expect_listA = (CANDIDATELIST *)expect_bufA;
5749 expect_listA->dwSize = offsetof(CANDIDATELIST, dwOffset[2]) + 32;
5750 expect_listA->dwStyle = 0xdeadbeef;
5751 expect_listA->dwCount = 2;
5752 expect_listA->dwSelection = 3;
5753 expect_listA->dwPageStart = 4;
5754 expect_listA->dwPageSize = 5;
5755 expect_listA->dwOffset[0] = offsetof(CANDIDATELIST, dwOffset[2]) + 2;
5756 expect_listA->dwOffset[1] = offsetof(CANDIDATELIST, dwOffset[2]) + 16;
5757 strcpy( (char *)(expect_bufA + expect_listA->dwOffset[0]), "Candidate 1" );
5758 strcpy( (char *)(expect_bufA + expect_listA->dwOffset[1]), "Candidate 2" );
5760 winetest_push_context( unicode ? "unicode" : "ansi" );
5762 /* IME_PROP_END_UNLOAD for the IME to unload / reload. */
5763 ime_info.fdwProperty = IME_PROP_END_UNLOAD;
5764 if (unicode) ime_info.fdwProperty |= IME_PROP_UNICODE;
5766 if (!(hkl = wineime_hkl)) goto cleanup;
5768 hwnd = CreateWindowW( test_class.lpszClassName, NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
5769 100, 100, 100, 100, NULL, NULL, NULL, NULL );
5770 ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() );
5772 ok_ret( 1, ImmActivateLayout( hkl ) );
5773 ok_ret( 1, ImmLoadIME( hkl ) );
5774 himc = ImmCreateContext();
5775 ok_ne( NULL, himc, HIMC, "%p" );
5776 ctx = ImmLockIMC( himc );
5777 ok_ne( NULL, ctx, INPUTCONTEXT *, "%p" );
5778 process_messages();
5779 memset( ime_calls, 0, sizeof(ime_calls) );
5780 ime_call_count = 0;
5782 ok_ret( 0, ImmGetCandidateListW( default_himc, 0, NULL, 0 ) );
5783 ok_seq( empty_sequence );
5784 ok_ret( 0, ImmGetCandidateListW( default_himc, 1, NULL, 0 ) );
5785 ok_seq( empty_sequence );
5786 ok_ret( 0, ImmGetCandidateListW( default_himc, 0, cand_list, sizeof(buffer) ) );
5787 ok_seq( empty_sequence );
5789 ok_ret( 0, ImmGetCandidateListW( himc, 0, cand_list, sizeof(buffer) ) );
5790 ok_seq( empty_sequence );
5792 todo_wine ok_seq( empty_sequence );
5793 ctx->hCandInfo = ImmReSizeIMCC( ctx->hCandInfo, sizeof(*cand_info) + sizeof(buffer) );
5794 ok( !!ctx->hCandInfo, "ImmReSizeIMCC failed, error %lu\n", GetLastError() );
5796 cand_info = ImmLockIMCC( ctx->hCandInfo );
5797 ok( !!cand_info, "ImmLockIMCC failed, error %lu\n", GetLastError() );
5798 cand_info->dwCount = 1;
5799 cand_info->dwOffset[0] = sizeof(*cand_info);
5800 if (unicode) memcpy( cand_info + 1, expect_bufW, sizeof(expect_bufW) );
5801 else memcpy( cand_info + 1, expect_bufA, sizeof(expect_bufA) );
5802 ok_ret( 0, ImmUnlockIMCC( ctx->hCandInfo ) );
5804 ok_ret( (unicode ? 96 : 80), ImmGetCandidateListW( himc, 0, NULL, 0 ) );
5805 ok_seq( empty_sequence );
5806 ok_ret( 0, ImmGetCandidateListW( himc, 1, NULL, 0 ) );
5807 ok_seq( empty_sequence );
5808 memset( buffer, 0xcd, sizeof(buffer) );
5809 ok_ret( (unicode ? 96 : 80), ImmGetCandidateListW( himc, 0, cand_list, sizeof(buffer) ) );
5810 ok_seq( empty_sequence );
5812 if (!unicode)
5814 expect_listW->dwSize = offsetof(CANDIDATELIST, dwOffset[2]) + 24 * sizeof(WCHAR);
5815 expect_listW->dwOffset[0] = offsetof(CANDIDATELIST, dwOffset[2]);
5816 expect_listW->dwOffset[1] = offsetof(CANDIDATELIST, dwOffset[2]) + 12 * sizeof(WCHAR);
5817 wcscpy( (WCHAR *)(expect_bufW + expect_listW->dwOffset[0]), L"Candidate 1" );
5818 wcscpy( (WCHAR *)(expect_bufW + expect_listW->dwOffset[1]), L"Candidate 2" );
5820 check_candidate_list_( __LINE__, cand_list, expect_listW, TRUE );
5822 ok_ret( (unicode ? 56 : 64), ImmGetCandidateListA( himc, 0, NULL, 0 ) );
5823 ok_seq( empty_sequence );
5824 ok_ret( 0, ImmGetCandidateListA( himc, 1, NULL, 0 ) );
5825 ok_seq( empty_sequence );
5826 memset( buffer, 0xcd, sizeof(buffer) );
5827 ok_ret( (unicode ? 56 : 64), ImmGetCandidateListA( himc, 0, cand_list, sizeof(buffer) ) );
5828 ok_seq( empty_sequence );
5830 if (unicode)
5832 expect_listA->dwSize = offsetof(CANDIDATELIST, dwOffset[2]) + 24;
5833 expect_listA->dwOffset[0] = offsetof(CANDIDATELIST, dwOffset[2]);
5834 expect_listA->dwOffset[1] = offsetof(CANDIDATELIST, dwOffset[2]) + 12;
5835 strcpy( (char *)(expect_bufA + expect_listA->dwOffset[0]), "Candidate 1" );
5836 strcpy( (char *)(expect_bufA + expect_listA->dwOffset[1]), "Candidate 2" );
5838 check_candidate_list_( __LINE__, cand_list, expect_listA, FALSE );
5840 ok_ret( 1, ImmUnlockIMC( himc ) );
5841 ok_ret( 1, ImmDestroyContext( himc ) );
5843 ok_ret( 1, ImmActivateLayout( default_hkl ) );
5844 ok_ret( 1, DestroyWindow( hwnd ) );
5845 process_messages();
5847 ok_ret( 1, ImmFreeLayout( hkl ) );
5848 memset( ime_calls, 0, sizeof(ime_calls) );
5849 ime_call_count = 0;
5851 cleanup:
5852 winetest_pop_context();
5855 static void test_ImmGetCandidateListCount( BOOL unicode )
5857 HKL hkl;
5858 CANDIDATEINFO *cand_info;
5859 INPUTCONTEXT *ctx;
5860 DWORD count;
5861 HIMC himc;
5863 winetest_push_context( unicode ? "unicode" : "ansi" );
5865 /* IME_PROP_END_UNLOAD for the IME to unload / reload. */
5866 ime_info.fdwProperty = IME_PROP_END_UNLOAD;
5867 if (unicode) ime_info.fdwProperty |= IME_PROP_UNICODE;
5869 if (!(hkl = wineime_hkl)) goto cleanup;
5871 hwnd = CreateWindowW( test_class.lpszClassName, NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
5872 100, 100, 100, 100, NULL, NULL, NULL, NULL );
5873 ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() );
5875 ok_ret( 1, ImmActivateLayout( hkl ) );
5876 ok_ret( 1, ImmLoadIME( hkl ) );
5877 himc = ImmCreateContext();
5878 ok_ne( NULL, himc, HIMC, "%p" );
5879 ctx = ImmLockIMC( himc );
5880 ok_ne( NULL, ctx, INPUTCONTEXT *, "%p" );
5881 process_messages();
5882 memset( ime_calls, 0, sizeof(ime_calls) );
5883 ime_call_count = 0;
5885 ok_ret( 144, ImmGetCandidateListCountW( default_himc, &count ) );
5886 ok_eq( 0, count, UINT, "%u" );
5887 ok_seq( empty_sequence );
5888 ok_ret( 144, ImmGetCandidateListCountA( default_himc, &count ) );
5889 ok_eq( 0, count, UINT, "%u" );
5890 ok_seq( empty_sequence );
5892 ok_ret( 144, ImmGetCandidateListCountW( himc, &count ) );
5893 ok_eq( 0, count, UINT, "%u" );
5894 ok_seq( empty_sequence );
5895 ok_ret( 144, ImmGetCandidateListCountA( himc, &count ) );
5896 ok_eq( 0, count, UINT, "%u" );
5897 ok_seq( empty_sequence );
5899 cand_info = ImmLockIMCC( ctx->hCandInfo );
5900 ok( !!cand_info, "ImmLockIMCC failed, error %lu\n", GetLastError() );
5901 cand_info->dwCount = 1;
5902 ok_ret( 0, ImmUnlockIMCC( ctx->hCandInfo ) );
5904 todo_wine_if(!unicode)
5905 ok_ret( (unicode ? 144 : 172), ImmGetCandidateListCountW( himc, &count ) );
5906 ok_eq( 1, count, UINT, "%u" );
5907 ok_seq( empty_sequence );
5908 todo_wine_if(unicode)
5909 ok_ret( (unicode ? 172 : 144), ImmGetCandidateListCountA( himc, &count ) );
5910 ok_eq( 1, count, UINT, "%u" );
5911 ok_seq( empty_sequence );
5913 ok_ret( 1, ImmUnlockIMC( himc ) );
5914 ok_ret( 1, ImmDestroyContext( himc ) );
5916 ok_ret( 1, ImmActivateLayout( default_hkl ) );
5917 ok_ret( 1, DestroyWindow( hwnd ) );
5918 process_messages();
5920 ok_ret( 1, ImmFreeLayout( hkl ) );
5921 memset( ime_calls, 0, sizeof(ime_calls) );
5922 ime_call_count = 0;
5924 cleanup:
5925 winetest_pop_context();
5928 static void test_ImmGetCandidateWindow(void)
5930 HKL hkl;
5931 const CANDIDATEFORM expect_form =
5933 .dwIndex = 0xdeadbeef,
5934 .dwStyle = 0xfeedcafe,
5935 .ptCurrentPos = {.x = 123, .y = 456},
5936 .rcArea = {.left = 1, .top = 2, .right = 3, .bottom = 4},
5938 CANDIDATEFORM cand_form;
5939 INPUTCONTEXT *ctx;
5940 HIMC himc;
5942 ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE;
5944 if (!(hkl = wineime_hkl)) return;
5946 hwnd = CreateWindowW( test_class.lpszClassName, NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
5947 100, 100, 100, 100, NULL, NULL, NULL, NULL );
5948 ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() );
5950 ok_ret( 1, ImmActivateLayout( hkl ) );
5951 ok_ret( 1, ImmLoadIME( hkl ) );
5952 himc = ImmCreateContext();
5953 ok_ne( NULL, himc, HIMC, "%p" );
5954 ctx = ImmLockIMC( himc );
5955 ok_ne( NULL, ctx, INPUTCONTEXT *, "%p" );
5956 process_messages();
5957 memset( ime_calls, 0, sizeof(ime_calls) );
5958 ime_call_count = 0;
5960 memset( &cand_form, 0xcd, sizeof(cand_form) );
5961 ok_ret( 0, ImmGetCandidateWindow( default_himc, 0, &cand_form ) );
5962 ok_eq( 0xcdcdcdcd, cand_form.dwIndex, UINT, "%u" );
5963 ok_ret( 0, ImmGetCandidateWindow( default_himc, 1, &cand_form ) );
5964 ok_eq( 0xcdcdcdcd, cand_form.dwIndex, UINT, "%u" );
5965 ok_ret( 0, ImmGetCandidateWindow( default_himc, 2, &cand_form ) );
5966 ok_eq( 0xcdcdcdcd, cand_form.dwIndex, UINT, "%u" );
5967 ok_ret( 0, ImmGetCandidateWindow( default_himc, 3, &cand_form ) );
5968 ok_eq( 0xcdcdcdcd, cand_form.dwIndex, UINT, "%u" );
5969 ok_ret( 1, ImmGetCandidateWindow( default_himc, 4, &cand_form ) );
5970 ok_seq( empty_sequence );
5972 ok_ret( 0, ImmGetCandidateWindow( himc, 0, &cand_form ) );
5973 ok_seq( empty_sequence );
5975 ok_seq( empty_sequence );
5976 ok( !!ctx, "ImmLockIMC failed, error %lu\n", GetLastError() );
5977 ctx->cfCandForm[0] = expect_form;
5979 ok_ret( 1, ImmGetCandidateWindow( himc, 0, &cand_form ) );
5980 check_candidate_form( &cand_form, &expect_form );
5981 ok_seq( empty_sequence );
5983 ok_seq( empty_sequence );
5984 ok( !!ctx, "ImmLockIMC failed, error %lu\n", GetLastError() );
5985 ctx->cfCandForm[0].dwIndex = -1;
5987 ok_ret( 0, ImmGetCandidateWindow( himc, 0, &cand_form ) );
5988 ok_seq( empty_sequence );
5990 ok_ret( 1, ImmUnlockIMC( himc ) );
5991 ok_ret( 1, ImmDestroyContext( himc ) );
5993 ok_ret( 1, ImmActivateLayout( default_hkl ) );
5994 ok_ret( 1, DestroyWindow( hwnd ) );
5995 process_messages();
5997 ok_ret( 1, ImmFreeLayout( hkl ) );
5998 memset( ime_calls, 0, sizeof(ime_calls) );
5999 ime_call_count = 0;
6002 static void test_ImmGetCompositionString( BOOL unicode )
6004 static COMPOSITIONSTRING expect_string_empty = {.dwSize = sizeof(COMPOSITIONSTRING)};
6005 static COMPOSITIONSTRING expect_stringA =
6007 .dwSize = 176,
6008 .dwCompReadAttrLen = 8,
6009 .dwCompReadAttrOffset = 116,
6010 .dwCompReadClauseLen = 8,
6011 .dwCompReadClauseOffset = 108,
6012 .dwCompReadStrLen = 8,
6013 .dwCompReadStrOffset = 100,
6014 .dwCompAttrLen = 4,
6015 .dwCompAttrOffset = 136,
6016 .dwCompClauseLen = 8,
6017 .dwCompClauseOffset = 128,
6018 .dwCompStrLen = 4,
6019 .dwCompStrOffset = 124,
6020 .dwCursorPos = 3,
6021 .dwDeltaStart = 1,
6022 .dwResultReadClauseLen = 8,
6023 .dwResultReadClauseOffset = 150,
6024 .dwResultReadStrLen = 10,
6025 .dwResultReadStrOffset = 140,
6026 .dwResultClauseLen = 8,
6027 .dwResultClauseOffset = 164,
6028 .dwResultStrLen = 6,
6029 .dwResultStrOffset = 158,
6030 .dwPrivateSize = 4,
6031 .dwPrivateOffset = 172,
6033 static const COMPOSITIONSTRING expect_stringW =
6035 .dwSize = 204,
6036 .dwCompReadAttrLen = 8,
6037 .dwCompReadAttrOffset = 124,
6038 .dwCompReadClauseLen = 8,
6039 .dwCompReadClauseOffset = 116,
6040 .dwCompReadStrLen = 8,
6041 .dwCompReadStrOffset = 100,
6042 .dwCompAttrLen = 4,
6043 .dwCompAttrOffset = 148,
6044 .dwCompClauseLen = 8,
6045 .dwCompClauseOffset = 140,
6046 .dwCompStrLen = 4,
6047 .dwCompStrOffset = 132,
6048 .dwCursorPos = 3,
6049 .dwDeltaStart = 1,
6050 .dwResultReadClauseLen = 8,
6051 .dwResultReadClauseOffset = 172,
6052 .dwResultReadStrLen = 10,
6053 .dwResultReadStrOffset = 152,
6054 .dwResultClauseLen = 8,
6055 .dwResultClauseOffset = 192,
6056 .dwResultStrLen = 6,
6057 .dwResultStrOffset = 180,
6058 .dwPrivateSize = 4,
6059 .dwPrivateOffset = 200,
6061 static const UINT gcs_indexes[] =
6063 GCS_COMPREADSTR,
6064 GCS_COMPREADATTR,
6065 GCS_COMPREADCLAUSE,
6066 GCS_COMPSTR,
6067 GCS_COMPATTR,
6068 GCS_COMPCLAUSE,
6069 GCS_CURSORPOS,
6070 GCS_DELTASTART,
6071 GCS_RESULTREADSTR,
6072 GCS_RESULTREADCLAUSE,
6073 GCS_RESULTSTR,
6074 GCS_RESULTCLAUSE,
6076 static const UINT scs_indexes[] =
6078 SCS_SETSTR,
6079 SCS_CHANGEATTR,
6080 SCS_CHANGECLAUSE,
6082 static const UINT expect_retW[ARRAY_SIZE(gcs_indexes)] = {16, 8, 8, 8, 4, 8, 3, 1, 20, 8, 12, 8};
6083 static const UINT expect_retA[ARRAY_SIZE(gcs_indexes)] = {8, 8, 8, 4, 4, 8, 3, 1, 10, 8, 6, 8};
6084 HKL hkl;
6085 COMPOSITIONSTRING *string;
6086 char buffer[1024];
6087 INPUTCONTEXT *old_ctx, *ctx;
6088 const void *str;
6089 HIMCC old_himcc;
6090 UINT i, len;
6091 BYTE *dst;
6092 HIMC himc;
6094 SET_ENABLE( ImeSetCompositionString, TRUE );
6096 winetest_push_context( unicode ? "unicode" : "ansi" );
6098 /* IME_PROP_END_UNLOAD for the IME to unload / reload. */
6099 ime_info.fdwProperty = IME_PROP_END_UNLOAD;
6100 if (unicode) ime_info.fdwProperty |= IME_PROP_UNICODE;
6102 if (!(hkl = wineime_hkl)) goto cleanup;
6104 hwnd = CreateWindowW( test_class.lpszClassName, NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
6105 100, 100, 100, 100, NULL, NULL, NULL, NULL );
6106 ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() );
6108 ok_ret( 1, ImmActivateLayout( hkl ) );
6109 ok_ret( 1, ImmLoadIME( hkl ) );
6110 himc = ImmCreateContext();
6111 ok_ne( NULL, himc, HIMC, "%p" );
6112 ctx = ImmLockIMC( himc );
6113 ok_ne( NULL, ctx, INPUTCONTEXT *, "%p" );
6114 process_messages();
6115 memset( ime_calls, 0, sizeof(ime_calls) );
6116 ime_call_count = 0;
6118 memset( buffer, 0xcd, sizeof(buffer) );
6119 todo_wine ok_ret( -2, ImmGetCompositionStringW( default_himc, GCS_COMPSTR | GCS_COMPATTR, buffer, sizeof(buffer) ) );
6120 memset( buffer, 0xcd, sizeof(buffer) );
6121 todo_wine ok_ret( -2, ImmGetCompositionStringA( default_himc, GCS_COMPSTR | GCS_COMPATTR, buffer, sizeof(buffer) ) );
6123 for (i = 0; i < ARRAY_SIZE(gcs_indexes); ++i)
6125 memset( buffer, 0xcd, sizeof(buffer) );
6126 ok_ret( 0, ImmGetCompositionStringW( default_himc, gcs_indexes[i], buffer, sizeof(buffer) ) );
6127 memset( buffer, 0xcd, sizeof(buffer) );
6128 ok_ret( 0, ImmGetCompositionStringA( default_himc, gcs_indexes[i], buffer, sizeof(buffer) ) );
6130 memset( buffer, 0xcd, sizeof(buffer) );
6131 ok_ret( 0, ImmGetCompositionStringW( himc, gcs_indexes[i], buffer, sizeof(buffer) ) );
6132 memset( buffer, 0xcd, sizeof(buffer) );
6133 ok_ret( 0, ImmGetCompositionStringA( himc, gcs_indexes[i], buffer, sizeof(buffer) ) );
6136 ctx->hCompStr = ImmReSizeIMCC( ctx->hCompStr, unicode ? expect_stringW.dwSize : expect_stringA.dwSize );
6137 string = ImmLockIMCC( ctx->hCompStr );
6138 ok( !!string, "ImmLockIMCC failed, error %lu\n", GetLastError() );
6139 check_composition_string( string, &expect_string_empty );
6141 string->dwCursorPos = 3;
6142 string->dwDeltaStart = 1;
6144 if (unicode) str = L"ReadComp";
6145 else str = "ReadComp";
6146 len = unicode ? wcslen( str ) : strlen( str );
6148 string->dwCompReadStrLen = len;
6149 string->dwCompReadStrOffset = string->dwSize;
6150 dst = (BYTE *)string + string->dwCompReadStrOffset;
6151 memcpy( dst, str, len * (unicode ? sizeof(WCHAR) : 1) );
6152 string->dwSize += len * (unicode ? sizeof(WCHAR) : 1);
6154 string->dwCompReadClauseLen = 2 * sizeof(DWORD);
6155 string->dwCompReadClauseOffset = string->dwSize;
6156 dst = (BYTE *)string + string->dwCompReadClauseOffset;
6157 *(DWORD *)(dst + 0 * sizeof(DWORD)) = 0;
6158 *(DWORD *)(dst + 1 * sizeof(DWORD)) = len;
6159 string->dwSize += 2 * sizeof(DWORD);
6161 string->dwCompReadAttrLen = len;
6162 string->dwCompReadAttrOffset = string->dwSize;
6163 dst = (BYTE *)string + string->dwCompReadAttrOffset;
6164 memset( dst, ATTR_INPUT, len );
6165 string->dwSize += len;
6167 if (unicode) str = L"Comp";
6168 else str = "Comp";
6169 len = unicode ? wcslen( str ) : strlen( str );
6171 string->dwCompStrLen = len;
6172 string->dwCompStrOffset = string->dwSize;
6173 dst = (BYTE *)string + string->dwCompStrOffset;
6174 memcpy( dst, str, len * (unicode ? sizeof(WCHAR) : 1) );
6175 string->dwSize += len * (unicode ? sizeof(WCHAR) : 1);
6177 string->dwCompClauseLen = 2 * sizeof(DWORD);
6178 string->dwCompClauseOffset = string->dwSize;
6179 dst = (BYTE *)string + string->dwCompClauseOffset;
6180 *(DWORD *)(dst + 0 * sizeof(DWORD)) = 0;
6181 *(DWORD *)(dst + 1 * sizeof(DWORD)) = len;
6182 string->dwSize += 2 * sizeof(DWORD);
6184 string->dwCompAttrLen = len;
6185 string->dwCompAttrOffset = string->dwSize;
6186 dst = (BYTE *)string + string->dwCompAttrOffset;
6187 memset( dst, ATTR_INPUT, len );
6188 string->dwSize += len;
6190 if (unicode) str = L"ReadResult";
6191 else str = "ReadResult";
6192 len = unicode ? wcslen( str ) : strlen( str );
6194 string->dwResultReadStrLen = len;
6195 string->dwResultReadStrOffset = string->dwSize;
6196 dst = (BYTE *)string + string->dwResultReadStrOffset;
6197 memcpy( dst, str, len * (unicode ? sizeof(WCHAR) : 1) );
6198 string->dwSize += len * (unicode ? sizeof(WCHAR) : 1);
6200 string->dwResultReadClauseLen = 2 * sizeof(DWORD);
6201 string->dwResultReadClauseOffset = string->dwSize;
6202 dst = (BYTE *)string + string->dwResultReadClauseOffset;
6203 *(DWORD *)(dst + 0 * sizeof(DWORD)) = 0;
6204 *(DWORD *)(dst + 1 * sizeof(DWORD)) = len;
6205 string->dwSize += 2 * sizeof(DWORD);
6207 if (unicode) str = L"Result";
6208 else str = "Result";
6209 len = unicode ? wcslen( str ) : strlen( str );
6211 string->dwResultStrLen = len;
6212 string->dwResultStrOffset = string->dwSize;
6213 dst = (BYTE *)string + string->dwResultStrOffset;
6214 memcpy( dst, str, len * (unicode ? sizeof(WCHAR) : 1) );
6215 string->dwSize += len * (unicode ? sizeof(WCHAR) : 1);
6217 string->dwResultClauseLen = 2 * sizeof(DWORD);
6218 string->dwResultClauseOffset = string->dwSize;
6219 dst = (BYTE *)string + string->dwResultClauseOffset;
6220 *(DWORD *)(dst + 0 * sizeof(DWORD)) = 0;
6221 *(DWORD *)(dst + 1 * sizeof(DWORD)) = len;
6222 string->dwSize += 2 * sizeof(DWORD);
6224 string->dwPrivateSize = 4;
6225 string->dwPrivateOffset = string->dwSize;
6226 dst = (BYTE *)string + string->dwPrivateOffset;
6227 memset( dst, 0xa5, string->dwPrivateSize );
6228 string->dwSize += 4;
6230 check_composition_string( string, unicode ? &expect_stringW : &expect_stringA );
6231 ok_ret( 0, ImmUnlockIMCC( ctx->hCompStr ) );
6232 old_himcc = ctx->hCompStr;
6234 for (i = 0; i < ARRAY_SIZE(gcs_indexes); ++i)
6236 UINT_PTR expect;
6238 winetest_push_context( "%u", i );
6240 memset( buffer, 0xcd, sizeof(buffer) );
6241 expect = expect_retW[i];
6242 ok_ret( expect, ImmGetCompositionStringW( himc, gcs_indexes[i], buffer, sizeof(buffer) ) );
6243 memset( buffer + expect, 0, 4 );
6245 if (i == 0) ok_wcs( L"ReadComp", (WCHAR *)buffer );
6246 else if (i == 3) ok_wcs( L"Comp", (WCHAR *)buffer );
6247 else if (i == 8) ok_wcs( L"ReadResult", (WCHAR *)buffer );
6248 else if (i == 10) ok_wcs( L"Result", (WCHAR *)buffer );
6249 else if (i != 6 && i != 7) ok_wcs( L"", (WCHAR *)buffer );
6251 memset( buffer, 0xcd, sizeof(buffer) );
6252 expect = expect_retA[i];
6253 ok_ret( expect, ImmGetCompositionStringA( himc, gcs_indexes[i], buffer, sizeof(buffer) ) );
6254 memset( buffer + expect, 0, 4 );
6256 if (i == 0) ok_str( "ReadComp", (char *)buffer );
6257 else if (i == 3) ok_str( "Comp", (char *)buffer );
6258 else if (i == 8) ok_str( "ReadResult", (char *)buffer );
6259 else if (i == 10) ok_str( "Result", (char *)buffer );
6260 else if (i != 6 && i != 7) ok_str( "", (char *)buffer );
6262 winetest_pop_context();
6265 for (i = 0; i < ARRAY_SIZE(gcs_indexes); ++i)
6267 winetest_push_context( "%u", i );
6268 ok_ret( 0, ImmSetCompositionStringW( himc, gcs_indexes[i], buffer, sizeof(buffer), NULL, 0 ) );
6269 ok_ret( 0, ImmSetCompositionStringA( himc, gcs_indexes[i], buffer, sizeof(buffer), NULL, 0 ) );
6270 winetest_pop_context();
6272 ok_ret( 0, ImmSetCompositionStringW( himc, SCS_SETSTR | SCS_CHANGEATTR, buffer, sizeof(buffer), NULL, 0 ) );
6273 ok_ret( 0, ImmSetCompositionStringA( himc, SCS_SETSTR | SCS_CHANGEATTR, buffer, sizeof(buffer), NULL, 0 ) );
6274 ok_ret( 0, ImmSetCompositionStringW( himc, SCS_CHANGECLAUSE | SCS_CHANGEATTR, buffer, sizeof(buffer), NULL, 0 ) );
6275 ok_ret( 0, ImmSetCompositionStringA( himc, SCS_CHANGECLAUSE | SCS_CHANGEATTR, buffer, sizeof(buffer), NULL, 0 ) );
6277 for (i = 0; i < ARRAY_SIZE(scs_indexes); ++i)
6279 winetest_push_context( "%u", i );
6281 if (scs_indexes[i] == SCS_CHANGECLAUSE)
6283 memset( buffer, 0, sizeof(buffer) );
6284 *((DWORD *)buffer + 1) = 1;
6285 len = 2 * sizeof(DWORD);
6287 else if (scs_indexes[i] == SCS_CHANGEATTR)
6289 memset( buffer, 0xcd, sizeof(buffer) );
6290 len = expect_stringW.dwCompAttrLen;
6292 else if (scs_indexes[i] == SCS_SETSTR)
6294 wcscpy( (WCHAR *)buffer, L"CompString" );
6295 len = 11 * sizeof(WCHAR);
6298 todo_ImeSetCompositionString = !unicode;
6299 SET_EXPECT( ImeSetCompositionString );
6300 ok_ret( 1, ImmSetCompositionStringW( himc, scs_indexes[i], buffer, len, NULL, 0 ) );
6301 CHECK_CALLED( ImeSetCompositionString );
6302 todo_ImeSetCompositionString = FALSE;
6303 ok_seq( empty_sequence );
6305 string = ImmLockIMCC( ctx->hCompStr );
6306 ok_ne( NULL, string, COMPOSITIONSTRING *, "%p" );
6307 check_composition_string( string, unicode ? &expect_stringW : &expect_stringA );
6308 ok_ret( 0, ImmUnlockIMCC( ctx->hCompStr ) );
6310 if (scs_indexes[i] == SCS_CHANGECLAUSE)
6312 memset( buffer, 0, sizeof(buffer) );
6313 *((DWORD *)buffer + 1) = 1;
6314 len = 2 * sizeof(DWORD);
6316 else if (scs_indexes[i] == SCS_CHANGEATTR)
6318 memset( buffer, 0xcd, sizeof(buffer) );
6319 len = expect_stringA.dwCompAttrLen;
6321 else if (scs_indexes[i] == SCS_SETSTR)
6323 strcpy( buffer, "CompString" );
6324 len = 11;
6327 todo_ImeSetCompositionString = unicode;
6328 SET_EXPECT( ImeSetCompositionString );
6329 ok_ret( 1, ImmSetCompositionStringA( himc, scs_indexes[i], buffer, len, NULL, 0 ) );
6330 CHECK_CALLED( ImeSetCompositionString );
6331 todo_ImeSetCompositionString = FALSE;
6332 ok_seq( empty_sequence );
6334 string = ImmLockIMCC( ctx->hCompStr );
6335 ok_ne( NULL, string, COMPOSITIONSTRING *, "%p" );
6336 check_composition_string( string, unicode ? &expect_stringW : &expect_stringA );
6337 ok_ret( 0, ImmUnlockIMCC( ctx->hCompStr ) );
6339 winetest_pop_context();
6341 ok_seq( empty_sequence );
6343 old_ctx = ctx;
6344 ok_ret( 1, ImmUnlockIMC( himc ) );
6346 /* composition strings are kept between IME selections */
6347 ok_ret( 1, ImmActivateLayout( default_hkl ) );
6348 ctx = ImmLockIMC( himc );
6349 ok_eq( old_ctx, ctx, INPUTCONTEXT *, "%p" );
6350 ok_eq( old_himcc, ctx->hCompStr, HIMCC, "%p" );
6351 string = ImmLockIMCC( ctx->hCompStr );
6352 ok_ne( NULL, string, COMPOSITIONSTRING *, "%p" );
6353 *string = expect_string_empty;
6354 ok_ret( 0, ImmUnlockIMCC( ctx->hCompStr ) );
6355 ok_ret( 1, ImmActivateLayout( hkl ) );
6356 ok_eq( old_himcc, ctx->hCompStr, HIMCC, "%p" );
6357 check_composition_string( string, &expect_string_empty );
6358 ok_ret( 1, ImmActivateLayout( default_hkl ) );
6359 ok_eq( old_himcc, ctx->hCompStr, HIMCC, "%p" );
6360 check_composition_string( string, &expect_string_empty );
6362 ok_ret( 1, ImmUnlockIMC( himc ) );
6363 ok_ret( 1, ImmDestroyContext( himc ) );
6365 ok_ret( 1, ImmActivateLayout( default_hkl ) );
6366 ok_ret( 1, DestroyWindow( hwnd ) );
6367 process_messages();
6369 ok_ret( 1, ImmFreeLayout( hkl ) );
6370 memset( ime_calls, 0, sizeof(ime_calls) );
6371 ime_call_count = 0;
6373 cleanup:
6374 winetest_pop_context();
6375 SET_ENABLE( ImeSetCompositionString, FALSE );
6378 static void test_ImmSetCompositionWindow(void)
6380 struct ime_call set_composition_window_0_seq[] =
6383 .hkl = expect_ime, .himc = 0/*himc*/,
6384 .func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETCOMPOSITIONWINDOW},
6387 .hkl = expect_ime, .himc = default_himc,
6388 .func = MSG_TEST_WIN, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETCOMPOSITIONWINDOW},
6391 .hkl = expect_ime, .himc = default_himc,
6392 .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETCOMPOSITIONWINDOW},
6394 {0},
6396 struct ime_call set_composition_window_1_seq[] =
6399 .hkl = expect_ime, .himc = 0/*himc*/,
6400 .func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETCOMPOSITIONWINDOW},
6402 {0},
6404 COMPOSITIONFORM comp_form, expect_form =
6406 .dwStyle = 0xfeedcafe,
6407 .ptCurrentPos = {.x = 123, .y = 456},
6408 .rcArea = {.left = 1, .top = 2, .right = 3, .bottom = 4},
6410 struct ime_windows ime_windows = {0};
6411 INPUTCONTEXT *ctx;
6412 HIMC himc;
6413 HKL hkl;
6415 ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE;
6417 if (!(hkl = wineime_hkl)) return;
6419 hwnd = CreateWindowW( test_class.lpszClassName, NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
6420 100, 100, 100, 100, NULL, NULL, NULL, NULL );
6421 ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() );
6423 ok_ret( 1, ImmActivateLayout( hkl ) );
6424 ok_ret( 1, ImmLoadIME( hkl ) );
6425 himc = ImmCreateContext();
6426 ok_ne( NULL, himc, HIMC, "%p" );
6427 ctx = ImmLockIMC( himc );
6428 ok_ne( NULL, ctx, INPUTCONTEXT *, "%p" );
6429 process_messages();
6430 memset( ime_calls, 0, sizeof(ime_calls) );
6431 ime_call_count = 0;
6433 set_composition_window_0_seq[0].himc = himc;
6434 set_composition_window_1_seq[0].himc = himc;
6436 ok_ret( 1, EnumThreadWindows( GetCurrentThreadId(), enum_thread_ime_windows, (LPARAM)&ime_windows ) );
6437 ok_ne( NULL, ime_windows.ime_hwnd, HWND, "%p" );
6438 ok_ne( NULL, ime_windows.ime_ui_hwnd, HWND, "%p" );
6440 ctx->cfCompForm = expect_form;
6441 ctx->fdwInit = ~INIT_COMPFORM;
6442 memset( &comp_form, 0xcd, sizeof(comp_form) );
6443 ok_ret( 0, ImmGetCompositionWindow( himc, &comp_form ) );
6444 ok_eq( 0xcdcdcdcd, comp_form.dwStyle, UINT, "%#x" );
6445 ctx->fdwInit = INIT_COMPFORM;
6446 ok_ret( 1, ImmGetCompositionWindow( himc, &comp_form ) );
6447 check_composition_form( &comp_form, &expect_form );
6448 ok_seq( empty_sequence );
6449 ok_ret( 0, IsWindowVisible( ime_windows.ime_ui_hwnd ) );
6451 ok_ret( 0, ShowWindow( ime_windows.ime_ui_hwnd, SW_SHOWNOACTIVATE ) );
6452 process_messages();
6453 ok_seq( empty_sequence );
6454 check_WM_SHOWWINDOW = TRUE;
6456 ctx->hWnd = hwnd;
6457 ctx->fdwInit = 0;
6458 memset( &comp_form, 0xcd, sizeof(comp_form) );
6459 ok_ret( 1, ImmSetCompositionWindow( himc, &comp_form ) );
6460 process_messages();
6461 ok_seq( set_composition_window_0_seq );
6462 ok_eq( INIT_COMPFORM, ctx->fdwInit, UINT, "%u" );
6463 check_composition_form( &ctx->cfCompForm, &comp_form );
6464 ok_ret( 1, IsWindowVisible( ime_windows.ime_ui_hwnd ) );
6465 check_WM_SHOWWINDOW = FALSE;
6467 ShowWindow( ime_windows.ime_ui_hwnd, SW_HIDE );
6468 process_messages();
6469 ok_seq( empty_sequence );
6471 ok_ret( 1, ImmSetCompositionWindow( himc, &expect_form ) );
6472 ok_seq( set_composition_window_0_seq );
6473 check_composition_form( &ctx->cfCompForm, &expect_form );
6475 ctx->cfCompForm = expect_form;
6476 ok_ret( 1, ImmGetCompositionWindow( himc, &comp_form ) );
6477 check_composition_form( &comp_form, &expect_form );
6478 ok_seq( empty_sequence );
6480 ctx->hWnd = 0;
6481 memset( &comp_form, 0xcd, sizeof(comp_form) );
6482 ok_ret( 1, ImmSetCompositionWindow( himc, &comp_form ) );
6483 ok_seq( set_composition_window_1_seq );
6484 check_composition_form( &ctx->cfCompForm, &comp_form );
6486 ok_ret( 1, ImmUnlockIMC( himc ) );
6487 ok_ret( 1, ImmDestroyContext( himc ) );
6489 ok_ret( 1, ImmActivateLayout( default_hkl ) );
6490 ok_ret( 1, DestroyWindow( hwnd ) );
6491 process_messages();
6493 ok_ret( 1, ImmFreeLayout( hkl ) );
6494 memset( ime_calls, 0, sizeof(ime_calls) );
6495 ime_call_count = 0;
6498 static void test_ImmSetStatusWindowPos(void)
6500 struct ime_call set_status_window_pos_0_seq[] =
6503 .hkl = expect_ime, .himc = 0/*himc*/,
6504 .func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETSTATUSWINDOWPOS},
6507 .hkl = expect_ime, .himc = default_himc,
6508 .func = MSG_TEST_WIN, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETSTATUSWINDOWPOS},
6511 .hkl = expect_ime, .himc = default_himc,
6512 .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETSTATUSWINDOWPOS},
6514 {0},
6516 struct ime_call set_status_window_pos_1_seq[] =
6519 .hkl = expect_ime, .himc = 0/*himc*/,
6520 .func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETSTATUSWINDOWPOS},
6522 {0},
6524 INPUTCONTEXT *ctx;
6525 POINT pos;
6526 HIMC himc;
6527 HKL hkl;
6529 ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE;
6531 if (!(hkl = wineime_hkl)) return;
6533 hwnd = CreateWindowW( test_class.lpszClassName, NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
6534 100, 100, 100, 100, NULL, NULL, NULL, NULL );
6535 ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() );
6537 ok_ret( 1, ImmActivateLayout( hkl ) );
6538 ok_ret( 1, ImmLoadIME( hkl ) );
6539 himc = ImmCreateContext();
6540 ok_ne( NULL, himc, HIMC, "%p" );
6541 ctx = ImmLockIMC( himc );
6542 ok_ne( NULL, ctx, INPUTCONTEXT *, "%p" );
6543 process_messages();
6544 memset( ime_calls, 0, sizeof(ime_calls) );
6545 ime_call_count = 0;
6547 set_status_window_pos_0_seq[0].himc = himc;
6548 set_status_window_pos_1_seq[0].himc = himc;
6550 memset( &pos, 0xcd, sizeof(pos) );
6551 ctx->ptStatusWndPos.x = 0xdeadbeef;
6552 ctx->ptStatusWndPos.y = 0xfeedcafe;
6553 ctx->fdwInit = ~INIT_STATUSWNDPOS;
6554 ok_ret( 0, ImmGetStatusWindowPos( himc, &pos ) );
6555 ok_eq( 0xcdcdcdcd, pos.x, UINT, "%u" );
6556 ok_eq( 0xcdcdcdcd, pos.y, UINT, "%u" );
6557 ctx->fdwInit = INIT_STATUSWNDPOS;
6558 ok_ret( 1, ImmGetStatusWindowPos( himc, &pos ) );
6559 ok_eq( 0xdeadbeef, pos.x, UINT, "%u" );
6560 ok_eq( 0xfeedcafe, pos.y, UINT, "%u" );
6561 ok_seq( empty_sequence );
6563 pos.x = 123;
6564 pos.y = 456;
6565 ctx->hWnd = hwnd;
6566 ctx->fdwInit = 0;
6567 ok_ret( 1, ImmSetStatusWindowPos( himc, &pos ) );
6568 ok_seq( set_status_window_pos_0_seq );
6569 ok_eq( INIT_STATUSWNDPOS, ctx->fdwInit, UINT, "%u" );
6571 ok_ret( 1, ImmSetStatusWindowPos( himc, &pos ) );
6572 ok_seq( set_status_window_pos_0_seq );
6573 ok_ret( 1, ImmGetStatusWindowPos( himc, &pos ) );
6574 ok_eq( 123, pos.x, UINT, "%u" );
6575 ok_eq( 123, ctx->ptStatusWndPos.x, UINT, "%u" );
6576 ok_eq( 456, pos.y, UINT, "%u" );
6577 ok_eq( 456, ctx->ptStatusWndPos.y, UINT, "%u" );
6578 ok_seq( empty_sequence );
6580 ctx->hWnd = 0;
6581 ok_ret( 1, ImmSetStatusWindowPos( himc, &pos ) );
6582 ok_seq( set_status_window_pos_1_seq );
6584 ok_ret( 1, ImmUnlockIMC( himc ) );
6585 ok_ret( 1, ImmDestroyContext( himc ) );
6587 ok_ret( 1, ImmActivateLayout( default_hkl ) );
6588 ok_ret( 1, DestroyWindow( hwnd ) );
6589 process_messages();
6591 ok_ret( 1, ImmFreeLayout( hkl ) );
6592 memset( ime_calls, 0, sizeof(ime_calls) );
6593 ime_call_count = 0;
6596 static void test_ImmSetCompositionFont( BOOL unicode )
6598 struct ime_call set_composition_font_0_seq[] =
6601 .hkl = expect_ime, .himc = 0/*himc*/,
6602 .func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETCOMPOSITIONFONT},
6605 .hkl = expect_ime, .himc = default_himc,
6606 .func = MSG_TEST_WIN, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETCOMPOSITIONFONT},
6609 .hkl = expect_ime, .himc = default_himc,
6610 .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETCOMPOSITIONFONT},
6612 {0},
6614 struct ime_call set_composition_font_1_seq[] =
6617 .hkl = expect_ime, .himc = 0/*himc*/,
6618 .func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETCOMPOSITIONFONT},
6620 {0},
6622 LOGFONTW fontW, expect_fontW =
6624 .lfHeight = 1,
6625 .lfWidth = 2,
6626 .lfEscapement = 3,
6627 .lfOrientation = 4,
6628 .lfWeight = 5,
6629 .lfItalic = 6,
6630 .lfUnderline = 7,
6631 .lfStrikeOut = 8,
6632 .lfCharSet = 8,
6633 .lfOutPrecision = 10,
6634 .lfClipPrecision = 11,
6635 .lfQuality = 12,
6636 .lfPitchAndFamily = 13,
6637 .lfFaceName = L"FontFace",
6639 LOGFONTA fontA, expect_fontA =
6641 .lfHeight = 1,
6642 .lfWidth = 2,
6643 .lfEscapement = 3,
6644 .lfOrientation = 4,
6645 .lfWeight = 5,
6646 .lfItalic = 6,
6647 .lfUnderline = 7,
6648 .lfStrikeOut = 8,
6649 .lfCharSet = 8,
6650 .lfOutPrecision = 10,
6651 .lfClipPrecision = 11,
6652 .lfQuality = 12,
6653 .lfPitchAndFamily = 13,
6654 .lfFaceName = "FontFace",
6656 INPUTCONTEXT *ctx;
6657 HIMC himc;
6658 HKL hkl;
6660 winetest_push_context( unicode ? "unicode" : "ansi" );
6662 /* IME_PROP_END_UNLOAD for the IME to unload / reload. */
6663 ime_info.fdwProperty = IME_PROP_END_UNLOAD;
6664 if (unicode) ime_info.fdwProperty |= IME_PROP_UNICODE;
6666 if (!(hkl = wineime_hkl)) goto cleanup;
6668 hwnd = CreateWindowW( test_class.lpszClassName, NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
6669 100, 100, 100, 100, NULL, NULL, NULL, NULL );
6670 ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() );
6672 ok_ret( 1, ImmActivateLayout( hkl ) );
6673 ok_ret( 1, ImmLoadIME( hkl ) );
6674 himc = ImmCreateContext();
6675 ok_ne( NULL, himc, HIMC, "%p" );
6676 ctx = ImmLockIMC( himc );
6677 ok_ne( NULL, ctx, INPUTCONTEXT *, "%p" );
6678 process_messages();
6679 memset( ime_calls, 0, sizeof(ime_calls) );
6680 ime_call_count = 0;
6682 set_composition_font_0_seq[0].himc = himc;
6683 set_composition_font_1_seq[0].himc = himc;
6685 memset( &fontW, 0xcd, sizeof(fontW) );
6686 memset( &fontA, 0xcd, sizeof(fontA) );
6688 if (unicode) ctx->lfFont.W = expect_fontW;
6689 else ctx->lfFont.A = expect_fontA;
6690 ctx->fdwInit = ~INIT_LOGFONT;
6691 ok_ret( 0, ImmGetCompositionFontW( himc, &fontW ) );
6692 ok_ret( 0, ImmGetCompositionFontA( himc, &fontA ) );
6693 ctx->fdwInit = INIT_LOGFONT;
6694 ok_ret( 1, ImmGetCompositionFontW( himc, &fontW ) );
6695 check_logfont_w( &fontW, &expect_fontW );
6696 ok_ret( 1, ImmGetCompositionFontA( himc, &fontA ) );
6697 check_logfont_a( &fontA, &expect_fontA );
6699 ctx->hWnd = hwnd;
6700 ctx->fdwInit = 0;
6701 memset( &ctx->lfFont, 0xcd, sizeof(ctx->lfFont) );
6702 ok_ret( 1, ImmSetCompositionFontW( himc, &expect_fontW ) );
6703 ok_eq( INIT_LOGFONT, ctx->fdwInit, UINT, "%u" );
6704 ok_seq( set_composition_font_0_seq );
6705 ok_ret( 1, ImmSetCompositionFontW( himc, &expect_fontW ) );
6706 ok_seq( set_composition_font_0_seq );
6707 if (unicode) check_logfont_w( &ctx->lfFont.W, &expect_fontW );
6708 else check_logfont_a( &ctx->lfFont.A, &expect_fontA );
6710 ok_ret( 1, ImmGetCompositionFontW( himc, &fontW ) );
6711 check_logfont_w( &fontW, &expect_fontW );
6712 ok_ret( 1, ImmGetCompositionFontA( himc, &fontA ) );
6713 check_logfont_a( &fontA, &expect_fontA );
6715 ctx->hWnd = hwnd;
6716 ctx->fdwInit = 0;
6717 memset( &ctx->lfFont, 0xcd, sizeof(ctx->lfFont) );
6718 ok_ret( 1, ImmSetCompositionFontA( himc, &expect_fontA ) );
6719 ok_eq( INIT_LOGFONT, ctx->fdwInit, UINT, "%u" );
6720 ok_seq( set_composition_font_0_seq );
6721 ok_ret( 1, ImmSetCompositionFontA( himc, &expect_fontA ) );
6722 ok_seq( set_composition_font_0_seq );
6723 if (unicode) check_logfont_w( &ctx->lfFont.W, &expect_fontW );
6724 else check_logfont_a( &ctx->lfFont.A, &expect_fontA );
6726 ok_ret( 1, ImmGetCompositionFontW( himc, &fontW ) );
6727 check_logfont_w( &fontW, &expect_fontW );
6728 ok_ret( 1, ImmGetCompositionFontA( himc, &fontA ) );
6729 check_logfont_a( &fontA, &expect_fontA );
6731 ctx->hWnd = 0;
6732 ok_ret( 1, ImmSetCompositionFontW( himc, &expect_fontW ) );
6733 ok_seq( set_composition_font_1_seq );
6734 ok_ret( 1, ImmSetCompositionFontA( himc, &expect_fontA ) );
6735 ok_seq( set_composition_font_1_seq );
6737 ok_ret( 1, ImmUnlockIMC( himc ) );
6738 ok_ret( 1, ImmDestroyContext( himc ) );
6740 ok_ret( 1, ImmActivateLayout( default_hkl ) );
6741 ok_ret( 1, DestroyWindow( hwnd ) );
6742 process_messages();
6744 ok_ret( 1, ImmFreeLayout( hkl ) );
6745 memset( ime_calls, 0, sizeof(ime_calls) );
6746 ime_call_count = 0;
6748 cleanup:
6749 winetest_pop_context();
6752 static void test_ImmSetCandidateWindow(void)
6754 struct ime_call set_candidate_window_0_seq[] =
6757 .hkl = expect_ime, .himc = 0/*himc*/,
6758 .func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETCANDIDATEPOS},
6761 .hkl = expect_ime, .himc = default_himc,
6762 .func = MSG_TEST_WIN, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETCANDIDATEPOS, .lparam = 4},
6765 .hkl = expect_ime, .himc = default_himc,
6766 .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETCANDIDATEPOS, .lparam = 4},
6768 {0},
6770 struct ime_call set_candidate_window_1_seq[] =
6773 .hkl = expect_ime, .himc = 0/*himc*/,
6774 .func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETCANDIDATEPOS},
6776 {0},
6778 CANDIDATEFORM cand_form, expect_form =
6780 .dwIndex = 2, .dwStyle = 0xfeedcafe,
6781 .ptCurrentPos = {.x = 123, .y = 456},
6782 .rcArea = {.left = 1, .top = 2, .right = 3, .bottom = 4},
6784 INPUTCONTEXT *ctx;
6785 HIMC himc;
6786 HKL hkl;
6788 ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE;
6790 if (!(hkl = wineime_hkl)) return;
6792 hwnd = CreateWindowW( test_class.lpszClassName, NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
6793 100, 100, 100, 100, NULL, NULL, NULL, NULL );
6794 ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() );
6796 ok_ret( 1, ImmActivateLayout( hkl ) );
6797 ok_ret( 1, ImmLoadIME( hkl ) );
6798 himc = ImmCreateContext();
6799 ok_ne( NULL, himc, HIMC, "%p" );
6800 ctx = ImmLockIMC( himc );
6801 ok_ne( NULL, ctx, INPUTCONTEXT *, "%p" );
6802 process_messages();
6803 memset( ime_calls, 0, sizeof(ime_calls) );
6804 ime_call_count = 0;
6806 set_candidate_window_0_seq[0].himc = himc;
6807 set_candidate_window_1_seq[0].himc = himc;
6809 ctx->cfCandForm[1] = expect_form;
6810 ctx->cfCandForm[2] = expect_form;
6811 ctx->fdwInit = 0;
6812 memset( &cand_form, 0xcd, sizeof(cand_form) );
6813 ok_ret( 0, ImmGetCandidateWindow( himc, 0, &cand_form ) );
6814 ok_eq( 0xcdcdcdcd, cand_form.dwStyle, UINT, "%#x" );
6815 ok_ret( 1, ImmGetCandidateWindow( himc, 1, &cand_form ) );
6816 check_candidate_form( &cand_form, &expect_form );
6817 ok_ret( 1, ImmGetCandidateWindow( himc, 2, &cand_form ) );
6818 check_candidate_form( &cand_form, &expect_form );
6819 ok_seq( empty_sequence );
6821 ctx->hWnd = hwnd;
6822 memset( &cand_form, 0xcd, sizeof(cand_form) );
6823 cand_form.dwIndex = 2;
6824 ok_ret( 1, ImmSetCandidateWindow( himc, &cand_form ) );
6825 ok_seq( set_candidate_window_0_seq );
6826 check_candidate_form( &ctx->cfCandForm[2], &cand_form );
6827 ok_eq( 0, ctx->fdwInit, UINT, "%u" );
6829 ok_ret( 1, ImmSetCandidateWindow( himc, &expect_form ) );
6830 ok_seq( set_candidate_window_0_seq );
6831 check_candidate_form( &ctx->cfCandForm[2], &expect_form );
6833 ctx->hWnd = 0;
6834 memset( &cand_form, 0xcd, sizeof(cand_form) );
6835 cand_form.dwIndex = 2;
6836 ok_ret( 1, ImmSetCandidateWindow( himc, &cand_form ) );
6837 ok_seq( set_candidate_window_1_seq );
6838 check_candidate_form( &ctx->cfCandForm[2], &cand_form );
6840 ok_ret( 1, ImmUnlockIMC( himc ) );
6841 ok_ret( 1, ImmDestroyContext( himc ) );
6843 ok_ret( 1, ImmActivateLayout( default_hkl ) );
6844 ok_ret( 1, DestroyWindow( hwnd ) );
6845 process_messages();
6847 ok_ret( 1, ImmFreeLayout( hkl ) );
6848 memset( ime_calls, 0, sizeof(ime_calls) );
6849 ime_call_count = 0;
6852 static void test_ImmGenerateMessage(void)
6854 const struct ime_call generate_sequence[] =
6857 .hkl = expect_ime, .himc = default_himc,
6858 .func = MSG_TEST_WIN, .message = {.msg = WM_IME_COMPOSITION, .wparam = 0, .lparam = GCS_COMPSTR},
6861 .hkl = expect_ime, .himc = default_himc,
6862 .func = MSG_IME_UI, .message = {.msg = WM_IME_COMPOSITION, .wparam = 0, .lparam = GCS_COMPSTR},
6864 {0},
6866 TRANSMSG *msgs, *tmp_msgs;
6867 INPUTCONTEXT *ctx;
6868 HIMC himc;
6869 HKL hkl;
6871 ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE;
6873 if (!(hkl = wineime_hkl)) return;
6875 hwnd = CreateWindowW( test_class.lpszClassName, NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
6876 100, 100, 100, 100, NULL, NULL, NULL, NULL );
6877 ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() );
6879 ok_ret( 1, ImmActivateLayout( hkl ) );
6880 ok_ret( 1, ImmLoadIME( hkl ) );
6881 himc = ImmCreateContext();
6882 ok_ne( NULL, himc, HIMC, "%p" );
6883 ctx = ImmLockIMC( himc );
6884 ok_ne( NULL, ctx, INPUTCONTEXT *, "%p" );
6885 process_messages();
6886 memset( ime_calls, 0, sizeof(ime_calls) );
6887 ime_call_count = 0;
6889 todo_wine ok_ret( 4, ImmGetIMCCSize( ctx->hMsgBuf ) );
6890 ctx->hMsgBuf = ImmReSizeIMCC( ctx->hMsgBuf, sizeof(*msgs) );
6891 ok_ne( NULL, ctx->hMsgBuf, HIMCC, "%p" );
6893 msgs = ImmLockIMCC( ctx->hMsgBuf );
6894 ok_ne( NULL, msgs, TRANSMSG *, "%p" );
6895 msgs[0].message = WM_IME_COMPOSITION;
6896 msgs[0].wParam = 0;
6897 msgs[0].lParam = GCS_COMPSTR;
6898 ok_ret( 0, ImmUnlockIMCC( ctx->hMsgBuf ) );
6900 ctx->hWnd = 0;
6901 ctx->dwNumMsgBuf = 0;
6902 ok_ret( 1, ImmGenerateMessage( himc ) );
6903 ok_seq( empty_sequence );
6904 ok_ret( sizeof(*msgs), ImmGetIMCCSize( ctx->hMsgBuf ) );
6905 tmp_msgs = ImmLockIMCC( ctx->hMsgBuf );
6906 ok_eq( msgs, tmp_msgs, TRANSMSG *, "%p" );
6907 ok_ret( 0, ImmUnlockIMCC( ctx->hMsgBuf ) );
6909 ctx->dwNumMsgBuf = 1;
6910 ok_ret( 1, ImmGenerateMessage( himc ) );
6911 ok_seq( empty_sequence );
6912 ok_eq( 0, ctx->dwNumMsgBuf, UINT, "%u" );
6913 ok_ret( sizeof(*msgs), ImmGetIMCCSize( ctx->hMsgBuf ) );
6914 tmp_msgs = ImmLockIMCC( ctx->hMsgBuf );
6915 ok_eq( msgs, tmp_msgs, TRANSMSG *, "%p" );
6916 ok_ret( 0, ImmUnlockIMCC( ctx->hMsgBuf ) );
6918 ctx->hWnd = hwnd;
6919 ctx->dwNumMsgBuf = 0;
6920 ok_ret( 1, ImmGenerateMessage( himc ) );
6921 ok_seq( empty_sequence );
6922 ok_ret( sizeof(*msgs), ImmGetIMCCSize( ctx->hMsgBuf ) );
6923 tmp_msgs = ImmLockIMCC( ctx->hMsgBuf );
6924 ok_eq( msgs, tmp_msgs, TRANSMSG *, "%p" );
6925 ok_ret( 0, ImmUnlockIMCC( ctx->hMsgBuf ) );
6927 ctx->dwNumMsgBuf = 1;
6928 ok_ret( 1, ImmGenerateMessage( himc ) );
6929 ok_seq( generate_sequence );
6930 ok_eq( 0, ctx->dwNumMsgBuf, UINT, "%u" );
6931 ok_ret( sizeof(*msgs), ImmGetIMCCSize( ctx->hMsgBuf ) );
6932 tmp_msgs = ImmLockIMCC( ctx->hMsgBuf );
6933 ok_eq( msgs, tmp_msgs, TRANSMSG *, "%p" );
6934 ok_ret( 0, ImmUnlockIMCC( ctx->hMsgBuf ) );
6936 tmp_msgs = ImmLockIMCC( ctx->hMsgBuf );
6937 ok_eq( msgs, tmp_msgs, TRANSMSG *, "%p" );
6938 ok_ret( 0, ImmUnlockIMCC( ctx->hMsgBuf ) );
6940 ok_ret( 1, ImmUnlockIMC( himc ) );
6941 ok_ret( 1, ImmDestroyContext( himc ) );
6943 ok_ret( 1, ImmActivateLayout( default_hkl ) );
6944 ok_ret( 1, DestroyWindow( hwnd ) );
6945 process_messages();
6947 ok_ret( 1, ImmFreeLayout( hkl ) );
6948 memset( ime_calls, 0, sizeof(ime_calls) );
6949 ime_call_count = 0;
6952 static void test_ImmTranslateMessage( BOOL kbd_char_first )
6954 const struct ime_call process_key_seq[] =
6957 .hkl = expect_ime, .himc = default_himc, .func = IME_PROCESS_KEY,
6958 .process_key = {.vkey = 'Q', .lparam = MAKELONG(2, 0x10)},
6961 .hkl = expect_ime, .himc = default_himc, .func = IME_PROCESS_KEY,
6962 .process_key = {.vkey = 'Q', .lparam = MAKELONG(2, 0xc010)},
6964 {.todo = TRUE},
6966 const struct ime_call to_ascii_ex_0[] =
6969 .hkl = expect_ime, .himc = default_himc, .func = IME_TO_ASCII_EX,
6970 .to_ascii_ex = {.vkey = kbd_char_first ? MAKELONG('Q', 'q') : 'Q', .vsc = 0x10},
6972 {0},
6974 const struct ime_call to_ascii_ex_1[] =
6977 .hkl = expect_ime, .himc = default_himc, .func = IME_PROCESS_KEY,
6978 .process_key = {.vkey = 'Q', .lparam = MAKELONG(2, 0xc010)},
6981 .hkl = expect_ime, .himc = default_himc, .func = IME_TO_ASCII_EX,
6982 /* FIXME what happened to kbd_char_first here!? */
6983 .to_ascii_ex = {.vkey = 'Q', .vsc = 0xc010},
6984 .todo_value = TRUE,
6986 {0},
6988 struct ime_call to_ascii_ex_2[] =
6991 .hkl = expect_ime, .himc = 0/*himc*/, .func = IME_PROCESS_KEY,
6992 .process_key = {.vkey = 'Q', .lparam = MAKELONG(2, 0x210)},
6995 .hkl = expect_ime, .himc = 0/*himc*/, .func = IME_TO_ASCII_EX,
6996 .to_ascii_ex = {.vkey = kbd_char_first ? MAKELONG('Q', 'q') : 'Q', .vsc = 0x210},
6997 .todo_value = TRUE,
7000 .hkl = expect_ime, .himc = 0/*himc*/, .func = IME_PROCESS_KEY,
7001 .process_key = {.vkey = 'Q', .lparam = MAKELONG(2, 0x410)},
7004 .hkl = expect_ime, .himc = 0/*himc*/, .func = IME_TO_ASCII_EX,
7005 .to_ascii_ex = {.vkey = kbd_char_first ? MAKELONG('Q', 'q') : 'Q', .vsc = 0x410},
7006 .todo_value = TRUE,
7008 {0},
7010 struct ime_call to_ascii_ex_3[] =
7013 .hkl = expect_ime, .himc = 0/*himc*/, .func = IME_PROCESS_KEY,
7014 .process_key = {.vkey = 'Q', .lparam = MAKELONG(2, 0xa10)},
7017 .hkl = expect_ime, .himc = 0/*himc*/, .func = IME_TO_ASCII_EX,
7018 .to_ascii_ex = {.vkey = kbd_char_first ? MAKELONG('Q', 'q') : 'Q', .vsc = 0xa10},
7019 .todo_value = TRUE,
7022 .hkl = expect_ime, .himc = 0/*himc*/, .func = IME_PROCESS_KEY,
7023 .process_key = {.vkey = 'Q', .lparam = MAKELONG(2, 0xc10)},
7026 .hkl = expect_ime, .himc = 0/*himc*/, .func = IME_TO_ASCII_EX,
7027 .to_ascii_ex = {.vkey = kbd_char_first ? MAKELONG('Q', 'q') : 'Q', .vsc = 0xc10},
7028 .todo_value = TRUE,
7030 {0},
7032 struct ime_call post_messages[] =
7034 {.hkl = expect_ime, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_STARTCOMPOSITION, .wparam = 1}},
7035 {.hkl = expect_ime, .himc = 0/*himc*/, .func = MSG_IME_UI, .message = {.msg = WM_IME_STARTCOMPOSITION, .wparam = 1}},
7036 {.hkl = expect_ime, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_ENDCOMPOSITION, .wparam = 1}},
7037 {.hkl = expect_ime, .himc = 0/*himc*/, .func = MSG_IME_UI, .message = {.msg = WM_IME_ENDCOMPOSITION, .wparam = 1}},
7038 {0},
7040 struct ime_call sent_messages[] =
7042 {.hkl = expect_ime, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_STARTCOMPOSITION, .wparam = 2}},
7043 {.hkl = expect_ime, .himc = 0/*himc*/, .func = MSG_IME_UI, .message = {.msg = WM_IME_STARTCOMPOSITION, .wparam = 2}},
7044 {.hkl = expect_ime, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_ENDCOMPOSITION, .wparam = 2}},
7045 {.hkl = expect_ime, .himc = 0/*himc*/, .func = MSG_IME_UI, .message = {.msg = WM_IME_ENDCOMPOSITION, .wparam = 2}},
7046 {0},
7048 HWND hwnd, other_hwnd;
7049 INPUTCONTEXT *ctx;
7050 HIMC himc;
7051 HKL hkl;
7052 UINT i, ret;
7054 ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE;
7055 if (kbd_char_first) ime_info.fdwProperty |= IME_PROP_KBD_CHAR_FIRST;
7057 winetest_push_context( kbd_char_first ? "kbd_char_first" : "default" );
7059 if (!(hkl = wineime_hkl)) goto cleanup;
7061 other_hwnd = CreateWindowW( test_class.lpszClassName, NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
7062 100, 100, 100, 100, NULL, NULL, NULL, NULL );
7063 ok( !!other_hwnd, "CreateWindowW failed, error %lu\n", GetLastError() );
7064 flush_events();
7066 hwnd = CreateWindowW( test_class.lpszClassName, NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
7067 100, 100, 100, 100, NULL, NULL, NULL, NULL );
7068 ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() );
7069 flush_events();
7071 ok_ret( 1, ImmActivateLayout( hkl ) );
7072 ok_ret( 1, ImmLoadIME( hkl ) );
7073 himc = ImmCreateContext();
7074 ok_ne( NULL, himc, HIMC, "%p" );
7075 ctx = ImmLockIMC( himc );
7076 ok_ne( NULL, ctx, INPUTCONTEXT *, "%p" );
7077 process_messages();
7078 memset( ime_calls, 0, sizeof(ime_calls) );
7079 ime_call_count = 0;
7081 ok_ret( 2, ImmProcessKey( hwnd, expect_ime, 'Q', MAKELONG(2, 0x10), 0 ) );
7082 ok_ret( 2, ImmProcessKey( hwnd, expect_ime, 'Q', MAKELONG(2, 0xc010), 0 ) );
7084 ok_ret( 0, ImmTranslateMessage( hwnd, 0, 0, 0 ) );
7085 ok_ret( 'Q', ImmGetVirtualKey( hwnd ) );
7086 ok_seq( process_key_seq );
7088 ok_ret( 0, ImmTranslateMessage( hwnd, WM_KEYDOWN, 'Q', MAKELONG(2, 0x10) ) );
7089 ok_ret( VK_PROCESSKEY, ImmGetVirtualKey( hwnd ) );
7090 ok_seq( to_ascii_ex_0 );
7092 ok_ret( 0, ImmTranslateMessage( hwnd, WM_KEYUP, 'Q', MAKELONG(2, 0xc010) ) );
7093 ok_seq( empty_sequence );
7095 ok_ret( 2, ImmProcessKey( hwnd, expect_ime, 'Q', MAKELONG(2, 0xc010), 0 ) );
7096 ok_ret( 0, ImmTranslateMessage( hwnd, WM_KEYUP, 'Q', MAKELONG(2, 0xc010) ) );
7097 ok_ret( VK_PROCESSKEY, ImmGetVirtualKey( hwnd ) );
7098 ok_seq( to_ascii_ex_1 );
7100 ok_eq( default_himc, ImmAssociateContext( hwnd, himc ), HIMC, "%p" );
7101 ok_eq( default_himc, ImmAssociateContext( other_hwnd, himc ), HIMC, "%p" );
7102 for (i = 0; i < ARRAY_SIZE(to_ascii_ex_2); i++) to_ascii_ex_2[i].himc = himc;
7103 for (i = 0; i < ARRAY_SIZE(to_ascii_ex_3); i++) to_ascii_ex_3[i].himc = himc;
7104 for (i = 0; i < ARRAY_SIZE(post_messages); i++) post_messages[i].himc = himc;
7105 for (i = 0; i < ARRAY_SIZE(sent_messages); i++) sent_messages[i].himc = himc;
7106 memset( ime_calls, 0, sizeof(ime_calls) );
7107 ime_call_count = 0;
7109 ctx->hWnd = hwnd;
7110 ok_ret( 2, ImmProcessKey( hwnd, expect_ime, 'Q', MAKELONG(2, 0x210), 0 ) );
7111 ret = ImmTranslateMessage( hwnd, WM_KEYUP, 'Q', MAKELONG(2, 0x210) );
7112 todo_wine ok_eq( 1, ret, UINT, "%u" );
7113 ok_ret( 2, ImmProcessKey( hwnd, expect_ime, 'Q', MAKELONG(2, 0x410), 0 ) );
7114 ok_ret( 0, ImmTranslateMessage( hwnd, WM_KEYUP, 'Q', MAKELONG(2, 0x410) ) );
7115 ok_ret( VK_PROCESSKEY, ImmGetVirtualKey( hwnd ) );
7116 ok_seq( to_ascii_ex_2 );
7117 process_messages();
7118 todo_wine ok_seq( post_messages );
7119 ok_ret( 1, ImmGenerateMessage( himc ) );
7120 todo_wine ok_seq( sent_messages );
7122 ok_ret( 2, ImmProcessKey( hwnd, expect_ime, 'Q', MAKELONG(2, 0xa10), 0 ) );
7123 ok_ret( 0, ImmTranslateMessage( hwnd, WM_KEYUP, 'Q', MAKELONG(2, 0xa10) ) );
7124 ok_ret( 2, ImmProcessKey( hwnd, expect_ime, 'Q', MAKELONG(2, 0xc10), 0 ) );
7125 ok_ret( 0, ImmTranslateMessage( hwnd, WM_KEYUP, 'Q', MAKELONG(2, 0xc10) ) );
7126 ok_ret( VK_PROCESSKEY, ImmGetVirtualKey( hwnd ) );
7127 ok_seq( to_ascii_ex_3 );
7128 process_messages();
7129 ok_seq( empty_sequence );
7130 ok_ret( 1, ImmGenerateMessage( himc ) );
7131 todo_wine ok_seq( sent_messages );
7133 ctx->hWnd = 0;
7134 ok_ret( 2, ImmProcessKey( other_hwnd, expect_ime, 'Q', MAKELONG(2, 0x210), 0 ) );
7135 ret = ImmTranslateMessage( other_hwnd, WM_KEYUP, 'Q', MAKELONG(2, 0x210) );
7136 todo_wine ok_eq( 1, ret, UINT, "%u" );
7137 ok_ret( 2, ImmProcessKey( other_hwnd, expect_ime, 'Q', MAKELONG(2, 0x410), 0 ) );
7138 ok_ret( 0, ImmTranslateMessage( other_hwnd, WM_KEYUP, 'Q', MAKELONG(2, 0x410) ) );
7139 ok_ret( VK_PROCESSKEY, ImmGetVirtualKey( other_hwnd ) );
7140 ok_seq( to_ascii_ex_2 );
7141 process_messages_( hwnd );
7142 ok_seq( empty_sequence );
7143 process_messages_( other_hwnd );
7144 todo_wine ok_seq( post_messages );
7145 ok_ret( 1, ImmGenerateMessage( himc ) );
7146 ok_seq( empty_sequence );
7148 ok_ret( 1, ImmUnlockIMC( himc ) );
7149 ok_ret( 1, ImmDestroyContext( himc ) );
7151 ok_ret( 1, ImmActivateLayout( default_hkl ) );
7152 ok_ret( 1, DestroyWindow( other_hwnd ) );
7153 ok_ret( 1, DestroyWindow( hwnd ) );
7154 process_messages();
7156 ok_ret( 1, ImmFreeLayout( hkl ) );
7157 memset( ime_calls, 0, sizeof(ime_calls) );
7158 ime_call_count = 0;
7160 cleanup:
7161 winetest_pop_context();
7164 START_TEST(imm32)
7166 default_hkl = GetKeyboardLayout( 0 );
7168 test_class.hInstance = GetModuleHandleW( NULL );
7169 RegisterClassExW( &test_class );
7171 if (!is_ime_enabled())
7173 win_skip("IME support not implemented\n");
7174 return;
7177 test_com_initialization();
7179 test_ImmEnumInputContext();
7181 test_ImmInstallIME();
7182 wineime_hkl = ime_install();
7184 test_ImmGetDescription();
7185 test_ImmGetIMEFileName();
7186 test_ImmIsIME();
7187 test_ImmGetProperty();
7189 test_ImmEscape( FALSE );
7190 test_ImmEscape( TRUE );
7191 test_ImmEnumRegisterWord( FALSE );
7192 test_ImmEnumRegisterWord( TRUE );
7193 test_ImmRegisterWord( FALSE );
7194 test_ImmRegisterWord( TRUE );
7195 test_ImmGetRegisterWordStyle( FALSE );
7196 test_ImmGetRegisterWordStyle( TRUE );
7197 test_ImmUnregisterWord( FALSE );
7198 test_ImmUnregisterWord( TRUE );
7200 /* test these first to sanitize conversion / open statuses */
7201 test_ImmSetConversionStatus();
7202 test_ImmSetOpenStatus();
7203 ImeSelect_init_status = TRUE;
7205 test_ImmActivateLayout();
7206 test_ImmCreateInputContext();
7207 test_ImmProcessKey();
7208 test_DefWindowProc();
7209 test_ImmSetActiveContext();
7210 test_ImmRequestMessage();
7212 test_ImmGetCandidateList( TRUE );
7213 test_ImmGetCandidateList( FALSE );
7214 test_ImmGetCandidateListCount( TRUE );
7215 test_ImmGetCandidateListCount( FALSE );
7216 test_ImmGetCandidateWindow();
7218 test_ImmGetCompositionString( TRUE );
7219 test_ImmGetCompositionString( FALSE );
7220 test_ImmSetCompositionWindow();
7221 test_ImmSetStatusWindowPos();
7222 test_ImmSetCompositionFont( TRUE );
7223 test_ImmSetCompositionFont( FALSE );
7224 test_ImmSetCandidateWindow();
7226 test_ImmGenerateMessage();
7227 test_ImmTranslateMessage( FALSE );
7228 test_ImmTranslateMessage( TRUE );
7230 if (wineime_hkl) ime_cleanup( wineime_hkl, TRUE );
7232 if (init())
7234 test_ImmNotifyIME();
7235 test_SCS_SETSTR();
7236 test_ImmIME();
7237 test_ImmAssociateContextEx();
7238 test_NtUserAssociateInputContext();
7239 test_cross_thread_himc();
7240 test_ImmIsUIMessage();
7241 test_ImmGetContext();
7242 test_ImmDefaultHwnd();
7243 test_default_ime_window_creation();
7244 test_ImmGetIMCLockCount();
7245 test_ImmGetIMCCLockCount();
7246 test_ImmDestroyContext();
7247 test_ImmDestroyIMCC();
7248 test_InvalidIMC();
7249 msg_spy_cleanup();
7250 /* Reinitialize the hooks to capture all windows */
7251 msg_spy_init(NULL);
7252 test_ImmMessages();
7253 msg_spy_cleanup();
7254 if (pSendInput)
7255 test_ime_processkey();
7256 else win_skip("SendInput is not available\n");
7258 /* there's no way of enabling IME - keep the test last */
7259 test_ImmDisableIME();
7261 cleanup();
7263 UnregisterClassW( test_class.lpszClassName, test_class.hInstance );