WinAppDriver扩展支持Win32 MFC自定义UI元素可行性问询
扩展WinAppDriver支持自定义MFC GUI元素的方案
核心思路:基于Windows自动化API(UIA)扩展识别能力
WinAppDriver底层依赖UIA框架,对于MFC自定义控件,核心是让你的自定义网格类被UIA识别并暴露交互能力,有两种可行路径:
路径1:修改MFC源码,添加UIA提供者支持
如果能拿到MFC程序的源码,这是最规范的方案:
- 为自定义网格类实现UIA的对应模式接口,比如
IGridProvider(暴露行列数)、ITableItemProvider(定位单元格)、IValueProvider(读写单元格内容) - 继承
CUIAutomationProvider基类,实现接口里的方法,把网格的内部数据(行列数、单元格值)通过UIA暴露出来 - 在网格控件的初始化方法(比如
OnCreate)里,调用UiaRegisterProvider将控件和你的UIA提供者绑定
示例代码片段(MFC环境):
// 自定义网格的UIA提供者类 class CMyGridUIAProvider : public CUIAutomationProvider, public IGridProvider, public IValueProvider { private: CMyGrid* m_pGrid; public: CMyGridUIAProvider(CMyGrid* pGrid) : m_pGrid(pGrid) {} // 实现IGridProvider接口,返回行数 STDMETHODIMP get_RowCount(int* pRetVal) override { *pRetVal = m_pGrid->GetRowCount(); return S_OK; } // 实现IGridProvider接口,返回列数 STDMETHODIMP get_ColumnCount(int* pRetVal) override { *pRetVal = m_pGrid->GetColumnCount(); return S_OK; } // 实现IValueProvider接口,读取单元格值 STDMETHODIMP get_Value(BSTR* pRetVal) override { // 这里需要根据当前单元格位置获取值,可结合ITableItemProvider的定位逻辑 CString cellValue = m_pGrid->GetCellValue(m_currentRow, m_currentCol); *pRetVal = cellValue.AllocSysString(); return S_OK; } // 其他接口方法按需实现... }; // 在网格控件中注册UIA提供者 BOOL CMyGrid::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CStatic::OnCreate(lpCreateStruct) == -1) return FALSE; CMyGridUIAProvider* pProvider = new CMyGridUIAProvider(this); UiaRegisterProvider(reinterpret_cast<IRawElementProviderSimple*>(pProvider)); return TRUE; }
完成后,WinAppDriver就能识别网格的UIA模式,你可以通过Appium客户端API直接调用:
- 获取行列数:
driver.findElement(By.xpath("//[contains(@ClassName,'你的网格类名')]")).getAttribute("RowCount") - 读写单元格:定位到具体单元格元素后,调用
getValue()或setValue()方法
路径2:不修改源码,通过Win32 API直接交互
如果无法修改MFC程序源码,可通过原生Win32 API绕开UIA直接操作控件:
- 用
FindWindowEx找到网格控件的句柄 - 发送程序内部约定的自定义消息(比如
WM_USER + XXX)来获取行列数、读写单元格(需要确认程序是否预留了这类消息接口) - 极端情况下,可通过读取控件内存的方式获取数据(风险较高,需了解控件内存结构)
示例Python代码(结合pywin32):
import win32api import win32gui import win32con # 获取主窗口句柄 main_handle = win32gui.FindWindow(None, "你的MFC程序窗口标题") # 获取网格控件句柄 grid_handle = win32gui.FindWindowEx(main_handle, 0, "你的自定义网格类名", None) # 发送自定义消息获取行数(假设程序预留了WM_GET_ROWCOUNT消息) row_count = win32api.SendMessage(grid_handle, win32con.WM_USER + 100, 0, 0) # 发送自定义消息读取指定单元格内容 cell_value = win32api.SendMessage(grid_handle, win32con.WM_USER + 101, (row, col), 0)
关键验证工具
用Windows SDK自带的Inspect.exe检查UIA提供者是否生效:打开Inspect.exe后定位到网格控件,查看是否能识别到Grid或GridItem模式,以及对应的属性值是否正确。
内容的提问来源于stack exchange,提问作者Sergey Alikin
相关产品推荐
相关产品推荐

