1 /*
2  * PROJECT:         ReactOS api tests
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * PURPOSE:         Test for AttachThreadInput
5  * PROGRAMMERS:     Giannis Adamopoulos
6  */
7 
8 #include "precomp.h"
9 
10 typedef struct {
11     DWORD tid;
12     HANDLE hThread;
13     HWND hWnd;
14     WCHAR* Desktop;
15     HANDLE StartEvent;
16     HANDLE QueueStatusEvent;
17     DWORD LastQueueStatus;
18 
19     MSG_CACHE cache;
20 } THREAD_DATA;
21 
22 static THREAD_DATA data[6];
23 static HHOOK hMouseHookLL = NULL;
24 static HHOOK hKbdHookLL = NULL;
25 
26 
27 #define EXPECT_FOREGROUND(expected) ok(GetForegroundWindow() == expected, \
28                                        "Expected hwnd%d at the foreground, got hwnd%d\n", \
29                                        get_iwnd(expected), get_iwnd(GetForegroundWindow()));
30 
31 #define EXPECT_ACTIVE(expected) ok(GetActiveWindow() == expected, \
32                                    "Expected hwnd%d to be active, got hwnd%d\n", \
33                                    get_iwnd(expected), get_iwnd(GetActiveWindow()));
34 
35 /*
36  *  Helper functions
37  */
38 
39 static int get_iwnd(HWND hWnd)
40 {
41     if(hWnd == data[0].hWnd) return 0;
42     else if(hWnd == data[1].hWnd) return 1;
43     else if(hWnd == data[2].hWnd) return 2;
44     else if(hWnd == data[3].hWnd) return 3;
45     else if(hWnd == data[4].hWnd) return 4;
46     else return -1;
47 }
48 
49 LRESULT CALLBACK TestProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
50 {
51     int iwnd = get_iwnd(hWnd);
52 
53     if(iwnd >= 0 && message > 0 && message < WM_APP && message != WM_TIMER)
54         record_message(&data[iwnd].cache, iwnd, message, SENT, wParam,0);
55 
56     return DefWindowProc(hWnd, message, wParam, lParam);
57 }
58 
59 static void FlushMessages()
60 {
61     MSG msg;
62     LRESULT res;
63 
64     while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE ))
65     {
66         int iwnd = get_iwnd(msg.hwnd);
67         if( iwnd >= 0 && msg.message > 0 && msg.message < WM_APP && msg.message != WM_TIMER)
68             record_message(&data[0].cache, iwnd, msg.message, POST, msg.wParam,0);
69         DispatchMessageA( &msg );
70     }
71 
72     /* Use SendMessage to sync with the other queues */
73     res = SendMessageTimeout(data[1].hWnd, WM_APP, 0,0, SMTO_NORMAL, 1000, NULL);
74     ok (res != ERROR_TIMEOUT, "SendMessageTimeout timed out\n");
75     res = SendMessageTimeout(data[2].hWnd, WM_APP, 0,0, SMTO_NORMAL, 1000, NULL);
76     ok (res != ERROR_TIMEOUT, "SendMessageTimeout timed out\n");
77     res = SendMessageTimeout(data[3].hWnd, WM_APP, 0,0, SMTO_NORMAL, 1000, NULL);
78     ok (res != ERROR_TIMEOUT, "SendMessageTimeout timed out\n");
79     res = SendMessageTimeout(data[4].hWnd, WM_APP, 0,0, SMTO_NORMAL, 1000, NULL);
80     ok (res != ERROR_TIMEOUT, "SendMessageTimeout timed out\n");
81 }
82 
83 static DWORD WINAPI thread_proc(void *param)
84 {
85     THREAD_DATA* current_data = (THREAD_DATA*)param;
86     MSG msg;
87     HDESK hdesk = NULL;
88     int iwnd;
89 
90     if(current_data->Desktop)
91     {
92         hdesk = CreateDesktopW(current_data->Desktop, NULL, NULL, 0, DESKTOP_ALL_ACCESS, NULL );
93         SetThreadDesktop(hdesk);
94     }
95 
96     /* create test window */
97     current_data->hWnd = CreateWindowW(L"TestClass", L"test", WS_OVERLAPPEDWINDOW,
98                                        100, 100, 500, 500, NULL, NULL, 0, NULL);
99     SetEvent( current_data->StartEvent );
100 
101     iwnd = get_iwnd(current_data->hWnd);
102 
103     /* Use MsgWaitForMultipleObjects to let the thread process apcs */
104     while( GetMessage(&msg, 0,0,0) )
105     {
106         if(msg.message > 0 && msg.message < WM_APP && msg.message != WM_TIMER )
107             record_message(&data[iwnd].cache, iwnd, msg.message, POST, msg.wParam,0);
108         DispatchMessage(&msg);
109     }
110 
111     if(hdesk)
112         CloseDesktop(hdesk);
113 
114     return 0;
115 }
116 
117 BOOL CreateTestThread(int i, WCHAR* Desktop)
118 {
119     DWORD ret;
120 
121     data[i].StartEvent = CreateEventW(NULL, 0, 0, NULL);
122     data[i].Desktop = Desktop;
123     data[i].hThread = CreateThread(NULL, 0, thread_proc, &data[i], 0, &data[i].tid);
124     if(!data[i].hThread) goto fail;
125     ret = WaitForSingleObject(data[i].StartEvent, 1000);
126     CloseHandle(data[i].StartEvent);
127     if(ret == WAIT_TIMEOUT)
128     {
129 fail:
130         win_skip("child thread failed to initialize\n");
131         return FALSE;
132     }
133     return TRUE;
134 }
135 
136 static LRESULT CALLBACK MouseLLHookProc(int nCode, WPARAM wParam, LPARAM lParam)
137 {
138     LRESULT ret;
139     MSLLHOOKSTRUCT* params = (MSLLHOOKSTRUCT*) lParam;
140 
141     ret = CallNextHookEx(hMouseHookLL, nCode, wParam, lParam);
142 
143     if((params->flags & LLKHF_INJECTED) == 0)
144         return TRUE;
145 
146     return ret;
147 }
148 
149 LRESULT CALLBACK KbdLLHookProc(int nCode, WPARAM wParam, LPARAM lParam)
150 {
151     LRESULT ret;
152     KBDLLHOOKSTRUCT* params = (KBDLLHOOKSTRUCT*) lParam;
153 
154     ret = CallNextHookEx(hMouseHookLL, nCode, wParam, lParam);
155 
156     if((params->flags & LLKHF_INJECTED) == 0)
157         return TRUE;
158 
159     return ret;
160 }
161 
162 BOOLEAN InitThreads()
163 {
164     /* Create a LL hook that drops any physical keyboard and mouse action
165        and prevent the user from interfering with the test results */
166     if(!IsDebuggerPresent())
167     {
168         hMouseHookLL = SetWindowsHookExW(WH_MOUSE_LL, MouseLLHookProc, GetModuleHandleW( NULL ), 0);
169         ok(hMouseHookLL!=NULL,"failed to set hook\n");
170         hKbdHookLL = SetWindowsHookExW(WH_KEYBOARD_LL, KbdLLHookProc, GetModuleHandleW( NULL ), 0);
171         ok(hKbdHookLL!=NULL,"failed to set hook\n");
172     }
173 
174     /* create test clases */
175     RegisterSimpleClass(TestProc, L"TestClass");
176 
177     memset(&data[0], 0, sizeof(data[0]));
178 
179     data[0].tid = GetCurrentThreadId();
180 
181     /* create test window */
182     data[0].hWnd = CreateWindowW(L"TestClass", L"test", WS_OVERLAPPEDWINDOW,
183                                  100, 100, 500, 500, NULL, NULL, 0, NULL);
184     if(!data[0].hWnd)
185     {
186         win_skip("CreateWindowW failed\n");
187         return FALSE;
188     }
189 
190     /* create thread1(same desktop) */
191     if(!CreateTestThread(1, NULL)) return FALSE;
192 
193     /* create thread2(same desktop) */
194     if(!CreateTestThread(2, NULL)) return FALSE;
195 
196     /* ugly ros hack to bypass desktop crapiness */
197     if(!CreateTestThread(6, L"ThreadTestDesktop")) return FALSE;
198 
199     /* create thread3(different desktop) */
200     if(!CreateTestThread(3, L"ThreadTestDesktop")) return FALSE;
201 
202     /* create thread4(different desktop) */
203     if(!CreateTestThread(4, L"ThreadTestDesktop")) return FALSE;
204 
205     return TRUE;
206 }
207 
208 static void cleanup_attachments()
209 {
210     int i,j;
211     BOOL ret;
212 
213     for(i = 0; i< 4; i++)
214     {
215         for(j = 0; j< 4; j++)
216         {
217             ret = AttachThreadInput(data[i].tid,data[j].tid, FALSE);
218             ok(ret==0, "expected AttachThreadInput to fail\n");
219         }
220     }
221 }
222 
223 
224 
225 
226 /*
227  *  The actual tests
228  */
229 
230 void Test_SimpleParameters()
231 {
232     BOOL ret;
233     /* FIXME: acording to msdn xp doesn't set last error but vista+ does*/
234 
235     /* test wrong thread */
236     ret = AttachThreadInput( 0, 1, TRUE);
237     ok(ret==0, "expected AttachThreadInput to fail\n");
238 
239     /* test same thread */
240     ret = AttachThreadInput( data[1].tid, data[1].tid, TRUE);
241     ok(ret==0, "expected AttachThreadInput to fail\n");
242 
243     /* try to attach to a thread on another desktop*/
244     ret = AttachThreadInput( data[2].tid,data[3].tid, TRUE);
245     ok(ret==0, "expected AttachThreadInput to fail\n");
246     if(ret == 1 )
247         AttachThreadInput( data[2].tid,data[3].tid, FALSE);
248 
249     /* test other desktop to this */
250     ret = AttachThreadInput( data[3].tid,data[2].tid, TRUE);
251     ok(ret==0, "expected AttachThreadInput to fail\n");
252     if(ret == 1 )
253         AttachThreadInput( data[3].tid,data[2].tid, FALSE);
254 
255     /* attach two threads that are both in ThreadTestDesktop */
256     {
257         /* Attach thread 3 and 4 */
258         ret = AttachThreadInput( data[3].tid,data[4].tid, TRUE);
259         ok(ret==1, "expected AttachThreadInput to succeed\n");
260 
261         /* cleanup previous attachment */
262         ret = AttachThreadInput( data[3].tid,data[4].tid, FALSE);
263         ok(ret==1, "expected AttachThreadInput to succeed\n");
264     }
265 
266     {
267         /* Attach thread 1 and 2 */
268         ret = AttachThreadInput( data[1].tid,data[2].tid, TRUE);
269         ok(ret==1, "expected AttachThreadInput to succeed\n");
270 
271         /* attach already attached*/
272         ret = AttachThreadInput( data[1].tid,data[2].tid, TRUE);
273         ok(ret==1, "expected AttachThreadInput to succeed\n");
274 
275         /* attach in the opposite order */
276         ret = AttachThreadInput( data[2].tid,data[1].tid, TRUE);
277         ok(ret==1, "expected AttachThreadInput to succeed\n");
278 
279         /* Now try to detach 0 from 1 */
280         ret = AttachThreadInput( data[0].tid,data[1].tid, FALSE);
281         ok(ret==0, "expected AttachThreadInput to fail\n");
282 
283         /* also try to detach 3 from 2 */
284         ret = AttachThreadInput( data[3].tid,data[2].tid, FALSE);
285         ok(ret==0, "expected AttachThreadInput to fail\n");
286 
287         /* cleanup previous attachment */
288         ret = AttachThreadInput( data[1].tid,data[2].tid, FALSE);
289         ok(ret==1, "expected AttachThreadInput to succeed\n");
290 
291         ret = AttachThreadInput( data[2].tid,data[1].tid, FALSE);
292         ok(ret==1, "expected AttachThreadInput to succeed\n");
293 
294         ret = AttachThreadInput( data[1].tid,data[2].tid, FALSE);
295         ok(ret==1, "expected AttachThreadInput to succeed\n");
296     }
297 
298     /* test triple attach */
299     {
300         ret = AttachThreadInput( data[0].tid, data[1].tid, TRUE);
301         ok(ret==1, "expected AttachThreadInput to succeed\n");
302         ret = AttachThreadInput( data[1].tid, data[2].tid, TRUE);
303         ok(ret==1, "expected AttachThreadInput to succeed\n");
304 
305         /* try to detach 2 and 0 */
306         ret = AttachThreadInput( data[0].tid, data[2].tid, FALSE);
307         ok(ret==0, "expected AttachThreadInput to fail\n");
308         ret = AttachThreadInput( data[2].tid, data[0].tid, FALSE);
309         ok(ret==0, "expected AttachThreadInput to fail\n");
310 
311         /* try to to attach 0 to 2. it works! */
312         ret = AttachThreadInput( data[0].tid, data[2].tid, TRUE);
313         ok(ret==1, "expected AttachThreadInput to succeed\n");
314 
315         ret = AttachThreadInput( data[0].tid, data[2].tid, FALSE);
316         ok(ret==1, "expected AttachThreadInput to succeed\n");
317 
318         /* detach in inverse order */
319         ret = AttachThreadInput( data[0].tid, data[1].tid, FALSE);
320         ok(ret==1, "expected AttachThreadInput to succeed\n");
321         ret = AttachThreadInput( data[1].tid, data[2].tid, FALSE);
322         ok(ret==1, "expected AttachThreadInput to succeed\n");
323     }
324 
325     /* test detaching in thread cleanup */
326     {
327         ret = AttachThreadInput( data[0].tid, data[1].tid, TRUE);
328         ok(ret==1, "expected AttachThreadInput to succeed\n");
329         ret = AttachThreadInput( data[0].tid, data[1].tid, TRUE);
330         ok(ret==1, "expected AttachThreadInput to succeed\n");
331         ret = AttachThreadInput( data[1].tid, data[2].tid, TRUE);
332         ok(ret==1, "expected AttachThreadInput to succeed\n");
333         ret = AttachThreadInput( data[1].tid, data[2].tid, TRUE);
334         ok(ret==1, "expected AttachThreadInput to succeed\n");
335 
336         TerminateThread(data[1].hThread, 0);
337 
338         ret = AttachThreadInput( data[0].tid, data[1].tid, FALSE);
339         ok(ret==0, "expected AttachThreadInput to fail\n");
340         ret = AttachThreadInput( data[1].tid, data[2].tid, FALSE);
341         ok(ret==0, "expected AttachThreadInput to fail\n");
342 
343         /* Create Thread1 again */
344         CreateTestThread(1, NULL);
345     }
346 
347 }
348 
349 void Test_Focus() //Focus Active Capture Foreground Capture
350 {
351     BOOL ret;
352 
353     trace("Thread hWnd0 0x%p hWnd1 0x%p\n",data[0].hWnd, data[1].hWnd);
354     /* Window 1 is in the foreground */
355     SetForegroundWindow(data[1].hWnd);
356     SetActiveWindow(data[0].hWnd);
357     FlushMessages();
358 
359     EXPECT_FOREGROUND(data[1].hWnd);
360     EXPECT_ACTIVE(data[0].hWnd);
361 
362     /* attach thread 0 to 1 */
363     {
364         ret = AttachThreadInput( data[0].tid, data[1].tid , TRUE);
365         ok(ret==1, "expected AttachThreadInput to succeed\n");
366         FlushMessages();
367 
368         EXPECT_FOREGROUND(data[1].hWnd);
369         EXPECT_ACTIVE(data[1].hWnd);
370 
371         ret = AttachThreadInput( data[0].tid, data[1].tid , FALSE);
372         ok(ret==1, "expected AttachThreadInput to succeed\n");
373     }
374 
375     EXPECT_FOREGROUND(data[1].hWnd);
376     EXPECT_ACTIVE(0);
377 
378     SetForegroundWindow(data[1].hWnd);
379     SetActiveWindow(data[0].hWnd);
380     FlushMessages();
381 
382     EXPECT_FOREGROUND(data[1].hWnd);
383     EXPECT_ACTIVE(data[0].hWnd);
384 
385     /* attach thread 1 to 0 */
386     {
387         ret = AttachThreadInput( data[1].tid, data[0].tid , TRUE);
388         ok(ret==1, "expected AttachThreadInput to succeed\n");
389         FlushMessages();
390 
391         EXPECT_FOREGROUND(data[1].hWnd);
392         EXPECT_ACTIVE(data[1].hWnd);
393 
394         ret = AttachThreadInput( data[1].tid, data[0].tid , FALSE);
395         ok(ret==1, "expected AttachThreadInput to succeed\n");
396     }
397 
398     /* Window 0 is in the foreground */
399     SetForegroundWindow(data[0].hWnd);
400     SetActiveWindow(data[1].hWnd);
401     FlushMessages();
402 
403     EXPECT_FOREGROUND(data[0].hWnd);
404     EXPECT_ACTIVE(data[0].hWnd);
405 
406     /* attach thread 0 to 1 */
407     {
408         ret = AttachThreadInput( data[0].tid, data[1].tid , TRUE);
409         ok(ret==1, "expected AttachThreadInput to succeed\n");
410         FlushMessages();
411 
412         EXPECT_FOREGROUND(data[0].hWnd);
413         EXPECT_ACTIVE(data[0].hWnd);
414 
415         SetForegroundWindow(data[0].hWnd);
416         SetActiveWindow(data[1].hWnd);
417         FlushMessages();
418 
419         EXPECT_FOREGROUND(data[1].hWnd);
420         EXPECT_ACTIVE(data[1].hWnd);
421 
422         ret = AttachThreadInput( data[0].tid, data[1].tid , FALSE);
423         ok(ret==1, "expected AttachThreadInput to succeed\n");
424     }
425 
426     EXPECT_FOREGROUND(data[1].hWnd);
427     EXPECT_ACTIVE(0);
428 
429     SetForegroundWindow(data[0].hWnd);
430     SetActiveWindow(data[1].hWnd);
431     FlushMessages();
432 
433     EXPECT_FOREGROUND(data[0].hWnd);
434     EXPECT_ACTIVE(data[0].hWnd);
435 
436     /* attach thread 1 to 0 */
437     {
438         ret = AttachThreadInput( data[1].tid, data[0].tid , TRUE);
439         ok(ret==1, "expected AttachThreadInput to succeed\n");
440         FlushMessages();
441 
442         EXPECT_FOREGROUND(data[0].hWnd);
443         EXPECT_ACTIVE(data[0].hWnd);
444 
445         SetForegroundWindow(data[0].hWnd);
446         SetActiveWindow(data[1].hWnd);
447         FlushMessages();
448 
449         EXPECT_FOREGROUND(data[1].hWnd);
450         EXPECT_ACTIVE(data[1].hWnd);
451 
452         ret = AttachThreadInput( data[1].tid, data[0].tid , FALSE);
453         ok(ret==1, "expected AttachThreadInput to succeed\n");
454     }
455 }
456 
457 /* test some functions like PostMessage and SendMessage that shouldn't be affected */
458 void Test_UnaffectedMessages()
459 {
460     BOOL ret;
461     LRESULT res;
462 
463     EMPTY_CACHE_(&data[0].cache);
464     EMPTY_CACHE_(&data[1].cache);
465 
466     /* test that messages posted before and after attachment are unaffected
467        and that we don't receive a meassage from a window we shouldn't */
468     PostMessage(data[0].hWnd, WM_USER, 0,0);
469     PostMessage(data[1].hWnd, WM_USER, 1,0);
470 
471     {
472         MSG_ENTRY Thread0_chain[]={
473           {0,WM_USER, POST, 0, 0},
474           {0,WM_USER, POST, 2, 0},
475           {0,0}};
476         MSG_ENTRY Thread1_chain[]={
477           {1,WM_USER, POST, 1, 0},
478           {1,WM_USER, POST, 3, 0},
479           {0,0}};
480 
481         ret = AttachThreadInput( data[1].tid, data[0].tid , TRUE);
482         ok(ret==1, "expected AttachThreadInput to succeed\n");
483 
484         PostMessage(data[0].hWnd, WM_USER, 2,0);
485         PostMessage(data[1].hWnd, WM_USER, 3,0);
486 
487         FlushMessages();
488         Sleep(100);
489 
490         COMPARE_CACHE_(&data[0].cache, Thread0_chain);
491         COMPARE_CACHE_(&data[1].cache, Thread1_chain);
492 
493         ret = AttachThreadInput( data[1].tid, data[0].tid , FALSE);
494         ok(ret==1, "expected AttachThreadInput to succeed\n");
495     }
496 
497     /* test messages send to the wrong thread */
498     res = SendMessageTimeout(data[0].hWnd, WM_USER, 0,0, SMTO_NORMAL, 1000, NULL);
499     ok (res != ERROR_TIMEOUT, "SendMessageTimeout timed out\n");
500     res = SendMessageTimeout(data[1].hWnd, WM_USER, 1,0, SMTO_NORMAL, 1000, NULL);
501     ok (res != ERROR_TIMEOUT, "SendMessageTimeout timed out\n");
502 
503     {
504         MSG_ENTRY Thread0_chain[]={
505           {0,WM_USER, SENT, 0, 0},
506           {0,WM_USER, SENT, 2, 0},
507           {0,0}};
508         MSG_ENTRY Thread1_chain[]={
509           {1,WM_USER, SENT, 1, 0},
510           {1,WM_USER, SENT, 3, 0},
511           {1,WM_MOUSEMOVE, SENT, 0, 0},
512           {0,0}};
513 
514         ret = AttachThreadInput( data[2].tid, data[1].tid , TRUE);
515         ok(ret==1, "expected AttachThreadInput to succeed\n");
516 
517         res = SendMessageTimeout(data[0].hWnd, WM_USER, 2,0, SMTO_NORMAL, 1000, NULL);
518         ok (res != ERROR_TIMEOUT, "SendMessageTimeout timed out\n");
519         res = SendMessageTimeout(data[1].hWnd, WM_USER, 3,0, SMTO_NORMAL, 1000, NULL);
520         ok (res != ERROR_TIMEOUT, "SendMessageTimeout timed out\n");
521 
522         /* Try to send a fake input message */
523         res = SendMessageTimeout(data[1].hWnd, WM_MOUSEMOVE, 0,0, SMTO_NORMAL, 1000, NULL);
524         ok (res != ERROR_TIMEOUT, "SendMessageTimeout timed out\n");
525 
526         COMPARE_CACHE_(&data[0].cache, Thread0_chain);
527         COMPARE_CACHE_(&data[1].cache, Thread1_chain);
528 
529         ret = AttachThreadInput( data[2].tid, data[1].tid , FALSE);
530         ok(ret==1, "expected AttachThreadInput to succeed\n");
531     }
532 
533     /* todo: test keyboard layout that shouldn't be affected */
534 }
535 
536 void Test_SendInput()
537 {
538     MSG_ENTRY Thread1_chain[]={
539           {1,WM_KEYDOWN, POST, VK_SHIFT, 0},
540           {1,WM_KEYUP, POST, VK_SHIFT, 0},
541           {0,0}};
542     MSG_ENTRY Thread0_chain[]={
543           {0,WM_KEYDOWN, POST, VK_SHIFT, 0},
544           {0,WM_KEYUP, POST, VK_SHIFT, 0},
545           {0,0}};
546 
547     BOOL ret;
548 
549     //trace("Thread hWnd0 0x%p hWnd1 0x%p\n",data[0].hWnd, data[1].hWnd);
550 
551     /* First try sending input without attaching. It will go to the foreground */
552     {
553         SetForegroundWindow(data[1].hWnd);
554         SetActiveWindow(data[0].hWnd);
555 
556         ok(GetForegroundWindow() == data[1].hWnd, "wrong foreground got 0x%p\n",GetForegroundWindow());
557         ok(GetActiveWindow() == data[0].hWnd, "wrong active got 0x%p\n",GetActiveWindow());
558 
559         FlushMessages();
560         EMPTY_CACHE_(&data[0].cache);
561         EMPTY_CACHE_(&data[1].cache);
562 
563         keybd_event(VK_SHIFT, 0,0,0);
564         keybd_event(VK_SHIFT, 0,KEYEVENTF_KEYUP,0);
565         Sleep(100);
566         FlushMessages();
567 
568         COMPARE_CACHE_(&data[0].cache, empty_chain);
569         COMPARE_CACHE_(&data[1].cache, Thread1_chain);
570     }
571 
572     /* Next attach and send input. It will go to the same thread as before */
573     { //                          from           to
574         ret = AttachThreadInput( data[1].tid, data[0].tid , TRUE);
575         ok(ret==1, "expected AttachThreadInput to succeed\n");
576 
577         FlushMessages();
578         EMPTY_CACHE_(&data[0].cache);
579         EMPTY_CACHE_(&data[1].cache);
580 
581         keybd_event(VK_SHIFT, 0,0,0);
582         keybd_event(VK_SHIFT, 0,KEYEVENTF_KEYUP,0);
583         Sleep(100);
584         FlushMessages();
585 
586         COMPARE_CACHE_(&data[0].cache, empty_chain);
587         COMPARE_CACHE_(&data[1].cache, Thread1_chain);
588     }
589 
590     /* Now set foreground and active again. Input will go to thread 0 */
591     {
592         SetForegroundWindow(data[1].hWnd);
593         SetActiveWindow(data[0].hWnd);
594         FlushMessages();
595 
596         ok(GetForegroundWindow() == data[0].hWnd, "wrong foreground got 0x%p\n",GetForegroundWindow());
597         ok(GetActiveWindow() == data[0].hWnd, "wrong active got 0x%p\n",GetActiveWindow());
598 
599         EMPTY_CACHE_(&data[0].cache);
600         EMPTY_CACHE_(&data[1].cache);
601 
602         keybd_event(VK_SHIFT, 0,0,0);
603         keybd_event(VK_SHIFT, 0,KEYEVENTF_KEYUP,0);
604         Sleep(100);
605         FlushMessages();
606 
607         COMPARE_CACHE_(&data[0].cache, Thread0_chain);
608         COMPARE_CACHE_(&data[1].cache, empty_chain);
609 
610         ret = AttachThreadInput( data[1].tid, data[0].tid , FALSE);
611         ok(ret==1, "expected AttachThreadInput to succeed\n");
612     }
613 
614     /* Attach in the opposite order and send input */
615     {
616         ret = AttachThreadInput( data[0].tid, data[1].tid , TRUE);
617         ok(ret==1, "expected AttachThreadInput to succeed\n");
618 
619         FlushMessages();
620         EMPTY_CACHE_(&data[0].cache);
621         EMPTY_CACHE_(&data[1].cache);
622 
623         keybd_event(VK_SHIFT, 0,0,0);
624         keybd_event(VK_SHIFT, 0,KEYEVENTF_KEYUP,0);
625         Sleep(100);
626         FlushMessages();
627 
628         COMPARE_CACHE_(&data[0].cache, Thread0_chain);
629         COMPARE_CACHE_(&data[1].cache, empty_chain);
630     }
631 
632     /* Now set foreground and active again. Input will go to thread 0 */
633     {
634         SetForegroundWindow(data[1].hWnd);
635         SetActiveWindow(data[0].hWnd);
636         FlushMessages();
637 
638         ok(GetForegroundWindow() == data[0].hWnd, "wrong foreground got 0x%p\n",GetForegroundWindow());
639         ok(GetActiveWindow() == data[0].hWnd, "wrong active got 0x%p\n",GetActiveWindow());
640 
641         EMPTY_CACHE_(&data[0].cache);
642         EMPTY_CACHE_(&data[1].cache);
643 
644         keybd_event(VK_SHIFT, 0,0,0);
645         keybd_event(VK_SHIFT, 0,KEYEVENTF_KEYUP,0);
646         Sleep(100);
647         FlushMessages();
648 
649         COMPARE_CACHE_(&data[0].cache, Thread0_chain);
650         COMPARE_CACHE_(&data[1].cache, empty_chain);
651 
652         ret = AttachThreadInput( data[0].tid, data[1].tid , FALSE);
653         ok(ret==1, "expected AttachThreadInput to succeed\n");
654     }
655 }
656 
657 START_TEST(AttachThreadInput)
658 {
659     if(!InitThreads())
660         return;
661 
662     Test_SimpleParameters();
663     cleanup_attachments();
664     Test_Focus();
665     cleanup_attachments();
666     Test_UnaffectedMessages();
667     cleanup_attachments();
668     Test_SendInput();
669     cleanup_attachments();
670 
671     if(hMouseHookLL)
672         UnhookWindowsHookEx(hMouseHookLL);
673     if(hKbdHookLL)
674         UnhookWindowsHookEx(hKbdHookLL);
675 
676     /* Stop all threads and exit gratefully */
677     PostThreadMessage(data[1].tid, WM_QUIT,0,0);
678     PostThreadMessage(data[2].tid, WM_QUIT,0,0);
679     PostThreadMessage(data[3].tid, WM_QUIT,0,0);
680     PostThreadMessage(data[4].tid, WM_QUIT,0,0);
681 }
682 
683