Jump to content

Recommended Posts

Posted

Is the HP "dotnetfactory" a dotnetAssembly ? is so can you provide me a copy so I can look into the Assembly DLL to see if an learn from that code base...

Regarding the reflection I posted some nice material a fews days back, can be of interest to see post 46

.Net Reflection

What is reflection in .Net  : https://www.codeproject.com/articles/55710/reflection-in-net

Downside of the reflection is the speed ... compared to te CLR approach is seems. But better slow speed than no speed :)

 

Posted

Event_handling.au3:

;#AutoIt3Wrapper_UseX64=y

#include "CLR.au3"

Example()

Func Example()
  ; Compile the helper class. This could be pre-compiled.
  Local $oHelper, $oHelperAsm = _CLR_CompileCSharp( FileRead( "EventHelper.cs" ) )
  $oHelperAsm.CreateInstance( "EventHelper", $oHelper )
  ConsoleWrite( "IsObj( $oHelper ) = " & IsObj( $oHelper ) & @CRLF )

  ; Create our test object, which simply exposes a single event.
  Local $oTest, $oTestAsm = _CLR_CompileCSharp( FileRead( "TestObject.cs" ) )
  $oTestAsm.CreateInstance( "ObjectWithEvent", $oTest )
  ConsoleWrite( "IsObj( $oTest ) = " & IsObj( $oTest ) & @CRLF )

  Local $hEventHandler = DllCallbackRegister( "EventHandler", "int", "ptr" )
  Local $pEventHandler = DllCallbackGetPtr( $hEventHandler )

  ; Add an event handler for the "OnEvent" event.  Use "" to pass the
  ; address as a string, since IDispatch doesn't support 64-bit integers.
  $oHelper.AddHandler( $oTest, "OnEvent", $pEventHandler )

  ; Make an event handler (event must be of the EventHandler type).
  Local $oHandler = $oHelper.MakeHandler( $pEventHandler )
  $oTest.add_OnEvent( $oHandler )

  ; Test the event handlers.
  $oTest.RaiseEvent()
EndFunc

; Our event handler is called with a SAFEARRAY of parameters.  This
; makes it much easier to get the type and value of each parameter.
Func EventHandler( $pprm )
  ConsoleWrite( "$pprm = " & $pprm & @CRLF )

  Local $iDim = SafeArrayGetDim( $pprm )
  ConsoleWrite( "$iDim = " & $iDim & @CRLF )

  Local $iLBound, $iUBound
  SafeArrayGetLBound( $pprm, 1, $iLBound )
  SafeArrayGetUBound( $pprm, 1, $iUBound )
  ConsoleWrite( "$iLBound = " & $iLBound & @CRLF )
  ConsoleWrite( "$iUBound = " & $iUBound & @CRLF )

  Local $tprm = DllStructCreate( $tagSAFEARRAY, $pprm )
  Local $fFeatures = DllStructGetData( $tprm, "fFeatures" )
  ConsoleWrite( "$fFeatures = 0x" & Hex( $fFeatures ) & @CRLF )
  Local $cbElements = DllStructGetData( $tprm, "cbElements" )
  ConsoleWrite( "$cbElements = " & $cbElements & @CRLF )

  Local $vt
  SafeArrayGetVartype( $pprm, $vt )
  ConsoleWrite( "$vt = " & $vt & @CRLF )

  Local $prmData, $tvt, $data, $obj
  SafeArrayAccessData( $pprm, $prmData )

  $tvt = DllStructCreate( "word", $prmData )
  $vt = DllStructGetData( $tvt, 1 )
  ConsoleWrite( "$vt = " & $vt & @CRLF ) ; EventArgs Class
  $data = DllStructGetData( DllStructCreate( "ptr", $prmData + 8 ), 1 )
  $obj = ConvertPtrToIDispatch($data)
  ConsoleWrite( $obj.ToString() & @CRLF )

  $tvt = DllStructCreate( "word", $prmData + ( @AutoItX64 ? 24 : 16 ) )
  $vt = DllStructGetData( $tvt, 1 )
  ConsoleWrite( "$vt = " & $vt & @CRLF ) ; EventArgs Class
  $data = DllStructGetData( DllStructCreate( "ptr", $prmData + ( @AutoItX64 ? 24 : 16 ) + 8 ), 1 )
  $obj = ConvertPtrToIDispatch($data)
  ConsoleWrite( $obj.ToString() & @CRLF )

  SafeArrayUnaccessData( $pprm )
EndFunc

; In the end we still want the autoit object. This function converts a raw pointer to an autoit object
Func ConvertPtrToIDispatch($IDispatch_Ptr)
  ; This would have been 10000x easier if autoit had supported the idispatch* type in dllstructs...
  ; Fortunetely memcpy can copy the pointer into a idispatch*, lucky us.
  $ptr_struct=DllStructCreate("ptr")
  DllStructSetData($ptr_struct,1,$IDispatch_Ptr)
  $aCall = DllCall("ntdll.dll","ptr:cdecl","memcpy","idispatch*","","ptr",DllStructGetPtr($ptr_struct),"long",4)
  return $aCall[1]
EndFunc

EventHelper.cs

using System;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.InteropServices;

public class EventHelper {
    // Delegate type for the AutoHotkey callback.
    public delegate void CallbackType([MarshalAs(UnmanagedType.SafeArray)] object[] argv);
    // AddHandler: Adds a callback as a handler for the given event of the given object.
    public void AddHandler(object target, string eventName, string pcb) {
        var cb = ParseCB(pcb);
        // Reference: http://msdn.microsoft.com/en-us/library/ms228976
        EventInfo evt = target.GetType().GetEvent(eventName);
        Type handlerType = evt.EventHandlerType;
        MethodInfo handlerSig = handlerType.GetMethod("Invoke");
        ParameterInfo[] parameters = handlerSig.GetParameters();
        Type[] parameterTypes = new Type[parameters.Length+1];
        parameterTypes[0] = typeof(CallbackType);
        for (int i = 0; i < parameters.Length; i++)
            parameterTypes[i+1] = parameters[i].ParameterType;
        
        var handler = new DynamicMethod("", handlerSig.ReturnType, parameterTypes, true);
        
        var il = handler.GetILGenerator();
        var loc = il.DeclareLocal(typeof(object[]));
        il.Emit(OpCodes.Ldc_I4_2);
        il.Emit(OpCodes.Newarr, typeof(object));
        il.Emit(OpCodes.Stloc_0);
        
        il.Emit(OpCodes.Ldloc_0);
        il.Emit(OpCodes.Ldc_I4_0);
        il.Emit(OpCodes.Ldarg_1);
        il.Emit(OpCodes.Stelem_Ref); 
        
        il.Emit(OpCodes.Ldloc_0);
        il.Emit(OpCodes.Ldc_I4_1);
        il.Emit(OpCodes.Ldarg_2);
        il.Emit(OpCodes.Stelem_Ref);
        
        il.Emit(OpCodes.Ldarg_0);
        il.Emit(OpCodes.Ldloc_0);
        il.Emit(OpCodes.Call, typeof(CallbackType).GetMethod("Invoke"));
        il.Emit(OpCodes.Ret);
        
        var delg = handler.CreateDelegate(handlerType, cb);
        var adder = evt.GetAddMethod();
        adder.Invoke(target, new object[] { delg });
    }
    // Much simpler method, restricted to a specific delegate type.
    public EventHandler MakeHandler(string pcb) {
        var cb = ParseCB(pcb);
        return (sender, e) => cb(new object[]{ sender, e });
    }
    public CallbackType ParseCB(string cb) {
        // For 32-bit, simply marking the parameter of AddHandler/MakeHandler with:
        //   [MarshalAs(UnmanagedType.FunctionPtr)] CallbackType cb
        // is adequate, but since IDispatch doesn't support 64-bit integers,
        // we have to pass the callback address as a string for x64 builds.
        return (CallbackType) Marshal.GetDelegateForFunctionPointer(
            (IntPtr)Int64.Parse(cb), typeof(CallbackType));
    }
}

TestObject.cs

public class ObjectWithEvent {
  public void RaiseEvent() {
    if (OnEvent != null)
      OnEvent(this, System.EventArgs.Empty);
  }
  public event System.EventHandler OnEvent;
}

tst00.au3

;#AutoIt3Wrapper_UseX64=y

#include "CLR.au3"

Example() ; Form Using System.Windows.Forms.Form

Func Example()
  ; Compile the helper class. This could be pre-compiled.
  Local $oHelper, $oHelperAsm = _CLR_CompileCSharp( FileRead( "EventHelper.cs" ) )
  $oHelperAsm.CreateInstance( "EventHelper", $oHelper )
  ConsoleWrite( "IsObj( $oHelper ) = " & IsObj( $oHelper ) & @CRLF )

  ConsoleWrite( "_CLR_LoadLibrary System.Windows.Forms" & @CRLF )
  Local $oAssembly = _CLR_LoadLibrary( "System.Windows.Forms" )
  ConsoleWrite( "IsObj( $oAssembly ) = " & IsObj( $oAssembly ) & @CRLF )

  ConsoleWrite( @CRLF & "_CRL_CreateObject: System.Windows.Forms.Form" & @CRLF )
  Local $oForm = _CRL_CreateObject( $oAssembly, "System.Windows.Forms.Form" )
  ConsoleWrite( "IsObj( $oForm ) = " & IsObj( $oForm ) & @CRLF )

  $oForm.Text = "Form From Net - AutoIt Rocks"
  $oForm.Width = 800
  $oForm.Height = 400

  ConsoleWrite( @CRLF & "_CRL_CreateObject System.Windows.Forms.Button" & @CRLF )
  Local $oButton1 = _CRL_CreateObject($oAssembly, "System.Windows.Forms.Button")
  $oButton1.Text = "button"
  $oButton1.Width = 60
  $oButton1.Height = 30

  Local $hEventHandler = DllCallbackRegister( "EventHandler", "int", "ptr" )
  Local $pEventHandler = DllCallbackGetPtr( $hEventHandler )

  ; Add an event handler for the "OnEvent" event.  Use "" to pass the
  ; address as a string, since IDispatch doesn't support 64-bit integers.
  $oHelper.AddHandler( $oButton1, "Click", $pEventHandler )

  ;$oForm.Controls.Add( $oButton1 ) ; ERR
  $oButton1.Parent = $oForm ; OK

  $oForm.ShowDialog()

  $oForm.Dispose()
EndFunc

; Our event handler is called with a SAFEARRAY of parameters.  This
; makes it much easier to get the type and value of each parameter.
Func EventHandler( $pprm )
  ConsoleWrite( "$pprm = " & $pprm & @CRLF )

  Local $iDim = SafeArrayGetDim( $pprm )
  ConsoleWrite( "$iDim = " & $iDim & @CRLF )

  Local $iLBound, $iUBound
  SafeArrayGetLBound( $pprm, 1, $iLBound )
  SafeArrayGetUBound( $pprm, 1, $iUBound )
  ConsoleWrite( "$iLBound = " & $iLBound & @CRLF )
  ConsoleWrite( "$iUBound = " & $iUBound & @CRLF )

  Local $tprm = DllStructCreate( $tagSAFEARRAY, $pprm )
  Local $fFeatures = DllStructGetData( $tprm, "fFeatures" )
  ConsoleWrite( "$fFeatures = 0x" & Hex( $fFeatures ) & @CRLF )
  Local $cbElements = DllStructGetData( $tprm, "cbElements" )
  ConsoleWrite( "$cbElements = " & $cbElements & @CRLF )

  Local $vt
  SafeArrayGetVartype( $pprm, $vt )
  ConsoleWrite( "$vt = " & $vt & @CRLF )

  Local $prmData, $tvt, $data, $obj
  SafeArrayAccessData( $pprm, $prmData )

  $tvt = DllStructCreate( "word", $prmData )
  $vt = DllStructGetData( $tvt, 1 )
  ConsoleWrite( "$vt = " & $vt & @CRLF ) ; EventArgs Class
  $data = DllStructGetData( DllStructCreate( "ptr", $prmData + 8 ), 1 )
  $obj = ConvertPtrToIDispatch($data)
  ConsoleWrite( $obj.ToString() & @CRLF )

  $tvt = DllStructCreate( "word", $prmData + ( @AutoItX64 ? 24 : 16 ) )
  $vt = DllStructGetData( $tvt, 1 )
  ConsoleWrite( "$vt = " & $vt & @CRLF ) ; EventArgs Class
  $data = DllStructGetData( DllStructCreate( "ptr", $prmData + ( @AutoItX64 ? 24 : 16 ) + 8 ), 1 )
  $obj = ConvertPtrToIDispatch($data)
  ConsoleWrite( $obj.ToString() & @CRLF )

  SafeArrayUnaccessData( $pprm )
EndFunc

; In the end we still want the autoit object. This function converts a raw pointer to an autoit object
Func ConvertPtrToIDispatch($IDispatch_Ptr)
  ; This would have been 10000x easier if autoit had supported the idispatch* type in dllstructs...
  ; Fortunetely memcpy can copy the pointer into a idispatch*, lucky us.
  $ptr_struct=DllStructCreate("ptr")
  DllStructSetData($ptr_struct,1,$IDispatch_Ptr)
  $aCall = DllCall("ntdll.dll","ptr:cdecl","memcpy","idispatch*","","ptr",DllStructGetPtr($ptr_struct),"long",4)
  return $aCall[1]
EndFunc

 

Posted (edited)

Hi Larsj, again a victory with your 'event handler' !!!

Also good to see that the SafeArray do really fit in, into this project...

A lot of hightech code to get it working though, but it works that's most important ...

I was reading through the code, and tried to figure out where you add the trigger in the eventhandler to activate a function ?

Like this :

$oButton1.add_click(test())
Edited by ptrex
Posted

Chimp, This was a good laugh.

ptrex, Add some Switch statements to the bottom of the event handler. Here demonstrated with MsgBoxes. Note that the MsgBoxes are not blocking. You can use Tab to navigate between buttons and Enter/Space to click.

tst01.au3

;#AutoIt3Wrapper_UseX64=y

#include "CLR.au3"

Example() ; Form Using System.Windows.Forms.Form

Func Example()
  ; Compile the helper class. This could be pre-compiled.
  Local $oHelper, $oHelperAsm = _CLR_CompileCSharp( FileRead( "EventHelper.cs" ) )
  $oHelperAsm.CreateInstance( "EventHelper", $oHelper )
  ConsoleWrite( "IsObj( $oHelper ) = " & IsObj( $oHelper ) & @CRLF )

  ConsoleWrite( "_CLR_LoadLibrary System.Windows.Forms" & @CRLF )
  Local $oAssembly = _CLR_LoadLibrary( "System.Windows.Forms" )
  ConsoleWrite( "IsObj( $oAssembly ) = " & IsObj( $oAssembly ) & @CRLF )

  ConsoleWrite( @CRLF & "_CRL_CreateObject: System.Windows.Forms.Form" & @CRLF )
  Local $oForm = _CRL_CreateObject( $oAssembly, "System.Windows.Forms.Form" )
  ConsoleWrite( "IsObj( $oForm ) = " & IsObj( $oForm ) & @CRLF )

  $oForm.Text = "Form From Net - AutoIt Rocks"
  $oForm.Width = 800
  $oForm.Height = 400

  ConsoleWrite( @CRLF & "_CRL_CreateObject System.Windows.Forms.Button" & @CRLF )
  Local $oButton1 = _CRL_CreateObject($oAssembly, "System.Windows.Forms.Button")
  $oButton1.Text = "button1"
  $oButton1.Width = 60
  $oButton1.Height = 30
  $oButton1.Left = 100
  $oButton1.Top = 100

  ConsoleWrite( @CRLF & "_CRL_CreateObject System.Windows.Forms.Button" & @CRLF )
  Local $oButton2 = _CRL_CreateObject($oAssembly, "System.Windows.Forms.Button")
  $oButton2.Text = "button2"
  $oButton2.Width = 60
  $oButton2.Height = 30
  $oButton2.Left = $oButton1.Right + 50
  $oButton2.Top = $oButton1.Top

  Local $hEventHandler = DllCallbackRegister( "EventHandler", "int", "ptr" )
  Local $pEventHandler = DllCallbackGetPtr( $hEventHandler )

  ; Add an event handler for the "OnEvent" event.  Use "" to pass the
  ; address as a string, since IDispatch doesn't support 64-bit integers.
  $oHelper.AddHandler( $oButton1, "Click", $pEventHandler )
  $oHelper.AddHandler( $oButton2, "Click", $pEventHandler )

  ;$oForm.Controls.Add( $oButton1 ) ; ERR
  $oButton1.Parent = $oForm ; OK
  $oButton2.Parent = $oForm ; OK

  $oForm.ShowDialog()

  $oForm.Dispose()
EndFunc

; Our event handler is called with a SAFEARRAY of parameters.  This
; makes it much easier to get the type and value of each parameter.
Func EventHandler( $pprm )
  ConsoleWrite( "$pprm = " & $pprm & @CRLF )

  Local $iDim = SafeArrayGetDim( $pprm )
  ConsoleWrite( "$iDim = " & $iDim & @CRLF )

  Local $iLBound, $iUBound
  SafeArrayGetLBound( $pprm, 1, $iLBound )
  SafeArrayGetUBound( $pprm, 1, $iUBound )
  ConsoleWrite( "$iLBound = " & $iLBound & @CRLF )
  ConsoleWrite( "$iUBound = " & $iUBound & @CRLF )

  Local $tprm = DllStructCreate( $tagSAFEARRAY, $pprm )
  Local $fFeatures = DllStructGetData( $tprm, "fFeatures" )
  ConsoleWrite( "$fFeatures = 0x" & Hex( $fFeatures ) & @CRLF )
  Local $cbElements = DllStructGetData( $tprm, "cbElements" )
  ConsoleWrite( "$cbElements = " & $cbElements & @CRLF )

  Local $vt
  SafeArrayGetVartype( $pprm, $vt )
  ConsoleWrite( "$vt = " & $vt & @CRLF )

  Local $prmData, $tvt, $data, $obj
  SafeArrayAccessData( $pprm, $prmData )

  $tvt = DllStructCreate( "word", $prmData )
  $vt = DllStructGetData( $tvt, 1 )
  ConsoleWrite( "$vt = " & $vt & @CRLF ) ; EventArgs Class
  $data = DllStructGetData( DllStructCreate( "ptr", $prmData + 8 ), 1 )
  $obj = ConvertPtrToIDispatch($data)
  Local $sCtrlInfo = $obj.ToString()
  ConsoleWrite( $sCtrlInfo & @CRLF )

  $tvt = DllStructCreate( "word", $prmData + ( @AutoItX64 ? 24 : 16 ) )
  $vt = DllStructGetData( $tvt, 1 )
  ConsoleWrite( "$vt = " & $vt & @CRLF ) ; EventArgs Class
  $data = DllStructGetData( DllStructCreate( "ptr", $prmData + ( @AutoItX64 ? 24 : 16 ) + 8 ), 1 )
  $obj = ConvertPtrToIDispatch($data)
  ConsoleWrite( $obj.ToString() & @CRLF )

  SafeArrayUnaccessData( $pprm )

  Local $aCtrlInfo = StringSplit( $sCtrlInfo, ", ", 2 ) ; 2 = $STR_NOCOUNT
  For $i = 0 To UBound( $aCtrlInfo ) - 1
    ConsoleWrite( "$i = " & $i & ", $aCtrlInfo[$i] = " & $aCtrlInfo[$i] & @CRLF )
  Next
  Switch $aCtrlInfo[0]
    Case "System.Windows.Forms.Button"
      Switch $aCtrlInfo[3]
        Case "button1"
          MsgBox( 0, "", "button1" )
        Case "button2"
          MsgBox( 0, "", "button2" )
      EndSwitch
  EndSwitch
EndFunc

; In the end we still want the autoit object. This function converts a raw pointer to an autoit object
Func ConvertPtrToIDispatch($IDispatch_Ptr)
  ; This would have been 10000x easier if autoit had supported the idispatch* type in dllstructs...
  ; Fortunetely memcpy can copy the pointer into a idispatch*, lucky us.
  $ptr_struct=DllStructCreate("ptr")
  DllStructSetData($ptr_struct,1,$IDispatch_Ptr)
  $aCall = DllCall("ntdll.dll","ptr:cdecl","memcpy","idispatch*","","ptr",DllStructGetPtr($ptr_struct),"long",4)
  return $aCall[1]
EndFunc

 

Posted (edited)

Hi Larsj,

Working like a charm again one step closer to getting the GUI to run including controls...

Thanks again for all the efforts :graduated: There are still some challenges to tackle though ;)

Trancexx,

Indeed works as well like this ? Thanks for looking over our shoulders :)

;#AutoIt3Wrapper_UseX64=y

#include "CLR.au3"

Example() ; Form Using System.Windows.Forms.Form

Func Example()
  ; Compile the helper class. This could be pre-compiled.
  Local $oHelper, $oHelperAsm = _CLR_CompileCSharp( FileRead( "EventHelper.cs" ) )
  $oHelperAsm.CreateInstance( "EventHelper", $oHelper )
  ConsoleWrite( "IsObj( $oHelper ) = " & IsObj( $oHelper ) & @CRLF )

  ConsoleWrite( "_CLR_LoadLibrary System.Windows.Forms" & @CRLF )
  Local $oAssembly = _CLR_LoadLibrary( "System.Windows.Forms" )
  ConsoleWrite( "IsObj( $oAssembly ) = " & IsObj( $oAssembly ) & @CRLF )

  ConsoleWrite( @CRLF & "_CRL_CreateObject: System.Windows.Forms.Form" & @CRLF )
  Local $oForm = _CRL_CreateObject( $oAssembly, "System.Windows.Forms.Form" )
  ConsoleWrite( "IsObj( $oForm ) = " & IsObj( $oForm ) & @CRLF )

  $oForm.Text = "Form From Net - AutoIt Rocks"
  $oForm.Width = 800
  $oForm.Height = 400

  ConsoleWrite( @CRLF & "_CRL_CreateObject System.Windows.Forms.Button" & @CRLF )
  Local $oButton1 = _CRL_CreateObject($oAssembly, "System.Windows.Forms.Button")
  $oButton1.Text = "button1"
  $oButton1.Width = 60
  $oButton1.Height = 30
  $oButton1.Left = 100
  $oButton1.Top = 100

  ConsoleWrite( @CRLF & "_CRL_CreateObject System.Windows.Forms.Button" & @CRLF )
  Local $oButton2 = _CRL_CreateObject($oAssembly, "System.Windows.Forms.Button")
  $oButton2.Text = "button2"
  $oButton2.Width = 60
  $oButton2.Height = 30
  $oButton2.Left = $oButton1.Right + 50
  $oButton2.Top = $oButton1.Top

  Local $hEventHandler = DllCallbackRegister( "EventHandler", "int", "ptr" )
  Local $pEventHandler = DllCallbackGetPtr( $hEventHandler )

  ; Add an event handler for the "OnEvent" event.  Use "" to pass the
  ; address as a string, since IDispatch doesn't support 64-bit integers.
  $oHelper.AddHandler( $oButton1, "Click", $pEventHandler )
  $oHelper.AddHandler( $oButton2, "Click", $pEventHandler )

  ;$oForm.Controls.Add( $oButton1 ) ; ERR
  $oButton1.Parent = $oForm ; OK
  $oButton2.Parent = $oForm ; OK

  $oForm.ShowDialog()

  $oForm.Dispose()
EndFunc

; Our event handler is called with a SAFEARRAY of parameters.  This
; makes it much easier to get the type and value of each parameter.
Func EventHandler( $pprm )
  ConsoleWrite( "$pprm = " & $pprm & @CRLF )

  Local $iDim = SafeArrayGetDim( $pprm )
  ConsoleWrite( "$iDim = " & $iDim & @CRLF )

  Local $iLBound, $iUBound
  SafeArrayGetLBound( $pprm, 1, $iLBound )
  SafeArrayGetUBound( $pprm, 1, $iUBound )
  ConsoleWrite( "$iLBound = " & $iLBound & @CRLF )
  ConsoleWrite( "$iUBound = " & $iUBound & @CRLF )

  Local $tprm = DllStructCreate( $tagSAFEARRAY, $pprm )
  Local $fFeatures = DllStructGetData( $tprm, "fFeatures" )
  ConsoleWrite( "$fFeatures = 0x" & Hex( $fFeatures ) & @CRLF )
  Local $cbElements = DllStructGetData( $tprm, "cbElements" )
  ConsoleWrite( "$cbElements = " & $cbElements & @CRLF )

  Local $vt
  SafeArrayGetVartype( $pprm, $vt )
  ConsoleWrite( "$vt = " & $vt & @CRLF )

  Local $prmData, $tvt, $data, $obj
  SafeArrayAccessData( $pprm, $prmData )

  $tvt = DllStructCreate( "word", $prmData )
  $vt = DllStructGetData( $tvt, 1 )
  ConsoleWrite( "$vt = " & $vt & @CRLF ) ; EventArgs Class
  $data = DllStructGetData( DllStructCreate( "ptr", $prmData + 8 ), 1 )

    ;~   $obj = ConvertPtrToIDispatch($data)
  $obj = ObjCreateInterface($data, $sIID_IDispatch)

  Local $sCtrlInfo = $obj.ToString()
  ConsoleWrite( $sCtrlInfo & @CRLF )

  $tvt = DllStructCreate( "word", $prmData + ( @AutoItX64 ? 24 : 16 ) )
  $vt = DllStructGetData( $tvt, 1 )
  ConsoleWrite( "$vt = " & $vt & @CRLF ) ; EventArgs Class
  $data = DllStructGetData( DllStructCreate( "ptr", $prmData + ( @AutoItX64 ? 24 : 16 ) + 8 ), 1 )
;~   $obj = ConvertPtrToIDispatch($data)
  $obj = ObjCreateInterface($data, $sIID_IDispatch)
  ConsoleWrite( $obj.ToString() & @CRLF )

  SafeArrayUnaccessData( $pprm )

  Local $aCtrlInfo = StringSplit( $sCtrlInfo, ", ", 2 ) ; 2 = $STR_NOCOUNT
  For $i = 0 To UBound( $aCtrlInfo ) - 1
    ConsoleWrite( "$i = " & $i & ", $aCtrlInfo[$i] = " & $aCtrlInfo[$i] & @CRLF )
  Next
  Switch $aCtrlInfo[0]
    Case "System.Windows.Forms.Button"
      Switch $aCtrlInfo[3]
        Case "button1"
          MsgBox( 0, "", "button1" )
        Case "button2"
          MsgBox( 0, "", "button2" )
      EndSwitch
  EndSwitch
EndFunc

#cs
; In the end we still want the autoit object. This function converts a raw pointer to an autoit object
Func ConvertPtrToIDispatch($IDispatch_Ptr)
  ; This would have been 10000x easier if autoit had supported the idispatch* type in dllstructs...
  ; Fortunetely memcpy can copy the pointer into a idispatch*, lucky us.
  $ptr_struct=DllStructCreate("ptr")
  DllStructSetData($ptr_struct,1,$IDispatch_Ptr)
  $aCall = DllCall("ntdll.dll","ptr:cdecl","memcpy","idispatch*","","ptr",DllStructGetPtr($ptr_struct),"long",4)
  return $aCall[1]
EndFunc
#ce

 

 

Edited by ptrex
Posted (edited)

In the code in the post above you can replace

; Add an event handler for the "OnEvent" event.  Use "" to pass the
; address as a string, since IDispatch doesn't support 64-bit integers.
$oHelper.AddHandler( $oButton1, "Click", $pEventHandler )
$oHelper.AddHandler( $oButton2, "Click", $pEventHandler )

with

; Make an event handler (event must be of the EventHandler type).
Local $oHandler = $oHelper.MakeHandler( $pEventHandler )
$oButton1.add_Click( $oHandler )
$oButton2.add_Click( $oHandler )

if you prefer. And in fact it's probably the best way.

 

And in bottom of the event handler it's much better to compare the objects directly instead of just checking the text of the object:

For $i = 0 To UBound( $aObjects ) - 1
  If $obj.Equals( $aObjects[$i] ) Then ExitLoop
Next

If $i < UBound( $aObjects ) Then
  MsgBox( 0, "", $obj.ToString() )
EndIf

 

tst02.au3:

;#AutoIt3Wrapper_UseX64=y

#include "CLR.au3"

Global $aObjects[2]

Example() ; Form Using System.Windows.Forms.Form

Func Example()
  ; Compile the helper class. This could be pre-compiled.
  Local $oHelper, $oHelperAsm = _CLR_CompileCSharp( FileRead( "EventHelper.cs" ) )
  $oHelperAsm.CreateInstance( "EventHelper", $oHelper )
  ConsoleWrite( "IsObj( $oHelper ) = " & IsObj( $oHelper ) & @CRLF )

  ConsoleWrite( "_CLR_LoadLibrary System.Windows.Forms" & @CRLF )
  Local $oAssembly = _CLR_LoadLibrary( "System.Windows.Forms" )
  ConsoleWrite( "IsObj( $oAssembly ) = " & IsObj( $oAssembly ) & @CRLF )

  ConsoleWrite( @CRLF & "_CRL_CreateObject: System.Windows.Forms.Form" & @CRLF )
  Local $oForm = _CRL_CreateObject( $oAssembly, "System.Windows.Forms.Form" )
  ConsoleWrite( "IsObj( $oForm ) = " & IsObj( $oForm ) & @CRLF )

  $oForm.Text = "Form From Net - AutoIt Rocks"
  $oForm.Width = 800
  $oForm.Height = 400

  ConsoleWrite( @CRLF & "_CRL_CreateObject System.Windows.Forms.Button" & @CRLF )
  Local $oButton1 = _CRL_CreateObject($oAssembly, "System.Windows.Forms.Button")
  $oButton1.Text = "button1"
  $oButton1.Width = 60
  $oButton1.Height = 30
  $oButton1.Left = 100
  $oButton1.Top = 100
  $aObjects[0] = $oButton1

  ConsoleWrite( @CRLF & "_CRL_CreateObject System.Windows.Forms.Button" & @CRLF )
  Local $oButton2 = _CRL_CreateObject($oAssembly, "System.Windows.Forms.Button")
  $oButton2.Text = "button2"
  $oButton2.Width = 60
  $oButton2.Height = 30
  $oButton2.Left = $oButton1.Right + 50
  $oButton2.Top = $oButton1.Top
  $aObjects[1] = $oButton2

  Local $hEventHandler = DllCallbackRegister( "EventHandler", "int", "ptr" )
  Local $pEventHandler = DllCallbackGetPtr( $hEventHandler )

  ; Add an event handler for the "OnEvent" event.  Use "" to pass the
  ; address as a string, since IDispatch doesn't support 64-bit integers.
  ;$oHelper.AddHandler( $oButton1, "Click", $pEventHandler )
  ;$oHelper.AddHandler( $oButton2, "Click", $pEventHandler )

  ; Make an event handler (event must be of the EventHandler type).
  Local $oHandler = $oHelper.MakeHandler( $pEventHandler )
  $oButton1.add_Click( $oHandler )
  $oButton2.add_Click( $oHandler )

  ;$oForm.Controls.Add( $oButton1 ) ; ERR
  $oButton1.Parent = $oForm ; OK
  $oButton2.Parent = $oForm ; OK

  $oForm.ShowDialog()

  $oForm.Dispose()
EndFunc

; Our event handler is called with a SAFEARRAY of parameters.  This
; makes it much easier to get the type and value of each parameter.
Func EventHandler( $pprm )
  ConsoleWrite( "$pprm = " & $pprm & @CRLF )

  Local $iDim = SafeArrayGetDim( $pprm )
  ConsoleWrite( "$iDim = " & $iDim & @CRLF )

  Local $iLBound, $iUBound
  SafeArrayGetLBound( $pprm, 1, $iLBound )
  SafeArrayGetUBound( $pprm, 1, $iUBound )
  ConsoleWrite( "$iLBound = " & $iLBound & @CRLF )
  ConsoleWrite( "$iUBound = " & $iUBound & @CRLF )

  Local $tprm = DllStructCreate( $tagSAFEARRAY, $pprm )
  Local $fFeatures = DllStructGetData( $tprm, "fFeatures" )
  ConsoleWrite( "$fFeatures = 0x" & Hex( $fFeatures ) & @CRLF )
  Local $cbElements = DllStructGetData( $tprm, "cbElements" )
  ConsoleWrite( "$cbElements = " & $cbElements & @CRLF )

  Local $vt
  SafeArrayGetVartype( $pprm, $vt )
  ConsoleWrite( "$vt = " & $vt & @CRLF )

  Local $prmData, $pData, $obj
  SafeArrayAccessData( $pprm, $prmData )

  $vt = DllStructGetData( DllStructCreate( "word", $prmData ), 1 )
  ConsoleWrite( "$vt = " & $vt & @CRLF & @CRLF ) ; EventArgs Class
  $pData = DllStructGetData( DllStructCreate( "ptr", $prmData + 8 ), 1 )
  $obj = ObjCreateInterface( $pData, $sIID_IDispatch )

  SafeArrayUnaccessData( $pprm )

  For $i = 0 To UBound( $aObjects ) - 1
    If $obj.Equals( $aObjects[$i] ) Then ExitLoop
  Next

  If $i < UBound( $aObjects ) Then
    MsgBox( 0, "", $obj.ToString() )
  EndIf
EndFunc

 

Edited by LarsJ
Posted

Hi Larsj,

Nice you reworked the EventHandler in clean code ... !

I added some more controls to the Example :

  1. Label
  2. CheckBox
  3. TextBox
  4. ComboBox
  5. Listview : issues here adding columns ?!
;#AutoIt3Wrapper_UseX64=y

#include "CLR.au3"

Global $aObjects[2]

Example() ; Form Using System.Windows.Forms.Form

Func Example()
#Region Register EventHelper
  ; Compile the helper class. This could be pre-compiled.
  Local $oHelper, $oHelperAsm = _CLR_CompileCSharp( FileRead( "EventHelper.cs" ) )
  $oHelperAsm.CreateInstance( "EventHelper", $oHelper )
  ConsoleWrite( "IsObj( $oHelper ) = " & IsObj( $oHelper ) & @CRLF )
#EndRegion

#Region Add Controls
  ConsoleWrite( "_CLR_LoadLibrary System.Windows.Forms" & @CRLF )
  Local $oAssembly = _CLR_LoadLibrary( "System.Windows.Forms" )
  ConsoleWrite( "IsObj( $oAssembly ) = " & IsObj( $oAssembly ) & @CRLF )

  ConsoleWrite( @CRLF & "_CRL_CreateObject: System.Windows.Forms.Form" & @CRLF )
  Local $oForm = _CRL_CreateObject( $oAssembly, "System.Windows.Forms.Form" )
  ConsoleWrite( "IsObj( $oForm ) = " & IsObj( $oForm ) & @CRLF )

  $oForm.Text = "Form From Net - AutoIt Rocks"
  $oForm.Width = 800
  $oForm.Height = 400

  ConsoleWrite( @CRLF & "_CRL_CreateObject System.Windows.Forms.Button 1" & @CRLF )
  Local $oButton1 = _CRL_CreateObject($oAssembly, "System.Windows.Forms.Button")
  $oButton1.Text = "button1"
  $oButton1.Width = 60
  $oButton1.Height = 30
  $oButton1.Left = 40
  $oButton1.Top = 10
  $aObjects[0] = $oButton1

  ConsoleWrite( @CRLF & "_CRL_CreateObject System.Windows.Forms.Button 2" & @CRLF )
  Local $oButton2 = _CRL_CreateObject($oAssembly, "System.Windows.Forms.Button")
  $oButton2.Text = "button2"
  $oButton2.Width = 60
  $oButton2.Height = 30
  $oButton2.Left = $oButton1.Right + 50
  $oButton2.Top = $oButton1.Top
  $aObjects[1] = $oButton2

  ConsoleWrite( @CRLF & "_CRL_CreateObject System.Windows.Forms.checkbox" & @CRLF )
  Local $checkbox1 = _CRL_CreateObject($oAssembly, "System.Windows.Forms.checkbox")
  $checkbox1.Top = 55
  $checkbox1.Left = 155

  ConsoleWrite( @CRLF & "_CRL_CreateObject System.Windows.Forms.ComboBox" & @CRLF )
  Local $Combo = _CRL_CreateObject($oAssembly, "System.Windows.Forms.ComboBox")
  $Combo.Width = 50
  $Combo.Top = 15
  $Combo.Left = 290

    For $i = 1 to 4
        $Combo.Items.Add("test" & $i)
    Next

  ConsoleWrite( @CRLF & "_CRL_CreateObject System.Windows.Forms.Label" & @CRLF )
  Local $Label = _CRL_CreateObject($oAssembly, "System.Windows.Forms.Label")
  $Label.Width = 140
  $Label.Top = 55
  $Label.Left = 15
;~   $Label.Font = "Microsoft Sans Serif, 18pt, style=Bold, Italic" ; Does not Work
  $Label.Text = "Press The Buttons ..."

  ConsoleWrite( @CRLF & "_CRL_CreateObject System.Windows.Forms.TextBox" & @CRLF )
  Local $Textbox = _CRL_CreateObject($oAssembly, "System.Windows.Forms.TextBox")
  $Textbox.Width = 120
  $Textbox.Top = 55
  $Textbox.Left = 295
  $Textbox.Text = "Enter Text Here ..."

  ConsoleWrite( @CRLF & "_CRL_CreateObject System.Windows.Forms.ListView" & @CRLF )
  Local $ListView = _CRL_CreateObject($oAssembly, "System.Windows.Forms.ListView")
  $ListView.Width = 220
  $ListView.Top = 95
  $ListView.Left = 295

  ConsoleWrite( @CRLF & "_CRL_CreateObject System.Windows.Forms.ColumnHeader" & @CRLF )
  Local $columnHeader1 = _CRL_CreateObject($oAssembly, "System.Windows.Forms.ColumnHeader")
  ConsoleWrite("$columnHeader1 : " & IsObj($columnHeader1) & @CRLF & @CRLF)

  $ListView.Columns.add($columnHeader1) ; Does not work !!!

#EndRegion

#Region Events
  Local $hEventHandler = DllCallbackRegister( "EventHandler", "int", "ptr" )
  Local $pEventHandler = DllCallbackGetPtr( $hEventHandler )

  ; Add an event handler for the "OnEvent" event.  Use "" to pass the
  ; address as a string, since IDispatch doesn't support 64-bit integers.
  ; $oHelper.AddHandler( $oButton1, "Click", $pEventHandler )
  ; $oHelper.AddHandler( $oButton2, "Click", $pEventHandler )

  ; Make an event handler (event must be of the EventHandler type).
  Local $oHandler = $oHelper.MakeHandler( $pEventHandler )

  $oButton1.add_Click( $oHandler )
  $oButton2.add_Click( $oHandler )
#EndRegion

#Region Add Controls
  ;$oForm.Controls.Add( $oButton1 ) ; ERR
  $oButton1.Parent = $oForm ; OK
  $oButton2.Parent = $oForm
  $checkbox1.Parent = $oForm
  $Combo.Parent = $oForm
  $Label.Parent = $oForm
  $Textbox.Parent = $oForm
  $ListView.Parent = $oForm
#EndRegion

  $oForm.ShowDialog()
  $oForm.Dispose()

EndFunc

#Region EventHandler
; Our event handler is called with a SAFEARRAY of parameters.  This
; makes it much easier to get the type and value of each parameter.
Func EventHandler( $pprm )
  ConsoleWrite( "$pprm = " & $pprm & @CRLF )

  Local $iDim = SafeArrayGetDim( $pprm )
  ConsoleWrite( "$iDim = " & $iDim & @CRLF )

  Local $iLBound, $iUBound
  SafeArrayGetLBound( $pprm, 1, $iLBound )
  SafeArrayGetUBound( $pprm, 1, $iUBound )
  ConsoleWrite( "$iLBound = " & $iLBound & @CRLF )
  ConsoleWrite( "$iUBound = " & $iUBound & @CRLF )

  Local $tprm = DllStructCreate( $tagSAFEARRAY, $pprm )
  Local $fFeatures = DllStructGetData( $tprm, "fFeatures" )
  ConsoleWrite( "$fFeatures = 0x" & Hex( $fFeatures ) & @CRLF )
  Local $cbElements = DllStructGetData( $tprm, "cbElements" )
  ConsoleWrite( "$cbElements = " & $cbElements & @CRLF )

  Local $vt
  SafeArrayGetVartype( $pprm, $vt )
  ConsoleWrite( "$vt = " & $vt & @CRLF )

  Local $prmData, $pData, $obj
  SafeArrayAccessData( $pprm, $prmData )

  $vt = DllStructGetData( DllStructCreate( "word", $prmData ), 1 )
  ConsoleWrite( "$vt = " & $vt & @CRLF & @CRLF ) ; EventArgs Class
  $pData = DllStructGetData( DllStructCreate( "ptr", $prmData + 8 ), 1 )
  $obj = ObjCreateInterface( $pData, $sIID_IDispatch ) ; Convert Pointer to iDispatch

  SafeArrayUnaccessData( $pprm )

  For $i = 0 To UBound( $aObjects ) - 1
    If $obj.Equals( $aObjects[$i] ) Then ExitLoop
  Next

  If $i < UBound( $aObjects ) Then
    MsgBox( 0, "", $obj.ToString() )
  EndIf

EndFunc
#EndRegion

Again issues with adding controls using the .NET syntax :(

Anyhow there are a few more hurdles to take ... like  the  _CRL_CreateObject( xxx, Arg1, Arg2, ...) is missing  list of arguments to pass to the object's constructor.

Examples : 

System.Drawing.Size(130,60)

- System.Drawing.Font("Times New Roman",12)

Also found this was implemented on the AHK CLR :  CLR_CreateObject( Assembly, sType [, Type1, Arg1, Type2, Arg2 ... ] ) (CLR)

Instantiates an object of the specified type from the specified assembly. Optionally accepts a list of arguments to pass to the object's constructor. For AutoHotkey Basic, TypeN is a value from the VARENUM enumeration

Again we are a few steps closer to the finish line ... :)

 

 

Posted

You don't need to use .Equals to compare objects. AutoIt can do that by itself.

Besides this all can be reduced on both sides, maybe like this:
EventHelper.cs

using System;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.InteropServices;

public class EventHelper {
  // Delegate type for the AutoIt callback.
  public delegate void CallbackType([MarshalAs(UnmanagedType.LPArray)] object[] argv);

  public EventHandler MakeHandler([MarshalAs(UnmanagedType.FunctionPtr)] CallbackType cb) {
    return (sender, e) => cb( new[] {sender} );
  }
}

And then the last example LarsJ posted

#include "CLR.au3"


Global $aObjects[2]
Example() ; Form Using System.Windows.Forms.Form



Func Example()
    ; Compile the helper class. This could be pre-compiled.
    Local $oHelper, $oHelperAsm = _CLR_CompileCSharp(FileRead("EventHelper.cs"))
    $oHelperAsm.CreateInstance("EventHelper", $oHelper)
    ConsoleWrite("IsObj( $oHelper ) = " & IsObj($oHelper) & @CRLF)

    ConsoleWrite("_CLR_LoadLibrary System.Windows.Forms" & @CRLF)
    Local $oAssembly = _CLR_LoadLibrary("System.Windows.Forms")
    ConsoleWrite("IsObj( $oAssembly ) = " & IsObj($oAssembly) & @CRLF)

    ConsoleWrite(@CRLF & "_CRL_CreateObject: System.Windows.Forms.Form" & @CRLF)
    Local $oForm = _CRL_CreateObject($oAssembly, "System.Windows.Forms.Form")
    ConsoleWrite("IsObj( $oForm ) = " & IsObj($oForm) & @CRLF)

    $oForm.Text = "Form From Net - AutoIt Rocks"
    $oForm.Width = 800
    $oForm.Height = 400

    ConsoleWrite(@CRLF & "_CRL_CreateObject System.Windows.Forms.Button" & @CRLF)
    Local $oButton1 = _CRL_CreateObject($oAssembly, "System.Windows.Forms.Button")
    $oButton1.Text = "button1"
    $oButton1.Width = 60
    $oButton1.Height = 30
    $oButton1.Left = 100
    $oButton1.Top = 100
    $aObjects[0] = $oButton1

    ConsoleWrite(@CRLF & "_CRL_CreateObject System.Windows.Forms.Button" & @CRLF)
    Local $oButton2 = _CRL_CreateObject($oAssembly, "System.Windows.Forms.Button")
    $oButton2.Text = "button2"
    $oButton2.Width = 60
    $oButton2.Height = 30
    $oButton2.Left = $oButton1.Right + 50
    $oButton2.Top = $oButton1.Top
    $aObjects[1] = $oButton2

    Local $hEventHandler = DllCallbackRegister("EventHandler", "int", "ptr")
    Local $pEventHandler = DllCallbackGetPtr($hEventHandler)


    ; Make an event handler (event must be of the EventHandler type).
    Local $oHandler = $oHelper.MakeHandler($pEventHandler)


    $oButton1.add_Click($oHandler)
    $oButton2.add_Click($oHandler)

    $oButton1.Parent = $oForm ; OK
    $oButton2.Parent = $oForm ; OK

    $oForm.ShowDialog()

    $oForm.Dispose()
EndFunc

; Event handler is called with a LPArray of 1 parameter (variant pointer).
Func EventHandler($pprm)

    ConsoleWrite("$pprm = " & $pprm & @CRLF)

    Local $vVar = DllStructCreate($tagVARIANT, $pprm)
    $obj = ObjCreateInterface(DllStructGetData($vVar, "data"), $sIID_IDispatch)

    For $i = 0 To UBound($aObjects) - 1
        If $obj = $aObjects[$i] Then
            MsgBox(0, "", $obj.ToString(), 0, $obj.Handle) ; give it parent handle not to have ten boxes one over other
            ExitLoop
        EndIf
    Next

EndFunc

 

♡♡♡

.

eMyvnE

Posted

trancexx,

Nice clean up ! works like a charm ... but

;#AutoIt3Wrapper_UseX64=y

#include "CLR.au3"

Global $aObjects[3]

Example() ; Form Using System.Windows.Forms.Form

Func Example()
#Region Register EventHelper
  ; Compile the helper class. This could be pre-compiled.
  Local $oHelper, $oHelperAsm = _CLR_CompileCSharp( FileRead( "EventHelperNEW.cs" ) ) ; NEWER Version
  $oHelperAsm.CreateInstance( "EventHelper", $oHelper )
  ConsoleWrite( "IsObj( $oHelper ) = " & IsObj( $oHelper ) & @CRLF )
#EndRegion

#Region Add Controls
  ConsoleWrite( "_CLR_LoadLibrary System.Drawing" & @CRLF )
  Local $oAssemblyDraw = _CLR_LoadLibrary( "System.Drawing" )
  ConsoleWrite( "IsObj( $oAssemblyDraw ) = " & IsObj( $oAssemblyDraw ) & @CRLF )

  ConsoleWrite( "_CLR_LoadLibrary System.Windows.Forms" & @CRLF )
  Local $oAssembly = _CLR_LoadLibrary( "System.Windows.Forms" )
  ConsoleWrite( "IsObj( $oAssembly ) = " & IsObj( $oAssembly ) & @CRLF )

  ConsoleWrite( @CRLF & "_CRL_CreateObject: System.Windows.Forms.Form" & @CRLF )
  Local $oForm = _CRL_CreateObject( $oAssembly, "System.Windows.Forms.Form" )
  ConsoleWrite( "IsObj( $oForm ) = " & IsObj( $oForm ) & @CRLF )

  $oForm.Text = "Form From Net - AutoIt Rocks"
  $oForm.Width = 800
  $oForm.Height = 400

  ConsoleWrite( @CRLF & "_CRL_CreateObject System.Windows.Forms.Button 1" & @CRLF )
  Local $oButton1 = _CRL_CreateObject($oAssembly, "System.Windows.Forms.Button")
  $oButton1.Text = "button1"
  $oButton1.Width = 60
  $oButton1.Height = 30
  $oButton1.Left = 40
  $oButton1.Top = 10
  $aObjects[0] = $oButton1

  ConsoleWrite( @CRLF & "_CRL_CreateObject System.Windows.Forms.Button 2" & @CRLF )
  Local $oButton2 = _CRL_CreateObject($oAssembly, "System.Windows.Forms.Button")
  $oButton2.Text = "button2"
  $oButton2.Width = 60
  $oButton2.Height = 30
  $oButton2.Left = $oButton1.Right + 50
  $oButton2.Top = $oButton1.Top
  $aObjects[1] = $oButton2

  ConsoleWrite( @CRLF & "_CRL_CreateObject System.Windows.Forms.checkbox" & @CRLF )
  Local $checkbox1 = _CRL_CreateObject($oAssembly, "System.Windows.Forms.checkbox")
  $checkbox1.Top = 55
  $checkbox1.Left = 155
  $checkbox1.Checked = True

  ConsoleWrite( @CRLF & "_CRL_CreateObject System.Windows.Forms.ComboBox" & @CRLF )
  Local $Combo = _CRL_CreateObject($oAssembly, "System.Windows.Forms.ComboBox")
  $Combo.Width = 50
  $Combo.Top = 15
  $Combo.Left = 290

    For $i = 1 to 4
        $Combo.Items.Add("test" & $i)
    Next

  ConsoleWrite( @CRLF & "_CRL_CreateObject System.Windows.Forms.Label" & @CRLF )
  Local $Label = _CRL_CreateObject($oAssembly, "System.Windows.Forms.Label")
  $Label.Width = 140
  $Label.Top = 55
  $Label.Left = 15
;~   $Label.Font = "Microsoft Sans Serif, 18pt, style=Bold, Italic" ; Does not Work
  $Label.Text = "Press The Buttons ..."

  ConsoleWrite( @CRLF & "_CRL_CreateObject System.Windows.Forms.TextBox" & @CRLF )
  Local $Textbox = _CRL_CreateObject($oAssembly, "System.Windows.Forms.TextBox")
  $Textbox.Width = 120
  $Textbox.Top = 55
  $Textbox.Left = 295
  $Textbox.Text = "Enter Text Here ..."

  ConsoleWrite( @CRLF & "_CRL_CreateObject System.Windows.Forms.ListView" & @CRLF )
  Local $ListView = _CRL_CreateObject($oAssembly, "System.Windows.Forms.ListView")
  $ListView.Width = 220
  $ListView.Top = 95
  $ListView.Left = 295
  $ListView.Name = "listView1"

  ConsoleWrite( @CRLF & "_CRL_CreateObject System.Windows.Forms.ColumnHeader" & @CRLF )
  Local $columnHeader1 = _CRL_CreateObject($oAssembly, "System.Windows.Forms.ColumnHeader")
  ConsoleWrite("$columnHeader1 : " & IsObj($columnHeader1) & @CRLF & @CRLF)

  $ListView.Columns.add($columnHeader1) ; Does not work !!!

#EndRegion

#Region Events
  Local $hEventHandler = DllCallbackRegister( "EventHandler", "int", "ptr" )
  Local $pEventHandler = DllCallbackGetPtr( $hEventHandler )

  ; Add an event handler for the "OnEvent" event.  Use "" to pass the
  ; address as a string, since IDispatch doesn't support 64-bit integers.
  ; $oHelper.AddHandler( $oButton1, "Click", $pEventHandler )
  ; $oHelper.AddHandler( $oButton2, "Click", $pEventHandler )

  ; Make an event handler (event must be of the EventHandler type).
  Local $oHandler = $oHelper.MakeHandler( $pEventHandler )

  $oButton1.add_Click( $oHandler )
  $oButton2.add_Click( $oHandler )
  $checkbox1.add_Click( $oHandler )
#EndRegion

#Region Add Controls
  ;$oForm.Controls.Add( $oButton1 ) ; ERR
  $oButton1.Parent = $oForm ; OK
  $oButton2.Parent = $oForm
  $checkbox1.Parent = $oForm
  $Combo.Parent = $oForm
  $Label.Parent = $oForm
  $Textbox.Parent = $oForm
  $ListView.Parent = $oForm
 ; $Panel.Parent = $oForm
#EndRegion

  $oForm.ShowDialog()
  $oForm.Dispose()

EndFunc

#Region EventHandler
; Event handler is called with a LPArray of 1 parameter (variant pointer).
Func EventHandler($pprm)

    ConsoleWrite("$pprm = " & $pprm & @CRLF)

    Local $vVar = DllStructCreate($tagVARIANT, $pprm)
    $obj = ObjCreateInterface(DllStructGetData($vVar, "data"), $sIID_IDispatch)

    For $i = 0 To UBound($aObjects) - 1

        ConsoleWrite("test " & $obj & " " & $aObjects[$i] & @CRLF)

        If $obj = $aObjects[$i] Then
            MsgBox(0, "Event Output", $obj.ToString(), 0, $obj.Handle) ; give it parent handle not to have ten boxes one over other
            ExitLoop
        EndIf
    Next

EndFunc
#EndRegion

Added an event to checkbox but no output ?

the $obj does not return anything ?

 

Posted
18 minutes ago, ptrex said:

trancexx,

Nice clean up ! works like a charm ... but

;#AutoIt3Wrapper_UseX64=y

#include "CLR.au3"

Global $aObjects[3]

Example() ; Form Using System.Windows.Forms.Form

Func Example()
#Region Register EventHelper
  ; Compile the helper class. This could be pre-compiled.
  Local $oHelper, $oHelperAsm = _CLR_CompileCSharp( FileRead( "EventHelperNEW.cs" ) ) ; NEWER Version
  $oHelperAsm.CreateInstance( "EventHelper", $oHelper )
  ConsoleWrite( "IsObj( $oHelper ) = " & IsObj( $oHelper ) & @CRLF )
#EndRegion

#Region Add Controls
  ConsoleWrite( "_CLR_LoadLibrary System.Drawing" & @CRLF )
  Local $oAssemblyDraw = _CLR_LoadLibrary( "System.Drawing" )
  ConsoleWrite( "IsObj( $oAssemblyDraw ) = " & IsObj( $oAssemblyDraw ) & @CRLF )

  ConsoleWrite( "_CLR_LoadLibrary System.Windows.Forms" & @CRLF )
  Local $oAssembly = _CLR_LoadLibrary( "System.Windows.Forms" )
  ConsoleWrite( "IsObj( $oAssembly ) = " & IsObj( $oAssembly ) & @CRLF )

  ConsoleWrite( @CRLF & "_CRL_CreateObject: System.Windows.Forms.Form" & @CRLF )
  Local $oForm = _CRL_CreateObject( $oAssembly, "System.Windows.Forms.Form" )
  ConsoleWrite( "IsObj( $oForm ) = " & IsObj( $oForm ) & @CRLF )

  $oForm.Text = "Form From Net - AutoIt Rocks"
  $oForm.Width = 800
  $oForm.Height = 400

  ConsoleWrite( @CRLF & "_CRL_CreateObject System.Windows.Forms.Button 1" & @CRLF )
  Local $oButton1 = _CRL_CreateObject($oAssembly, "System.Windows.Forms.Button")
  $oButton1.Text = "button1"
  $oButton1.Width = 60
  $oButton1.Height = 30
  $oButton1.Left = 40
  $oButton1.Top = 10
  $aObjects[0] = $oButton1

  ConsoleWrite( @CRLF & "_CRL_CreateObject System.Windows.Forms.Button 2" & @CRLF )
  Local $oButton2 = _CRL_CreateObject($oAssembly, "System.Windows.Forms.Button")
  $oButton2.Text = "button2"
  $oButton2.Width = 60
  $oButton2.Height = 30
  $oButton2.Left = $oButton1.Right + 50
  $oButton2.Top = $oButton1.Top
  $aObjects[1] = $oButton2

  ConsoleWrite( @CRLF & "_CRL_CreateObject System.Windows.Forms.checkbox" & @CRLF )
  Local $checkbox1 = _CRL_CreateObject($oAssembly, "System.Windows.Forms.checkbox")
  $checkbox1.Top = 55
  $checkbox1.Left = 155
  $checkbox1.Checked = True

  ConsoleWrite( @CRLF & "_CRL_CreateObject System.Windows.Forms.ComboBox" & @CRLF )
  Local $Combo = _CRL_CreateObject($oAssembly, "System.Windows.Forms.ComboBox")
  $Combo.Width = 50
  $Combo.Top = 15
  $Combo.Left = 290

    For $i = 1 to 4
        $Combo.Items.Add("test" & $i)
    Next

  ConsoleWrite( @CRLF & "_CRL_CreateObject System.Windows.Forms.Label" & @CRLF )
  Local $Label = _CRL_CreateObject($oAssembly, "System.Windows.Forms.Label")
  $Label.Width = 140
  $Label.Top = 55
  $Label.Left = 15
;~   $Label.Font = "Microsoft Sans Serif, 18pt, style=Bold, Italic" ; Does not Work
  $Label.Text = "Press The Buttons ..."

  ConsoleWrite( @CRLF & "_CRL_CreateObject System.Windows.Forms.TextBox" & @CRLF )
  Local $Textbox = _CRL_CreateObject($oAssembly, "System.Windows.Forms.TextBox")
  $Textbox.Width = 120
  $Textbox.Top = 55
  $Textbox.Left = 295
  $Textbox.Text = "Enter Text Here ..."

  ConsoleWrite( @CRLF & "_CRL_CreateObject System.Windows.Forms.ListView" & @CRLF )
  Local $ListView = _CRL_CreateObject($oAssembly, "System.Windows.Forms.ListView")
  $ListView.Width = 220
  $ListView.Top = 95
  $ListView.Left = 295
  $ListView.Name = "listView1"

  ConsoleWrite( @CRLF & "_CRL_CreateObject System.Windows.Forms.ColumnHeader" & @CRLF )
  Local $columnHeader1 = _CRL_CreateObject($oAssembly, "System.Windows.Forms.ColumnHeader")
  ConsoleWrite("$columnHeader1 : " & IsObj($columnHeader1) & @CRLF & @CRLF)

  $ListView.Columns.add($columnHeader1) ; Does not work !!!

#EndRegion

#Region Events
  Local $hEventHandler = DllCallbackRegister( "EventHandler", "int", "ptr" )
  Local $pEventHandler = DllCallbackGetPtr( $hEventHandler )

  ; Add an event handler for the "OnEvent" event.  Use "" to pass the
  ; address as a string, since IDispatch doesn't support 64-bit integers.
  ; $oHelper.AddHandler( $oButton1, "Click", $pEventHandler )
  ; $oHelper.AddHandler( $oButton2, "Click", $pEventHandler )

  ; Make an event handler (event must be of the EventHandler type).
  Local $oHandler = $oHelper.MakeHandler( $pEventHandler )

  $oButton1.add_Click( $oHandler )
  $oButton2.add_Click( $oHandler )
  $checkbox1.add_Click( $oHandler )
#EndRegion

#Region Add Controls
  ;$oForm.Controls.Add( $oButton1 ) ; ERR
  $oButton1.Parent = $oForm ; OK
  $oButton2.Parent = $oForm
  $checkbox1.Parent = $oForm
  $Combo.Parent = $oForm
  $Label.Parent = $oForm
  $Textbox.Parent = $oForm
  $ListView.Parent = $oForm
 ; $Panel.Parent = $oForm
#EndRegion

  $oForm.ShowDialog()
  $oForm.Dispose()

EndFunc

#Region EventHandler
; Event handler is called with a LPArray of 1 parameter (variant pointer).
Func EventHandler($pprm)

    ConsoleWrite("$pprm = " & $pprm & @CRLF)

    Local $vVar = DllStructCreate($tagVARIANT, $pprm)
    $obj = ObjCreateInterface(DllStructGetData($vVar, "data"), $sIID_IDispatch)

    For $i = 0 To UBound($aObjects) - 1

        ConsoleWrite("test " & $obj & " " & $aObjects[$i] & @CRLF)

        If $obj = $aObjects[$i] Then
            MsgBox(0, "Event Output", $obj.ToString(), 0, $obj.Handle) ; give it parent handle not to have ten boxes one over other
            ExitLoop
        EndIf
    Next

EndFunc
#EndRegion

Added an event to checkbox but no output ?

the $obj does not return anything ?

 

Shouldn't you have $aObjects[2] = $checkbox1 somewhere there?

♡♡♡

.

eMyvnE

Posted

Listview. Can't you use the parent parameter of $columnHeader1?

So far CLR.au3 only contains code that is used in the specific examples.

The code in my examples is not finished code. So it may well be possible to optimize some bits of the code.

EventHelper.cs. The AddHandler function is not used in these examples. But I still think there is a need for it. Eg. to detect events generated through C#/VB code.

trancexx, Do you have an idea of what this problem with Controls.Add and Columns.Add (Form.Controls.Add(Button), ListView.Columns.Add(ColumnHeader)) is? And if so how we solve the problem?

Posted (edited)

The question is first to find out where the problem is in CLR or in AU3 ?

Junkew can you do a test if it works in VBA ? 

Larsj is it working in AHK ?

If it is not working there it is not an AU3 issue but a limitation of CLR. :mad:

Edited by ptrex
Posted

Little to busy with other stuff maybe in the weekend Ï will try further

Nice reading on

Posted

So far we have successfully been able to implement the following:

  • Load the Common Language Runtime (CLR) into the AutoIt process (post 25 by Danyfirex)
  • Examples in the following posts by Danyfirex, junkew and ptrex
  • From the code in post 25 it was possible to create the _AppDomain object (post 56)
  • From there we've managed to translate two AutoHotkey examples. The first example in post 62 loads a XPTable.dll assembly, creates a listview with checkboxes and buttons, and displays the listview in an AutoIt GUI. The second example in post 65 compiles and executes C#-code on the fly.
  • Danyfirex has collected all code and examples in the zip file in post 68
  • At this point we can load .Net assemblies (DLL-files), compile and execute C#/VB-code on the fly and create simple Windows.Forms GUIs.
  • Example code by ptrex and junkew in posts 80 - 98 shows some issues with .Net Windows.Forms code related to collections of controls.
  • Examples in posts 102 - 110 demonstrates how to handle events. Two event handlers are translated from AutoHotkey code in post 102. trancexx has shown an event handler with reduced code in post 110. (If $aObjects[] is replaced with a dictionary object, the array loop in bottom of the event handler function can be avoided and replaced by a dictionary lookup.)
  • With these event handlers we can handle events from Windows.Forms controls, .Net assemblies and C#/VB-code

We've been able to implement all this without too overwhelming effort. Personally I'm happy with what we've achieved and that means I'll scale down my own efforts significantly.

It should be possible to create up to several interesting UDFs based on the code in previous posts. Of course the code has to be completed and cleaned up but that should not be too hard. I'll not go into this. Maybe I'll create a few UDFs for my own purposes. But it'll not be soon.

It has been fun. Regards Lars.

Posted

LarsJ, My thanks as well, you made it at least possible to access the .Net framework !!!

Junkew,

if I look at the AHK examples there is an other issue not solved yet and that is the parameters, that are missing in the _CRL_CreateObject.

Like this :

drawing := CLR_LoadLibrary("System.Drawing")
headerFont := CLR_CreateObject(drawing, "System.Drawing.Font", "Tahoma", 20)
rowFont := CLR_CreateObject(drawing, "System.Drawing.Font", "Tahoma", 12)

This is crucial in many in many of this objects; that need to be created, not only the GUI Controls...

Anyhow let's see how this evolves... the links you posted is definitely interesting reading material too.

Posted

@ptrex

Did we already have an example on that parameter thing? should be "straight forward" with a safe array as args parameter

public:
[SecurityCriticalAttribute]
[ObsoleteAttribute("Methods which use evidence to sandbox are obsolete and will be removed in a future release of the .NET Framework. Please use an overload of CreateInstance which does not take an Evidence parameter. See http://go.microsoft.com/fwlink/?LinkID=155570 for more information.")]
static ObjectHandle^ CreateInstance(
    AppDomain^ domain,
    String^ assemblyName,
    String^ typeName,
    bool ignoreCase,
    BindingFlags bindingAttr,
    Binder^ binder,
    array<Object^>^ args,
    CultureInfo^ culture,
    array<Object^>^ activationAttributes,
    Evidence^ securityAttributes
)

some HP UFT dotnetfactory examples (so no reason to believe it will not work in AutoIt)

Set dialogForm = DotNetFactory.CreateInstance("System.Windows.Forms.Form", "System.Windows.Forms")
            Set dialogStartPosition = DotNetFactory.CreateInstance("System.Windows.Forms.FormStartPosition", "System.Windows.Forms")
            Set dialogBorderStyle = DotNetFactory.CreateInstance("System.Windows.Forms.FormBorderStyle", "System.Windows.Forms")
            Set dialogLabel = DotNetFactory.CreateInstance("System.Windows.Forms.Label", "System.Windows.Forms")
            Set dialogFont = DotNetFactory.CreateInstance("System.Drawing.Font", "System.Drawing", "Microsoft Sans Serif", 11)
            Set dialogContentAlign = DotNetFactory.CreateInstance("System.Drawing.ContentAlignment", "System.Drawing")

 

Posted

Hi Junkew / Larsj,

This is the CreateObject function, it only accepts 1 parameter ?

Func _CRL_CreateObject(ByRef $oAssembly, $sTypeName = "", $sParameter3 = "")
    #forceref $oAssembly, $sTypeName, $sParameter3

    If @NumParams = 2 Then
        Local $oObject = 0
        $oAssembly.CreateInstance_2($sTypeName, True, $oObject)
        Return $oObject
    EndIf

    If @NumParams = 3 Then
        ; static Array_Empty := ComObjArray(0xC,0), null := ComObject(13,0)
        Local $pSAEmpty, $tSAB = DllStructCreate($tagSAFEARRAYBOUND)
        DllStructSetData($tSAB, "cElements", 0)
        DllStructSetData($tSAB, "lLbound", 0)
        $pSAEmpty = SafeArrayCreate($VT_VARIANT, 0, $tSAB)
        Local $oObject = 0
        $oAssembly.CreateInstance_3($sTypeName, True, 0, 0, CreateSafeArray($sParameter3), 0, $pSAEmpty, $oObject)
        Return $oObject
    EndIf

EndFunc   ;==>_CRL_CreateObject

If it exceeds 3 parameters or more, the SafeArray should probably be loaded with the additional parameters  data.

By looping through the parameters, this can be passed on to the $Assembly.CreateInstance_3

This is the AHK function ... https://pastebin.com/G1BrL6p6

CLR_CreateObject(Assembly, TypeName, Args*)
{
    if !(argCount := Args.MaxIndex())
        return Assembly.CreateInstance_2(TypeName, true)
   
    vargs := ComObjArray(0xC, argCount)
    Loop % argCount
        vargs[A_Index-1] := Args[A_Index]
   
    static Array_Empty := ComObjArray(0xC,0), nulln := ComObject(13,0)
   
    return Assembly.CreateInstance_3(TypeName, true, 0, nulln, vargs, nulln, Array_Empty)
}

Example is like this :

;~  #AutoIt3Wrapper_UseX64=y

#include "CLR.Au3"

_Example()

Func _Example()
;~  Local $ofrmAssembly = _CLR_LoadLibrary("System.Windows.Forms")
    Local $oAssembly = _CLR_LoadLibrary("System.Windows.Forms")

    ConsoleWrite("!$oAssembly: " & IsObj($oAssembly) & @CRLF)
    Local $oForm = _CRL_CreateObject($oAssembly, "System.Windows.Forms.Form")
    ConsoleWrite("!$oForm: " & IsObj($oForm) & @CRLF)

$oform.name="Form1"

#Region dynamic controls
 $oCol=$oForm.controls
 ConsoleWrite("!$oCol: " & IsObj($oCol) & @CRLF)

 Local $oBtn= _CRL_CreateObject($oAssembly, "System.Windows.Forms.Button")
 ConsoleWrite("!$oBtn " & IsObj($oBtn) & @CRLF)
 $oBtn.Name="Button1"
 $oBtn.Text="Button1"
 $oBtn.Top = 20
 $oBtn.Width=80
 $oBtn.Left = 150

 Local $oText= _CRL_CreateObject($oAssembly, "System.Windows.Forms.TextBox")
 ConsoleWrite("!$oText: " & IsObj($oText) & @CRLF)
  $oText.Top = 20
  $oText.Left = 20
  $oText.Text = "Font Test"

#cs AHK Syntax using Paramters is missing in AU3 - https://pastebin.com/G1BrL6p6
  drawing := CLR_LoadLibrary("System.Drawing")
  headerFont := CLR_CreateObject(drawing, "System.Drawing.Font", "Tahoma", 20)
  rowFont := CLR_CreateObject(drawing, "System.Drawing.Font", "Tahoma", 12)
 #ce
 Local $drawing = _CLR_LoadLibrary("System.Drawing")
 ConsoleWrite("!$drawing: " & IsObj($drawing) & @CRLF)

 $oFontText = _CRL_CreateObject($drawing, "System.Drawing.Font","Courier New", 10, 20)
 ConsoleWrite("!$oFontText: " & IsObj($oFontText) & @CRLF)

;~  Local $oSize = _CRL_CreateObject($drawing, "System.Drawing.Size", 10, 20)
;~  ConsoleWrite("!$oSize: " & IsObj($oSize) & @CRLF)
;~  $oBtn.Size = $oSize

;~  $oForm.opacity=0.75
 $oForm.Text = "Form From Net - AutoIt Rocks"
 $oForm.Width = 800
 $oForm.Height = 400

 ;~  consolewrite("ocol.count " & $ocol.count & @crlf)
 ;~  $oForm.controls.Add($oText) ; this does does not work ?!

 $oBtn.parent = $oForm
 $oText.parent = $oForm

#EndRegion

;~  $oForm.Show()
    $oForm.ShowDialog()

;~     Sleep(1000)

    ConsoleWrite("$oForm Handle: " & $oForm.Handle & @CRLF)

    $oForm.Dispose()

EndFunc   ;==>_Example

Most of the Type constructors have 2 or more parameters... so without handling this properly the use is very limited.

 

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
×
×
  • Create New...