Creating Bitmaps and About Icon data for Add-ins is still a royal pain in the ass in Visual Studio 2005. The process has changed some and you can now at least use managed resources, but the process to accomplish this as outlined on MSDN is substantial:
http://msdn2.microsoft.com/en-us/library/e9zazcx5.aspx
This process outline is fraught with bad practices. It basically advocates adding resources to a project, creating a RESX file from these resources, modifying the resx source file and then compiling these resources. Uh, I don’t think so…
The first problem with this is that the Visual Studio 2005 Managed Resource Editor – while a big improvement over none at all in Visual Studio 2003 – is that it can’t create named resources. It can only generate numeric ids for icons, which is a problem if you want to say use a resource as an AboutIcon.
Creating Resources more easily
So, do yourself a favor and skip the VS resource editor and download Lutz Roeders nice and simple Resourcer utility and build your resources. Resourcer works against .RESX files or creates them and lets you add bitmaps, icons and text to the resource project easily. With it you can select files and more importantly you can name the resources properly.
![](http://www.west-wind.com/weblog/images/21/o_Resourcer.png)
In the shot above there’s is a bitmap for use as a menu icon. Menu icons should be 16x16 and can be up to true color (32 bit). If you want your menu bitmap to be transparent, make the background BrightGreen with RGB (0,254,0). Notice 254, not 255! For the AboutIcon use Icon transparency to indicate your transparency. I use IconLover for editing small images to create icons both for real icons or icon like images in Web apps, which makes transparency and general image filling a snap.
Menu bitmaps should be assigned numeric name starting no lower than 100 because the VS.ADD Command object uses numeric index ids to identify images. Menu bitmaps should be imported from Bitmap files.
The AboutIcon that is used in the Visual Studio About Box should be 32x32 pixels and should use standard Icon transparency (IconLover or the like can do this easily). AboutIcon data should be imported from an Icon not a bitmap.
Once you have your resources together, you need to compile this resource file into a Resource assembly dll. To do this you need ResGen.exe and AL.exe (Assembly Linker). I tend to create a batch file that does this for me, so I can just double click in Explorer to create my resource DLL and immediately copy it to my target directory:
cd %curdir%
@set PATH=D:\Programs\vs2005\Common7\IDE;D:\Programs\vs2005\VC\BIN;D:\Programs\vs2005\Common7\Tools;D:\Programs\vs2005\Common7\Tools\bin;D:\Programs\vs2005\VC\PlatformSDK\bin;D:\Programs\vs2005\SDK\v2.0\bin;D:\WINDOWS\Microsoft.NET\Framework\v2.0.50727;D:\Programs\vs2005\VC\VCPackages;%PATH%
Resgen.exe ImageResources.resx
AL.exe /embed:ImageResources.resources /culture:en-US /out:HelpBuilderVs2005Addin.resources.dll
copy HelpBuilderVs2005Addin.resources.dll d:\installs\wwhelp\vsAddin\en-us
pause
Note that your paths may be different. You also need to make sure you have the .NET Framework Sdk 2.0 installed to find ResGen and the Assembly Linker.
Most of this is to get all the paths for the required SDKs that hold the resource compilers. You need to ResGen create a resource binary meta file and the Assembly Linker (AL) to embed the resource into a DLL assembly. Finally I like to copy the DLL into my actual add-in directory’s resource directory.
In order to use a satellite resource DLL with your Add-in you need to create a resource directory below your add-in DLLs directory. The directory needs to be named with the locale ID (en-us, de-de,fr-CA etc.) and copy the resource assembly into it. Note that you have to create directories for each locale you will end up using – there’s no way that I could see to use a culture neutral directory.
So:
AddInDllDirectory
\en-us
\de-de
\fr-fr
Each of those resource directories requires your Resource DLL. Usually you’ll install the right DLL into the right directory when you install your Add-in.
You now have a resource DLL that you can access from menu buttons and for your about icon.
Hooking up Command Button Images
Command button images are hooked up with one of the various Command addition options. Here’s an example using AddNamedCommand:
command = ((Commands2)this.DTE.Commands).AddNamedCommand(
this.addInInstance,
CommandName, Caption, Description,
true, 101,
ref contextGUIDS,
(int)vsCommandStatus.vsCommandStatusSupported +
(int)vsCommandStatus.vsCommandStatusEnabled);
Note the true and 101 parameters which state that an icon resource is to be used and the id is 101. The value is an integer, which is why you want to create menu bitmaps with numeric ids in the resource file. Make sure you use values over 100 or even higher to make sure you don’t conflict with an existing icon that VS.NET natively provides.
Because creating command bar buttons is a pain, I tend to use wrapper methods to add command bars and buttons and add them to the command bars:
public AddCommandReturn AddCommand(string CommandName,string Caption, string Description,
int IconId,CommandBar commandBar, int InsertionIndex,
bool BeginGroup, string HotKey)
{
object []contextGUIDS = new object[] { };
// *** Check to see if the Command exists already to be safe
Command command = null;
try
{
command = this.DTE.Commands.Item(this.addInInstance.ProgID + "." + CommandName,-1);
}
catch {;}
// *** If not create it!
if (command == null)
{
command = ((Commands2)this.DTE.Commands).AddNamedCommand(
this.addInInstance,
CommandName, Caption, Description,
IconId == 0? true : false, IconId,
ref contextGUIDS,
(int)vsCommandStatus.vsCommandStatusSupported +
(int)vsCommandStatus.vsCommandStatusEnabled);
if (HotKey != null && HotKey != "")
{
object [] bindings = (object [])command.Bindings ;
if (bindings != null)
{
bindings = new object[1];
//bindings[0] = (object)"Windows Forms Designer::alt+f1";
bindings[0] = (object) HotKey;
try
{
command.Bindings = (object) bindings;
}
catch(Exception ex) { string t = ex.Message; }
}
}
}
CommandBarControl cb = (CommandBarControl) command.AddControl(commandBar,InsertionIndex);
cb.BeginGroup = BeginGroup;
return new AddCommandReturn(command,cb);
}
/// <summary>
/// Creates a new CommandBar Control object ready to add new items to
/// </summary>
/// <param name="ParentCommandBar"></param>
/// <param name="InsertionIndex"></param>
/// <param name="Caption"></param>
/// <param name="BeginGroup"></param>
/// <returns></returns>
public CommandBarControl AddCommandBar(CommandBar ParentCommandBar, string Caption, int InsertionIndex, bool BeginGroup)
{
CommandBarControl CommandBar =
ParentCommandBar.Controls.Add(
MsoControlType.msoControlPopup,
System.Type.Missing, System.Type.Missing,
InsertionIndex, true);
CommandBarPopup commandBarPopup = (CommandBarPopup) CommandBar;
commandBarPopup.Caption = Caption;
commandBarPopup.BeginGroup = true;
commandBarPopup.Visible = true;
return CommandBar;
}
This can then be used to create a new command bar and add buttons to it with code like this:
// *** Retrieve our Main Menu bar to attach menu to
CommandBars commandBars = this.applicationObject.CommandBars as CommandBars;
CommandBar commandBarHelp = (CommandBar)commandBars[this.GetLocalizedString("Help")];
// *** This code is easier but creates a 'permanent' menu bar that needs to be removed
CommandBarPopup HelpMenuPopup = this.AddInHelper.AddCommandBar(toolsPopup.CommandBar,
AddinVs2005.WWHELP_APPNAME,
this.AddInHelper.GetIndexOfCommandBarControl(toolsPopup.CommandBar,
AddinVs2005.WWHELP_HELPMENU_PREVITEM_ID) + 1,
true) as CommandBarPopup;
int HelpPopupInsertionIndex = 1;
// *** Add button to menu
this.AddInHelper.AddCommand("ShowHelpBuilder", "Show Html Help Builder",
"Brings up Html Help Builder for the current control", 101,
HelpMenuPopup.CommandBar, HelpPopupInsertionIndex, true, "Global::alt+f1");
HelpPopupInsertionIndex++;
// *** Add another button
this.AddInHelper.AddCommand("UpdateComment", "Update Xml Comment ",
"Updates XML Comment from Html Help Builder Topic", 101,
HelpMenuPopup.CommandBar, HelpPopupInsertionIndex, true, "Global::ctrl+f1");
A typical Add-in will add options to a number of command bars, so once you’ve created a Command you won’t want to re-create the command again. Instead you’ll want to simply add the first one with the full command, then use the Command’s AddControl method to add the command to another toolbar:
// *** Add to the Context Menu for Code - same as above but no Hotkeys
AddCommandReturn Return = this.AddInHelper.AddCommand(
"ShowHelpBuilderContext", "Show Html Help Builder",
"Brings up Html Help Builder in the current control",
101, SelectionPopup.CommandBar, SelectionIndex, true, null);
Command command = Return.Command;
// *** Add same command to another menu – WinForm Container Context in this case
CommandBarControl cb = null;
cb = command.AddControl(ContainerPopup.CommandBar, ContainerInsertionIndex) as CommandBarControl;
cb.BeginGroup = true;
// *** And to another – CodeWindow Popup
cb = command.AddControl(CodeWindowPopup.CommandBar, CodeWindowInsertionIndex) as CommandBarControl;
cb.BeginGroup = true;
With these helpers adding command buttons gets manageable, but it still gets pretty hectic especially if you attach commands to multiple menus.
Registering your Add-in
Once you’ve built your Add-in you need to register it. This is one place where things get a lot easier in Visual Studio 2005! To register your addin all you need to do is place a .Addin file in the Visual Studio Add-ins directory in My Documents folder:
D:\Documents and Settings\ricks\My Documents\Visual Studio 2005\Addins
The .Addin file contains information on the location and name of your add-in – everything VS needs to load and display information about your add-in. The .Addin file is an XML document and it looks something like this:
?xml version="1.0" encoding="UTF-16" standalone="no"?>
<Extensibility xmlns="http://schemas.microsoft.com/AutomationExtensibility">
<HostApplication>
<Name>Microsoft Visual Studio Macros</Name>
<Version>8.0</Version>
</HostApplication>
<HostApplication>
<Name>Microsoft Visual Studio</Name>
<Version>8.0</Version>
</HostApplication>
<Addin>
<FriendlyName>Html Help Builder VS.NET 2005 Add-in</FriendlyName>
<Description>Provides Html Help Builder integration to VS.NET 2005</Description>
<AboutBoxDetails>West Wind Html Html Help Builder Add-In\r\n(c) 2003-2006 West Wind Technologies</AboutBoxDetails>
<AboutIconData>@AboutIcon</AboutIconData>
<Assembly>D:\INSTALLS\WWHELP\vsAddin\HelpBuilderVs2005Addin.dll</Assembly>
<FullClassName>HelpBuilderAutomation.AddinVs2005</FullClassName>
<LoadBehavior>1</LoadBehavior>
<CommandPreload>1</CommandPreload>
<CommandLineSafe>0</CommandLineSafe>
</Addin>
</Extensibility>
The key elements are the AssemblyPath, which points your Add-in’s main Assembly and provides VS with a startup path for looking for related resources. The FullClassName is the name of your Add-in class that gets loaded when the Add-in is fired up.
As you see you can also add descriptive text and information that is displayed in the About box specifically the AboutBoxDetails and AboutIconData.
Getting the AboutIcon to work
An Add-in gets an About Icon in the Visual Studio About box once the Add-in is loaded. To do this you need to use a 32x32 icon. Here’s what this looks like loaded into Visual Studio’s About box:
![](http://www.west-wind.com/weblog/images/21/o_VsAboutLogo.png)
To get this to work first you need a 32x32 icon. If you already have a resource DLL associated with your project, the easiest thing to do is add the icon to the resource DLL. Note that you can can’t use the VS.NET resource editor to create properly named resources (as outlined by the MSDN article) – if you do you have to manually edit the resource XML file to change the name from a numeric ID. Therefore again I highly recommend you use a separate resource editor like Resourcer. With it you can name your icon – in the example above I named it AboutIcon.
As you can see in the .AddIn file you can reference the icon by its Icon name prefixed with a @. Note that value cannot start with a number and this is why the stock VS.NET Resource editor doesn’t work without manually editing the resx file.
<AboutIconData>@AboutIcon</AboutIconData>
This works great if you already use a resource dll. But if you don’t use custom icons or localized text in your menus then you probably don’t want to create a resource DLL just for the About icon. Instead, you can also embed a HexBinary representation of the raw Icon data.
How do you get a HexBinary representation? Here’s a small snippet that does the trick on a Win Form:
private void btnDir_Click(object sender, EventArgs e)
{
OpenFileDialog fd = new OpenFileDialog();
fd.CheckFileExists = true;
if (fd.ShowDialog() == DialogResult.OK)
{
this.txtFilename.Text = fd.FileName;
StringBuilder sb = new StringBuilder();
Stream sr = fd.OpenFile();
while (true)
{
int LastByte = sr.ReadByte();
if (LastByte == -1)
break;
sb.Append(LastByte.ToString("X2"));
}
this.txtResult.Text = sb.ToString();
Clipboard.SetData(DataFormats.Text, this.txtResult.Text);
}
}
You can download this simple utility from:
http://www.west-wind.com/files/tools/binarystringconverter.zip
The output that this thing generates can then be pasted directly into your AboutIconData element like this:
<AboutBoxDetails>For more information about West Wind Technologies, see the West Wind Technologies website at\r\nhttp://www.West Wind Technologies.com\r\n (c) 2005 West Wind Technologies Inc.</AboutBoxDetails>
<AboutIconData>0000010002002020100000000000E8020000260000001010100000000000280100000E0300002800000020000000400000000100040000000000000200000000000000000000000000000000000000000000000080000080000000808000800000008000800080800000C0C0C000808080000000FF0000FF000000FFFF00FF000000FF00FF00FFFF0000FFFFFF00FFFFFFFFFFFFFFFFFF88CCCCCC88FFFFFFFFFFFFFFFFFFFF8CCCCCCCCCCCC8FFFFFFFFFFFFFFFF8CCCCCCCCCCCCCCCCFFFFFFFFFFFFFF8CCCCCCCCCCCCCCCCCFFFFFFFFFFFFFFCCCCCCCCCCCCCCCCCCFFFFFFFFFFFFFCCCCCCCC8FFFFF88CCCFFFFFFFFFFFF8CCCCCCCFFFFFFFFF8CCFFFFFFFFFFFF8CCCCCCFFFFFFFFFFFFFFFFFFFFFFFFFCCCCCC8FFFFFFFFFFFFFFFFFFFFFFFFFCCCCCCFFFFFFFFFFFFFFFFFFFFFFFFF0CCCCCCFFFFFFFFFFFFFFFFFFFF9999980CCCCC99999F0FFFFFFFFFFFF99999990CCCCC089990FFFFFFFFFFFFF999999990CCCC889908FFFFFFFFFFFF79990999900CCC089809FFFFFF88FFFF9999980898C0CC0C8099FFF88CC8FFFF9999998008C00C880888CCCCCCC8FFFF999999999000080800008CCCCCC8FFF79999999999800000000008CCCCC8FFF79999999999800000077008CCCC8FFFF99999999999800000878000888FFFFFF99999999999800000780008FFFFFFFF7999999999998000080000097FFFFFFF7999999F99980888880888997FFFFFFF9999999F08099089890999999FFFFFFF999999880999089909F8099997FFFFF79999990FF998099908F9009997FFFFF7999999FFF990999990FF998099FFFFF9999999FFF80999999F0F999999FFFFF9999999FFF09999999FFF9999997FFFF9999997FFFF9999997FFF9999999FFFF9999997FFFF9999997FFF8999999FFFF00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002800000010000000200000000100040000000000C00000000000000000000000000000000000000000000000000080000080000000808000800000008000800080800000C0C0C000808080000000FF0000FF000000FFFF00FF000000FF00FF00FFFF0000FFFFFF00FFFFFFFF78CCC87FFFFFFF7CCCCCCCC7FFFFFFCCCC8778C7FFFFF7CCC7FFFF77FFFFF7CC8FFFFFFFFF7774CC5777FFFFFF99914C4818FFFFF79119444817F787F79991408888CCC7F899998000808CC7F9999980088087FF7999998888818FFF7999811811199FFF8991F919181117FF9997F199977998FF9997F7998F7999FF00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000</AboutIconData>
And there you have it. Once you know what to do it’s not too difficult to get this taken care of but the documentation for this process can’t be found in one place that I’ve seen so far. So I hope this helps resolve some of the issues around displaying custom icons and bitmaps in Visual Studio .NET 2005 Add-ins.