_CAST

Public support forum for peer to peer support with related to the Visual Objects and Vulcan.NET products
Post Reply
JKCanada604
Posts: 49
Joined: Wed Aug 11, 2021 11:03 am

_CAST

Post 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
User avatar
robert
Posts: 4518
Joined: Fri Aug 21, 2015 10:57 am
Location: Netherlands

_CAST

Post by robert »

John,

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

Robert
XSharp Development Team
The Netherlands
robert@xsharp.eu
JKCanada604
Posts: 49
Joined: Wed Aug 11, 2021 11:03 am

_CAST

Post 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!
User avatar
robert
Posts: 4518
Joined: Fri Aug 21, 2015 10:57 am
Location: Netherlands

_CAST

Post 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
XSharp Development Team
The Netherlands
robert@xsharp.eu
JKCanada604
Posts: 49
Joined: Wed Aug 11, 2021 11:03 am

_CAST

Post 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!
leon-ts
Posts: 435
Joined: Fri Feb 03, 2017 1:43 pm

_CAST

Post 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
Best regards,
Leonid
User avatar
Chris
Posts: 4898
Joined: Thu Oct 08, 2015 7:48 am
Location: Greece

_CAST

Post 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
Chris Pyrgas

XSharp Development Team
chris(at)xsharp.eu
JKCanada604
Posts: 49
Joined: Wed Aug 11, 2021 11:03 am

_CAST

Post 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
Karl-Heinz
Posts: 774
Joined: Wed May 17, 2017 8:50 am
Location: Germany

_CAST

Post 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
User avatar
Chris
Posts: 4898
Joined: Thu Oct 08, 2015 7:48 am
Location: Greece

_CAST

Post 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.

.
Chris Pyrgas

XSharp Development Team
chris(at)xsharp.eu
Post Reply