Thread: Encapsulating cmd.exe

  1. #1
    mustang benny bennyandthejets's Avatar
    Join Date
    Jul 2002
    Posts
    1,401

    Encapsulating cmd.exe

    I'm trying to encapsulate cmd.exe in a C# program by spawning the process and redirecting StdIn and StdOut to different controls.

    Basically, I have a text box for output and text box for input. When the program starts, I spawn the process, and then start two threads, for StdOut and StdError.

    The StdOut thread loops indefinitely, appending line at a time to the output text control. If the stream ends, the thread exits. The StdError thread just reads in anything in the stream, because I heard that the process stalls if something is sitting in StdError.

    When some text is entered into the input text control, I send that input to the StdIn stream.

    The results are discouraging. When the process is spawned, nothing is written to the text control. Nothing happens when I write to the input control. However, task manager shows that cmd.exe has been spawned.

    I managed to get partial results using a couple non-thread methods previously. One involved using a console project, so I could just have a loop running indefinitely on the main thread for the StdOut stream, and the console provided the StdIn from the keyboard, using the cmd.exe process. This worked ok, but that was without redirecting the StdIn. I didn't want the console hanging around, so I moved to just windows forms.

    I tried a similar method with the forms, redirecting StdOut by running a loop on the main thread, to write each line to a text control. I didn't redirect StdIn. Unfortunately, the program only wrote the output of cmd.exe up until input would have been required, then cmd.exe was terminated. I believe this was because there was no console around to provide StdIn, so the spawned process had no choice but to terminate.

    Now with the threads, nothing happens. Any advice on how I could resolve this, or go about it better?

    Code:
    public void Spawn()
    {
    	//Start the process
    	m_proc = new Process();
    	m_proc.StartInfo.FileName = "cmd.exe";
    	m_proc.StartInfo.UseShellExecute = false;
    	m_proc.StartInfo.RedirectStandardOutput = true;
    	m_proc.StartInfo.RedirectStandardError=true;
    	m_proc.StartInfo.RedirectStandardInput=true;
    	m_proc.StartInfo.CreateNoWindow=true;
    	m_proc.Start();
    
    	
    	//Threading
    	StdOutUseThreads();
    	StdErrUseThreads();
    }
    
    //Threading for stdout, a similar thread is created for stderr
    public void StdOutUseThreads()
    {
    	Thread ReaderThread=new Thread( new ThreadStart (StdOutReader) );
    	ReaderThread.Start();
    }
    
    public void StdOutReader()
    {
    	for (;;)
    	{
    		string output=m_proc.StandardOutput.ReadLine();
    		if (null==output)
    			break;
    		m_text.AppendText(output+"\r\n");
    	}
    }
    
    //Runs on main thread, writes a line to StdIn
    private void m_input_Leave(object sender, EventArgs e)
    {
    	if (m_input.Text.Length>0)
    	{
    		m_proc.StandardInput.WriteLine(m_input.Text);
    	}
    }
    [email protected]
    Microsoft Visual Studio .NET 2003 Enterprise Architect
    Windows XP Pro

    Code Tags
    Programming FAQ
    Tutorials

  2. #2
    mustang benny bennyandthejets's Avatar
    Join Date
    Jul 2002
    Posts
    1,401
    Does anyone have any advice?
    [email protected]
    Microsoft Visual Studio .NET 2003 Enterprise Architect
    Windows XP Pro

    Code Tags
    Programming FAQ
    Tutorials

  3. #3
    -AppearingOnThis..........
    Join Date
    May 2005
    Location
    Netherlands
    Posts
    44
    I'm afraid I'm too good at C++, but here's how I did it in C in a text-based environment, if that helps at all.
    Code:
    #include <windows.h>
    #include <stdio.h>
    
    #define MAX_BUFFER_SIZE 512
    
    int EmulateCommandPrompt()
    {
    	STARTUPINFO sti = { 0 };
    	SECURITY_ATTRIBUTES sats = { 0 };
    	PROCESS_INFORMATION pi = { 0 };
    	HANDLE pipin_w, pipin_r, pipout_w, pipout_r;
    	BYTE buffer[MAX_BUFFER_SIZE];
    	DWORD writ, excode, read, available;
    	int ret = 0;
    	
    	pipin_w = pipin_r = pipout_w = pipout_r = NULL;
    	
    	for(;;)
    	{
    		//set SECURITY_ATTRIBUTES struct fields
    		sats.nLength = sizeof(sats);
    		sats.bInheritHandle = TRUE;
    		sats.lpSecurityDescriptor = NULL;
    		
    		//create child's stdout pipes
    		if(!CreatePipe(&pipout_r, &pipout_w, &sats, 0)) break;
    		//and its stdin pipes
    		if(!CreatePipe(&pipin_r, &pipin_w, &sats, 0)) break;
    		//printf("Created pipes\n");
    		
    		//now set STARTUPINFO struct fields (from the child's point of view)
    		sti.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
    		sti.wShowWindow = SW_HIDE;
    		sti.hStdInput = pipin_r;
    		sti.hStdOutput = pipout_w;
    		sti.hStdError = pipout_w;
    		
    		//create the process...
    		if(!CreateProcess(NULL, "cmd.exe", NULL, NULL, TRUE, 
    			0, NULL, NULL, &sti, &pi)) break;
    		//printf("Created process (%s)\n", cmdline);
    		
    		//now have a continuous loop to get and recieve info
    		for(;;)
    		{
    			//make sure process is still running
    			GetExitCodeProcess(pi.hProcess, &excode);
    			if(excode != STILL_ACTIVE) break;
    			//printf("Process still running\n");
    			
    			//give it time to set up/react
    			Sleep(500);
    			
    			//now check to see if process has anything to say
    			if(!PeekNamedPipe(pipout_r, buffer, 
    				sizeof(buffer), &read, &available, NULL)) ret = 10;
    			//printf("Peeked\n");
    			
    			//is there anything to be read in the pipe?
    			if(read)
    			{
    				do
    				{
    					ZeroMemory(buffer, sizeof(buffer));
    					//read it and print to stdout
    					if(!ReadFile(pipout_r, buffer, sizeof
    
    (buffer), &read, NULL) || !read) ret = 7;
    					buffer[read] = 0;
    					fprintf(stdout, "%s", buffer);
    					if(ret) break;
    				}
    				while(read >= sizeof(buffer));
    			}
    			
    			//make sure we didn't run into any errors
    			if(!ret)
    			{
    				//get info and write it to pipe
    				ZeroMemory(buffer, sizeof(buffer));
    				fgets(buffer, sizeof(buffer), stdin);
    				if(!strnicmp(buffer, "exit", 4)) ret = 12;
    				if(!WriteFile(pipin_w, buffer, strlen(buffer), 
    
    &writ, NULL)) ret = 8;
    			}
    			if(ret) break;
    		}
    		
    		break;
    	}
    	
    	//clean up any unfinished business
    	if(pipin_w != NULL) CloseHandle(pipin_w);
    	if(pipin_r != NULL) CloseHandle(pipin_r);
    	if(pipout_w != NULL) CloseHandle(pipout_w);
    	if(pipout_r != NULL) CloseHandle(pipout_r);
    	if(pi.hProcess != NULL) CloseHandle(pi.hProcess);
    	if(pi.hThread != NULL) CloseHandle(pi.hThread);
    	
    	return ret;
    }
    
    int main(int argc, char *argv[])
    {
    	EmulateCommandPrompt();
    	
    	return 0;
    }

  4. #4
    mustang benny bennyandthejets's Avatar
    Join Date
    Jul 2002
    Posts
    1,401
    Thanks for the code but I think my primary problem is that I am trying to encapsulate a console based process without a console running in my process. This causes issues which I don't exactly understand. I'll try to incorporate your code though.

    If anyone has any ideas please let me know.
    [email protected]
    Microsoft Visual Studio .NET 2003 Enterprise Architect
    Windows XP Pro

    Code Tags
    Programming FAQ
    Tutorials

  5. #5
    mustang benny bennyandthejets's Avatar
    Join Date
    Jul 2002
    Posts
    1,401
    Problem solved. Instead of reading a line at a time from Standard Output, read a character at a time.
    [email protected]
    Microsoft Visual Studio .NET 2003 Enterprise Architect
    Windows XP Pro

    Code Tags
    Programming FAQ
    Tutorials

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. error PRJ0003 : Error spawning 'cmd.exe'.
    By hung555 in forum C++ Programming
    Replies: 17
    Last Post: 01-17-2009, 03:43 AM
  2. Communication with cmd.exe
    By Livijn in forum C++ Programming
    Replies: 16
    Last Post: 03-31-2007, 09:14 AM
  3. Unrestrict cmd.exe for normal users in XP
    By civix in forum Tech Board
    Replies: 4
    Last Post: 12-04-2006, 05:45 PM
  4. executing cmd.exe commands?
    By Dragoon_42 in forum C++ Programming
    Replies: 3
    Last Post: 11-03-2005, 12:22 PM
  5. cmd.exe
    By cnoob in forum C++ Programming
    Replies: 1
    Last Post: 06-08-2005, 12:23 PM