We are working on accessibility compliance (WCAG/Section 508) for a Clarion 11.0 app (Clarion Template chain) we sell to government clients. Our LIST and DROP controls show up in Inspect.exe as a blank “pane” (UIA_PaneControlTypeId, no name) — the native class is ClaDrop_09560000H, which isn’t a standard Windows control class, so screen readers like NVDA read nothing when they hit it.
Has anyone run into this and found a fix? Specifically:
Does a newer Clarion version/patch add native UIA or MSAA support for these controls?
Has anyone built a custom accessibility wrapper (WM_GETOBJECT / IAccessible) for ClaList/ClaDrop windows they could point me to?
We have ~500 of these controls across the app, so a runtime-level or template-level fix would save us a lot of pain. Any pointers appreciated.
Inspect.exe is showing a dropdown as two window controls.
Other window/control inspector type have pickers so the drop down part is never seen.
I also found the .net WPF framework iirc (one of the .net frameworks at least) also hid all controls and their text using spy/inspector tools so it wasnt possible to extract data from apps.
Best example of this behaviour is using a spy/inspector tool on the new windows settings (gear icon) (old control panel) for win10+.
ControlSpy2 shows you how the standard win32 api controls work with different styles.
The Clarion dropdown list isnt a std win32 api ComboBox or ComboBoxEx control so you’ll need to do a bit more digging to get the data out of the control.
Have you tried a clarion combobox in Inspect.exe to see if it works the same way as a clarion drop down list ie two parts no text to extract or like a std win32 api combobox control?
So firstly, all versions of clarion have the same owner drawn controls.
I set Claude to work on this issue to judge how well it could work with a complex problem. I haven’t got Claude to write the template wrappers around this, but the following solution and code works in narrator for a LIST control, I presume it would be easy to convert to others.
Since Clarion 11.13505 there is an undocumented PROP to get a &QUEUE of the LIST FROM(Q) that was added for AnyScreen.
This can have a bad value so I protect it with code like this that checks for a String FROM or a VLB that return bad values that would GPF:
MAP
ListFromQProp PROCEDURE(LONG ListFEQ),LONG !New 11.13505 LIST FROM() &Queue as a NUMBER to &=()
...
ListFromQProp PROCEDURE(LONG ListFEQ) ! wrap code to get 7A23h &Queue to protect from GPF
FromQ LONG
FromP LONG
PROP:FromPtr EQUATE(7A0EH) !ABLLIST.INT &= IMappedListContents ? 0800xxxxH usually, 7C98h also an II ptr?
PROP:FromQRef EQUATE(7A23H) !Showed up in 13505 = &QUEUE unless no FROM(Q) then ?= Interface for WB see 0800xxxxh
CODE
IF ~ListFEQ{PROP:From} AND ListFEQ{PROP:VLBval}=0 THEN !From('Text') or VLB &A23h would return a pointer that GPFs
FromQ=ListFEQ{PROP:FromQRef} !The New 11.13505 Property of LIST for FROM(Q)
IF FromQ=08004FF0h THEN FromQ=0. !If No From(Q) RTL {7A23h}=08004FF0h always ????
IF FromQ THEN
FromP=ListFEQ{PROP:FromPtr}
IF ABS(FromQ-FromP)<7FFFh THEN FromQ=0. !HACK - Interface pointer? not From(Q), can't use this.
END ! This would only happen with No FROM() at all.
IF FromQ>0 AND FromQ<=4096 THEN FromQ=0. !Null Pointer memory region
END
RETURN FromQ
Another way that would work in all versions is to use a Template to define a ?List{'Prop_FromQ'} user defined property with the Queue address to QRef &= (QAddress). That calls the below procedure for all LIST’s in the APP.
CBListPropFromQ PROCEDURE(LONG FEQ,*QUEUE FrmQ,<STRING NameQ>)
Ref GROUP,AUTO
Q &QUEUE
L LONG,OVER(Q) !FYI probably better to use INSTANCE(Q,Thread)
END
CODE
Ref.Q &=FrmQ
FEQ{'FromQ'}=Ref.L
FEQ{'FromWho'}=CHOOSE(~OMITTED(NameQ),NameQ,'Queue' & Ref.L)
RETURN
Clarion Folders is an example that uses this property:
Mark, thanks this is great. However, I was not able to incorporate it into a multi-dll APP. I was trying to just put it into one of the .app DLLs. Maybe some of this stuff needs to be put into the .exe app and/or the data DLL? I tried moving the call to OleInitialize(0) to various spots. The program crashes without any warning message when OleInitialize(0) is called.