Leaderboard
Popular Content
Showing content with the highest reputation on 11/05/2021 in all areas
-
The .NET IntPtr type is useful for copying data in the form of memory blocks between managed .NET code and unmanaged AutoIt code. An advantage of using this technique is that the data copying isn't subject to internal AutoIt COM conversions. This means that even copying large memory blocks can be done very fast. A disadvantage is that such memory blocks can only be handled through DllStructs in AutoIt, unless the memory blocks can be used directly eg. if they represent image data. Another technique for passing data between AutoIt and C#/VB.NET is demonstrated in Using C# and VB Code. With this technique, variables and arrays are exchanged through comparable COM data types. Eg. passing an AutoIt array to .NET as a safearray of variants. On the AutoIt side the safearray of variants is created through internal AutoIt COM conversions. On the .NET side data is received as an array of objects. Although the COM conversions are performed by internal AutoIt functions as compiled C/C++ code, the conversions take some time for large arrays. Since AutoIt arrays are limited to 2^24 = 16,777,216 elements, this is the maximum amount of data that can be passed at one time. DotNetAll.au3 UDFCommon to the two techniques is that they are both based on DotNetAll.au3 UDF (contained in the 7z-file below). The DotNetAll.au3 UDF is by far the easiest way to execute compiled and multi-threaded code in AutoIt through C# or VB.NET code, thereby managing bottlenecks in the AutoIt code. With this new IntPtr data copying technique, it's possible to handle more bottlenecks than can be handled with the existing COM data passing technique. The benefits of the DotNetAll.au3 UDF are: The .NET source code can be easily written in SciTE without the need for a specific development tool The .NET code is compiled and executed directly in your AutoIt script, where you apply the code If the AutoIt script is run in SciTE with F5, .NET error messages appear in the console The .NET code can be easily distributed in the form of source files (text files) You avoid the registration/unregistration tyranny of .NET assembly Dll files Although DotNetAll.au3 UDF contains a lot of code, it's a complete UDF in the way that you can concentrate on the main functions. There's no need to go through all the details of the code. There are two main functions in the UDF. A function for loading C#/VB.NET code and a function for creating an object based on the C#/VB.NET class. In AutoIt, the functions are used as follows: Local $oNetCode = DotNet_LoadVBcode( FileRead( "Example01.vb" ), "System.dll" ) ; Load VB.NET code Local $oNetCode = DotNet_LoadCScode( FileRead( "Example02.cs" ), "System.dll" ) ; Load C# code Local $oTestClass = DotNet_CreateObject( $oNetCode, "TestClass" ) ; Create object Then just call the object methods and get/set the object properties. This is demonstrated in the examples below. ExamplesThe examples can be found in the Examples folder. All examples use the DotNetAll.au3 UDF, which is located in the Includes folder. It's the only file in Includes. Only the Introductory examples demonstrates C# code. Since VB.NET code is most easily accessible with an AutoIt background, the rest of the examples are shown only as VB.NET code. Note that in all examples, all memory allocation and all data copying is performed exclusively in the .NET code. Run the examples with F5 in SciTE. Memory Access Violation error The Introductory examples and Performance measurements all caused a Memory Access Violation error and a consequent crash in the first version of the examples when executed on Windows 10 as 64 bit code. This is because the IntPtr data type isn't handled correctly in AutoIt on Windows 10 as 64 bit code. This applies to all AutoIt versions including the current beta. The usual workaround is to return the IntPtr as a string. This error occurs when IntPtr is a void memory pointer. However, the error does not occur if IntPtr refers to a memory block that represents a valid handle or HWnd memory block. This is because a handle and HWnd pointer by Microsoft definition is a 4 byte pointer in both 32 and 64 bit code. In 64 bit code, the 4 most significant bytes are therefore all zeros. Thus, there are no errors in the Image manipulation examples. The function definition and return lines therefore looks like this in the published version in the 7z-file of the Introductory examples and Performance measurements (VB.NET code): Public Function Test() As String ... Return pnt.ToString() And look like this in Image manipulation examples (VB.NET code): Public Function Test() As IntPtr ... Return hBitmap Introductory examplesWith the IntPtr technique, part of the code is based on the .NET Marshal Class. The introductory examples in \1) Examples\ demonstrate how to apply this class. Example01.au3 and Example01.vb show how to copy a memory block of 16 bytes from VB.NET to AutoIt: #AutoIt3Wrapper_Au3Check_Parameters=-d -w- 1 -w 2 -w 3 -w 4 -w 5 -w 6 -w 7 #AutoIt3Wrapper_UseX64=Y Opt( "MustDeclareVars", 1 ) #include "..\..\Includes\DotNetAll.au3" Example() Func Example() ; Load .NET code, create object, execute method Local $oNetCode = DotNet_LoadVBcode( FileRead( "Example01.vb" ), "System.dll" ) Local $oTestClass = DotNet_CreateObject( $oNetCode, "TestClass" ) Local $pPtr = $oTestClass.Test() ; View data as a DllStruct of Bytes Local $tBytes = DllStructCreate( "byte[16]", $pPtr ) ; Display data For $i = 1 To 16 ConsoleWrite( StringFormat( "$tBytes[%2s]", $i ) & StringFormat( " = %3s", DllStructGetData( $tBytes, 1, $i ) ) & @CRLF ) Next EndFunc Imports System Imports System.Runtime.InteropServices Class TestClass Public Function Test() As String 'Create and fill aArray with values 97 - 112 Dim aArray(15) As Byte For i As Integer = 0 To 15 aArray(i) = i + 97 Next 'Allocate memory in unmanaged AutoIt code Dim size As Integer = Marshal.SizeOf( aArray(0) ) * aArray.Length Dim pnt As IntPtr = Marshal.AllocHGlobal( size ) 'Copy from VB.NET to AutoIt 'Copy data to unmanaged memory Marshal.Copy( aArray, 0, pnt, aArray.Length ) 'Return memory pointer to AutoIt Return pnt.ToString() End Function End Class Example02.cs is a C# version of the same code: using System; using System.Runtime.InteropServices; class TestClass { public String Test() { // Create and fill aArray with values 97 - 112 byte[] aArray = new byte[16]; for( int i = 0; i < 16; i++ ) { aArray[i] = (byte)(i+97); } // (byte) => cast to byte // Allocate memory in unmanaged AutoIt code int size = Marshal.SizeOf( aArray[0] ) * aArray.Length; IntPtr pnt = Marshal.AllocHGlobal( size ); // Copy from C# to AutoIt // Copy data to unmanaged memory Marshal.Copy( aArray, 0, pnt, aArray.Length ); // Return memory pointer to AutoIt return pnt.ToString(); } } The code in Example02.au3 is the same as the code in Example01.au3. Example03.au3, Example03.vb and Example04.cs show how to copy a memory block of 16 bytes from AutoIt to VB.NET/C#. Performance measurementsExample01.au3 in \2) Runtimes\ calls the same Example() function 4 times: #AutoIt3Wrapper_Au3Check_Parameters=-d -w- 1 -w 2 -w 3 -w 4 -w 5 -w 6 -w 7 #AutoIt3Wrapper_UseX64=Y Opt( "MustDeclareVars", 1 ) #include "..\..\Includes\DotNetAll.au3" Example( 1000000, True ) ; 1,000,000 Example( 1000000 ) ; 1,000,000 Example( 16777216 ) ; 16,777,216 = 2^24 Example( 100000000 ) ; 100,000,000 Func Example( $iInts, $bDisplay = False ) ; Load .NET code, create object Local $oNetCode = DotNet_LoadVBcode( FileRead( "Example01.vb" ), "System.dll" ) Local $oTestClass = DotNet_CreateObject( $oNetCode, "TestClass" ) ; Pass number of integers to VB.NET code $oTestClass.Test0( $iInts ) ; Create and fill VB.NET integer aArray with $iInts elements ; Allocate memory through managed VB.NET code Local $pPtr = $oTestClass.Test1() ; Copy $iInts integers from .NET aArray to AutoIt Local $hTimer = TimerInit() $oTestClass.Test2() ConsoleWrite( @CRLF & "Time to copy " & StringRegExpReplace( $iInts, "(\d{1,3}(?=(\d{3})+\z)|\d{3}(?=\d))", "\1" & "," ) & _ " integers from .NET aArray to AutoIt" & ( $bDisplay ? " = " : " = " ) & TimerDiff( $hTimer ) & @CRLF ) $hTimer = 0 ; View aArray data as a DllStruct of Bytes Local $tUints = DllStructCreate( "uint[" & $iInts & "]", $pPtr ) ; Display data If $bDisplay Then For $i = $iInts/2 To $iInts/2+16 ConsoleWrite( "$tUints[" & $i & "]" & " = " & DllStructGetData( $tUints, 1, $i+1 ) & @CRLF ) Next ConsoleWrite( @CRLF ) EndIf ; Copy $iInts integers from AutoIt to .NET aArray2 $hTimer = TimerInit() $oTestClass.Test3() ConsoleWrite( "Time to copy " & StringRegExpReplace( $iInts, "(\d{1,3}(?=(\d{3})+\z)|\d{3}(?=\d))", "\1" & "," ) & _ " integers from AutoIt to .NET aArray2 = " & TimerDiff( $hTimer ) & @CRLF ) $hTimer = 0 ; Display aArray2 data If $bDisplay Then $oTestClass.Test4() If $iInts <= 16777216 And Not $bDisplay Then Local $aArray2 ; Pass $iInts integers from .NET aArray2 to AutoIt $aArray2 ; .NET aArray2 is returned from a method directly into AutoIt $aArray2 ; In this way aArray2 is passed as a safearray of integers through default marshalling ; Internal AutoIt COM conversions of the safearray converts it to a native AutoIt array $hTimer = TimerInit() $aArray2 = $oTestClass.Test5() ConsoleWrite( @CRLF & "Time to pass " & StringRegExpReplace( $iInts, "(\d{1,3}(?=(\d{3})+\z)|\d{3}(?=\d))", "\1" & "," ) & _ " integers from .NET aArray to AutoIt $aArray2 = " & TimerDiff( $hTimer ) & @CRLF ) $hTimer = 0 ; Pass $iInts integers from AutoIt $aArray2 to .NET aArray3 ; AutoIt $aArray2 is passed as a function parameter directly into .NET aArray3 ; Internal AutoIt COM conversions pass $aArray2 as a safearray of variants with variant type integer ; The safearray of variants is passed to .NET through default marshalling as an array of objects with object type integer $hTimer = TimerInit() $oTestClass.Test6( $aArray2 ) ConsoleWrite( "Time to pass " & StringRegExpReplace( $iInts, "(\d{1,3}(?=(\d{3})+\z)|\d{3}(?=\d))", "\1" & "," ) & _ " variants from AutoIt $aArray2 to .NET aArray3 = " & TimerDiff( $hTimer ) & @CRLF ) $hTimer = 0 EndIf ConsoleWrite( @CRLF ) EndFunc Imports System Imports System.Runtime.InteropServices Class TestClass 'Globals in class Dim pnt As IntPtr Dim iInts As Integer Dim aArray() As Integer Dim aArray2() As Integer Public Sub Test0( iElements As Integer ) 'Get number of integers iInts = iElements ReDim aArray( iInts-1 ) ReDim aArray2( iInts-1 ) End Sub Public Function Test1() As String 'Create and fill integer aArray with iInts elements For i As Integer = 0 To iInts-1 aArray(i) = i Next 'Allocate memory in unmanaged AutoIt code Dim size As Integer = Marshal.SizeOf( aArray(0) ) * aArray.Length pnt = Marshal.AllocHGlobal( size ) 'Return memory pointer to AutoIt Return pnt.ToString() End Function Public Sub Test2() 'Copy iInts integers from .NET aArray to AutoIt Marshal.Copy( aArray, 0, pnt, aArray.Length ) End Sub Public Sub Test3() 'Copy iInts integers from AutoIt to .NET aArray2 Marshal.Copy( pnt, aArray2, 0, iInts ) End Sub Public Sub Test4() 'Display aArray2 data For i As Integer = iInts/2 To iInts/2+15 Console.WriteLine( "aArray2({0}) = {1}", i, aArray2(i) ) Next End Sub Public Function Test5() As Integer() 'Pass iInts integers from .NET aArray2 to AutoIt Return aArray2 End Function Public Sub Test6( aArray3 As Object() ) 'Pass iInts variants from AutoIt to .NET aArray3 Dim iLength As Integer = aArray3.Length 'Console.WriteLine( "iLength = {0}", iLength ) End Sub End Class In the first example, time measurements are made and a few data are printed in the console. In the other 3 examples, only time measurements are performed. The text box below is console output for these 3 examples. The "copy" lines are time measurements for the new IntPtr data copying technique. The "pass" lines are time measurements for the old COM data passing technique. Time to copy 1,000,000 integers from .NET aArray to AutoIt = 1.72992070180921 Time to copy 1,000,000 integers from AutoIt to .NET aArray2 = 1.72077794345564 Time to pass 1,000,000 integers from .NET aArray to AutoIt $aArray2 = 45.4910409279733 Time to pass 1,000,000 variants from AutoIt $aArray2 to .NET aArray3 = 218.360099451047 Time to copy 16,777,216 integers from .NET aArray to AutoIt = 19.8287034959137 Time to copy 16,777,216 integers from AutoIt to .NET aArray2 = 23.0162015219091 Time to pass 16,777,216 integers from .NET aArray to AutoIt $aArray2 = 743.615722663014 Time to pass 16,777,216 variants from AutoIt $aArray2 to .NET aArray3 = 4165.03925567973 Time to copy 100,000,000 integers from .NET aArray to AutoIt = 118.826768001676 Time to copy 100,000,000 integers from AutoIt to .NET aArray2 = 115.565850855568 The results clearly show that the new copying technique is much faster than the old passing technique. In particular, passing arrays from AutoIt to .NET is slow with the old technique. With the new technique, very large amounts of data can be copied. The reason for the slow passing of AutoIt arrays to .NET is that AutoIt arrays can only be passed as safearrays of variants. And passing variants is time consuming. Not at least due to internal AutoIt COM conversions which perform a by value copy of all AutoIt array elements into variant structures in the safearray. Image manipulationExample01.au3 and Example01.vb in \3) Images\ shows how to load an image in VB.NET and display the image in AutoIt: #AutoIt3Wrapper_Au3Check_Parameters=-d -w- 1 -w 2 -w 3 -w 4 -w 5 -w 6 -w 7 #AutoIt3Wrapper_UseX64=Y Opt( "MustDeclareVars", 1 ) #include <GDIPlus.au3> #include <GUIConstantsEx.au3> #include "..\..\Includes\DotNetAll.au3" Example() Func Example() ; Load .NET code, create object, execute method Local $oNetCode = DotNet_LoadVBcode( FileRead( "Example01.vb" ), "System.dll | System.Drawing.dll" ) Local $oTestClass = DotNet_CreateObject( $oNetCode, "TestClass" ) Local $pBitmap = $oTestClass.Test() ; Create GUI Local $hGui = GUICreate( "GDI+ Through .NET", 240, 164 ) GUISetState( @SW_SHOW ) ; Initialize GDI+ _GDIPlus_Startup() ; Draw bitmap to GUI Local $hBitmap = _GDIPlus_BitmapCreateFromHBITMAP( $pBitmap ) Local $hGraphic = _GDIPlus_GraphicsCreateFromHWND( $hGui ) _GDIPlus_GraphicsDrawImage( $hGraphic, $hBitmap, 0, 0 ) ; Clean up resources _GDIPlus_GraphicsDispose( $hGraphic ) _GDIPlus_BitmapDispose( $hBitmap ) _WinAPI_DeleteObject( $pBitmap ) ; Shut down GDI+ _GDIPlus_Shutdown() ; Loop Do Until GUIGetMsg() = $GUI_EVENT_CLOSE EndFunc Imports System Imports System.Drawing Class TestClass Public Function Test() As IntPtr 'Create a bitmap Dim bmp As New Bitmap( "Test.bmp" ) 'Create a GDI bitmap Dim hBitmap As IntPtr = bmp.GetHbitmap() 'Return GDI bitmap Return hBitmap End Function End Class Note that at the bottom of Example01.vb, the GDI bitmap pointer (hBitmap) is passed directly from VB.NET to AutoIt without a copy of the corresponding memory block. This is because the memory layout for a GDI bitmap memory block is the same in .NET and AutoIt. The same GDI bitmap memory block can without any further be used in both .NET and AutoIt. Without data copying, the code is very fast. Example02.au3 (same as Example01.au3) and Example02.vb is an example of real image manipulation. However, simple image manipulation. It's the same image as above, which is a 24 bpp (bits per pixel) image. 8 bits or 1 byte is used per RGB color. In the example, all the red bytes are set to the value 255 so that the whole image gets a red expression. Imports System Imports System.Drawing Imports System.Drawing.Imaging Imports System.Runtime.InteropServices Class TestClass Public Function Test() As IntPtr 'Create bitmap Dim bmp As New Bitmap( "Test.bmp" ) 'Lock the bits in the bitmap Dim rect As New Rectangle( 0, 0, bmp.Width, bmp.Height ) Dim bmpData As BitmapData = bmp.LockBits( rect, ImageLockMode.ReadWrite, bmp.PixelFormat ) 'Get the address of the first line Dim ptr As IntPtr = bmpData.Scan0 'Declare an array to hold the bytes of the bitmap 'This code is specific to a bitmap with 24 bits per pixel (bpp) Dim iBytes As Integer = Math.Abs( bmpData.Stride ) * bmp.Height Dim aRGBValues(iBytes-1) As Byte 'Copy the RGB values into the array Marshal.Copy( ptr, aRGBValues, 0, iBytes ) 'Set every third value to 255, a 24bpp image will look red For i As Integer = 2 To aRGBValues.Length - 1 Step 3 aRGBValues(i) = 255 Next 'Copy the RGB values back to the bitmap Marshal.Copy( aRGBValues, 0, ptr, iBytes ) 'Unlock the bits bmp.UnlockBits( bmpData ) 'Create GDI bitmap Dim hBitmap As IntPtr = bmp.GetHbitmap() 'Return GDI bitmap Return hBitmap End Function End Class Posts below Post 2 and the following posts is a discussion of the possibilities of executing PowerShell commands and scripts directly in AutoIt, and returning PowerShell output back to AutoIt. This is possible through C# code that acts as a host to execute PowerShell commands and scripts. C# Pointers demonstrates the use of pointers to performance optimize C# code. Use of structures to transfer data between AutoIt and .NET. Struct arrays in .NET code 7z-fileThe 7z-file contains source code for UDFs and examples. You need AutoIt 3.3.12 or later. Tested on Windows 7 and Windows 10. Comments are welcome. Let me know if there are any issues. DotNetIntPtr.7z1 point
-
Help me please for StringRegExpReplace
mixim reacted to ViciousXUSMC for a topic
Same as I posted except no need for RegEx just StringReplace will suffice. The main rule of RegEx is only use RegEx when its needed1 point -
Help me please for StringRegExpReplace
mixim reacted to JockoDundee for a topic
Well, if you insist: $string=StringRegExpReplace(StringRegExpReplace(StringRegExpReplace(StringRegExpReplace(StringRegExpReplace(StringRegExpReplace($string, "Ğ", "G"), "İ", "I"), "Ş", "S"), "ğ", "g"), "ı", "i"),"ş", "s") *not tested1 point -
Help me please for StringRegExpReplace
mixim reacted to ViciousXUSMC for a topic
I am sure there is a way to do this with pure regex using StringRegExpReplace() however since the matches are simple enough my thought process is to use a function that contains all your corresponding matches and replacements in an array or map or something and just use StringReplace() Also you can try nesting functions. $sString = "+h!s is @ test" $sNewString = StringReplace(StringReplace(StringReplace($sString, "+", "t"), "!", "i"), "@", "a") MsgBox(0, "", $sNewString)1 point -
Hi. I think Jos meant to say, change this: $BtnXXX = GUICtrlCreateButton ("xxx", 250, 10, 30, 30) GUISetOnEvent (-1, "xxx") To: $BtnXXX = GUICtrlCreateButton ("xxx", 250, 10, 30, 30) GUICtrlSetOnEvent (-1, "xxx") It's not a Gui, it's a control.1 point
-
Extended Message Box - New Version: 16 Feb 24
matwachich reacted to Melba23 for a topic
matwachich, Thanks for the suggestion, but I will not be amending the code. You can very easily set/cancel the relevant parameter using _ExtMsgBoxSet before/after creating the ExtMsgBox which required the checkbox so I see no need to change the current method. Sorry about that. M231 point -
Something in this direction (no AutoIt available at the moment) In powershell you can reach win32, .net and you need a way to communicate back in below example just sending a message to notepad but that can be an AutoIt window. add-type -assemblyname System add-type -assemblyname System.Drawing # [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing") $winApi = add-type -name user32 -passThru ` -memberDefinition ' [DllImport("user32.dll")] public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter,String lpszClass,String lpszWindow); [DllImport("User32.dll")] public static extern IntPtr SendMessage(IntPtr hWnd,int uMsg, int wParam, string lParam);' $notepad_1 = start-process -passThru notepad start-sleep 1 $hwndNotepad_1 = $notepad_1.MainWindowHandle $hwndEdit_1 = $winApi::FindWindowEx($hwndNotepad_1, [IntPtr]::Zero, "Edit", $null) $null = $winApi::SendMessage($hwndEdit_1, 0x000C, 0, "This is the first notepad window.") Class TestClass { [intptr]Test() { $bmp = New-Object System.Drawing.Bitmap(320, 240) for ($i = 0; $i -lt 100; $i++) { for ($j = 0; $j -lt 100; $j += 2) { $bmp.SetPixel($i, $j, 'Red') $bmp.SetPixel($i, $j + 1, [System.Drawing.Color]::FromArgb(0, 100, 200)) } } $bmp.Save("test.bmp") $newintptr = New-Object system.Intptr $newIntPtr = $bmp.GetHbitmap() return $newintptr } } $bmp1=new-object testclass $null = $winApi::SendMessage($hwndEdit_1, 0x000C, 0, "The pointer address " + $bmp1.test().tostring()) #[IntPtr]$Pointer = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($Bytes.Length) #[System.Runtime.InteropServices.Marshal]::Copy($Bytes, 0, $Pointer, $Bytes.Length) In powershell you cannot create COM objects as far as I know but you could use 1. sendmessage as shown above 2. tcp client/server there are many powershell examples around to make a simple tcpip server and client in AutoIt should not be hard 3. .....1 point
-
Copying Data to/from C# or VB.NET Through IntPtr
Earthshine reacted to ptrex for a topic
Sure this was already implemented running PS scripts in a .NET PowerShell Host. But there are subtle technical difference and limitations involved. The prior PS Host that runs a script, is running in an Appdomain Object in the unmanaged environment. So it runs in an isolated environment (and even in a seperate process if I remember well) So the limitation is that you can't interact between the host and the PS process. While your example above makes interacting (Copying Data to/from C# or VB.NET Through IntPtr) between the 2 environments possible. But well noticed of you, that the foundation blocks are already there. It is now a question of putting the pieces together, and test if it works as expected... And example would be the copy console output from a PS script to Au3, or the other way around.1 point -
Copying Data to/from C# or VB.NET Through IntPtr
Earthshine reacted to LarsJ for a topic
Isn't it more or less already implemented in the example here that shows how to execute PowerShell code directly in an AutoIt script.1 point -
Share UDF (GUI)
Danyfirex reacted to Mateocedillo for a topic
Hello, I share one of my growth UDFs created. Share: This UDF or script will allow us to share content (link or text) to different social networks such as Facebook, Skype and WhatsApp. If you want to add another social network, comment it here. Note: This requires GUIEventMode and a running GUI to work (example below). share.au3 share_example.au31 point -
This is a known bug : https://www.autoitscript.com/trac/autoit/ticket/38381 point