MTA Thread VSTA V2.0

Latest post 06-26-2008 2:53 PM by Gary. 10 replies.
  • 05-06-2008 7:48 AM

    MTA Thread VSTA V2.0

    Hi,

    I'm writing a WPF application that calls code in an AddIn which is effectively

        public Splash GetAboutDialog(){
          return new Splash();
        }

      class Splash: Window
      { .... }

    I use MethodInvoke to call via RunCommand( "GetAboutDialog" )

        internal bool RunCommand(string macroName)
        {
          IExtendedEntryPoint entryPoint = this.macroAddIn as IExtendedEntryPoint;
          if (entryPoint == null)
            return false;

          object obj = entryPoint.GetEntryPointObject();
          Type t = obj.GetType();
          try
          {
            t.InvokeMember(macroName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod, null, obj, null);
            return true;
          }
          ....
        }

    Now this code works perfectly when not in Debug mode, but once in the debugger, it will fail with the following:

    The calling thread must be STA, because many UI components require this.

    Delving further, the thread calling GetAboutDialog is indeed MTA when in the debugger, but presumably not when the code is called normally. I presume that AddInProcess that is hosting the addin in the debugger is starting the thread the MTA apartment model. What can I do to resolve this?

    Many thanks,

    Alec

  • 05-06-2008 3:16 PM In reply to

    Re: MTA Thread VSTA V2.0

    Hi Alec,

    Let me do some testing and get some more information from Microsoft on this problem.

    Best regards,
    Rebecca

     

  • 05-07-2008 1:23 AM In reply to

    Re: MTA Thread VSTA V2.0

    This little macro demonstrates exactly what you suspect...

    public void Macro3()
    {
    MessageBox.Show("Threading State: " + System.Threading.Thread
    .CurrentThread.GetApartmentState().ToString());
    }

    I'm still digging for answers on it.

  • 05-07-2008 2:18 PM In reply to

    Re: MTA Thread VSTA V2.0

    FYI - we're working with Microsoft on this. I'll update this thread as soon as I get more information.
  • 05-08-2008 12:43 AM In reply to

    Re: MTA Thread VSTA V2.0

    Thanks for keeping me informed Rebecca.

    Ideally, I suppose that the thread model should be configurable in some way.

    Regards,

    Alec

  • 05-28-2008 8:50 AM In reply to

    Re: MTA Thread VSTA V2.0

    Any news on this because if I can't use the debugger, there's very little advantage in using VSTA.
  • 05-28-2008 2:33 PM In reply to

    Re: MTA Thread VSTA V2.0

    We're still working with Microsoft on this issue and they are aware of its impact. (others are encountering it now, too). As soon as we have any information, we'll post it.

    The VS-style debugger runs in an STA. Is there any way that could work with your application?

    I'm sorry it is taking so long for an answer.

  • 06-25-2008 11:32 AM In reply to

    Re: MTA Thread VSTA V2.0

    Here is some information from Microsoft on how to support UI in the seamless debugger.  I hope this is helpful.

    It is actually possible for add-in to use WinForm or WPF. For WinForm, you cannot just call form1.Show(), you need to call either ShowDialog or Application.Run to set up the message pump. For WPF, you have to spawn a STA thread to do it.

    For example:

             public void MacroWinForm()

             {

                 bool useNewThread = false;

                 if (useNewThread)

                 {

                     Thread t = new Thread(() =>

                     {

                         Form1 myWinForm = new Form1();

                         System.Windows.Forms.Application.Run(myWinForm);

                     });

                     t.SetApartmentState(ApartmentState.STA);

                     t.Start();

                     t.Join();

                     MessageBox.Show("Winform new thread");

                 }

                 else // works if using ShowDialog, will block if using Show and without using System.Windows.Forms.Application.Run.

                 {

                     Form1 myWinForm = new Form1();

                     myWinForm.ShowDialog(); // Modal

                     //System.Windows.Forms.Application.Run(myWinForm); // Modeless

                     MessageBox.Show("Winform same thread");

                 }            

             }

     

             public void MacroWPF()

             {

                 bool useNewThread = true;

                 if (useNewThread)

                 {

                     Thread t = new Thread(() =>

                     {

                         System.Windows.Window w = new System.Windows.Window();

                         System.Windows.Application app = new System.Windows.Application();

                         app.Run(w);

                     });

                     t.SetApartmentState(ApartmentState.STA);

                     t.Start();

                     t.Join();

                     MessageBox.Show("Winform new thread");

                 }

                 else // Won't work without creating a new thread

                 {

                     System.Windows.Window w = new System.Windows.Window();

                     System.Windows.Application app = new System.Windows.Application();

                     app.Run(w);

                     MessageBox.Show("Winform same thread");

                 }

             }

  • 06-26-2008 9:53 AM In reply to

    Re: MTA Thread VSTA V2.0

    Thanks. I don't think that this solves my problem though.

    Thread t = new Thread(
      delegate(){
        System.Windows.Window w = new System.Windows.Window();
        System.Windows.Application app = new System.Windows.Application();
        app.Run(w);
       });

    Where does the above go in my initial example? In my application, the class is:

      public partial class Splash : Window  {
        public Splash( Window parentWindow ) {
         ...

        }

        public new void ShowDialog() {
          base.ShowDialog();
        }
      }

     class Main: Window {
       ...

       public Splash GetAboutDialog(){
        return new Splash();
      }

    I have created proxies for Main and for Splash.

    From what I understand, the objects running in the macro aren't windows, they're proxies to objects created and running in my application.

    So, when my addin does

    Partial Class AddIn
     
    Public Sub About()
     
      Dim about As Splash = Main.GetAboutDialog()
        about.ShowDialog()
      End Sub
      ....
    End Class

    So, Main is a proxy to an instance of the Main class and about is a proxy to an instance of the the Splash class. In particular about is not a window, the actual window is created in the main application's domain. The ShowDialog is a remote function that calls the ShowDialog of Splash. I am not trying to create or show the dialog in the addin code's domain, I am controlling the one that is created in my application.

    Probably, I am misunderstanding something here.

    Regards,
    Alec

  • 06-26-2008 11:32 AM In reply to

    Re: MTA Thread VSTA V2.0

    Hi Alec,

    No, you're not misunderstaning. I offered the answer for "how to create UI in an add-in so that it works properly in the seamless debugger" - a different problem caused by the debugger running in an MTA.

    How did you create proxies for Main and Splash? Did you use proxygen? Or did you create your own proxy?

    Here's a thought... What happens if you create a new STA thread in your Add-in that calls Splash.ShowDialog? I'm just guessing, but when you try to show a WPF form that was created in the add-in (workaround shown in MacroWPF above), you get the same error message that you report. Try something like:

    Thread t = new Thread(() =>
    {
         Splash about
    = Main.GetAboutDialog();
         about.ShowDialog();
    }
    t.SetApartmentState(ApartmentState.STA);
    t.Start();
    t.Join();

    Please let me know about your proxy, too, and I'll try to mock up the same scenario.

    Best regards,
    Rebecca

  • 06-26-2008 2:53 PM In reply to

    • Gary
    • Top 10 Contributor
    • Joined on 07-13-2006
    • Posts 320

    Re: MTA Thread VSTA V2.0

    Passing Windows Forms or WPF windows or controls between host and add-in is not supported in out-of-process debugging scenario.

    Proxygen does not support passing UIs between host and add-in either. A custom contract using FrameworkElementAdapters is required to accomplish this:

    http://blogs.msdn.com/wpfsdk/archive/2007/11/09/building-visual-add-ins-with-wpf-new-in-net-3-5.aspx

    >>I am not trying to create or show the dialog in the addin code's domain, I am controlling the one that is created in my application.

    The simple approach to controlling the UI in your application is to create Automation API elements to control the host UI from an add-in. 

    In VSTA V2 hosting AddIn in a different process (as is the case with seamless debugging) requires the host to do more work if the host OM allows AddIn to manipulate host UI. There are two things the host needs to do.

         1.  Make sure all the UI operations are carried out in the main UI thread. For example:

    public bool Visible

            {

                get

                {

                    return form.Visible;

                }

                set

                {

                    if (form.InvokeRequired)

                    {

                        VisibleDelegate vd = delegate()

                        {

                            form.Visible = value;   

                        };

                        form.Invoke(vd);

                    }

                    else

                    {

                        form.Visible = value;

                    }

                }

            }

            private delegate void VisibleDelegate();

     

    2.       When executing macros, you’ll need to call it from a different thread other than the main thread. Otherwise, the main thread is blocked and cannot service the Macro’s callback request and we’ll get a deadlock. For example,

           

    internal void ExecuteMacro(string macroName)

            {

                Thread t = new Thread(() =>

                {

                    ExecuteMacroInternal(macroName); // The original ExecuteMacro function is called here.

                });

                t.SetApartmentState(Thread.CurrentThread.GetApartmentState());

                t.Start();

                // Do not call t.Join(); Otherwise it'll block and the host will be frozen.

            }

     

    The reason for this limitation is because System.AddIn.dll, that VSTA relies on, uses .Net thread-pool threads to serve remote method calls. So when the AddIn calls a Host API, the call will be executed on some thread-pool thread other than the main thread. This problem only shows up in out-of-proc debugging because in-proc debuggingt doesn’t use the thread-pool threads.

     

    These considerations were not added to the beta SDK sample for the sake of simplicity.

     

    We’re discussing, with the VSTA team, ways to improve the VSTA 2.0 seamless debugging integration and end-user experience.

     

    Hope this helps.

     

Page 1 of 1 (11 items) | RSS
Copyright Summit Software Company, 2008. All rights reserved.