HOWTO: Get an inverted bitmap to use with the Dark theme of Visual Studio 2012 from an add-in.

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

When using the Dark theme, Visual Studio 2012 automatically inverts the colors of bitmaps of commands on toolbars, commandbars, etc. However, an add-in may require to invert the colors of some bitmap on some other user interface element.

More information

The following sample shows an add-in that inverts the bitmap shown on a form, using the ThemeDIBits method of the Microsoft.VisualStudio.Shell.Interop.IVsUIShell5 interface. To get that interface from an add-in, see the article HOWTO: Get the Microsoft.VisualStudio.Shell.Interop.IVsUIShell5 interface to theme a Visual Studio 2012 add-in. The sample uses the LockBits method of the Bitmap class, as explained in this sample from the MSDN documentation.

To show the sample:

  • Select the Dark theme in Visual Studio 2012.
  • Create a 24-bit bitmap using the value RGB=0,254,0 as transparent color. The sample uses this color, but you can change it to other color.
  • Create an add-in for Visual Studio 2012 with a form Form1 with two PictureBox controls on it (PictureBox1 and PictureBox2).
  • Add references to the Microsoft.VisualStudio.OLE.Interop.dll, Microsoft.VisualStudio.Shell.Interop.dll and Microsoft.VisualStudio.Shell.Interop.11.0 assemblies as explained in the article HOWTO: Get the Microsoft.VisualStudio.Shell.Interop.IVsUIShell5 interface to theme a Visual Studio 2012 add-in.
  • Set the image of the PictureBox1 control to the bitmap created before.
  • Use this code in the Connect class, that shows the form modally with the inverted bitmap in the PictureBox2 control:
Language: VB.NET   Copy Code Copy Code (IE only)
Imports System
Imports Microsoft.VisualStudio.CommandBars
Imports Extensibility
Imports EnvDTE
Imports EnvDTE80
Imports System.Runtime.InteropServices
Imports System.Drawing

Public Class Connect
   Implements IDTExtensibility2
   Private _applicationObject As DTE2
   Private _addInInstance As AddIn
   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, DTE2)
      _addInInstance = CType(addInInst, AddIn)

      Select Case connectMode

         Case ext_ConnectMode.ext_cm_AfterStartup


         Case ext_ConnectMode.ext_cm_Startup
            ' OnStartupComplete will be called

      End Select

   End Sub

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


   End Sub

   Private Function GetIVsUIShell5() As Microsoft.VisualStudio.Shell.Interop.IVsUIShell5

      Dim sp As Microsoft.VisualStudio.OLE.Interop.IServiceProvider
      Dim SVsUIShellType As Type
      Dim hr As Integer
      Dim serviceIntPtr As IntPtr
      Dim shell5 As Microsoft.VisualStudio.Shell.Interop.IVsUIShell5 = Nothing
      Dim serviceObject As Object

      sp = DirectCast(_applicationObject, Microsoft.VisualStudio.OLE.Interop.IServiceProvider)

      SVsUIShellType = GetType(Microsoft.VisualStudio.Shell.Interop.SVsUIShell)

      hr = sp.QueryService(SVsUIShellType.GUID, SVsUIShellType.GUID, serviceIntPtr)

      If hr <> 0 Then


      ElseIf Not serviceIntPtr.Equals(IntPtr.Zero) Then

         serviceObject = System.Runtime.InteropServices.Marshal.GetObjectForIUnknown(serviceIntPtr)

         shell5 = CType(serviceObject, Microsoft.VisualStudio.Shell.Interop.IVsUIShell5)


      End If

      Return shell5

   End Function

   Private Function GetInvertedBitmap(ByVal shell5 As Microsoft.VisualStudio.Shell.Interop.IVsUIShell5, _
      ByVal inputBitmap As Bitmap, ByVal transparentColor As Color, ByVal backgroundColor As UInteger) As Bitmap

      Dim outputBitmap As Bitmap = Nothing
      Dim outputBytes() As Byte
      Dim rect As Rectangle
      Dim bitmapData As Imaging.BitmapData
      Dim sourcePointer As IntPtr
      Dim length As Integer


         outputBitmap = New Bitmap(inputBitmap)


         rect = New Rectangle(0, 0, outputBitmap.Width, outputBitmap.Height)

         bitmapData = outputBitmap.LockBits(rect, Imaging.ImageLockMode.ReadWrite, outputBitmap.PixelFormat)

         sourcePointer = bitmapData.Scan0

         length = (Math.Abs(bitmapData.Stride) * outputBitmap.Height)

         outputBytes = New Byte(length - 1) {}

         Marshal.Copy(sourcePointer, outputBytes, 0, length)

         shell5.ThemeDIBits(CType(outputBytes.Length, UInt32), outputBytes, CType(outputBitmap.Width, UInt32), _
                            CType(outputBitmap.Height, UInt32), True, backgroundColor)

         Marshal.Copy(outputBytes, 0, sourcePointer, length)


      Catch ex As Exception


      End Try

      Return outputBitmap

   End Function

   Private Sub InitializeAddIn()

      Const COLOR_NAME As String = "ToolWindowBackground"

      Const GUID_COLOR_TABLE_ENVIRONMENT_CATEGORY As String = "624ed9c3-bdfd-41fa-96c3-7c824ea32e3d"

      Dim backgroundColor As UInteger
      Dim frm1 As Form1
      Dim inputBitmap As Bitmap
      Dim outputBitmap As Bitmap
      Dim almostGreenColor As Color
      Dim shell5 As Microsoft.VisualStudio.Shell.Interop.IVsUIShell5


         shell5 = GetIVsUIShell5()

         ' Get the background color of toolwindows
         backgroundColor = shell5.GetThemedColor(New System.Guid(GUID_COLOR_TABLE_ENVIRONMENT_CATEGORY), COLOR_NAME, 0)

         frm1 = New Form1()

         ' Apply the background color to the form
         frm1.BackColor = ColorTranslator.FromWin32(CType(backgroundColor And &HFFFFFF, Integer))

         ' Get the unthemed input bitmap
         inputBitmap = New Bitmap(frm1.PictureBox1.Image)

         ' Assume that the transparent color is almost green. It can be any color
         almostGreenColor = Color.FromArgb(0, 254, 0)

         ' Get the themed output bitmap
         outputBitmap = GetInvertedBitmap(shell5, inputBitmap, almostGreenColor, backgroundColor)


         frm1.PictureBox2.Image = outputBitmap



      Catch ex As Exception


      End Try

   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)
using System;
using Extensibility;
using EnvDTE;
using EnvDTE80;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Drawing;
using System.Windows.Forms;

namespace MyAddIn
   public class Connect : IDTExtensibility2
      private DTE2 _applicationObject;
      private AddIn _addInInstance;

      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:


            case ext_ConnectMode.ext_cm_Startup:
               // OnStartupComplete will be called

      public void OnStartupComplete(ref Array custom)

      private Microsoft.VisualStudio.Shell.Interop.IVsUIShell5 GetIVsUIShell5()

         Microsoft.VisualStudio.OLE.Interop.IServiceProvider sp = null;
         Type SVsUIShellType = null;
         int hr = 0;
         IntPtr serviceIntPtr;
         Microsoft.VisualStudio.Shell.Interop.IVsUIShell5 shell5 = null;
         object serviceObject = null;

         sp = (Microsoft.VisualStudio.OLE.Interop.IServiceProvider)_applicationObject;

         SVsUIShellType = typeof(Microsoft.VisualStudio.Shell.Interop.SVsUIShell);

         hr = sp.QueryService(SVsUIShellType.GUID,  SVsUIShellType.GUID, out serviceIntPtr);

         if (hr != 0)
         else if (!serviceIntPtr.Equals(IntPtr.Zero))
            serviceObject = System.Runtime.InteropServices.Marshal.GetObjectForIUnknown(serviceIntPtr);

            shell5 = (Microsoft.VisualStudio.Shell.Interop.IVsUIShell5)serviceObject;

         return shell5;

      private Bitmap GetInvertedBitmap(Microsoft.VisualStudio.Shell.Interop.IVsUIShell5 shell5, Bitmap inputBitmap, 
         Color transparentColor, uint backgroundColor)
         Bitmap outputBitmap = null;
         byte[] outputBytes = null;
         Rectangle rect;
         System.Drawing.Imaging.BitmapData bitmapData = null;
         IntPtr sourcePointer;
         int length = 0;

            outputBitmap = new Bitmap(inputBitmap);


            rect = new Rectangle(0, 0, outputBitmap.Width, outputBitmap.Height);

            bitmapData = outputBitmap.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, outputBitmap.PixelFormat);

            sourcePointer = bitmapData.Scan0;

            length = (Math.Abs(bitmapData.Stride) * outputBitmap.Height);

            outputBytes = new byte[length];

            Marshal.Copy(sourcePointer, outputBytes, 0, length);

            shell5.ThemeDIBits((UInt32)outputBytes.Length, outputBytes, (UInt32)outputBitmap.Width, 
               (UInt32)outputBitmap.Height, true, backgroundColor);

            Marshal.Copy(outputBytes, 0, sourcePointer, length);


         catch (Exception ex)

         return outputBitmap;

      private void InitializeAddIn()
         const string COLOR_NAME = "ToolWindowBackground";

         const string GUID_COLOR_TABLE_ENVIRONMENT_CATEGORY = "624ed9c3-bdfd-41fa-96c3-7c824ea32e3d";

         uint backgroundColor = 0;
         Form1 frm1 = null;
         Bitmap inputBitmap = null;
         Bitmap outputBitmap = null;
         Color almostGreenColor;
         Microsoft.VisualStudio.Shell.Interop.IVsUIShell5 shell5 = null;

            shell5 = GetIVsUIShell5();

            // Get the background color of toolwindows
            backgroundColor = shell5.GetThemedColor(new System.Guid(GUID_COLOR_TABLE_ENVIRONMENT_CATEGORY), COLOR_NAME, 0);

            frm1 = new Form1();

            // Apply the background color to the form
            frm1.BackColor = ColorTranslator.FromWin32(Convert.ToInt32(backgroundColor & 0xffffff));

            // Get the unthemed input bitmap.
            // Note: Change the modifer from private to public
            inputBitmap = new Bitmap(frm1.pictureBox1.Image);

            // Assume that the transparent color is almost green. It can be any color
            almostGreenColor = Color.FromArgb(0, 254, 0);

            // Get the themed output bitmap
            outputBitmap = GetInvertedBitmap(shell5, inputBitmap, almostGreenColor, backgroundColor);


            // Note: Change the modifer from private to public
            frm1.pictureBox2.Image = outputBitmap;



         catch (Exception ex)

      public void OnDisconnection(ext_DisconnectMode disconnectMode, ref Array custom)

      public void OnAddInsUpdate(ref Array custom)

      public void OnBeginShutdown(ref Array custom)


Related articles

Go to the 'Visual Studio Extensibility (VSX)' web site for more articles like this (Articles section)