Author:
|
Carlos J. Quintero (Microsoft MVP)
|
Applies to:
|
Microsoft Visual Studio 2005
|
Date:
|
April 2008
|
|
Microsoft Visual Studio 2008
|
Updated:
|
March 2013 |
|
Microsoft Visual Studio 2010
|
|
|
|
Microsoft Visual Studio 2012
|
Introduction
This article explains how to create a setup for a Visual Studio add-in for all users
(requiring administrator privileges), not just for the current user, using
Inno Setup, a popular free installer for Windows.
Inno Setup is script-based (not MSI-based) but there is a companion ISTool script
editor that allows you to create the script for its different sections in a friendly
way setting properties in dialog windows. There is also an Inno Setup preprocessor
that can be used to create compilation constants, etc. Inno Setup and the companion
Inno Setup Quick Start Pack with the ISTool and preprocessor tools can be dowloaded
here. This article assumes that
you are somewhat familiar with Inno Setup.
More Information
The provided setup assumes an add-in that creates commands, buttons and commandbars using
a temporary user interface (see
the article HOWTO: Adding buttons, commandbars and
toolbars to Visual Studio .NET from an add-in), not a permanent user
interface (specifically, it does not use permanent commandbars). Some
considerations about the setup are the following:
- Since the add-in is installed for all users with administrator rights (PrivilegesRequired=admin in the sample):
- It is installed in the "C:\Program Files" folder ("C:\Program Files (x86)" on Windows 64-bit).
- It creates the links to the help file, to a web site and to the unsinstaller in the Start menu, Programs for all users.
- It uses XML registration through an .AddIn file that will be placed in the following
folders making the add-in available to all users (see the article
INFO: Default .AddIn file locations for Visual Studio add-ins):
- Windows XP:
- Visual Studio 2005: "C:\Documents and Settings\All Users\Application Data\Microsoft\VisualStudio\8.0\Addins"
- Visual Studio 2008: "C:\Documents and Settings\All Users\Application Data\Microsoft\VisualStudio\9.0\Addins"
- Visual Studio 2010: "C:\Documents and Settings\All Users\Application Data\Microsoft\VisualStudio\10.0\Addins"
- Visual Studio 2012: "C:\Documents and Settings\All Users\Application Data\Microsoft\VisualStudio\11.0\Addins"
- Windows Vista and higher:
- Visual Studio 2005: "C:\ProgramData\Microsoft\VisualStudio\8.0\Addins"
- Visual Studio 2008: "C:\ProgramData\Microsoft\VisualStudio\9.0\Addins"
- Visual Studio 2010: "C:\ProgramData\Microsoft\VisualStudio\10.0\Addins"
- Visual Studio 2012: "C:\ProgramData\Microsoft\VisualStudio\11.0\Addins"
- The add-in targets each Visual Studio version with a different DLL ("MyAddInVS2005.dll",
"MyAddInVS2008.dll", etc. in the sample), although you can use
a single 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.)
- The setup doesn't install a satellite DLL, but you can add it easily.
- The setup will prompt which Visual Studio versions of the add-in will be installed
through a list with checkboxes.
- A custom action will be used on uninstalling to remove the commands of the add-in,
using the devenv.exe /ResetAddin command of Visual Studio (see the article
HOWTO: Removing commands and UI elements during Visual Studio .NET add-in uninstallation).
- The add-in uses a help file ("MyAddIn.chm" in the sample) and a license agreement
file ("LicenseAgreement.rtf" in the sample), although you can change the script
file to remove them.
- The setup is multilanguage (the sample provided uses English and Spanish, although
you can add/remove languages easily). The setup uses the language of the Regional
Settings without prompting for selection (you can change it).
- The setup checks that Visual Studio is installed (Visual Studio Express editions
don't qualify since they don't support add-ins).
- The setup checks that Visual Studio is not running before installing or uninstalling.
The Inno Setup script is the below. It assumes that all the source files (.dll,
.chm, .rtf) and the own script (.iss) are in the same folder, and the output (.exe)
will be created in a "Setup" subfolder.
Language: Text | Copy Code (IE only) |
; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
; To add a localization:
; 1) Add the language to the [Languages] section
; 2) Add localizations to the [Messages] section
; 3) Add localizations to the [CustomMessages] section
; 4) Add License Agreement localization
; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
[Setup]
; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
; BEGIN CUSTOMIZATION SECTION: Customize these constants
; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
; Ensure that you use YOUR OWN APP_ID; DO NOT REUSE THIS ONE
#define APP_ID "{{58534807-17E7-4254-8E2A-AAA932A00320}"
#define APP_NAME "My Add-In"
#define APP_VERSION "1.0.0.0"
#define DEFAULT_GROUP_NAME "{cm:MyAddInDefaultGroupName}"
#define HELP_SHORTCUT "{cm:MyAddInHelpFile}"
#define DEST_SUB_DIR "MyAddIn"
#define HELP_FILE_NAME "MyAddIn.chm"
#define LICENSE_AGREEMENT_FILE_NAME "LicenseAgreement.rtf"
#define DLL_FILE_NAME_VS2005 "MyAddInVS2005.dll"
#define DLL_FILE_NAME_VS2008 "MyAddInVS2008.dll"
#define DLL_FILE_NAME_VS2010 "MyAddInVS2010.dll"
#define DLL_FILE_NAME_VS2012 "MyAddInVS2012.dll"
#define ADDIN_XML_FILE_NAME "MyAddIn.AddIn"
#define MY_COMPANY_NAME "My Company"
#define MY_COMPANY_WEB_SITE "www.mycompany.com"
#define OUTPUT_FOLDER_NAME ".\Setup"
#define OUTPUT_BASE_FILE_NAME "MyAddInSetup"
#define COPYRIGHT_YEAR "2008"
; Ensure that these values are used for the Connect class
#define CONNECT_CLASS_FULL_NAME_VS_2005 "MyAddInVS2005.Connect"
#define CONNECT_CLASS_FULL_NAME_VS_2008 "MyAddInVS2008.Connect"
#define CONNECT_CLASS_FULL_NAME_VS_2010 "MyAddInVS2010.Connect"
#define CONNECT_CLASS_FULL_NAME_VS_2012 "MyAddInVS2012.Connect"
; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
; END CUSTOMIZATION SECTION
; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
OutputDir={#OUTPUT_FOLDER_NAME}
OutputBaseFilename={#OUTPUT_BASE_FILE_NAME}
AppID={#APP_ID}
VersionInfoVersion={#APP_VERSION}
PrivilegesRequired=admin
MinVersion=0,5.0.2195
AppName={#APP_NAME}
AppVerName={#APP_NAME}
DefaultGroupName={#DEFAULT_GROUP_NAME}
AppPublisher={#MY_COMPANY_NAME}
AppPublisherURL={#MY_COMPANY_WEB_SITE}
AppSupportURL={#MY_COMPANY_WEB_SITE}
AppUpdatesURL={#MY_COMPANY_WEB_SITE}
DefaultDirName={pf}\{#DEST_SUB_DIR}
LicenseFile={#LICENSE_AGREEMENT_FILE_NAME}
Compression=lzma
SolidCompression=true
DisableReadyPage=true
ShowLanguageDialog=no
UninstallLogMode=append
DisableProgramGroupPage=true
VersionInfoCompany={#MY_COMPANY_NAME}
AppCopyright=Copyright © {#COPYRIGHT_YEAR} {#MY_COMPANY_NAME}
AlwaysUsePersonalGroup=false
InternalCompressLevel=ultra
AllowNoIcons=true
LanguageDetectionMethod=locale
DisableDirPage=true
[Languages]
; USE ENGLISH AS THE FIRST LANGUAGE!!!
Name: English; MessagesFile: compiler:Default.isl
Name: Spanish; MessagesFile: compiler:Languages\Spanish.isl
[Types]
Name: Custom; Description: Custom; Flags: iscustom
[Files]
Source: {#DLL_FILE_NAME_VS2005}; DestDir: {app}; Flags:ignoreversion; AfterInstall:CreateAddInXMLFileVS2005(); Check: IsMyAddInVS2005Selected()
Source: {#DLL_FILE_NAME_VS2008}; DestDir: {app}; Flags:ignoreversion; AfterInstall:CreateAddInXMLFileVS2008(); Check: IsMyAddInVS2008Selected()
Source: {#DLL_FILE_NAME_VS2010}; DestDir: {app}; Flags:ignoreversion; AfterInstall:CreateAddInXMLFileVS2010(); Check: IsMyAddInVS2010Selected()
Source: {#DLL_FILE_NAME_VS2012}; DestDir: {app}; Flags:ignoreversion; AfterInstall:CreateAddInXMLFileVS2012(); Check: IsMyAddInVS2012Selected()
Source: {#HELP_FILE_NAME}; DestDir: {app}; Flags: ignoreversion
[Icons]
Name: {group}\{cm:MyAddInWebSite}; Filename: http://www.mycompany.com
Name: {group}\{cm:UninstallProgram,{#APP_NAME}}; Filename: {uninstallexe}
Name: {group}\{#HELP_SHORTCUT}; Filename: {app}\{#HELP_FILE_NAME}; IconFilename: {app}\{#HELP_FILE_NAME}
[Messages]
English.FinishedLabel=Setup has finished installing [name] on your computer.
Spanish.FinishedLabel=La instalación de [name] en su ordenador ha finalizado.
English.UninstallStatusLabel=The uninstallation can take up to a minute while the commands of the add-in are removed from Visual Studio.
Spanish.UninstallStatusLabel=La desinstalación puede tardar hasta un minuto mientras los comandos del complemento se eliminan de Visual Studio.
[CustomMessages]
English.MyAddInWebSite={{#APP_NAME} Web Site
Spanish.MyAddInWebSite=Sitio web de {#APP_NAME}
English.VSNeedsToBeClosed=Close Visual Studio before continuing.
Spanish.VSNeedsToBeClosed=Cierre Visual Studio antes de continuar.
English.RegisteringAddIn=Registering the add-in...
Spanish.RegisteringAddIn=Registrando el complemento...
English.MyAddInDefaultGroupName={#APP_NAME} for Visual Studio
Spanish.MyAddInDefaultGroupName={#APP_NAME} para Visual Studio
English.MyAddInHelpFile={#APP_NAME} Help
Spanish.MyAddInHelpFile=Ayuda de {#APP_NAME} (en inglés)
English.VSRequired=Visual Studio Standard Edition or higher is required to install this product.
Spanish.VSRequired=Se requiere Visual Studio Standard Edition o superior para instalar este producto.
[Code]
var
IDEsPage: TWizardPage;
IDEsCheckListBox: TNewCheckListBox;
(***************************************************************************************************)
(* Auxiliar function *)
(***************************************************************************************************)
function GetXMLAddInFullFolderName(Param: String): String;
var
sCommonAppDataFolder: String;
sResult: String;
begin
sCommonAppDataFolder := ExpandConstant('{commonappdata}');
(* Param is the version such as '8.0' or '9.0' *)
sResult := sCommonAppDataFolder + '\Microsoft\VisualStudio\' + Param + '\Addins\'
Result := sResult;
end;
(***************************************************************************************************)
(* Auxiliar function *)
(***************************************************************************************************)
procedure CreateAddInXMLFile(sVersion: String; sConnectClassFullName: String; sDLLFileName: String);
var
sLines: TArrayOfString;
sXMLAddInFullFileName: String;
sAddInDLLFullFileName: String;
sInstallationFolder: String;
sFolder: String;
begin
(* Compose the full name of the add-in DLL *)
sInstallationFolder := ExpandConstant('{app}');
sAddInDLLFullFileName := sInstallationFolder + '\' + sDLLFileName;
(* Get the folder where to put the .AddIn XML registration file *)
sFolder := GetXMLAddInFullFolderName(sVersion);
(* Ensure that the folder is created *)
CreateDir(sFolder);
(* Compose the full name of the .AddIn XML registration file *)
sXMLAddInFullFileName := sFolder + '{#ADDIN_XML_FILE_NAME}';
(* Create the .AddIn XML registration file *)
SetArrayLength(sLines,16);
sLines[0] := '<?xml version="1.0" encoding="windows-1252" standalone="no"?>';
sLines[1] := '<Extensibility xmlns="http://schemas.microsoft.com/AutomationExtensibility">';
sLines[2] := ' <HostApplication>';
sLines[3] := ' <Name>Microsoft Visual Studio</Name>';
sLines[4] := ' <Version>' + sVersion + '</Version>';
sLines[5] := ' </HostApplication>';
sLines[6] := ' <Addin>';
sLines[7] := ' <FriendlyName>{#APP_NAME}</FriendlyName>';
sLines[8] := ' <Description>{#APP_NAME}</Description>';
sLines[9] := ' <Assembly>' + sAddInDLLFullFileName + '</Assembly>';
sLines[10] := ' <FullClassName>' + sConnectClassFullName + '</FullClassName>';
sLines[11] := ' <LoadBehavior>3</LoadBehavior>';
sLines[12] := ' <CommandPreload>0</CommandPreload>';
sLines[13] := ' <CommandLineSafe>0</CommandLineSafe>';
sLines[14] := ' </Addin>';
sLines[15] := '</Extensibility>';
SaveStringsToFile(sXMLAddInFullFileName, sLines, False);
end;
(***************************************************************************************************)
(* Auxiliar function *)
(***************************************************************************************************)
procedure CreateAddInXMLFileVS2005();
begin
CreateAddInXMLFile('8.0','{#CONNECT_CLASS_FULL_NAME_VS_2005}', '{#DLL_FILE_NAME_VS2005}')
end ;
(***************************************************************************************************)
(* Auxiliar function *)
(***************************************************************************************************)
procedure CreateAddInXMLFileVS2008();
begin
CreateAddInXMLFile('9.0','{#CONNECT_CLASS_FULL_NAME_VS_2008}', '{#DLL_FILE_NAME_VS2008}')
end ;
(***************************************************************************************************)
(* Auxiliar function *)
(***************************************************************************************************)
procedure CreateAddInXMLFileVS2010();
begin
CreateAddInXMLFile('10.0','{#CONNECT_CLASS_FULL_NAME_VS_2010}', '{#DLL_FILE_NAME_VS2010}')
end ;
(***************************************************************************************************)
(* Auxiliar function *)
(***************************************************************************************************)
procedure CreateAddInXMLFileVS2012();
begin
CreateAddInXMLFile('11.0','{#CONNECT_CLASS_FULL_NAME_VS_2012}', '{#DLL_FILE_NAME_VS2012}')
end ;
(***************************************************************************************************)
(* Auxiliar function *)
(***************************************************************************************************)
procedure ShowCloseVisualStudioMessage();
var
sMsg: String;
begin
sMsg := CustomMessage('VSNeedsToBeClosed');
MsgBox(sMsg, mbCriticalError, mb_Ok)
end;
(***************************************************************************************************)
(* Auxiliar function *)
(***************************************************************************************************)
procedure ShowVisualStudioRequiredMessage();
var
sMsg: String;
begin
sMsg := CustomMessage('VSRequired');
MsgBox(sMsg, mbCriticalError, mb_Ok)
end;
(***************************************************************************************************)
(* Auxiliar function *)
(***************************************************************************************************)
function IsVSInstalledByProgID(ProgID: String):Boolean;
begin
if RegKeyExists(HKCR, ProgID) then
Result := True
else
Result := False
end;
(***************************************************************************************************)
(* Auxiliar function *)
(***************************************************************************************************)
function IsVSRunningByProgID(ProgID: String):Boolean;
var
IDE: Variant;
begin
try
IDE := GetActiveOleObject(ProgID);
except
end;
if VarIsEmpty(IDE) then
Result := False
else
Result := True
end;
(***************************************************************************************************)
(* Auxiliar function *)
(***************************************************************************************************)
function IsVS2005Installed():Boolean;
begin
if IsVSInstalledByProgID('VisualStudio.DTE.8.0') then
Result := True
else
Result := False
end;
(***************************************************************************************************)
(* Auxiliar function *)
(***************************************************************************************************)
function IsVS2008Installed():Boolean;
begin
if IsVSInstalledByProgID('VisualStudio.DTE.9.0') then
Result := True
else
Result := False
end;
(***************************************************************************************************)
(* Auxiliar function *)
(***************************************************************************************************)
function IsVS2010Installed():Boolean;
begin
if IsVSInstalledByProgID('VisualStudio.DTE.10.0') then
Result := True
else
Result := False
end;
(***************************************************************************************************)
(* Auxiliar function *)
(***************************************************************************************************)
function IsVS2012Installed():Boolean;
begin
if IsVSInstalledByProgID('VisualStudio.DTE.11.0') then
Result := True
else
Result := False
end;
(***************************************************************************************************)
(* Auxiliar function *)
(***************************************************************************************************)
function IsVSInstalled():Boolean;
begin
if IsVS2005Installed() then
Result := True
else if IsVS2008Installed() then
Result := True
else if IsVS2010Installed() then
Result := True
else if IsVS2012Installed() then
Result := True
else
begin
Result := False
ShowVisualStudioRequiredMessage();
end
end;
(***************************************************************************************************)
(* Auxiliar function *)
(***************************************************************************************************)
function IsSomeVSRunning():Boolean;
begin
if IsVSRunningByProgID('VisualStudio.DTE.8.0') then
Result := True
else if IsVSRunningByProgID('VisualStudio.DTE.9.0') then
Result := True
else if IsVSRunningByProgID('VisualStudio.DTE.10.0') then
Result := True
else if IsVSRunningByProgID('VisualStudio.DTE.11.0') then
Result := True
else
Result := False
end;
(***************************************************************************************************)
(* Auxiliar function *)
(***************************************************************************************************)
procedure UnregisterAddInIfInstalled(sVersion: String; sConnectClassFullName: String);
var
sXMLAddInFullFileName: String;
sFolder: String;
sIDEFullFileName: String;
iResultCode: Integer;
begin
(* Compose the full name of the .AddIn XML registration file *)
sFolder := GetXMLAddInFullFolderName(sVersion);
sXMLAddInFullFileName := sFolder + '{#ADDIN_XML_FILE_NAME}';
if FileExists(sXMLAddInFullFileName) then
begin
(* Delete the .AddIn XML registration file to unregister the add-in *)
DeleteFile(sXMLAddInFullFileName);
(* Compose the full name of the Visual Studio IDE *)
sIDEFullFileName := ExpandConstant('{reg:HKLM\SOFTWARE\Microsoft\VisualStudio\' + sVersion + ',InstallDir}') + 'devenv.exe'
(* Remove the commands of the IDE *)
Exec(sIDEFullFileName, '/ResetAddin ' + sConnectClassFullName + ' /command File.Exit', '',SW_HIDE,ewWaitUntilTerminated,iResultCode);
end
end;
(***************************************************************************************************)
(* Auxiliar function *)
(***************************************************************************************************)
function IsMyAddInVS2005Selected():Boolean;
begin
Result := IDEsCheckListBox.Checked[0]
end;
(***************************************************************************************************)
(* Auxiliar function *)
(***************************************************************************************************)
function IsMyAddInVS2008Selected():Boolean;
begin
Result := IDEsCheckListBox.Checked[1]
end;
(***************************************************************************************************)
(* Auxiliar function *)
(***************************************************************************************************)
function IsMyAddInVS2010Selected():Boolean;
begin
Result := IDEsCheckListBox.Checked[2]
end;
(***************************************************************************************************)
(* Auxiliar function *)
(***************************************************************************************************)
function IsMyAddInVS2012Selected():Boolean;
begin
Result := IDEsCheckListBox.Checked[3]
end;
(***************************************************************************************************)
(* InnoSetup event function *)
(***************************************************************************************************)
function InitializeSetup(): Boolean;
begin
if not IsVSInstalled() then
begin
Result := False;
end
else
begin
if IsSomeVSRunning() then
begin
ShowCloseVisualStudioMessage()
Result := False;
end
else
Result := True;
end
end;
(***************************************************************************************************)
(* InnoSetup event function *)
(***************************************************************************************************)
procedure InitializeWizard();
var
IDEsLabel: TLabel;
bIsVS2005Installed: Boolean;
bIsVS2008Installed: Boolean;
bIsVS2010Installed: Boolean;
bIsVS2012Installed: Boolean;
begin IDEsPage := CreateCustomPage(wpSelectComponents, SetupMessage(msgWizardSelectComponents), SetupMessage(msgSelectComponentsDesc));
IDEsLabel := TLabel.Create(IDEsPage);
IDEsLabel.Caption := SetupMessage(msgSelectComponentsLabel2);
IDEsLabel.Width := IDEsPage.SurfaceWidth;
IDEsLabel.Height := ScaleY(40);
IDEsLabel.AutoSize := False;
IDEsLabel.WordWrap := True;
IDEsLabel.Parent := IDEsPage.Surface;
IDEsCheckListBox := TNewCheckListBox.Create(IDEsPage);
IDEsCheckListBox.Top := IDEsLabel.Top + IDEsLabel.Height + ScaleY(8);
IDEsCheckListBox.Width := IDEsPage.SurfaceWidth;
IDEsCheckListBox.Height := ScaleX(100);
IDEsCheckListBox.Flat := True;
IDEsCheckListBox.Parent := IDEsPage.Surface;
bIsVS2005Installed := IsVS2005Installed();
bIsVS2008Installed := IsVS2008Installed();
bIsVS2010Installed := IsVS2010Installed();
bIsVS2012Installed := IsVS2012Installed();
IDEsCheckListBox.AddCheckBox('{#APP_NAME} - Visual Studio 2005', '', 0, bIsVS2005Installed, bIsVS2005Installed, False, True, nil)
IDEsCheckListBox.AddCheckBox('{#APP_NAME} - Visual Studio 2008', '', 0, bIsVS2008Installed, bIsVS2008Installed, False, True, nil)
IDEsCheckListBox.AddCheckBox('{#APP_NAME} - Visual Studio 2010', '', 0, bIsVS2010Installed, bIsVS2010Installed, False, True, nil)
IDEsCheckListBox.AddCheckBox('{#APP_NAME} - Visual Studio 2012', '', 0, bIsVS2012Installed, bIsVS2012Installed, False, True, nil)
end;
(***************************************************************************************************)
(* InnoSetup event function *)
(***************************************************************************************************)
function InitializeUninstall(): Boolean;
begin
if IsSomeVSRunning() then
begin
ShowCloseVisualStudioMessage()
Result := False;
end
else
begin
Result := True;
end
end;
(***************************************************************************************************)
(* InnoSetup event function *)
(***************************************************************************************************)
procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
begin
if CurUninstallStep = usUninstall then
begin
UnregisterAddInIfInstalled('8.0', '{#CONNECT_CLASS_FULL_NAME_VS_2005}' );
UnregisterAddInIfInstalled('9.0', '{#CONNECT_CLASS_FULL_NAME_VS_2008}' );
UnregisterAddInIfInstalled('10.0', '{#CONNECT_CLASS_FULL_NAME_VS_2010}' );
UnregisterAddInIfInstalled('11.0', '{#CONNECT_CLASS_FULL_NAME_VS_2012}' );
end;
end;
Related articles
Go to the 'Visual Studio Extensibility (VSX)' web site for more articles like this (Articles section)
|