LarsJ

Using C# and VB Code in AutoIt through .NET Framework

8 posts in this topic




#2 ·  Posted (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:

; 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 by LarsJ
"Save array" section updated
3 people like this

Share this post


Link to post
Share on other sites

#3 ·  Posted (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 by LarsJ
Updated
1 person likes this

Share this post


Link to post
Share on other sites

#4 ·  Posted (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:

#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:

Imports 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:

#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:

#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

Av1NyHP.png

You can click Button1 and Button2 while the prime numbers are calculated. The MsgBoxes closes automatically after one second.

Edited by LarsJ
Updated
1 person likes this

Share this post


Link to post
Share on other sites

#5 ·  Posted

Share this post


Link to post
Share on other sites

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

3 people like this

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now