You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 23:27:02