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