Something went wrong. Try again.
Reactos
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680/* * PROJECT: ReactOS api tests * LICENSE: GPL - See COPYING in the top level directory * PURPOSE: Test for AttachThreadInput * PROGRAMMERS: Giannis Adamopoulos */
#include "precomp.h"
typedef struct { DWORD tid; HANDLE hThread; HWND hWnd; WCHAR* Desktop; HANDLE StartEvent; HANDLE QueueStatusEvent; DWORD LastQueueStatus;
MSG_CACHE cache;} THREAD_DATA;
static THREAD_DATA data[6];static HHOOK hMouseHookLL = NULL;static HHOOK hKbdHookLL = NULL;
#define EXPECT_FOREGROUND(expected) ok(GetForegroundWindow() == expected, \ "Expected hwnd%d at the foreground, got hwnd%d\n", \ get_iwnd(expected), get_iwnd(GetForegroundWindow()));
#define EXPECT_ACTIVE(expected) ok(GetActiveWindow() == expected, \ "Expected hwnd%d to be active, got hwnd%d\n", \ get_iwnd(expected), get_iwnd(GetActiveWindow()));
/* * Helper functions */
static int get_iwnd(HWND hWnd){ if(hWnd == data[0].hWnd) return 0; else if(hWnd == data[1].hWnd) return 1; else if(hWnd == data[2].hWnd) return 2; else if(hWnd == data[3].hWnd) return 3; else if(hWnd == data[4].hWnd) return 4; else return -1;}
LRESULT CALLBACK TestProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam){ int iwnd = get_iwnd(hWnd);
if(iwnd >= 0 && message > 0 && message < WM_APP && message != WM_TIMER) record_message(&data[iwnd].cache, iwnd, message, SENT, wParam,0);
return DefWindowProc(hWnd, message, wParam, lParam);}
static void FlushMessages(){ MSG msg; LRESULT res;
while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) { int iwnd = get_iwnd(msg.hwnd); if( iwnd >= 0 && msg.message > 0 && msg.message < WM_APP && msg.message != WM_TIMER) record_message(&data[0].cache, iwnd, msg.message, POST, msg.wParam,0); DispatchMessageA( &msg ); }
/* Use SendMessage to sync with the other queues */ res = SendMessageTimeout(data[1].hWnd, WM_APP, 0,0, SMTO_NORMAL, 1000, NULL); ok (res != ERROR_TIMEOUT, "SendMessageTimeout timed out\n"); res = SendMessageTimeout(data[2].hWnd, WM_APP, 0,0, SMTO_NORMAL, 1000, NULL); ok (res != ERROR_TIMEOUT, "SendMessageTimeout timed out\n"); res = SendMessageTimeout(data[3].hWnd, WM_APP, 0,0, SMTO_NORMAL, 1000, NULL); ok (res != ERROR_TIMEOUT, "SendMessageTimeout timed out\n"); res = SendMessageTimeout(data[4].hWnd, WM_APP, 0,0, SMTO_NORMAL, 1000, NULL); ok (res != ERROR_TIMEOUT, "SendMessageTimeout timed out\n");}
static DWORD WINAPI thread_proc(void *param){ THREAD_DATA* current_data = (THREAD_DATA*)param; MSG msg; HDESK hdesk = NULL; int iwnd;
if(current_data->Desktop) { hdesk = CreateDesktopW(current_data->Desktop, NULL, NULL, 0, DESKTOP_ALL_ACCESS, NULL ); SetThreadDesktop(hdesk); }
/* create test window */ current_data->hWnd = CreateWindowW(L"TestClass", L"test", WS_OVERLAPPEDWINDOW, 100, 100, 500, 500, NULL, NULL, 0, NULL); SetEvent( current_data->StartEvent );
iwnd = get_iwnd(current_data->hWnd);
/* Use MsgWaitForMultipleObjects to let the thread process apcs */ while( GetMessage(&msg, 0,0,0) ) { if(msg.message > 0 && msg.message < WM_APP && msg.message != WM_TIMER ) record_message(&data[iwnd].cache, iwnd, msg.message, POST, msg.wParam,0); DispatchMessage(&msg); }
if(hdesk) CloseDesktop(hdesk);
return 0;}
BOOL CreateTestThread(int i, WCHAR* Desktop){ DWORD ret;
data[i].StartEvent = CreateEventW(NULL, 0, 0, NULL); data[i].Desktop = Desktop; data[i].hThread = CreateThread(NULL, 0, thread_proc, &data[i], 0, &data[i].tid); if(!data[i].hThread) goto fail; ret = WaitForSingleObject(data[i].StartEvent, 1000); CloseHandle(data[i].StartEvent); if(ret == WAIT_TIMEOUT) {fail: win_skip("child thread failed to initialize\n"); return FALSE; } return TRUE;}
static LRESULT CALLBACK MouseLLHookProc(int nCode, WPARAM wParam, LPARAM lParam){ LRESULT ret; MSLLHOOKSTRUCT* params = (MSLLHOOKSTRUCT*) lParam;
ret = CallNextHookEx(hMouseHookLL, nCode, wParam, lParam);
if((params->flags & LLKHF_INJECTED) == 0) return TRUE;
return ret;}
LRESULT CALLBACK KbdLLHookProc(int nCode, WPARAM wParam, LPARAM lParam){ LRESULT ret; KBDLLHOOKSTRUCT* params = (KBDLLHOOKSTRUCT*) lParam;
ret = CallNextHookEx(hMouseHookLL, nCode, wParam, lParam);
if((params->flags & LLKHF_INJECTED) == 0) return TRUE;
return ret;}
BOOLEAN InitThreads(){ /* Create a LL hook that drops any physical keyboard and mouse action and prevent the user from interfering with the test results */ if(!IsDebuggerPresent()) { hMouseHookLL = SetWindowsHookExW(WH_MOUSE_LL, MouseLLHookProc, GetModuleHandleW( NULL ), 0); ok(hMouseHookLL!=NULL,"failed to set hook\n"); hKbdHookLL = SetWindowsHookExW(WH_KEYBOARD_LL, KbdLLHookProc, GetModuleHandleW( NULL ), 0); ok(hKbdHookLL!=NULL,"failed to set hook\n"); }
/* create test clases */ RegisterSimpleClass(TestProc, L"TestClass");
memset(&data[0], 0, sizeof(data[0]));
data[0].tid = GetCurrentThreadId();
/* create test window */ data[0].hWnd = CreateWindowW(L"TestClass", L"test", WS_OVERLAPPEDWINDOW, 100, 100, 500, 500, NULL, NULL, 0, NULL); if(!data[0].hWnd) { win_skip("CreateWindowW failed\n"); return FALSE; }
/* create thread1(same desktop) */ if(!CreateTestThread(1, NULL)) return FALSE;
/* create thread2(same desktop) */ if(!CreateTestThread(2, NULL)) return FALSE;
/* create thread3(different desktop) */ if(!CreateTestThread(3, L"ThreadTestDesktop")) return FALSE;
/* create thread4(different desktop) */ if(!CreateTestThread(4, L"ThreadTestDesktop")) return FALSE;
return TRUE;}
static void cleanup_attachments(){ int i,j; BOOL ret;
for(i = 0; i< 4; i++) { for(j = 0; j< 4; j++) { ret = AttachThreadInput(data[i].tid,data[j].tid, FALSE); ok(ret==0, "expected AttachThreadInput to fail\n"); } }}
/* * The actual tests */
void Test_SimpleParameters(){ BOOL ret; /* FIXME: acording to msdn xp doesn't set last error but vista+ does*/
/* test wrong thread */ ret = AttachThreadInput( 0, 1, TRUE); ok(ret==0, "expected AttachThreadInput to fail\n");
/* test same thread */ ret = AttachThreadInput( data[1].tid, data[1].tid, TRUE); ok(ret==0, "expected AttachThreadInput to fail\n");
/* try to attach to a thread on another desktop*/ ret = AttachThreadInput( data[2].tid,data[3].tid, TRUE); ok(ret==0, "expected AttachThreadInput to fail\n"); if(ret == 1 ) AttachThreadInput( data[2].tid,data[3].tid, FALSE);
/* test other desktop to this */ ret = AttachThreadInput( data[3].tid,data[2].tid, TRUE); ok(ret==0, "expected AttachThreadInput to fail\n"); if(ret == 1 ) AttachThreadInput( data[3].tid,data[2].tid, FALSE);
/* attach two threads that are both in ThreadTestDesktop */ { /* Attach thread 3 and 4 */ ret = AttachThreadInput( data[3].tid,data[4].tid, TRUE); ok(ret==1, "expected AttachThreadInput to succeed\n");
/* cleanup previous attachment */ ret = AttachThreadInput( data[3].tid,data[4].tid, FALSE); ok(ret==1, "expected AttachThreadInput to succeed\n"); }
{ /* Attach thread 1 and 2 */ ret = AttachThreadInput( data[1].tid,data[2].tid, TRUE); ok(ret==1, "expected AttachThreadInput to succeed\n");
/* attach already attached*/ ret = AttachThreadInput( data[1].tid,data[2].tid, TRUE); ok(ret==1, "expected AttachThreadInput to succeed\n");
/* attach in the opposite order */ ret = AttachThreadInput( data[2].tid,data[1].tid, TRUE); ok(ret==1, "expected AttachThreadInput to succeed\n");
/* Now try to detach 0 from 1 */ ret = AttachThreadInput( data[0].tid,data[1].tid, FALSE); ok(ret==0, "expected AttachThreadInput to fail\n");
/* also try to detach 3 from 2 */ ret = AttachThreadInput( data[3].tid,data[2].tid, FALSE); ok(ret==0, "expected AttachThreadInput to fail\n");
/* cleanup previous attachment */ ret = AttachThreadInput( data[1].tid,data[2].tid, FALSE); ok(ret==1, "expected AttachThreadInput to succeed\n");
ret = AttachThreadInput( data[2].tid,data[1].tid, FALSE); ok(ret==1, "expected AttachThreadInput to succeed\n");
ret = AttachThreadInput( data[1].tid,data[2].tid, FALSE); ok(ret==1, "expected AttachThreadInput to succeed\n"); }
/* test triple attach */ { ret = AttachThreadInput( data[0].tid, data[1].tid, TRUE); ok(ret==1, "expected AttachThreadInput to succeed\n"); ret = AttachThreadInput( data[1].tid, data[2].tid, TRUE); ok(ret==1, "expected AttachThreadInput to succeed\n");
/* try to detach 2 and 0 */ ret = AttachThreadInput( data[0].tid, data[2].tid, FALSE); ok(ret==0, "expected AttachThreadInput to fail\n"); ret = AttachThreadInput( data[2].tid, data[0].tid, FALSE); ok(ret==0, "expected AttachThreadInput to fail\n");
/* try to to attach 0 to 2. it works! */ ret = AttachThreadInput( data[0].tid, data[2].tid, TRUE); ok(ret==1, "expected AttachThreadInput to succeed\n");
ret = AttachThreadInput( data[0].tid, data[2].tid, FALSE); ok(ret==1, "expected AttachThreadInput to succeed\n");
/* detach in inverse order */ ret = AttachThreadInput( data[0].tid, data[1].tid, FALSE); ok(ret==1, "expected AttachThreadInput to succeed\n"); ret = AttachThreadInput( data[1].tid, data[2].tid, FALSE); ok(ret==1, "expected AttachThreadInput to succeed\n"); }
/* test detaching in thread cleanup */ { ret = AttachThreadInput( data[0].tid, data[1].tid, TRUE); ok(ret==1, "expected AttachThreadInput to succeed\n"); ret = AttachThreadInput( data[0].tid, data[1].tid, TRUE); ok(ret==1, "expected AttachThreadInput to succeed\n"); ret = AttachThreadInput( data[1].tid, data[2].tid, TRUE); ok(ret==1, "expected AttachThreadInput to succeed\n"); ret = AttachThreadInput( data[1].tid, data[2].tid, TRUE); ok(ret==1, "expected AttachThreadInput to succeed\n");
TerminateThread(data[1].hThread, 0);
ret = AttachThreadInput( data[0].tid, data[1].tid, FALSE); ok(ret==0, "expected AttachThreadInput to fail\n"); ret = AttachThreadInput( data[1].tid, data[2].tid, FALSE); ok(ret==0, "expected AttachThreadInput to fail\n");
/* Create Thread1 again */ CreateTestThread(1, NULL); }
}
void Test_Focus() //Focus Active Capture Foreground Capture{ BOOL ret;
trace("Thread hWnd0 0x%p hWnd1 0x%p\n",data[0].hWnd, data[1].hWnd); /* Window 1 is in the foreground */ SetForegroundWindow(data[1].hWnd); SetActiveWindow(data[0].hWnd); FlushMessages();
EXPECT_FOREGROUND(data[1].hWnd); EXPECT_ACTIVE(data[0].hWnd);
/* attach thread 0 to 1 */ { ret = AttachThreadInput( data[0].tid, data[1].tid , TRUE); ok(ret==1, "expected AttachThreadInput to succeed\n"); FlushMessages();
EXPECT_FOREGROUND(data[1].hWnd); EXPECT_ACTIVE(data[1].hWnd);
ret = AttachThreadInput( data[0].tid, data[1].tid , FALSE); ok(ret==1, "expected AttachThreadInput to succeed\n"); }
EXPECT_FOREGROUND(data[1].hWnd); EXPECT_ACTIVE(0);
SetForegroundWindow(data[1].hWnd); SetActiveWindow(data[0].hWnd); FlushMessages();
EXPECT_FOREGROUND(data[1].hWnd); EXPECT_ACTIVE(data[0].hWnd);
/* attach thread 1 to 0 */ { ret = AttachThreadInput( data[1].tid, data[0].tid , TRUE); ok(ret==1, "expected AttachThreadInput to succeed\n"); FlushMessages();
EXPECT_FOREGROUND(data[1].hWnd); EXPECT_ACTIVE(data[1].hWnd);
ret = AttachThreadInput( data[1].tid, data[0].tid , FALSE); ok(ret==1, "expected AttachThreadInput to succeed\n"); }
/* Window 0 is in the foreground */ SetForegroundWindow(data[0].hWnd); SetActiveWindow(data[1].hWnd); FlushMessages();
EXPECT_FOREGROUND(data[0].hWnd); EXPECT_ACTIVE(data[0].hWnd);
/* attach thread 0 to 1 */ { ret = AttachThreadInput( data[0].tid, data[1].tid , TRUE); ok(ret==1, "expected AttachThreadInput to succeed\n"); FlushMessages();
EXPECT_FOREGROUND(data[0].hWnd); EXPECT_ACTIVE(data[0].hWnd);
SetForegroundWindow(data[0].hWnd); SetActiveWindow(data[1].hWnd); FlushMessages();
EXPECT_FOREGROUND(data[1].hWnd); EXPECT_ACTIVE(data[1].hWnd);
ret = AttachThreadInput( data[0].tid, data[1].tid , FALSE); ok(ret==1, "expected AttachThreadInput to succeed\n"); }
EXPECT_FOREGROUND(data[1].hWnd); EXPECT_ACTIVE(0);
SetForegroundWindow(data[0].hWnd); SetActiveWindow(data[1].hWnd); FlushMessages();
EXPECT_FOREGROUND(data[0].hWnd); EXPECT_ACTIVE(data[0].hWnd);
/* attach thread 1 to 0 */ { ret = AttachThreadInput( data[1].tid, data[0].tid , TRUE); ok(ret==1, "expected AttachThreadInput to succeed\n"); FlushMessages();
EXPECT_FOREGROUND(data[0].hWnd); EXPECT_ACTIVE(data[0].hWnd);
SetForegroundWindow(data[0].hWnd); SetActiveWindow(data[1].hWnd); FlushMessages();
EXPECT_FOREGROUND(data[1].hWnd); EXPECT_ACTIVE(data[1].hWnd);
ret = AttachThreadInput( data[1].tid, data[0].tid , FALSE); ok(ret==1, "expected AttachThreadInput to succeed\n"); }}
/* test some functions like PostMessage and SendMessage that shouldn't be affected */void Test_UnaffectedMessages(){ BOOL ret; LRESULT res;
EMPTY_CACHE_(&data[0].cache); EMPTY_CACHE_(&data[1].cache);
/* test that messages posted before and after attachment are unaffected and that we don't receive a meassage from a window we shouldn't */ PostMessage(data[0].hWnd, WM_USER, 0,0); PostMessage(data[1].hWnd, WM_USER, 1,0);
{ MSG_ENTRY Thread0_chain[]={ {0,WM_USER, POST, 0, 0}, {0,WM_USER, POST, 2, 0}, {0,0}}; MSG_ENTRY Thread1_chain[]={ {1,WM_USER, POST, 1, 0}, {1,WM_USER, POST, 3, 0}, {0,0}};
ret = AttachThreadInput( data[1].tid, data[0].tid , TRUE); ok(ret==1, "expected AttachThreadInput to succeed\n");
PostMessage(data[0].hWnd, WM_USER, 2,0); PostMessage(data[1].hWnd, WM_USER, 3,0);
FlushMessages(); Sleep(100);
COMPARE_CACHE_(&data[0].cache, Thread0_chain); COMPARE_CACHE_(&data[1].cache, Thread1_chain);
ret = AttachThreadInput( data[1].tid, data[0].tid , FALSE); ok(ret==1, "expected AttachThreadInput to succeed\n"); }
/* test messages send to the wrong thread */ res = SendMessageTimeout(data[0].hWnd, WM_USER, 0,0, SMTO_NORMAL, 1000, NULL); ok (res != ERROR_TIMEOUT, "SendMessageTimeout timed out\n"); res = SendMessageTimeout(data[1].hWnd, WM_USER, 1,0, SMTO_NORMAL, 1000, NULL); ok (res != ERROR_TIMEOUT, "SendMessageTimeout timed out\n");
{ MSG_ENTRY Thread0_chain[]={ {0,WM_USER, SENT, 0, 0}, {0,WM_USER, SENT, 2, 0}, {0,0}}; MSG_ENTRY Thread1_chain[]={ {1,WM_USER, SENT, 1, 0}, {1,WM_USER, SENT, 3, 0}, {1,WM_MOUSEMOVE, SENT, 0, 0}, {0,0}};
ret = AttachThreadInput( data[2].tid, data[1].tid , TRUE); ok(ret==1, "expected AttachThreadInput to succeed\n");
res = SendMessageTimeout(data[0].hWnd, WM_USER, 2,0, SMTO_NORMAL, 1000, NULL); ok (res != ERROR_TIMEOUT, "SendMessageTimeout timed out\n"); res = SendMessageTimeout(data[1].hWnd, WM_USER, 3,0, SMTO_NORMAL, 1000, NULL); ok (res != ERROR_TIMEOUT, "SendMessageTimeout timed out\n");
/* Try to send a fake input message */ res = SendMessageTimeout(data[1].hWnd, WM_MOUSEMOVE, 0,0, SMTO_NORMAL, 1000, NULL); ok (res != ERROR_TIMEOUT, "SendMessageTimeout timed out\n");
COMPARE_CACHE_(&data[0].cache, Thread0_chain); COMPARE_CACHE_(&data[1].cache, Thread1_chain);
ret = AttachThreadInput( data[2].tid, data[1].tid , FALSE); ok(ret==1, "expected AttachThreadInput to succeed\n"); }
/* todo: test keyboard layout that shouldn't be affected */}
void Test_SendInput(){ MSG_ENTRY Thread1_chain[]={ {1,WM_KEYDOWN, POST, VK_SHIFT, 0}, {1,WM_KEYUP, POST, VK_SHIFT, 0}, {0,0}}; MSG_ENTRY Thread0_chain[]={ {0,WM_KEYDOWN, POST, VK_SHIFT, 0}, {0,WM_KEYUP, POST, VK_SHIFT, 0}, {0,0}};
BOOL ret;
//trace("Thread hWnd0 0x%p hWnd1 0x%p\n",data[0].hWnd, data[1].hWnd);
/* First try sending input without attaching. It will go to the foreground */ { SetForegroundWindow(data[1].hWnd); SetActiveWindow(data[0].hWnd);
ok(GetForegroundWindow() == data[1].hWnd, "wrong foreground got 0x%p\n",GetForegroundWindow()); ok(GetActiveWindow() == data[0].hWnd, "wrong active got 0x%p\n",GetActiveWindow());
FlushMessages(); EMPTY_CACHE_(&data[0].cache); EMPTY_CACHE_(&data[1].cache);
keybd_event(VK_SHIFT, 0,0,0); keybd_event(VK_SHIFT, 0,KEYEVENTF_KEYUP,0); Sleep(100); FlushMessages();
COMPARE_CACHE_(&data[0].cache, empty_chain); COMPARE_CACHE_(&data[1].cache, Thread1_chain); }
/* Next attach and send input. It will go to the same thread as before */ { // from to ret = AttachThreadInput( data[1].tid, data[0].tid , TRUE); ok(ret==1, "expected AttachThreadInput to succeed\n");
FlushMessages(); EMPTY_CACHE_(&data[0].cache); EMPTY_CACHE_(&data[1].cache);
keybd_event(VK_SHIFT, 0,0,0); keybd_event(VK_SHIFT, 0,KEYEVENTF_KEYUP,0); Sleep(100); FlushMessages();
COMPARE_CACHE_(&data[0].cache, empty_chain); COMPARE_CACHE_(&data[1].cache, Thread1_chain); }
/* Now set foreground and active again. Input will go to thread 0 */ { SetForegroundWindow(data[1].hWnd); SetActiveWindow(data[0].hWnd); FlushMessages();
ok(GetForegroundWindow() == data[0].hWnd, "wrong foreground got 0x%p\n",GetForegroundWindow()); ok(GetActiveWindow() == data[0].hWnd, "wrong active got 0x%p\n",GetActiveWindow());
EMPTY_CACHE_(&data[0].cache); EMPTY_CACHE_(&data[1].cache);
keybd_event(VK_SHIFT, 0,0,0); keybd_event(VK_SHIFT, 0,KEYEVENTF_KEYUP,0); Sleep(100); FlushMessages();
COMPARE_CACHE_(&data[0].cache, Thread0_chain); COMPARE_CACHE_(&data[1].cache, empty_chain);
ret = AttachThreadInput( data[1].tid, data[0].tid , FALSE); ok(ret==1, "expected AttachThreadInput to succeed\n"); }
/* Attach in the opposite order and send input */ { ret = AttachThreadInput( data[0].tid, data[1].tid , TRUE); ok(ret==1, "expected AttachThreadInput to succeed\n");
FlushMessages(); EMPTY_CACHE_(&data[0].cache); EMPTY_CACHE_(&data[1].cache);
keybd_event(VK_SHIFT, 0,0,0); keybd_event(VK_SHIFT, 0,KEYEVENTF_KEYUP,0); Sleep(100); FlushMessages();
COMPARE_CACHE_(&data[0].cache, Thread0_chain); COMPARE_CACHE_(&data[1].cache, empty_chain); }
/* Now set foreground and active again. Input will go to thread 0 */ { SetForegroundWindow(data[1].hWnd); SetActiveWindow(data[0].hWnd); FlushMessages();
ok(GetForegroundWindow() == data[0].hWnd, "wrong foreground got 0x%p\n",GetForegroundWindow()); ok(GetActiveWindow() == data[0].hWnd, "wrong active got 0x%p\n",GetActiveWindow());
EMPTY_CACHE_(&data[0].cache); EMPTY_CACHE_(&data[1].cache);
keybd_event(VK_SHIFT, 0,0,0); keybd_event(VK_SHIFT, 0,KEYEVENTF_KEYUP,0); Sleep(100); FlushMessages();
COMPARE_CACHE_(&data[0].cache, Thread0_chain); COMPARE_CACHE_(&data[1].cache, empty_chain);
ret = AttachThreadInput( data[0].tid, data[1].tid , FALSE); ok(ret==1, "expected AttachThreadInput to succeed\n"); }}
START_TEST(AttachThreadInput){ if(!InitThreads()) return;
Test_SimpleParameters(); cleanup_attachments(); Test_Focus(); cleanup_attachments(); Test_UnaffectedMessages(); cleanup_attachments(); Test_SendInput(); cleanup_attachments();
if(hMouseHookLL) UnhookWindowsHookEx(hMouseHookLL); if(hKbdHookLL) UnhookWindowsHookEx(hKbdHookLL);
/* Stop all threads and exit gratefully */ PostThreadMessage(data[1].tid, WM_QUIT,0,0); PostThreadMessage(data[2].tid, WM_QUIT,0,0); PostThreadMessage(data[3].tid, WM_QUIT,0,0); PostThreadMessage(data[4].tid, WM_QUIT,0,0);}