Author Topic: BackgroundWorker & ShowModalDialog  (Read 17381 times)

0 Members and 1 Guest are viewing this topic.

Bobby C. Jones

  • Swamp Rat
  • Posts: 516
  • Cry havoc and let loose the dogs of war.
Re: BackgroundWorker & ShowModalDialog
« Reply #15 on: March 04, 2009, 10:05:26 AM »
This is a VS2008 project stripped down to bare bones.  When you load it the palette will display.  Press the open drawing button a few times and open several drawings.  All appears to be well.  Now press the open modal dialog button and close the dialog that shows.

The next time you press the open drawing button you'll get the crash.

I've commented out a couple of methods and a delegate that invoke the Open() call on the UI thread and this works, but because of my lack of understanding of what's really going on, I get a feeling that I'm just covering up a deeper issue that's going to come back and bite me in the a$$.  I'm putting this on hold until I gain a better understanding.

...  In the example that I put together for this post...


Can you post your sample solution so others might play? 
Bobby C. Jones

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8690
  • AKA Daniel
Re: BackgroundWorker & ShowModalDialog
« Reply #16 on: March 04, 2009, 11:57:31 PM »
interesting, have a look at this...

Quote
OnOpenDwgBtnClickThread 3
workerThread_DoWork 1
workerThread_RunWorkerCompleted 3
OnOpenDwgBtnClickThread 3
workerThread_DoWork 1
workerThread_RunWorkerCompleted 3
OnShowDialogBtnClick 3
OnShowDialogBtnClick 3
OnShowDialogBtnClick 3
OnOpenDwgBtnClickThread 3
workerThread_DoWork 1
workerThread_RunWorkerCompleted 1
OnOpenDwgBtnClickThread 3
workerThread_DoWork 1
workerThread_RunWorkerCompleted 1
OnOpenDwgBtnClickThread 3
workerThread_DoWork 1
workerThread_RunWorkerCompleted 1



Code: [Select]
using System;
using System.Threading;
using System.ComponentModel;
using System.Windows.Forms;
using System.IO;

using acadApp = Autodesk.AutoCAD.ApplicationServices.Application;
using acadFileDlg = Autodesk.AutoCAD.Windows.OpenFileDialog;


namespace ThreadingExample
{
  public partial class UserControl1 : UserControl
  {
    TextWriter writer;
    public UserControl1()
    {
      InitializeComponent();
      writer = StreamWriter.Synchronized(new StreamWriter(@"C:\Thread.txt", true));
    }

    ~UserControl1()
    {
      writer.Flush();
      writer.Close();
    }

    private string FileName { get; set; }

    private void OnOpenDwgBtnClick(object sender, EventArgs e)
    {
      writer.WriteLine(String.Format("OnOpenDwgBtnClickThread {0}",
        Thread.CurrentThread.ManagedThreadId));

      //Select a file to open
      acadFileDlg fileDlg = new acadFileDlg
        ("Theading Example", "", "DWG", "dialog name", acadFileDlg.OpenFileDialogFlags.ForceDefaultFolder);

      DialogResult result = fileDlg.ShowDialog();

      if (result != DialogResult.OK)
      {
        return;
      }

      //Store the file name
      this.FileName = fileDlg.Filename;

      //Launch a worker thread
      BackgroundWorker workerThread = new BackgroundWorker();
      workerThread.DoWork += new DoWorkEventHandler(workerThread_DoWork);
      workerThread.RunWorkerCompleted +=
        new RunWorkerCompletedEventHandler(workerThread_RunWorkerCompleted);
      workerThread.RunWorkerAsync();
    }

    private void workerThread_DoWork(object sender, DoWorkEventArgs e)
    {
      writer.WriteLine(String.Format
        ("workerThread_DoWork {0}", Thread.CurrentThread.ManagedThreadId));
      //Define background process here.  For this example I do no work on the background thread.
    }


    private void workerThread_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
      writer.WriteLine(String.Format
        ("workerThread_RunWorkerCompleted {0}", Thread.CurrentThread.ManagedThreadId));
      //The worker thread is complete and this call should be on the main thread, right?
      //if(e.Error == null)
      //acadApp.DocumentManager.Open(this.FileName, false);
    }


    #region Invoke acadApp.DocumentManager.Open()
    //private void workerThread_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    //{
    //  if (this.InvokeRequired)
    //  {
    //    this.Invoke(new OpenDrawingHandler(OpenDrawing), this.FileName, false);
    //  }
    //  else
    //  {
    //    acadApp.DocumentManager.Open(this.FileName, false);
    //  }
    //}

    //private delegate void OpenDrawingHandler(string fileName, bool openReadOnly);

    //private void OpenDrawing(string fileName, bool openReadOnly)
    //{
    //  acadApp.DocumentManager.Open(fileName, openReadOnly);
    //}
    #endregion


    private void OnShowDialogBtnClick(object sender, EventArgs e)
    {
      writer.WriteLine(String.Format
        ("OnShowDialogBtnClick {0}", Thread.CurrentThread.ManagedThreadId));
      Form1 modalForm = new Form1();
      acadApp.ShowModalDialog(modalForm);
    }
  }
}


It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8690
  • AKA Daniel
Re: BackgroundWorker & ShowModalDialog
« Reply #17 on: March 05, 2009, 12:08:39 AM »
The threads seem to change up after the modal form is shown, Iím not sure how to fix this though.

Quote
OnOpenDwgBtnClickThread 3
workerThread_DoWork 1
workerThread_RunWorkerCompleted 3
OnOpenDwgBtnClickThread 3
workerThread_DoWork 1
workerThread_RunWorkerCompleted 3
OnOpenDwgBtnClickThread 3
workerThread_DoWork 1
workerThread_RunWorkerCompleted 3
OnOpenDwgBtnClickThread 3
workerThread_DoWork 1
workerThread_RunWorkerCompleted 3
OnOpenDwgBtnClickThread 3
workerThread_DoWork 1
workerThread_RunWorkerCompleted 3
OnOpenDwgBtnClickThread 3
workerThread_DoWork 1
workerThread_RunWorkerCompleted 3
OnShowDialogBtnClick 3;------------------------------
OnOpenDwgBtnClickThread 3
workerThread_DoWork 1
workerThread_RunWorkerCompleted 1
OnOpenDwgBtnClickThread 3
workerThread_DoWork 1
workerThread_RunWorkerCompleted 1
OnOpenDwgBtnClickThread 3
workerThread_DoWork 1
workerThread_RunWorkerCompleted 1
OnOpenDwgBtnClickThread 3
workerThread_DoWork 1
workerThread_RunWorkerCompleted 1
OnOpenDwgBtnClickThread 3
workerThread_DoWork 1
workerThread_RunWorkerCompleted 1

Bobby C. Jones

  • Swamp Rat
  • Posts: 516
  • Cry havoc and let loose the dogs of war.
Re: BackgroundWorker & ShowModalDialog
« Reply #18 on: March 05, 2009, 10:19:01 AM »
The threads seem to change up after the modal form is shown, Iím not sure how to fix this though.

Well there it is in a visual format.  Thanks a lot for taking a look at this Daniel.  The call can be fixed by invoking it on the UI thread, but this leaves too many unknowns for me to be comfortable.
Bobby C. Jones

MikeTuersley

  • Guest
Re: BackgroundWorker & ShowModalDialog
« Reply #19 on: March 07, 2009, 04:49:38 PM »
A simple fix is to use Windows Show methods, not the AutoCAD.Application Show methods. Threading then is not an issue.

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8690
  • AKA Daniel
Re: BackgroundWorker & ShowModalDialog
« Reply #20 on: March 07, 2009, 07:51:30 PM »
A simple fix is to use Windows Show methods, not the AutoCAD.Application Show methods. Threading then is not an issue.

I thought about this too as AutoCAD.Application Show methods have a little background mojo going on, but it didnít make any difference

ps: Welcome to theSwamp!!

Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11654
  • class keyThumper<T>:ILazy<T>
Re: BackgroundWorker & ShowModalDialog
« Reply #21 on: March 07, 2009, 08:21:48 PM »
A simple fix is ......

Do you have a solution to demonstrate this Mike ?

Welcome to TheSwamp.
kdub, kdub_nz in other timelines.
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

TonyT

  • Guest
Re: BackgroundWorker & ShowModalDialog
« Reply #22 on: March 07, 2009, 11:10:13 PM »
A simple fix is to use Windows Show methods, not the AutoCAD.Application Show methods. Threading then is not an issue.

Mike - Why is threading not an issue with the Form's Show()/ShowDialog() methods?

MikeTuersley

  • Guest
Re: BackgroundWorker & ShowModalDialog
« Reply #23 on: March 08, 2009, 10:28:43 AM »
Kerry - just substitute modalForm.Show(); for acadApp.ShowModalDialog(modalForm); in Bobby's example.

Tony - I've used threading for years without any problems. Granted, though, I have not used the BackGroundWorker object - I've always implemented standard threading. I tried Bobby's sample with the Windows' Show and ShowDialog and his problem disappears. It is possible that, at some low level, there could still be an issue but I've not run into it. It's possible that the problem is AutoCAD interacting with the BackGroundWorker - I didn't have time to swap in my standard thread implementation.

My suggestion of using Windows' functionality does have drawbacks per Kean's blog http://tinyurl.com/bakxjm :

Quote
There are a number of reasons to use Application.Show[Modal/Modeless]Dialog() rather than Form.Show[Dialog]:

1. Dialogs will automatically pick up the icon of the host product
2. Dialog size and position will be persisted automatically
3. Other floating AutoCAD windows (e.g. the Properties palette) will be disabled, as needed
4. The DIASTAT system variable will be set properly with the exit status of the dialog

Assuming you can live with the 4 items listed above, I don't see a reason not to use Windows' functionality.

TonyT

  • Guest
Re: BackgroundWorker & ShowModalDialog
« Reply #24 on: March 08, 2009, 06:05:11 PM »


Kerry - just substitute modalForm.Show(); for acadApp.ShowModalDialog(modalForm); in Bobby's example.


Mike - Sorry, but I think you seem to have
gotten modal and modeless confused.

Show() displays a form modelessly, not modally as
Application.ShowModalDialog() and Form.ShowDialog() does.

If what you are suggesting is that the way to solve the
problem is to use a modeless dialog rather than a modal
dialog, well, that's not really a solution at all.

Quote
Tony - I've used threading for years without any problems. Granted, though, I have not used the BackGroundWorker object - I've always implemented standard threading.

Mike - feel free to expand on how you've used threading for years without any problems.


MikeTuersley

  • Guest
Re: BackgroundWorker & ShowModalDialog
« Reply #25 on: March 09, 2009, 01:17:38 AM »
Glad to see some people never change, Tony!

Thank you, yes, I did make a mistake in replying too fast to Kerry. After testing out my recommendation, showing the form modally via the Application or Windows, still throws the error. To get around it, you could go really hokie:

- Take a screen cap of the AutoCAD window [see below]
- Size the dialog to the AutoCAD Window and position it over it
- Add the screen cap as the form's background image
- Use Show() instead of ShowModalDialog()
- Do the processing and close the form

Not pretty, but it works.

Code: [Select]
public static Image CaptureWindow(IntPtr handle)
{
    // get te hDC of the target window
    IntPtr hdcSrc = User32.GetWindowDC(handle);
    // get the size
    Win32.RECT windowRect = new Win32.RECT();
    User32.GetWindowRect(handle, windowRect);
    int width = windowRect.right - windowRect.left;
    int height = windowRect.bottom - windowRect.top;
    // create a device context to copy to
    IntPtr hdcDest = GDI32.CreateCompatibleDC(hdcSrc);
    // create a bitmap to copy it to,
    // using GetDeviceCaps to get the width/height
    IntPtr hBitmap = GDI32.CreateCompatibleBitmap(hdcSrc, width, height);
    // select the bitmap object
    IntPtr hOld = GDI32.SelectObject(hdcDest, hBitmap);
    // bitblt over
    GDI32.BitBlt(hdcDest, 0, 0, width, height, hdcSrc, 0, 0, GDI32.SRCCOPY);
    // restore selection
    GDI32.SelectObject(hdcDest, hOld);
    // clean up
    GDI32.DeleteDC(hdcDest);
    User32.ReleaseDC(handle, hdcSrc);
     // get a .NET image object for it
    Image img = Image.FromHbitmap(hBitmap);
    // free up the Bitmap object
    GDI32.DeleteObject(hBitmap);
    return img;
}

MikeTuersley

  • Guest
Re: BackgroundWorker & ShowModalDialog
« Reply #26 on: March 09, 2009, 01:22:11 AM »
Oh! And threading? I have always used it with accont and then the toolpalette control. The threading never directly interacts with AutoCAD; it's always pulling in data from sql or an xml file to populate the controls on the dockable container.

Chuck Gabriel

  • Guest
Re: BackgroundWorker & ShowModalDialog
« Reply #27 on: March 09, 2009, 07:22:30 AM »
Not trying to change the subject or anything, but welcome to the swamp Mike.

MikeTuersley

  • Guest
Re: BackgroundWorker & ShowModalDialog
« Reply #28 on: March 09, 2009, 08:00:17 AM »
Hi Chuck! Thanks!

TonyT

  • Guest
Re: BackgroundWorker & ShowModalDialog
« Reply #29 on: March 09, 2009, 12:22:36 PM »
Glad to see some people never change, Tony!

Ditto.

Quote

Thank you, yes, I did make a mistake in replying too fast to Kerry. After testing out my recommendation, showing the form modally via the Application or Windows, still throws the error. To get around it, you could go really hokie:

- Take a screen cap of the AutoCAD window [see below]
- Size the dialog to the AutoCAD Window and position it over it
- Add the screen cap as the form's background image
- Use Show() instead of ShowModalDialog()
- Do the processing and close the form

Not pretty, but it works.


:lmao: 

Mike - No, I'm afraid that doesn't work, but thanks
for a good laugh.