View Issue Details

IDProjectCategoryView StatusLast Update
0006671JEDI VCL00 JVCL Componentspublic2019-04-17 16:40
ReportermhAssigned ToAHUser 
PrioritynormalSeveritycrashReproducibilityalways
Status resolvedResolutionfixed 
Product Version3.48 
Target VersionFixed in VersionDaily / GIT 
Summary0006671: Changing TJvDialButton Frequency, Min or Max at runtime leads to exception
DescriptionTrying to set frequency, min or max property of a TJvDialButton, which had been created at runtime in code, crashes with access violation. The crash is in Unit Vcl.Graphics in the if of this function:

function TBitmap.GetCanvas: TCanvas;
begin
  if FCanvas = nil then

The crash happens after the parent of the TJvDialButton instance had been set. It does not crash if the new value is the default value or if the assignment of the value is before setting the parent.

TagsNo tags attached.

Activities

2019-04-15 15:12

 

CrashExample.zip (53,631 bytes)

mh

2019-04-15 22:22

reporter   ~0021703

While I have the question why the Rio Debugger doesn't properly let me debug JVCL (\run directory is in the search path, optimization is off), I land in CPU view and breakpoints inside JvDialButton unit are not honored I managed to find out, that the TBitmap.GetCanvas call which fails fails, because the TBitMap object is nil and the call is in this method:

procedure TJvCustomDialButton.SetTick(Value: Integer; Length: TJvTickLength);
const
  Lengths: array [TJvTickLength] of Byte =
    (tlShortLen, tlMiddleLen, tlLongLen);
var
  P: PTick;
  I: Integer;
begin
  if (Value < FMin) or (Value > FMax) then
    raise EInvalidOperation.CreateResFmt(@SOutOfRange, [FMin, FMax]);
  for I := 0 to FTicks.Count - 1 do
  begin
    P := FTicks.Items[I];
    if P^.Value = Value then
    begin
      if P^.Length <> Lengths[Length] then
      begin
        P^.Length := Lengths[Length];
        P^.Changed := True;
        Invalidate;
      end;
      Exit;
    end;
  end;
  New(P);
  P^.Value := Value;
  P^.Length := Lengths[Length];
  P^.Changed := True;
  P^.Color := clBtnText;
  FTicks.Add(P);
  if HandleAllocated then
  begin
    DrawTick(FBitmap.Canvas, P^);
    DrawTick(Canvas, P^);
  end;
end;

I suspect it is the DrawTick(FBitmap.Canvas, P^); call.

mh

2019-04-15 22:54

reporter   ~0021704

Further investigation result: FBitmap is being created in the BitmapNeeded method, which is called in two places:

- Paint method
- DrawBorder method.

Ok, FBitmap is not created if we create the component at runtime in code and set Max before it had any chance to get itsself painted.

=> BitmapNeeded method must be called "earlier", but that one contains calls to DrawButton and DrawTicks.

mh

2019-04-15 23:00

reporter   ~0021705

I tried to place a call to BitmapNeeded as first call in SetMax method to test what happens, but it looks like it never got called.

I created a public method in TJvDialButton which simply calls Bitmap needed, but the compiler says he cannot find this method. But I did save the JVCL unit, did a clean of the demo (the one in the zip of this issue) and checked if I altered the right copy of JvDialButton.pas. What did I do wrong?

mh

2019-04-15 23:01

reporter   ~0021706

Did another test, altered the Unit1.pas of the attached zip file like this and found out by this, that it works then:

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ComCtrls, Vcl.ExtCtrls, Vcl.ToolWin,
  Vcl.StdCtrls, JvDialButton;

type
  TMyDialButton = class(TJvDialButton)
  public
    procedure DoBitmapNeeded;
  end;

  TForm1 = class(TForm)
    FlowPanel1: TFlowPanel;
    CrashButton: TButton;
    procedure CrashButtonClick(Sender: TObject);
  private
// TestDialButton : TJvDialButton;
    TestDialButton : TMyDialButton;
    procedure CreateJvDialogButton;
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

{ TForm1 }

procedure TForm1.CrashButtonClick(Sender: TObject);
begin
  CreateJVDialogButton;
end;

procedure TForm1.CreateJvDialogButton;
begin
// TestDialButton := TJvDialButton.Create(FlowPanel1);
  TestDialButton := TMyDialButton.Create(FlowPanel1);
  TestDialButton.Parent := FlowPanel1;
  TestDialButton.DoBitmapNeeded;
  TestDialButton.Max := 200;
end;

{ TMyDialButton }

procedure TMyDialButton.DoBitmapNeeded;
begin
  BitmapNeeded;
end;

end.

Talkbaze

2019-04-16 17:35

reporter   ~0021707

https://talkbaze.com/

mh

2019-04-16 19:25

reporter   ~0021708

Proposed a fix via pull request:
https://github.com/project-jedi/jvcl/pull/76

AHUser

2019-04-17 16:40

developer   ~0021716

Fixed in master branch.

Issue History

Date Modified Username Field Change
2019-04-15 14:57 mh New Issue
2019-04-15 15:12 mh File Added: CrashExample.zip
2019-04-15 22:22 mh Note Added: 0021703
2019-04-15 22:54 mh Note Added: 0021704
2019-04-15 23:00 mh Note Added: 0021705
2019-04-15 23:01 mh Note Added: 0021706
2019-04-16 17:35 Talkbaze Note Added: 0021707
2019-04-16 19:25 mh Note Added: 0021708
2019-04-17 16:40 AHUser Note Added: 0021716
2019-04-17 16:40 AHUser Status new => resolved
2019-04-17 16:40 AHUser Fixed in Version => Daily / GIT
2019-04-17 16:40 AHUser Resolution open => fixed
2019-04-17 16:40 AHUser Assigned To => AHUser