Category Archives: VBScript & Batch Files

How to Batch Optimize Images in a Folder Using VBScript and ImageMagick

As the title implies, I wanted to share a small utility script I created to meet my own needs, in the hopes that it might help others who also need to process and optimize all the images in a folder in one go.

My goal was to quickly and efficiently resize images in a folder based on a specified maximum width and height, then output them in multiple formats to achieve the smallest possible file size—typically WebP, but not always.
 
Continue reading

Ask Microsoft to Cancel Their Planned Removal of VBScript & Classic ASP

If you’re not up on the news, Microsoft has decided to deprecate and remove VBScript!

Continue reading

Launching Internet Explorer for Testing Purposes

I’m always amazed at the level of pure stupidity that Microsoft exudes at times!

I know, we all have our moments in the sun, myself included, but Microsoft just seems to manage to outdo themselves.

I’m well aware that in an ideal world, Microsoft wants us all running MS365 and thus we would have all migrated all of our existing Web Browser controls to use the ‘new’ Modern Web Browser control, but that isn’t reality, not even close!  The fact of the matter, the legacy Web Browser control is still fully supported and thus why fix something that isn’t broken.  Beyond which, many people are simply not running MS365 so they don’t even have access to the Modern Web Browser control at all.

All of this to say, that many a database are still using the ‘Legacy’ Web Browser control, thus IE (Internet Explorer)!

Continue reading

VBA – Run PowerShell Command

Windows PowerShell

Have you ever wanted to run a PowerShell command from within VBA and get back a value?

Executing a PowerShell Command From VBA

Before getting into returning a response, let first look at simply executing a command. There are many instances in which we simply want to run a PowerShell command and don’t need to get any response back.

It turns out that it is very easy to do and very similar to the approach taken in VBA – Get Host IP Address.

PowerShell can simply be called/executed from a standard command prompt using the command

powershell -command

Armed with this information when can simple use a WScript Shell object to execute the command, thus giving us a simple function along the lines of

Continue reading

MS Access – Backup a Database Using a VBScript

The following goes to support my previous article MS Access – Automatic Backup of an Access Database, specifically the section regarding using a VBScript in conjunction with Windows Scheduled Task.  I had a very basic VBScript, VBScript – Backup a File and add a Date Time Stamp, which illustrated how to add a Date/Time stamp to a file when you copy it and this made a great starting point for a backup script.

Several people have mentioned that they need more of a helping hand to transform that basic script into a more complete backup solution.

So I decided to try and help everyone out some more and below is a pretty comprehensive backup script that you can use.

Continue reading

Wscript.shell Specialfolders Not Behaving

I was recently doing some work in an old database trying to convert a very simple function which used Wscript.shell Specialfolders into a more versatile function that would accept a single input variable instead of a hardcoded value.

My original function was

Function m() As String
    Set WshShell = CreateObject("WScript.Shell")
    m = WshShell.SpecialFolders("Desktop")
End Function

Running it would return the path to the current user’s Desktop folder accurately.

C:\Users\MyUserName\Desktop

A very slight variation of the same function

Function mm() As String
    Set WshShell = CreateObject("WScript.Shell")
    sFldr = "Desktop"
    m = WshShell.SpecialFolders(sFldr)
End Function

would also return the path to the current user’s Desktop folder accurately.

C:\Users\MyUserName\Desktop

However, where things got weird was when I made another modification to use sFldr as an input variable and created the following function

Function n(sFldr As String) As String
    Set WshShell = CreateObject("WScript.Shell")
    n = WshShell.SpecialFolders(sFldr)
End Function

and ran it by calling by using the command n(“Desktop”) it would not return the current user’s Desktop folder, but rather the public desktop path???

C:\Users\Public\Desktop

So I started to do a little digging and came to find I was not the first person to encounter this issue. For instance, see:

Unexpected behavior of WScript.Shell SpecialFolders function

So I did a little more playing around with code and switched the input variable sFldr from String to Long

Function n(sFldr As Long) As String
    Set WSHShell = CreateObject("WScript.Shell")
    n = WSHShell.SpecialFolders(sFldr)
End Function

and performed a few iterations (1 to 16) and determined the following

1->ProgramData\Microsoft\Windows\Start Menu
2->ProgramData\Microsoft\Windows\Start Menu\Programs
3->ProgramData\Microsoft\Windows\Start Menu\Programs\Startup
4->User Desktop
5->User AppData\Roaming
6->User AppData\Roaming\Microsoft\Windows\Printer Shortcuts
7->User AppData\Roaming\Microsoft\Windows\Templates
8->Windows\Fonts
9->User AppData\Roaming\Microsoft\Windows\Network Shortcuts
10->User Desktop
11->User AppData\Roaming\Microsoft\Windows\Start Menu
12->User AppData\Roaming\Microsoft\Windows\SendTo
13->User AppData\Roaming\Microsoft\Windows\Recent
14->User AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup
15->User Favorites
16->User Documents
17->User AppData\Roaming\Microsoft\Windows\Start Menu\Programs

which is all odd to me. In the hardcoded version it expects a string value, but the last version takes a numeric value to get proper paths returned. The above (last function) has, by far, not been tested in any manner to ensure it is reliable, it is simply at this point an interesting observational fact.

What is a fact is that Wscript.shell Specialfolders does not appear to be reliable when the objWshSpecialFolders variable is not a hardcoded value! As such, I’d recommend using an alternate method to determine the paths of Windows Special folders like: Enumerating Special Folders which uses the CreateObject(“Shell.Application”).Application.Namespace() to get the values.

Update and Solution – 2016-09-28

Too stubborn for my own good, I couldn’t let this one go. So I kept playing around and testing… eventually I managed to get it to work. Below is the reusable function.

'---------------------------------------------------------------------------------------
' Procedure : GetWindowsSpecialFldrPath
' Author    : Daniel Pineault, CARDA Consultants Inc.
' Website   : http://www.cardaconsultants.com
' Purpose   : Return the full path for the specified Windows Special folder
' Copyright : The following may be altered and reused as you wish so long as the
'             copyright notice is left unchanged (including Author, Website and
'             Copyright).  It may not be sold/resold or reposted on other sites (links
'             back to this site are allowed).
' Req'd Refs: Uses Late Binding, so none required
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' vFldrName : The name of the folder to return the path of
'             Values can be: AllUsersDesktop, AllUsersStartMenu, AllUsersPrograms,
'                            AllUsersStartup, Desktop, Favorites, Fonts, MyDocuments,
'                            NetHood, PrintHood, Programs, Recent, SendTo, StartMenu,
'                            Startup, Templates
'
' Usage:
' ~~~~~~
' sPath = GetWindowsSpecialFldrPath("Desktop")
' sPath = GetWindowsSpecialFldrPath("MyDocuments")
'
' Revision History:
' Rev       Date(yyyy/mm/dd)        Description
' ********************************************************************************
******
' 1         2016-09-28              Initial Release
'---------------------------------------------------------------------------------------
Function GetWindowsSpecialFldrPath(vFldrName As Variant) As String
      '***Special Note: If vFldrName this is dimmed as a string it returns the wrong value ***
10        On Error GoTo Error_Handler
          Dim WshShell              As Object

20        If IsNull(vFldrName) = True Then GoTo Error_Handler_Exit
30        Set WshShell = CreateObject("WScript.Shell")
40        GetWindowsSpecialFldrPath = WshShell.SpecialFolders(vFldrName)

Error_Handler_Exit:
50        On Error Resume Next
60        If Not WshShell Is Nothing Then Set WshShell = Nothing
70        Exit Function

Error_Handler:
80        MsgBox "The following error has occurred" & vbCrLf & vbCrLf & _
                 "Error Number: " & Err.Number & vbCrLf & _
                 "Error Source: " & sModName & "\GetWindowsSpecialFldrPath" & vbCrLf & _
                 "Error Description: " & Err.Description & _
                 Switch(Erl = 0, "", Erl <> 0, vbCrLf & "Line No: " & Erl) _
                 , vbOKOnly + vbCritical, "An Error has Occurred!"
90        Resume Error_Handler_Exit
End Function

The entire issue is the way the input variable, vFldrName, is dimmed. If it is dimmed as a string it acts weird. Dim it as a variant and it works as expected. Since the documentations that I found simply mentions “The name of the special folder.” and does not specify the type to use and the examples provided were .SpecialFolders(“Desktop”) one would think it is a string, but apparently not.

Alternate Solution Update 2016-09-29

User daolix on UtterAccess recently answered my post on this subject and provided an interesting answer/solution:

If you want to use a string variable so you have to force a ByVal call at the “Special Folders”.
Your function n is just to expand only to a clip.

Function n(sFldr As String) As String
    Set WshShell = CreateObject("WScript.Shell")
    n = WshShell.SpecialFolders((sFldr))
End Function

If you pass on a “ByRef” variant, a variable which does not correspond to the data type “Variant”, a variant is created at the interface of the function, which contains a pointer to the passed variable as a value. A true “ByRef” transfer does not take place here, this is only “simulated” with the help of the variant, and corresponds rather to a “ByVal” transfer.
If you pass a string variable to this function, the system must dereference twice to get the content, because a string itself is only a pointer. And here probably the function “SpecialFolders” failed. Error from Microsoft? So wanted? No idea.

I tested his answer and it does work.

MS Access – VBA – Kill a Process

Every once in a while, programs do not shutdown properly and thus cause headaches. So it can become necessary to terminate the process. Below are a couple different approaches you can employ to terminate, forcibly close, a process/program.
 
Continue reading

VBA-WMI-Determine If a Process or Program Is Running Or Not

Here’s a simple technique (pulling it out of archive to help answer a thread in a discussion forum) to verify whether or not a program (actually a process) is currently running on a computer, local or remote (if the permissions have been establish to validate such information remotely), using a simple WMI query.

'---------------------------------------------------------------------------------------
' Procedure : WMI_IsProcessRunning
' Author    : Daniel Pineault, CARDA Consultants Inc.
' Website   : http://www.cardaconsultants.com
' Purpose   : Determine if a process or Program is running or not
'               Returns: True/False
' Copyright : The following is release as Attribution-ShareAlike 4.0 International
'             (CC BY-SA 4.0) - https://creativecommons.org/licenses/by-sa/4.0/
' Req'd Refs: Uses Late Binding, so none required
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' sExeName  : Name of the process to look for
' sHost     : Host computer to query, omit for the local PC
'
' Usage:
' ~~~~~~
' WMI_IsProcessRunning("firefox.exe")
' WMI_IsProcessRunning("outlook.exe")
' WMI_IsProcessRunning("msaccess.exe", "HomePC01")
' WMI_IsProcessRunning("msaccess.exe", "192.168.100.12")
'
' Revision History:
' Rev       Date(yyyy/mm/dd)        Description
' **************************************************************************************
' 1         2010-Feb-03             Initial Release
' 2         2020-08-21              Renamed the Function
'                                   Updated Proc Header
'                                   Code updated
'                                   Updated Error Handler
'---------------------------------------------------------------------------------------
Public Function WMI_IsProcessRunning(sProcessName As String, Optional sHost As String = ".") As Boolean
    On Error GoTo Error_Handler
    Dim oWMI                  As Object    'WMI object to query about the PC's OS
    Dim sWMIQuery             As String    'WMI Query
    Dim oCols                 As Object
    Dim oCol                  As Object

    Set oWMI = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & sHost & "\root\cimv2")
    sWMIQuery = "SELECT * FROM Win32_Process WHERE Name = '" & sProcessName & "'"
    Set oCols = oWMI.ExecQuery(sWMIQuery)
    If oCols.Count <> 0 Then WMI_IsProcessRunning = True

Error_Handler_Exit:
    On Error Resume Next
    Set oCols = Nothing
    Set oWMI = Nothing
    Exit Function

Error_Handler:
    MsgBox "The following error has occurred" & vbCrLf & vbCrLf & _
           "Error Number: " & Err.Number & vbCrLf & _
           "Error Source: WMI_IsProcessRunning" & vbCrLf & _
           "Error Description: " & Err.Description & _
           Switch(Erl = 0, "", Erl <> 0, vbCrLf & "Line No: " & Erl) _
           , vbOKOnly + vbCritical, "An Error has Occurred!"
    Resume Error_Handler_Exit
End Function

There are some other great resources on the subject of WMI to further develop such code. I good starting point is MSDN, see: WMI Tasks: Processes

VBScript – Backup a File and add a Date Time Stamp

In an Access forum a user was asking how they could make an automated backup of their database.

Their are a number of possible solutions, including:

  • If your IT department already performs routine backups, simply ask them to add your database to their backups
  • You could install a backup program yourself

Another viable solution is to create a simple Batch file (*.bat) of VBScript file to perform the backup and then use the Windows Task Scheduler to run it on the basis of your choice (typically nightly).  To help you get this process setup and running, below are 2 VBScripts to perform the backup of the database file.

Continue reading

Determine Installed Version of any MS Office Program – VBScript

In my previous post I show 2 vbscripts to determine the versions of MS Access, as well as, Internet Explorer. But what do you do if you need to determine the version of Excel, Word, Outlook, etc? Well it can be done as well. Below is a basic, but versatile vbscript that can return the version of almost any program installed on a PC. Please note, it needs more coding to allow for exceptions, but the basic are there.

Method 1 – File System Object (FSO)

Dim oRegistry
Dim oFSO
Dim sKey
Dim sAppExe
Dim sValue
Dim sAppVersion
Const HKEY_LOCAL_MACHINE 	= &H80000002

Set oRegistry = GetObject("winmgmts:{impersonationLevel=impersonate}//./root/default:StdRegProv")
Set oFSO = CreateObject("Scripting.FileSystemObject")
sKey = "Software\Microsoft\Windows\CurrentVersion\App Paths"
'sAppExe = "excel.exe"
'sAppExe = "GROOVE.exe"
'sAppExe = "infopath.exe"
sAppExe = "MSACCESS.EXE"
'sAppExe = "MSPUB.EXE"
'sAppExe = "OneNote.exe"
'sAppExe = "OUTLOOK.EXE"
'sAppExe = "winword.exe"
'sAppExe = "firefox.exe" 'Even works with a number of other programs!
oRegistry.GetStringValue HKEY_LOCAL_MACHINE, sKey & "\" & sAppExe, "", sValue
MsgBox oFSO.GetFileVersion(sValue)
Set oFSO = Nothing
Set oRegistry = Nothing

Method 2 – WMI

Dim oRegistry
Dim oWMIService
Dim colFiles
Dim oFile
Dim sKey
Dim sAppExe
Dim sValue
Dim sAppVersion
Const HKEY_LOCAL_MACHINE 	= &H80000002

Set oRegistry = GetObject("winmgmts:{impersonationLevel=impersonate}//./root/default:StdRegProv")
Set oWMIService = GetObject("winmgmts:\\.\root\cimv2")
sKey = "Software\Microsoft\Windows\CurrentVersion\App Paths"
'sAppExe = "excel.exe"
'sAppExe = "GROOVE.exe"
'sAppExe = "infopath.exe"
sAppExe = "MSACCESS.EXE"
'sAppExe = "MSPUB.EXE"
'sAppExe = "OneNote.exe"
'sAppExe = "OUTLOOK.EXE"
'sAppExe = "winword.exe"
'sAppExe = "firefox.exe" 'Even works with a number of other programs!
oRegistry.GetStringValue HKEY_LOCAL_MACHINE, sKey & "\" & sAppExe, "", sValue
Set colFiles = oWMIService.ExecQuery _
    ("Select * from CIM_Datafile Where Name = '" & replace(sValue, "\", "\\") & "'")
For Each oFile in colFiles
    MsgBox oFile.Version
Next

Set colFiles = Nothing
Set oWMIService = Nothing
Set oRegistry = Nothing

In both cases, the procedures return a value such as: 15.0.4749.1000 which you can easily parse out the first set of numbers to identify the major version of the software (if that is what you are after).

Now, according to http://blogs.technet.com/b/heyscriptingguy/archive/2005/04/18/how-can-i-determine-the-version-number-of-a-file.aspx?Redirected=true Method 2 is best. Although both limitations of Method 1 have no impact on my usage. Furthermore, Method 1 was faster in my tests.

Also, if you are running 64-bit, then you may need to also check the

sKey = "Software\Wow6432Node\Microsoft\Windows\CurrentVersion\App Paths"