Entwickler-Ecke

Grafische Benutzeroberflächen (VCL & FireMonkey) - Firemonkey, Fehler bei grafischer Komponente


jjturbo - Mi 02.02.22 11:51
Titel: Firemonkey, Fehler bei grafischer Komponente
Moin Forum,

ich habe auf meinem (FMX-)Form selbst geschriebene, von TRectangle abgeleitete Komponenten, diese erzeugen jeweils ein TLabel um das zugehörige TRectangle beschriften zu können. Funktioniert auch wunderbar.
Jetzt werden aus zwei weiteren Threads Informationen gesendet, die die Farben und/oder die Beschriftungen der Komponenten anpassen. Das passiert aus den beiden Threads ca. alle 200-300 Millisekunden. Irgendwann nach längerer Zeit, (undefinierbar, mal 30 Minuten, mal mehrere Stunden) kommt es zu einem Fehler:



Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
exception message  : Zugriffsverletzung bei Adresse 656F5C31 in Modul 'DWRITE.DLL'. Lesen von Adresse FEEEFEEE.

thread $b18:
656f5c31 +000 DWRITE.DLL
008789fe +50a HC3079.exe   FMX.Canvas.D2D            2651 +84 TTextLayoutD2D.DoRenderLayout
005d44b0 +01c HC3079.exe   FMX.TextLayout             397  +6 TTextLayout.EndUpdate
00731a6e +4b2 HC3079.exe   FMX.Objects               2778 +42 TText.Paint
005fb009 +1a1 HC3079.exe   FMX.Graphics              6030 +24 TCanvas.SetMatrix
007644e5 +1a1 HC3079.exe   FMX.Controls              3281 +14 DoPaintInternal
00764794 +198 HC3079.exe   FMX.Controls              3312 +15 PaintAndClipChild
00764880 +064 HC3079.exe   FMX.Controls              3329  +7 TControl.PaintInternal
00764ff8 +1a8 HC3079.exe   FMX.Controls              3468 +34 TControl.PaintChildren
007e448c +02c HC3079.exe   FMX.Controls.Presentation  952  +5 TPresentedControl.PaintChildren
0076458b +247 HC3079.exe   FMX.Controls              3288 +21 DoPaintInternal
00764794 +198 HC3079.exe   FMX.Controls              3312 +15 PaintAndClipChild
00764880 +064 HC3079.exe   FMX.Controls              3329  +7 TControl.PaintInternal
00a09b7d +241 HC3079.exe   FMX.Forms                 6123 +47 TCustomForm.PaintRects
0097933c +274 HC3079.exe   FMX.Platform.Win          1680 +40 WMPaint
0097a94c +47c HC3079.exe   FMX.Platform.Win          2169 +74 WndProc
7664687a +07a USER32.dll                                      GetWindowLongW
77384e9b +04b ntdll.dll                                       KiUserCallbackDispatcher
009fc0df +03b HC3079.exe   FMX.Forms                 1875  +3 TApplication.HandleMessage
0097726a +03a HC3079.exe   FMX.Platform.Win           800  +6 TPlatformWin.Run
009fcac7 +03f HC3079.exe   FMX.Forms                 2176  +3 TApplication.Run
01453b87 +2af HC3079.exe   HC3079                      98 +41 initialization
75e7fa27 +017 KERNEL32.DLL                                    BaseThreadInitThunk




In dieser Procedure, meistens in Zeile 99, manchmal aber auch in einer der Zeilen von 82-88:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
unit FMX.Canvas.D2D;


procedure TTextLayoutD2D.DoRenderLayout;
var
  TextRange: TDWriteTextRange;
  TextFormat: IDWriteTextFormat;
  I: Integer;
  Attr: TTextAttributedRange;
  TrimOptions: TDwriteTrimming;
  TrimmingSign: IDWriteInlineObject;
  FontDescriptor: TDWriteFontDescriptor;
  SolidBrush: ID2D1SolidColorBrush;
begin
  FLayout := nil;
  FTextRect := TRectF.Empty;

  FontDescriptor := GetFontStyles(Font);

  if Succeeded(TCanvasD2D.SharedDWriteFactory.CreateTextFormat(PChar(FontDescriptor.FamilyName), FFontCollection,
    FontDescriptor.Weight, FontDescriptor.Style, FontDescriptor.Stretch, Font.Size, PChar(FLocaleName), TextFormat)) then
  try
    if RightToLeft then
      TextFormat.SetReadingDirection(DWRITE_READING_DIRECTION_RIGHT_TO_LEFT)
    else
      TextFormat.SetReadingDirection(DWRITE_READING_DIRECTION_LEFT_TO_RIGHT);

    if WordWrap then
      TextFormat.SetWordWrapping(DWRITE_WORD_WRAPPING_WRAP)
    else
      TextFormat.SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP);

    FillChar(TrimOptions, SizeOf(TDwriteTrimming), 0);
    case Trimming of
      TTextTrimming.None:
        TrimOptions.granularity := DWRITE_TRIMMING_GRANULARITY_NONE;
      TTextTrimming.Character:
        TrimOptions.granularity := DWRITE_TRIMMING_GRANULARITY_CHARACTER;
      TTextTrimming.Word:
        TrimOptions.granularity := DWRITE_TRIMMING_GRANULARITY_WORD;
    end;

    TrimmingSign := nil;
    if Trimming <> TTextTrimming.None then
      TCanvasD2D.SharedDWriteFactory.CreateEllipsisTrimmingSign(TextFormat, TrimmingSign);
    TextFormat.SetTrimming(TrimOptions, TrimmingSign);
    TrimmingSign := nil;

    if Succeeded(TCanvasD2D.SharedDWriteFactory.CreateTextLayout(PChar(Text), Text.Length, TextFormat, MaxSize.X -
      Padding.Left - Padding.Right, MaxSize.Y - Padding.Top - Padding.Bottom, FLayout)) then
    begin
      TextRange.StartPosition := 0;
      TextRange.Length := Text.Length;
      FLayout.SetStrikethrough(TFontStyle.fsStrikeOut in Font.StyleExt.SimpleStyle, TextRange);
      [b]FLayout.SetUnderline(TFontStyle.fsUnderline in Font.StyleExt.SimpleStyle, TextRange);[/b]
      FLayout.SetFontWeight(FontDescriptor.Weight, TextRange);

      for I := 0 to AttributesCount - 1 do
      begin
        Attr := Attributes[I];
        TextRange.startPosition := Attr.Range.Pos;
        TextRange.length := Attr.Range.Length;
        if Attr.Attribute.Font <> nil then
        begin
          FLayout.SetStrikethrough(TFontStyle.fsStrikeOut in Attr.Attribute.Font.StyleExt.SimpleStyle, TextRange);
          FLayout.SetUnderline(TFontStyle.fsUnderline in Attr.Attribute.Font.StyleExt.SimpleStyle, TextRange);
          if not SameValue(Attr.Attribute.Font.Size, Font.Size, TEpsilon.FontSize) then
            FLayout.SetFontSize(Attr.Attribute.Font.Size, TextRange);

          FontDescriptor := GetFontStyles(Attr.Attribute.Font);
          FLayout.SetFontWeight(FontDescriptor.Weight, TextRange);
          FLayout.SetFontStyle(FontDescriptor.Style, TextRange);
          FLayout.SetFontStretch(FontDescriptor.Stretch, TextRange);
          FLayout.SetFontFamilyName(PChar(FontDescriptor.FamilyName), TextRange);
        end;
        if (Attr.Attribute.Color <> TAlphaColorRec.Null) and
           Succeeded(TCanvasD2D.SharedRenderTarget.CreateSolidColorBrush(D2Color(Attr.Attribute.Color, Opacity), nil,
             SolidBrush)) then
          FLayout.SetDrawingEffect(SolidBrush, TextRange);
      end;

      case HorizontalAlign of
        TTextAlign.Center:
          FLayout.SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER);
        TTextAlign.Leading:
          FLayout.SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING);
        TTextAlign.Trailing:
          FLayout.SetTextAlignment(DWRITE_TEXT_ALIGNMENT_TRAILING);
      end;
      case VerticalAlign of
        TTextAlign.Center:
          FLayout.SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER);
        TTextAlign.Leading:
          FLayout.SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_NEAR);
        TTextAlign.Trailing:
          FLayout.SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_FAR);
      end;
      FLayout.GetMetrics(FMetrics)
      FLayout.GetOverhangMetrics(FOverhangMetrics);
      UpdateTextRect;
    end;
  finally
    TextFormat := nil;
  end;
end;


Th69 - Mi 02.02.22 13:28

Ich tippe mal auf Threadsynchronisationsprobleme.


Sinspin - Do 03.02.22 17:58

Hallo,

wie werden die Informationen denn gesendet?
Verwendest Du dafür eventuell ein Object das zu dem Zeitpunkt nil/nicht existent sein könnte?


jjturbo - Fr 04.02.22 11:27

Ich habe mir einen MessageHandler geschrieben, der, ähnlich wie bei VCL, Botschaften an meine Mainform schickt. Dort werden sie ausgewertet und Werte auf visuellen Komponenten ausgegeben.
Keine der Komponenten die da benutzt werden sind NIL, diese existieren bereits zur Entwurfszeit.


Sinspin - Di 08.02.22 09:51

Die Frage ist, wenn die Nachricht ankommt, wie dann die Verarbeitung stattfindet.
Lößt dein Threadcall direkt eine Aktion im Fenster aus?
Ich mache es immer so das mein Fenster nur eine Info bekommt (nur ein Flag) und sich dann zum passenden Zeitpunkt selber kümmert die echte Nachricht abzuhohlen und auszuwerten. Das funktioniert seit Jahren in allen Programmen problemlos.