如何在运行时嵌入到 EXE 中另一个 TForm 的 TPanel 中的 DLL 中的 TForm 上的控件中切换到 TForm 上的控件,并获得正确的 Tab 顺序?

如何解决如何在运行时嵌入到 EXE 中另一个 TForm 的 TPanel 中的 DLL 中的 TForm 上的控件中切换到 TForm 上的控件,并获得正确的 Tab 顺序?

给定一个包含表单和 2 个用于显示/隐藏表单的导出程序的动态链接库,以及一个包含 2 个按钮和一个面板的表单的 VCL 可执行文件,我如何让 Tab 键在两个控件之间循环在exe中的窗体上,在dll中的窗体上的控件,显示在面板中?

这是 DLL 的 DPR:

library Project18;

{ Important note about DLL memory management: ShareMem must be the
  first unit in your library's USES clause AND your project's (select
  Project-View Source) USES clause if your DLL exports any procedures or
  functions that pass strings as parameters or function results. This
  applies to all strings passed to and from your DLL--even those that
  are nested in records and classes. ShareMem is the interface unit to
  the BORLNDMM.DLL shared memory manager,which must be deployed along
  with your DLL. To avoid using BORLNDMM.DLL,pass string information
  using PChar or ShortString parameters. }

uses
  System.SysUtils,System.Classes,Winapi.Windows,Unit25 in 'Unit25.pas' {Form25};

{$R *.res}

procedure ShowForm(const AParentWindow: HWND); StdCall;
begin
  Form25 := TForm25.Create(nil);
  Form25.ParentWindow := AParentWindow;
  Form25.Show;
end;

procedure CloseForm; StdCall;
begin
  if Assigned(Form25) then
  begin
    Form25.Close;
    FreeAndNil(Form25);
  end;
end;

exports
  ShowForm,CloseForm;

begin
end.

以及 EXE 的 DPR:

program Project19;

uses
  Vcl.Forms,Unit26 in 'Unit26.pas' {Form26};

{$R *.res}

begin
  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  Application.CreateForm(TForm26,Form26);
  Application.Run;
end.

这是 DLL 中的表单布局:

Unit25 TForm in Project18 DLL

这是 EXE 中的表单布局:

enter image description here

这是EXE中表单的代码:

unit Unit26;

interface

uses
  Winapi.Windows,Winapi.Messages,System.SysUtils,System.Variants,Vcl.Graphics,Vcl.Controls,Vcl.Forms,Vcl.Dialogs,Vcl.ExtCtrls,Vcl.StdCtrls,Vcl.ComCtrls;

type
  TShowForm = procedure(AParentWindow: HWND); stdcall;
  TCloseForm = procedure; stdcall;

  TForm26 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Panel1: TPanel;
    Panel2: TPanel;
    ComboBox1: TComboBox;
    DateTimePicker1: TDateTimePicker;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private declarations }
    LibHandle: THandle;
    ShowForm: TShowForm;
    CloseForm: TCloseForm;
  public
    { Public declarations }
  end;

var
  Form26: TForm26;

implementation

{$R *.dfm}

procedure TForm26.Button1Click(Sender: TObject);
begin
  ShowForm(Panel1.Handle);
end;

procedure TForm26.Button2Click(Sender: TObject);
begin
  CloseForm;
end;

procedure TForm26.FormCreate(Sender: TObject);
begin
  LibHandle := LoadLibrary('Project18.dll');

  @ShowForm := GetProcAddress(LibHandle,'ShowForm');
  @CloseForm := GetProcAddress(LibHandle,'CloseForm');
end;

procedure TForm26.FormDestroy(Sender: TObject);
begin
  FreeLibrary(LibHandle);
end;

end.

DLL 中表单的代码是标准的自动生成的东西。

当我第一次运行 EXE 时,表单如下所示:

enter image description here

点击Button1后,在Panel1内的DLL中显示窗体,如下所示:

enter image description here

但是当我通过 Tab 键浏览控件时,它会跳过 DLL 中表单中显示的那些:

enter image description here

例如,如果我单击 Edit1 并按 Tab,它会跳转到 exe 主窗体上的下一个控件,而不是嵌入窗体中的下一个控件。

我在 Stack Overflow 和更广泛的网络上查看了 How to avoid issues when embedding a TForm in another TForm? 和其他答案,但我无法使用 TFrame,因为我嵌入的 GUI 是不同 DLL 中的 TForm。

我是否需要捕获选项卡的按键,然后将 Windows 消息发送到嵌入式表单?为此,我想我需要嵌入表单的 HWND,而我没有。

编辑:我将 ShowForm 更改为返回所创建表单的 HWND 的函数,但这没有区别,因为即使 KeyPreview 设置为 true,按下 Tab 时 FormKeyPress 和 FormKeyUp 事件也不会触发.

编辑:必须添加一个 CM_Dialog 消息处理程序来捕获 Tab 键,根据 https://community.embarcadero.com/article/technical-articles/149-tools/13071-detecting-tab-key-press

编辑:但是这样做会阻止 Tab 键在 EXE 中的窗体上的控件中实际使用 Tab 键进行切换,因此这根本不是解决方案,而且使用 PostMessage 将选项卡发送到嵌入的表单也没有任何作用。

如何在使用 Tab 键时获得控件焦点以包含嵌入表单中的控件?

编辑:如果我向 EXE 添加第二个表单,再添加两个按钮,并对该表单执行相同的步骤(关于显示和隐藏),它具有相同的行为,即使第二个表单在EXE 而不是 DLL。

显然,仅设置表单 ParentWindow 不足以使其正确处理。如果我将表单 Parent 属性设置为 TPanel,而不是使用 ParentWindow,它确实适用于 EXE 中的第二个表单。这似乎表明必须将 TWinControl 传递给 DLL。

编辑:不确定这是否可以完成。正如这个问题中所述,它不能,

How to get instance of TForm from a Handle?

我查看了这个问题中提到的 CreateParented,

How to instantiate a class which normally needs parent(TWinControl) in a dll?

它仍然不允许选项卡流入创建的控件。

编辑:我现在发现的一个部分解决方案是在 EXE 中为应用程序提供一个 OnMessage 事件处理程序,它使用 TMsgIsDialogMessage 传递给 DLL 中的表单,通过对示例程序进行这些更改:

  public
    { Public declarations }
  protected
    procedure MessageHandler(var Msg: TMsg; var Handled: Boolean);
  end;

procedure TForm26.FormCreate(Sender: TObject);
begin
  LibHandle := LoadLibrary('Project18.dll');

  @ShowForm := GetProcAddress(LibHandle,'CloseForm');

  Application.OnMessage := MessageHandler;
end;

procedure TForm26.MessageHandler(var Msg: TMsg; var Handled: Boolean);
begin
  if (Msg.message = WM_KEYDOWN) then
  begin
    if IsDialogMessage(FormHandle,Msg) then
      Handled := True;
  end;
end;

只有两个问题。

  1. 该选项卡在具有焦点的任何窗体的控件之间循环。它不会从 DLL 中切换到窗体上的控件,也不会从这些控件中跳出到 EXE 中窗体上的控件。
  2. 虽然 DLL 中的表单具有焦点,但该表单上控件的选项卡顺序是相反的。

enter image description here

我大概可以编写代码,在退出EXE窗体的最后一个控件时,利用Winapi.Windows.SetFocus()将焦点移到DLL窗体中,同样地,在最后一个控件退出时,将焦点设置回EXE窗体DLL 中的选项卡。

对于处理反向 Tab 键顺序还没有答案。我知道这不是控件的 TabOrder 属性。他们是正确的。也许我需要在 DLL 中使用另一个 WM_KEYDOWN 消息处理程序?

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams['font.sans-serif'] = ['SimHei'] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -> systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping("/hires") public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate<String
使用vite构建项目报错 C:\Users\ychen\work>npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-
参考1 参考2 解决方案 # 点击安装源 协议选择 http:// 路径填写 mirrors.aliyun.com/centos/8.3.2011/BaseOS/x86_64/os URL类型 软件库URL 其他路径 # 版本 7 mirrors.aliyun.com/centos/7/os/x86
报错1 [root@slave1 data_mocker]# kafka-console-consumer.sh --bootstrap-server slave1:9092 --topic topic_db [2023-12-19 18:31:12,770] WARN [Consumer clie
错误1 # 重写数据 hive (edu)> insert overwrite table dwd_trade_cart_add_inc > select data.id, > data.user_id, > data.course_id, > date_format(
错误1 hive (edu)> insert into huanhuan values(1,'haoge'); Query ID = root_20240110071417_fe1517ad-3607-41f4-bdcf-d00b98ac443e Total jobs = 1
报错1:执行到如下就不执行了,没有显示Successfully registered new MBean. [root@slave1 bin]# /usr/local/software/flume-1.9.0/bin/flume-ng agent -n a1 -c /usr/local/softwa
虚拟及没有启动任何服务器查看jps会显示jps,如果没有显示任何东西 [root@slave2 ~]# jps 9647 Jps 解决方案 # 进入/tmp查看 [root@slave1 dfs]# cd /tmp [root@slave1 tmp]# ll 总用量 48 drwxr-xr-x. 2
报错1 hive> show databases; OK Failed with exception java.io.IOException:java.lang.RuntimeException: Error in configuring object Time taken: 0.474 se
报错1 [root@localhost ~]# vim -bash: vim: 未找到命令 安装vim yum -y install vim* # 查看是否安装成功 [root@hadoop01 hadoop]# rpm -qa |grep vim vim-X11-7.4.629-8.el7_9.x
修改hadoop配置 vi /usr/local/software/hadoop-2.9.2/etc/hadoop/yarn-site.xml # 添加如下 <configuration> <property> <name>yarn.nodemanager.res