1 /*
2  * PROJECT:         ReactOS API tests
3  * LICENSE:         LGPLv2.1+ - See COPYING.LIB in the top level directory
4  * PURPOSE:         Test for SendMessageTimeout
5  * PROGRAMMERS:     Thomas Faber <thomas.faber@reactos.org>
6  */
7 
8 #include "precomp.h"
9 
10 static DWORD dwThread1;
11 static DWORD dwThread2;
12 static HANDLE hThread1;
13 static HANDLE hThread2;
14 static HWND hWndThread1;
15 static HWND hWndThread2;
16 
17 static
18 void
19 TestSendMessageTimeout(
20     _In_ HWND hWnd,
21     _In_ UINT Msg)
22 {
23     LRESULT ret;
24     DWORD_PTR result;
25 
26     ret = SendMessageTimeoutW(hWnd, Msg, 0, 0, SMTO_NORMAL, 0, NULL);
27     ok(ret == 0, "ret = %Id\n", ret);
28 
29     result = 0x55555555;
30     ret = SendMessageTimeoutW(hWnd, Msg, 0, 0, SMTO_NORMAL, 0, &result);
31     ok(ret == 0, "ret = %Id\n", ret);
32     ok(result == 0, "result = %Iu\n", result);
33 
34     ret = SendMessageTimeoutA(hWnd, Msg, 0, 0, SMTO_NORMAL, 0, NULL);
35     ok(ret == 0, "ret = %Id\n", ret);
36 
37     result = 0x55555555;
38     ret = SendMessageTimeoutA(hWnd, Msg, 0, 0, SMTO_NORMAL, 0, &result);
39     ok(ret == 0, "ret = %Id\n", ret);
40     ok(result == 0, "result = %Iu\n", result);
41 }
42 
43 #define WM_SENDTOOTHERTHREAD (WM_USER + 14)
44 
45 #define KILL_THREAD1_FLAG 0x40000000
46 #define KILL_THREAD2_FLAG 0x20000000
47 #define KILL_THREAD_FLAGS (KILL_THREAD1_FLAG | KILL_THREAD2_FLAG)
48 
49 static
50 LRESULT
51 CALLBACK
52 WndProc(
53     _In_ HWND hWnd,
54     _In_ UINT message,
55     _In_ WPARAM wParam,
56     _In_ LPARAM lParam)
57 {
58     if (IsDWmMsg(message) || IseKeyMsg(message))
59         return DefWindowProcW(hWnd, message, wParam, lParam);
60 
61     if (hWnd == hWndThread1)
62     {
63         RECORD_MESSAGE(1, message, SENT, wParam, lParam);
64     }
65     else if (hWnd == hWndThread2)
66     {
67         RECORD_MESSAGE(2, message, SENT, wParam, lParam);
68     }
69     else
70     {
71         RECORD_MESSAGE(3, message, SENT, wParam, lParam);
72     }
73 
74     switch (message)
75     {
76     case WM_SENDTOOTHERTHREAD:
77         if (GetCurrentThreadId() == dwThread1)
78         {
79             if ((wParam & KILL_THREAD2_FLAG) &&
80                 (wParam & ~KILL_THREAD_FLAGS) > 10)
81             {
82                 TerminateThread(hThread2, 123);
83             }
84             if ((wParam & KILL_THREAD1_FLAG) &&
85                 (wParam & ~KILL_THREAD_FLAGS) > 10)
86             {
87                 TerminateThread(hThread1, 456);
88             }
89             ok(lParam == dwThread2, "lParam = %Iu, expected %lu\n", lParam, dwThread2);
90             return SendMessage(hWndThread2, WM_SENDTOOTHERTHREAD, wParam + 1, GetCurrentThreadId());
91         }
92         else
93         {
94             ok(lParam == dwThread1, "lParam = %Iu, expected %lu\n", lParam, dwThread1);
95             return SendMessage(hWndThread1, WM_SENDTOOTHERTHREAD, wParam + 1, GetCurrentThreadId());
96         }
97     }
98 
99     return DefWindowProcW(hWnd, message, wParam, lParam);
100 }
101 
102 static
103 DWORD
104 WINAPI
105 Thread1(
106     _Inout_opt_ PVOID Parameter)
107 {
108     MSG msg;
109 
110     hWndThread1 = CreateWindowExW(0, L"SendTest", NULL, 0,  10, 10, 20, 20,  NULL, NULL, 0, NULL);
111     ok(hWndThread1 != NULL, "CreateWindow failed\n");
112 
113     while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
114     {
115         if (!(msg.message > WM_USER || IsDWmMsg(msg.message) || IseKeyMsg(msg.message)))
116             RECORD_MESSAGE(1, msg.message, POST, 0, 0);
117         DispatchMessageA(&msg);
118     }
119 
120     ResumeThread(hThread2);
121 
122     while (MsgWaitForMultipleObjectsEx(1, &hThread2, FALSE, QS_ALLEVENTS, MWMO_ALERTABLE) != WAIT_OBJECT_0)
123     {
124         while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
125         {
126             if (!(msg.message > WM_USER || IsDWmMsg(msg.message) || IseKeyMsg(msg.message)))
127                 RECORD_MESSAGE(1, msg.message, POST, 0, 0);
128             DispatchMessageA(&msg);
129         }
130     }
131 
132     DestroyWindow(hWndThread1);
133     while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
134     {
135         if (!(msg.message > WM_USER || IsDWmMsg(msg.message) || IseKeyMsg(msg.message)))
136             RECORD_MESSAGE(1, msg.message, POST, 0, 0);
137         DispatchMessageA(&msg);
138     }
139 
140     return 6;
141 }
142 
143 static
144 DWORD
145 WINAPI
146 Thread2(
147     _Inout_opt_ PVOID Parameter)
148 {
149     MSG msg;
150     LRESULT ret;
151     WPARAM wParam;
152 
153     hWndThread2 = CreateWindowExW(0, L"SendTest", NULL, 0,  10, 10, 20, 20,  NULL, NULL, 0, NULL);
154     ok(hWndThread2 != NULL, "CreateWindow failed\n");
155 
156     while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
157     {
158         if (!(msg.message > WM_USER || IsDWmMsg(msg.message) || IseKeyMsg(msg.message)))
159             RECORD_MESSAGE(2, msg.message, POST, 0, 0);
160         DispatchMessageA(&msg);
161     }
162 
163     wParam = (WPARAM)Parameter;
164     ret = SendMessage(hWndThread1, WM_SENDTOOTHERTHREAD, wParam, dwThread2);
165     ok(ret == 0, "ret = %lu\n", ret);
166 
167     while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
168     {
169         if (!(msg.message > WM_USER || IsDWmMsg(msg.message) || IseKeyMsg(msg.message)))
170             RECORD_MESSAGE(2, msg.message, POST, 0, 0);
171         DispatchMessageA(&msg);
172     }
173 
174     DestroyWindow(hWndThread2);
175     while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
176     {
177         if (!(msg.message > WM_USER || IsDWmMsg(msg.message) || IseKeyMsg(msg.message)))
178             RECORD_MESSAGE(2, msg.message, POST, 0, 0);
179         DispatchMessageA(&msg);
180     }
181 
182     return 7;
183 }
184 
185 static
186 void
187 TestRecursiveInterThreadMessages(
188     _In_ BOOL KillThread1,
189     _In_ BOOL KillThread2)
190 {
191     PVOID Parameter;
192     HANDLE Handles[2];
193     BOOL Ret;
194     DWORD ExitCode;
195 
196     Parameter = UlongToPtr((KillThread1 ? KILL_THREAD1_FLAG : 0) |
197                            (KillThread2 ? KILL_THREAD2_FLAG : 0));
198     hThread1 = CreateThread(NULL, 0, Thread1, Parameter, CREATE_SUSPENDED, &dwThread1);
199     hThread2 = CreateThread(NULL, 0, Thread2, Parameter, CREATE_SUSPENDED, &dwThread2);
200 
201     ResumeThread(hThread1);
202 
203     Handles[0] = hThread1;
204     Handles[1] = hThread2;
205     WaitForMultipleObjects(2, Handles, TRUE, INFINITE);
206 
207     Ret = GetExitCodeThread(hThread1, &ExitCode);
208     ok(Ret == TRUE, "GetExitCodeThread failed with %lu\n", GetLastError());
209     if (KillThread1)
210         ok(ExitCode == 456, "Thread1 exit code is %lu\n", ExitCode);
211     else
212         ok(ExitCode == 6, "Thread1 exit code is %lu\n", ExitCode);
213 
214     Ret = GetExitCodeThread(hThread2, &ExitCode);
215     ok(Ret == TRUE, "GetExitCodeThread failed with %lu\n", GetLastError());
216     if (KillThread2)
217         ok(ExitCode == 123, "Thread2 exit code is %lu\n", ExitCode);
218     else
219         ok(ExitCode == 7, "Thread2 exit code is %lu\n", ExitCode);
220 
221     CloseHandle(hThread2);
222     CloseHandle(hThread1);
223 
224     //TRACE_CACHE();
225 }
226 
227 START_TEST(SendMessageTimeout)
228 {
229     TestSendMessageTimeout(NULL, WM_USER);
230     TestSendMessageTimeout(NULL, WM_PAINT);
231     TestSendMessageTimeout(NULL, WM_GETICON);
232 
233     RegisterSimpleClass(WndProc, L"SendTest");
234 
235     TestRecursiveInterThreadMessages(FALSE, FALSE);
236     TestRecursiveInterThreadMessages(FALSE, TRUE);
237     TestRecursiveInterThreadMessages(TRUE, FALSE);
238     TestRecursiveInterThreadMessages(TRUE, TRUE);
239 }
240