Best way to cancel a unix call (using SSH library) via the UI in a synchronous application in C#?
My program (.NET 4.0) uses a 3rd party SSH library (SSH.NET, works great so far) to send a unix comman开发者_如何学Cd that is constructed based on the user's choices in the UI, waits for the respose and outputs it. My problem arises when sometimes the unix response just "hangs" for lack of a better term, in which cases my program is stuck waiting. In unix you can just escape that command (for me CTRL-C) and try again, but when using a library, I'm not sure how to provide the equivelent.
Another problem is that the UI thread is locked when this is happening, so even if there was some sort of "Cancel" button the user wouldn't be able to click on it. I looked into BackgroundWorker and Threading, but that doesn't really make sense because my operation is synchronous - the program has to wait for the response so it can parse it (format, color, ect).
Any thoughts on this would much appriciated, thanks all!
I can't speak specifically to the SSH.NET and how you can cancel a command via that library, but here's how I would approach it.
I would launch the "command" on a Task, which is essentially a separate thread. Here's my sample code:
private Task _commandTask;
private CancellationTokenSource _cancellation;
protected void SendCommand(object sender, EventArgs e)
{
if(_commandTask != null)
return; // Can't run the task twice
_cancellation = new CancellationTokenSource();
_commandTask = new Task(SendSshCommand, _cancellation.Token);
_commandTask.ContinueWith(FinishCommand, TaskScheduler.FromCurrentSynchronizationContext());
_commandTask.Start();
// returning control to the UI thread now
}
protected void CancelCommand(object sender, EventArgs e)
{
if(_commandTask == null)
return;
_cancellation.Cancel();
}
private static void SendSshCommand()
{
// Send your SSH Commands here
}
private void FinishCommand(Task task)
{
// Update your UI here
// remove this task
_commandTask = null;
_cancellation = null;
}
So the SendCommand
method would be invoked from some event on your UI (button click I assume). It creates a CancellationTokenSource
and a new Task. This task calls the SendSshCommand
method on a separate thread. We then create a task continuation with the ContinueWith
method. Passing in the TaskScheduler.FromCurrentSyncrhonizationContext()
tells the continuation task that we want to execute on the UI's thread instead of a separate thread. This is important because if we're not on the UI thread we cannot access any of the UI controls.
The CancelCommand
would be called from your cancel button, and it simply tells the _commandTask
to stop executing.
Now this isn't 100%, I'm sure you're going to have state that you need to pass around to the tasks, and there are Generic Task structures to help you with that. Also, in your FinishCommand
, you're going to want to check to make sure the task was completed, not cancelled, and didn't throw an exception. Read up on Tasks and the TPL in .Net 4 and you'll see how to do that.
It's also worth noting, that I'm not sure what will happen with your SSH.NET library after the cancellation. I would assume it would just disconnect the SSH session, which in turn would end the process on the Unix machine. But again, I'm not familiar with that particular library so you'll have to do some testing.
Hope that helps!
I'm not familiar with the library but if you have one SSH session, that is, the same shell then launch the command as a background task and cancel it with a kill %1
Launch:-
command &
Cancel:-
kill %1
This does require it to be in the same shell of course.
CTRL-C is ASCII 3 if you've got a stream type interface to the shell sending that might work.
精彩评论