3 * gdb2 - gdb output splitter
5 * Copyright (C) 2000,2001 Nedko Arnaoudov <nedkohome@atia.com>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; see the file COPYING. If not, write to
19 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
25 #define GDB_INITIAL_COMMAND "gdb.exe"
26 #define PARAMS_SEPARATOR " "
27 #define PARAMS_SEPARATOR_LEN strlen(PARAMS_SEPARATOR);
29 void DisplayError(char *pszAPI
);
30 void ReadAndHandleOutput(HANDLE hPipeRead
);
31 void PrepAndLaunchRedirectedChild(HANDLE hChildStdOut
,
34 char *pchCommandLine
);
36 DWORD WINAPI
GetAndSendInputThread(LPVOID lpvThreadParam
);
37 HANDLE hChildProcess
= NULL
;
38 HANDLE hStdIn
= NULL
; // Handle to parents std input.
39 BOOL bRunThread
= TRUE
;
41 int main(int argc
, char* argv
[])
43 HANDLE hOutputReadTmp
,hOutputRead
,hOutputWrite
;
44 HANDLE hInputWriteTmp
,hInputRead
,hInputWrite
;
48 SECURITY_ATTRIBUTES sa
;
50 // Set up the security attributes struct.
51 sa
.nLength
= sizeof(SECURITY_ATTRIBUTES
);
52 sa
.lpSecurityDescriptor
= NULL
;
53 sa
.bInheritHandle
= TRUE
;
56 // Create the child output pipe.
57 if (!CreatePipe(&hOutputReadTmp
,&hOutputWrite
,&sa
,0))
58 DisplayError("CreatePipe");
61 // Create a duplicate of the output write handle for the std error
62 // write handle. This is necessary in case the child application
63 // closes one of its std output handles.
64 if (!DuplicateHandle(GetCurrentProcess(),hOutputWrite
,
65 GetCurrentProcess(),&hErrorWrite
,0,
66 TRUE
,DUPLICATE_SAME_ACCESS
))
67 DisplayError("DuplicateHandle");
70 // Create the child input pipe.
71 if (!CreatePipe(&hInputRead
,&hInputWriteTmp
,&sa
,0))
72 DisplayError("CreatePipe");
75 // Create new output read handle and the input write handles. Set
76 // the Properties to FALSE. Otherwise, the child inherits the
77 // properties and, as a result, non-closeable handles to the pipes
79 if (!DuplicateHandle(GetCurrentProcess(),hOutputReadTmp
,
81 &hOutputRead
, // Address of new handle.
82 0,FALSE
, // Make it uninheritable.
83 DUPLICATE_SAME_ACCESS
))
84 DisplayError("DupliateHandle");
86 if (!DuplicateHandle(GetCurrentProcess(),hInputWriteTmp
,
88 &hInputWrite
, // Address of new handle.
89 0,FALSE
, // Make it uninheritable.
90 DUPLICATE_SAME_ACCESS
))
91 DisplayError("DupliateHandle");
94 // Close inheritable copies of the handles you do not want to be
96 if (!CloseHandle(hOutputReadTmp
)) DisplayError("CloseHandle");
97 if (!CloseHandle(hInputWriteTmp
)) DisplayError("CloseHandle");
100 // Get std input handle so you can close it and force the ReadFile to
101 // fail when you want the input thread to exit.
102 if ( (hStdIn
= GetStdHandle(STD_INPUT_HANDLE
)) ==
103 INVALID_HANDLE_VALUE
)
104 DisplayError("GetStdHandle");
106 SetConsoleTitle("gdb control console");
108 size_t size
= strlen(GDB_INITIAL_COMMAND
)+PARAMS_SEPARATOR_LEN
;
110 // get parameters length
111 for (int i
= 1 ; i
< argc
; i
++)
112 size
+= strlen(argv
[i
])+PARAMS_SEPARATOR_LEN
;
114 size
++; // terminating null
116 char *pszCommandLine
= new char [size
];
119 printf("out of memory.\n");
123 strcpy(pszCommandLine
,GDB_INITIAL_COMMAND
);
124 for (int i
= 1 ; i
< argc
; i
++)
126 strcat(pszCommandLine
,PARAMS_SEPARATOR
);
127 strcat(pszCommandLine
,argv
[i
]);
130 PrepAndLaunchRedirectedChild(hOutputWrite
,hInputRead
,hErrorWrite
,pszCommandLine
);
133 // Close pipe handles (do not continue to modify the parent).
134 // You need to make sure that no handles to the write end of the
135 // output pipe are maintained in this process or else the pipe will
136 // not close when the child process exits and the ReadFile will hang.
137 if (!CloseHandle(hOutputWrite
)) DisplayError("CloseHandle");
138 if (!CloseHandle(hInputRead
)) DisplayError("CloseHandle");
139 if (!CloseHandle(hErrorWrite
)) DisplayError("CloseHandle");
142 // Launch the thread that gets the input and sends it to the child.
143 hThread
= CreateThread(NULL
,0,GetAndSendInputThread
,
144 (LPVOID
)hInputWrite
,0,&ThreadId
);
145 if (hThread
== NULL
) DisplayError("CreateThread");
148 // Read the child's output.
149 ReadAndHandleOutput(hOutputRead
);
150 // Redirection is complete
153 // Force the read on the input to return by closing the stdin handle.
154 if (!CloseHandle(hStdIn
)) DisplayError("CloseHandle");
157 // Tell the thread to exit and wait for thread to die.
160 if (WaitForSingleObject(hThread
,INFINITE
) == WAIT_FAILED
)
161 DisplayError("WaitForSingleObject");
163 if (!CloseHandle(hOutputRead
)) DisplayError("CloseHandle");
164 if (!CloseHandle(hInputWrite
)) DisplayError("CloseHandle");
169 ///////////////////////////////////////////////////////////////////////
170 // PrepAndLaunchRedirectedChild
171 // Sets up STARTUPINFO structure, and launches redirected child.
172 ///////////////////////////////////////////////////////////////////////
173 void PrepAndLaunchRedirectedChild(HANDLE hChildStdOut
,
176 char *pchCommandLine
)
178 PROCESS_INFORMATION pi
;
181 // Set up the start up info struct.
182 ZeroMemory(&si
,sizeof(STARTUPINFO
));
183 si
.cb
= sizeof(STARTUPINFO
);
184 si
.dwFlags
= STARTF_USESTDHANDLES
;
185 si
.hStdOutput
= hChildStdOut
;
186 si
.hStdInput
= hChildStdIn
;
187 si
.hStdError
= hChildStdErr
;
188 si
.lpTitle
= "debugged program console";
189 // Use this if you want to hide the child:
190 // si.wShowWindow = SW_HIDE;
191 // Note that dwFlags must include STARTF_USESHOWWINDOW if you want to
192 // use the wShowWindow flags.
195 // Launch the process that you want to redirect (in this case,
196 // Child.exe). Make sure Child.exe is in the same directory as
197 // redirect.c launch redirect from a command line to prevent location
199 if (!CreateProcess(NULL
,pchCommandLine
,NULL
,NULL
,TRUE
,
200 CREATE_NEW_CONSOLE
,NULL
,NULL
,&si
,&pi
))
201 DisplayError("CreateProcess");
204 // Set global child process handle to cause threads to exit.
205 hChildProcess
= pi
.hProcess
;
208 // Close any unnecessary handles.
209 if (!CloseHandle(pi
.hThread
)) DisplayError("CloseHandle");
212 ///////////////////////////////////////////////////////////////////////
213 // ReadAndHandleOutput
214 // Monitors handle for input. Exits when child exits or pipe breaks.
215 ///////////////////////////////////////////////////////////////////////
216 void ReadAndHandleOutput(HANDLE hPipeRead
)
224 if (!ReadFile(hPipeRead
,lpBuffer
,sizeof(lpBuffer
),
225 &nBytesRead
,NULL
) || !nBytesRead
)
227 if (GetLastError() == ERROR_BROKEN_PIPE
)
228 break; // pipe done - normal exit path.
230 DisplayError("ReadFile"); // Something bad happened.
233 // Display the character read on the screen.
234 if (!WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE
),lpBuffer
,
235 nBytesRead
,&nCharsWritten
,NULL
))
236 DisplayError("WriteConsole");
241 ///////////////////////////////////////////////////////////////////////
242 // GetAndSendInputThread
243 // Thread procedure that monitors the console for input and sends input
244 // to the child process through the input pipe.
245 // This thread ends when the child application exits.
246 ///////////////////////////////////////////////////////////////////////
247 DWORD WINAPI
GetAndSendInputThread(LPVOID lpvThreadParam
)
250 DWORD nBytesRead
,nBytesWrote
;
251 HANDLE hPipeWrite
= (HANDLE
)lpvThreadParam
;
253 // Get input from our console and send it to child through the pipe.
256 if(!ReadConsole(hStdIn
,read_buff
,1,&nBytesRead
,NULL
))
257 DisplayError("ReadConsole");
259 read_buff
[nBytesRead
] = '\0'; // Follow input with a NULL.
261 if (!WriteFile(hPipeWrite
,read_buff
,nBytesRead
,&nBytesWrote
,NULL
))
263 if (GetLastError() == ERROR_NO_DATA
)
264 break; // Pipe was closed (normal exit path).
266 DisplayError("WriteFile");
273 ///////////////////////////////////////////////////////////////////////
275 // Displays the error number and corresponding message.
276 ///////////////////////////////////////////////////////////////////////
277 void DisplayError(char *pszAPI
)
279 LPVOID lpvMessageBuffer
;
280 CHAR szPrintBuffer
[512];
284 FORMAT_MESSAGE_ALLOCATE_BUFFER
|FORMAT_MESSAGE_FROM_SYSTEM
,
285 NULL
, GetLastError(),
286 MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
),
287 (LPTSTR
)&lpvMessageBuffer
, 0, NULL
);
289 wsprintf(szPrintBuffer
,
290 "ERROR: API = %s.\n error code = %d.\n message = %s.\n",
291 pszAPI
, GetLastError(), (char *)lpvMessageBuffer
);
293 WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE
),szPrintBuffer
,
294 lstrlen(szPrintBuffer
),&nCharsWritten
,NULL
);
296 LocalFree(lpvMessageBuffer
);
297 ExitProcess(GetLastError());