Popular Post LarsJ Posted September 24, 2017 Popular Post Share Posted September 24, 2017 (edited) The collection of examples in .NET Common Language Runtime (CLR) Framework shows that there are virtually no limits to the possibilities that the usage of .NET Framework and .NET code in AutoIt opens up for. One possibility which certainly is very interesting is the possibility of using C# and VB code in AutoIt. That is, to create, compile and execute C# and VB source code directly through an AutoIt script without the need for any external tools at all, eg. an integrated development environment (IDE) program or similar. You can even create a .NET assembly dll-file with your C# or VB code that you can simply load and execute. Why is it interesting to execute C# or VB code in an AutoIt script? It's interesting because C# and VB code is executed as compiled code and not as interpreted code like AutoIt code. Compiled code is very fast compared to interpreted code. In AutoIt and all other interpreted languages probably 99% or more of the total execution time is spend by the code interpretor to interpret the code lines, while only 1% or less of the total execution time is spend by executing the actual code. Compiled code is directly executable without the need for a code interpretor. That's the reason why compiled code is so much faster than interpreted code. Using C# and VB code in AutoIt is interesting because it can be used to performance optimize the AutoIt code. There may be many other good reasons for using C# and VB code in AutoIt, but here the focus is on code optimization. In the help and support forums you can regularly find questions related to this topic. You can find many examples where assembler code is used in connection with performance optimization. Recently, there has been some interest in FreeBASIC. Code optimization is clearly a topic that has some interest. How difficult is writing C# and VB code compared to assembler and FreeBASIC code? It's certainly much easier and faster than writing assembler code. Because you can do everything through AutoIt without the need for an IDE, it's probably also easier than FreeBASIC. As usually you get nothing for free. The cost is that there is some overhead associated with executing compiled code. You must load and start the code. You need methods to move data back and forth between the AutoIt code and the compiled code. You'll not see that all compiled code is 100 times faster than AutoIt code. Somewhere between 10 and 100 times faster is realistic depending on the complexity of the code. And probably also only code running in loops is interesting and preferably a lot of loops. How C# and VB code can be used in AutoIt through .NET Framework is what this example is about. The rest of first post is a review of introductory C# and VB examples. The purpose of the examples is to make it easier to use C# and VB code in AutoIt. They show how to do some of the basic things in C#/VB that you can do in AutoIt. They focus on topics that are relevant when both AutoIt and C#/VB code is involved. Eg. how to pass variables or arrays back and forth between AutoIt and C#/VB code. The examples are not meant to be a regular C#/VB tutorial. C# and VB Guides in Microsoft .NET Documentation is a good place to find information about C# and VB code. Dot Net Perls example pages have some nice examples. To avoid first post being too long, three posts are reserved for topics that will be presented in the coming weeks. DotNet.au3 UDFDotNet.au3 UDF to access .NET Framework from AutoIt is used to access the .NET Framework. But you do not at all need a detailed knowledge of the code in DotNet.au3 to use C#/VB code in AutoIt. The UDF is stored as DotNetAll.au3 in Includes\ in the zip-file in bottom of post. Includes\ only contains this file. Introductory C# and VB examples The code in the examples below is VB code. But the zip-file in bottom of post contains both C# and VB versions of the examples. Code templates This is the vb and au3 code templates that's used in all of the examples. TemplateVB.vb (TemplateVB-a.vb is provided with comments): Imports System Class Au3Class Public Sub MyMethod() Console.WriteLine( "Hello world from VB!" ) End Sub End Class Note that Console.WriteLine writes output to SciTE console. TemplateVB.au3: #include "..\..\..\Includes\DotNetAll.au3" Opt( "MustDeclareVars", 1 ) Example() Func Example() Local $oNetCode = DotNet_LoadVBcode( FileRead( "TemplateVB.vb" ), "System.dll" ) Local $oAu3Class = DotNet_CreateObject( $oNetCode, "Au3Class" ) $oAu3Class.MyMethod() EndFunc Usually, 2 code lines are sufficient to make .NET code available in AutoIt. DotNet_LoadVBcode() compiles the VB code, creates the .NET code in memory, loads the code into the default domain and returns a .NET code object which is a reference to the .NET code. DotNet_CreateObject() takes the .NET code object and a class name as input parameters and creates an object from the class. See DotNet.au3 UDF. Now the sub procedure MyMethod in the VB code can be executed as an object method. Most examples contains just a few code lines like the templates. I don't want to review all examples, but to get an idea of what this is about, here's a list of the top level folders in the zip-file: Code templates Introductory topics Comments Comment block Line continuation ConsoleWrite MessageBox Public keyword Multiple methods Subs, functions Global variable Error handling Missing DLL-file Imports, using CS-VB mismatch Code typing errors Set @error macro Passing variables Passing 1D arrays Passing 2D arrays Simple examples Prime numbers Create DLL Prime numbers So far, there is only one example with more than just a few code lines. This is an example of calculating prime numbers. This example is also used to show how to create a .NET assembly dll-file. These two examples are reviewed with more details below. Prime numbers The example calculates a certain number of prime numbers and returns the prime numbers as a 1D array. It shows how to pass an AutoIt variable (number of prime numbers) to the C#/VB code and how to return a 1D array of integers (the prime numbers) from the C#/VB code to AutoIt. Especially arrays are interesting in relation to compiled code. This is Microsoft documentation for VB arrays and C# arrays. Design considerations If you want to create a UDF that uses advanced techniques such as compiled code, and you want to make it available to other members, you should consider the design. Consider how the code should be designed to be attractive to other members. You should probably not design the code so other members will need to execute .NET code, create objects, and call object methods in their own code. This should be done in a function in the UDF so that a user can simply call an easy-to-use AutoIt function in the usual way. AutoIt and VB code There are three versions of the example. A pure AutoIt version in the au3-folder, an AutoIt/VB version in the VB-folder and an AutoIt/C# version in the CS-folder. The pure AutoIt and the AutoIt/VB versions are reviewed below. AutoIt code in au3\CalcPrimes.au3. This is the pure AutoIt UDF to calculate primes: #include-once Func CalcPrimes( $nPrimes ) Local $aPrimes[$nPrimes], $iPrime = 2, $iPrimes = 0 If $nPrimes <= 100 Then ConsoleWrite( $iPrime & @CRLF ) ; Store first prime $aPrimes[$iPrimes] = $iPrime $iPrimes += 1 $iPrime += 1 ; Loop to calculate primes While $iPrimes < $nPrimes For $i = 0 To $iPrimes - 1 If Mod( $iPrime, $aPrimes[$i] ) = 0 Then ExitLoop Next If $i = $iPrimes Then If $nPrimes <= 100 Then ConsoleWrite( $iPrime & @CRLF ) $aPrimes[$iPrimes] = $iPrime $iPrimes += 1 EndIf $iPrime += 1 WEnd Return $aPrimes EndFunc Note the similarity between the AutoIt code above and the VB code below. If you can write the AutoIt code you can also write the VB code. VB code in VB\CalcPrimesVB.vb to calculate primes: Imports System Class PrimesClass Public Function CalcPrimes( nPrimes As Integer ) As Integer() Dim aPrimes(nPrimes-1) As Integer, iPrime As Integer = 2, iPrimes As Integer = 0, i As Integer If nPrimes <= 100 Then Console.WriteLine( iPrime ) 'Store first prime aPrimes(iPrimes) = iPrime iPrimes += 1 iPrime += 1 'Loop to calculate primes While iPrimes < nPrimes For i = 0 To iPrimes - 1 If iPrime Mod aPrimes(i) = 0 Then Exit For Next If i = iPrimes Then If nPrimes <= 100 Then Console.WriteLine( iPrime ) aPrimes(iPrimes) = iPrime iPrimes += 1 End If iPrime += 1 End While Return aPrimes End Function End Class AutoIt code in VB\CalcPrimesVB.au3. This is the AutoIt/VB UDF to calculate primes. #include-once #include "..\..\..\..\..\Includes\DotNetAll.au3" Func CalcPrimesVBInit() CalcPrimesVB( 0 ) EndFunc Func CalcPrimesVB( $nPrimes ) Static $oNetCode = 0, $oPrimesClass = 0 If $nPrimes = 0 Or $oNetCode = 0 Then ; Compile and load VB code, create PrimesClass object $oNetCode = DotNet_LoadVBcode( FileRead( "CalcPrimesVB.vb" ), "System.dll" ) $oPrimesClass = DotNet_CreateObject( $oNetCode, "PrimesClass" ) If $nPrimes = 0 Then Return EndIf ; Execute CalcPrimes method and return 1D array of primes Return $oPrimesClass.CalcPrimes( $nPrimes ) EndFunc Note the initialization code in CalcPrimesVB() where the VB code is compiled and loaded and the $oPrimesClass object is created. If the user forgets to call CalcPrimesVBInit() it'll work anyway. Examples with pure AutoIt code in au3\Examples.au3. This is user code: #include <Array.au3> #include "CalcPrimes.au3" Opt( "MustDeclareVars", 1 ) Examples() Func Examples() ShowPrimes( 10 ) ; Used under development ShowPrimes( 1000 ) ; 400 milliseconds ShowPrimes( 5000 ) ; 8 seconds EndFunc Func ShowPrimes( $nPrimes ) ConsoleWrite( "$nPrimes = " & _ $nPrimes & @CRLF ) Local $hTimer = TimerInit() Local $aPrimes = CalcPrimes( $nPrimes ) ConsoleWrite( "Time = " & _ TimerDiff( $hTimer ) & @CRLF & @CRLF ) _ArrayDisplay( $aPrimes ) EndFunc Note again that the user code in the pure AutoIt examples above is almost identical to the user code in the AutoIt/VB examples below. The only difference the user will notice is the speed. To calculate 5000 prime numbers, the C#/VB code is 100 times faster. Try yourself. Examples with AutoIt/VB code in VB\ExamplesVB.au3. This is user code: #include <Array.au3> #include "CalcPrimesVB.au3" Opt( "MustDeclareVars", 1 ) ExamplesVB() Func ExamplesVB() CalcPrimesVBInit() ShowPrimesVB( 10 ) ; Used under development ShowPrimesVB( 1000 ) ; 10 milliseconds ShowPrimesVB( 5000 ) ; 80 milliseconds ShowPrimesVB( 10000 ) ; 200 milliseconds ;ShowPrimesVB( 50000 ) ; 5 seconds EndFunc Func ShowPrimesVB( $nPrimes ) ConsoleWrite( "$nPrimes = " & _ $nPrimes & @CRLF ) Local $hTimer = TimerInit() Local $aPrimes = CalcPrimesVB( $nPrimes ) ConsoleWrite( "Time = " & _ TimerDiff( $hTimer ) & @CRLF & @CRLF ) _ArrayDisplay( $aPrimes ) EndFunc .NET assembly dll-file In a production environment the compiled VB code should be stored in a .NET assembly dll-file. The first step is to create the dll-file from the VB source code: #include "..\..\..\..\..\Includes\DotNetAll.au3" ; Compile VB code and load the code into CalcPrimesVB.dll: A .NET assembly dll-file DotNet_LoadVBcode( FileRead( "CalcPrimesVB.vb" ), "System.dll", 0, "CalcPrimesVB.dll" ) ; You can delete the PDB-file (binary file containing debug information) If you inspect the dll-file with ILSpy.exe (see DotNet.au3 UDF) you'll see these comments in top of the output in the right pane window: // ...\7) Create DLL\Prime numbers\VB\CalcPrimesVB.dll // CalcPrimesVB, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null // Global type: <Module> // Architecture: AnyCPU (64-bit preferred) // Runtime: .NET 4.0 Note that the dll-file can be used in both 32 and 64 bit code (Architecture: AnyCPU). The second step is to modify the AutoIt/VB UDF to load the code from the dll-file: #include-once #include "..\..\..\..\..\Includes\DotNetAll.au3" Func CalcPrimesVBInit() CalcPrimesVB( 0 ) EndFunc Func CalcPrimesVB( $nPrimes ) Static $oNetCode = 0, $oPrimesClass = 0 If $nPrimes = 0 Or $oNetCode = 0 Then ; Load CalcPrimesVB.dll and create PrimesClass object $oNetCode = DotNet_LoadAssembly( "CalcPrimesVB.dll" ) $oPrimesClass = DotNet_CreateObject( $oNetCode, "PrimesClass" ) If $nPrimes = 0 Then Return EndIf ; Execute CalcPrimes method and return 1D array of primes Return $oPrimesClass.CalcPrimes( $nPrimes ) EndFunc The user code in the examples is exactly the same. But in a production environment the AutoIt user code is usually compiled into an exe-file. Please compile the user code and double click the exe-file to run it. If the AutoIt user code is compiled into an exe-file and the VB dll-file is stored in the same folder as the exe-file, the AutoIt code is always able to find and load the VB dll-file. Summary C# and VB code through .NET Framework is without any doubt the absolute easiest way to execute compiled code in an AutoIt script. It's especially easy because everything (write, compile, load and execute the code and even create an assembly dll-file) can be done through AutoIt. There is no need for any external tools at all. Usually, only 2 lines of AutoIt code are required to make the compiled code available in an AutoIt script. When it comes to calculations and array manipulations, the difference between C#/VB code and AutoIt code is not that big. Under development of C#/VB code (debug) information can be written to SciTE console or a message box. Syntax errors in the code are reported in SciTE console. Because the compiled code is executed as object methods, this solves an otherwise impossible problem of passing arrays back and forth between AutoIt code and compiled code. Posts below Real C# and VB examples. Four examples about generating a 2D array of random data, sorting the array by one or more columns through an index, converting the 2D array to a 1D array in CSV format, and finally saving the 1D array as a CSV file. Post 2. UDF version of examples in post 2. Also a version with a .NET assembly dll-file. Post 3. Adv. C# and VB examples. An introduction to threading. Post 4. Some considerations regarding calculation of prime numbers. Post 7. Optimizing C# and VB code. Optimizing code through multithreading. Optimizing code by storing array as global variable in VB code, thereby avoiding spending time passing arrays back and forth between AutoIt code and VB code. Post 8. Zip-file The zip-file contains two folders: Examples\ and Includes\. Includes\ only contains DotNetAll.au3. You need AutoIt 3.3.10 or later. Tested on Windows 10, Windows 7 and Windows XP. Comments are welcome. Let me know if there are any issues. UsingCSandVB.7z Edited January 17, 2020 by LarsJ New zip-file Digisoul, mLipok, Zedna and 12 others 14 1 Controls, File Explorer, ROT objects, UI Automation, Windows Message MonitorCompiled code: Accessing AutoIt variables, DotNet.au3 UDF, Using C# and VB codeShell menus: The Context menu, The Favorites menu. Shell related: Control Panel, System Image ListsGraphics related: Rubik's Cube, OpenGL without external libraries, Navigating in an image, Non-rectangular selectionsListView controls: Colors and fonts, Multi-line header, Multi-line items, Checkboxes and icons, Incremental searchListView controls: Virtual ListViews, Editing cells, Data display functions Link to comment Share on other sites More sharing options...
Popular Post LarsJ Posted September 24, 2017 Author Popular Post Share Posted September 24, 2017 (edited) Real C# and VB examples One of the great advantages of AutoIt is that the code development process is easy and fast. One of the (few) disadvantages is that the code execution speed is limited by the fact that AutoIt is an interpreted language. Especially calculations in loops eg. calculations on arrays with many elements can be slow compared to the speed of the same calculations in compiled code. Another problem related to arrays is that AutoIt arrays cannot easily be accessed from compiled languages. Nearly a year ago, a technique was introduced in Accessing AutoIt Variables to access AutoIt arrays from compiled code. But this technique is complicated and external development tools are needed to generate the compiled code. The possibility to use C# and VB code in AutoIt through .NET Framework makes it easy to execute compiled code in an AutoIt script, and it makes it easy to pass arrays back and forth between the AutoIt code and the compiled code. And everything (write, compile, load and execute the code and even create an assembly dll-file) can be done through AutoIt. Although it's different languages, the difference between pure calculations and loops is often not so great. If the C# and VB code is restricted to a single function or a single central loop, that's crucial for the execution speed, it should not be too hard to convert AutoIt code to C# or VB code. With the possibility to use C# and VB code in AutoIt through .NET Framework, processing loops and arrays in compiled code has never been easier. C# and VB are very popular programming languages. There are literally millions of examples on the internet. This means that you almost never have to start completely from scratch. You can almost always find an example to copy. Four new examples In first post, an example of calculating prime numbers has been examined. The C#/VB code is 100 times faster than the AutoIt code. This post is a review of four new examples. The examples are about generating a 2D array of random data, sorting the array by one or more columns through an index, converting the 2D array to a 1D array in CSV format, and finally saving the 1D array as a CSV file. The purpose of the examples is to show how to convert AutoIt code to compiled code in different but common areas. The examples also shows which types of code can be optimized and which types of code cannot be much optimized. The compiled code in these examples is VB code. There is no C# code. For each example there is an implementation in pure AutoIt code, and an implementation in AutoIt/VB code. Files related to optimized AutoIt/VB code has "Opt" in the file name. A little bit of error checking is done in each of the examples. At least checking function parameters. All error checking is of course done in AutoIt code. There seems not to be much point in optimizing error checking with compiled code. The examples are added to the zip-file in bottom of first post. They are stored in Examples\2) Real C# and VB examples\. All four examples are structured in the same way. There is a main folder and two subfolders: Examples\ and Includes\. The subfolders contains a handful of files. The code in the second example is a continuation of the code in the first example. The code in the third example is a continuation of the code in the first and second example. The code in the last example is a continuation of the code in the previous examples. If you want to try all the examples at once you can go directly to the last example. If you run the example with pure AutoIt code and the example with optimized AutoIt/VB code, you can easily get an impression of the speed difference. Because arrays and CSV-files are large arrays and files, they are shown in virtual listviews with _ArrayDisplayEx() and CSVfileDisplay(). These functions are stored in Display\ folder in the zip-file. Below is a review of the four examples with focus on the second example about sorting. Generate random data The code in this example creates a 2D array of random data where the columns can contain 5 different data types: strings, integers, floats (doubles), dates and times. Dates and times are integers on the formats yyyymmdd and hhmmss, and are created as correct dates and times. See Includes\Rand2DArray.txt for documentation. The optimized AutoIt/VB code is about 10 times faster than the pure AutoIt code. Index based sorting A 2D array can be sorted by one or more columns. Sorting by more columns is relevant if the columns contains duplicates. A column can be sorted as either strings or numbers (integers or floats) in ascending or descending order. See Includes\Sort2DArray.txt for documentation. That it's an index based sorting means that the array itself is not sorted but that an index is created that contains the array row indices in an order that matches the sorting order. This is usually a good sorting technique for arrays with many rows and columns where more than one column is included in the sorting. And it makes it possible to sort the same array in several ways by creating multiple sorting indexes. This is the AutoIt sorting code: expandcollapse popup; Index based sorting of a 2D array by one or more columns. Returns the ; sorting index as a DllStruct (Sort2DArray) or an array (Sort2DArrayOpt). Func Sort2DArray( _ $aArray, _ ; The 2D array to be sorted by index $aCompare ) ; Info about columns used in sorting, see 1) in docu ; Check parameters ; ... Local $iRows = UBound( $aArray ) Local $iCmps = UBound( $aCompare ) Local $tIndex = DllStructCreate( "uint[" & $iRows & "]" ) Local $pIndex = DllStructGetPtr( $tIndex ) Static $hDll = DllOpen( "kernel32.dll" ) ; Sorting by multiple columns Local $lo, $hi, $mi, $r, $j For $i = 1 To $iRows - 1 $lo = 0 $hi = $i - 1 Do $r = 0 ; Compare result (-1,0,1) $j = 0 ; Index in $aCompare array $mi = Int( ( $lo + $hi ) / 2 ) While $r = 0 And $j < $iCmps $r = ( $aCompare[$j][1] ? StringCompare( $aArray[$i][$aCompare[$j][0]], $aArray[DllStructGetData($tIndex,1,$mi+1)][$aCompare[$j][0]] ) : _ $aArray[$i][$aCompare[$j][0]] < $aArray[DllStructGetData($tIndex,1,$mi+1)][$aCompare[$j][0]] ? -1 : _ $aArray[$i][$aCompare[$j][0]] > $aArray[DllStructGetData($tIndex,1,$mi+1)][$aCompare[$j][0]] ? 1 : 0 ) * $aCompare[$j][2] $j += 1 WEnd Switch $r Case -1 $hi = $mi - 1 Case 1 $lo = $mi + 1 Case 0 ExitLoop EndSwitch Until $lo > $hi DllCall( $hDll, "none", "RtlMoveMemory", "struct*", $pIndex+($mi+1)*4, "struct*", $pIndex+$mi*4, "ulong_ptr", ($i-$mi)*4 ) DllStructSetData( $tIndex, 1, $i, $mi+1+($lo=$mi+1) ) Next Return $tIndex EndFunc Note that a DllStruct is used to implement the sorting index. And this is the VB sorting code: Public Function Sort2DArray( aObjects As Object(,), aCompare As Object(,) ) As Integer() Dim iRows As Integer = aObjects.GetUpperBound(1) + 1 Dim iCmps As Integer = aCompare.GetUpperBound(1) + 1 Dim MyList As New Generic.List( Of Integer ) MyList.Capacity = iRows MyList.Add(0) 'Sorting by multiple columns Dim lo, hi, mi, r, j As Integer For i As Integer = 1 To iRows - 1 lo = 0 hi = i - 1 Do r = 0 'Compare result (-1,0,1) j = 0 'Index in $aCompare array mi = ( lo + hi ) / 2 While r = 0 And j < iCmps r = If( aCompare(1,j), String.Compare( aObjects(aCompare(0,j),i), aObjects(aCompare(0,j),MyList.Item(mi)) ), If( aObjects(aCompare(0,j),i) < aObjects(aCompare(0,j),MyList.Item(mi)), -1, If( aObjects(aCompare(0,j),i) > aObjects(aCompare(0,j),MyList.Item(mi)), 1, 0 ) ) ) * aCompare(2,j) j += 1 End While Select r Case -1 hi = mi - 1 Case 1 lo = mi + 1 Case 0 Exit Do End Select Loop Until lo > hi MyList.Insert( If(lo=mi+1,mi+1,mi), i ) Next Return MyList.ToArray() End Function Here MyList is used to implement the sorting index. A list in VB is a 1D array where items can be inserted into the list without the need for manually (in a loop) to move subsequent items a row down to get room for the new item. This is handled internally in the implementation of the list. In both the AutoIt and VB code, the index is created through a binary sorting and insertion algorithm. Note the similarity between the AutoIt and the VB code. Due to the list the VB code seems to be the easiest code. The optimized AutoIt/VB code is about 10 times faster than the pure AutoIt code. Convert array The 2D array is converted into a 1D array of strings in CSV format. The most remarkable of this example is that the AutoIt/VB code is only 1-2 times faster than the pure AutoIt code. There are mainly two reasons for this. Firstly, it's very simple code and that's to the advantage of the AutoIt code. Secondly, the COM conversions (discussed in a section in Accessing AutoIt Variables) plays an important role. And that's to the disadvantage of the VB code. Save array In the last example the 1D array of strings is saved as a CSV file. The AutoIt/VB code is a bit faster than the pure AutoIt code. The execution speed is predominantly determined by how fast a file can be written to the disk drive. This takes approximately equal time in VB and AutoIt code. Note that the VB code does not contain a loop to save the strings. There is an internal function for storing a 1D array of strings. Zip-file at bottom of first post is updated. Edited October 14, 2017 by LarsJ "Save array" section updated Digisoul, IndianSage, ptrex and 2 others 5 Controls, File Explorer, ROT objects, UI Automation, Windows Message MonitorCompiled code: Accessing AutoIt variables, DotNet.au3 UDF, Using C# and VB codeShell menus: The Context menu, The Favorites menu. Shell related: Control Panel, System Image ListsGraphics related: Rubik's Cube, OpenGL without external libraries, Navigating in an image, Non-rectangular selectionsListView controls: Colors and fonts, Multi-line header, Multi-line items, Checkboxes and icons, Incremental searchListView controls: Virtual ListViews, Editing cells, Data display functions Link to comment Share on other sites More sharing options...
LarsJ Posted September 24, 2017 Author Share Posted September 24, 2017 (edited) UDF Version of examples "Examples\3) UDF version of examples" in zip-file in bottom of first post contains a version of the optimized AutoIt/VB examples in the post above implemented as a single UDF. There is also a DLL-version of the UDF. Note the initialization function at the top of Includes\ArrayFuncsOpt.au3: Func ArrayFuncsOptInit() Static $oNetCode = 0, $oArrayClass = 0 If IsObj( $oArrayClass ) Then Return $oArrayClass ; Compile and load VB code, create ArrayClass object $oNetCode = DotNet_LoadVBcode( FileRead( "..\Includes\ArrayFuncsOpt.vb" ), "System.dll" ) $oArrayClass = DotNet_CreateObject( $oNetCode, "ArrayClass" ) Return $oArrayClass EndFunc $oNetCode is a reference to the VB code. $oArrayClass is an object that's created from the ArrayClass in the VB code. These variables are static to be able to avoid any global variables. The initialization function is usually called at the beginning of the user script. But the initialization function is also called in each of the four functions in ArrayFuncsOpt.au3 in this way: Static $oArrayClass = 0 If $oArrayClass = 0 Then $oArrayClass = ArrayFuncsOptInit() If the initialization function isn't called it'll work anyway. This is very convenient for example in relation to tests. DLL-version In a production environment the compiled VB code should be stored in a .NET assembly DLL-file, and the AutoIt user code should be compiled as an EXE-file. In the DLL-version the initialization function looks like this: Func ArrayFuncsOptInit() Static $oNetCode = 0, $oArrayClass = 0 If IsObj( $oArrayClass ) Then Return $oArrayClass ; Load ArrayFuncsOpt.dll and create ArrayClass object $oNetCode = DotNet_LoadAssembly( "ArrayFuncsOpt.dll" ) $oArrayClass = DotNet_CreateObject( $oNetCode, "ArrayClass" ) Return $oArrayClass EndFunc If the .NET assembly DLL-file is stored in the same folder as the AutoIt EXE-file, the AutoIt code is always able to find and load the DLL-file. Edited October 14, 2017 by LarsJ Updated IndianSage and Earthshine 2 Controls, File Explorer, ROT objects, UI Automation, Windows Message MonitorCompiled code: Accessing AutoIt variables, DotNet.au3 UDF, Using C# and VB codeShell menus: The Context menu, The Favorites menu. Shell related: Control Panel, System Image ListsGraphics related: Rubik's Cube, OpenGL without external libraries, Navigating in an image, Non-rectangular selectionsListView controls: Colors and fonts, Multi-line header, Multi-line items, Checkboxes and icons, Incremental searchListView controls: Virtual ListViews, Editing cells, Data display functions Link to comment Share on other sites More sharing options...
LarsJ Posted September 24, 2017 Author Share Posted September 24, 2017 (edited) Adv. C# and VB examples Threading Perform a lengthy calculation of prime numbers in a worker thread in VB code while the AutoIt script is still responsive in the main thread. Code in "Examples\4) Adv. C# and VB examples\Threading" in zip-file in bottom of first post. Copy code We'll start by copying TemplateVB.au3 and TemplateVB.vb from "Examples\1) Introductory C# and VB examples\0) Code templates". The files are renamed to tst00.au3 and tst00.vb. tst00.vb: Imports System Class Au3Class Public Sub MyMethod() Console.WriteLine( "Hello world from VB!" ) End Sub End Class tst00.au3: #include "..\..\..\Includes\DotNetAll.au3" Opt( "MustDeclareVars", 1 ) Example() Func Example() Local $oNetCode = DotNet_LoadVBcode( FileRead( "tst00.vb" ), "System.dll" ) Local $oAu3Class = DotNet_CreateObject( $oNetCode, "Au3Class" ) $oAu3Class.MyMethod() EndFunc #cs Hello world from VB! #ce Copy tst00.au3/tst00.vb to tst01.au3/tst01.vb. Copy this VB code from Microsoft documentation to tst01.vb. tst01.vb must look like this: 'https://msdn.microsoft.com/en-us/library/system.threading.thread(v=vs.110).aspx?cs-save-lang=1&cs-lang=vb#Starting Imports System Imports System.Threading Imports System.Diagnostics Class Au3Class Public Sub MyMethod() Dim th As New Thread(AddressOf ExecuteInForeground) th.Start() Thread.Sleep(1000) Console.WriteLine("Main thread ({0}) exiting...", Thread.CurrentThread.ManagedThreadId) End Sub Private Sub ExecuteInForeground() Dim start As DateTime = DateTime.Now Dim sw As Stopwatch = Stopwatch.StartNew() Console.WriteLine("Thread {0}: {1}, Priority {2}", Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.ThreadState, Thread.CurrentThread.Priority) Do Console.WriteLine("Thread {0}: Elapsed {1:N2} seconds", Thread.CurrentThread.ManagedThreadId, sw.ElapsedMilliseconds / 1000) Thread.Sleep(500) Loop While sw.ElapsedMilliseconds <= 5000 sw.Stop() End Sub End Class tst01.au3: #include "..\..\..\Includes\DotNetAll.au3" Opt( "MustDeclareVars", 1 ) Example() Func Example() Local $oNetCode = DotNet_LoadVBcode( FileRead( "tst01.vb" ), "System.dll" ) Local $oAu3Class = DotNet_CreateObject( $oNetCode, "Au3Class" ) $oAu3Class.MyMethod() EndFunc #cs Thread 3: Running, Priority Normal Thread 3: Elapsed 0,00 seconds Thread 3: Elapsed 0,50 seconds Main thread (1) exiting... Thread 3: Elapsed 1,02 seconds #ce When we run the code in tst01.au3 there seems to be something wrong. Some of the console output is missing. The problem is that there is no code in tst01.au3 to keep the script running. tst01.au3 exits as soon as $oAu3Class.MyMethod() has been executed. And when tst01.au3 exits, the .NET Framework host exits, the VB code exits and the worker thread that was created within the VB code exits. Lets add a Sleep(5000) to keep the script alive: tst02.au3: #include "..\..\..\Includes\DotNetAll.au3" Opt( "MustDeclareVars", 1 ) Example() Func Example() Local $oNetCode = DotNet_LoadVBcode( FileRead( "tst01.vb" ), "System.dll" ) Local $oAu3Class = DotNet_CreateObject( $oNetCode, "Au3Class" ) $oAu3Class.MyMethod() Sleep(5000) EndFunc #cs Thread 3: Running, Priority Normal Thread 3: Elapsed 0,00 seconds Thread 3: Elapsed 0,51 seconds Main thread (1) exiting... Thread 3: Elapsed 1,03 seconds Thread 3: Elapsed 1,54 seconds Thread 3: Elapsed 2,06 seconds Thread 3: Elapsed 2,57 seconds Thread 3: Elapsed 3,09 seconds Thread 3: Elapsed 3,60 seconds Thread 3: Elapsed 4,12 seconds Thread 3: Elapsed 4,63 seconds #ce Now the output is similar to the output of the Microsoft example. Add a ConsoleWrite before and after the Sleep. tst03.au3: #include "..\..\..\Includes\DotNetAll.au3" Opt( "MustDeclareVars", 1 ) Example() Func Example() Local $oNetCode = DotNet_LoadVBcode( FileRead( "tst01.vb" ), "System.dll" ) Local $oAu3Class = DotNet_CreateObject( $oNetCode, "Au3Class" ) $oAu3Class.MyMethod() ConsoleWrite( "Sleep starts" & @CRLF ) Sleep(5000) ConsoleWrite( "Sleep stops" & @CRLF ) EndFunc #cs Thread 3: Running, Priority Normal Thread 3: Elapsed 0,00 seconds Thread 3: Elapsed 0,50 seconds Main thread (1) exiting... Sleep starts Thread 3: Elapsed 1,01 seconds Thread 3: Elapsed 1,53 seconds Thread 3: Elapsed 2,04 seconds Thread 3: Elapsed 2,56 seconds Thread 3: Elapsed 3,07 seconds Thread 3: Elapsed 3,59 seconds Thread 3: Elapsed 4,10 seconds Thread 3: Elapsed 4,62 seconds Sleep stops #ce The VB code in the worker thread is clearly running while the AutoIt code in the main thread is sleeping. Use counter Add a counter to the VB code. tst04.vb: Imports System Imports System.Threading Imports System.Diagnostics Class Au3Class Dim iCounter As Integer = 0 Public Function GetCounter() As Integer Return iCounter End Function Public Sub MyMethod() Dim th As New Thread(AddressOf ExecuteInForeground) th.Start() Thread.Sleep(1000) Console.WriteLine("Main thread ({0}) exiting...", Thread.CurrentThread.ManagedThreadId) End Sub Private Sub ExecuteInForeground() Dim start As DateTime = DateTime.Now Dim sw As Stopwatch = Stopwatch.StartNew() Console.WriteLine("Thread {0}: {1}, Priority {2}", Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.ThreadState, Thread.CurrentThread.Priority) Do Console.WriteLine("Thread {0}: Elapsed {1:N2} seconds", Thread.CurrentThread.ManagedThreadId, sw.ElapsedMilliseconds / 1000) iCounter += 1 Thread.Sleep(500) Loop While sw.ElapsedMilliseconds <= 5000 sw.Stop() End Sub End Class Replace the Sleep with a While loop in the AutoIt code. tst04.au3: expandcollapse popup#include "..\..\..\Includes\DotNetAll.au3" Opt( "MustDeclareVars", 1 ) Example() Func Example() Local $oNetCode = DotNet_LoadVBcode( FileRead( "tst04.vb" ), "System.dll" ) Local $oAu3Class = DotNet_CreateObject( $oNetCode, "Au3Class" ) $oAu3Class.MyMethod() Local $iCounter = 0 While $iCounter < 10 $iCounter = $oAu3Class.GetCounter() ConsoleWrite( "$iCounter = " & $iCounter & @CRLF ) Sleep( 250 ) WEnd EndFunc #cs Thread 3: Running, Priority Normal Thread 3: Elapsed 0,00 seconds Thread 3: Elapsed 0,51 seconds Main thread (1) exiting... $iCounter = 2 Thread 3: Elapsed 1,02 seconds $iCounter = 3 $iCounter = 3 Thread 3: Elapsed 1,54 seconds $iCounter = 4 $iCounter = 4 Thread 3: Elapsed 2,05 seconds $iCounter = 5 $iCounter = 5 Thread 3: Elapsed 2,57 seconds $iCounter = 6 $iCounter = 6 Thread 3: Elapsed 3,08 seconds $iCounter = 7 $iCounter = 7 Thread 3: Elapsed 3,60 seconds $iCounter = 8 Thread 3: Elapsed 4,11 seconds $iCounter = 9 $iCounter = 9 Thread 3: Elapsed 4,63 seconds $iCounter = 10 #ce Prime numbers At this point we can insert the VB code and AutoIt code to calculate prime numbers. tst05.vb: expandcollapse popupImports System Imports System.Threading Class Au3Class Dim aPrimes(1) As Integer Dim iCntPrimes As Integer = 0 Public Function GetNumberOfPrimes() As Integer Return iCntPrimes End Function Public Function GetArrayOfPrimes() As Integer() Return aPrimes End Function Public Sub CalcPrimes( nPrimes As Integer ) Dim th As New Thread( AddressOf CalcPrimesSub ) th.Start( nPrimes ) End Sub Private Sub CalcPrimesSub( oPrimes As Object ) Dim nPrimes As Integer = CInt( oPrimes ) : ReDim aPrimes(nPrimes-1) Dim iPrime As Integer = 2, iPrimes As Integer = 0, i As Integer If nPrimes <= 100 Then Console.WriteLine( iPrime ) 'Store first prime aPrimes(iPrimes) = iPrime iPrimes += 1 iPrime += 1 'Loop to calculate primes While iPrimes < nPrimes For i = 0 To iPrimes - 1 If iPrime Mod aPrimes(i) = 0 Then Exit For Next If i = iPrimes Then If nPrimes <= 100 Then Console.WriteLine( iPrime ) aPrimes(iPrimes) = iPrime iPrimes += 1 If iPrimes Mod 1000 = 0 Then iCntPrimes = iPrimes End If iPrime += 1 End While iCntPrimes = iPrimes End Sub End Class tst05.au3: expandcollapse popup#include "..\..\..\Includes\DotNetAll.au3" #include "..\..\..\Display\Functions\ArrayDisplayEx\ArrayDisplayEx.au3" Opt( "MustDeclareVars", 1 ) Example() Func Example() Local $oNetCode = DotNet_LoadVBcode( FileRead( "tst05.vb" ), "System.dll" ) Local $oAu3Class = DotNet_CreateObject( $oNetCode, "Au3Class" ) Local $iPrimes = 100000 $oAu3Class.CalcPrimes( $iPrimes ) Local $iCntPrimes = 0 While $iCntPrimes < $iPrimes $iCntPrimes = $oAu3Class.GetNumberOfPrimes() ConsoleWrite( "$iCntPrimes = " & $iCntPrimes & @CRLF ) Sleep( 1000 ) WEnd Local $aPrimes = $oAu3Class.GetArrayOfPrimes() _ArrayDisplayEx( $aPrimes ) EndFunc #cs $iCntPrimes = 0 $iCntPrimes = 24000 $iCntPrimes = 34000 $iCntPrimes = 42000 $iCntPrimes = 49000 $iCntPrimes = 55000 $iCntPrimes = 60000 $iCntPrimes = 65000 $iCntPrimes = 69000 $iCntPrimes = 74000 $iCntPrimes = 78000 $iCntPrimes = 81000 $iCntPrimes = 85000 $iCntPrimes = 88000 $iCntPrimes = 92000 $iCntPrimes = 95000 $iCntPrimes = 98000 $iCntPrimes = 100000 #ce AutoIt GUI Finally we can make a GUI. tst06.au3: expandcollapse popup#AutoIt3Wrapper_Au3Check_Parameters=-d -w- 1 -w 2 -w 3 -w 4 -w 5 -w 6 #include <GUIConstantsEx.au3> #include <WindowsConstants.au3> #include "..\..\..\Includes\DotNetAll.au3" #include "..\..\..\Display\Functions\ArrayDisplayEx\ArrayDisplayEx.au3" Opt( "MustDeclareVars", 1 ) Example() Func Example() Local $oNetCode = DotNet_LoadVBcode( FileRead( "tst05.vb" ), "System.dll" ) Local $oAu3Class = DotNet_CreateObject( $oNetCode, "Au3Class" ) Local $hGui1 = GUICreate( "Prime numbers", 300, 100, 250, 250 ) Local $idLabel = GUICtrlCreateLabel( "Primes:", 20, 22, 35, 18 ) Local $idCombo = GUICtrlCreateCombo( "", 60, 20, 80, 20 ) GUICtrlSetData( $idCombo, "50000|100000|150000|200000|250000", "50000" ) Local $idPrimes = GUICtrlCreateButton( "Calculate primes", 160, 20, 120, 20 ) Local $idButton1 = GUICtrlCreateButton( "Button1", 20, 60, 120, 20 ) Local $idButton2 = GUICtrlCreateButton( "Button2", 160, 60, 120, 20 ) GUISetState() Local $s, $hGui2, $idProgress, $idPrgPct, $hTimer = 0, $iTimes, $nPrimes, $iPrimes While 1 Switch GUIGetMsg() Case $idPrimes GUICtrlSetState( $idPrimes, $GUI_DISABLE ) $nPrimes = Int( GUICtrlRead( $idCombo ) ) $oAu3Class.CalcPrimes( $nPrimes ) $s = StringRegExpReplace( $nPrimes, "(\d{1,3}(?=(\d{3})+\z)|\d{3}(?=\d))", "\1,") $hGui2 = GUICreate( "Calculating " & $s & " primes ...", 340, 90, 350, 400, $WS_OVERLAPPED ) $idProgress = GUICtrlCreateProgress( 20, 20, 260, 20 ) $idPrgPct = GUICtrlCreateLabel( "0%", 290, 23, 25, 18 ) $hTimer = TimerInit() $iTimes = 1 $iPrimes = 0 GUISetState() Case $idButton1 MsgBox( 0, "Button1", "Button1", 1, $hGui1 ) Case $idButton2 MsgBox( 0, "Button2", "Button2", 1, $hGui1 ) Case $GUI_EVENT_CLOSE ExitLoop Case Else If $hTimer And TimerDiff( $hTimer ) > $iTimes * 1000 Then $iTimes += 1 $iPrimes = $oAu3Class.GetNumberOfPrimes() GUICtrlSetData( $idProgress, 100*$iPrimes/$nPrimes ) GUICtrlSetData( $idPrgPct, Int( 100*$iPrimes/$nPrimes ) & "%" ) If $iPrimes = $nPrimes Then Sleep(1000) $hTimer = 0 GUIDelete( $hGui2 ) GUICtrlSetState( $idPrimes, $GUI_ENABLE ) _ArrayDisplayEx( $oAu3Class.GetArrayOfPrimes() ) EndIf EndIf EndSwitch WEnd GUIDelete( $hGui2 ) GUIDelete( $hGui1 ) #forceref $idLabel EndFunc You can click Button1 and Button2 while the prime numbers are calculated. The MsgBoxes closes automatically after one second. Edited October 14, 2017 by LarsJ Updated Earthshine 1 Controls, File Explorer, ROT objects, UI Automation, Windows Message MonitorCompiled code: Accessing AutoIt variables, DotNet.au3 UDF, Using C# and VB codeShell menus: The Context menu, The Favorites menu. Shell related: Control Panel, System Image ListsGraphics related: Rubik's Cube, OpenGL without external libraries, Navigating in an image, Non-rectangular selectionsListView controls: Colors and fonts, Multi-line header, Multi-line items, Checkboxes and icons, Incremental searchListView controls: Virtual ListViews, Editing cells, Data display functions Link to comment Share on other sites More sharing options...
LarsJ Posted October 8, 2017 Author Share Posted October 8, 2017 Real VB examples added to post 2. New zip in bottom of first post. Earthshine 1 Controls, File Explorer, ROT objects, UI Automation, Windows Message MonitorCompiled code: Accessing AutoIt variables, DotNet.au3 UDF, Using C# and VB codeShell menus: The Context menu, The Favorites menu. Shell related: Control Panel, System Image ListsGraphics related: Rubik's Cube, OpenGL without external libraries, Navigating in an image, Non-rectangular selectionsListView controls: Colors and fonts, Multi-line header, Multi-line items, Checkboxes and icons, Incremental searchListView controls: Virtual ListViews, Editing cells, Data display functions Link to comment Share on other sites More sharing options...
LarsJ Posted October 14, 2017 Author Share Posted October 14, 2017 A few updates to the examples in post 2 "2) Real C# and VB examples\1) Index based sorting\Includes\Sort2DArrayOpt.au3" System.Collections.dll is not needed in DotNet_LoadVBcode(). "2) Real C# and VB examples\2) Convert array\Includes\Conv2DArrayOpt.vb" CType( aObjects(j,i), String ) replaced with CStr( aObjects(j,i) ). "2) Real C# and VB examples\3) Save array\Includes\Save1DArrayOpt.au3" System.IO.dll and System.Text.Encoding.dll are not needed in DotNet_LoadVBcode(). "2) Real C# and VB examples\3) Save array\Examples\ExampleOpt.au3" In the previous version (ExampleOpt-a.au3), I had forgotten to initialize the VB code with Save1DArrayOptInit() in top of the script. This meant that the initialization instead was performed at the top of Save1DArrayOpt() function, and that the runtime for the initialization (about 100 milliseconds) was included in the runtime for Save1DArrayOpt(). That made the total runtime for Save1DArrayOpt() about 100 milliseconds too high. I've updated ExampleOpt.au3 and included Save1DArrayOptInit() in top of the script. This makes Save1DArrayOpt() about 100 milliseconds faster. And now, the optimized AutoIt/VB code is a bit faster than the pure AutoIt code. In the old version, the pure AutoIt code was fastest. I've updated the conclusion about the runtimes in "Save array" section in bottom of post 2. Post 3 UDF version of examples in post 2 Post 4 Threading Earthshine, argumentum and junkew 3 Controls, File Explorer, ROT objects, UI Automation, Windows Message MonitorCompiled code: Accessing AutoIt variables, DotNet.au3 UDF, Using C# and VB codeShell menus: The Context menu, The Favorites menu. Shell related: Control Panel, System Image ListsGraphics related: Rubik's Cube, OpenGL without external libraries, Navigating in an image, Non-rectangular selectionsListView controls: Colors and fonts, Multi-line header, Multi-line items, Checkboxes and icons, Incremental searchListView controls: Virtual ListViews, Editing cells, Data display functions Link to comment Share on other sites More sharing options...
LarsJ Posted October 21, 2017 Author Share Posted October 21, 2017 Prime numbers To illustrate some of the issues associated with using C# and VB code, I've used an example for calculating prime numbers. A prime number (or a prime) is a natural number greater than 1 that has no positive divisors other than 1 and itself. Therefore, this loop can be used to determine whether a number is a prime number: For $i = 0 To $iPrimes - 1 If Mod( $iPrime, $aPrimes[$i] ) = 0 Then ExitLoop Next $iPrime is the candidate number to check, $aPrimes contains the existing prime numbers and $iPrimes is the number of existing primes. If the loop is interrupted before all existing prime numbers are checked, it's not a prime number. There is nothing wrong with this loop to determine prime numbers. But the loop can be optimized much because it's not necessary to check all existing prime numbers. It's enough to check primes up to and including the number given by this formula: Ceiling(Sqrt($iPrime)). Then the loop can be coded in this way instead of: $iMaxFactor = Ceiling(Sqrt($iPrime)) While $i < $iPrimes And $aPrimes[$i] <= $iMaxFactor If Mod( $iPrime, $aPrimes[$i] ) = 0 Then $i = $iPrimes $i += 1 WEnd Again if the loop is interrupted before all prime numbers are checked, it's not a prime. The second version of the loop is a huge performance improvement. All examples are updated with the new version of the loop. The 5 numbers in the range 50,000 - 250,000 in tst06.au3 in post 4 (the example with the progress bar) have been replaced with 5 numbers in the range 3,000,000 - 7,000,000. And that, of course, is much more fun. Controls, File Explorer, ROT objects, UI Automation, Windows Message MonitorCompiled code: Accessing AutoIt variables, DotNet.au3 UDF, Using C# and VB codeShell menus: The Context menu, The Favorites menu. Shell related: Control Panel, System Image ListsGraphics related: Rubik's Cube, OpenGL without external libraries, Navigating in an image, Non-rectangular selectionsListView controls: Colors and fonts, Multi-line header, Multi-line items, Checkboxes and icons, Incremental searchListView controls: Virtual ListViews, Editing cells, Data display functions Link to comment Share on other sites More sharing options...
Popular Post LarsJ Posted October 21, 2017 Author Popular Post Share Posted October 21, 2017 Optimizing C# and VB code Index based sorting The examples in post 4 is an introduction to threading. The examples here shows how VB code can be optimized using multithreading. The examples are a continuation of the code in post 2 regarding index based sorting. Multithreaded sorting The idea is that if 4 threads are used to sort 100,000 rows, the first thread can sort the first 25,000 rows, the next thread can sort the next 25,000 rows, etc. It results in 4 indexes that finally can be combined into one big index. This is the code in Sort2DArrayOptEx.vb: expandcollapse popupImports System Imports System.Threading Imports System.Collections Class SortArrayClass Dim aObjects, aCompare As Object(,) Dim iPartRows, iRestRows, iCmps As Integer Dim iThreads = 4, aIndex(iThreads)(), aRows(iThreads) As Integer Public Function Sort2DArray( aObjPar As Object(,), aCompPar As Object(,) ) As Integer() aObjects = aObjPar : aCompare = aCompPar Dim iRows As Integer = aObjects.GetUpperBound(1) + 1 iRestRows = iRows Mod iThreads : iPartRows = ( iRows - iRestRows ) / iThreads iCmps = aCompare.GetUpperBound(1) + 1 'Multithreaded index based sorting Dim th0 As New Thread( AddressOf Sort2DArraySub ) : th0.Start(0) Dim th1 As New Thread( AddressOf Sort2DArraySub ) : th1.Start(1) Dim th2 As New Thread( AddressOf Sort2DArraySub ) : th2.Start(2) Dim th3 As New Thread( AddressOf Sort2DArraySub ) : th3.Start(3) 'Main thread sleeps until sorting is done While aRows(0) = 0 Or aRows(1) = 0 Or aRows(2) = 0 Or aRows(3) = 0 Thread.Sleep(20) End While 'Combine four indexes into one Dim aIndexAll(iRows-1) As Integer Dim aIndexIdx(iThreads), iLeft, iRight, r, j As Integer For i As Integer = 0 To iRows - 1 iLeft = If( aIndexIdx(0) < aRows(0), 0, If( aIndexIdx(1) < aRows(1), 1, If( aIndexIdx(2) < aRows(2), 2, 3 ) ) ) iRight = iLeft While iRight < iThreads iRight += 1 Select iRight Case 1 iRight = If( aIndexIdx(1) < aRows(1), 1, If( aIndexIdx(2) < aRows(2), 2, If( aIndexIdx(3) < aRows(3), 3, iThreads ) ) ) Case 2 iRight = If( aIndexIdx(2) < aRows(2), 2, If( aIndexIdx(3) < aRows(3), 3, iThreads ) ) Case 3 iRight = If( aIndexIdx(3) < aRows(3), 3, iThreads ) End Select If iRight = iThreads Then Exit While r = 0 'Compare result (-1,0,1) j = 0 'Index in $aCompare array While r = 0 And j < iCmps r = If( aCompare(1,j), String.Compare( aObjects(aCompare(0,j),aIndex(iLeft)(aIndexIdx(iLeft))), aObjects(aCompare(0,j),aIndex(iRight)(aIndexIdx(iRight))) ), If( aObjects(aCompare(0,j),aIndex(iLeft)(aIndexIdx(iLeft))) < aObjects(aCompare(0,j),aIndex(iRight)(aIndexIdx(iRight))), -1, If( aObjects(aCompare(0,j),aIndex(iLeft)(aIndexIdx(iLeft))) > aObjects(aCompare(0,j),aIndex(iRight)(aIndexIdx(iRight))), 1, 0 ) ) ) * aCompare(2,j) j += 1 End While Select r Case 1 iLeft = iRight Case 0 If aIndex(iLeft)(aIndexIdx(iLeft)) > aIndex(iRight)(aIndexIdx(iRight)) Then iLeft = iRight End Select End While aIndexAll(i) = aIndex(iLeft)(aIndexIdx(iLeft)) aIndexIdx(iLeft) += 1 Next Return aIndexAll End Function 'Multithreaded index based sorting Private Sub Sort2DArraySub( oThread As Object ) Dim iThread As Integer = CInt( oThread ) Dim iFirstRow As Integer = If( iThread < iRestRows, iPartRows * iThread + iThread, iPartRows * iThread + iRestRows ) Dim iLastRow As Integer = iFirstRow + If( iThread < iRestRows, iPartRows, iPartRows - 1 ) Dim iRows As Integer = iLastRow - iFirstRow + 1 Dim MyList As New Generic.List( Of Integer ) MyList.Capacity = iRows MyList.Add(iFirstRow) 'Sorting by multiple columns Dim lo, hi, mi, r, j As Integer For i As Integer = iFirstRow + 1 To iLastRow lo = iFirstRow hi = i - 1 Do r = 0 'Compare result (-1,0,1) j = 0 'Index in $aCompare array mi = ( lo + hi ) / 2 While r = 0 And j < iCmps r = If( aCompare(1,j), String.Compare( aObjects(aCompare(0,j),i), aObjects(aCompare(0,j),MyList.Item(mi-iFirstRow)) ), If( aObjects(aCompare(0,j),i) < aObjects(aCompare(0,j),MyList.Item(mi-iFirstRow)), -1, If( aObjects(aCompare(0,j),i) > aObjects(aCompare(0,j),MyList.Item(mi-iFirstRow)), 1, 0 ) ) ) * aCompare(2,j) j += 1 End While Select r Case -1 hi = mi - 1 Case 1 lo = mi + 1 Case 0 Exit Do End Select Loop Until lo > hi MyList.Insert( If(lo=mi+1,mi+1,mi)-iFirstRow, i ) Next aIndex(iThread) = MyList.ToArray() aRows(iThread) = iRows End Sub End Class In Runtimes.au3 the execution times for sorting arrays with different number of rows are measuered for pure AutoIt code, for optimized VB code and for multithreaded VB code. This is the results on my PC: expandcollapse popupCode executed as 32 bit code Code executed as 64 bit code ============================ ============================ $iRows = 100, 6 columns $iRows = 100, 6 columns Sort array by columns 0, 1, 2 Sort array by columns 0, 1, 2 ----------------------------------- ----------------------------------- Pure AutoIt code: 4.3340 Pure AutoIt code: 3.7655 Optimized VB code: 1.2553 Optimized VB code: 1.4281 Multithreaded VB code: 19.0067 Multithreaded VB code: 33.5323 $iRows = 500, 6 columns $iRows = 500, 6 columns Sort array by columns 0, 1, 2 Sort array by columns 0, 1, 2 ----------------------------------- ----------------------------------- Pure AutoIt code: 27.5916 Pure AutoIt code: 25.5753 Optimized VB code: 2.6330 Optimized VB code: 2.8050 Multithreaded VB code: 31.2496 Multithreaded VB code: 36.4108 $iRows = 1000, 6 columns $iRows = 1000, 6 columns Sort array by columns 0, 1, 2 Sort array by columns 0, 1, 2 ----------------------------------- ----------------------------------- Pure AutoIt code: 106.4464 Pure AutoIt code: 90.3135 Optimized VB code: 35.7356 Optimized VB code: 27.4234 Multithreaded VB code: 64.3406 Multithreaded VB code: 57.4474 $iRows = 2000, 6 columns $iRows = 2000, 6 columns Sort array by columns 0, 1, 2 Sort array by columns 0, 1, 2 ----------------------------------- ----------------------------------- Pure AutoIt code: 142.1940 Pure AutoIt code: 134.2208 Optimized VB code: 11.6813 Optimized VB code: 10.9643 Multithreaded VB code: 30.2622 Multithreaded VB code: 33.4628 $iRows = 5000, 6 columns $iRows = 5000, 6 columns Sort array by columns 0, 1, 2 Sort array by columns 0, 1, 2 ----------------------------------- ----------------------------------- Pure AutoIt code: 401.1128 Pure AutoIt code: 365.6661 Optimized VB code: 38.3885 Optimized VB code: 31.2551 Multithreaded VB code: 79.6997 Multithreaded VB code: 89.7060 $iRows = 10000, 6 columns $iRows = 10000, 6 columns Sort array by columns 0, 1, 2 Sort array by columns 0, 1, 2 ----------------------------------- ----------------------------------- Pure AutoIt code: 826.2889 Pure AutoIt code: 691.7514 Optimized VB code: 70.5041 Optimized VB code: 65.1964 Multithreaded VB code: 89.2076 Multithreaded VB code: 60.4300 $iRows = 20000, 6 columns $iRows = 20000, 6 columns Sort array by columns 0, 1, 2 Sort array by columns 0, 1, 2 ----------------------------------- ----------------------------------- Pure AutoIt code: 1816.3940 Pure AutoIt code: 1517.2513 Optimized VB code: 183.4977 Optimized VB code: 151.7480 Multithreaded VB code: 150.1049 Multithreaded VB code: 135.3445 $iRows = 50000, 6 columns $iRows = 50000, 6 columns Sort array by columns 0, 1, 2 Sort array by columns 0, 1, 2 ----------------------------------- ----------------------------------- Pure AutoIt code: 5386.5884 Pure AutoIt code: 4239.2327 Optimized VB code: 602.9203 Optimized VB code: 471.3676 Multithreaded VB code: 373.1342 Multithreaded VB code: 371.5393 $iRows = 100000, 6 columns $iRows = 100000, 6 columns Sort array by columns 0, 1, 2 Sort array by columns 0, 1, 2 ----------------------------------- ----------------------------------- Pure AutoIt code: 12798.0606 Pure AutoIt code: 9489.1929 Optimized VB code: 1590.7280 Optimized VB code: 1273.4829 Multithreaded VB code: 800.1118 Multithreaded VB code: 684.8815 $iRows = 250000, 6 columns $iRows = 250000, 6 columns Sort array by columns 0, 1, 2 Sort array by columns 0, 1, 2 ----------------------------------- ----------------------------------- Pure AutoIt code: 44781.3723 Pure AutoIt code: 27407.9705 Optimized VB code: 7217.6899 Optimized VB code: 5222.3106 Multithreaded VB code: 2330.1729 Multithreaded VB code: 1914.2506 $iRows = 500000, 6 columns $iRows = 500000, 6 columns Sort array by columns 0, 1, 2 Sort array by columns 0, 1, 2 ----------------------------------- ----------------------------------- Pure AutoIt code: 128479.2462 Pure AutoIt code: 69438.5295 Optimized VB code: 25016.8543 Optimized VB code: 16692.2426 Multithreaded VB code: 5949.0161 Multithreaded VB code: 4565.7862 $iRows = 750000, 6 columns $iRows = 750000, 6 columns Sort array by columns 0, 1, 2 Sort array by columns 0, 1, 2 ----------------------------------- ----------------------------------- Pure AutoIt code: 250176.5552 Pure AutoIt code: 107785.2414 Optimized VB code: 54193.4737 Optimized VB code: 35217.0613 Multithreaded VB code: 10827.3308 Multithreaded VB code: 8186.0242 $iRows = 1000000, 6 columns $iRows = 1000000, 6 columns Sort array by columns 0, 1, 2 Sort array by columns 0, 1, 2 ----------------------------------- ----------------------------------- Pure AutoIt code: 408457.7649 Pure AutoIt code: 162282.8254 Optimized VB code: 96291.4960 Optimized VB code: 62412.9602 Multithreaded VB code: 17399.9110 Multithreaded VB code: 12690.8546 $iRows = 2000000, 6 columns $iRows = 2000000, 6 columns Sort array by columns 0, 1, 2 Sort array by columns 0, 1, 2 ----------------------------------- ----------------------------------- Pure AutoIt code: 1445235.5246 Pure AutoIt code: 498648.9821 Optimized VB code: 409872.8400 Optimized VB code: 282116.6518 Multithreaded VB code: 63527.4207 Multithreaded VB code: 51087.1290 Code is added to "Examples\5) Optimizing C# and VB code\1) Index based sorting\" in zip-file in bottom of first post. Implementing a progress bar Another advantage of performing the sorting in 4 worker threads is that the main thread can be released by the VB code and thereby making the AutoIt code responsive while the sorting is going on. This makes it possible to implement a progress bar in the AutoIt code. The code in Sort2DArrayOptEx2.vb is changed a little bit: expandcollapse popupImports System Imports System.Threading Imports System.Collections Class SortArrayClass Dim aObjects, aCompare As Object(,) Dim iRows, iPartRows, iRestRows, iCmps As Integer Dim iThreads = 4, aIndex(iThreads)(), aRows(iThreads) As Integer Public Sub Sort2DArray( aObjPar As Object(,), aCompPar As Object(,) ) aObjects = aObjPar : aCompare = aCompPar : iRows = aObjects.GetUpperBound(1) + 1 iRestRows = iRows Mod iThreads : iPartRows = ( iRows - iRestRows ) / iThreads iCmps = aCompare.GetUpperBound(1) + 1 'Multithreaded index based sorting Dim th0 As New Thread( AddressOf Sort2DArraySub ) : th0.Start(0) Dim th1 As New Thread( AddressOf Sort2DArraySub ) : th1.Start(1) Dim th2 As New Thread( AddressOf Sort2DArraySub ) : th2.Start(2) Dim th3 As New Thread( AddressOf Sort2DArraySub ) : th3.Start(3) End Sub 'Multithreaded index based sorting Private Sub Sort2DArraySub( oThread As Object ) Dim iThread As Integer = CInt( oThread ) Dim iFirstRow As Integer = If( iThread < iRestRows, iPartRows * iThread + iThread, iPartRows * iThread + iRestRows ) Dim iLastRow As Integer = iFirstRow + If( iThread < iRestRows, iPartRows, iPartRows - 1 ) Dim iSubRows As Integer = iLastRow - iFirstRow + 1 Dim MyList As New Generic.List( Of Integer ) MyList.Capacity = iSubRows MyList.Add(iFirstRow) 'Sorting by multiple columns Dim lo, hi, mi, r, j As Integer For i As Integer = iFirstRow + 1 To iLastRow lo = iFirstRow hi = i - 1 Do r = 0 'Compare result (-1,0,1) j = 0 'Index in $aCompare array mi = ( lo + hi ) / 2 While r = 0 And j < iCmps r = If( aCompare(1,j), String.Compare( aObjects(aCompare(0,j),i), aObjects(aCompare(0,j),MyList.Item(mi-iFirstRow)) ), If( aObjects(aCompare(0,j),i) < aObjects(aCompare(0,j),MyList.Item(mi-iFirstRow)), -1, If( aObjects(aCompare(0,j),i) > aObjects(aCompare(0,j),MyList.Item(mi-iFirstRow)), 1, 0 ) ) ) * aCompare(2,j) j += 1 End While Select r Case -1 hi = mi - 1 Case 1 lo = mi + 1 Case 0 Exit Do End Select Loop Until lo > hi MyList.Insert( If(lo=mi+1,mi+1,mi)-iFirstRow, i ) If ( i - iFirstRow ) Mod 1000 = 0 Then aRows(iThread) = i - iFirstRow Next aIndex(iThread) = MyList.ToArray() aRows(iThread) = iSubRows End Sub Public Function Sort2DArrayGetCount() As Integer Return aRows(0) + aRows(1) + aRows(2) + aRows(3) End Function Public Function Sort2DArrayGetIndex() As Integer() 'Combine four indexes into one Dim aIndexAll(iRows-1) As Integer Dim aIndexIdx(iThreads), iLeft, iRight, r, j As Integer For i As Integer = 0 To iRows - 1 iLeft = If( aIndexIdx(0) < aRows(0), 0, If( aIndexIdx(1) < aRows(1), 1, If( aIndexIdx(2) < aRows(2), 2, 3 ) ) ) iRight = iLeft While iRight < iThreads iRight += 1 Select iRight Case 1 iRight = If( aIndexIdx(1) < aRows(1), 1, If( aIndexIdx(2) < aRows(2), 2, If( aIndexIdx(3) < aRows(3), 3, iThreads ) ) ) Case 2 iRight = If( aIndexIdx(2) < aRows(2), 2, If( aIndexIdx(3) < aRows(3), 3, iThreads ) ) Case 3 iRight = If( aIndexIdx(3) < aRows(3), 3, iThreads ) End Select If iRight = iThreads Then Exit While r = 0 'Compare result (-1,0,1) j = 0 'Index in $aCompare array While r = 0 And j < iCmps r = If( aCompare(1,j), String.Compare( aObjects(aCompare(0,j),aIndex(iLeft)(aIndexIdx(iLeft))), aObjects(aCompare(0,j),aIndex(iRight)(aIndexIdx(iRight))) ), If( aObjects(aCompare(0,j),aIndex(iLeft)(aIndexIdx(iLeft))) < aObjects(aCompare(0,j),aIndex(iRight)(aIndexIdx(iRight))), -1, If( aObjects(aCompare(0,j),aIndex(iLeft)(aIndexIdx(iLeft))) > aObjects(aCompare(0,j),aIndex(iRight)(aIndexIdx(iRight))), 1, 0 ) ) ) * aCompare(2,j) j += 1 End While Select r Case 1 iLeft = iRight Case 0 If aIndex(iLeft)(aIndexIdx(iLeft)) > aIndex(iRight)(aIndexIdx(iRight)) Then iLeft = iRight End Select End While aIndexAll(i) = aIndex(iLeft)(aIndexIdx(iLeft)) aIndexIdx(iLeft) += 1 Next Return aIndexAll End Function End Class In Progressbar.au3 the execution times are measuered for the new code. This is the results on my PC when the code is run as 64 bit code: expandcollapse popup250,000 rows Generate array Array generated 782.829209100278 Start sorting ... Sorting started 1052.33601879453 Sorting finished 3112.24138122098 Get index ... Index received 628.809605602899 500,000 rows Generate array Array generated 1692.53510123118 Start sorting ... Sorting started 1736.08362238057 Sorting finished 5300.57901794125 Get index ... Index received 987.373807333858 750,000 rows Generate array Array generated 2701.3300790124 Start sorting ... Sorting started 2846.97747093829 Sorting finished 8147.51853411496 Get index ... Index received 1427.32770753222 1,000,000 rows Generate array Array generated 3671.26491871585 Start sorting ... Sorting started 3360.98083976994 Sorting finished 11515.8238123206 Get index ... Index received 1913.89753875819 2,000,000 rows Generate array Array generated 7789.33304890347 Start sorting ... Sorting started 6773.98630303971 Sorting finished 48250.9211649065 Get index ... Index received 3852.50335220913 Note the lines titled "Sorting started". These are the lines that measure the time it takes to execute this code: Public Sub Sort2DArray( aObjPar As Object(,), aCompPar As Object(,) ) aObjects = aObjPar : aCompare = aCompPar : iRows = aObjects.GetUpperBound(1) + 1 iRestRows = iRows Mod iThreads : iPartRows = ( iRows - iRestRows ) / iThreads iCmps = aCompare.GetUpperBound(1) + 1 'Multithreaded index based sorting Dim th0 As New Thread( AddressOf Sort2DArraySub ) : th0.Start(0) Dim th1 As New Thread( AddressOf Sort2DArraySub ) : th1.Start(1) Dim th2 As New Thread( AddressOf Sort2DArraySub ) : th2.Start(2) Dim th3 As New Thread( AddressOf Sort2DArraySub ) : th3.Start(3) End Sub Most of the time is spent by passing the array ($aArray) as a parameter from the AutoIt code to the VB method (in aObjPar). Code is added to "Examples\5) Optimizing C# and VB code\1) Index based sorting\" in zip-file in bottom of first post. Global array variable Post 2 is a UDF version of four examples about generating a 2D array of random data, sorting the array by one or more columns through an index, converting the 2D array to a 1D array in CSV format, and finally saving the 1D array as a CSV file. The code is stored in "Examples\3) UDF version of examples\" in zip-file in bottom of first post. I've added a new example, ExampleOpt2.au3, to the Examples folder, where the execution times and the total execution time for these 4 procedures are measured for an array with 250,000 rows. This is the results: Code executed as 32 bit code Code executed as 64 bit code ============================ ============================ 250000 rows 250000 rows Generate array: 995.724559926393 Generate array: 797.652719123923 Sort array: 7236.27375117504 Sort array: 5291.30258795446 Convert array: 1426.65439887983 Convert array: 1224.09933028633 Save array: 450.983570086659 Save array: 383.220670832949 Total time: 10109.6362800679 Total time: 7696.27530819767 At the bottom of the example above we saw that quite a lot of time is being used to transfer arrays between the AutoIt code and the VB code. One way to avoid the time used to transfer arrays between AutoIt code and VB code is to store the array ($aArray/aObjects) as a global variable in the VB code. This is implemented in "Examples\5) Optimizing C# and VB code\2) Global array variable\Includes\ArrayFuncsOpt.vb". There is also used a multithreaded version of the sorting code. And the two procedures to convert and save the array as a CSV file is combined into one procedure to avoid transfering the array of strings back and forth. This is the time measurement for the new code: Code executed as 32 bit code Code executed as 64 bit code ============================ ============================ 250000 rows 250000 rows Generate array: 696.800872597593 Generate array: 516.643380589801 Sort array: 1542.99252546023 Sort array: 1465.04082474547 Save array: 446.520844017085 Save array: 418.329314718878 Total time: 2686.31424207491 Total time: 2400.01352005415 Code in "Examples\5) Optimizing C# and VB code\2) Global array variable\" in zip-file in bottom of first post. Delete global variables To delete global variables used in the VB code simply delete the object. The objects are solely handled in Includes\ArrayFuncsOpt.au3 UDF (there are no objects directly in the user code). The $oArrayClass object which is an instance of the ArrayClass in the VB code is created in ArrayFuncsOptInit() in this way: Func ArrayFuncsOptInit( $bDelObj = False ) Static $oNetCode = 0, $oArrayClass = 0 If $bDelObj Then $oArrayClass = 0 $oArrayClass = DotNet_CreateObject( $oNetCode, "ArrayClass" ) Return EndIf If IsObj( $oArrayClass ) Then Return $oArrayClass ; Compile and load VB code, create ArrayClass object $oNetCode = DotNet_LoadVBcode( FileRead( "..\Includes\ArrayFuncsOpt.vb" ), "System.dll" ) $oArrayClass = DotNet_CreateObject( $oNetCode, "ArrayClass" ) Return $oArrayClass EndFunc And the object is deleted in ArrayFuncsDelObject() by calling ArrayFuncsOptInit() with the $bDelObj parameter set to True: Func ArrayFuncsDelObject() ArrayFuncsOptInit( True ) EndFunc When the old object is deleted, a new object is created. ArrayFuncsDelObject() is called in bottom of ExampleOpt2.au3. Similar procedures are used in the example with the progress bar above to delete the old object and create a new. Gianni, argumentum, Danyfirex and 3 others 6 Controls, File Explorer, ROT objects, UI Automation, Windows Message MonitorCompiled code: Accessing AutoIt variables, DotNet.au3 UDF, Using C# and VB codeShell menus: The Context menu, The Favorites menu. Shell related: Control Panel, System Image ListsGraphics related: Rubik's Cube, OpenGL without external libraries, Navigating in an image, Non-rectangular selectionsListView controls: Colors and fonts, Multi-line header, Multi-line items, Checkboxes and icons, Incremental searchListView controls: Virtual ListViews, Editing cells, Data display functions Link to comment Share on other sites More sharing options...
Gianni Posted December 28, 2017 Share Posted December 28, 2017 Hi, I would like to try some VB code that makes use of the WebBrowser Class. at this link there is an example script that I've tryed to use, but had not success. I get this serror: "DotNet\Examples\6) DotNet_LoadVBcode\DotNet_LoadVB_WebBrowser.au3" (14) : ==> The requested action with this object has failed.: $oForm1.Main() $oForm1^ ERROR Here is the AutoIt code I've used. I've saved in the DotNet\Examples\6) DotNet_LoadVBcode directory. #include "..\..\Includes\DotNetAll.au3" Opt("MustDeclareVars", 1) Example() MsgBox(0, "Debug", "Click OK to end program") Func Example() Local $oBrowser, $oCode = DotNet_LoadVBcode(FileRead("WebBrowser.vb"), "System.Windows.Forms.dll | System.Drawing.dll") If @error Then Return ConsoleWrite("DotNet_LoadVBcode ERR" & @CRLF) ConsoleWrite("DotNet_LoadVBcode OK" & @CRLF) Local $oForm1 = DotNet_CreateObject($oCode, "Form1") If IsObj($oForm1) Then $oForm1.Main() EndIf EndFunc ;==>Example I've also saved a file named WebBrowser.vb in the same directory containing the VB listing from the above link. (https://docs.microsoft.com/en-us/dotnet/framework/winforms/controls/how-to-add-web-browser-capabilities-to-a-windows-forms-application) I would be glad if someone could try this code and say how to modify it so to be run successfully. Thanks on advance. LarsJ 1 Chimp small minds discuss people average minds discuss events great minds discuss ideas.... and use AutoIt.... Link to comment Share on other sites More sharing options...
LarsJ Posted December 29, 2017 Author Share Posted December 29, 2017 (edited) Add an error handler (COMUtils.au3 section in DotNetAll.au3) in this way to get a little bit more information about the error: #include "DotNetAll.au3" Opt("MustDeclareVars", 1) Example() MsgBox(0, "Debug", "Click OK to end program") Func Example() ; Add these lines to a script to activate the error handler: Local $oComErrFunc = ObjEvent( "AutoIt.Error", "ComErrFunc" ) #forceref $oComErrFunc Local $oBrowser, $oCode = DotNet_LoadVBcode(FileRead("WebBrowser.vb"), "System.Windows.Forms.dll | System.Drawing.dll") If @error Then Return ConsoleWrite("DotNet_LoadVBcode ERR" & @CRLF) ConsoleWrite("DotNet_LoadVBcode OK" & @CRLF) Local $oForm1 = DotNet_CreateObject($oCode, "Form1") If IsObj($oForm1) Then $oForm1.Main() EndIf EndFunc SciTE output: DotNet_LoadVBcode OK zTest.au3(18): ==> COM Error intercepted! Err.number is: 0x80020006 Err.windescription: Unknown name. Err.description is: Err.source is: Err.helpfile is: Err.helpcontext is: Err.lastdllerror is: 0 Err.scriptline is: 18 Err.retcode is: 0x00000000 There is something related to the Main Sub that does not work. Since I've been playing with VB code, I know that there are some keywords that does not work in AutoIt. In this case, the keyword "Shared" in this line in the VB code (one of the last lines): Public Shared Sub Main() Delete the keyword "Shared" and the code will run. Complete code: WebBrowser.7z Edited December 29, 2017 by LarsJ Danyfirex and Gianni 2 Controls, File Explorer, ROT objects, UI Automation, Windows Message MonitorCompiled code: Accessing AutoIt variables, DotNet.au3 UDF, Using C# and VB codeShell menus: The Context menu, The Favorites menu. Shell related: Control Panel, System Image ListsGraphics related: Rubik's Cube, OpenGL without external libraries, Navigating in an image, Non-rectangular selectionsListView controls: Colors and fonts, Multi-line header, Multi-line items, Checkboxes and icons, Incremental searchListView controls: Virtual ListViews, Editing cells, Data display functions Link to comment Share on other sites More sharing options...
Gianni Posted December 29, 2017 Share Posted December 29, 2017 (edited) Thank You @LarsJ, with that simple fix it works well. This nice stuff you shared here makes it easy from AutoIt to test .NET libraries with VB code. Thanks a lot! Edited December 29, 2017 by Chimp Chimp small minds discuss people average minds discuss events great minds discuss ideas.... and use AutoIt.... Link to comment Share on other sites More sharing options...
ur Posted February 2, 2018 Share Posted February 2, 2018 With the above code I am able to create a dotnet object and use its instance methods. But how to use static methods in the library. I tried like below thinking it is an normal method "GetSubmenuCount". How to run it as it is a static method. #include-once #include "DotNetAll.au3" $oNetCode = DotNet_LoadAssembly(@ScriptDir&"\ERwinUIAutomation.dll") MsgBox(0,"",$oNetCode) $oPrimesClass = DotNet_CreateObject( $oNetCode, "ERwinUIAutomation.ToolBarMenu" ) $r = $oPrimesClass.GetSubmenuCount("Window") Link to comment Share on other sites More sharing options...
junkew Posted February 2, 2018 Share Posted February 2, 2018 Not sure fully anymore as it has been a year ago when I looked into this but highlevel the "normal" logic is in C# like Type type = GetTypeFromName(typeName); type.InvokeMember(method, BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod, null, type, parms); So its not really different. I will look it up but the activator class is similar within the library beeing a static object FAQ 31 How to click some elements, FAQ 40 Test automation with AutoIt, Multithreading CLR .NET Powershell CMDLets Link to comment Share on other sites More sharing options...
ur Posted February 4, 2018 Share Posted February 4, 2018 On 2/2/2018 at 11:24 PM, junkew said: Not sure fully anymore as it has been a year ago when I looked into this but highlevel the "normal" logic is in C# like Type type = GetTypeFromName(typeName); type.InvokeMember(method, BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod, null, type, parms); So its not really different. I will look it up but the activator class is similar within the library beeing a static object Thanks @junkew In powershell, if it is an instance method, the below code is there. [Reflection.Assembly]::LoadFile("C:\ERwinAutomationITC\ERwinUIAutomation\ERwinUIAutomation.dll $m = new-object ERwinUIAutomation.ToolBarMenu $m.GetSubmenuCount("Window") And this is not giving any result, as the method is of static type.So below code is worked. [Reflection.Assembly]::LoadFile("C:\ERwinAutomationITC\ERwinUIAutomation\ERwinUIAutomation.dll [ERwinUIAutomation.ToolBarMenu]::GetSubmenuCount("Window") Which is returning a numeric value.5 for example. But with Autoit, I am not getting any result. Link to comment Share on other sites More sharing options...
LarsJ Posted February 4, 2018 Author Share Posted February 4, 2018 The CreateObject functions in DotNet.au3 UDF to access .NET Framework from AutoIt, .NET Common Language Runtime (CLR) Framework and .NET Framework Interop (CLR, C#, VB) is only for instantiating a class. There is a discussion in Using .NET libary with AutoIt, possible? from post 184 and onwards on the use of static methods, but there are no working examples. The easy way to use static methods is to do it directly in C# or VB code. If interaction with AutoIt code is required, create a class and method in the C#/VB code and use this class and method in the AutoIt code. Earthshine 1 Controls, File Explorer, ROT objects, UI Automation, Windows Message MonitorCompiled code: Accessing AutoIt variables, DotNet.au3 UDF, Using C# and VB codeShell menus: The Context menu, The Favorites menu. Shell related: Control Panel, System Image ListsGraphics related: Rubik's Cube, OpenGL without external libraries, Navigating in an image, Non-rectangular selectionsListView controls: Colors and fonts, Multi-line header, Multi-line items, Checkboxes and icons, Incremental searchListView controls: Virtual ListViews, Editing cells, Data display functions Link to comment Share on other sites More sharing options...
junkew Posted February 4, 2018 Share Posted February 4, 2018 As given by Larsj this thread post 91 gave an initial way of creating activator class post 218 gave an actual example (but still complicated) post 222 another example with clipboard stuff post 233,247,251,255,256,257,261,276 maybe can help you to get the answer to your dll example as it are some smaller examples And the results out of that lengthy R&D thread is basically over here and also this thread that deals with some subset and all collected examples FAQ 31 How to click some elements, FAQ 40 Test automation with AutoIt, Multithreading CLR .NET Powershell CMDLets Link to comment Share on other sites More sharing options...
ur Posted February 5, 2018 Share Posted February 5, 2018 Present I have created another DLL as a com component or COM dll as a wrapper on top of the existing dotnet dll. And below VBScript code is working Set obj1 = CreateObject("AccessErwin.CallERwinUIAutomation") var1= obj1.getTabCount() MsgBox(var1) But when I try the same with AutoIT as below. Local $oShell = ObjCreate("AccessErwin.CallERwinUIAutomation") Local $var1= $oShell.getTabCount() MsgBox(0,"",$var1) I am getting the error as below. "C:\Users\Administrator\Desktop\New AutoIt v3 Script.au3" (2) : ==> Variable must be of type "Object".: Local $var1= $oShell.getTabCount() Local $var1= $oShell^ ERROR Any suggestion please. Link to comment Share on other sites More sharing options...
Earthshine Posted February 5, 2018 Share Posted February 5, 2018 (edited) it's a .net dll, you need to use the CLR wrapper to create a VALID object. like this _CLR_LoadLibrary("C:\Users\Michał\Downloads\CLRv1\XPTable.dll") Edited February 5, 2018 by Earthshine My resources are limited. You must ask the right questions Link to comment Share on other sites More sharing options...
LarsJ Posted February 6, 2018 Author Share Posted February 6, 2018 (edited) junkew, There is no easy to use function (like the CreateObject functions) to handle static methods. The examples that you refer to use concepts and techniques that are so difficult to understand and apply that the examples simply will not be used. It will not happen. The most valuable result of Using .NET libary with AutoIt, possible? is the ability to use C# and VB code. This is the result that can be used without too much trouble of other AutoIt users in addition to the few that were active in the thread. In C# and VB code you can do everything. You can use multi-threaded code, you can use Windows.Forms and you can use static methods. Because C#/VB are popular languages, there are usually always examples that you can copy. And generally all .NET code is much easier to handle in C#/VB than in AutoIt. These languages are .NET languages. Because C# and VB code is accessed as object methods through .NET Framework, data (method parameters) can easily be passed back and forth between AutoIt code and C#/VB code. Even arrays. The objects can be used immediately without any kind of registration. Using C# and VB Code in AutoIt through .NET Framework makes the development process of even complicated code and especially .NET code easy and fast. That's the AutoIt way. Edited February 6, 2018 by LarsJ junkew 1 Controls, File Explorer, ROT objects, UI Automation, Windows Message MonitorCompiled code: Accessing AutoIt variables, DotNet.au3 UDF, Using C# and VB codeShell menus: The Context menu, The Favorites menu. Shell related: Control Panel, System Image ListsGraphics related: Rubik's Cube, OpenGL without external libraries, Navigating in an image, Non-rectangular selectionsListView controls: Colors and fonts, Multi-line header, Multi-line items, Checkboxes and icons, Incremental searchListView controls: Virtual ListViews, Editing cells, Data display functions Link to comment Share on other sites More sharing options...
Earthshine Posted July 2, 2018 Share Posted July 2, 2018 (edited) This is so great @LarsJ Thank you guys SO MUCH. I have one question, I cannot seem to be able to pass a string array to my methods. That multithreaded directory walker/file finder is my example, works great when passing two strings, a dir and a filespec but I can't just pass a string[] array. thanks so much for your time my code file to load CodeCS.cs expandcollapse popupusing System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Diagnostics; class Foo { public void Test(string dir, string filespec) { // if (argv.Length < 2) // { // Console.WriteLine(value: "ERROR: args = null please provide a path and a file specification to search for."); // Console.WriteLine(value: "Example: list c:\\ *.dll"); // } // else // { //DirectoryInfo testdr = new DirectoryInfo(path: argv[0]); //WalkDirectoryTree(testdr, searchname: argv[1]); DirectoryInfo testdr = new DirectoryInfo(path: dir); WalkDirectoryTree(testdr, searchname: filespec); // } } public static void WalkDirectoryTree(DirectoryInfo dr, string searchname) { System.IO.FileInfo[] files = null; DirectoryInfo[] subDirs = null; try { files = dr.GetFiles(searchname); } catch (Exception ex) { Console.WriteLine(ex.Message); } if (files != null) { foreach (FileInfo fi in files) { string dir = fi.DirectoryName; string file = fi.ToString(); if (dir.Length <= 3) { Console.WriteLine(value: dir + file); } else { Console.WriteLine(value: dir + '\\' + file); } } subDirs = dr.GetDirectories(); Parallel.ForEach(subDirs, dir => WalkDirectoryTree(dir, searchname)); } } public override bool Equals(object obj) { return base.Equals(obj); } public override int GetHashCode() { return base.GetHashCode(); } public override string ToString() { return base.ToString(); } } and my AutoIt code from your sample, modified #include "..\..\Includes\DotNet.au3" Opt( "MustDeclareVars", 1 ) Example() Func Example() ; Compiles C# source code on the fly ; Creates a .NET assembly DLL-file in memory ; Loads the memory assembly file into the default domain ; Local $oCode = DotNet_LoadCScode( FileRead( "CodeCS.cs" ), "System.Windows.Forms.dll"); Local $oCode = DotNet_LoadCScode( FileRead( "CodeCS.cs" ), "Microsoft.CSharp.dll | System.dll | System.Core.dll | System.Data.dll | System.Windows.Forms.dll | System.Data.DataSetExtensions.dll | System.Net.Http.dll | System.Xml.dll | System.Xml.Linq.dll" ) ; You must add the "System.Windows.Forms.dll" If @error Then Return ConsoleWrite( "DotNet_LoadCScode ERR" & @CRLF ) ; assembly because of line 1 in the C# code. ConsoleWrite( "DotNet_LoadCScode OK" & @CRLF ) Local $oFoo = DotNet_CreateObject( $oCode, "Foo" ) ; $oFoo is an object of the "Foo" class in the C# code Local $str[2] = ["d:\", "*.rul"] ; ConsoleWrite("Argument 1: " & $str[0] & @CRLF) ; ConsoleWrite("Argument 2: " & $str[1] & @CRLF) If IsObj( $oFoo ) Then $oFoo.Test("d:\", "*.rul") ; Test is a method (function) of the $oFoo object EndFunc you can make your search criteria whatever you want. I just wanted to be able to pass a string array with args like so public void Test(string[] argv) { if (argv.Length < 2) { Console.WriteLine(value: "ERROR: args = null please provide a path and a file specification to search for."); Console.WriteLine(value: "Example: list c:\\ *.dll"); } else { DirectoryInfo testdr = new DirectoryInfo(path: argv[0]); WalkDirectoryTree(testdr, searchname: argv[1]); } } Edited July 2, 2018 by Earthshine My resources are limited. You must ask the right questions Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now