Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 10/27/2019 in all areas

  1. LarsJ

    Variants and Safearrays

    This week there have been some posts regarding use of compiled code in AutoIt through dll files and through C# and VB code via .NET. Use of compiled code is based, among other things, on Variants and Safearrays. This is an opportunity to distribute the latest updated version of these UDFs: Variant.au3 and SafeArray.au3. Variants and Safearrays are also used in UI Automation threads (Framework, UIASpy, Using) and in AutoItObject. In addition, references to these UDFs can be found in quite a number of posts. If you search for the UDFs it can be difficult to find the newest version. Hence this thread. Variant.au3: #include-once ; >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> ; Copied from AutoItObject.au3 by the AutoItObject-Team: monoceres, trancexx, Kip, ProgAndy ; https://www.autoitscript.com/forum/index.php?showtopic=110379 Global Const $tagVARIANT = "word vt;word r1;word r2;word r3;ptr data; ptr" ; The structure takes up 16/24 bytes when running 32/64 bit ; Space for the data element at the end represents 2 pointers ; This is 8 bytes running 32 bit and 16 bytes running 64 bit Global Const $VT_EMPTY = 0 ; 0x0000 Global Const $VT_NULL = 1 ; 0x0001 Global Const $VT_I2 = 2 ; 0x0002 Global Const $VT_I4 = 3 ; 0x0003 Global Const $VT_R4 = 4 ; 0x0004 Global Const $VT_R8 = 5 ; 0x0005 Global Const $VT_CY = 6 ; 0x0006 Global Const $VT_DATE = 7 ; 0x0007 Global Const $VT_BSTR = 8 ; 0x0008 Global Const $VT_DISPATCH = 9 ; 0x0009 Global Const $VT_ERROR = 10 ; 0x000A Global Const $VT_BOOL = 11 ; 0x000B Global Const $VT_VARIANT = 12 ; 0x000C Global Const $VT_UNKNOWN = 13 ; 0x000D Global Const $VT_DECIMAL = 14 ; 0x000E Global Const $VT_I1 = 16 ; 0x0010 Global Const $VT_UI1 = 17 ; 0x0011 Global Const $VT_UI2 = 18 ; 0x0012 Global Const $VT_UI4 = 19 ; 0x0013 Global Const $VT_I8 = 20 ; 0x0014 Global Const $VT_UI8 = 21 ; 0x0015 Global Const $VT_INT = 22 ; 0x0016 Global Const $VT_UINT = 23 ; 0x0017 Global Const $VT_VOID = 24 ; 0x0018 Global Const $VT_HRESULT = 25 ; 0x0019 Global Const $VT_PTR = 26 ; 0x001A Global Const $VT_SAFEARRAY = 27 ; 0x001B Global Const $VT_CARRAY = 28 ; 0x001C Global Const $VT_USERDEFINED = 29 ; 0x001D Global Const $VT_LPSTR = 30 ; 0x001E Global Const $VT_LPWSTR = 31 ; 0x001F Global Const $VT_RECORD = 36 ; 0x0024 Global Const $VT_INT_PTR = 37 ; 0x0025 Global Const $VT_UINT_PTR = 38 ; 0x0026 Global Const $VT_FILETIME = 64 ; 0x0040 Global Const $VT_BLOB = 65 ; 0x0041 Global Const $VT_STREAM = 66 ; 0x0042 Global Const $VT_STORAGE = 67 ; 0x0043 Global Const $VT_STREAMED_OBJECT = 68 ; 0x0044 Global Const $VT_STORED_OBJECT = 69 ; 0x0045 Global Const $VT_BLOB_OBJECT = 70 ; 0x0046 Global Const $VT_CF = 71 ; 0x0047 Global Const $VT_CLSID = 72 ; 0x0048 Global Const $VT_VERSIONED_STREAM = 73 ; 0x0049 Global Const $VT_BSTR_BLOB = 0xFFF Global Const $VT_VECTOR = 0x1000 Global Const $VT_ARRAY = 0x2000 Global Const $VT_BYREF = 0x4000 Global Const $VT_RESERVED = 0x8000 Global Const $VT_ILLEGAL = 0xFFFF Global Const $VT_ILLEGALMASKED = 0xFFF Global Const $VT_TYPEMASK = 0xFFF ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ;Global Const $tagVARIANT = "word vt;word r1;word r2;word r3;ptr data; ptr" ; The structure takes up 16/24 bytes when running 32/64 bit ; Space for the data element at the end represents 2 pointers ; This is 8 bytes running 32 bit and 16 bytes running 64 bit #cs DECIMAL structure https://msdn.microsoft.com/en-us/library/windows/desktop/ms221061(v=vs.85).aspx From oledb.h: typedef struct tagDEC { USHORT wReserved; ; vt, 2 bytes union { ; r1, 2 bytes struct { BYTE scale; BYTE sign; }; USHORT signscale; }; ULONG Hi32; ; r2, r3, 4 bytes union { ; data, 8 bytes struct { #ifdef _MAC ULONG Mid32; ULONG Lo32; #else ULONG Lo32; ULONG Mid32; #endif }; ULONGLONG Lo64; }; } DECIMAL; #ce Global Const $tagDEC = "word wReserved;byte scale;byte sign;uint Hi32;uint Lo32;uint Mid32" ; >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> ; Variant functions ; Copied from AutoItObject.au3 by the AutoItObject-Team: monoceres, trancexx, Kip, ProgAndy ; https://www.autoitscript.com/forum/index.php?showtopic=110379 ; #FUNCTION# ==================================================================================================================== ; Name...........: VariantClear ; Description ...: Clears the value of a variant ; Syntax.........: VariantClear($pvarg) ; Parameters ....: $pvarg - the VARIANT to clear ; Return values .: Success - 0 ; Failure - nonzero ; Author ........: Prog@ndy ; Modified.......: ; Remarks .......: ; Related .......: VariantFree ; Link ..........: http://msdn.microsoft.com/en-us/library/ms221165.aspx ; Example .......: ; =============================================================================================================================== Func VariantClear($pvarg) ; Author: Prog@ndy Local $aCall = DllCall("OleAut32.dll", "long", "VariantClear", "ptr", $pvarg) If @error Then Return SetError(1, 0, 1) Return $aCall[0] EndFunc ; #FUNCTION# ==================================================================================================================== ; Name...........: VariantCopy ; Description ...: Copies a VARIANT to another ; Syntax.........: VariantCopy($pvargDest, $pvargSrc) ; Parameters ....: $pvargDest - Destionation variant ; $pvargSrc - Source variant ; Return values .: Success - 0 ; Failure - nonzero ; Author ........: Prog@ndy ; Modified.......: ; Remarks .......: ; Related .......: VariantRead ; Link ..........: http://msdn.microsoft.com/en-us/library/ms221697.aspx ; Example .......: ; =============================================================================================================================== Func VariantCopy($pvargDest, $pvargSrc) ; Author: Prog@ndy Local $aCall = DllCall("OleAut32.dll", "long", "VariantCopy", "ptr", $pvargDest, 'ptr', $pvargSrc) If @error Then Return SetError(1, 0, 1) Return $aCall[0] EndFunc ; #FUNCTION# ==================================================================================================================== ; Name...........: VariantInit ; Description ...: Initializes a variant. ; Syntax.........: VariantInit($pvarg) ; Parameters ....: $pvarg - the VARIANT to initialize ; Return values .: Success - 0 ; Failure - nonzero ; Author ........: Prog@ndy ; Modified.......: ; Remarks .......: ; Related .......: VariantClear ; Link ..........: http://msdn.microsoft.com/en-us/library/ms221402.aspx ; Example .......: ; =============================================================================================================================== Func VariantInit($pvarg) ; Author: Prog@ndy Local $aCall = DllCall("OleAut32.dll", "long", "VariantInit", "ptr", $pvarg) If @error Then Return SetError(1, 0, 1) Return $aCall[0] EndFunc ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< Func VariantChangeType( $pVarDest, $pVarSrc, $wFlags, $vt ) Local $aRet = DllCall( "OleAut32.dll", "long", "VariantChangeType", "ptr", $pVarDest, "ptr", $pVarSrc, "word", $wFlags, "word", $vt ) If @error Then Return SetError(1,0,1) Return $aRet[0] EndFunc Func VariantChangeTypeEx( $pVarDest, $pVarSrc, $lcid, $wFlags, $vt ) Local $aRet = DllCall( "OleAut32.dll", "long", "VariantChangeTypeEx", "ptr", $pVarDest, "ptr", $pVarSrc, "word", $lcid, "word", $wFlags, "word", $vt ) If @error Then Return SetError(1,0,1) Return $aRet[0] EndFunc Func VarAdd( $pVarLeft, $pVarRight, $pVarResult ) Local $aRet = DllCall( "OleAut32.dll", "long", "VarAdd", "ptr", $pVarLeft, "ptr", $pVarRight, "ptr", $pVarResult ) If @error Then Return SetError(1,0,1) Return $aRet[0] EndFunc Func VarSub( $pVarLeft, $pVarRight, $pVarResult ) Local $aRet = DllCall( "OleAut32.dll", "long", "VarSub", "ptr", $pVarLeft, "ptr", $pVarRight, "ptr", $pVarResult ) If @error Then Return SetError(1,0,1) Return $aRet[0] EndFunc ; >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> ; BSTR (basic string) functions ; Copied from AutoItObject.au3 by the AutoItObject-Team: monoceres, trancexx, Kip, ProgAndy ; https://www.autoitscript.com/forum/index.php?showtopic=110379 Func SysAllocString( $str ) Local $aRet = DllCall( "OleAut32.dll", "ptr", "SysAllocString", "wstr", $str ) If @error Then Return SetError(1, 0, 0) Return $aRet[0] EndFunc Func SysFreeString( $pBSTR ) If Not $pBSTR Then Return SetError(1, 0, 0) DllCall( "OleAut32.dll", "none", "SysFreeString", "ptr", $pBSTR ) If @error Then Return SetError(2, 0, 0) EndFunc Func SysReadString( $pBSTR, $iLen = -1 ) If Not $pBSTR Then Return SetError(1, 0, "") If $iLen < 1 Then $iLen = SysStringLen( $pBSTR ) If $iLen < 1 Then Return SetError(2, 0, "") Return DllStructGetData( DllStructCreate( "wchar[" & $iLen & "]", $pBSTR ), 1 ) EndFunc Func SysStringLen( $pBSTR ) If Not $pBSTR Then Return SetError(1, 0, 0) Local $aRet = DllCall( "OleAut32.dll", "uint", "SysStringLen", "ptr", $pBSTR ) If @error Then Return SetError(2, 0, 0) Return $aRet[0] EndFunc ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< Func VarBstrCat( $pBSTRLeft, $pBSTRRight, $pBSTRResult ) Local $aRet = DllCall( "OleAut32.dll", "long", "VarBstrCat", "ptr", $pBSTRLeft, "ptr", $pBSTRRight, "ptr", $pBSTRResult ) If @error Then Return SetError(1,0,1) Return $aRet[0] EndFunc SafeArray.au3: #include-once 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. ; fFeatures flags Global Const $FADF_AUTO = 0x0001 ; An array that is allocated on the stack. Global Const $FADF_STATIC = 0x0002 ; An array that is statically allocated. Global Const $FADF_EMBEDDED = 0x0004 ; An array that is embedded in a structure. Global Const $FADF_FIXEDSIZE = 0x0010 ; An array that may not be resized or reallocated. Global Const $FADF_RECORD = 0x0020 ; An array that contains records. When set, there will be a pointer to the IRecordInfo interface at negative offset 4 in the array descriptor. Global Const $FADF_HAVEIID = 0x0040 ; An array that has an IID identifying interface. When set, there will be a GUID at negative offset 16 in the safearray descriptor. Flag is set only when FADF_DISPATCH or FADF_UNKNOWN is also set. Global Const $FADF_HAVEVARTYPE = 0x0080 ; An array that has a variant type. The variant type can be retrieved with SafeArrayGetVartype. Global Const $FADF_BSTR = 0x0100 ; An array of BSTRs. Global Const $FADF_UNKNOWN = 0x0200 ; An array of IUnknown*. Global Const $FADF_DISPATCH = 0x0400 ; An array of IDispatch*. Global Const $FADF_VARIANT = 0x0800 ; An array of VARIANTs. Global Const $FADF_RESERVED = 0xF008 ; Bits reserved for future use. ; >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> ; Safearray functions ; Copied from AutoItObject.au3 by the AutoItObject-Team: monoceres, trancexx, Kip, ProgAndy ; https://www.autoitscript.com/forum/index.php?showtopic=110379 Func SafeArrayCreate($vType, $cDims, $tsaBound) ; Author: Prog@ndy Local $aCall = DllCall("OleAut32.dll", "ptr", "SafeArrayCreate", "dword", $vType, "uint", $cDims, "struct*", $tsaBound) If @error Then Return SetError(1, 0, 0) Return $aCall[0] EndFunc Func SafeArrayDestroy($pSafeArray) ; Author: Prog@ndy Local $aCall = DllCall("OleAut32.dll", "int", "SafeArrayDestroy", "ptr", $pSafeArray) If @error Then Return SetError(1, 0, 1) Return $aCall[0] EndFunc Func SafeArrayAccessData($pSafeArray, ByRef $pArrayData) ; Author: Prog@ndy Local $aCall = DllCall("OleAut32.dll", "int", "SafeArrayAccessData", "ptr", $pSafeArray, "ptr*", 0) If @error Then Return SetError(1, 0, 1) $pArrayData = $aCall[2] Return $aCall[0] EndFunc Func SafeArrayUnaccessData($pSafeArray) ; Author: Prog@ndy Local $aCall = DllCall("OleAut32.dll", "int", "SafeArrayUnaccessData", "ptr", $pSafeArray) If @error Then Return SetError(1, 0, 1) Return $aCall[0] EndFunc Func SafeArrayGetUBound($pSafeArray, $iDim, ByRef $iBound) ; Author: Prog@ndy Local $aCall = DllCall("OleAut32.dll", "int", "SafeArrayGetUBound", "ptr", $pSafeArray, "uint", $iDim, "long*", 0) If @error Then Return SetError(1, 0, 1) $iBound = $aCall[3] Return $aCall[0] EndFunc Func SafeArrayGetLBound($pSafeArray, $iDim, ByRef $iBound) ; Author: Prog@ndy Local $aCall = DllCall("OleAut32.dll", "int", "SafeArrayGetLBound", "ptr", $pSafeArray, "uint", $iDim, "long*", 0) If @error Then Return SetError(1, 0, 1) $iBound = $aCall[3] Return $aCall[0] EndFunc Func SafeArrayGetDim($pSafeArray) Local $aResult = DllCall("OleAut32.dll", "uint", "SafeArrayGetDim", "ptr", $pSafeArray) If @error Then Return SetError(1, 0, 0) Return $aResult[0] EndFunc ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< Func SafeArrayCopy( $psaSource, ByRef $psaDestination ) Local $aRet = DllCall( "OleAut32.dll", "int", "SafeArrayCopy", "ptr", $psaSource, "ptr*", 0 ) If @error Then Return SetError(1,0,1) $psaDestination = $aRet[2] Return $aRet[0] EndFunc Func SafeArrayCopyData( $psaSource, $psaDestination ) Local $aRet = DllCall( "OleAut32.dll", "int", "SafeArrayCopyData", "ptr", $psaSource, "ptr", $psaDestination ) If @error Then Return SetError(1,0,1) Return $aRet[0] EndFunc Func SafeArrayCreateEmptyWr( $vType ) Local $tsaBound = DllStructCreate( $tagSAFEARRAYBOUND ) DllStructSetData( $tsaBound, "cElements", 0 ) DllStructSetData( $tsaBound, "lLbound", 0 ) Return SafeArrayCreate( $vType, 0, $tsaBound ) EndFunc Func SafeArrayDestroyData( $pSafeArray ) Local $aRet = DllCall( "OleAut32.dll", "int", "SafeArrayDestroyData", "ptr", $pSafeArray ) If @error Then Return SetError(1,0,1) Return $aRet[0] EndFunc Func SafeArrayGetVartype( $pSafeArray, ByRef $iVartype ) Local $aRet = DllCall( "OleAut32.dll", "int", "SafeArrayGetVartype", "ptr", $pSafeArray, "ptr*", 0 ) If @error Then Return SetError(1,0,1) $iVartype = $aRet[2] Return $aRet[0] EndFunc Func SafeArrayRedim( ByRef $pSafeArray, $tsaBound ) Local $aRet = DllCall( "OleAut32.dll", "int", "SafeArrayRedim", "ptr", $pSafeArray, "struct*", $tsaBound ) If @error Then Return SetError(1,0,1) $pSafeArray = $aRet[1] Return $aRet[0] EndFunc SafeArrayCreateEmptyWr() is a wrapper function to create an empty safearray. It's not a translation of a similar Windows API function. AutoIt arrays are safearrays Note that AutoIt arrays are stored internally as safearrays. AutoIt arrays are accessed through normal AutoIt variables, and a set of operators and functions have been created to manipulate the arrays. This makes AutoIt arrays proprietary arrays that cannot be easily used in other programming languages. Zip-file You need AutoIt 3.3.12 or later. Tested on Windows 7 and Windows 10. Comments are welcome. Let me know if there are any issues. VariantsSafearrays.7z
    2 points
  2. If you have been using AutoIt for any length of time you will know that it is a great, and powerful scripting language. As with all powerful languages there comes a downside. Virus creation by those that are malicious. AutoIt has no virii installed on your system, and if a script you have created has been marked as a virus, (and you're not malicious) then this is a false positive. They found a set of instructions in an AutoIt EXE out there somewhere, took the general signature of the file, and now all AutoIt EXE's are marked (or most of them). This can be due to several reasons. AutoIt is packed with UPX. UPX is an open source software compression packer. It is used with many virii (to make them smaller). Malicious scripter got the AutoIt script engine recognized as a virus. And I am sure there are more ways your executable could be marked, but that covers the basics. Now I am sure you are wanting to know what you can do to get back up and running without being recognized as a virus. You have to send in a report to the offending AV company alerting them to the false positive they have made. It never hurts to send in your source code along with a compiled exe, to help them realize their mistake. You may have to wait up to 24 hours for them to release an update. The time it takes really depends on the offending AV company. Anti-Virus Links AntiVir Website Contact Avast! Website Contact McAfee Website Contact (email address) Symantec (Norton) Website Contact AVG Website Contact (It says sales or other ?'s I assume this will work) ClamWin Website Contact ClamAV Website Contact (I would only contact the ones with "virusdb maintainer or virus submission management") BitDefender Website Contact ZoneLabs Website Contact Norman Website Contact (email address) eSafe Website Contact (login required) A2 (A-Squared) Website Contact (email address) Edit: Added Website links and Contact links. I hope this helps you understand why your AutoIt executables are marked as virii. JS
    1 point
  3. @CYCho Here's my "quick & dirty" interpretation of the original NTP time request script with a little more error checking, a lot less binary string conversions, and a lot of extra documentation of each step in case you want to study and modify it for your own purposes. #include <Constants.au3> #include <date.au3> ntp_example() Func ntp_example() Const $NTP_SERVER = "time.windows.com" Const $MY_TZ_OFFSET = -5 ; Timezone offset in hours Local $tBuffer Local $aSocket Local $xRequest = Binary(""), $xResponse = Binary("") Local $hTimeoutTimer Local $iSeconds = 0 ; Create the NTP request using a 48 byte buffer $tBuffer = DllStructCreate("byte[48]") DllStructSetData($tBuffer, 1, 0x23) ; 00 100 011 = LI(0) / VN(4-NTPv4) / Mode(3-Client) ; Copy the buffer to a variable for sending $xRequest = DllStructGetData($tBuffer, 1) ConsoleWrite("DEBUG: Binary request = " & $xRequest & @CRLF) ; Send NTP request UDPStartup() If @error Then Exit MsgBox($MB_ICONERROR, "ERROR", "UDPStartup failed - @error = " & @error) OnAutoItExitRegister("udp_shutdown") ; Do UDPShutdown upon exit $aSocket = UDPOpen(TCPNameToIP($NTP_SERVER), 123) If @error Then Exit MsgBox($MB_ICONERROR, "ERROR", "UDPOpen failed - @error = " & @error) UDPSend($aSocket, $xRequest) If @error Then Exit MsgBox($MB_ICONERROR, "ERROR", "UDPSend failed - @error = " & @error) ; Receive NTP response $hTimeoutTimer = TimerInit() Do If TimerDiff($hTimeoutTimer) > 2000 Then ExitLoop ; If no response within 2 seconds, then exit the loop $xResponse = UDPRecv($aSocket, DllStructGetSize($tBuffer), $UDP_DATA_BINARY) If @error Then Exit MsgBox($MB_ICONERROR, "ERROR", "UDPRecv failed - @error = " & @error) Until $xResponse <> Binary("") ConsoleWrite("DEBUG: Binary response = " & Binary($xResponse) & @CRLF) ; Close the socket UDPCloseSocket($aSocket) ; If the request timed out, exit with a message If $xResponse = Binary("") Then Exit MsgBox($MB_ICONERROR, "ERROR", "UDPRecv timed out") ; Get & display timestamp ; - UTC timestamp is taken from xmit timestamp in NTP response ; - Xmit timestamp is a big-endian, uint32, at binary position 41 ; - Xmit timestamp represents the number of seconds since 1900-01-01 00:00:00 $iSeconds = Dec(Hex(BinaryMid($xResponse, 41, 4)), $NUMBER_64BIT) ConsoleWrite(StringFormat("DEBUG: Xmit timestamp = 0x%s (%s)", Hex(BinaryMid($xResponse, 41, 4)), $iSeconds) & @CRLF) ConsoleWrite("INFO: UTC date/time = " & _DateAdd("s", $iSeconds, "1900/01/01 00:00:00") & @CRLF) ConsoleWrite("INFO: Local date/time = " & _DateAdd("s", $iSeconds + (3600 * $MY_TZ_OFFSET), "1900/01/01 00:00:00") & @CRLF) EndFunc Func udp_shutdown() UDPShutdown() EndFunc
    1 point
  4. Yes the loop is valid now and no more recursion. Sorry I don't know what you mean when you say "image search in the background" but either way we are getting out of scope for what my function does and Im not going to troubleshoot someone else's code for you because I would still be on a wild goose chase. All my function does is take two inputs which will be handles to a big bitmap and (presumably) a little bitmap and searches the big for existence of the little. The core loop of the function is honestly no different then searching for a string within in a string (it literally uses CMPSD--Compare String instruction) which is the main reason there's no tolerance. How one goes about getting those bitmaps, weather screencapture or some other app creating them, and then also processes those bitmaps via loading and getting handles, converting, clean up (big key here*) before sending to my function is a much different scope. I don't think my function is the issue here but if it was I would need the source and search bmps to troubleshoot. That's the only way to trouble shoot a function like this. In your case I belive you need to dig in and learn a bit more of what the code is doing and play with debugging it. Try and get an understanding of what every variable being passed to every function is suppose to be and confirm it is what it should be. Make "ALT-D" key shortcut your friend. _arraydisplay() for looking at anything passing an array. Your getting stuck at _WinAPI_GetBitmapDimension(). Open the documentation and then open the example it has. Make sure you understand not only how that function works, but also each other supporting function in the example. Then modify the example and substitute your bmp with the one used in the example. See what you can learn from just playing with that and then go back to your code and see if you can confirm all the requirements for _WinAPI_GetBitmapDimension() are getting met with whatever bmps your working with.
    1 point
  5. 1 point
  6. GPT conversion is a go! Thank you. The mbr2gpt /convert /allowfullOS command worked without a hitch and more smoothly than I anticipated. The drive is now listed as GPT and my BIOS Mode = UEFI. There are some other things I have to check out, but all is good! Thank you again!
    1 point
  7. This should be read like below: 0x1B = 00011011 00 = Leap Indicator (0) 011 = Version Number (NTPv3) 011 = Mode (3-Client)
    1 point
  8. You can use _WinAPI_SetThreadExecutionState. Remarks say: This function does not stop the screen saver from executing. But actually You could do this to prevent screen saver execution. _WinAPI_SetThreadExecutionState(BitOR($ES_DISPLAY_REQUIRED, $ES_CONTINUOUS)) Saludos
    1 point
  9. 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.7z
    1 point
×
×
  • Create New...