Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 03/25/2018 in all areas

  1. An experienced AutoIt user can make the observation that it's possible to pass an array to compiled code as a parameter to a method in a COM object. This also seems to be the only way. An obvious way to process an array in compiled code is therefore simply to create an object and a method to process the array. Another but not quite so obvious way is to replace the methods in an existing object with new methods coded in AutoIt. Inside these methods, you can access an AutoIt array as a pointer to a safearray or as a pointer to the data area in a safearray. And one of these pointers can be passed to compiled code. Note that through a pointer to the data area in a safearray it's possible to access array memory directly. In both of these ways, safarrays plays an important role because COM arrays are safearrays. And you can also conclude that AutoIt arrays are internally stored as safarrays. The first way to create a new object is convenient in programming languages like C# and VB.NET. This way is demonstrated in Using C# and VB Code in AutoIt. Using C# and VB Code in AutoIt is based on DotNet.au3 UDF to access .NET Framework from AutoIt. The advantage of using the latter UDF is that there is no need for an IDE to develop C# or VB code. There is no need other than AutoIt and SciTE. When the code is run in SciTE, C# and VB error messages are displayed in the console. Registering DLL files to access the objects is also not an issue. This is handled through .NET Framework. The other way of replacing the methods in an existing object is convenient in programming languages like assembler, C, C++ and FreeBasic. This way is demonstrated in Accessing AutoIt Variables. When using this way, an IDE is usually needed to develop and compile code. The exception is flat assembler code, that can be handled entirely through AutoIt, SciTE and FASM.DLL. There is a short description and example here. This new project is a common UDF to access AutoIt arrays from compiled code in one of these two ways and to access array memory directly. A small example which implements a Delete.au3 UDF to delete rows and columns demonstrates the usage of the UDF. It's shown how to use Delete.au3 in your own project. Accessing Arrays UDF The zip-file below is structured this way: AccArrays\ - Accessing Arrays UDF Delete\ - Delete example Display\ - Display funcs Project\ - Project example AccArrays\ is further divided into AccArrays\ - 7 files that implements the UDF Utilities\ - Utility functions The 7 files that implements the UDF are copied from Accessing AutoIt Variables and Using C# and VB Code in AutoIt. Some of the files are shortened to only include code necessary for this project. Also, some function and variable names have been changed and shortened. Delete\ and Project\ are discussed below. Display\ contains functions copied from Data display functions. Delete example The Delete example implements 3 functions: DelRowsNum, DeleteRows and DeleteColumns. DelRowsNum demonstrates how to access array memory directly to delete rows in a 1D or 2D array of numbers (no strings). This function is coded entirely in AutoIt. DeleteRows is the general function to delete rows that also can handle arrays containing strings. C++ code is used to performance optimize the function. DeleteColumns deletes columns in a 2D array. The hard work in this function is performed by VB code. Delete\ is divided into 5 main folders: DLLFiles\ - C++ and VB dll-files Examples\ - Function examples Includes\ - Include files Delete.au3 - Main include Functions\ - Implementations DelRowsNum.au3 - Code for DelRowsNum() DeleteRows.au3 - Code for DeleteRows() DeleteColumns.au3 - Code for DeleteColumns() Initialization.au3 - Code to load dll-files Internal\ - Internal files Sources\ - C++ and VB source files Utilities\ - Create .NET assembly dll Before the functions are reviewed, we need to look at a few concepts and topics. Row- and column-major arrays Row- and column-major order of arrays is about how arrays are stored in memory. This is an array with 3 rows and 4 columns $aArray[3][4] = [ [ 1, 2, 3, 4 ], _ [ 5, 6, 7, 8 ], _ [ 9,10,11,12 ] ] If the array is stored in memory as a row-major array it's stored row by row: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 If the array is stored in memory as a column-major array it's stored column by column: 1 | 5 | 9 | 2 | 6 | 10 | 3 | 7 | 11 | 4 | 8 | 12 If array memory can be accessed directly then row manipulations can be performed solely by memory move operations (without any loops) in a row-major array. Column manipulations can be performed solely by memory move operations (without any loops) in a column-major array. This is not important in a conventional AutoIt function because array memory cannot be accessed directly. But it's different for a safearay in an AutoIt object method and for a VB array in a VB object method. When a true AutoIt array is passed to an AutoIt object method as a safearray of variants, this safearray is a row-major array and row manipulations can be performed solely by memory move operations with RtlMoveMemory. When an AutoIt array is passed to a VB object method as an array of objects, this array is a column-major array and column manipulations can be performed solely by memory move operations with Array.Copy and similar methods. Use safearrays to create fast functions to manipulate rows in an array. Use VB arrays to create fast functions to manipulate columns in an array. Problem with safearrays But there is a significant problem with safarrays. Direct memory manipulation of safarrays of variants can only be performed if data in the variants is entirely stored within the variant structure. This generally means that data in the variants and safearrays must be numbers (integers, floats). For strings, only a pointer to the string is stored in the variant. The string itself is stored as a BSTR somewhere else in memory. Direct memory manipulation of safarrays containing strings isn't possible. Elements in a safarray containing strings must be handled ono by one in a loop eg. a C++ loop. Passing arrays to/from methods Another problem is passing arrays back and forth between AutoIt code and object method code. This takes time. For large arrays with more than 1,000,000 elements the time can be significant. Especially passing arrays from AutoIt code to object method code. And particularly arrays containing strings. This topic will be dealed with in more details in a later post. Because it takes a long time to pass an array from AutoIt code to object method code, it's a great advantage to be able to process multiple rows/columns and multiple groups of rows/columns at once. In this way you only need to pass the array once. DelRowsNum DelRowsNum accesses array memory directly and can therefore only handle arrays of numbers (no strings). For arrays of numbers this function is very fast: #include-once #include "Internal\DelFunc.au3" ; Delete one or more rows at the specified (row) indices in a 1D or 2D array that contains numbers only ; DelRowsNum( _ ; ByRef $aArray, _ ; The 1D or 2D array to delete rows in. ; $vIndex ) ; The (row) indices in $aArray where the rows are to be deleted. This can be a single index, a 1D array of indi- ; ; ces or a 2D array of "index/number of rows" pairs. Indices must be in ascending order and cannot be duplicated. ; ; @error: 1 = $aArray is not a 1D or 2D array ; 2 = $vIndex is not a valid specification of $aArray indices/rows ; 3 = Mismatch between $aArray and $vIndex ; Indices plus number of rows in $vIndex must be smaller than number of rows in $aArray, indices ; must be in ascending order, and rows to delete must not overlap (a row must not be deleted twice). Func DelRowsNum( _ ByRef $aArray, _ ; The 1D or 2D array to delete rows in. $vIndex ) ; The (row) indices in $aArray where the rows are to be deleted. ; Check parameters, pass data to method, execute method RowsDelete( $aArray, $vIndex, DelRowsNumMtd ) ; DelFunc.au3 If @error Then Return SetError(@error,0,0) EndFunc Func DelRowsNumMtd( $pvArray ) ; Get data Local $aData AccArrays_PassDataToMethod( 1, $aData ) Local $iRows = $aData[0], $iCols = $aData[1], $aIndex = $aData[2], $iIndex = $aData[3] ; Get safearray Local $psaArrayData Local $psaArray = DllStructGetData( DllStructCreate( "ptr", $pvArray + 8 ), 1 ) SafeArrayAccessData( $psaArray, $psaArrayData ) ; Bytes per row Local $iBytesPrRow = $iCols * ( @AutoItX64 ? 24 : 16 ) ; Delete rows with RtlMoveMemory ; Delete rows by moving existing rows up Local $pCopyTo, $pCopyFrom For $i = $iIndex - 1 To 0 Step -1 $pCopyTo = $psaArrayData + $aIndex[$i][0] * $iBytesPrRow $pCopyFrom = $pCopyTo + $aIndex[$i][1] * $iBytesPrRow DllCall( "kernel32.dll", "none", "RtlMoveMemory", "struct*", $pCopyTo, "struct*", $pCopyFrom, "ulong_ptr", ( $iRows - $aIndex[$i][0] - $aIndex[$i][1] ) * $iBytesPrRow ) $iRows -= $aIndex[$i][1] Next SafeArrayUnaccessData( $psaArray ) ; ReDim safearray to account for deleted rows AccArrays_SafeArrayRedim( $psaArray, $iRows ) EndFunc The purpose of the For-loop in the method code is to delete multiple groups of rows at once. DeleteRows The general function that also can handle arrays containing strings is based on a C++ loop: #include-once #include "Initialization.au3" #include "Internal\DelFunc.au3" ; Delete one or more rows at the specified (row) indices in a 1D or 2D array ; DeleteRows( _ ; ByRef $aArray, _ ; The 1D or 2D array to delete rows in. ; $vIndex ) ; The (row) indices in $aArray where the rows are to be deleted. This can be a single index, a 1D array of indi- ; ; ces or a 2D array of "index/number of rows" pairs. Indices must be in ascending order and cannot be duplicated. ; ; @error: 1 = $aArray is not a 1D or 2D array ; 2 = $vIndex is not a valid specification of $aArray indices/rows ; 3 = Mismatch between $aArray and $vIndex ; Indices plus number of rows in $vIndex must be smaller than number of rows in $aArray, indices ; must be in ascending order, and rows to delete must not overlap (a row must not be deleted twice). ; 4 = C++ code is not initialized Func DeleteRows( _ ByRef $aArray, _ ; The 1D or 2D array to delete rows in. $vIndex ) ; The (row) indices in $aArray where the rows are to be deleted. If Not DeleteInitDll( "IsDeleteInitDll" ) Then Return SetError(4,0,0) ; Check parameters, pass data to method, execute method RowsDelete( $aArray, $vIndex, DeleteRowsMtd ) ; DelFunc.au3 If @error Then Return SetError(@error,0,0) EndFunc Func DeleteRowsMtd( $pvArray, $pvIndex ) ; Get data Local $aData AccArrays_PassDataToMethod( 1, $aData ) Local $iRows = $aData[0], $iCols = $aData[1], $iIndex = $aData[3] ; Get safearrays Local $psaArrayData, $psaIndexData Local $psaArray = DllStructGetData( DllStructCreate( "ptr", $pvArray + 8 ), 1 ) Local $psaIndex = DllStructGetData( DllStructCreate( "ptr", $pvIndex + 8 ), 1 ) SafeArrayAccessData( $psaArray, $psaArrayData ) SafeArrayAccessData( $psaIndex, $psaIndexData ) ; Delete rows through C++ loop ; Delete rows by moving existing rows up $iRows = DllCall( DeleteInitDll(), "int", "DeleteRows", "int", $iRows, "int", $iCols, "int", $iIndex, "ptr", $psaArrayData, "ptr", $psaIndexData )[0] SafeArrayUnaccessData( $psaArray ) SafeArrayUnaccessData( $psaIndex ) ; ReDim safearray to account for deleted rows AccArrays_SafeArrayRedim( $psaArray, $iRows ) EndFunc Sources\Cpp\Delete.cpp: #include <Windows.h> #include <OleAuto.h> int __stdcall DeleteRows( int iRows, int iCols, int iIndex, VARIANT *psaArray, VARIANT *psaIndex ) { int iArrayIdx, iDelRows; int iMoveTo, iMoveFrom, iMoveCnt; for ( int i = iIndex - 1; i >= 0; i-- ) { iArrayIdx = psaIndex[2*i+0].intVal; iDelRows = psaIndex[2*i+1].intVal; // Move existing rows up iMoveTo = iArrayIdx * iCols; iMoveFrom = iMoveTo + iDelRows * iCols; iMoveCnt = ( iRows - iArrayIdx - iDelRows ) * iCols; for ( int j = 0; j < iMoveCnt; j++, VariantCopy( &psaArray[iMoveTo++], &psaArray[iMoveFrom++] ) ); iRows -= iDelRows; } return iRows; } DeleteColumns This function is based on VB code: #include-once #include "Initialization.au3" ; Delete one or more columns at the specified (column) indices in a 2D array ; DeleteColumns( _ ; ByRef $aArray, _ ; The 2D array to delete columns in. ; $vIndex ) ; The (column) indices in $aArray where the columns are to be deleted. This can be a single index, a 1D array of indi- ; ; ces or a 2D array of "index/number of columns" pairs. Indices must be in ascending order and cannot be duplicated. ; ; @error: 1 = $aArray is not a 2D array ; 2 = $vIndex is not a valid specification of $aArray indices/columns ; 3 = Mismatch between $aArray and $vIndex ; Indices plus number of columns in $vIndex must be smaller than number of columns in $aArray, indices ; must be in ascending order, and columns to delete must not overlap (a column must not be deleted twice). ; 4 = VB-code is not initialized ; 5 = No valid VB class object Func DeleteColumns( _ ByRef $aArray, _ ; The 2D array to delete columns in. $vIndex ) ; The (column) indices in $aArray where the columns are to be deleted. ; Check array dimensions Local $aI = [ $vIndex ], $aIndex = IsArray( $vIndex ) ? $vIndex : $aI If Not ( IsArray( $aArray ) And UBound( $aArray, 0 ) = 2 ) Then Return SetError(1,0,0) If Not ( UBound( $aIndex, 0 ) = 1 Or ( UBound( $aIndex, 0 ) = 2 And UBound( $aIndex, 2 ) = 2 ) ) Then Return SetError(2,0,0) ; Indices plus columns in $vIndex must be smaller than columns in $aArray, indices must be in ; ascending order, and columns to delete must not overlap (a column must not be deleted twice). Local $iCols = UBound( $aArray, 2 ), $iIndex = UBound( $aIndex ) If UBound( $aIndex, 0 ) = 1 Then ; 1D array ; Make the 1D array into a 2D array of "index/number of columns" pairs Local $aIndexTmp[$iIndex][2] For $i = 0 To $iIndex - 1 $aIndexTmp[$i][0] = $aIndex[$i] ; $aIndexTmp[$i][0] is the index in $aArray where the columns are to be deleted $aIndexTmp[$i][1] = 1 ; $aIndexTmp[$i][1] is the number of columns to delete in $aArray at the position given by the index Next $aIndex = $aIndexTmp ; $aIndex = $aIndexTmp EndIf Local $iAllCols = $iCols, $j = $iIndex - 1 ; Check that index do not exceed $iCols If $aIndex[$j][0] >= $iCols Then Return SetError(3,0,0) ; Indices in $aIndex must be in ascending order ; Check $aIndex for valid indices and row numbers ; This means that it's enough to check the last index If $aIndex[$j][1] < 1 Or $aIndex[$j][0] + $aIndex[$j][1] > $iCols Then Return SetError(3,0,0) If $iCols - $aIndex[$j][1] < 1 Then Return SetError(3,0,0) $iCols -= $aIndex[$j][1] For $i = $iIndex - 2 To 0 Step -1 If $aIndex[$i][0] > $aIndex[$i+1][0] Or $aIndex[$i][1] < 1 Or $aIndex[$i][0] + $aIndex[$i][1] > $iCols Or _ $aIndex[$i][0] + $aIndex[$i][1] > $aIndex[$i+1][0] Then Return SetError(3,0,0) If $iCols - $aIndex[$i][1] < 1 Then Return SetError(3,0,0) $iCols -= $aIndex[$i][1] Next ; Convert $aIndex to an index of columns that are not deleted ; These columns can be copied into a new array ReDim $aIndex[$iIndex+1][5] $aIndex[0][2] = 0 ; $aIndex[$i][2] is the index in $aArray where from the columns are to be copied $aIndex[0][3] = 0 ; $aIndex[$i][3] is the index in the new array where the columns are to be inserted $aIndex[0][4] = $aIndex[0][0] ; $aIndex[$i][4] is the number of columns to copy For $i = 1 To $iIndex - 1 $aIndex[$i][2] = $aIndex[$i-1][0] + $aIndex[$i-1][1] $aIndex[$i][3] = $aIndex[$i-1][3] + $aIndex[$i-1][4] $aIndex[$i][4] = $aIndex[$i][0] - $aIndex[$i][2] Next $aIndex[$i][2] = $aIndex[$i-1][0] + $aIndex[$i-1][1] $aIndex[$i][3] = $aIndex[$i-1][3] + $aIndex[$i-1][4] $aIndex[$i][4] = $iAllCols - $aIndex[$i][2] If Not DeleteInitVbSrc( "IsDeleteInitVbSrc" ) And _ Not DeleteInitVbDll( "IsDeleteInitVbDll" ) Then Return SetError(4,0,0) Local $oDeleteClass = DeleteInit() If Not IsObj( $oDeleteClass ) Then Return SetError(5,0,0) $aArray = $oDeleteClass.DeleteColumns( $aArray, $aIndex, $iCols ) EndFunc Sources\Delete.vb: Imports System Class DeleteClass Public Function DeleteColumns( aArray As Object(,), aIndex As Object(,), iCols As Integer ) As Object(,) Dim iRows As Integer = aArray.GetUpperBound(1) + 1 Dim aNewArray(iCols-1,iRows-1) As Object For i As Integer = 0 To aIndex.GetUpperBound(1) Array.Copy( aArray, aIndex(2,i) * iRows, aNewArray, aIndex(3,i) * iRows, aIndex(4,i) * iRows ) Next Return aNewArray End Function End Class Initialization Because DeleteRows and DeleteColumns are optimized with compiled code it's necessary to do some initialization to load the compiled code into the AutoIt process. This is done with the functions in Initialization.au3. DeleteRows is optimized with C++ code which is compiled into dll-files and stored in DLLFiles\ as Delete.dll and Delete_x64.dll. Delete.dll and Delete_x64.dll is delivered in the zip-file. DeleteInitDll() in Initialization.au3 loads the dll-files: ; Load compiled code from C++ DLL file ; The DLL file must be specified with a full path ; Or it must be specified with a path relative to the running script ; Or it must be placed in the same folder as the running script ; ; If you're having trouble loading a DLL file, copy the DLL file to the same ; folder as the running script, and compile the script into an EXE file. ; ; @error: 1 = $sDllPath is not specified and "Delete.dll" or ; "Delete_x64.dll" is not located with the running script ; 2 = "Delete.dll" or "Delete_x64.dll" cannot be found in $sDllPath Func DeleteInitDll( $sDllPath = "" ) ; $sDllPath must be the path WITHOUT DLL filename Static $hDeleteDll = 0 If $hDeleteDll Then Return $hDeleteDll If $sDllPath = "IsDeleteInitDll" Then Return $hDeleteDll Local $sDllFile = @AutoItX64 ? "Delete_x64.dll" : "Delete.dll" If Not $sDllPath Then If FileExists( $sDllFile ) Then $sDllPath = ".\" Else ConsoleWrite( "File path (without DLL filename) for """ & $sDllFile & """ must be specified, or" & @CRLF & _ """ & $sDllFile & "" must be placed in the same folder as the running script." & @CRLF ) Return SetError(1,0,0) EndIf EndIf If StringRight( $sDllPath, 1 ) <> "\" Then $sDllPath &= "\" If Not FileExists( $sDllPath & $sDllFile ) Then ConsoleWrite( "DLL file """ & $sDllPath & $sDllFile & """ cannot be found" & @CRLF ) Return SetError(2,0,0) EndIf $hDeleteDll = DllOpen( $sDllPath & $sDllFile ) EndFunc DeleteColumns is optimized with VB code, but only the source code is delivered in the zip-file. The code is stored in Sources\Delete.vb. Delete.vb can be loaded in two ways. It can be compiled and loaded on the fly directly from the source. Or it can be compiled into a .NET assembly dll-file and loaded from the dll-file. You have to create the .NET assembly dll-file yourself with Utilities\Delete_CreateDLL.au3. When you run this script Delete.vb is compiled into a dll-file and stored as DLLFiles\DeleteVb.dll. The same dll-file can be used in 32 and 64 bit code. There are two reasons why you have to create the .NET assembly dll-file yourself: You can create the dll-file with your installed version of .NET Framework There is no need to deliver a .NET assembly dll-file in the zip-file DeleteInitVbSrc() in Initialization.au3 compiles and loads Delete.vb on the fly: ; Compile and load .NET code from source file ; The source file must be specified with a full path ; Or it must be specified with a path relative to the running script Func DeleteInitVbSrc( $sSrcPath ) Static $bIsDeleteInitVbSrc = 0 If $sSrcPath = "IsDeleteInitVbSrc" Then Return $bIsDeleteInitVbSrc If Not FileExists( $sSrcPath ) Then ConsoleWrite( "Source file """ & $sSrcPath & """ cannot be found" & @CRLF ) Return SetError(2,0,0) EndIf ; Compile and load VB code Local $oNetCode = DotNet_LoadVBcode( FileRead( $sSrcPath ), "System.dll" ) DeleteInit( $oNetCode ) $bIsDeleteInitVbSrc = 1 EndFunc DeleteInitVbDll() loads DeleteVb.dll: ; Load .NET code from .NET assembly DLL file ; The DLL file must be specified with a full path ; Or it must be specified with a path relative to the running script ; Or it must be placed in the same folder as the running script ; ; If you're having trouble loading a DLL file, copy the DLL file to the same ; folder as the running script, and compile the script into an EXE file. Func DeleteInitVbDll( $sDllPath = "" ) Static $bIsDeleteInitVbDll = 0 If $sDllPath = "IsDeleteInitVbDll" Then Return $bIsDeleteInitVbDll If Not $sDllPath Then If FileExists( "DeleteVb.dll" ) Then $sDllPath = "DeleteVb.dll" Else ConsoleWrite( "File path for ""DeleteVb.dll"" must be specified, or" & @CRLF & _ """DeleteVb.dll"" must be placed in the same folder as the running script." & @CRLF ) Return SetError(1,0,0) EndIf EndIf If Not FileExists( $sDllPath ) Then ConsoleWrite( "DLL file """ & $sDllPath & """ cannot be found" & @CRLF ) Return SetError(2,0,0) EndIf ; Load DeleteVb.dll Local $oNetCode = DotNet_LoadAssembly( $sDllPath ) DeleteInit( $oNetCode ) $bIsDeleteInitVbDll = 1 EndFunc Examples This is the code for Examples\DeleteColumns\Using Delete.vb\10,000 rows.au3: #AutoIt3Wrapper_Au3Check_Parameters=-d -w- 1 -w 2 -w 3 -w 4 -w 5 -w 6 #include "..\..\..\..\Delete\Includes\Delete.au3" DeleteInitVbSrc( "..\..\..\..\Delete\Sources\Delete.vb" ) #include "..\..\..\..\Display\Functions\ArrayDisplayEx\ArrayDisplayEx.au3" Opt( "MustDeclareVars", 1 ) Example() Func Example() ; Create array Local $iRows = 10000, $iCols = 16 Local $aArray0[$iRows][$iCols] For $i = 0 To $iRows - 1 For $j = 0 To $iCols - 1 $aArray0[$i][$j] = $i & "/" & $j Next Next _ArrayDisplayEx( $aArray0, "$aArray0" ) Local $aArray ; Example 1 $aArray = $aArray0 Local $aIndex1 = 3 ; Delete column 3 DeleteColumns( $aArray, $aIndex1 ) If @error Then Return ConsoleWrite( "DeleteColumns: @error = " & @error & @CRLF ) _ArrayDisplayEx( $aArray, "Example 1" ) ; Example 2 $aArray = $aArray0 Local $aIndex2 = [ 3, 8 ] ; Delete column 3 and 8 DeleteColumns( $aArray, $aIndex2 ) If @error Then Return ConsoleWrite( "DeleteColumns: @error = " & @error & @CRLF ) _ArrayDisplayEx( $aArray, "Example 2" ) ; Example 3 $aArray = $aArray0 Local $aIndex3 = [ [ 3, 2 ], _ ; Delete column 3 and 4 [ 8, 3 ] ] ; Delete column 8, 9, 10 DeleteColumns( $aArray, $aIndex3 ) If @error Then Return ConsoleWrite( "DeleteColumns: @error = " & @error & @CRLF ) _ArrayDisplayEx( $aArray, "Example 3" ) EndFunc Note how Delete.au3 UDF is included and Delete.vb is initialized in top of the script with these lines: #include "..\..\..\..\Delete\Includes\Delete.au3" DeleteInitVbSrc( "..\..\..\..\Delete\Sources\Delete.vb" ) Use these lines to initialize Delete.dll or Delete_x64.dll: #include "..\..\..\Delete\Includes\Delete.au3" DeleteInitDll( "..\..\..\Delete\DLLFiles" ) ; No filename Note how the folders Includes, Sources, and DLLFiles are located just below Delete\. Project example To use Delete.au3 UDF in your own project copy AccArrays\ and Delete\ (and also Display\ if you need display functions) to the project. In a production environment, Delete.vb should be compiled as a .NET assembly dll-file. Delete unnecessary subfolders in Delete\. Use the following code blocks to initialize VB and C++ code. Initialize VB code from source file: #include "Delete\Includes\Delete.au3" DeleteInitVbSrc( "Delete\Sources\Delete.vb" ) Initialize VB code from .NET assembly dll-file: #include "Delete\Includes\Delete.au3" DeleteInitVbDll( "Delete\DLLFiles\DeleteVb.dll" ) Initialize C++ code from dll-files: #include "Delete\Includes\Delete.au3" DeleteInitDll( "Delete\DLLFiles" ) ; No filename Zip-file You need AutoIt 3.3.10 or later. Tested on Windows 10 and Windows 7. Tomorrow some larger examples will be presented that are using AccArrays UDF. That's the really interesting code. Comments are welcome. Let me know if there are any issues. AccArrays.7z
    2 points
  2. Hi I was looking for a way to detect if the workstation is locked on all Windows platforms and could not find a working solution in this thread. The original thread seems to have diverted from its original topic, so I thought a new topic would be more appropriate. So after reading this and this and this and this and this and this, I wrote the following which seems to work, and I'd like to hear your opinion (perhaps it can be improved). Btw, IsWorkstationLockedModern() seems more resource consuming than IsWorkstationLockedLegacy() but if, for some reason, it is preferred over the latter, it may still be used on Windows 7 and Windows Server 2008 R2, by excluding them from IsLegacyOS(), and checking for SessionFlags==1 instead of 0 (due to a Windows bug; this is explained in the last "this" link). Func IsWorkstationLocked() If (IsLegacyOS()) Then Return IsWorkstationLockedLegacy() Else Return IsWorkstationLockedModern() EndIf EndFunc Func IsLegacyOS() Return ((@OSVersion == "WIN_2008R2") OR (@OSVersion == "WIN_2008") OR (@OSVersion == "WIN_7") OR (@OSVersion == "WIN_VISTA") OR (@OSVersion == "WIN_2003") OR (@OSVersion == "WIN_XP") OR (@OSVersion == "WIN_XPe") OR (@OSVersion == "WIN_2000")) EndFunc Func IsWorkstationLockedLegacy() Local Const $DESKTOP_SWITCHDESKTOP = 0x100 Local $hUser32dll = DllOpen("user32.dll") Local $hDesktop = DllCall($hUser32dll, "int", "OpenDesktop", "str", "Default", "int", 0, "int", 0, "int", $DESKTOP_SWITCHDESKTOP) If ((@error) OR ($hDesktop[0] == 0)) Then Return SetError(1, 0, False) EndIf Local $result = DllCall($hUser32dll, "int", "SwitchDesktop", "int", $hDesktop[0]) Local $isLocked = ($result[0] == 0) DllCall($hUser32dll, "int", "CloseDesktop", "int", $hDesktop[0]) DllClose($hUser32dll) Return $isLocked EndFunc Func IsWorkstationLockedModern() Local Const $WTS_CURRENT_SERVER_HANDLE = 0 Local Const $WTS_CURRENT_SESSION = -1 Local Const $WTS_SESSION_INFO_EX = 25 Local $hWtsapi32dll = DllOpen("Wtsapi32.dll") Local $result = DllCall($hWtsapi32dll, "int", "WTSQuerySessionInformation", "int", $WTS_CURRENT_SERVER_HANDLE, "int", $WTS_CURRENT_SESSION, "int", $WTS_SESSION_INFO_EX, "ptr*", 0, "dword*", 0) If ((@error) OR ($result[0] == 0)) Then Return SetError(1, 0, False) EndIf Local $buffer_ptr = $result[4] Local $buffer_size = $result[5] Local $buffer = DllStructCreate("uint64 SessionId;uint64 SessionState;int SessionFlags;byte[" & $buffer_size - 20 & "]", $buffer_ptr) Local $isLocked = (DllStructGetData($buffer, "SessionFlags") == 0) $buffer = 0 DllCall($hWtsapi32dll, "int", "WTSFreeMemory", "ptr", $buffer_ptr) DllClose($hWtsapi32dll) Return $isLocked EndFunc
    1 point
  3. You are correct! I didn't need to give the FF file path once I sorted the correct versions of udfs and geckodriver.exe - I didn't realize that I was running ff 64-bit. Now to try to download some Word documents from a site.
    1 point
×
×
  • Create New...