gpt4 book ai didi

delphi - DLL 中的 VCL 风格问题

转载 作者:行者123 更新时间:2023-12-03 14:48:21 25 4
gpt4 key购买 nike

我开发了一个具有一种形式的 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 窗口的最小化按钮。您将遇到访问冲突。

最佳答案

访问冲突的原因是,Delphi XE2中的vcl样式似乎没有考虑到dll中的VCL样式。 AV 被抛出到表单样式钩子(Hook)的 WM_SIZE 处理程序中:

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

...

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


下面的解决方法引入了一个后代样式钩子(Hook)来防止这种情况,它绕过对主窗体的检查,并让消息的处理在 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 中使用样式时,可能会继续遇到此类问题。

关于delphi - DLL 中的 VCL 风格问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18502190/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com