将UIAutomation提供程序添加到Delphi控件(特别是网格)

我们的VCL Delphi应用程序有许多网格,我们需要通过UIAutomation开始交互。 有很多问题,不是最少的,TStringGrid没有实现任何IUIAutomation模式(IGridProvider或ITableProvider,或者就此而言甚至是IValueProvider)。

我试图找出我需要添加到TStringGrid以允许它实现提供程序(在.NET中的System.Windows.Automation.Provider命名空间中)。


尽管我无法提供实现TStringGrid所需的自动化功能所需的具体步骤,但我可以根据您的评论来说,您几乎拥有了所需的一切。

您发现的文章描述了Win32非托管代码的UI Automation支持的基本实现,这是一个很好的开始。

然后,通过UIAutomationCore.DLL中的IDL暴露的问题以及未通过IDIA暴露的问题由以下事实来解决所讨论的DLL本身意图被非托管代码使用。 它本身不包含托管代码。 至少不会涉及非托管用例。

包含的内容是由IDL描述的COM接口,还有一些由DLL简单导出的函数。 据我所知,IDL并没有描述DLL的导出表。 即使它能够这样做,在这个DLL的情况下它也不会(至少在所有情况下都是如此)。

例如,您提到的UiaHostProviderFromHwnd()函数是一个简单的DLL导出。 以这种方式导出的一些附加功能在本MSDN博客文章中描述,描述为该库创建.net interop接口。 在那篇文章中,他们被称为“平面API方法”。

使用PE资源管理器我可以看到由UIAutomationCore.dll库导出的81个这样的函数。

不幸的是,DLL导出表不会描述任何导出函数的参数或返回类型,只有名称。 因此,除了类型库(由IDL生成)之外,还需要查找并转换UIAutomationCore.h头文件以便与Delphi(即Pascal)一起使用。

然后,您应该拥有所有需要为您所需的任何VCL控件实现UI Automation功能的应用程序。


这是我的步骤...

(实际文件太大而无法发布所有文件,所以这是对主要观点的一种提升)。

此外 - 这仍然有重大问题,可能是我自己制造的,但对我来说取得进展已足够。

  • 获取UIAutomationCore.idl(我的是Visual Studio安装的一部分)。
  • 运行midl.exe来创建类型库。
  • 从命令行运行tlibimp.exe(因为Delphi似乎不喜欢步骤3中创建的.tlb),并创建UIAutomationCore_TLB.pas文件。 这最终是一个相当大的文件,UIAutomationCore的所有COM部分都在pascal中定义。
  • 原始DLL中的方法不是COM,而且这些方法也需要定义。 我将这些文件添加到第3步生成的文件中 - 尽管在重新生成该文件的情况下可能应该在别处定义它们。
  • 
        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接口以及任何其他提供者(在示例中我已经使用了ISelectionProvide)。
  •     // 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;
    

    这些实施如下。

    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;
    

    为了实际获得控件,需要处理WM_GETOBJECT消息...

        procedure WMGetObject(var Message: TMessage); message WM_GETOBJECT;
    

    这是实施如下..

    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;
    
    链接地址: http://www.djcxy.com/p/84561.html

    上一篇: Adding UIAutomation Providers to Delphi controls (specifically grids)

    下一篇: How to fetch the filename from CSV and insert it into one of columns of a table