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