Jeff replied with a perfect example of using the undocumented RTL function Cla$FIELDNAME:
PROGRAM
MAP
MODULE('')
FEQtoFieldName (SIGNED pFeq), *CSTRING, RAW, NAME('Cla$FIELDNAME')
END
END
Window WINDOW('Caption'),AT(,,395,224),GRAY,FONT('Segoe UI',9)
BUTTON('&OK'),AT(291,201,41,14),USE(?OkButton),DEFAULT
BUTTON('&Cancel'),AT(340,201,42,14),USE(?CancelButton)
END
CODE
OPEN(Window)
MESSAGE('?OkButton FEQ='& ?OkButton & |
'|FEQ to Field Name='& FEQtoFieldName(?OkButton))
Above I enhanced the original MESSAGE(FEQtoFieldName(?OkButton))
I normally wrap calls to the Cla$FIELDNAME function to catch some problems. If there is no USE(), or the FEQ is invalid, then the FieldName returns blank so this returns __Feq_####__ to have an idea of what control it was. When FEQ=0 it returns 'Window' … I know of no way to get the Window Label at runtime.
MAP
ClaFeqName PROCEDURE(LONG Feq),STRING,PRIVATE
MODULE('RTL')
ClaFieldNameRTL PROCEDURE(LONG pFEQ),CSTRING,RAW,NAME('Cla$FIELDNAME'),DLL(dll_mode)
END
END
ClaFeqName PROCEDURE(LONG F)!,STRING
n CSTRING(128)
CODE
if ~F then return 'Window'.
n=ClaFieldNameRTL(F)
if n <= ' ' then
n = '__Feq_'& F &'__' !No USE(?xx) so return __Feq_#__
end
return n
One other detail is FIRSTFIELD() and LASTFIELD() are somewhat deprecated as they can return control numbers that do not exist due to gaps. The better way is to use PROP:NextField:
FldNo LONG
CODE
FldNo = 0
LOOP
FldNo = 0{PROP:NextField, FldNo} !instead of First / LastField()
IF FldNo = 0 THEN BREAK.
IF FldNo{prop:Type} = Create:Button
stop('FEQ='& FldNo &' , Use='& ClaFeqName(FldNo) )
END
END