Create failed for form containing a TChart lazarus / 55-TAChart ---------------------------------------------------------------------- mtanner · 2017-11-09 15:07 I have been having a problem where TabcForm.Create(Main) fails, and it seems to occur only when the form contains a TAChart. Some form with TCharts do create ok, but I cannot see what is different between the TCharts of the forms that fail/work. ---------------------------------------------------------------------- howardpc · 2017-11-09 16:00 You need to share a (simple) compilable project that shows the problem. ---------------------------------------------------------------------- Noodly · 2017-11-09 16:13 Probably unrelated, but worth a look. Have you read this bug report and the workaround for it in the TaChart Legend demo; https://bugs.freepascal.org/view.php?id=19632 Code: [Select]procedure TForm1.FormCreate(Sender: TObject);   procedure PrepareData;   const     XMIN = -10;     XMAX = +10;     N = 30;   var     i: Integer;     x, y: Double;     ynorm: Double;     c: TColor;   begin     // Create some data and store in series' internal listsource     for i:=0 to N-1 do begin       x := XMIN + (XMAX - XMIN) * i / (N-1) + (random - 0.5) * 0.5;       y := exp(-0.2*sqr(x)) + (random-0.5) * 0.1;       GradientLineSeries.AddXY(x, y);     end;     // Here we define the value range mapped to the gradient     FGradientMinValue := GradientLineSeries.ListSource.Extent.a.y;     FGradientMaxValue := GradientLineSeries.ListSource.Extent.b.y;     // Colorize the data points     for i:=0 to N-1 do begin       y := GradientLineSeries.ListSource.Item[i]^.Y;       ynorm := (y - FGradientMinValue) / (FGradientMaxValue - FGradientMinValue);       c := InterpolateRGB(START_COLOR, END_COLOR, ynorm);       GradientLineSeries.ListSource.Item[i]^.Color := c;     end;   end; var   li: TLegendItem; begin   // Workaround for issue #19632   Chart1FuncSeries1.Legend.OnCreate := @Chart1FuncSeries1LegendCreate;   FItems := Chart1.GetLegendItems;   Chart1.Legend.SortItemsByOrder(FItems);   for li in FItems do     cbSeries.AddItem('', nil);   // Prepare data for chart with owner-drawn legend   PrepareData; end; ---------------------------------------------------------------------- mtanner · 2017-11-09 16:27 To HowardPC. I have been trying to build a simple example. Trouble is I cannot reproduce the work/fail behaviour in an example - ( it always works) (at least yet) only in my very large real apps. I'll keep woring onit, biut when I discover what casuses work/fail I will be welel on the way to at least a work-around. What I'm hoping fpor is that someone has some ideas that will narrow my search. ---------------------------------------------------------------------- engkin · 2017-11-09 18:44 Did you compare the TChart part in the LFM files of both working and non-working forms? ---------------------------------------------------------------------- wp · 2017-11-09 20:59 Quote from: mtanner on November 09, 2017, 04:07:10 pmI have been having a problem where TabcForm.Create(Main) fails, and it seems to occur only when the form contains a TAChart. What is TabcForm, what is Main? And before I check too many combinations: what is your Lazarus/fpc version? Which operating system? Which bitness of OS and Lazarus/fpc? And what exactly is the "problem"? Some time ago we had the problem that charts crashed if the unit TATools was not on the uses list because this unit initializes a function pointer needed by the chart for built-in zooming/panning. This has been fixed in trunk, I don't know if it has been backported to version 1.8RC. Could you make sure that the unit TATools is listed in the uses clause of the unit in which the Chart resides? If not, just add the unit and check if the crash persists. ---------------------------------------------------------------------- mtanner · 2017-11-09 22:14 To WP. TabcForm is the class name for the form containing the chart. Main is the name of the main form of the app. I just did a quick test, adding TATools to the interface uses list for the unit defining the form that contains the chart. Not completely successful, but an improvement. Without TATools the program just hangs in the TabcForm.Create(Main) call or gives an error message without displaying the form at all. With TATools the form appears, and it looks as if the chart has been partly built, but then I get an "access errror" message. I'll take a closer look tomorrow and report more detail. ---------------------------------------------------------------------- wp · 2017-11-09 22:30 And which versions do you use? ---------------------------------------------------------------------- mtanner · 2017-11-10 14:19 I am beginning to think this problem is about strings. The unit TAChartAxisUtils has H+, so that in the declaration of TChartAxisMarkToTextEvent          String means AnsiString If I have a form unit with {$mode objfpc) and no H command then          String means shortstring and an OnMarkToText procedure declaration generated by the form designer will then be declared effectively with a shortstring parameter in which the user modified text will be returned. So presumably the TChart code will take this shortstring returned value and try and use it, effectively typecast it, into an ansistring. So an access error is unsurprising. If this analysis is correct then the problem is not one of creating the form, it is becaus I have changed the mode statement. The lesson is, I suppose, allways use $H+ at the top of form units, and be aware that TChart parameters should be assumed to be ansostrings. (I have allways used shortstrings in my apps. They are sufficient for my purposes, and don't give heap reference difficulties. Seems I no longer have that luxury of string simplicity.) ---------------------------------------------------------------------- wp · 2017-11-10 15:16 Quote from: mtanner on November 10, 2017, 03:19:18 pmIf I have a form unit with {$mode objfpc) and no H command then          String means shortstring Not necessarily. There's a checkbox in the project options in which you can make the {$H+} the default. Quote from: mtanner on November 10, 2017, 03:19:18 pm(I have allways used shortstrings in my apps. They are sufficient for my purposes, and don't give heap reference difficulties. Seems I no longer have that luxury of string simplicity.) A bad idea (strings limited to 255 chars, predefined string length, no UTF8, probably lots of encoding conversion issues). Never had any "heap reference difficulties" - what do you mean? Quote from: mtanner on November 10, 2017, 03:19:18 pmIf this analysis is correctSimply turn on the {$H+} option in the project options (page "Compiler options" > "Parsing" > "Use Ansistrings"), rebuild your project, and you'll see if it solves your issues. ---------------------------------------------------------------------- mtanner · 2017-11-10 16:10 The disadvantages of shortstrings that you mention are not a problem in the kind of apps that I write. An advantage of shortstrings is that when part of record you know the length, and that the field is contiguous with other fields of the record, not a pointer to something on the heap. So if I want to pass a pointer to a record containing strings to a procedure possibly in a dll, then the dll procedure does not have to access the callers heap to get the string data. I appreciate that shortstrings are a bit limited, but they are also simpler and straightforward.  I am told by some associates, not my first-hand knowledge,  that this can get especially complicated when you write aDLL in Lazarus to be used by a NET language. because of "data marshalling". Useful to know about the global H+ setting. ---------------------------------------------------------------------- mtanner · 2017-11-10 16:14 If, for some strange reason or carelessness, I have H+ set globally, I would assume that H- in a particular module would override the global setting for that module? ---------------------------------------------------------------------- mtanner · 2017-11-10 16:17 The "use ansistrings" option is  checked already. ---------------------------------------------------------------------- wp · 2017-11-10 17:29 I think my statements on the global {$H+} are not correct. Create a new LCL project; by default, it has {$H+}. In the header of the unit there's the directive {$mode objfpc}{$H+}Add a label and this code for the OnCreate event of the form: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 procedure TForm1.FormCreate(Sender: TObject);var  s: String;begin  s := 'Hello world';  Label1.Caption := IntToStr(SizeOf(s));end; By default, the label displays the value 4 on my system - this is the size of a pointer on a 32-bit program. As expected because we have H+.Then remove the {$H+}. The label displays 256, the default size of a shortstring. The 256 tells that we effectively  have H- (this is the default if mode objfpc is used). If my statement would have been correct, I'd have expected to see 4 again! Remove also the {$mode objfpc}. Now I see 4 again! This means: the default value of "use ansistrings" is used only if there is no mode directive. ---------------------------------------------------------------------- mtanner · 2017-11-13 08:47 I shall summarise the problem and solution. (I would suggest the moderator deletes all the intermediate comments). I had a problem with creation of forms failing. The TMyForm.Create(Owner) would crash, with no traceable info or other clues as to why.  The create failed on only some forms in my app, and not at all in another similar app. My apps use a table of data structures, with each data structure having an associated form that can be created/hidden/deleted by the user, together with a TTreeView having a node for each data structure, and showing a hierarchical relationship between the data structures. Selecting a node in the TTreeView will automatically create the corresponding form if not already in existence and show it. So I wasted a lot of time checking for problems such as referencing data structures or tree nodes that did not actually exist, but found no such bugs. Also spent time re-definng forms to ensure the LFM files were ok. Eventually I realised that all forms that failed to create contained a TAChart, though some forms with a TAChart did create ok. (I am sure most of you will empathise with the huge relief on finally finding a solid and consistent lead on a bug!). Further systematic trial-and- error showed that failing forms had an OnMarkToText method, successful forms did not. I tried deleting the OnMarkToText method reference in the form designer, then adding it by code in the FormCreate method for the form.  This revealed the true source of the problem. I had been "tidying up" my code, and that included removing all $mode and $H compiler commands, my thinking being just to go with Lazarus defaults. It is important to note that the TAGraph modules are themselves Pascal code compiled with Lazarus, and subject to $mode and $H commands. TAGraph uses {$H+}, and also specifies a parameter (for returning the text to be displayed) to OnMarkToText as "string". With H+ this means the parameter is treated as ansistring. My form code, without $mode or $H+ seems to treat "string" as shortstring, despite the global compiler option being  to default to H+. The compiler does not detect a type erro in these circumstances, presumably because both specifications are "string". On making sure all my form units (and all the others too for consistency) have {$mode objfpc}($H+} the problem goes away. TATools was mentioned, and adding that to the form units did seem to allow the creation to get further, such that the form was displayed, and the graph partly displayed, before getting an access error. So I am leaving TATools in the uses statements, though I do not understand why that makes a difference. I would tentatively suggest that it could be better for the TAGraph code to explicitly designate parameters as "ansistring" rather than the ambiguous "string", though there may be good reasons for specifying string which I do not realise. My conclusions:   Do not mess with the Lazarus defaults of {$mode objfpc}($H+} without good reason and serious thought.   Do not trust the global compiler H+ setting.   Br grateful for the forum support when in the depths of an intractable and obscure bug. ---------------------------------------------------------------------- wp · 2017-11-13 09:46 Quote from: mtanner on November 13, 2017, 09:47:28 amI would tentatively suggest that it could be better for the TAGraph code to explicitly designate parameters as "ansistring" rather than the ambiguous "string", though there may be good reasons for specifying string which I do not realise.Absolutely not.  It would limit applicability of TAChart. Quote from: mtanner on November 13, 2017, 09:47:28 amMy conclusions:   Do not mess with the Lazarus defaults of {$mode objfpc}($H+} without good reason and serious thought.   Do not trust the global compiler H+ setting.   Br grateful for the forum support when in the depths of an intractable and obscure bug. I would add a forth one: Don't play with the string type. If you need shortstrings for some reasons (and your case is a valid one) then you should mark them as such (var s: String[255]) instead of modifying the general string type. As much as possible, avoid all the string variations such as ansistring, utf8string, unicodestring, widestring unless you absolutely know what your are doing. Plain old "string" will cover more than 90% of all programming needs. ---------------------------------------------------------------------- mtanner · 2017-11-13 11:31 Just to add a minor clarification. I was not messing with string types, I would not dream of redefining a system-defined data type.  The confusion over string types arose because, inadvertently, the H+ option became ineffective in my form units. That led to a conflict in the definition of OnMarkToText parameters, and an access error, not actually during the Create for the form, but in the call to OnMarkText when the chart was created. I do always explicitly specify shortstring when that is what I want.  I never use "string", partly because the string variables I need to define myself are, so far, well enough handled as shortstring, and partly because ambiguity in meaning of definitions makes me nervous. ---------------------------------------------------------------------- Mike.Cornflake · 2017-11-13 16:10 Marathon effort, well done on finally nailing it! :-) Many thanks for the information sharing as well.