Jump to content

DllStructCreate not allocating memory for byte array?


c.haslam
 Share

Recommended Posts

In the code that follows, it appears that DllStructCreate() is not allocating memory in

$tag = 'byte val['&$iLength&']'
.
.
Static Local $tvalue = DllStructCreate($tag)

Running the code below, the problem does not show, but it does show in a much longer script: there _GDIPlus_ImageSaveToFile only writes about 30K bytes when it should write about 1MB, which it does when the code is

$tag = 'char val['&$iLength&']'
.
.
Static Local $tvalue = DllStructCreate($tag)

It should write about 1 MB.

I suggest caution in running the code below. On my PC, it caused a second instance of SciTE to appear at the top left of the Desktop, showing only the title bar, with a width of only approximately 200 pixels! Then rebooting the PC showed this at login. Further, double-clicking on the SciTE shortcut on the Desktop showed SciTE in the same way! (After running Regedit, and searching for SciTE, the shortcut behaves normally.)

I would appreciate help in determining whether or not there is a bug in DLLStructCreate, preferably a way which does not clobber Windows. Of course, It is possible that there is a bug in my code.

I have made $tvalue Static in the hope that this might make the code run properly. Doing this did not help.

My code is based on code written by Authenticity and ChrisL.

#include <GDIPlus.au3>
#include <Array.au3>
Opt('MustDeclareVars',1)

; Property Item structure
Global Const $tagGDIPPROPERTYITEM = _
    "uint id;" & _          ; ID of this property
    "ulong length;" & _     ; Length of the property value, in bytes
    "word type;" & _        ; Type of the value, as one of TAG_TYPE_XXX constants
    "ptr pvalue;"           ; pointer to property value

; Image property types constants
; Ref: https://www.media.mit.edu/pia/Research/deepview/exif.html
Global Const $GDIP_PROPERTYTAGTYPEUBYTE = 1
Global Const $GDIP_PROPERTYTAGTYPEASCII = 2
Global Const $GDIP_PROPERTYTAGTYPEUSHORT = 3
Global Const $GDIP_PROPERTYTAGTYPEULONG = 4
Global Const $GDIP_PROPERTYTAGTYPEURATIONAL = 5
Global Const $GDIP_PROPERTYTAGTYPESBYTE = 6
Global Const $GDIP_PROPERTYTAGTYPEUNDEFINED = 7
Global Const $GDIP_PROPERTYTAGTYPESSHORT = 8
Global Const $GDIP_PROPERTYTAGTYPESLONG = 9
Global Const $GDIP_PROPERTYTAGTYPESRATIONAL = 10
Global Const $GDIP_PROPERTYTAGTYPESFLOAT = 11
Global Const $GDIP_PROPERTYTAGTYPEDFLOAT = 12
main()

Func main()
    _GDIPlus_Startup()

    Local $hImage = _GDIPlus_ImageLoadFromFile('H:\temp\AP test data\DSC00824 - Copy.jpg')

    Local $ar = _GDIPlus_ImageGetAllPropertyItemsEx($hImage)
    Local $propsAr[UBound($ar,1)-1][5],$vec,$j=-1
    For $i = 1 To $ar[0][0]
        If $ar[$i][3]<>0 Then   ; pValue -- for Sony!
            $j += 1
            $propsAr[$j][0] = $ar[$i][0]    ; id
            $propsAr[$j][1] = $ar[$i][1]    ; length
            $propsAr[$j][2] =$ar[$i][2]; type
            $vec = _GDIPlus_ImageGetPropertyItemValue($ar[$i][1],$ar[$i][2],$ar[$i][3])
            $propsAr[$j][3] = $vec[0]   ; val1
            Switch $ar[$i][2]
                Case 5,10   ; $GDIP_PROPERTYTAGTYPEURATIONAL,$GDIP_PROPERTYTAGTYPESRATIONAL
                    $propsAr[$j][4] = $vec[1]
                Case Else
                    $propsAr[$j][4] = ''
            EndSwitch
        EndIf
    Next
    ReDim $propsAr[$j+1][5]

    For $i = 0 To UBound($propsAr,1)-1
        Switch $propsAr[$i][2]  ; type
            Case 5,10   ; $GDIP_PROPERTYTAGTYPEURATIONAL,$GDIP_PROPERTYTAGTYPESRATIONAL
                _GDIPlus_ImageSetPropertyItemEx($hImage,$propsAr[$i][0],$propsAr[$i][1], _
                    $propsAr[$i][2],$propsAr[$i][3],$propsAr[$i][4])
            Case Else
                _GDIPlus_ImageSetPropertyItemEx($hImage,$propsAr[$i][0],$propsAr[$i][1], _
                    $propsAr[$i][2],$propsAr[$i][3])
        EndSwitch
    Next
    _GDIPlus_ImageSaveToFile($hImage,'H:\b\1.jpg')
    _GDIPlus_ImageDispose($hImage)
    _GDIPlus_Shutdown()
EndFunc

Func _GDIPlus_ImageGetAllPropertyItemsEx($hImage)
    Local $iI, $iCount, $tBuffer, $pBuffer, $iBuffer, $tPropertyItem, $aSize, $aPropertyItems[1][1], $aResult

    $aSize = _GDIPlus_ImageGetPropertySize($hImage)
    If @error Then Return SetError(@error, @extended, -1)

    $iBuffer = $aSize[0]
    $tBuffer = DllStructCreate("byte[" & $iBuffer & "]")
    $pBuffer = DllStructGetPtr($tBuffer)
    $iCount = $aSize[1]

    $aResult = DllCall($__g_hGDIPDll, "uint", "GdipGetAllPropertyItems", "hwnd", $hImage, "uint", $iBuffer, "uint", $iCount, "ptr", $pBuffer)
    If @error Then Return SetError(@error, @extended, -1)
    If $aResult[0] Then Return SetError(10, $aResult[0], False)

    ReDim $aPropertyItems[$iCount + 1][4]
    $aPropertyItems[0][0] = $iCount

    For $iI = 1 To $iCount
        $tPropertyItem = DllStructCreate($tagGDIPPROPERTYITEM, $pBuffer)
        $aPropertyItems[$iI][0] = DllStructGetData($tPropertyItem, "id")
        $aPropertyItems[$iI][1] = DllStructGetData($tPropertyItem, "length")
        $aPropertyItems[$iI][2] = DllStructGetData($tPropertyItem, "type")
        $aPropertyItems[$iI][3] = DllStructGetData($tPropertyItem, "pvalue")
        $pBuffer += DllStructGetSize($tPropertyItem)
    Next

    Return $aPropertyItems
EndFunc

Func _GDIPlus_ImageGetPropertyItemValue($iLength, $iType, $pValue)
    Static Local $tvalue
    Switch $iType
        Case 1,6    ; $GDIP_PROPERTYTAGTYPEUBYTE,$GDIP_PROPERTYTAGTYPESBYTE
            $tvalue = DllStructCreate('byte val',$pValue)
        Case 2      ; $GDIP_PROPERTYTAGTYPEASCII
            $tvalue = DllStructCreate('char val['&$iLength&']',$pValue)
        Case 3      ; $GDIP_PROPERTYTAGTYPEUSHORT
            $tvalue = DllStructCreate('ushort val',$pValue)
        Case 4      ; $GDIP_PROPERTYTAGTYPEULONG
            $tvalue = DllStructCreate('ulong val',$pValue)
        Case 5      ; $GDIP_PROPERTYTAGTYPEURATIONAL
            $tvalue = DllStructCreate('ulong val1;ulong val2',$pValue)
        Case 7      ; $GDIP_PROPERTYTAGTYPEUNDEFINED
            ; undefined, per specification, but may be a long but is sometimes a string
            $tvalue = DllStructCreate('byte val['&$ilength&']',$pValue) ; see _GDIPlus_ImageSetPropertyItemEx
;~          $tvalue = DllStructCreate('char val['&$ilength&']',$pValue)
        Case 8      ; $GDIP_PROPERTYTAGTYPESSHORT
            $tvalue = DllStructCreate('short val',$pValue)
        Case 9      ; $GDIP_PROPERTYTAGTYPEULONG
            $tvalue = DllStructCreate('ulong val',$pValue)
        Case 10     ; $GDIP_PROPERTYTAGTYPESRATIONAL
            $tvalue = DllStructCreate('ulong val1;ulong val2',$pValue)
        Case 11     ; $GDIP_PROPERTYTAGTYPESFLOAT
            $tvalue = DllStructCreate('float val',$pValue)
        Case 12     ; $GDIP_PROPERTYTAGTYPEDFLOAT
            $tvalue = DllStructCreate('double val',$pValue)
    EndSwitch
    If @error Then Return SetError(@error,0,-1)
    Switch $iType
            Case 5,10   ; $GDIP_PROPERTYTAGTYPEURATIONAL,$GDIP_PROPERTYTAGTYPESRATIONAL
            Local $aRet[2]
            $aRet[0] = DllStructGetData($tvalue,'val1')
            $aRet[1] = DllStructGetData($tvalue,'val2')
        Case Else
            Local $aRet[1]
            $aRet[0] = DllStructGetData($tvalue,'val')
    EndSwitch
    Return $aRet
EndFunc

Func _GDIPlus_ImageGetPropertySize($hImage)
    Local $aSize[2], $aResult

    $aResult = DllCall($__g_hGDIPDll, "uint", "GdipGetPropertySize", "hwnd", $hImage, "uint*", 0, "uint*", 0)
    If @error Then Return SetError(@error, @extended, -1)

    $aSize[0] = $aResult[2]
    $aSize[1] = $aResult[3]
    Return $aSize
EndFunc   ;==>_GDIPlus_ImageGetPropertySize

Func _GDIPlus_ImageSetPropertyItemEx($hImage,$id,$iLength,$iType,$value1,$value2=-1)
    Local $tProp = DllStructCreate($tagGDIPPROPERTYITEM)
    DllStructSetData($tProp,'id',$id)
    DllStructSetData($tProp,'type',$itype)
    DllStructSetData($tProp,'length',$ilength)
    Local $tag
    Switch $iType
        Case $GDIP_PROPERTYTAGTYPEUBYTE,$GDIP_PROPERTYTAGTYPESBYTE
            $tag = 'byte val'
        Case $GDIP_PROPERTYTAGTYPEASCII
            $tag = 'char val['&$iLength&']'
        Case $GDIP_PROPERTYTAGTYPEUSHORT
            $tag = 'ushort val'
        Case $GDIP_PROPERTYTAGTYPEULONG
            $tag = 'ulong val'
        Case $GDIP_PROPERTYTAGTYPEURATIONAL
            $tag =  'ulong val1;ulong val2'
        Case $GDIP_PROPERTYTAGTYPEUNDEFINED
            ; undefined, per specification, but may be a long but is sometimes a string
            $tag = 'byte val['&$iLength&']' ; causes saving to jpeg to write junk
;~          $tag = 'char val['&$iLength&']'
        Case $GDIP_PROPERTYTAGTYPESSHORT
            $tag = 'short val'
        Case $GDIP_PROPERTYTAGTYPEULONG
            $tag = 'ulong val'
        Case $GDIP_PROPERTYTAGTYPESRATIONAL
            $tag =  'long val1;long val2'
        Case $GDIP_PROPERTYTAGTYPESFLOAT
            $tag = 'float val'
        Case $GDIP_PROPERTYTAGTYPEDFLOAT
            $tag = 'double val'
    EndSwitch
    Static Local $tvalue = DllStructCreate($tag)
    Switch $iType
        Case $GDIP_PROPERTYTAGTYPEURATIONAL,$GDIP_PROPERTYTAGTYPESRATIONAL
            DllStructSetData($tvalue,'val1',$value1)
            DllStructSetData($tvalue,'val2',$value2)
        Case Else
            DllStructSetData($tvalue,1,$value1)
    EndSwitch
    DllStructSetData($tProp,'pvalue',DllStructGetPtr($tvalue))

    Local $aResult = DllCall($__g_hGDIPDll, "int", "GdipSetPropertyItem", "hwnd", $hImage, "ptr", _
    DllStructGetPtr($tProp))
    If @error Then Return SetError(@error, @extended, -1)
    If $aResult[0] Then Return SetError(10, $aResult[0], -1)

    Return $aResult[0] = 0
EndFunc

I have seen $iLength be as much as 37 KB for $GDIP_PROPERTYTAGTYPEUNDEFINED.

Is there is a bug, how can this be demonstrated to the developers in a few lines of code (without clobbering Windows)?

 

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

I have now found, with code similar to that in the first post, that a script can produce different results when run several times. My new testing shows that there are inconsistent results in handling property types other than $GDIP_PROPERTYTAGTYPEUNDEFINED.

A result consists of:

  • ArrayDisplay .png file, showing the id, length, type and value(s) of each of the properties
  • Output file size reported by the script
  • @extended (GDIPLUS Status value) from _GDIPlus_ImageSaveToFile()

I know what val1 and val2 should be from Irfanview. The output file size should be 916K, it being a copy of the input file. The Status value should be zero.

I ran the script 7 times:

  • 3 displayed the values of the first 27 properties OK in _ArrayDisplay() and most of properties 30 to 63 as zeroes when they should be non-zero, output file size as 36K and GDIP Status as 7 (Win32 error)
  • 3 displayed the values of all properties correctly in _ArrayDisplay(), file size as 916K and GDIP status as 0 (OK)
  • 1 the values of the first 27 properties as zeros (which is wrong), file size as 916K and GDIP status as 0 (OK)

The PC is running AOK (except as noted in Post1, which happened once). I have an AutoIt script which runs automatically daily with no problems. So the cause of this odd behavior is either a subtle error in my code or a bug in AutoIt.

My thoughts turn to the idea that there is a memory leak somewhere, possibly caused by my code. The input file has not changed.

I am wondering how I can allocate memory to structs independent of DllStructCreate(). Doing so might identify the problem. Do I use _MemGlobalAlloc() to allocate memory and _MemGlobalFree() to release it in _GDIPlus_ImageGetPropertyItemValue()? If so, with what parameters?

The code follows, and the attached .png's show _ArrayDisplay() results.

 

#include <GDIPlus.au3>
#include <Array.au3>
Opt('MustDeclareVars',1)
;~ #include "..\cDebug.au3"

; Property Item structure
Global Const $tagGDIPPROPERTYITEM = _
    "uint id;" & _          ; ID of this property
    "ulong length;" & _     ; Length of the property value, in bytes
    "word type;" & _        ; Type of the value, as one of TAG_TYPE_XXX constants
    "ptr pvalue;"           ; pointer to property value

; Image property types constants
; Ref: https://www.media.mit.edu/pia/Research/deepview/exif.html
Global Const $GDIP_PROPERTYTAGTYPEUBYTE = 1
Global Const $GDIP_PROPERTYTAGTYPEASCII = 2
Global Const $GDIP_PROPERTYTAGTYPEUSHORT = 3
Global Const $GDIP_PROPERTYTAGTYPEULONG = 4
Global Const $GDIP_PROPERTYTAGTYPEURATIONAL = 5
Global Const $GDIP_PROPERTYTAGTYPESBYTE = 6
Global Const $GDIP_PROPERTYTAGTYPEUNDEFINED = 7
Global Const $GDIP_PROPERTYTAGTYPESSHORT = 8
Global Const $GDIP_PROPERTYTAGTYPESLONG = 9
Global Const $GDIP_PROPERTYTAGTYPESRATIONAL = 10
Global Const $GDIP_PROPERTYTAGTYPESFLOAT = 11
Global Const $GDIP_PROPERTYTAGTYPEDFLOAT = 12

OnAutoItExitRegister(FinishUp)

main()

Func main()
    _GDIPlus_Startup()

    Local $hImage = _GDIPlus_ImageLoadFromFile('H:\temp\AP test data\DSC00824 - Copy.jpg')
    Local $propsAr = GetProperties($hImage)

Local $dAr = $propsAr
For $i = 0 To UBound($propsAr,1)-1
    $dAr[$i][0] = Hex($propsAr[$i][0],4)
Next
_ArrayDisplay($dAr,'At line '&@ScriptLineNumber,'',0,Default,'id|length|type|val1|val2')

    For $i = 0 To UBound($propsAr,1)-1
        Switch $propsAr[$i][2]  ; type
            Case 5,10   ; $GDIP_PROPERTYTAGTYPEURATIONAL,$GDIP_PROPERTYTAGTYPESRATIONAL
                _GDIPlus_ImageSetPropertyItemEx($hImage,$propsAr[$i][0],$propsAr[$i][1], _
                    $propsAr[$i][2],$propsAr[$i][3],$propsAr[$i][4])
            Case Else
                _GDIPlus_ImageSetPropertyItemEx($hImage,$propsAr[$i][0],$propsAr[$i][1], _
                    $propsAr[$i][2],$propsAr[$i][3])
        EndSwitch
    Next
    $propsAr = GetProperties($hImage)   ; causes save file to fail
;~ _ArrayDisplay($propsAr,@ScriptLineNumber)
    Local $ouFilspc = 'H:\b\1.jpg'
    _GDIPlus_ImageSaveToFile($hImage,$ouFilspc)
    If @error Then MsgBox(0,'_GDIPlus_ImageSaveToFile','@error '&@error&' @extended '&@extended)
    _GDIPlus_ImageDispose($hImage)
    Local $vec = FileGetTime($ouFilspc)
    MsgBox(0,@ScriptLineNumber,'Output: file size '&FileGetSize($ouFilspc)& _
        ' bytes at '&StringFormat('%s-%02s-%02s %02s:%02s',$vec[0],$vec[1],$vec[2],$vec[3],$vec[4]))
EndFunc

Func GetProperties($hImage)
    Local $ar = _GDIPlus_ImageGetAllPropertyItemsEx($hImage)
    Local $propsAr[UBound($ar,1)-1][5],$vec,$j=-1
    For $i = 1 To $ar[0][0]
        If Not ($ar[$i][3]=0 Or ($ar[$i][2]=2 And $ar[$i][1]=0)) Then   ; pValue, type, length -- for Sony!
            $j += 1
            $propsAr[$j][0] = $ar[$i][0]    ; id
            $propsAr[$j][1] = $ar[$i][1]    ; length
            $propsAr[$j][2] =$ar[$i][2]; type
            $vec = _GDIPlus_ImageGetPropertyItemValue($ar[$i][1],$ar[$i][2],$ar[$i][3])
            $propsAr[$j][3] = $vec[0]   ; val1
            Switch $ar[$i][2]
                Case 5,10   ; $GDIP_PROPERTYTAGTYPEURATIONAL,$GDIP_PROPERTYTAGTYPESRATIONAL
                    $propsAr[$j][4] = $vec[1]
                Case Else
                    $propsAr[$j][4] = ''
            EndSwitch
        EndIf
    Next
    ReDim $propsAr[$j+1][5]
    Return $propsAr
EndFunc

Func _GDIPlus_ImageGetAllPropertyItemsEx($hImage)
    Local $iI, $iCount, $tBuffer, $pBuffer, $iBuffer, $tPropertyItem, $aSize, $aPropertyItems[1][1], $aResult

    $aSize = _GDIPlus_ImageGetPropertySize($hImage)
    If @error Then Return SetError(@error, @extended, -1)

    $iBuffer = $aSize[0]
    $tBuffer = DllStructCreate("byte[" & $iBuffer & "]")
    $pBuffer = DllStructGetPtr($tBuffer)
    $iCount = $aSize[1]

    $aResult = DllCall($__g_hGDIPDll, "uint", "GdipGetAllPropertyItems", "hwnd", $hImage, "uint", $iBuffer, "uint", $iCount, "ptr", $pBuffer)
    If @error Then Return SetError(@error, @extended, -1)
    If $aResult[0] Then Return SetError(10, $aResult[0], False)

    ReDim $aPropertyItems[$iCount + 1][4]
    $aPropertyItems[0][0] = $iCount

    For $iI = 1 To $iCount
        $tPropertyItem = DllStructCreate($tagGDIPPROPERTYITEM, $pBuffer)
        $aPropertyItems[$iI][0] = DllStructGetData($tPropertyItem, "id")
        $aPropertyItems[$iI][1] = DllStructGetData($tPropertyItem, "length")
        $aPropertyItems[$iI][2] = DllStructGetData($tPropertyItem, "type")
        $aPropertyItems[$iI][3] = DllStructGetData($tPropertyItem, "pvalue")
        $pBuffer += DllStructGetSize($tPropertyItem)
    Next

    Return $aPropertyItems
EndFunc

Func _GDIPlus_ImageGetPropertyItemValue($iLength, $iType, $pValue)
    Static Local $tvalue
    Switch $iType
        Case 1,6    ; $GDIP_PROPERTYTAGTYPEUBYTE,$GDIP_PROPERTYTAGTYPESBYTE
            $tvalue = DllStructCreate('byte val',$pValue)
        Case 2      ; $GDIP_PROPERTYTAGTYPEASCII
            $tvalue = DllStructCreate('char val['&$iLength&']',$pValue)
        Case 3      ; $GDIP_PROPERTYTAGTYPEUSHORT
            $tvalue = DllStructCreate('ushort val',$pValue)
        Case 4      ; $GDIP_PROPERTYTAGTYPEULONG
            $tvalue = DllStructCreate('ulong val',$pValue)
        Case 5      ; $GDIP_PROPERTYTAGTYPEURATIONAL
            $tvalue = DllStructCreate('ulong val1;ulong val2',$pValue)
        Case 7      ; $GDIP_PROPERTYTAGTYPEUNDEFINED
            ; undefined, per specification, but may be a long but is sometimes a string
            $tvalue = DllStructCreate('byte val['&$ilength&']',$pValue) ; see _GDIPlus_ImageSetPropertyItemEx
;~          $tvalue = DllStructCreate('char val['&$ilength&']',$pValue)
        Case 8      ; $GDIP_PROPERTYTAGTYPESSHORT
            $tvalue = DllStructCreate('short val',$pValue)
        Case 9      ; $GDIP_PROPERTYTAGTYPEULONG
            $tvalue = DllStructCreate('ulong val',$pValue)
        Case 10     ; $GDIP_PROPERTYTAGTYPESRATIONAL
            $tvalue = DllStructCreate('ulong val1;ulong val2',$pValue)
        Case 11     ; $GDIP_PROPERTYTAGTYPESFLOAT
            $tvalue = DllStructCreate('float val',$pValue)
        Case 12     ; $GDIP_PROPERTYTAGTYPEDFLOAT
            $tvalue = DllStructCreate('double val',$pValue)
    EndSwitch
    If @error Then Return SetError(@error,0,-1)
    Switch $iType
            Case 5,10   ; $GDIP_PROPERTYTAGTYPEURATIONAL,$GDIP_PROPERTYTAGTYPESRATIONAL
            Local $aRet[2]
            $aRet[0] = DllStructGetData($tvalue,'val1')
            $aRet[1] = DllStructGetData($tvalue,'val2')
        Case Else
            Local $aRet[1]
            $aRet[0] = DllStructGetData($tvalue,'val')
    EndSwitch
    Return $aRet
EndFunc

Func _GDIPlus_ImageGetPropertySize($hImage)
    Local $aSize[2], $aResult

    $aResult = DllCall($__g_hGDIPDll, "uint", "GdipGetPropertySize", "hwnd", $hImage, "uint*", 0, "uint*", 0)
    If @error Then Return SetError(@error, @extended, -1)

    $aSize[0] = $aResult[2]
    $aSize[1] = $aResult[3]
    Return $aSize
EndFunc   ;==>_GDIPlus_ImageGetPropertySize

Func _GDIPlus_ImageSetPropertyItemEx($hImage,$id,$iLength,$iType,$value1,$value2=-1)
    Local $tProp = DllStructCreate($tagGDIPPROPERTYITEM)
    DllStructSetData($tProp,'id',$id)
    DllStructSetData($tProp,'type',$itype)
    DllStructSetData($tProp,'length',$ilength)
    Local $tag,$tvalue
    Switch $iType
        Case $GDIP_PROPERTYTAGTYPEUBYTE,$GDIP_PROPERTYTAGTYPESBYTE
            $tvalue = DllStructCreate('byte val;')
        Case $GDIP_PROPERTYTAGTYPEASCII
            $tvalue = DllStructCreate('char val['&$iLength&'];')
        Case $GDIP_PROPERTYTAGTYPEUSHORT
            $tvalue = DllStructCreate('ushort val;')
        Case $GDIP_PROPERTYTAGTYPEULONG
            $tvalue = DllStructCreate('ulong val;')
        Case $GDIP_PROPERTYTAGTYPEURATIONAL
            $tvalue = DllStructCreate('ulong val1;ulong val2;')
        Case $GDIP_PROPERTYTAGTYPEUNDEFINED
            ; undefined, per specification, but may be a long but is sometimes a string
;~          $tag = 'byte val['&$iLength&']' ; causes saving to jpeg to write junk
            $tvalue = DllStructCreate('char val['&$iLength&'];')
        Case $GDIP_PROPERTYTAGTYPESSHORT
            $tvalue = DllStructCreate('short val;')
        Case $GDIP_PROPERTYTAGTYPEULONG
            $tvalue = DllStructCreate('ulong val;')
        Case $GDIP_PROPERTYTAGTYPESRATIONAL
            $tvalue = DllStructCreate('long val1;long val2;')
        Case $GDIP_PROPERTYTAGTYPESFLOAT
            $tvalue = DllStructCreate('float val;')
        Case $GDIP_PROPERTYTAGTYPEDFLOAT
            $tvalue = DllStructCreate('double val;')
    EndSwitch
;~ If @error Then _Msgdebug(@ScriptLineNumber&' create','@error,$id,$ilength,$iType,$value1,$value2',@error,Hex($id,4),$ilength,$iType,$value1,$value2)
    Switch $iType
        Case $GDIP_PROPERTYTAGTYPEURATIONAL,$GDIP_PROPERTYTAGTYPESRATIONAL
            DllStructSetData($tvalue,1,$value1)
;~ If @error Then _Msgdebug(@ScriptLineNumber&' val1','@error,$id,$ilength,$iType,$value1,$value2',@error,Hex($id,4),$ilength,$iType,$value1,$value2)
            DllStructSetData($tvalue,2,$value2)
;~ If @error Then _Msgdebug(@ScriptLineNumber&' val2','@error,$id,$ilength,$iType,$value1,$value2',@error,Hex($id,4),$ilength,$iType,$value1,$value2)
        Case Else
            DllStructSetData($tvalue,1,$value1)
;~ If @error Then _Msgdebug(@ScriptLineNumber&' val','@error,$id,$ilength,$iType,$value1,$value2',@error,Hex($id,4),$ilength,$iType,$value1,$value2)
    EndSwitch
    DllStructSetData($tProp,'pvalue',DllStructGetPtr($tvalue))
;~ If @error Then _Msgdebug(@ScriptLineNumber&' pvalue','@error,$id,$ilength,$iType,$value1,$value2',@error,Hex($id,4),$ilength,$iType,$value1,$value2)

    Local $aResult = DllCall($__g_hGDIPDll, "int", "GdipSetPropertyItem", "hwnd", $hImage, "ptr", _
    DllStructGetPtr($tProp))
    If @error Then Return SetError(@error, @extended, -1)
    If $aResult[0] Then Return SetError(10, $aResult[0], -1)

    Return $aResult[0] = 0
EndFunc

Func FinishUp()
    _GDIPlus_Shutdown()
EndFunc

cDebug.au3 (which is commented out) is my variable-dump library, which can be found here .

The screenshot below shows the values of the first 27 properties as zeros (which is wrong). _GDIPlus_ImageSaveToFile() worked as expected.

Display 1st 27 0, 30 up OK File save 916K, GDIP status 0.png

The screenshot below shows the values of all properties correctly in _ArrayDisplay(). _GDIPlus_ImageSaveToFile() worked as expected.

Display OK File size 916K GDIP status 0.png

The screenshot below shows the values of the first 27 properties OK in _ArrayDisplay() and most of properties 30 to 63 as zeroes when they should be non-zero. The output file size as 36K (which is wrong) and GDIP Status as 7 (Win32 error).

Display first 27 OK 30 up 0 file save 36K GDIP status Win32 error.png

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

I have found an error in my understanding of how EXIF data is stored: for example, type $GDIP_PROPERTYTAGTYPEUSHORT (3) indicates that pValue points to an array of unsigned shorts. So there may not be an error in memory allocation by DllStructCreate().

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 modified the script, recognizing that most of the metadata is stored in a jpeg as arrays. This version abends at run-time with Variable used without being declared on $ouFilspc despite Au3Check not finding any errors, and $oufilspc being declared as Local a few lines before.

Suggestions are most welcome.

#include <GDIPlus.au3>
#include <Array.au3>
Opt('MustDeclareVars',1)
;~ #include "..\cDebug.au3"

; Property Item structure
Global Const $tagGDIPPROPERTYITEM = _
    "uint id;" & _          ; ID of this property
    "ulong length;" & _     ; Length of the property value, in bytes
    "word type;" & _        ; Type of the value, as one of TAG_TYPE_XXX constants
    "ptr pvalue;"           ; pointer to property value

; Image property types constants
; Ref: https://www.media.mit.edu/pia/Research/deepview/exif.html
Global Const $GDIP_PROPERTYTAGTYPEUBYTE = 1
Global Const $GDIP_PROPERTYTAGTYPEASCII = 2
Global Const $GDIP_PROPERTYTAGTYPEUSHORT = 3
Global Const $GDIP_PROPERTYTAGTYPEULONG = 4
Global Const $GDIP_PROPERTYTAGTYPEURATIONAL = 5
Global Const $GDIP_PROPERTYTAGTYPESBYTE = 6
Global Const $GDIP_PROPERTYTAGTYPEUNDEFINED = 7
Global Const $GDIP_PROPERTYTAGTYPESSHORT = 8
Global Const $GDIP_PROPERTYTAGTYPESLONG = 9
Global Const $GDIP_PROPERTYTAGTYPESRATIONAL = 10
Global Const $GDIP_PROPERTYTAGTYPESFLOAT = 11
Global Const $GDIP_PROPERTYTAGTYPEDFLOAT = 12

Global $gId

OnAutoItExitRegister(FinishUp)

main()

Func main()
    _GDIPlus_Startup()

;~  Local $hImage = _GDIPlus_ImageLoadFromFile('H:\b\1.jpg')
    Local $hImage = _GDIPlus_ImageLoadFromFile('H:\temp\AP test data\DSC00824 - Copy.jpg')
;~  Local $hImage = _GDIPlus_ImageLoadFromFile('H:\temp\AP test data\pergola photo - Copy.JPG')
    Local $propsAr = GetProperties($hImage)

Local $dAr = $propsAr
For $i = 0 To UBound($propsAr,1)-1
    $dAr[$i][0] = Hex($propsAr[$i][0],4)
    Switch True
        Case IsArray($propsAr[$i][3])
            Local $t='',$vec = $propsAr[$i][3]
            For $j = 0 To UBound($vec)-1
                $t &= '|'&$vec[$j]
            Next
            $t = StringMid($t,2)
            $dAr[$i][3] = $t
    EndSwitch
Next
_ArrayDisplay($dAr,'At line '&@ScriptLineNumber,'',0,Default,'id|length|type|value')

    For $i = 0 To UBound($propsAr,1)-1
        If Not _GDIPlus_ImageSetPropertyItemEx($hImage,$propsAr[$i][0],$propsAr[$i][1], _
            $propsAr[$i][2],$propsAr[$i][3]) Then
            ConsoleWrite('_GDIPlus_ImageSetPropertyItemEx failed'&@CRLF)
        EndIf
    Next
    Local $date = '2017:12:09 14:01:23'
    If Not _GDIPlus_ImageSetPropertyItemEx($hImage,0x9003,20,2,$date) Then

    Local $ouFilspc = 'H:\b\2.jpg'
    _GDIPlus_ImageSaveToFile($hImage,$ouFilspc)
    If @error Then MsgBox(0,'_GDIPlus_ImageSaveToFile','@error '&@error&' @extended '&@extended)
        ConsoleWrite('_GDIPlus_ImageSetPropertyItemEx failed'&@CRLF)
    EndIf

    _GDIPlus_ImageDispose($hImage)
    Local $vec = FileGetTime($ouFilspc) ; at run time:  $ouFilspc "Variable used without being declared"
    MsgBox(0,@ScriptLineNumber,'Output: file size '&FileGetSize($ouFilspc)& _
        ' bytes at '&StringFormat('%s-%02s-%02s %02s:%02s',$vec[0],$vec[1],$vec[2],$vec[3],$vec[4]))
EndFunc

Func GetProperties($hImage)
    Local $ar = _GDIPlus_ImageGetAllPropertyItemsEx($hImage)
    Local $j=-1,$id,$length,$type,$value
    For $i = 1 To $ar[0][0]
        $length = $ar[$i][1]
        $type = $ar[$i][2]
        $value = $ar[$i][3]
        If Not ($value=0 Or ($type=2 And $length=0)) Then
            $j += 1
            $ar[$j][0] = $ar[$i][0]
            $ar[$j][1] = $length
            $ar[$j][2] = $type
            $ar[$j][3] = $value
        EndIf
    Next
    ReDim $ar[$j+1][4]

    Local $propsAr[$j+1][4]
    For $i = 0 To UBound($ar,1)-1
        $propsAr[$i][0] = $ar[$i][0]    ; id
        $propsAr[$i][1] = $ar[$i][1]    ; length
        $propsAr[$i][2] = $ar[$i][2]    ; type
$gId = $propsAr[$i][0]
        $propsAr[$i][3] = _GDIPlus_ImageGetPropertyItemValue($ar[$i][1],$ar[$i][2],$ar[$i][3])
    Next
    Return $propsAr
EndFunc

Func _GDIPlus_ImageGetAllPropertyItemsEx($hImage)
    Local $iI, $iCount, $tBuffer, $pBuffer, $iBuffer, $tPropertyItem, $aSize, $aPropertyItems[1][1], $aResult

    $aSize = _GDIPlus_ImageGetPropertySize($hImage)
    If @error Then Return SetError(@error, @extended, -1)

    $iBuffer = $aSize[0]
    $tBuffer = DllStructCreate("byte[" & $iBuffer & "]")
    $pBuffer = DllStructGetPtr($tBuffer)
    $iCount = $aSize[1]

    $aResult = DllCall($__g_hGDIPDll, "uint", "GdipGetAllPropertyItems", "hwnd", $hImage, "uint", $iBuffer, "uint", $iCount, "ptr", $pBuffer)
    If @error Then Return SetError(@error, @extended, -1)
    If $aResult[0] Then Return SetError(10, $aResult[0], False)

    ReDim $aPropertyItems[$iCount + 1][4]
    $aPropertyItems[0][0] = $iCount

    For $iI = 1 To $iCount
        $tPropertyItem = DllStructCreate($tagGDIPPROPERTYITEM, $pBuffer)
        $aPropertyItems[$iI][0] = DllStructGetData($tPropertyItem, "id")
        $aPropertyItems[$iI][1] = DllStructGetData($tPropertyItem, "length")
        $aPropertyItems[$iI][2] = DllStructGetData($tPropertyItem, "type")
        $aPropertyItems[$iI][3] = DllStructGetData($tPropertyItem, "pvalue")
        $pBuffer += DllStructGetSize($tPropertyItem)
    Next

    Return $aPropertyItems
EndFunc

; Returns:
;   byte and undefined: binary
;   string              string
;   rational            vector: [n] numerator, [n=1] denominator
;   else                vector
; Vectors are ref 0
Func _GDIPlus_ImageGetPropertyItemValue($iLength, $iType, $pValue)
    Static Local $tvalue
    Local $qels

    Switch $iType
        Case 1,6,7  ; UBYTE,SBYTE,UNDEFINED
            $tvalue = DllStructCreate('byte binary['&$ilength&']',$pValue)
        Case 2      ; ASCII
            $tvalue = DllStructCreate('char str['&$iLength&']',$pValue)
        Case 3      ; USHORT
            $qels = $iLength/2
            $tvalue = DllStructCreate('ushort valVec['&$qels&']',$pValue)
        Case 4,5    ; ULONG,URATIONAL
            $qels = $iLength/4
            $tvalue = DllStructCreate('ulong valVec['&$qels&']',$pValue)
        Case 8      ; SSHORT
            $qels = $iLength/4
            $tvalue = DllStructCreate('short valVec['&$qels&']',$pValue)
        Case 9,10   ; SLONG,SRATIONAL
            $qels = $iLength/4
            $tvalue = DllStructCreate('long valVec['&$qels&']',$pValue)
        $tvalue = DllStructCreate('double val',$pValue)
    EndSwitch
Local $err = @error
If $err Then
    ConsoleWrite('@error '&$err&@CRLF)
    ConsoleWrite('id '&Hex($gId,4)&' ilength ' &$ilength&' type '&$itype&@CRLF)
    MsgBox(0,@ScriptLineNumber,'id '&Hex($gId,4)&' ilength ' &$ilength&' type '&$itype)
EndIf
    If $err Then Return SetError($err,0,-1)
    Local $ret
    Switch $itype
        Case 1,6,7  ; UBYTE,SBYTE,UNDEFINED
            Local $ret =  DllStructGetData($tvalue,'binary')
        Case 2 ; ASCII
            Local $ret = DllStructGetData($tvalue,'str')
        Case Else
            Local $ret[$qels]
            For $i = 0 To $qels-1
                $ret[$i] = DllStructGetData($tvalue,'valVec',$i+1)
            Next
    EndSwitch
If $err Then
    ConsoleWrite('@error '&$err&@CRLF)
    ConsoleWrite('GetData: id '&Hex($gId,4)&' ilength ' &$ilength&' type '&$itype&@CRLF)
    MsgBox(0,@ScriptLineNumber,'id '&Hex($gId,4)&' ilength ' &$ilength&' type '&$itype)
EndIf
    Return $ret
EndFunc

Func _GDIPlus_ImageGetPropertySize($hImage)
    Local $aSize[2], $aResult

    $aResult = DllCall($__g_hGDIPDll, "uint", "GdipGetPropertySize", "hwnd", $hImage, "uint*", 0, "uint*", 0)
    If @error Then Return SetError(@error, @extended, -1)

    $aSize[0] = $aResult[2]
    $aSize[1] = $aResult[3]
    Return $aSize
EndFunc   ;==>_GDIPlus_ImageGetPropertySize

Func _GDIPlus_ImageSetPropertyItemEx($hImage,$id,$iLength,$iType,$value)
    Local $tProp = DllStructCreate($tagGDIPPROPERTYITEM)
    DllStructSetData($tProp,'id',$id)
    DllStructSetData($tProp,'type',$itype)
    DllStructSetData($tProp,'length',$ilength)
    Local $tag,$tvalue,$qels
    Switch $iType
        Case 1,6,7  ; UBYTE,SBYTE,UNDEFINED
            $tvalue = DllStructCreate('byte binary['&$ilength&']')
        Case 2      ; ASCII
            $tvalue = DllStructCreate('char str['&$iLength&']')
        Case 3      ; USHORT
            $qels = $iLength/2
            $tvalue = DllStructCreate('ushort valVec['&$qels&']')
        Case 4,5    ; ULONG,URATIONAL
            $qels = $iLength/4
            $tvalue = DllStructCreate('ulong valVec['&$qels&']')
        Case 8      ; SSHORT
            $qels = $iLength/2
            $tvalue = DllStructCreate('short valVec['&$qels&']')
        Case 9,10   ; SLONG,SRATIONAL
            $qels = $iLength/4
            $tvalue = DllStructCreate('long valVec['&($iLength/4)&']')
    EndSwitch
Local $err = @error
If $err Then
    ConsoleWrite('@error '&$err&@CRLF)
    ConsoleWrite('SetData: id '&Hex($gId,4)&' ilength ' &$ilength&' type '&$itype&@CRLF)
    MsgBox(0,@ScriptLineNumber,'id '&Hex($gId,4)&' ilength ' &$ilength&' type '&$itype)
EndIf
    If $err Then Return SetError($err,0,-1)
    Switch $itype
        Case 1,6,7  ; UBYTE,SBYTE,UNDEFINED
            DllStructSetData($tvalue,'binary',$value)
        Case 2  ; ASCII
            DllStructSetData($tvalue,'str',$value)
        Case Else
            For $i = 0 To $qels-1
                DllStructSetData($tvalue,'valVec',$value[$i],$i+1)
            Next
    EndSwitch
    DllStructSetData($tProp,'pvalue',DllStructGetPtr($tvalue))
Local $err = @error
If $err Then
    ConsoleWrite('@error '&$err&@CRLF)
    ConsoleWrite('SetData: id '&Hex($gId,4)&' ilength ' &$ilength&' type '&$itype&@CRLF)
    MsgBox(0,@ScriptLineNumber,'id '&Hex($gId,4)&' ilength ' &$ilength&' type '&$itype)
EndIf

Local $ptr = DllStructGetPtr($tProp)
Local $err = @error
If $err Then
    ConsoleWrite('@error '&$err&@CRLF)
    ConsoleWrite('GetPtr: id '&Hex($gId,4)&' ilength ' &$ilength&' type '&$itype&@CRLF)
    MsgBox(0,@ScriptLineNumber,'id '&Hex($gId,4)&' ilength ' &$ilength&' type '&$itype)
EndIf
    Local $aResult = DllCall($__g_hGDIPDll, "int", "GdipSetPropertyItem", "hwnd", $hImage, "ptr", _
    $ptr)
    If @error Then Return SetError(@error, @extended, -1)
    If $aResult[0] Then Return SetError(10, $aResult[0], -1)
    Return $aResult[0] = 0
EndFunc
Func FinishUp()
    _GDIPlus_Shutdown()
EndFunc

 

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

The problem is that $outFilSpc is only declared if this If statement evaluates to true. If it's undeclared, then the If statement failed.

If Not _GDIPlus_ImageSetPropertyItemEx($hImage, 0x9003, 20, 2, $date) Then

        Local $ouFilspc = 'H:\b\2.jpg'
        _GDIPlus_ImageSaveToFile($hImage, $ouFilspc)
        If @error Then MsgBox(0, '_GDIPlus_ImageSaveToFile', '@error ' & @error & ' @extended ' & @extended)
        ConsoleWrite('_GDIPlus_ImageSetPropertyItemEx failed' & @CRLF)
    EndIf

 

If I posted any code, assume that code was written using the latest release version unless stated otherwise. Also, if it doesn't work on XP I can't help with that because I don't have access to XP, and I'm not going to.
Give a programmer the correct code and he can do his work for a day. Teach a programmer to debug and he can do his work for a lifetime - by Chirag Gude
How to ask questions the smart way!

I hereby grant any person the right to use any code I post, that I am the original author of, on the autoitscript.com forums, unless I've specifically stated otherwise in the code or the thread post. If you do use my code all I ask, as a courtesy, is to make note of where you got it from.

Back up and restore Windows user files _Array.au3 - Modified array functions that include support for 2D arrays.  -  ColorChooser - An add-on for SciTE that pops up a color dialog so you can select and paste a color code into a script.  -  Customizable Splashscreen GUI w/Progress Bar - Create a custom "splash screen" GUI with a progress bar and custom label.  -  _FileGetProperty - Retrieve the properties of a file  -  SciTE Toolbar - A toolbar demo for use with the SciTE editor  -  GUIRegisterMsg demo - Demo script to show how to use the Windows messages to interact with controls and your GUI.  -   Latin Square password generator

Link to comment
Share on other sites

I use single-line Ifs very seldom, because I often get them wrong, as I did above!

There are many alligators in my coding swamp right now, so it is not too surprising that I didn't seem that baby one!

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 found a curious situation. Snippet:

Func _GDIPlus_ImageGetAllPropertyItemsEx($hImage)
    Local $iI, $iCount, $tBuffer, $pBuffer, $iBuffer, $tPropertyItem, $aPropertyItems[1][1], $aResult
;~  Local $iI, $iCount, $tBuffer, $pBuffer, $iBuffer, $tPropertyItem, $aSize, $aPropertyItems[1][1], $aResult

    Local $ar = _GDIPlus_ImageGetPropertySize($hImage)
    If @error Then Return SetError(@error, @extended, -1)

    $iBuffer = $ar[0]
    $tBuffer = DllStructCreate("byte[" & $iBuffer & "]")
    $pBuffer = DllStructGetPtr($tBuffer)
    $iCount = $ar[1]
    $aResult = DllCall($__g_hGDIPDll, "uint", "GdipGetAllPropertyItems", "hwnd", $hImage, _
        "uint", $iBuffer, "uint", $iCount, "ptr", $pBuffer)
    If @error Then Return SetError(@error, @extended, -1)
    If $aResult[0] Then Return SetError(10, $aResult[0], False)
    ReDim $aPropertyItems[$iCount + 1][4]
    $aPropertyItems[0][0] = $iCount

    For $iI = 1 To $iCount
        $tPropertyItem = DllStructCreate($tagGDIPPROPERTYITEM, $pBuffer)
        $aPropertyItems[$iI][0] = DllStructGetData($tPropertyItem, "id")
        $aPropertyItems[$iI][1] = DllStructGetData($tPropertyItem, "length")
        $aPropertyItems[$iI][2] = DllStructGetData($tPropertyItem, "type")
        $aPropertyItems[$iI][3] = DllStructGetData($tPropertyItem, "pvalue")
        $pBuffer += DllStructGetSize($tPropertyItem)
    Next

    Return $aPropertyItems
EndFunc

Func _GDIPlus_ImageGetPropertySize($hImage)
    Local $aRet[2], $aResult

    $aResult = DllCall($__g_hGDIPDll, "uint", "GdipGetPropertySize", "hwnd", $hImage, "uint*", 0, "uint*", 0)
    If @error Then Return SetError(@error, @extended, -1)

    $aRet[0] = $aResult[2]  ; size in bytes
    $aRet[1] = $aResult[3]  ; number of property items
    Return $aRet
EndFunc   ;==>_GDIPlus_ImageGetPropertySize

When I run the script of which this is a part, $iBuffer is 43763 bytes long and $iCount is 66. The code above, which is from GDIP.7z, assumes that GdipGetAllProperty Items sets pBuffer to point to PropertyItem1, PropertyItem2, ... PropertyItemN. The property item struct is

$tagGDIPPROPERTYITEM = _
    "uint id;" & _          ; ID of this property
    "ulong length;" & _     ; Length of the property value, in bytes
    "word type;" & _        ; Type of the value, as one of TAG_TYPE_XXX constants
    "ptr pvalue;"           ; pointer to property value

Note the pointer. The length of this struct is 16 bytes. So if the data is arranged in this way, $iBuffer would be 66 x 16 = 1056 bytes! So the data cannot be arranged linearly, with one property item after another.

Scenario 2

What if the tag should be like

$tagGDIPPROPERTYITEM = _
    "uint id;" & _          ; ID of this property
    "ulong length;" & _     ; Length of the property value, in bytes
    "word type;" & _        ; Type of the value, as one of TAG_TYPE_XXX constants
    "byte valueVec["&$iLength&"];"

?

I know that one of the property items is about 37K long, and another 1.1K long. The lengths of id, length and type add up to 12 bytes. The lengths of the other values probably average 5.

37K + 1.1K + (12 + 5)*66 = 39222. This is fairly close to 43763 -- but not close enough to convince me that Scenario 2 is correct.

Also,it is strange that the buffer needs to be an odd number of bytes long, because the length of the struct in Scenario1 above (the one I am using) is an even number of bytes.

So what goes?

Early in my trials, I thought that the data is arranged as in Scenario 2. I don't remember where I got this idea.

If the data is arranged per Scenario 2, why does getting property items work as well as it does?

After a night's sleep, I realized that $iBuffer may be the total size occupied by property items. My script now adds up the lengths. They add up to 42707 bytes. 43763 - 42707 = 1056. 1056 / 16 = 66 which is the number of property items! So $iBuffer is the space occupied by values! It is time to do some more thinking.

This does mean that $tBuffer is larger than it needs to be. It also implies that $tagGDIPPROPERTYITEM is correct.

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

5 hours ago, c.haslam said:

There are many alligators in my coding swamp

:lol: You're lucky your swamp is in your code; mine is between my ears!:D

Not sure whether you've considered this (if so, just ignore this), but be aware of potential member alignment issues within your structs; unless you explicitly align, AutoIt may adjust alignment (implicit align 8) and apply padding. Note in particular (from the Help for DllstructCreate):

Quote

The alignment of a member will be on a boundary that is either a multiple of n or a multiple of the size of the member, whichever is smaller.  (...)

If a change of alignment is needed "align" can be use[d] before the first element which that needs to be changed (...)

The alignment of the beginning of a structure is the maximum alignment of any individual member.
Each member within the structure is be placed at its proper alignment as defined in the previous table, which require implicit internal padding, depending on the previous member.

The interplay of these "rules" may yield some unexpected results (which is why I tend to align 1 explicitly). Just my two cents.

;)

Edited by RTFC

My Contributions and Wrappers

Link to comment
Share on other sites

RTFC,

I found a bug in GDIPlus_ImageGetPropertyItemValue(): for Case 9,10 I had accidentally left in

$tvalue = DllStructCreate('double val',$pValue)

I have removed it.

I tried adding align 1; at the start of all tags in GDIPlus_ImageGetPropertyItemValue() . This did not change the results in _ArrayDisplay().

I tried adding align 1; at the start of $tagGDIPPROPERTYITEM. This crashed with rc:-1073741819

I was had been hopeful that your suggestion would solve the problem.

GDIPlus_ImageGetPropertyItemValue() now looks like this:

; Returns:
;   byte array and undefined:   binary
;   byte scalar                 scalar
;   string                      string
;   rational                    vector: [n] numerator, [n=1] denominator
;   else                        vector
; Vectors are ref 0
Func _GDIPlus_ImageGetPropertyItemValue($iLength, $iType, $pValue)
    Static Local $tvalue
    Local $qels
If $pValue=0 Then
Return 0
EndIf
    Switch $iType
        Case 1,6,7  ; UBYTE,SBYTE,UNDEFINED
            $qels = $ilength
If $qels=1 Then
$tvalue = DllStructCreate('align 1;byte binary',$pValue)
Else
            $tvalue = DllStructCreate('align 1;byte binary['&$ilength&']',$pValue)
EndIf
        Case 2      ; ASCII
If $ilength=0 Then
Return ''
EndIf
            $qels = $ilength
            $tvalue = DllStructCreate('align 1;char str['&$iLength&']',$pValue)
        Case 3      ; USHORT
            $qels = $iLength/2
            $tvalue = DllStructCreate('align 1;ushort valVec['&$qels&']',$pValue)
        Case 4,5    ; ULONG,URATIONAL
            $qels = $iLength/4
            $tvalue = DllStructCreate('align 1;ulong valVec['&$qels&']',$pValue)
        Case 8      ; SSHORT
            $qels = $iLength/4
            $tvalue = DllStructCreate('align 1;short valVec['&$qels&']',$pValue)
        Case 9,10   ; SLONG,SRATIONAL
            $qels = $iLength/4
            $tvalue = DllStructCreate('align 1;long valVec['&$qels&']',$pValue)
        Case Else
            MsgBox(0,@ScriptLineNumber,'_GDIPlus_ImageGetPropertyItemValue(): type '&$itype& ' found')
    EndSwitch
Local $err = @error
If $err Then
    ConsoleWrite('@error '&$err&@CRLF)
    ConsoleWrite('id '&Hex($gId,4)&' ilength ' &$ilength&' type '&$itype&@CRLF)
    MsgBox(0,@ScriptLineNumber,'id '&Hex($gId,4)&' ilength ' &$ilength&' type '&$itype)
EndIf
    If $err Then Return SetError($err,0,-1)
    Local $ret
    Switch $itype
        Case 1,6,7  ; UBYTE,SBYTE,UNDEFINED
            Local $ret =  DllStructGetData($tvalue,'binary')
        Case 2 ; ASCII
            Local $ret = DllStructGetData($tvalue,'str')
        Case Else
            Local $ret[$qels]
            For $i = 0 To $qels-1
                $ret[$i] = DllStructGetData($tvalue,'valVec',$i+1)
            Next
    EndSwitch
If $err Then
    ConsoleWrite('@error '&$err&@CRLF)
    ConsoleWrite('GetData: id '&Hex($gId,4)&' ilength ' &$ilength&' type '&$itype&@CRLF)
    MsgBox(0,@ScriptLineNumber,'id '&Hex($gId,4)&' ilength ' &$ilength&' type '&$itype)
EndIf
    Return $ret
EndFunc

Diagnostic code and most of the new code is outdented, for now.

I had been excluding property items for which the value is zero and ASCII property items for which the length is zero in the caller. These cases are now handled in this function.

For now, I am only working on the script through to the array display.

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

There are 3 possibilities for bugs:

  • In my code
  • In a DLLStruct* function
  • In Microsoft's gdip* functions

I have been working on the third possibility.

I have run the script that follows several times. Desk-checking the output to the console has convinced me, at least for now, that Microsoft's GdipGetAllPropertyItems() works consistently. Its return is as advertised, i.e. it is my Scenario 1. This does not rule out the possibility of a memory leak.

That leaves the other two possibilities. A memory leak is possible in both.

It is beyond me as to how the second script below sometimes reports an ASCII string, and sometimes doesn't.

The script used to check GdipGetAllPropertyItems() is below. It reports in the odd format because I needed to be able to find Id's if the alignment was other than 2.

#include <GDIPlus.au3>
#include <Array.au3>
Opt('MustDeclareVars',1)
#include "..\cDebug.au3"

OnAutoItExitRegister(FinishUp)

main()

Func main()
    _GDIPlus_Startup()
    Local $hImage = _GDIPlus_ImageLoadFromFile('H:\temp\AP test data\DSC00824 - Copy.jpg')
    Local $ar = _GDIPlus_ImageGetAllPropertyItemsEx($hImage)
EndFunc

Func _GDIPlus_ImageGetAllPropertyItemsEx($hImage)
    Local $iI, $iCount, $tBuffer, $pBuffer, $iBuffer, $tPropertyItem, $aPropertyItems[1][1], $aResult

    Local $ar = _GDIPlus_ImageGetPropertySize($hImage)
    If @error Then Return SetError(@error, @extended, -1)

    $iBuffer = $ar[0]
    $tBuffer = DllStructCreate("byte[" & $iBuffer & "]")
    $pBuffer = DllStructGetPtr($tBuffer)
    $iCount = $ar[1]
    $aResult = DllCall($__g_hGDIPDll, "uint", "GdipGetAllPropertyItems", "hwnd", $hImage, _
        "uint", $iBuffer, "uint", $iCount, "ptr", $pBuffer)
    If @error Then Return SetError(@error, @extended, -1)
    If $aResult[0] Then Return SetError(10, $aResult[0], False)
    ReDim $aPropertyItems[$iCount + 1][4]
    $aPropertyItems[0][0] = $iCount

    Local $byAr[$iCount*16]
    For $j = 0 To ($iCount-1)*16
        Local $t = DllStructCreate('byte;',$pBuffer+$j)
        $byAr[$j] = DllStructGetData($t,1)
    Next
    For $j = 0 To ($iCount-1)*16
        _consdebug(@ScriptLineNumber,'j,val',($j+1)&'-'&$j,Hex($byAr[$j+1],2)&Hex($byAr[$j],2))
    Next
EndFunc

Func _GDIPlus_ImageGetPropertySize($hImage)
    Local $aRet[2], $aResult

    $aResult = DllCall($__g_hGDIPDll, "uint", "GdipGetPropertySize", "hwnd", $hImage, "uint*", 0, "uint*", 0)
    If @error Then Return SetError(@error, @extended, -1)

    $aRet[0] = $aResult[2]  ; size in bytes
    $aRet[1] = $aResult[3]  ; number of property items
    Return $aRet
EndFunc   ;==>_GDIPlus_ImageGetPropertySize

Func FinishUp()
    _GDIPlus_Shutdown()
EndFunc

There is one strange finding: as mentioned in an earlier post, $tBuffer only needs to be large enough for the 66 property items, so I tried a buffer of 66*16 bytes.  GdipGetAllPropertyItems() returned an error code!

 

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

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...