Why if I skip object creation, my code is still working ?
AbUnzipper1.FileName := 'C:\MyFile.zip'; never raises an access violation at runtime.
This (simplified) code is from a DLL, without form to put components on.
uses AbArcTyp, AbUnZper,...
...
var
AbUnZipper1: TAbUnZipper;
begin
// AbUnZipper1 := TAbUnZipper.Create(nil); COMMENTED !!!!!!!!
AbUnzipper1.FileName := 'C:\MyFile.zip';
AbUnzipper1.BaseDirectory := 'C:\temp\MyFolder';
AbUnzipper1.ExtractOptions := [eoCreateDirs, eoRestorePath];
AbUnzipper1.ExtractFiles('*');
AbUnZipper1.Free;
end;
With your code AbUnZipper1 is an undefined reference (i.e. a pointer). Behaviour when you access it is undefined. It may point to valid memory, or it may point to invalid memory.
If the latter, when you attempt to use the reference you will encounter a runtime error, an access violation. If the former, then you will succeed in accessing the memory, but that memory belongs to something else in your program and you will corrupt it.
If your program runs without error then that is by chance. You will likely have corrupted memory elsewhere along the way.
Your code is wrong, and you should fix it by restoring AbUnZipper1 := TAbUnZipper.Create(nil).
You should also make sure that you use try / finally in this code to avoid memory leak in case of exceptions.
Related
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.)
There is an example which illustrates my question:
procedure Test;
begin
try
ShowMessage('1');
raise EWarning.Create(12345, 'Warning: something is happens!');
ShowMessage('2');
except
on E: EWarning do
if IWantToContinue(E.ErrorCode) then
E.SkipThisWarning // shows '1' then '2'
else
E.StopExecution; // shows '1'
end;
end;
function IWantToContinue(const ErrorCode: Integer): Boolean;
begin
//...
end;
I tried to use something like this:
asm
jmp ExceptAddr
end;
but it's wont' work...
Any ideas?
Thanks.
No, it is not possible:
There are two kinds of exceptions: logic exceptions that are raised by the programmer using the raise command, and external exceptions that are initiated by the CPU for various conditions: division by zero, stack overflow, access violation. For the first kind, the logic exceptions, there's nothing you can do because they're part of the application "flow". You can't mess with the flow of 3rd party code, you can't even mess with the flow of your own code.
External exceptions
Those are normally raised as a consequence of running a single CPU instruction, when that instruction fails. In Delphi those are made available as EExternal descendants. The list includes access violations, division by zero, stack overflow, privileged instruction and not-so-many others. Theoretically, for some of those exceptions, the causing condition of the exception could be removed and the single CPU instruction retried, allowing the original code to continue as if no error happened. For example SOME access violations might be "fixed" by actually mapping a page of RAM at the address where the error occurred.
There are provisions in the SEH (Structured Exception Handling) mechanism provided by Windows for dealing with such retry-able errors, and Delphi is using SEH under the hood. Unfortunately Delphi doesn't expose the required elements to make this easily accessible, so using them would be very difficult if not impossible. None the less, for particular types of EExternal errors, smart Delphinians might attempt writing custom SEH code and get things working. If that's the case, please ask a new question mentioning the particular type of error you're getting plus the steps you'd like to take to remove the error condition: you'll either get some working code or a customized explanation of why your idea would not work.
Logic exceptions initiated through the use of raise
Most exceptions fall into this category, because most code will check it's inputs before doing potentially dangerous low level stuff. For example, when trying to access an invalid index in a TList, the index would be checked and an invalid index exception raised before attempting to access the requested index. Without the check, accessing the invalid index would either return invalid data or raise an Access Violation. Both of those conditions would be very hard to track errors, so the invalid index exception is a very good thing. For the sake of this question, even if code were allowed to access an invalid index, causing an Access Violation, it would be impossible to "fix" the code and continue, because there's no way to guess what the correct index should be.
In other words, fixing "logic" exceptions doesn't work, shouldn't work, and it's insanely dangerous. If the code that raises the error is yours then you can simply restructure it to NOT raise exceptions for warnings. If that's not your code, then continuing the exception falls into the "insanely dangerous" category (not to mention it's technically not possible). When looking at already written code, ask yourself: would the code behave properly if the raise Exeption were replaced with ShowMessage? The answer should mostly be "NO, the code would fail anyway". For the very rare, very wrong case of 3rd party code that raises an exception for no good reason, you may ask for specific help on patching the code at run-time to NEVER raise the exception.
Here's what could be in some 3rd party code:
function ThirdPartyCode(A, B: Integer): Integer;
begin
if B = 0 then raise Exception.Create('Division by zero is not possible, you called ThirdPartyCode with B=0!');
Result := A div B;
end;
It should be obvious that continuing that code after the exception is not going to allow stuff to "self heal".
Third party code might also look like this:
procedure DoSomeStuff;
begin
if SomeCondition then
begin
// do useful stuff
end
else
raise Exception.Create('Ooops.');
end;
Where would that code "continue"? Quite obviously not the "do usefull stuff" part, unless the code is specifically designed that way.
Those were, of course, simple examples only scratching the surface. From a technical perspective, "continuing" after an exception as you're suggesting is a lot more difficult then jumping to the address of the error. Method calls use stack space to set up local variables. That space was released in the process of "rolling back" after the error, on the way to your exception handler. finally blocks were executed, possibly de-allocating resources where needed. Jumping back to the originating address would be very wrong, because the calling code no longer has what it expects on stack, it's local variables are no longer what they're ment to be.
If it's your code that's raising the exception
Your code can easily be fixed. Use something like this:
procedure Warning(const ErrorText:string);
begin
if not UserWantsToContinue(ErrorText) then
raise Exception.Create(ErrorText);
end;
// in your raising code, replace:
raise Exception.Create('Some Text');
// with:
Warning('Some Text');
AFAIK no. You have to restructure your code to something like
procedure Test;
begin
ShowMessage('1');
try
raise EWarning.Create(12345, 'Warning: something is happens!');
except
on E: EWarning do
if IWantToContinue(E.ErrorCode) then
// shows '1' then '2'
else
raise; // shows '1'
end;
ShowMessage('2');
end;
In C++, it is possible using an SEH __try/__except block whose __except expression evaluates to EXCEPTION_CONTINUE_EXECUTION.
In Delphi, it is not possible to use SEH directly, AFAIK.
BASICally, your code wont work because ExceptAddr is a function, not a variable. So, your code snippet changes as follows:
{$O-}
procedure TForm1.FormCreate(Sender: TObject);
begin
try
OutputDebugString('entering');
raise Exception.Create('Error Message');
OutputDebugString('leaving');
except on E: Exception do
begin
OutputDebugString(PChar(Format('ExceptAddr = %p', [ExceptAddr])));
asm
CALL ExceptAddr
JMP EAX
end;
end;
end;
end;
..you could nest multiple try-except and determine if continue inside each exception:
try
try
some code
except
ret := message('want to continue?');
if ret <> mrYes then
exit;
end;
some other code to perform when no exception or user choose to continue
except
etc..
end;
Greetings
the following code gives a error:
"Project prKlanten.exe reaised exception class EAccessviolation with message 'acces violation at address 004d7767 in module 'prKlanten.exe'.
Read of address 0000005C."
procedure TfmOrder.FormCreate(Sender: TObject);
begin
dm.atDier.Open;
while not dm.atDier.eof do
begin
cbKeuze.Items.Add(dm.atDier['Diernaam']);
dm.atDier.Next
end;
dm.atDier.Close;
end;
Anyone know why? If you need more information feel free to ask.
[edit]
For more information see jasper's post
I bet that dm hasn't been created yet.
I bet that cbKeuse.Items hasn't been created yet.
I bet that dm hasn't been created yet.
Change this bit and run again:
[snip]
begin
Assert(dm<>nil);
Assert(dm.atDier<>nil);
dm.atDier.Open;
while not dm.atDier.eof do
[snip]
Do the assertions pass?
Access violations are caused when you access memory that is not yours such as:
using a nil pointer
buffer overrun
accessing an uninitialized pointer/object
accessing a freed pointer/object
Possible sources of access violations (or illegal/uninitialized pointers):
dm is not set
dm.atDier is not set
cbKeuze is not set
dm.atDier['Diernaam'] fails
In order to give more help, we need to known which line caused the access violation.
Addition
Does the AV also happens if you move the code to a temporary OnClick of a temporary button? In other words, remove the code from the OnCreate as you can't be 100% sure the dm is fully operational. The OnActivate is another possibility, but you need to check for second activation.
I'm developing as small diabetes program using Delphi 5 and ADO. I do a little query like this:
function GetLowestGlucoseLevel(StartDate:string;EndDate:string): Integer;
var
Q:TADOQuery;
begin
try
Q:=TADOQuery.Create(Application); //Separate unit, owner set to App
Q.Connection:=dtMod.ADOCon;
Q.DisableControls;
Q.Close;
Q.SQL.Clear;
Q.SQL.Add('SELECT Min(qGlucose.Glucose) AS MinOfGlucose from qGlucose');
Q.Parameters[0].Value:=StartDate;
Q.Parameters[1].Value:=EndDate;
Q.Open;
Result:=Q.FieldByName('MinOfGlucose').AsInteger;
Q.Close;
finally
Q:=nil;
Q.Free;
end;
end;
The query runs OK and returns the result as expected. However, when I checked Windows Task Manager, memory usage keeps on rising rather than decreasing after the query.
How to fix this?
Thanks!
You are leaking the TADOQuery by first setting it to nil, and then calling Free on the nil variable (which does nothing)
Did you install Delphi 5 updates? The
RTM ADO implementation is known to
have issues.
Use FastMM4, it should work with
Delphi 5 as well, and tell you more
about where the leaks are.
The Delphi way:
function GetLowestGlucoseLevel(const StartDate:string; const EndDate:string): Integer;
var
Q:TADOQuery;
begin
Q:=TADOQuery.Create(nil); //(nil) because local use only. Placed before try\finally block
//because if it fails to .create then there would be no object to
//.Free
try
Q.Connection := dtMod.ADOCon;
//------can erase these------
//Q.DisableControls; //No controls attached so unnecessary
//Q.Close; //Q is local and was never opened so no need to close
//Q.SQL.Clear; //Q is local and was never filled so no need to clear
Q.SQL.Add('SELECT Min(qGlucose.Glucose) AS MinOfGlucose from qGlucose');
Q.Parameters[0].Value:=StartDate;
Q.Parameters[1].Value:=EndDate;
Q.Open;
Result := Q.FieldByName('MinOfGlucose').AsInteger;
Q.Close;
finally
Q.Free;
//Q := nil //not needed because Q's scope is local
end;
end;
Quote:
finally
Q:=nil;
Q.Free;
end;
You're kidding, right? First nil the variable, then free it? You're a genius! :-)
Use:
finally
Q.Free;
Q:=nil;
end;
Or don't even bother assigning nil to it, since Q is a local variable...
But rereading your code, I notice you use Application as owner. As a result, it will not really be a leak, since it will be freed when the application is freed. If you use a form, it would be freed when the owner form gets freed. What you should try is to call this query about 100.000 times to check if it keeps reserving memory or if it's just increasing memory until a certain size has been reached. The latter is more likely, since the memory is reserved for future ADO calls.
As others pointed out, the finally section should have the 2 statements reversed, like so:
finally
Q.Free;
Q:=nil; // <- not even necessary since this is a local var
end;
Or you can call SysUtils.FreeAndNil(Q) (if that is available in Delphi 5, not sure).
Beside that, the TaskManager is an awful instrument to determine memory usage anyway. You might release the memory for Q, but that does not automatically mean the Delphi memory manager releases the memory to the OS.
Aside inversing the lines as Arjan, jasonpenny and WorkShop Alex said, you can use Process Explorer to see the real consumption of memory (Private Bytes) of the process. Task Manager is not really suited to this task, as it only shows the working set of the process.
We are using a class called ODNCServer here - at initialization, an TAutoObjectFactory object is created:
initialization
pAutoObjectFactory := TAutoObjectFactory.Create(ComServer, TODNCServer, Class_ODNCServer, ciSingleInstance, tmApartment);
Now FastMM is complaining about a memory leak because this object isn't freed anywhere. If I add a finalization statement like this
finalization
if assigned(pAutoObjectFactory) then
TAutoObjectFactory(pAutoObjectFactory).Free;
then the object is freed, but after the FastMM dialog about the memory leak pops up, so actually, the OS seems to be unloading the DLL, not the program. Instances of ODNCServer are created like this
fODNCServer := TODNCServer.Create(nil);
//register into ROT
OleCheck(
RegisterActiveObject(
fODNCServer.DefaultInterface, // instance
CLASS_ODNCServer, // class ID
ACTIVEOBJECT_STRONG, //strong registration flag
fODNCServerGlobalHandle //registration handle result
));
and freed like this:
if ((assigned(fODNCServer)) and (fODNCServerGlobalHandle <> -1)) then
begin
Reserved := nil;
OleCheck(RevokeActiveObject(fODNCServerGlobalHandle,Reserved));
fDTRODNCServerGlobalHandle := -1;
end;
FreeAndNil(fODNCServer);
So, does anybody know what I have to change to get rid of that memory leak? By the way, I also tried using FastMM's RegisterExpectedMemoryLeaks to register and ignore the leak, but this doesn't seem to work. Additionally, even if, it would just be a workaround and I'd like to know the right way to do this.
Don't worry about it. It's not a "leak" in the strict sense. Yes you are creating an object that is never free'd, but the keyword is "an". Singular.
Your application/DLL will not "leak" memory in the sense that it will create numerous instances of these objects,continually increase it's memory use. Furthermore the memory used by that single factory object (and others like it) will be cleaned up when the process terminates anyway.
If you showed the code you are using to call RegisterExpectedMemoryLeak() it might be possible to determine why it is not working your specific case.