1 // Launcher.cpp : Defines the entry point for the console application.
2 //
3 // This program is designed to be a console wrapper program for other graphical
4 // programs that want to produce console output. It's very basic; all it does
5 // is look at argv[0], replace the extension (if present) with .exe (or append
6 // .exe if no extension is present), and then launch the .exe with the same
7 // name. The program is given a .com extension (even though it's really an exe
8 // file) so that the command interpreter will find it first when run from the
9 // command line. Note that this program is only really useful in Windows XP and
10 // later Windows OSes, since it only works in conjunction with the AttachConsole
11 // system call, which was introduced in Windows XP. Setting up pipes for the
12 // child program to use (with threads feeding/monitoring them here) would be
13 // required in the absense of the AttachConsole system call.
14
15 #include <windows.h>
16 #include <tchar.h>
17 #include <shlwapi.h>
18 #include <stdio.h>
19
20 #if _MSC_VER < 1400 // VC < VC 2005
21 #define _tcscpy_s(dst, bufSize, src) _tcscpy(dst, src)
22 #define _tcscat_s(dst, bufSize, src) _tcscat(dst, src)
23 #define _tcsncpy_s(dst, bufSize, src, len) _tcsncpy(dst, src, len)
24 #define __w64
25 #endif // VC < VC 2005
26
isConsoleRedirected()27 static bool isConsoleRedirected() {
28 HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
29 if (hStdOut != INVALID_HANDLE_VALUE) {
30 DWORD dwMode;
31 BOOL retval = GetConsoleMode(hStdOut, &dwMode);
32 DWORD lastError = GetLastError();
33 if (!retval && lastError == ERROR_INVALID_HANDLE) {
34 return true;
35 }
36 else {
37 return false;
38 }
39 }
40 // TODO: Not even a stdout so this is not even a console?
41 return false;
42 }
43
launchExe(_TCHAR * appName,_TCHAR * exeFilename,int argc,_TCHAR * argv[],bool consoleRedirected)44 int launchExe(_TCHAR *appName, _TCHAR *exeFilename, int argc, _TCHAR* argv[], bool consoleRedirected)
45 {
46 int i;
47 size_t len = _tcslen(exeFilename) + 2;
48 _TCHAR *commandLine;
49 STARTUPINFO startupInfo;
50 PROCESS_INFORMATION processInfo;
51 DWORD priority = NORMAL_PRIORITY_CLASS;
52 _TCHAR title[1024];
53 SECURITY_ATTRIBUTES sa;
54 HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
55 HANDLE hChildStdOutRd;
56 HANDLE hChildStdOutRdDup;
57 HANDLE hChildStdOutWr;
58
59 memset(&sa, 0, sizeof(sa));
60 sa.nLength = sizeof(sa);
61 sa.bInheritHandle = TRUE;
62 if (!CreatePipe(&hChildStdOutRd, &hChildStdOutWr, &sa, 0))
63 {
64 _tprintf(_T("Error creating pipe.\n"));
65 return 1;
66 }
67 if (!DuplicateHandle(GetCurrentProcess(), hChildStdOutRd,
68 GetCurrentProcess(), &hChildStdOutRdDup, 0, FALSE,
69 DUPLICATE_SAME_ACCESS))
70 {
71 _tprintf(_T("Error restricting pipe.\n"));
72 return 1;
73 }
74 CloseHandle(hChildStdOutRd);
75 for (i = 1; i < argc; i++)
76 {
77 len += _tcslen(argv[i]) + 3;
78 }
79 len += _tcslen(_T(" -HaveStdOut=1")) + 1;
80 commandLine = new _TCHAR[len];
81 commandLine[0] = 0;
82 bool quoted = false;
83 if (exeFilename[0] != '"' && _tcschr(exeFilename, ' ') != NULL)
84 {
85 quoted = true;
86 _tcscat_s(commandLine, len, _T("\""));
87 }
88 _tcscat_s(commandLine, len, exeFilename);
89 if (quoted)
90 {
91 _tcscat_s(commandLine, len, _T("\""));
92 }
93 for (i = 1; i < argc; i++)
94 {
95 quoted = false;
96 _tcscat_s(commandLine, len, _T(" "));
97 if (argv[i][0] != '"' && _tcschr(argv[i], ' ') != NULL)
98 {
99 quoted = true;
100 _tcscat_s(commandLine, len, _T("\""));
101 }
102 _tcscat_s(commandLine, len, argv[i]);
103 if (quoted)
104 {
105 _tcscat_s(commandLine, len, _T("\""));
106 }
107 }
108 _tcscat_s(commandLine, len, _T(" -HaveStdOut=1"));
109 memset(&startupInfo, 0, sizeof(startupInfo));
110 startupInfo.cb = sizeof(startupInfo);
111 #if _MSC_VER < 1400 // VC < VC 2005
112 _stprintf(title, _T("Command Line %s"), appName);
113 #else // VC < VC 2005
114 _stprintf_s(title, sizeof(title) / sizeof(title[0]), _T("Command Line %s"),
115 appName);
116 #endif // VC < VC 2005
117 startupInfo.lpTitle = title;
118 startupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
119 startupInfo.hStdOutput = hChildStdOutWr;
120 startupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
121 startupInfo.dwFlags = STARTF_USESTDHANDLES;
122 if (CreateProcess(exeFilename, commandLine, NULL, NULL, TRUE,
123 priority, NULL, NULL, &startupInfo, &processInfo))
124 {
125 #ifdef UNICODE
126 bool haveExtra = false;
127 CHAR extraChar = '\0';
128 #endif
129 CloseHandle(processInfo.hThread);
130 CloseHandle(hChildStdOutWr);
131 // while (true) now generates a warning.
132 for ( ; ; )
133 {
134 CHAR chBuf[4096];
135 DWORD dwRead = 0;
136 DWORD dwWritten = 0;
137 DWORD exitCode;
138
139 if (ReadFile(hChildStdOutRdDup, chBuf, sizeof(chBuf), &dwRead, NULL)
140 && dwRead > 0)
141 {
142 if (consoleRedirected)
143 {
144 #ifdef UNICODE
145 wchar_t wchBuf[4096];
146 CHAR* chBuf2 = (CHAR*)wchBuf;
147 DWORD ofs = 0;
148 if (haveExtra)
149 {
150 chBuf2[0] = extraChar;
151 ofs = 1;
152 }
153 memcpy(&chBuf2[ofs], chBuf, dwRead);
154 dwRead += ofs;
155 haveExtra = dwRead % 2 != 0;
156 if (haveExtra)
157 {
158 --dwRead;
159 extraChar = chBuf2[dwRead];
160 }
161 int utf8Len = WideCharToMultiByte(CP_UTF8, 0, wchBuf, (int)dwRead, NULL, 0, NULL, NULL);
162 if (utf8Len > 0)
163 {
164 char* utf8String = new char[(size_t)utf8Len + 1];
165 utf8String[utf8Len] = 0;
166 if ((utf8Len = WideCharToMultiByte(CP_UTF8, 0, wchBuf, (int)dwRead, utf8String, utf8Len, NULL, NULL)) > 0)
167 {
168 WriteFile(hStdOut, utf8String, (DWORD)strlen(utf8String), &dwWritten, NULL);
169 }
170 }
171 #else
172 WriteFile(hStdOut, chBuf, dwRead, &dwWritten, NULL);
173 #endif
174 }
175 else
176 {
177 WriteConsole(hStdOut, chBuf, dwRead / sizeof(TCHAR), &dwWritten, NULL);
178 }
179 }
180 else
181 {
182 Sleep(50);
183 }
184 GetExitCodeProcess(processInfo.hProcess, &exitCode);
185 if (exitCode != STILL_ACTIVE)
186 {
187 CloseHandle(processInfo.hProcess);
188 CloseHandle(hChildStdOutRdDup);
189 return 0;
190 }
191 }
192 }
193 else
194 {
195 DWORD error = GetLastError();
196 _TCHAR *buf;
197
198 CloseHandle(hChildStdOutRdDup);
199 CloseHandle(hChildStdOutWr);
200 _tprintf(_T("Failed to launch %s\n"), exeFilename);
201 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
202 FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL,
203 error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&buf,
204 0, NULL);
205 _tprintf(_T("%s\n"), buf);
206 LocalFree(buf);
207 return 1;
208 }
209 }
210
_tmain(int argc,_TCHAR * argv[])211 int _tmain(int argc, _TCHAR* argv[])
212 {
213 size_t appLen = _tcslen(argv[0]);
214 _TCHAR *dotSpot = &argv[0][appLen - 4];
215
216 //MessageBox(NULL, _T("Attach Debugger"), _T("Debug"), MB_OK);
217 bool consoleRedirected = isConsoleRedirected();
218 if (dotSpot[0] != '.')
219 {
220 dotSpot = NULL;
221 }
222 if (!dotSpot)
223 {
224 dotSpot = &argv[0][_tcslen(argv[0])];
225 }
226 size_t len = dotSpot - argv[0];
227 size_t fullLen = len + 7;
228 _TCHAR *appName = new _TCHAR[len + 1];
229 _TCHAR *exeFilename = new _TCHAR[fullLen];
230 _tcsncpy_s(appName, len + 1, argv[0], len);
231 appName[len] = 0;
232 _tcsncpy_s(exeFilename, fullLen, argv[0], len);
233 _tcscpy_s(&exeFilename[len], fullLen - len, _T("64.exe"));
234 if (!PathFileExists(exeFilename))
235 {
236 _tcscpy_s(&exeFilename[len], fullLen - len, _T(".exe"));
237 }
238 return launchExe(appName, exeFilename, argc, argv, consoleRedirected);
239 }
240
241