Jump to content
LarsJ

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

Recommended Posts

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

Share this post


Link to post
Share on other sites

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

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

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

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.


small minds discuss people average minds discuss events great minds discuss ideas.... and use AutoIt....

Share this post


Link to post
Share on other sites

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 by LarsJ

Share this post


Link to post
Share on other sites

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 by Chimp

small minds discuss people average minds discuss events great minds discuss ideas.... and use AutoIt....

Share this post


Link to post
Share on other sites

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")

 

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites
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.

 

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites

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

 

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites

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 by Earthshine

My resources are limited. You must ask the right questions

 

Share this post


Link to post
Share on other sites

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 by LarsJ

Share this post


Link to post
Share on other sites

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

using 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 by Earthshine

My resources are limited. You must ask the right questions

 

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

  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...