我有一个问题,我试图搜索解决方案,但无法达到我想要的效果。对不起,如果这很简单,请指出我正确的方法。
原来如此!我有一个C程序是一个加载器。它必须调用我用Delphi或Lazarus(Free Pascal)编写的DLL。 DLL实际上是一个独立的GUI应用程序:在调试期间,我有条件地将其编译为EXE并且它正常工作。
我的构建脚本将其编译为DLL,其中包含一个必须执行它的入口点,就像它独立工作一样。我期望完全相同的行为,但如果需要,我可以做一些不同的事情(特别是设置应用程序图标)。
Loader是一个控制台式程序,但没有控制台编译 - 没有窗口,没有任何东西。它只是加载DLL并调用一个函数。
问题是当我构建一个空的默认项目时,一个表单作为EXE - 它实际上会有#34; master"任务栏中的应用程序(.Handle<> 0)窗口。所以我可以独立于主表单标题设置其标题。
但是当DLL中存在同样的东西 - 没有应用程序窗口(.Handle = 0)时,标题将是表单标题,但最重要的错误:表单不能是最小化!
在Delphi 7中,它在其他窗口下显示背景(但任务栏的东西仍然存在!);在拉撒路,它只是最小化到无处(隐藏,无法再恢复);两者都没有任何最小化的动画。
除此之外,我的应用程序似乎表现正常。这只是我的问题。
好的,我知道库中的表单是一件坏事,但是:
我很好地实例化另一个" VCL完全独立于主机的实例,甚至可能在不同的线程中。
我的特定主机应用程序中没有VCL!对我来说,它必须与EXE一样完全正常工作......
我在DLL中搜索了一些关于Application.Handle的内容,现在明白我需要将一个句柄传递给主机的Application对象,所以DLL将与其他主机表单连接,但我没有!它甚至不是Delphi ......(和应用程序:= TApplication.Create(nil);也没有帮助)
以下任何内容都可能对我有所帮助:
A)如何指示VCL为我创建一个普通的Application对象?如何在EXE中执行此操作,也许我可以复制该代码?
B)如何从C(适当的样式等)创建合适的主窗口以将其句柄传递给DLL?另外,我相信,在Free Pascal中没有直接访问TApplication句柄值,所以我不可能分配它。
C)如何在没有任务栏窗口的情况下生活,但是我的表格(好消息:我的程序只有一个表格!)可以正确地最小化(或者只是某种方式......)?
我现在你们都喜欢看一些代码,所以这里是:
// 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.
我特意设计了可以用rundll32调用的entry()函数,仅用于测试。
另外,我试图将身体直接放到" 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命令" rundll32 project1.dll entry
"将立即运行它。 (是的,这样我就可以得到Rundll给我的一个句柄,但这不是我想要的。)
最后说明:(a)DLL必须在Lazarus中编译;实际上我首先认为它是LCL中的一个错误,但是现在在Delphi7中测试时我看到了同样的错误;由于Delphi的案例更简单,更强大,我决定把它放在这里; (b)我的C loader没有调用LoadLibrary,它使用TFakeDLL hack(OBJ文件被调整为没有Delphi包装器工作)并从内存加载我的DLL(所以我没有DLL本身的句柄),但否则他们的行为是一样的。
答案 0 :(得分:1)
好的,感谢@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.
仍然没有应用程序窗口(任务栏标题等于表单标题),但现在我的表单可以成功最小化(使用系统动画)。请注意,对于EXE编译,我必须使用Application的默认方式,因为当我尝试创建这样的表单时 - 它开始最小化到桌面(iconify)而不是任务栏。
它在空的默认Lazarus项目中也很完美。但当我试图将它实现到我的生产程序时,它给了我" Disk Full" .ShowModal的例外!
那件事早些时候令我感到沮丧(这就是为什么我完全摆脱了模态,再也没有尝试过了),但现在我已经下定决心了解这一点。
我发现了问题!我的构建脚本没有通过" -WG
" ("指定图形类型应用程序")编译器选项。看起来LCL中的某些东西正在使用类似控制台的模式,并且模态循环由于某种原因而失败。
然后,我想要分享另一个非常令人困惑的问题。我的表单的OnCreate相当大而复杂(甚至开始其他线程),并且一些内部函数在尝试使用表单上的一个控件执行某些操作时会给我访问冲突。它看起来还没有构建控件,或者表单本身......
事实证明,实际调用Form1:=TForm1.Create(nil);
显然会留下全局变量" Form1" FormCreate事件期间未分配。修复很简单:在Form1:=Self;
TForm1.FormCreate(Sender: TObject);
现在一切正常,没有任何问题。如果我首先将它们添加到我的entry()函数中,我甚至可以使用普通Form2.Show();
的其他表单,例如Form2:=TForm2.Create(Form1);
(编辑:次要注意,如果您使用Lazarus并尝试从任何不同于加载DLL库本身的线程运行entry()函数 - 那么您应该将MainThreadID:=GetCurrentThreadId();
放在Application.Initialize;)之上
是的,这个问题已经解决了!