[SOLVED] Runtime-Error 210 (Object Reference nil) in Memo1.Font.Canvas.TextWidth lazarus / 31-LCL ---------------------------------------------------------------------- Hartmut · 2020-06-10 11:14 With the following program I get Runtime-Error 210 (Object Reference is nil) in line    n:=Memo1.Font.Canvas.TextWidth('...');      Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i unit Unit1; {$mode objfpc}{$H+} interface uses Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls; type TForm1 = class(TForm)  Button1: TButton;  Memo1: TMemo;  procedure Button1Click(Sender: TObject); private public end; var Form1: TForm1; implementation {$R *.lfm} procedure TForm1.Button1Click(Sender: TObject);   var n: integer;   begin   Memo1.Lines.Add('Hello');   n:=Memo1.Font.Canvas.TextWidth('Get the width of this text');   Memo1.Lines.Add(IntToStr(n));   end; end.          I tried it with several Lazarus versions on different OS:  - Windows 7 (32-bit):    Laz 1.8.4/FPC 3.0.4 + Laz 2.0.6/FPC 3.0.4 + Laz 2.1.0 rev=62449 / FPC 3.3.1 rev=43796  - Linux Ubuntu 18.04 (64-bit):    Laz 1.8.4/FPC 3.0.4 + Laz 2.0.6/FPC 3.0.4   Because the explanation for Runtime-Error 210 says:    Object not initialized. When compiled with range checking on, a program will report this    error if you call a virtual method without having called its object’s constructor. I disabled in Project Options / Debugging all 6 checks: -Ci -Cr -Co -Ct -CR -Sa, but this made no difference. Then I remembered, that I had enabled those Debug checks in fpc.cfg and made a try with the original fpc.cfg (which I had saved), but this made no difference too. Can somebody reproduce the problem? Do I something wrong? Thanks for your help. I attached my demo. ---------------------------------------------------------------------- Handoko · 2020-06-10 11:28 It should be:    n:=Memo1.Font.GetTextWidth('Get the width of this text'); ---------------------------------------------------------------------- Hartmut · 2020-06-10 11:48 Great, this works! Thanks a lot, Handoko. But does anybody know, why Font.Canvas.TextWidth() causes this Runtime-Error? Is this a bug, which should be reported? Or is Font.Canvas.TextWidth() deprecated? And should I generally replace all Font.Canvas.TextWidth() with Font.GetTextWidth()? ---------------------------------------------------------------------- Hartmut · 2020-06-10 12:17 Hmmm, meanwhile I found out, that Font.GetTextWidth() causes no Runtime-Error, but the result is always "16", independently of the length of the text :-(( When looking into the source code, I found this in /.../fpcsrc/packages/fcl-image/src/fpfont.inc of Lazarus 2.0.6: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i function TFPCustomFont.GetTextWidth (text:string) : integer;begin  if inheritsFrom (TFPCustomDrawFont) then    result := TFPCustomDrawFont(self).DoGetTextWidth (text)  else   if assigned(FCanvas) then     result := FCanvas.GetTextWidth (text)   else      result :=16; // *some* default better than none.end; So unfortunately my problem is not solved yet... ---------------------------------------------------------------------- lucamar · 2020-06-10 12:22 Quote from: Hartmut on June 10, 2020, 01:48:07 pmBut does anybody know, why Font.Canvas.TextWidth() causes this Runtime-Error? Is this a bug, which should be reported? Or is Font.Canvas.TextWidth() deprecated? It's not a bug; that canvas comes from TFPCanvasHelper which should normally be dimmed as "for the internal use of the LCL". It's not a general property like, say, the form's canvas. QuoteAnd should I generally replace all Font.Canvas.TextWidth() with Font.GetTextWidth()? Yes, that's the proper way to do it. ---------------------------------------------------------------------- rvk · 2020-06-10 12:32 Quote from: Hartmut on June 10, 2020, 02:17:35 pmWhen looking into the source code, I found this in /.../fpcsrc/packages/fcl- image/src/fpfont.inc of Lazarus 2.0.6: And there you have exactly the reason why you've got am exception on Memo1.Font.Canvas.GetTextWidth(). (because Canvas is not assigned for Font) I'm also not sure why a Font would need a Canvas? Normally you would put a font on a Canvas. Not a Canvas on a Font. The problem here is that TMemo doesn't have a Canvas. You need to assign TMemo.Font to something else and calculate the Width there. Something like: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i var  F: TForm;  N: Integer;begin  F := TForm.Create(nil);  try    F.Font.Assign(Memo1.Font);    N := F.Canvas.TextWidth('Get the width of this text');    Showmessage(N.ToString);  finally    F.Free;  end;end; (Not sure if there are easier methods) Quote from: lucamar on June 10, 2020, 02:22:14 pmQuoteAnd should I generally replace all Font.Canvas.TextWidth() with Font.GetTextWidth()?Yes, that's the proper way to do it. No, that won't work either. Look at the given code by poster. When FCanvas is not assigned, 16 is always returned. You'll need to assign Font to another object to do the calculations. ---------------------------------------------------------------------- Hartmut · 2020-06-10 13:41 Thanks a lot to lucamar and rvk for your explainations and suggestions. I'm a little disappointed that GUI-Controls like TMemo come with 2 TextWidth() funktions like  - Memo1.Font.Canvas.TextWidth() and   - Memo1.Font.GetTextWidth() but one causes a Runtime-Error and the other returns a wrong result. From my opinion, either they should work correctly or should not exist... I will use as solution the workaround from wp in reply #4 of https://forum.lazarus.freepascal.org/index.php?topic=36579.0 (which is related to the idea of rvk): Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i function myTextWidth(AText: String; AFont: TFont): Integer;var  bmp: TBitmap;begin  Result := 0;  bmp := TBitmap.Create;  try    bmp.Canvas.Font.Assign(AFont);    Result := bmp.Canvas.TextWidth(AText);  finally    bmp.Free;  end;end; ---------------------------------------------------------------------- rvk · 2020-06-10 13:45 Quote from: Hartmut on June 10, 2020, 03:41:10 pmI'm a little disappointed that GUI-Controls like TMemo come with 2 TextWidth() funktions like  - Memo1.Font.Canvas.TextWidth() and   - Memo1.Font.GetTextWidth() but one causes a Runtime-Error and the other returns a wrong result. From my opinion, either they should work correctly or should not exist... The reason for this is that IF you access Font for a TLabel of TForm, the Canvas does exists. And in that case Font.Canvas.TextWidth() and Font.GetTextWidth() DO work. So the function does have meaning for lots of components. But because TMemo just doesn't have a Canvas (blame Microsoft), the TextWidth() just doesn't work (it need a Canvas for that). QuoteI will use as solution the workaround from wp in reply #4 of https://forum.lazarus.freepascal.org/index.php?topic=36579.0 (which is related to the idea of rvk):Yep. That one will work too. ---------------------------------------------------------------------- PascalDragon · 2020-06-10 14:27 Quote from: Hartmut on June 10, 2020, 03:41:10 pmI'm a little disappointed that GUI-Controls like TMemo come with 2 TextWidth() funktions like  - Memo1.Font.Canvas.TextWidth() and   - Memo1.Font.GetTextWidth() but one causes a Runtime-Error and the other returns a wrong result. From my opinion, either they should work correctly or should not exist... It's not the fault TMemo. Any other control with a Font property of type TFont will provide these two methods as well (though one is provided indirectly through the font's Canvas property). And TFont.GetTextWidth is a convenience method for accessing the TextWidth method of the font's canvas. Ideally you wouldn't have to deal with TFont.Canvas. Also the code of TFont.GetTextWidth you showed demonstrates the problem as rvk stated: Memo1.Font.Canvas is Nil, thus of course you'll get an exception when accessing it and of course you'll get the dummy result, because there is no canvas to calculate the width with. But that would be the case for any control that does not have a canvas (only those that descend from TCustomControl or TGraphicControl have a canvas). ---------------------------------------------------------------------- Hartmut · 2020-06-10 16:07 Thanks a lot to rvk and PascalDragon for your additional informations. It helps me to understand how things work under the hood and to learn. @PascalDragon: do you have the open answer to reply #11 in https://forum.lazarus.freepascal.org/index.php/topic,49670.0.html still on your list? I would be very happy if you could find the time to check this question definitely :-)