2 * gdb2 - gdb output splitter
4 * Copyright (C) 2000,2001 Nedko Arnaoudov <nedkohome@atia.com>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; see the file COPYING. If not, write to
18 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
24 #define GDB_INITIAL_COMMAND "gdb.exe"
25 #define PARAMS_SEPARATOR " "
26 #define PARAMS_SEPARATOR_LEN strlen(PARAMS_SEPARATOR);
28 void DisplayError(const char *pszAPI
);
29 void ReadAndHandleOutput(HANDLE hPipeRead
);
30 void PrepAndLaunchRedirectedChild(HANDLE hChildStdOut
,
33 char *pchCommandLine
);
35 DWORD WINAPI
GetAndSendInputThread(LPVOID lpvThreadParam
);
36 HANDLE hChildProcess
= NULL
;
37 HANDLE hStdIn
= NULL
; // Handle to parents std input.
38 BOOL bRunThread
= TRUE
;
40 int main(int argc
, char* argv
[])
42 HANDLE hOutputReadTmp
,hOutputRead
,hOutputWrite
;
43 HANDLE hInputWriteTmp
,hInputRead
,hInputWrite
;
47 SECURITY_ATTRIBUTES sa
;
49 // Set up the security attributes struct.
50 sa
.nLength
= sizeof(SECURITY_ATTRIBUTES
);
51 sa
.lpSecurityDescriptor
= NULL
;
52 sa
.bInheritHandle
= TRUE
;
55 // Create the child output pipe.
56 if (!CreatePipe(&hOutputReadTmp
,&hOutputWrite
,&sa
,0))
57 DisplayError("CreatePipe");
60 // Create a duplicate of the output write handle for the std error
61 // write handle. This is necessary in case the child application
62 // closes one of its std output handles.
63 if (!DuplicateHandle(GetCurrentProcess(),hOutputWrite
,
64 GetCurrentProcess(),&hErrorWrite
,0,
65 TRUE
,DUPLICATE_SAME_ACCESS
))
66 DisplayError("DuplicateHandle");
69 // Create the child input pipe.
70 if (!CreatePipe(&hInputRead
,&hInputWriteTmp
,&sa
,0))
71 DisplayError("CreatePipe");
74 // Create new output read handle and the input write handles. Set
75 // the Properties to FALSE. Otherwise, the child inherits the
76 // properties and, as a result, non-closeable handles to the pipes
78 if (!DuplicateHandle(GetCurrentProcess(),hOutputReadTmp
,
80 &hOutputRead
, // Address of new handle.
81 0,FALSE
, // Make it uninheritable.
82 DUPLICATE_SAME_ACCESS
))
83 DisplayError("DupliateHandle");
85 if (!DuplicateHandle(GetCurrentProcess(),hInputWriteTmp
,
87 &hInputWrite
, // Address of new handle.
88 0,FALSE
, // Make it uninheritable.
89 DUPLICATE_SAME_ACCESS
))
90 DisplayError("DupliateHandle");
93 // Close inheritable copies of the handles you do not want to be
95 if (!CloseHandle(hOutputReadTmp
)) DisplayError("CloseHandle");
96 if (!CloseHandle(hInputWriteTmp
)) DisplayError("CloseHandle");
99 // Get std input handle so you can close it and force the ReadFile to
100 // fail when you want the input thread to exit.
101 if ( (hStdIn
= GetStdHandle(STD_INPUT_HANDLE
)) ==
102 INVALID_HANDLE_VALUE
)
103 DisplayError("GetStdHandle");
105 SetConsoleTitle("gdb control console");
107 size_t size
= strlen(GDB_INITIAL_COMMAND
)+PARAMS_SEPARATOR_LEN
;
109 // get parameters length
110 for (int i
= 1 ; i
< argc
; i
++)
111 size
+= strlen(argv
[i
])+PARAMS_SEPARATOR_LEN
;
113 size
++; // terminating null
115 char *pszCommandLine
= new char [size
];
118 printf("out of memory.\n");
122 strcpy(pszCommandLine
,GDB_INITIAL_COMMAND
);
123 for (int i
= 1 ; i
< argc
; i
++)
125 strcat(pszCommandLine
,PARAMS_SEPARATOR
);
126 strcat(pszCommandLine
,argv
[i
]);
129 PrepAndLaunchRedirectedChild(hOutputWrite
,hInputRead
,hErrorWrite
,pszCommandLine
);
132 // Close pipe handles (do not continue to modify the parent).
133 // You need to make sure that no handles to the write end of the
134 // output pipe are maintained in this process or else the pipe will
135 // not close when the child process exits and the ReadFile will hang.
136 if (!CloseHandle(hOutputWrite
)) DisplayError("CloseHandle");
137 if (!CloseHandle(hInputRead
)) DisplayError("CloseHandle");
138 if (!CloseHandle(hErrorWrite
)) DisplayError("CloseHandle");
141 // Launch the thread that gets the input and sends it to the child.
142 hThread
= CreateThread(NULL
,0,GetAndSendInputThread
,
143 (LPVOID
)hInputWrite
,0,&ThreadId
);
144 if (hThread
== NULL
) DisplayError("CreateThread");
147 // Read the child's output.
148 ReadAndHandleOutput(hOutputRead
);
149 // Redirection is complete
152 // Force the read on the input to return by closing the stdin handle.
153 if (!CloseHandle(hStdIn
)) DisplayError("CloseHandle");
156 // Tell the thread to exit and wait for thread to die.
159 if (WaitForSingleObject(hThread
,INFINITE
) == WAIT_FAILED
)
160 DisplayError("WaitForSingleObject");
162 if (!CloseHandle(hOutputRead
)) DisplayError("CloseHandle");
163 if (!CloseHandle(hInputWrite
)) DisplayError("CloseHandle");
168 ///////////////////////////////////////////////////////////////////////
169 // PrepAndLaunchRedirectedChild
170 // Sets up STARTUPINFO structure, and launches redirected child.
171 ///////////////////////////////////////////////////////////////////////
172 void PrepAndLaunchRedirectedChild(HANDLE hChildStdOut
,
175 char *pchCommandLine
)
177 PROCESS_INFORMATION pi
;
179 static CHAR Title
[] = "debugged program console";
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
;
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(const 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());