Welcome, Guest
Username: Password: Remember me
Visual Objects

Please use this forum to post questions about Visual Objects and Vulcan.NET
  • Page:
  • 1

TOPIC:

_CAST 23 Sep 2021 05:34 #19670

  • JKCanada604
  • JKCanada604's Avatar
  • Topic Author


  • Posts: 48
  • Good day to you all once again!

    Here is another wowzer!

    FUNC __GetCtrl(hWnd AS PTR, lParam AS DWORD ) AS LOGIC CALLBACK

    LOCAL oObj AS USUAL

    oObj := GetObjectByHandle( hWnd )

    IF oObj != Null_Object
    AAdd( ARRAY(_CAST,lParam), oObj ) // <- this one throws an error with the _CAST
    ENDIF

    RETURN TRUE

    We are trying to CAST the pointer to an array and then add it...

    Yikes!

    As always, thank you, keep well and,

    Cheers, JK

    Please Log in or Create an account to join the conversation.

    _CAST 23 Sep 2021 06:59 #19673

    • robert
    • robert's Avatar


  • Posts: 3610
  • John,

    Casting a long to an array ?
    Can you elaborate why you do this ?

    Robert
    XSharp Development Team
    The Netherlands

    Please Log in or Create an account to join the conversation.

    _CAST 23 Sep 2021 07:12 #19674

    • JKCanada604
    • JKCanada604's Avatar
    • Topic Author


  • Posts: 48
  • Hi Robert,

    This is a callback function that Gets the Control from the lParam and then adds it into the list of controls so that the controls are pollable.

    We create a dialog at run time that we really do not know ahead of time what it will be onto which we place controls - all dynamically ..

    Then we need to get access to these controls so that we can exercise one of it's properties namely :Value..

    Does this make any sense?

    Again, always, thank you!

    Please Log in or Create an account to join the conversation.

    _CAST 23 Sep 2021 09:04 #19675

    • robert
    • robert's Avatar


  • Posts: 3610
  • John,
    I think I understand what you are doing, but I would need to see some code to give you an advise on how to solve this.
    Casting arrays to longs and longs to arrays is normally not allowed in a "managed" environment.
    But there are other ways to do this.
    Something like this is done in the GUI classes too (in the code that associates an object to a handle).

    Robert
    XSharp Development Team
    The Netherlands

    Please Log in or Create an account to join the conversation.

    _CAST 23 Sep 2021 09:13 #19676

    • JKCanada604
    • JKCanada604's Avatar
    • Topic Author


  • Posts: 48
  • Hi Robert,

    Here are the guts - it is a call back..

    METHOD GetAllControls() CLASS DataWindow

    LOCAL ___aControls := {} AS ARRAY
    LOCAL hWnd AS PTR

    hWnd := oSurface:Handle()
    EnumChildWindows(hwnd , @__GetCtrl(), LONGINT(@___aControls) )

    RETURN ___aControls


    FUNC __GetCtrl(hWnd AS PTR, lParam AS DWORD ) AS LOGIC CALLBACK

    LOCAL oObj as USUAL
    Local a as USUAL

    oObj := GetObjectByHandle( hWnd )

    IF oObj != null_object
    AAdd( ARRAY(_CAST,lParam), oObj )
    ENDIF

    RETURN TRUE

    Does this make any sense?

    Again, always, thank you!

    Please Log in or Create an account to join the conversation.

    _CAST 23 Sep 2021 10:12 #19677

    • leon-ts
    • leon-ts's Avatar


  • Posts: 282
  • Hi John,

    Even in VO code, you have a hidden problem. In the GetAllControls method, you must register the pointer to the array with the RegisterKit function so that it does not lose contact with the GC. This is important because in the __GetCtrl function, the array is expanded, which can lead to garbage collection and move the pointer to the array, and the pointer in lParam will remain unchanged.

    In X#, you can use GCAlloc.

    Best regards,
    Leonid
    Best regards,
    Leonid

    Please Log in or Create an account to join the conversation.

    _CAST 23 Sep 2021 12:06 #19678

    • Chris
    • Chris's Avatar


  • Posts: 3990
  • Hi John,

    It's two things that need to be adjusted in this code:
    1. In .Net you can't take the address of a function or method, need to use a DELEGATE to that function instead
    2. As Robert and Leonid mentioned, you need to use a GCHandle as a container for your array, in order to pass it as an object to the callback

    Here's some online documentation about GCHandle: docs.microsoft.com/en-us/dotnet/api/syst...chandle?view=net-5.0

    USING System.Runtime.InteropServices // put this at the top of the prg file
    
    ...
    
    // The delegate needs to have the same parameters and return type as the function/method to be associated with
    DELEGATE EnumChildWindows_Delegate(hWnd AS PTR, lParam AS DWORD ) AS LOGIC 
    
    ...
    
    METHOD GetAllControls()
    	LOCAL ___aControls := {} AS ARRAY
    
    	LOCAL oArrayHandle AS GCHandle
    	oArrayHandle := GCHandle.Alloc( ___aControls ) // create a handle to the managed object (array)
    		
    	LOCAL oEnumDelegate AS EnumChildWindows_Delegate
    	oEnumDelegate := __GetCtrl // instantiate the delegate (managed function pointer) to our callback function
    
    	LOCAL hWnd AS PTR
    	hWnd := oSurface:Handle()
    
    	// pass to EnumChildWindows() a managed pointer (via the delegate) to the 
    	// callback function and the handle of the array as a pointer:
    	EnumChildWindows(hWnd , Marshal.GetFunctionPointerForDelegate( oEnumDelegate ), GCHandle.ToIntPtr( oArrayHandle ) )
    		
    	// we don't need it anymore, release memory
    	oArrayHandle:Free()
    	// make sure the delegate is not collected by the GC, while it is still 
    	// being used by the win32 code of EnumChildWindows(), before we reach here
    	GC.KeepAlive( oEnumDelegate )
    RETURN ___aControls
    
    ...
    
    FUNC __GetCtrl(hWnd AS PTR, lParam AS DWORD ) AS LOGIC
    	LOCAL oArrayHandle AS GCHandle
    	oArrayHandle := GCHandle.FromIntPtr( (IntPtr) lParam ) // retrieve back the handle to the array
    
    	LOCAL aControls AS ARRAY
    	aControls := (ARRAY) oArrayHandle:Target // retrieve the original array object
    	
    	LOCAL oObj AS USUAL
    	oObj := GetObjectByHandle( hWnd )
    	IF oObj != null_object
    		AAdd( aControls, oObj )
    	ENDIF
    RETURN TRUE
    XSharp Development Team
    chris(at)xsharp.eu

    Please Log in or Create an account to join the conversation.

    Last edit: by Chris.

    _CAST 23 Sep 2021 17:26 #19680

    • JKCanada604
    • JKCanada604's Avatar
    • Topic Author


  • Posts: 48
  • Hi Chris et al,

    Fantastic and very much appreciated.

    I would have never figured this one out!

    I will take a stab at it see where this takes me.

    Again, thank you!

    Keep well and,

    Cheers, JK

    Please Log in or Create an account to join the conversation.

    _CAST 23 Sep 2021 17:44 #19681

    • Karl-Heinz
    • Karl-Heinz's Avatar


  • Posts: 774
  • Hi Chris,

    nice sample !

    But why not just use the existing Window method GetAllChildren() ?

    www.xsharp.eu/runtimehelp/html/M_VO_Data...w_GetAllChildren.htm

    i compared both methods and the results are the same.
    METHOD ShowControls()
    LOCAL a AS ARRAY
    LOCAL i AS DWORD  	
    	
    	a := SELF:GetAllControls()  // John's method	
    	
    	? ALen ( a )
    	
    	FOR i := 1 TO ALen ( a )
    		
    		? a[i]
    		
    	NEXT
    	
    	?
    	? 
    	
    	a := SELF:GetAllChildren() // existing VO method	
    	
    	? ALen ( a )
    	
    	FOR i := 1 TO ALen ( a )
    		
    		? a[i]
    		
    	NEXT
    
    	RETURN SELF

    regards
    Karl-Heinz

    Please Log in or Create an account to join the conversation.

    _CAST 23 Sep 2021 18:07 #19683

    • Chris
    • Chris's Avatar


  • Posts: 3990
  • Hi Karl-Heinz,

    You are right of course, but I wanted to also explain hands on how this specific and similar chunks of code can be ported to X#. Hopefully this can also serve as future reference for other people and similar cases as well.

    .
    XSharp Development Team
    chris(at)xsharp.eu

    Please Log in or Create an account to join the conversation.

    Last edit: by Chris.
    • Page:
    • 1