Logo
HOWTO: Get the Microsoft.VisualStudio.Shell.Interop.IVsUIShell5 interface to theme a Visual Studio 2012 add-in.

Author: Carlos J. Quintero (Microsoft MVP) Applies to: Microsoft Visual Studio 2012
Date: October 2012    
       
Introduction

This article explains how to get the Microsoft.VisualStudio.Shell.Interop.IVsUIShell5 interface from a Visual Studio 2012 add-in. This interface provides several methods (GetThemedColor, ThemeWindow, CreateThemedImageList, ThemeDIBits) that are useful to theme a Visual Studio 2012 add-in when using the Dark / Light themes.

More information

There are two ways to get the declaration of the SVsUIShell and IVsUIShell5 types from a Visual Studio 2012 add-in:

1) If your add-in only targets Visual Studio 2012 (and not Visual Studio 2010 and below) then you can safely add a reference to the assembly that contains this interface, which is the assembly Microsoft.VisualStudio.Shell.Interop.11.0, Version=11.0.0.0. This assembly resides in the .NET Framework 2.0 Global Assembly Cache (GAC), in the folder C:\Windows\Assembly, not in the .NET Framework 4.0 GAC (in the folder C:\Windows\Microsoft.NET\assembly). To add a reference to an assembly in the GAC 2.0 see the article HOWTO: Reference a Visual Studio assembly in the GAC from an add-in. The subfolder that contains the assembly is GAC_MSIL\Microsoft.VisualStudio.Shell.Interop.11.0\11.0.0.0__b03f5f7f11d50a3a.

2) If you add-in targets several Visual Studio versions with the same dll (see the article HOWTO: Create an add-in that targets several Visual Studio versions with the same add-in DLL using C# or VB.NET) then you can not add a reference to the assembly Microsoft.VisualStudio.Shell.Interop.11.0, Version=11.0.0.0, because that assembly won't be present on the target computer if Visual Studio 2012 is not installed. In this case you can define the type in the own code of your add-in (note: if you use obfuscation, you need to ensure that this type is not obfuscated):

Language: VB.NET   Copy Code Copy Code (IE only)
Imports System.Runtime.InteropServices
Imports System.Runtime.CompilerServices

<ComVisible(False), Guid("B61FC35B-EEBF-4dec-BFF1-28A2DD43C38F")> _
Public Interface SVsUIShell
End Interface

<StructLayout(LayoutKind.Sequential, Pack:=4), ComConversionLoss()> _
Public Structure VSOPENFILENAMEW

   <ComAliasName("Microsoft.VisualStudio.OLE.Interop.DWORD")> _
   Public lStructSize As UInt32

   <ComConversionLoss()> _
   Public hwndOwner As IntPtr

   <ComAliasName("Microsoft.VisualStudio.OLE.Interop.LPCWSTR"), MarshalAs(UnmanagedType.LPWStr)> _
   Public pwzDlgTitle As String

   Public pwzFileName As IntPtr

   <ComAliasName("Microsoft.VisualStudio.OLE.Interop.DWORD")> _
   Public nMaxFileName As UInt32

   <ComAliasName("Microsoft.VisualStudio.OLE.Interop.LPCWSTR"), MarshalAs(UnmanagedType.LPWStr)> _
   Public pwzInitialDir As String

   <ComAliasName("Microsoft.VisualStudio.OLE.Interop.LPCWSTR"), MarshalAs(UnmanagedType.LPWStr)> _
   Public pwzFilter As String

   <ComAliasName("Microsoft.VisualStudio.OLE.Interop.DWORD")> _
   Public nFilterIndex As UInt32

   <ComAliasName("Microsoft.VisualStudio.OLE.Interop.DWORD")> _
   Public nFileOffset As UInt32

   <ComAliasName("Microsoft.VisualStudio.OLE.Interop.DWORD")> _
   Public nFileExtension As UInt32

   <ComAliasName("Microsoft.VisualStudio.OLE.Interop.DWORD")> _
   Public dwHelpTopic As UInt32

   <ComAliasName("Microsoft.VisualStudio.OLE.Interop.DWORD")> _
   Public dwFlags As UInt32

End Structure

<ComImport(), Guid("BEC804F7-F5DE-4F3E-8EBB-DAB26649F33F"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)> _
Public Interface IVsEnumGuids

   <PreserveSig(), MethodImpl(MethodImplOptions.InternalCall, MethodCodeType:=MethodCodeType.Runtime)> _
   Function [Next](<[In](), ComAliasName("OLE.ULONG")> ByVal celt As UInt32, <Out(), MarshalAs(UnmanagedType.LPArray, _
      SizeParamIndex:=0)> ByVal rgelt As System.Guid(), <Out(), ComAliasName("OLE.ULONG")> ByRef pceltFetched As UInt32) As Integer

   <PreserveSig(), MethodImpl(MethodImplOptions.InternalCall, MethodCodeType:=MethodCodeType.Runtime)> _
   Function Skip(<[In](), ComAliasName("OLE.ULONG")> ByVal celt As UInt32) As Integer

   <MethodImpl(MethodImplOptions.InternalCall, MethodCodeType:=MethodCodeType.Runtime)> _
   Sub Reset()

   <MethodImpl(MethodImplOptions.InternalCall, MethodCodeType:=MethodCodeType.Runtime)> _
   Sub Clone(<Out(), MarshalAs(UnmanagedType.Interface)> ByRef ppEnum As IVsEnumGuids)

End Interface

<ComImport(), Guid("2B70EA30-51F2-48BB-ABA8-051946A37283"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)> _
Public Interface IVsUIShell5

   <MethodImpl(MethodImplOptions.InternalCall, MethodCodeType:=MethodCodeType.Runtime)> _
   Sub GetOpenFileNameViaDlgEx2(<[In](), Out(), ComAliasName("VsShell.VSOPENFILENAMEW"), _
      MarshalAs(UnmanagedType.LPArray)> ByVal openFileName As VSOPENFILENAMEW(), _
      <[In](), ComAliasName("OLE.LPCOLESTR"), MarshalAs(UnmanagedType.LPWStr)> ByVal HelpTopic As String, _
      <[In](), ComAliasName("OLE.LPCOLESTR"), MarshalAs(UnmanagedType.LPWStr)> ByVal openButtonLabel As String)

   <MethodImpl(MethodImplOptions.InternalCall, MethodCodeType:=MethodCodeType.Runtime)> _
   Function GetThemedColor(<[In](), ComAliasName("OLE.REFGUID")> ByRef colorCategory As System.Guid, _
      <[In](), ComAliasName("OLE.LPCOLESTR"), MarshalAs(UnmanagedType.LPWStr)> ByVal colorName As String, _
      <[In](), ComAliasName("Microsoft.VisualStudio.Shell.Interop.THEMEDCOLORTYPE")> ByVal colorType As UInt32) _
         As <ComAliasName("Microsoft.VisualStudio.Shell.Interop.VS_RGBA")> UInt32

   <MethodImpl(MethodImplOptions.InternalCall, MethodCodeType:=MethodCodeType.Runtime)> _
   Function GetKeyBindingScope(<[In](), ComAliasName("OLE.REFGUID")> ByRef keyBindingScope As System.Guid) _
      As <MarshalAs(UnmanagedType.BStr)> String

   <MethodImpl(MethodImplOptions.InternalCall, MethodCodeType:=MethodCodeType.Runtime)> _
   Function EnumKeyBindingScopes() As <MarshalAs(UnmanagedType.Interface)> IVsEnumGuids

   <MethodImpl(MethodImplOptions.InternalCall, MethodCodeType:=MethodCodeType.Runtime)> _
   Function ThemeWindow(<[In]()> ByVal hwnd As IntPtr) As Boolean

   <MethodImpl(MethodImplOptions.InternalCall, MethodCodeType:=MethodCodeType.Runtime)> _
   Function CreateThemedImageList(<[In]()> ByVal hImageList As IntPtr, _
      <[In](), ComAliasName("VsShell.COLORREF")> ByVal crBackground As UInt32) As IntPtr

   <MethodImpl(MethodImplOptions.InternalCall, MethodCodeType:=MethodCodeType.Runtime)> _
   Sub ThemeDIBits(<[In](), ComAliasName("OLE.DWORD")> ByVal dwBitmapLength As UInt32, _
      <Out(), ComAliasName("TextManager.BYTE"), MarshalAs(UnmanagedType.LPArray, SizeParamIndex:=0)> ByVal pBitmap As Byte(), _
      <[In](), ComAliasName("OLE.DWORD")> ByVal dwPixelWidth As UInt32, <[In](), ComAliasName("OLE.DWORD")> ByVal dwPixelHeight As UInt32, _
      <[In]()> ByVal fIsTopDownBitmap As Boolean, <[In](), ComAliasName("VsShell.COLORREF")> ByVal crBackground As UInt32)

End Interface
Language: C#   Copy Code Copy Code (IE only)
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

[ComVisible(false), Guid("B61FC35B-EEBF-4dec-BFF1-28A2DD43C38F")]
public interface SVsUIShell
{
}

[StructLayout(LayoutKind.Sequential, Pack = 4), ComConversionLoss]
public struct VSOPENFILENAMEW
{
   [ComAliasName("Microsoft.VisualStudio.OLE.Interop.DWORD")]
   public uint lStructSize;

   [ComConversionLoss]
   public IntPtr hwndOwner;

   [ComAliasName("Microsoft.VisualStudio.OLE.Interop.LPCWSTR"), MarshalAs(UnmanagedType.LPWStr)]
   public string pwzDlgTitle;

   public IntPtr pwzFileName;

   [ComAliasName("Microsoft.VisualStudio.OLE.Interop.DWORD")]
   public uint nMaxFileName;

   [ComAliasName("Microsoft.VisualStudio.OLE.Interop.LPCWSTR"), MarshalAs(UnmanagedType.LPWStr)]
   public string pwzInitialDir;

   [ComAliasName("Microsoft.VisualStudio.OLE.Interop.LPCWSTR"), MarshalAs(UnmanagedType.LPWStr)]
   public string pwzFilter;

   [ComAliasName("Microsoft.VisualStudio.OLE.Interop.DWORD")]
   public uint nFilterIndex;

   [ComAliasName("Microsoft.VisualStudio.OLE.Interop.DWORD")]
   public uint nFileOffset;

   [ComAliasName("Microsoft.VisualStudio.OLE.Interop.DWORD")]
   public uint nFileExtension;

   [ComAliasName("Microsoft.VisualStudio.OLE.Interop.DWORD")]
   public uint dwHelpTopic;

   [ComAliasName("Microsoft.VisualStudio.OLE.Interop.DWORD")]
   public uint dwFlags;
}

[ComImport, Guid("BEC804F7-F5DE-4F3E-8EBB-DAB26649F33F"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IVsEnumGuids
{
   [PreserveSig, MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
   int Next([In, ComAliasName("OLE.ULONG")] uint celt,
      [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] Guid[] rgelt, 
      [ComAliasName("OLE.ULONG")] out uint pceltFetched);

   [PreserveSig, MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
   int Skip([In, ComAliasName("OLE.ULONG")] uint celt);

   [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
   void Reset();

   [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
   void Clone([MarshalAs(UnmanagedType.Interface)] out IVsEnumGuids ppEnum);
}

[ComImport, Guid("2B70EA30-51F2-48BB-ABA8-051946A37283"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IVsUIShell5
{
   [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
   void GetOpenFileNameViaDlgEx2([In, Out, ComAliasName("VsShell.VSOPENFILENAMEW"), 
      MarshalAs(UnmanagedType.LPArray)] VSOPENFILENAMEW[] openFileName,
      [In, ComAliasName("OLE.LPCOLESTR"), MarshalAs(UnmanagedType.LPWStr)] string HelpTopic,
      [In, ComAliasName("OLE.LPCOLESTR"), MarshalAs(UnmanagedType.LPWStr)] string openButtonLabel);

   [return: ComAliasName("Microsoft.VisualStudio.Shell.Interop.VS_RGBA")]
   [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
   uint GetThemedColor([In, ComAliasName("OLE.REFGUID")] ref Guid colorCategory,
     [In, ComAliasName("OLE.LPCOLESTR"), MarshalAs(UnmanagedType.LPWStr)] string colorName,
     [In, ComAliasName("Microsoft.VisualStudio.Shell.Interop.THEMEDCOLORTYPE")] uint colorType);

   [return: MarshalAs(UnmanagedType.BStr)]
   [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
   string GetKeyBindingScope([In, ComAliasName("OLE.REFGUID")] ref Guid keyBindingScope);

   [return: MarshalAs(UnmanagedType.Interface)]
   [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
   IVsEnumGuids EnumKeyBindingScopes();

   [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
   bool ThemeWindow([In] IntPtr hwnd);

   [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
   IntPtr CreateThemedImageList([In] IntPtr hImageList,
      [In, ComAliasName("VsShell.COLORREF")] uint crBackground);

   [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
   void ThemeDIBits([In, ComAliasName("OLE.DWORD")] uint dwBitmapLength,
      [Out, ComAliasName("TextManager.BYTE"), MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] byte[] pBitmap,
      [In, ComAliasName("OLE.DWORD")] uint dwPixelWidth,
      [In, ComAliasName("OLE.DWORD")] uint dwPixelHeight,
      [In] bool fIsTopDownBitmap,
      [In, ComAliasName("VsShell.COLORREF")] uint crBackground);
}

Once you have the interface type definition, to get the service follow the approach explained in the article HOWTO: Get a Visual Studio service from an add-in. The following code shows how to call the GetService method:

Language: VB.NET   Copy Code Copy Code (IE only)
Public Class Connect
   Implements IDTExtensibility2
	
   Private _applicationObject As DTE
   Private _addInInstance As AddIn
   Private _IVsUIShell5 As IVsUIShell5

   Public Sub OnConnection(ByVal application As Object, ByVal connectMode As ext_ConnectMode, _
      ByVal addInInst As Object, ByRef custom As Array) Implements IDTExtensibility2.OnConnection

      _applicationObject = CType(application, DTE)
      _addInInstance = CType(addInInst, AddIn)

      Select Case connectMode

         Case ext_ConnectMode.ext_cm_AfterStartup

            InitializeAddIn()

         Case ext_ConnectMode.ext_cm_Startup

            ' OnStartupComplete will be called

      End Select
     
   End Sub

   Public Sub OnStartupComplete(ByRef custom As Array) Implements IDTExtensibility2.OnStartupComplete

      InitializeAddIn()

   End Sub

   Private Sub InitializeAddIn()

      Dim service As Object

      service = GetService(_applicationObject, GetType(SVsUIShell))

      _IVsUIShell5 = CType(service, IVsUIShell5)

   End Sub

   Public Sub OnDisconnection(ByVal disconnectMode As ext_DisconnectMode, ByRef custom As Array) _
      Implements IDTExtensibility2.OnDisconnection
   End Sub

   Public Sub OnAddInsUpdate(ByRef custom As Array) Implements IDTExtensibility2.OnAddInsUpdate
   End Sub

   Public Sub OnBeginShutdown(ByRef custom As Array) Implements IDTExtensibility2.OnBeginShutdown
   End Sub
	
End Class
Language: C#   Copy Code Copy Code (IE only)
public class Connect : IDTExtensibility2
{
   private DTE2 _applicationObject;
   private AddIn _addInInstance;
   private IVsUIShell5 _IVsUIShell5;

   public void OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom)
   {
      _applicationObject = (DTE2)application;
      _addInInstance = (AddIn)addInInst;

      switch (connectMode)
      {
         case ext_ConnectMode.ext_cm_AfterStartup:

            Initialize();
            break;

         case ext_ConnectMode.ext_cm_Startup:
            // OnStartupComplete will be called
            break;
      }
         
   }

   public void OnStartupComplete(ref Array custom)
   {
      Initialize();
   }

   private void Initialize()
   {
      object service = null;

      service = GetService(_applicationObject, typeof(SVsUIShell));

      _IVsUIShell5 = (IVsUIShell5)service;

   }

   public void OnDisconnection(ext_DisconnectMode disconnectMode, ref Array custom)
   {
   }

   public void OnAddInsUpdate(ref Array custom)
   {
   }

   public void OnBeginShutdown(ref Array custom)
   {
   }

}

Related articles


Go back to the 'Resources for Visual Studio .NET extensibility' section for more articles like this


Top