我开发了一个具有一种形式的DLL。我使用下面的代码为它设置了一个样式。
library TestLib;
uses Vcl.Themes, Vcl.Styles,....
.
.
exports
function1,
function2;
begin
TStyleManager.TrySetStyle('Style1');
end.
当我加载此dll并调用function1打开此表单时。表单将打开并应用样式。
现在,当我最小化该窗口时,我收到了访问冲突。一切包括最大化&恢复工作正常。此外,所有功能都正常。
我想这不是处理由此表单的Minimize事件生成的消息。请指教。
注意:当我删除样式时,一切正常。
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窗口的最小化按钮。您将获得访问冲突。
答案 0 :(得分:6)
访问冲突的原因是,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中使用样式时继续遇到类似问题的可能性。
答案 1 :(得分:0)
您应该在启用了“运行时包”的相同版本的RAD Studio中编译bouth dll和exe。只有在这种情况下,dll中的表单才能保证正常工作。
答案 2 :(得分:0)
样式越来越流行,因此也许会对将来的读者有所帮助,尤其是因为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);