9

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.

Dev
  • 629
  • 2
  • 9
  • 23
  • 1
    Dude - why not check for "if not TStyleManager.TrySetStyle()"? Where's your error detection/error handling??? ALSO: You typically set up a custom style in the IDE. This might do some implicit "initialization". It sounds like the style manager hasn't been correctly initialized the way you're doing it. IMHO... Check out [This link](http://stackoverflow.com/questions/9906312/delphi-xe2-vcl-styles-tutorial-how-to-change-the-style-at-runtime). – paulsm4 Aug 29 '13 at 04:26
  • @paulsm4 : In this link also similar code is written. TStyleManager.TrySetStyle('Style1'); Configuration using IDE (Project Option) can be done for EXE only. This option of setting style is not available for DLL. – Dev Aug 29 '13 at 05:05
  • 1
    Possible duplicate [`VCL-Styles embedded in dll?`](http://stackoverflow.com/q/9703309/576719) – LU RD Aug 29 '13 at 05:22
  • 1
    @Dev - my point is that the IDE does a lot of stuff "under the covers" that *wouldn't* necessarily happen if you're making calls from a .dll. I strongly suspect that important initialization hasn't happened. Having said that: if "TrySetStyle()" returns a boolean (i.e. if it can fail) ... then it behooves you to CHECK for "false". IMHO... – paulsm4 Aug 29 '13 at 05:36
  • @LURD - I have gone through your link. I have done same thing to add a style in DLL. Only one issue I am facing here as mentioned above. – Dev Aug 29 '13 at 05:53
  • @paulsm4 - I have checked return value for this operation and it is returning TRUE. – Dev Aug 29 '13 at 05:54
  • 2
    This could be a bug. I added the Vcl-Styles tag to the question so that people watching this can respond (@RRUZ for example). – LU RD Aug 29 '13 at 07:10
  • 1
    Very likely the problem is the usual one of sharing VCL objects across DLL boundary. Show a full SSCCE. – David Heffernan Aug 29 '13 at 07:34
  • 1
    Sine many people don't know what `SSCCE` means: [Short, Self Contained, Correct (Compilable), Example](http://sscce.org/) – Jeroen Wiert Pluimers Aug 29 '13 at 08:05
  • possible duplicate of [How to disable VCL styles in Delphi](http://stackoverflow.com/questions/8598728/how-to-disable-vcl-styles-in-delphi) – Adam Arold Aug 29 '13 at 08:33
  • @JeroenWiertPluimers If people are incapable of typing SSCCE into a websearch then they may as well give up programming – David Heffernan Aug 29 '13 at 08:44
  • 1
    @AdamArold - I have only one form in my DLL and I don't want to disable VCL style for it. Instead I have applied it and working great except this one issue. – Dev Aug 29 '13 at 08:44
  • Updated Description to add a working example of this issue. – Dev Aug 29 '13 at 08:56
  • 1
    +1 Your SSCCE is good and I can confirm the behaviour. No idea why yet...... – David Heffernan Aug 29 '13 at 09:16
  • @DavidHeffernan it might be that an abbreviation means something different to them, or they do not recognize it even if the context is perfectly clear to you. The first time I had the impression SSCCE was something about some system software configuration control engine. Don't get me started on SOA... – Jeroen Wiert Pluimers Aug 29 '13 at 12:21
  • @JeroenWiertPluimers I'm not about to start explaining SSCCE every time I use it. As I said, if somebody is incapable of typing SSCCE into a websearch and clicking on the top hit, then they are not in the right business – David Heffernan Aug 29 '13 at 13:28
  • 2
    I am trying hard to understand how this question might be a duplicate of " How to disable VCL styles in Delphi ". .. and failing at that.. – Sertac Akyuz Aug 29 '13 at 20:03
  • Boy am I glad Jon Skeet is more friendly to the SO users: http://meta.stackexchange.com/a/22762/137204 "Please provide a short but complete example which demonstrates the problem." – Jeroen Wiert Pluimers Aug 30 '13 at 17:06

4 Answers4

6

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.

Sertac Akyuz
  • 54,131
  • 4
  • 102
  • 169
  • Note to @Dev: While this code solves that particular bug, VCL-in-dll has lots of others, not only styles-related. – Torbins Aug 30 '13 at 07:55
0

You should compile bouth dll and exe in the same version of RAD Studio with "Runtime packages" enabled. Only in this case form in dll is guaranteed to work properly.

Torbins
  • 2,111
  • 14
  • 16
0

Styles are getting more popular, so maybe it'll help future readers, especially since the AV on minimize is still there with Delphi RIO 10.3.3.

I found out that creating a dummy hidden mainform in the dll helps.

Just pass the application object to the dll and do this inside

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

In DLLs, system hooks must be turned off before calling SetStyle in DLLs, otherwise it will try to access a Mainform that does not exist and you'll get Access Violations, etc.

TStyleManager.SystemHooks := [];
TStyleManager.SetStyle(StyleName);

It took me a long time to find this out, it needs to be more widely documented, hence this post.

SoftDeveloper
  • 160
  • 1
  • 8