Getting All of an Image’s Properties Using The GDI+ API

Continuing on my multipart series on using the GDI+ APIs, after looking at how we can retrieve a single property as discussed in my previous article

I thought I’d explore retrieving all of the available properties.

The GDI Plus API

Once again, I won’t bore you with the details. Instead, here’s the code for you to review, dissect or simply use.

Option Explicit

'API Declarations, ENUMS, TYPES, Global Variables, ...
'-------------------------------------------------------------------------------
'-------------------------------------------------------------------------------
'GDI - General
Private Declare Function GdiplusStartup Lib "gdiplus" (ByRef token As Long, ByRef lpInput As GDIPlusStartupInput, Optional ByRef lpOutput As Any) As Status
Private Declare Function GdiplusShutdown Lib "gdiplus" (ByVal token As Long) As Status
Private Declare Function GdipCreateBitmapFromFile Lib "gdiplus" (ByVal FileName As Long, ByRef Bitmap As Long) As Status
Private Declare Function GdipDisposeImage Lib "gdiplus" (ByVal image As Long) As Status
'GDI - Image / Properties
Private Declare Function GdipGetAllPropertyItems Lib "gdiplus" (ByVal image As Long, ByVal totalBufferSize As Long, ByVal numProperties As Long, ByRef allItems As PropertyItem) As Status
Private Declare Function GdipGetPropertySize Lib "gdiplus" (ByVal image As Long, ByRef totalBufferSize As Long, ByRef numProperties As Long) As Status

'Helper API Declarations
Private Declare Function lstrlenW Lib "kernel32" (lpString As Any) As Long
Private Declare Function lstrcpyW Lib "kernel32" (lpString1 As Any, lpString2 As Any) As Long
Private Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)

Private Const GdiPlusVersion  As Long = 1

Private Type GDIPlusStartupInput
    GdiPlusVersion            As Long
    DebugEventCallback        As Long
    SuppressBackgroundThread  As Long
    SuppressExternalCodecs    As Long
End Type

Private Type PropertyItem
    id                        As Long    'PropertyTagId
    Length                    As Long
    type                      As Integer 'PropertyTagType
    Value                     As Long
End Type

    'GDI+ Status Constants
Private Enum Status
    'https://docs.microsoft.com/en-us/windows/win32/api/gdiplustypes/ne-gdiplustypes-status
    OK = 0
    GenericError = 1
    InvalidParameter = 2
    OutOfMemory = 3
    ObjectBusy = 4
    InsufficientBuffer = 5
    NotImplemented = 6
    Win32Error = 7
    WrongState = 8
    Aborted = 9
    FileNotFound = 10
    ValueOverflow = 11
    AccessDenied = 12
    UnknownImageFormat = 13
    FontFamilyNotFound = 14
    FontStyleNotFound = 15
    NotTrueTypeFont = 16
    UnsupportedGdiplusVersion = 17
    GdiplusNotInitialized = 18
    PropertyNotFound = 19
    PropertyNotSupported = 20
    ProfileNotFound = 21
End Enum

Private Enum PropertyTagType
    'https://docs.microsoft.com/en-us/windows/win32/gdiplus/-gdiplus-constant-image-property-tag-type-constants
    TypeByte = 1
    TypeASCII = 2
    TypeShort = 3
    TypeLong = 4
    TypeRational = 5
    TypeUndefined = 7
    TypeSLong = 9
    TypeSRational = 10
End Enum

'Image Property Tag Constants
Public Enum PropertyTagId
    'https://docs.microsoft.com/en-us/windows/win32/gdiplus/-gdiplus-constant-property-tags-in-alphabetical-order
    'https://docs.microsoft.com/en-us/windows/win32/gdiplus/-gdiplus-constant-property-tags-in-numerical-order
    '   0x0... => &H...
    '   https://docs.microsoft.com/en-us/windows/win32/gdiplus/-gdiplus-constant-property-item-descriptions
    GpsVer = 0    '&H0&
    GpsLatitudeRef = 1    '&H1&
    GpsLatitude = 2    '&H2&
    GpsLongitudeRef = 3    '&H3&
    GpsLongitude = 4    '&H4&
    GpsAltitudeRef = 5    '&H5&
    GpsAltitude = 6    '&H6&
    GpsGpsTime = 7    '&H7&
    GpsGpsSatellites = 8    '&H8&
    GpsGpsStatus = 9    '&H9&
    GpsGpsMeasureMode = 10    '&HA&
    GpsGpsDop = 11    '&HB&
    GpsSpeedRef = 12    '&HC&
    GpsSpeed = 13    '&HD&
    GpsTrackRef = 14    '&HE&
    GpsTrack = 15    '&HF&
    GpsImgDirRef = 16    '&H10&
    GpsImgDir = 17    '&H11&
    GpsMapDatum = 18    '&H12&
    GpsDestLatRef = 19    '&H13&
    GpsDestLat = 20    '&H14&
    GpsDestLongRef = 21    '&H15&
    GpsDestLong = 22    '&H16
    GpsDestBearRef = 23    '&H17&
    GpsDestBear = 24    '&H18&
    GpsDestDistRef = 25    '&H19&
    GpsDestDist = 26    '&H1A&
    NewSubfileType = 254    '&HFE&
    SubfileType = 255    '&HFF&
    ImageWidth = 256    '&H100&
    ImageHeight = 257    '&H101&
    BitsPerSample = 258    '&H102&
    Compression = 259    '&H103&
    PhotometricInterp = 262    '&H106&
    ThreshHolding = 263    '&H107&
    CellWidth = 264    '&H108&
    CellHeight = 265    '&H109&
    FillOrder = 266    '&H10A&
    DocumentName = 269    '&H10D&
    ImageDescription = 270    '&H10E&
    EquipMake = 271    '&H10F&
    EquipModel = 272    '&H110&
    StripOffsets = 273    '&H111&
    Orientation = 274    '&H112&
    SamplesPerPixel = 277    '&H115&
    RowsPerStrip = 278    '&H116&
    StripBytesCount = 279    '&H117&
    MinSampleValue = 280    '&H118&
    MaxSampleValue = 281    '&H119&
    XResolution = 282    '&H11A&
    YResolution = 283    '&H11B&
    PlanarConfig = 284    '&H11C&
    PageName = 285    '&H11D&
    XPosition = 286    '&H11E&
    YPosition = 287    '&H11F&
    FreeOffset = 288    '&H120&
    FreeByteCounts = 289    '&H121&
    GrayResponseUnit = 290    '&H122&
    GrayResponseCurve = 291    '&H123&
    T4Option = 292    '&H124&
    T6Option = 293    '&H125&
    ResolutionUnit = 296    '&H128&
    PageNumber = 297    '&H129&
    TransferFunction = 301    '&H12D&
    SoftwareUsed = 305    '&H131&
    DateTime = 306    '&H132&
    Artist = 315    '&H13B&
    HostComputer = 316    '&H13C&
    Predictor = 317    '&H13D&
    WhitePoint = 318    '&H13E&
    PrimaryChromaticities = 319    '&H13F&
    ColorMap = 320    '&H140&
    HalftoneHints = 321    '&H141&
    TileWidth = 322    '&H142&
    TileLength = 323    '&H143&
    TileOffset = 324    '&H144&
    TileByteCounts = 325    '&H145&
    InkSet = 332    '&H14C&
    InkNames = 333    '&H14D&
    NumberOfInks = 334    '&H14E&
    DotRange = 336    '&H150&
    TargetPrinter = 337    '&H151&
    ExtraSamples = 338    '&H152&
    SampleFormat = 339    '&H153&
    TransferRange = 342    '&H156&
    JPEGProc = 512    '&H200&
    JPEGInterFormat = 513    '&H201&
    JPEGInterLength = 514    '&H202&
    JPEGRestartInterval = 515    '&H203&
    JPEGLosslessPredictors = 517    '&H205&
    JPEGPointTransforms = 518    '&H206&
    JPEGQTables = 519    '&H207&
    JPEGDCTables = 520    '&H208&
    JPEGACTables = 521    '&H209&
    YCbCrCoefficients = 529    '&H211&
    YCbCrSubsampling = 530    '&H212&
    YCbCrPositioning = 531    '&H213&
    REFBlackWhite = 532    '&H214&
    Gamma = 769    '&H301&
    ICCProfileDescriptor = 770    '&H302&
    SRGBRenderingIntent = 771    '&H303&
    ImageTitle = 800    '&H320&
    ResolutionXUnit = 20481    '&H5001&
    ResolutionYUnit = 20482    '&H5002&
    ResolutionXLengthUnit = 20483    '&H5003&
    ResolutionYLengthUnit = 20484    '&H5004&
    PrintFlags = 20485    '&H5005&
    PrintFlagsVersion = 20486    '&H5006&
    PrintFlagsCrop = 20487    '&H5007&
    PrintFlagsBleedWidth = 20488    '&H5008&
    PrintFlagsBleedWidthScale = 20489    '&H5009&
    HalftoneLPI = 20490    '&H500A&
    HalftoneLPIUnit = 20491    '&H500B&
    HalftoneDegree = 20492    '&H500C&
    HalftoneShape = 20493    '&H500D&
    HalftoneMisc = 20494    '&H500E&
    HalftoneScreen = 20495    '&H500F&
    JPEGQuality = 20496    '&H5010&
    GridSize = 20497    '&H5011&
    ThumbnailFormat = 20498    '&H5012&
    ThumbnailWidth = 20499    '&H5013&
    ThumbnailHeight = 20500    '&H5014&
    ThumbnailColorDepth = 20501    '&H5015&
    ThumbnailPlanes = 20502    '&H5016&
    ThumbnailRawBytes = 20503    '&H5017&
    ThumbnailSize = 20504    '&H5018&
    ThumbnailCompressedSize = 20505    '&H5019&
    ColorTransferFunction = 20506    '&H501A&
    ThumbnailData = 20507    '&H501B&
    ThumbnailImageWidth = 20512    '&H5020&
    ThumbnailImageHeight = 20513    '&H5021&
    ThumbnailBitsPerSample = 20514    '&H5022&
    ThumbnailCompression = 20515    '&H5023&
    ThumbnailPhotometricInterp = 20516    '&H5024&
    ThumbnailImageDescription = 20517    '&H5025&
    ThumbnailEquipMake = 20518    '&H5026&
    ThumbnailEquipModel = 20519    '&H5027&
    ThumbnailStripOffsets = 20520    '&H5028&
    ThumbnailOrientation = 20521    '&H5029&
    ThumbnailSamplesPerPixel = 20522    '&H502A&
    ThumbnailRowsPerStrip = 20523    '&H502B&
    ThumbnailStripBytesCount = 20524    '&H502C&
    ThumbnailResolutionX = 20525    '&H502D&
    ThumbnailResolutionY = 20526    '&H502E&
    ThumbnailPlanarConfig = 20527    '&H502F&
    ThumbnailResolutionUnit = 20528    '&H5030&
    ThumbnailTransferFunction = 20529    '&H5031&
    ThumbnailSoftwareUsed = 20530    '&H5032&
    ThumbnailDateTime = 20531    '&H5033&
    ThumbnailArtist = 20532    '&H5034&
    ThumbnailWhitePoint = 20533    '&H5035&
    ThumbnailPrimaryChromaticities = 20534    '&H5036&
    ThumbnailYCbCrCoefficients = 20535    '&H5037&
    ThumbnailYCbCrSubsampling = 20536    '&H5038&
    ThumbnailYCbCrPositioning = 20537    '&H5039&
    ThumbnailRefBlackWhite = 20538    '&H503A&
    ThumbnailCopyRight = 20539    '&H503B&
    LuminanceTable = 20624    '&H5090&
    ChrominanceTable = 20625    '&H5091&
    FrameDelay = 20736    '&H5100&
    LoopCount = 20737    '&H5101&
    GlobalPalette = 20738    '&H5102&
    IndexBackground = 20739    '&H5103&
    IndexTransparent = 20740    '&H5104&
    PixelUnit = 20752    '&H5110&
    PixelPerUnitX = 20753    '&H5111&
    PixelPerUnitY = 20754    '&H5112&
    PaletteHistogram = 20755    '&H5113&
    Copyright = 33432    '&H8298&
    ExifExposureTime = 33434    '&H829A&
    ExifFNumber = 33437    '&H829D&
    ExifIFD = 34665    '&H8769&
    ICCProfile = 34675    '&H8773&
    ExifExposureProg = 34850    '&H8822&
    ExifSpectralSense = 34852    '&H8824&
    GpsIFD = 34853    '&H8825&
    ExifISOSpeed = 34855    '&H8827&
    ExifOECF = 34856    '&H8828&
    ExifVer = 36864    '&H9000&
    ExifDTOrig = 36867    '&H9003&
    ExifDTDigitized = 36868    '&H9004&
    ExifCompConfig = 37121    '&H9101&
    ExifCompBPP = 37122    '&H9102&
    ExifShutterSpeed = 37377    '&H9201&
    ExifAperture = 37378    '&H9202&
    ExifBrightness = 37379    '&H9203&
    ExifExposureBias = 37380    '&H9204&
    ExifMaxAperture = 37381    '&H9205&
    ExifSubjectDist = 37382    '&H9206&
    ExifMeteringMode = 37383    '&H9207&
    ExifLightSource = 37384    '&H9208&
    ExifFlash = 37385    '&H9209&
    ExifFocalLength = 37386    '&H920A&
    ExifMakerNote = 37500    '&H927C&
    ExifUserComment = 37510    '&H9286&
    ExifDTSubsec = 37520    '&H9290&
    ExifDTOrigSS = 37521    '&H9291&
    ExifDTDigSS = 37522    '&H9292&
    ExifFPXVer = 40960    '&HA000&
    ExifColorSpace = 40961    '&HA001&
    ExifPixXDim = 40962    '&HA002&
    ExifPixYDim = 40963    '&HA003&
    ExifRelatedWav = 40964    '&HA004&
    ExifInterop = 40965    '&HA005&
    ExifFlashEnergy = 41483    '&HA20B&
    ExifSpatialFR = 41484    '&HA20C&
    ExifFocalXRes = 41486    '&HA20E&
    ExifFocalYRes = 41487    '&HA20F&
    ExifFocalResUnit = 41488    '&HA210&
    ExifSubjectLoc = 41492    '&HA214&
    ExifExposureIndex = 41493    '&HA215&
    ExifSensingMethod = 41495    '&HA217&
    ExifFileSource = 41728    '&HA300&
    ExifSceneType = 41729    '&HA301&
    ExifCfaPattern = 41730    '&HA302&
End Enum

Dim lGDIpToken                 As Long
Dim bGDIpInitialized           As Boolean
Dim lBitmap                    As Long
'-------------------------------------------------------------------------------
'-------------------------------------------------------------------------------


Private Function GDIErrorToString(ByVal lGDIError As Status) As String
    Select Case lGDIError
        Case GenericError
            GDIErrorToString = "Generic Error."
        Case InvalidParameter
            GDIErrorToString = "Invalid Parameter."
        Case OutOfMemory
            GDIErrorToString = "Out Of Memory."
        Case ObjectBusy
            GDIErrorToString = "Object Busy."
        Case InsufficientBuffer
            GDIErrorToString = "Insufficient Buffer."
        Case NotImplemented
            GDIErrorToString = "Not Implemented."
        Case Win32Error
            GDIErrorToString = "Win32 Error."
        Case WrongState
            GDIErrorToString = "Wrong State."
        Case Aborted
            GDIErrorToString = "Aborted."
        Case FileNotFound
            GDIErrorToString = "File Not Found."
        Case ValueOverflow
            GDIErrorToString = "Value Overflow."
        Case AccessDenied
            GDIErrorToString = "Access Denied."
        Case UnknownImageFormat
            GDIErrorToString = "Unknown Image Format."
        Case FontFamilyNotFound
            GDIErrorToString = "FontFamily Not Found."
        Case FontStyleNotFound
            GDIErrorToString = "FontStyle Not Found."
        Case NotTrueTypeFont
            GDIErrorToString = "Not TrueType Font."
        Case UnsupportedGdiplusVersion
            GDIErrorToString = "Unsupported Gdiplus Version."
        Case GdiplusNotInitialized
            GDIErrorToString = "Gdiplus Not Initialized."
        Case PropertyNotFound
            GDIErrorToString = "Property Not Found."
        Case PropertyNotSupported
            GDIErrorToString = "Property Not Supported."
        Case Else
            GDIErrorToString = "Unknown Error."
    End Select
End Function

Private Function GetPropertyValue(ByVal PropertyItemLength As Long, ByVal PropertyItemValue As Long, ByVal lPropertyType As PropertyTagType) As String
    Dim byteRetValue          As Byte
    Dim lRetValue             As Long
    Dim iRetValue             As Integer
    Dim bytProperty()         As Byte
    Dim sProperty             As String
    Dim DataLength            As Long
    Dim Numerator             As Long
    Dim Denominator           As Long
    Dim sTemp                 As String
    Dim i                     As Long

    If PropertyItemLength = 0 Then
        GetPropertyValue = ""
        Exit Function
    End If

    Select Case lPropertyType
        Case PropertyTagType.TypeByte
            DataLength = 1
            ReDim bytProperty(PropertyItemLength - 1)
            Call CopyMemory(bytProperty(0), ByVal PropertyItemValue, PropertyItemLength)
            Call CopyMemory(byteRetValue, bytProperty(0), DataLength)
            Erase bytProperty
            GetPropertyValue = byteRetValue
        Case PropertyTagType.TypeASCII
            sProperty = Space$(lstrlenW(ByVal PropertyItemValue))
            Call lstrcpyW(ByVal StrPtr(sProperty), ByVal PropertyItemValue)
            GetPropertyValue = Trim$(Left$(StrConv(sProperty, vbUnicode), PropertyItemLength - 1))
        Case PropertyTagType.TypeShort
            DataLength = 2
            ReDim bytProperty(PropertyItemLength - 1)
            Call CopyMemory(bytProperty(0), ByVal PropertyItemValue, PropertyItemLength)
            Call CopyMemory(iRetValue, bytProperty(0), DataLength)
            Erase bytProperty
            GetPropertyValue = iRetValue
        Case PropertyTagType.TypeLong, PropertyTagType.TypeSLong
            DataLength = 4
            ReDim bytProperty(PropertyItemLength - 1)
            Call CopyMemory(bytProperty(0), ByVal PropertyItemValue, PropertyItemLength)
            Call CopyMemory(lRetValue, bytProperty(0), DataLength)
            Erase bytProperty
            GetPropertyValue = lRetValue
        Case PropertyTagType.TypeRational, PropertyTagType.TypeSRational
            DataLength = 8
            ReDim bytProperty(PropertyItemLength - 1)
            Call CopyMemory(bytProperty(0), ByVal PropertyItemValue, PropertyItemLength)
            Call CopyMemory(Numerator, bytProperty(0), DataLength / 2)
            Call CopyMemory(Denominator, bytProperty(0 + (DataLength / 2)), DataLength / 2)
            Erase bytProperty
            GetPropertyValue = CStr(Numerator) & "/" & CStr(Denominator)
            GetPropertyValue = Eval(GetPropertyValue)
        Case PropertyTagType.TypeUndefined
            ReDim bytProperty(PropertyItemLength - 1)
            Call CopyMemory(bytProperty(0), ByVal PropertyItemValue, PropertyItemLength)
            For i = 1 To PropertyItemLength - 1
                If i > 1 Then sProperty = sProperty & Chr$(32)
                sTemp = Hex$(bytProperty(i - 1))
                If Len(sTemp) = 1 Then sTemp = "0" & sTemp
                sProperty = sProperty & sTemp
            Next i
            Erase bytProperty
            GetPropertyValue = sProperty
    End Select
End Function

Private Function PropertyTagIdToString(ByVal lPropertyTagId As PropertyTagId) As String
    Select Case lPropertyTagId
        Case &H0&
            PropertyTagIdToString = "GPS Ver"
        Case &H1&
            PropertyTagIdToString = "GPS Latitude Ref"
        Case &H2&
            PropertyTagIdToString = "GPS Latitude"
        Case &H3&
            PropertyTagIdToString = "GPS Longitude Ref"
        Case &H4&
            PropertyTagIdToString = "GPS Longitude"
        Case &H5&
            PropertyTagIdToString = "GPS Altitude Ref"
        Case &H6&
            PropertyTagIdToString = "GPS Altitude"
        Case &H7&
            PropertyTagIdToString = "GPS Time"
        Case &H8&
            PropertyTagIdToString = "GPS Satellites"
        Case &H9&
            PropertyTagIdToString = "GPS Status"
        Case &HA&
            PropertyTagIdToString = "GPS Measure Mode"
        Case &HB&
            PropertyTagIdToString = "GPS Dop"
        Case &HC&
            PropertyTagIdToString = "GPS Speed Ref"
        Case &HD&
            PropertyTagIdToString = "GPS Speed"
        Case &HE&
            PropertyTagIdToString = "GPS Track Ref"
        Case &HF&
            PropertyTagIdToString = "GPS Track"
        Case &H10&
            PropertyTagIdToString = "GPS Img Dir Ref"
        Case &H11&
            PropertyTagIdToString = "GPS Img Dir"
        Case &H12&
            PropertyTagIdToString = "GPS Map Datum"
        Case &H13&
            PropertyTagIdToString = "GPS Dest Lat Ref"
        Case &H14&
            PropertyTagIdToString = "GPS Dest Lat"
        Case &H15&
            PropertyTagIdToString = "GPS Dest Long Ref"
        Case &H16&
            PropertyTagIdToString = "GPS Dest Long"
        Case &H17&
            PropertyTagIdToString = "GPS Dest Bear Ref"
        Case &H18&
            PropertyTagIdToString = "GPS Dest Bear"
        Case &H19&
            PropertyTagIdToString = "GPS Dest Dist Ref"
        Case &H1A&
            PropertyTagIdToString = "GPS Des tDist"
        Case &HFE&
            PropertyTagIdToString = "New Subfile Type"
        Case &HFF&
            PropertyTagIdToString = "Sub file Type"
        Case &H100&
            PropertyTagIdToString = "Image Width"
        Case &H101&
            PropertyTagIdToString = "Image Height"
        Case &H102&
            PropertyTagIdToString = "Bits Per Sample"
        Case &H103&
            PropertyTagIdToString = "Compression"
        Case &H106&
            PropertyTagIdToString = "Photometric Interp"
        Case &H107&
            PropertyTagIdToString = "Thresh Holding"
        Case &H108&
            PropertyTagIdToString = "Cell Width"
        Case &H109&
            PropertyTagIdToString = "Cell Height"
        Case &H10A&
            PropertyTagIdToString = "Fill Order"
        Case &H10D&
            PropertyTagIdToString = "Document Name"
        Case &H10E&
            PropertyTagIdToString = "Image Description"
        Case &H10F&
            PropertyTagIdToString = "Equip Make"
        Case &H110&
            PropertyTagIdToString = "Equip Model"
        Case &H111&
            PropertyTagIdToString = "Strip Offsets"
        Case &H112&
            PropertyTagIdToString = "Orientation"
        Case &H115&
            PropertyTagIdToString = "Samples Per Pixel"
        Case &H116&
            PropertyTagIdToString = "Rows Per Strip"
        Case &H117&
            PropertyTagIdToString = "Strip Bytes Count"
        Case &H118&
            PropertyTagIdToString = "Min Sample Value"
        Case &H119&
            PropertyTagIdToString = "Max Sample Value"
        Case &H11A&
            PropertyTagIdToString = "X Resolution"
        Case &H11B&
            PropertyTagIdToString = "Y Resolution"
        Case &H11C&
            PropertyTagIdToString = "Planar Config"
        Case &H11D&
            PropertyTagIdToString = "Page Name"
        Case &H11E&
            PropertyTagIdToString = "X Position"
        Case &H11F&
            PropertyTagIdToString = "Y Position"
        Case &H120&
            PropertyTagIdToString = "Free Offset"
        Case &H121&
            PropertyTagIdToString = "Free Byte Counts"
        Case &H122&
            PropertyTagIdToString = "Gray Response Unit"
        Case &H123&
            PropertyTagIdToString = "Gray Response Curve"
        Case &H124&
            PropertyTagIdToString = "T4 Option"
        Case &H125&
            PropertyTagIdToString = "T6 Option"
        Case &H128&
            PropertyTagIdToString = "Resolution Unit"
        Case &H129&
            PropertyTagIdToString = "Page Number"
        Case &H12D&
            PropertyTagIdToString = "Transfer Function"
        Case &H131&
            PropertyTagIdToString = "Software Used"
        Case &H132&
            PropertyTagIdToString = "Date/Time"
        Case &H13B&
            PropertyTagIdToString = "Artist"
        Case &H13C&
            PropertyTagIdToString = "Host Computer"
        Case &H13D&
            PropertyTagIdToString = "Predictor"
        Case &H13E&
            PropertyTagIdToString = "White Point"
        Case &H13F&
            PropertyTagIdToString = "Primary Chromaticities"
        Case &H140&
            PropertyTagIdToString = "Color Map"
        Case &H141&
            PropertyTagIdToString = "Halftone Hints"
        Case &H142&
            PropertyTagIdToString = "Tile Width"
        Case &H143&
            PropertyTagIdToString = "Tile Length"
        Case &H144&
            PropertyTagIdToString = "Tile Offset"
        Case &H145&
            PropertyTagIdToString = "Tile Byte Counts"
        Case &H14C&
            PropertyTagIdToString = "Ink Set"
        Case &H14D&
            PropertyTagIdToString = "Ink Names"
        Case &H14E&
            PropertyTagIdToString = "Number Of Inks"
        Case &H150&
            PropertyTagIdToString = "Dot Range"
        Case &H151&
            PropertyTagIdToString = "Target Printer"
        Case &H152&
            PropertyTagIdToString = "ExtraS amples"
        Case &H153&
            PropertyTagIdToString = "Sample Format"
        Case &H156&
            PropertyTagIdToString = "Transfer Range"
        Case &H200&
            PropertyTagIdToString = "JPEG Proc"
        Case &H201&
            PropertyTagIdToString = "JPEG Inter Format"
        Case &H202&
            PropertyTagIdToString = "JPEG Inter Length"
        Case &H203&
            PropertyTagIdToString = "JPEG Restart Interval"
        Case &H205&
            PropertyTagIdToString = "JPEG Lossless Predictors"
        Case &H206&
            PropertyTagIdToString = "JPEG Point Transforms"
        Case &H207&
            PropertyTagIdToString = "JPEG Q Tables"
        Case &H208&
            PropertyTagIdToString = "JPEG DC Tables"
        Case &H209&
            PropertyTagIdToString = "JPEG AC Tables"
        Case &H211&
            PropertyTagIdToString = "YCb Cr Coefficients"
        Case &H212&
            PropertyTagIdToString = "YCb Cr Subsampling"
        Case &H213&
            PropertyTagIdToString = "YCb Cr Positioning"
        Case &H214&
            PropertyTagIdToString = "REF Black White"
        Case &H301&
            PropertyTagIdToString = "Gamma"
        Case &H302&
            PropertyTagIdToString = "ICC Profile Descriptor"
        Case &H303&
            PropertyTagIdToString = "SRGB Rendering Intent"
        Case &H320&
            PropertyTagIdToString = "Image Title"
        Case &H5001&
            PropertyTagIdToString = "Resolution X Unit"
        Case &H5002&
            PropertyTagIdToString = "Resolution Y Unit"
        Case &H5003&
            PropertyTagIdToString = "Resolution X Length Unit"
        Case &H5004&
            PropertyTagIdToString = "Resolution Y Length Unit"
        Case &H5005&
            PropertyTagIdToString = "Print Flags"
        Case &H5006&
            PropertyTagIdToString = "Print Flags Version"
        Case &H5007&
            PropertyTagIdToString = "Print Flags Crop"
        Case &H5008&
            PropertyTagIdToString = "Print Flags Bleed Width"
        Case &H5009&
            PropertyTagIdToString = "Print Flags Bleed Width Scale"
        Case &H500A&
            PropertyTagIdToString = "Halftone LPI"
        Case &H500B&
            PropertyTagIdToString = "Halftone LPI Unit"
        Case &H500C&
            PropertyTagIdToString = "Halftone Degree"
        Case &H500D&
            PropertyTagIdToString = "Halftone Shape"
        Case &H500E&
            PropertyTagIdToString = "Halftone Misc"
        Case &H500F&
            PropertyTagIdToString = "Halftone Screen"
        Case &H5010&
            PropertyTagIdToString = "JPEG Quality"
        Case &H5011&
            PropertyTagIdToString = "Grid Size"
        Case &H5012&
            PropertyTagIdToString = "Thumbnail Format"
        Case &H5013&
            PropertyTagIdToString = "Thumbnail Width"
        Case &H5014&
            PropertyTagIdToString = "Thumbnail Height"
        Case &H5015&
            PropertyTagIdToString = "Thumbnail Color Depth"
        Case &H5016&
            PropertyTagIdToString = "Thumbnail Planes"
        Case &H5017&
            PropertyTagIdToString = "Thumbnail Raw Bytes"
        Case &H5018&
            PropertyTagIdToString = "Thumbnail Size"
        Case &H5019&
            PropertyTagIdToString = "Thumbnail Compressed Size"
        Case &H501A&
            PropertyTagIdToString = "Color Transfer Function"
        Case &H501B&
            PropertyTagIdToString = "Thumbnail Data"
        Case &H5020&
            PropertyTagIdToString = "Thumbnail Image Width"
        Case &H5021&
            PropertyTagIdToString = "Thumbnail Image Height"
        Case &H5022&
            PropertyTagIdToString = "Thumbnail Bits Per Sample"
        Case &H5023&
            PropertyTagIdToString = "Thumbnail Compression"
        Case &H5024&
            PropertyTagIdToString = "Thumbnail Photometric Interp"
        Case &H5025&
            PropertyTagIdToString = "Thumbnail Image Description"
        Case &H5026&
            PropertyTagIdToString = "Thumbnail EquipMake"
        Case &H5027&
            PropertyTagIdToString = "Thumbnail EquipModel"
        Case &H5028&
            PropertyTagIdToString = "Thumbnail Strip Offsets"
        Case &H5029&
            PropertyTagIdToString = "Thumbnail Orientation"
        Case &H502A&
            PropertyTagIdToString = "Thumbnail Samples Per Pixel"
        Case &H502B&
            PropertyTagIdToString = "Thumbnail Rows Per Strip"
        Case &H502C&
            PropertyTagIdToString = "Thumbnail Strip Bytes Count"
        Case &H502D&
            PropertyTagIdToString = "Thumbnail Resolution X"
        Case &H502E&
            PropertyTagIdToString = "Thumbnail Resolution Y"
        Case &H502F&
            PropertyTagIdToString = "Thumbnail Planar Config"
        Case &H5030&
            PropertyTagIdToString = "Thumbnail Resolution Unit"
        Case &H5031&
            PropertyTagIdToString = "Thumbnail Transfer Function"
        Case &H5032&
            PropertyTagIdToString = "Thumbnail Software Used"
        Case &H5033&
            PropertyTagIdToString = "Thumbnail Date/Time"
        Case &H5034&
            PropertyTagIdToString = "Thumbnail Artist"
        Case &H5035&
            PropertyTagIdToString = "Thumbnail White Point"
        Case &H5036&
            PropertyTagIdToString = "Thumbnail Primary Chromaticities"
        Case &H5037&
            PropertyTagIdToString = "Thumbnail YCb Cr Coefficients"
        Case &H5038&
            PropertyTagIdToString = "Thumbnail YCb Cr Subsampling"
        Case &H5039&
            PropertyTagIdToString = "Thumbnail YCb Cr Positioning"
        Case &H503A&
            PropertyTagIdToString = "Thumbnail Ref Black White"
        Case &H503B&
            PropertyTagIdToString = "Thumbnail Copyright"
        Case &H5090&
            PropertyTagIdToString = "Luminance Table"
        Case &H5091&
            PropertyTagIdToString = "Chrominance Table"
        Case &H5100&
            PropertyTagIdToString = "Frame Delay"
        Case &H5101&
            PropertyTagIdToString = "Loop Count"
        Case &H5102&
            PropertyTagIdToString = "Global Palette"
        Case &H5103&
            PropertyTagIdToString = "Index Background"
        Case &H5104&
            PropertyTagIdToString = "Index Transparent"
        Case &H5110&
            PropertyTagIdToString = "Pixe lUnit"
        Case &H5111&
            PropertyTagIdToString = "Pixel Per Unit X"
        Case &H5112&
            PropertyTagIdToString = "Pixel Per Unit Y"
        Case &H5113&
            PropertyTagIdToString = "Palette Histogram"
        Case &H8298&
            PropertyTagIdToString = "Copyright"
        Case &H829A&
            PropertyTagIdToString = "Exif Exposure Time"
        Case &H829D&
            PropertyTagIdToString = "Exif F Number"
        Case &H8769&
            PropertyTagIdToString = "Exif IFD"
        Case &H8773&
            PropertyTagIdToString = "ICC Profile"
        Case &H8822&
            PropertyTagIdToString = "Exif Exposure Prog"
        Case &H8824&
            PropertyTagIdToString = "Exif Spectral Sense"
        Case &H8825&
            PropertyTagIdToString = "GPS IFD"
        Case &H8827&
            PropertyTagIdToString = "Exif ISOS peed"
        Case &H8828&
            PropertyTagIdToString = "Exif OECF"
        Case &H9000&
            PropertyTagIdToString = "Exif Ver"
        Case &H9003&
            PropertyTagIdToString = "Exif DT Orig"
        Case &H9004&
            PropertyTagIdToString = "Exif DT Digitized"
        Case &H9101&
            PropertyTagIdToString = "Exif Comp Config"
        Case &H9102&
            PropertyTagIdToString = "Exif Comp BPP"
        Case &H9201&
            PropertyTagIdToString = "Exif Shutter Speed"
        Case &H9202&
            PropertyTagIdToString = "Exif Aperture"
        Case &H9203&
            PropertyTagIdToString = "Exif Brightness"
        Case &H9204&
            PropertyTagIdToString = "Exif Exposure Bias"
        Case &H9205&
            PropertyTagIdToString = "Exif Max Aperture"
        Case &H9206&
            PropertyTagIdToString = "Exif Subject Dist"
        Case &H9207&
            PropertyTagIdToString = "Exif Metering Mode"
        Case &H9208&
            PropertyTagIdToString = "Exif Light Source"
        Case &H9209&
            PropertyTagIdToString = "Exif Flash"
        Case &H920A&
            PropertyTagIdToString = "Exif Focal Length"
        Case &H927C&
            PropertyTagIdToString = "Exif Maker Note"
        Case &H9286&
            PropertyTagIdToString = "Exif User Comment"
        Case &H9290&
            PropertyTagIdToString = "Exif DT Subsec"
        Case &H9291&
            PropertyTagIdToString = "Exif DT Orig SS"
        Case &H9292&
            PropertyTagIdToString = "Exif DT Dig SS"
        Case &HA000&
            PropertyTagIdToString = "Exif FPX Ver"
        Case &HA001&
            PropertyTagIdToString = "Exif Color Space"
        Case &HA002&
            PropertyTagIdToString = "Exif Pix X Dim"
        Case &HA003&
            PropertyTagIdToString = "Exif Pix Y Dim"
        Case &HA004&
            PropertyTagIdToString = "Exif Related Wav"
        Case &HA005&
            PropertyTagIdToString = "Exif Interop"
        Case &HA20B&
            PropertyTagIdToString = "Exif Flash Energy"
        Case &HA20C&
            PropertyTagIdToString = "Exif Spatial FR"
        Case &HA20E&
            PropertyTagIdToString = "Exif Focal X Res"
        Case &HA20F&
            PropertyTagIdToString = "Exif Focal Y Res"
        Case &HA210&
            PropertyTagIdToString = "Exif Focal Res Unit"
        Case &HA214&
            PropertyTagIdToString = "Exif Subject Loc"
        Case &HA215&
            PropertyTagIdToString = "Exif Exposure Index"
        Case &HA217&
            PropertyTagIdToString = "Exif Sensing Method"
        Case &HA300&
            PropertyTagIdToString = "Exif File Source"
        Case &HA301&
            PropertyTagIdToString = "Exif Scene Type"
        Case &HA302&
            PropertyTagIdToString = "Exif Cfa Pattern"
        Case Else
            PropertyTagIdToString = "Unknown Property"
    End Select
End Function

'---------------------------------------------------------------------------------------
' Procedure : GetImageProperties
' Author    : Daniel Pineault, CARDA Consultants Inc.
' Website   : http://www.cardaconsultants.com
' Purpose   : Returns all the properties of a given image file
' Copyright : The following is release as Attribution-ShareAlike 4.0 International
'             (CC BY-SA 4.0) - https://creativecommons.org/licenses/by-sa/4.0/
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' sFile     : Fully qualified path and filename of the image file to get info about
'
' Usage:
' ~~~~~~
' sImgProperties = GetImageProperties("C:\Temp\IMG_20210508_170154.jpg")
'
' Revision History:
' Rev       Date(yyyy-mm-dd)        Description
' **************************************************************************************
' 1         2022-01-09              Initial Blog Release
'---------------------------------------------------------------------------------------
Public Function GetImageProperties(sFile As String) As String
    On Error GoTo Error_Handler
    Dim GDIpStartupInput      As GDIPlusStartupInput
    Dim GDIStatus             As Status
    Dim lAllPropertiesSize    As Long
    Dim lAllPropertiesCount   As Long
    Dim PI()                  As PropertyItem
    Dim sOutput               As String
    Dim propItem              As Long

    'Start GDI
    '-------------------------------------------------------------------------------------
    If bGDIpInitialized = False Then
        GDIpStartupInput.GdiPlusVersion = 1
        GDIStatus = GdiplusStartup(lGDIpToken, GDIpStartupInput, ByVal 0)
        If GDIStatus <> Status.OK Then
            MsgBox "Unable to start the GDI+ API" & vbCrLf & vbCrLf & GDIErrorToString(GDIStatus), vbCritical Or vbOKOnly, "Operation Aborted"
            GoTo Error_Handler_Exit
        Else
            bGDIpInitialized = True
        End If
    End If

    'Load our Image to work with
    '-------------------------------------------------------------------------------------
    'In case we already have something in memory let's dispose of it properly
    If lBitmap <> 0 Then
        GDIStatus = GdipDisposeImage(lBitmap)
        If GDIStatus <> Status.OK Then
            MsgBox "Unable to dispose of the current image in memory" & vbCrLf & vbCrLf & GDIErrorToString(GDIStatus), vbCritical Or vbOKOnly, "Operation Aborted"
            GoTo Error_Handler_Exit
        End If
    End If
    'Now let's proceed with loading the actual image we want to work with
    GDIStatus = GdipCreateBitmapFromFile(StrPtr(sFile), lBitmap)
    If GDIStatus <> Status.OK Then
        MsgBox "Unable to load the specified image" & vbCrLf & vbCrLf & GDIErrorToString(GDIStatus), vbCritical Or vbOKOnly, "Operation Aborted"
        GoTo Error_Handler_Exit
    End If

    'Work with the image
    '-------------------------------------------------------------------------------------
    If lBitmap Then
        GDIStatus = GdipGetPropertySize(lBitmap, lAllPropertiesSize, lAllPropertiesCount)
        If GDIStatus <> Status.OK Then
            MsgBox "Unable to determine the size of all the properties" & vbCrLf & vbCrLf & GDIErrorToString(GDIStatus), vbCritical Or vbOKOnly, "Operation Aborted"
            GoTo Error_Handler_Exit
        End If
        If (lAllPropertiesCount > 0) Then
            ReDim PI(0 To lAllPropertiesSize \ Len(PI(0)) - 1)
            GDIStatus = GdipGetAllPropertyItems(lBitmap, lAllPropertiesSize, lAllPropertiesCount, PI(0))
            If GDIStatus <> Status.OK Then
                MsgBox "Unable to get all the image properties" & vbCrLf & vbCrLf & GDIErrorToString(GDIStatus), vbCritical Or vbOKOnly, "Operation Aborted"
                GoTo Error_Handler_Exit
            End If
            For propItem = 0 To lAllPropertiesCount - 1
                If propItem <> 0 Then sOutput = sOutput & vbNewLine
                sOutput = sOutput & PropertyTagIdToString(PI(propItem).id) & ": " & GetPropertyValue(PI(propItem).Length, PI(propItem).Value, PI(propItem).type)
            Next propItem
            GetImageProperties = sOutput

            Erase PI
        End If
    End If

Error_Handler_Exit:
    On Error Resume Next
    'Shutdown GDI
    '-------------------------------------------------------------------------------------
    If bGDIpInitialized = True Then
        If lBitmap <> 0 Then
            GDIStatus = GdipDisposeImage(lBitmap)
            If GDIStatus <> Status.OK Then
                MsgBox "Unable to dispose of the processed image" & vbCrLf & vbCrLf & GDIErrorToString(GDIStatus), vbCritical Or vbOKOnly, "Operation Aborted"
                Exit Function
            Else
                lBitmap = 0
            End If
        End If
        GDIStatus = GdiplusShutdown(lGDIpToken)
        If GDIStatus <> Status.OK Then
            MsgBox "Unable to shutdown the GDI+ API" & vbCrLf & vbCrLf & GDIErrorToString(GDIStatus), vbCritical Or vbOKOnly, "Operation Aborted"
            Exit Function
        Else
            bGDIpInitialized = False
        End If
    End If
    Exit Function

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

You can call it by doing

? GetImageProperties("C:\Temp\IMG_20210508_170154.jpg")

and it will return something along the lines of

Image Width: 2736
Image Height: 3648
Bits Per Sample: 8
Image Description: rpt
Equip Make: HUAWEI
Equip Model: ELE-L04
Orientation: 0
X Resolution: 72
Y Resolution: 72
Resolution Unit: 2
Software Used: ELE-L04 11.0.0.146(C792E5R1P3)
Date/Time: 2021:05:08 17:01:56
YCb Cr Positioning: 1
Document Name: 
Exif Exposure Time: 0.02
Exif F Number: 1.8
Exif Exposure Prog: 2
Exif ISOS peed: 250
Exif Ver: 30 32 31
Exif DT Orig: 2021:05:08 17:01:56
Exif DT Digitized: 2021:05:08 17:01:56
Exif Comp Config: 01 02 03
Exif Comp BPP: 0.95
Exif Shutter Speed: 29.8973
Exif Aperture: 1.69
Exif Brightness: 0
Exif Exposure Bias: 0
Exif Max Aperture: 1.69
Exif Metering Mode: 5
Exif Light Source: 1
Exif Flash: 0
Exif Focal Length: 5.58
Exif Maker Note: 23 23 23 23 0A 00 00 00 AE C8 33 01 01 22 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 01 00 00 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
Exif DT Subsec: 573235
Exif DT Orig SS: 573235
Exif DT Dig SS: 573235
Exif FPX Ver: 30 31 30
Exif Color Space: 1
Exif Pix X Dim: 2736
Exif Pix Y Dim: 3648
Interoperability Index: R98
Interoperability Version: 30 31 30
Exif Sensing Method: 2
Exif File Source: 
Exif Scene Type: 
Exif Custom Rendered: 1
Exif Exposure Mode: 0
Exif White Balance: 0
Exif Digital Zoom Ratio: 1
Exif Focal Length In 35mm Film: 27
Exif Scene Capture Type: 0
Exif Gain Control: 0
Exif Contrast: 0
Exif Saturation: 0
Exif Sharpness: 0
Unknown Property: 0
Unknown Property: 69 70 70
Thumbnail Data: 255
Thumbnail Image Width: 384
Thumbnail Image Height: 512
Thumbnail Compression: 6
Thumbnail Orientation: 0
Thumbnail Resolution X: 72
Thumbnail Resolution Y: 72
Thumbnail Resolution Unit: 2
JPEG Inter Format: 1458
JPEG Inter Length: 34016
Chrominance Table: 1
Luminance Table: 1

Stay tuned for my next article on GDI+, things are just starting to become interesting!

A Few Resources on the Subject