Windows world is full of clicks. For some people, this is a nightmare.
While others may try to use batch file to automate, batch itself is too awkward and ancient with very limited capabilities.
There is Windows Powershell to allow more scripting, but for some people, powershell is still "not good enough" (or read: syntaces are too dirty and messy) compared to other popular script languages like Python, Ruby or Groovy.
Below is an example install script written with Ruby to perform simple file copies and eliminate the needs of writing Powershell:
- Delete or backup files if previous installation is detected
- Copy files from source to destination
- Perform Windows registry setup
- Perform auto-reboot if certain files cannot be copied due to file locking by other Windows process
install_program.rb
require 'fileutils'
$REBOOT_ON_ERROR = true #Change to false if we don't want to auto-reboot upon exception error (e.g. during debugging)
$INSTALL_RETRY_BAT = 'C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup\installretry.bat' #Startup location on Windows 7
$IS_RESUMING = File.exist?( $INSTALL_RETRY_BAT )
def copy_files
_TARGET_FOLDER = 'C:\MyApp\RubyApp'
FileUtils.mv(_TARGET_FOLDER, "#{_TARGET_FOLDER}_#{Time.now.strftime "%Y-%m-%d_%H-%M-%S"}") if (Dir.exist? _TARGET_FOLDER) #Make backup instead of overwriting it
FileUtils::mkdir_p(_TARGET_FOLDER)
FileUtils.cp_r(".\\RubyAppSource\\", "#{_TARGET_FOLDER}")
end
def configure_windows
system( "net stop \"Windows Time\" /y 2>&1" )
2.times do #Handle Windows bug (https://whiskykilo.com/w32tm-or-w32time-or-whatever-ms-wants-to-call-it/)
system( "w32tm /unregister >nul 2>&1" )
system( "w32tm /unregister >nul 2>&1" )
system( "w32tm /register >nul 2>&1" )
end
system( "reg add \"HKLM\\SYSTEM\\CurrentControlSet\\services\\W32Time\\TimeProviders\\NtpServer\" /v Enabled /t REG_DWORD /d 1 /f 2>&1" )
system( "reg add \"HKLM\\SYSTEM\\CurrentControlSet\\services\\W32Time\\Config\" /v AnnounceFlags /t REG_DWORD /d 5 /f 2>&1" )
system( "net start \"Windows Time\" /y 2>&1" )
system( "w32tm /config /update 2>&1" )
end
def show_retry_if_it_is_executed_from_installretry_bat
if $IS_RESUMING
puts "*** Retrying installation after previous error and reboot ***"
sleep 10
end
end
def do_auto_reboot_and_retry
if $REBOOT_ON_ERROR == true and !$IS_RESUMING
puts "Script engine is trying to perform reboot on error now..."
File.open( $INSTALL_RETRY_BAT, "w" ) { |file| file.write( File.expand_path($0).gsub('/', '\\') ) }
system "shutdown /f /r /t 1 2>&1"
else
( puts "Fatal error, still unable to complete installation after the reboot" ) if $IS_RESUMING
end
end
if (__FILE__ == $0)
begin
show_retry_if_it_is_executed_from_installretry_bat()
copy_files()
configure_windows()
puts "Installation done"
rescue => e
do_auto_reboot_and_retry() #in case some files or folders are locked and require reboot during copy_files()
ensure
( FileUtils.rm( $INSTALL_RETRY_BAT ) rescue nil ) if $IS_RESUMING #Ensure we resume only ONCE, not repeatedly
puts "Press ENTER to exit"
$stdin.gets
end
end
That's it.
We don't need fancy tool like WiX or InstallShield. Just fire the text editor and then run it.
And if we want to call WinAPI, we can use FFI or Win32API for it
This is an example on how to fire Visual Studio COM automation using WIN32OLE:
require 'win32ole'
_MY_SOLUTION_SLN='D:/myvisualstudio_projects/project1/solution1.sln'
_dte = WIN32OLE.new('VisualStudio.DTE.12.0') rescue WIN32OLE.new('VisualStudio.DTE.10.0') rescue raise "Cannot find Visual Studio 2010 nor 2013"
_vs_version = _dte.RegistryRoot[-4..-1] rescue ""
puts "\r\nDetected Visual Studio version: #{_vs_version}"
_dte.SuppressUI = false
_mainWindow = _dte.MainWindow
_mainWindow.Visible = true
_mainWindow.WindowState = 2 #maximize
#_mainWindow.WindowState = 0 #restore
#_mainWindow.WindowState = 1 #minimize
_sln = _dte.Solution
_sln.Open("#{_MY_SOLUTION_SLN}")
Happy scripting with Ruby :)