【问题标题】:VCL-Style Issues in DLLDLL 中的 VCL 样式问题
【发布时间】:2013-09-01 08:52:57
【问题描述】:

我开发了一个具有一种形式的 DLL。我已经使用下面的代码为其设置了样式。

library TestLib;

uses Vcl.Themes, Vcl.Styles,....
.
.
exports
   function1,
   function2;

begin
   TStyleManager.TrySetStyle('Style1');
end.

当我加载这个 dll 并调用打开这个表单的 function1 时。表单打开并应用了样式。

现在,当我最小化该窗口时,我遇到了访问冲突。包括最大化和恢复在内的一切工作正常。此外,所有功能都运行良好。

我猜它没有处理这种形式的最小化事件生成的消息。请指教。

注意:当我删除样式时,一切正常。

Call Stack

:0976742b TWinControl.HandleNeeded + $3
:0978ad8a TStyleManager.HandleMessage + $56
:09762a3c TWinControl.DoHandleStyleMessage + $14
:0972e6be TCustomForm.WndProc + $612
:09763c2b TWinControl.MainWndProc + $2F

更新:SSCCE

Project1.EXE(有一个表格 Unit1.pas/dfm)

unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    function InitDLL: Boolean;
  end;

var
  Form1: TForm1;

implementation

const
   cLIBRARY = 'Project2.dll';

var
   DLLHandle : THandle;
   showfrm: procedure;

procedure TForm1.Button1Click(Sender: TObject);
begin
   if InitDLL then
      showfrm;
end;

function TForm1.InitDLL: Boolean;
begin
   if DLLHandle = 0 then
   begin
      DLLHandle := LoadLibrary(PChar(cLIBRARY));
      if DLLHandle <> 0 then
      begin
         @showfrm := GetProcAddress(DLLHandle, 'showfrm');
      end
      else
      begin
         Result := False;
         raise Exception.Create('Error loading DLL: ' + cLIBRARY);
      end;
   end;

   Result := (DLLHandle > 0);
end;

{$R *.dfm}

end.

创建一个 DLL Project2.dll,其中 unit2 为任何形式,unit3 将调用该形式。 将样式(比如 AnyStyle1)作为资源添加到此 dll。

library Project2;

{ 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. }

{$R *.dres}

uses
  ShareMem,
  Vcl.Themes,
  Vcl.Styles,
  Vcl.Dialogs,
  System.SysUtils,
  System.Classes,
  Unit2 in 'Unit2.pas' {Form2},
  Unit3 in 'Unit3.pas';

{$R *.res}

exports
showfrm;

begin
   if TStyleManager.TrySetStyle('AnyStyle1') then
   begin
      ShowMessage('True');
   end
   else
      ShowMessage('False');
end.

unit3.pas

unit Unit3;

interface

uses Unit2;

   procedure showfrm;

implementation
   procedure showfrm;
   begin
      with TForm2.Create(nil) do
         Show;
   end;
end.

现在按下 Unit2 窗口的最小化按钮。您将遇到访问冲突。

【问题讨论】:

  • Dude - 为什么不检查“如果不是 TStyleManager.TrySetStyle()”?你的错误检测/错误处理在哪里???另外:您通常在 IDE 中设置自定义样式。这可能会做一些隐含的“初始化”。听起来样式管理器没有按照您的方式正确初始化。恕我直言...查看This link
  • @Dev - 我的意思是 IDE 会“在幕后”做很多事情,如果您从 .dll 进行调用,这些事情不会发生 .我强烈怀疑没有发生重要的初始化。话虽如此:如果“TrySetStyle()”返回一个布尔值(即如果它可能失败)......那么你应该检查“假”。恕我直言...
  • 这可能是一个错误。我在问题中添加了 Vcl-Styles 标签,以便观看此内容的人可以做出回应(例如@RRUZ)。
  • 我正在努力理解这个问题可能与“如何在 Delphi 中禁用 VCL 样式”重复。 .. 失败了..

标签: delphi dll delphi-xe2 vcl-styles


【解决方案1】:

访问冲突的原因是,Delphi XE2 中的 vcl 样式在设计时似乎没有考虑到 dll 中的 VCL 样式。 AV 被扔在表单样式钩子的WM_SIZE 处理程序中:

procedure TFormStyleHook.WMSize(var Message: TWMSize);
begin
  if IsIconic(Handle) and (Application.MainForm.Handle <> Handle) then
    InvalidateNC;

  ...

样式挂钩测试消息是否在主窗体上处理,但 dll 中没有主窗体。访问未分配引用的句柄会导致异常。


下面的解决方法引入了一个后代样式挂钩来防止这种情况,它绕过对主窗体的检查,并让消息的处理在 TWinControl 处继续。

这是整个 dll 中修改后的“unit3”:

unit Unit3;

interface

uses forms, messages, themes, windows, Unit2;

procedure showfrm;

implementation

type
  TForm2StyleHook = class(TFormStyleHook)
  private
    procedure WMSize(var Message: TWMSIZE); message WM_SIZE;
  end;

procedure TForm2StyleHook.WMSize(var Message: TWMSIZE);
begin
  if IsIconic(Handle) then begin
    // duplicate the code in ascendant, for whatever it serves
    InvalidateNC;
    // the rest of the code in ascendant class is related with MDI

    Handled := False; // if this is set to true TWinControl.WndProc returns
  end else
    inherited;
end;

procedure showfrm;
begin
  TStyleManager.Engine.RegisterStyleHook(TForm2, TForm2StyleHook);

  with TForm2.Create(nil) do
    Show;
end;

end.

还要注意在考虑在 dll 中使用样式时继续遇到此类问题的可能性。

【讨论】:

  • @Dev 的注意事项:虽然这段代码解决了那个特定的错误,但 VCL-in-dll 还有很多其他错误,不仅与样式相关。
【解决方案2】:

您应该在启用“运行时包”的相同版本的 RAD Studio 中编译 dll 和 exe。只有在这种情况下,dll 中的表单才能保证正常工作。

【讨论】:

  • 这看起来不像是 SSCCE 提出的问题
  • 这正是我几年前使用另一个皮肤引擎时遇到的问题。 VCL 在 dll 中表现不佳,并且皮肤使所有这些错误都可见。
  • 好吧,我想可能是这样。常见问题的更微妙表现。
【解决方案3】:

样式越来越流行,所以也许它会对未来的读者有所帮助,特别是因为 Delphi RIO 10.3.3 中仍然存在关于最小化的 AV。

我发现在 dll 中创建一个虚拟的隐藏主窗体会有所帮助。

只需将应用程序对象传递给 dll 并在内部执行此操作

var
  aMainForm: TForm;
begin
      Application.Handle := App.Handle;
      Application.OnIdle := App.OnIdle;
      Application.OnMessage := App.OnMessage;
      Application.CreateForm(TForm, aMainForm);

【讨论】:

    猜你喜欢
    • 2012-03-30
    • 2019-09-20
    • 1970-01-01
    • 2013-04-15
    • 1970-01-01
    • 1970-01-01
    • 2017-07-21
    • 2012-08-25
    • 2014-11-14
    相关资源
    最近更新 更多