I'm trying to use TChart component to export a graph to a bitmap file, but i got to a very odd error.
Everytime i try adding a value to the chart, i call the AddY function from the TLineSeries component passing the value as a parameter. When i try adding small values, the graph behaves completely normal as shown:
The problem happens when i try adding some big value at it's first point, i get an access violation like this:
Project Project1.exe raised exception class EAccessViolation with message 'Access violation at address 00450047 in module 'Project1.exe'. Write of address 022AC000'. Process stopped. Used Step or Run to continue.
And the callstack is completely empty, the only line there is the name of my project, and the highlighted line is the "end" of my dpr file.
As i keep pressing ok other erros pops up:
As i'm not skilled enough with those errors, i didn't manage to debug exactly what's going on, i presume its some sorte of corrupted heap, but i can't seem to find out where, since the code is so simple and the component has it's code hidden because it's a third party component.
The funny part is that if i just click ok until the errors are gone and add another value, it shows normally, as well as adding some small value first, and then a big value (by big i don't know exactly the number, but 2000+ starts happening this)
I isolated the TChart from my main software and build a snippet just to test things out, and here's the simple code i'm running:
procedure TdesktopForm.Button1Click(Sender: TObject);
var
vBMP: TBitmap;
begin
vBMP := TBitmap.Create();
try
config();
c.series[0].AddY(StrToFloat(edit.text));
c.PaintTo(vBMP.Canvas.Handle, 0, 0);
vBMP.SaveToFile('D:\test.bmp');
finally
vBMP.Free();
end;
end;
Where c is my visually added TChart component, edit is just a visual TEdit to manipulate what i'm adding and config is a method to configure visual stuff on the graphic with the following code:
procedure TdesktopForm.config();
begin
c.Height:=200;
c.Width:=200;
c.LeftAxis.LabelsFont.Size:=13;
c.BottomAxis.LabelsFont.Size:=13;
c.MarginBottom:=20;
c.MarginTop:=10;
c.LeftAxis.StartPosition:=10;
c.LeftAxis.EndPosition:=90;
c.BottomAxis.LabelsAngle:=0;
c.BottomAxis.Grid.Color:=clBlack;
c.LeftAxis.Grid.Color:=clBlack;
c.BottomAxis.Ticks.Color:=clBlack;
c.LeftAxis.Ticks.Color:=clBlack;
c.LeftAxis.MinorTicks.Color:=clBlack;
c.BottomAxis.MinorTicks.Color:=clBlack;
c.BottomAxis.LabelsMultiLine:=True;
c.BottomAxis.DateTimeFormat:='DD/MM/AAAA hh:mm:ss';
c.BottomAxis.StartPosition:=10;
c.BottomAxis.EndPosition:=90;
c.View3D:=False;
c.Color:= clWhite;
c.Legend.Visible:=False;
c.Series[0].Marks.Visible:=true;
c.Series[0].Marks.Transparent:=false;
c.Series[0].Marks.Style:= smsValue;
c.Series[0].Marks.Frame.Visible:=True;
c.Series[0].Marks.Frame.Color:=clBlack;
c.Series[0].Marks.BackColor:=clWhite;
c.Series[0].Marks.Font.Size:=12;
c.Series[0].Marks.Font.Color:=clBlack;
end;
If it wasn't strangely enough, all of the tests above was using the "Line series", if i try the same thing with Bar series for example, everything works completely normal, i tried adding some absurd values like 999999999 and no errors were raised.
All of the above was using Delphi 5, as my main software is built on Delphi 5.
Does anyone have an idea of what's going on?
Related
i know how to make Tstringlist onchange event , but what about Tstrings ? i dont want to use VCL like tmemo or something . can i do that ? is it possible to have this event on tstrings and do something when its changed ?
i tried to do something like this but got access violation
//on form show event
stringlist:= TStringList.Create;
stringlist.OnChange := HandleStringListChange;
//implantation
procedure TChatFo.HandleStringListChange(Sender: tObject);
begin
if stringlist.Text <> '' then
ProcessCommands(stringlist.Text);
stringlist.Clear;
end;
exception messages
Project Project1.exe raised exception class $C0000005 with message
'access violation at 0x6d40c92c: read of address 0x00000150'.
Project Project1.exe raised exception class EStringListError with
message 'List index out of bounds (5)'.
Project Project1.exe raised exception class EStringListError with
message 'List index out of bounds (5)'.
this tstringlist should work as command identifier i creat it with my thread
as example
type
TReadingThread = class(TThread)
protected
FConnection : TIdTCPConnection;
FLogResult : TStrings;
procedure Execute; override;
public
constructor Create(AConn: TIdTCPConnection; ALogResult: TStrings); reintroduce;
end;
ListeningThread := TReadingThread.Create( TCPClient, stringlist);
constructor TReadingThread.Create(AConn: TIdTCPConnection; ALogResult: TStrings);
begin
FConnection := AConn;
FLogResult := ALogResult;
inherited Create(False);
end;
procedure TReadingThread.Execute;
Var
strData : String;
begin
while not Terminated do
begin
try
strData := FConnection.IOHandler.ReadLn;
if strData <> '' then
begin
FLogResult.Add( strData );
end;
except
on E: Exception do
begin
FConnection.Disconnect(False);
if FConnection.IOHandler <> nil
then FConnection.IOHandler.InputBuffer.Clear;
Break;
end;
end;
Sleep(10);
end; // While
end;
if i use Tmemo no errors or exception happened.
We're all shooting in the dark here because you haven't provided all the relevant code. That said, the information you have provided has a lot of problems, and I can offer advice to help you solve it.
Debugging 101
Run your code through the IDE. When you get your exception, the debugger stops at the line that caused the exception.
This is usually the most important step in figuring out what went wrong. You have access to this information. Run your program and carefully look at the line that raised the error. You might be able to already figure what caused the error. If not, a other basic techniques can be applied to get more information:
Get values of objects and variables on the error line and other close lines. You can hover your mouse cursor to get tool-tips, or press Ctrl + F7.
You can examine the call stack of lines leading to the one that caused the error by double-clicking the previous line in the call-stack.
Put a breakpoint on the line before the error and re-run the app. The debugger will stop on that line and give you a chance to check values as explained earlier, but before the error happens.
Asking for help 101
Getting help from people is much more effective when you give them all the relevant information. For a start, the line of code where the access violation occurs would be extremely useful.... Tell us!
Give us real code.
Saying "I tried to do something like this" is not particularly useful. Please copy and paste exactly what you tried. If your code is different, your mistake might no longer be there.
Access Violations
You get an access violation in the following situations:
You forgot to create the object you want to use or didn't assign it to the appropriate variable.
You created the object but Destroyed or Freed it already before trying to use it again.
You changed the variable that was referencing the object.
You performed a 'hard-cast' (or unchecked typecast) from one type to an incompatible type.
The above are the basics. There some variations, and a few special edge cases, but these account for the vast majority of mistakes.
So using the above, that's what you need to check. If you had copy-pasted more of your code, we might be able to see what you did wrong.
NOTE: One shot-in-the-dark possibility is that you are destroying your string list in the wrong place. And perhaps the memo works because as a component dropped on the form, you're not trying to destroy it.
Stack overflow
Let's examine what happens in your OnChange event when for example a string is added:
The event fires.
Text is not empty.
So you call ProcessCommands
You then call Clear
The the end of Clear, Changed is called again.
Which fires your event again.
This time Text is empty, so you won't call ProcessCommands
But you do try to Clear the string list again...
This could go on forever; well at least until the call-stack runs out of space and you get a stack-overflow error.
Saved by the bell
The only reason you don't get a stack overflow is because Clear doesn't do anything if the string list is empty:
procedure TStringList.Clear;
begin
if FCount <> 0 then //You're lucky these 2 lines stop your stack-overflow
begin
...
FCount := 0; //You're lucky these 2 lines stop your stack-overflow
SetCapacity(0);
Changed;
end;
end;
I suggest you rethink how to solve you problem because code that leads to unintentional recursion is going to make your life difficult.
Working with threads
You really need to get a handle on the basics of programming before trying to work with threads. Multi-threaded programming throws in a huge amount of complexity.
I can already see a huge possibility of one potential mistake. (Though it depends what you're doing inside ProcessCommands.)
You modify your string list in the context of a thread.
This means your OnChange event handler also fires in the context of the thread. (The fact it's implemented on the form is irrelevant.)
If your ProcessCommands method does anything that requires it to operate on the main thread, you're going to encounter problems.
As a final consideration, I've noted quite a few beginner programmers completely miss the point that code starting a thread can finish before the thread does. E.g. (Going back to the topic on Access Violations.): If you're destroying your string list soon after creating your thread, your thread could suddenly throw an access violation when the object it had earlier is suddenly gone.
The likely explanation for your error is that you are modifying the list in its OnChange event handler. That is simply not allowed. An OnChange handler must not mutate the state of the object.
What is happening is that you have some other code, that we cannot see, that modifies the list, perhaps in a loop. As it modifies the list your event handler clears the list and then the calling code has had the rug pulled from underneath it.
Of course, I'm having to guess here since you did not show complete code. Perhaps the specifics vary somewhat but it seems likely that this is the root of your problem.
You'll need to find another way to solve your problem. With the information available we cannot suggest how to do that.
Looking at your updated code, there's no need for a string list at all. Remove it completely. Instead, where you do:
FLogResult.Add( strData );
do this:
ProcessCommands( strData );
TStrings is an abstract class, ancestor of TStringList. You can create an instance of TStringList and use it as a TStrings.
Well, here I am again, trying to resolve an old problem.
Briefly, I get an AV when I try to free a modal form which does not have any owner, and didnt have been freed before.
frmItensVenda := TfrmItensVenda.Create(nil);
frmItensVenda.vtipo := vtipo;
frmItensVenda.vcontrole := strtoint(edit1.Text);
frmItensVenda.Ednota.Text := Edit5.Text;
frmitensvenda.lbvend.Caption := combobox3.Text;
frmitensvenda.lbnome.Caption := combobox1x.Text;
frmItensVenda.limite := limite;
if label10.caption <> '' then
frmItensVenda.vcli := strtoint(label10.caption);
frmItensVenda.ShowModal;
Frmitensvenda.Close;
frmItensVenda.Free;
If I just activate it and then close(without doing a thing), no AV happens. Putting a break-point before the 'free' command, it shows me the variable inside the form if I put the mouse cursor on it.
But if I insert one item in the grid, using the breakpoint at the same place, when I move the cursor to the same line, it doesnt show the variables anymore, but says 'inacessible value'.
And if I proceed running the code, as the next line has the 'free' command I get an AV.
What makes me believe there is some piece of code on that procedure that is doing something unexpected to the code, but I can tell you that there is no 'free' or similar command to the form in question there.
My solution(temporary) was to just comment the '.free' command, but if I run MadException I got a memory leak when I close the application (hey, anything is better than this EAccessViolation thing for me right now..)
Any sugestions?
Ok, found the answer, finally.
The problem was a global array.
It was declared
vm1 : array[1..100] of currency;
but it was assigned a value at position 0.
To my despair, there was no error when the variable was assigned, just when I tried to free the form.
So simple when you find it.. (!!!)
Well, at least I figured it out. Thanks everyone for the support!
OP : frmItensVenda is a global variable automatic created(but not initialized).
I see you do frmItensVenda := TfrmItensVenda.Create(nil);
Look for Application.CreateForm(TfrmItensVenda, frmItensVenda); in your .dpr file.
If it is there you creating a new instance !
{$R *.RES}
begin
Application.Initialize;
Application.Title := 'AServer';
...
Application.CreateForm(TfrmItensVenda, frmItensVenda);
...
Application.Run;
end.
And yes, dynamic form management is a must really (expecially in large applications.).
At trouble with large Forms, part of my solution was to, as much as possible to create dynamic.
Only when they are needed and then free them straight after.
frmItensVenda := TfrmItensVenda.Create(nil);
frmItensVenda.ShowModal;
OP : My solution(temporary) was to just comment the '.free' command,
don't do that : use instead
frmItensVenda.Release;
The "Method Release" removes the form and frees its associated memory.
Release procedure;
description
With release, you can remove the form from memory.
Release is the form to be taken until after the execution of event handlers of the form and its child components is completed.
In all event handlers release should be used instead free to avoid access violations.
The cases where you need to use Release are times when you're in the middle of an event handler (eg, OnClick), where further processing after the event will have to access the form.
In that case calling Release instead posts a WM_RELEASE message which doesn't free the event until the event handler is done and control has returned to the message pump (ProcessMessages/Application.Run).
Reading the delphi help though, it is recommended that you use the release command.
As for the Release v.s Free method. My understanding is that "Release" is specific to forms, and allows form-related even-handlers to finish before freeing resources.
Whereas "Free" is a generic method of freeing an object from memory (so should also work with forms.)
I have a minimalist project with FireDac FDConnection & FDSqlQuery,
DataSetProvider and ClientDataSet, trying to access the example
Employee.FDB that came with the Firebird 2.5 package I downloaded
from SourceForge today.
Everything is set to the defaults as theycame off the palette apart from the database name in the FDConnection's pop-up Information tab and the FDQuery's Sql, which is set to select * from employee.
The FDQuery opens fine but as soon as I try
to open the CDS, in the IDE or running my app, I get an access violation.
This is all my code:
FDQuery1.Open;
Caption := IntToStr(FDQuery1.RecordCount); // this shows 42 on the form's caption
CDS1.Open; // AV here
So, the FDQuery opens ok but the CDS doesn't.
At run time, the exception occurs here:
function TCustomClientDataSet.CreateDSBase: IDSBase;
begin
CreateDbClientObject(CLSID_DSBase, IDSBase, Result);
Check(Result.SetProp(dspropANSICODEPAGE, DefaultSystemCodePage)); <-- Exception here
Check(Result.SetProp(dspropUTF8METADATA, NativeUInt(True)));
Check(Result.SetProp(dspropUTF8ERRORMSG, NativeUInt(True)));
end;
The exception msg is
Project FBTest1 raised exception class $C0000005 with
message 'access vioaltion at 0x0075d05b: read of address 0x00000000'.
In the IDE I get a similar exception if I try to set Active = True on the CDS,
which the message said occurred in DSnap200.Bpl.
The first time it happened at run-time I had some kind of "Incident Report" pop-up
offering to report it to Embarcadero. First time I've seen that.
If I substitute a SqlConnection and SqlQuery for the FDac components, I get
the same error.
So, I guess my question is, can a CDS be provoked into this behaviour simply by using default property settings for a project as simple as this one, i.e. did I miss a step, or is it likely an EMBA QC thing?
Solved! Thanks to Graymatter's suggestion to try Using MidasLib, I've got
to the bottom of the problem and fixed it so that the app now works using Midas.Dll. I'm posting this as an answer, rather than a comment, firstly because it's a bit too long for that, but, more importantly, the cause was actually rather strange and the solution may assist anyone else who runs into the problem.
First, I tried Using MidasLib, and the app ran fine, w/o the AV that the q is about.
So, reassured that the problem doesn't arise with the current MidasLib code, I went
back to trying to get the app to work with Midas.Dll. I checked the Midas.Dll
versions in the XE6 bin directory and \Windows\SysWOW64, and they were both as I
was expecting, 20.0.16277.1276 dated 16 June 2014.
Tracing into CheckDBlient in DataSnap.DSIntf and observing carefully, the penny
dropped and I realised what was happening:
procedure CheckDbClient(const CLSID: TGUID);
[...]
begin
[...]
if DbClientHandle = 0 then
begin
Size := 256;
SetLength(FileName, Size);
if RegQueryValue(HKEY_CLASSES_ROOT, PChar(Format('CLSID\%s\InProcServer32',
[GUIDToString(CLSID)])), PChar(FileName), Size) = ERROR_SUCCESS then
SetLength(FileName, Size) else
begin
[...]
end;
DbClientHandle := LoadLibrary(PChar(FileName));
This gets the path to Midas.Dll from the InProcServer key in its registration, and this
wasn't pointing to XE6's bin directory or SysWOW64, but to another location that was not
of my creation and which contained a version of Midas.Dll dating from 2007. Oddly, unlike a copy I have of the Dll dating from the D7 era, this Dll does not have version info on its property page but it has a creation date of 9 August 2002 and a file size of 351Kb.
So, once I'd found that, fixing the problem was as simple as renaming that Dll so that
the OS won't load it, and re-registering the version in SysWOW64.
Where the rogue Midas.Dll came from isn't clear, but it certainly arrived since
last week, because it isn't in the back-up I happen to have from last Thursday evening.
The only things I've installed since then are a handful of 3rd-party GUI utilities for
managing/accessing Access, IB and Firebird databases, so the culprit seems to have been
one of them.
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions concerning problems with code you've written must describe the specific problem — and include valid code to reproduce it — in the question itself. See SSCCE.org for guidance.
Closed 9 years ago.
Improve this question
In my execute block that gets called with a button click, I create a popup menu to appear where the button was clicked. It current appears properly, with a few items with one of them having a couple sub items. When this that runs once then calls the destructor, it is fine. But if I execute it twice (show the popup and click an item twice) then destruct, the application crashes. I think it is because I'm not freeing the popup correctly (which is declared as a private property).
procedure TPlugIn.Execute(AParameters : WideString);
var
i: Integer;
pnt: TPoint;
begin
GetCursorPos(pnt);
FPopup := TPopupMenu.Create(nil);
FPopup.OwnerDraw:=True;
FPopup.AutoHotkeys := maManual;
//SQL Upgrade
Item := TMenuItem.Create(FPopup);
Item.Caption := 'Database Install/Upgrade';
Item.OnClick := ShowItemCaption;
FPopup.Items.Add(Item);
//Language Folder
Item := TMenuItem.Create(FPopup);
Item.Caption := 'Language Folder';
Item.OnClick := ShowItemCaption;
FPopup.Items.Add(Item);
//Machines
Item := TMenuItem.Create(FPopup);
Item.Caption := 'Machines';
MachineItem := TMenuItem.Create(FPopup);
MachineItem.Caption := 'Sample Machine 1';
MachineItem.OnClick := ShowItemCaption;
Item.Add(MachineItem);
MachineItem := TMenuItem.Create(FPopup);
MachineItem.Caption := 'Sample Machine 2';
MachineItem.OnClick := ShowItemCaption;
Item.Add(MachineItem);
FPopup.Items.Add(Item);
Self.FPopup := FPopup;
FPopup.Popup(pnt.X, pnt.Y);
end;
In the ShowItemCaption procedure I just show the caption of that sender object. I haven't coded specific events yet. If it free the popup in the execute procedure, the popup doesn't appear anymore.
destructor TPlugIn.Destroy;
begin
inherited;
FPopup.Free;
//ShowMessage('freed');
end;
First of all, you have completely misdiagnosed your problem. As a result you haven't given the information that we need in order to give you a definite solution.
If I take the code you provided, and test it similar to your description: using one button to call the code in the Execute method, and another button to Free FPopup, I don't get an error. In fact you should try this yourself; you also shouldn't get an error; which means that the problem doesn't lie in the code you've provided.
However, that said: I can help you to better diagnose the problem, after which you may be able to solve it yourself - or at least give us better information to help you.
Also you do still have a number of mistakes in this code that need to be fixed - even if the mistakes don't cause your application to crash.
Let's start by diagnosing the real problem. Is your program really crashing or are you just getting an exception in the debugger? I ask, because it usually requires something a little more extreme to truly crash a Delphi application.
If you are just getting an exception, I suspect the debugger is taking you to the line FPopup.Free; (Note this is not the line where the problem is - the debugger usually takes you to the line after; which would mean the problem is somewhere in the inherited Destroy). Also you need to tell us the exception class and message you are getting.
Either way, even if your application really is crashing, it will almost always be preceded by an exception. And you need to pinpoint exactly where that exception is happening. To do so, you need to:
Run your application through the debugger.
Make sure the debugger is set to stop whenever an exception occurs.
Given that the exception may be happening inside the TPlugIn class, make sure that unit is not disabling debug information.
You might also need to set your project options to "Use Debug DCU's".
Do your test.
When you get your exception, remember the debugger will usually show you the line after the one that caused the exception. You now need to consider the problem line in conjunction with the error message to figure out what might be going wrong.
If you're getting an Access Violation, that is usually because you're trying to:
Use something that hasn't been created.
Destroy something that has already been destroyed.
Use something that has already been destroyed.
To investigate the Access Violation further:
Identify the problem object.
Put breakpoints in your code where the object is created/destroyed.
Run through your code, hitting the breakpoints and figure out what is going on.
Additional Problems
You mentioned that "if you Free the popup in the Execute procedure, it doesn't appear anymore". (Presumably this was your attempt to avoid the memory leak.) This is because when you call FPopup.Popup(pnt.X, pnt.Y); it doesn't "pause your code" and wait for an item to be selected. Your code continues running because menus use an event driven model to callback when the item is clicked. Therefore your popup menu would be destroyed, and disappear immediately after it popped up.
The line Self.FPopup := FPopup; is totally redundant and does nothing. You're effectively saying FPopup := FPopup - you're not changing FPopup's value in any way.
It should be very obvious that the title "Freeing the popup twice crashes application" is totally incorrect. As per your code and description: you are creating the popup twice and freeing it only once.
That in itself is a problem, because as Jerry pointed out - you have a memory leak. Basically your code overwrites the reference to the first TPopup you created, leaving it "orphaned" and holding onto memory. You then only Free/Destroy the last one created in the TPlugIn destructor.
And therein: free the popup before calling the inherited destructor of TPlugIn. It is not neccessary in this case, but normally it is wise to clean up in reverse order of creation.
There isn't (or at least shouldn't be) any need to re-create the popup every time Execute is called. All you should be doing is causing it to popup again with FPopup.Popup. This is in fact part of the point behind making FPopup a private class field. I.e. you set it up once and reuse it as needed.
You could use a technique called lazy-initialisation; but really it's usually an unnecessary complication. You're much better off simply mirroring your creation and destruction of FPopup. I.e. If you Destroy FPopup when TPlugIn is destroyed - you should Create FPopup when TPlugIn is created.
Im not sure if i have explaned this the best i can but, here we go...
I have 2 Custom components on a form, Which are link together at design time through the IDE. Whenever i call a procedure from on of the Component i get the Access violation,
Access violation at address 0049A614
in module 'Project2.exe'. Read of
address 00000034.
This is a small section of my code
TMyClient = class(TClientSocket)
{...}
end;
and...
TPresence = class(TComponent)
private
ftheClient: TMyClient
public
procedure SetStatus(status: string);
published
property UserName : string read fUserName write fUserName;
property theClient: TMyClient read ftheClient write ftheClient;
end;
procedure TPresence.SetStatus(status: string);
begin
try
***** if theClient = nil then
Exception.Create('theClient is Nil');
except
on e:Exception do
MessageDlg(e.classname+', '+e.message, mtWarning, [mbOK], 0);
end;
{...}
end;
0049A614 is at the *****, and the IDE stops here.
I Have also tried to do the assign at run time with
Presence1.theClient := MyClient1;
with no luck
using procedures from Presence1 or MyClient1 that do not rely on each other work fine.
Delphi 7
Follow Up:
from mghie comments, i rethought about it.
I removed the TPresence Component from the form (which caused some strange IDE errors, that might have had something to do with it) and created it design time, assigning everything that was needed. Now it works, but putting the TPresence Component back on the from brings the error back.
Thankyou for your help guys, i should be able to work this one out now, if i can't ill reopen another question :)
You seem to be thinking that the exception is raised because the client field of Presence1 is not set - if you do however get the exception "Read of address 00000034" it means that the Self pointer in the SetStatus() call is nil. That would indicate that you call SetStatus() on an unassigned TPresence reference. It is not really possible to tell the reason for that from the snippet you posted, but it should get you started debugging.
I would still advise you to write a proper setter method for all component references in your own custom components - first because you have a better hook when debugging such problems (you can set a breakpoint there), and second because you should always call TComponent.FreeNotification() on such linked components to be able to track their destruction and set the internal reference to nil.
We probably need more of your code. It is possible you are not correctly creating an instance of TPresence which would give you the error you are experiencing. Try to give us a simple as possible code snippet that causes your error.