Using ExifTool in VBA to Work With File Exif Meta Information

A while back I published a several articles about working with images and other files to read/write Exif meta data:

Well, while I was developing all of those techniques, I kept seeing mentions of ‘exiftool’ which is a standalone program designed by Phil Harvey to specifically work with Exif file information.  So today, I thought I’d show you all how we can take advantage of this tool via VBA.
 

 

ExifTool

Well, the first thing we have to do to able to work with the tool is download a free copy.  Did I mention it is FREE!

So go over to the official ExifTool webpage:

and download the appropriate copy for your setup.

Next, simply extract its content to a folder of your choosing.  I chose to install it in an ‘\exiftool\’ sub-directory.

Now, if you read the instructions on the site/readme file, next you should rename the ‘exiftool(-k).exe’ file to ‘exiftool.exe’.

Now, we’re ready to use it!  Yep, the nice thing with this tool is that you don’t actually have to install it to work with it!  You only need to call it and supply the command-line switches you are interested in.  So it’s 100% portable.
 

Using ExifTool With VBA (Access, Excel, Word, Outlook, …)

Some Helper Functions

Well, as per the usual, to simplify the actual ExifTool coding, I used a couple helper functions.  So here they are.

I used my Clipboard_GetText() function from VBA – Save String To Clipboard, Get String From Clipboard, but below is the function itself to save you running around:

'---------------------------------------------------------------------------------------
' Procedure : Clipboard_GetText
' Author    : Daniel Pineault, CARDA Consultants Inc.
' Website   : http://www.cardaconsultants.com
' Purpose   : Retrieve the clipboard value
' 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: Late Binding  -> none required
' https://www.devhut.net/vba-save-string-to-clipboard-get-string-from-clipboard/
'
' Usage:
' ~~~~~~
' Debug.Print Clipboard_GetText
' sClipboardValue = Clipboard_GetText
'
' Revision History:
' Rev       Date(yyyy-mm-dd)        Description
' **************************************************************************************
' 1         2020-11-25              Initial Public Release
'---------------------------------------------------------------------------------------
Public Function Clipboard_GetText() As String
    On Error GoTo Error_Handler

    With CreateObject("New:{1C3B4210-F441-11CE-B9EA-00AA006B1A69}")
        .GetFromClipboard
        Clipboard_GetText = .GetText
    End With

Error_Handler_Exit:
    On Error Resume Next
    Exit Function

Error_Handler:
    MsgBox "The following error has occurred" & vbCrLf & vbCrLf & _
           "Error Number: " & Err.Number & vbCrLf & _
           "Error Source: Clipboard_GetText" & 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

This will be used to retrieve the results from the exiftool execution in some cases.

Next, we need to be able to run command and to this end we use WScript.Shell. Now, instead of embedding that in each procedure it makes more sense to have a reusable function we can execute. So I used:

Function oWshShell_RunCommand(sCmd As String, _
                              Optional bDebugMode As Boolean = False) As String
    If bDebugMode Then
        ' Development => Display window to user / Do not close cmd prompt
        Debug.Print sCmd
        CreateObject("Wscript.Shell").Run "cmd /k " & sCmd, 1, True
    Else
        ' Production  => Hide window from user / Close cmd prompt once operation completed
        CreateObject("Wscript.Shell").Run "cmd /c " & sCmd, 0, True
    End If
End Function

but if you’d like a more robust version I’d urge you to actually take the procedure from my article Running Cmd Commands and Returning The Results.

So, now, we are truly ready to actually utilize the ExifTool utility to play around with Exif meta data.

Exif VBA Procedures

Getting The ExifTool Path/Filename

To make it easier to update the code should it be required, I made the location of the exiftool.exe retrieved by a function. This way there is only one update to be made should it be move.

Public Function ET_GetExe() As String
    Static sAppPath As String
    
    If sAppPath = "" Then
        'sAppPath = """" & "C:\...\...\exiftool.exe" & """"
        sAppPath = """" & Application.CurrentProject.Path & "\exiftool\exiftool.exe" & """"
    End If
    
    ET_GetExe = sAppPath
End Function

So now, you need only change the sAppPath = … line to update the location of the exiftool.exe and the remainder of the code remains unchanged!

Getting All The Meta Data

'? ET_GetAllMetaInformation("C:\Temp\IMG01.jpg")
Function ET_GetAllMetaInformation(sFile As String, _
                                  Optional bShowDuplicateTags As Boolean = False, _
                                  Optional bDisplayOriginalTagNames As Boolean = False) As String
    Dim sCmd                  As String

    sCmd = ET_GetExe()
    If bShowDuplicateTags Then sCmd = sCmd & " -a"
    If bDisplayOriginalTagNames Then sCmd = sCmd & " -S"
    sCmd = sCmd & " -fast"
    sCmd = sCmd & " -sort"
    sCmd = sCmd & " """ & sFile & """ | clip"
    sCmd = """" & sCmd & """"
    Call oWshShell_RunCommand(sCmd)

    DoEvents
    ET_GetAllMetaInformation = Clipboard_GetText
End Function

Getting a Specific Meta Tag Value

'? ET_GetSpecificMetaTag("C:\Temp\IMG01.jpg", "ShutterSpeed")
'? ET_GetSpecificMetaTag("C:\Temp\IMG01.jpg", "Software")
'? ET_GetSpecificMetaTag("C:\Temp\IMG01.jpg", "MIMEType")
Function ET_GetSpecificMetaTag(sFile As String, _
                               sTagName As String) As String
    Dim sCmd                  As String
    Dim sValue                As String

    sCmd = ET_GetExe()
    sCmd = sCmd & " -fast"
    sCmd = sCmd & " -s -s -s" 'Only return value
    sCmd = sCmd & " -" & sTagName
    sCmd = sCmd & " """ & sFile & """ | clip"
    sCmd = """" & sCmd & """"
    Call oWshShell_RunCommand(sCmd)

    DoEvents
    ET_GetSpecificMetaTag = Clipboard_GetText
    
    ' Approach without -s -s -s
    '    sValue = Clipboard_GetText
    '    ET_GetSpecificMetaTag = Mid(sValue, InStr(sValue, ": ") + 2)
End Function

Getting Meta Data Using a WildCard Search

'? ET_GetWildcardMetaTag("C:\Temp\IMG01.jpg", "File")
'? ET_GetWildcardMetaTag("C:\Temp\IMG01.jpg", "File", , True)
Function ET_GetWildcardMetaTag(sFile As String, _
                               sTagName As String, _
                               Optional bShowDuplicateTags As Boolean = False, _
                               Optional bDisplayOriginalTagNames As Boolean = False) As String
    Dim sCmd                  As String
    Dim sValue                As String

    sCmd = ET_GetExe()
    If bShowDuplicateTags Then sCmd = sCmd & " -a"
    If bDisplayOriginalTagNames Then sCmd = sCmd & " -S"
    sCmd = sCmd & " -fast"
    sCmd = sCmd & " -sort"
    sCmd = sCmd & " -*" & sTagName & "*"
    sCmd = sCmd & " """ & sFile & """ | clip"
    sCmd = """" & sCmd & """"
    Call oWshShell_RunCommand(sCmd)

    DoEvents
    ET_GetWildcardMetaTag = Clipboard_GetText
End Function

Removing All The Meta Data From A File

'ET_RemoveAllMetaTags "C:\Temp\IMG01a.jpg"
Function ET_RemoveAllMetaTags(sFile As String)
    Dim sCmd                  As String
    Dim sValue                As String

    sCmd = ET_GetExe()
    sCmd = sCmd & " -All="
    sCmd = sCmd & " -overwrite_original"
    sCmd = sCmd & " """ & sFile & """"
    sCmd = """" & sCmd & """"
    Call oWshShell_RunCommand(sCmd)
End Function

Removing a Specific Meta Tag Value From A File

'ET_RemoveSpecificMetaTag "C:\Temp\IMG01a.jpg", "ImageDescription"
'ET_RemoveSpecificMetaTag "C:\Temp\IMG01a.jpg", "Make"
Function ET_RemoveSpecificMetaTag(sFile As String, _
                                  sTagName As String)
    Dim sCmd                  As String
    Dim sValue                As String

    sTagName = Replace(sTagName, " ", "") 'Cheap fix

    sCmd = ET_GetExe()
    sCmd = sCmd & " -" & sTagName & "="
    sCmd = sCmd & " -overwrite_original"
    sCmd = sCmd & " """ & sFile & """"
    sCmd = """" & sCmd & """"
    Call oWshShell_RunCommand(sCmd)
End Function

Update A Specific Meta Tag Value

The following can be used to both create and/or update an existing meta tag entry.

' ET_UpdateSpecificMetaTag "C:\Temp\IMG01a.jpg", "Make", "IMakeCamerasNow"
Function ET_UpdateSpecificMetaTag(sFile As String, _
                                  sTagName As String, _
                                  sTagValue As String)
    Dim sCmd                  As String
    Dim sValue                As String

    sTagName = Replace(sTagName, " ", "") 'Cheap fix

    sCmd = ET_GetExe()
    sCmd = sCmd & " -" & sTagName & "=""" & sTagValue & """"
    sCmd = sCmd & " -overwrite_original"
    sCmd = sCmd & " """ & sFile & """"
    sCmd = """" & sCmd & """"
    Call oWshShell_RunCommand(sCmd)
End Function

Get The Page Count Of A Tif/Tiff File

'? ET_GetTifPageCount("C:\Temp\MultiPageTif_Test.tif")
Function ET_GetTifPageCount(sFile As String) As Long
    'PageCount doesn't return a value for tifs with 1 page
    Dim vPageCount As Variant
    
    vPageCount = ET_GetSpecificMetaTag(sFile, "PageCount")
    If vPageCount = "" Then vPageCount = 1
    
    ET_GetTifPageCount = vPageCount
End Function

 

Usage Example(s)

In most cases, I’ve included a commented example of the usage of each procedure. However, the ET_GetAllMetaInformation() could be a little tricky for new developers to breakdown so I thought I’d provide one possible example of how it might be used beyond just plain calling it.

Sub ET_GetAllMetaInformation_Test()
    Dim oTagDic               As Object
    Dim sTagsData             As String
    Dim aTags()               As String
    Dim sTag                  As String
    Dim sTagName              As String
    Dim sTagValue             As String
    Dim iCounter              As Long

    On Error Resume Next

    sTagsData = ET_GetAllMetaInformation("C:\Temp\IMG01.jpg", , True)
    aTags = Split(sTagsData, vbCrLf)

    Set oTagDic = CreateObject("Scripting.Dictionary")
    For iCounter = 0 To UBound(aTags()) - 1
        sTag = aTags(iCounter)
        sTagName = Mid(sTag, 1, InStr(sTag, ":") - 1)
        sTagValue = Mid(sTag, InStr(sTag, ": ") + 2)
        oTagDic.Add sTagName, sTagValue
    Next iCounter

    'Now that we have a collection we can pull the data as we please...
    Debug.Print "File Name", oTagDic("FileName")
    Debug.Print "Image Size", oTagDic("ImageSize")
    Debug.Print "File Type", oTagDic("FileType")
    Debug.Print "Color Space", oTagDic("ColorSpace")

    Set oTagDic = Nothing
End Sub

resulting in 4 lines of information being output to the immediate window.

This example demonstrates how you can effectively breakup all the data returned by ET_GetAllMetaInformation() into individual lines and further break that down into Tag Name and Values and pass that along to a dictionary object that you can then work with as you please. Just one of many possible uses.

Another possibility would be to push the retrieved information into a listbox, we could do so with something like a helper sub like:

Private Sub PushMetaDatToListBox(sFile As String, _
                                 lst As Access.ListBox)
    Dim sTagsData             As String
    Dim aTags()               As String
    Dim sTag                  As String
    Dim sTagName              As String
    Dim sTagValue             As String
    Dim iCounter              As Long

    On Error Resume Next

    lst.RowSource = ""

    sTagsData = ET_GetAllMetaInformation(sFile, , True)
    aTags = Split(sTagsData, vbCrLf)

    For iCounter = 0 To UBound(aTags()) - 1
        sTag = aTags(iCounter)
        sTagName = Mid(sTag, 1, InStr(sTag, ":") - 1)
        sTagValue = Mid(sTag, InStr(sTag, ": ") + 2)
        lst.AddItem sTagName & ";" & sTagValue
    Next iCounter
End Sub

and then we could implement it by doing:

Call PushMetaDatToListBox("C:\Temp\IMG01.jpg", Me.YourListboxName)

 

Demo Database

Feel free to download a 100% unlocked copy of a sample database I have put together to illustrate some of what is discussed above by using the link provided below:

Download “Using ExifTool in VBA to Work With File Exif Meta Information” ExifTool.zip – Downloaded 4183 times – 53.59 KB

This download is of only my demo database, you must also independently download a copy of the ExifTool from the link provided above and then edit the ET_GetExe() function within the mod_ExifTool VBA module of the demo database to reflect your exiftool.exe installation path. Then, everything should work.
 

Notice About Content/Downloads/Demos

Disclaimer/Notes:

If you do not have Microsoft Access, simply download and install the freely available runtime version (this permits running MS Access databases, but not modifying their design):

Microsoft Access 2010 Runtime
Microsoft Access 2013 Runtime
Microsoft Access 2016 Runtime
Microsoft 365 Access Runtime

All code samples, download samples, links, ... on this site are provided 'AS IS'.

In no event will Devhut.net or CARDA Consultants Inc. be liable to the client/end-user or any third party for any damages, including any lost profits, lost savings or other incidental, consequential or special damages arising out of the operation of or inability to operate the software which CARDA Consultants Inc. has provided, even if CARDA Consultants Inc. has been advised of the possibility of such damages.

 

A Few Final Words

ExifTool works very nicely in my testing and as you can see is relatively easy to implement. It’s another nice tool to be aware of and to add to our VBA toolbox.

The above is just scratching the surface of some of what ExifTool can do with Meta Data, so if this is an area of interest to you do review the source website as it contains much more information than what I am providing here. My goal was simply to demonstrate its ease of implementation within the VBA environment for accomplishing the most common Meta Data tasks.

Also, I know most people think of images whenever people mention the word Exif or Meta Data, but almost all files have Meta info and this tool can access them all. Images (jpg, png, tiff, bmp, ico, … and even newer formats like webp), txt, zip, xlsx, docx, pdf, … It actually interesting to test it out on other files to see what it attached to a file, but typically hidden to the user.

If you’re looking for a listing of available options (command-line switches) then look at: Option Overview.

If you are looking for available Meta Data Tag Names then look at: ExifTool Tag Names
 

Page History

Date Summary of Changes
2024-10-25 Initial Release
2024-11-01 Updated the demo file (V1.001) to include a fully functional form implementing all of the functions from this page.
2024-11-03 Updated the demo file (V1.002) to include a fully functional form implementing all of the functions from this page.
Updated code to add surrounding quotes around exiftool.exe path and overall shell command to deal with spaces in paths.

3 responses on “Using ExifTool in VBA to Work With File Exif Meta Information

  1. Carl Runge

    Down loaded latest version get the following on the immediate window:
    ? ET_GetAllMetaInformation(“D:\Wren and Reiner 001”)
    “D:\Access\Testing\ExitIf\exiftool\exiftool.exe -fast -sort “D:\Wren and Reiner 001” | clip “D:\Wren and Reiner 001″ && pause”
    Wren and Reiner 001
    But I get an error on the CMD prompt:
    “Invalid argument option – ‘D:Wren and Reiner 001”.
    Type “CLIP /?” for usage
    Error file not found : D:/Wrenand Reiner 001

    1. Daniel Pineault Post author

      pause only appears in one location in my code and it is commented out, so the only way it will show up is if you’ve uncommented the line, which you shouldn’t.

      You doing:

      ? ET_GetAllMetaInformation(“D:\Wren and Reiner 001”)

      but for which file, file extenstion?

      ? ET_GetAllMetaInformation(“D:\Wren and Reiner 001\YourImage.jpg”)

  2. Carl Runge

    After a while I thought I would have another try at ExitIf. I deleted all the previous down loads and it worked first time out!!. For me, “to understand is to know”, so the principles you are explaining needs to be understood by me before I can apply them in my projects. Many thanks.