Avoid calling Invoke when the control is disposed

后端 未结 14 1342
花落未央
花落未央 2020-12-01 16:09

I have the following code in my worker thread (ImageListView below is derived from Control):

if (mImageListView != null &&          


        
相关标签:
14条回答
  • 2020-12-01 16:24

    One way might be to call the method itself ones more instead of invoking the ImageListView-Method:

    if (mImageListView != null && 
        mImageListView.IsHandleCreated &&
        !mImageListView.IsDisposed)
    {
        if (mImageListView.InvokeRequired)
            mImageListView.Invoke(new YourDelegate(thisMethod));
        else
            mImageListView.RefreshInternal();
    }
    

    That way it would check one more time before finally calling RefreshInternal().

    0 讨论(0)
  • 2020-12-01 16:27

    may be lock(mImageListView){...} ?

    0 讨论(0)
  • 2020-12-01 16:28

    There are implicit race conditions in your code. The control can be disposed between your IsDisposed test and the InvokeRequired test. There's another one between InvokeRequired and Invoke(). You can't fix this without ensuring the control outlives the life of the thread. Given that your thread is generating data for a list view, it ought to stop running before the list view disappears.

    Do so by setting e.Cancel in the FormClosing event and signaling the thread to stop with a ManualResetEvent. When the thread completes, call Form.Close() again. Using BackgroundWorker makes it easy to implement the thread completion logic, find sample code in this post.

    0 讨论(0)
  • 2020-12-01 16:29

    If a BackGroundWorker is a possibility, there's a very simple way to circumvent this:

    public partial class MyForm : Form
    {
        private void InvokeViaBgw(Action action)
        {
            BGW.ReportProgress(0, action);
        }
    
        private void BGW_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            if (this.IsDisposed) return; //You are on the UI thread now, so no race condition
    
            var action = (Action)e.UserState;
            action();
        }
    
        private private void BGW_DoWork(object sender, DoWorkEventArgs e)
        {
           //Sample usage:
           this.InvokeViaBgw(() => MyTextBox.Text = "Foo");
        }
    }
    
    0 讨论(0)
  • 2020-12-01 16:29

    The solution proposed by Isak Savo

    try
      {
      myForm.Invoke(myForm.myDelegate, new Object[] { message });
      }
    catch (ObjectDisposedException)
      { //catch exception if the owner window is already closed
      }
    

    works in C# 4.0 but for some reasons it fails in C#3.0 (the exception is raised anyway)

    So I used another solution based on a flag indicating if the form is closing and consequently preventing the use of invoke if the flag is set

       public partial class Form1 : Form
       {
        bool _closing;
        public bool closing { get { return _closing; } }
    
        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            _closing = true;
        }
    
     ...
    
     // part executing in another thread: 
    
     if (_owner.closing == false)
      { // the invoke is skipped if the form is closing
      myForm.Invoke(myForm.myDelegate, new Object[] { message });
      }
    

    This has the advantage of completely avoiding the use of try/catch.

    0 讨论(0)
  • 2020-12-01 16:37

    You could use mutexes.

    Somewhere at the start of the thread :

     Mutex m=new Mutex();
    

    Then :

    if (mImageListView != null && 
        mImageListView.IsHandleCreated &&
        !mImageListView.IsDisposed)
    {
        m.WaitOne(); 
    
        if (mImageListView.InvokeRequired)
            mImageListView.Invoke(
                new RefreshDelegateInternal(mImageListView.RefreshInternal));
        else
            mImageListView.RefreshInternal();
    
        m.ReleaseMutex();
    }
    

    And whereever it is you are disposing of mImageListView :

     m.WaitOne(); 
     mImageListView.Dispose();
     m.ReleaseMutex();
    

    This should ensure you cant dispose and invoke at the same time.

    0 讨论(0)
提交回复
热议问题