Three New Managed Package Framework features for Visual Studio 2010

Three New Managed Package Framework features for Visual Studio 2010

Rate This
  • Comments 7

This post describes three new features that have been added to the Managed Package Framework (MPF) version 10.0 in Visual Studio 2010. These features are available to anyone developing extensions in managed code for Visual Studio 2010. To get started building extensions for Visual Studio, start by visiting the Visual Studio Extensibility Developer Center on MSDN and then go and download the Visual Studio 2010 SDK.

To use these features, your extension must reference the assembly Microsoft.VisualStudio.Shell.10.0.

1. ServiceProvider.GlobalProvider

This new static property on the ServiceProvider class allows access to the global service provider from any code, as long as it is called from the main UI thread. This property is closely related to the Package.GetGlobalService static method which was available in previous versions of the MPF. The problem with Package.GetGlobalService was that it would fail if a package had not yet been initialized. This led to subtle ordering bugs in code that used the MPF libraries without initializing a package of their own. Sometimes they would work only because another package had already initialized the global ServiceProvider on their behalf. If that other package was uninstalled, or perhaps moved to a different version of the MPF, that static would no longer be initialized causing Package.GetGlobalService to fail.

Now, in MPF 10, you can call ServiceProvider.GlobalProvider at any time as long as you are calling from the UI thread. For compatibility, this mechanism will still use the ServiceProvider created by the first Package to be sited but, in the case where no Package has yet been initialized, MPF 10.0 now has the ability to obtain the global provider from the registered COM message filter. Package.GetGlobalService() is also hooked up to this new mechanism.

2. ActivityLog

The activity log is a feature first introduced in Visual Studio 2005 for diagnosing package load failures. In Visual Studio 2010, we started to log a few more things to the activity log and would like to promote it as the service for all extensions to use for low-frequency diagnostics. To that end, we created a very simple-to-use ActivityLog class. It’s small enough to post the public interface here.

/// <summary>
/// Class which provides convenient managed methods for logging messages to the IVsActivityLog.
/// </summary>
public static class ActivityLog
{
    /// <summary>
    /// Gets the path to the activity log for the current application.
    /// </summary>
    public static string LogFilePath { get; }

    /// <summary>
    /// Logs an error message in the activity log with the given source.
    /// </summary>
    /// <param name="source">A name associated with the source contributing the message.</param>
    /// <param name="message">The message to be written to the activity log.</param>
    public static void LogError(string source, string message);

    /// <summary>
    /// Logs a warning message in the activity log with the given source.
    /// </summary>
    /// <param name="source">A name associated with the source contributing the message.</param>
    /// <param name="message">The message to be written to the activity log.</param>
    public static void LogWarning(string source, string message);

    /// <summary>
    /// Logs an information message in the activity log with the given source.
    /// </summary>
    /// <param name="source">A name associated with the source contributing the message.</param>
    /// <param name="message">The message to be written to the activity log.</param>
    public static void LogInformation(string source, string message);
}

All public methods and properties on the class are static which makes it incredibly easy to use from your own code. e.g.:

ActivityLog.LogInformation("ACME Widget Package", "The widget is disconnected");

Internally, of course, ActivityLog uses ServiceProvider.GlobalProvider to locate the global service provider.

I have to give credit and thanks for this idea to István Novák, one of our MVPs, who regularly visits us in Redmond to attend and also present at our Visual Studio developer clinics. István has developed a set of managed classes called “VSXtra” for easing the development of managed packages by providing a useful set of helper classes. This new class was inspired by a discussion I had with him at the Developer Tools Summit last October and was incorporated into MPF 10.0 with his permission.

3. ThreadHelper

The ThreadHelper class allows managed code running on any thread to run operations on the UI thread. This may be necessary if you are calling into a component that is not thread-safe and must be called on the UI thread. This mechanism is preferred over other existing techniques like Dispatcher.Invoke from WPF or Control.Invoke from Windows Forms because neither of these is “RPC aware” and using them may result in deadlocks in some scenarios. As you may know, Visual Studio is heavily rooted in COM and RPC is the primary mechanism used by components to communicate across threads or processes.

Under the covers, ThreadHelper makes a private cross-apartment COM call to the UI thread where the ‘invokable’ operation completes. By using a genuine COM call, we maintain what’s known as “logical thread identity” throughout the call. If the call to the UI thread is itself part of a cross-thread COM call, then it will be recognized as part of the same call (CALLTYPE_NESTED) and not a new top-level call. Both WPF’s and Windows Forms’ invoke operations post private messages to the UI thread and this breaks the chain of logical thread identity.

ThreadHelper really has only one interesting method, Invoke, although it takes two forms:

public void Invoke(Action action);
public TResult Invoke<TResult>(Func<TResult> method);

The argument passed to Invoke is a delegate for the operation you want to perform on the UI thread. If the caller is already on the UI thread, then the delegate is invoked directly. If the operation throws an exception, then the exception is re-raised on the calling thread.

Using ThreadHelper in your own code is straightforward, but note that ThreadHelper itself is abstract to allow for advanced extensibility. Most of the time, you’ll use the general purpose implementation accessed through the static property ThreadHelper.Generic. Here’s an example:

public class MyComponent
{
    public string Name 
    {
        get
        {
            // The Name property may be accessed from any thread, but the
            // implementation requires that the call comes in on the UI thread.
            return ThreadHelper.Generic.Invoke(() => GetName());
        }
    }

    // Must be called on the UI thread
    private string GetName()
    {
        // ...
    }

    // ...
}

Conclusion

I hope you’ll find these three new features useful when developing your own managed extensions to Visual Studio 2010. As always, please feel free to use the comment stream to ask questions.

clip_image002

Paul Harrington – Principal Developer, Visual Studio Platform Team.
Biography: Paul has worked on every version of Visual Studio .Net to date. Prior to joining the Visual Studio team in 2000, Paul spent six years working on mapping and trip planning software for what is today known as Bing Maps. For Visual Studio 2010, Paul designed and helped write the code that enabled the Visual Studio Shell team to move from a native, Windows 32-based implementation to a modern, fully managed presentation layer based on the Windows Presentation Foundation (WPF). Paul holds a master’s degree from the University of Cambridge, England and lives with his wife and two cats in Seattle, Washington.

Leave a Comment
  • Please add 7 and 2 and type the answer here:
  • Post
  • Thank you for submitting this cool story - Trackback from DotNetShoutout

  • Hi Paul,

    We are currently experiencing some problem about multi-thread in an Visual Studio 2008 Isolated Shell. Your post about ThreadHelper is very interesting. Do you know where I can some documentation about that kind of  problem ? I mean, a good way to deal with such problem in VS 2008 ?

  • Hi Paul,

    Same for us, any workaround for Visual Studio 2008?

    Thanks.

  • Using Control.Invoke and MessageFilter then Winforms should become is some way "RPC aware". Using this pattern I never had any problems.

    MessageFilter

    msdn.microsoft.com/.../ms228772(VS.80).aspx

  • Is there any way to check if current thread is in fact UI thread?

    Usually I would do something like:

    public string Name

    {

     set

     {

      if (!uithread)

         Invoke(() => Name = "newName"

      else

         this.Name = "newName"

      }

    }

    Or is this check performed automatically by ThreadHelper?

  • @me: That check is performed automatically by ThreadHelper.

  • thanks for quick answer!

Page 1 of 1 (7 items)