/* See LICENSE.txt for the full license governing this code. */ /** * \file windows_process.c * * Source file for the process API on windows. */ #include #include #include #include #include "SDL_visualtest_process.h" #if defined(__WIN32__) void LogLastError(char* str) { LPVOID buffer; DWORD dw = GetLastError(); FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM| FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&buffer, 0, NULL); SDLTest_LogError("%s: %s", str, (char*)buffer); LocalFree(buffer); } int SDL_LaunchProcess(char* file, char* args, SDL_ProcessInfo* pinfo) { BOOL success; char* working_directory; char* command_line; int path_length, args_length; STARTUPINFO sui = {0}; sui.cb = sizeof(sui); if(!file) { SDLTest_LogError("Path to executable to launched cannot be NULL."); return 0; } if(!pinfo) { SDLTest_LogError("pinfo cannot be NULL."); return 0; } /* get the working directory of the process being launched, so that the process can load any resources it has in it's working directory */ path_length = SDL_strlen(file); if(path_length == 0) { SDLTest_LogError("Length of the file parameter is zero."); return 0; } working_directory = (char*)SDL_malloc(path_length + 1); if(!working_directory) { SDLTest_LogError("Could not allocate working_directory - malloc() failed."); return 0; } SDL_memcpy(working_directory, file, path_length + 1); PathRemoveFileSpec(working_directory); if(SDL_strlen(working_directory) == 0) { SDL_free(working_directory); working_directory = NULL; } /* join the file path and the args string together */ if(!args) args = ""; args_length = SDL_strlen(args); command_line = (char*)SDL_malloc(path_length + args_length + 2); if(!command_line) { SDLTest_LogError("Could not allocate command_line - malloc() failed."); return 0; } SDL_memcpy(command_line, file, path_length); command_line[path_length] = ' '; SDL_memcpy(command_line + path_length + 1, args, args_length + 1); /* create the process */ success = CreateProcess(NULL, command_line, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW, NULL, working_directory, &sui, &pinfo->pi); if(working_directory) { SDL_free(working_directory); working_directory = NULL; } SDL_free(command_line); if(!success) { LogLastError("CreateProcess() failed"); return 0; } return 1; } int SDL_GetProcessExitStatus(SDL_ProcessInfo* pinfo, SDL_ProcessExitStatus* ps) { DWORD exit_status; BOOL success; if(!pinfo) { SDLTest_LogError("pinfo cannot be NULL"); return 0; } if(!ps) { SDLTest_LogError("ps cannot be NULL"); return 0; } /* get the exit code */ success = GetExitCodeProcess(pinfo->pi.hProcess, &exit_status); if(!success) { LogLastError("GetExitCodeProcess() failed"); return 0; } if(exit_status == STILL_ACTIVE) ps->exit_status = -1; else ps->exit_status = exit_status; ps->exit_success = 1; return 1; } int SDL_IsProcessRunning(SDL_ProcessInfo* pinfo) { DWORD exit_status; BOOL success; if(!pinfo) { SDLTest_LogError("pinfo cannot be NULL"); return -1; } success = GetExitCodeProcess(pinfo->pi.hProcess, &exit_status); if(!success) { LogLastError("GetExitCodeProcess() failed"); return -1; } if(exit_status == STILL_ACTIVE) return 1; return 0; } static BOOL CALLBACK CloseWindowCallback(HWND hwnd, LPARAM lparam) { DWORD pid; SDL_ProcessInfo* pinfo; pinfo = (SDL_ProcessInfo*)lparam; GetWindowThreadProcessId(hwnd, &pid); if(pid == pinfo->pi.dwProcessId) { DWORD result; if(!SendMessageTimeout(hwnd, WM_CLOSE, 0, 0, SMTO_BLOCK, 1000, &result)) { if(GetLastError() != ERROR_TIMEOUT) { LogLastError("SendMessageTimeout() failed"); return FALSE; } } } return TRUE; } int SDL_QuitProcess(SDL_ProcessInfo* pinfo, SDL_ProcessExitStatus* ps) { DWORD wait_result; if(!pinfo) { SDLTest_LogError("pinfo argument cannot be NULL"); return 0; } if(!ps) { SDLTest_LogError("ps argument cannot be NULL"); return 0; } /* enumerate through all the windows, trying to close each one */ if(!EnumWindows(CloseWindowCallback, (LPARAM)pinfo)) { SDLTest_LogError("EnumWindows() failed"); return 0; } /* wait until the process terminates */ wait_result = WaitForSingleObject(pinfo->pi.hProcess, 1000); if(wait_result == WAIT_FAILED) { LogLastError("WaitForSingleObject() failed"); return 0; } if(wait_result != WAIT_OBJECT_0) { SDLTest_LogError("Process did not quit."); return 0; } /* get the exit code */ if(!SDL_GetProcessExitStatus(pinfo, ps)) { SDLTest_LogError("SDL_GetProcessExitStatus() failed"); return 0; } return 1; } int SDL_KillProcess(SDL_ProcessInfo* pinfo, SDL_ProcessExitStatus* ps) { BOOL success; DWORD exit_status, wait_result; if(!pinfo) { SDLTest_LogError("pinfo argument cannot be NULL"); return 0; } if(!ps) { SDLTest_LogError("ps argument cannot be NULL"); return 0; } /* initiate termination of the process */ success = TerminateProcess(pinfo->pi.hProcess, 0); if(!success) { LogLastError("TerminateProcess() failed"); return 0; } /* wait until the process terminates */ wait_result = WaitForSingleObject(pinfo->pi.hProcess, INFINITE); if(wait_result == WAIT_FAILED) { LogLastError("WaitForSingleObject() failed"); return 0; } /* get the exit code */ success = GetExitCodeProcess(pinfo->pi.hProcess, &exit_status); if(!success) { LogLastError("GetExitCodeProcess() failed"); return 0; } ps->exit_status = exit_status; ps->exit_success = 1; return 1; } #endif