none
Calling Application.Run twice causing odd behavior

    Question

  • Hi,

    I have an application that transfers data over the web.  Should the transfer error, we want to ask the user if they want to cancel it.  If they select "No" or 5 mins elapses, the transfer should start over.  The application is multithreaded so it can have multiple transfers occurring at once.  Because of this, passing certain information to figure out which transfer errored was necessary so I created my own dialog box with custom properties to handle this.  On the transfer error, I prompt the user with the custom dialog box and call Application.Run(form) because form.Show() wasn't working.  It works great the first time.

    If the transfer errors again, it hits the code, display the custom dialog for less than a second and closes it.  Then, when I go and close the main form, the custom dialog appears, but it non-interactive.  Does anyone have an idea of why this is happening?

    The code that calls the Application.Run is below.

    Thanks,
    Matt

    void Processor_TransferError(object sender, HandleBatchAsyncEventArgs e)
            {
                StringBuilder displayMsg = new StringBuilder();
                displayMsg.Append("The following error occurred while transferring for the ");
                displayMsg.Append(e.Processor.PartnerName);
                displayMsg.Append(" Processor:");
                displayMsg.Append(Environment.NewLine);
                displayMsg.Append(Environment.NewLine);
                displayMsg.Append(e.Exception.Message);
                displayMsg.Append(Environment.NewLine);
                displayMsg.Append(Environment.NewLine);
                displayMsg.Append("Would you like to stop the transfer?");
    
                frmTransferErrorDialog frmDialog = new frmTransferErrorDialog(displayMsg.ToString(), e.Processor);
                frmDialog.CustomFormClosed += new CustomDialogFormClosedEventHandler(frmDialog_CustomFormClosed);
                Application.Run(frmDialog);
            }


    There is indeed a harmony to the universe.
    Tuesday, March 16, 2010 2:04 PM

Answers

  • You should be using Form.Show, not Application.Run.

    Application.Run(form) sets up an entire message pump for Windows Forms.  Form.Show, on the other hand, uses the existing message pump, and just adds the form to it, which is pretty much always what you really want.

    The key to getting Form.Show to work properly, and the reason its failing, is because of your multi-threading.

    In Windows Forms, all UI methods must be called from on the UI thread.  If your TransferError event is happening on a background thread, you must marshal the call to show (and create!) your new form back onto the UI thread.

    If this is happening within an existing form, you can use Control.Invoke with the "this' parameter to handle this cleanly.  Just change your last three lines to:

    this.BeginInvoke( new Action( () =>
        {
             frmTransferErrorDialog frmDialog = new frmTransferErrorDialog(displayMsg.ToString(), e.Processor);
             frmDialog.CustomFormClosed += new CustomDialogFormClosedEventHandler(frmDialog_CustomFormClosed);
             frmDialog.Show();
         } ));
    
    By using Control.BeginInvoke, you marshal the calls back onto the UI thread, which will make it behave properly.


    Reed Copsey, Jr. - http://reedcopsey.com
    Tuesday, March 16, 2010 4:41 PM
    Moderator

All replies

  • You should be using Form.Show, not Application.Run.

    Application.Run(form) sets up an entire message pump for Windows Forms.  Form.Show, on the other hand, uses the existing message pump, and just adds the form to it, which is pretty much always what you really want.

    The key to getting Form.Show to work properly, and the reason its failing, is because of your multi-threading.

    In Windows Forms, all UI methods must be called from on the UI thread.  If your TransferError event is happening on a background thread, you must marshal the call to show (and create!) your new form back onto the UI thread.

    If this is happening within an existing form, you can use Control.Invoke with the "this' parameter to handle this cleanly.  Just change your last three lines to:

    this.BeginInvoke( new Action( () =>
        {
             frmTransferErrorDialog frmDialog = new frmTransferErrorDialog(displayMsg.ToString(), e.Processor);
             frmDialog.CustomFormClosed += new CustomDialogFormClosedEventHandler(frmDialog_CustomFormClosed);
             frmDialog.Show();
         } ));
    
    By using Control.BeginInvoke, you marshal the calls back onto the UI thread, which will make it behave properly.


    Reed Copsey, Jr. - http://reedcopsey.com
    Tuesday, March 16, 2010 4:41 PM
    Moderator
  • Hi Reed,

    Thank you for replying.

    Your code seems to have fixed the immediate problem (thanks so much!), however, another has arisen. In the custom dialog, there is a Yes and No button.  If they click Yes, the transfer stops.  If they click No, the process restarts.  Yes has no issue but when No is clicked, the program locks up on Close() (Close() being in the custom dialog).  Any insight into that?

    Edit:  I stepped through and it doesn't actually lock on the Close().  It fires of the event I have tied to it, relaying the selection back to the main form.  However, the custom dialog never actually closes, it just continues processing code, tying up the entire program.  Is there a way to make sure the custom dialog actually closes before it fires off the event?

    There is indeed a harmony to the universe.
    • Edited by Mulhearn22 Tuesday, March 16, 2010 5:43 PM Update to condition
    Tuesday, March 16, 2010 5:29 PM
  • Call this.Close() before you fire off the event, instead of after.
    Reed Copsey, Jr. - http://reedcopsey.com
    Tuesday, March 16, 2010 6:50 PM
    Moderator
  • I do that.  Here is the code:

    #region btnNo_Click(object, EventArgs)
    private void btnNo_Click(object sender, EventArgs e)
    {
          _result = false;
          Close();
    }
    #endregion
    
    #region Form_Closed()
            protected void Form_Closed(object sender, FormClosedEventArgs e)
            {
                RaiseCustomFormClosed(new CustomDialogFormClosedEventArgs(_result, _processor));
            }
            #endregion
    
    #region OnCustomFormClosed
            public event CustomDialogFormClosedEventHandler CustomFormClosed;
            protected void RaiseCustomFormClosed(CustomDialogFormClosedEventArgs e)
            {
                if (CustomFormClosed != null)
                {
                    CustomFormClosed(this, e);
                }   //if
            }
            #endregion
    
    
    Once the event is fired, it relays back to the main form.

    There is indeed a harmony to the universe.
    Tuesday, March 16, 2010 7:06 PM
  • You mentioned "it just continues processing code, tying up the entire program. "

    What is the form doing that's still processing?  There isn't any code, in what you showed, that would prevent it from "processing" work.  Is there a timer involved on the form, or some other issue, that is keeping it running?

    Reed Copsey, Jr. - http://reedcopsey.com
    Tuesday, March 16, 2010 7:17 PM
    Moderator
  • That was fairly unclear, I'm sorry.

    I figured out that the reason it locked up is because I was calling the processor and telling it to transfer directly, so it was no longer multithreaded.  So I tried creating a new thread, but then it if errored again, it would give me two custom dialogs, then four, and so on because I guess it was running on multiple threads.

    What I have to deal with is a backend that I have interfaced with my own class.  My class is what the thread calls.  So, I thought that calling the instance of the interface class related to the erroring processor might fix it, but that just locked it up as well.  My issues appears to be that I am unable to restart the transfer procedure on its existing thread from the main form.

    I'm not sure how clear this is or how much you can help me withe provided information.  Please let me know any information you need, code or otherwise.  I appreacite you taking the time to help me through this.

    There is indeed a harmony to the universe.
    Tuesday, March 16, 2010 7:45 PM
  • Hi,

     

    You’d better set a breakpoint at showing customDialogs method to check who calls this method. Have you register event multiple times?

     

    By the way, could you please give us the full example that we can reproduce?

     

    Best regards,

    Ling Wang


    Please remember to click “Mark as Answer” on the post that helps you, and to click “Unmark as Answer” if a marked post does not actually answer your question. This can be beneficial to other community members reading the thread.
    Tuesday, March 23, 2010 9:15 AM
    Moderator