Recent

Author Topic: Several buttons on taskbar for my app  (Read 15126 times)

Talker

  • New Member
  • *
  • Posts: 18
Several buttons on taskbar for my app
« on: December 17, 2013, 04:19:06 pm »
Hi!

I have an app which loads a form from a DLL, and that form opens another form. (First form shows a grid and the second one alows to edit a selected row).
App works fine but there is a strange problem - there is a separate button on Windows Taskbar for each form! I.e. there are THREE buttons - for main window for first form and for second form.

Could somebody give me a hint is there a way to show just one button on Taskbar for the whole application?

Thank you so much and sorry for my poor English!

kpeters58

  • Sr. Member
  • ****
  • Posts: 267
Re: Several buttons on taskbar for my app
« Reply #1 on: December 17, 2013, 05:48:26 pm »
Lazarus 2.0.4/FPC 3.0.4/Win 64

Talker

  • New Member
  • *
  • Posts: 18
Re: Several buttons on taskbar for my app
« Reply #2 on: December 18, 2013, 07:56:53 am »
Here is the simplified code.

Main window:
Code: [Select]
unit Main;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, Windows, mysql51conn, sqldb, db, dbf, FileUtil, dbdateedit,
  rxdbgrid, RxSortSqlDB, RxSortZeos, ZDataset, ZConnection, Forms, Controls,
  Graphics, Dialogs, StdCtrls, ExtCtrls, ComCtrls, DBGrids, ExtDlgs, Calendar,
  EditBtn, Grids, Buttons, Menus,
  dynlibs;

type
  TfrmMain = class(TForm)
    btnFJournal: TButton;
    ZConnection1: TZConnection;
    procedure btnFJournalClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { private declarations }
  public
    { public declarations }
  end;

const
  {$IFDEF windows}
   libraryName = 'fjournal.dll';
  {$ENDIF}
    {$IFDEF unix}
    libraryName = 'fjournal.so';
  {$ENDIF}

var
  frmMain: TfrmMain;
  aUserName, aUserPass, aAlias: string;

implementation

uses
  FizEdit, Login, gettext, translations, rxFileUtils;

{$R *.lfm}

procedure TfrmMain.FormCreate(Sender: TObject);
begin
  if not Log_in(ZConnection1, aUserName, aUserPass, aAlias, '') then Application.Terminate;
end;

procedure OpenFJournal;
type
  TOpenFJournal = procedure(db: Pointer; aHandle: HWND);
var
  lib: TLibHandle;
  func: TOpenFJournal;
begin
  lib := LoadLibrary(libraryName);
  try
    Pointer(func) := GetProcedureAddress(lib, 'OpenFJournal');
    if Assigned(func) then begin
      frmMain.Enabled := False;
      try
        func(@frmMain.ZConnection1, frmMain.Handle);
      finally
        frmMain.Enabled := True;
      end;
    end;
  finally
    FreeLibrary(lib);
  end;
end;

procedure TfrmMain.btnFJournalClick(Sender: TObject);
begin
  OpenFJournal;
end;

end.

First form:
Code: [Select]
unit fJournalGrid;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, Controls, Windows, Forms, db, FileUtil, StdCtrls, ExtCtrls,
  Dialogs, Graphics,
  rxdbgrid, RxSortZeos, curredit, ZComponent, ZConnection, ZDataset,
  Menus, EditBtn;

type
  TfrmJournalGrid = class(TForm)
    ZConnection: TZConnection;
    ZQuery: TZQuery;
    Datasource: TDatasource;
    DBGrid: TRxDBGrid;
    PopupMenu1: TPopupMenu;
    popEdit: TMenuItem;
    procedure FormShow(Sender: TObject);
    procedure popEditClick(Sender: TObject);
  private
    { private declarations }
  public
    { public declarations }
  end;

var
  frmJournalGrid: TfrmJournalGrid;

procedure OpenFJournal(db: Pointer; aHandle: HWND);

implementation

uses
  FizEdit;

{$R *.lfm}

procedure OpenFJournal(db: Pointer; aHandle: HWND);
var
  pDB:^TZConnection;
begin
  pDB := db;
  frmJournalGrid := TfrmJournalGrid.Create(Application);
  with frmJournalGrid do
  try
    ZQuery.Connection := pDB^;
    ZQuery.Open;
    ShowModal;
  finally
    Release;
  end;
end;

procedure TfrmJournalGrid.popEditClick(Sender: TObject);
begin
  FizEdit.OpenFizEdit(Datasource.DataSet.FieldByName('id').AsInteger);
end;

end.

Second form:
Code: [Select]
unit FizEdit;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, sqldb, db, dbf, FileUtil, dbdateedit, rxdbcomb, rxdbgrid,
  rxlookup, ZDataset, ZSqlUpdate, ZConnection, Forms,
  Controls, Graphics, Dialogs, ExtCtrls, StdCtrls, DbCtrls, DBGrids, Buttons,
  Menus;

type
  TfrmFizEdit = class(TForm)
    btnOk: TButton;
    btnCancel: TButton;
    ZQuery1: TZQuery;
    ZUpdateSQL1: TZUpdateSQL;
    Datasource1: TDatasource;
    Label1: TLabel;
    DBEdit1: TDBEdit;
    ...
    procedure btnOkClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { private declarations }
  public
    { public declarations }
  end;

var
  frmFizEdit: TfrmFizEdit;

  procedure OpenFizEdit(id: Integer);

implementation

uses
  fJournalGrid, Func;

{$R *.lfm}

procedure OpenFizEdit(id: Integer);
begin
  with TfrmFizEdit.Create(frmJournalGrid) do
  begin
    try
      ZQuery1.ParamByName('id').AsInteger := id;
      ZQuery1.Open;
      if ShowModal = mrOK then
        if ZQuery1.State in [dsEdit, dsInsert] then
          ZQuery1.Post
      else
        ZQuery1.Cancel;
    finally
      Free;
    end;
  end;
end;

procedure TfrmFizEdit.FormCreate(Sender: TObject);
begin
  ZQuery1.Connection := frmJournalGrid.ZQuery.Connection;
end;

procedure TfrmFizEdit.btnOkClick(Sender: TObject);
begin
  ModalResult := mrOK;
  Close;
end;

end.

See in attachment how does it look.
« Last Edit: December 18, 2013, 12:47:33 pm by Talker »

CM630

  • Hero Member
  • *****
  • Posts: 1522
  • Не съм сигурен, че те разбирам.
    • http://sourceforge.net/u/cm630/profile/
Re: Several buttons on taskbar for my app
« Reply #3 on: December 18, 2013, 08:31:16 am »
Lazarus forms have a ShowInTaskBar property, but it is not working. There is a bug, which no one seems to be able to fix. If there was no bug, you should use ShowInTaskBar:= stNever.
http://bugs.freepascal.org/view.php?id=17294
So if you have three forms open, you will have three buttons in the task bar.
I would recommend you to report the bug again, so probably someone take care of it.
Well, there are, of course some other options:
- Try to fix the bug yourself.
- Get used to it (that's my solution, after reporting the bug again made no result).
- Migrate to Delphi.
- Pay someone to fix it.
« Last Edit: December 18, 2013, 08:36:05 am by paskal »
Лазар 4,2 32 bit (sometimes 64 bit); FPC3,2,2

Talker

  • New Member
  • *
  • Posts: 18
Re: Several buttons on taskbar for my app
« Reply #4 on: December 18, 2013, 04:20:23 pm »
paskal
thanks for info..((

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 11792
  • Debugger - SynEdit - and more
    • wiki
Re: Several buttons on taskbar for my app
« Reply #5 on: December 18, 2013, 04:43:13 pm »
Quote
I have an app which loads a form from a DLL,

Afaik using LCL in DLL is the problem (Not sure, but I am not aware, that this was fixed yet)

If you have LCL (e.g. forms, controls, ...) in a DLL, and in your main app, then both of them create there own instance of TApplication (and others, such as TScreen(s)). Essentially behaving like 2 apps running.

This may also lead to one of the forms not fully reacting to user input.

They also have individual memory managers. So anything allocated/created in the dll, must be destroyed there too.

Talker

  • New Member
  • *
  • Posts: 18
Re: Several buttons on taskbar for my app
« Reply #6 on: December 18, 2013, 05:52:55 pm »
Thanks a lot, Martin

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 11792
  • Debugger - SynEdit - and more
    • wiki
Re: Several buttons on taskbar for my app
« Reply #7 on: December 18, 2013, 07:39:54 pm »
You can try, but at your own risk, with lots and lots of work, and no guarantee:

Pass the application (and others, see globals Unit Forms) from your app to the dll, and substitute them there.
But you may have to change LCL code to prevent auto-creation in the initialization sections.
Also you would still have 2 memory managers, which needs a lot of care to be taken.

And all that is theory only. I have no idea how much work it will end up to be.

engkin

  • Hero Member
  • *****
  • Posts: 3112
Re: Several buttons on taskbar for my app
« Reply #8 on: December 19, 2013, 04:59:09 am »
Talker, before you call Show or ShowModal let Windows know that you do not need these buttons:

Code: [Select]
var
  ExStyle: Long; //longint
...
  ExStyle := GetWindowLong(YourForm.Handle, GWL_EXSTYLE);
  SetWindowLong(YourForm.Handle, GWL_EXSTYLE, ExStyle and not WS_EX_APPWINDOW);
  YourForm.ShowModal;
...

A more formal way would involve overriding the procedure that tells Windows to show that button, CreateParams has the following section:

Code: [Select]
procedure TCustomForm.CreateParams(var Params : TCreateParams);
..
        if (WndParent = 0) and
           (((Self = Application.MainForm) and Application.MainFormOnTaskBar) or (GetEffectiveShowInTaskBar = stAlways)) then
          ExStyle := ExStyle or WS_EX_APPWINDOW;
..

If you decided to override it in your forms, add the following:

Code: [Select]
  inherited CreateParams(Params);
  Params.ExStyle := Params.ExStyle and not WS_EX_APPWINDOW;

kpeters58 did point at the right direction. and Martin did explain the core of the problem.

Talker

  • New Member
  • *
  • Posts: 18
Re: Several buttons on taskbar for my app
« Reply #9 on: December 19, 2013, 08:42:16 am »
engkin

I tried both ways - first I tried to override CreateParams procedure:
Code: [Select]
  TfrmJournalGrid = class(TForm)
  ...
  private
    { private declarations }
    procedure CreateParams(var Params: TCreateParams); override;
  ...
  end;

procedure TfrmJournalGrid.CreateParams(var Params: TCreateParams);
begin
  inherited CreateParams(Params);
  Params.ExStyle := Params.ExStyle and not WS_EX_APPWINDOW;
end;

It does not work.

And then I tried to change window style before calling ShowModal method:
Code: [Select]
  '''
  with frmJournalGrid do
  try
    ...
    ExStyle := GetWindowLong(Handle, GWL_EXSTYLE);
    SetWindowLong(Handle, GWL_EXSTYLE, ExStyle and not WS_EX_APPWINDOW);
    ShowModal;
  finally
    Release;
  end;
  ...
Unfortunaly, it does not work too.
 :(
« Last Edit: December 19, 2013, 08:46:09 am by Talker »

Talker

  • New Member
  • *
  • Posts: 18
Re: Several buttons on taskbar for my app
« Reply #10 on: December 19, 2013, 09:00:37 am »
To be clear i'd like to say that there's no problem when I do not use DLL - I see one button on the Taskbar for the whole app.
« Last Edit: December 19, 2013, 09:03:18 am by Talker »

CM630

  • Hero Member
  • *****
  • Posts: 1522
  • Не съм сигурен, че те разбирам.
    • http://sourceforge.net/u/cm630/profile/
Re: Several buttons on taskbar for my app
« Reply #11 on: December 19, 2013, 11:34:28 am »
Talker, before you call Show or ShowModal let Windows know that you do not need these buttons:

Code: [Select]
var
  ExStyle: Long; //longint
...
  ExStyle := GetWindowLong(YourForm.Handle, GWL_EXSTYLE);
  SetWindowLong(YourForm.Handle, GWL_EXSTYLE, ExStyle and not WS_EX_APPWINDOW);
  YourForm.ShowModal;
...

This does not work for me, too. I use no DLLs.


EDIT: Oddly, I did a simple project from the scratch and it is fine  :o
EDIT: I disabled the ShowModal dialog. I still have two buttons.  I really do use DLLs but they have no forms, it's hard for me to believe, that it is related.
« Last Edit: December 19, 2013, 12:11:47 pm by paskal »
Лазар 4,2 32 bit (sometimes 64 bit); FPC3,2,2

engkin

  • Hero Member
  • *****
  • Posts: 3112
Re: Several buttons on taskbar for my app
« Reply #12 on: December 19, 2013, 05:08:32 pm »
@Talker, @paskal would either one of you check that WS_EX_APPWINDOW flag is cleared after the form is visible.

Code: [Select]
var
  IsFlagCleared: boolean;
...
 IsFlagCleared := (GetWindowLong(YourForm.Handle, GWL_EXSTYLE) and WS_EX_APPWINDOW) = 0;

Talker

  • New Member
  • *
  • Posts: 18
Re: Several buttons on taskbar for my app
« Reply #13 on: December 20, 2013, 06:56:56 am »
engkin

Flag is cleared after the form is shown.

Code: [Select]
unit FizGrid;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, Controls, Windows, Forms, db, FileUtil, StdCtrls, ExtCtrls, Dialogs, Graphics,
  rxdbgrid, curredit, ZConnection, ZDataset, ZSqlUpdate, EditBtn, lcltype;

type
  TfrmFizGrid = class(TForm)
    Datasource: TDatasource;
    ZConnection: TZConnection;
    ZQuery: TZQuery;
    ...
    procedure FormActivate(Sender: TObject);
  private
    procedure CreateParams(var Params: TCreateParams); override;
  public
  end;

var
  frmFizGrid: TfrmFizGrid;

procedure OpenFJournal(db: Pointer; aHandle: HWND);

implementation

{$R *.lfm}

procedure OpenFJournal(db: Pointer; aHandle: HWND);
var
  pDB:^TZConnection;
  ExStyle: Long;
begin
  pDB := db;
  frmFizGrid := TfrmFizGrid.Create(Application);
  with frmFizGrid do
  try
    ZQuery.Connection := pDB^;

    ExStyle := GetWindowLong(Handle, GWL_EXSTYLE);
    SetWindowLong(Handle, GWL_EXSTYLE, ExStyle and not WS_EX_APPWINDOW);
    ShowModal;
  finally
    Release;
  end;
end;

procedure TfrmFizGrid.CreateParams(var Params: TCreateParams);
begin
  inherited CreateParams(Params);
  Params.ExStyle := Params.ExStyle and not WS_EX_APPWINDOW;
end;

procedure TfrmFizGrid.FormActivate(Sender: TObject);
var
  IsFlagCleared: boolean;
 function b2s(B: Boolean): string;
 begin
   if B then Result := 'TRUE' else Result := 'FALSE';
 end;
begin
  IsFlagCleared := (GetWindowLong(frmFizGrid.Handle, GWL_EXSTYLE) and WS_EX_APPWINDOW) = 0;
  ShowMessage('IsFlagCleared = ' + b2s(IsFlagCleared));

  (******************************************)
  (********** Here it shows "TRUE" **********)
  (******************************************)
end;

end.
« Last Edit: December 20, 2013, 06:58:30 am by Talker »

engkin

  • Hero Member
  • *****
  • Posts: 3112
Re: Several buttons on taskbar for my app
« Reply #14 on: December 21, 2013, 01:18:17 am »
Talker, I was wrong  :-[ . For a form in a DLL you just need to change the owner.

Code: [Select]
  RightOwner := GetWindow(YourMainForm.Handle, GW_OWNER);
  WrongOwner := SetWindowLong(YourDllForm.Handle, GWL_HWNDPARENT, RightOwner);

 

TinyPortal © 2005-2018