A while back I published a several articles about working with images and other files to read/write Exif meta data:
- Removing Image Exif Properties Using WIA in VBA
- Setting an Image Exif Property Using WIA in VBA
- Getting Image Properties (Exif Metadata) Using WIA in VBA
- Getting Image Properties (Exif Metadata) Using FreeImage
- Get Image Exif Properties using VBA and PowerShell
- Creating/Setting an Image Property Using The GDI+ API
- How to Retrieve a File’s Properties with VBA
- etc.
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 KBThis 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
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. |
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
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”)
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.