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
23 #include "wine/test.h"
28 #define NUMELEMS(array) (sizeof((array))/sizeof((array)[0]))
30 static BOOL (WINAPI
*pImmAssociateContextEx
)(HWND
,HIMC
,DWORD
);
31 static BOOL (WINAPI
*pImmIsUIMessageA
)(HWND
,UINT
,WPARAM
,LPARAM
);
34 * msgspy - record and analyse message traces sent to a certain window
36 typedef struct _msgs
{
41 static struct _msg_spy
{
44 HHOOK call_wnd_proc_hook
;
49 static LRESULT CALLBACK
get_msg_filter(int nCode
, WPARAM wParam
, LPARAM lParam
)
51 if (HC_ACTION
== nCode
) {
52 MSG
*msg
= (MSG
*)lParam
;
54 if ((msg
->hwnd
== msg_spy
.hwnd
|| msg_spy
.hwnd
== NULL
) &&
55 (msg_spy
.i_msg
< NUMELEMS(msg_spy
.msgs
)))
57 msg_spy
.msgs
[msg_spy
.i_msg
].msg
.hwnd
= msg
->hwnd
;
58 msg_spy
.msgs
[msg_spy
.i_msg
].msg
.message
= msg
->message
;
59 msg_spy
.msgs
[msg_spy
.i_msg
].msg
.wParam
= msg
->wParam
;
60 msg_spy
.msgs
[msg_spy
.i_msg
].msg
.lParam
= msg
->lParam
;
61 msg_spy
.msgs
[msg_spy
.i_msg
].post
= TRUE
;
66 return CallNextHookEx(msg_spy
.get_msg_hook
, nCode
, wParam
, lParam
);
69 static LRESULT CALLBACK
call_wnd_proc_filter(int nCode
, WPARAM wParam
,
72 if (HC_ACTION
== nCode
) {
73 CWPSTRUCT
*cwp
= (CWPSTRUCT
*)lParam
;
75 if (((cwp
->hwnd
== msg_spy
.hwnd
|| msg_spy
.hwnd
== NULL
)) &&
76 (msg_spy
.i_msg
< NUMELEMS(msg_spy
.msgs
)))
78 memcpy(&msg_spy
.msgs
[msg_spy
.i_msg
].msg
, cwp
, sizeof(msg_spy
.msgs
[0].msg
));
79 msg_spy
.msgs
[msg_spy
.i_msg
].post
= FALSE
;
84 return CallNextHookEx(msg_spy
.call_wnd_proc_hook
, nCode
, wParam
, lParam
);
87 static void msg_spy_pump_msg_queue(void) {
90 while(PeekMessage(&msg
, NULL
, 0, 0, PM_REMOVE
)) {
91 TranslateMessage(&msg
);
92 DispatchMessage(&msg
);
98 static void msg_spy_flush_msgs(void) {
99 msg_spy_pump_msg_queue();
103 static imm_msgs
* msg_spy_find_next_msg(UINT message
, UINT
*start
) {
106 msg_spy_pump_msg_queue();
108 if (msg_spy
.i_msg
>= NUMELEMS(msg_spy
.msgs
))
109 fprintf(stdout
, "%s:%d: msg_spy: message buffer overflow!\n",
112 for (i
= *start
; i
< msg_spy
.i_msg
; i
++)
113 if (msg_spy
.msgs
[i
].msg
.message
== message
)
116 return &msg_spy
.msgs
[i
];
122 static imm_msgs
* msg_spy_find_msg(UINT message
) {
125 return msg_spy_find_next_msg(message
, &i
);
128 static void msg_spy_init(HWND hwnd
) {
130 msg_spy
.get_msg_hook
=
131 SetWindowsHookEx(WH_GETMESSAGE
, get_msg_filter
, GetModuleHandle(0),
132 GetCurrentThreadId());
133 msg_spy
.call_wnd_proc_hook
=
134 SetWindowsHookEx(WH_CALLWNDPROC
, call_wnd_proc_filter
,
135 GetModuleHandle(0), GetCurrentThreadId());
138 msg_spy_flush_msgs();
141 static void msg_spy_cleanup(void) {
142 if (msg_spy
.get_msg_hook
)
143 UnhookWindowsHookEx(msg_spy
.get_msg_hook
);
144 if (msg_spy
.call_wnd_proc_hook
)
145 UnhookWindowsHookEx(msg_spy
.call_wnd_proc_hook
);
146 memset(&msg_spy
, 0, sizeof(msg_spy
));
150 * imm32 test cases - Issue some IMM commands on a dummy window and analyse the
151 * messages being sent to this window in response.
153 static const char wndcls
[] = "winetest_imm32_wndcls";
156 static LRESULT WINAPI
wndProc(HWND hWnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
160 case WM_IME_SETCONTEXT
:
166 return DefWindowProcA(hwnd
,msg
,wParam
,lParam
);
169 static BOOL
init(void) {
174 hmod
= GetModuleHandleA("imm32.dll");
175 pImmAssociateContextEx
= (void*)GetProcAddress(hmod
, "ImmAssociateContextEx");
176 pImmIsUIMessageA
= (void*)GetProcAddress(hmod
, "ImmIsUIMessageA");
178 wc
.cbSize
= sizeof(WNDCLASSEX
);
180 wc
.lpfnWndProc
= wndProc
;
183 wc
.hInstance
= GetModuleHandle(0);
184 wc
.hIcon
= LoadIcon(NULL
, IDI_APPLICATION
);
185 wc
.hCursor
= LoadCursor(NULL
, IDC_ARROW
);
186 wc
.hbrBackground
= (HBRUSH
)(COLOR_WINDOW
+1);
187 wc
.lpszMenuName
= NULL
;
188 wc
.lpszClassName
= wndcls
;
189 wc
.hIconSm
= LoadIcon(NULL
, IDI_APPLICATION
);
191 if (!RegisterClassExA(&wc
))
194 hwnd
= CreateWindowEx(WS_EX_CLIENTEDGE
, wndcls
, "Wine imm32.dll test",
195 WS_OVERLAPPEDWINDOW
, CW_USEDEFAULT
, CW_USEDEFAULT
,
196 240, 120, NULL
, NULL
, GetModuleHandle(0), NULL
);
200 imc
= ImmGetContext(hwnd
);
203 win_skip("IME support not implemented\n");
206 ImmReleaseContext(hwnd
, imc
);
208 ShowWindow(hwnd
, SW_SHOWNORMAL
);
216 static void cleanup(void) {
220 UnregisterClass(wndcls
, GetModuleHandle(0));
223 static void test_ImmNotifyIME(void) {
224 static const char string
[] = "wine";
225 char resstr
[16] = "";
229 imc
= ImmGetContext(hwnd
);
230 msg_spy_flush_msgs();
232 ret
= ImmNotifyIME(imc
, NI_COMPOSITIONSTR
, CPS_CANCEL
, 0);
235 "Canceling an empty composition string should succeed.\n");
236 ok(!msg_spy_find_msg(WM_IME_COMPOSITION
), "Windows does not post "
237 "WM_IME_COMPOSITION in response to NI_COMPOSITIONSTR / CPS_CANCEL, if "
238 "the composition string being canceled is empty.\n");
240 ImmSetCompositionString(imc
, SCS_SETSTR
, string
, sizeof(string
), NULL
, 0);
241 msg_spy_flush_msgs();
243 ImmNotifyIME(imc
, NI_COMPOSITIONSTR
, CPS_CANCEL
, 0);
244 msg_spy_flush_msgs();
246 /* behavior differs between win9x and NT */
247 ret
= ImmGetCompositionString(imc
, GCS_COMPSTR
, resstr
, sizeof(resstr
));
248 ok(!ret
, "After being cancelled the composition string is empty.\n");
250 msg_spy_flush_msgs();
252 ret
= ImmNotifyIME(imc
, NI_COMPOSITIONSTR
, CPS_CANCEL
, 0);
255 "Canceling an empty composition string should succeed.\n");
256 ok(!msg_spy_find_msg(WM_IME_COMPOSITION
), "Windows does not post "
257 "WM_IME_COMPOSITION in response to NI_COMPOSITIONSTR / CPS_CANCEL, if "
258 "the composition string being canceled is empty.\n");
260 msg_spy_flush_msgs();
261 ImmReleaseContext(hwnd
, imc
);
264 static void test_ImmGetCompositionString(void)
267 static const WCHAR string
[] = {'w','i','n','e',0x65e5,0x672c,0x8a9e};
273 imc
= ImmGetContext(hwnd
);
274 ImmSetCompositionStringW(imc
, SCS_SETSTR
, string
, sizeof(string
), NULL
,0);
275 alen
= ImmGetCompositionStringA(imc
, GCS_COMPSTR
, cstring
, 20);
276 wlen
= ImmGetCompositionStringW(imc
, GCS_COMPSTR
, wstring
, 20);
277 /* windows machines without any IME installed just return 0 above */
280 len
= ImmGetCompositionStringW(imc
, GCS_COMPATTR
, NULL
, 0);
281 ok(len
*sizeof(WCHAR
)==wlen
,"GCS_COMPATTR(W) not returning correct count\n");
282 len
= ImmGetCompositionStringA(imc
, GCS_COMPATTR
, NULL
, 0);
283 ok(len
==alen
,"GCS_COMPATTR(A) not returning correct count\n");
285 ImmReleaseContext(hwnd
, imc
);
288 static void test_ImmSetCompositionString(void)
293 SetLastError(0xdeadbeef);
294 imc
= ImmGetContext(hwnd
);
295 ok(imc
!= 0, "ImmGetContext() failed. Last error: %u\n", GetLastError());
299 ret
= ImmSetCompositionStringW(imc
, SCS_SETSTR
, NULL
, 0, NULL
, 0);
302 "ImmSetCompositionStringW() failed.\n");
304 ret
= ImmSetCompositionStringW(imc
, SCS_SETSTR
| SCS_CHANGEATTR
,
306 ok(!ret
, "ImmSetCompositionStringW() succeeded.\n");
308 ret
= ImmSetCompositionStringW(imc
, SCS_SETSTR
| SCS_CHANGECLAUSE
,
310 ok(!ret
, "ImmSetCompositionStringW() succeeded.\n");
312 ret
= ImmSetCompositionStringW(imc
, SCS_CHANGEATTR
| SCS_CHANGECLAUSE
,
314 ok(!ret
, "ImmSetCompositionStringW() succeeded.\n");
316 ret
= ImmSetCompositionStringW(imc
, SCS_SETSTR
| SCS_CHANGEATTR
| SCS_CHANGECLAUSE
,
318 ok(!ret
, "ImmSetCompositionStringW() succeeded.\n");
320 ImmReleaseContext(hwnd
, imc
);
323 static void test_ImmIME(void)
327 imc
= ImmGetContext(hwnd
);
331 rc
= ImmConfigureIMEA(imc
, NULL
, IME_CONFIG_REGISTERWORD
, NULL
);
332 ok (rc
== 0, "ImmConfigureIMEA did not fail\n");
333 rc
= ImmConfigureIMEW(imc
, NULL
, IME_CONFIG_REGISTERWORD
, NULL
);
334 ok (rc
== 0, "ImmConfigureIMEW did not fail\n");
336 ImmReleaseContext(hwnd
,imc
);
339 static void test_ImmAssociateContextEx(void)
344 if (!pImmAssociateContextEx
) return;
346 imc
= ImmGetContext(hwnd
);
351 newimc
= ImmCreateContext();
352 ok(newimc
!= imc
, "handles should not be the same\n");
353 rc
= pImmAssociateContextEx(NULL
, NULL
, 0);
354 ok(!rc
, "ImmAssociateContextEx succeeded\n");
355 rc
= pImmAssociateContextEx(hwnd
, NULL
, 0);
356 ok(rc
, "ImmAssociateContextEx failed\n");
357 rc
= pImmAssociateContextEx(NULL
, imc
, 0);
358 ok(!rc
, "ImmAssociateContextEx succeeded\n");
360 rc
= pImmAssociateContextEx(hwnd
, imc
, 0);
361 ok(rc
, "ImmAssociateContextEx failed\n");
362 retimc
= ImmGetContext(hwnd
);
363 ok(retimc
== imc
, "handles should be the same\n");
364 ImmReleaseContext(hwnd
,retimc
);
366 rc
= pImmAssociateContextEx(hwnd
, newimc
, 0);
367 ok(rc
, "ImmAssociateContextEx failed\n");
368 retimc
= ImmGetContext(hwnd
);
369 ok(retimc
== newimc
, "handles should be the same\n");
370 ImmReleaseContext(hwnd
,retimc
);
372 rc
= pImmAssociateContextEx(hwnd
, NULL
, IACE_DEFAULT
);
373 ok(rc
, "ImmAssociateContextEx failed\n");
375 ImmReleaseContext(hwnd
,imc
);
378 typedef struct _igc_threadinfo
{
385 static DWORD WINAPI
ImmGetContextThreadFunc( LPVOID lpParam
)
391 igc_threadinfo
*info
= (igc_threadinfo
*)lpParam
;
392 info
->hwnd
= CreateWindowEx(WS_EX_CLIENTEDGE
, wndcls
, "Wine imm32.dll test",
393 WS_OVERLAPPEDWINDOW
, CW_USEDEFAULT
, CW_USEDEFAULT
,
394 240, 120, NULL
, NULL
, GetModuleHandle(0), NULL
);
396 h1
= ImmGetContext(hwnd
);
397 todo_wine
ok(info
->himc
== h1
, "hwnd context changed in new thread\n");
398 h2
= ImmGetContext(info
->hwnd
);
399 todo_wine
ok(h2
!= h1
, "new hwnd in new thread should have different context\n");
401 ImmReleaseContext(hwnd
,h1
);
403 hwnd2
= CreateWindowEx(WS_EX_CLIENTEDGE
, wndcls
, "Wine imm32.dll test",
404 WS_OVERLAPPEDWINDOW
, CW_USEDEFAULT
, CW_USEDEFAULT
,
405 240, 120, NULL
, NULL
, GetModuleHandle(0), NULL
);
406 h1
= ImmGetContext(hwnd2
);
408 ok(h1
== h2
, "Windows in same thread should have same default context\n");
409 ImmReleaseContext(hwnd2
,h1
);
410 ImmReleaseContext(info
->hwnd
,h2
);
411 DestroyWindow(hwnd2
);
413 /* priming for later tests */
414 ImmSetCompositionWindow(h1
, &cf
);
415 ImmSetStatusWindowPos(h1
, &pt
);
417 SetEvent(info
->event
);
422 static void test_ImmThreads(void)
424 HIMC himc
, otherHimc
, h1
;
425 igc_threadinfo threadinfo
;
431 DWORD status
, sentence
;
434 himc
= ImmGetContext(hwnd
);
435 threadinfo
.event
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
436 threadinfo
.himc
= himc
;
437 hThread
= CreateThread(NULL
, 0, ImmGetContextThreadFunc
, &threadinfo
, 0, &dwThreadId
);
438 WaitForSingleObject(threadinfo
.event
, INFINITE
);
440 otherHimc
= ImmGetContext(threadinfo
.hwnd
);
442 todo_wine
ok(himc
!= otherHimc
, "Windows from other threads should have different himc\n");
443 todo_wine
ok(otherHimc
== threadinfo
.himc
, "Context from other thread should not change in main thread\n");
445 if (0) /* FIXME: Causes wine to hang */
447 h1
= ImmAssociateContext(hwnd
,otherHimc
);
448 ok(h1
== NULL
, "Should fail to be able to Associate a default context from a different thread\n");
449 h1
= ImmGetContext(hwnd
);
450 ok(h1
== himc
, "Context for window should remain unchanged\n");
451 ImmReleaseContext(hwnd
,h1
);
456 rc
= ImmSetOpenStatus(himc
, TRUE
);
457 ok(rc
!= 0, "ImmSetOpenStatus failed\n");
458 rc
= ImmGetOpenStatus(himc
);
459 ok(rc
!= 0, "ImmGetOpenStatus failed\n");
460 rc
= ImmSetOpenStatus(himc
, FALSE
);
461 ok(rc
!= 0, "ImmSetOpenStatus failed\n");
462 rc
= ImmGetOpenStatus(himc
);
463 ok(rc
== 0, "ImmGetOpenStatus failed\n");
465 rc
= ImmSetOpenStatus(otherHimc
, TRUE
);
466 todo_wine
ok(rc
== 0, "ImmSetOpenStatus should fail\n");
467 rc
= ImmGetOpenStatus(otherHimc
);
468 todo_wine
ok(rc
== 0, "ImmGetOpenStatus failed\n");
469 rc
= ImmSetOpenStatus(otherHimc
, FALSE
);
470 todo_wine
ok(rc
== 0, "ImmSetOpenStatus should fail\n");
471 rc
= ImmGetOpenStatus(otherHimc
);
472 ok(rc
== 0, "ImmGetOpenStatus failed\n");
474 /* CompositionFont */
475 rc
= ImmGetCompositionFont(himc
, &lf
);
476 ok(rc
!= 0, "ImmGetCompositionFont failed\n");
477 rc
= ImmSetCompositionFont(himc
, &lf
);
478 ok(rc
!= 0, "ImmSetCompositionFont failed\n");
480 rc
= ImmGetCompositionFont(otherHimc
, &lf
);
481 ok(rc
!= 0 || broken(rc
== 0), "ImmGetCompositionFont failed\n");
482 rc
= ImmSetCompositionFont(otherHimc
, &lf
);
483 todo_wine
ok(rc
== 0, "ImmSetCompositionFont should fail\n");
485 /* CompositionWindow */
486 rc
= ImmSetCompositionWindow(himc
, &cf
);
487 ok(rc
!= 0, "ImmSetCompositionWindow failed\n");
488 rc
= ImmGetCompositionWindow(himc
, &cf
);
489 ok(rc
!= 0, "ImmGetCompositionWindow failed\n");
491 rc
= ImmSetCompositionWindow(otherHimc
, &cf
);
492 todo_wine
ok(rc
== 0, "ImmSetCompositionWindow should fail\n");
493 rc
= ImmGetCompositionWindow(otherHimc
, &cf
);
494 ok(rc
!= 0 || broken(rc
== 0), "ImmGetCompositionWindow failed\n");
496 /* ConversionStatus */
497 rc
= ImmGetConversionStatus(himc
, &status
, &sentence
);
498 ok(rc
!= 0, "ImmGetConversionStatus failed\n");
499 rc
= ImmSetConversionStatus(himc
, status
, sentence
);
500 ok(rc
!= 0, "ImmSetConversionStatus failed\n");
502 rc
= ImmGetConversionStatus(otherHimc
, &status
, &sentence
);
503 ok(rc
!= 0 || broken(rc
== 0), "ImmGetConversionStatus failed\n");
504 rc
= ImmSetConversionStatus(otherHimc
, status
, sentence
);
505 todo_wine
ok(rc
== 0, "ImmSetConversionStatus should fail\n");
507 /* StatusWindowPos */
508 rc
= ImmSetStatusWindowPos(himc
, &pt
);
509 ok(rc
!= 0, "ImmSetStatusWindowPos failed\n");
510 rc
= ImmGetStatusWindowPos(himc
, &pt
);
511 ok(rc
!= 0, "ImmGetStatusWindowPos failed\n");
513 rc
= ImmSetStatusWindowPos(otherHimc
, &pt
);
514 todo_wine
ok(rc
== 0, "ImmSetStatusWindowPos should fail\n");
515 rc
= ImmGetStatusWindowPos(otherHimc
, &pt
);
516 ok(rc
!= 0 || broken(rc
== 0), "ImmGetStatusWindowPos failed\n");
518 ImmReleaseContext(threadinfo
.hwnd
,otherHimc
);
519 ImmReleaseContext(hwnd
,himc
);
521 DestroyWindow(threadinfo
.hwnd
);
522 TerminateThread(hThread
, 1);
524 himc
= ImmGetContext(GetDesktopWindow());
525 todo_wine
ok(himc
== NULL
, "Should not be able to get himc from other process window\n");
528 static void test_ImmIsUIMessage(void)
536 static const struct test tests
[] =
538 { WM_MOUSEMOVE
, FALSE
},
539 { WM_IME_STARTCOMPOSITION
, TRUE
},
540 { WM_IME_ENDCOMPOSITION
, TRUE
},
541 { WM_IME_COMPOSITION
, TRUE
},
542 { WM_IME_SETCONTEXT
, TRUE
},
543 { WM_IME_NOTIFY
, TRUE
},
544 { WM_IME_CONTROL
, FALSE
},
545 { WM_IME_COMPOSITIONFULL
, TRUE
},
546 { WM_IME_SELECT
, TRUE
},
547 { WM_IME_CHAR
, FALSE
},
548 { 0x287 /* FIXME */, TRUE
},
549 { WM_IME_REQUEST
, FALSE
},
550 { WM_IME_KEYDOWN
, FALSE
},
551 { WM_IME_KEYUP
, FALSE
},
552 { 0, FALSE
} /* mark the end */
555 const struct test
*test
;
558 if (!pImmIsUIMessageA
) return;
560 for (test
= tests
; test
->msg
; test
++)
562 msg_spy_flush_msgs();
563 ret
= pImmIsUIMessageA(NULL
, test
->msg
, 0, 0);
564 ok(ret
== test
->ret
, "ImmIsUIMessageA returned %x for %x\n", ret
, test
->msg
);
565 ok(!msg_spy_find_msg(test
->msg
), "Windows does not send 0x%x for NULL hwnd\n", test
->msg
);
567 ret
= pImmIsUIMessageA(hwnd
, test
->msg
, 0, 0);
568 ok(ret
== test
->ret
, "ImmIsUIMessageA returned %x for %x\n", ret
, test
->msg
);
570 ok(msg_spy_find_msg(test
->msg
) != NULL
, "Windows does send 0x%x\n", test
->msg
);
572 ok(!msg_spy_find_msg(test
->msg
), "Windows does not send 0x%x\n", test
->msg
);
576 static void test_ImmGetContext(void)
581 SetLastError(0xdeadbeef);
582 himc
= ImmGetContext((HWND
)0xffffffff);
583 err
= GetLastError();
584 ok(himc
== NULL
, "ImmGetContext succeeded\n");
585 ok(err
== ERROR_INVALID_WINDOW_HANDLE
, "got %u\n", err
);
587 himc
= ImmGetContext(hwnd
);
588 ok(himc
!= NULL
, "ImmGetContext failed\n");
589 ok(ImmReleaseContext(hwnd
, himc
), "ImmReleaseContext failed\n");
592 static void test_ImmGetDescription(void)
595 WCHAR japime
[] = { 'E', '0', '0', '1', '0', '4', '1', '1', 0 };
600 /* FIXME: invalid keyboard layouts should not pass */
601 ret
= ImmGetDescriptionW(NULL
, NULL
, 0);
602 todo_wine
ok(!ret
, "ImmGetDescriptionW failed, expected 0 received %d.\n", ret
);
604 /* load a language with valid IMM descriptions */
605 hkl
= LoadKeyboardLayoutW(japime
, KLF_ACTIVATE
);
606 todo_wine
ok(hkl
!= 0, "LoadKeyboardLayoutW failed, expected != 0.\n");
608 ret
= ImmGetDescriptionW(hkl
, NULL
, 0);
611 win_skip("ImmGetDescriptionW is not working for current loaded keyboard.\n");
615 ret
= ImmGetDescriptionW(hkl
, descW
, 0);
616 ok(ret
, "ImmGetDescriptionW failed, expected != 0 received 0.\n");
618 lret
= ImmGetDescriptionW(hkl
, descW
, ret
+ 1);
619 ok(lret
, "ImmGetDescriptionW failed, expected != 0 received 0.\n");
620 ok(lret
== ret
, "ImmGetDescriptionW failed to return the correct amount of data. Expected %d, got %d.\n", ret
, lret
);
622 lret
= ImmGetDescriptionA(hkl
, descA
, ret
+ 1);
623 ok(lret
, "ImmGetDescriptionA failed, expected != 0 received 0.\n");
624 todo_wine
ok(lret
== ret
, "ImmGetDescriptionA failed to return the correct amount of data. Expected %d, got %d.\n", ret
, lret
);
626 ret
/= 2; /* try to copy partially */
627 lret
= ImmGetDescriptionW(hkl
, descW
, ret
+ 1);
628 ok(lret
, "ImmGetDescriptionW failed, expected != 0 received 0.\n");
629 ok(lret
== ret
, "ImmGetDescriptionW failed to return the correct amount of data. Expected %d, got %d.\n", ret
, lret
);
631 ret
= ImmGetDescriptionW(hkl
, descW
, 1);
632 ok(!ret
, "ImmGetDescriptionW failed, expected 0 received %d.\n", ret
);
634 UnloadKeyboardLayout(hkl
);
637 static void test_ImmDefaultHwnd(void)
639 HIMC imc1
, imc2
, imc3
;
643 hwnd
= CreateWindowEx(WS_EX_CLIENTEDGE
, "EDIT", "Wine imm32.dll test",
644 WS_OVERLAPPEDWINDOW
, CW_USEDEFAULT
, CW_USEDEFAULT
,
645 240, 120, NULL
, NULL
, GetModuleHandle(0), NULL
);
647 ShowWindow(hwnd
, SW_SHOWNORMAL
);
649 imc1
= ImmGetContext(hwnd
);
652 win_skip("IME support not implemented\n");
656 def1
= ImmGetDefaultIMEWnd(hwnd
);
658 imc2
= ImmCreateContext();
659 ImmSetOpenStatus(imc2
, TRUE
);
661 imc3
= ImmGetContext(hwnd
);
662 def3
= ImmGetDefaultIMEWnd(hwnd
);
664 ok(def3
== def1
, "Default IME window should not change\n");
665 ok(imc1
== imc3
, "IME context should not change\n");
666 ImmSetOpenStatus(imc2
, FALSE
);
668 ImmReleaseContext(hwnd
, imc1
);
669 ImmReleaseContext(hwnd
, imc3
);
670 ImmDestroyContext(imc2
);
674 static void test_ImmMessages(void)
682 HWND hwnd
= CreateWindowEx(WS_EX_CLIENTEDGE
, "EDIT", "Wine imm32.dll test",
683 WS_OVERLAPPEDWINDOW
, CW_USEDEFAULT
, CW_USEDEFAULT
,
684 240, 120, NULL
, NULL
, GetModuleHandle(0), NULL
);
686 ShowWindow(hwnd
, SW_SHOWNORMAL
);
687 defwnd
= ImmGetDefaultIMEWnd(hwnd
);
688 imc
= ImmGetContext(hwnd
);
690 ImmSetOpenStatus(imc
, TRUE
);
691 msg_spy_flush_msgs();
692 SendMessage(defwnd
, WM_IME_CONTROL
, IMC_GETCANDIDATEPOS
, (LPARAM
)&cf
);
695 msg
= msg_spy_find_next_msg(WM_IME_CONTROL
,&idx
);
696 if (msg
) ok(!msg
->post
, "Message should not be posted\n");
698 msg_spy_flush_msgs();
699 ImmSetOpenStatus(imc
, FALSE
);
700 ImmReleaseContext(hwnd
, imc
);
708 test_ImmGetCompositionString();
709 test_ImmSetCompositionString();
711 test_ImmAssociateContextEx();
713 test_ImmIsUIMessage();
714 test_ImmGetContext();
715 test_ImmGetDescription();
716 test_ImmDefaultHwnd();
718 /* Reinitialize the hooks to capture all windows */