How to track down access violation "at address 00000000" - delphi

I know how to create a .map file to track down access violation errors when the error message includes an actual address.
But what if the error message says
Access violation at address 00000000. Read of address 00000000.
Where do I start looking for the cause of this problem... ?

The accepted answer does not tell the entire story.
Yes, whenever you see zeros, a NULL pointer is involved. That is because NULL is by definition zero. So calling zero NULL may not be saying much.
What is interesting about the message you get is the fact that NULL is mentioned twice. In fact, the message you report looks a little bit like the messages Windows-brand operating systems show the user.
The message says the address NULL tried to read NULL. So what does that mean? Specifically, how does an address read itself?
We typically think of the instructions at an address reading and writing from memory at certain addresses. Knowing that allows us to parse the error message. The message is trying to articulate that the instruction at address NULL tried to read NULL.
Of course, there is no instruction at address NULL, that is why we think of NULL as special in our code. But every instruction can be thought of as commencing with the attempt to read itself. If the CPUs EIP register is at address NULL, then the CPU will attempt to read the opcode for an instruction from address 0x00000000 (NULL). This attempt to read NULL will fail, and generate the message you have received.
In the debugger, notice that EIP equals 0x00000000 when you receive this message. This confirms the description I have given you.
The question then becomes, "why does my program attempt to execute the NULL address." There are three possibilities which spring to mind:
You have attempt to make a function call via a function pointer which you have declared, assigned to NULL, never initialized otherwise, and are dereferencing.
Similarly, you may be calling an "abstract" C++ method which has a NULL entry in the object's vtable. These are created in your code with the syntax virtual function_name()=0.
In your code, a stack buffer has been overflowed while writing zeros. The zeros have been written beyond the end of the stack buffer, over the preserved return address. When the function later executes its ret instruction, the value 0x00000000 (NULL) is loaded from the overwritten memory spot. This type of error, stack overflow, is the eponym of our forum.
Since you mention that you are calling a third-party library, I will point out that it may be a situation of the library expecting you to provide a non-NULL function pointer as input to some API. These are sometimes known as "call back" functions.
You will have to use the debugger to narrow down the cause of your problem further, but the above possiblities should help you solve the riddle.

An access violation at anywhere near adress '00000000' indicates a null pointer access. You're using something before it's ever been created, most likely, or after it's been FreeAndNil()'d.
A lot of times this is caused by accessing a component in the wrong place during form creation, or by having your main form try and access something in a datamodule that hasn't been created yet.
MadExcept makes it pretty easy to track these things down, and is free for non-commercial use. (Actually, a commercial use license is pretty inexpensive as well, and well worth the money.)

You start looking near that code that you know ran, and you stop looking when you reach the code you know didn't run.
What you're looking for is probably some place where your program calls a function through a function pointer, but that pointer is null.
It's also possible you have stack corruption. You might have overwritten a function's return address with zero, and the exception occurs at the end of the function. Check for possible buffer overflows, and if you are calling any DLL functions, make sure you used the right calling convention and parameter count.
This isn't an ordinary case of using a null pointer, like an unassigned object reference or PChar. In those cases, you'll have a non-zero "at address x" value. Since the instruction occurred at address zero, you know the CPU's instruction pointer was not pointing at any valid instruction. That's why the debugger can't show you which line of code caused the problem — there is no line of code. You need to find it by finding the code that lead up to the place where the CPU jumped to the invalid address.
The call stack might still be intact, which should at least get you pretty close to your goal. If you have stack corruption, though, you might not be able to trust the call stack.

If you get 'Access violation at address 00000000.', you are calling a function pointer that hasn't been assigned - possibly an event handler or a callback function.
for example
type
TTest = class(TForm);
protected
procedure DoCustomEvent;
public
property OnCustomEvent : TNotifyEvent read FOnCustomEvent write FOnCustomEvent;
end;
procedure TTest.DoCustomEvent;
begin
FOnCustomEvent(Self);
end;
Instead of
procedure TTest.DoCustomEvent;
begin
if Assigned(FOnCustomEvent) then // need to check event handler is assigned!
FOnCustomEvent(Self);
end;
If the error is in a third party component, and you can track the offending code down, use an empty event handler to prevent the AV.

When I've stumbled upon this problem I usually start looking at the places where I FreeAndNil() or just xxx := NIL; variables and the code after that.
When nothing else has helped I've added a Log() function to output messages from various suspect places during execution, and then later looked at that log to trace where in the code the access violation comes.
There are ofcourse many more elegant solutions available for tracing these violations, but if you do not have them at your disposal the old-fashioned trial & error method works fine.

It's probably because you are directly or indirectly through a library call accessing a NULL pointer. In this particular case, it looks like you've jumped to a NULL address, which is a b bit hairier.
In my experience, the easiest way to track these down are to run it with a debugger, and dump a stack trace.
Alternatively, you can do it "by hand" and add lots of logging until you can track down exactly which function (and possibly LOC) this violation occurred in.
Take a look at Stack Tracer, which might help you improve your debugging.

Use MadExcept. Or JclDebug.

I will second madExcept and similar tools, like Eurekalog, but I think you can come a good way with FastMM also. With full debugmode enabled, it should give you some clues of whats wrong.
Anyway, even though Delphi uses FastMM as default, it's worth getting the full FastMM for it's additional control over logging.

Here is a real quick temporary fix, at least until you reboot again but it will get rid of a persistent access. I had installed a program that works fine but for some reason, there is a point that did not install correctly in the right file. So when it cannot access the file, it pops up the access denied but instead of just one, it keeps trying to start it up so even searching for the location to stop it permanently, it will continue to pop up more and more and more every 3 seconds. To stop that from happening at least temporarily, do the following...
Ctl+Alt+Del
Open your Task Manager
Note down the name of the program that's requesting access (you may see it in your application's tab)
Click on your Processes tab
Scroll through until you find the Process matching the program name and click on it
Click End Process
That will prevent the window from persistently popping up, at least until you reboot. I know that does not solve the problem but like anything, there is a process of elimination and this step here will at least make it a little less annoying.

Related

Error at Delphi : Dataset not in edit or insert mode

Right now I'm struggling to solve a bug that caused by the dataset mode at Delphi (using ADODataset),
details as below for the add button mechanism :
IDMain: =self.DBTextIDUser.Caption+'-'+self.DBEditWorkingDate.Text;
datamodule1.ADODataSetWorkingDetails.Append;
with datamodule1.ADODataSetWorkingDetails do
begin
dbgridworkinghours.Fields[0].AsString := IDMain;
dbgridworkinghours.Fields[7].AsString := self.DBTextIDUser.Caption;
dbgridworkinghours.Fields[8].AsString := self.DBTextName.Caption;
dbgridworkinghours.Fields[9].AsString := self.DBEditWorkingDate.Text;
dbgridworkinghours.Fields[11].AsString := self.DBTextPeriod.caption;
dbgridworkinghours.Fields[10].AsString := self.DBTextToday.Caption;
end;
I already set the adodataset to append mode at the save button :
datamodule1.ADODataSetWorkingDetails.post;
when I click the save button, an error appears that:
The adodataset not in edit/insert mode
I already used this mechanism at the other form and it works
note: I already tried setting the adodataset mode to insert, but still faced the same error
What #kobik said.
Your problem is most likely being caused by something you haven't told us in your q.
I think the important thing is for you to find out how to debug this sort of thing
yourself, so that even if you don't understand the cause, you can at least isolate
it and provide better information when you ask for help here. So I'm going to
outline how to do that.
In your Project Options, check the box "Use Debug DCUs"
Set up two event handlers, for your ADODataSetWorkingDetails's
AfterPost and AfterScroll events, put a some "do nothing" code in both of them
(to stop the IDE removing them). Put a debugger breakpoint on the first line
inside the AfterScroll handler, but not (yet) the AfterScroll one.
Compile and run your program.
You should find that somewhere after you call Append but before you click your
Save button, the debugger stops on your AfterPost breakpoint.
When it does,
go to View | Debug windows | Call stack. This will show you a list of
program lines, the one at the top being the one closest to where the breakpoint
tripped. This will likely be deep inside the VCL's run-time code (which is why
I said to check "Use Debug DCUs". Scroll down the list towards the bottom, and
eventually you should come to a line which is the cause of why Post was called.
If it isn't obvious to you why the AfterPost event was called, put a breakpoint
on your Append line and run the program again. When this breakpoint trips,
put another breakpoint inside your AfterScroll event, resume the program
by pressing F9 and see if the AfterScroll breakpoint is hit. If
it is, again view the Call stack and that should show you why it was called -
if it isn't obvious, then add the contents of tthe Call stack window to your q.
If the cause is obvious, then change your code to avoid it.
The reason I've gone on about the AfterScroll event is that what isn't obvious
is that when your code causes a dataset to scroll, any pending change (because
the dtaset is in dsInsert or dsEdit state will cause the change to be posted
and you will then got the error you've quoted if you try to call Post on the
dataset again. Calling Append initially sets a dataset into dsInsert state, btw.
See if you can at least identify what is causing your dataset to post before
it is supposed to, and let us know in a comment to your q or this answer.
Btw, I strongly recommend that you get out of the habit of using the with construct in your code. Although it may save you a bit of typing, in the long term it will likely make bugs far more likely to happen and far harder to find.
Update TDataSet and its descendants have a State property which is of type TDataSetState (see
DB.Pas). Normally, for browsing data and navigating around the dataset, the
dataset is in dsBrowse state. If you call Edit or Append (or Insert), the dataset
is temporarily put in dsEdit or dsInsert state, respectively. Various routines in DB.Pas
check the dataset state before certain operations are performed and raise an exception if the
DataSet in not in the correct state for the operation to go ahead. It is very, very likely
that it is one of these checks that is giving you the exception.
My original hunch was that your error was occurring because something was happening which
was causing Post to be called, because if Post succeeds, it puts the dataset back into
dsBrowse state, so when clicking your Save button calls Post, the dataset is already
in dsBrowse state. You can, of course, put a breakpoint in TDataSet.Post in DB.Pas
check which state the dataset is actually in when it is called.
There are two other main possibilities for the cause of your exception, namely that
either TDataSet.Cancel or the general Abort method is being called. To investigate
these, put breakpoints on the first lines inside TDataSet.Cancel (in DB.Pas) and
Abort (in SysUtils.Pas). If either of these breakpoints trips between you calling
Append and Post, then you can use the Call Stack view to try and figure
out why execution has reached there.

FastCode: studying its code

I'm studying the code of FastMM that change the address of the methods and function in run time. But I didn't understand some things.
http://koders.com/delphi/fid356C72C9C454FA74A916971690F624B0FF9111E5.aspx?s=pos
My doubt is on line 47: "if PBYTE(AStub)^ = $E8 then"
Why this comparison? Ok, to check if it's assigned or not. Am I wrong ? But why $E8(232) ?
If it's always the same value, wont it be always the same result ?
The rest of the code it's "ok" in my mind. But if some one could explain all of it I thank.
tks in advice.
That code returns the target address of a CALL instruction. The test is just to check that it really is a CALL instruction that has been passed to the routine. It's not fool proof because you could be passing the address of the middle of an instruction, or even something that isn't code.
So yes, the test should always succeed, if the function is called correctly.
These two routines are presumably used in tandem. First of all you get the address of the CALL instruction, and then the code at that site is replaced with a JMP ($E9).

How to get the Process Environment Block (PEB) from extern process?

I want to get the PEB from the "notepad.exe" process. Does someone know how to do it?
I tried the GetModuleHandle API, but it doesn't return a valid pointer (it return zero every time) because I have to be the caller process of that module.
For that reason, I want to know how to get it to work with EnumProcessModules or CreateToolhelp32Snapshot.
Matt Pietrek described how to do that in a 1994 Under the Hood column. It was about how to get the environment variables of another process, where the first step is to get a pointer to the PEB. To do that, he says, call NtQueryInformationProcess. The PROCESS_BASIC_INFORMATION structure it fills contains the base address of the PEB structure. (You'll need to use ReadProcessMemory to read it since the address will be in the context of the external process's address space, not yours.)
To call NtQueryInformationProcess, you'll need a handle to the process. If you started the process yourself (by calling CreateProcess), then you already have a handle. Otherwise, you'll need to find the process ID and then call OpenProcess. To get the process ID, search for the process you want with EnumProcesses or Process32First/Process32Next. (I prefer the latter because it provides more information with less work.)

madExcept, getting top of current stack

In an delphi 2007 application, we have used madExcept to handle exeptions for us. We use the standard dialog, letting the user send us a report with mail if he want to.
We want to add a little to this functionality. If the user choose not to send a full report, we still want to log the exception to a file, but not a full report. We want the exception class and message, and the source file and line number.
The first two is easy, but I can't find a straight forward way of finding the latter.
I have the full exception report in the form of an IMEException interface. As far as I can see, the call stack is available only through the BugReportSections property, which gives me the stack in plain text.
In madStackTrace.pas, there is a StackTrace( )-function that should give me what I want. It takes a lot of parameters, and the documentation is a little short on information..
Question:
Does anybody know the right parameters to feed into StackTrace( ), so that it only return the top item of the stack from the thread where the exception was thrown?
madStackTrace.pas has another function, StackAddrToStr that just takes a single address and returns the formatted text for it. If you're in the except block for the crashing thread you can pass the global ExceptAddr variable to it. If you're in one of the madExcept callbacks use `IMEException.ExceptAddr' instead.

Any way to find out which TField is raising EDBClient with message 'Field value required.'?

I have one TClientDataSet with several Fields and I'm getting this exception, I'm sure that I forgot to set one field value, but the question is, which Field?
Is there some way to find out quickly which field is raising this exception?
EDIT
I solved the problem, I was connecting the TClientDataSet to the wrong provider, which had a required field on the server but didn't have this field on the client.
But it still doesn't invalidate my question, I lost a lot of time to figure it out, and if I had some way to know which server side required field was raising this error it would be very quick to realise what was my mistake.
EDIT 2
Related QCs:
#5557 - Solved as "As Designed".
#54380 - Open
When the debugger notifies you of the exception, look at the call stack. Navigate to the routine that is iterating over the list of fields, and inspect the loop-control variable at that point to find out how far through the field list your program got before raising an exception.
If the loop-control variable isn't accurate, then set a breakpoint in that routine and re-run your program. Step through until you get the exception again.
Another option is to look at all the fields and see for yourself which one lacks a value.

Resources