What is an Access Violation
Every computer program uses memory for running. Memory is consumed by every variable in your program. It can be form, component, object, array, record, string or simple integer. Memory can be allocated automatically for certain types of variables (such as integer or static arrays), the other types require manual control of memory (for example, dynamic arrays). Essentially, from the point of operating system, each variable is characterized by its address (i.e. - location) and size.
Memory for global variables is allocated by OS loader when executable module is loading and it is freed when module is unloading. Global variables are those, which declared outside of class or any routine. The stack is used for allocating memory for local variables (which are declared in some function or procedure) and auxiliary data (such as return addresses or exception handlers). The heap is used for storing dynamic data (such as objects, dynamic arrays, strings, etc.).
Note, that for variables of dynamic types (such as dynamic arrays, strings, objects or components) - though the variable itself is stored in global area or stack, but its data is always allocated on the heap and it (often) require manual control.
Regardless of who allocates memory for the variable (you manually, or the compiler automatically), memory for each variable must be allocated before its using, and later (when the variable is no longer needed) it should be freed.
Sometimes there can be a situation, where your application trying to get access to certain memory location, which wasn't allocated or was already released - due to bugs in your code. When such things happens - the CPU raises an exception of class EAccessViolation. The usual text for this error is as follows: "Access violation at address XXX in module 'YYY'. Write/read of address ZZZ". Though there is the one simple reason for this kind of error, the real situations for it can be very different.
Looking for source code line of Access Violation
So, what should you do with access violation? Well, first you should try to identificate a source line in your code, where it appears.
If you are getting EAccessViolation while running under debugger:
A typical debugger's notification about access violation exception
Then you should just click "Break" (it is called "Ok" in older Delphi's versions) and the debugger will point you to source line immediately. Additionally you can take a look at call stack by choosing View/Debug Windows/Call stack from Delphi's main menu:
A typical call stack as displayed by IDE debugger
This window shows you a call stack - the trace of executing to current code's point. You should read this from top to bottom. The current location is marked by little blue arrow. You can also double-click on line to go to a particular location. For example, if you double-click on "Unit12.Test" line - debugger will show you location where exception was raised.
If you are using an exception tracer tool (such as EurekaLog) then there would be a bug-report instead of usual error message. You can see a call stack in the report (call stack view can differ due to different building algorithm):
A typical EurekaLog report about exception
You can see there the same information. And you also can double-click on lines to go to that locations in IDE code editor.
Okay, finding the error's location - this is only half of the case. Determination why there is an error in this line - it is the second half of the case.
Looking for the Access Violation's reason by analyzing the code
If you got an error while using debugger, then it is quite simple - you should place a breakpoint to your problem-line and check all variables and expressions in this line after breakpoint's hit - and here it is, the reason for access violation. Just use the debugger.
If there is only a bug-report - then you should use your telepathic abilities to find out the truth. Those psychic powers are comes with experience and we can help you a little with it - by giving you a list of most common mistakes, which can lead to EAccessViolation exceptions.
1. First, there are all kinds of errors of accessing an array's element outside of its borders. For example, the typical newbie's mistake can look like this:
So, if your problem line contains  - there is a good reason to validate your expression inside .
Usually, you should catch errors of this sort at development/testing stage by using "Range Check Errors" option. The point is that such errors are very dangerous, because they may go unnoticed, even more than that - they can destroy the stack, so that you can not get the location of the error. But more on this later.
2. All kinds of messing with arguments. Usually those are untyped parameters and buffer-overflow errors:
S1: array of Integer;
Stream.ReadBuffer(S1, 256); // this corrupts the S1 pointer
Stream.ReadBuffer(S1, 256); // this reads data into S1 array
FillChar(S2, Length(S2), 0); // this damages the S2 pointer
FillChar(Pointer(S2)^, Length(S2), 0); // this clears the S2 string by filling it with zeroes
Usually these errors are catched immediately upon function call. You should just examine a function's documentation to figure out what you did wrong. Check: what function expects to receive and what actually you give to it.
3. Passing data between modules. Well, newbies likes to pass data (especially String) between exe and DLL, without caring much about two different memory managers in modules.
These errors are usually detected at development time.
4. Wrong declaration of functions, which are imported from DLL. The most common mistake is wrong calling convention. If you are getting EAccessViolation just by calling a function from DLL - just carefully verify its declaration. Be sure, that its signature is correct and you didn't forget about stdcall or cdecl.
Though these errors usually detected at development stage, there can be cases, when wrong declaration will make it at production code.
5. Missing of proper synchronization, when working with threads. If you are using more than one thread in your application, then there can be troubles. For example, you can not access a VCL objects from another thread as VCL is not thread-safe - you should use Synchronize for this. Actually, the problem is encountered when one thread changes the data, which is used by another thread - and that becomes a complete surprise for the second thread.
Unfortunately, the problems with thread are the most complex ones. They are very hard to diagnose. The best you can do is to guarantee, that such things can not happen. If you are in doubt - place you code in synchronize or guard it by critical section, when working with shared variables. Sometimes programmer uses CreateThread instead of BeginThread or TThread and forgets about changing IsMultiThreaded variable.
6. Calling a function via invalid procedural variable. For example:
Lib1, Lib2: HMODULE;
Lib1 := LoadLibrary('MyDll.dll'); // one piece of code loads DLL. It can be in different thread
Lib2 := GetModuleHandle('MyDll.dll');
Proc := GetProcAddress(Lib2, 'MyProc'); // there is no checks! There can be no function named 'MyProc'
Proc; // Proc can be = nil -> there will be an Access Violation
FreeLibrary(Lib1); // some code unloads library
Proc; // though Proc <> nil, its code is no longer available
// that is why there will be an AV.
The whole case is very similar to the next situation.
7. Calling of methods or any other access of objects/components, which wasn't created yet or were already released. You should consider this reason if there is some object variables in your problem line of code. Especially, if you do a manual allocate or free of objects somewhere in your program.
The one part of the problem is that when you destroy an object, its variable is not cleared automatically - it continues to point at invalid memory location. The other part is that local variables are not initialized to zero and contains trash at function's call. The last part: there can be multiple reference to one object/component via different variables. Here are few examples:
All such memory access errors are dangerous as they may be unnoticed. For example, we can access a deleted object, but our memory manager still wasn't return memory to the system, so our access can be successful.
It's recommended to use FreeAndNil to destroy objects or (better yet) to use interfaces instead of objects - because interfaces are auto-managed types, which will be released automatically.
The situation with local arrays is even worse: the point is that local arrays are allocated in the stack, so there is large areas of available memory at its borders. To make things worse: this memory is heavily used by application (as oppose to the memory, which were released by the object destruction).
procedure TForm1.Button1Click(Sender: TObject);
It is very treacherous situation, isn't it? Depending on how we messed up with the array's index, we can get:
a). Application, which produces the correct results.
b). Application, which produces the wrong results.
c). Application, which raises an exception.
d). Application, which crashes.
To make things worse: the very same application can display any of the above behavior, depending on external conditions, such as OS and Delphi's version, user actions before error and so on.
That is why it is extremely important to use "Range Check Errors" option while you develop and testing your application.
Well, you can also enable it for production code, if you isn't sure that your testing was good enough.
So what exactly should we do with access violation? Well, we have a source line, so we should just look through above mentioned cases and try to apply them to our line of code:
There can be a great help if we can also use few hints from the data.
Looking for Access Violation's reason by analyzing the data
First, we can retrieve some useful information from error's message itself. Let's remember it:
Access violation at address XXX in module 'YYY'. Write/read of address ZZZ.
Okay, the address XXX points to exact location of code, where exception was raised. This is the same address, which is used by Delphi's debugger and EurekaLog to point you to your line of code. The executable module for this address is also displayed in the error message - as YYY. Usually it is your exe, DLL or some system/third-party DLL. Sometimes, however, there can be cases when XXX do not hold any meaningful value. For example, if there is no YYY in the message of if XXX looks suspicious (less then $400000 or greater than $7FFFFFFF on x86-32), then you definitely have problems either with stack corruption (for example, "c" item from the previous section), of call of invalid function (item 6 or, sometimes, 4 from previous section).
The next useful piece of information is "write" or "read" word. The "write" means that the exception occurred during writing, the "read" means that, well, the problem while reading (quite obvious, isn't it?). That means, that we only need to check write or read parts in the problem source line. For example, if the problem line is "P := W" then we should check P if there was "write" word and check W if there was "read" word in the error's message.
And the last hint comes from ZZZ. Actually, we do not care about exact value, but rather about if it is small or large. "Small values" are something like $00000000, $0000000A or $00000010. The "large values" are, for example, $00563F6A, $705D7800 and so on. So, if ZZZ is small - then your code tried to access an object via nil reference. If ZZZ is large - then your code tried to access an object via non-nil invalid pointer. In the first case you should check: why do you try to use nil pointer (or who is the bad guy, who set pointer to nil). In the second case you should search for bad guy, who released the object, but doesn't clear the variable itself.
Apart from error's message, there can be another information, which comes from assembly and CPU tabs in EurekaLog's bug-report.
You can see the assembly listing of your program on the "Assembler" tab. It is provided here only for convenience - that way you do not have to search it somewhere else. This is no additional information there. But on the "CPU" tab - you can see the status of CPU's registers, (part of) the stack and (part of) the memory at the moment of exception raising.
For example, we can look at the assembler listing and see that the problem line involves, say, EAX and EDX registers. We can check that EAX is 0 on CPU tab, which means that we are trying to assign value via nil pointer. Then we take a look at the line of source code, which we learned from the call stack, and we will know the name of the variable. And here's the reason for you: the variable, used in assignment, was = nil.
Of course, to work with this information you need a minimum knowledge of assembler, but it is a quite powerful tool.