views:

108

answers:

1

Currently, I have something like:

public partial class Form1 : Form
{
delegate void StringDelegate(string value);

private FTP m_ftp;

public Form1()
{
InitializeComponent();
}

private void connect_Click(object sender, EventArgs e)
{
OnResponse("Connecting");
m_ftp = new FTP(server.Text);
m_ftp.ResponseReceived += new FTPResponseHandler(m_ftp_ResponseReceived);
m_ftp.Connected += new FTPConnectedHandler(m_ftp_Connected);
m_ftp.BeginConnect(user.Text, password.Text);
}

void m_ftp_Connected(FTP source)
{
// when this happens we're ready to send command
OnResponse("Connected.");
}

void m_ftp_ResponseReceived(FTP source, FTPResponse Response)
{
OnResponse(Response.Text);
}

private void OnResponse(string response)
{
if (this.InvokeRequired)
{
this.Invoke(new StringDelegate(OnResponse), new object[] { response } );
return;
}

}

private void getFileList_Click(object sender, EventArgs e)
{
FTPFiles files = m_ftp.EnumFiles();

fileList.Items.Clear();

foreach (FTPFile file in files)
{
fileList.Items.Add( new ListViewItem( new string[] { file.Name, file.Size.ToString() } ));
}

tabs.SelectedIndex = 1;
}

private void upload_Click(object sender, EventArgs e)
{
FileStream stream = File.OpenRead("\\My Documents\\My Pictures\\Waterfall.jpg");
m_ftp.SendFile(stream, "waterfall.jpg");
stream.Close();
}

Which works fine - this example was taken from the samples. However, after a recent re-visit I have a question. In this particular case since OnResponse() function doesn't update the UI, it seems to serve no purpose here. I removed it (as well as all the calls to it) and it still works like before. Am I missing something?

After reading up more about multi threading with forms, I came to understand that this mechanism (demonstrated in the code above) is there to make sure the UI is responsive.

So in case when we need to say, update a UI element (such as textbox, label etc) we would have OnResponse implemented as follows:

delegate void StringDelegate(string dummy); 
void OnResponse(string dummy)
{
    if(!InvokeRequired)
    {
        button1.Text = dummy; 
     } 
    else 
        Invoke(new StringDelegate(OnResponse),new object[] {enabled}); 
}

If this function is implemented as:

    delegate void StringDelegate(string dummy); 
    void OnResponse(string dummy)
    {
        if(InvokeRequired)
        {            
            Invoke(new StringDelegate(OnResponse),new object[] {dummy}); 
            return;
        }
    }

What's the use to have it at all? Is it absolutely necessary?

And another question: is ftp object running on its own thread here?

A: 

The FTP object is definitely running on its own thread. How do I know? This line:

m_ftp.BeginConnect(user.Text, password.Text);

This is an asynchronous method. Once you call this, the FTP component will use a thread from the .NET threadpool to do all of the work. This dedicated thread is the one that is used to "raise" the events. Ultimately a "raised event" is just one or more method calls to all of the delegates added to the event invocation list; it is this dedicated thread spun up by the Begin method that calls these methods. This thread is not the same thread as the thread that runs the UI, hence the need for the Invoke calls.

If you want the FTP component to use the UI thread, you'd use the Connect method instead of the BeginConnect method. This means your events wont work either, nor will your UI respond to interaction - this is completely expected because a thread can only do one thing at a time: it's either servicing the UI, or executing the FTP code. This is why you need a 2nd thread.

Make sense?

-Oisin

x0n
Thanks! sorta does. The current code can update the UI inside m_ftp_ResponseReceived()...That's why I am confused. My understanding WAS that FTP object WAS running in its OWN THREAD, but then I shouldn't be able to update UI inside m_ftp_ResponseReceived() unless I am using Invokes. What gives? i would like to keep using the BeginConnect () as I want the UI to be responsive.
gnomixa
also please note that OpenNetCF doesn't have Connect method in the FTP class, just BeginConnect()
gnomixa
I just tested it more, and it looks like I AM ABLE TO update the UI from m_ftp_ResponseReceived(). but UNABLE to do it from within m_ftp_Connected(). Quite an interesting observation. m_ftp_Connected must be running on UI thread.
gnomixa
My question actually was more to do with OnResponse() function. It looks completely useless in this context as I am not trying to update UI anywhere.
gnomixa
WinForms doesn't prevent you from _trying_ to update the UI from the wrong thread; this has been fixed in WPF (where it will fail hard). Just because it looks like it works in one handler doesn't mean it's going to continue to work; it may deadlock randomly.
x0n
Put "System.Diagnostics.Debug.WriteLine(Threading.Thread.CurrentThread.ManagedThreadId)" in each handler and in the form .ctor to see what thread is being used - I guarantee it will not be the UI thread.
x0n
thanks for the suggestion. It is interesting - looks like the onResponseReceived alternates between different threads. Up until the ftp is connected it is not running in UI thread, but after it is connected it is running in UI. But anyways, I am using Invokes inside ftp handlers. My initial question came from the fact that OnResponse() function (see original code in the question body) was useless in my case, I was wondering what is its purpose.
gnomixa
if you feel I have answered your question, please mark my answer as "answered." thanks!
x0n