
' UltraMon Wallpaper Auto Changer

' Version: Alpha 7
' Date: July 3, 2007

' Changes:
' Added support for UltraMon 3. GetUMWPFolderLocation replaced with SetFileLocations, which has support for both UltraMon 2
' and 3. Added global variable UMWPBitmap, which is set by SetFileLocations, and is used by ApplyWallpaperChanges to delete
' the existing wallpaper bitmap. Updated documentation

' Version History - See end of this script file


' Syntax:
' UltraMon Wallpaper Auto Changer.vbs


' Usage:
' Set UltraMon 'Default' wallpaper for each monitor
' to an image in a folder containing images for that monitors wallpaper.
' Run UltraMon Wallpaper Auto Changer.

' To alter the change interval, image order, or images folder, for each monitor, 
' edit the 'UMWPAutoChanger.cfg' file, located in the UltraMon Wallpapers folder.
' To prevent the wallpaper of a monitor from being changed, set the interval to zero (00:00:00).

' Configuration file format is comma separated, (spaces permitted but not tabs) as follows:
' Monitor Number, Change Interval (hh:mm:ss), Order (Random, Sequential), Images Folder Path
' Initial Defaults are:
' <Monitor Number>,00:01:00,Sequential,<image folder path from UltraMon Default Wallpaper>

' Typical UltraMon wallpaper folder location is:
' 'My Documents\My Wallpapers' or 'All Users\Documents\Shared Wallpapers' for UltraMon 2,
' %APPDATA%\Realtime Soft\UltraMon\<version>\Wallpapers for UltraMon 3.

' To stop the 'UltraMon Wallpaper Auto Changer', use task manager to end the 'wscript.exe' process.

' Images folder can contain shortcuts to image files, rather than being duplicates.
' Folder shortcuts though are disabled by default.
' To enable/disable folder shortcuts support, set 'FolderShortcuts' constant in code below to 'True'/'False'.
' There is a significant processing hit when Folder Shortcuts are enabled.  Default is disabled ('False').


' Making configuration changes on the fly:
' Changes to the UltraMon 'Default' wallpaper are applied to ‘UMWPAutoChanger’ at the next change interval.
' Changes to the UltraMon 'UMWPAutoChanger' wallpaper are applied at the next change interval, 
' except image path, which always comes from the UltraMon 'Default.wallpaper' file.
' Changes made to the 'UMWPAutoChanger.cfg' configuration file are applied at the next 'SleepTimeSec' interval.


' How it Works Overview:
' Looks in personal Users UltraMon wallpaper folder for 'Default.wallpaper', 
' if found copies it to 'UMWPAutoChanger.wallpaper'.
' If 'Default.wallpaper' file was not found in the personal users UltraMon wallpaper folder, 
' then looks in All Users UltraMon wallpaper folder for 'Default.wallpaper', 
' if found copies it to 'UMWPAutoChanger.wallpaper'.

' Obtains images folder paths of each monitor from strucs in 'Default.wallpaper'.
' Creates a configuration file for storing - 
' monitor number, interval, image order, and images folder path for each monitor.

' Selects an image file from the images folder of each monitor to be changed.
' Updates the struc of each monitor to be changed in 'UMWPAutoChanger.wallpaper' with new image path.
' Runs 'UltraMonDesktop.exe' to have UltraMon rebuild the wallpaper.
' Repeats loop to change the wallpaper at specified interval.


Option Explicit

'Const UMDESKTOP_EXE = "%ProgramFiles%UltraMon\UltraMonDesktop.exe" ' This is obtained dynamically now.
Const FolderShortcuts = False ' Disabled (False) by default.  See 'Usage' for additional information.
Const SleepTimeSec = 1 ' How often to check if a monitors wallpaper needs changed, in seconds.


' UltraMon Wallpaper file structure positions/lengths.
Const wpfsNumMonPos = 8		' Position for Number of Monitors
Const wpfsHeaderLen = 19	' Number of Bytes Preceeding Monitor Structures (excluding Monitor Rectangles)
Const wpfsMonRectLen = 16	' Length of Each Monitor Rectangle (16 bytes each)
Const wpfsMaxPathLen = 260	' Maximum Path Length
Const wpfsMonStrucHeadLen = 16	' Background, Color 1, Color 2, Image Style (4 bytes each)


Dim UMDESKTOP_EXE, UMWPFolder, UMWPBitmap
Dim MonStrucStart, NumStrucs, NumMonitors


' Re-Sized in ConfigFileMonInfo()
' Offset '0' ingnored/unused.  Offset '1' = Monitor '1', etc.
Dim IntervalSec(), IntervalTimerSec()
Dim ImagesOrder(), ImageFileNum(), ImagesFolder()


'WScript Shell
Dim sh
Set sh = CreateObject("WScript.Shell")


'FSO
Const ForReading = 1, ForWriting = 2, ForAppending = 8
Const Create = True, DontCreate = False
Const SysDefault = -2, Unicode = -1, ASCII = 0
Const OverWrite = True, DontOverWrite = False
Const OverRideReadOnlyAttribute = True, HonorReadOnlyAttribute = False

Dim fso
Set fso = CreateObject("Scripting.FileSystemObject")


' Start Here
Start()


Sub Start()

	If UltraMonWallpaperMangerInstalled() Then
		SetFileLocations UMWPFolder, UMWPBitmap
		
		' Update config file <interval>,<units> format to hh:mm:ss
		' This can be removed once previous config file format has been converted.
		cfgFileIntervalTimeFormatChange()

		Initialize()
		TimerLoop()
	Else
		MsgBox( _
		"UltraMon Wallpaper Manager installation location could not be found." & vbCr & _
		"Terminating UltraMon Wallpaper Auto Changer.")
	End If

End Sub


' Nerver ending loop to check if wallpaper needs changed.
Sub TimerLoop()



	Do While True

		UpdateNeededCheck()

		'wait
		WScript.Sleep(SleepTimeSec * 1000)

	Loop

End Sub


' Check if it is time to change wallpaper for any of the monitors.
Sub UpdateNeededCheck()

	Dim ChangesMade, UMWPFileData, i
	ChangesMade = False
	UMWPFileData = ""

	ChangesMade = CfgFileChangeCheck(UMWPFileData)

	For i = 1 To NumMonitors

		If IntervalSec(i) > 0 Then ' Skip Monitor if this is not set to greater than zero.

			If IntervalTimerSec(i) <= 0 Then

				' Refresh data from the file to ensure we have most recent data.
				If Len(UMWPFileData) < 1 Then ' Get UMWPAutoChanger.wallpaper file data.
					UMWPFileData = GetFileData(UMWPFolder & "UMWPAutoChanger.wallpaper")
				End If

				UpdateWallpaperFileData i, UMWPFileData
				IntervalTimerSec(i) = IntervalSec(i)
				ChangesMade = True
			End If

			IntervalTimerSec(i) = IntervalTimerSec(i) - SleepTimeSec

		End If
	Next

	If ChangesMade Then
		ApplyWallpaperChanges(UMWPFileData)
		UMWPFileData = ""
	End If

End Sub


' Check if configuration file (.cfg) has changed (newer than UMWPAC wallpaper file)
' If changed, update wallpaper file for all monitors and return 'True'.
Function CfgFileChangeCheck(ByRef UMWPFileData)

	' Initialize Return Value
	CfgFileChangeCheck = False

	If fso.FileExists(UMWPFolder & "UMWPAutoChanger.wallpaper") _
	And fso.FileExists(UMWPFolder & "UMWPAutoChanger.cfg") Then

		Dim wpFile, cfgFile
		Set cfgFile = fso.GetFile(UMWPFolder & "UMWPAutoChanger.cfg")
		Set wpFile = fso.GetFile(UMWPFolder & "UMWPAutoChanger.wallpaper")

		If cfgFile.DateLastModified > wpFile.DateLastModified Then

			ConfigFileMonInfo()

			Dim i
			For i = 1 To NumMonitors
				If IntervalSec(i) > 0 Then ' Skip Monitor if this is not set to > zero.
					If Len(UMWPFileData) < 1 Then ' Get UMWPAutoChanger.wallpaper file data.
						UMWPFileData = GetFileData(UMWPFolder & "UMWPAutoChanger.wallpaper")
					End If

					UpdateWallpaperFileData i, UMWPFileData
				End If
			Next

			For i = 1 To UBound(IntervalSec)
				IntervalTimerSec(i) = IntervalSec(i)
			Next

			CfgFileChangeCheck = True

		End If
	End If

End Function


' Get wallpaper file contents, Initialize variables, create/update files if need be.
Sub Initialize()

	Dim UMWPFileData

	CopyUMDefaultWallpaper()

	UMWPFileData = GetFileData(UMWPFolder & "UMWPAutoChanger.wallpaper")

	MonStrucsInfo 1, UMWPFileData

	CreateUpdateConfigFile(UMWPFileData)
	ConfigFileMonInfo()

End Sub


' Determine if and where UltraMon Wallpaper Manager is installed.
Function UltraMonWallpaperMangerInstalled()

	Dim msi, cmpIds(2), umDesktopExe, prod, i

	' Initialize Return Value
	UltraMonWallpaperMangerInstalled = False

	Set msi = CreateObject("WindowsInstaller.Installer")

	'Dim cmpIds
	'cmpIds = Array("", "{BEDCF68A-6628-48D7-ABA9-85A28ACE5B6C}", "{B8105F70-BFBE-4FCC-99B7-81417F56AAF6}")

        'cmpIds(0) = ""
        cmpIds(1) = "{BEDCF68A-6628-48D7-ABA9-85A28ACE5B6C}"
        cmpIds(2) = "{B8105F70-BFBE-4FCC-99B7-81417F56AAF6}"

	i = 1
	umDesktopExe = ""
	Do While umDesktopExe = "" And i <= UBound(cmpIds)
		For Each prod In msi.ComponentClients(cmpIds(i))
			umDesktopExe = msi.ComponentPath(prod, cmpIds(i))
			Exit For
		Next
		i = i + 1
	Loop

	If Len(umDesktopExe) > 0 Then
		UMDESKTOP_EXE = umDesktopExe
		UltraMonWallpaperMangerInstalled = True
	End If

End Function


' Get location of the wallpaper folder and the wallpaper bitmap file.
Sub SetFileLocations(wpFolder, wpBitmap)
	
	wpFolder = ""
	wpBitmap = ""
	
	'check if UltraMon 3 or later is installed
	Dim umVer : umVer = ""
	On Error Resume Next
	umVer = sh.RegRead("HKLM\Software\Realtime Soft\UltraMon\CurrentVersion")
	On Error Goto 0

	'get the location of the wallpaper folder(s)
	Dim dirWps(1)
	If umVer = "" Then
		'UltraMon 2, location of the user and shared wallpaper folders stored in the registry
		dirWps(0) = sh.RegRead("HKCU\Software\Realtime Soft\UltraMon\Wallpaper\Wallpaper Directory")
		dirWps(1) = sh.RegRead("HKLM\Software\Realtime Soft\UltraMon\Wallpaper\All Users Wallpaper Directory")
	Else
		'UltraMon 3 or later, wallpaper folder is at a known location
		dirWps(0) = sh.ExpandEnvironmentStrings("%APPDATA%\Realtime Soft\UltraMon\" & umVer & "\Wallpapers")
	End If

	Dim i
	For i = 0 To UBound(dirWps)
		If dirWps(i) <> "" Then
			If Right(dirWps(i), 1) <> "\" Then dirWps(i) = dirWps(i) & "\"
			If fso.FileExists(dirWps(i) & "UMWPAutoChanger.wallpaper") Or fso.FileExists(dirWps(i) & "Default.wallpaper") Then
				wpFolder = dirWps(i)
				Exit For
			End If
		End If
	Next
	
	If wpFolder = "" Then
		MsgBox( _
		"UltraMon Wallpaper Auto Changer" & vbCr & _
		"Wallpaper file not found, Terminating.")

		WScript.Quit()
	End If
	
	'get the name of the wallpaper bitmap file
	If umVer = "" Then
		'UltraMon 2, bitmap file in same folder as wallpaper file
		wpBitmap = wpFolder & "UMWPAutoChanger.bmp"
	Else
		'UltraMon 3 or later, bitmap stored in local user data folder
		wpBitmap = sh.ExpandEnvironmentStrings("%LOCALAPPDATA%\Realtime Soft\UltraMon\UltraMon Wallpaper.bmp")
		If InStr(wpBitmap, "%LOCALAPPDATA%") <> 0 Then
			'%LOCALAPPDATA% is only available on Vista and later, construct the path manually
			wpBitmap = sh.ExpandEnvironmentStrings("%USERPROFILE%") & "\Local Settings\Application Data\Realtime Soft\UltraMon\UltraMon Wallpaper.bmp"
		End If
	End If
	
End Sub


' Create or update auto changer wallpaper file from copy of the UltraMon Default wallpaper file.
Function CopyUMDefaultWallpaper()

	Dim DefaultWP, UMWPAC

	' Initialize Return Value
	CopyUMDefaultWallpaper = False

	' Copy 'Personal' or 'All Users' Default.wallpaper file to UMWPAutoChanger.wallpaper, unless already exists.

	     If fso.FileExists(UMWPFolder & "Default.wallpaper") _
	And Not fso.FileExists(UMWPFolder & "UMWPAutoChanger.wallpaper") Then

		CopyUMDefaultWallpaper = True

	' If Default.wallpaper is newer, copy it to UMWPAutoChanger.wallpaper.

	ElseIf fso.FileExists(UMWPFolder & "Default.wallpaper") _
	   And fso.FileExists(UMWPFolder & "UMWPAutoChanger.wallpaper") Then

		Set DefaultWP = fso.GetFile(UMWPFolder & "Default.wallpaper")
		Set UMWPAC = fso.GetFile(UMWPFolder & "UMWPAutoChanger.wallpaper")

		If DefaultWP.DateLastModified > UMWPAC.DateLastModified Then
			CopyUMDefaultWallpaper = True
		End If
	End If

	If CopyUMDefaultWallpaper Then
		fso.CopyFile UMWPFolder & "Default.wallpaper", UMWPFolder & "UMWPAutoChanger.wallpaper", OverWrite
	End If

End Function


' Create or update auto changer configuration file from info obtained from the UltraMon Default wallpaper file.
Sub CreateUpdateConfigFile(ByRef UMWPFileData)

	Dim Create, Update, FileText, cfgFile, wpFile

	Create = False
	     If fso.FileExists(UMWPFolder & "Default.wallpaper") _
	    And fso.FileExists(UMWPFolder & "UMWPAutoChanger.wallpaper") _
	And Not fso.FileExists(UMWPFolder & "UMWPAutoChanger.cfg") Then
		Create = True
	End If

	Update = False
	 If fso.FileExists(UMWPFolder & "Default.wallpaper") _
	And fso.FileExists(UMWPFolder & "UMWPAutoChanger.cfg") Then

		Set wpFile = fso.GetFile(UMWPFolder & "Default.wallpaper")
		Set cfgFile = fso.GetFile(UMWPFolder & "UMWPAutoChanger.cfg")

		If wpFile.DateLastModified > cfgFile.DateLastModified Then
			Set cfgFile = fso.OpenTextFile(UMWPFolder & "UMWPAutoChanger.cfg", ForReading, DontCreate, ASCII)
			FileText = cfgFile.ReadAll
			cfgFile.Close()
			Update = True
		End If
	End If

	If Update Or Create Then

		Dim WPImagesFolder, ImageFilePath, Lines, FileLines, LnPos, UnicodeImageFilePath, i, j

		Lines = ""
		FileLines = Split(FileText, vbNewLine)

		For i = 1 To NumStrucs
			' Calculate Position of 'Unicode' Image File Path in UltraMon Wallpaper File Data.
			LnPos = MonStrucStart + ((i - 1) * ((wpfsMaxPathLen * 2) + wpfsMonStrucHeadLen)) + wpfsMonStrucHeadLen

			' Get 'Unicode' Image File Path from UltraMon Wallpaper File.
			UnicodeImageFilePath = Mid(UMWPFileData, LnPos, wpfsMaxPathLen * 2)

			' Convert 'Unicode' Image File Path to Regular String.
			ImageFilePath = Replace(UnicodeImageFilePath, Chr(0), "")

			' Get just the folder path portion of ImageFilePath (everything to last backslash).
			WPImagesFolder = Mid(ImageFilePath, 1, InStrRev(ImageFilePath, "\"))

			' Get a config file line.
			Dim LineUpdated, Line, Count
			LineUpdated = False
			For Each Line In FileLines
				Count = InStr(1, Line, ",") - 1
				If Count > 0 Then
					If Trim(Mid(Line, 1, Count)) = CStr(i) Then ' Update with new images folder path.
						LnPos = 1
						For j = 1 To 3
							LnPos = InStr(LnPos, Line, ",") + 1
						Next

						Lines = Lines & Mid(Line, 1, LnPos - 1) & WPImagesFolder & vbNewLine
						LineUpdated = True
					End If
				End If
			Next

			' If an existing entry for monitor was not found, create a new defalut entry with folder path.
			If Not LineUpdated Then
				Lines = Lines & i & ",00:01:00,Sequential," & WPImagesFolder & vbNewLine
			End If
		Next

		Set cfgFile = fso.OpenTextFile(UMWPFolder & "UMWPAutoChanger.cfg", ForWriting, Create, ASCII)
		cfgFile.Write(Lines)
		cfgFile.Close()
	End If

End Sub


' Obtain UltraMon wallpaper file contents.
Function GetFileData(ByRef FilePath)

	' Initialize Return String
	'GetFileData = ""

	Dim File, FileData, FileString
	Set File = fso.GetFile(FilePath)
	Set FileData = fso.OpenTextFile(File, ForReading, DontCreate, ASCII)
	GetFileData = FileData.Read(File.Size)

	' Don't Use the ReadAll method, it won't work because of the 'binary' file contents
	'GetFileData = FileData.ReadAll

	FileData.Close()

End Function


' Calculate and set values we need from UltraMon wallpaper file data.
Sub MonStrucsInfo(ByRef MonNum, ByRef UMWPFileData)

	' Get Number of Monitors, Monitor Strucs and Struc Posistion 
	' from UMWPAutoChanger.wallpaper file data
        NumMonitors = Asc(Mid(UMWPFileData, wpfsNumMonPos, 1))
        NumStrucs = Asc(Mid(UMWPFileData, wpfsHeaderLen + (NumMonitors * wpfsMonRectLen) + 1 - 4))
	MonStrucStart = wpfsHeaderLen + (NumMonitors * wpfsMonRectLen) + ((MonNum - 1) * ((wpfsMaxPathLen * 2) + wpfsMonStrucHeadLen)) + 1

'	MsgBox( _
'	"MonStrucsInfo" & vbCr & _
'	"Monitor Number: " & MonNum & vbCr & _
'	"Monitors: " & NumMonitors & vbCr & _
'	"Strucs: " & NumStrucs & vbCr & _
'	"Struc Start: " & MonStrucStart)

End Sub


' Load information from auto changer config file into variables.
Sub ConfigFileMonInfo()

	Dim FileText, FileLines, cfgFile
	Dim Hours, Minutes, Seconds
	Dim Line, LnPos, Count, MonNum, Size

	Set cfgFile = fso.OpenTextFile(UMWPFolder & "UMWPAutoChanger.cfg", ForReading, DontCreate, ASCII)

	FileText = cfgFile.ReadAll
	cfgFile.Close()

	FileLines = Split(FileText, vbNewLine)

	' Remove trailing blank lines
	Do While Len(FileLines(UBound(FileLines))) <= 0
		ReDim Preserve FileLines(UBound(FileLines) - 1)
	Loop

	' Get number of config file lines.
	Size = UBound(FileLines)

	' Set array size to greater of monitors, monitor structures, or config file lines.
	If NumStrucs > Size Then Size = NumStrucs
	If NumMonitors > Size Then Size = NumMonitors

	ReDim Preserve IntervalSec(Size + 1)
	ReDim Preserve IntervalTimerSec(Size + 1)
	ReDim Preserve ImagesOrder(Size + 1)
	ReDim Preserve ImageFileNum(Size + 1)
	ReDim Preserve ImagesFolder(Size + 1)

	' Update Parameters Arrays For All Monitors In Config File
	For Each Line In FileLines

		' If line doesn't have at least one ',' then don't get anymore lines.
		If InStr(1, Line, ",") <= 0 Then Exit For

		' Get Monitor Number
		LnPos = 1
		Count = InStr(LnPos, Line, ",") - LnPos
		MonNum = CInt(Trim(Mid(Line, LnPos, Count)))
		'MonNumber(MonNum) = CInt(Trim(Mid(Line, LnPos, Count)))

		' Get Hours
		LnPos = LnPos + Count + 1
		Count = InStr(LnPos, Line, ":") - LnPos
		Hours = CInt(Trim(Mid(Line, LnPos, Count)))

		' Get Minutes
		LnPos = LnPos + Count + 1
		Count = InStr(LnPos, Line, ":") - LnPos
		Minutes = CInt(Trim(Mid(Line, LnPos, Count)))

		' Get Seconds
		LnPos = LnPos + Count + 1
		Count = InStr(LnPos, Line, ",") - LnPos
		Seconds = CInt(Trim(Mid(Line, LnPos, Count)))

		' Convert Hours, Minutes & Seconds to Seconds
		IntervalSec(MonNum) = (Hours * 3600) + (Minutes * 60) + Seconds

		' Get Order
		LnPos = LnPos + Count + 1
		Count = InStr(LnPos, Line, ",") - LnPos
		ImagesOrder(MonNum) = Trim(Mid(Line, LnPos, Count))

		' Get Images Folder
		LnPos = LnPos + Count + 1
		Count = InStr(LnPos, Line, ",") - LnPos
		ImagesFolder(MonNum) = Trim(Mid(Line, LnPos)) ' To End of Line

		' Add trailing backslash to Images Folder if not present
		If Right(ImagesFolder(MonNum), 1) <> "\" Then ImagesFolder(MonNum) = ImagesFolder(MonNum) & "\"

'		MSgBox( _
'		"Config File Mon Info" & vbCR & _
'		"Monitor: " & MonNum & vbCR & _
'		"Interval: " & Hours & ":" & Minutes & ":" & Seconds & vbCR & _
'		"Order: " & ImagesOrder(MonNum) & vbCR & _
'		"WP Images folder: " & ImagesFolder(MonNum))

	Next

End Sub


' Determine which image file to select.
Function ImageFileSelector(ByRef MonNum)

	Dim ImageFileCount, objFolder

	If ImageFileNum(MonNum) < 1 Then ImageFileNum(MonNum) = 1

	' Enumerate Available Image Files and Select Next One
	Set objFolder = fso.GetFolder(ImagesFolder(MonNum))

	If LCase(ImagesOrder(MonNum)) = "random" Then

		Dim Max, Min
		ImageFileNum(MonNum) = 4294967296	' Supposedly 4 Giga Files is maximum for an NTFS volume
		ImageFileCount = 1
		SelectImageFile MonNum, objFolder, ImageFileCount

		Max = ImageFileCount - 1
		Min = 1
		Randomize ' Give Rnd a new seed value so same random order is not repeated.
		ImageFileNum(MonNum) = Int((Max - Min + 1) * Rnd() + Min)

	ElseIf LCase(ImagesOrder(MonNum)) = "Sequential" Then

	End If

	ImageFileCount = 1
	ImageFileSelector = SelectImageFile(MonNum, objFolder, ImageFileCount)

	'If ImageFileSelector = False Then
	If Len(ImageFileSelector) < 1 Then
		ImageFileNum(MonNum) = 1
		ImageFileCount = 1
		ImageFileSelector = SelectImageFile(MonNum, objFolder, ImageFileCount)

		'If ImageFileSelector = False Then
		If Len(ImageFileSelector) < 1 Then

			MsgBox( _
			"UltraMon Wallpaper Auto Changer" & vbCr & _
			"No Images Found in: " & vbCr & _
			ImagesFolder(MonNum) & vbCr & _
			"Terminating.")

			WScript.Quit()

		End If

	End If

	ImageFileNum(MonNum) = ImageFileNum(MonNum) + 1

End Function


' Locate image file selected and return full path to target.
Function SelectImageFile(ByRef MonNum, ByVal objFolder, ByRef ImageFileCount)

	'SelectImageFile = False
	SelectImageFile = ""

	Dim objSubFolder

	If objFolder.Files.Count + ImageFileCount > ImageFileNum(MonNum) Then ' This folder has the file we want.

		Dim File
		For Each File In objFolder.Files

			If (ImageFileCount = ImageFileNum(MonNum)) Then ' This is file 'number' we want.

				Dim FilePath, FileExtn

				If File.Type = "Shortcut" Then 'Get target path)
					Dim Link
					set Link = sh.CreateShortcut(File)
					FilePath = Link.targetpath

				Else ' Not a shortcut so use path and name directly.
					FilePath = File.Path
				End If

				' Get Image File Extension and Convert to Non-Cap Letters
				FileExtn = LCase(Mid(FilePath, InStrRev(FilePath, ".") + 1))

				Select Case (FileExtn) ' Only use if file has 'blessed' image file extension.

					Case "bmp", "jpg", "pcx", "png", "tga", "tif", "jpeg", "tiff"

						If (fso.FileExists(FilePath)) Then ' Proceed with this file.
							SelectImageFile = FilePath
						Else ' Probably a folder shortcut.
							SelectImageFile = ""
							ImageFileNum(MonNum) = ImageFileNum(MonNum) + 1
						End If

					Case Else
						' Not a 'blessed' image file extension.
						SelectImageFile = ""
						ImageFileNum(MonNum) = ImageFileNum(MonNum) + 1
				End Select

			End If

			' Increment counter until it is passed the correct image file.
			ImageFileCount = ImageFileCount + 1
			If ImageFileCount > ImageFileNum(MonNum) Then Exit Function
		Next

	Else ' This folder doesn't have the file we want, continue on to next subfolder.
		ImageFileCount = ImageFileCount + objFolder.Files.Count
	End If


	' Folder Shortcuts - Disabled (False) by default.  See 'Usage' for additional information.
	' There is a significant processing hit when Folder Shortcuts are enabled.  Default is disabled ('False').
	' There must be a more efficient method of determining if target of shortcut is a folder.
	' The processing hit comes from having to sift through all the image files and shortcuts to identify folder shortcuts.
	If FolderShortcuts Then
		' Recursion through all the folder shortcuts
		'Dim File
		For Each File In objFolder.Files

			If ImageFileCount > ImageFileNum(MonNum) Then Exit Function

			If File.Type = "Shortcut" Then ' Remove shortcut file extension (.lnk).
				Dim FileName
				FileName = Mid(File.Name, 1, InStrRev(File.Name, ".") - 1)
				FileExtn = LCase(Mid(FileName, InStrRev(FileName, ".") + 1))

				' Exclude image files
				Select Case (FileExtn)
					Case "bmp", "jpg", "pcx", "png", "tga", "tif", "jpeg", "tiff"
						' A 'blessed' image file extension - Do nothing (skip it).

					Case Else ' Not a 'blessed' image file extension - proceed.
						Dim FolderPath
						set Link = sh.CreateShortcut(File)
						FolderPath = Link.targetpath

						If (fso.FolderExists(FolderPath)) Then ' Proceed with this folder.
							Set objSubFolder = fso.GetFolder(FolderPath)

							SelectImageFile = SelectImageFile(MonNum, objSubFolder, ImageFileCount)
						End If
				End Select

			End If
		Next
	End If


	' Recursion through all the sub-folders
	For Each objSubFolder In objFolder.SubFolders

		If ImageFileCount > ImageFileNum(MonNum) Then Exit Function

		SelectImageFile = SelectImageFile(MonNum, objSubFolder, ImageFileCount)

	Next

End Function


' Update the UltraMon Wallpaper Auto Changer file data string with path to newly seleted image file.
Sub UpdateWallpaperFileData(ByRef MonNum, ByRef UMWPFileData)

	Dim NewUMWPFileData, ImageFilePath, UnicodeImageFilePath, i

	' Build New UltraMon Wallpaper File Data

	' If changes have been made to Default.wallpaper, 
	' Update UMWPAutoChanger.wallpaper and config file by reinitializing.
	If CopyUMDefaultWallpaper() Then Initialize()

	' Select an image file from monitor specific folder
	ImageFilePath = ImageFileSelector(MonNum)

	' Convert regular image file path string to 'unicode'.
	UnicodeImageFilePath = ""
	For i = 1 To Len(ImageFilePath)
		UnicodeImageFilePath = UnicodeImageFilePath & Mid(ImageFilePath, i, 1) & Chr(0)
	Next

        ' Fill to max path length
	UnicodeImageFilePath = UnicodeImageFilePath & String((wpfsMaxPathLen * 2) - Len(UnicodeImageFilePath), Chr(0))

	' Refresh Monitor Struc Info for MonNum from UMWPFileData.
	MonStrucsInfo MonNum, UMWPFileData

        ' Update the UltraMon Wallpaper File Data with new Image File Path.

	' Get everything preceding the File Path.
	' (everything preceding the Struc we are working on, pluse the first 16 bytes of the Struc we are working on)
	NewUMWPFileData = Mid(UMWPFileData, 1, MonStrucStart - 1 + wpfsMonStrucHeadLen)

	' Append the ImageFilePath for the Struc we are working on
	NewUMWPFileData = NewUMWPFileData & UnicodeImageFilePath

	' Append all remaining monitor Strucs.
	NewUMWPFileData = NewUMWPFileData & Mid(UMWPFileData, MonStrucStart + wpfsMonStrucHeadLen + (wpfsMaxPathLen * 2))

	' Save the new file data string.
	UMWPFileData = NewUMWPFileData

End Sub


' Apply updated UltraMon Wallpaper.
Sub ApplyWallpaperChanges(ByRef NewUMWPFileData)

	' Write the New Ultra Mon Wallpaper Data String to the UMWPAutoChanger.wallpaper File.
	Dim UMWPACFile
	Set UMWPACFile = fso.OpenTextFile(UMWPFolder & "UMWPAutoChanger.wallpaper", ForWriting, Create, ASCII)
	UMWPACFile.Write(NewUMWPFileData)
	UMWPACFile.Close()

	' Delete current wallpaper bitmap (UMWPAutoChanger.BMP)
	' so UltaMon Desktop will recreate it with the new images.

	If fso.FileExists(UMWPBitmap) Then
		fso.DeleteFile UMWPBitmap, OverRideReadOnlyAttribute
	End If

	' Have UltraMon Rebuild and Apply the New Wallpaper
	Dim cmd
	cmd = """" & UMDESKTOP_EXE & """ /load " & UMWPFolder & "UMWPAutoChanger.wallpaper"

	sh.Run(cmd)

End Sub



' Update config file <interval>,<units> format to hh:mm:ss
' This subroutine can be removed once previous config file format has been converted.
Sub cfgFileIntervalTimeFormatChange()

	If fso.FileExists(UMWPFolder & "UMWPAutoChanger.cfg") Then

		Dim FileText, FileLines, Line, Convert, i

		Dim cfgFile
		Set cfgFile = fso.OpenTextFile(UMWPFolder & "UMWPAutoChanger.cfg", ForReading, DontCreate, ASCII)

		FileText = cfgFile.ReadAll
		cfgFile.Close()

		FileLines = Split(FileText, vbNewLine)

		' Remove trailing blank lines
		Do While Len(FileLines(UBound(FileLines))) <= 0
			ReDim Preserve FileLines(UBound(FileLines) - 1)
		Loop

		Convert = False
		i = 0
		For Each Line In FileLines

			' If line doesn't have at least one ',' then skip it.
			If InStr(1, Line, ",") <= 0 Then ' Skip

			Else
				Dim LnPos, Count, Monitor, Interval, Units, Order, Path
				LnPos = 1
				Count = InStr(LnPos, Line, ",") - LnPos
				Monitor = Mid(Line, LnPos, Count)

				LnPos = LnPos + Count + 1
				Count = InStr(LnPos, Line, ",") - LnPos
				Interval = Mid(Line, LnPos, Count)

				LnPos = LnPos + Count + 1
				Count = InStr(LnPos, Line, ",") - LnPos
				Units = LCase(Mid(Line, LnPos, Count))

				LnPos = LnPos + Count + 1
				Count = InStr(LnPos, Line, ",") - LnPos
				If Count > 0 Then
					Order = Mid(Line, LnPos, Count)

					LnPos = LnPos + Count + 1
				End If

				Path = Mid(Line, LnPos) ' To end of line

				Select Case (Units)

					Case "hrs"
						If Interval < 10 Then Interval = "0" & Interval
						If Interval > 99 Then Interval = "99"
						Interval = "" & Interval & ":00:00"
						Convert = True

					Case "min"
						If Interval < 10 Then Interval = "0" & Interval
						If Interval > 99 Then Interval = "99"
						Interval = "00:" & Interval & ":00"
						Convert = True

					Case "sec"
						If Interval < 10 Then Interval = "0" & Interval
						If Interval > 99 Then Interval = "99"
						Interval = "00:00:" & Interval
						Convert = True

					Case Else


				End Select

				FileLines(i) = Monitor & "," & Interval & "," & Order & "," & Path
				i = i + 1
			End If
		Next

		If Convert Then
			Set cfgFile = fso.OpenTextFile(UMWPFolder & "UMWPAutoChanger.cfg", ForWriting, DontCreate, ASCII)

			For Each Line In FileLines
				cfgFile.WriteLine(Line)
			Next

			cfgFile.Close()
		End If
	End If
End Sub



' Version History

' Version: Alpha 1
' Date: December 25, 2006
' Original Creation 


' Version: Alpha 2
' Date: December 26, 2006

' Fixes:
' Image file extension check case sensitive. To make case insensitive...
' Replace line 132
' If (Right(fileWp.Name, 4) = .jpg) or (Right(fileWp.Name, 4) = .bmp) Then
' With this
' If (LCase(Right(fileWp.Name, 4)) = .jpg) or (LCase(Right(fileWp.Name, 4)) = .bmp) Then

' Line 74
' dirWps(0) = sh.RegRead(HKLMSoftwareRealtime SoftUltraMonWallpaperAll Users Wallpaper Directory)
' Should be 
' dirWps(1) = sh.RegRead(HKLMSoftwareRealtime SoftUltraMonWallpaperAll Users Wallpaper Directory)

' Changes:
' In addition to bmp and jpg, accepting pcx, png, tga, and tif, jpeg, and tiff image file extensions


' Version: Alpha 3
' Date: December 29, 2006

' Changes:
' Image folder recursion added.
' Random or sequential image file order selection option added.
' Command line options monitor number now must be preceded with 'M'.  Example M3 30 Sec Rand M5 10 Min Seq
' Organized into functions and subroutines.


' Version: Alpha 4
' Date December 31, 2006

' Changes:
' Single instance of script supporting each monitor with unique change interval, image order, and image folder.
' Command line parameters support removed.  Real-time dynamic control through configuration file.


' Version: Alpha 5
' Date: January 12, 2007

' Fixes:
' Repositioned interval timer update to correct from being of by 1 loop count (1 x SleepTimeSec seconds).
' Increased maximum image file number from 100,000 to 4,294,967,296.
' Supposedly 4 Giga Files is maximum for an NTFS volume.

' Changes:
' Config file (.cfg) format changed from <Interval>,<Units> to <hh:mm:ss> (all digits required).
' Dynamic Locate of 'UltraMon Wallpaper Manager' Installation via Windows Installer.
' Dynamic array sizing for number of monitors.


' Version: Alpha 6
' Date: January 19, 2007

' Fixes:
' Significant efficiency improvement of the 'SelectImageFile' function.
' Read/Write of wallpaper file for each monitor wallpaper being changed reduced to
' a single read/write of wallpaper file for all monintor wallpapers being changed.
' Some code, comments and documentation cleanup and corrections.

' Changes:
' Added support for image file shortcuts.  Folder shortcuts though are disabled by default.
' Images folders can now contain shortcuts to the actual image files, rather than being duplicates.