Our VCL Delphi application has a number of grids that we need to start to interact with via UIAutomation. There are a number of issues, not least that the TStringGrid doesn't implement any of the IUIAutomation patterns (IGridProvider or ITableProvider, or for that matter even IValueProvider).
I am trying to find out what I need to added to a TStringGrid to allow it to implement the providers (which in the System.Windows.Automation.Provider namespace in .NET).
Here are my steps ...
(The actual files are too large to post all of them, so this is a distillation of the major points).
ALSO - This still has major issues, probably of my own making, but it is enough for me to make progress.
function UiaHostProviderFromHwnd(hwnd: HWND; provider: IRawElementProviderSimple): LRESULT; stdcall; external 'UIAutomationCore.dll' name 'UiaHostProviderFromHwnd';
function UiaReturnRawElementProvider(hwnd: HWND; wParam: WPARAM; lParam: LPARAM; element : IRawElementProviderSimple) : LRESULT; stdcall; external 'UIAutomationCore.dll' name 'UiaReturnRawElementProvider';
// IRawElementProviderSimple function Get_ProviderOptions(out pRetVal: ProviderOptions): HResult; stdcall; function GetPatternProvider(patternId: SYSINT; out pRetVal: IUnknown): HResult; stdcall; function GetPropertyValue(propertyId: SYSINT; out pRetVal: OleVariant): HResult; stdcall; function Get_HostRawElementProvider(out pRetVal: IRawElementProviderSimple): HResult; stdcall; // ISelectionProvider function GetSelection(out pRetVal: PSafeArray): HResult; stdcall; function Get_CanSelectMultiple(out pRetVal: Integer): HResult; stdcall; function Get_IsSelectionRequired(out pRetVal: Integer): HResult; stdcall;
These are implemented as follows ..
function TAutomationStringGrid.Get_ProviderOptions( out pRetVal: ProviderOptions): HResult; begin pRetVal:= ProviderOptions_ClientSideProvider; Result := S_OK; end; function TAutomationStringGrid.GetPatternProvider(patternId: SYSINT; out pRetVal: IInterface): HResult; begin pRetval := nil; if (patternID = UIA_SelectionPatternId) then begin result := QueryInterface(ISelectionProvider, pRetVal); end else result := S_OK; end; function TAutomationStringGrid.GetPropertyValue(propertyId: SYSINT; out pRetVal: OleVariant): HResult; begin if(propertyId = UIA_ControlTypePropertyId) then begin TVarData(pRetVal).VType := varWord; TVarData(pRetVal).VWord := UIA_DataGridControlTypeId; end; result := S_OK; end; function TAutomationStringGrid.Get_HostRawElementProvider( out pRetVal: IRawElementProviderSimple): HResult; begin result := UiaHostProviderFromHwnd (self.Handle, pRetVal); end; function TAutomationStringGrid.GetSelection(out pRetVal: PSafeArray): HResult; begin end; function TAutomationStringGrid.Get_CanSelectMultiple( out pRetVal: Integer): HResult; begin end; function TAutomationStringGrid.Get_IsSelectionRequired( out pRetVal: Integer): HResult; begin end;
In order to actually get the control, the WM_GETOBJECT message needs to be handled ...
procedure WMGetObject(var Message: TMessage); message WM_GETOBJECT;
This is implemented as follows ..
procedure TAutomationStringGrid.WMGetObject(var Message: TMessage); begin if (Message.Msg = WM_GETOBJECT) then begin QueryInterface(IID_IRawElementProviderSimple, FRawElementProviderSimple); message.Result := UiaReturnRawElementProvider(self.Handle, Message.WParam, Message.LParam, FRawElementProviderSimple); end else Message.Result := DefWindowProc(self.Handle, Message.Msg, Message.WParam, Message.LParam); end;
Although I cannot provide the specific steps required to implement the automation capabilities you require on TStringGrid, I can say that based on the comments you have almost everything you need.
The article you found describing the basic implementation of UI Automation support for Win32 Unmanaged code is a good place to start.
The questions over what is and is not exposed through the IDL in UIAutomationCore.DLL are then addressed by the fact that the DLL in question is itself intended to be consumed by unmanaged code. It contains no managed code itself. At least not that is involved in an unmanaged use case.
What is does contain is a COM interface described by IDL, but also some functions simply exported by the DLL. As far as I know, IDL does not describe the exports table of a DLL. Even if it is capable of doing so, in the case of this DLL it does not (at least not in all cases).
For example, the UiaHostProviderFromHwnd() function that you have mentioned is a simple DLL export. Some of the additional functions exported in this way are described in this MSDN blog post describing creating a .net interop interface for this library. In that article they are called "flat API methods".
Using PE Explorer I can see 81 such functions exported by the UIAutomationCore.dll library.
Unfortunately a DLL exports table does not describe the parameters or return types of any exported function, only the names. So, in addition to the type library (produced from the IDL) you will also need to locate and convert the UIAutomationCore.h header file for use with Delphi (i.e. Pascal).
You should then have everything need to implement the UI Automation capabilities for any VCL control you desire.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With