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"
29 #define NUMELEMS(array) (sizeof((array))/sizeof((array)[0]))
31 static BOOL (WINAPI
*pImmAssociateContextEx
)(HWND
,HIMC
,DWORD
);
32 static BOOL (WINAPI
*pImmIsUIMessageA
)(HWND
,UINT
,WPARAM
,LPARAM
);
33 static UINT (WINAPI
*pSendInput
) (UINT
, INPUT
*, size_t);
36 * msgspy - record and analyse message traces sent to a certain window
38 typedef struct _msgs
{
43 static struct _msg_spy
{
46 HHOOK call_wnd_proc_hook
;
62 static UINT (WINAPI
*pSendInput
) (UINT
, INPUT
*, size_t);
64 static LRESULT CALLBACK
get_msg_filter(int nCode
, WPARAM wParam
, LPARAM lParam
)
66 if (HC_ACTION
== nCode
) {
67 MSG
*msg
= (MSG
*)lParam
;
69 if ((msg
->hwnd
== msg_spy
.hwnd
|| msg_spy
.hwnd
== NULL
) &&
70 (msg_spy
.i_msg
< NUMELEMS(msg_spy
.msgs
)))
72 msg_spy
.msgs
[msg_spy
.i_msg
].msg
.hwnd
= msg
->hwnd
;
73 msg_spy
.msgs
[msg_spy
.i_msg
].msg
.message
= msg
->message
;
74 msg_spy
.msgs
[msg_spy
.i_msg
].msg
.wParam
= msg
->wParam
;
75 msg_spy
.msgs
[msg_spy
.i_msg
].msg
.lParam
= msg
->lParam
;
76 msg_spy
.msgs
[msg_spy
.i_msg
].post
= TRUE
;
81 return CallNextHookEx(msg_spy
.get_msg_hook
, nCode
, wParam
, lParam
);
84 static LRESULT CALLBACK
call_wnd_proc_filter(int nCode
, WPARAM wParam
,
87 if (HC_ACTION
== nCode
) {
88 CWPSTRUCT
*cwp
= (CWPSTRUCT
*)lParam
;
90 if (((cwp
->hwnd
== msg_spy
.hwnd
|| msg_spy
.hwnd
== NULL
)) &&
91 (msg_spy
.i_msg
< NUMELEMS(msg_spy
.msgs
)))
93 memcpy(&msg_spy
.msgs
[msg_spy
.i_msg
].msg
, cwp
, sizeof(msg_spy
.msgs
[0].msg
));
94 msg_spy
.msgs
[msg_spy
.i_msg
].post
= FALSE
;
99 return CallNextHookEx(msg_spy
.call_wnd_proc_hook
, nCode
, wParam
, lParam
);
102 static void msg_spy_pump_msg_queue(void) {
105 while(PeekMessage(&msg
, NULL
, 0, 0, PM_REMOVE
)) {
106 TranslateMessage(&msg
);
107 DispatchMessage(&msg
);
113 static void msg_spy_flush_msgs(void) {
114 msg_spy_pump_msg_queue();
118 static imm_msgs
* msg_spy_find_next_msg(UINT message
, UINT
*start
) {
121 msg_spy_pump_msg_queue();
123 if (msg_spy
.i_msg
>= NUMELEMS(msg_spy
.msgs
))
124 fprintf(stdout
, "%s:%d: msg_spy: message buffer overflow!\n",
127 for (i
= *start
; i
< msg_spy
.i_msg
; i
++)
128 if (msg_spy
.msgs
[i
].msg
.message
== message
)
131 return &msg_spy
.msgs
[i
];
137 static imm_msgs
* msg_spy_find_msg(UINT message
) {
140 return msg_spy_find_next_msg(message
, &i
);
143 static void msg_spy_init(HWND hwnd
) {
145 msg_spy
.get_msg_hook
=
146 SetWindowsHookEx(WH_GETMESSAGE
, get_msg_filter
, GetModuleHandle(0),
147 GetCurrentThreadId());
148 msg_spy
.call_wnd_proc_hook
=
149 SetWindowsHookEx(WH_CALLWNDPROC
, call_wnd_proc_filter
,
150 GetModuleHandle(0), GetCurrentThreadId());
153 msg_spy_flush_msgs();
156 static void msg_spy_cleanup(void) {
157 if (msg_spy
.get_msg_hook
)
158 UnhookWindowsHookEx(msg_spy
.get_msg_hook
);
159 if (msg_spy
.call_wnd_proc_hook
)
160 UnhookWindowsHookEx(msg_spy
.call_wnd_proc_hook
);
161 memset(&msg_spy
, 0, sizeof(msg_spy
));
165 * imm32 test cases - Issue some IMM commands on a dummy window and analyse the
166 * messages being sent to this window in response.
168 static const char wndcls
[] = "winetest_imm32_wndcls";
171 static LRESULT WINAPI
wndProc(HWND hWnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
175 case WM_IME_SETCONTEXT
:
181 return DefWindowProcA(hwnd
,msg
,wParam
,lParam
);
184 static BOOL
init(void) {
189 hmod
= GetModuleHandleA("imm32.dll");
190 huser
= GetModuleHandleA("user32");
191 pImmAssociateContextEx
= (void*)GetProcAddress(hmod
, "ImmAssociateContextEx");
192 pImmIsUIMessageA
= (void*)GetProcAddress(hmod
, "ImmIsUIMessageA");
193 pSendInput
= (void*)GetProcAddress(huser
, "SendInput");
195 wc
.cbSize
= sizeof(WNDCLASSEX
);
197 wc
.lpfnWndProc
= wndProc
;
200 wc
.hInstance
= GetModuleHandle(0);
201 wc
.hIcon
= LoadIcon(NULL
, IDI_APPLICATION
);
202 wc
.hCursor
= LoadCursor(NULL
, IDC_ARROW
);
203 wc
.hbrBackground
= (HBRUSH
)(COLOR_WINDOW
+1);
204 wc
.lpszMenuName
= NULL
;
205 wc
.lpszClassName
= wndcls
;
206 wc
.hIconSm
= LoadIcon(NULL
, IDI_APPLICATION
);
208 if (!RegisterClassExA(&wc
))
211 hwnd
= CreateWindowEx(WS_EX_CLIENTEDGE
, wndcls
, "Wine imm32.dll test",
212 WS_OVERLAPPEDWINDOW
, CW_USEDEFAULT
, CW_USEDEFAULT
,
213 240, 120, NULL
, NULL
, GetModuleHandle(0), NULL
);
217 imc
= ImmGetContext(hwnd
);
220 win_skip("IME support not implemented\n");
223 ImmReleaseContext(hwnd
, imc
);
225 ShowWindow(hwnd
, SW_SHOWNORMAL
);
233 static void cleanup(void) {
237 UnregisterClass(wndcls
, GetModuleHandle(0));
240 static void test_ImmNotifyIME(void) {
241 static const char string
[] = "wine";
242 char resstr
[16] = "";
246 imc
= ImmGetContext(hwnd
);
247 msg_spy_flush_msgs();
249 ret
= ImmNotifyIME(imc
, NI_COMPOSITIONSTR
, CPS_CANCEL
, 0);
252 "Canceling an empty composition string should succeed.\n");
253 ok(!msg_spy_find_msg(WM_IME_COMPOSITION
), "Windows does not post "
254 "WM_IME_COMPOSITION in response to NI_COMPOSITIONSTR / CPS_CANCEL, if "
255 "the composition string being canceled is empty.\n");
257 ImmSetCompositionString(imc
, SCS_SETSTR
, string
, sizeof(string
), NULL
, 0);
258 msg_spy_flush_msgs();
260 ImmNotifyIME(imc
, NI_COMPOSITIONSTR
, CPS_CANCEL
, 0);
261 msg_spy_flush_msgs();
263 /* behavior differs between win9x and NT */
264 ret
= ImmGetCompositionString(imc
, GCS_COMPSTR
, resstr
, sizeof(resstr
));
265 ok(!ret
, "After being cancelled the composition string is empty.\n");
267 msg_spy_flush_msgs();
269 ret
= ImmNotifyIME(imc
, NI_COMPOSITIONSTR
, CPS_CANCEL
, 0);
272 "Canceling an empty composition string should succeed.\n");
273 ok(!msg_spy_find_msg(WM_IME_COMPOSITION
), "Windows does not post "
274 "WM_IME_COMPOSITION in response to NI_COMPOSITIONSTR / CPS_CANCEL, if "
275 "the composition string being canceled is empty.\n");
277 msg_spy_flush_msgs();
278 ImmReleaseContext(hwnd
, imc
);
281 static void test_ImmGetCompositionString(void)
284 static const WCHAR string
[] = {'w','i','n','e',0x65e5,0x672c,0x8a9e};
290 imc
= ImmGetContext(hwnd
);
291 ImmSetCompositionStringW(imc
, SCS_SETSTR
, string
, sizeof(string
), NULL
,0);
292 alen
= ImmGetCompositionStringA(imc
, GCS_COMPSTR
, cstring
, 20);
293 wlen
= ImmGetCompositionStringW(imc
, GCS_COMPSTR
, wstring
, 20);
294 /* windows machines without any IME installed just return 0 above */
297 len
= ImmGetCompositionStringW(imc
, GCS_COMPATTR
, NULL
, 0);
298 ok(len
*sizeof(WCHAR
)==wlen
,"GCS_COMPATTR(W) not returning correct count\n");
299 len
= ImmGetCompositionStringA(imc
, GCS_COMPATTR
, NULL
, 0);
300 ok(len
==alen
,"GCS_COMPATTR(A) not returning correct count\n");
302 ImmReleaseContext(hwnd
, imc
);
305 static void test_ImmSetCompositionString(void)
310 SetLastError(0xdeadbeef);
311 imc
= ImmGetContext(hwnd
);
312 ok(imc
!= 0, "ImmGetContext() failed. Last error: %u\n", GetLastError());
316 ret
= ImmSetCompositionStringW(imc
, SCS_SETSTR
, NULL
, 0, NULL
, 0);
319 "ImmSetCompositionStringW() failed.\n");
321 ret
= ImmSetCompositionStringW(imc
, SCS_SETSTR
| SCS_CHANGEATTR
,
323 ok(!ret
, "ImmSetCompositionStringW() succeeded.\n");
325 ret
= ImmSetCompositionStringW(imc
, SCS_SETSTR
| SCS_CHANGECLAUSE
,
327 ok(!ret
, "ImmSetCompositionStringW() succeeded.\n");
329 ret
= ImmSetCompositionStringW(imc
, SCS_CHANGEATTR
| SCS_CHANGECLAUSE
,
331 ok(!ret
, "ImmSetCompositionStringW() succeeded.\n");
333 ret
= ImmSetCompositionStringW(imc
, SCS_SETSTR
| SCS_CHANGEATTR
| SCS_CHANGECLAUSE
,
335 ok(!ret
, "ImmSetCompositionStringW() succeeded.\n");
337 ImmReleaseContext(hwnd
, imc
);
340 static void test_ImmIME(void)
344 imc
= ImmGetContext(hwnd
);
348 rc
= ImmConfigureIMEA(imc
, NULL
, IME_CONFIG_REGISTERWORD
, NULL
);
349 ok (rc
== 0, "ImmConfigureIMEA did not fail\n");
350 rc
= ImmConfigureIMEW(imc
, NULL
, IME_CONFIG_REGISTERWORD
, NULL
);
351 ok (rc
== 0, "ImmConfigureIMEW did not fail\n");
353 ImmReleaseContext(hwnd
,imc
);
356 static void test_ImmAssociateContextEx(void)
361 if (!pImmAssociateContextEx
) return;
363 imc
= ImmGetContext(hwnd
);
368 newimc
= ImmCreateContext();
369 ok(newimc
!= imc
, "handles should not be the same\n");
370 rc
= pImmAssociateContextEx(NULL
, NULL
, 0);
371 ok(!rc
, "ImmAssociateContextEx succeeded\n");
372 rc
= pImmAssociateContextEx(hwnd
, NULL
, 0);
373 ok(rc
, "ImmAssociateContextEx failed\n");
374 rc
= pImmAssociateContextEx(NULL
, imc
, 0);
375 ok(!rc
, "ImmAssociateContextEx succeeded\n");
377 rc
= pImmAssociateContextEx(hwnd
, imc
, 0);
378 ok(rc
, "ImmAssociateContextEx failed\n");
379 retimc
= ImmGetContext(hwnd
);
380 ok(retimc
== imc
, "handles should be the same\n");
381 ImmReleaseContext(hwnd
,retimc
);
383 rc
= pImmAssociateContextEx(hwnd
, newimc
, 0);
384 ok(rc
, "ImmAssociateContextEx failed\n");
385 retimc
= ImmGetContext(hwnd
);
386 ok(retimc
== newimc
, "handles should be the same\n");
387 ImmReleaseContext(hwnd
,retimc
);
389 rc
= pImmAssociateContextEx(hwnd
, NULL
, IACE_DEFAULT
);
390 ok(rc
, "ImmAssociateContextEx failed\n");
392 ImmReleaseContext(hwnd
,imc
);
395 typedef struct _igc_threadinfo
{
402 static DWORD WINAPI
ImmGetContextThreadFunc( LPVOID lpParam
)
408 igc_threadinfo
*info
= (igc_threadinfo
*)lpParam
;
409 info
->hwnd
= CreateWindowEx(WS_EX_CLIENTEDGE
, wndcls
, "Wine imm32.dll test",
410 WS_OVERLAPPEDWINDOW
, CW_USEDEFAULT
, CW_USEDEFAULT
,
411 240, 120, NULL
, NULL
, GetModuleHandle(0), NULL
);
413 h1
= ImmGetContext(hwnd
);
414 todo_wine
ok(info
->himc
== h1
, "hwnd context changed in new thread\n");
415 h2
= ImmGetContext(info
->hwnd
);
416 todo_wine
ok(h2
!= h1
, "new hwnd in new thread should have different context\n");
418 ImmReleaseContext(hwnd
,h1
);
420 hwnd2
= CreateWindowEx(WS_EX_CLIENTEDGE
, wndcls
, "Wine imm32.dll test",
421 WS_OVERLAPPEDWINDOW
, CW_USEDEFAULT
, CW_USEDEFAULT
,
422 240, 120, NULL
, NULL
, GetModuleHandle(0), NULL
);
423 h1
= ImmGetContext(hwnd2
);
425 ok(h1
== h2
, "Windows in same thread should have same default context\n");
426 ImmReleaseContext(hwnd2
,h1
);
427 ImmReleaseContext(info
->hwnd
,h2
);
428 DestroyWindow(hwnd2
);
430 /* priming for later tests */
431 ImmSetCompositionWindow(h1
, &cf
);
432 ImmSetStatusWindowPos(h1
, &pt
);
434 SetEvent(info
->event
);
439 static void test_ImmThreads(void)
441 HIMC himc
, otherHimc
, h1
;
442 igc_threadinfo threadinfo
;
448 DWORD status
, sentence
;
451 himc
= ImmGetContext(hwnd
);
452 threadinfo
.event
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
453 threadinfo
.himc
= himc
;
454 hThread
= CreateThread(NULL
, 0, ImmGetContextThreadFunc
, &threadinfo
, 0, &dwThreadId
);
455 WaitForSingleObject(threadinfo
.event
, INFINITE
);
457 otherHimc
= ImmGetContext(threadinfo
.hwnd
);
459 todo_wine
ok(himc
!= otherHimc
, "Windows from other threads should have different himc\n");
460 todo_wine
ok(otherHimc
== threadinfo
.himc
, "Context from other thread should not change in main thread\n");
462 if (0) /* FIXME: Causes wine to hang */
464 h1
= ImmAssociateContext(hwnd
,otherHimc
);
465 ok(h1
== NULL
, "Should fail to be able to Associate a default context from a different thread\n");
466 h1
= ImmGetContext(hwnd
);
467 ok(h1
== himc
, "Context for window should remain unchanged\n");
468 ImmReleaseContext(hwnd
,h1
);
473 rc
= ImmSetOpenStatus(himc
, TRUE
);
474 ok(rc
!= 0, "ImmSetOpenStatus failed\n");
475 rc
= ImmGetOpenStatus(himc
);
476 ok(rc
!= 0, "ImmGetOpenStatus failed\n");
477 rc
= ImmSetOpenStatus(himc
, FALSE
);
478 ok(rc
!= 0, "ImmSetOpenStatus failed\n");
479 rc
= ImmGetOpenStatus(himc
);
480 ok(rc
== 0, "ImmGetOpenStatus failed\n");
482 rc
= ImmSetOpenStatus(otherHimc
, TRUE
);
483 todo_wine
ok(rc
== 0, "ImmSetOpenStatus should fail\n");
484 rc
= ImmGetOpenStatus(otherHimc
);
485 todo_wine
ok(rc
== 0, "ImmGetOpenStatus failed\n");
486 rc
= ImmSetOpenStatus(otherHimc
, FALSE
);
487 todo_wine
ok(rc
== 0, "ImmSetOpenStatus should fail\n");
488 rc
= ImmGetOpenStatus(otherHimc
);
489 ok(rc
== 0, "ImmGetOpenStatus failed\n");
491 /* CompositionFont */
492 rc
= ImmGetCompositionFont(himc
, &lf
);
493 ok(rc
!= 0, "ImmGetCompositionFont failed\n");
494 rc
= ImmSetCompositionFont(himc
, &lf
);
495 ok(rc
!= 0, "ImmSetCompositionFont failed\n");
497 rc
= ImmGetCompositionFont(otherHimc
, &lf
);
498 ok(rc
!= 0 || broken(rc
== 0), "ImmGetCompositionFont failed\n");
499 rc
= ImmSetCompositionFont(otherHimc
, &lf
);
500 todo_wine
ok(rc
== 0, "ImmSetCompositionFont should fail\n");
502 /* CompositionWindow */
503 rc
= ImmSetCompositionWindow(himc
, &cf
);
504 ok(rc
!= 0, "ImmSetCompositionWindow failed\n");
505 rc
= ImmGetCompositionWindow(himc
, &cf
);
506 ok(rc
!= 0, "ImmGetCompositionWindow failed\n");
508 rc
= ImmSetCompositionWindow(otherHimc
, &cf
);
509 todo_wine
ok(rc
== 0, "ImmSetCompositionWindow should fail\n");
510 rc
= ImmGetCompositionWindow(otherHimc
, &cf
);
511 ok(rc
!= 0 || broken(rc
== 0), "ImmGetCompositionWindow failed\n");
513 /* ConversionStatus */
514 rc
= ImmGetConversionStatus(himc
, &status
, &sentence
);
515 ok(rc
!= 0, "ImmGetConversionStatus failed\n");
516 rc
= ImmSetConversionStatus(himc
, status
, sentence
);
517 ok(rc
!= 0, "ImmSetConversionStatus failed\n");
519 rc
= ImmGetConversionStatus(otherHimc
, &status
, &sentence
);
520 ok(rc
!= 0 || broken(rc
== 0), "ImmGetConversionStatus failed\n");
521 rc
= ImmSetConversionStatus(otherHimc
, status
, sentence
);
522 todo_wine
ok(rc
== 0, "ImmSetConversionStatus should fail\n");
524 /* StatusWindowPos */
525 rc
= ImmSetStatusWindowPos(himc
, &pt
);
526 ok(rc
!= 0, "ImmSetStatusWindowPos failed\n");
527 rc
= ImmGetStatusWindowPos(himc
, &pt
);
528 ok(rc
!= 0, "ImmGetStatusWindowPos failed\n");
530 rc
= ImmSetStatusWindowPos(otherHimc
, &pt
);
531 todo_wine
ok(rc
== 0, "ImmSetStatusWindowPos should fail\n");
532 rc
= ImmGetStatusWindowPos(otherHimc
, &pt
);
533 ok(rc
!= 0 || broken(rc
== 0), "ImmGetStatusWindowPos failed\n");
535 ImmReleaseContext(threadinfo
.hwnd
,otherHimc
);
536 ImmReleaseContext(hwnd
,himc
);
538 DestroyWindow(threadinfo
.hwnd
);
539 TerminateThread(hThread
, 1);
541 himc
= ImmGetContext(GetDesktopWindow());
542 todo_wine
ok(himc
== NULL
, "Should not be able to get himc from other process window\n");
545 static void test_ImmIsUIMessage(void)
553 static const struct test tests
[] =
555 { WM_MOUSEMOVE
, FALSE
},
556 { WM_IME_STARTCOMPOSITION
, TRUE
},
557 { WM_IME_ENDCOMPOSITION
, TRUE
},
558 { WM_IME_COMPOSITION
, TRUE
},
559 { WM_IME_SETCONTEXT
, TRUE
},
560 { WM_IME_NOTIFY
, TRUE
},
561 { WM_IME_CONTROL
, FALSE
},
562 { WM_IME_COMPOSITIONFULL
, TRUE
},
563 { WM_IME_SELECT
, TRUE
},
564 { WM_IME_CHAR
, FALSE
},
565 { 0x287 /* FIXME */, TRUE
},
566 { WM_IME_REQUEST
, FALSE
},
567 { WM_IME_KEYDOWN
, FALSE
},
568 { WM_IME_KEYUP
, FALSE
},
569 { 0, FALSE
} /* mark the end */
572 const struct test
*test
;
575 if (!pImmIsUIMessageA
) return;
577 for (test
= tests
; test
->msg
; test
++)
579 msg_spy_flush_msgs();
580 ret
= pImmIsUIMessageA(NULL
, test
->msg
, 0, 0);
581 ok(ret
== test
->ret
, "ImmIsUIMessageA returned %x for %x\n", ret
, test
->msg
);
582 ok(!msg_spy_find_msg(test
->msg
), "Windows does not send 0x%x for NULL hwnd\n", test
->msg
);
584 ret
= pImmIsUIMessageA(hwnd
, test
->msg
, 0, 0);
585 ok(ret
== test
->ret
, "ImmIsUIMessageA returned %x for %x\n", ret
, test
->msg
);
587 ok(msg_spy_find_msg(test
->msg
) != NULL
, "Windows does send 0x%x\n", test
->msg
);
589 ok(!msg_spy_find_msg(test
->msg
), "Windows does not send 0x%x\n", test
->msg
);
593 static void test_ImmGetContext(void)
598 SetLastError(0xdeadbeef);
599 himc
= ImmGetContext((HWND
)0xffffffff);
600 err
= GetLastError();
601 ok(himc
== NULL
, "ImmGetContext succeeded\n");
602 ok(err
== ERROR_INVALID_WINDOW_HANDLE
, "got %u\n", err
);
604 himc
= ImmGetContext(hwnd
);
605 ok(himc
!= NULL
, "ImmGetContext failed\n");
606 ok(ImmReleaseContext(hwnd
, himc
), "ImmReleaseContext failed\n");
609 static void test_ImmGetDescription(void)
612 WCHAR japime
[] = { 'E', '0', '0', '1', '0', '4', '1', '1', 0 };
617 /* FIXME: invalid keyboard layouts should not pass */
618 ret
= ImmGetDescriptionW(NULL
, NULL
, 0);
619 todo_wine
ok(!ret
, "ImmGetDescriptionW failed, expected 0 received %d.\n", ret
);
621 /* load a language with valid IMM descriptions */
622 hkl
= LoadKeyboardLayoutW(japime
, KLF_ACTIVATE
);
623 todo_wine
ok(hkl
!= 0, "LoadKeyboardLayoutW failed, expected != 0.\n");
625 ret
= ImmGetDescriptionW(hkl
, NULL
, 0);
628 win_skip("ImmGetDescriptionW is not working for current loaded keyboard.\n");
632 ret
= ImmGetDescriptionW(hkl
, descW
, 0);
633 ok(ret
, "ImmGetDescriptionW failed, expected != 0 received 0.\n");
635 lret
= ImmGetDescriptionW(hkl
, descW
, ret
+ 1);
636 ok(lret
, "ImmGetDescriptionW failed, expected != 0 received 0.\n");
637 ok(lret
== ret
, "ImmGetDescriptionW failed to return the correct amount of data. Expected %d, got %d.\n", ret
, lret
);
639 lret
= ImmGetDescriptionA(hkl
, descA
, ret
+ 1);
640 ok(lret
, "ImmGetDescriptionA failed, expected != 0 received 0.\n");
641 todo_wine
ok(lret
== ret
, "ImmGetDescriptionA failed to return the correct amount of data. Expected %d, got %d.\n", ret
, lret
);
643 ret
/= 2; /* try to copy partially */
644 lret
= ImmGetDescriptionW(hkl
, descW
, ret
+ 1);
645 ok(lret
, "ImmGetDescriptionW failed, expected != 0 received 0.\n");
646 ok(lret
== ret
, "ImmGetDescriptionW failed to return the correct amount of data. Expected %d, got %d.\n", ret
, lret
);
648 ret
= ImmGetDescriptionW(hkl
, descW
, 1);
649 ok(!ret
, "ImmGetDescriptionW failed, expected 0 received %d.\n", ret
);
651 UnloadKeyboardLayout(hkl
);
654 static void test_ImmDefaultHwnd(void)
656 HIMC imc1
, imc2
, imc3
;
660 hwnd
= CreateWindowEx(WS_EX_CLIENTEDGE
, "EDIT", "Wine imm32.dll test",
661 WS_OVERLAPPEDWINDOW
, CW_USEDEFAULT
, CW_USEDEFAULT
,
662 240, 120, NULL
, NULL
, GetModuleHandle(0), NULL
);
664 ShowWindow(hwnd
, SW_SHOWNORMAL
);
666 imc1
= ImmGetContext(hwnd
);
669 win_skip("IME support not implemented\n");
673 def1
= ImmGetDefaultIMEWnd(hwnd
);
675 imc2
= ImmCreateContext();
676 ImmSetOpenStatus(imc2
, TRUE
);
678 imc3
= ImmGetContext(hwnd
);
679 def3
= ImmGetDefaultIMEWnd(hwnd
);
681 ok(def3
== def1
, "Default IME window should not change\n");
682 ok(imc1
== imc3
, "IME context should not change\n");
683 ImmSetOpenStatus(imc2
, FALSE
);
685 ImmReleaseContext(hwnd
, imc1
);
686 ImmReleaseContext(hwnd
, imc3
);
687 ImmDestroyContext(imc2
);
691 static void test_ImmGetIMCLockCount(void)
697 imc
= ImmCreateContext();
698 count
= ImmGetIMCLockCount(imc
);
699 ok(count
== 0, "expect 0, returned %d\n", count
);
700 ic
= ImmLockIMC(imc
);
701 ok(ic
!= NULL
, "ImmLockIMC failed\n!");
702 count
= ImmGetIMCLockCount(imc
);
703 ok(count
== 1, "expect 1, returned %d\n", count
);
704 ret
= ImmUnlockIMC(imc
);
705 ok(ret
== TRUE
, "expect TRUE, ret %d\n", ret
);
706 count
= ImmGetIMCLockCount(imc
);
707 ok(count
== 0, "expect 0, returned %d\n", count
);
708 ret
= ImmUnlockIMC(imc
);
709 ok(ret
== TRUE
, "expect TRUE, ret %d\n", ret
);
710 count
= ImmGetIMCLockCount(imc
);
711 ok(count
== 0, "expect 0, returned %d\n", count
);
712 ImmDestroyContext(imc
);
715 static void test_ImmGetIMCCLockCount(void)
718 DWORD count
, g_count
, ret
, i
;
721 imcc
= ImmCreateIMCC(sizeof(CANDIDATEINFO
));
722 count
= ImmGetIMCCLockCount(imcc
);
723 ok(count
== 0, "expect 0, returned %d\n", count
);
725 count
= ImmGetIMCCLockCount(imcc
);
726 ok(count
== 1, "expect 1, returned %d\n", count
);
727 ret
= ImmUnlockIMCC(imcc
);
728 ok(ret
== FALSE
, "expect FALSE, ret %d\n", ret
);
729 count
= ImmGetIMCCLockCount(imcc
);
730 ok(count
== 0, "expect 0, returned %d\n", count
);
731 ret
= ImmUnlockIMCC(imcc
);
732 ok(ret
== FALSE
, "expect FALSE, ret %d\n", ret
);
733 count
= ImmGetIMCCLockCount(imcc
);
734 ok(count
== 0, "expect 0, returned %d\n", count
);
736 p
= ImmLockIMCC(imcc
);
737 ok(GlobalHandle(p
) == imcc
, "expect %p, returned %p\n", imcc
, GlobalHandle(p
));
739 for (i
= 0; i
< GMEM_LOCKCOUNT
* 2; i
++)
742 count
= ImmGetIMCCLockCount(imcc
);
743 g_count
= GlobalFlags(imcc
) & GMEM_LOCKCOUNT
;
744 ok(count
== g_count
, "count %d, g_count %d\n", count
, g_count
);
746 count
= ImmGetIMCCLockCount(imcc
);
747 ok(count
== GMEM_LOCKCOUNT
, "expect GMEM_LOCKCOUNT, returned %d\n", count
);
749 for (i
= 0; i
< GMEM_LOCKCOUNT
- 1; i
++)
751 count
= ImmGetIMCCLockCount(imcc
);
752 ok(count
== 1, "expect 1, returned %d\n", count
);
754 count
= ImmGetIMCCLockCount(imcc
);
755 ok(count
== 0, "expect 0, returned %d\n", count
);
757 ImmDestroyIMCC(imcc
);
760 static void test_ImmDestroyIMCC(void)
763 DWORD ret
, count
, size
;
766 imcc
= ImmCreateIMCC(sizeof(CANDIDATEINFO
));
767 count
= ImmGetIMCCLockCount(imcc
);
768 ok(count
== 0, "expect 0, returned %d\n", count
);
769 p
= ImmLockIMCC(imcc
);
770 ok(p
!= NULL
, "ImmLockIMCC failed!\n");
771 count
= ImmGetIMCCLockCount(imcc
);
772 ok(count
== 1, "expect 1, returned %d\n", count
);
773 size
= ImmGetIMCCSize(imcc
);
774 ok(size
== sizeof(CANDIDATEINFO
), "returned %d\n", size
);
775 p
= ImmDestroyIMCC(imcc
);
776 ok(p
== NULL
, "Destroy a locked IMCC should success!\n");
777 p
= ImmLockIMCC(imcc
);
778 ok(p
== NULL
, "Lock a destroyed IMCC should fail!\n");
779 ret
= ImmUnlockIMCC(imcc
);
780 ok(ret
== FALSE
, "Unlock a destroyed IMCC should return FALSE!\n");
781 count
= ImmGetIMCCLockCount(imcc
);
782 ok(count
== 0, "Get lock count of a destroyed IMCC should return 0!\n");
783 size
= ImmGetIMCCSize(imcc
);
784 ok(size
== 0, "Get size of a destroyed IMCC should return 0!\n");
785 SetLastError(0xdeadbeef);
786 p
= ImmDestroyIMCC(imcc
);
787 ok(p
!= NULL
, "returned NULL\n");
788 ret
= GetLastError();
789 ok(ret
== ERROR_INVALID_HANDLE
, "wrong last error %08x!\n", ret
);
792 static void test_ImmMessages(void)
800 HWND hwnd
= CreateWindowEx(WS_EX_CLIENTEDGE
, "EDIT", "Wine imm32.dll test",
801 WS_OVERLAPPEDWINDOW
, CW_USEDEFAULT
, CW_USEDEFAULT
,
802 240, 120, NULL
, NULL
, GetModuleHandle(0), NULL
);
804 ShowWindow(hwnd
, SW_SHOWNORMAL
);
805 defwnd
= ImmGetDefaultIMEWnd(hwnd
);
806 imc
= ImmGetContext(hwnd
);
808 ImmSetOpenStatus(imc
, TRUE
);
809 msg_spy_flush_msgs();
810 SendMessage(defwnd
, WM_IME_CONTROL
, IMC_GETCANDIDATEPOS
, (LPARAM
)&cf
);
813 msg
= msg_spy_find_next_msg(WM_IME_CONTROL
,&idx
);
814 if (msg
) ok(!msg
->post
, "Message should not be posted\n");
816 msg_spy_flush_msgs();
817 ImmSetOpenStatus(imc
, FALSE
);
818 ImmReleaseContext(hwnd
, imc
);
822 static LRESULT CALLBACK
processkey_wnd_proc( HWND hWnd
, UINT msg
, WPARAM wParam
,
825 return DefWindowProcW(hWnd
, msg
, wParam
, lParam
);
828 static void test_ime_processkey(void)
830 WCHAR classNameW
[] = {'P','r','o','c','e','s','s', 'K','e','y','T','e','s','t','C','l','a','s','s',0};
831 WCHAR windowNameW
[] = {'P','r','o','c','e','s','s', 'K','e','y',0};
835 HANDLE hInstance
= GetModuleHandleW(NULL
);
836 TEST_INPUT inputs
[2];
841 wclass
.lpszClassName
= classNameW
;
842 wclass
.style
= CS_HREDRAW
| CS_VREDRAW
;
843 wclass
.lpfnWndProc
= processkey_wnd_proc
;
844 wclass
.hInstance
= hInstance
;
845 wclass
.hIcon
= LoadIcon(0, IDI_APPLICATION
);
846 wclass
.hCursor
= LoadCursor( NULL
, IDC_ARROW
);
847 wclass
.hbrBackground
= (HBRUSH
)(COLOR_WINDOW
+ 1);
848 wclass
.lpszMenuName
= 0;
849 wclass
.cbClsExtra
= 0;
850 wclass
.cbWndExtra
= 0;
851 if(!RegisterClassW(&wclass
)){
852 win_skip("Failed to register window.\n");
856 /* create the test window that will receive the keystrokes */
857 hWndTest
= CreateWindowW(wclass
.lpszClassName
, windowNameW
,
858 WS_OVERLAPPEDWINDOW
, CW_USEDEFAULT
, 0, 100, 100,
859 NULL
, NULL
, hInstance
, NULL
);
861 ShowWindow(hWndTest
, SW_SHOW
);
862 SetWindowPos(hWndTest
, HWND_TOPMOST
, 0, 0, 0, 0, SWP_NOSIZE
|SWP_NOMOVE
);
863 SetForegroundWindow(hWndTest
);
864 UpdateWindow(hWndTest
);
866 imc
= ImmGetContext(hWndTest
);
869 win_skip("IME not supported\n");
870 DestroyWindow(hWndTest
);
874 rc
= ImmSetOpenStatus(imc
, TRUE
);
877 win_skip("Unable to open IME\n");
878 ImmReleaseContext(hWndTest
, imc
);
879 DestroyWindow(hWndTest
);
883 /* flush pending messages */
884 while (PeekMessageW(&msg
, 0, 0, 0, PM_REMOVE
)) DispatchMessageW(&msg
);
888 /* init input data that never changes */
889 inputs
[1].type
= inputs
[0].type
= INPUT_KEYBOARD
;
890 inputs
[1].u
.ki
.dwExtraInfo
= inputs
[0].u
.ki
.dwExtraInfo
= 0;
891 inputs
[1].u
.ki
.time
= inputs
[0].u
.ki
.time
= 0;
894 inputs
[0].u
.ki
.wVk
= 0x41;
895 inputs
[0].u
.ki
.wScan
= 0x1e;
896 inputs
[0].u
.ki
.dwFlags
= 0x0;
898 pSendInput(1, (INPUT
*)inputs
, sizeof(INPUT
));
900 while(PeekMessageW(&msg
, hWndTest
, 0, 0, PM_NOREMOVE
)) {
901 if(msg
.message
!= WM_KEYDOWN
)
902 PeekMessageW(&msg
, hWndTest
, 0, 0, PM_REMOVE
);
905 ok(msg
.wParam
!= VK_PROCESSKEY
,"Incorrect ProcessKey Found\n");
906 PeekMessageW(&msg
, hWndTest
, 0, 0, PM_REMOVE
);
907 if(msg
.wParam
== VK_PROCESSKEY
)
908 trace("ProcessKey was correctly found\n");
910 TranslateMessage(&msg
);
911 DispatchMessageW(&msg
);
914 inputs
[0].u
.ki
.wVk
= 0x41;
915 inputs
[0].u
.ki
.wScan
= 0x1e;
916 inputs
[0].u
.ki
.dwFlags
= KEYEVENTF_KEYUP
;
918 pSendInput(1, (INPUT
*)inputs
, sizeof(INPUT
));
920 while(PeekMessageW(&msg
, hWndTest
, 0, 0, PM_NOREMOVE
)) {
921 if(msg
.message
!= WM_KEYUP
)
922 PeekMessageW(&msg
, hWndTest
, 0, 0, PM_REMOVE
);
925 ok(msg
.wParam
!= VK_PROCESSKEY
,"Incorrect ProcessKey Found\n");
926 PeekMessageW(&msg
, hWndTest
, 0, 0, PM_REMOVE
);
927 ok(msg
.wParam
!= VK_PROCESSKEY
,"ProcessKey should still not be Found\n");
929 TranslateMessage(&msg
);
930 DispatchMessageW(&msg
);
933 ImmReleaseContext(hWndTest
, imc
);
934 ImmSetOpenStatus(imc
, FALSE
);
935 DestroyWindow(hWndTest
);
942 test_ImmGetCompositionString();
943 test_ImmSetCompositionString();
945 test_ImmAssociateContextEx();
947 test_ImmIsUIMessage();
948 test_ImmGetContext();
949 test_ImmGetDescription();
950 test_ImmDefaultHwnd();
951 test_ImmGetIMCLockCount();
952 test_ImmGetIMCCLockCount();
953 test_ImmDestroyIMCC();
955 /* Reinitialize the hooks to capture all windows */
960 test_ime_processkey();
961 else win_skip("SendInput is not available\n");