2021-12-29 09:43:32 +08:00
/*
2021-12-30 19:25:47 +08:00
* This file is part of Red Panda C + +
2021-12-29 09:43:32 +08:00
* Copyright ( C ) 2020 - 2022 Roy Qu ( royqh1979 @ gmail . com )
*
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < https : //www.gnu.org/licenses/>.
*/
# include <string>
using std : : string ;
# include <stdio.h>
# include <windows.h>
2022-12-13 12:36:16 +08:00
# include <psapi.h>
2023-02-07 20:27:31 +08:00
# include <processthreadsapi.h>
2021-12-29 09:43:32 +08:00
# include <conio.h>
2024-05-09 21:31:22 +08:00
# include <stdbool.h>
2024-05-10 17:55:48 +08:00
# include <versionhelpers.h>
2021-12-29 09:43:32 +08:00
2022-03-29 09:43:24 +08:00
# ifndef WINBOOL
# define WINBOOL BOOL
# endif
2023-09-20 10:02:50 +08:00
# ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
# define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
# endif
2023-09-21 07:19:23 +08:00
# ifndef ENABLE_PROCESSED_OUTPUT
# define ENABLE_PROCESSED_OUTPUT 0x0001
# endif
2021-12-29 09:43:32 +08:00
# define MAX_COMMAND_LENGTH 32768
# define MAX_ERROR_LENGTH 2048
2024-05-09 21:31:22 +08:00
# define EXIT_WRONG_ARGUMENTS -1
# define EXIT_COMMAND_TOO_LONG -2
# define EXIT_CREATE_JOB_OBJ_FAILED -3
# define EXIT_SET_JOB_OBJ_INFO_FAILED -4
# define EXIT_CREATE_PROCESS_FAILED -5
# define EXIT_ASSGIN_PROCESS_JOB_FAILED -6
2021-12-29 09:43:32 +08:00
enum RunProgramFlag {
RPF_PAUSE_CONSOLE = 0x0001 ,
2023-09-21 08:17:07 +08:00
RPF_REDIRECT_INPUT = 0x0002 ,
RPF_ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004
2021-12-29 09:43:32 +08:00
} ;
HANDLE hJob ;
2024-05-10 17:55:48 +08:00
bool enableJobControl = IsWindowsXPOrGreater ( ) ;
2021-12-29 09:43:32 +08:00
2024-05-09 21:31:22 +08:00
bool pauseBeforeExit = false ;
2021-12-29 09:43:32 +08:00
LONGLONG GetClockTick ( ) {
LARGE_INTEGER dummy ;
QueryPerformanceCounter ( & dummy ) ;
return dummy . QuadPart ;
}
LONGLONG GetClockFrequency ( ) {
LARGE_INTEGER dummy ;
QueryPerformanceFrequency ( & dummy ) ;
return dummy . QuadPart ;
}
string GetErrorMessage ( ) {
string result ( MAX_ERROR_LENGTH , 0 ) ;
FormatMessageA (
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS ,
NULL , GetLastError ( ) , MAKELANGID ( LANG_NEUTRAL , SUBLANG_DEFAULT ) , & result [ 0 ] , result . size ( ) , NULL ) ;
// Clear newlines at end of string
for ( int i = result . length ( ) - 1 ; i > = 0 ; i - - ) {
if ( isspace ( result [ i ] ) ) {
result [ i ] = 0 ;
} else {
break ;
}
}
return result ;
}
void PauseExit ( int exitcode , bool reInp ) {
2024-05-09 21:31:22 +08:00
if ( pauseBeforeExit ) {
HANDLE hInp = NULL ;
if ( reInp ) {
SECURITY_ATTRIBUTES sa ;
sa . nLength = sizeof ( sa ) ;
sa . lpSecurityDescriptor = NULL ;
sa . bInheritHandle = TRUE ;
HANDLE hInp = CreateFileA ( " CONIN$ " , GENERIC_WRITE | GENERIC_READ ,
FILE_SHARE_READ , & sa , OPEN_EXISTING , /*FILE_ATTRIBUTE_NORMAL*/ 0 , NULL ) ;
//si.hStdInput = hInp;
SetStdHandle ( STD_INPUT_HANDLE , hInp ) ;
freopen ( " CONIN$ " , " r " , stdin ) ;
}
FlushConsoleInputBuffer ( GetStdHandle ( STD_INPUT_HANDLE ) ) ;
fflush ( stdin ) ;
printf ( " \n " ) ;
printf ( " Press ANY key to exit... " ) ;
getch ( ) ;
if ( reInp ) {
CloseHandle ( hInp ) ;
}
2021-12-29 09:43:32 +08:00
}
exit ( exitcode ) ;
}
2024-05-09 21:31:22 +08:00
string GetCommand ( int argc , char * * argv , bool & reInp , bool & enableVisualTerminalSeq ) {
2021-12-29 09:43:32 +08:00
string result ;
int flags = atoi ( argv [ 1 ] ) ;
reInp = flags & RPF_REDIRECT_INPUT ;
2024-05-09 21:31:22 +08:00
pauseBeforeExit = flags & RPF_PAUSE_CONSOLE ;
2023-09-21 08:17:07 +08:00
enableVisualTerminalSeq = flags & RPF_ENABLE_VIRTUAL_TERMINAL_PROCESSING ;
2022-03-16 20:08:39 +08:00
for ( int i = 3 ; i < argc ; i + + ) {
2021-12-29 09:43:32 +08:00
// Quote the argument in case the path name contains spaces
result + = string ( " \" " ) + string ( argv [ i ] ) + string ( " \" " ) ;
// Add a space except for the last argument
if ( i ! = ( argc - 1 ) ) {
result + = string ( " " ) ;
}
}
if ( result . length ( ) > MAX_COMMAND_LENGTH ) {
printf ( " \n -------------------------------- " ) ;
printf ( " \n Error: Length of command line string is over %d characters \n " , MAX_COMMAND_LENGTH ) ;
2024-05-09 21:31:22 +08:00
PauseExit ( EXIT_COMMAND_TOO_LONG , reInp ) ;
2021-12-29 09:43:32 +08:00
}
return result ;
}
2023-02-07 20:27:31 +08:00
DWORD ExecuteCommand ( string & command , bool reInp , LONGLONG & peakMemory , LONGLONG & execTime ) {
2021-12-29 09:43:32 +08:00
STARTUPINFOA si ;
PROCESS_INFORMATION pi ;
memset ( & si , 0 , sizeof ( si ) ) ;
si . cb = sizeof ( si ) ;
memset ( & pi , 0 , sizeof ( pi ) ) ;
DWORD dwCreationFlags = CREATE_BREAKAWAY_FROM_JOB ;
if ( ! CreateProcessA ( NULL , ( LPSTR ) command . c_str ( ) , NULL , NULL , true , dwCreationFlags , NULL , NULL , & si , & pi ) ) {
printf ( " \n -------------------------------- " ) ;
printf ( " \n Failed to execute \" %s \" : " , command . c_str ( ) ) ;
printf ( " \n Error %lu: %s \n " , GetLastError ( ) , GetErrorMessage ( ) . c_str ( ) ) ;
2024-05-09 21:31:22 +08:00
PauseExit ( EXIT_CREATE_PROCESS_FAILED , reInp ) ;
2021-12-29 09:43:32 +08:00
}
2024-05-10 17:55:48 +08:00
if ( enableJobControl ) {
WINBOOL bSuccess = AssignProcessToJobObject ( hJob , pi . hProcess ) ;
if ( bSuccess = = FALSE ) {
printf ( " AssignProcessToJobObject failed: error %lu \n " , GetLastError ( ) ) ;
PauseExit ( EXIT_ASSGIN_PROCESS_JOB_FAILED , reInp ) ;
}
2021-12-29 09:43:32 +08:00
}
WaitForSingleObject ( pi . hProcess , INFINITE ) ; // Wait for it to finish
2022-12-13 12:36:16 +08:00
peakMemory = 0 ;
PROCESS_MEMORY_COUNTERS counter ;
counter . cb = sizeof ( counter ) ;
if ( GetProcessMemoryInfo ( pi . hProcess , & counter ,
sizeof ( counter ) ) ) {
2024-05-05 10:24:36 +08:00
peakMemory = counter . PeakPagefileUsage / 1024 ;
2022-12-13 12:36:16 +08:00
}
2023-02-07 20:27:31 +08:00
FILETIME creationTime ;
FILETIME exitTime ;
FILETIME kernelTime ;
FILETIME userTime ;
execTime = 0 ;
if ( GetProcessTimes ( pi . hProcess , & creationTime , & exitTime , & kernelTime , & userTime ) ) {
execTime = ( ( LONGLONG ) kernelTime . dwHighDateTime < < 32 )
+ ( ( LONGLONG ) userTime . dwHighDateTime < < 32 )
+ ( kernelTime . dwLowDateTime ) + ( userTime . dwLowDateTime ) ;
}
2021-12-29 09:43:32 +08:00
DWORD result = 0 ;
GetExitCodeProcess ( pi . hProcess , & result ) ;
return result ;
}
2023-09-20 10:02:50 +08:00
void EnableVtSequence ( ) {
DWORD mode ;
HANDLE hConsole = GetStdHandle ( STD_OUTPUT_HANDLE ) ;
if ( GetConsoleMode ( hConsole , & mode ) )
2023-09-21 07:19:23 +08:00
SetConsoleMode ( hConsole , mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING | ENABLE_PROCESSED_OUTPUT ) ;
2023-09-20 10:02:50 +08:00
hConsole = GetStdHandle ( STD_ERROR_HANDLE ) ;
if ( GetConsoleMode ( hConsole , & mode ) )
2023-09-21 07:19:23 +08:00
SetConsoleMode ( hConsole , mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING | ENABLE_PROCESSED_OUTPUT ) ;
2023-09-20 10:02:50 +08:00
}
2021-12-29 09:43:32 +08:00
int main ( int argc , char * * argv ) {
2022-03-16 20:08:39 +08:00
const char * sharedMemoryId ;
2021-12-29 09:43:32 +08:00
// First make sure we aren't going to read nonexistent arrays
2022-03-16 20:08:39 +08:00
if ( argc < 4 ) {
2021-12-29 09:43:32 +08:00
printf ( " \n -------------------------------- " ) ;
2022-12-30 09:06:39 +08:00
printf ( " \n Usage: consolepauser.exe <0|1> <shared_memory_id> <filename> <parameters> \n " ) ;
2021-12-30 19:25:47 +08:00
printf ( " \n 1 means the STDIN is redirected by Red Panda C++; 0 means not \n " ) ;
2024-05-09 21:31:22 +08:00
PauseExit ( EXIT_WRONG_ARGUMENTS , false ) ;
2021-12-29 09:43:32 +08:00
}
// Make us look like the paused program
2022-03-16 20:08:39 +08:00
SetConsoleTitleA ( argv [ 3 ] ) ;
sharedMemoryId = argv [ 2 ] ;
2021-12-29 09:43:32 +08:00
SECURITY_ATTRIBUTES sa ;
sa . nLength = sizeof ( sa ) ;
sa . lpSecurityDescriptor = NULL ;
sa . bInheritHandle = FALSE ;
2024-05-09 21:31:22 +08:00
bool reInp ;
bool enableVisualTerminalSeq ;
// Then build the to-run application command
string command = GetCommand ( argc , argv , reInp , enableVisualTerminalSeq ) ;
2024-05-10 17:55:48 +08:00
if ( enableJobControl ) {
hJob = CreateJobObject ( & sa , NULL ) ;
2021-12-29 09:43:32 +08:00
2024-05-10 17:55:48 +08:00
if ( hJob = = NULL ) {
printf ( " CreateJobObject failed: error %lu \n " , GetLastError ( ) ) ;
PauseExit ( EXIT_CREATE_JOB_OBJ_FAILED , reInp ) ;
}
2021-12-29 09:43:32 +08:00
2024-05-10 17:55:48 +08:00
JOBOBJECT_EXTENDED_LIMIT_INFORMATION info ;
memset ( & info , 0 , sizeof ( JOBOBJECT_EXTENDED_LIMIT_INFORMATION ) ) ;
info . BasicLimitInformation . LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE ;
WINBOOL bSuccess = SetInformationJobObject ( hJob , JobObjectExtendedLimitInformation , & info , sizeof ( info ) ) ;
if ( bSuccess = = FALSE ) {
printf ( " SetInformationJobObject failed: error %lu \n " , GetLastError ( ) ) ;
PauseExit ( EXIT_SET_JOB_OBJ_INFO_FAILED , reInp ) ;
}
2021-12-29 09:43:32 +08:00
}
HANDLE hOutput = NULL ;
if ( reInp ) {
SECURITY_ATTRIBUTES sa ;
sa . nLength = sizeof ( sa ) ;
sa . lpSecurityDescriptor = NULL ;
sa . bInheritHandle = TRUE ;
hOutput = CreateFileA ( " CONOUT$ " , GENERIC_WRITE | GENERIC_READ ,
FILE_SHARE_WRITE , & sa , OPEN_EXISTING , /*FILE_ATTRIBUTE_NORMAL*/ 0 , NULL ) ;
SetStdHandle ( STD_OUTPUT_HANDLE , hOutput ) ;
SetStdHandle ( STD_ERROR_HANDLE , hOutput ) ;
freopen ( " CONOUT$ " , " w+ " , stdout ) ;
freopen ( " CONOUT$ " , " w+ " , stderr ) ;
} else {
FlushConsoleInputBuffer ( GetStdHandle ( STD_INPUT_HANDLE ) ) ;
}
2023-09-21 08:17:07 +08:00
if ( enableVisualTerminalSeq ) {
EnableVtSequence ( ) ;
}
2021-12-29 09:43:32 +08:00
HANDLE hSharedMemory = INVALID_HANDLE_VALUE ;
int BUF_SIZE = 1024 ;
char * pBuf = nullptr ;
hSharedMemory = OpenFileMappingA (
FILE_MAP_ALL_ACCESS ,
FALSE ,
2022-03-16 20:08:39 +08:00
sharedMemoryId
2021-12-29 09:43:32 +08:00
) ;
if ( hSharedMemory ! = NULL )
{
pBuf = ( char * ) MapViewOfFile ( hSharedMemory , // handle to map object
FILE_MAP_ALL_ACCESS , // read/write permission
0 ,
0 ,
BUF_SIZE ) ;
2023-09-21 21:27:24 +08:00
// } else {
// printf("can't open shared memory!\n");
2021-12-29 09:43:32 +08:00
}
// Save starting timestamp
LONGLONG starttime = GetClockTick ( ) ;
2022-12-13 12:36:16 +08:00
LONGLONG peakMemory = 0 ;
2023-02-07 20:27:31 +08:00
LONGLONG execTime = 0 ;
2021-12-29 09:43:32 +08:00
// Then execute said command
2023-02-07 20:27:31 +08:00
DWORD returnvalue = ExecuteCommand ( command , reInp , peakMemory , execTime ) ;
2021-12-29 09:43:32 +08:00
// Get ending timestamp
LONGLONG endtime = GetClockTick ( ) ;
double seconds = ( endtime - starttime ) / ( double ) GetClockFrequency ( ) ;
2023-02-07 20:27:31 +08:00
double execSeconds = ( double ) execTime / 10000 ;
2021-12-29 09:43:32 +08:00
if ( pBuf ) {
strcpy ( pBuf , " FINISHED " ) ;
2022-03-16 20:08:39 +08:00
UnmapViewOfFile ( pBuf ) ;
}
if ( hSharedMemory ! = NULL & & hSharedMemory ! = INVALID_HANDLE_VALUE ) {
CloseHandle ( hSharedMemory ) ;
2021-12-29 09:43:32 +08:00
}
// Done? Print return value of executed program
printf ( " \n -------------------------------- " ) ;
2023-08-21 10:45:09 +08:00
printf ( " \n Process exited after %.4g seconds with return value %lu (%.4g ms cpu time, %lld KB mem used). \n " , seconds , returnvalue , execSeconds , peakMemory ) ;
2024-05-09 21:31:22 +08:00
PauseExit ( returnvalue , reInp ) ;
2021-12-29 09:43:32 +08:00
return 0 ;
}