Solved

TBitBtn Access Violations - Bug?

Posted on 2004-04-26
13
575 Views
Last Modified: 2010-04-05
Is this a bug, or am i doing somthing i can't see?

This works great for TSpeedbuttons:

procedure TForm1.ClearButtons;
var
 I: integer;
begin
 for I:= Scrollbox1.ControlCount -1 downto 0 do
  if ScrollBox1.COntrols[I] is TSpeedbutton then
   TSpeedbutton(ScrollBox1.COntrols[I]).Free;
end;

procedure TForm1.UpdateCategories;
var
 I, X, Y: integer;
 Btn: TSpeedbutton;
begin
 X:= 0; Y:= 0;
 for I:= 0 to 10 do
 begin
  Btn:= TSpeedbutton.Create(ScrollBox1);
  Btn.Parent:= ScrollBox1;
  Btn.Width:= 50;
  Btn.Height:= 50;
  Btn.Left:= X;
  Btn.Top:= Y;
  Btn.OnClick:= BitBtn2Click;
  X:= X + Btn.Width;
 end;
end;

procedure TForm1.UpdateItems;
var
 I, X, Y: integer;
 Btn: TSpeedbutton;
begin
 X:= 0; Y:= 0;
 for I:= 0 to 5 do
 begin
  Btn:= TSpeedbutton.Create(ScrollBox1);
  Btn.Parent:= ScrollBox1;
  Btn.Width:= 50;
  Btn.Height:= 50;
  Btn.Left:= X;
  Btn.Top:= Y;
  Btn.OnClick:= BitBtn1Click;
  X:= X + Btn.Width;
 end;
end;

{$R *.dfm}

procedure TForm1.BitBtn1Click(Sender: TObject);
begin
 ClearButtons;
 UpdateCategories;
end;

procedure TForm1.BitBtn2Click(Sender: TObject);
begin
ClearButtons;
UpdateItems;
end;

end.



but if I change TSpeedbuttons to TbitBtns, I get Access Violations:

Im using Delphi 7 on Win XP


I need to use Raize BitBtns, but I get Access violations as well. So I went to see if I go them using the Standard Delphi BitBtn - and sure enough still.

So I tried SpeedButtons - And no Access Violations.

Shane
0
Comment
Question by:shaneholmes
  • 6
  • 5
  • 2
13 Comments
 
LVL 26

Expert Comment

by:Russell Libby
ID: 10923670

The speedbutton does not generate the violation because the actual "click" event handling is done as the very last statement in the MouseUp event. Regardless, its still wrong to do what you are doing...

You should NEVER be releasing a component in its own event handler. Per the Delphi help:

Warning:      Never explicitly free a component within one of its own event handlers or free a component from the event handler of a component it owns or contains. For example, don’t free a button in its OnClick event handler or free the form that owns the button from the button's OnClick event.

----

What you should be doing is posting a WM_USER+{your message} message to the main window, then in the message handler for your custom message, handle the button removal.

Regards,
Russell
0
 
LVL 17

Expert Comment

by:mokule
ID: 10923689
When I copy/paste Your code and press Ctrl+Shift+C these procedures
    procedure BitBtn1Click(Sender: TObject);
    procedure BitBtn2Click(Sender: TObject);
declarations went to private section - causing access violation

When I moved them to right place everything is OK both for TSpeedBtn and bitbtn

Maybe You've got the same problem
0
 
LVL 17

Expert Comment

by:mokule
ID: 10923750
rlibby is of course right.
I didn't make all needed changes so at first there were no access violations
0
 
LVL 11

Author Comment

by:shaneholmes
ID: 10923753
no, they weren't in the private section, they were above it...

but when i remove them and put them in the protected section, i got access violations, then when i cut and pasted them back into above the private section, i no longer get access violations - totally weired....

0
 
LVL 11

Author Comment

by:shaneholmes
ID: 10923760

Can you give me an example:

"What you should be doing is posting a WM_USER+{your message} message to the main window, then in the message handler for your custom message, handle the button removal."

Shane
0
 
LVL 11

Author Comment

by:shaneholmes
ID: 10923791
That is totally weired....


If I cut
 
  procedure BitBtn1Click(Sender: TObject);
    procedure BitBtn2Click(Sender: TObject);

TForm1 = class(TForm)
    ScrollBox1: TScrollBox;
    BitBtn1: TBitBtn;
    BitBtn2: TBitBtn;
    procedure BitBtn1Click(Sender: TObject);
    procedure BitBtn2Click(Sender: TObject);
  private
    { Private declarations }
  protected
  public
    { Public declarations }
  end;


 and paste to private section

TForm1 = class(TForm)
    ScrollBox1: TScrollBox;
    BitBtn1: TBitBtn;
    BitBtn2: TBitBtn;
  private
    { Private declarations }
    procedure BitBtn1Click(Sender: TObject);
    procedure BitBtn2Click(Sender: TObject);
  protected
  public
    { Public declarations }
  end;

of course i get the access violations, but when i cut & paste them back to top, and run again, it works fine.

However, If I close doen and re-run it again, I get access violations again!

Shane
0
Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

 
LVL 26

Accepted Solution

by:
Russell Libby earned 100 total points
ID: 10923826

It has to do with the way Delphi's memory manager works. For example, you can free an object, and in the next statement, peform a call on the object and get away with it, because the mem manager does not clear out the memory where the object existed.

Anyways, suffice it to say that regardless of throwing the access violation or not, it is STILL wrong.

Regarding the message handling, its very simple:

const
  WM_DOSOMETHING    =  WM_USER+100;

type
  TForm1         =  class(TForm)
     Button1:    TButton;
     procedure   Button1Click(Sender: TObject);
  private
     // Private declarations
  protected
     // Handle the cleanup
     procedure   DoSomething(var Message: TMessage); message WM_DOSOMETHING;
  public
     // Public declarations
  end;

var
  Form1: TForm1;

implementation
{$R *.DFM}

procedure TForm1.DoSomething(var Message: TMessage);
begin
  Button1.Free;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  PostMessage(Handle, WM_DOSOMETHING, 0, 0);
end;

Regards,
Russell

0
 
LVL 11

Author Comment

by:shaneholmes
ID: 10923853
hmmm, you learn something new everyday, let me try that....

Shane
0
 
LVL 11

Author Comment

by:shaneholmes
ID: 10923919
Works Perfect - Thanks Russell!

Learn something new everyday!

Shane


unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Buttons, RzButton;


 const
  WM_CLEARBUTTONS    =  WM_USER+100;
  WM_UPDATECATS    =  WM_USER+200;
  WM_UPDATEITEMS    =  WM_USER+300;

type
  TForm1 = class(TForm)
    ScrollBox1: TScrollBox;
    BitBtn1: TBitBtn;
    BitBtn2: TBitBtn;
    procedure BitBtn1Click(Sender: TObject);
    procedure BitBtn2Click(Sender: TObject);
  private
    { Private declarations }
  protected
  procedure  ClearButtons(var Message: TMessage); message WM_CLEARBUTTONS ;
  procedure  UpdateCats(var Message: TMessage); message WM_UPDATECATS ;
  procedure  UpdateItems(var Message: TMessage); message WM_UPDATEITEMS ;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

procedure TForm1.ClearButtons(var Message: TMessage);
var
 I: integer;
begin
 for I:= Scrollbox1.ControlCount -1 downto 0 do
  if ScrollBox1.COntrols[I] is TBitBtn then
    TBitBtn(ScrollBox1.COntrols[I]).Free;
end;

procedure  TForm1.UpdateCats(var Message: TMessage);
var
 I, X, Y: integer;
 Btn: TBitBtn;
begin
 X:= 0; Y:= 0;
 for I:= 0 to 10 do
 begin
  Btn:= TBitBtn.Create(ScrollBox1);
  Btn.Parent:= ScrollBox1;
  Btn.Width:= 50;
  Btn.Height:= 50;
  Btn.Left:= X;
  Btn.Top:= Y;
  Btn.OnClick:= BitBtn2Click;
  X:= X + Btn.Width;
 end;
end;

procedure  TForm1.UpdateItems(var Message: TMessage);
var
 I, X, Y: integer;
 Btn: TBitBtn;
begin
 X:= 0; Y:= 0;
 for I:= 0 to 5 do
 begin
  Btn:= TBitBtn.Create(ScrollBox1);
  Btn.Parent:= ScrollBox1;
  Btn.Width:= 50;
  Btn.Height:= 50;
  Btn.Left:= X;
  Btn.Top:= Y;
  Btn.OnClick:= BitBtn1Click;
  X:= X + Btn.Width;
 end;
end;

{$R *.dfm}

procedure TForm1.BitBtn1Click(Sender: TObject);
begin
 PostMessage(Handle, WM_CLEARBUTTONS, 0, 0);
 PostMessage(Handle, WM_UPDATECATS, 0, 0);
end;

procedure TForm1.BitBtn2Click(Sender: TObject);
begin
 PostMessage(Handle, WM_CLEARBUTTONS, 0, 0);
 PostMessage(Handle, WM_UPDATEITEMS, 0, 0);
end;

end.
0
 
LVL 26

Expert Comment

by:Russell Libby
ID: 10924081

Glad I could help. Your code above is definitely the preferred way to handle component "self-suicide".

Russell
0
 
LVL 11

Author Comment

by:shaneholmes
ID: 10924094
WHat if I had to perform a Routine which requires parameters...?

procedure  UpdateItems(Cat: Category)
begin
 //
end;

procedure TForm1.BitBtn1Click(Sender: TObject);
begin
 // PostMessage
  UpdateItems(Cat: Category);
end;


Shane
0
 
LVL 26

Expert Comment

by:Russell Libby
ID: 10924636

If the params are numeric in nature, then they could be passed in the wParam and lParam. If strings are involved, or more than 2 params are required, then the following could be done (as the message handling is all done within the same app).

1.) Define a record structure, eg:

type
 TMyRecord          = packed record
   lpszParam1:    PChar;
   dwParam2:     Integer;
   szData:          Array [0..MAX_PATH] of Char;
 end;

2) Allocate a block of memory for a structure, and fill the parameters in.
3.) Pass the structure with a cast, eg

var
  MyRecord: TMyRecord;
begin

 // Allocate and set
 // ....

 // Pass into PostMessage
 PostMessage(Handle, Message, wParam, Integer(@MyRecord));

end;

4.) In the message handler, recast back to the correct structure, do whatever needs to be done, then free the memory.

It doesn't have to be a record pointer either, as it could be an object, ect. Just as long as the record/object/ect exists when the post message is processed.

Using PostMessage, the one type of coding that you need to avoid is something along the lines of PChar passing, eg:

var s: String;
begin
 
 s:='Something';  
 PostMessage(Handle, Message, wParam, Integer(PChar(s)));

end;

Because the data may be cleared, the stack may be popped, etc, before the message is processed.


------------

Russell







0
 
LVL 26

Expert Comment

by:Russell Libby
ID: 10924643
Actually, in my above example, it should have been:

3.) Pass the structure with a cast, eg

type
 PMyRecord = ^TMyRecord;

var
  lpMyRecord: PMyRecord;
begin

 // Allocate and set
 // ....
 lpMyRecord:=AllocMem(SizeOf(TMyRecord));

 // Pass into PostMessage
 PostMessage(Handle, Message, wParam, Integer(lpMyRecord));

--------

Russell
0

Featured Post

Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Title # Comments Views Activity
Internet Explorer View Settings Question 15 105
FMX enumerated colours 2 84
code issue 8 97
How to load 2 images in same column in Delphi 2 27
Hello everybody This Article will show you how to validate number with TEdit control, What's the TEdit control? TEdit is a standard Windows edit control on a form, it allows to user to write, read and copy/paste single line of text. Usua…
Introduction I have seen many questions in this Delphi topic area where queries in threads are needed or suggested. I know bumped into a similar need. This article will address some of the concepts when dealing with a multithreaded delphi database…
Concerto provides fully managed cloud services and the expertise to provide an easy and reliable route to the cloud. Our best-in-class solutions help you address the toughest IT challenges, find new efficiencies and deliver the best application expe…
Delivering innovative fully-managed cloud services for mission-critical applications requires expertise in multiple areas plus vision and commitment. Meet a few of the people behind the quality services of Concerto.

947 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

20 Experts available now in Live!

Get 1:1 Help Now