![]() |
![]() |
|||
![]() |
||||
![]() |
![]() |
This article explains how to create a setup for an add-in for the VBA editor of Microsoft Office (32-bit or 64-bit) for the current user (not requiring administrator privileges) 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 informationThe setup sample assumes that you have generated the following input files:
To install the add-in for the current user (not for all users), not requiring admin rights, the setup does the following:
The setup runs as a 32-bit application on Windows 32-bit, and as a 64-bit application on Windows 64-bit, so it can register the add-in dll as 64-bit COM component on Windows 64-bit, which is required for Microsoft Office 2010 64-bit (it doesn't support 32-bit COM add-ins). The setup doesn't include the installation of the .NET Framework 2.0. If detects if it is installed, and if not it opens a web page where the user can download it. You will need to adjust the value of constants used at the start of the script (customization section).
[Setup] ; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ; BEGIN CUSTOMIZATION SECTION: Customize these constants ; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ; Ensure that you use YOUR OWN APP_ID; DO NOT REUSE THIS ONE #define APP_ID "{{4EC56406-AD74-492e-8BB4-56E854BE1A1E}" #define APP_NAME "My VBA Add-in 1.0" #define DEST_SUB_DIR "MyVBAAddin" #define CONNECT_CLASS_FULL_NAME "MyVBAAddin.Connect" #define COMPANY_NAME "My Company" #define RUNTIME_VERSION "v2.0.50727" #define COPYRIGHT_YEAR "2012" #define INTEROP_OFFICE_FILE_NAME "MyCompany.Interop.Office12.dll" #define INTEROP_STDOLE_FILE_NAME "MyCompany.Interop.Stdole.dll" #define INTEROP_EXTENSIBILITY_FILE_NAME "MyCompany.Interop.Extensibility.dll" #define INTEROP_VBA_EXTENSIBILITY_FILE_NAME "MyCompany.Interop.VBAExtensibility.dll" #define DLL_FILE_NAME "MyVBAAddin.dll" #define OUTPUT_FOLDER_NAME ".\Setup" #define SETUP_FILE_NAME "MyVBAAddinSetup" #define VERSION "1.0.0.00" #define CONNECT_PROGID "MyVBAAddin.Connect" #define CONNECT_CLSID "{2CA1C920-2D5E-3E17-86D7-24DEB9303A3E}" #define ASSEMBLY_FULL_NAME "MyVBAAddin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=869cad219d7a35e2" ; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ; END CUSTOMIZATION SECTION ; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ArchitecturesAllowed=x86 x64 ; The setup must run in 64-bit mode on x64 systems to allow the installation of the VBA add-in for Office 2010 64-bit ArchitecturesInstallIn64BitMode=x64 AppID={#APP_ID} VersionInfoVersion={#VERSION} OutputBaseFilename={#SETUP_FILE_NAME} OutputDir={#OUTPUT_FOLDER_NAME} PrivilegesRequired=lowest MinVersion=0,5.0.2195 AppName={#APP_NAME} AppVerName={#APP_NAME} DefaultGroupName={#APP_NAME} AppPublisher={#COMPANY_NAME} DefaultDirName={localappdata}\{#COMPANY_NAME}\{#DEST_SUB_DIR} Compression=lzma/Max SolidCompression=true DisableReadyPage=true ShowLanguageDialog=no UninstallLogMode=append DisableProgramGroupPage=true VersionInfoCompany={#COMPANY_NAME} AppCopyright=Copyright © {#COPYRIGHT_YEAR} {#COMPANY_NAME} AlwaysUsePersonalGroup=true InternalCompressLevel=Ultra AllowNoIcons=true DisableDirPage=true LanguageDetectionMethod=locale [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: bin\release\{#INTEROP_OFFICE_FILE_NAME}; DestDir: {app}; Flags: ignoreversion; Source: bin\release\{#INTEROP_STDOLE_FILE_NAME}; DestDir: {app}; Flags: ignoreversion; Source: bin\release\{#INTEROP_VBA_EXTENSIBILITY_FILE_NAME}; DestDir: {app}; Flags: ignoreversion; Source: bin\release\{#INTEROP_EXTENSIBILITY_FILE_NAME}; DestDir: {app}; Flags: ignoreversion; Source: bin\release\{#DLL_FILE_NAME}; DestDir: {app}; Flags: ignoreversion; AfterInstall: RegisterAddin() [UninstallDelete] Name: {app}; Type: filesandordirs [CustomMessages] English.NETFramework20NotInstalled=Microsoft .NET Framework 2.0 installation was not detected. Spanish.NETFramework20NotInstalled=No se encontró la instalación de Microsoft .NET Framework 2.0. [Run] [UninstallRun] [Code] var m_IDEsPage: TWizardPage; m_IDEsCheckListBox: TNewCheckListBox; function IsVBA64Installed(): Boolean; var sBitness: String; begin Result := False if IsWin64() then begin if RegQueryStringValue(HKLM, 'Software\Microsoft\Office\14.0\Outlook', 'Bitness', sBitness) then begin if sBitness = 'x64' then begin Result := True end; end; end; end; function IsVBA32Installed(): Boolean; begin if RegKeyExists(HKLM32, 'SOFTWARE\Microsoft\Office\10.0') then Result := True else if RegKeyExists(HKLM32, 'SOFTWARE\Microsoft\Office\11.0') then Result := True else if RegKeyExists(HKLM32, 'SOFTWARE\Microsoft\Office\12.0') then Result := True else if RegKeyExists(HKLM32, 'SOFTWARE\Microsoft\Office\14.0') then Result := not IsVBA64Installed() else Result := False end; function IsVBA32Selected(): Boolean; begin Result := m_IDEsCheckListBox.Checked[0] end; function IsVBA64Selected(): Boolean; begin Result := m_IDEsCheckListBox.Checked[1] end; procedure IDEsCheckListBoxOnClickCheck(Sender: TObject); var index: Integer; begin WizardForm.NextButton.Enabled := False; for index := 0 to m_IDEsCheckListBox.Items.Count - 1 do begin if m_IDEsCheckListBox.Checked[index] then WizardForm.NextButton.Enabled := True; end; end; //*************************************************************************************************** // InnoSetup event function //*************************************************************************************************** function InitializeSetup(): Boolean; var iErrorCode: Integer; begin // Detect if Microsoft .NET Framework 2.0 is installed if Not RegKeyExists(HKLM, 'SOFTWARE\Microsoft\.NETFramework\v2.0.50727') then begin MsgBox(ExpandConstant('{cm:NETFramework20NotInstalled}'), mbCriticalError, mb_Ok); ShellExec('open', 'http://msdn.microsoft.com/en-us/netframework/aa731542', '', '', SW_SHOW, ewNoWait, iErrorCode) Result := False; end else begin Result := True; end end; procedure CreateComponentsPage(); var IDEsLabel: TLabel; bVBA32Enabled: Boolean; bVBA64Enabled: Boolean; bVBA32Checked: Boolean; bVBA64Checked: Boolean; begin m_IDEsPage := CreateCustomPage(wpSelectComponents, SetupMessage(msgWizardSelectComponents), SetupMessage(msgSelectComponentsDesc)); IDEsLabel := TLabel.Create(m_IDEsPage); IDEsLabel.Caption := SetupMessage(msgSelectComponentsLabel2); IDEsLabel.Width := m_IDEsPage.SurfaceWidth; IDEsLabel.Height := ScaleY(40); IDEsLabel.AutoSize := False; IDEsLabel.WordWrap := True; IDEsLabel.Parent := m_IDEsPage.Surface; m_IDEsCheckListBox := TNewCheckListBox.Create(m_IDEsPage); m_IDEsCheckListBox.Top := IDEsLabel.Top + IDEsLabel.Height + ScaleY(8); m_IDEsCheckListBox.Width := m_IDEsPage.SurfaceWidth; m_IDEsCheckListBox.Height := ScaleX(100); m_IDEsCheckListBox.Flat := True; m_IDEsCheckListBox.Parent := m_IDEsPage.Surface; m_IDEsCheckListBox.OnClickCheck := @IDEsCheckListBoxOnClickCheck; bVBA32Enabled := IsVBA32Installed(); bVBA64Enabled := IsVBA64Installed(); bVBA32Checked := bVBA32Enabled; bVBA64Checked := bVBA64Enabled; m_IDEsCheckListBox.AddCheckBox('My VBA Add-in - VBA Editor 32-bit (Office 2003/2007/2010 32-bit)', '', 0, bVBA32Checked, bVBA32Enabled, False, True, nil) m_IDEsCheckListBox.AddCheckBox('My VBA Add-in - VBA Editor 64-bit (Office 2010 64-bit)', '', 0, bVBA64Checked, bVBA64Enabled, False, True, nil) end; //*************************************************************************************************** // InnoSetup event function //*************************************************************************************************** procedure InitializeWizard(); begin CreateComponentsPage(); end; procedure RegisterCOMClass(const iRootKey: Integer; const sProgID: String; const sCLSID: String; const sClass: String; const sFileName: String; const sAssemblyFullName: String); var sCodeBase: String; sFileFullName: String; begin sFileFullName := ExpandConstant('{app}') + '\' + sFileName; StringChangeEx(sFileFullName, '\', '/', True); sCodeBase := 'file:///' + sFileFullName; RegWriteStringValue(iRootKey, 'Software\Classes\' + sProgID, '', sProgID); RegWriteStringValue(iRootKey, 'Software\Classes\' + sProgID + '\CLSID', '', sCLSID); RegWriteStringValue(iRootKey, 'Software\Classes\CLSID\' + sCLSID, '', sClass); (* Do not add this implemented category that identifies .NET components: RegWriteStringValue(iRootKey, 'Software\Classes\CLSID\' + sCLSID + '\Implemented Categories', '', ''); RegWriteStringValue(iRootKey, 'Software\Classes\CLSID\' + sCLSID + '\Implemented Categories\{62C8FE65-4EBB-45e7-B440-6E39B2CDBF29}', '', ''); because it is not absolutely needed and creating it could cause the following error: FIX: "Access to the Registry Key Denied" Error Message When You Register .NET Assembly for COM Interop http://support.microsoft.com/kb/327507 *) RegWriteStringValue(iRootKey, 'Software\Classes\CLSID\' + sCLSID + '\InprocServer32', '', 'mscoree.dll'); RegWriteStringValue(iRootKey, 'Software\Classes\CLSID\' + sCLSID + '\InprocServer32', 'Assembly', sAssemblyFullName); RegWriteStringValue(iRootKey, 'Software\Classes\CLSID\' + sCLSID + '\InprocServer32', 'Class', sClass); RegWriteStringValue(iRootKey, 'Software\Classes\CLSID\' + sCLSID + '\InprocServer32', 'CodeBase', sCodeBase); RegWriteStringValue(iRootKey, 'Software\Classes\CLSID\' + sCLSID + '\InprocServer32', 'RuntimeVersion', '{#RUNTIME_VERSION}'); RegWriteStringValue(iRootKey, 'Software\Classes\CLSID\' + sCLSID + '\InprocServer32', 'ThreadingModel', 'Both'); RegWriteStringValue(iRootKey, 'Software\Classes\CLSID\' + sCLSID + '\ProgId', '', sProgID); end; procedure UnregisterCOMClass(const iRootKey: Integer; const sProgID: String; const sCLSID: String); begin if RegKeyExists(iRootKey, 'Software\Classes\' + sProgID) then begin RegDeleteKeyIncludingSubkeys(iRootKey, 'Software\Classes\' + sProgID); end; if RegKeyExists(iRootKey, 'Software\Classes\CLSID\' + sCLSID) then begin RegDeleteKeyIncludingSubkeys(iRootKey, 'Software\Classes\CLSID\' + sCLSID); end; end; procedure RegisterAddinForIDE(const iRootKey: Integer; const sAddinSubKey: String; const sProgIDConnect: String); begin RegWriteStringValue(iRootKey, sAddinSubKey + '\' + sProgIDConnect, 'FriendlyName', '{#APP_NAME}'); RegWriteStringValue(iRootKey, sAddinSubKey + '\' + sProgIDConnect, 'Description' , '{#APP_NAME}'); RegWriteDWordValue (iRootKey, sAddinSubKey + '\' + sProgIDConnect, 'LoadBehavior', 3); end; procedure UnregisterAddinForIDE(const iRootKey: Integer; const sAddinSubKey: String; const sProgIDConnect: String); begin if RegKeyExists(iRootKey, sAddinSubKey + '\' + sProgIDConnect) then begin RegDeleteKeyIncludingSubkeys(iRootKey, sAddinSubKey + '\' + sProgIDConnect); end; end; procedure RegisterAddinForCOM(const iRootKey: Integer; const sProgIDConnect: String; const sCLSIDConnect: String; const sConnectClassFullName: String; const sFileName: String; const sAssemblyFullName: String); begin RegisterCOMClass(iRootKey, sProgIDConnect, sCLSIDConnect, sConnectClassFullName, sFileName, sAssemblyFullName); // Here you would register toolwindow usercontrols if required end; procedure UnregisterAddinForCOM(const iRootKey: Integer; const sProgIDConnect: String; const sCLSIDConnect: String); begin UnregisterCOMClass(iRootKey, sProgIDConnect, sCLSIDConnect); // Here you would unregister toolwindow usercontrols if required end; procedure RegisterAddin(); begin if IsVBA32Selected() then begin RegisterAddinForCOM(HKCU32, '{#CONNECT_PROGID}', '{#CONNECT_CLSID}', '{#CONNECT_CLASS_FULL_NAME}', '{#DLL_FILE_NAME}', '{#ASSEMBLY_FULL_NAME}'); RegisterAddinForIDE(HKCU32, 'Software\Microsoft\VBA\VBE\6.0\Addins', '{#CONNECT_PROGID}'); end; if IsVBA64Selected() then begin RegisterAddinForCOM(HKCU64, '{#CONNECT_PROGID}', '{#CONNECT_CLSID}', '{#CONNECT_CLASS_FULL_NAME}', '{#DLL_FILE_NAME}', '{#ASSEMBLY_FULL_NAME}'); RegisterAddinForIDE(HKCU64, 'Software\Microsoft\VBA\VBE\6.0\Addins64', '{#CONNECT_PROGID}'); end; end; procedure UnregisterAddin(); begin UnregisterAddinForIDE(HKCU32, 'Software\Microsoft\VBA\VBE\6.0\Addins', '{#CONNECT_PROGID}'); UnregisterAddinForCOM(HKCU32, '{#CONNECT_PROGID}', '{#CONNECT_CLSID}'); if IsWin64() then begin UnregisterAddinForIDE(HKCU64, 'Software\Microsoft\VBA\VBE\6.0\Addins64', '{#CONNECT_PROGID}'); UnregisterAddinForCOM(HKCU64, '{#CONNECT_PROGID}', '{#CONNECT_CLSID}'); end end; //*************************************************************************************************** // InnoSetup event function //*************************************************************************************************** procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep); begin if CurUninstallStep = usUninstall then begin UnregisterAddin() end; end; Related articles
|
Copyright © 2000-2021 MZTools Software. All Rights Reserved. Legal Notice |