Jump to content

Rethinking _GDIPlus_Image*PropertyItem* functions


c.haslam
 Share

Recommended Posts

Will you change my GDIPlus* functions to set @error and @extended the same as other GDIPlus* functions, or do you wish me to change my functions?

As cGDIPlus_ImageSetPropertyItem is now, it does not detect a number of user errors.

Given

Func cGDIPlus_ImageSetPropertyItem($hImage, $a1PropertyItem)
 Local $iId = $a1PropertyItem[0]
 Local $iLength = $a1PropertyItem[1]
 Local $iType = $a1PropertyItem[2]
 Local $a1values = $a1PropertyItem[3]

Some of the errors it does not detect are:

  • $iType is not one of the $GDIP_PROPERTYTAGTYPE* constants
  • $a1Values is not a 1-d array (a crasher in my function)
  • If $iType not ASCII, $iLength and UBound($a1values) don't agree
  • If $iType is $GDIP_PROPERTYTAGTYPELONG, $a1values[0] not a multiple of 4
  • $iId is not an Int
  • UBound($a1PropertyItem) not 4 ( a crasher in my function)

All of the non-crashers above are not caught by my function. It has

Local $aResult = DllCall($__g_hGDIPDll, "uint", "GdipSetPropertyItem", "handle", $hImage, "struct*", $tPropItem)
    If @error Then Return SetError(@error, @extended, -1)
    If $aResult[0] Then Return SetError(10, $aResult[0], -1)

In these situations (and perhaps others) should my function  set @error (and @extended)? If so, to what values?

 

Spoiler

CDebug Dumps values of variables including arrays and DLL structs, to a GUI, to the Console, and to the Clipboard

 

Link to comment
Share on other sites

Here are my functions with error checking:

#AutoIt3Wrapper_UseX64=n
#include <GDIPlus.au3>
#include <Array.au3>
#include <WinAPIDiag.au3>


;--------- should be moved to GDIPlusConstants.au3
Global Const $GDIP_PROPERTYTAGTYPEBYTE = 1, $GDIP_PROPERTYTAGTYPEASCII = 2, $GDIP_PROPERTYTAGTYPESHORT = 3, $GDIP_PROPERTYTAGTYPELONG = 4, _
        $GDIP_PROPERTYTAGTYPERATIONAL = 5, $GDIP_PROPERTYTAGTYPEUNDEFINED = 7, $GDIP_PROPERTYTAGTYPESLONG = 9, _
        $GDIP_PROPERTYTAGTYPESRATIONAL = 10
;-------------------------------------------------

Global $aPropID

Example()


Func Example()
    Local $sImagefile = FileOpenDialog("Select a JPG image file", "", "JPG Image (*.jpg;*.jpeg)")
    If @error Then Exit
    _GDIPlus_Startup()
    Local $hImage = _GDIPlus_ImageLoadFromFile($sImagefile)
    $aPropID = _GDIPlus_ImageGetPropertyIdList($hImage)
    Local $a2PropertyItems = cGDIPlus_ImageGetAllPropertyItems($hImage)
    DisplayPropertyItems($a2PropertyItems)

    Local $hImageClone = _GDIPlus_ImageClone($hImage) ; copy image without property items

    Local $a1[4]
    For $i = 0 To UBound($a2PropertyItems, 1) - 1 ; set all property items
        For $j = 0 To 3
            $a1[$j] = $a2PropertyItems[$i][$j]
        Next
        cGDIPlus_ImageSetPropertyItem($hImageClone, $a1)
    Next

    ; update a property item
    Local $a1value = ['Your name']
    Local $a1 = [0x13B, 20, 2, $a1value] ; Artist
    cGDIPlus_ImageSetPropertyItem($hImageClone, $a1)

    ; add a property item
    Local $a1value = [2000]
    Local $a1 = [0xa002, -1, 4, $a1value] ; ExifWidth
    cGDIPlus_ImageSetPropertyItem($hImageClone, $a1)

    Local $a1value = [0xFFFE01]
    Local $a1 = [0x927C, 3, 7, $a1value] ; MakerNote
    cGDIPlus_ImageSetPropertyItem($hImageClone, $a1)

    $a2PropertyItems = cGDIPlus_ImageGetAllPropertyItems($hImageClone)
    DisplayPropertyItems($a2PropertyItems)

    $a1 = cGDIPlus_ImageGetPropertyItem($hImageClone, 0x927c) ;https://msdn.microsoft.com/en-us/library/ms534417.aspx
    Local $a2[1][4]
    For $i = 0 To 3
        $a2[0][$i] = $a1[$i]
    Next
    DisplayPropertyItems($a2)

    _GDIPlus_Shutdown()
EndFunc   ;==>Example

Func DisplayPropertyItems($a2PropertyItems)
    ; Collapse values arrays so _ArrayDisplay can display them
    Local $a2 = $a2PropertyItems, $iPos
    ReDim $a2[UBound($a2PropertyItems)][5]

    For $i = 0 To UBound($a2, 1) - 1
        $a2[$i][0] = '0x' & Hex($a2PropertyItems[$i][0], 4)
        $a2[$i][3] = ''
        For $j = 0 To UBound($a2PropertyItems[$i][3]) - 1
            $a2[$i][3] &= ($a2PropertyItems[$i][3])[$j] & '|'
        Next
        $a2[$i][3] = StringTrimRight($a2[$i][3], 1)
        $iPos = _ArraySearch($aPropID, $a2[$i][0])
        If $iPos > -1 Then $a2[$i][4] = $aPropID[$iPos][1]
    Next
    _ArrayDisplay($a2, "Image Property Items", Default, 0, Default, "Identifier|Size|Type|Value|Description")
EndFunc   ;==>DisplayPropertyItems

; #FUNCTION# ====================================================================================================================
; Name ..........: cGDIPlus_ImageGetAllPropertyItems
; Description ...: Gets all property item (piece of meta data) from an Image object
; Syntax ........: cGDIPlus_ImageGetAllPropertyItems($hImage)
; Parameters ....: $hImage              - Pointer to an image object
; Return values .: Success: Array in which each row contains these columns:
;                      [0] - identifier
;                      [1] - size, in bytes, of the value array
;                      [2] - type of value(s) in the value array
;                      [3] - value array
;                  Failure: sets the @error flag to:
;                      1:  DllCall failed.  Common cause: _GIDPlus_Startup() has not been called
;                     10:  @extended may contain GPSTATUS error code ($GPID_ERR* see GDIPlusConstants.au3).
; Author ........: c.haslam
; Modified ......:
; Remarks .......: types: unsigned byte = 1, ASCII string = 2, unsigned short = 3, unsigned long = 4,
;                         unsinged rational = 5, signed byte = 6, undefined = 7, signed long = 9,
;                         signed rational = 10
;+
;                   For list of EXIF property items, see https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html
;                  + and https://www.media.mit.edu/pia/Research/deepview/exif.html
; Related .......: cGDIPlus_ImageGetPropertyItem, _GDIPlus_ImageGetPropertyIdList, cGDIPlus_ImageSetPropertyItem
; Link ..........: https://msdn.microsoft.com/en-us/library/windows/desktop/ms535390(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/windows/desktop/ms534493(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/windows/desktop/ms534414(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/ms534416.aspx#_gdiplus_constant_propertytaggpsver
; Example .......: Yes
; ===============================================================================================================================

Func cGDIPlus_ImageGetAllPropertyItems($hImage)
    Local $aResult = DllCall($__g_hGDIPDll, "uint", "GdipGetPropertySize", "handle", $hImage, "uint*", 0, "uint*", 0)
    If @error Then Return SetError(@error, @extended, -1)
    If $aResult[0] Then Return SetError(10, $aResult[0], -1)
    Local $iTotalBufferSize = $aResult[2]
    Local $iNumProperties = $aResult[3]

    Local $tBuffer = DllStructCreate("byte[" & $iTotalBufferSize & "]")
    Local $pBuffer = DllStructGetPtr($tBuffer)

    $aResult = DllCall($__g_hGDIPDll, "uint", "GdipGetAllPropertyItems", "handle", $hImage, _
            "uint", $iTotalBufferSize, "uint", $iNumProperties, "ptr", $pBuffer)
    If @error Then Return SetError(@error, @extended, -1)
    If $aResult[0] Then Return SetError(10, $aResult[0], -1)

    Local $aPropertyItems[$iNumProperties][4]
    Local $a1
    Local $tPropertyItem = DllStructCreate("int id; int length; short type; ptr value;")

    For $iI = 0 To $iNumProperties - 1
        $a1 = __GDIPlus_ImageGetPropertyItemValues($pBuffer)
        For $j = 0 To 3
            $aPropertyItems[$iI][$j] = $a1[$j]
        Next
        $pBuffer += DllStructGetSize($tPropertyItem)
    Next

    Return $aPropertyItems
EndFunc   ;==>cGDIPlus_ImageGetAllPropertyItems

; #FUNCTION# ====================================================================================================================
; Name ..........: cGDIPlus_ImageGetPropertyItem
; Description ...: Gets a specified property item (piece of meta data) from an Image object
; Syntax ........: cGDIPlus_ImageGetPropertyItem($hImage, $iPropID)
; Parameters ....: $hImage              - Pointer to an image object
;                  $iPropID             - Identifier of the property item to be retrieved
; Return values .: Success: Array containing the values of the property item
;                      [0] - identifier
;                      [1] - size, in bytes, of the value array
;                      [2] - type of value(s) in the value array
;                      [3] - value array
;                  Failure: sets the @error flag to:
;                      1:  DllCall failed.  Common cause: _GIDPlus_Startup() has not been called
;                     10:  @extended may contain GPSTATUS error code ($GPID_ERR* see GDIPlusConstants.au3).
; Author ........: Eukalyptus
; Modified ......: c.haslam
; Remarks .......: types: unsigned byte = 1, ASCII string = 2, unsigned short = 3, unsigned long = 4,
;                         unsinged rational = 5, signed byte = 6, undefined = 7, signed long = 9,
;                         signed rational = 10
;+
;                   For list of EXIF property items, see https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html
;                  + and https://www.media.mit.edu/pia/Research/deepview/exif.html
; Related .......: _GDIPlus_ImageGetPropertyIdList, cGDIPlus_ImageSetPropertyItem
; Link ..........: https://msdn.microsoft.com/en-us/library/windows/desktop/ms535390(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/windows/desktop/ms534493(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/windows/desktop/ms534414(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/ms534416.aspx#_gdiplus_constant_propertytaggpsver
; Example .......: Yes
; ===============================================================================================================================
Func cGDIPlus_ImageGetPropertyItem($hImage, $iPropID)
    Local $iSize = __GDIPlus_ImageGetPropertyItemSize($hImage, $iPropID)
    If @error Then Return SetError(@error + 10, @extended, False)

    Local $tBuffer = DllStructCreate("byte[" & $iSize & "];")
    Local $pBuffer = DllStructGetPtr($tBuffer)
    Local $aResult = DllCall($__g_hGDIPDll, "int", "GdipGetPropertyItem", "handle", $hImage, "uint", _
            $iPropID, "uint", $iSize, "struct*", $tBuffer)
    If @error Then Return SetError(@error, @extended, False)
    If $aResult[0] Then Return SetError(10, $aResult[0], False)

    Return __GDIPlus_ImageGetPropertyItemValues($pBuffer)
EndFunc   ;==>cGDIPlus_ImageGetPropertyItem

; #INTERNAL_USE_ONLY# ===========================================================================================================
; Name ..........: __GDIPlus_ImageGetPropertyItemValues($pBuffer)
; Description ...: Converts a project item from structured to equivalent nested arrays
; Syntax ........: __GDIPlus_ImageGetPropertyItemValues($pBuffer)
; Parameters ....: $bBuffer             - Pointer to start of Property Item sructure
; Return values .:     [0] - identifier
;                      [1] - size, in bytes, of the value array
;                      [2] - type of value(s) in the value array
;                      [3] - value array
; Author ........: c.haslam
; Modified ......: UEZ
; Remarks .......: types: unsigned byte = 1, ASCII string = 2, unsigned short = 3, unsigned long = 4,
;                         unsigned rational = 5, signed byte = 6, undefined = 7, signed long = 9,
;                         signed rational = 10
; Related .......:
; Link ..........:
; Example .......: No
; ===============================================================================================================================

Func __GDIPlus_ImageGetPropertyItemValues($pBuffer)
    Local $tPropertyItem = DllStructCreate("int id; int length; short type; ptr value;", $pBuffer)
    Local $iBytes = DllStructGetData($tPropertyItem, "length")
    Local $pValue = DllStructGetData($tPropertyItem, "value")

    Local $aRet[4]
    Local $type = DllStructGetData($tPropertyItem, 'type')
    Local $tValues, $iValues

    Switch $type
        Case $GDIP_PROPERTYTAGTYPEASCII ;ASCII String
            $iValues = 1
            $tValues = DllStructCreate("char[" & $iBytes & "];", $pValue)
        Case $GDIP_PROPERTYTAGTYPESHORT ;Array of UShort
            $iValues = Int($iBytes / 2)
            $tValues = DllStructCreate("ushort[" & $iValues & "];", $pValue)
        Case $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL ;Array of UInt / Fraction
            $iValues = Int($iBytes / 4)
            $tValues = DllStructCreate("uint[" & $iValues & "];", $pValue)
        Case $GDIP_PROPERTYTAGTYPESLONG, $GDIP_PROPERTYTAGTYPESRATIONAL ;Array of Int / Fraction
            $iValues = Int($iBytes / 4)
            $tValues = DllStructCreate("int[" & $iValues & "];", $pValue)
        Case Else ;Array of Bytes
            $iValues = 1
            $tValues = DllStructCreate("byte[" & $iBytes & "];", $pValue)
    EndSwitch
    $aRet[0] = DllStructGetData($tPropertyItem, 'id')
    $aRet[1] = $iBytes
    $aRet[2] = $type
    Local $a1values[$iValues]
    If $type = $GDIP_PROPERTYTAGTYPEASCII Or $type = $GDIP_PROPERTYTAGTYPEUNDEFINED Then ; ASCII string or undefined
        $a1values[0] = DllStructGetData($tValues, 1)
    Else
        For $i = 0 To $iValues - 1
            $a1values[$i] = DllStructGetData($tValues, 1, $i + 1)
        Next
    EndIf
    $aRet[3] = $a1values
    Return $aRet
EndFunc   ;==>__GDIPlus_ImageGetPropertyItemValues

; #FUNCTION# ====================================================================================================================
; Name ..........: cGDIPlus_ImageSetPropertyItem
; Description ...: Sets a specified property item (piece of meta data) for an Image object
; Syntax ........: cGDIPlus_ImageGetPropertyItem($hImage, $a1PropertyItem)
; Parameters ....: $hImage              - Pointer to an image object
;                  $a1PropertyItem      - Array containing the values of the property item
;                      [0] - identifier
;                      [1] - size of the value array
;                               positive: in bytes
;                               negative: for numeric types: in number of values
;                      [2] - type of value(s) in the value array
;                      [3] - value array
; Return values .: Success: True and @error = 0
;                  Failure: sets the @error flag to:
;                      1:  DllCall failed.  Common cause: _GIDPlus_Startup() has not been called
;                      2:  $a1PropertyItem is not a 4-element 1-d array
;                     10:  @extended may contain GPSTATUS error code ($GPID_ERR* see GDIPlusConstants.au3).
;                    101:  identifier is not an integer
;                    201:  size is not an integer
;                    202:  size cannot be number of values for ASCII string and undefined types
;                    202:  size is zero
;                    301:  type is illegal
;                    401:  value array is either not a 1-d array or has no elements
;                    402:  size is incompatible with type
;                    403:  size cannot be negative for ASCII string and undefined types
;                    404:  Numerator and denominator are required for rational types
;                    405:  size and number of elements in value array differ
; Author ........: Eukalyptus
; Modified ......: c.haslam, UEZ
; Remarks .......: If size parameter is negative, calculates size in bytes from type.
;                  Convenient when setting a few property items
;+
;                  types: unsigned byte = 1, ASCII string = 2, unsigned short = 3, unsigned long = 4,
;                         unsinged rational = 5, undefined = 7, signed long = 9, signed rational = 10
;+
;                   For list of EXIF property items, see https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html
;                  + and https://www.media.mit.edu/pia/Research/deepview/exif.html
; Related .......: _GDIPlus_ImageGetPropertyIdList, cGDIPlus_ImageGetPropertyItem
; Link ..........: https://msdn.microsoft.com/en-us/library/windows/desktop/ms535390(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/windows/desktop/ms534493(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/windows/desktop/ms534414(v=vs.85).aspx
; Example .......: Yes
; ===============================================================================================================================
Func cGDIPlus_ImageSetPropertyItem($hImage, $a1PropertyItem)
    If (Not IsArray($a1PropertyItem)) Or UBound($a1PropertyItem, 0) <> 1 Or UBound($a1PropertyItem) <> 4 Then
        Return SetError(2, 0, False)
    EndIf
    Local $iId = $a1PropertyItem[0]
    Local $iLength = $a1PropertyItem[1]
    Local $iType = $a1PropertyItem[2]
    Local $a1values = $a1PropertyItem[3]

    If Not IsInt($iId) Then
        Return SetError(101, 0, False)
    EndIf
    If Not IsInt($iLength) Then
        Return SetError(201, 0, False)
    EndIf
    If $iLength = 0 Then
        Return SetError(202, 0, False)
    EndIf
    Switch $iType
        Case $GDIP_PROPERTYTAGTYPEBYTE, $GDIP_PROPERTYTAGTYPEASCII, $GDIP_PROPERTYTAGTYPESHORT, _
                $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL, $GDIP_PROPERTYTAGTYPEUNDEFINED, _
                $GDIP_PROPERTYTAGTYPESLONG, $GDIP_PROPERTYTAGTYPESRATIONAL
            ; do nothing
        Case Else
            Return SetError(301, 0, False)
    EndSwitch
    If (Not IsArray($a1values)) Or UBound($a1values, 0) <> 1 Or UBound($a1values) = 0 Then
        Return SetError(401, 0, False)
    EndIf

    Local $iBytes, $iqValues
    If $iLength > 0 Then ; useful when copying all property items
        $iBytes = $iLength
        Switch $iType
            Case $GDIP_PROPERTYTAGTYPEASCII ;ASCII String
                $iqValues = 1
            Case $GDIP_PROPERTYTAGTYPESHORT ;Array of UShort
                If BitAND($iLength, 1) <> 0 Then
                    Return SetError(402, 0, False)
                EndIf
                $iqValues = Int($iLength / 2)
            Case $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL, $GDIP_PROPERTYTAGTYPESLONG, _
                    $GDIP_PROPERTYTAGTYPESRATIONAL ;Array of UInt / Fraction
                If BitAND($iLength, 3) <> 0 Then
                    Return SetError(402, 0, False)
                EndIf
                $iqValues = Int($iLength / 4)
            Case Else ; Array of Bytes, undefined
                $iqValues = 1
        EndSwitch
    ElseIf $iLength < 0 Then ; convenient when setting a few property items
        Switch $iType
            Case $GDIP_PROPERTYTAGTYPEASCII, $GDIP_PROPERTYTAGTYPEUNDEFINED ;ASCII String, undefined
                Return SetError(403, 0, False)
            Case $GDIP_PROPERTYTAGTYPESHORT ;Array of UShort
                $iBytes = -$iLength * 2
            Case $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL, $GDIP_PROPERTYTAGTYPESLONG, _
                    $GDIP_PROPERTYTAGTYPESRATIONAL ;Array of UInt / Fraction
                $iBytes = -$iLength * 4
            Case Else ;Array of Bytes
                $iBytes = -$iLength
        EndSwitch
        $iqValues = -$iLength
    EndIf

    If $iType = $GDIP_PROPERTYTAGTYPERATIONAL Or $iType = $GDIP_PROPERTYTAGTYPERATIONAL Then
        If BitAND($iqValues, 1) <> 0 Then
            Return SetError(404, 0, False)
        EndIf
    EndIf

    Switch $iType
        Case $GDIP_PROPERTYTAGTYPESHORT, $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL, _
            $GDIP_PROPERTYTAGTYPESLONG, $GDIP_PROPERTYTAGTYPESRATIONAL
            If UBound($a1values)<>$iqValues Then
                Return SetError(405,0,False)
            EndIf
    EndSwitch

    Local $tPropItem = DllStructCreate("int id; int length; short type; ptr pValue;")
    DllStructSetData($tPropItem, 'id', $iId)
    DllStructSetData($tPropItem, 'length', $iBytes)
    DllStructSetData($tPropItem, 'type', $iType)

    Local $tValues

    Switch $iType
        Case $GDIP_PROPERTYTAGTYPEASCII ;ASCII String
            $tValues = DllStructCreate("char[" & $iBytes & "];")
        Case $GDIP_PROPERTYTAGTYPESHORT ;Array of UShort
            $tValues = DllStructCreate("ushort[" & $iqValues & "];")
        Case $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL ;Array of UInt / Fraction
            $tValues = DllStructCreate("uint[" & $iqValues & "];")
        Case $GDIP_PROPERTYTAGTYPESLONG, $GDIP_PROPERTYTAGTYPESRATIONAL ;Array of Int / Fraction
            $tValues = DllStructCreate("int[" & $iqValues & "];")
        Case Else ;Array of Bytes
            $tValues = DllStructCreate("byte[" & $iBytes & "];")
    EndSwitch
    If $iType = $GDIP_PROPERTYTAGTYPEASCII Or $iType = $GDIP_PROPERTYTAGTYPEUNDEFINED Then ; ASCII string or undefined
        DllStructSetData($tValues, 1, $a1values[0])
    Else
        For $i = 0 To $iqValues - 1
            DllStructSetData($tValues, 1, $a1values[$i], $i + 1)
        Next
    EndIf
    DllStructSetData($tPropItem, 'pValue', DllStructGetPtr($tValues))

    Local $aResult = DllCall($__g_hGDIPDll, "uint", "GdipSetPropertyItem", "handle", $hImage, "struct*", $tPropItem)
    If @error Then Return SetError(@error, @extended, -1)
    If $aResult[0] Then Return SetError(10, $aResult[0], -1)
    Return True
EndFunc   ;==>cGDIPlus_ImageSetPropertyItem

I have been unable to get the DLLCall to fail for any other reason than $__g_hGDIPDll not being set by _GDIPlus_Startup(). When DLLCall fails, the error code always, in my experience, is 1. Looking at the Help for DLLCall, this makes sense to me.

I am willing to change the error codes if you wish.

Edited by c.haslam
typo
Spoiler

CDebug Dumps values of variables including arrays and DLL structs, to a GUI, to the Console, and to the Clipboard

 

Link to comment
Share on other sites

I have discovered that, if a property item does not exist in a JPG, GdipGetPropertyItemSize does not set a $GDIP_ERR*.  The listing below includes c__GDIPlus_ImageGetPropertyItemSize(), which handles this situation. I have also changed the example:

  1. adding a case where a property item does not exist in the JPG file
  2. using a JPG file that is included in AutoIt distribution, rather than a JPG file the user chooses, so the property item tested in point 1 will always not occur

I wonder if there are other Gdip* functions for which no $GDIP_ERR code is set.

#AutoIt3Wrapper_UseX64=n
#include <GDIPlus.au3>
#include <Array.au3>
#include <WinAPIDiag.au3>


;--------- should be moved to GDIPlusConstants.au3
Global Const $GDIP_PROPERTYTAGTYPEBYTE = 1, $GDIP_PROPERTYTAGTYPEASCII = 2, $GDIP_PROPERTYTAGTYPESHORT = 3, $GDIP_PROPERTYTAGTYPELONG = 4, _
        $GDIP_PROPERTYTAGTYPERATIONAL = 5, $GDIP_PROPERTYTAGTYPEUNDEFINED = 7, $GDIP_PROPERTYTAGTYPESLONG = 9, _
        $GDIP_PROPERTYTAGTYPESRATIONAL = 10
;-------------------------------------------------

Global $a2PropID

Example()


Func Example()
    _GDIPlus_Startup()
    Local $hImage = _GDIPlus_ImageLoadFromFile('F:\Program Files\AutoIt3\Examples\GUI\mslogo.jpg')
    $a2PropID = _GDIPlus_ImageGetPropertyIdList($hImage)
    Local $a2PropertyItems = cGDIPlus_ImageGetAllPropertyItems($hImage)
    DisplayPropertyItems($a2PropertyItems)

    Local $a1 = cGDIPlus_ImageGetPropertyItem($hImage,0x138)

    If @error=10 Then
        If @extended=$GDIP_ERRPROPERTYNOTFOUND Then
            MsgBox(0,'','Property item 0x138 not found in JPG file')
        Else
            MsgBox(0,@ScriptLineNumber,'GDIPlus error '&@extended)
        EndIf
    Else
        Local $a2[1][4]
        For $i = 0 To 3
            $a2[0][$i] = $a1[$i]
        Next
        DisplayPropertyItems($a2)
    EndIf


    Local $hImageClone = _GDIPlus_ImageClone($hImage) ; copy image without property items

    Local $a1[4]
    For $i = 0 To UBound($a2PropertyItems, 1) - 1 ; set all property items
        For $j = 0 To 3
            $a1[$j] = $a2PropertyItems[$i][$j]
        Next
        cGDIPlus_ImageSetPropertyItem($hImageClone, $a1)
    Next

    ; update a property item
    Local $a1value = ['Your name']
    Local $a1 = [0x13B, 20, 2, $a1value] ; Artist
    cGDIPlus_ImageSetPropertyItem($hImageClone, $a1)

    ; add a property item
    Local $a1value = [2000]
    Local $a1 = [0xa002, -1, 4, $a1value] ; ExifWidth
    cGDIPlus_ImageSetPropertyItem($hImageClone, $a1)

    Local $a1value = [0xFFFE01]
    Local $a1 = [0x927C, 3, 7, $a1value] ; MakerNote
    cGDIPlus_ImageSetPropertyItem($hImageClone, $a1)

    $a2PropertyItems = cGDIPlus_ImageGetAllPropertyItems($hImageClone)
    DisplayPropertyItems($a2PropertyItems)

    $a1 = cGDIPlus_ImageGetPropertyItem($hImageClone, 0x927c) ;https://msdn.microsoft.com/en-us/library/ms534417.aspx
    Local $a2[1][4]
    For $i = 0 To 3
        $a2[0][$i] = $a1[$i]
    Next
    DisplayPropertyItems($a2)

    _GDIPlus_Shutdown()
EndFunc   ;==>Example

Func DisplayPropertyItems($a2PropertyItems)
    ; Collapse values arrays so _ArrayDisplay can display them
    Local $a2 = $a2PropertyItems, $iPos
    ReDim $a2[UBound($a2PropertyItems)][5]

    For $i = 0 To UBound($a2, 1) - 1
        $a2[$i][0] = '0x' & Hex($a2PropertyItems[$i][0], 4)
        $a2[$i][3] = ''
        For $j = 0 To UBound($a2PropertyItems[$i][3]) - 1
            $a2[$i][3] &= ($a2PropertyItems[$i][3])[$j] & '|'
        Next
        $a2[$i][3] = StringTrimRight($a2[$i][3], 1)
        $iPos = _ArraySearch($a2PropID, $a2[$i][0])
        If $iPos > -1 Then $a2[$i][4] = $a2PropID[$iPos][1]
    Next
    _ArrayDisplay($a2, "Image Property Items", Default, 0, Default, "Identifier|Size|Type|Value|Description")
EndFunc   ;==>DisplayPropertyItems

; #FUNCTION# ====================================================================================================================
; Name ..........: cGDIPlus_ImageGetAllPropertyItems
; Description ...: Gets all property item (piece of meta data) from an Image object
; Syntax ........: cGDIPlus_ImageGetAllPropertyItems($hImage)
; Parameters ....: $hImage              - Pointer to an image object
; Return values .: Success: Array in which each row contains these columns:
;                      [0] - identifier
;                      [1] - size, in bytes, of the value array
;                      [2] - type of value(s) in the value array
;                      [3] - value array
;                  Failure: sets the @error flag to:
;                      1:  DllCall failed.  Common cause: _GIDPlus_Startup() has not been called
;                     10:  @extended may contain GPSTATUS error code ($GPID_ERR* see GDIPlusConstants.au3).
; Author ........: c.haslam
; Modified ......:
; Remarks .......: types: unsigned byte = 1, ASCII string = 2, unsigned short = 3, unsigned long = 4,
;                         unsinged rational = 5, signed byte = 6, undefined = 7, signed long = 9,
;                         signed rational = 10
;+
;                   For list of EXIF property items, see https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html
;                  + and https://www.media.mit.edu/pia/Research/deepview/exif.html
; Related .......: cGDIPlus_ImageGetPropertyItem, _GDIPlus_ImageGetPropertyIdList, cGDIPlus_ImageSetPropertyItem
; Link ..........: https://msdn.microsoft.com/en-us/library/windows/desktop/ms535390(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/windows/desktop/ms534493(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/windows/desktop/ms534414(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/ms534416.aspx#_gdiplus_constant_propertytaggpsver
; Example .......: Yes
; ===============================================================================================================================

Func cGDIPlus_ImageGetAllPropertyItems($hImage)
    Local $aResult = DllCall($__g_hGDIPDll, "uint", "GdipGetPropertySize", "handle", $hImage, "uint*", 0, "uint*", 0)
    If @error Then Return SetError(@error, @extended, -1)
    If $aResult[0] Then Return SetError(10, $aResult[0], -1)
    Local $iTotalBufferSize = $aResult[2]
    Local $iNumProperties = $aResult[3]

    Local $tBuffer = DllStructCreate("byte[" & $iTotalBufferSize & "]")
    Local $pBuffer = DllStructGetPtr($tBuffer)

    $aResult = DllCall($__g_hGDIPDll, "uint", "GdipGetAllPropertyItems", "handle", $hImage, _
            "uint", $iTotalBufferSize, "uint", $iNumProperties, "ptr", $pBuffer)
    If @error Then Return SetError(@error, @extended, -1)
    If $aResult[0] Then Return SetError(10, $aResult[0], -1)

    Local $aPropertyItems[$iNumProperties][4]
    Local $a1
    Local $tPropertyItem = DllStructCreate("int id; int length; short type; ptr value;")

    For $iI = 0 To $iNumProperties - 1
        $a1 = __GDIPlus_ImageGetPropertyItemValues($pBuffer)
        For $j = 0 To 3
            $aPropertyItems[$iI][$j] = $a1[$j]
        Next
        $pBuffer += DllStructGetSize($tPropertyItem)
    Next

    Return $aPropertyItems
EndFunc   ;==>cGDIPlus_ImageGetAllPropertyItems

; #FUNCTION# ====================================================================================================================
; Name ..........: cGDIPlus_ImageGetPropertyItem
; Description ...: Gets a specified property item (piece of meta data) from an Image object
; Syntax ........: cGDIPlus_ImageGetPropertyItem($hImage, $iPropID)
; Parameters ....: $hImage              - Pointer to an image object
;                  $iPropID             - Identifier of the property item to be retrieved
; Return values .: Success: Array containing the values of the property item
;                      [0] - identifier
;                      [1] - size, in bytes, of the value array
;                      [2] - type of value(s) in the value array
;                      [3] - value array
;                  Failure: sets the @error flag to:
;                      1:  DllCall failed.  Common cause: _GIDPlus_Startup() has not been called
;                     10:  @extended may contain GPSTATUS error code ($GPID_ERR* see GDIPlusConstants.au3).
;                          @extended=19 ($$GDIP_ERRPROPERTYNOTFOUND): property item not found
; Author ........: Eukalyptus
; Modified ......: c.haslam
; Remarks .......: types: unsigned byte = 1, ASCII string = 2, unsigned short = 3, unsigned long = 4,
;                         unsinged rational = 5, signed byte = 6, undefined = 7, signed long = 9,
;                         signed rational = 10
;+
;                   For list of EXIF property items, see https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html
;                  + and https://www.media.mit.edu/pia/Research/deepview/exif.html
; Related .......: _GDIPlus_ImageGetPropertyIdList, cGDIPlus_ImageSetPropertyItem
; Link ..........: https://msdn.microsoft.com/en-us/library/windows/desktop/ms535390(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/windows/desktop/ms534493(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/windows/desktop/ms534414(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/ms534416.aspx#_gdiplus_constant_propertytaggpsver
; Example .......: Yes
; ===============================================================================================================================
Func cGDIPlus_ImageGetPropertyItem($hImage, $iPropID)
    Local $iSize = c__GDIPlus_ImageGetPropertyItemSize($hImage, $iPropID)
    If @error Then Return SetError(10, @extended, False)

    Local $tBuffer = DllStructCreate("byte[" & $iSize & "];")
    Local $pBuffer = DllStructGetPtr($tBuffer)
    Local $aResult = DllCall($__g_hGDIPDll, "int", "GdipGetPropertyItem", "handle", $hImage, "uint", _
            $iPropID, "uint", $iSize, "struct*", $tBuffer)
    If @error Then Return SetError(@error, @extended, False)
    If $aResult[0] Then Return SetError(10, $aResult[0], False)

    Return __GDIPlus_ImageGetPropertyItemValues($pBuffer)
EndFunc   ;==>cGDIPlus_ImageGetPropertyItem

; #INTERNAL_USE_ONLY# ===========================================================================================================
; Name ..........: c__GDIPlus_ImageGetPropertyItemSize
; Description ...: Gets the size, in bytes, of a specified property item of an image object
; Syntax ........: __GDIPlus_ImageGetPropertyItemSize($hImage, $iPropID)
; Parameters ....: $hImage              - Pointer to an image object
;                  $iPropID             - Identifier of the property item to be retrieved
; Return values .: Success      - Size in bytes of the property item
;                  Failure: sets the @error flag to:
;                      1:  DllCall failed.  Common cause: _GIDPlus_Startup() has not been called
;                     10:  @extended may contain GPSTATUS error code ($GPID_ERR* see GDIPlusConstants.au3).
;                          @extended=19 ($$GDIP_ERRPROPERTYNOTFOUND): property item not found
; Author ........: Authenticity
; Modified ......: Eukalyptus, c.haslam
; Remarks .......:
; Related .......: _GDIPlus_ImageGetPropertyItem
; Link ..........: @@MsdnLink@@ GdipGetPropertyItemSize
; Example .......: No
; ===============================================================================================================================
Func c__GDIPlus_ImageGetPropertyItemSize($hImage, $iPropID)
    Local $aResult = DllCall($__g_hGDIPDll, "int", "GdipGetPropertyItemSize", "handle", $hImage, "uint", $iPropID, "uint*", 0)
    If $aResult[3]=-1 Then
        Return SetError(10,$GDIP_ERRPROPERTYNOTFOUND,-1)    ; GdipGetPropertyItemSize doesn't set it!
    EndIf
    If @error Then Return SetError(@error, @extended, -1)
    If $aResult[0] Then Return SetError(10, $aResult[0], -1)
    Return $aResult[3]
EndFunc   ;==>__GDIPlus_ImageGetPropertyItemSize

; #INTERNAL_USE_ONLY# ===========================================================================================================
; Name ..........: __GDIPlus_ImageGetPropertyItemValues($pBuffer)
; Description ...: Converts a project item from structured to equivalent nested arrays
; Syntax ........: __GDIPlus_ImageGetPropertyItemValues($pBuffer)
; Parameters ....: $bBuffer             - Pointer to start of Property Item sructure
; Return values .:     [0] - identifier
;                      [1] - size, in bytes, of the value array
;                      [2] - type of value(s) in the value array
;                      [3] - value array
; Author ........: c.haslam
; Modified ......: UEZ
; Remarks .......: types: unsigned byte = 1, ASCII string = 2, unsigned short = 3, unsigned long = 4,
;                         unsigned rational = 5, signed byte = 6, undefined = 7, signed long = 9,
;                         signed rational = 10
; Related .......:
; Link ..........:
; Example .......: No
; ===============================================================================================================================

Func __GDIPlus_ImageGetPropertyItemValues($pBuffer)
    Local $tPropertyItem = DllStructCreate("int id; int length; short type; ptr value;", $pBuffer)
    Local $iBytes = DllStructGetData($tPropertyItem, "length")
    Local $pValue = DllStructGetData($tPropertyItem, "value")

    Local $aRet[4]
    Local $type = DllStructGetData($tPropertyItem, 'type')
    Local $tValues, $iValues

    Switch $type
        Case $GDIP_PROPERTYTAGTYPEASCII ;ASCII String
            $iValues = 1
            $tValues = DllStructCreate("char[" & $iBytes & "];", $pValue)
        Case $GDIP_PROPERTYTAGTYPESHORT ;Array of UShort
            $iValues = Int($iBytes / 2)
            $tValues = DllStructCreate("ushort[" & $iValues & "];", $pValue)
        Case $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL ;Array of UInt / Fraction
            $iValues = Int($iBytes / 4)
            $tValues = DllStructCreate("uint[" & $iValues & "];", $pValue)
        Case $GDIP_PROPERTYTAGTYPESLONG, $GDIP_PROPERTYTAGTYPESRATIONAL ;Array of Int / Fraction
            $iValues = Int($iBytes / 4)
            $tValues = DllStructCreate("int[" & $iValues & "];", $pValue)
        Case Else ;Array of Bytes
            $iValues = 1
            $tValues = DllStructCreate("byte[" & $iBytes & "];", $pValue)
    EndSwitch
    $aRet[0] = DllStructGetData($tPropertyItem, 'id')
    $aRet[1] = $iBytes
    $aRet[2] = $type
    Local $a1values[$iValues]
    If $type = $GDIP_PROPERTYTAGTYPEASCII Or $type = $GDIP_PROPERTYTAGTYPEUNDEFINED Then ; ASCII string or undefined
        $a1values[0] = DllStructGetData($tValues, 1)
    Else
        For $i = 0 To $iValues - 1
            $a1values[$i] = DllStructGetData($tValues, 1, $i + 1)
        Next
    EndIf
    $aRet[3] = $a1values
    Return $aRet
EndFunc   ;==>__GDIPlus_ImageGetPropertyItemValues

; #FUNCTION# ====================================================================================================================
; Name ..........: cGDIPlus_ImageSetPropertyItem
; Description ...: Sets a specified property item (piece of meta data) for an Image object
; Syntax ........: cGDIPlus_ImageGetPropertyItem($hImage, $a1PropertyItem)
; Parameters ....: $hImage              - Pointer to an image object
;                  $a1PropertyItem      - Array containing the values of the property item
;                      [0] - identifier
;                      [1] - size of the value array
;                               positive: in bytes
;                               negative: for numeric types: in number of values
;                      [2] - type of value(s) in the value array
;                      [3] - value array
; Return values .: Success: True and @error = 0
;                  Failure: sets the @error flag to:
;                      1:  DllCall failed.  Common cause: _GIDPlus_Startup() has not been called
;                      2:  $a1PropertyItem is not a 4-element 1-d array
;                     10:  @extended may contain GPSTATUS error code ($GPID_ERR* see GDIPlusConstants.au3).
;                    101:  identifier is not an integer
;                    201:  size is not an integer
;                    202:  size cannot be number of values for ASCII string and undefined types
;                    202:  size is zero
;                    301:  type is illegal
;                    401:  value array is either not a 1-d array or has no elements
;                    402:  size is incompatible with type
;                    403:  size cannot be negative for ASCII string and undefined types
;                    404:  Numerator and denominator are required for rational types
;                    405:  size and number of elements in value array differ
; Author ........: Eukalyptus
; Modified ......: c.haslam, UEZ
; Remarks .......: If size parameter is negative, calculates size in bytes from type.
;                  Convenient when setting a few property items
;+
;                  types: unsigned byte = 1, ASCII string = 2, unsigned short = 3, unsigned long = 4,
;                         unsinged rational = 5, undefined = 7, signed long = 9, signed rational = 10
;+
;                   For list of EXIF property items, see https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html
;                  + and https://www.media.mit.edu/pia/Research/deepview/exif.html
; Related .......: _GDIPlus_ImageGetPropertyIdList, cGDIPlus_ImageGetPropertyItem
; Link ..........: https://msdn.microsoft.com/en-us/library/windows/desktop/ms535390(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/windows/desktop/ms534493(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/windows/desktop/ms534414(v=vs.85).aspx
; Example .......: Yes
; ===============================================================================================================================
Func cGDIPlus_ImageSetPropertyItem($hImage, $a1PropertyItem)
    If (Not IsArray($a1PropertyItem)) Or UBound($a1PropertyItem, 0) <> 1 Or UBound($a1PropertyItem) <> 4 Then
        Return SetError(2, 0, False)
    EndIf
    Local $iId = $a1PropertyItem[0]
    Local $iLength = $a1PropertyItem[1]
    Local $iType = $a1PropertyItem[2]
    Local $a1values = $a1PropertyItem[3]

    If Not IsInt($iId) Then
        Return SetError(101, 0, False)
    EndIf
    If Not IsInt($iLength) Then
        Return SetError(201, 0, False)
    EndIf
    If $iLength = 0 Then
        Return SetError(202, 0, False)
    EndIf
    Switch $iType
        Case $GDIP_PROPERTYTAGTYPEBYTE, $GDIP_PROPERTYTAGTYPEASCII, $GDIP_PROPERTYTAGTYPESHORT, _
                $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL, $GDIP_PROPERTYTAGTYPEUNDEFINED, _
                $GDIP_PROPERTYTAGTYPESLONG, $GDIP_PROPERTYTAGTYPESRATIONAL
            ; do nothing
        Case Else
            Return SetError(301, 0, False)
    EndSwitch
    If (Not IsArray($a1values)) Or UBound($a1values, 0) <> 1 Or UBound($a1values) = 0 Then
        Return SetError(401, 0, False)
    EndIf

    Local $iBytes, $iqValues
    If $iLength > 0 Then ; useful when copying all property items
        $iBytes = $iLength
        Switch $iType
            Case $GDIP_PROPERTYTAGTYPEASCII ;ASCII String
                $iqValues = 1
            Case $GDIP_PROPERTYTAGTYPESHORT ;Array of UShort
                If BitAND($iLength, 1) <> 0 Then
                    Return SetError(402, 0, False)
                EndIf
                $iqValues = Int($iLength / 2)
            Case $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL, $GDIP_PROPERTYTAGTYPESLONG, _
                    $GDIP_PROPERTYTAGTYPESRATIONAL ;Array of UInt / Fraction
                If BitAND($iLength, 3) <> 0 Then
                    Return SetError(402, 0, False)
                EndIf
                $iqValues = Int($iLength / 4)
            Case Else ; Array of Bytes, undefined
                $iqValues = 1
        EndSwitch
    ElseIf $iLength < 0 Then ; convenient when setting a few property items
        Switch $iType
            Case $GDIP_PROPERTYTAGTYPEASCII, $GDIP_PROPERTYTAGTYPEUNDEFINED ;ASCII String, undefined
                Return SetError(403, 0, False)
            Case $GDIP_PROPERTYTAGTYPESHORT ;Array of UShort
                $iBytes = -$iLength * 2
            Case $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL, $GDIP_PROPERTYTAGTYPESLONG, _
                    $GDIP_PROPERTYTAGTYPESRATIONAL ;Array of UInt / Fraction
                $iBytes = -$iLength * 4
            Case Else ;Array of Bytes
                $iBytes = -$iLength
        EndSwitch
        $iqValues = -$iLength
    EndIf

    If $iType = $GDIP_PROPERTYTAGTYPERATIONAL Or $iType = $GDIP_PROPERTYTAGTYPERATIONAL Then
        If BitAND($iqValues, 1) <> 0 Then
            Return SetError(404, 0, False)
        EndIf
    EndIf

    Switch $iType
        Case $GDIP_PROPERTYTAGTYPESHORT, $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL, _
            $GDIP_PROPERTYTAGTYPESLONG, $GDIP_PROPERTYTAGTYPESRATIONAL
            If UBound($a1values)<>$iqValues Then
                Return SetError(405,0,False)
            EndIf
    EndSwitch

    Local $tPropItem = DllStructCreate("int id; int length; short type; ptr pValue;")
    DllStructSetData($tPropItem, 'id', $iId)
    DllStructSetData($tPropItem, 'length', $iBytes)
    DllStructSetData($tPropItem, 'type', $iType)

    Local $tValues

    Switch $iType
        Case $GDIP_PROPERTYTAGTYPEASCII ;ASCII String
            $tValues = DllStructCreate("char[" & $iBytes & "];")
        Case $GDIP_PROPERTYTAGTYPESHORT ;Array of UShort
            $tValues = DllStructCreate("ushort[" & $iqValues & "];")
        Case $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL ;Array of UInt / Fraction
            $tValues = DllStructCreate("uint[" & $iqValues & "];")
        Case $GDIP_PROPERTYTAGTYPESLONG, $GDIP_PROPERTYTAGTYPESRATIONAL ;Array of Int / Fraction
            $tValues = DllStructCreate("int[" & $iqValues & "];")
        Case Else ;Array of Bytes
            $tValues = DllStructCreate("byte[" & $iBytes & "];")
    EndSwitch
    If $iType = $GDIP_PROPERTYTAGTYPEASCII Or $iType = $GDIP_PROPERTYTAGTYPEUNDEFINED Then ; ASCII string or undefined
        DllStructSetData($tValues, 1, $a1values[0])
    Else
        For $i = 0 To $iqValues - 1
            DllStructSetData($tValues, 1, $a1values[$i], $i + 1)
        Next
    EndIf
    DllStructSetData($tPropItem, 'pValue', DllStructGetPtr($tValues))

    Local $aResult = DllCall($__g_hGDIPDll, "uint", "GdipSetPropertyItem", "handle", $hImage, "struct*", $tPropItem)
    If @error Then Return SetError(@error, @extended, -1)
    If $aResult[0] Then Return SetError(10, $aResult[0], -1)
    Return True
EndFunc   ;==>cGDIPlus_ImageSetPropertyItem

 

Edited by c.haslam
Clarified help for cGDIPlus_ImageGetPropertyItem and c__GDIPlus_ImageGetPropertyItemSize
Spoiler

CDebug Dumps values of variables including arrays and DLL structs, to a GUI, to the Console, and to the Clipboard

 

Link to comment
Share on other sites

I have learnt that a cloned image has the same properties as the original image. I then discovered how to create a copy of an image that has property items to an image that does not. This caused changes in Example().

#AutoIt3Wrapper_UseX64=n
#include <GDIPlus.au3>
#include <Array.au3>
#include <WinAPIDiag.au3>


;--------- should be moved to GDIPlusConstants.au3
Global Const $GDIP_PROPERTYTAGTYPEBYTE = 1, $GDIP_PROPERTYTAGTYPEASCII = 2, $GDIP_PROPERTYTAGTYPESHORT = 3, $GDIP_PROPERTYTAGTYPELONG = 4, _
        $GDIP_PROPERTYTAGTYPERATIONAL = 5, $GDIP_PROPERTYTAGTYPEUNDEFINED = 7, $GDIP_PROPERTYTAGTYPESLONG = 9, _
        $GDIP_PROPERTYTAGTYPESRATIONAL = 10
;-------------------------------------------------

Global $a2PropID

Example()

Func Example()
    _GDIPlus_Startup()
    Local $hImageF = _GDIPlus_ImageLoadFromFile('F:\Program Files\AutoIt3\Examples\GUI\mslogo.jpg')
    $a2PropID = _GDIPlus_ImageGetPropertyIdList($hImageF)
    Local $a2PropertyItems = cGDIPlus_ImageGetAllPropertyItems($hImageF)
    DisplayPropertyItems($a2PropertyItems,'as read')

    Local $a1 = cGDIPlus_ImageGetPropertyItem($hImageF,0x138)
    If @error=10 Then
        If @extended=$GDIP_ERRPROPERTYNOTFOUND Then
            MsgBox(0,'','Property item 0x138 not found in image from JPG file')
        Else
            MsgBox(0,@ScriptLineNumber,'GDIPlus error '&@extended)
        EndIf
    EndIf

    Local $nWidImg = _GDIPlus_ImageGetWidth($hImageF)
    Local $nHtImg = _GDIPlus_ImageGetHeight($hImageF)
    ; hImageN has no property items
    Local $hImageN = _GDIPlus_BitmapCloneArea($hImageF,$nWidImg,$nHtImg,$nWidImg,$nHtImg)

    Local $a2PropertyItems = cGDIPlus_ImageGetAllPropertyItems($hImageN)
    If $a2PropertyItems=-1 Then
        MsgBox(0,'','No property items')
    EndIf

    For $i = 0 To UBound($a2PropertyItems, 1) - 1 ; set all property items
        For $j = 0 To 3
            $a1[$j] = $a2PropertyItems[$i][$j]
        Next
        cGDIPlus_ImageSetPropertyItem($hImageN, $a1)
    Next

    ; add a property item
    Local $a1value = ['Your name']
    Local $a1 = [0x13B, 20, 2, $a1value] ; Artist
    cGDIPlus_ImageSetPropertyItem($hImageF, $a1)

    ; add a property item
    Local $a1value = [2]
    Local $a1 = [0xa002, -1, 3, $a1value] ; ResolutionUnit
    cGDIPlus_ImageSetPropertyItem($hImageF, $a1)

    $a2PropertyItems = cGDIPlus_ImageGetAllPropertyItems($hImageF)
    Local $a2[1][4]
    For $i = 0 To 3
        $a2[0][$i] = $a1[$i]
    Next
    DisplayPropertyItems($a2PropertyItems,'from file, after adding')

    ; add a property item
    Local $a1value = [0xFFFE01]
    Local $a1 = [0x927C, 3, 7, $a1value] ; MakerNote
    cGDIPlus_ImageSetPropertyItem($hImageN, $a1)

    $a2PropertyItems = cGDIPlus_ImageGetAllPropertyItems($hImageF)
    DisplayPropertyItems($a2PropertyItems,'')

    $a1 = cGDIPlus_ImageGetPropertyItem($hImageF, 0x13B) ;https://msdn.microsoft.com/en-us/library/ms534417.aspx
    Local $a2[1][4]
    For $i = 0 To 3
        $a2[0][$i] = $a1[$i]
    Next
    DisplayPropertyItems($a2,'one item')

    _GDIPlus_ImageDispose($hImageF)
    _GDIPlus_ImageDispose($hImageN)
    _GDIPlus_Shutdown()
EndFunc   ;==>Example

Func DisplayPropertyItems($a2PropertyItems,$str)
    ; Collapse values arrays so _ArrayDisplay can display them
    Local $a2 = $a2PropertyItems, $iPos
    ReDim $a2[UBound($a2PropertyItems)][5]

    For $i = 0 To UBound($a2, 1) - 1
        $a2[$i][0] = '0x' & Hex($a2PropertyItems[$i][0], 4)
        $a2[$i][3] = ''
        For $j = 0 To UBound($a2PropertyItems[$i][3]) - 1
            $a2[$i][3] &= ($a2PropertyItems[$i][3])[$j] & '|'
        Next
        $a2[$i][3] = StringTrimRight($a2[$i][3], 1)
        $iPos = _ArraySearch($a2PropID, $a2[$i][0])
        If $iPos > -1 Then $a2[$i][4] = $a2PropID[$iPos][1]
    Next
    _ArrayDisplay($a2, "Image Property Items " &$str, Default, 0, Default, "Identifier|Size|Type|Value|Description")
EndFunc   ;==>DisplayPropertyItems

; #FUNCTION# ====================================================================================================================
; Name ..........: cGDIPlus_ImageGetAllPropertyItems
; Description ...: Gets all property item (piece of meta data) from an Image object
; Syntax ........: cGDIPlus_ImageGetAllPropertyItems($hImage)
; Parameters ....: $hImage              - Pointer to an image object
; Return values .: Success: Array in which each row contains these columns:
;                      [0] - identifier
;                      [1] - size, in bytes, of the value array
;                      [2] - type of value(s) in the value array
;                      [3] - value array
;                  Failure: sets the @error flag to:
;                      1:  DllCall failed.  Common cause: _GIDPlus_Startup() has not been called
;                     10:  @extended may contain GPSTATUS error code ($GPID_ERR* see GDIPlusConstants.au3).
;                          @extended=2; no property items found
; Author ........: c.haslam
; Modified ......:
; Remarks .......: types: unsigned byte = 1, ASCII string = 2, unsigned short = 3, unsigned long = 4,
;                         unsinged rational = 5, signed byte = 6, undefined = 7, signed long = 9,
;                         signed rational = 10
;+
;                   For list of EXIF property items, see https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html
;                  + and https://www.media.mit.edu/pia/Research/deepview/exif.html
; Related .......: cGDIPlus_ImageGetPropertyItem, _GDIPlus_ImageGetPropertyIdList, cGDIPlus_ImageSetPropertyItem
; Link ..........: https://msdn.microsoft.com/en-us/library/windows/desktop/ms535390(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/windows/desktop/ms534493(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/windows/desktop/ms534414(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/ms534416.aspx#_gdiplus_constant_propertytaggpsver
; Example .......: Yes
; ===============================================================================================================================

Func cGDIPlus_ImageGetAllPropertyItems($hImage)
    Local $aResult = DllCall($__g_hGDIPDll, "uint", "GdipGetPropertySize", "handle", $hImage, "uint*", 0, "uint*", 0)
    If @error Then Return SetError(@error, @extended, -1)
    If $aResult[0] Then Return SetError(10, $aResult[0], -1)
    Local $iTotalBufferSize = $aResult[2]
    Local $iNumProperties = $aResult[3]

    Local $tBuffer = DllStructCreate("byte[" & $iTotalBufferSize & "]")
    Local $pBuffer = DllStructGetPtr($tBuffer)

    $aResult = DllCall($__g_hGDIPDll, "uint", "GdipGetAllPropertyItems", "handle", $hImage, _
            "uint", $iTotalBufferSize, "uint", $iNumProperties, "ptr", $pBuffer)
    If @error Then Return SetError(@error, @extended, -1)
    If $aResult[0] Then Return SetError(10, $aResult[0], -1)

    Local $aPropertyItems[$iNumProperties][4]
    Local $a1
    Local $tPropertyItem = DllStructCreate("int id; int length; short type; ptr value;")

    For $iI = 0 To $iNumProperties - 1
        $a1 = __GDIPlus_ImageGetPropertyItemValues($pBuffer)
        For $j = 0 To 3
            $aPropertyItems[$iI][$j] = $a1[$j]
        Next
        $pBuffer += DllStructGetSize($tPropertyItem)
    Next

    Return $aPropertyItems
EndFunc   ;==>cGDIPlus_ImageGetAllPropertyItems

; #FUNCTION# ====================================================================================================================
; Name ..........: cGDIPlus_ImageGetPropertyItem
; Description ...: Gets a specified property item (piece of meta data) from an Image object
; Syntax ........: cGDIPlus_ImageGetPropertyItem($hImage, $iPropID)
; Parameters ....: $hImage              - Pointer to an image object
;                  $iPropID             - Identifier of the property item to be retrieved
; Return values .: Success: Array containing the values of the property item
;                      [0] - identifier
;                      [1] - size, in bytes, of the value array
;                      [2] - type of value(s) in the value array
;                      [3] - value array
;                  Failure: sets the @error flag to:
;                      1:  DllCall failed.  Common cause: _GIDPlus_Startup() has not been called
;                     10:  @extended may contain GPSTATUS error code ($GPID_ERR* see GDIPlusConstants.au3).
;                          @extended=19 ($$GDIP_ERRPROPERTYNOTFOUND): property item not found
; Author ........: Eukalyptus
; Modified ......: c.haslam
; Remarks .......: types: unsigned byte = 1, ASCII string = 2, unsigned short = 3, unsigned long = 4,
;                         unsinged rational = 5, signed byte = 6, undefined = 7, signed long = 9,
;                         signed rational = 10
;+
;                   For list of EXIF property items, see https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html
;                  + and https://www.media.mit.edu/pia/Research/deepview/exif.html
; Related .......: _GDIPlus_ImageGetPropertyIdList, cGDIPlus_ImageSetPropertyItem
; Link ..........: https://msdn.microsoft.com/en-us/library/windows/desktop/ms535390(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/windows/desktop/ms534493(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/windows/desktop/ms534414(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/ms534416.aspx#_gdiplus_constant_propertytaggpsver
; Example .......: Yes
; ===============================================================================================================================
Func cGDIPlus_ImageGetPropertyItem($hImage, $iPropID)
    Local $iSize = c__GDIPlus_ImageGetPropertyItemSize($hImage, $iPropID)
    If @error Then Return SetError(10, @extended, False)

    Local $tBuffer = DllStructCreate("byte[" & $iSize & "];")
    Local $pBuffer = DllStructGetPtr($tBuffer)
    Local $aResult = DllCall($__g_hGDIPDll, "int", "GdipGetPropertyItem", "handle", $hImage, "uint", _
            $iPropID, "uint", $iSize, "struct*", $tBuffer)
    If @error Then Return SetError(@error, @extended, False)
    If $aResult[0] Then Return SetError(10, $aResult[0], False)

    Return __GDIPlus_ImageGetPropertyItemValues($pBuffer)
EndFunc   ;==>cGDIPlus_ImageGetPropertyItem

; #INTERNAL_USE_ONLY# ===========================================================================================================
; Name ..........: c__GDIPlus_ImageGetPropertyItemSize
; Description ...: Gets the size, in bytes, of a specified property item of an image object
; Syntax ........: __GDIPlus_ImageGetPropertyItemSize($hImage, $iPropID)
; Parameters ....: $hImage              - Pointer to an image object
;                  $iPropID             - Identifier of the property item to be retrieved
; Return values .: Success      - Size in bytes of the property item
;                  Failure: sets the @error flag to:
;                      1:  DllCall failed.  Common cause: _GIDPlus_Startup() has not been called
;                     10:  @extended may contain GPSTATUS error code ($GPID_ERR* see GDIPlusConstants.au3).
;                          @extended=19 ($$GDIP_ERRPROPERTYNOTFOUND): property item not found
; Author ........: Authenticity
; Modified ......: Eukalyptus, c.haslam
; Remarks .......:
; Related .......: _GDIPlus_ImageGetPropertyItem
; Link ..........: @@MsdnLink@@ GdipGetPropertyItemSize
; Example .......: No
; ===============================================================================================================================
Func c__GDIPlus_ImageGetPropertyItemSize($hImage, $iPropID)
    Local $aResult = DllCall($__g_hGDIPDll, "int", "GdipGetPropertyItemSize", "handle", $hImage, "uint", $iPropID, "uint*", 0)
    If $aResult[3]=-1 Then
        Return SetError(10,$GDIP_ERRPROPERTYNOTFOUND,-1)    ; GdipGetPropertyItemSize doesn't set it!
    EndIf
    If @error Then Return SetError(@error, @extended, -1)
    If $aResult[0] Then Return SetError(10, $aResult[0], -1)
    Return $aResult[3]
EndFunc   ;==>__GDIPlus_ImageGetPropertyItemSize

; #INTERNAL_USE_ONLY# ===========================================================================================================
; Name ..........: __GDIPlus_ImageGetPropertyItemValues($pBuffer)
; Description ...: Converts a project item from structured to equivalent nested arrays
; Syntax ........: __GDIPlus_ImageGetPropertyItemValues($pBuffer)
; Parameters ....: $bBuffer             - Pointer to start of Property Item sructure
; Return values .:     [0] - identifier
;                      [1] - size, in bytes, of the value array
;                      [2] - type of value(s) in the value array
;                      [3] - value array
; Author ........: c.haslam
; Modified ......: UEZ
; Remarks .......: types: unsigned byte = 1, ASCII string = 2, unsigned short = 3, unsigned long = 4,
;                         unsigned rational = 5, signed byte = 6, undefined = 7, signed long = 9,
;                         signed rational = 10
; Related .......:
; Link ..........:
; Example .......: No
; ===============================================================================================================================

Func __GDIPlus_ImageGetPropertyItemValues($pBuffer)
    Local $tPropertyItem = DllStructCreate("int id; int length; short type; ptr value;", $pBuffer)
    Local $iBytes = DllStructGetData($tPropertyItem, "length")
    Local $pValue = DllStructGetData($tPropertyItem, "value")

    Local $aRet[4]
    Local $type = DllStructGetData($tPropertyItem, 'type')
    Local $tValues, $iValues

    Switch $type
        Case $GDIP_PROPERTYTAGTYPEASCII ;ASCII String
            $iValues = 1
            $tValues = DllStructCreate("char[" & $iBytes & "];", $pValue)
        Case $GDIP_PROPERTYTAGTYPESHORT ;Array of UShort
            $iValues = Int($iBytes / 2)
            $tValues = DllStructCreate("ushort[" & $iValues & "];", $pValue)
        Case $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL ;Array of UInt / Fraction
            $iValues = Int($iBytes / 4)
            $tValues = DllStructCreate("uint[" & $iValues & "];", $pValue)
        Case $GDIP_PROPERTYTAGTYPESLONG, $GDIP_PROPERTYTAGTYPESRATIONAL ;Array of Int / Fraction
            $iValues = Int($iBytes / 4)
            $tValues = DllStructCreate("int[" & $iValues & "];", $pValue)
        Case Else ;Array of Bytes
            $iValues = 1
            $tValues = DllStructCreate("byte[" & $iBytes & "];", $pValue)
    EndSwitch
    $aRet[0] = DllStructGetData($tPropertyItem, 'id')
    $aRet[1] = $iBytes
    $aRet[2] = $type
    Local $a1values[$iValues]
    If $type = $GDIP_PROPERTYTAGTYPEASCII Or $type = $GDIP_PROPERTYTAGTYPEUNDEFINED Then ; ASCII string or undefined
        $a1values[0] = DllStructGetData($tValues, 1)
    Else
        For $i = 0 To $iValues - 1
            $a1values[$i] = DllStructGetData($tValues, 1, $i + 1)
        Next
    EndIf
    $aRet[3] = $a1values
    Return $aRet
EndFunc   ;==>__GDIPlus_ImageGetPropertyItemValues

; #FUNCTION# ====================================================================================================================
; Name ..........: cGDIPlus_ImageSetPropertyItem
; Description ...: Sets a specified property item (piece of meta data) for an Image object
; Syntax ........: cGDIPlus_ImageGetPropertyItem($hImage, $a1PropertyItem)
; Parameters ....: $hImage              - Pointer to an image object
;                  $a1PropertyItem      - Array containing the values of the property item
;                      [0] - identifier
;                      [1] - size of the value array
;                               positive: in bytes
;                               negative: for numeric types: in number of values
;                      [2] - type of value(s) in the value array
;                      [3] - value array
; Return values .: Success: True and @error = 0
;                  Failure: sets the @error flag to:
;                      1:  DllCall failed.  Common cause: _GIDPlus_Startup() has not been called
;                      2:  $a1PropertyItem is not a 4-element 1-d array
;                     10:  @extended may contain GPSTATUS error code ($GPID_ERR* see GDIPlusConstants.au3).
;                    101:  identifier is not an integer
;                    201:  size is not an integer
;                    202:  size cannot be number of values for ASCII string and undefined types
;                    202:  size is zero
;                    301:  type is illegal
;                    401:  value array is either not a 1-d array or has no elements
;                    402:  size is incompatible with type
;                    403:  size cannot be negative for ASCII string and undefined types
;                    404:  Numerator and denominator are required for rational types
;                    405:  size and number of elements in value array differ
; Author ........: Eukalyptus
; Modified ......: c.haslam, UEZ
; Remarks .......: If size parameter is negative, calculates size in bytes from type.
;                  Convenient when setting a few property items
;+
;                  types: unsigned byte = 1, ASCII string = 2, unsigned short = 3, unsigned long = 4,
;                         unsinged rational = 5, undefined = 7, signed long = 9, signed rational = 10
;+
;                   For list of EXIF property items, see https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html
;                  + and https://www.media.mit.edu/pia/Research/deepview/exif.html
; Related .......: _GDIPlus_ImageGetPropertyIdList, cGDIPlus_ImageGetPropertyItem
; Link ..........: https://msdn.microsoft.com/en-us/library/windows/desktop/ms535390(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/windows/desktop/ms534493(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/windows/desktop/ms534414(v=vs.85).aspx
; Example .......: Yes
; ===============================================================================================================================
Func cGDIPlus_ImageSetPropertyItem($hImage, $a1PropertyItem)
    If (Not IsArray($a1PropertyItem)) Or UBound($a1PropertyItem, 0) <> 1 Or UBound($a1PropertyItem) <> 4 Then
        Return SetError(2, 0, False)
    EndIf
    Local $iId = $a1PropertyItem[0]
    Local $iLength = $a1PropertyItem[1]
    Local $iType = $a1PropertyItem[2]
    Local $a1values = $a1PropertyItem[3]

    If Not IsInt($iId) Then
        Return SetError(101, 0, False)
    EndIf
    If Not IsInt($iLength) Then
        Return SetError(201, 0, False)
    EndIf
    If $iLength = 0 Then
        Return SetError(202, 0, False)
    EndIf
    Switch $iType
        Case $GDIP_PROPERTYTAGTYPEBYTE, $GDIP_PROPERTYTAGTYPEASCII, $GDIP_PROPERTYTAGTYPESHORT, _
                $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL, $GDIP_PROPERTYTAGTYPEUNDEFINED, _
                $GDIP_PROPERTYTAGTYPESLONG, $GDIP_PROPERTYTAGTYPESRATIONAL
            ; do nothing
        Case Else
            Return SetError(301, 0, False)
    EndSwitch
    If (Not IsArray($a1values)) Or UBound($a1values, 0) <> 1 Or UBound($a1values) = 0 Then
        Return SetError(401, 0, False)
    EndIf

    Local $iBytes, $iqValues
    If $iLength > 0 Then ; useful when copying all property items
        $iBytes = $iLength
        Switch $iType
            Case $GDIP_PROPERTYTAGTYPEASCII ;ASCII String
                $iqValues = 1
            Case $GDIP_PROPERTYTAGTYPESHORT ;Array of UShort
                If BitAND($iLength, 1) <> 0 Then
                    Return SetError(402, 0, False)
                EndIf
                $iqValues = Int($iLength / 2)
            Case $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL, $GDIP_PROPERTYTAGTYPESLONG, _
                    $GDIP_PROPERTYTAGTYPESRATIONAL ;Array of UInt / Fraction
                If BitAND($iLength, 3) <> 0 Then
                    Return SetError(402, 0, False)
                EndIf
                $iqValues = Int($iLength / 4)
            Case Else ; Array of Bytes, undefined
                $iqValues = 1
        EndSwitch
    ElseIf $iLength < 0 Then ; convenient when setting a few property items
        Switch $iType
            Case $GDIP_PROPERTYTAGTYPEASCII, $GDIP_PROPERTYTAGTYPEUNDEFINED ;ASCII String, undefined
                Return SetError(403, 0, False)
            Case $GDIP_PROPERTYTAGTYPESHORT ;Array of UShort
                $iBytes = -$iLength * 2
            Case $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL, $GDIP_PROPERTYTAGTYPESLONG, _
                    $GDIP_PROPERTYTAGTYPESRATIONAL ;Array of UInt / Fraction
                $iBytes = -$iLength * 4
            Case Else ;Array of Bytes
                $iBytes = -$iLength
        EndSwitch
        $iqValues = -$iLength
    EndIf

    If $iType = $GDIP_PROPERTYTAGTYPERATIONAL Or $iType = $GDIP_PROPERTYTAGTYPERATIONAL Then
        If BitAND($iqValues, 1) <> 0 Then
            Return SetError(404, 0, False)
        EndIf
    EndIf

    Switch $iType
        Case $GDIP_PROPERTYTAGTYPESHORT, $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL, _
            $GDIP_PROPERTYTAGTYPESLONG, $GDIP_PROPERTYTAGTYPESRATIONAL
            If UBound($a1values)<>$iqValues Then
                Return SetError(405,0,False)
            EndIf
    EndSwitch

    Local $tPropItem = DllStructCreate("int id; int length; short type; ptr pValue;")
    DllStructSetData($tPropItem, 'id', $iId)
    DllStructSetData($tPropItem, 'length', $iBytes)
    DllStructSetData($tPropItem, 'type', $iType)

    Local $tValues

    Switch $iType
        Case $GDIP_PROPERTYTAGTYPEASCII ;ASCII String
            $tValues = DllStructCreate("char[" & $iBytes & "];")
        Case $GDIP_PROPERTYTAGTYPESHORT ;Array of UShort
            $tValues = DllStructCreate("ushort[" & $iqValues & "];")
        Case $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL ;Array of UInt / Fraction
            $tValues = DllStructCreate("uint[" & $iqValues & "];")
        Case $GDIP_PROPERTYTAGTYPESLONG, $GDIP_PROPERTYTAGTYPESRATIONAL ;Array of Int / Fraction
            $tValues = DllStructCreate("int[" & $iqValues & "];")
        Case Else ;Array of Bytes
            $tValues = DllStructCreate("byte[" & $iBytes & "];")
    EndSwitch
    If $iType = $GDIP_PROPERTYTAGTYPEASCII Or $iType = $GDIP_PROPERTYTAGTYPEUNDEFINED Then ; ASCII string or undefined
        DllStructSetData($tValues, 1, $a1values[0])
    Else
        For $i = 0 To $iqValues - 1
            DllStructSetData($tValues, 1, $a1values[$i], $i + 1)
        Next
    EndIf
    DllStructSetData($tPropItem, 'pValue', DllStructGetPtr($tValues))

    Local $aResult = DllCall($__g_hGDIPDll, "uint", "GdipSetPropertyItem", "handle", $hImage, "struct*", $tPropItem)
    If @error Then Return SetError(@error, @extended, -1)
    If $aResult[0] Then Return SetError(10, $aResult[0], -1)
    Return True
EndFunc   ;==>cGDIPlus_ImageSetPropertyItem

I discovered that GdipGetPropertySize returns 2 ($GDIP_ERRINVALIDPARAMETER) if an image has no properties. $GDIP_ERRPROPERTYNOTFOUND would be more logical. I added this specific error code to the Help for cGDIPlus_ImageGetAllPropertyItems()

Edited by c.haslam
Spoiler

CDebug Dumps values of variables including arrays and DLL structs, to a GUI, to the Console, and to the Clipboard

 

Link to comment
Share on other sites

for me is it not good to have the same @error = 1à the one after the size return and the other @error +10 should stay

it allows to differentiate whe the error come from

Link to comment
Share on other sites

It is up to you as a developer. Looking through GDIPlus.au3, I see that, for a user error. some functions return 0 (False) and others return -1.

Spoiler

CDebug Dumps values of variables including arrays and DLL structs, to a GUI, to the Console, and to the Clipboard

 

Link to comment
Share on other sites

If __GDIPlus_ImageGetPropertyItemSize() returns -1 and does not set an error, c_GDIPlus_ImageGetPropertyItemSize() will need to be changed. GdipGetPropertyItemSize returns 0 even if the property item does not exist.

Spoiler

CDebug Dumps values of variables including arrays and DLL structs, to a GUI, to the Console, and to the Clipboard

 

Link to comment
Share on other sites

  • 4 months later...

Hi Team,

looks promising :), but changed properties seem not to be saved with _GDIPlus_ImageSaveToFile(), or do I miss something? Maybe it's necessary to pass it along with the encoder params in_GDIPlus_ImageSaveToFileEx()?

Best Regards

Link to comment
Share on other sites

It has been a while since I was into this subject. I think that you are correct. I temporarily abandoned this project because of that, and because my code or the UDFs were causing a failure elsewhere in my script: something that worked until I got into changing properties. It seemed that memory was being overwritten. My script is several thousand lines, and I did not find a way of reproducing the problem in a short script. All I can say is that something is wrong somewhere. I was new to structs at the time, so spent a lot of time trying to discover whether I was misusing them.

Spoiler

CDebug Dumps values of variables including arrays and DLL structs, to a GUI, to the Console, and to the Clipboard

 

Link to comment
Share on other sites

Ah, me bad :>.

Quote

GDI+ does not allow you to save an image to the same file that you used to construct the image.

Works fine :thumbsup: ...

#AutoIt3Wrapper_UseX64=n
#include <GDIPlus.au3>
#include <Array.au3>
#include <WinAPIDiag.au3>

;--------- should be moved to GDIPlusConstants.au3
Global Const $GDIP_PROPERTYTAGTYPEBYTE = 1, $GDIP_PROPERTYTAGTYPEASCII = 2, $GDIP_PROPERTYTAGTYPESHORT = 3, $GDIP_PROPERTYTAGTYPELONG = 4, _
        $GDIP_PROPERTYTAGTYPERATIONAL = 5, $GDIP_PROPERTYTAGTYPEUNDEFINED = 7, $GDIP_PROPERTYTAGTYPESLONG = 9, _
        $GDIP_PROPERTYTAGTYPESRATIONAL = 10
;-------------------------------------------------

_GDIPlus_Startup()
Local $hImageF = _GDIPlus_ImageLoadFromFile(@ScriptDir & "\test.jpg")

; http://www.exiv2.org/tags.html
Local $a_PropertyValue = ['Cropped with BIC'] ; 0x9286 = Exif.Photo.UserComment
Local $a_PropertyItem = [0x9286, 40, 2, $a_PropertyValue]
cGDIPlus_ImageSetPropertyItem($hImageF, $a_PropertyItem)

_GDIPlus_ImageSaveToFile($hImageF, @ScriptDir & "\test2.jpg")
; GDI+ does not allow you to save an image to the same file that you used to construct the image.

_GDIPlus_ImageDispose($hImageF)
_GDIPlus_Shutdown()

ShellExecute(@ScriptDir & "\test2.jpg")

; #FUNCTION# ====================================================================================================================
; Name ..........: cGDIPlus_ImageSetPropertyItem
; Description ...: Sets a specified property item (piece of meta data) for an Image object
; Syntax ........: cGDIPlus_ImageGetPropertyItem($hImage, $a1PropertyItem)
; Parameters ....: $hImage              - Pointer to an image object
;                  $a1PropertyItem      - Array containing the values of the property item
;                      [0] - identifier
;                      [1] - size of the value array
;                               positive: in bytes
;                               negative: for numeric types: in number of values
;                      [2] - type of value(s) in the value array
;                      [3] - value array
; Return values .: Success: True and @error = 0
;                  Failure: sets the @error flag to:
;                      1:  DllCall failed.  Common cause: _GIDPlus_Startup() has not been called
;                      2:  $a1PropertyItem is not a 4-element 1-d array
;                     10:  @extended may contain GPSTATUS error code ($GPID_ERR* see GDIPlusConstants.au3).
;                    101:  identifier is not an integer
;                    201:  size is not an integer
;                    202:  size cannot be number of values for ASCII string and undefined types
;                    202:  size is zero
;                    301:  type is illegal
;                    401:  value array is either not a 1-d array or has no elements
;                    402:  size is incompatible with type
;                    403:  size cannot be negative for ASCII string and undefined types
;                    404:  Numerator and denominator are required for rational types
;                    405:  size and number of elements in value array differ
; Author ........: Eukalyptus
; Modified ......: c.haslam, UEZ
; Remarks .......: If size parameter is negative, calculates size in bytes from type.
;                  Convenient when setting a few property items
;+
;                  types: unsigned byte = 1, ASCII string = 2, unsigned short = 3, unsigned long = 4,
;                         unsinged rational = 5, undefined = 7, signed long = 9, signed rational = 10
;+
;                   For list of EXIF property items, see https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html
;                  + and https://www.media.mit.edu/pia/Research/deepview/exif.html
; Related .......: _GDIPlus_ImageGetPropertyIdList, cGDIPlus_ImageGetPropertyItem
; Link ..........: https://msdn.microsoft.com/en-us/library/windows/desktop/ms535390(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/windows/desktop/ms534493(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/windows/desktop/ms534414(v=vs.85).aspx
; Example .......: Yes
; ===============================================================================================================================
Func cGDIPlus_ImageSetPropertyItem($hImage, $a1PropertyItem)
    If (Not IsArray($a1PropertyItem)) Or UBound($a1PropertyItem, 0) <> 1 Or UBound($a1PropertyItem) <> 4 Then
        Return SetError(2, 0, False)
    EndIf
    Local $iId = $a1PropertyItem[0]
    Local $iLength = $a1PropertyItem[1]
    Local $iType = $a1PropertyItem[2]
    Local $a1values = $a1PropertyItem[3]

    If Not IsInt($iId) Then
        Return SetError(101, 0, False)
    EndIf
    If Not IsInt($iLength) Then
        Return SetError(201, 0, False)
    EndIf
    If $iLength = 0 Then
        Return SetError(202, 0, False)
    EndIf
    Switch $iType
        Case $GDIP_PROPERTYTAGTYPEBYTE, $GDIP_PROPERTYTAGTYPEASCII, $GDIP_PROPERTYTAGTYPESHORT, _
                $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL, $GDIP_PROPERTYTAGTYPEUNDEFINED, _
                $GDIP_PROPERTYTAGTYPESLONG, $GDIP_PROPERTYTAGTYPESRATIONAL
            ; do nothing
        Case Else
            Return SetError(301, 0, False)
    EndSwitch
    If (Not IsArray($a1values)) Or UBound($a1values, 0) <> 1 Or UBound($a1values) = 0 Then
        Return SetError(401, 0, False)
    EndIf

    Local $iBytes, $iqValues
    If $iLength > 0 Then ; useful when copying all property items
        $iBytes = $iLength
        Switch $iType
            Case $GDIP_PROPERTYTAGTYPEASCII ;ASCII String
                $iqValues = 1
            Case $GDIP_PROPERTYTAGTYPESHORT ;Array of UShort
                If BitAND($iLength, 1) <> 0 Then
                    Return SetError(402, 0, False)
                EndIf
                $iqValues = Int($iLength / 2)
            Case $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL, $GDIP_PROPERTYTAGTYPESLONG, _
                    $GDIP_PROPERTYTAGTYPESRATIONAL ;Array of UInt / Fraction
                If BitAND($iLength, 3) <> 0 Then
                    Return SetError(402, 0, False)
                EndIf
                $iqValues = Int($iLength / 4)
            Case Else ; Array of Bytes, undefined
                $iqValues = 1
        EndSwitch
    ElseIf $iLength < 0 Then ; convenient when setting a few property items
        Switch $iType
            Case $GDIP_PROPERTYTAGTYPEASCII, $GDIP_PROPERTYTAGTYPEUNDEFINED ;ASCII String, undefined
                Return SetError(403, 0, False)
            Case $GDIP_PROPERTYTAGTYPESHORT ;Array of UShort
                $iBytes = -$iLength * 2
            Case $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL, $GDIP_PROPERTYTAGTYPESLONG, _
                    $GDIP_PROPERTYTAGTYPESRATIONAL ;Array of UInt / Fraction
                $iBytes = -$iLength * 4
            Case Else ;Array of Bytes
                $iBytes = -$iLength
        EndSwitch
        $iqValues = -$iLength
    EndIf

    If $iType = $GDIP_PROPERTYTAGTYPERATIONAL Or $iType = $GDIP_PROPERTYTAGTYPERATIONAL Then
        If BitAND($iqValues, 1) <> 0 Then
            Return SetError(404, 0, False)
        EndIf
    EndIf

    Switch $iType
        Case $GDIP_PROPERTYTAGTYPESHORT, $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL, _
                $GDIP_PROPERTYTAGTYPESLONG, $GDIP_PROPERTYTAGTYPESRATIONAL
            If UBound($a1values) <> $iqValues Then
                Return SetError(405, 0, False)
            EndIf
    EndSwitch

    Local $tPropItem = DllStructCreate("int id; int length; short type; ptr pValue;")
    DllStructSetData($tPropItem, 'id', $iId)
    DllStructSetData($tPropItem, 'length', $iBytes)
    DllStructSetData($tPropItem, 'type', $iType)

    Local $tValues

    Switch $iType
        Case $GDIP_PROPERTYTAGTYPEASCII ;ASCII String
            $tValues = DllStructCreate("char[" & $iBytes & "];")
        Case $GDIP_PROPERTYTAGTYPESHORT ;Array of UShort
            $tValues = DllStructCreate("ushort[" & $iqValues & "];")
        Case $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL ;Array of UInt / Fraction
            $tValues = DllStructCreate("uint[" & $iqValues & "];")
        Case $GDIP_PROPERTYTAGTYPESLONG, $GDIP_PROPERTYTAGTYPESRATIONAL ;Array of Int / Fraction
            $tValues = DllStructCreate("int[" & $iqValues & "];")
        Case Else ;Array of Bytes
            $tValues = DllStructCreate("byte[" & $iBytes & "];")
    EndSwitch
    If $iType = $GDIP_PROPERTYTAGTYPEASCII Or $iType = $GDIP_PROPERTYTAGTYPEUNDEFINED Then ; ASCII string or undefined
        DllStructSetData($tValues, 1, $a1values[0])
    Else
        For $i = 0 To $iqValues - 1
            DllStructSetData($tValues, 1, $a1values[$i], $i + 1)
        Next
    EndIf
    DllStructSetData($tPropItem, 'pValue', DllStructGetPtr($tValues))

    Local $aResult = DllCall($__g_hGDIPDll, "uint", "GdipSetPropertyItem", "handle", $hImage, "struct*", $tPropItem)
    If @error Then Return SetError(@error, @extended, -1)
    If $aResult[0] Then Return SetError(10, $aResult[0], -1)
    Return True
EndFunc   ;==>cGDIPlus_ImageSetPropertyItem

 

Link to comment
Share on other sites

  • 4 years later...

Hello,

I need to recurse through a directory, grab the lat+lon of each JPG file therein, and check if it was shot within 100m from a reference location.

Can this UDF read the values of GPSLatitude + GPSLongitude?

Thank you.

#include <GUIConstantsEx.au3>
#include <ListViewConstants.au3>
#include <GDIPlus.au3>
#include <File.au3>
;NO GPS #include "image_get_info.au3" ;https://www.autoitscript.com/forum/topic/13096-udf-imagegetinfo/
#include <GDIPlus.au3>

_GDIPlus_Startup()

Local $hImage = _GDIPlus_ImageLoadFromFile("test.jpg")
If @error Then
    _GDIPlus_Shutdown()
    ConsoleWrite("An error has occured - unable to load image!")
    Exit
EndIf

Local $aPropID = _GDIPlus_ImageGetPropertyIdList($hImage)
;_ArrayDisplay($aPropID)
Local $aValues
For $i = 1 To $aPropID[0][0]
    $aValues = _GDIPlus_ImageGetPropertyItem($hImage, $aPropID[$i][0])
    ;GPSLatitude + GPSLongitude?
    ;_ArrayDisplay($aValues, $aPropID[$i][1])
    ConsoleWrite($aPropID[$i][1] & @CRLF)
Next

_GDIPlus_ImageDispose($hImage)
_GDIPlus_Shutdown()

 

Link to comment
Share on other sites

Yes it can. To calculate the distance use something like this: https://www.movable-type.co.uk/scripts/latlong.html

#include <GUIConstantsEx.au3>
#include <ListViewConstants.au3>
#include <GDIPlus.au3>
#include <File.au3>
;NO GPS #include "image_get_info.au3" ;https://www.autoitscript.com/forum/topic/13096-udf-imagegetinfo/
#include <GDIPlus.au3>

_GDIPlus_Startup()

Local $hImage = _GDIPlus_ImageLoadFromFile("test.jpg")
If @error Then
    _GDIPlus_Shutdown()
    ConsoleWrite("An error has occured - unable to load image!")
    Exit
EndIf

Local $aPropID = _GDIPlus_ImageGetPropertyIdList($hImage)
;_ArrayDisplay($aPropID)
Local $aValues, $GpsLatitudeRef, $GpsLongitudeRef
For $i = 1 To $aPropID[0][0]
    ;GPSLatitude + GPSLongitude?

    Switch $aPropID[$i][1]
        Case "GpsLatitudeRef"
            $aValues = _GDIPlus_ImageGetPropertyItem($hImage, $aPropID[$i][0])
            If IsArray($aValues) Then
                $GpsLatitudeRef = $aValues[1]
            EndIf

        Case "GpsLongitudeRef"
            $aValues = _GDIPlus_ImageGetPropertyItem($hImage, $aPropID[$i][0])
            If IsArray($aValues) Then
                $GpsLongitudeRef = $aValues[1]
            EndIf

        Case "GpsLatitude"
            Local $sOutput = ""
            $aValues = _GDIPlus_ImageGetPropertyItem($hImage, $aPropID[$i][0])
            If IsArray($aValues) Then
                For $y = 1 To $aValues[0]
                    $sOutput &= $aValues[$y] & "."
                Next
            EndIf
            $sOutput = $aPropID[$i][1] & " = " & $GpsLatitudeRef & " " & StringTrimRight($sOutput, 1)
            ConsoleWrite($sOutput & @CRLF)

            Local $sOutput = ""
            $aValues = _GDIPlus_ImageGetPropertyItem($hImage, $aPropID[$i][0])
            If IsArray($aValues) And $aValues[0] = 3 Then
                Local $GpsLatitude_Decimal
                For $y = 1 To $aValues[0]
                    Switch $y
                        Case 1
                            $GpsLatitude_Decimal = $aValues[$y]
                        Case 2
                            $GpsLatitude_Decimal += ($aValues[$y] / 60)
                        Case 3
                            $GpsLatitude_Decimal += ($aValues[$y] / 3600)
                    EndSwitch
                Next
            EndIf
            $sOutput = $aPropID[$i][1] & "Decimal = " & $GpsLatitudeRef & " " & $GpsLatitude_Decimal
            ConsoleWrite($sOutput & @CRLF & @CRLF)


        Case "GpsLongitude"
            Local $sOutput = ""
            $aValues = _GDIPlus_ImageGetPropertyItem($hImage, $aPropID[$i][0])
            If IsArray($aValues) Then
                For $y = 1 To $aValues[0]
                    $sOutput &= $aValues[$y] & "."
                Next
            EndIf
            $sOutput = $aPropID[$i][1] & " = " & $GpsLongitudeRef & " " & StringTrimRight($sOutput, 1)
            ConsoleWrite($sOutput & @CRLF)

            Local $sOutput = ""
            $aValues = _GDIPlus_ImageGetPropertyItem($hImage, $aPropID[$i][0])
            If IsArray($aValues) And $aValues[0] = 3 Then
                Local $GpsLongitude_Decimal
                For $y = 1 To $aValues[0]
                    Switch $y
                        Case 1
                            $GpsLongitude_Decimal = $aValues[$y]
                        Case 2
                            $GpsLongitude_Decimal += ($aValues[$y] / 60)
                        Case 3
                            $GpsLongitude_Decimal += ($aValues[$y] / 3600)
                    EndSwitch
                Next
            EndIf
            $sOutput = $aPropID[$i][1] & "Decimal = " & $GpsLongitudeRef & " " & $GpsLongitude_Decimal
            ConsoleWrite($sOutput & @CRLF & @CRLF)

    EndSwitch
Next

_GDIPlus_ImageDispose($hImage)
_GDIPlus_Shutdown()

 

Link to comment
Share on other sites

Thanks.

For others' benefit, here's some code that seems to work to find geotagged pictures that were shot within 100 meters from a reference location:
 

#include <GUIConstantsEx.au3>
#include <ListViewConstants.au3>
#include <GDIPlus.au3>
#include <File.au3>
#include <Math.au3>

;https://stackoverflow.com/questions/37484988/distance-formula-between-two-lat-longs-autoit
Func _distanceInKm($lat1, $lon1, $lat2, $lon2)
    Local $iRadius = 6371

    Local $iLat = _Radian($lat2 - $lat1)
    Local $iLon = _Radian($lon2- $lon1)

    Local $a = Sin($iLat / 2) * Sin($iLat / 2) + Cos(_Radian($lat1)) * Cos(_Radian($lat2)) * Sin($iLon / 2) * Sin($iLon / 2)
    Local $c = 2 * ATan2(Sqrt($a), Sqrt(1 - $a))
    Local $d = $iRadius * $c

    Return Abs($d)
EndFunc

Func ATan2($y, $x)
    Return (2 * ATan($y / ($x + Sqrt($x * $x + $y * $y))))
EndFunc

Local Const $REF_LAT = 1.23, $REF_LON = 4.56
Local Const $RADIUS = 100 ; meters
Local $distance ; meters

ConsoleWrite("Let's get going" & @CRLF)

Local $sFileSelectFolder = FileSelectFolder("Select a folder", "")
If @error Then Exit MsgBox($MB_SYSTEMMODAL, "", "No folder was selected.")

Local $aArray = _FileListToArrayRec($sFileSelectFolder, "*.jpg", $FLTAR_FILES , $FLTAR_RECUR, $FLTAR_NOSORT,$FLTAR_FULLPATH  )
;_ArrayDisplay($aArray, "JPG")
If UBound($aArray) = 0 Then Exit ConsoleWrite("zero elements") ;1D array only

_GDIPlus_Startup()

Local $filename, $OSM_URL = "https://www.openstreetmap.org/?"
For $picindx = 1 to UBound($aArray) -1
    $filename = $aArray[$picindx]
    Local $hImage = _GDIPlus_ImageLoadFromFile($filename)
    If @error Then
        _GDIPlus_Shutdown()
        ConsoleWrite("An error has occured - unable to load image!  " & $filename)
        Exit
    EndIf
    Local $aPropID = _GDIPlus_ImageGetPropertyIdList($hImage)
    ;_ArrayDisplay($aPropID)
    Local $aValues, $GpsLatitudeRef, $GpsLongitudeRef
    For $i = 1 To $aPropID[0][0]
        Switch $aPropID[$i][1]
            Case "GpsLatitudeRef"
                $aValues = _GDIPlus_ImageGetPropertyItem($hImage, $aPropID[$i][0])
                If IsArray($aValues) Then
                    $GpsLatitudeRef = $aValues[1]
                EndIf

            Case "GpsLongitudeRef"
                $aValues = _GDIPlus_ImageGetPropertyItem($hImage, $aPropID[$i][0])
                If IsArray($aValues) Then
                    $GpsLongitudeRef = $aValues[1]
                EndIf

            Case "GpsLatitude"
                Local $sOutput = ""
                $aValues = _GDIPlus_ImageGetPropertyItem($hImage, $aPropID[$i][0])
                If IsArray($aValues) Then
                    For $y = 1 To $aValues[0]
                        $sOutput &= $aValues[$y] & "."
                    Next
                EndIf
;~                 $sOutput = $aPropID[$i][1] & " = " & $GpsLatitudeRef & " " & StringTrimRight($sOutput, 1)
;~                 ConsoleWrite($sOutput & @CRLF)

                Local $sOutput = ""
                $aValues = _GDIPlus_ImageGetPropertyItem($hImage, $aPropID[$i][0])
                If IsArray($aValues) And $aValues[0] = 3 Then
                    Local $GpsLatitude_Decimal
                    For $y = 1 To $aValues[0]
                        Switch $y
                            Case 1
                                $GpsLatitude_Decimal = $aValues[$y]
                            Case 2
                                $GpsLatitude_Decimal += ($aValues[$y] / 60)
                            Case 3
                                $GpsLatitude_Decimal += ($aValues[$y] / 3600)
                        EndSwitch
                    Next
                EndIf
                $sOutput = $aPropID[$i][1] & "Decimal = " & $GpsLatitudeRef & " " & $GpsLatitude_Decimal
                ;ConsoleWrite($sOutput & @CRLF & @CRLF)
                ;ConsoleWrite($GpsLatitude_Decimal & ",")

            Case "GpsLongitude"
                Local $sOutput = ""
                $aValues = _GDIPlus_ImageGetPropertyItem($hImage, $aPropID[$i][0])
                If IsArray($aValues) Then
                    For $y = 1 To $aValues[0]
                        $sOutput &= $aValues[$y] & "."
                    Next
                EndIf
;~                 $sOutput = $aPropID[$i][1] & " = " & $GpsLongitudeRef & " " & StringTrimRight($sOutput, 1)
;~                 ConsoleWrite($sOutput & @CRLF)

                Local $sOutput = ""
                $aValues = _GDIPlus_ImageGetPropertyItem($hImage, $aPropID[$i][0])
                If IsArray($aValues) And $aValues[0] = 3 Then
                    Local $GpsLongitude_Decimal
                    For $y = 1 To $aValues[0]
                        Switch $y
                            Case 1
                                $GpsLongitude_Decimal = $aValues[$y]
                            Case 2
                                $GpsLongitude_Decimal += ($aValues[$y] / 60)
                            Case 3
                                $GpsLongitude_Decimal += ($aValues[$y] / 3600)
                        EndSwitch
                    Next
                EndIf
                ;$sOutput = $aPropID[$i][1] & "Decimal = " & $GpsLongitudeRef & " " & $GpsLongitude_Decimal
                $OSM_URL &= StringFormat("mlat=%s&mlon=%s",$GpsLatitude_Decimal,$GpsLongitude_Decimal)
                $distance =Int(1000 * _distanceInKm($REF_LAT, $REF_LON, $GpsLatitude_Decimal, $GpsLongitude_Decimal))
                If $distance <= $RADIUS Then ;picture taken within $RADIUS of reference location?
                    ConsoleWrite($filename & @CRLF)
                    ConsoleWrite("Distance : " & $distance & @CRLF)
                    ConsoleWrite($OSM_URL & @CRLF & @CRLF)
                EndIf
                $OSM_URL = "https://www.openstreetmap.org/?"  ;reset
        EndSwitch
    Next
    _GDIPlus_ImageDispose($hImage)
Next

_GDIPlus_Shutdown()

ConsoleWrite("Done." & @CRLF)

 

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...