VCL/LCL – DLL 中的窗体 – 没有应用程序任务栏 window, 无法最小化主窗体

VCL/LCL – a form in DLL – no Application taskbar window, cannot minimize the main form

我有一个问题,我试图搜索解决方案但无法实现我想要的。抱歉,如果这真的很简单,请指出正确的操作方法。

所以!我有一个 C 程序,它是一个加载程序。它必须调用我用 Delphi 或 Lazarus (Free Pascal) 编写的 DLL。 DLL 实际上是一个独立的 GUI 应用程序:在调试期间,我有条件地将它编译为 EXE 并且它可以工作。

我的构建脚本将其编译为具有一个入口点的 DLL,该入口点必须像独立运行一样执行它。我期待完全相同的行为,但如果需要,我可以做一些不同的事情(尤其是设置应用程序图标)。

Loader 是一个 console-style 程序,但在没有控制台的情况下编译 – 没有 windows,没有任何东西。它只是加载 DLL 并调用一个函数。

问题是,当我使用一种形式构建一个空的默认项目作为 EXE – 它实际上会在任务栏中有 "master" Application (.Handle <> 0) window。所以我可以独立于主窗体标题设置它的标题。

但是当同样的东西在DLL里面时——没有Applicationwindow(.Handle=0),标题会是form caption,但是最重要的bug:一个form不能被最小化!

在 Delphi 7 中,它在其他 windows 下进入后台(但任务栏的东西仍然存在!);在 Lazarus 中,它只是最小化到无处(隐藏,无法再恢复);两者都没有任何最小化动画。

除此之外,我的应用程序似乎运行正常。这是我遇到的唯一问题。

好的,我知道图书馆中的表格是一件坏事,但是:

  1. 我很好地实例化 "another" VCL 完全独立于主机的实例,甚至可能在不同的线程中。

  2. 我的特定主机应用程序中没有 VCL!对我来说,它必须完全像在 EXE 中一样工作......

我在 DLL 中搜索了一些关于 Application.Handle 的内容,现在明白我需要将句柄传递给主机的应用程序 object,这样 DLL 将与其他主机形式结合,但我有 none!它甚至不是 Delphi...(和 Application:=TApplication.Create(nil);也没有帮助)

以下任何内容都可能对我有所帮助:

我现在你们都喜欢看一些代码,所以这里是:

// default empty project code, produces valid working EXE:
program Project1;

uses Forms, Unit1 in 'Unit1.pas' {Form1};
{$R *.res}

begin
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.

+

// that's how I tried to put it in a DLL:
library Project1;

uses Forms, Unit1 in 'Unit1.pas' {Form1};
{$R *.res}

function entry(a, b, c, d: Integer): Integer; stdcall;
begin
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
  Result := 0;
end;

exports
  entry;

begin
end.

我特制的 entry() 函数可以用 运行dll32 调用,仅用于测试。

此外,我尝试将 body 直接放入“begin end.”初始化部分——同样的错误行为。

// To call a DLL, this can be used:
program Project1;

function entry(a, b, c, d: Integer): Integer; stdcall; external 'Project1.dll';

begin
  entry(0, 0, 0, 0);
end.

此外,CMD-command“rundll32 project1.dll entry”会立即运行。 (是的,那样我可能会得到 Rundll 给我的句柄,但这不是我想要的。)

最后说明:(a) DLL 必须在 Lazarus 中编译;实际上,我首先认为这是 LCL 中的一个错误,但现在在 Delphi7 中进行测试时,我看到了同样的情况;由于 Delphi 案例更简单、更健壮,我决定把它放在这里; (b) 我的 C 加载程序不调用 LoadLibrary,它使用 TFakeDLL hack(该 OBJ 文件被调整为在没有 Delphi 包装器的情况下工作)并从内存加载我的 DLL(所以我没有 DLL 本身的句柄), 但除此之外它们的行为是相同的。

好的,感谢@Sertac Akyuz,我尝试使用 .ShowModal:

// working Delphi solution:
library Project1;

uses Forms, Dialogs, SysUtils, Unit1 in 'Unit1.pas' {Form1};
{$R *.res}

function entry(a, b, c, d: Integer): Integer; stdcall;
begin
  Result := 0;
  Application.Initialize;
  Form1 := TForm1.Create(nil);
  try
    Form1.ShowModal;
  except
    on e: Exception do
      ShowMessage(e.message);
  end;
  Form1.Free;
end;

exports
  entry;

begin
end.

仍然没有应用程序window(任务栏标题等于表单标题),但现在我的表单可以成功最小化(有系统动画)。请注意,对于 EXE 编译,我必须使用应用程序的默认方式,因为当我尝试创建这样的表单时 - 它开始最小化到桌面(图标化)而不是任务栏。

它在空的默认 Lazarus 项目中也能完美运行。但是当我试图将它实现到我的生产程序时,它在 .ShowModal!

处给了我 "Disk Full" 异常

早些时候那件事让我很沮丧(这就是为什么我完全摆脱模态,不再尝试),但现在我有足够的决心弄清楚这个问题。

我发现了问题!我的构建脚本没有传递“-WG”("Specify graphic type application")编译器选项。看起来 LCL 中的某些东西正在使用 console-alike 模式,并且由于某种原因模态循环失败。

然后,我想分享另一个非常令人困惑的问题。我的窗体的 OnCreate 相当大且复杂(甚至启动其他线程),并且当我尝试使用窗体上的控件之一执行某些操作时,某些内部函数会给我访问冲突。看起来控件还没有构建,或者表单本身…

原来实际的调用Form1:=TForm1.Create(nil);显然会在FormCreate事件中让全局变量"Form1"未赋值。解决方法很简单:在 TForm1.FormCreate(Sender: TObject);

的开头添加 Form1:=Self;

现在一切正常,没有任何问题。如果我首先将它们添加到我的 entry() 函数中,我什至可以使用具有正常 Form2.Show(); 的其他形式,例如 Form2:=TForm2.Create(Form1);

(编辑:小提示,如果你想使用 Lazarus 并尝试从任何不同的线程而不是加载 DLL 库本身的线程 运行 entry() 函数——那么你应该把 MainThreadID:=GetCurrentThreadId(); 放在以上 Application.Initialize;)

耶,这个问题解决了!