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