Reactos
1/*
2 * PROJECT: ReactOS api tests
3 * LICENSE: LGPLv2.1+ - See COPYING.LIB in the top level directory
4 * PURPOSE: Test for TerminateProcess
5 * PROGRAMMER: Thomas Faber <thomas.faber@reactos.org>
6 */
7
8#include "precomp.h"
9
10#include <ndk/obfuncs.h>
11
12static
13HANDLE
14StartChild(
15 _In_ PCWSTR Argument,
16 _In_ DWORD Flags,
17 _Out_opt_ PDWORD ProcessId)
18{
19 BOOL Success;
20 WCHAR FileName[MAX_PATH];
21 WCHAR CommandLine[MAX_PATH];
22 STARTUPINFOW StartupInfo;
23 PROCESS_INFORMATION ProcessInfo;
24
25 GetModuleFileNameW(NULL, FileName, _countof(FileName));
26 StringCbPrintfW(CommandLine,
27 sizeof(CommandLine),
28 L"\"%ls\" TerminateProcess %ls",
29 FileName,
30 Argument);
31
32 RtlZeroMemory(&StartupInfo, sizeof(StartupInfo));
33 StartupInfo.cb = sizeof(StartupInfo);
34 /* HACK: running the test under rosautotest seems to keep another reference
35 * to the child process around until the test finishes (on both ROS and
36 * Windows)... I'm too lazy to investigate very much so let's just redirect
37 * the child std handles to nowhere. ok() is useless in half the child
38 * processes anyway.
39 */
40 StartupInfo.dwFlags = STARTF_USESTDHANDLES;
41
42 Success = CreateProcessW(FileName,
43 CommandLine,
44 NULL,
45 NULL,
46 FALSE,
47 Flags,
48 NULL,
49 NULL,
50 &StartupInfo,
51 &ProcessInfo);
52 if (!Success)
53 {
54 skip("CreateProcess failed with %lu\n", GetLastError());
55 if (ProcessId)
56 *ProcessId = 0;
57 return NULL;
58 }
59 CloseHandle(ProcessInfo.hThread);
60 if (ProcessId)
61 *ProcessId = ProcessInfo.dwProcessId;
62 return ProcessInfo.hProcess;
63}
64
65static
66VOID
67TraceHandleCount_(
68 _In_ HANDLE hObject,
69 _In_ PCSTR File,
70 _In_ INT Line)
71{
72 NTSTATUS Status;
73 OBJECT_BASIC_INFORMATION BasicInfo;
74
75 Status = NtQueryObject(hObject,
76 ObjectBasicInformation,
77 &BasicInfo,
78 sizeof(BasicInfo),
79 NULL);
80 if (!NT_SUCCESS(Status))
81 {
82 ok_(File, Line)(0, "NtQueryObject failed with status 0x%lx\n", Status);
83 return;
84 }
85 ok_(File, Line)(0, "Handle %p still has %lu open handles, %lu references\n", hObject, BasicInfo.HandleCount, BasicInfo.PointerCount);
86}
87
88#define WaitExpectSuccess(h, ms) WaitExpect_(h, ms, WAIT_OBJECT_0, __FILE__, __LINE__)
89#define WaitExpectTimeout(h, ms) WaitExpect_(h, ms, WAIT_TIMEOUT, __FILE__, __LINE__)
90static
91VOID
92WaitExpect_(
93 _In_ HANDLE hWait,
94 _In_ DWORD Milliseconds,
95 _In_ DWORD ExpectedError,
96 _In_ PCSTR File,
97 _In_ INT Line)
98{
99 DWORD Error;
100
101 Error = WaitForSingleObject(hWait, Milliseconds);
102 ok_(File, Line)(Error == ExpectedError, "Wait for %p return %lu\n", hWait, Error);
103}
104
105#define CloseProcessAndVerify(hp, pid, code) CloseProcessAndVerify_(hp, pid, code, __FILE__, __LINE__)
106static
107VOID
108CloseProcessAndVerify_(
109 _In_ HANDLE hProcess,
110 _In_ DWORD ProcessId,
111 _In_ UINT ExpectedExitCode,
112 _In_ PCSTR File,
113 _In_ INT Line)
114{
115 int i = 0;
116 DWORD Error;
117 DWORD ExitCode;
118 BOOL Success;
119
120 WaitExpect_(hProcess, 0, WAIT_OBJECT_0, File, Line);
121 Success = GetExitCodeProcess(hProcess, &ExitCode);
122 ok_(File, Line)(Success, "GetExitCodeProcess failed with %lu\n", GetLastError());
123 CloseHandle(hProcess);
124 while ((hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, ProcessId)) != NULL)
125 {
126 if (++i >= 100)
127 {
128 TraceHandleCount_(hProcess, File, Line);
129 CloseHandle(hProcess);
130 break;
131 }
132 CloseHandle(hProcess);
133 Sleep(100);
134 }
135 Error = GetLastError();
136 ok_(File, Line)(hProcess == NULL, "OpenProcess succeeded unexpectedly for pid 0x%lx\n", ProcessId);
137 ok_(File, Line)(Error == ERROR_INVALID_PARAMETER, "Error = %lu\n", Error);
138 ok_(File, Line)(ExitCode == ExpectedExitCode, "Exit code is %lu but expected %u\n", ExitCode, ExpectedExitCode);
139}
140
141static
142VOID
143TestTerminateProcess(
144 _In_ HANDLE hEvent)
145{
146 HANDLE hProcess;
147 DWORD ProcessId;
148
149 /* Regular child process that returns from the test function */
150 /* HACK: These two tests don't work if stdout is a pipe. See StartChild */
151 ResetEvent(hEvent);
152 hProcess = StartChild(L"child", 0, &ProcessId);
153 WaitExpectSuccess(hEvent, 5000);
154 WaitExpectSuccess(hProcess, 5000);
155 CloseProcessAndVerify(hProcess, ProcessId, 0);
156
157 ResetEvent(hEvent);
158 hProcess = StartChild(L"child", 0, &ProcessId);
159 WaitExpectSuccess(hProcess, 5000);
160 WaitExpectSuccess(hEvent, 0);
161 CloseProcessAndVerify(hProcess, ProcessId, 0);
162
163 /* Suspended process -- never gets a chance to initialize */
164 ResetEvent(hEvent);
165 hProcess = StartChild(L"child", CREATE_SUSPENDED, &ProcessId);
166 WaitExpectTimeout(hEvent, 100);
167 WaitExpectTimeout(hProcess, 100);
168 TerminateProcess(hProcess, 123);
169 WaitExpectSuccess(hProcess, 5000);
170 CloseProcessAndVerify(hProcess, ProcessId, 123);
171
172 /* Waiting process -- we have to terminate it */
173 ResetEvent(hEvent);
174 hProcess = StartChild(L"wait", 0, &ProcessId);
175 WaitExpectTimeout(hProcess, 100);
176 TerminateProcess(hProcess, 123);
177 WaitExpectSuccess(hProcess, 5000);
178 CloseProcessAndVerify(hProcess, ProcessId, 123);
179
180 /* Process calls ExitProcess */
181 ResetEvent(hEvent);
182 hProcess = StartChild(L"child exit 456", 0, &ProcessId);
183 WaitExpectSuccess(hEvent, 5000);
184 WaitExpectSuccess(hProcess, 5000);
185 CloseProcessAndVerify(hProcess, ProcessId, 456);
186
187 /* Process calls TerminateProcess with GetCurrentProcess */
188 ResetEvent(hEvent);
189 hProcess = StartChild(L"child terminate 456", 0, &ProcessId);
190 WaitExpectSuccess(hEvent, 5000);
191 WaitExpectSuccess(hProcess, 5000);
192 CloseProcessAndVerify(hProcess, ProcessId, 456);
193
194 /* Process calls TerminateProcess with real handle to itself */
195 ResetEvent(hEvent);
196 hProcess = StartChild(L"child terminate2 456", 0, &ProcessId);
197 WaitExpectSuccess(hEvent, 5000);
198 WaitExpectSuccess(hProcess, 5000);
199 CloseProcessAndVerify(hProcess, ProcessId, 456);
200}
201
202START_TEST(TerminateProcess)
203{
204 HANDLE hEvent;
205 BOOL Success;
206 DWORD Error;
207 int argc;
208 char **argv;
209
210 hEvent = CreateEventW(NULL, TRUE, FALSE, L"kernel32_apitest_TerminateProcess_event");
211 Error = GetLastError();
212 if (!hEvent)
213 {
214 skip("CreateEvent failed with error %lu\n", Error);
215 return;
216 }
217 argc = winetest_get_mainargs(&argv);
218 if (argc >= 3)
219 {
220 ok(Error == ERROR_ALREADY_EXISTS, "Error = %lu\n", Error);
221 if (!strcmp(argv[2], "wait"))
222 {
223 WaitExpectSuccess(hEvent, 30000);
224 }
225 else
226 {
227 Success = SetEvent(hEvent);
228 ok(Success, "SetEvent failed with return %d, error %lu\n", Success, GetLastError());
229 }
230 }
231 else
232 {
233 ok(Error == NO_ERROR, "Error = %lu\n", Error);
234 TestTerminateProcess(hEvent);
235 }
236 CloseHandle(hEvent);
237 if (argc >= 5)
238 {
239 UINT ExitCode = strtol(argv[4], NULL, 10);
240
241 fflush(stdout);
242 if (!strcmp(argv[3], "exit"))
243 ExitProcess(ExitCode);
244 else if (!strcmp(argv[3], "terminate"))
245 TerminateProcess(GetCurrentProcess(), ExitCode);
246 else if (!strcmp(argv[3], "terminate2"))
247 {
248 HANDLE hProcess;
249 hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, GetCurrentProcessId());
250 TerminateProcess(hProcess, ExitCode);
251 }
252 ok(0, "Should have terminated\n");
253 }
254}