d145c8cca49c1204dc9e6139b0f4e09c0f463526
[idea/community.git] / native / runner / runnerw / runnerw.cpp
1 #include <windows.h>
2 #include <stdio.h>
3 #include <tlhelp32.h>
4 #include <iostream>
5 #include <string>
6
7 void PrintUsage() {
8         printf("Usage: runnerw.exe <app> <args>\n");
9         printf("where <app> is console application and <args> it's arguments.\n");
10         printf("\n");
11         printf(
12                         "Runner invokes console application as a process with inherited input and output streams.\n");
13         printf(
14                         "Input stream is scanned for presence of 2 char 255(IAC) and 243(BRK) sequence and generates Ctrl-Break event in that case.\n");
15         printf(
16                         "Also in case of all type of event(Ctrl-C, Close, Shutdown etc) Ctrl-Break event is generated.\n");
17
18         exit(0);
19 }
20
21 void ErrorMessage(char *str) {
22
23         LPVOID msg;
24
25         FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
26                         NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
27                         (LPTSTR) &msg, 0, NULL);
28
29         printf("%s: %s\n", str, msg);
30         LocalFree(msg);
31 }
32
33 void CtrlBreak() {
34         if (!GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, 0)) {
35                 ErrorMessage("GenerateConsoleCtrlEvent");
36         }
37 }
38
39 BOOL is_iac = FALSE;
40
41 char IAC = 5;
42 char BRK = 3;
43
44 BOOL Scan(char buf[], int count) {
45         for (int i = 0; i < count; i++) {
46                 if (is_iac) {
47                         if (buf[i] == BRK) {
48                                 CtrlBreak();
49                                 return TRUE;
50                         } else {
51                                 is_iac = FALSE;
52                         }
53                 }
54                 if (buf[i] == IAC) {
55                         is_iac = TRUE;
56                 }
57         }
58
59         return FALSE;
60 }
61
62 BOOL CtrlHandler(DWORD fdwCtrlType) {
63         switch (fdwCtrlType) {
64         case CTRL_C_EVENT:
65         case CTRL_CLOSE_EVENT:
66         case CTRL_LOGOFF_EVENT:
67         case CTRL_SHUTDOWN_EVENT:
68                 CtrlBreak();
69                 return (TRUE);
70         case CTRL_BREAK_EVENT:
71                 return FALSE;
72         default:
73                 return FALSE;
74         }
75 }
76
77 struct StdInThreadParams {
78         HANDLE hEvent;
79         HANDLE write_stdin;
80 };
81
82 DWORD WINAPI StdInThread(void *param) {
83         StdInThreadParams *threadParams = (StdInThreadParams *) param;
84         char buf[1];
85         memset(buf, 0, sizeof(buf));
86
87         HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
88         while (true) {
89                 DWORD cbRead = 0;
90                 DWORD cbWrite = 0;
91
92                 char c;
93                 ReadFile(hStdin, &c, 1, &cbRead, NULL);
94                 if (cbRead > 0) {
95                         buf[0] = c;
96                         bool ctrlBroken = Scan(buf, 1);
97                         WriteFile(threadParams->write_stdin, buf, 1, &cbWrite, NULL);
98                         if (ctrlBroken) {
99                                 SetEvent(threadParams->hEvent);
100                                 break;
101                         }
102                 }
103         }
104         return 0;
105 }
106
107 bool hasEnding(std::string const &fullString, std::string const &ending) {
108         if (fullString.length() > ending.length()) {
109                 return (0 == fullString.compare(fullString.length() - ending.length(),
110                                 ending.length(), ending));
111         } else {
112                 return false;
113         }
114 }
115
116 int main(int argc, char * argv[]) {
117         if (argc < 2) {
118                 PrintUsage();
119         }
120
121         std::string app(argv[1]);
122         std::string args("");
123
124         for (int i = 2; i < argc; i++) {
125                 args += " ";
126                 if (strchr(argv[i], ' ')) {
127                         args += "\"";
128                         args += argv[i];
129                         args += "\"";
130                 } else {
131                         args += argv[i];
132                 }
133         }
134
135         if (app.length() == 0) {
136                 PrintUsage();
137         }
138
139         STARTUPINFO si;
140         SECURITY_ATTRIBUTES sa;
141         PROCESS_INFORMATION pi;
142
143         HANDLE newstdin, write_stdin;
144
145         sa.lpSecurityDescriptor = NULL;
146
147         sa.nLength = sizeof(SECURITY_ATTRIBUTES);
148         sa.bInheritHandle = true;
149
150         if (!CreatePipe(&newstdin, &write_stdin, &sa, 0)) {
151                 ErrorMessage("CreatePipe");
152                 exit(0);
153         }
154
155         GetStartupInfo(&si);
156
157         si.dwFlags = STARTF_USESTDHANDLES;
158         si.wShowWindow = SW_HIDE;
159         si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
160         si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
161         si.hStdInput = newstdin;
162
163         char* c_app = new char[app.size() + 1];
164         strcpy(c_app, app.c_str());
165
166         if (hasEnding(app, std::string(".bat"))) {
167                 args = app + " " + args;
168                 c_app = NULL;
169         }
170
171         char* c_args = new char[args.size() + 1];
172         strcpy(c_args, args.c_str());
173
174         SetConsoleCtrlHandler((PHANDLER_ROUTINE) CtrlHandler, TRUE);
175
176         if (!CreateProcess(c_app, // Application name
177                         c_args, // Application arguments
178                         NULL, NULL, TRUE, CREATE_DEFAULT_ERROR_MODE, NULL, NULL, &si, &pi)) {
179                 ErrorMessage("CreateProcess");
180                 CloseHandle(newstdin);
181                 CloseHandle(write_stdin);
182                 exit(0);
183         }
184
185         unsigned long exit = 0;
186         unsigned long b_read;
187         unsigned long avail;
188
189         HANDLE threadEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
190
191         StdInThreadParams params;
192         params.hEvent = threadEvent;
193         params.write_stdin = write_stdin;
194
195         CreateThread(NULL, 0, &StdInThread, &params, 0, NULL);
196
197         HANDLE objects_to_wait[2];
198         objects_to_wait[0] = threadEvent;
199         objects_to_wait[1] = pi.hProcess;
200
201         while (true) {
202                 int rc = WaitForMultipleObjects(2, objects_to_wait, FALSE, INFINITE);
203                 if (rc == WAIT_OBJECT_0 + 1) {
204                         break;
205                 }
206         }
207
208         GetExitCodeProcess(pi.hProcess, &exit);
209
210         CloseHandle(pi.hThread);
211         CloseHandle(pi.hProcess);
212         CloseHandle(newstdin);
213         CloseHandle(write_stdin);
214         return exit;
215 }