Leaderboard
Popular Content
Showing content with the highest reputation on 07/30/2017 in all areas
-
This script allows you to 'browse' an array and view it's content. It's similar to the _ArrayDisplay() command, but this one has no limit on the number of dimensions. Nested array (array of arrays) are also allowed. (I'm been inspired by this @JohnOne's Post) Since multidimensional arrays can be considered as many bidimensional arrays grouped toghether, with this script you can view any of the many 2d arrays by selecting the dimension (the sheet number) you want to see through the ComboBoxes located at the top of the viewed sheet. regarding the arrays of array, if are present, those are listed on the treeview on the left side, where you can see their locations within the tree, and easily selected to be as well browsed by a click. I'm aware that there is a wide margin of aesthetic improvement, anyway the basic functionality works quite well. I hope it can be usefull ; Func _ArrayView() ; Will display content of arrays of any dimension, and even nested arrays if any ; #include <GUIConstantsEx.au3> #include <WindowsConstants.au3> #include <GuiListView.au3> #include <GUITreeView.au3> #include <GUIScrollBars.au3> #include <ScrollBarConstants.au3> #include <ComboConstants.au3> #include <GuiStatusBar.au3> ; -- build an example array (somewhat chaotic.. just to show something) -- ; 4D array (main array) Local $aA[10][5][6][3] For $d1 = 0 To 9 For $d2 = 0 To 4 For $d3 = 0 To 5 For $d4 = 0 To 2 $aA[$d1][$d2][$d3][$d4] = $d1 & "." & $d2 & "." & $d3 & "." & $d4 Next Next Next Next ; some 1D arrays with multi nested arrays Local $aMonths1 = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'] Local $aMonths2 = ["Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember", $aMonths1] Local $aMonths3 = ["Sijecanj", "veljaca", "ožujka", "travanj", "Svibanj", "lipanj", "srpanj", "kolovoz", "rujan", "listopad", "studeni", "prosinac", $aMonths2] Local $aMonths4 = ["Janvier", "Février", "Mars", "Avril", "mai", "Juin", "Juillet", "Août", "Septembre", "Octobre", "Novembre", "Décembre", $aMonths3] Local $aMonths5 = ['Gennaio', 'Febbraio', 'Marzo', 'Aprile', 'Maggio', 'giu', 'Luglio', 'Agosto', 'Settembre', 'ottobre', 'Novembre', 'Dicembre', $aMonths4] Local $aMonths = ["Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember", $aMonths5] ; a simple 2D array Local $aWeekdays = [['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'], _ ['Lunedì', 'martedì', 'mercoledì', 'giovedì', 'venerdì', 'sabato', 'domenica'], _ ['Lundi', 'Mardi', 'Mercredi', 'Jeudi', 'Vendredi', 'Samedi', 'Dimanche'], _ ['Måndag', 'tisdag', 'onsdag', 'torsdag', 'fredag', 'lördag', 'söndag'], _ ['Poniedziałek', 'Wtorek', 'Środa', 'Czwartek', 'Piątek', 'Sobota', 'Niedziela'], _ ['Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag', 'Sonntag']] ; array $aMonths goes in cell [5][3] dimension [0][0] of main array $aA[5][3][0][0] = $aMonths ; array $aWeekdays goes in cell [6][3] dimension [0][0] of main array $aA[6][3][0][0] = $aWeekdays $aWeekdays[1][1] = $aWeekdays ; self nest $aWeekdays in cell [1][1] $aA[9][4][0][1] = $aWeekdays ; new $aWeekdays goes in cel [9][4] dimension [0][1] of main array _ArrayView($aA) ; show main array ; #FUNCTION# ==================================================================================================================== ; Name ..........: _ArrayView ; Description ...: Allows to view content of amono or multidimensional array. Array of arrays are also allowed ; Syntax ........: _ArrayView(Byref $_aInput) ; Parameters ....: $_aInput - The array that you want to 'Browse' ; Return values .: on success returns 1 ; on faillure returns 0 and set @Error to 1 (passed argument is not an array) ; Author ........: @Chimp ; Modified ......: ; Remarks .......: ; Related .......: ; Link ..........: ; Example .......: ; =============================================================================================================================== Func _ArrayView(ByRef $_aInput) If Not IsArray($_aInput) Then Return SetError(1, 0, 0) ; if error set @Error and return 0 Local $iGUIwidth = 900 Local $iGUIheight = 600 Local $iTreeWidth = 150 Local $iCombosZone = 60 Local $hGui = GUICreate("Array viewer", $iGUIwidth, $iGUIheight) Local $StatusBar = _GUICtrlStatusBar_Create($hGui), $iStatusBarheight = 23 Local $aSubscripts[64][4] ; It holds IDs of controls ; - creates all ComboBox in an embedded window. All controls are hidden at startup. ; - Only the necessary combo will be shown at run time. One combo for each dimension Local $hSubscriptSelectors = GUICreate('', $iGUIwidth - $iTreeWidth - 6, $iCombosZone - 2, $iTreeWidth + 4, 2, BitOR($WS_CHILD, $WS_HSCROLL), -1, $hGui) ; GUISetBkColor(0xEEFFEE) For $i = 0 To 63 ; Create the labels $aSubscripts[$i][0] = GUICtrlCreateLabel('D' & $i + 1, ($i * 60) + 8, 1) GUICtrlSetFont(-1, 10, 0, 0, "Courier new") GUICtrlSetState(-1, $GUI_HIDE) ; Labels will be hidden at startup. $aSubscripts[$i][1] = GUICtrlCreateLabel('[', ($i * 60), 18) GUICtrlSetFont(-1, 13, 800) GUICtrlSetState(-1, $GUI_HIDE) $aSubscripts[$i][3] = GUICtrlCreateLabel(']', ($i * 60) + 50, 18) GUICtrlSetFont(-1, 13, 800) GUICtrlSetState(-1, $GUI_HIDE) Next For $i = 0 To 63 ; Create the ComboBox (creates separatelly from labels so that ControlIDs of ComboBaxes has it's own sequence) GUICtrlSetState(-1, $GUI_HIDE) ; ComboBox will be hidden at startup. If $i < 2 Then ; all the content of the first 2 dimensions is already shown in the listview (no need of ComboBox) $aSubscripts[$i][2] = GUICtrlCreateCombo("---", ($i * 60) + 8, 17, 40, -1, $CBS_DROPDOWNLIST) GUICtrlSetState(-1, $GUI_DISABLE) Else $aSubscripts[$i][2] = GUICtrlCreateCombo("0", ($i * 60) + 8, 17, 40, -1, $CBS_DROPDOWNLIST) EndIf GUICtrlSetFont(-1, 8, 800) GUICtrlSetState(-1, $GUI_HIDE) ; ComboBox hidden at startup. Next GUISwitch($hGui) ; back to main window ; Create the TreeView structure $hTree = GUICtrlCreateTreeView(2, 2, $iTreeWidth - 2, $iGUIheight - 4 - $iStatusBarheight, BitOR($TVS_HASBUTTONS, $TVS_HASLINES, $TVS_LINESATROOT, $TVS_DISABLEDRAGDROP, $TVS_SHOWSELALWAYS), $WS_EX_CLIENTEDGE) Local $hRoot = _GUICtrlTreeView_Add($hTree, 0, "Root") ; first insert the root key in treeview _ArrayTraverse($_aInput, $hTree, $hRoot) ; Search for SubArrays (array in array) ; Create the ListView Local $idListview = GUICtrlCreateListView('', $iTreeWidth + 2, $iCombosZone + 2, $iGUIwidth - $iTreeWidth - 4, $iGUIheight - $iCombosZone - 4 - $iStatusBarheight, Default, BitOR($LVS_EX_DOUBLEBUFFER, $LVS_EX_GRIDLINES)) ; If Array has many dimensions, and so all ComboBoxes doesn't fit in window, this allows to scroll GUIRegisterMsg($WM_HSCROLL, "WM_HSCROLL") _GUIScrollBars_Init($hSubscriptSelectors, 60 * UBound($_aInput, 0), 0) GUISetState(@SW_SHOW, $hGui) GUISetState(@SW_SHOW, $hSubscriptSelectors) ; Main Loop until the user exits. ; ------------------------------- Local $sLastWholeKey, $sWholeKey, $vContent, $bRebuild While 1 $Msg = GUIGetMsg() Switch $Msg Case $GUI_EVENT_CLOSE GUIDelete($hGui) ExitLoop Case $aSubscripts[2][2] To $aSubscripts[63][2] ; some ComboBox has changed _ArrayDisplayByLayer($vContent, $idListview, $aSubscripts, False) EndSwitch ; $sWholeKey = _GUICtrlTreeView_GetTree($hTree, _GUICtrlTreeView_GetSelection($hTree)) If $sLastWholeKey <> $sWholeKey Then ; clicked on a new KeyPath or (again) on the one already selected? GUISetCursor(15, 1) ; set cursor to "wait" ; Adapt the TreePath to the array access syntax $sElement = _TreePathParser($sWholeKey) ; address of main array or subarray to peek _GUICtrlStatusBar_SetText($StatusBar, StringReplace($sWholeKey, '|', '->') & ' --> ' & $sElement) ; show the 'address' of selected element on statusbar $vContent = Execute(_TreePathParser($sWholeKey)) _ArrayDisplayByLayer($vContent, $idListview, $aSubscripts, True) _GUICtrlTreeView_ClickItem($hTree, _GUICtrlTreeView_GetSelection($hTree)) $sLastWholeKey = $sWholeKey ; keep track of already clicked KeyPath so we will not redraw the same Array if clicked again GUISetCursor() ; cursor back to default EndIf ; WEnd Return SetError(0, 0, 1) ; if no errors return 1 EndFunc ;==>_ArrayView Func _ArrayDisplayByLayer(ByRef $_aInput, ByRef $idListview, ByRef $aSubscripts, $bRebuild = False) Opt('GUIOnEventMode', 1) ; Disable GUIGetMsg() so is not fired while redrawing ComboBoxes. Local $sTarghet = '$_aInput[$y]' Local $iDimensions = UBound($_aInput, 0) Local $iRows = UBound($_aInput, 1) Local $iColumnsCount, $iColumns = UBound($_aInput, 2) Local $sSubscripts = '' ; Clear the ListView _GUICtrlListView_DeleteAllItems($idListview) If $bRebuild Then ; (Re)Create the ListView $iColumnsCount = _GUICtrlListView_GetColumnCount($idListview) If $iColumnsCount Then For $i = $iColumnsCount To 1 Step -1 _GUICtrlListView_DeleteColumn($idListview, $i - 1) Next EndIf ; Hide and clear all ComboBox For $i = 0 To 63 GUICtrlSetState($aSubscripts[$i][0], $GUI_HIDE) ; Header GUICtrlSetState($aSubscripts[$i][1], $GUI_HIDE) ; '[' GUICtrlSetState($aSubscripts[$i][2], $GUI_HIDE) ; ComboBox Handle GUICtrlSetData($aSubscripts[$i][2], '') ; clear ComboBox items GUICtrlSetState($aSubscripts[$i][3], $GUI_HIDE) ; ']' Next ; (Re)Build the ListView's frame If $iDimensions = 1 Then $iColumns = 1 Else $iColumns = UBound($_aInput, 2) ; nr. of columns in the ListView (second dimension) $sTarghet &= '[$x]' EndIf _GUICtrlListView_AddColumn($idListview, 'Row') For $i = 1 To $iColumns _GUICtrlListView_AddColumn($idListview, 'Col ' & $i - 1, 100) Next For $i = 0 To $iDimensions - 1 ; Show only necessary ComboBox (one for each dimension) GUICtrlSetState($aSubscripts[$i][0], $GUI_SHOW) ; Header GUICtrlSetState($aSubscripts[$i][1], $GUI_SHOW) ; '[' GUICtrlSetState($aSubscripts[$i][2], $GUI_SHOW) ; ComboBox Handle GUICtrlSetState($aSubscripts[$i][3], $GUI_SHOW) ; ']' If $i > 1 Then $sTarghet &= '[0]' ; dimensions over the second all setting to 0 (begin showing first lyer) $sSubscripts = "" For $iSubscript = 0 To UBound($_aInput, $i + 1) - 1 $sSubscripts &= $iSubscript & '|' Next GUICtrlSetData($aSubscripts[$i][2], StringTrimRight($sSubscripts, 1)) ControlFocus('', '', $aSubscripts[$i][2]) ControlSend('', '', $aSubscripts[$i][2], 0) EndIf Next Else ; Just refill the listview with data from the Array dimension selected by ComboBoxes ; Create the 'dimension' string $sTarghet &= '[$x]' For $i = 2 To $iDimensions - 1 $sTarghet &= '[' & GUICtrlRead($aSubscripts[$i][2]) & ']' Next $iColumns = UBound($_aInput, 2) EndIf For $y = 0 To $iRows - 1 GUICtrlCreateListViewItem('', $idListview) _GUICtrlListView_SetItemText($idListview, $y, '[' & $y & ']', 0) ; row number For $x = 0 To $iColumns - 1 $vCellContent = Execute($sTarghet) If IsArray($vCellContent) Then _GUICtrlListView_SetItemText($idListview, $y, '{array}', $x + 1) Else _GUICtrlListView_SetItemText($idListview, $y, $vCellContent, $x + 1) EndIf Next Next Opt('GUIOnEventMode', 0) ; reenable GUIGetMsg() EndFunc ;==>_ArrayDisplayByLayer Func _ArrayTraverse(ByRef $aMyArray, ByRef $hTree, $hParent) #cs since this is a recursive Function, the same Func runs many times, self called from within itself. The variables declared as Global at the top of the script are able to be accessed from any instance of the function, whereas the variable declared (as Local) within the function may be different for each instance of the function. #ce If Not IsArray($aMyArray) Then Return SetError(1, 0, -1) ; we have to know how many nested for-next loops we need ; that is one loop for each dimension Local $iDimensions = UBound($aMyArray, 0) ; number of nested for-next loops Local $sArrayPointer = "$aMyArray", $sElement For $i = 0 To $iDimensions - 1 $sArrayPointer &= '[$aLoops[' & $i & '][2]]' Next ; ----------------------------------------------------------------------------------- ; This is a nested For-Next loops simulator with variable depth of nested loops ; pass a 2D zero based array[n][3] ; with as many records as nested loops needed ; as following: ; ; Example; For $i = start To end ; ----- --- ; [n][0] = Start value ; [n][1] = End value ; [n][2] = actual loop counter (at startup is = to Start value [n][0]) ; ; --- Initializes custom nested For-Next loops -------------------------------------- Local $aLoops[$iDimensions][3] ; nr of nested loops is $iDimensions For $i = 0 To $iDimensions - 1 $aLoops[$i][0] = 0 ; Start value $aLoops[$i][1] = UBound($aMyArray, $i + 1) - 1 ; End value $aLoops[$i][2] = $aLoops[$i][0] ; actual loop counter Next ; ----------------------------------------------------------------------------------- Local $x, $vContent Do $vContent = Execute($sArrayPointer) If IsArray($vContent) Then ; here there is a Nested array, populate the TreeView with a child element $sElement = "" For $i = 0 To $iDimensions - 1 $sElement &= '[' & $aLoops[$i][2] & ']' Next Local $hNode = _GUICtrlTreeView_AddChild($hTree, $hParent, $sElement) ; recursive call for this nested array to search if there are any further nested arrays _ArrayTraverse($vContent, $hTree, $hNode) ; <-- recursive call EndIf ; ------------------------------------------------------------------------------- $x = UBound($aLoops) - 1 $aLoops[$x][2] += 1 While ($aLoops[$x][2] > $aLoops[$x][1]) ; check if and which nested loops are out of bound $aLoops[$x][2] = $aLoops[$x][0] ; reset the counter of this loop ($x) $x -= 1 ; check next outer nest If $x < 0 Then ExitLoop ; if we have finished all nested loops then Exit $aLoops[$x][2] += 1 ; when a deeper loop complete, increment the outer one WEnd Until $x < 0 ; If no more nested loops then exit EndFunc ;==>_ArrayTraverse ; Tree Path to Subscript Func _TreePathParser($Input) Local $sReturn = '$_aInput' Local $aSubArrays = StringSplit($Input, '|', 3) If UBound($aSubArrays) > 1 Then For $i = 1 To UBound($aSubArrays) - 1 $sReturn &= $aSubArrays[$i] If $i < UBound($aSubArrays) - 1 Then $sReturn = '(' & $sReturn & ')' Next EndIf Return $sReturn EndFunc ;==>_TreePathParser ; this will allow the scrolling of window containing ComboBoxes (if number of Combo doesn't fit in window) ; see _GUIScrollBars_Init() in the Help of AutoIt Func WM_HSCROLL($hWnd, $iMsg, $wParam, $lParam) #forceref $iMsg, $lParam Local $iScrollCode = BitAND($wParam, 0x0000FFFF) Local $iIndex = -1, $iCharX, $iPosX Local $iMin, $iMax, $iPage, $iPos, $iTrackPos For $x = 0 To UBound($__g_aSB_WindowInfo) - 1 If $__g_aSB_WindowInfo[$x][0] = $hWnd Then $iIndex = $x $iCharX = $__g_aSB_WindowInfo[$iIndex][2] ExitLoop EndIf Next If $iIndex = -1 Then Return 0 ; ; Get all the horizontal scroll bar information Local $tSCROLLINFO = _GUIScrollBars_GetScrollInfoEx($hWnd, $SB_HORZ) $iMin = DllStructGetData($tSCROLLINFO, "nMin") $iMax = DllStructGetData($tSCROLLINFO, "nMax") $iPage = DllStructGetData($tSCROLLINFO, "nPage") ; Save the position for comparison later on $iPosX = DllStructGetData($tSCROLLINFO, "nPos") $iPos = $iPosX $iTrackPos = DllStructGetData($tSCROLLINFO, "nTrackPos") #forceref $iMin, $iMax Switch $iScrollCode Case $SB_LINELEFT ; user clicked left arrow DllStructSetData($tSCROLLINFO, "nPos", $iPos - 1) Case $SB_LINERIGHT ; user clicked right arrow DllStructSetData($tSCROLLINFO, "nPos", $iPos + 1) Case $SB_PAGELEFT ; user clicked the scroll bar shaft left of the scroll box DllStructSetData($tSCROLLINFO, "nPos", $iPos - $iPage) Case $SB_PAGERIGHT ; user clicked the scroll bar shaft right of the scroll box DllStructSetData($tSCROLLINFO, "nPos", $iPos + $iPage) Case $SB_THUMBTRACK ; user dragged the scroll box DllStructSetData($tSCROLLINFO, "nPos", $iTrackPos) EndSwitch ; // Set the position and then retrieve it. Due to adjustments ; // by Windows it may not be the same as the value set. DllStructSetData($tSCROLLINFO, "fMask", $SIF_POS) _GUIScrollBars_SetScrollInfo($hWnd, $SB_HORZ, $tSCROLLINFO) _GUIScrollBars_GetScrollInfo($hWnd, $SB_HORZ, $tSCROLLINFO) ;// If the position has changed, scroll the window and update it $iPos = DllStructGetData($tSCROLLINFO, "nPos") If ($iPos <> $iPosX) Then _GUIScrollBars_ScrollWindow($hWnd, $iCharX * ($iPosX - $iPos), 0) Return $GUI_RUNDEFMSG EndFunc ;==>WM_HSCROLL1 point
-
Since I disovered FreeBasic I decided to create a DLL to implement much faster image processing functionality to AutoIt. Following functions are implemented yet: _GDIPlus_BitmapApplyFilter_BWJJNDithering _GDIPlus_BitmapApplyFilter_BWBayerOrderedDithering _GDIPlus_BitmapApplyFilter_Cartoon1 _GDIPlus_BitmapApplyFilter_ColorAccent _GDIPlus_BitmapApplyFilter_Convolution_AnotherBlur _GDIPlus_BitmapApplyFilter_Convolution_BoxBlur _GDIPlus_BitmapApplyFilter_Convolution_EdgeDetection1 _GDIPlus_BitmapApplyFilter_Convolution_EdgeDetection2 _GDIPlus_BitmapApplyFilter_Convolution_EdgeDetection3 _GDIPlus_BitmapApplyFilter_Convolution_EdgeDetection4 _GDIPlus_BitmapApplyFilter_Convolution_EdgeDetection5 _GDIPlus_BitmapApplyFilter_Convolution_EdgeDetection6 _GDIPlus_BitmapApplyFilter_Convolution_Emboss1 _GDIPlus_BitmapApplyFilter_Convolution_Emboss45Degree _GDIPlus_BitmapApplyFilter_Convolution_EmbossTopLeftBottomRight _GDIPlus_BitmapApplyFilter_Convolution_Gaussian3x3 _GDIPlus_BitmapApplyFilter_Convolution_Gaussian5x5_1 _GDIPlus_BitmapApplyFilter_Convolution_Gaussian5x5_2 _GDIPlus_BitmapApplyFilter_Convolution_GaussianBlur _GDIPlus_BitmapApplyFilter_Convolution_IntenseEmboss _GDIPlus_BitmapApplyFilter_Convolution_Kirsch _GDIPlus_BitmapApplyFilter_Convolution_Laplace1 _GDIPlus_BitmapApplyFilter_Convolution_Laplace2 _GDIPlus_BitmapApplyFilter_Convolution_Laplace3 _GDIPlus_BitmapApplyFilter_Convolution_LaplacianOfGaussian _GDIPlus_BitmapApplyFilter_Convolution_ManualMatrix _GDIPlus_BitmapApplyFilter_Convolution_MotionBlur _GDIPlus_BitmapApplyFilter_Convolution_Outline3x3 _GDIPlus_BitmapApplyFilter_Convolution_Prewitt _GDIPlus_BitmapApplyFilter_Convolution_Sharpen1 _GDIPlus_BitmapApplyFilter_Convolution_Sharpen2 _GDIPlus_BitmapApplyFilter_Convolution_Sobel _GDIPlus_BitmapApplyFilter_Convolution_SovelVsPrewitt _GDIPlus_BitmapApplyFilter_Convolution_TriangleBlur _GDIPlus_BitmapApplyFilter_Convolution_Unsharp _GDIPlus_BitmapApplyFilter_Convolution_Unsharp5x5 _GDIPlus_BitmapApplyFilter_Delaunay _GDIPlus_BitmapApplyFilter_Dilatation _GDIPlus_BitmapApplyFilter_DistortionBlur _GDIPlus_BitmapApplyFilter_Edges _GDIPlus_BitmapApplyFilter_Erosion _GDIPlus_BitmapApplyFilter_FakeGreyscale _GDIPlus_BitmapApplyFilter_FishEye _GDIPlus_BitmapApplyFilter_Indexed _GDIPlus_BitmapApplyFilter_Jitter _GDIPlus_BitmapApplyFilter_Kuwahara _GDIPlus_BitmapApplyFilter_Linellism _GDIPlus_BitmapApplyFilter_Median _GDIPlus_BitmapApplyFilter_Median2 _GDIPlus_BitmapApplyFilter_Mosaic _GDIPlus_BitmapApplyFilter_OilPainting _GDIPlus_BitmapApplyFilter_Open _GDIPlus_BitmapApplyFilter_PenSketch _GDIPlus_BitmapApplyFilter_PenSketch2 _GDIPlus_BitmapApplyFilter_Pixelate _GDIPlus_BitmapApplyFilter_Pointillism _GDIPlus_BitmapApplyFilter_RadialBlur _GDIPlus_BitmapApplyFilter_Raster _GDIPlus_BitmapApplyFilter_Spiral _GDIPlus_BitmapApplyFilter_Swirl _GDIPlus_BitmapApplyFilter_SymmetricNearestNeighbour _GDIPlus_BitmapApplyFilter_TiltShift _GDIPlus_BitmapApplyFilter_TimeWarp _GDIPlus_BitmapApplyFilter_Ver _GDIPlus_BitmapApplyFilter_Wave _GDIPlus_BitmapApplyFilter_XRay Since I am absolutely a newbie in FreeBasic, the DLL may contain errors. Please report any bug. FreeBasic source code can be found here: https://pastebin.com/Lugp6rCR To do: add function headers with descriptions speed-up FB code -> partly done add more filters -> ongoing Credits to: Jakub Szymanowski rdc Dewald Esterhuizen Santhosh G_ Christian Graus www.gutgames.com Have fun. You can compare the speed with AutoIt version: #AutoIt3Wrapper_Version=b #include <Array.au3> #include <GDIPlus.au3> Global $sFile = FileOpenDialog("Select an image", "", "Images (*.jpg;*.png;*.gif;*.bmp)") If @error Then Exit _GDIPlus_Startup() Global Const $STM_SETIMAGE = 0x0172 Global Const $hImage = _GDIPlus_ImageLoadFromFile($sFile) Global Const $iW = _GDIPlus_ImageGetWidth($hImage), $iH = _GDIPlus_ImageGetHeight($hImage) Global Const $hGUI = GUICreate("GDI+ Image Filters", $iW * 2, $iH) Global $fProg = 0, $iEnd = $iW * $iH - 1 AdlibRegister("Progress", 490) Global $t = TimerInit() Global Const $hGDIBitmap = _GDIPlus_BitmapApplyFilter_Median($hImage, 4) ConsoleWrite(Round(TimerDiff($t) / 1000, 2) & " s / " & Round(TimerDiff($t) / 60000, 2) & " min" & @CRLF) Global Const $iPic = GUICtrlCreatePic("", 0, 0, $iW - 1, $iH - 1) Global Const $iPic_o = GUICtrlCreatePic("", $iW, 0, $iW - 1, $iH - 1) _WinAPI_DeleteObject(GUICtrlSendMsg($iPic, $STM_SETIMAGE, $IMAGE_BITMAP, $hGDIBitmap)) Global Const $hGDIBitmap2 = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hImage) _WinAPI_DeleteObject(GUICtrlSendMsg($iPic_o, $STM_SETIMAGE, $IMAGE_BITMAP, $hGDIBitmap2)) GUISetState() AdlibUnRegister("Progress") ToolTip("") Do Until GUIGetMsg() = -3 _GDIPlus_ImageDispose($hImage) _WinAPI_DeleteObject($hGDIBitmap) _WinAPI_DeleteObject($hGDIBitmap2) _GDIPlus_Shutdown() Exit Func Progress() ToolTip(Int($fProg / $iEnd * 100) & " % / " & Round(TimerDiff($t) / 60000, 2) & " min", MouseGetPos(0) + 30, MouseGetPos(1) + 30) EndFunc #Region Symmetric Nearest Neighbour Func _GDIPlus_BitmapApplyFilter_SymmetricNearestNeighbour($hImage, $fRadius = 2, $bGDI = True) ;no alpha channel implemented yet Local Const $iW = _GDIPlus_ImageGetWidth($hImage), $iH = _GDIPlus_ImageGetHeight($hImage) Local Const $hBitmap_Dest = _GDIPlus_BitmapCreateFromScan0($iW, $iH) Local Const $tBitmapData_Dest = _GDIPlus_BitmapLockBits($hBitmap_Dest, 0, 0, $iW - 1, $iH - 1, $GDIP_ILMWRITE, $GDIP_PXF32ARGB) Local Const $iScan0_Dest = DllStructGetData($tBitmapData_Dest, "Scan0") Local Const $tPixel_Dest = DllStructCreate("int[" & $iW * $iH & "];", $iScan0_Dest) Local Const $tBitmapData = _GDIPlus_BitmapLockBits($hImage, 0, 0, $iW - 1, $iH - 1, $GDIP_ILMREAD, $GDIP_PXF32ARGB) Local Const $iScan0 = DllStructGetData($tBitmapData, "Scan0") Local Const $tPixel = DllStructCreate("int[" & $iW * $iH & "];", $iScan0) Local $iRowOffset, $iX, $iY, $c, $k, $sumR, $sumG, $sumB, $iCount, $xx, $yy, $iR, $iG, $iB, $iR1, $iG1, $iB1, $iR2, $iG2, $iB2, $x, $y For $iY = 0 To $iH - 1 $iRowOffset = $iY * $iW For $iX = 0 To $iW - 1 $sumR = 0 $sumG = 0 $sumB = 0 $iCount = 0 $c = DllStructGetData($tPixel, 1, $iRowOffset + $iX) $iR = BitShift(BitAND(0x00FF0000, $c), 16) $iG = BitShift(BitAND(0x0000FF00, $c), 8) $iB = BitAND(0x000000FF, $c) For $yy = -$fRadius To $fRadius For $xx = -$fRadius To $fRadius $k = $iX + $xx $x = $k < 0 ? 0 : $k > $iW - 1 ? $iW - 1 : $k $k = $iY + $yy $y = $k < 0 ? 0 : $k > $iH - 1 ? $iH - 1 : $k $c = DllStructGetData($tPixel, 1, $y * $iW + $x) $iR1 = BitShift(BitAND(0x00FF0000, $c), 16) $iG1 = BitShift(BitAND(0x0000FF00, $c), 8) $iB1 = BitAND(0x000000FF, $c) $k = $iX - $xx $x = $k < 0 ? 0 : $k > $iW - 1 ? $iW - 1 : $k $k = ($iY - $yy) $y = $k < 0 ? 0 : $k > $iH - 1 ? $iH - 1 : $k $c = DllStructGetData($tPixel, 1, $y * $iW + $x) $iR2 = BitShift(BitAND(0x00FF0000, $c), 16) $iG2 = BitShift(BitAND(0x0000FF00, $c), 8) $iB2 = BitAND(0x000000FF, $c) If __DeltaE($iR, $iG, $iB, $iR1, $iG1, $iB1) < __DeltaE($iR, $iG, $iB, $iR2, $iG2, $iB2) Then $sumR += $iR1 $sumG += $iG1 $sumB += $iB1 Else $sumR += $iR2 $sumG += $iG2 $sumB += $iB2 EndIf $iCount += 1 Next Next DllStructSetData($tPixel_Dest, 1, 0xFF000000 + Int($sumR / $iCount) * 0x10000 + Int($sumG / $iCount) * 0x100 + Int($sumB / $iCount), $iRowOffset + $iX) $fProg += 1 Next Next _GDIPlus_BitmapUnlockBits($hImage, $tBitmapData) _GDIPlus_BitmapUnlockBits($hBitmap_Dest, $tBitmapData_Dest) _GDIPlus_ImageSaveToFile($hBitmap_Dest, @ScriptDir & "\Filter_SNN" & $fRadius & "_" & @YEAR & @MON & @MDAY & @MIN & @SEC & ".png") If $bGDI Then Local $hGDIBitmap = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hBitmap_Dest) _GDIPlus_BitmapDispose($hBitmap_Dest) Return $hGDIBitmap EndIf Return $hBitmap_Dest EndFunc Func __DeltaE($iR1, $iG1, $iB1, $iR2, $iG2, $iB2) Return Sqrt(($iR1 - $iR2) * ($iR1 - $iR2) + ($iG1 - $iG2) * ($iG1 - $iG2) + ($iB1 - $iB2) * ($iB1 - $iB2)) EndFunc #EndRegion #Region Jitter Func _GDIPlus_BitmapApplyFilter_Jitter($hImage, $iAmount = 20, $bGDI = True) Local Const $iW = _GDIPlus_ImageGetWidth($hImage), $iH = _GDIPlus_ImageGetHeight($hImage) Local Const $hBitmap_Dest = _GDIPlus_BitmapCreateFromScan0($iW, $iH) Local Const $tBitmapData_Dest = _GDIPlus_BitmapLockBits($hBitmap_Dest, 0, 0, $iW - 1, $iH - 1, $GDIP_ILMWRITE, $GDIP_PXF32ARGB) Local Const $iScan0_Dest = DllStructGetData($tBitmapData_Dest, "Scan0") Local Const $tPixel_Dest = DllStructCreate("int[" & $iW * $iH & "];", $iScan0_Dest) Local Const $tBitmapData = _GDIPlus_BitmapLockBits($hImage, 0, 0, $iW - 1, $iH - 1, $GDIP_ILMREAD, $GDIP_PXF32ARGB) Local Const $iScan0 = DllStructGetData($tBitmapData, "Scan0") Local Const $tPixel = DllStructCreate("int[" & $iW * $iH & "];", $iScan0) Local $iX, $iY, $iRowOffset, $fNX, $fNY For $iY = 0 To $iH - 1 $iRowOffset = $iY * $iW + 1 For $iX = 0 To $iW - 1 $fNX = $iX + Int((Random() - 0.5) * $iAmount) $fNX = $fNX < 1 ? 1 : $fNX > $iW - 1 ? $iW - 1 : $fNX $fNY = ($iY + Int((Random() - 0.5) * $iAmount)) $fNY = $fNY < 1 ? 1 : $fNY > $iH - 1 ? $iH - 1 : $fNY $fNY *= $iW DllStructSetData($tPixel_Dest, 1, DllStructGetData($tPixel, 1, $fNY + $fNX), $iRowOffset + $iX) $fProg += 1 Next Next _GDIPlus_BitmapUnlockBits($hImage, $tBitmapData) _GDIPlus_BitmapUnlockBits($hBitmap_Dest, $tBitmapData_Dest) _GDIPlus_ImageSaveToFile($hBitmap_Dest, @ScriptDir & "\Filter_Jitter" & $iAmount & "_" & @YEAR & @MON & @MDAY & @MIN & @SEC & ".png") If $bGDI Then Local $hGDIBitmap = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hBitmap_Dest) _GDIPlus_BitmapDispose($hBitmap_Dest) Return $hGDIBitmap EndIf Return $hBitmap_Dest EndFunc #EndRegion #Region Median Func _GDIPlus_BitmapApplyFilter_Median($hImage, $fRadius = 3, $bGDI = True) Local Const $iW = _GDIPlus_ImageGetWidth($hImage), $iH = _GDIPlus_ImageGetHeight($hImage) Local Const $hBitmap_Dest = _GDIPlus_BitmapCreateFromScan0($iW, $iH) Local Const $tBitmapData_Dest = _GDIPlus_BitmapLockBits($hBitmap_Dest, 0, 0, $iW - 1, $iH - 1, $GDIP_ILMWRITE, $GDIP_PXF32ARGB) Local Const $iScan0_Dest = DllStructGetData($tBitmapData_Dest, "Scan0") Local Const $tPixel_Dest = DllStructCreate("int[" & $iW * $iH & "];", $iScan0_Dest) Local Const $tBitmapData = _GDIPlus_BitmapLockBits($hImage, 0, 0, $iW - 1, $iH - 1, $GDIP_ILMREAD, $GDIP_PXF32ARGB) Local Const $iScan0 = DllStructGetData($tBitmapData, "Scan0") Local Const $tPixel = DllStructCreate("int[" & $iW * $iH & "];", $iScan0) Local $iX, $iY, $iRowOffset For $iY = 0 To $iH - 1 $iRowOffset = $iY * $iW + 1 For $iX = 0 To $iW - 1 DllStructSetData($tPixel_Dest, 1, __Median_Value($iX, $iY, $fRadius, $tPixel, $iW, $iH), $iRowOffset + $iX) $fProg += 1 Next Next _GDIPlus_BitmapUnlockBits($hImage, $tBitmapData) _GDIPlus_BitmapUnlockBits($hBitmap_Dest, $tBitmapData_Dest) _GDIPlus_ImageSaveToFile($hBitmap_Dest, @ScriptDir & "\Filter_Median" & $fRadius & "_" & @YEAR & @MON & @MDAY & @MIN & @SEC & ".png") If $bGDI Then Local $hGDIBitmap = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hBitmap_Dest) _GDIPlus_BitmapDispose($hBitmap_Dest) Return $hGDIBitmap EndIf Return $hBitmap_Dest EndFunc Func __Median_Value($iPosX, $iPosY, $fRadius, $tPixel, $iW, $iH) Local $iX, $iY, $aColors[1000], $iColors = 0, $iSize = $iW * $iH - 1, $iOff, $e For $iX = $iPosX - $fRadius To $iPosX + $fRadius For $iY = $iPosY - $fRadius To $iPosY + $fRadius $iOff = 1 + $iY * $iW + $iX $aColors[$iColors] = DllStructGetData($tPixel, 1, $iOff < 1 ? 1 : $iOff > $iSize ? $iSize : $iOff) $iColors += 1 Next Next ReDim $aColors[$iColors] ;~ _ArraySort($aColors, 0) $e = $iColors - 1 __ArrayQuickSort1D($aColors, 0, $e) Local $iMid = Floor($iColors / 2), $iMedian If BitAND($iColors, 1) Then $iMedian = Int($aColors[$iMid + 1]) Else $iMedian = Int(($aColors[$iMid] + $aColors[$iMid + 1]) / 2) EndIf Return $iMedian EndFunc #EndRegion _GDIPlus_BitmapApplyFilter v0.9.8 build 2024-04-17 beta.7z1 point
-
Simulated Annealing (SA) is a simple technique for finding an acceptable solution (but not necessarily always the absolute best one that exists!) to very hard combinatorial problems, that is, ones for which a brute-force approach of cycling through all possible alternatives to find the global optimum just takes too darn long. Typically, one would be seeking some specific sequence or permutation of a (sub)set, and the number of possibilities is astronomically large. In addition, for SA to be applicable, you'll need to be able to quantify in some way how good any particular trial solution is, or how far distant from the ideal result. The real power of SA lies in these so-called cost functions; you can define as many as you like, with different weights if you like, and these conditions are even allowed to be conflicting. User-defined settings define how tenaciously it should be exploring solution space before homing in on a region with desirable properties, and finding its minimum. You can think of it as a learning algorithm that is allowed to make lots of mistakes in the beginning, before gradually avoiding ever more potentially bad decisions. A simple Analogy: You're standing in the middle of an extensive, rough terrain with hills, ridges, bumps, valleys, and tiny, deep depressions. You've got a GPS altimeter to tell you how high above sea-level you currently are, but this area is perpetually shrouded in thick fog. Now find the lowest point. And quickly please. Now what? There's no time to systematically grid and traverse the entire area, and you cannot see more than a few feet ahead. Following the local gradient down-slope will likely get you stuck at a very local minimum, whereas a much lower point may be just beyond the hext hill. Luckily, you brought your magic boots (the red ones with the twinkling silver stars). These allow you to make huge leaps through the fog, landing safely somewhere else. And although you don't know in advance where you'll end up, the boots magically remember their last-previous departure point, so if you don't like your new surroundings, you can go back one jump (but never more than that). Now to find the lowest point in the landscape, you keep tracking your altimeter changes with every jump. Some jumps will get you to higher ground, others might land you in a deep valley. The trick is not to settle for ever-lower heights immediately, but to allow going back up ever so often, so you can get beyond some high ridge behind which you might find a much lower depression than your best-previous result. Crucially, to decide whether or not to gain height in the misty terrain, you roll some dice you've got in your pocket, and the more jumps you've made, the lower you make the upper bound below which you would still go back uphill. So in the beginning you'll be jumping all over the place (allowing you to sample the terrain extensively), but eventually you'll be limiting yourself to some deep valley you've found, which might be the deepest one all round, but even if not, it will still be a pretty good guess. And you will have found this deep valley in a tiny fraction of the time it would take to do a full land survey. Simulated annealing needs to be defined in terms of the specific problem you're trying to solve. So it's not possible to provide you with a generic UDF that'll figure out in advance what your ideal solution would look like (for example, in the analogy above, we might be looking for the highest point instead of the lowest one). You need to define that ideal in terms of one or more cost functions that SA will then attempt to minimise. What can be done is to provide you with specific examples. Example 1. From a list of user-defined pre-supplied values, select the fewest terms that sum to (or approximate) a predefined target total. This solution was written in response to this thread. Current cost updates are periodically written to console. This example attempts to satisfy two conditions simultaneously: getting a sum that matches the target, and using the smallest number of terms. ; Simulated Annealing example (combinatorial minimisation), by RTFC (22 Feb 2016) ; Note that this algorithm converges on A *local* minimum (in terms of the ; user-defined cost-function(s)), which is not necessarily THE *global* minimum. ; Note also that the search path, duration, and final result may differ from run to run. ; Several parameters can be tweaked to adjust this. #include <Array.au3> #include <Math.au3> Global $temperat,$path,$kk,$nswap,$nswapstep,$cost,$altcost,$tempstep Global $costadjust,$ttlsites,$totalcost,$factor,$maxsumlength,$maxsize Global $site1,$site2,$index1,$index2,$weight_sum,$weight_length Global $options,$sumlength,$prevlength ; initialise SRandom(@SEC+@AutoItPID) ; initialise radnomising seed $verbose=True ; T: write regular progress updates to console $factor=1 ; optional Oracle-response adjustment (not used here) $prevlength=$sumlength ; to enable reverting to previous state after _TryChange $minsumlength=0 ; if nothing else is know, we'll start with a single array entry (base-0) $options=10 ; larger value = larger likelihood of swapping vs changing sumlength if $options<3 then Exit ; minimum size for this set-up (see Func _TrySwap) ; adjust the balance of conditions here (see _Cost function) $weight_sum=1 ; relative importance of matching condition 1 (sum = target) $weight_length=1 ; relative importance of matching condition 2 (lowest number of terms) ; summation results buffer Global $bestsum[10] Global $bestsumlength=UBound($bestsum) ; define the summation result we wish to achieve (try different values here!) $target = 27 ; Note: if you change this value, you may have to adjust $minsumlength below as well! ; define our array of summation terms to select from $dim=9 ; this is just the way this problem was presented $ttlsites=$dim*$dim $maxsize=$ttlsites-1 $maxsumlength=$ttlsites-2 ; need at least one tail slot for swapping ; enable this for the predefined problem... $doIntegerTest=True ; False if $doIntegerTest Then Global $aArray = [9,2,2,3,1,1,6,3,4, _ 4,2,3,4,5,6,7,8,7, _ 7,2,3,4,5,1,7,2,2, _ 2,2,3,1,5,5,7,1,4, _ 3,2,1,2,3,6,6,6,4, _ 3,2,3,4,5,6,7,8,3, _ 2,2,3,8,1,4,7,1,2, _ 1,7,3,5,5,6,7,1,2, _ 7,2,3,7,5,1,7,8,9] ; in this specific case, we already know that we'll need at least 4 terms ; because a) 9=max value in array, b) there are only 2 nines in the array, and c) target =3x9 $minsumlength=3 ; base-0, so 4 entries altogether Else ; OR use this for random floats in range 1-$dim (just as an example) Global $aArray[$ttlsites] For $cc=0 to $maxsize $aArray[$cc]=random(1,$dim,0) ; non-integer values used here! Next ; no clue about this constraint in this case $minsumlength=0 ; one entry (base-0) EndIf ;______START OF ANNEALING ROUTINE____________ $nover =1000 ; maximum number of changes at any temperature (for more complicated problems, set this several orders of magnitude higher) $nlimit=Int($nover/4) ; maximum number of successful changes before continuing $nwrite=Int($nover/5) ; default status update interval if verbose=.t. $tempsteps=100 ; number of temperature steps to try $tfactor=0.95 ; annealing schedule: temperature is reduced by this factor after each step While True $temperat=0.5 ; initial temperature; smaller = more aggressive + more myopic search $absimp=0 ; counter $nswapstepzero=0 ; counter $sumlength=$minsumlength ; base-0 ; prep the cost vars $totalcost=_Cost() $cost=$totalcost $lowestcost=$totalcost $initcost=$totalcost ; main loop starts here For $tempstep=1 to $tempsteps ; try up to N temperature steps $nswap=0 $nswapstep=0 For $kk=1 to $nover _TrySwap() ; swap and determine cost adjustment Switch _AskOracle() ; Feel the Force, Luke. Case True $nswap+=1 $totalcost+=$costadjust $cost=$altcost If $lowestcost>$totalcost Then $nswapstep+=1 $absimp+=1 $lowestcost=$totalcost ; ensure results buffer is sufficiently large If $bestsumlength<=$sumlength Then $bestsumlength+=5 ReDim $bestsum[$bestsumlength] EndIf ; flush current-best summation For $bc=0 to $sumlength-1 $bestsum[$bc]=$aArray[$bc] Next ; pad tail with zeroes For $bc=$sumlength to $bestsumlength-1 $bestsum[$bc]=0 Next _ScreenOut() If $totalcost<=0 Then ExitLoop Endif Case Else ; restore the previous state $sumlength=$prevlength $aArray[$index1]=$site1 $aArray[$index2]=$site2 EndSwitch ; show we're still alive If $verbose And mod($kk,$nwrite)=0 Then _ScreenOut() If $nswap>=$nlimit Or $lowestcost<=0 then ExitLoop Next ; optional early-out scenario (disable for a more thorough search) If $nswapstep=0 then $nswapstepzero+=1 If $nswapstepzero=10 then ExitLoop ; no more improvements in the last N temperature steps ; reduce temperature = likelihood of following a trajectory away from the nearest LOCAL optimum (in the hope of getting nearer to the GLOBAL optimum) $temperat*=$tfactor Next ; present final result _Arraysort($bestsum) ; just for clarity $summation="Best result so far (at a cost of " & $lowestcost & ") is: " & @CRLF $terms=0 $result=0 For $cc=0 to $sumlength If $aArray[$cc]>0 then $summation&=$aArray[$cc] & "+" $result+=$aArray[$cc] $terms+=1 Endif Next $summation=StringTrimRight($summation,1) & " = " & $result & " (target = " & $target & ")" & @CRLF $summation&="Number of summation terms: " & $terms & @CR & "Temperature steps: " & $tempstep & @CR & @CR & "Press <Ok> to try again, <Cancel> to Quit" if Msgbox($MB_OKCANCEL,"Simulated Annealing Test Result",$summation)=$IDCANCEL then Exit ; shuffle entries a bit for variety For $cc=1 to $maxsize*10 $index1=random(0,$maxsize,1) $index2=random(0,$maxsize,1) $tmp=$aArray[$index1] $aArray[$index1]=$aArray[$index2] $aArray[$index2]=$tmp Next WEnd Exit Func _AskOracle() If $costadjust<0 Then Return True Else ; this is where all the magic happens! Return (random()<Exp(-($costadjust*$factor)/$temperat)) Endif EndFunc Func _TrySwap() $index1=0 ; these vars are all Globals $index2=0 $altcost=0 $prevlength=$sumlength ; decide whether to reduce/increase number of terms, or swap an existing term Switch Random(1,$options,1) Case 1 ; crop $sumlength=_Max($minsumlength,$sumlength-1) Case 2 ; extend $sumlength=_Min($maxsumlength,$sumlength+1) Case Else ; this likelhood is determined by the value of $options (>=3) $index1=random(0,$sumlength,1) $index2=random($sumlength+1,$maxsize,1) EndSwitch ; store current contents, in case we decide later that this was a bad idea $site1=$aArray[$index1] $site2=$aArray[$index2] ; swap contents for now $aArray[$index1]=$site2 $aArray[$index2]=$site1 ; compute the new sum (as either length or content has changed) $altcost=_Cost() ; performance difference between original and new state $costadjust=$altcost-$cost ; $cost is already filled in previous pass EndFunc Func _Cost() Local $cc,$result=0 For $cc=0 to $sumlength $result+=$aArray[$cc] Next Return (Abs($result-$target)*$weight_sum) + (($sumlength-$minsumlength)*$weight_length) EndFunc Func _ScreenOut() ConsoleWrite("Simulated Annealing. Initial total cost: " & $initcost & @CRLF) ConsoleWrite("Step: " & $tempstep & " of " & $tempsteps & "; Temperature: " & $temperat & @CRLF) ConsoleWrite("Executed Swaps: " & $nswap & "; Lowest Cost so far: " & $lowestcost & @CRLF) ConsoleWrite("Total Improvements: " & $absimp & "; Improvements this step: " & $nswapstep & @CRLF & @CRLF) EndFunc Example 2. The Travelling Salesman problem (TSP) This is a classic combinatorial minimisation problem, and relevant to real-world logistics: to find the shortest route for visiting all cities exactly once, before returning to the original starting point. As it is quite entertaining to see how the algorithm gradually solves this brain teaser, I've added a simple GUI that visualises the cities (red circles) and the changing routes between them (blue). The problem becomes exponentially harder to solve when the number of cities is increased. This example (adapted from Press et al., Numerical recipes, 2nd ed., pp. 438-443) employs a single cost function of the total route distance. TSP.au3 It's important to stress that simulated annealing cannot guarantee that the global optimum will always be found, only that it will likely come up with a fairly good solution, and much faster than brute force ever could. If that's good enough for you, then those red, silver-starred boots might fit you too.1 point
-
RichEdit Hyperlinks (Friendly)
pixelsearch reacted to InunoTaishou for a topic
I think RichEdit has been my favorite thing I've ever discovered on AutoIt lol. In my quest to add in more html tags to my _StringToRichEditArray I needed a way to do href! There was an example I found that I followed but it didn't format correctly and didn't work 100% but it gave me a good base. Think I'll tackle inserting an image next, not looking forward to that. If anyone has an idea on how to do it let me know. Known issues (these will cause the hyperlink to lose the +li attribute after the RichEdit is updated): The hyperlink and friendly text are appended/inserted (directly adjacent to a non whitespace) but the hyperlink is not a valid hyperlink. Changing the char color for the control causes the hyperlink to lose it's hyperlink color (the light blue). Fix for Issue 1: Use the full URL for the hyperlink (https://www.autoitscript.com/site/ instead of www.autoitscript.com/site) Use any hyperlink with any friendly text that does not have www at the beginning (Hyperlink: www.google.com, Friendly Text: google.com) Use any hyperlink with any, or no, friendly text, but have a whitespace to the left of the hyperlink. Fix for Issue 2: I have no fix. Updated RichEdit Hyperlink.au3 Demo Original Post, outdated: Had a problem with inserting/appending hyperlinks that pointed to the local computer. (C:\Windows\)1 point -
GPU acceleration in AutoIt with NVIDIA CUDA
Biatu reacted to scintilla4evr for a topic
First, let me shock you. AutoIt is SLOW. I know, right! But really, it may seem fast enough for "regular" automation and stuff, but when it comes to processing large datasets (image processing for example), it's horrendously slow. There are 2 solutions to this problem: Write the processing code in, for example C or C++ and compile it to a DLL, and then use it in AutoIt Ditch the CPU and let GPU do the processing. You see, CPU is not suited for processing large data in parallel. While it may have some extra cores, but it still not enough. That's where the GPU comes in. CPU with its 8 or more cores is nowhere close the amount of those on GPU - reaching thousands (2560 CUDA cores in GeForce GTX 1080, for example). With this potential, NVIDIA decided to create CUDA - a feature available in all current NVIDIA GPU's allowing developers to harness their computing potential. After this not so short introduction to GPU computing, here is the CUDA UDF. Features include: Running precompiled programs (use NVCC from CUDA Toolkit, available here) Transferring data into and out of the GPU to the "host" (main RAM) CUDA UDF1 point -
FModMem UDF build 2019-11-24
argumentum reacted to UEZ for a topic
Hi, I created a FMOD UDF (x86 / x64 compatible) whereas the DLL calls are done from memory directly! Credits to trancexx for the Subrogation functions! Supported formats: - .MOD (protracker/fasttracker modules) - .S3M (screamtracker 3 modules) - .XM (fasttracker 2 modules) - .IT (impulse tracker modules) - .MID (MIDI files) - .RMI (MIDI files) - .SGT (DirectMusic segment files) - .FSB (FMOD Sample Bank files) - .MP3 - .OGG - .WAV - .MP2 - .RAW Current added function list (sum = 70): FMUSICMEM_FreeSong FMUSICMEM_GetBPM FMUSICMEM_GetGlobalVolume FMUSICMEM_GetMasterVolume FMUSICMEM_GetName FMUSICMEM_GetNumChannels FMUSICMEM_GetNumInstruments FMUSICMEM_GetNumOrders FMUSICMEM_GetNumPatterns FMUSICMEM_GetNumSamples FMUSICMEM_GetOrder FMUSICMEM_GetPaused FMUSICMEM_GetRow FMUSICMEM_GetSpeed FMUSICMEM_GetTime FMUSICMEM_GetType FMUSICMEM_IsFinished FMUSICMEM_LoadSong FMUSICMEM_LoadSongEx FMUSICMEM_OptimizeChannels FMUSICMEM_PlaySong FMUSICMEM_SetMasterVolume FMUSICMEM_SetOrder FMUSICMEM_SetPaused FMUSICMEM_StopAllSongs FMUSICMEM_StopSong FSOUNDMEM_Close FSOUNDMEM_DSP_Create FSOUNDMEM_DSP_Free FSOUNDMEM_DSP_GetActive FSOUNDMEM_DSP_GetFFTUnit FSOUNDMEM_DSP_GetSpectrum FSOUNDMEM_DSP_SetActive FSOUNDMEM_DSP_SetPriority FSOUNDMEM_GetAmplitude FSOUNDMEM_GetChannelsPlaying FSOUNDMEM_GetCPUUsage FSOUNDMEM_GetCurrentLevels FSOUNDMEM_GetError FSOUNDMEM_GetFrequency FSOUNDMEM_GetGetSurround FSOUNDMEM_GetMaxChannels FSOUNDMEM_GetPan FSOUNDMEM_GetPaused FSOUNDMEM_GetVolume FSOUNDMEM_Init FSOUNDMEM_SetPaused FSOUNDMEM_SetSpeakerMode FSOUNDMEM_SetSurround FSOUNDMEM_SetVolume FSOUNDMEM_SetVolumeAbsolute FSOUNDMEM_Stream_Close FSOUNDMEM_Stream_FindTagField FSOUNDMEM_Stream_GetLength FSOUNDMEM_Stream_GetLengthMs FSOUNDMEM_Stream_GetNumTagFields FSOUNDMEM_Stream_GetOpenState FSOUNDMEM_Stream_GetPosition FSOUNDMEM_Stream_GetTagField FSOUNDMEM_Stream_GetTime FSOUNDMEM_Stream_Net_GetStatus FSOUNDMEM_Stream_Net_SetMetadataCallback FSOUNDMEM_Stream_Net_SetProxy FSOUNDMEM_Stream_Open FSOUNDMEM_Stream_Play FSOUNDMEM_Stream_PlayEx FSOUNDMEM_Stream_SetLoopCount FSOUNDMEM_Stream_SetPosition FSOUNDMEM_Stream_SetTime FSOUNDMEM_Stream_Stop In the zip archive you can find 6 examples. Download: FModMem UDF build 2019-11-24.zip If you have created additional examples, feel free to post it here.1 point -
https://sourceforge.net/projects/rhash/ librhash.dll - supports CRC32, MD4, MD5, SHA1, SHA256, SHA512, SHA3, Tiger, TTH, Torrent BTIH, AICH, ED2K, GOST R 34.11-94, RIPEMD-160, HAS-160, EDON-R 256/512, WHIRLPOOL and SNEFRU hash sums. faster than _Crypt_HashFile about 2 times librhash.au3, librhash.dll in archive Changelog: 2017-07-11 Added: rhash_timer_start, rhash_timer_stop Changed: DLL - rhash_run_benchmark_mod - passing file as HANDLE 2017-07-07 Changed: rhash_ex() Added: DLL - rhash_run_benchmark_mod 2017-07-04 Changed rhash_ex() librhash_134.zip1 point
-
Is it possible to pass a native AutoIt array as a parameter to a function coded in assembler, C, C++, C# or FreeBasic? And how is this possible? That's what this example is about. If possible, it may increase the performance of array manipulations significantly through fast functions of compiled code. The very, very short answers to the two questions above are: Yes. And through COM objects. Here is a small example that shows what it's all about. You can find the example in zip file in bottom of post (goto top of second post and scroll up a little bit). ;#AutoIt3Wrapper_UseX64=y #include "Includes\AccVarsUtilities.au3" #include "Includes\InspectVariable.au3" #include "Includes\ArrayDisplayEx.au3" #include "Includes\FasmUtils.au3" Opt( "MustDeclareVars", 1 ) Example() Func Example() ; === UDF and flat assembler (fasm) code === Local $hTimer1 = TimerInit() ; --- Create and fill safearray --- ; Create safearray Local $tSafeArrayBound = DllStructCreate( $tagSAFEARRAYBOUND ) DllStructSetData( $tSafeArrayBound, "cElements", 2^24 ) ; Number of elements in array DllStructSetData( $tSafeArrayBound, "lLbound", 0 ) ; Lower bound of array index Local $pSafeArray = SafeArrayCreate( $VT_I4, 1, $tSafeArrayBound ) ConsoleWrite( @CRLF & "--- Inspect safearray ---" & @CRLF ) InspectSafeArray( $pSafeArray ) ; Pointer to data Local $pSafeArrayData SafeArrayAccessData( $pSafeArray, $pSafeArrayData ) ; AutoIt code ;For $i = 0 To 2^24 - 1 ; DllStructSetData( $tSafeArrayData, 1, $i, $i + 1 ) ;Next ; <<<< Execute your assembler, C, C++, C# or FreeBasic code at this point >>>> ; Get fasm code Local $sFasmCode = @AutoItX64 ? "0xB80000000089024883C204FFC0E2F6C3" _ ; Example-x64.asm : "0x5589E58B4D088B550CB800000000890283C20440E2F85DC20800" ; Example-x86.asm Local $pFasmCode = FasmGetBinaryString( $sFasmCode, 64 ) If Not $pFasmCode Then Return ConsoleWrite( "$pFasmCode ERR" & @CRLF ) ; Execute fasm code to fill safearray DllCallAddress( "int", $pFasmCode, "int", 2^24, "ptr", $pSafeArrayData ) SafeArrayUnaccessData( $pSafeArray ) ; --- Convert safearray to native AutoIt array --- Local $aArray1 AccVars_SafeArrayToArray( $pSafeArray, $aArray1 ) ; <<<< The UDF >>>> ConsoleWrite( @CRLF & "Applied time to create and fill array (UDF and fasm): " & TimerDiff( $hTimer1 ) & @CRLF ) ; ------------------------------------------------ _ArrayDisplayEx( $aArray1, Default, "", 0x0010, "", "", "", 75 ) ConsoleWrite( @CRLF & "--- Inspect $aArray1 ---" & @CRLF ) InspectArray( $aArray1 ) $aArray1 = 0 ; === Pure AutoIt code === ConsoleWrite( @CRLF & "Executing pure AutoIt code to create and fill array (~10 seconds) ..." & @CRLF ) Local $hTimer2 = TimerInit() Local $aArray2[2^24] For $i = 0 To 2^24 - 1 $aArray2[$i] = $i Next ConsoleWrite( @CRLF & "Applied time to create and fill array (pure AutoIt): " & TimerDiff( $hTimer2 ) & @CRLF ) _ArrayDisplayEx( $aArray2, Default, "", 0x0010, "", "", "", 75 ) ConsoleWrite( @CRLF & "--- Inspect $aArray2 ---" & @CRLF ) InspectArray( $aArray2 ) $aArray2 = 0 EndFunc Line 53 is the crucial line of code: AccVars_SafeArrayToArray( $pSafeArray, $aArray1 ) ; <<<< The UDF >>>> Output in SciTE console should look like this: --- Inspect safearray --- Number of dimensions = 1 Features flags = 0x00000080 ($FADF_HAVEVARTYPE, array of variant type) Variant type = 0x0003 (VT_I4, 4 bytes signed integer) Size of array element (bytes) = 4 (size of the element structure) Number of locks = 0 Pointer to data = 0x02B80020 (pvData) Dimension 1: Elements in dimension = 16777216 Lower bound of dimension = 0 Applied time to create and fill array (UDF and fasm): 797.855906880413 --- Inspect $aArray1 --- Number of dimensions = 1 Features flags = 0x00000880 ($FADF_VARIANT+$FADF_HAVEVARTYPE, array of variants) Variant type = 0x000C (VT_VARIANT, variant data type) Size of array element (bytes) = 16 (size of the variant structure) Number of locks = 0 Pointer to data = 0x7FFF0020 (pvData) Dimension 1: Elements in dimension = 16777216 Lower bound of dimension = 0 Executing pure AutoIt code to create and fill array (~10 seconds) ... Applied time to create and fill array (pure AutoIt): 8670.46279987079 --- Inspect $aArray2 --- Number of dimensions = 1 Features flags = 0x00000880 ($FADF_VARIANT+$FADF_HAVEVARTYPE, array of variants) Variant type = 0x000C (VT_VARIANT, variant data type) Size of array element (bytes) = 16 (size of the variant structure) Number of locks = 0 Pointer to data = 0x7FFF0020 (pvData) Dimension 1: Elements in dimension = 16777216 Lower bound of dimension = 0 The UDF and fasm code is about 10 times faster than the pure AutoIt code. The code to populate the array is very simple. That's why the AutoIt code is doing relatively well compared to the UDF and fasm code. The technique implemented here can also be used to pass simple AutoIt variables to a function coded in a another language. This makes it possible to test on simple variables, rather than more complex arrays. And simple variables are needed in the final UDF. Post 7 contains a brief description of how the idea for this project arose. You'll find the following sections below: Accessing variables COM objects - COM objects can handle AutoIt arrays. Can this be exploited? AccessingVariablesTest.au3 - Test with UIAutomation::RectToVariant (Examples - Tests\Examples\0) Accessing variables\) Simple variables Variants - Introduction Basic strings - Introduction Examples - Tests\Examples\1) Simple variables\ Numeric variables (post 11) Numeric variants (post 11) Array variables Safearrays - Introduction Examples - Tests\Examples\2) Array variables\ Assembler code SafeArrayDisplay.au3 (post 24) Safearrays of integers (post 24) Internal conversions Exploiting conversions Avoiding conversions (Examples - Tests\Examples\3) Internal conversions\) Limitations (post 13) Final UDF - AccessingVariables.au3 AccessVariablesXY - 30 functions Restrictions - No literal params, no nested funcs Utility funcs - AccVarsUtilities.au3 InspectVariable - InspectVariable.au3 Using the UDF - Examples\Demo examples\4) Other demo examples\6) Using the UDF\ Subclassing (post 21) - Examples\Subclassing\ Examples Demo examples - Examples\Demo examples\ Assembler code - Tools to create fasm code not included Other demo examples - Examples\Demo examples\4) Other demo examples\ Real examples - Examples\Real examples\ sqlite3_get_table (post 25) What's next (post 26) Zip file For tests and for implementing the final UDF I've copied code written by monoceres, I've copied code from AutoItObject.au3 by the AutoItObject-Team: monoceres, trancexx, Kip, ProgAndy, and from CUIAutomation2.au3 by junkew. Lots of credit to these guys. Accessing variables AutoIt is a BASIC-like language. In BASIC-like languages simple variables are internally stored as variants, and arrays are internally stored as safearrays contained in variants. Assuming that an AutoIt variable is internally stored as a variant, is it possible to get a pointer to this variant? Assuming that an AutoIt array is internally stored as a safearray contained in a variant, then it should be possible to get a pointer to the safearray through the pointer to the variant. Why is a pointer to a safearray interesting? Because such a pointer can be passed as a parameter to a function coded in assembler, C, C++, C# or FreeBasic. We can thus access an AutoIt array directly from a function coded in another language, without the need to convert the array to a structure (DllStructCreate) or similar. In this way it's possible to code very fast array manipulation functions in a real compiled language. The crucial step is to get a pointer to the variant that contains the variable or array. COM objects If you have been using COM objects like the Dictionary ("Scripting.Dictionary") object, you know that this object can return AutoIt arrays in this way: $aKeys = $oDict.Keys() $aItems = $oDict.Items() In an example in "Obj/COM Reference" chapter in AutoIt Help file you can find this code line: $oExcel.activesheet.range("A1:O16").value = $aArray ; Fill cells with example numbers The Excel object seems to know how to handle a native AutoIt array. For objects created with ObjCreateInterface it's also easy to find examples where these objects understands how to handle native AutoIt arrays. An example is RectToVariant method of the UIAutomation object. This method converts a rectangle structure to an array: ; Create UIAutomation object Local $oUIAutomation = ObjCreateInterface( $sCLSID_CUIAutomation, $sIID_IUIAutomation, $stag_IUIAutomation ) If Not IsObj( $oUIAutomation ) Then Return ConsoleWrite( "$oUIAutomation ERR" & @CRLF ) ConsoleWrite( "$oUIAutomation OK" & @CRLF ) ; Create rectangle structure Local $tRect = DllStructCreate( $tagRECT ) DllStructSetData( $tRect, "Left", 100 ) DllStructSetData( $tRect, "Top", 200 ) DllStructSetData( $tRect, "Right", 3000 ) DllStructSetData( $tRect, "Bottom", 4000 ) Local $aArray $oUIAutomation.RectToVariant( $tRect, $aArray ) ; Variant array: VT_ARRAY + VT_R8 If Not IsArray( $aArray ) Then Return ConsoleWrite( "$aArray ERR" & @CRLF ) ConsoleWrite( "$aArray OK" & @CRLF ) _ArrayDisplay( $aArray ) You can find the example (Tests\Examples\0) Accessing variables\Example1.au3) in the zip below. AccessingVariablesTest.au3 I've been playing with RectToVariant method of the UIAutomation object. And I have studied how the output array ($aArray in the code above) looks like in C++ (see also last code box in post 15). The description for RectToVariant in $stag_IUIAutomation looks like this: "RectToVariant hresult(" & ( @AutoItX64 ? "struct*;" : "struct;" ) & "variant*);" Note the last parameter "variant*". What's that? That's a pointer to a variant. Exactly what we need. You can get a pointer to the variant that contains $aArray in the example above in this way: Replace the RectToVariant method with your own function. Inside the function the pointer to the variant that contains $aArray is simply the last parameter. It's necessary to replace RectToVariant with our own function to be able to add code inside the function. And it's very important that the parameter type in the function (or method) description string is "variant*". Exactly this parameter type ensures that the parameter coming from the AutoIt code is converted to a pointer to a variant. Inside the RectToVariant method or our own function the last parameter is not a native AutoIt array. It's a pointer to a variant. This conversion between different data types is performed by internal AutoIt code. And the conversion is only performed in relation to objects. There is no such conversion in relation to eg. the DllCall function. That's why we have to deal with objects. The technique of replacing an object method with our own function has been seen many times before. Eg. in this old example by monoceres. Now when we have ObjCreateInterface it's much easier. You don't need much code: #include-once #include "..\Includes\Variant.au3" #include "..\Includes\SafeArray.au3" #include "..\Includes\Utilities.au3" Global $hAccessVariableFunction Func AccessVariable( $hAccessVariableFunc, ByRef $vVariable ) Static $oAccessVariable = AccessVariableInit() ; Init $oAccessVariable (only once) $hAccessVariableFunction = $hAccessVariableFunc ; Code to execute in VariableToVariant $oAccessVariable.VariableToVariant( $vVariable ) ; Execute VariableToVariant method EndFunc Func AccessVariableInit() ; Three locals copied from "IUIAutomation MS framework automate chrome, FF, IE, ...." by junkew ; https://www.autoitscript.com/forum/index.php?showtopic=153520 Local $sCLSID_CUIAutomation = "{FF48DBA4-60EF-4201-AA87-54103EEF594E}" Local $sIID_IUIAutomation = "{30CBE57D-D9D0-452A-AB13-7AC5AC4825EE}" Local $stag_IUIAutomation = _ "f01 hresult();f02 hresult();f03 hresult();f04 hresult();f05 hresult();f06 hresult();f07 hresult();" & _ "f08 hresult();f09 hresult();f10 hresult();f11 hresult();f12 hresult();f13 hresult();f14 hresult();" & _ "f15 hresult();f16 hresult();f17 hresult();f18 hresult();f19 hresult();f20 hresult();f21 hresult();" & _ "f22 hresult();f23 hresult();f24 hresult();f25 hresult();f26 hresult();f27 hresult();f28 hresult();" & _ "f29 hresult();f30 hresult();f31 hresult();f32 hresult();f33 hresult();f34 hresult();f35 hresult();" & _ "f36 hresult();f37 hresult();f38 hresult();f39 hresult();f40 hresult();f41 hresult();" & _ "VariableToVariant hresult(variant*);" & _ ; "RectToVariant hresult(" & ( @AutoItX64 ? "struct*;" : "struct;" ) & "variant*);" "f43 hresult();f44 hresult();f45 hresult();f46 hresult();f47 hresult();f48 hresult();f49 hresult();" & _ "f50 hresult();f51 hresult();f52 hresult();f53 hresult();f54 hresult();f55 hresult();" ; Create AccessVariable object (Automation object) Local $oAccessVariable = ObjCreateInterface( $sCLSID_CUIAutomation, $sIID_IUIAutomation, $stag_IUIAutomation ) If Not IsObj( $oAccessVariable ) Then Return SetError(1,0,1) ; Replace RectToVariant method with VariableToVariant method Local $pVariableToVariant = DllCallbackGetPtr( DllCallbackRegister( "VariableToVariant", "long", "ptr;ptr*" ) ) ReplaceVTableFuncPtr( Ptr( $oAccessVariable() ), ( 3 + 42 - 1 ) * ( @AutoItX64 ? 8 : 4 ), $pVariableToVariant ) Return $oAccessVariable EndFunc Func VariableToVariant( $pSelf, $pVariant ) $hAccessVariableFunction( $pVariant ) Return 0 ; $S_OK (COM constant) #forceref $pSelf EndFunc The code is saved in Tests\AccessingVariablesTest.au3 in the zip. To use the UDF you call AccessVariable function in the top. Note the ByRef keyword in the function. The ByRef keyword is very important. The function will not work without this keyword. Note also that the pointer to the variant in VariableToVariant function in bottom of the UDF is created by some internal AutoIt conversion code. And it's a local function parameter. The pointer is only valid within VariableToVariant and in the function you specify as a parameter when you call AccessVariable. As soon as VariableToVariant returns, the pointer is invalid. Let's try some small examples with simple variables and array variables. Simple variables Before we go to the examples let's take a quick look at variants and basic strings (BSTRs). Variants and basic strings are needed in our assembler, C, C++, C# or FreeBasic functions. Variants A variant is defined by this structure: Global Const $tagVARIANT = "word vt;word r1;word r2;word r3;ptr data; ptr" Only vt and data elements are used. The structure takes up 16/24 bytes when you're running 32/64 bit. Space for the data element at the end represents 2 pointers. This is 8/16 bytes when you're running 32/64 bit. If $pVariant is a pointer to a variant you can get vt and data elements in this way: Local $vt = DllStructGetData( DllStructCreate( "word", $pVariant ), 1 ) Local $data = DllStructGetData( DllStructCreate( "<vt>", $pVariant + 8 ), 1 ) The four word (word = 2 bytes) elements before the data element takes up 8 bytes. Common values of vt in AutoIt are: $VT_I4 = 3 ; Integer, "<vt>" = "int" $VT_R8 = 5 ; Double, "<vt>" = "double" $VT_BSTR = 8 ; Basic string, "<vt>" = "ptr" $VT_UI4 = 19 ; Pointer running 32 bit, "<vt>" = "ptr" $VT_UI8 = 21 ; Pointer running 64 bit, "<vt>" = "ptr" If $pVariant is a pointer to a variant which contains an array you'll always get this value for vt: $VT_ARRAY + $VT_VARIANT = 0x200C ; Pointer, "<vt>" = "ptr" A native AutoIt array is stored as a safearray ($VT_ARRAY = 0x2000) contained in a variant ($VT_VARIANT = 0x000C). The pointer to the safearray is stored in the data element of the variant. Variant constants and functions are defined in Includes\Variant.au3. Most of the code is shamelessly copied from AutoItObject.au3 by the AutoItObject-Team: monoceres, trancexx, Kip, ProgAndy. Basic strings Internally AutoIt strings are stored as basic strings or BSTRs. The pointer to the BSTR is stored in a variant. A BSTR is defined by this structure: Global Const $tagBSTR = & _ "dword Length;" & _ ; Length in bytes (2 * $iLen), does not include the Terminator "wchar String[" & $iLen & "];" & _ ; $iLen is wchars, $pBSTR = DllStructGetPtr( $tBSTR, "String" ) "word Terminator;" ; Two null characters Use this code to get the pointer to the BSTR (the pointer which is stored in a variant): Local $pBSTR = DllStructGetPtr( $tBSTR, "String" ) Note that the BSTR pointer is the start of the "String" element and not the start of the structure. Normally you do not handle BSTRs directly through this structure. You use the BSTR functions in Variant.au3. Also copied from AutoItObject.au3. You can find information about variant and BSTR conversion and manipulation functions here. Examples The examples are stored in "Tests\Examples\1) Simple variables\". There are three small examples. These examples are just AutoIt code. This is a part of Example2.au3: Local $sStr = "AutoIt" ConsoleWrite( "$sStr = " & $sStr & @CRLF ) AccessVariable( InspectVariableMtd, $sStr ) ; InspectVariableMtd is coded in Includes\InspectVariable.au3 AccessVariable( SetString, $sStr ) ; Shows how to use the AccessVariable function ConsoleWrite( "$sStr = " & $sStr & @CRLF ) Func SetString( $pVariant ) Local $pData = $pVariant + 8 ; See InspectVariable.au3 Local $tData = DllStructCreate( "ptr", $pData ) Local $pBStr = DllStructGetData( $tData, 1 ) SysFreeString( $pBStr ) ; See Variant.au3 $pBStr = SysAllocString( "Hello world" ) DllStructSetData( $tData, 1, $pBStr ) EndFunc The output i SciTE console should look like this: $sStr = AutoIt ptr = 0x006F9630 ($pVariant) vt = 0x0008 (VT_BSTR, basic string) data = AutoIt $sStr = Hello world Example3.au3 is similar to the example for VarGetType in the Help file. It prints the variant vt-values for the corresponding AutoIt data types. Array variables Before we go to the examples let's take a quick look at safearrays. Safearrays A safearray is defined by these structures: Global Const $tagSAFEARRAYBOUND = _ "ulong cElements;" & _ ; The number of elements in the dimension. "long lLbound;" ; The lower bound of the dimension. Global Const $tagSAFEARRAY = _ "ushort cDims;" & _ ; The number of dimensions. "ushort fFeatures;" & _ ; Flags, see below. "ulong cbElements;" & _ ; The size of an array element. "ulong cLocks;" & _ ; The number of times the array has been locked without a corresponding unlock. "ptr pvData;" & _ ; The data. $tagSAFEARRAYBOUND ; One $tagSAFEARRAYBOUND for each dimension. ; Examples ; 1D, 2D and 3D safearrays: Local $tagSAFEARRAY1D = $tagSAFEARRAY Local $tagSAFEARRAY2D = $tagSAFEARRAY & $tagSAFEARRAYBOUND Local $tagSAFEARRAY3D = $tagSAFEARRAY & $tagSAFEARRAYBOUND & $tagSAFEARRAYBOUND In AutoIt an array is stored as a safearray contained in a variant. The safearray is always an array of variants. That the safearray is contained in a variant means that the pointer to the safearray is stored in the data element of a variant. That the safearray is an array of variants means that the pvData element of the safearray points to a memory area which contains a continuous row of variant structures. If we're running 32 bit a variant takes up 16 bytes. For a 1D-array with three elements: Local $aArray[3] = [ 1, 2, 3 ] The pvData element of the safearray points to a memory area which takes up 48 bytes and consists of three variant structures. Normally you do not handle safearrays directly through these structures. You use the safearray functions in Includes\SafeArray.au3. Copied from AutoItObject.au3. You can find information about safearray conversion and manipulation functions here. Examples The examples are stored in "Tests\Examples\2) Array variables\". The examples are still just AutoIt code. Example1.au3 prints information for 1D-arrays of integers, floats and strings. Information for the integer array should look like this: $aInts = [ 0, 1, 2 ] --- InspectVariable $aInts --- ptr = 0x00990378 ($pVariant) vt = 0x200C (VT_ARRAY+VT_VARIANT, array of variants, safearray) data = 0x009981E8 (pointer to safearray) --- InspectVariable $aInts[0] --- ptr = 0x0098FEC8 ($pVariant) vt = 0x0003 (VT_I4, 4 bytes signed integer) data = 0 --- InspectArray $aInts --- Number of dimensions = 1 Features flags = 0x00000880 ($FADF_VARIANT+$FADF_HAVEVARTYPE, array of variants) Variant type = 0x000C (VT_VARIANT, variant data type) Size of array element (bytes) = 16 (size of the variant structure) Number of locks = 0 Pointer to data = 0x00998710 (pvData) Dimension 1: Elements in dimension = 3 Lower bound of dimension = 0 Example2.au3 prints information for 2D-arrays of integers, floats and strings, and prints the contents of the arrays through the data area of the safearray. pvData element of $tagSAFEARRAY structure is a pointer to the data area. Example3.au3 fills an existing array, $aArray[50000], with integers. Example4.au3 creates and fills an array with 50000 integers and assigns it to an uninitialized variable: $aArray (empty string). Assembler code Example5.au3 is the first small example with fasm code (fasm = flat assembler, more info about fasm in one of the next sections). A 1D-array with 2^24 (16,777,216) elements is filled with integers from zero to 2^24 - 1. The array is first filled through AutoIt code. Then AccessVariable is used to fill the corresponding safearray through fasm code. There are two versions of the fasm code: A 32 bit version and a 64 bit version. AutoIt code (Example5.au3): ;#AutoIt3Wrapper_UseX64=y #include "..\..\AccessingVariablesTest.au3" #include "..\..\..\Includes\ArrayDisplayEx.au3" #include "..\..\..\Includes\FasmUtils.au3" Opt( "MustDeclareVars", 1 ) Example5() Func Example5() ConsoleWrite( "Executing AutoIt code to fill array (~10 seconds) ..." & @CRLF ) Local $aArray1[2^24] ; 2^24 = 16,777,216, maximum number of elements for an array Local $hTimer1 = TimerInit() For $i = 0 To 2^24 - 1 $aArray1[$i] = $i Next ConsoleWrite( "Time for AutoIt code to fill array: " & TimerDiff( $hTimer1 ) & @CRLF & @CRLF ) _ArrayDisplayEx( $aArray1 ) $aArray1 = 0 ConsoleWrite( "Executing FillArray to fill array ..." & @CRLF ) Local $aArray2[2^24] ; 2^24 = 16,777,216, maximum number of elements for an array Local $hTimer4 = TimerInit() AccessVariable( FillArray, $aArray2 ) Local $fTime4 = TimerDiff( $hTimer4 ) ConsoleWrite( "Time for FillArray to fill array (outside FillArray): " & $fTime4 & @CRLF ) ConsoleWrite( "Time for FillArray to fill array: " & $fTime4 & @CRLF ) _ArrayDisplayEx( $aArray2 ) $aArray2 = 0 EndFunc Func FillArray( $pVariant ) Local $hTimer3 = TimerInit() ; Pointer to safearray Local $pData = $pVariant + 8 Local $tData = DllStructCreate( "ptr", $pData ) Local $pSafeArray = DllStructGetData( $tData, 1 ) ; Pointer to data Local $pSafeArrayData SafeArrayAccessData( $pSafeArray, $pSafeArrayData ) ; Get fasm code Static $sFasmCode = @AutoItX64 ? "0xB80000000066C70203008942084883C218FFC0E2F0C3" _ ; Example5-x64.asm : "0x5589E58B4D088B550CB80000000066C702030089420883C21040E2F25DC20800" ; Example5-x86.asm Static $pFasmCode = FasmGetBinaryString( $sFasmCode, 64 ) If Not $pFasmCode Then Exit ConsoleWrite( "$pFasmCode ERR" & @CRLF ) ; Execute fasm code Local $hTimer2 = TimerInit() DllCallAddress( "int", $pFasmCode, "int", 2^24, "ptr", $pSafeArrayData ) ConsoleWrite( "Time for FillArray to fill array (fasm code only): " & TimerDiff( $hTimer2 ) & @CRLF ) SafeArrayUnaccessData( $pSafeArray ) ConsoleWrite( "Time for FillArray to fill array (inside FillArray): " & TimerDiff( $hTimer3 ) & @CRLF ) EndFunc Note that the fasm code is executed inside FillArray function. And FillArray function is executed inside the $oAccessVariable.VariableToVariant method (in AccessingVariablesTest.au3). The pointer to the safearray data ($pSafeArrayData) is valid only inside this method. $pSafeArrayData is a pointer that points to the data area that contains the data of $aArray2. $pSafeArrayData however does not point directly to the same data as contained in $aArray2. Internal AutoIt conversions on function entry copies data from $aArray2 to the data area. Internal AutoIt conversions on function exit copies data from the data area to $aArray2. See next section. 32 bit fasm code (Example5-x86.asm): ; flat assembler code ; Translate AutoIt code to fasm code: ; For $i = 0 To 2^24 - 1 ; $aArray[$i] = $i ; Next ; Parameters: ; [ebp + 08] : iRows ; First parameter ; [ebp + 12] : pSafeArrayData ; Second parameter ; Init directive use32 ; 32 bit code ; Entry code push ebp ; Store base pointer on stack mov ebp, esp ; Use stack pointer as base pointer ; Function code mov ecx, [ebp + 08] ; ecx corresponds to 2^24 in "For $i = 0 To 2^24 - 1" mov edx, [ebp + 12] ; edx is pointer in safearray data area mov eax, 0 ; eax = 0, eax corresponds to $i iLoop: mov [edx], word 3 ; Set vt element in variant to 3 (VT_I4, integer) mov [edx + 08], eax ; Set data element in variant to eax ($i) add edx, 16 ; Add size of variant structure to edx inc eax ; eax += 1, corresponds to $i += 1 loop iLoop ; ecx -= 1, jump to iLoop if not zero ; Exit code pop ebp ; Restore base pointer from stack ret 08 ; Return and cleanup stack 64 bit fasm code (Example5-x64.asm): ; flat assembler code ; Translate AutoIt code to fasm code: ; For $i = 0 To 2^24 - 1 ; $aArray[$i] = $i ; Next ; Parameters: ; rcx : iRows ; First parameter, ecx corresponds to 2^24 in "For $i = 0 To 2^24 - 1" ; rdx : pSafeArrayData ; Second parameter, rdx is pointer in safearray data area ; Init directive use64 ; 64 bit code ; Function code mov eax, 0 ; eax = 0, eax corresponds to $i iLoop: mov [rdx], word 3 ; Set vt element in variant to 3 (VT_I4, integer) mov [rdx + 08], eax ; Set data element in variant to eax ($i) add rdx, 24 ; Add size of variant structure to rdx inc eax ; eax += 1, corresponds to $i += 1 loop iLoop ; ecx -= 1, jump to iLoop if not zero ; Exit code ret ; Return Output in SciTE console: Executing AutoIt code to fill array (~10 seconds) ... Time for AutoIt code to fill array: 9033.87383563625 Executing FillArray to fill array ... Time for FillArray to fill array (fasm code only): 28.5154490311906 Time for FillArray to fill array (inside FillArray): 28.6592369763861 Time for FillArray to fill array (outside FillArray): 2388.37594979003 Time for FillArray to fill array: 2388.37594979003 Why is there such a big difference in the time it takes to execute FillArray when the time is measured inside and outside the function? Internal conversions In "Obj/COM Reference" chapter, "COM Events" section and "Limitations on COM Events in AutoIt" subsection in AutoIt Help file you can find the following sentence: "... AutoIt uses its own variable scheme, which is not compatible to COM variables. This means that all values from Objects need to be converted into AutoIt variables ...". In the AutoIt website you can find small bits of information like this one. These internal conversions takes place between native AutoIt data types and COM data types, when AutoIt variables are passed to object methods as function parameters, and when COM variables are returned to AutoIt. There are two sets of conversions. Conversions on function entry (object method entry), and conversions on function exit. The previous section ended with this question: Why is there such a big difference in the time it takes to execute FillArray when the time is measured inside and outside the function (FillArray is executed inside the object method)? The large time difference is caused by the conversions. The conversions are performed by internal AutoIt code and Windows API functions. Both consists of compiled C++ code. Judging from the time the conversions takes (about 2.5 seconds on my PC), they seem to perform a complete (by value) copy of the entire array. The array with 16,777,216 integers. Even for compiled C++ code it takes time to copy such a large array. Example1.au3 ("Tests\Examples\3) Internal conversions\") shows how long time the conversions takes: ;#AutoIt3Wrapper_UseX64=y #include "..\..\AccessingVariablesTest.au3" #include "..\..\..\Includes\ArrayDisplayEx.au3" Opt( "MustDeclareVars", 1 ) Example1() Func Example1() ConsoleWrite( "Filling array of 16,777,216 integers (~10 seconds) ..." & @CRLF ) Local $aArray1[2^24] ; 2^24 = 16,777,216, maximum number of elements for an array For $i = 0 To 2^24 - 1 $aArray1[$i] = 1234 Next Local $hTimer = TimerInit() AccessVariable( ConversionTime, $aArray1 ) ConsoleWrite( "Time for conversion code to execute: " & TimerDiff( $hTimer ) & @CRLF & @CRLF ) _ArrayDisplayEx( $aArray1 ) $aArray1 = 0 ConsoleWrite( "Filling array of 16,777,216 doubles (~10 seconds) ..." & @CRLF ) Local $aArray2[2^24] ; 2^24 = 16,777,216, maximum number of elements for an array For $i = 0 To 2^24 - 1 $aArray2[$i] = 1234.5678 Next $hTimer = TimerInit() AccessVariable( ConversionTime, $aArray2 ) ConsoleWrite( "Time for conversion code to execute: " & TimerDiff( $hTimer ) & @CRLF & @CRLF ) _ArrayDisplayEx( $aArray2 ) $aArray2 = 0 ConsoleWrite( "Filling array of 1,048,576 100-chars strings (~5 seconds) ..." & @CRLF ) Local $aArray3[2^20] ; 2^20 = 1,048,576 For $i = 0 To 2^20 - 1 $aArray3[$i] = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" ; 100 chars Next $hTimer = TimerInit() AccessVariable( ConversionTime, $aArray3 ) ConsoleWrite( "Time for conversion code to execute: " & TimerDiff( $hTimer ) & @CRLF ) _ArrayDisplayEx( $aArray3 ) $aArray3 = 0 EndFunc ; Because this is an empty function the time measured above ; is the total time for all conversions of one array parameter. Func ConversionTime( $pVariant ) EndFunc Output in SciTE console: Filling array of 16,777,216 integers (~10 seconds) ... Time for conversion code to execute: 4303.24786930952 Filling array of 16,777,216 doubles (~10 seconds) ... Time for conversion code to execute: 4341.52716013949 Filling array of 1,048,576 100-chars strings (~5 seconds) ... Time for conversion code to execute: 2416.14925416137 Because 16,777,216 elements is the maximum number of elements in an array, the conversions will never take longer than 4.5 seconds on my PC for an array of integers or doubles. The time is about the same for integers and doubles because they are both stored as variants. A variant takes up 16 bytes when you're running 32 bit whether the variant contains a 4 bytes integer or an 8 bytes double. Why did the conversions in the previous section only take 2.5 seconds on my PC? Only about half as much time. I can only guess that this must be caused by the fact that this was an uninitialized array. And for an uninitialized array the conversions on function entry seems to be much faster. Conversions of an array of strings takes longer time. The variants which contains pointers to the strings has to be copied. And the strings (BSTRs) themselves has to be copied. That's the reason why the number of strings is limited to about 1,000,000. Still a decent number of strings. Although the assembler, C, C++, C# or FreeBasic code is lightning fast, the conversions especially for very large arrays means that the total execution time for the entire function (FillArray in the example in the previous section) will be increased with a few seconds. On the other hand we need the conversions. We don't want the array to be returned as a safearray contained in a variant. We want the array to be returned as a native AutoIt array. Exploiting conversions As I wrote in first section I've been playing with RectToVariant method of the UIAutomation object. In Remarks section in the link you can read this sentence: "The returned VARIANT has a data type of VT_ARRAY | VT_R8." (= VT_ARRAY + VT_R8). But this does not match the internal implementation of an AutoIt array which is VT_ARRAY + VT_VARIANT. Because RectToVariant returns a perfect AutoIt array this must mean that the conversion code also inspects the variant array type and converts it to a VT_ARRAY + VT_VARIANT type if necessary. I've tested that variants of types VT_ARRAY+VT_I4 (integers), VT_ARRAY+VT_R8 (doubles) and VT_ARRAY+VT_BSTR (strings) are properly converted to variants of type VT_ARRAY+VT_VARIANT. Rememeber that a variant of type VT_ARRAY+VT_I4 is a variant which contains a safearray (VT_ARRAY), where the pvData element of the safearray structure points to a memory area which contains a continuous row of integers (VT_I4). What is a continuous row of integers? Well, in AutoIt you create a continuous row of integers with DllStructCreate like this: Local $tIntegers = DllStructCreate( "int[50000]" ) This is a continuous row of 50000 integers. This means that if you are manipulating an array of integers in your assembler, C, C++, C# or FreeBasic code, you don't have to mess around with variant structures containing integers. You can simply use an array of integers. When you've finished the array manipulations you can store the integers as a fairly simple VT_ARRAY+VT_I4 variant (safearray of integers). And then you can leave it to the conversion code on function exit to convert the variant to a VT_ARRAY+VT_VARIANT variant (safearray of variants) which is understandable by AutoIt. And in fact, all these safearray data types are correctly converted to safearrays of variants: $VT_I2, $VT_I4, $VT_R4, $VT_R8, $VT_BSTR, $VT_BOOL, $VT_UI4, $VT_UI8 Example2a/b/c.au3, Example3a/b/c.au3 and Example4a/b/c.au3 demonstrates this technique in three slightly different ways for integers, doubles and strings. These techniques are needed in the assembler, C, C++, C# or FreeBasic code. Note that the examples are based on the final UDF and not the test UDF. See next section. This is Example2a.au3: ;#AutoIt3Wrapper_UseX64=y #include "..\..\..\Includes\AccessingVariables.au3" ; <<<< Final UDF (not test UDF) >>>> #include "..\..\..\Includes\InspectVariable.au3" #include <Array.au3> Opt( "MustDeclareVars", 1 ) Example2() Func Example2() Local $aArray ; Empty string ConsoleWrite( "--- InspectVariable ---" & @CRLF ) InspectVariable( $aArray ) ; $aArray is an empty string AccessVariables01( CreateArray, $aArray ) ConsoleWrite( "--- InspectVariable ---" & @CRLF ) InspectVariable( $aArray ) ; $aArray is an array ConsoleWrite( "--- InspectArray ---" & @CRLF ) InspectArray( $aArray ) _ArrayDisplay( $aArray ) EndFunc Func CreateArray( $pVariant ) ; --- Create and fill structure of integers --- ; Create structure Local $tIntegers = DllStructCreate( "int[50000]" ) Local $pIntegers = DllStructGetPtr( $tIntegers ) ; Fill structure ; Array manipulation For $i = 0 To 50000 - 1 DllStructSetData( $tIntegers, 1, $i, $i + 1 ) Next ; --- Create and fill safearray --- ; Create safearray Local $tSafeArrayBound = DllStructCreate( $tagSAFEARRAYBOUND ) DllStructSetData( $tSafeArrayBound, "cElements", 50000 ) ; Number of elements in array DllStructSetData( $tSafeArrayBound, "lLbound", 0 ) ; Lower bound of array index Local $pSafeArray = SafeArrayCreate( $VT_I4, 1, $tSafeArrayBound ) ; <<<< Not a proper AutoIt safearray >>>> ; This is a safearray of integers and not variants as a usual AutoIt array ; Pointer to data Local $pSafeArrayData SafeArrayAccessData( $pSafeArray, $pSafeArrayData ) ; Create structure from safearray data area Local $tSafeArrayBytes = DllStructCreate( "byte[200000]", $pSafeArrayData ) ; Fill safearray data area with data from $tIntegers DllStructSetData( $tSafeArrayBytes, 1, DllStructGetData( DllStructCreate( "byte[200000]", $pIntegers ), 1 ) ) ; This technique only works with byte's, char's and wchar's SafeArrayUnaccessData( $pSafeArray ) ; --- Set variant to match an array of integers --- ; Set vt element to $VT_ARRAY + $VT_I4 Local $tvt = DllStructCreate( "word", $pVariant ) DllStructSetData( $tvt, 1, $VT_ARRAY + $VT_I4 ) ; <<<< Not a proper AutoIt array >>>> ; This is an array of integers and not variants as a usual AutoIt array ; Set data element to safearray pointer Local $pData = $pVariant + 8 Local $tData = DllStructCreate( "ptr", $pData ) DllStructSetData( $tData, 1, $pSafeArray ) ; <<<< On function exit the safearray contained in a variant is converted to a native AutoIt array >>>> EndFunc This is output in SciTE console. The array is displayed with _ArrayDisplay. --- InspectVariable --- ptr = 0x00D0E698 ($pVariant) vt = 0x0008 (VT_BSTR, basic string) data = --- InspectVariable --- ptr = 0x02B78028 ($pVariant) vt = 0x200C (VT_ARRAY+VT_VARIANT, array of variants, safearray) data = 0x00CAB170 (pointer to safearray) --- InspectArray --- Number of dimensions = 1 Features flags = 0x00000880 ($FADF_VARIANT+$FADF_HAVEVARTYPE, array of variants) Variant type = 0x000C (VT_VARIANT, variant data type) Size of array element (bytes) = 16 (size of the variant structure) Number of locks = 0 Pointer to data = 0x02EF0020 (pvData) Dimension 1: Elements in dimension = 50000 Lower bound of dimension = 0 Avoiding conversions For large arrays conversions may take quite some time. The conversions cannot be avoided, but in some situations they can be limited. Eg. a large array with 10 columns to sort by four columns (like the details view in Windows Explorer can be sorted by name, date, type and size). In this situation four indexes can be used to implement the sorting. And because it's a large array the indexes should be created with compiled code. Instead of converting the large array four times (once for each index), it would be much better to get a pointer to the safearray, and then reuse this pointer for each index. It'll only require one conversion of the large array to get a pointer to the safearray. For this purpose two functions in Includes\AccVarsUtilities.au3 can be used: AccVars_ArrayToSafeArray which creates a pointer to a safearray from a native AutoIt array, and AccVars_SafeArrayToArray which creates a native AutoIt array from a pointer to a safearray. More about these functions i a later section. Example5.au3 shows how long time it takes for the two functions to create a safearray and an array: ;#AutoIt3Wrapper_UseX64=y #include "..\..\..\Includes\AccessingVariables.au3" ; <<<< Final UDF (not test UDF) >>>> #include "..\..\..\Includes\AccVarsUtilities.au3" #include "..\..\..\Includes\ArrayDisplayEx.au3" Opt( "MustDeclareVars", 1 ) Example5() Func Example5() ; --- Array of integers --- ConsoleWrite( "Filling array of 16,777,216 integers (~10 seconds) ..." & @CRLF ) Local $aArray1[2^24] ; 2^24 = 16,777,216, maximum number of elements for an array For $i = 0 To 2^24 - 1 $aArray1[$i] = 1234 Next Local $pSafeArray1 Local $hTimer = TimerInit() AccVars_ArrayToSafeArray( $aArray1, $pSafeArray1 ) ConsoleWrite( "Time for AccVars_ArrayToSafeArray to execute: " & TimerDiff( $hTimer ) & @CRLF ) _ArrayDisplayEx( $aArray1 ) $aArray1 = 0 Local $aArray2 $hTimer = TimerInit() AccVars_SafeArrayToArray( $pSafeArray1, $aArray2 ) ConsoleWrite( "Time for AccVars_SafeArrayToArray to execute: " & TimerDiff( $hTimer ) & @CRLF & @CRLF ) _ArrayDisplayEx( $aArray2 ) $aArray2 = 0 ; --- Array of doubles --- ConsoleWrite( "Filling array of 16,777,216 doubles (~10 seconds) ..." & @CRLF ) Local $aArray3[2^24] ; 2^24 = 16,777,216, maximum number of elements for an array For $i = 0 To 2^24 - 1 $aArray3[$i] = 1234.5678 Next Local $pSafeArray3 $hTimer = TimerInit() AccVars_ArrayToSafeArray( $aArray3, $pSafeArray3 ) ConsoleWrite( "Time for AccVars_ArrayToSafeArray to execute: " & TimerDiff( $hTimer ) & @CRLF ) _ArrayDisplayEx( $aArray3 ) $aArray3 = 0 Local $aArray4 $hTimer = TimerInit() AccVars_SafeArrayToArray( $pSafeArray3, $aArray4 ) ConsoleWrite( "Time for AccVars_SafeArrayToArray to execute: " & TimerDiff( $hTimer ) & @CRLF & @CRLF ) _ArrayDisplayEx( $aArray4 ) $aArray4 = 0 ; --- Array of strings --- ConsoleWrite( "Filling array of 1,048,576 100-chars strings (~5 seconds) ..." & @CRLF ) Local $aArray5[2^20] ; 2^20 = 1,048,576 For $i = 0 To 2^20 - 1 $aArray5[$i] = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" ; 100 chars Next Local $pSafeArray5 $hTimer = TimerInit() AccVars_ArrayToSafeArray( $aArray5, $pSafeArray5 ) ConsoleWrite( "Time for AccVars_ArrayToSafeArray to execute: " & TimerDiff( $hTimer ) & @CRLF ) _ArrayDisplayEx( $aArray5 ) $aArray5 = 0 Local $aArray6 $hTimer = TimerInit() AccVars_SafeArrayToArray( $pSafeArray5, $aArray6 ) ConsoleWrite( "Time for AccVars_SafeArrayToArray to execute: " & TimerDiff( $hTimer ) & @CRLF ) _ArrayDisplayEx( $aArray6 ) $aArray6 = 0 EndFunc Output in SciTE console: Filling array of 16,777,216 integers (~10 seconds) ... Time for AccVars_ArrayToSafeArray to execute: 4493.62536677101 Time for AccVars_SafeArrayToArray to execute: 1164.59904458766 Filling array of 16,777,216 doubles (~10 seconds) ... Time for AccVars_ArrayToSafeArray to execute: 4549.6914981711 Time for AccVars_SafeArrayToArray to execute: 1159.73609940987 Filling array of 1,048,576 100-chars strings (~5 seconds) ... Time for AccVars_ArrayToSafeArray to execute: 2419.86058615209 Time for AccVars_SafeArrayToArray to execute: 675.048988223156 Note that a consequence of the conversions is that the input safearray for AccVars_SafeArrayToArray is deleted. If you need to use the same safearray several times you must make a copy of the safearray with SafeArrayCopy function in Includes\SafeArray.au3. In the example with fasm code in previous section we got this result: Time for FillArray to fill array: 2388.37594979003 In top of post we got this result for more or less the same code: Applied time to create and fill array (UDF and fasm): 797.855906880413 Three times as fast. This is a consequence of limiting conversions. In a few situations it may be possible to completely avoid conversions. Eg. if the data will be used to fill the rows in a virtual listview. In a virtual listview rows are filled through $LVN_GETDISPINFOW notifications. And for $LVN_GETDISPINFOW notifications it doesn't matter whether the data source is a native AutoIt array or the data structure in a safearray. Final UDF The whole idea of this UDF is to utilize the information we already know: We know that COM objects and methods are working very well in AutoIt. We also know that the only variable type as COM object methods are familiar with is variants (only completely true for IDispatch based objects and objects of Automation compatible types). But how is it possible for an object method to handle variables that are passed to the method as native AutoIt variables, when the only variable type as COM object methods are familiar with is variants? The only explanation is that native AutoIt input parameters must be converted to variants immediately before the method code is executed, and that variant output parameters must be converted to native AutoIt variables immediately after the method code has finished. These conversions must be carried out by internal AutoIt code. The final UDF (Includes\AccessingVariables.au3) makes it possible to pass native AutoIt arrays and simple variables as parameters to a function coded in another language eg. assembler, C, C++, C# or FreeBasic. This is done by executing the function inside an object method and passing the array and variable parameters as variant pointers. Internal AutoIt conversions ensures that AutoIt variables outside the method are properly converted to COM variants inside the method. And the other way around. In the test UDF (Tests\AccessingVariablesTest.au3) only one AutoIt variable is passed to the object method by AccessVariable function. The final UDF contains 30 functions named AccessVariables01 - AccessVariables30 where the number indicates the number of AutoIt variables passed to the object method. Manipulating AutoIt variables in another language (a compiled language) is especially relevant for arrays with a large number of elements, or smaller arrays where complex calculations are performed on the elements. AccessVariablesXY To use the UDF you call one of the AccessVariablesXY functions eg. AccessVariables01. AccessVariables01 is coded in this way: Func AccessVariables01( $hAccVars_Method, ByRef $vVariable01 ) $hAccVars_MethodFunc = $hAccVars_Method $oAccVars_Object.AccVars_VariableToVariant01( $vVariable01 ) EndFunc The first parameter is a function type parameter (the name of an AutoIt function). You must code this function yourself. The function is assigned to the global variable $hAccVars_MethodFunc. The second parameter is an AutoIt variable. Typically an array. This parameter is passed to the AccVars_VariableToVariant01 object method. AccessVariables01 is just a wrapper function to make it easier to call the object method. The description string for the object method looks like this: "AccVars_VariableToVariant01 hresult(variant*);" The AutoIt variable is passed to the method as a variant pointer. And the method is coded in this way: Func AccVars_VariableToVariant01( $pSelf, $pVariant01 ) $hAccVars_MethodFunc( $pVariant01 ) Return 0 ; $S_OK (COM constant) #forceref $pSelf EndFunc The first parameter $pSelf must be a pointer to the object $oAccVars_Object. This is a COM rule. The second parameter is the AutoIt variable you passed to the AccessVariables01 function. But inside AccVars_VariableToVariant01 this is not an AutoIt variable any more. Inside AccVars_VariableToVariant01 it's a pointer to a variant. Inside AccVars_VariableToVariant01 the $hAccVars_MethodFunc is called and the variant pointer is passed as a parameter. $hAccVars_MethodFunc is the function you passed to AccessVariables01. If you passed a native AutoIt array to AccessVariables01, you can extract this array as a pointer to a safearray inside $hAccVars_MethodFunc. And this pointer or a pointer directly to the safearray data area you can pass to a function coded in assembler, C, C++, C# or FreeBasic. When $hAccVars_MethodFunc is finished zero is returned to indicate that everything is OK. Restrictions Because the $vVariableXY parameters in AccessVariablesXY functions are ByRef parameters you cannot pass literal values as parameters. You must store the literal value in a variable and pass the variable. You cannot call an AccessVariablesXY function inside another AccessVariablesXY function. No nested function calls. This also means that you cannot call InspectVariable or InspectArray inside an AccessVariablesXY function. But you can call InspectSafeArray. See InspectVariable section below for info about Inspect-functions. Utility funcs Typically arrays are passed to the object methods. But this does not exclude the need to pass simple variables to the object methods. Eg. the number of rows and columns in the arrays. Simple variables are also passed to object methods as variant pointers. In most cases you probably want to treat simple variables as native AutoIt variables inside object methods and not as variant pointers. AccVars_VariantToVariable (Includes\AccVarsUtilities.au3) converts variant pointers to native AutoIt variables. Arrays are not converted. See "Examples\Demo examples\4) Other demo examples\2) Multiple parameters\Example1.au3". AccVars_VariableToVariant converts native AutoIt variables to variant pointers. Arrays are not converted. See "Examples\Demo examples\4) Other demo examples\6) Using the UDF\Example6.au3" AccVars_ArrayToSafeArray and AccVars_SafeArrayToArray are already mentioned above. AccVars_ArrayToSafeArray creates a pointer to a safearray from a native AutoIt array. AccVars_SafeArrayToArray creates a native AutoIt array from a pointer to a safearray. "Examples\Demo examples\4) Other demo examples\4) ArrayToSafearray\" includes a few examples of AccVars_ArrayToSafeArray. "Examples\Demo examples\4) Other demo examples\5) SafearrayToArray\" includes a few examples of AccVars_SafeArrayToArray. In both folders Example1.au3 shows how everything can be done manually without using the two functions. And Example2.au3 shows how it can be done using the two functions. Note that a consequence of the conversions is that the input safearray for AccVars_SafeArrayToArray is deleted. If you need to use the same safearray several times you must make a copy of the safearray with SafeArrayCopy function in Includes\SafeArray.au3. InspectVariable Includes\InspectVariable.au3 contains three functions: InspectVariable (corresponds to InspectVariableMtd), InspectArray (corresponds to InspectVariableMtd) and InspectSafeArray. The first two are used in many of the test examples. The last is used in the examples for AccVars_ArrayToSafeArray and AccVars_SafeArrayToArray. InspectVariable and InspectArray takes AutoIt variables as input parameters and prints information about the variables in SciTE console after they have been converted to variants. InspectSafeArray prints information about safearrays in SciTE console. Using the UDF Here are six small scripts which shows how to use the UDF. You can use these scripts as templates for your own code. The scripts are saved in "Examples\Demo examples\4) Other demo examples\6) Using the UDF\". The first four scripts is about creating and filling a new array. The array is in all cases a 1D-array with 2^24 (16,777,216) integer elements. In Example1.au3 an AutoIt integer structure is filled with data, the data in the structure is copied into a safearray which is converted to a native AutoIt array: ;#AutoIt3Wrapper_UseX64=y #include "..\..\..\..\Includes\AccVarsUtilities.au3" #include "..\..\..\..\Includes\ArrayDisplayEx.au3" #include "..\..\..\..\Includes\FasmUtils.au3" Opt( "MustDeclareVars", 1 ) Example1() Func Example1() Local $hTimer = TimerInit() ; --- Create and fill structure of integers --- ; Create structure of integers Local $tIntegers = DllStructCreate( "int[" & 2^24 & "]" ) ; 2^24 = 16,777,216 elements Local $pIntegers = DllStructGetPtr( $tIntegers ) ; AutoIt code ;For $i = 0 To 2^24 - 1 ; DllStructSetData( $tIntegers, 1, $i, $i + 1 ) ;Next ; <<<< Execute your assembler, C, C++, C# or FreeBasic code at this point >>>> ; Get fasm code Local $sFasmCode = @AutoItX64 ? "0xB80000000089024883C204FFC0E2F6C3" _ ; Example1-x64.asm : "0x5589E58B4D088B550CB800000000890283C20440E2F85DC20800" ; Example1-x86.asm Local $pFasmCode = FasmGetBinaryString( $sFasmCode, 64 ) If Not $pFasmCode Then Return ConsoleWrite( "$pFasmCode ERR" & @CRLF ) ; Execute fasm code to fill structure DllCallAddress( "int", $pFasmCode, "int", 2^24, "ptr", $pIntegers ) ; --- Create and fill safearray --- ; Create safearray Local $tSafeArrayBound = DllStructCreate( $tagSAFEARRAYBOUND ) DllStructSetData( $tSafeArrayBound, "cElements", 2^24 ) ; Number of elements in array DllStructSetData( $tSafeArrayBound, "lLbound", 0 ) ; Lower bound of array index Local $pSafeArray = SafeArrayCreate( $VT_I4, 1, $tSafeArrayBound ) ; Pointer to data Local $pSafeArrayData SafeArrayAccessData( $pSafeArray, $pSafeArrayData ) ; Create structure from safearray data area Local $tSafeArrayBytes = DllStructCreate( "byte[" & 4 * 2^24 & "]", $pSafeArrayData ) ; Fill safearray data area with data from $tIntegers DllStructSetData( $tSafeArrayBytes, 1, DllStructGetData( DllStructCreate( "byte[" & 4 * 2^24 & "]", $pIntegers ), 1 ) ) ; This technique only works with byte's, char's and wchar's SafeArrayUnaccessData( $pSafeArray ) $tIntegers = 0 ; --- Convert safearray to native AutoIt array --- Local $aArray AccVars_SafeArrayToArray( $pSafeArray, $aArray ) ConsoleWrite( "Time to fill array: " & TimerDiff( $hTimer ) & @CRLF ) _ArrayDisplayEx( $aArray ) $aArray = 0 EndFunc ; Time to fill array: 939.174260660843 This example is simple because you're not dealing with variants or safearrays at all in your assembler, C, C++, C# or FreeBasic code. In Example2.au3 the data area of a safearray is directly filled with integers and the safearray is converted to a native AutoIt array: ;#AutoIt3Wrapper_UseX64=y #include "..\..\..\..\Includes\AccVarsUtilities.au3" #include "..\..\..\..\Includes\ArrayDisplayEx.au3" #include "..\..\..\..\Includes\FasmUtils.au3" Opt( "MustDeclareVars", 1 ) Example2() Func Example2() Local $hTimer = TimerInit() ; --- Create and fill safearray --- ; Create safearray Local $tSafeArrayBound = DllStructCreate( $tagSAFEARRAYBOUND ) DllStructSetData( $tSafeArrayBound, "cElements", 2^24 ) ; Number of elements in array DllStructSetData( $tSafeArrayBound, "lLbound", 0 ) ; Lower bound of array index Local $pSafeArray = SafeArrayCreate( $VT_I4, 1, $tSafeArrayBound ) ; Pointer to data Local $pSafeArrayData SafeArrayAccessData( $pSafeArray, $pSafeArrayData ) ; AutoIt code ;For $i = 0 To 2^24 - 1 ; DllStructSetData( $tSafeArrayData, 1, $i, $i + 1 ) ;Next ; <<<< Execute your assembler, C, C++, C# or FreeBasic code at this point >>>> ; Get fasm code Local $sFasmCode = @AutoItX64 ? "0xB80000000089024883C204FFC0E2F6C3" _ ; Example1-x64.asm : "0x5589E58B4D088B550CB800000000890283C20440E2F85DC20800" ; Example1-x86.asm Local $pFasmCode = FasmGetBinaryString( $sFasmCode, 64 ) If Not $pFasmCode Then Exit ConsoleWrite( "$pFasmCode ERR" & @CRLF ) ; Execute fasm code to fill safearray DllCallAddress( "int", $pFasmCode, "int", 2^24, "ptr", $pSafeArrayData ) SafeArrayUnaccessData( $pSafeArray ) ; --- Convert safearray to native AutoIt array --- Local $aArray AccVars_SafeArrayToArray( $pSafeArray, $aArray ) ConsoleWrite( "Time to fill array: " & TimerDiff( $hTimer ) & @CRLF ) _ArrayDisplayEx( $aArray ) $aArray = 0 EndFunc ; Time to fill array: 805.347826773543 Example2 is more efficient than Example1 because you're avoiding the integer structure. The conversion in Example3.au3 is not performed with AccVars_SafeArrayToArray but with your own SafeArrayToArray method function which is executed through AccessVariables02: ;#AutoIt3Wrapper_UseX64=y #include "..\..\..\..\Includes\AccVarsUtilities.au3" #include "..\..\..\..\Includes\ArrayDisplayEx.au3" #include "..\..\..\..\Includes\FasmUtils.au3" Opt( "MustDeclareVars", 1 ) Example3() Func Example3() Local $hTimer = TimerInit() ; --- Create and fill safearray --- ; Create safearray Local $tSafeArrayBound = DllStructCreate( $tagSAFEARRAYBOUND ) DllStructSetData( $tSafeArrayBound, "cElements", 2^24 ) ; Number of elements in array DllStructSetData( $tSafeArrayBound, "lLbound", 0 ) ; Lower bound of array index Local $pSafeArray = SafeArrayCreate( $VT_I4, 1, $tSafeArrayBound ) ; Pointer to data Local $pSafeArrayData SafeArrayAccessData( $pSafeArray, $pSafeArrayData ) ; AutoIt code ;For $i = 0 To 2^24 - 1 ; DllStructSetData( $tSafeArrayData, 1, $i, $i + 1 ) ;Next ; <<<< Execute your assembler, C, C++, C# or FreeBasic code at this point >>>> ; Get fasm code Local $sFasmCode = @AutoItX64 ? "0xB80000000089024883C204FFC0E2F6C3" _ ; Example1-x64.asm : "0x5589E58B4D088B550CB800000000890283C20440E2F85DC20800" ; Example1-x86.asm Local $pFasmCode = FasmGetBinaryString( $sFasmCode, 64 ) If Not $pFasmCode Then Exit ConsoleWrite( "$pFasmCode ERR" & @CRLF ) ; Execute fasm code to fill safearray DllCallAddress( "int", $pFasmCode, "int", 2^24, "ptr", $pSafeArrayData ) SafeArrayUnaccessData( $pSafeArray ) ; --- Convert safearray to native AutoIt array --- Local $aArray AccessVariables02( SafeArrayToArray, $pSafeArray, $aArray ) ConsoleWrite( "Time to fill array: " & TimerDiff( $hTimer ) & @CRLF ) _ArrayDisplayEx( $aArray ) $aArray = 0 EndFunc Func SafeArrayToArray( $pSafeArray, $pArray ) ; --- Set $pArray to match an AutoIt array --- ; Set vt element to $VT_ARRAY + $VT_VARIANT DllStructSetData( DllStructCreate( "word", $pArray ), 1, $VT_ARRAY + $VT_VARIANT ) ; Set data element to safearray pointer DllStructSetData( DllStructCreate( "ptr", $pArray + 8 ), 1, AccVars_VariantToVariable( $pSafeArray ) ) ; <<<< On function exit $pArray (safearray contained in a variant) is converted to an AutoIt array >>>> EndFunc ; Time to fill array: 800.813455228795 Example3 is a more general example than Example2. Creation of the safearray is moved from AutoIt code to your assembler, C, C++, C# or FreeBasic code in Example4.au3: ;#AutoIt3Wrapper_UseX64=y #include "..\..\..\..\Includes\AccessingVariables.au3" #include "..\..\..\..\Includes\ArrayDisplayEx.au3" #include "..\..\..\..\Includes\FasmUtils.au3" Opt( "MustDeclareVars", 1 ) Example4() Func Example4() ; --- Create and fill array --- Local $aArray Local $hTimer = TimerInit() AccessVariables01( CreateArray, $aArray ) ConsoleWrite( "Time to fill array: " & TimerDiff( $hTimer ) & @CRLF ) _ArrayDisplayEx( $aArray ) $aArray = 0 EndFunc Func CreateArray( $pArray ) ; --- Create and fill safearray --- ; <<<< Execute your assembler, C, C++, C# or FreeBasic code at this point >>>> ; Create safearray Local $tSafeArrayBound = DllStructCreate( $tagSAFEARRAYBOUND ) DllStructSetData( $tSafeArrayBound, "cElements", 2^24 ) ; Number of elements in array DllStructSetData( $tSafeArrayBound, "lLbound", 0 ) ; Lower bound of array index Local $pSafeArray = SafeArrayCreate( $VT_I4, 1, $tSafeArrayBound ) ; Pointer to data Local $pSafeArrayData SafeArrayAccessData( $pSafeArray, $pSafeArrayData ) ; AutoIt code ;For $i = 0 To 2^24 - 1 ; DllStructSetData( $tSafeArrayData, 1, $i, $i + 1 ) ;Next ; Get fasm code Local $sFasmCode = @AutoItX64 ? "0xB80000000089024883C204FFC0E2F6C3" _ ; Example1-x64.asm : "0x5589E58B4D088B550CB800000000890283C20440E2F85DC20800" ; Example1-x86.asm Local $pFasmCode = FasmGetBinaryString( $sFasmCode, 64 ) If Not $pFasmCode Then Exit ConsoleWrite( "$pFasmCode ERR" & @CRLF ) ; Execute fasm code to fill safearray DllCallAddress( "int", $pFasmCode, "int", 2^24, "ptr", $pSafeArrayData ) SafeArrayUnaccessData( $pSafeArray ) ; --- Set $pArray to match an AutoIt array --- ; Set vt element to $VT_ARRAY + $VT_VARIANT DllStructSetData( DllStructCreate( "word", $pArray ), 1, $VT_ARRAY + $VT_VARIANT ) ; Set data element to safearray pointer DllStructSetData( DllStructCreate( "ptr", $pArray + 8 ), 1, $pSafeArray ) ; <<<< On function exit $pArray (safearray contained in a variant) is converted to an AutoIt array >>>> EndFunc ; Time to fill array: 799.20854202792 If you code in assembler it's more advantageous to create the safearray in AutoIt code. The last two scripts is about manipulating an existing array. In both scripts a 1D-array with 2^23 (8,388,608) random integers is searched to find the minimum and maximum value. In Example5.au3 AccVars_ArrayToSafeArray is used to convert the AutoIt array to a safearray: ;#AutoIt3Wrapper_UseX64=y #include "..\..\..\..\Includes\AccVarsUtilities.au3" ;#include "..\..\..\..\Includes\InspectVariable.au3" #include "..\..\..\..\Includes\ArrayDisplayEx.au3" #include "..\..\..\..\Includes\FasmUtils.au3" Opt( "MustDeclareVars", 1 ) Example5() Func Example5() ; --- Create test array of random integers --- ConsoleWrite( "Create test array (~10 seconds) ..." & @CRLF ) Local $aArray[2^23] ; 2^23 = 8,388,608 For $i = 0 To 2^23 - 1 $aArray[$i] = Random( 0, 2^31-1, 1 ) Next _ArrayDisplayEx( $aArray ) ; --- Convert native AutoIt array to safearray --- Local $hTimer = TimerInit() Local $pSafeArray AccVars_ArrayToSafeArray( $aArray, $pSafeArray ) ;InspectSafeArray( $pSafeArray ) ; Pointer to data Local $pSafeArrayData SafeArrayAccessData( $pSafeArray, $pSafeArrayData ) ; --- Test safearray for min/max values --- ; AutoIt code ;Local $iMin1 = 2^31-1, $iMax1 = 0 ;For $i = 0 To 2^23 - 1 ; If $aArray[$i] < $iMin1 Then $iMin1 = $aArray[$i] ; If $aArray[$i] > $iMax1 Then $iMax1 = $aArray[$i] ;Next ; <<<< Execute your assembler, C, C++, C# or FreeBasic code at this point >>>> ; $iMin, $iMax storage for fasm code Local $tMinMax = DllStructCreate( "int iMin;int iMax" ) DllStructSetData( $tMinMax, "iMin", 2^31-1 ) DllStructSetData( $tMinMax, "iMax", 0 ) Local $pMinMax = DllStructGetPtr( $tMinMax ) ; iMin = $pMinMax, iMax = $pMinMax + 4 (int = 4 bytes) ; Get fasm code Static $sFasmCode = @AutoItX64 ? "0x4D8B4804418B003B420876038B4208443B4A087304448B4A084883C218E2E841890045894804C3" _ ; Example5-x64.asm : "0x5589E58B4D088B550C8B45108B58048B003B420876038B42083B5A0873038B5A0883C210E2EB8B55108902895A045DC20C00" ; Example5-x86.asm Static $pFasmCode = FasmGetBinaryString( $sFasmCode, 64 ) If Not $pFasmCode Then Exit ConsoleWrite( "$pFasmCode ERR" & @CRLF ) ; Execute fasm code DllCallAddress( "int", $pFasmCode, "int", 2^23, "ptr", $pSafeArrayData, "ptr", $pMinMax ) ConsoleWrite( "$iMin, $iMax = " & DllStructGetData( $tMinMax, "iMin" ) & ", " & DllStructGetData( $tMinMax, "iMax" ) & @CRLF ) ConsoleWrite( "Time to test array: " & TimerDiff( $hTimer ) & @CRLF ) SafeArrayDestroy( $pSafeArray ) $aArray = 0 EndFunc ; $iMin, $iMax = 262, 2147483599 ; Time to test array: 2293.62874670214 In Example6.au3 the conversion is done in your own TestArray method function which is executed through AccessVariables03: ;#AutoIt3Wrapper_UseX64=y #include "..\..\..\..\Includes\AccVarsUtilities.au3" #include "..\..\..\..\Includes\ArrayDisplayEx.au3" #include "..\..\..\..\Includes\FasmUtils.au3" Opt( "MustDeclareVars", 1 ) Example6() Func Example6() ; --- Create test array of random integers --- ConsoleWrite( "Create test array (~10 seconds) ..." & @CRLF ) Local $aArray[2^23] ; 2^23 = 8,388,608 For $i = 0 To 2^23 - 1 $aArray[$i] = Random( 0, 2^31-1, 1 ) Next _ArrayDisplayEx( $aArray ) ; --- Test array for min/max values --- ; AutoIt code ;Local $iMin1 = 2^31-1, $iMax1 = 0 ;For $i = 0 To 2^23 - 1 ; If $aArray[$i] < $iMin1 Then $iMin1 = $aArray[$i] ; If $aArray[$i] > $iMax1 Then $iMax1 = $aArray[$i] ;Next Local $hTimer = TimerInit() Local $iMin = 2^31-1, $iMax = 0 AccessVariables03( TestArray, $aArray, $iMin, $iMax ) ConsoleWrite( "$iMin, $iMax = " & $iMin & ", " & $iMax & @CRLF ) ConsoleWrite( "Time to test array: " & TimerDiff( $hTimer ) & @CRLF ) $aArray = 0 EndFunc Func TestArray( $pvArray, $pvMin, $pvMax ) ; <<<< On function entry $aArray is converted to $pvArray (safearray contained in a variant) >>>> ; Pointer to safearray Local $pData = $pvArray + 8 Local $tData = DllStructCreate( "ptr", $pData ) Local $pSafeArray = DllStructGetData( $tData, 1 ) ; Pointer to data Local $pSafeArrayData SafeArrayAccessData( $pSafeArray, $pSafeArrayData ) ; <<<< Execute your assembler, C, C++, C# or FreeBasic code at this point >>>> ; $iMin, $iMax storage for fasm code Local $tMinMax = DllStructCreate( "int iMin;int iMax" ) DllStructSetData( $tMinMax, "iMin", AccVars_VariantToVariable( $pvMin ) ) DllStructSetData( $tMinMax, "iMax", AccVars_VariantToVariable( $pvMax ) ) Local $pMinMax = DllStructGetPtr( $tMinMax ) ; iMin = $pMinMax, iMax = $pMinMax + 4 (int = 4 bytes) ; Get fasm code Static $sFasmCode = @AutoItX64 ? "0x4D8B4804418B003B420876038B4208443B4A087304448B4A084883C218E2E841890045894804C3" _ ; Example5-x64.asm : "0x5589E58B4D088B550C8B45108B58048B003B420876038B42083B5A0873038B5A0883C210E2EB8B55108902895A045DC20C00" ; Example5-x86.asm Static $pFasmCode = FasmGetBinaryString( $sFasmCode, 64 ) If Not $pFasmCode Then Exit ConsoleWrite( "$pFasmCode ERR" & @CRLF ) ; Execute fasm code DllCallAddress( "int", $pFasmCode, "int", 2^23, "ptr", $pSafeArrayData, "ptr", $pMinMax ) ; Get $iMin, $iMax AccVars_VariableToVariant( DllStructGetData( $tMinMax, "iMin" ), $pvMin ) AccVars_VariableToVariant( DllStructGetData( $tMinMax, "iMax" ), $pvMax ) SafeArrayUnaccessData( $pSafeArray ) EndFunc ; $iMin, $iMax = 39, 2147483610 ; Time to test array: 2182.54509368366 Examples There are two obvious uses for this: To create and fill a new array. And to manipulate one or more existing arrays. Demo examples "Examples\Demo examples\" contains a few simple demonstration examples. 1) Create and fill new array There are already examples above where a 1D-array is filled with integers. As a demonstration example of creating and filling out an array, a little more complex 2D-array is filled with integers. Example2.au3 is an optimized version of Example1.au3. See post 6. 2) Manipulate existing array As a demonstration example of manipulating a single existing array, minimum and maximum values are determined in a 1D-array of random integers. This is the same as Example5 and Example6 above. See post 6. 3) Concatenate 1D-arrays As a demonstration example of manipulating several existing arrays 2, 8 and 16 1D-arrays of integers with 2^20 (1,048,576) rows are concatenated into a 2D-array with 2, 8 and 16 columns. See post 6. In all three demonstration examples flat assembler (fasm) is used as the fast language to manipulate the arrays. Note that in the three examples the pure AutoIt code is doing relatively well compared to the fasm code. The reason is that the code in the array loops is very simple. For more complex code the AutoIt code will not do so well. Assembler code flat assembler (fasm) is available as a DLL-file. This means that all you need to create fasm code is this DLL and the DllCall command. DllCall is a native AutoIt command. The DLL-file is a 32 bit DLL, but it can generate both 32 and 64 bit fasm code. Furthermore, the assembled code (the executable machine code) is very compact. None of the example programs are larger than 64 bytes. There are several threads in these forums regarding fasm and development of fasm code. And there are threads in the German forums regarding fasm. For the example programs included here I'm just using the DLL-file, DllCall and a few AutoIt functions to make things easier. Nothing of this is included in the zip. Other demo examples These are small examples I used while I developed and tested the UDF. The examples are stored in "Examples\Demo examples\4) Other demo examples\". Create and fill array\ contains the the same examples as "Tests\Examples\3) Internal conversions\". Multiple parameters\ tests all 30 AccessVariablesXY functions. The functions are tested with simple variables where some variables are added and the result is stored in another variable. Example1.au3. Inside the AccessVariablesXY functions the variables are true variants. This is demonstrated by adding the variables with VarAdd API function. Example2.au3. Safearray dimensions\ shows how to handle safearrays of 1, 2 and 3 dimensions. ArrayToSafearray\ and SafearrayToArray\ is about the AccVars_ArrayToSafeArray and AccVars_SafeArrayToArray utility functions. Using the UDF\ contains the six examples in the previous section. Real examples The examples of fasm code that I've used for tests and demonstrations are almost too small to be realistic examples. But so far I've not finalized any more realistic examples. I'll add some examples when they are completed. Zip file At the top level the zip contains the following folders and files: Examples\ - Examples main section Includes\ - Final UDF main section Tests\ - The first four sections Example.au3 - Example in top of post Example-x64.asm - 64 bit fasm code Example-x86.asm - 32 bit fasm code You need AutoIt 3.3.10 or later. Tested on Windows 10/7 32/64 bit and Windows XP 32 bit. If you have less than 4GB of RAM in your PC, you should reduce the number of array rows by a factor of four (change 2^24, 2^23, 2^20 to 2^22, 2^21, 2^18 in .au3-files) in examples with fasm code. Comments are welcome. Let me know if there are any issues. AccessingVariables.7z1 point
-
Ok nearly. If you use FileSelectFolder you will always only get folders. So you don't have to check whether it has the attribute "D". What you want to know is if the folder is hidden (means attribute "H"). So change in line 2 "D" with "H" and it should work. Conrad1 point
-
You have to change +H and -H: $aFileFolder = FileOpenDialog("Test", @ScriptDir & "\", "All (*)") If StringInStr(FileGetAttrib($aFileFolder),"H") Then FileSetAttrib($aFileFolder, "-H", 1) Else FileSetAttrib($aFileFolder, "+H", 1) EndIf If attribute is "H" then it is hidden and you want to unhide it so set new attribute to "-H". Otherwise set it hidden with "+H" Conrad1 point
-
Edit Box with "Save" and "Cancel"
nacerbaaziz reacted to XanzyX for a topic
It took a while, but it was done. The edit area is rough but ot does the job Func Example2($GUIEditTitle, $FileName) GUICreate($GUIEditTitle) $Save_BT = GUICtrlCreateButton("Save", 245, 365, 70, 30) $Cancel_BT = GUICtrlCreateButton("Cancel", 325, 365, 70, 30) $MyEdit = GUICtrlCreateEdit(FileRead($FileName), 5, 5, 385, 340, $ES_AUTOVSCROLL + $WS_VSCROLL + $ES_AUTOVSCROLL + $ES_AUTOHSCROLL + $ES_MULTILINE) GUISetState(@SW_SHOW) While 1 $nMsg = GUIGetMsg() Switch $nMsg Case $Save_BT ... Save Condition ExitLoop Case $Cancel_BT ... Cancel Condition ExitLoop Case $GUI_EVENT_CLOSE ExitLoop EndSwitch WEnd GUIDelete($GUIEditTitle) EndFunc ;==>Example1 point -
libcpuid provides CPU identification for the x86. It wraps the CPUID and RDTSC instructions in a portable manner and provides a lot of technical info about the processor, such as vendor, core codename, features/instruction sets, cache sizes, and more. libcpuid Web Site http://libcpuid.sourceforge.net/ libcpuid.au3 and libcpuid.dll in attached arhive libcpuid_030.zip1 point
-
Advanced DllCall and DllStruct AutoIt Code Generator BETA v0.6
robertocm reacted to tarretarretarre for a topic
Hello fellas! The other night night i was converting a Msdn function to autoit and I stumbled across this topic Which inspired me like crazy and I decided to take it a step further and require the user to make almost ZERO effort to export a c++ Msdn function and or a Structure to AutoIt Shoutout to toasterking So after 18-20 effective hours: The GUI is really simple, all you need is a link to a MSDN page and the program does the rest, most of the options is just for user preferences. On the inside I have spent a decent amount of work to make sure the code come out correctly, any particular event during the conversion will get fed-back to the user, so he or she will know if anything noticeable happen. Regular DllCall example http://i.imgur.com/HZLijeu.png Struct example http://i.imgur.com/l3j6wTR.png Expand spoiler for more pictures In the "Msdn Examples" folder you will find some examples of code i have generated, in most of them I only manually added 2-3 lines to make them work. If you dont know where to get these functions you can browse the MSDN Library https://msdn.microsoft.com/en-us/library/ee663300(v=vs.85).aspx and look for any function refrence, or just google "somethingsomething msdn" and the first result will almost always contain the function you are looking for. Here is some functions you can play around with https://msdn.microsoft.com/en-us/library/windows/desktop/ms724390(v=vs.85).aspx https://msdn.microsoft.com/en-us/library/windows/desktop/ms633519(v=vs.85).aspx https://msdn.microsoft.com/en-us/library/windows/desktop/ms645505(v=vs.85).aspx https://msdn.microsoft.com/en-us/library/windows/desktop/ms724408(v=vs.85).aspx I would really appreciate any kind of feedback, improvements or requests If you get any type of error just post the MSDN url + the error message and ID and I will troubleshoot it. Update 0.2 Fixed some minor issues Added highlight for a more pleasent view Fixed minor bugs Made it run faster when working with the same URL (It dosent load the page entierly) No struct search is now done when no POINTER is used in the call Added more options for the user Update 0.3 Removed _IeNavigate and fixed the template for DllCall not including function name Update 0.4 Switched method to InetGet from _Ie* H0tfix3s Update 0.5 Added more options for function-layout Removed old code Added more auto detection Now using @TmpDir instead of @ScriptDir for html files etc. Update 0.6 More Output logic added Added a detection for SAL aswell, since it seems to be inconsistent according to MSDN community and myself. Better feedback on what happend with parse Code cleanup / Removed old code /Tarre DllCall and Struct Generator V 0.6.zip DllCall and Struct Generator V 0.5.zip DllCall and Struct Generator V 0.4.zip1 point -
Here is a preliminary Shellbag parser for desktop icons I used information found at http://www.williballenthin.com/forensics/shellbags/, http://forensicswiki.org/wiki/Shell_Item#Format, and https://github.com/libyal/libfwsi/blob/master/documentation/Windows%20Shell%20Item%20format.asciidoc So far This only works with the desktop ShItem Specs although, I admit this is the only one I tried it with. The code makes a file called data.txt containing the data from the registry key and a file with the name 'RegKeyValNameXXXXxyyyy'_FileData for each ItemPos Value found The Information is returned in an INI file With [SHITEM#] as the section name This Code is not commented very well yet nor is it a final version Please let me know what kind of errors you find and Post the improvements you make. Sample Entry For Computer: [SHITEM0] FileStart= 0x0000000000000010 FileEnd= 0x000000000000002C RecordLen= 28 FilePtr= 16 SHIconX= 36 SHIconY= -1 Size= 20 Flags= 501F GUID= 20D04FE0-3AEA-1069-A2D8-08002B30309D Shortname= Computer Longname= Computer Sample Entry for Google Chrome [SHITEM3] FileStart= 0x00000000000000C4 FileEnd= 0x000000000000013A RecordLen= 118 FilePtr= 196 SHIconX= 36 SHIconY= 368 Size= 108 Flags= 003A FileSize= 2193 ModifiedDate= 02/19/2016 ModifiedTime= 22:56.10 FileAttribs= 8224 Shortname= GOOGLE~1.LNK ExtSize= 80 ExtVer= 8 ExtSIG= 0xBEEF0004 CreatedDate= 09/26/2014 CreatedTime= 03:09.38 AccessDate= 09/26/2014 AccessTime= 03:09.38 Unknown2= 42 64FileRef= 000300000000F689 Unknown3= 0 LongNameSize= 0 Unknown4= 0 LongName= Google Chrome.lnk LongNameAddl= Unknown5= 0024001C The Code #include <WinAPIFiles.au3> ;FileOpen Opt("MustDeclareVars", 1) Local $sSubKey = "" Local $aRegKeys[30] Local $iRegCount = 0 For $i = 1 To 30 $sSubKey = RegEnumVal("HKEY_CURRENT_USER\Software\Microsoft\Windows\Shell\Bags\1\Desktop\", $i) If @error = -1 Then ExitLoop If StringInStr($sSubKey, "ItemPos", 0) Then $aRegKeys[$iRegCount + 1] = $sSubKey $iRegCount += 1 EndIf Next $aRegKeys[0] = $iRegCount Local $sFilePath = "" Local $PathResultsIni = "" Local $sRegEntry = "" For $i = 1 To $aRegKeys[0] $sSubKey = $aRegKeys[$i] $sRegEntry = RegRead("HKEY_CURRENT_USER\Software\Microsoft\Windows\Shell\Bags\1\Desktop", $sSubKey) If Not (@extended = 3) Then MsgBox(0, "Incorrect Key Type", $sSubKey & @CRLF & "Key Not REG_BINARY ") ;Const $REG_BINARY = 3 EndIf If Not (StringLeft($sRegEntry, 34) = "0x00000000000000000000000000000000") Then MsgBox(0, "Incorrect File Header", $sSubKey & @CRLF & "Header does not match ShItem Header " & @CRLF & StringLeft($sRegEntry, 32) & @CRLF & "0x00000000000000000000000000000000") EndIf $sFilePath = @ScriptDir & "\RegData.txt" $PathResultsIni = @ScriptDir & "\" & $sSubKey & "_" & "FileData.Ini" FileDelete($PathResultsIni) FileDelete($sFilePath) If Not FileWrite($sFilePath, $sRegEntry) Then ; Create a file to read data from. MsgBox("0", @ScriptName, "An error occurred while writing the temporary data file.") EndIf $sRegEntry = "" $iRegCount = ParseSH_ItemToIni($sFilePath, $PathResultsIni) $aRegKeys[$i] = $iRegCount & " Entries saved to:" & @CRLF & $PathResultsIni Next $sSubKey = "" For $i = 1 To $aRegKeys[0] $sSubKey &= $aRegKeys[$i] & @CRLF Next MsgBox(0, @ScriptName & " Finished Parsing", $sSubKey) ;------------------------------------------------------------------- Func ParseSH_ItemToIni($sFilePath, $PathResultsIni) Local Const $hFile = FileOpen($sFilePath, $FO_BINARY + $FO_FULLFILE_DETECT) Local $iFilePos0 = 0 ;File Pointer FileSetPos($hFile, 0, 2) Local $iFileLast = FileGetPos($hFile) ;End Of File FileSetPos($hFile, $iFilePos0, 0) Local $iFileStart = 0, $FileEnd = 0, $iRecordStart = 0, $iRecordNumber = 0, $iRecordEnd Local $iSHIconX = 0, $iSHIconY = 0 Local $iSize = 0, $iFlags = 0, $iSortOrderIndex = 0 ;added Local $sGUID = 0 Local $iFileSize = 0 Local $sModDate = "", $sModTime = "" Local $iFileAttribs = 0 Local $sShortname = "" Local $iExtSize = 0, $iExtVer = 0 Local $iExtSig = 0 Local $iUnknown0 = 0, $iUnknown1 = 0 Local $sCreDate = 0, $sCreTime = 0 Local $sAccDate = 0, $sAccTime = 0 Local $iUnknown2 = 0 Local $i64FileRef = 0 Local $iUnknown3 = 0 Local $iLongNameSize = 0 Local $iUnknown4 = 0 Local $sLongName = "", $sLongNameAddl = "" Local $iUnknown5 = 0 For $iPos = 0 To $iFileLast - 2 ;Step 2 Removed causes the loss of the last item ;Set all fields to default Values $iFileStart = 0 $FileEnd = 0 $iRecordStart = 0 $iSHIconX = 0 $iSHIconY = 0 $iSize = 0 $iFlags = 0 $iSortOrderIndex = 0 ;added $sGUID = 0 $iFileSize = 0 $sModDate = "" $sModTime = "" $iFileAttribs = 0 $sShortname = "" $iExtSize = 0 $iExtVer = 0 $iExtSig = 0 $sCreDate = 0 $sCreTime = 0 $sAccDate = 0 $sAccTime = 0 $iUnknown2 = 0 $i64FileRef = 0 $iUnknown3 = 0 $iLongNameSize = 0 $iUnknown4 = 0 $sLongName = "" $sLongNameAddl = "" $iUnknown5 = 0 ;$iUnknown0 = 0 Replaced By $iExtSig ;$iUnknown1 = 0 Replaced By $iExtSig $iFileStart = FileGetPos($hFile) $iSHIconX = ReadRegHexStringValue($hFile, 32, "INT") $iSHIconY = ReadRegHexStringValue($hFile, 32, "INT") $iRecordStart = FileGetPos($hFile) $iSize = ReadRegHexStringValue($hFile, 16, "INT") $iFlags = ReadRegHexStringValue($hFile, 8, "INT") ;16 changed to 8 $iSortOrderIndex = ReadRegHexStringValue($hFile, 8, "INT") ;Added If $iSize <= 21 Then If Not (BitAND($iFlags, 0x00FF) = 0x1F) Then $iFilePos0 += 2 FileSetPos($hFile, $iFilePos0, 0) ContinueLoop ElseIf $iSize > 15 And $iSortOrderIndex > 0 Then $sGUID = ReadRegHexStringValue($hFile, 128, "GUID") $sShortname = RegRead("HKEY_CLASSES_ROOT\CLSID\{" & $sGUID & "}", "") If Not (@error) Then $iRecordEnd = FileGetPos($hFile) ConsoleWrite(@CRLF & "[SHITEM" & $iRecordNumber & "] " & $sShortname & ":" & $iExtSig & ",") FileWrite($PathResultsIni, _ @CRLF & "[SHITEM" & $iRecordNumber & "]" & _ @CRLF & "FileStart= 0x" & Hex($iFileStart, 16) & _ @CRLF & "FileEnd= 0x" & Hex($iRecordEnd, 16) & _ @CRLF & "RecordLen= " & ($iRecordEnd - $iFileStart) & _ @CRLF & "FilePtr= " & ($iFilePos0) & _ ; the second part of FilePtr is SortOrderIndex @CRLF & "SHIconX= " & $iSHIconX & _ @CRLF & "SHIconY= " & $iSHIconY & _ @CRLF & "Size= " & $iSize & _ @CRLF & "Flags= " & Hex($iFlags, 2) & _ @CRLF & "SortOrderIndex= " & $iSortOrderIndex & _ @CRLF & "GUID= " & $sGUID & _ @CRLF & "Shortname= " & $sShortname & _ @CRLF & "Longname= " & $sShortname & @CRLF & @CRLF) $iRecordNumber += 1 $iFilePos0 = FileGetPos($hFile) Else $iFilePos0 += 2 FileSetPos($hFile, $iFilePos0, 0) MsgBox(0, "INVALID GUID", "Invalid GUID encountered", 10) EndIf ContinueLoop EndIf EndIf $iFileSize = ReadRegHexStringValue($hFile, 32, "INT") $sModDate = ReadRegHexStringValue($hFile, 16, "DOSDATE") $sModTime = ReadRegHexStringValue($hFile, 16, "DOSTIME") $iFileAttribs = ReadRegHexStringValue($hFile, 16, "INT") $sShortname = ReadRegHexStringValue($hFile, $iSize, "String") $iExtSize = ReadRegHexStringValue($hFile, 16, "INT") $iExtVer = ReadRegHexStringValue($hFile, 16, "INT") $iExtSig = "0x" & Hex(ReadRegHexStringValue($hFile, 32, "INT")) ;msgbox(0,"",$iExtSig & " " & HEX(BitAND($iExtSig, 0xFFFF0000))) If ($iSize <= 21 Or Not (BitAND($iExtSig, 0xFFFF0000) = 0xBEEF0000) Or Not (BitAND($iFlags, 0x70)) = 0x30) Then $iFilePos0 += 2 FileSetPos($hFile, $iFilePos0, 0) ContinueLoop EndIf ;FileRead($hFile,2) If $iExtVer >= 0x0003 Then ;$iUnknown0 = ReadRegHexStringValue($hFile, 16, "INT") Replaced By $iExtSig ;$iUnknown1 = ReadRegHexStringValue($hFile, 16, "INT") Replaced By $iExtSig $sCreDate = ReadRegHexStringValue($hFile, 16, "DOSDATE") $sCreTime = ReadRegHexStringValue($hFile, 16, "DOSTIME") $sAccDate = ReadRegHexStringValue($hFile, 16, "DOSDATE") $sAccTime = ReadRegHexStringValue($hFile, 16, "DOSTIME") $iUnknown2 = ReadRegHexStringValue($hFile, 32, "INT") EndIf If $iExtVer >= 0x0007 Then $i64FileRef = Hex(ReadRegHexStringValue($hFile, 64, "INT"), 16) $iUnknown3 = ReadRegHexStringValue($hFile, 64, "INT") $iLongNameSize = ReadRegHexStringValue($hFile, 16, "INT") If $iExtVer <= 0x0008 Then ;Changed to <= from >= Not working right otherwise $iUnknown4 = ReadRegHexStringValue($hFile, 32, "INT") EndIf $sLongName = ReadRegHexStringValue($hFile, 0, "WSTRING") If $iLongNameSize > 0 Then ;MsgBox(0, "$iLongNameSize", $iLongNameSize) $sLongNameAddl = ReadRegHexStringValue($hFile, $iLongNameSize + 2, "WString") EndIf ElseIf $iExtVer >= 0x0003 Then $iUnknown5 = ReadRegHexStringValue($hFile, 16, "INT") EndIf $iUnknown5 = ReadRegHexStringValue($hFile, 32, "HEX") $iRecordEnd = FileGetPos($hFile) If Not (($iRecordEnd - $iRecordStart) = ($iSize + 2)) Then $iFilePos0 += 2 FileSetPos($hFile, $iFilePos0, 0) ContinueLoop ElseIf ($iFilePos0 > $iFileLast - 4) Then ExitLoop ElseIf $iSize < $iFileLast Then ConsoleWrite(@CRLF & "[SHITEM" & $iRecordNumber & "] " & $sLongName & ":" & $iExtSig & ",") EndIf FileWrite($PathResultsIni, _ @CRLF & "[SHITEM" & $iRecordNumber & "]" & _ @CRLF & "FileStart= 0x" & Hex($iFileStart, 16) & @CRLF & "FileEnd= 0x" & Hex($iRecordEnd, 16) & _ @CRLF & "RecordLen= " & ($iRecordEnd - $iFileStart) & _ @CRLF & "FilePtr= " & ($iFilePos0) & _ @CRLF & "SHIconX= " & $iSHIconX & @CRLF & "SHIconY= " & $iSHIconY & _ @CRLF & "Size= " & $iSize & _ @CRLF & "Flags= " & Hex($iFlags, 2) & _ @CRLF & "SortOrderIndex= " & $iSortOrderIndex & _ ;added @CRLF & "FileSize= " & $iFileSize & _ @CRLF & "ModifiedDate= " & $sModDate & @CRLF & "ModifiedTime= " & $sModTime & _ @CRLF & "FileAttribs= " & $iFileAttribs & _ @CRLF & "Shortname= " & $sShortname & _ @CRLF & "ExtSize= " & $iExtSize & _ @CRLF & "ExtVer= " & $iExtVer & _ @CRLF & "ExtSIG= " & ($iExtSig) & _ @CRLF & "CreatedDate= " & $sCreDate & @CRLF & "CreatedTime= " & $sCreTime & _ @CRLF & "AccessDate= " & $sAccDate & @CRLF & "AccessTime= " & $sAccTime & _ @CRLF & "Unknown2= " & $iUnknown2 & _ @CRLF & "64FileRef= " & $i64FileRef & _ @CRLF & "Unknown3= " & $iUnknown3 & _ @CRLF & "LongNameSize= " & $iLongNameSize & _ @CRLF & "Unknown4= " & $iUnknown4 & _ @CRLF & "LongName= " & $sLongName & _ @CRLF & "LongNameAddl= " & $sLongNameAddl & _ @CRLF & "Unknown5= " & $iUnknown5 & @CRLF & @CRLF) ;@CRLF & "Unknown0= " & $iUnknown0 & _;@CRLF & "Unknown1= " & $iUnknown1 & _ (Replaced By $iExtSig) $iRecordNumber += 1 Next FileClose($hFile) Return $iRecordNumber EndFunc ;==>ParseSH_ItemToIni Func ReadRegHexStringValue(Const $hFile, $iLength, $sType) $sType = StringUpper($sType) Local $sData = "" If Not ($sType = "WSTRING" Or $sType = "STRING" Or $sType = "GUID") Then $iLength /= 8 $sData = FileRead($hFile, $iLength) EndIf Local $vReturn = "" Local $iCount = 0 Local $aTemp, $iTemp, $sTemp Switch $sType Case "STRING" $vReturn = "" For $i = 0 To 260 ;$iFilePos += 8 $sData = FileRead($hFile, 1) If $sData = Chr(00) Then If Not (FileRead($hFile, 1) = Chr(00)) Then FileSetPos($hFile, FileGetPos($hFile) - 1, 0) ;Sometimes Double Null Terminated ExitLoop EndIf $vReturn &= BinaryToString($sData) Next Case "WSTRING" $vReturn = "" For $i = 0 To 260 $sData = FileRead($hFile, 2) If $sData = Chr(00) & Chr(00) Then ExitLoop $vReturn &= BinaryToString($sData, $SB_UTF16LE) Next $aTemp = 0 Case "INT" $vReturn = Int($sData) Case "DOSDATE" $vReturn &= StringFormat("%02d/%02d/%04d", BitShift(BitAND($sData, 0x01e0), 5), BitAND($sData, 0x1F), BitShift($sData, 9) + 1980) Case "DOSTIME" ;UTC $vReturn &= StringFormat("%02d:%02d.%02d", BitShift($sData, 11), BitShift(BitAND($sData, 0x07e0), 5), BitAND($sData, 0x1F) * 2) Case "HEX" $vReturn = Hex(Int($sData), $iLength * 2) Case "HEXSTR" $vReturn = Hex($sData, $iLength * 2) Case "GUID" If $iLength = 128 Then $sData = FileRead($hFile, 4) $vReturn = Hex(Int($sData), 8) & "-" $sData = FileRead($hFile, 2) $vReturn &= Hex(Int($sData), 4) & "-" $sData = FileRead($hFile, 2) $vReturn &= Hex(Int($sData), 4) & "-" $sData = FileRead($hFile, 2) $vReturn &= Hex($sData, 2) & "-" $sData = FileRead($hFile, 6) $vReturn &= Hex($sData, 4) Else $vReturn = 0 EndIf #cs ;Old "GUID" ;= Hex(ReadRegHexStringValue($hFile, 32, "INT"), 8) & "-" & _ ;Hex(ReadRegHexStringValue($hFile, 16, "INT"), 4) & "-" & _ ;Hex(ReadRegHexStringValue($hFile, 16, "INT"), 4) & "-" & _ ;ReadRegHexStringValue($hFile, 16, "Hex") & "-" & _ ;ReadRegHexStringValue($hFile, 48, "HEX") #CE Case Else MsgBox(0, "ReadRegHexStringValue Error", "Datatype " & $sType & " not found.") EndSwitch Return $vReturn EndFunc ;==>ReadRegHexStringValue1 point
-
_IsUEFIBoot——Detemine the Current OS is boot in UEFI mode or not
coffeeturtle reacted to czyt for a topic
May it helps those who needed. #include <WinAPI.au3> ; #FUNCTION# ==================================================================================================================== ; Name ..........: _IsUEFIBoot ; Description ...: Detemine the Current OS is boot in UEFI mode or not ; Syntax ........: _IsUEFIBoot() ; Parameters ....: ; Return values .: True:OS is boot in UEFI ; False: OS is boot in Legacy BIOS ; Author ........: czyt ; Modified ......: ; Remarks .......: ; Related .......: http://techsupt.winbatch.com/webcgi/webbatch.exe?techsupt/nftechsupt.web+WinBatch/DllCall~Information+Booted~UEFI~or~BIOS.txt ; Link ..........: ; Example .......: MsgBox(0,'(*^__^*) The OS is in UEFI Boot mode?',_IsUEFIBoot()) ; =============================================================================================================================== #include <WinAPI.au3> Func _IsUEFIBoot() Local Const $ERROR_INVALID_FUNCTION = 0x1 Local $hDLL = DllOpen("Kernel32.dll") If @OSBuild > 8000 Then Local $aCall = DllCall($hDLL, "int", "GetFirmwareType", "int*", 0) DllClose($hDLL) If Not @error And $aCall[0] Then Switch $aCall[1] ; 1 - bios 2- uefi 3-unknown Case 2 Return True Case Else Return False EndSwitch EndIf Return False Else DllCall($hDLL, "dword", "GetFirmwareEnvironmentVariableW", "wstr", "", "wstr", '{00000000-0000-0000-0000-000000000000}', "wstr", Null, "dword", 0) DllClose($hDLL) If _WinAPI_GetLastError() = $ERROR_INVALID_FUNCTION Then Return False Else Return True EndIf EndIf EndFunc ;==>IsUEFIBoot updated at 2021-08-13: fixed check err on windows 8 above OS1 point -
Geir1983, You can get the size of the image and then create the Pic control to be the correct size: #include <GDIPlus.au3> Global $sFileName = "Image_Path" ; <<<<<<<<<<<<<<<<<<<<<<<<< Put your image path here ; Initialize GDI+ library _GDIPlus_Startup() ; Get bitmap $hBitmap = _GDIPlus_BitmapCreateFromFile($sFileName) ; Get size $iX = _GDIPlus_ImageGetWidth($hBitmap) $iY = _GDIPlus_ImageGetHeight($hBitmap) ; Display result MsgBox(0, "Size", $sFileName & @CRLF & @CRLF & $iX & " x " & $iY) ; Clean up resources _GDIPlus_BitmapDispose($hBitmap) ; Shut down GDI+ library _GDIPlus_Shutdown() I hope that helps. M231 point