ReSharper has two kinds of components – shell and solution components. Knowledge and understanding of how these differ is critical to being able to write plug-ins that successfully use ReSharper services.
A Shell Component is a component that gets created when the Visual Studio shell is created. Shell components are typically marked with the
Shell components are useful if you want to use some service in your plug-in that you want to be created virtually as soon as you plug-in is loaded. To do that, simply write:
In certain times, you might need to access other shell components (e.g., ReSharper’s shell components) from within your code. There are, essentially, two ways of doing this.
First, you can use constructor injection, i.e., simply define the component in the constructor of your class and, if the shell component is available, it will be injected automatically:
The other, more straightforward solution, is simply calling
Shell.Instance.GetComponent() as follows:
Solution components are components that are tied to the solution currently opened. Consequently, they cannot be accessed when a solution is not present (e.g., in shell components). Solution components use the
Solution components are typically acquired through different means compared to shell components. In most cases, you are likely to need a solution component in some method that has been provided, e.g., an
IDataContext or even an
ISolution. That being the case, you can simply get the solution from this context, and subsequently call
GetComponent() on the solution. For example:
In addition to getting the components directly, it's also possible to get them to only be acquired at the time of use. To do this, simply use the type
Lazy<ComponentType> instead of
ComponentType when querying or injecting a component.
Acquiring Visual Studio interfaces
ReSharper also allows access to interfaces implemented by Visual Studio. However, it is recommended that you try and avoid the use of Visual Studio interfaces, as this usually ties your plugin to a particular version of Visual Studio. You are strongly encouraged to use ReSharper interfaces and services whenever possible.
If you still need to get an interface implemented by Visual Studio, or by a Visual Studio extension, you can do this in several ways. The easiest is to simply inject the interface as a constructor parameter to your plugin component, or retrieve it from the Shell’s container. For example, to get to VS's Output Window interface
IVsOutputWindow, you can acquire it with the following:
Note the use of
Lazy<T>. Various Visual Studio interfaces exposed by ReSharper are available as Lazy-only, meaning you can only get them if you ask for
Lazy<T>. This allows for delay loading resources, such as VS packages and assemblies, until they are used.
ReSharper does not expose all of Visual Studio’s interfaces in this manner. Instead, it exposes only those needed to support ReSharper's own integration with Visual Studio. In addition, some interfaces are wrapped, to ensure correct usage, e.g. wrapping parameters for COM interop, releasing returned COM intptr's, checking returned HRESULTs, etc. You should consult the whitelist below to see what interfaces are available. If it's there, you can consume it via a component's construcutor.
If you need to access interfaces that aren’t exposed, you have two options:
- Get access to the
Microsoft.VisualStudio.OLE.Interop.IServiceProviderinterface, and request it yourself
- Set up a class to expose the required interfaces from the container
Retrieving interfaces from IServiceProvider
First, it’s important to note that there are several IServiceProvider types in the CLR. We are interested in
Microsoft.VisualStudio.OLE.Interop.IServiceProvider, which is a COM interface.
ReSharper does not expose this interface directly. If you try and consume IServiceProvider in your constructor, ReSharper will throw an exception, and tell you to use the
JetBrains.VsIntegration.Application.RawVsServiceProvider wrapper class. The purpose of this class is to ensure that you know you are deliberately bypassing ReSharper and talking directly to Visual Studio. This isn't a recommended solution, but is sometimes necessary. Make sure you know what you are doing. You should always try and use either a ReSharper interface, or request the Visual Studio interface from ReSharper first - remember that ReSharper wraps some Visual Studio interfaces.
Once you have the RawVsServiceProvider class, you can simply use the
Value property to get access to the interface, and query Visual Studio for a specific service. It is now your responsibility to ensure the interface is called correctly, and any COM requirements are met.
Expose interfaces to the container
You can implement a class that will be called by ReSharper during initialisation that can register additional Visual Studio interfaces. It needs to be marked with the
WrapVsInterfaces attribute, and implement the
This class allows you to expose one or more interfaces that comes from a service (in the example above, it exposes MEF’s IComponentModel interface by first querying for the SComponentModel service), or write a custom function that will be called to return an instance of a specific interface.
One important thing to note is that you can only register an interface once. If there are two plugins both registering the same interface, only the first registration (arbitrarily chosen) will be used. The second is ignored. So it is important not to do anything “fancy” in a custom function - it might never get called! Also, while the second registration is ignored and the registration method completes successfully, ReSharper handles and logs an exception. Since these exceptions are displayed to the user in the exception reporter, it is a good practice to check if the interface is already registered before trying to register yourself. You can use the following extension method to do this (and doesn't interfere with lazy instantiation):
These interfaces are now exposed to ReSharper’s container, and can be injected into the constructor, or retrieved via the Shell’s GetComponent method.
You should use
Lazy<TInterface> when consuming a Visual Studio interface, so that Visual Studio packages are not loaded until they are used. For example, accepting a parameter of
IEditorOperationsFactoryService will cause the VS editor to be loaded into memory as soon as your plugin is loaded, which is at application startup, rather than when the editor is first needed, which might be after a solution has loaded.
If you expect the interface to not be available, you can mark the parameter with the
OptionalAttribute. Note that this only works with interfaces, not
Lazy<T>, which means you will lose the benefit of using
Lazy<T> (i.e. delay loading of resources) if you use
Optional. It is recommended to prefer
To expose a MEF advertised service, copy and paste this extension method into your code, and call with
map.Mef<IVSInterface>() from your
The interface is being registered as
LazyOnly, which means you can only get it if you request
Lazy<TMefInterface. If there is a chance that the MEF interface is not available, you should request
IComponentModel (which isn't exposed by default, see example above) and calling
IComponentModel.GetService<> yourself, wrapped in a try/catch block.
Default list of exposed interfaces
For ReSharper 7, the default whitelist of supported interfaces is as follows:
- IVsSolutionBuildManager, IVsSolutionBuildManager2, IVsSolutionBuildManager3
- IVsTextManager, IVsTextManager2, IVsHiddenTextManager
- ILocalRegistry, ILocalRegistry2
- IVsUIShell, IVsUIShell2
- Help, Help2
- IVsProfferCommands, IVsProfferCommands2, IVsProfferCommands3
- IVsDebugger, IVsDebugger2
- DTE, DTE2
- IVsLaunchPad, IVsLaunchPad2
Also, the following Visual Studio 2010 are exposed:
And Visual Studio 2012:
Furthermore, the following classes are used instead of accessing the VS interfaces directly:
- RawVsServiceProvider (IServiceProvider - use advisedly!)
- JetBrains.VsIntegration.Application.VsUIHostCommandDispatcher (SUIHostCommandDispatcher’s IOleCommandTarget)