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     /* create thread3(different desktop) */
197     if(!CreateTestThread(3, L"ThreadTestDesktop")) return FALSE;
198 
199     /* create thread4(different desktop) */
200     if(!CreateTestThread(4, L"ThreadTestDesktop")) return FALSE;
201 
202     return TRUE;
203 }
204 
205 static void cleanup_attachments()
206 {
207     int i,j;
208     BOOL ret;
209 
210     for(i = 0; i< 4; i++)
211     {
212         for(j = 0; j< 4; j++)
213         {
214             ret = AttachThreadInput(data[i].tid,data[j].tid, FALSE);
215             ok(ret==0, "expected AttachThreadInput to fail\n");
216         }
217     }
218 }
219 
220 
221 
222 
223 /*
224  *  The actual tests
225  */
226 
227 void Test_SimpleParameters()
228 {
229     BOOL ret;
230     /* FIXME: acording to msdn xp doesn't set last error but vista+ does*/
231 
232     /* test wrong thread */
233     ret = AttachThreadInput( 0, 1, TRUE);
234     ok(ret==0, "expected AttachThreadInput to fail\n");
235 
236     /* test same thread */
237     ret = AttachThreadInput( data[1].tid, data[1].tid, TRUE);
238     ok(ret==0, "expected AttachThreadInput to fail\n");
239 
240     /* try to attach to a thread on another desktop*/
241     ret = AttachThreadInput( data[2].tid,data[3].tid, TRUE);
242     ok(ret==0, "expected AttachThreadInput to fail\n");
243     if(ret == 1 )
244         AttachThreadInput( data[2].tid,data[3].tid, FALSE);
245 
246     /* test other desktop to this */
247     ret = AttachThreadInput( data[3].tid,data[2].tid, TRUE);
248     ok(ret==0, "expected AttachThreadInput to fail\n");
249     if(ret == 1 )
250         AttachThreadInput( data[3].tid,data[2].tid, FALSE);
251 
252     /* attach two threads that are both in ThreadTestDesktop */
253     {
254         /* Attach thread 3 and 4 */
255         ret = AttachThreadInput( data[3].tid,data[4].tid, TRUE);
256         ok(ret==1, "expected AttachThreadInput to succeed\n");
257 
258         /* cleanup previous attachment */
259         ret = AttachThreadInput( data[3].tid,data[4].tid, FALSE);
260         ok(ret==1, "expected AttachThreadInput to succeed\n");
261     }
262 
263     {
264         /* Attach thread 1 and 2 */
265         ret = AttachThreadInput( data[1].tid,data[2].tid, TRUE);
266         ok(ret==1, "expected AttachThreadInput to succeed\n");
267 
268         /* attach already attached*/
269         ret = AttachThreadInput( data[1].tid,data[2].tid, TRUE);
270         ok(ret==1, "expected AttachThreadInput to succeed\n");
271 
272         /* attach in the opposite order */
273         ret = AttachThreadInput( data[2].tid,data[1].tid, TRUE);
274         ok(ret==1, "expected AttachThreadInput to succeed\n");
275 
276         /* Now try to detach 0 from 1 */
277         ret = AttachThreadInput( data[0].tid,data[1].tid, FALSE);
278         ok(ret==0, "expected AttachThreadInput to fail\n");
279 
280         /* also try to detach 3 from 2 */
281         ret = AttachThreadInput( data[3].tid,data[2].tid, FALSE);
282         ok(ret==0, "expected AttachThreadInput to fail\n");
283 
284         /* cleanup previous attachment */
285         ret = AttachThreadInput( data[1].tid,data[2].tid, FALSE);
286         ok(ret==1, "expected AttachThreadInput to succeed\n");
287 
288         ret = AttachThreadInput( data[2].tid,data[1].tid, FALSE);
289         ok(ret==1, "expected AttachThreadInput to succeed\n");
290 
291         ret = AttachThreadInput( data[1].tid,data[2].tid, FALSE);
292         ok(ret==1, "expected AttachThreadInput to succeed\n");
293     }
294 
295     /* test triple attach */
296     {
297         ret = AttachThreadInput( data[0].tid, data[1].tid, TRUE);
298         ok(ret==1, "expected AttachThreadInput to succeed\n");
299         ret = AttachThreadInput( data[1].tid, data[2].tid, TRUE);
300         ok(ret==1, "expected AttachThreadInput to succeed\n");
301 
302         /* try to detach 2 and 0 */
303         ret = AttachThreadInput( data[0].tid, data[2].tid, FALSE);
304         ok(ret==0, "expected AttachThreadInput to fail\n");
305         ret = AttachThreadInput( data[2].tid, data[0].tid, FALSE);
306         ok(ret==0, "expected AttachThreadInput to fail\n");
307 
308         /* try to to attach 0 to 2. it works! */
309         ret = AttachThreadInput( data[0].tid, data[2].tid, TRUE);
310         ok(ret==1, "expected AttachThreadInput to succeed\n");
311 
312         ret = AttachThreadInput( data[0].tid, data[2].tid, FALSE);
313         ok(ret==1, "expected AttachThreadInput to succeed\n");
314 
315         /* detach in inverse order */
316         ret = AttachThreadInput( data[0].tid, data[1].tid, FALSE);
317         ok(ret==1, "expected AttachThreadInput to succeed\n");
318         ret = AttachThreadInput( data[1].tid, data[2].tid, FALSE);
319         ok(ret==1, "expected AttachThreadInput to succeed\n");
320     }
321 
322     /* test detaching in thread cleanup */
323     {
324         ret = AttachThreadInput( data[0].tid, data[1].tid, TRUE);
325         ok(ret==1, "expected AttachThreadInput to succeed\n");
326         ret = AttachThreadInput( data[0].tid, data[1].tid, TRUE);
327         ok(ret==1, "expected AttachThreadInput to succeed\n");
328         ret = AttachThreadInput( data[1].tid, data[2].tid, TRUE);
329         ok(ret==1, "expected AttachThreadInput to succeed\n");
330         ret = AttachThreadInput( data[1].tid, data[2].tid, TRUE);
331         ok(ret==1, "expected AttachThreadInput to succeed\n");
332 
333         TerminateThread(data[1].hThread, 0);
334 
335         ret = AttachThreadInput( data[0].tid, data[1].tid, FALSE);
336         ok(ret==0, "expected AttachThreadInput to fail\n");
337         ret = AttachThreadInput( data[1].tid, data[2].tid, FALSE);
338         ok(ret==0, "expected AttachThreadInput to fail\n");
339 
340         /* Create Thread1 again */
341         CreateTestThread(1, NULL);
342     }
343 
344 }
345 
346 void Test_Focus() //Focus Active Capture Foreground Capture
347 {
348     BOOL ret;
349 
350     trace("Thread hWnd0 0x%p hWnd1 0x%p\n",data[0].hWnd, data[1].hWnd);
351     /* Window 1 is in the foreground */
352     SetForegroundWindow(data[1].hWnd);
353     SetActiveWindow(data[0].hWnd);
354     FlushMessages();
355 
356     EXPECT_FOREGROUND(data[1].hWnd);
357     EXPECT_ACTIVE(data[0].hWnd);
358 
359     /* attach thread 0 to 1 */
360     {
361         ret = AttachThreadInput( data[0].tid, data[1].tid , TRUE);
362         ok(ret==1, "expected AttachThreadInput to succeed\n");
363         FlushMessages();
364 
365         EXPECT_FOREGROUND(data[1].hWnd);
366         EXPECT_ACTIVE(data[1].hWnd);
367 
368         ret = AttachThreadInput( data[0].tid, data[1].tid , FALSE);
369         ok(ret==1, "expected AttachThreadInput to succeed\n");
370     }
371 
372     EXPECT_FOREGROUND(data[1].hWnd);
373     EXPECT_ACTIVE(0);
374 
375     SetForegroundWindow(data[1].hWnd);
376     SetActiveWindow(data[0].hWnd);
377     FlushMessages();
378 
379     EXPECT_FOREGROUND(data[1].hWnd);
380     EXPECT_ACTIVE(data[0].hWnd);
381 
382     /* attach thread 1 to 0 */
383     {
384         ret = AttachThreadInput( data[1].tid, data[0].tid , TRUE);
385         ok(ret==1, "expected AttachThreadInput to succeed\n");
386         FlushMessages();
387 
388         EXPECT_FOREGROUND(data[1].hWnd);
389         EXPECT_ACTIVE(data[1].hWnd);
390 
391         ret = AttachThreadInput( data[1].tid, data[0].tid , FALSE);
392         ok(ret==1, "expected AttachThreadInput to succeed\n");
393     }
394 
395     /* Window 0 is in the foreground */
396     SetForegroundWindow(data[0].hWnd);
397     SetActiveWindow(data[1].hWnd);
398     FlushMessages();
399 
400     EXPECT_FOREGROUND(data[0].hWnd);
401     EXPECT_ACTIVE(data[0].hWnd);
402 
403     /* attach thread 0 to 1 */
404     {
405         ret = AttachThreadInput( data[0].tid, data[1].tid , TRUE);
406         ok(ret==1, "expected AttachThreadInput to succeed\n");
407         FlushMessages();
408 
409         EXPECT_FOREGROUND(data[0].hWnd);
410         EXPECT_ACTIVE(data[0].hWnd);
411 
412         SetForegroundWindow(data[0].hWnd);
413         SetActiveWindow(data[1].hWnd);
414         FlushMessages();
415 
416         EXPECT_FOREGROUND(data[1].hWnd);
417         EXPECT_ACTIVE(data[1].hWnd);
418 
419         ret = AttachThreadInput( data[0].tid, data[1].tid , FALSE);
420         ok(ret==1, "expected AttachThreadInput to succeed\n");
421     }
422 
423     EXPECT_FOREGROUND(data[1].hWnd);
424     EXPECT_ACTIVE(0);
425 
426     SetForegroundWindow(data[0].hWnd);
427     SetActiveWindow(data[1].hWnd);
428     FlushMessages();
429 
430     EXPECT_FOREGROUND(data[0].hWnd);
431     EXPECT_ACTIVE(data[0].hWnd);
432 
433     /* attach thread 1 to 0 */
434     {
435         ret = AttachThreadInput( data[1].tid, data[0].tid , TRUE);
436         ok(ret==1, "expected AttachThreadInput to succeed\n");
437         FlushMessages();
438 
439         EXPECT_FOREGROUND(data[0].hWnd);
440         EXPECT_ACTIVE(data[0].hWnd);
441 
442         SetForegroundWindow(data[0].hWnd);
443         SetActiveWindow(data[1].hWnd);
444         FlushMessages();
445 
446         EXPECT_FOREGROUND(data[1].hWnd);
447         EXPECT_ACTIVE(data[1].hWnd);
448 
449         ret = AttachThreadInput( data[1].tid, data[0].tid , FALSE);
450         ok(ret==1, "expected AttachThreadInput to succeed\n");
451     }
452 }
453 
454 /* test some functions like PostMessage and SendMessage that shouldn't be affected */
455 void Test_UnaffectedMessages()
456 {
457     BOOL ret;
458     LRESULT res;
459 
460     EMPTY_CACHE_(&data[0].cache);
461     EMPTY_CACHE_(&data[1].cache);
462 
463     /* test that messages posted before and after attachment are unaffected
464        and that we don't receive a meassage from a window we shouldn't */
465     PostMessage(data[0].hWnd, WM_USER, 0,0);
466     PostMessage(data[1].hWnd, WM_USER, 1,0);
467 
468     {
469         MSG_ENTRY Thread0_chain[]={
470           {0,WM_USER, POST, 0, 0},
471           {0,WM_USER, POST, 2, 0},
472           {0,0}};
473         MSG_ENTRY Thread1_chain[]={
474           {1,WM_USER, POST, 1, 0},
475           {1,WM_USER, POST, 3, 0},
476           {0,0}};
477 
478         ret = AttachThreadInput( data[1].tid, data[0].tid , TRUE);
479         ok(ret==1, "expected AttachThreadInput to succeed\n");
480 
481         PostMessage(data[0].hWnd, WM_USER, 2,0);
482         PostMessage(data[1].hWnd, WM_USER, 3,0);
483 
484         FlushMessages();
485         Sleep(100);
486 
487         COMPARE_CACHE_(&data[0].cache, Thread0_chain);
488         COMPARE_CACHE_(&data[1].cache, Thread1_chain);
489 
490         ret = AttachThreadInput( data[1].tid, data[0].tid , FALSE);
491         ok(ret==1, "expected AttachThreadInput to succeed\n");
492     }
493 
494     /* test messages send to the wrong thread */
495     res = SendMessageTimeout(data[0].hWnd, WM_USER, 0,0, SMTO_NORMAL, 1000, NULL);
496     ok (res != ERROR_TIMEOUT, "SendMessageTimeout timed out\n");
497     res = SendMessageTimeout(data[1].hWnd, WM_USER, 1,0, SMTO_NORMAL, 1000, NULL);
498     ok (res != ERROR_TIMEOUT, "SendMessageTimeout timed out\n");
499 
500     {
501         MSG_ENTRY Thread0_chain[]={
502           {0,WM_USER, SENT, 0, 0},
503           {0,WM_USER, SENT, 2, 0},
504           {0,0}};
505         MSG_ENTRY Thread1_chain[]={
506           {1,WM_USER, SENT, 1, 0},
507           {1,WM_USER, SENT, 3, 0},
508           {1,WM_MOUSEMOVE, SENT, 0, 0},
509           {0,0}};
510 
511         ret = AttachThreadInput( data[2].tid, data[1].tid , TRUE);
512         ok(ret==1, "expected AttachThreadInput to succeed\n");
513 
514         res = SendMessageTimeout(data[0].hWnd, WM_USER, 2,0, SMTO_NORMAL, 1000, NULL);
515         ok (res != ERROR_TIMEOUT, "SendMessageTimeout timed out\n");
516         res = SendMessageTimeout(data[1].hWnd, WM_USER, 3,0, SMTO_NORMAL, 1000, NULL);
517         ok (res != ERROR_TIMEOUT, "SendMessageTimeout timed out\n");
518 
519         /* Try to send a fake input message */
520         res = SendMessageTimeout(data[1].hWnd, WM_MOUSEMOVE, 0,0, SMTO_NORMAL, 1000, NULL);
521         ok (res != ERROR_TIMEOUT, "SendMessageTimeout timed out\n");
522 
523         COMPARE_CACHE_(&data[0].cache, Thread0_chain);
524         COMPARE_CACHE_(&data[1].cache, Thread1_chain);
525 
526         ret = AttachThreadInput( data[2].tid, data[1].tid , FALSE);
527         ok(ret==1, "expected AttachThreadInput to succeed\n");
528     }
529 
530     /* todo: test keyboard layout that shouldn't be affected */
531 }
532 
533 void Test_SendInput()
534 {
535     MSG_ENTRY Thread1_chain[]={
536           {1,WM_KEYDOWN, POST, VK_SHIFT, 0},
537           {1,WM_KEYUP, POST, VK_SHIFT, 0},
538           {0,0}};
539     MSG_ENTRY Thread0_chain[]={
540           {0,WM_KEYDOWN, POST, VK_SHIFT, 0},
541           {0,WM_KEYUP, POST, VK_SHIFT, 0},
542           {0,0}};
543 
544     BOOL ret;
545 
546     //trace("Thread hWnd0 0x%p hWnd1 0x%p\n",data[0].hWnd, data[1].hWnd);
547 
548     /* First try sending input without attaching. It will go to the foreground */
549     {
550         SetForegroundWindow(data[1].hWnd);
551         SetActiveWindow(data[0].hWnd);
552 
553         ok(GetForegroundWindow() == data[1].hWnd, "wrong foreground got 0x%p\n",GetForegroundWindow());
554         ok(GetActiveWindow() == data[0].hWnd, "wrong active got 0x%p\n",GetActiveWindow());
555 
556         FlushMessages();
557         EMPTY_CACHE_(&data[0].cache);
558         EMPTY_CACHE_(&data[1].cache);
559 
560         keybd_event(VK_SHIFT, 0,0,0);
561         keybd_event(VK_SHIFT, 0,KEYEVENTF_KEYUP,0);
562         Sleep(100);
563         FlushMessages();
564 
565         COMPARE_CACHE_(&data[0].cache, empty_chain);
566         COMPARE_CACHE_(&data[1].cache, Thread1_chain);
567     }
568 
569     /* Next attach and send input. It will go to the same thread as before */
570     { //                          from           to
571         ret = AttachThreadInput( data[1].tid, data[0].tid , TRUE);
572         ok(ret==1, "expected AttachThreadInput to succeed\n");
573 
574         FlushMessages();
575         EMPTY_CACHE_(&data[0].cache);
576         EMPTY_CACHE_(&data[1].cache);
577 
578         keybd_event(VK_SHIFT, 0,0,0);
579         keybd_event(VK_SHIFT, 0,KEYEVENTF_KEYUP,0);
580         Sleep(100);
581         FlushMessages();
582 
583         COMPARE_CACHE_(&data[0].cache, empty_chain);
584         COMPARE_CACHE_(&data[1].cache, Thread1_chain);
585     }
586 
587     /* Now set foreground and active again. Input will go to thread 0 */
588     {
589         SetForegroundWindow(data[1].hWnd);
590         SetActiveWindow(data[0].hWnd);
591         FlushMessages();
592 
593         ok(GetForegroundWindow() == data[0].hWnd, "wrong foreground got 0x%p\n",GetForegroundWindow());
594         ok(GetActiveWindow() == data[0].hWnd, "wrong active got 0x%p\n",GetActiveWindow());
595 
596         EMPTY_CACHE_(&data[0].cache);
597         EMPTY_CACHE_(&data[1].cache);
598 
599         keybd_event(VK_SHIFT, 0,0,0);
600         keybd_event(VK_SHIFT, 0,KEYEVENTF_KEYUP,0);
601         Sleep(100);
602         FlushMessages();
603 
604         COMPARE_CACHE_(&data[0].cache, Thread0_chain);
605         COMPARE_CACHE_(&data[1].cache, empty_chain);
606 
607         ret = AttachThreadInput( data[1].tid, data[0].tid , FALSE);
608         ok(ret==1, "expected AttachThreadInput to succeed\n");
609     }
610 
611     /* Attach in the opposite order and send input */
612     {
613         ret = AttachThreadInput( data[0].tid, data[1].tid , TRUE);
614         ok(ret==1, "expected AttachThreadInput to succeed\n");
615 
616         FlushMessages();
617         EMPTY_CACHE_(&data[0].cache);
618         EMPTY_CACHE_(&data[1].cache);
619 
620         keybd_event(VK_SHIFT, 0,0,0);
621         keybd_event(VK_SHIFT, 0,KEYEVENTF_KEYUP,0);
622         Sleep(100);
623         FlushMessages();
624 
625         COMPARE_CACHE_(&data[0].cache, Thread0_chain);
626         COMPARE_CACHE_(&data[1].cache, empty_chain);
627     }
628 
629     /* Now set foreground and active again. Input will go to thread 0 */
630     {
631         SetForegroundWindow(data[1].hWnd);
632         SetActiveWindow(data[0].hWnd);
633         FlushMessages();
634 
635         ok(GetForegroundWindow() == data[0].hWnd, "wrong foreground got 0x%p\n",GetForegroundWindow());
636         ok(GetActiveWindow() == data[0].hWnd, "wrong active got 0x%p\n",GetActiveWindow());
637 
638         EMPTY_CACHE_(&data[0].cache);
639         EMPTY_CACHE_(&data[1].cache);
640 
641         keybd_event(VK_SHIFT, 0,0,0);
642         keybd_event(VK_SHIFT, 0,KEYEVENTF_KEYUP,0);
643         Sleep(100);
644         FlushMessages();
645 
646         COMPARE_CACHE_(&data[0].cache, Thread0_chain);
647         COMPARE_CACHE_(&data[1].cache, empty_chain);
648 
649         ret = AttachThreadInput( data[0].tid, data[1].tid , FALSE);
650         ok(ret==1, "expected AttachThreadInput to succeed\n");
651     }
652 }
653 
654 START_TEST(AttachThreadInput)
655 {
656     if(!InitThreads())
657         return;
658 
659     Test_SimpleParameters();
660     cleanup_attachments();
661     Test_Focus();
662     cleanup_attachments();
663     Test_UnaffectedMessages();
664     cleanup_attachments();
665     Test_SendInput();
666     cleanup_attachments();
667 
668     if(hMouseHookLL)
669         UnhookWindowsHookEx(hMouseHookLL);
670     if(hKbdHookLL)
671         UnhookWindowsHookEx(hKbdHookLL);
672 
673     /* Stop all threads and exit gratefully */
674     PostThreadMessage(data[1].tid, WM_QUIT,0,0);
675     PostThreadMessage(data[2].tid, WM_QUIT,0,0);
676     PostThreadMessage(data[3].tid, WM_QUIT,0,0);
677     PostThreadMessage(data[4].tid, WM_QUIT,0,0);
678 }
679 
680