如何用Python脚本明确引用Delphi 2007应用控件实现自动化
遗留Delphi应用自动化测试:控件精准定位问题及解决方法
问题复现代码
Project1.dpr
program Project1; uses Forms, Unit1 in 'Unit1.pas' {Form1}; {$R *.res} begin Application.Initialize; Application.MainFormOnTaskbar := True; Application.CreateForm(TForm1, Form1); Application.Run; end.
Unit1.pas
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls; type TForm1 = class(TForm) Memo1: TMemo; Button1: TButton; CheckBox1: TCheckBox; Timer1: TTimer; Button2: TButton; Button3: TButton; Button4: TButton; Button5: TButton; Label2: TLabel; Panel1: TPanel; procedure ControlClick(Sender: TObject); procedure Timer1Timer(Sender: TObject); procedure FormCreate(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.ControlClick(Sender: TObject); begin Memo1.Lines.Add(Format('Control "%s" clicked', [TControl(Sender).Name])); end; procedure TForm1.Timer1Timer(Sender: TObject); begin if Assigned(Screen.ActiveControl) then Panel1.Caption := Screen.ActiveControl.Name else Panel1.Caption := 'None.'; end; procedure TForm1.FormCreate(Sender: TObject); var i: Integer; begin for i := 0 to ComponentCount - 1 do if Components[i] is TWinControl then (Components[i] as TWinControl).AccessibleName := (Components[i] as TWinControl).Name; end; end.
Unit1.dfm
object Form1: TForm1 Left = 0 Top = 0 Caption = 'Form1' ClientHeight = 212 ClientWidth = 407 Color = clBtnFace Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -11 Font.Name = 'Tahoma' Font.Style = [] OldCreateOrder = False OnCreate = FormCreate PixelsPerInch = 96 TextHeight = 13 object Label2: TLabel Left = 73 Top = 193 Width = 32 Height = 13 Caption = 'Focus:' end object Memo1: TMemo Left = 24 Top = 8 Width = 201 Height = 177 TabOrder = 0 end object Button1: TButton Left = 304 Top = 11 Width = 75 Height = 25 Caption = '1' TabOrder = 1 OnClick = ControlClick end object CheckBox1: TCheckBox Left = 296 Top = 171 Width = 97 Height = 17 Caption = 'CheckBox1' TabOrder = 6 OnClick = ControlClick end object Button2: TButton Left = 304 Top = 42 Width = 75 Height = 25 Caption = '2' TabOrder = 2 OnClick = ControlClick end object Button3: TButton Left = 304 Top = 73 Width = 75 Height = 25 Caption = '3' TabOrder = 3 OnClick = ControlClick end object Button4: TButton Left = 304 Top = 104 Width = 75 Height = 25 Caption = '4' TabOrder = 4 OnClick = ControlClick end object Button5: TButton Left = 304 Top = 135 Width = 75 Height = 25 Caption = '5' TabOrder = 5 OnClick = ControlClick end object Panel1: TPanel Left = 116 Top = 191 Width = 109 Height = 17 TabOrder = 7 end object Timer1: TTimer Interval = 300 OnTimer = Timer1Timer Left = 240 Top = 152 end end
原问题代码:Automate.py
import pywinauto from pywinauto import application def Click(AForm, AControlName): FControl = AForm[AControlName] FControl.click() return () FormTitle = "Form1" try: app = application.Application().connect(title=FormTitle) form1 = app[FormTitle] Click(form1, "Button1") Click(form1, "Button2") Click(form1, "Button3") Click(form1, "Button4") Click(form1, "Button5") Click(form1, "Checkbox1") exit(0) except pywinauto.application.ProcessNotFoundError: print(f"No running instance of {FormTitle} found.") exit(1) except Exception as e: print(f"An error occurred: {str(e)}") exit(1)
问题现象
运行上述Python代码时,指定点击Button1实际触发Button5,Button2触发Button4,控件名称匹配完全反向;但Checkbox1可正常响应。原因是Delphi Win32控件在Windows自动化接口中的枚举顺序与DFM文件声明顺序相反,pywinauto默认按枚举顺序匹配名称,导致错位。
可靠解决方案
方案1:通过控件Caption定位
利用控件显示文本(Caption)定位,不受枚举顺序影响,是最直接的可靠方案。修改后的Automate.py代码如下:
import pywinauto from pywinauto import application def ClickByCaption(AForm, ACaption, ControlType="Button"): FControl = AForm.child_window(title=ACaption, control_type=ControlType) FControl.click() FormTitle = "Form1" try: app = application.Application().connect(title=FormTitle) form1 = app[FormTitle] ClickByCaption(form1, "1") ClickByCaption(form1, "2") ClickByCaption(form1, "3") ClickByCaption(form1, "4") ClickByCaption(form1, "5") ClickByCaption(form1, "CheckBox1", "CheckBox") exit(0) except pywinauto.application.ProcessNotFoundError: print(f"No running instance of {FormTitle} found.") exit(1) except Exception as e: print(f"An error occurred: {str(e)}") exit(1)
方案2:启用Delphi控件的MSAA支持(需修改应用代码)
若可修改遗留Delphi应用代码,开启MSAA(Microsoft Active Accessibility)支持后,pywinauto可精准识别控件Name属性。
- 在Unit1.pas中添加FormCreate事件(如上述代码所示),遍历控件设置
AccessibleName为控件Name; - 重新编译应用后,原问题中的Automate.py代码即可正常工作,控件名称匹配不再错位。
方案3:通过类名+索引定位(需确认顺序)
若无法修改应用代码且Caption不可用,可结合控件类名和索引定位。注意:Delphi控件枚举顺序反向,Button1对应索引4,Button5对应索引0:
def ClickByClassIndex(AForm, ClassName, Index): FControl = AForm.child_window(class_name=ClassName, found_index=Index) FControl.click() # 调用示例 ClickByClassIndex(form1, "TButton", 4) # 点击Button1 ClickByClassIndex(form1, "TButton", 0) # 点击Button5
此方案可靠性较低,控件布局变更需重新调整索引。
内容的提问来源于stack exchange,提问作者rossmcm
相关产品推荐
相关产品推荐

