/* $Id$ * * gdb2 - gdb output splitter * * Copyright (C) 2000,2001 Nedko Arnaoudov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include "ph.h" #define GDB_INITIAL_COMMAND "gdb.exe" #define PARAMS_SEPARATOR " " #define PARAMS_SEPARATOR_LEN strlen(PARAMS_SEPARATOR); void DisplayError(char *pszAPI); void ReadAndHandleOutput(HANDLE hPipeRead); void PrepAndLaunchRedirectedChild(HANDLE hChildStdOut, HANDLE hChildStdIn, HANDLE hChildStdErr, char *pchCommandLine); DWORD WINAPI GetAndSendInputThread(LPVOID lpvThreadParam); HANDLE hChildProcess = NULL; HANDLE hStdIn = NULL; // Handle to parents std input. BOOL bRunThread = TRUE; int main(int argc, char* argv[]) { HANDLE hOutputReadTmp,hOutputRead,hOutputWrite; HANDLE hInputWriteTmp,hInputRead,hInputWrite; HANDLE hErrorWrite; HANDLE hThread; DWORD ThreadId; SECURITY_ATTRIBUTES sa; // Set up the security attributes struct. sa.nLength= sizeof(SECURITY_ATTRIBUTES); sa.lpSecurityDescriptor = NULL; sa.bInheritHandle = TRUE; // Create the child output pipe. if (!CreatePipe(&hOutputReadTmp,&hOutputWrite,&sa,0)) DisplayError("CreatePipe"); // Create a duplicate of the output write handle for the std error // write handle. This is necessary in case the child application // closes one of its std output handles. if (!DuplicateHandle(GetCurrentProcess(),hOutputWrite, GetCurrentProcess(),&hErrorWrite,0, TRUE,DUPLICATE_SAME_ACCESS)) DisplayError("DuplicateHandle"); // Create the child input pipe. if (!CreatePipe(&hInputRead,&hInputWriteTmp,&sa,0)) DisplayError("CreatePipe"); // Create new output read handle and the input write handles. Set // the Properties to FALSE. Otherwise, the child inherits the // properties and, as a result, non-closeable handles to the pipes // are created. if (!DuplicateHandle(GetCurrentProcess(),hOutputReadTmp, GetCurrentProcess(), &hOutputRead, // Address of new handle. 0,FALSE, // Make it uninheritable. DUPLICATE_SAME_ACCESS)) DisplayError("DupliateHandle"); if (!DuplicateHandle(GetCurrentProcess(),hInputWriteTmp, GetCurrentProcess(), &hInputWrite, // Address of new handle. 0,FALSE, // Make it uninheritable. DUPLICATE_SAME_ACCESS)) DisplayError("DupliateHandle"); // Close inheritable copies of the handles you do not want to be // inherited. if (!CloseHandle(hOutputReadTmp)) DisplayError("CloseHandle"); if (!CloseHandle(hInputWriteTmp)) DisplayError("CloseHandle"); // Get std input handle so you can close it and force the ReadFile to // fail when you want the input thread to exit. if ( (hStdIn = GetStdHandle(STD_INPUT_HANDLE)) == INVALID_HANDLE_VALUE ) DisplayError("GetStdHandle"); SetConsoleTitle("gdb control console"); size_t size = strlen(GDB_INITIAL_COMMAND)+PARAMS_SEPARATOR_LEN; // get parameters length for (int i = 1 ; i < argc ; i++) size += strlen(argv[i])+PARAMS_SEPARATOR_LEN; size++; // terminating null char *pszCommandLine = new char [size]; if (!pszCommandLine) { printf("out of memory.\n"); return -1; } strcpy(pszCommandLine,GDB_INITIAL_COMMAND); for (int i = 1 ; i < argc ; i++) { strcat(pszCommandLine,PARAMS_SEPARATOR); strcat(pszCommandLine,argv[i]); } PrepAndLaunchRedirectedChild(hOutputWrite,hInputRead,hErrorWrite,pszCommandLine); // Close pipe handles (do not continue to modify the parent). // You need to make sure that no handles to the write end of the // output pipe are maintained in this process or else the pipe will // not close when the child process exits and the ReadFile will hang. if (!CloseHandle(hOutputWrite)) DisplayError("CloseHandle"); if (!CloseHandle(hInputRead )) DisplayError("CloseHandle"); if (!CloseHandle(hErrorWrite)) DisplayError("CloseHandle"); // Launch the thread that gets the input and sends it to the child. hThread = CreateThread(NULL,0,GetAndSendInputThread, (LPVOID)hInputWrite,0,&ThreadId); if (hThread == NULL) DisplayError("CreateThread"); // Read the child's output. ReadAndHandleOutput(hOutputRead); // Redirection is complete // Force the read on the input to return by closing the stdin handle. if (!CloseHandle(hStdIn)) DisplayError("CloseHandle"); // Tell the thread to exit and wait for thread to die. bRunThread = FALSE; if (WaitForSingleObject(hThread,INFINITE) == WAIT_FAILED) DisplayError("WaitForSingleObject"); if (!CloseHandle(hOutputRead)) DisplayError("CloseHandle"); if (!CloseHandle(hInputWrite)) DisplayError("CloseHandle"); return 0; } /////////////////////////////////////////////////////////////////////// // PrepAndLaunchRedirectedChild // Sets up STARTUPINFO structure, and launches redirected child. /////////////////////////////////////////////////////////////////////// void PrepAndLaunchRedirectedChild(HANDLE hChildStdOut, HANDLE hChildStdIn, HANDLE hChildStdErr, char *pchCommandLine) { PROCESS_INFORMATION pi; STARTUPINFO si; // Set up the start up info struct. ZeroMemory(&si,sizeof(STARTUPINFO)); si.cb = sizeof(STARTUPINFO); si.dwFlags = STARTF_USESTDHANDLES; si.hStdOutput = hChildStdOut; si.hStdInput = hChildStdIn; si.hStdError = hChildStdErr; si.lpTitle = "debugged program console"; // Use this if you want to hide the child: // si.wShowWindow = SW_HIDE; // Note that dwFlags must include STARTF_USESHOWWINDOW if you want to // use the wShowWindow flags. // Launch the process that you want to redirect (in this case, // Child.exe). Make sure Child.exe is in the same directory as // redirect.c launch redirect from a command line to prevent location // confusion. if (!CreateProcess(NULL,pchCommandLine,NULL,NULL,TRUE, CREATE_NEW_CONSOLE,NULL,NULL,&si,&pi)) DisplayError("CreateProcess"); // Set global child process handle to cause threads to exit. hChildProcess = pi.hProcess; // Close any unnecessary handles. if (!CloseHandle(pi.hThread)) DisplayError("CloseHandle"); } /////////////////////////////////////////////////////////////////////// // ReadAndHandleOutput // Monitors handle for input. Exits when child exits or pipe breaks. /////////////////////////////////////////////////////////////////////// void ReadAndHandleOutput(HANDLE hPipeRead) { CHAR lpBuffer[256]; DWORD nBytesRead; DWORD nCharsWritten; while(TRUE) { if (!ReadFile(hPipeRead,lpBuffer,sizeof(lpBuffer), &nBytesRead,NULL) || !nBytesRead) { if (GetLastError() == ERROR_BROKEN_PIPE) break; // pipe done - normal exit path. else DisplayError("ReadFile"); // Something bad happened. } // Display the character read on the screen. if (!WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE),lpBuffer, nBytesRead,&nCharsWritten,NULL)) DisplayError("WriteConsole"); } } /////////////////////////////////////////////////////////////////////// // GetAndSendInputThread // Thread procedure that monitors the console for input and sends input // to the child process through the input pipe. // This thread ends when the child application exits. /////////////////////////////////////////////////////////////////////// DWORD WINAPI GetAndSendInputThread(LPVOID lpvThreadParam) { CHAR read_buff[256]; DWORD nBytesRead,nBytesWrote; HANDLE hPipeWrite = (HANDLE)lpvThreadParam; // Get input from our console and send it to child through the pipe. while (bRunThread) { if(!ReadConsole(hStdIn,read_buff,1,&nBytesRead,NULL)) DisplayError("ReadConsole"); read_buff[nBytesRead] = '\0'; // Follow input with a NULL. if (!WriteFile(hPipeWrite,read_buff,nBytesRead,&nBytesWrote,NULL)) { if (GetLastError() == ERROR_NO_DATA) break; // Pipe was closed (normal exit path). else DisplayError("WriteFile"); } } return 1; } /////////////////////////////////////////////////////////////////////// // DisplayError // Displays the error number and corresponding message. /////////////////////////////////////////////////////////////////////// void DisplayError(char *pszAPI) { LPVOID lpvMessageBuffer; CHAR szPrintBuffer[512]; DWORD nCharsWritten; FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpvMessageBuffer, 0, NULL); wsprintf(szPrintBuffer, "ERROR: API = %s.\n error code = %d.\n message = %s.\n", pszAPI, GetLastError(), (char *)lpvMessageBuffer); WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE),szPrintBuffer, lstrlen(szPrintBuffer),&nCharsWritten,NULL); LocalFree(lpvMessageBuffer); ExitProcess(GetLastError()); }