[Image] [Image] [Image] [Image] [Image] [Image] [Image] [Return] Redirect Console I/O With Pipes [Return] --------------------------------------------------------------------------- Console programs (DOS boxes) do their thing, but they are, after all, DOS boxes. There are times when a program's output would be better suited in a GUI environment, such as in the case of an IDE which drives a command line compiler and presents the output information in a status window. Windows NT has a nice and easy way of doing this "sort of" by simply plugging a Winsock socket handle in the STARTUPINFO structure before running a command line program, but that doesn't seem to work under Windows 95 and it does require TCP/IP, sockets and Windows NT. Not that it's a big deal if you're reading this, but that's not the majority. Instead, pipes may be used to redirect the standard I/O from a command line to a GUI program and vice versa, the GUI program doing whatever it likes with the data. Listed below is a small but useful GUI program which uses a dialog box as it's main window. It's nothing pretty and hardly what I'd recommend as a program take off point as it's practical function is very limited, but it does attempt to show how you can redirect command line I/O to a GUI app and the other way around. An event is used to allow the GUI end to shut down any running threads and console windows when the dialog box is closed. When the dialog box is opened, a thread is created which starts cmd.exe or command.com and waits until either the event is signaled (GUI app is closing) or the console window is closed. The thread then breaks out of it's monitor loop and ends. Data entered into an edit control is sent to the console app upon pressing "Send" and data received is put into a listbox. Note that the point isn't to make the listbox output very readable, but rather to show that there is some. Thus, I didn't bother to break lines at CRLF's and so on. That's your job (heh). Anonymous pipes are used to communicate between the GUI and console ends. Also note that I did a version check to run the correct "DOS box command" depending on whether it's run under Windows NT or Windows 95. If running something else, that test can be omitted as long as it's not version dependent, like some other "DOS" program. #define WIN32_LEAN_AND_MEAN #include "windows.h" // === Function Prototypes ==================================================== BOOL WINAPI MainDlgProc( HWND, UINT, WPARAM, LPARAM ); DWORD StartCommandLine( HWND ); // === Global Variables ======================================================= HINSTANCE hInst; // Program instance handle HANDLE hThreadEvent; // Used to signal thread(s) to stop HANDLE hPipeOut; // Pipe from here to console (dos box) // === Program Entry Point ==================================================== int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrev, LPSTR lpCmd, int nShow ) { hInst = hInstance; DialogBox( hInstance, MAKEINTRESOURCE( 10000 ), NULL, MainDlgProc ); return( FALSE ); } // === Main Window (Dialog Box) Procedure ===================================== BOOL WINAPI MainDlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam ) { switch( msg ) { case WM_INITDIALOG: { HANDLE hThread; DWORD dwID; // Create signal event to tell threads to stop hThreadEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); hPipeOut = NULL; // Clear the pipe "out" handle // Start the DOS box monitor thread hThread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)StartCommandLine, (LPVOID)hDlg, 0, &dwID ); if( ! hThread ) { CloseHandle( hThreadEvent ); EndDialog( hDlg, TRUE ); // End dialog (and app) if thread fails return( TRUE ); } return( TRUE ); } case WM_COMMAND: if( wParam == IDCANCEL ) // Dialog box is closing...shut down { if( hThreadEvent ) // If event handle is valid { SetEvent( hThreadEvent ); // Tell threads to quit ASAP Sleep( 500 ); // Wait a little to let it die CloseHandle( hThreadEvent ); // Kill the event handle } EndDialog( hDlg, TRUE ); // Then exit the dialog and the app return( TRUE ); } if( wParam == IDOK ) // Send something to the dos box { char szOut[ 128 ]; DWORD dwOut; if( hPipeOut ) // If the "out" pipe is valid { // And there's something to send if( GetDlgItemText( hDlg, 101, szOut, 128 ) ) { strcat( szOut, "\r\n" ); // Write it to the pipe WriteFile( hPipeOut, szOut, strlen( szOut ), &dwOut, NULL ); } } break; } break; } return( FALSE ); } // End of MainDlgProc // === Command Line Monitoring Thread ========================================= DWORD StartCommandLine( HWND hDlg ) { OSVERSIONINFO osvi; STARTUPINFO si; PROCESS_INFORMATION pi; HANDLE hInputRead, hInputWrite; HANDLE hOutputRead, hOutputWrite; HANDLE hDupInputWrite; HANDLE hDupOutputRead; BOOL bStatus; DWORD dwRead, dwAvail, dwMsg; char szInput[ 1024 ]; memset( &si, 0, sizeof(STARTUPINFO) ); // Initialize structures memset( &pi, 0, sizeof(PROCESS_INFORMATION) ); // Create pipe for console -> GUI data bStatus = CreatePipe( &hInputRead, &hInputWrite, NULL, 1024 ); if( ! bStatus ) return( FALSE ); // Create pipe for GUI -> console data bStatus = CreatePipe( &hOutputRead, &hOutputWrite, NULL, 1024 ); if( ! bStatus ) { CloseHandle( hInputRead ); // Close first pipe if second fails CloseHandle( hInputWrite ); return( FALSE ); } // Make an inheritable pipe endpoint handle DuplicateHandle( GetCurrentProcess(), hInputWrite, GetCurrentProcess(), &hDupInputWrite, 0L, TRUE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS ); // and a second for GUI -> console DuplicateHandle( GetCurrentProcess(), hOutputRead, GetCurrentProcess(), &hDupOutputRead, 0L, TRUE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS ); si.cb = sizeof(STARTUPINFO); si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; si.wShowWindow = SW_HIDE; // Don't show the console window (DOS box) si.hStdOutput = hDupInputWrite; // Redirect command line to GUI si.hStdError = hDupInputWrite; si.hStdInput = hDupOutputRead; // and send stuff from here to command line hPipeOut = hOutputWrite; // Global so window can access it memset( &osvi, 0, sizeof(OSVERSIONINFO) ); // Initialize version struct osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); // Fill in size GetVersionEx( &osvi ); // Get OS version info // Start a DOS box if( osvi.dwPlatformId == VER_PLATFORM_WIN32_NT ) // If it's WinNT bStatus = CreateProcess( NULL, "cmd /K", NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi ); // Use CMD.EXE else // Use command.com for Win95 bStatus = CreateProcess( NULL, "c:\\command.com", NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi ); CloseHandle( hDupInputWrite ); // Close duplicates so GUI end CloseHandle( hDupOutputRead ); // can use the pipes if( ! bStatus ) { CloseHandle( hInputRead ); CloseHandle( hInputWrite ); CloseHandle( hOutputRead ); CloseHandle( hOutputWrite ); return( FALSE ); } // Loop until either the window closure sets the event or while( WaitForSingleObject( hThreadEvent, 0 ) != WAIT_OBJECT_0 ) { // The dos box is closed if( WaitForSingleObject( pi.hProcess, 0 ) == WAIT_OBJECT_0 ) { pi.hProcess = NULL; break; } // If there's anything to read if( PeekNamedPipe( hInputRead, szInput, 1024, &dwRead, &dwAvail, &dwMsg ) ) { // Read it into a buffer ReadFile( hInputRead, szInput, dwAvail, &dwRead, NULL ); if( dwRead ) // and send to the window control SendDlgItemMessage( hDlg, 100, LB_ADDSTRING, 0, (LPARAM)szInput ); } Sleep( 100 ); // Sit a spell...kick yer shoes off } if( pi.hProcess ) // If the dos box is running, kill it TerminateProcess( pi.hProcess, 0 ); CloseHandle( hInputWrite ); CloseHandle( hOutputRead ); return( TRUE ); // And exit the thread } . .