2013年2月24日星期日

Reuse of a Process instance problem



I have to post some code chunks before I ask my question.
Code:
public PuttyShell()
        {
            PLink = new Process();
            PLink.StartInfo.FileName = "plink.exe";
            PLink.StartInfo.CreateNoWindow = true;
            PLink.StartInfo.UseShellExecute = false;
            PLink.StartInfo.RedirectStandardError = true;
            PLink.StartInfo.RedirectStandardInput = true;
            PLink.StartInfo.RedirectStandardOutput = true;

            PLink.OutputDataReceived += new DataReceivedEventHandler( PLink_OutputDataReceived );
            PLink.ErrorDataReceived += new DataReceivedEventHandler( PLink_ErrorDataReceived );

            PLink.EnableRaisingEvents = true;
            PLink.Exited += new EventHandler( PLink_Exited );
        }
Code:
        void PLink_Exited( object sender, EventArgs e )
        {
            ThreadAbort = true;

            if ( MyReadMode == ReadModes.MODE_ASYNC )
            {
                PLink.CancelOutputRead();
                PLink.CancelErrorRead();
            }

            ReadModeLock = false;

            if ( Disconnected != null )
            {
                Disconnected( this, e );
            }
        }
Code:
public bool Connect()
        {
            bool RetVal = false;
            if ( IsConnected == false )
            {
                ReadModeLock = true;
         
                PLink.StartInfo.Arguments = string.Format( "-ssh {0}@{1} -pw {2}", username, hostAddr, password );
                RetVal = PLink.Start();
                if ( RetVal == true )
                {
                    ThreadAbort = false;
                   

                    if ( MyReadMode == ReadModes.MODE_ASYNC )
                    {
                            PLink.BeginOutputReadLine();
                            PLink.BeginErrorReadLine();
                    }
                    else
                    {
                        // add synch thread init here
                       
                        SynchStdOutThread = new Thread( ThreadStdOut );
                        SynchStdOutThread.Name = hostAddr + "StdOut";
                        SynchStdOutThread.IsBackground = true;


                        SynchStdErrThread = new Thread( ThreadStdErr );
                        SynchStdErrThread.Name = hostAddr + "StdErr";
                        SynchStdErrThread.IsBackground = true;

                        SynchStdOutThread.Start();
                        SynchStdErrThread.Start();
                     
                    }
                }
            }
            return RetVal;
        }
OK, this is a wrapper to use a command line too, PLink.exe. Now the first time through on an instance, when run in Asynch or Synch modes it works just fine.

If the process PLink.exe exists and you are in Synch mode, you can call connect and disconnect (or logout) as many times as you want and it works perfectly.

The problem is in Asynch mode. The events:
PLink.OutputDataReceived
PLink.ErrorDataReceived

Never get called. Remember, PLink is a Process type. In the debugger the streams say they are in Asynch mode, the event handlers are still there (I even tried re-adding them). I look in the process table and PLink.exe is running, I am just not getting these events to fire after the first PLink.Start(); command.

Does anyone have any idea what is going on? If you wish to run in ASynch mode, your only choice if you wish to reconnect to something is to destroy the instance of my class and make a new one. I do not get why Synch mode works, but ASynch fails after the first run.
Reply With Quote
August 21st, 2008 12:58 PM #2 Arjay
Moderator / MS MVP
Power Poster
         
Join Date
Aug 2004
Posts
10,170
Re: Reuse of a Process instance problem
A couple of comments:

1) You are better off signalling the thread to exit rather than using thread abort.
2) When you stop the thread, unwire the event handlers before breaking everything down.
Code:
PLink.OutputDataReceived -= new DataReceivedEventHandler( PLink_OutputDataReceived );
PLink.ErrorDataReceived -= new DataReceivedEventHandler( PLink_ErrorDataReceived );
PLink.Exited -= new EventHandler( PLink_Exited );
3) Mmm Putty and PLink - gotta love it.
My Code Guru Articles
Reply With Quote
August 21st, 2008 02:38 PM #3 DeepT
Senior Member
 
Join Date
Sep 2004
Posts
1,361
Re: Reuse of a Process instance problem
Actually I do signal the the threads. The "ThreadAbort" is the signal because each thread is like:
while( ThreadAbort == false )
{
...
}

So when it is set to true, they fall out of their loops naturally.

You suggest unwiring the event handlers and then wiring them again? Ill try it, but adding them again (which should of caused a double call) didn't even cause a the async events to be called. All my other events work, even the exit event, its just the:
Code:
PLink.OutputDataReceived += new DataReceivedEventHandler( PLink_OutputDataReceived );
            PLink.ErrorDataReceived += new DataReceivedEventHandler( PLink_ErrorDataReceived );
Events no longer fire. Ill try what you suggested, but perhaps the answer is that the constructor doesn't do anything (beyond setting some vars) and in the connect call I have an Init() which creates a new instance of the Process object each time. It would still bug me why I would need to do that.


Edit: I tried that, removing the handlers like you suggested and then adding them again before I attempt to connect again in ASynch mode. No difference, it still didn't work. Synch still works, ASynch fails after the first start.
Last edited by DeepT; August 21st, 2008 at 02:52 PM.
Reply With Quote
August 21st, 2008 03:41 PM #4 Arjay
Moderator / MS MVP
Power Poster
         
Join Date
Aug 2004
Posts
10,170
Re: Reuse of a Process instance problem
Given that you've stated that the async works the first time, but not subsequent times, it means that resources aren't getting released.

The Process class implements IDisposable so this means the class is holding on to some resources that need to be released before starting a new process.

I see that you are instantiating the Process class in the PuttyShell constructor.

I would recommend moving this code into the Connect method and then call Dispose in the Disconnect method. I would do the same for the threads as well.

P.S. I didn't intially notice that you were creating the Process class in the PuttyShell constructor otherwise I would recommended moving the initialization to the Connect method earlier.
Last edited by Arjay; August 21st, 2008 at 03:51 PM.

没有评论:

发表评论