Delphi:在DLL中定位一个窗体

3
我正在创建一个没有任何包的dll中的表单。可以通过使用导出的过程调用dll中的表单:
procedure ShowAbout(const AppHandle: THandle); stdcall;
  var
    aHandle: THandle;
    form:  TfrmAbout;  / my form in some other unit in the dll
  begin

    aHandle:= Application.Handle;
    Application.Handle:= AppHandle;

    form :=TfrmAbout.Create(Application);
    form.ShowModal;
    form.Free;
    Application.Handle:= aHandle;
  end;

表单显示良好,没有问题。现在,我想让它以poMainFormCenter的方式定位(我希望它始终显示在主窗体上(调用dll的窗体)。
我尝试过使用form :=TfrmAbout.Create(Application.MainForm);等等,但没有成功。
有什么技巧可以帮助吗?

你应该传递 Application 指针而不仅是窗口句柄。虽然这很简单,但不幸的是没有其他方法可行。 - Premature Optimization
4
绝对不能传递 Application 指针。无法将对象从 VCL 的一个实例传递到另一个实例。你需要使用运行时包来解决这个问题。 - David Heffernan
3个回答

8
VCL的Position机制依赖于应用程序中所有窗体都使用同一版本的VCL。显然这里并非如此,您需要手动定位窗体。
通过调用GetWindowRect()方法并传递主窗体句柄来查找主窗体的位置,然后需要计算出您的窗体需要在哪个位置才能处于该主窗体的中心。
procedure PositionForm(Form: TForm; MainWindow: HWND);
var
  MainBounds: TRect;
  MainWidth, MainHeight: Integer;
begin
  if GetWindowRect(MainWindow, MainBounds) then
  begin
    MainWidth := MainBounds.Right-MainBounds.Left;
    MainHeight := MainBounds.Bottom-MainBounds.Top;
    Form.Left := MainBounds.Left + (MainWidth - Form.Width) div 2;
    Form.Top := MainBounds.Top + (MainHeight - Form.Height) div 2
  end;

顺便提一下,你传递的句柄是 HWND 而不是 THandle。你应该相应地更改你的代码。这不会改变行为,但在逻辑上是正确的。


2

由于您没有使用包,您的EXE和DLL都有单独的TApplication实例。在您的EXE中的TApplication.MainForm在您的DLL中不可见。更改TApplication.Handle不会使MainForm更改。找到其他方式来正确定位表单,但更好的方法是:使用包,如果不使用包,您将遇到更多问题。


1
有时候需要使用DLL文件,例如Office插件。尽管如此,如果符合您的需求,软件包肯定更加整洁。 - David Heffernan

1

你尝试过将form.ParentWindow设置为父窗口的句柄吗?你应该将它作为参数传递给ShowAbout,或者你可以从Application对象中找到它(类似于Application.ActiveForm),但我不确定它是否有效。

调用TfrmAbout.Create(Application.MainForm)只是指定Application.MainForm负责销毁该窗体,它与窗口层次结构无关,而且如果你在单独的dll中创建窗体,我不确定你是否应该使用自动销毁。


是的,我试过了。不幸的是,如果我添加form.ParentWindow := parentHandle(作为参数传递),窗体会挂起并且不会显示。 - Lobuno
是的,ParentWindow 似乎是关于其他事情的。顺便说一下,aHandle 存储了从 dll 中使用的 Forms 单元中的应用程序句柄,仅在分配来自调用应用程序的句柄之前,以便在 dll 即将关闭时将其恢复。 - Lobuno
@DavidHeffernan:是的,但如果MainForm是一个窗体的父窗口,那么当该窗体以模态方式显示时,该窗体将成为顶级窗口。此外,一个受欢迎的副作用是,当MainForm被激活时,它将自动激活模态窗体,即具有焦点并捕获应用程序消息循环的模态窗体不会被主窗体隐藏。此外,这应该使CenterParent起作用,正如OP所请求的那样。 - Boris B.
不,当您设置ParentWindow时,子窗口不再是顶级窗口。您是否在考虑窗口所有者?微软通过重载术语混淆了这些术语。 - David Heffernan
2
是的,那就是我所说的。MSDN上说:“您不能使用GWL_HWNDPARENT索引调用SetWindowLong来更改子窗口的父级。而应该使用SetParent函数。” - David Heffernan
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接