I have developed a DLL having one form. I have set a style to it using below code.
library TestLib;
uses Vcl.Themes, Vcl.Styles,....
.
.
exports
function1,
function2;
begin
TStyleManager.TrySetStyle('Style1');
end.
When I load this dll and call function1 which opens this form. Form gets open with style applied to it.
Now I am getting an access violation when I minimize that window. Everything including Maximize & Restore is working fine. Also All the functionality are working fine.
I guess it is not handling message generated by Minimize event of this form. Please advice.
Note: When I remove style, everything is working fine.
Call Stack
:0976742b TWinControl.HandleNeeded + $3
:0978ad8a TStyleManager.HandleMessage + $56
:09762a3c TWinControl.DoHandleStyleMessage + $14
:0972e6be TCustomForm.WndProc + $612
:09763c2b TWinControl.MainWndProc + $2F
UPDATE : SSCCE
Project1.EXE (having one form 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.
create one DLL Project2.dll having unit2 as any form and unit3 which will invoke that form. Add a style (say AnyStyle1) to this dll as resource.
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.
Now press minimize button of Unit2 window. You will get an access violation.
The reason of the access violation is that, vcl styles in Delphi XE2 does not seem to be designed with VCL styles in a dll in mind. The AV is thrown in the WM_SIZE
handler of the form style hook:
procedure TFormStyleHook.WMSize(var Message: TWMSize);
begin
if IsIconic(Handle) and (Application.MainForm.Handle <> Handle) then
InvalidateNC;
...
The style hook tests if the message is being handled on the main form, but there's no main form in a dll. Accessing the handle of the unassigned reference causes the exception.
The below workaround introduces a descendant style hook to prevent this, it bypasses the check for the main form and lets the processing of the message continue at TWinControl
.
This is the entire, modified 'unit3' in the dll:
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.
Note also the possibility of continuing to hit issues like these while considering to use styles in a dll.