1/*
2 * This file is part of Red Panda C++
3 * Copyright (C) 2020-2022 Roy Qu (royqh1979@gmail.com)
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 */
18
19#include <string>
20using std::string;
21#include <stdio.h>
22#include <windows.h>
23#include <conio.h>
24
25#ifndef WINBOOL
26#define WINBOOL BOOL
27#endif
28#define MAX_COMMAND_LENGTH 32768
29#define MAX_ERROR_LENGTH 2048
30
31enum RunProgramFlag {
32 RPF_PAUSE_CONSOLE = 0x0001,
33 RPF_REDIRECT_INPUT = 0x0002
34};
35
36HANDLE hJob;
37
38LONGLONG GetClockTick() {
39 LARGE_INTEGER dummy;
40 QueryPerformanceCounter(&dummy);
41 return dummy.QuadPart;
42}
43
44LONGLONG GetClockFrequency() {
45 LARGE_INTEGER dummy;
46 QueryPerformanceFrequency(&dummy);
47 return dummy.QuadPart;
48}
49
50
51string GetErrorMessage() {
52 string result(MAX_ERROR_LENGTH,0);
53 FormatMessageA(
54 FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
55 NULL,GetLastError(),MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),&result[0],result.size(),NULL);
56
57 // Clear newlines at end of string
58 for(int i = result.length()-1;i >= 0;i--) {
59 if(isspace(result[i])) {
60 result[i] = 0;
61 } else {
62 break;
63 }
64 }
65 return result;
66}
67
68void PauseExit(int exitcode, bool reInp) {
69 HANDLE hInp=NULL;
70 if (reInp) {
71 SECURITY_ATTRIBUTES sa;
72 sa.nLength = sizeof(sa);
73 sa.lpSecurityDescriptor = NULL;
74 sa.bInheritHandle = TRUE;
75
76 HANDLE hInp = CreateFileA("CONIN$", GENERIC_WRITE | GENERIC_READ,
77 FILE_SHARE_READ , &sa, OPEN_EXISTING, /*FILE_ATTRIBUTE_NORMAL*/0, NULL);
78 //si.hStdInput = hInp;
79 SetStdHandle(STD_INPUT_HANDLE,hInp);
80 freopen("CONIN$","r",stdin);
81 }
82 FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE));
83 fflush(stdin);
84 printf("\n");
85 printf("Press ANY key to exit...");
86 getch();
87 if (reInp) {
88 CloseHandle(hInp);
89 }
90 exit(exitcode);
91}
92
93string GetCommand(int argc,char** argv,bool &reInp,bool &pauseAfterExit) {
94 string result;
95 int flags = atoi(argv[1]);
96 reInp = flags & RPF_REDIRECT_INPUT;
97 pauseAfterExit = flags & RPF_PAUSE_CONSOLE;
98 for(int i = 3;i < argc;i++) {
99 // Quote the argument in case the path name contains spaces
100 result += string("\"") + string(argv[i]) + string("\"");
101
102 // Add a space except for the last argument
103 if(i != (argc-1)) {
104 result += string(" ");
105 }
106 }
107
108 if(result.length() > MAX_COMMAND_LENGTH) {
109 printf("\n--------------------------------");
110 printf("\nError: Length of command line string is over %d characters\n",MAX_COMMAND_LENGTH);
111 PauseExit(EXIT_FAILURE,reInp);
112 }
113
114 return result;
115}
116
117DWORD ExecuteCommand(string& command,bool reInp) {
118 STARTUPINFOA si;
119 PROCESS_INFORMATION pi;
120
121 memset(&si,0,sizeof(si));
122 si.cb = sizeof(si);
123 memset(&pi,0,sizeof(pi));
124
125 DWORD dwCreationFlags = CREATE_BREAKAWAY_FROM_JOB;
126
127
128 if(!CreateProcessA(NULL, (LPSTR)command.c_str(), NULL, NULL, true, dwCreationFlags, NULL, NULL, &si, &pi)) {
129 printf("\n--------------------------------");
130 printf("\nFailed to execute \"%s\":",command.c_str());
131 printf("\nError %lu: %s\n",GetLastError(),GetErrorMessage().c_str());
132 PauseExit(EXIT_FAILURE,reInp);
133 }
134 WINBOOL bSuccess = AssignProcessToJobObject( hJob, pi.hProcess );
135 if ( bSuccess == FALSE ) {
136 printf( "AssignProcessToJobObject failed: error %d\n", GetLastError() );
137 return 0;
138 }
139
140 WaitForSingleObject(pi.hProcess, INFINITE); // Wait for it to finish
141
142
143 DWORD result = 0;
144 GetExitCodeProcess(pi.hProcess, &result);
145 return result;
146}
147
148int main(int argc, char** argv) {
149
150 const char *sharedMemoryId;
151 // First make sure we aren't going to read nonexistent arrays
152 if(argc < 4) {
153 printf("\n--------------------------------");
154 printf("\nUsage: ConsolePauser.exe <0|1> <shared_memory_id> <filename> <parameters>\n");
155 printf("\n 1 means the STDIN is redirected by Red Panda C++; 0 means not\n");
156 PauseExit(EXIT_SUCCESS,false);
157 }
158
159 // Make us look like the paused program
160 SetConsoleTitleA(argv[3]);
161
162 sharedMemoryId = argv[2];
163
164 SECURITY_ATTRIBUTES sa;
165 sa.nLength = sizeof(sa);
166 sa.lpSecurityDescriptor = NULL;
167 sa.bInheritHandle = FALSE;
168
169 hJob= CreateJobObject( &sa, NULL );
170
171 if ( hJob == NULL ) {
172 printf( "CreateJobObject failed: error %d\n", GetLastError() );
173 return 0;
174 }
175
176 JOBOBJECT_EXTENDED_LIMIT_INFORMATION info;
177 memset(&info,0,sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
178 info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
179 WINBOOL bSuccess = SetInformationJobObject( hJob, JobObjectExtendedLimitInformation, &info, sizeof( info ) );
180 if ( bSuccess == FALSE ) {
181 printf( "SetInformationJobObject failed: error %d\n", GetLastError() );
182 return 0;
183 }
184
185 bool reInp;
186 bool pauseAfterExit;
187 // Then build the to-run application command
188 string command = GetCommand(argc,argv,reInp, pauseAfterExit);
189 HANDLE hOutput = NULL;
190 if (reInp) {
191 SECURITY_ATTRIBUTES sa;
192 sa.nLength = sizeof(sa);
193 sa.lpSecurityDescriptor = NULL;
194 sa.bInheritHandle = TRUE;
195
196 hOutput = CreateFileA("CONOUT$", GENERIC_WRITE | GENERIC_READ,
197 FILE_SHARE_WRITE , &sa, OPEN_EXISTING, /*FILE_ATTRIBUTE_NORMAL*/0, NULL);
198 SetStdHandle(STD_OUTPUT_HANDLE, hOutput);
199 SetStdHandle(STD_ERROR_HANDLE, hOutput);
200 freopen("CONOUT$","w+",stdout);
201 freopen("CONOUT$","w+",stderr);
202 } else {
203 FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE));
204 }
205
206 HANDLE hSharedMemory=INVALID_HANDLE_VALUE;
207 int BUF_SIZE=1024;
208 char* pBuf=nullptr;
209 hSharedMemory = OpenFileMappingA(
210 FILE_MAP_ALL_ACCESS,
211 FALSE,
212 sharedMemoryId
213 );
214 if (hSharedMemory != NULL)
215 {
216 pBuf = (char*) MapViewOfFile(hSharedMemory, // handle to map object
217 FILE_MAP_ALL_ACCESS, // read/write permission
218 0,
219 0,
220 BUF_SIZE);
221 } else {
222 printf("can't open shared memory!");
223 }
224
225 // Save starting timestamp
226 LONGLONG starttime = GetClockTick();
227
228 // Then execute said command
229 DWORD returnvalue = ExecuteCommand(command,reInp);
230
231 // Get ending timestamp
232 LONGLONG endtime = GetClockTick();
233 double seconds = (endtime - starttime) / (double)GetClockFrequency();
234
235 if (pBuf) {
236 strcpy(pBuf,"FINISHED");
237 UnmapViewOfFile(pBuf);
238 }
239 if (hSharedMemory != NULL && hSharedMemory!=INVALID_HANDLE_VALUE) {
240 CloseHandle(hSharedMemory);
241 }
242
243 // Done? Print return value of executed program
244 printf("\n--------------------------------");
245 printf("\nProcess exited after %.4g seconds with return value %lu\n",seconds,returnvalue);
246 if (pauseAfterExit)
247 PauseExit(returnvalue,reInp);
248 return 0;
249}
250
251