xsharp.eu • _CAST
Page 1 of 1

_CAST

Posted: Thu Sep 23, 2021 3:34 am
by JKCanada604
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

_CAST

Posted: Thu Sep 23, 2021 4:59 am
by robert
John,

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

Robert

_CAST

Posted: Thu Sep 23, 2021 5:12 am
by JKCanada604
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!

_CAST

Posted: Thu Sep 23, 2021 7:04 am
by robert
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

_CAST

Posted: Thu Sep 23, 2021 7:13 am
by JKCanada604
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!

_CAST

Posted: Thu Sep 23, 2021 8:12 am
by leon-ts
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

_CAST

Posted: Thu Sep 23, 2021 10:06 am
by Chris
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: https://docs.microsoft.com/en-us/dotnet ... ew=net-5.0

Code: Select all

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

_CAST

Posted: Thu Sep 23, 2021 3:26 pm
by JKCanada604
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

_CAST

Posted: Thu Sep 23, 2021 3:44 pm
by Karl-Heinz
Hi Chris,

nice sample !

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

https://www.xsharp.eu/runtimehelp/html/ ... ildren.htm

i compared both methods and the results are the same.

Code: Select all

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

_CAST

Posted: Thu Sep 23, 2021 4:07 pm
by Chris
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.

.