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