I made this script a while ago because I was having trouble with the installers and fixes I found around the internet. It incorporates all the tricks and tips that I found around the internet into one powershell script.
To run the script you will need to following:
Each of the above should be placed in its own folder. If you save (and then dot-source) the script in a folder that contains these three folders it'll auto-detect everything for you. You can also set your current location in powershell to this folder and copy the script directly to you session and it'll detect everything as well.
Once the script is dot-sourced or pasted in an elevated powershell instance you can run it by calling Install-VB6
.
It also has the following parameters if you want to override any default behaviour:
Parameter |
Type |
Usage |
Vb6InstallerPath |
String |
Path of main VB6 Installer folder |
SP6InstallerPath |
String |
Path of Service Pack 6 Installer folder |
SkipMouseWheel |
Switch |
Skip installing the Mouse Wheel Fixer folder |
MouseWheelPath |
String |
Path of Mouse Wheel fixer. Ignored if -SkipMouseWheel is specified |
Regsvr32Path |
String |
Path to regsvr32.exe. Uses '%SYSTEMROOT%\SysWoW64\regsvr32.exe' if not specified |
RegEditPath |
String |
Path to regedit.exe. '%SYSTEMROOT%\regedit.exe' if not specified |
OrganizationName |
String |
Sets the organization name in the VB6 installer |
Notes:
- The VB6 and SP6 installer don't like being run from a network drive, so the script will stop if it detects one of the install folders is not on a local drive.
- I've only tested this with VB6 Pro, not VB6 Enterprise.
- It doesn't install MSDN.
Install-VB6.ps1
#Requires -RunAsAdministrator
#Requires -Version 3
<#
.SYNOPSIS
Installs VB6 to the computer.
.DESCRIPTION
Installs VB6 ide with Service Pack 6 and (optional) Mouse Wheel Fix to the local computer.
.PARAMETER Vb6InstallerPath
The path to the VB6 installer folder.
.PARAMETER SP6InstallerPath
The path the the Service Pack 6 installer folder.
.PARAMETER SkipMouseWheel
Skip installing the Mouse Wheel fix.
.PARAMETER MouseWheelPath
The path the Mouse wheel fix folder.
.PARAMETER Regsvr32Path
The path to RegSvr32.exe
.PARAMETER OrganizationName
The organization name
.PARAMETER RegEditPath
The path to regedit.exe
#>
Function Install-VB6{
[CmdletBinding()]
param (
[Parameter(Mandatory=$false)]
[string]$Vb6InstallerPath,
[Parameter(Mandatory=$false)]
[string]$SP6InstallerPath,
[Parameter(Mandatory=$false)]
[switch]$SkipMouseWheel,
[Parameter(Mandatory=$false)]
[string]$MouseWheelPath,
[Parameter(Mandatory=$false)]
[string]$Regsvr32Path,
[Parameter(Mandatory=$false)]
[string]$OrganizationName,
[Parameter(Mandatory=$false)]
[string]$RegEditPath
)
# Tests if the path is a local path. The installer doesn't like network paths.
function Test-LocalDrive{
[CmdletBinding()]
[OutputType([bool])]
param(
[Parameter(Mandatory=$true,
Position=0)]
[string]$Path
)
begin{
$localDrives = Get-WmiObject -Class Win32_logicaldisk -Filter "DriveType<>4" -Property "DeviceID" | Foreach-Object {$_.DeviceID.Replace(":", "")}
}
process{
if(!([bool](Test-Path -Path $Path))){
return $false
}
$item = Get-Item -Path $Path
$drive = $item.PSDrive
if($null -eq $drive){
return $false
}
return ($localDrives -contains $drive.Name)
}
}
function Search-ForFile{
[CmdletBinding()]
[OutputType([System.IO.FileInfo])]
param(
[Parameter(Mandatory=$true,
Position=0)]
[string]$File,
[Parameter(Mandatory=$true,
Position=1)]
[string]$CurrentLocation,
[switch]$IncludeSubDirectory
)
process{
$newPath = $currentLocation
if($IncludeSubDirectory.IsPresent){
$newPath = Join-Path -Path $newPath -ChildPath "*"
}
$newPath = Join-Path -Path $newPath -ChildPath $file
$item = @(Get-Item -Path $newPath)
if($null -eq $item -or $item.Count -eq 0 -or $null -eq $item[0]){
throw ("Could Not find the {0} file." -f $file)
}
return $item[0]
}
}
#region Setting Up File Paths
$currentLocation = $PSScriptRoot
if([System.String]::IsNullOrWhiteSpace($currentLocation)){
$currentLocation = (Get-Location)
}
if([System.String]::IsNullOrWhiteSpace($currentLocation)){
throw "Unable to determine current location"
}
if(!$PSBoundParameters.ContainsKey("Vb6InstallerPath") -or [System.String]::IsNullOrWhiteSpace($Vb6InstallerPath)){
if(!(Test-LocalDrive -Path ($currentLocation))){
Write-Error "The script cannot be ran from a network share."
Write-Host -NoNewLine 'Press any key to continue...';
$null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown');
return
}
$installerInfo = Search-ForFile -File "SETUP.EXE" -CurrentLocation $currentLocation -IncludeSubDirectory
$installFolder = $installerInfo.DirectoryName
}
else {
if(!(Test-LocalDrive -Path ($Vb6InstallerPath))){
Write-Error "The VB6 Installer Path cannot be a share."
Write-Host -NoNewLine 'Press any key to continue...';
$null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown');
return
}
$installFolder = $Vb6InstallerPath
$installerInfo = Search-ForFile -File "SETUP.EXE" -CurrentLocation $installFolder
}
$installer2Info = Search-ForFile -File "ACMSETUP.EXE" -CurrentLocation $installFolder -IncludeSubDirectory
$installLocation = $installerInfo.FullName
$install2Location = $installer2Info.FullName
if(!$PSBoundParameters.ContainsKey("SP6InstallerPath") -or [System.String]::IsNullOrWhiteSpace($SP6InstallerPath)){
if(!(Test-LocalDrive -Path ($currentLocation))){
Write-Error "The script cannot be ran from a network share."
Write-Host -NoNewLine 'Press any key to continue...';
$null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown');
return
}
$SP6Info = Search-ForFile -File "setupsp6.exe" -CurrentLocation $currentLocation -IncludeSubDirectory
$SP6Folder = $SP6Info.DirectoryName
}
else {
if(!(Test-LocalDrive -Path ($SP6InstallerPath))){
Write-Error "The SP6 Installer Path cannot be a network share."
Write-Host -NoNewLine 'Press any key to continue...';
$null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown');
return
}
$SP6Folder = $SP6InstallerPath
$SP6Info = Search-ForFile -File "setupsp6.exe" -CurrentLocation $SP6Folder
}
$SP6Location = $SP6Info.FullName
if(!$SkipMouseWheel.IsPresent){
if(!$PSBoundParameters.ContainsKey("MouseWheelPath") -or [System.String]::IsNullOrWhiteSpace($MouseWheelPath)){
if(!(Test-LocalDrive -Path ($currentLocation))){
Write-Error "The script cannot be ran from a network share."
Write-Host -NoNewLine 'Press any key to continue...';
$null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown');
return
}
$MouseWheelDllInfo = Search-ForFile -File "VB6IDEMouseWheelAddin.dll" -CurrentLocation $currentLocation -IncludeSubDirectory
$MouseWheelRegistryInfo = Search-ForFile -File "VBA Mouse Wheel Fix.reg" -CurrentLocation $currentLocation -IncludeSubDirectory
}
else {
if(!(Test-LocalDrive -Path ($SP6InstallerPath))){
Write-Error "The Mouse Wheel Path cannot be a network share."
Write-Host -NoNewLine 'Press any key to continue...';
$null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown');
return
}
$MouseWheelDllInfo = Search-ForFile -File "VB6IDEMouseWheelAddin.dll" -CurrentLocation $MouseWheelPath
$MouseWheelRegistryInfo = Search-ForFile -File "VBA Mouse Wheel Fix.reg" -CurrentLocation $MouseWheelPath
}
$MouseWheelDll = $MouseWheelDllInfo.FullName
$MouseWheelRegistry = $MouseWheelRegistryInfo.FullName
}
if(!$PSBoundParameters.ContainsKey("Regsvr32Path") -or [System.String]::IsNullOrWhiteSpace($Regsvr32Path)){
$regSvrPath = "$($env:systemroot)\SysWoW64\regsvr32.exe"
}
else{
$regSvrPath = $Regsvr32Path
}
if(!$PSBoundParameters.ContainsKey("RegEditPath") -or [System.String]::IsNullOrWhiteSpace($RegEditPath)){
$RegEditPath = "$($env:systemroot)\regedit.exe"
}
#endregion Setting Up File Paths
#region Test Required Installer Paths Exist
if(!([bool](Test-Path -Path $regSvrPath))){
Write-Error ("Unable to find '{0}'.`r`nThe exe must exist." -f $regSvrPath)
Write-Host -NoNewLine 'Press any key to continue...';
$null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown');
return
}
if(!([bool](Test-Path -Path $RegEditPath))){
Write-Error ("Unable to find '{0}'.`r`nThe exe must exist." -f $RegEditPath)
Write-Host -NoNewLine 'Press any key to continue...';
$null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown');
return
}
if(!$SkipMouseWheel.IsPresent){
if(!([bool](Test-Path -Path $MouseWheelDll))){
Write-Error ("Unable to find '{0}'.`r`nThe 'MouseWheel' Folder must be in the same directory as the install script and the file must exist." -f $MouseWheelDll)
Write-Host -NoNewLine 'Press any key to continue...';
$null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown');
return
}
if(!([bool](Test-Path -Path $MouseWheelRegistry))){
Write-Error ("Unable to find '{0}'.`r`nThe 'MouseWheel' Folder must be in the same directory as the install script and the file must exist." -f $MouseWheelRegistry)
Write-Host -NoNewLine 'Press any key to continue...';
$null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown');
return
}
}
if(!([bool](Test-Path -Path $installFolder))){
Write-Error ("Unable to find '{0}'.`r`nThe 'Installer' Folder must be in the same directory as the install script." -f $installFolder)
Write-Host -NoNewLine 'Press any key to continue...';
$null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown');
return
}
if(!([bool](Test-Path -Path $installLocation))){
Write-Error ("Unable to find '{0}'.`r`nThe 'Installer' Folder must be in the same directory as the install script and the file must exist." -f $installLocation)
Write-Host -NoNewLine 'Press any key to continue...';
$null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown');
return
}
if(!([bool](Test-Path -Path $install2Location))){
Write-Error ("Unable to find '{0}'.`r`nThe 'Installer' Folder must be in the same directory as the install script and the file must exist." -f $install2Location)
Write-Host -NoNewLine 'Press any key to continue...';
$null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown');
return
}
if(!([bool](Test-Path -Path $SP6Location))){
Write-Error ("Unable to find '{0}'.`r`nThe 'SP6' Folder must be in the same directory as the install script and the file must exist." -f $SP6Location)
Write-Host -NoNewLine 'Press any key to continue...';
$null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown');
return
}
#endregion Test Required Installer Paths Exist
#region Installer Compatibility
# The installer doesn't auto-elevate to run as an administrator.
# We are setting the required keys in the registry to force the installers to run as administrator
# Same as running the 'troubleshoot compatibilty' wizard and selecting the exe's to run as admins.
Write-Host "Setting compatibility mode on setup files."
$layersPath = "REGISTRY::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers"
if(){
New-Item -Path $layersPath -Force | Out-Null
}
$registryPath = Get-Item -LiteralPath $layersPath
if($null -eq $registryPath.GetValue($installLocation, $null)){
New-ItemProperty -Path $layersPath -Name $installLocation -Value "^ WINXPSP3" -PropertyType "String" -Force | Out-Null
}
if($null -eq $registryPath.GetValue($SP6Location, $null)){
New-ItemProperty -Path $layersPath -Name $SP6Location -Value "^ WINXPSP3" -PropertyType "String" -Force | Out-Null
}
#endregion Installer Compatibility
#region Previous Install Cleanup
# Locations and keys where old vb6 installs can live.
Write-Host "Cleaning up previous install."
$itemsToDelete = @(
"C:\Program Files*\Microsoft Visual Studio\Common",
"C:\Program Files*\Microsoft Visual Studio\MSDN",
"C:\Program Files*\Microsoft Visual Studio\MSDN98",
"C:\Program Files*\Microsoft Visual Studio\VB98",
"C:\Program Files*\Microsoft Visual Studio\VC98",
"C:\Program Files*\Microsoft Visual Studio\*.HTM",
"C:\Program Files*\Microsoft Visual Studio\*.TXT",
"C:\Program Files*\Common Files\Microsoft Shared\MSDesigners98",
"C:\Program Files*\Common Files\Microsoft Shared\MSDN",
"C:\Program Files*\Common Files\Microsoft Shared\VS98",
"C:\Program Files*\Common Files\Microsoft Shared\Wizards98",
"REGISTRY::HKEY_LOCAL_MACHINE\Software\Microsoft\DevStudio",
"REGISTRY::HKEY_LOCAL_MACHINE\Software\Microsoft\HTML Help Collections",
"REGISTRY::HKEY_LOCAL_MACHINE\Software\Microsoft\MSVSDG",
"REGISTRY::HKEY_LOCAL_MACHINE\Software\Microsoft\Visual Basic\6.0",
"REGISTRY::HKEY_LOCAL_MACHINE\Software\Microsoft\Visual Component Manager",
"REGISTRY::HKEY_LOCAL_MACHINE\Software\Microsoft\Visual Modeler",
"REGISTRY::HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0",
"REGISTRY::HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\DevStudio",
"REGISTRY::HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\HTML Help Collections",
"REGISTRY::HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\MSVSDG",
"REGISTRY::HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\Visual Basic\6.0",
"REGISTRY::HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\Visual Component Manager",
"REGISTRY::HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\Visual Modeler",
"REGISTRY::HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\VisualStudio\6.0",
"REGISTRY::HKEY_CURRENT_USER\Software\Microsoft\DevStudio",
"REGISTRY::HKEY_CURRENT_USER\Software\Microsoft\MSVSDG",
"REGISTRY::HKEY_CURRENT_USER\Software\Microsoft\Visual Basic\6.0",
"REGISTRY::HKEY_CURRENT_USER\Software\Microsoft\Visual Modeler",
"REGISTRY::HKEY_CURRENT_USER\Software\Microsoft\VisualFoxPro",
"REGISTRY::HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\6.0"
)
$itemsToDelete | Where-Object { Test-Path -Path $_ } | Remove-Item -Force -Recurse | Out-Null
#endregion Previous Install Cleanup
#region Installer Registry Permissions
# The installer needs to be able to write to 'HKEY_CLASSES_ROOT\RDSServer.DataFactory\Clsid'
# but since the installer isn't built for windows and we have to force it to run as an administrator
# we have to give explicit permissions for your computers Administrators group to write to this key (and all its children)
Write-Host "Setting required permissions for installing user on registry."
$registryPermissionPath = "REGISTRY::HKEY_CLASSES_ROOT\RDSServer.DataFactory\Clsid"
Write-Host "`tSetting Up required information."
$acl = Get-ACL -Path $registryPermissionPath
$oldOwner = [System.Security.Principal.NTAccount]::new($acl.Owner)
$administratorIdentity = [System.Security.Principal.NTAccount]::new("Administrators")
Write-Host "`tGiving the script required permissions."
$import = '[DllImport("ntdll.dll")] public static extern int RtlAdjustPrivilege(ulong a, bool b, bool c, ref bool d);'
$ntdll = Add-Type -Member $import -Name NtDll -PassThru
$privileges = @{ SeTakeOwnership = 9; SeBackup = 17; SeRestore = 18 }
foreach ($i in $privileges.Values) {
$null = $ntdll::RtlAdjustPrivilege($i, 1, 0, [ref]0)
}
Write-Host "`tGettting The registry key."
$regKey = [Microsoft.Win32.Registry]::ClassesRoot.OpenSubKey("RDSServer.DataFactory\Clsid", 'ReadWriteSubTree', 'TakeOwnership')
# We force the Administrators group to be the owner on the key so we can then add required the permissions.
Write-Host "`tSetting Owner."
$acl.SetOwner($administratorIdentity)
$regKey.SetAccessControl($acl)
Write-Host "`tSetting Permission."
$permission = [System.Security.AccessControl.RegistryAccessRule]::new($administratorIdentity, "FullControl", "ContainerInherit", "InheritOnly", "Allow")
$acl.AddAccessRule($permission)
$permission2 = [System.Security.AccessControl.RegistryAccessRule]::new($administratorIdentity, "FullControl", "Allow")
$acl.AddAccessRule($permission2)
$regKey.SetAccessControl($acl)
# Reset the owner to clean-up
Write-Host "`tResetting Owner."
$acl.SetOwner($oldOwner)
$regKey.SetAccessControl($acl)
#endregion Installer Registry Permissions
#region Install
Write-Host "`tStarting Install."
$tempPath = [System.IO.Path]::GetTempPath()
$tempFolder = Join-Path -Path $tempPath -ChildPath ([System.Guid]::NewGuid().ToString("n"))
New-Item -Path $tempFolder -ItemType Directory -Force | Out-Null
$KeyFile = Join-Path -Path $tempFolder -ChildPath ("{0}.dat" -f [System.Guid]::NewGuid().ToString("n"))
$keyFileText = @"
REGEDIT4
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\6.0\Setup\Microsoft Visual Basic\SetupWizard]
"aspo"=dword:00000000
[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\VisualStudio\6.0\Setup\Microsoft Visual Basic\SetupWizard]
"aspo"=dword:00000000
"@
$keyFileText | Set-Content -Path $keyFile -Force
& $RegEditPath /S $KeyFile
[string[]]$installerArguments = ("/T", "VB98PRO.stf", "/S", $installFolder, "/n", ($env:USERNAME), "/k", "0000000000", "/b", "1", "/qn1")
if($PSBoundParameters.ContainsKey("OrganizationName") -and ![System.String]::IsNullOrWhiteSpace($OrganizationName)){
$installerArguments += "/o"
$installerArguments += $OrganizationName
}
Start-Process -FilePath $install2Location -wait -NoNewWindow -ArgumentList $installerArguments
Start-Process -FilePath $SP6Location -wait -NoNewWindow -ArgumentList ("/qn1")
Write-Host "Setting Vb6 Compatibility"
$vb6ExeLocations = @(Get-Item -Path "C:\Program Files*\Microsoft Visual Studio\VB98\VB6.EXE" | Select-Object -ExpandProperty FullName)
$registryPath = Get-Item -LiteralPath $layersPath
foreach($vb6ExeLocation in $vb6ExeLocations){
if($null -eq $registryPath.GetValue($vb6ExeLocation, $null)){
New-ItemProperty -Path $layersPath -Name $vb6ExeLocation -Value "^ WINXPSP3" -PropertyType "String" -Force | Out-Null
}
}
if(!$SkipMouseWheel.IsPresent){
Write-Host "Installing Mouse Wheel"
& $regSvrPath /s $MouseWheelDll
& $RegEditPath /S $MouseWheelRegistry
$registryHeaderText = @"
Windows Registry Editor Version 5.00
"@
$registryItemFormat = @"
[{0}\SOFTWARE\Microsoft\Visual Basic\6.0\Addins\VB6IDEMouseWheelAddin.Connect]
"FriendlyName"="MouseWheel Fix"
"LoadBehavior"=dword:00000003
"CommandLineSafe"=dword:00000000
"@
$users = Get-ChildItem -Path "REGISTRY::HKEY_USERS" | Where-Object {$_.Name -notlike "*_Classes"} | Select-Object -ExpandProperty Name
$content = $registryHeaderText
# Install for every user.
foreach($user in $users){
$content += ($registryItemFormat -f $user)
}
$MouseWheelApplyRegistry = Join-Path -Path $tempFolder -ChildPath ("{0}.reg" -f [System.Guid]::NewGuid().ToString("n"))
$content | Set-Content -Path $MouseWheelApplyRegistry -Force
Start-Process $RegEditPath -wait -NoNewWindow -ArgumentList ("/S", $MouseWheelApplyRegistry)
Write-Host "You will still need to enable Mouse Wheel fix in the VB6 IDE." -BackgroundColor Black -ForegroundColor Red
Write-Host "Open a Visual Basic project and go to 'Add-Ins' -> 'Add-In Manager...' " -BackgroundColor Black -ForegroundColor Red
Write-Host "Select 'MouseWheel Fix' and click 'Loaded/Unloaded' and 'Load on Startup'" -BackgroundColor Black -ForegroundColor Red
}
Remove-Item -Path $tempFolder -Force -Recurse | Out-Null
#endregion Install
Write-Host "Install Complete"
Write-Host -NoNewLine 'Press any key to continue...';
$null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown');
}