Меню

Делфи ошибка access violation

I find that the really hard-to-find access violations don’t always occur while I’m running in a debugger. Worse yet, they happen to customers and not to me. The accepted answer mentions this, but I really think it should be given more detail: MadExcept provides a stack traceback which gives me valuable context information and helps me see where the code fails, or has unhandled exceptions (it’s not just for access violations). It even provides a way for customers to email you the bug reports right from inside your program. That leads to more access violations found and fixed, reported by your beta testers, or your users.

Secondly, I have noticed that compiler hints and warnings are in fact detecting for you, some of the common problems. Clean up hints and warnings and you might find many access violations and other subtle problems. Forgetting to declare your destructors properly, for example, can lead to a compiler warning, but to serious problems at runtime.

Thirdly, I’ve found tools like Pascal Analyzer from Peganza, and the audits-and-metrics feature in some editions of Delphi, can help you find areas of your code that have problems. As a single concrete example, Pascal Analyzer has found places where I forgot to do something important, that lead to a crash or access violation.

Fourth, you can hardly beat the technique of having another developer critique your code. You might feel a bit sheepish afterwards, but you’re going to learn something, hopefully, and get better at doing what you’re doing. Chances are, there is more way than one to use a tree view, and more way than one to do the work you’re doing, and a better architecture, and a clean way of doing things is going to result in more reliable code that doesn’t break each time you touch it. THere is not a finite list of rules to produce clean code, it is rather, a lifetime effort, and a matter of degrees. You’d be surprised how innocent looking code can be a hotbed of potential crashes, access violations, race conditions, freezes and deadlocks.

Содержание

  1. Почему возникает ошибка Access Violation
  2. What is an Access Violation
  3. Looking for source code line of Access Violation
  4. Looking for the Access Violation’s reason by analyzing the code
  5. Looking for Access Violation’s reason by analyzing the data

Почему возникает ошибка Access Violation

Ошибка «Access Violation» возникает, когда идёт обращение к памяти к которой обращение запрещено. Это возможно во многих случаях, но наиболее типичные ситуации я попытаюсь перечислить:

1) Обращение к не созданному объекту.

В данном случае объект e ещё не создан и идёт обращение к памяти, которая ещё не выделена.

2) Обращение к уже разрушенному объекту:

Тут есть хитрость, допустим вы хотите проверить есть ли объект и модернизируете код:

Особенно часто приходится такое делать когда надо уничтожить объект:

Так вот — такой код может быть источником ошибки, так как метод Free автоматически не устанавливает указатель в Nil. Обязательно после каждого Free используйте установление указателя в nil:

3) При выходе за границы динамического массива обычно генерится ошибка «Index out of bound», но возможно и возникновение Access Violation, особенно когда не стоят опции компилляции по проверки границ массивов. Эта ошибка может быть очень сложна в отлаживании — дело в том что допустим у вас есть массив а длиной в 10 элементов, в пишете:

И эта строка может пройти как и надо, без всяких проблем, но её выполнение повредит какой-то другой код, причём каждый раз другой! Теперь самая безобидная операция типа i:=10 может вдруг внезапно дать Access Violation.

3) На форме на onCreate вызывается что-то с других форм — эти другие формы на этот момент еще не созданы

4) На форме на onDestroy вызывается что-то с других форм — эти другие формы на этот момент уже разрушены

Источник

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.

Roughly speaking, program uses 3 «types» of memory: area for global variables, the stack and the heap.

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.

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:


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:


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.

If you are using an exception diagnostic 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):


It doesn’t matter if you got an error by using debugger or EurekaLog — it is best to prepare for this situation in good time — by setting the proper project’s options. Typically, this is the options «Use Debug DCUs» and «Stack frames».

Okay, finding the error’s location — this is only half of the case. Determinating 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. I won’t discuss the using of debugger here, rather I want to discuss other approaches.

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 I want to 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. I mean here cases with untyped parameters and buffer-overflow errors:
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. I won’t cover this issue here, as it might take a long time.

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. Here is a good story about such case by Raymond Chen.

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 .

6. Calling a function via invalid procedural variable. For example:
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 multiply 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. We already talked about such situations before.

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).

For example:
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:

  • Do we have the [] in our line? If so: can there be an invalid index here?
  • Are there any work with objects? If so: check the logic — is there a too early object’s release?
  • Do we use a DLL? If so: is a function declaration correct? Does all dynamic data exchanges properly handle?
  • and so on.

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 entry), of call of invalid function (item 6 or, sometimes, 4 from previous entry).

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 first tab. It is provided here only for convenience — that way you do not have to search it somewhere else. This is no information there. But on the second 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. In this case, we can look at the assembler listing and see that the problem involves 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.

In the next time, we’ll talk about cases, when there IS a bug in your code, but there is no access violation! Partially, we already talked about such situations (like silent stack corruption), but in the next time we’ll be specifically focused on them and will consider what can we do to catch such errors.

Источник

Ошибки — неизбежное зло программирования. Видимо пока трудно даже представить средство с помощью которого можно избавится от них. Человеку, которые выдумает это чудодейственное лекарство, благодарные потомки-программисты, несомненно, воздвигнут памятник. Пока же остается лишь заниматься обычным делом: ловлей багов.
«Нарушение Доступа» — фраза, которую пользователи видят, когда приложение делает попытки обратиться к памяти, которая не обозначена для их использования — и как следствие происходит сбой в работе программы:

Access violation at address

in module .
Read of address
Ситуация при которой Windows давала бы полную свободу программам — записывай данные куда хочешь, скорее всего бы привела к разноголосице программ и полной потери управления над компьютером. Но этого не происходит — Windows стоит на страже «границ памяти» и отслеживает недопустимые операции. Если сама она справиться с ними не в силах — происходит запуск утилиты Dr. Watson, которая записывает данные о возникшей ошибки, а сама программа закрывается.
Известно что, при программирование, особенно крупных программных продуктов, уследить за всеми процессами в коде невозможно, да и нет необходимости. Использование сторонних компонентов и библиотек только усложняет дело. Именно поэтому программисты Delphi, порой и сталкиваются со «своенравными» программами, которые то и дело норовят «сбросить пользователя». Итак, давайте рассмотрим некоторые вопросы, связанные с корректной среды программирования, так и непосредственно проблемы написания кода, которые ведут к возникновению ошибок типа «ошибка доступа» (AVS) и очертим наиболее известные пути их исправления.
Мы можем поделить AVS, с которыми сталкиваются при разработке в Delphi на два основных типах: ошибки при выполнения и некорректная разработка проекта, что вызывает ошибки при работе программы.
Ошибки возникают при старте и закрытии Delphi или формировании проекта. Причиной могут являться сбои в «железе» компьютера.
Эти ошибки могут быть вызваны различными источниками, включая систему BIOS, операционную систему или аппаратные подпрограммы драйверов. Некоторые видео-, звуковые или сетевые платы могут фактически вызывать подобного рода ошибки в Delphi. Для решения подобных аппаратных проблем можно предпринять последовательность неких «стандартных» ходов:
проверить, что не имеется никаких конфликтов между установленными устройствами, устранить обнаруженные конфликты;
попробовать слегка уменьшить «аппетита» видеодрайвера — поставить меньшее разрешение;
в случае если у вас двухпроцесорная система обеспечить равное изменение шага для каждого процессора;
И в конце концов просто попытаться заменить драйвера на более свежие.

Но помимо чисто железных проблем — большую головную боль могут вызвать ошибки в работе программного обеспечения. Особенно это касается непосредственно операционной системы. Зачастую Windows терпит крах спонтанно. Вот рекомендации которые помогут вам создать более устойчивую среду программирования:
Хотя Windows 9X популярная система, разработку лучше проводить в Windows NT или Windows 2000 — это более устойчивые операционные системы. Естественно при переходе на них придется отказаться от некоторых благ семейства Windows 95/98/Me — в частности не все программы адоптированы для Windows NT/2000. Зато вы получите более надежную и стабильную систему.
Не забывайте о том, как важно всегда иметь под рукой свежие версии компонентов для Delphi и дополнительных библиотек. В отличие от Windows создатели данных пакетов стараются от версии к версии уменьшать количество ошибок.
Следите за тем, что бы устанавливаемые компоненты были предназначены непосредственно для вашей версии Delphi. Попробуйте деинсталлировать чужеродные компоненты один за другим (или пакет за пакетом) пока проблема не будет устранена.
Контролируйте все программные продукты установленные на вашей машине и деинсталлируйте те из них, которые сбоят. Фаворитами AV среди них являются шароварные утилиты и программы и бета версии программных продуктов.
Все вышеперечисленное в основном не касалось самого процесса программирования и в малой степени зависит от разработчика. Теперь же обратимся к теме, как не допустить при разработки программного продукта ситуации при которой, он сам будет являться причиной ошибки.
Вы могли бы рассмотреть компилирование вашего приложения с директивой {$D}, данная директива компилятора может создавать файлы карты (файлы с расширением map, которые можно найти в том же каталоге, что и файлы проекта), которые могут послужить большой справкой в локализации источника подобных ошибок. Для лучшего «контроля» за своим приложением, компилируйте его с директивой {$D}. Таким образом, вы заставите Delphi генерировать информацию для отладки, которая может послужить подспорьем при выявление возникающих ошибок.
Следующая позиция в Project Options — Linker & Compiler позволяет вам, определить все для последующей отладки. Лучше всего, если помимо самого выполняемого кода будет доступна и отладочная информация — это поможет при поиске ошибок. Отладочная информация увеличивает размер файла и занимает дополнительную память при компилировании программ, но непосредственно на размер или быстродействие выполняемой программы не влияет. Включение опций отладочной информации и файла карты дают детальную информацию только, если вы компилируете программу с директивой {$D+}.
Эта информация состоит из таблицы номеров строк для каждой процедуры, которая отображает адреса объектных кодов в номера строк исходного текста. Директива $D обычно используется совместно с другой директивой — $L, что позволяет или запрещает генерацию информации о локальных символах для отладки.
Таким образом вы без труда сможете найти точный адрес той подпрограммы, которая была ответственна за ошибку. Одна из наиболее общих причин ошибок выполнения — использование объекта, который еще не был создан. Если второй адрес при выдачи ошибки — FFFFFFF (или 0000000) Вы можете почти утверждать, что было обращение к объекту, который еще не был создан. Например, вызов метода формы, которая не была создана.

procedure TfrMain.OnCreate(Sender: TObject);
 var BadForm: TBadForm;
 begin
   BadForm.Refresh; // причина  ошибки
 end;
 

Попытаемся разобратся в этой ситуации. Предположим, что BadForm есть в списке «Available forms » в окне Project Options|Forms. В этом списке находятся формы, которые должны быть созданы и уничтожены вручную. В коде выше происходит вызов метода Refresh формы BadForm, что вызывает нарушение доступа, так как форма еще не была создана, т.е. для объекта формы не было выделено памяти.
Если вы установите «Stop on Delphi Exceptions » в Language Exceptions tab в окне Debugger Options, возможно возникновения сообщение об ошибке, которое покажет, что произошло ошибка типа EACCESSVIOLATION. EACCESSVIOLATION — класс исключение для недопустимых ошибок доступа к памяти. Вы будете видеть это сообщение при разработке вашего приложения, т.е. при работе приложения, которое было запущено из среды Delphi.
Следующее окно сообщения будет видеть пользователь — и программа будет закрыта при совершение недопустимой операции:

 Access violation at address 0043F193
 in module 'Project1.exe'
 Read of address 000000.
 

Первое шестнадцатиричное число (‘0043F193’) — адрес ошибки во время выполнения программы в программе. Выберите, опцию меню ‘Search|Find Error’, введите адрес, в котором произошла ошибка (‘0043F193’) в диалоге и нажмите OK. Теперь Delphi перетранслирует ваш проект и покажет вам, строку исходного текста, где произошла ошибка во время выполнения программы, то есть BadForm.Refresh.
Естественно, что списка наиболее общих причин ошибок, вызывающих аварийное завершение работы программы, написанной в Delphi в чистом виде нет. Есть несколько общих «узких мест» в коде и структуре программы, когда подобная ошибка может произойти. Перечислим наиболее распространенные.

Недопустимый параметр API

Если вы пытаетесь передать недопустимый параметр в процедуру Win API, может произойти ошибка. Необходимо отслеживать все нововведения в API при выходе новых версий операционных систем и их обновлений.

Уничтожение исключения

Никогда не уничтожайте временный объект исключения. Обработка исключения автоматически уничтожает объект исключения. Если вы уничтожите объект самостоятельно, то приложение попытается уничтожать объект снова, и произойдет ошибка.

 Zero:=0;
 try
    dummy:= 10 / Zero;
 except on E: EZeroDivide do
    MessageDlg('Can not divide by zero!', mtError, [mbOK], 0);
    E.free. // причина ошибки
 end;
 

Индексация пустой строки
Пустая строка не имеет никаких достоверных данных. Следовательно, попытка индексировать пустую строку — подобно попытке обратиться к нулю, что приведет также к ошибке:

 var s: string;
 begin
    s:='';
    s[1]:='a'; // причина ошибки
 end;
 

Обращение к динамической переменной
Вы должны строить обращение к динамической переменной корректно, иначе вы перемещаете адреса указателей и возможно разрушаете другие выделенные ячейки памяти.

 procedure TForm1.Button1Click(Sender: TObject);
 var
   p1 : pointer;
   p2 : pointer;
 begin
   GetMem(p1, 128);
   GetMem(p2, 128);
  {эта строка может быть причиной ошибки}
   Move(p1, p2, 128);
  {данная строка корректна }
   Move(p1^, p2^, 128);
   FreeMem(p1, 128);
   FreeMem(p2, 128);
 end;
 

Вот и всё, Удачи!

2001 г

Ловим баги или Почему программы допускают «недопустимые операции»

Е. Левшаков, В. Ковалев, mcsa.ru

Ошибки — неизбежное зло программирования. Видимо, пока трудно даже представить средство с помощью которого можно избавится от них. Человеку, который выдумает это чудодейственное лекарство, благодарные потомки-программисты, несомненно, воздвигнут памятник. Пока же остается лишь заниматься обычным делом: ловлей багов.

«Нарушение Доступа» — фраза, которую пользователи видят, когда приложение делает попытки обратиться к памяти, которая не обозначена для их использования — и как следствие происходит сбой в работе программы:

Access violation at address <HEX_value>
in module <Application.Exe>.
Read of address <HEX_value_2>

Ситуация, при которой Windows давала бы полную свободу программам — записывай данные куда хочешь, скорее всего бы привела к разноголосице программ и полной потери управления над компьютером. Но этого не происходит — Windows стоит на страже «границ памяти» и отслеживает недопустимые операции. Если сама она справиться с ними не в силах — происходит запуск утилиты Dr. Watson, которая записывает данные о возникшей ошибке, а сама программа закрывается.

Известно, что при программировании, особенно крупных программных продуктов, уследить за всеми процессами в коде невозможно, да и нет необходимости. Использование сторонних компонентов и библиотек только усложняет дело. Именно поэтому программисты Delphi порой и сталкиваются со «своенравными» программами, которые то и дело норовят «сбросить пользователя». Итак, давайте рассмотрим некоторые вопросы, связанные с корректной средой программирования, так и непосредственно проблемы написания кода, которые ведут к возникновению ошибок типа «ошибка доступа» (AVS) и очертим наиболее известные пути их исправления.

Мы можем поделить AVS, с которыми сталкиваются при разработке в Delphi, на два основных типах: ошибки при выполнения и некорректная разработка проекта, что вызывает ошибки при работе программы.

Ошибки возникают при старте и закрытии Delphi или формировании проекта. Причиной могут являться сбои в «железе» компьютера.

Эти ошибки могут быть вызваны различными источниками, включая систему BIOS, операционную систему или аппаратные подпрограммы драйверов. Некоторые видео-, звуковые или сетевые платы могут фактически вызывать подобного рода ошибки в Delphi. Для решения подобных аппаратных проблем можно предпринять последовательность неких «стандартных» ходов:

  • проверить, что не имеется никаких конфликтов между установленными устройствами, устранить обнаруженные конфликты;
  • попробовать слегка уменьшить «аппетиты» видеодрайвера — поставить меньшее разрешение;
  • в случае если у вас двухпроцесорная система обеспечить равное изменение шага для каждого процессора;

И в конце концов просто попытаться заменить драйвера на более свежие.

Но помимо чисто железных проблем — большую головную боль могут вызвать ошибки в работе программного обеспечения. Особенно это касается непосредственно операционной системы. Зачастую Windows терпит крах спонтанно. Вот рекомендации которые помогут вам создать более устойчивую среду программирования:

Хотя Windows 9X популярная система, разработку лучше проводить в Windows NT или Windows 2000 — это более устойчивые операционные системы. Естественно, при переходе на них придется отказаться от некоторых благ семейства Windows 95/98/Me — в частности, не все программы адаптированы для Windows NT/2000. Зато вы получите более надежную и стабильную систему.

Не забывайте о том, как важно всегда иметь под рукой свежие версии компонентов для Delphi и дополнительных библиотек. В отличие от Windows создатели данных пакетов стараются от версии к версии уменьшать количество ошибок.

Следите за тем, чтобы устанавливаемые компоненты были предназначены непосредственно для вашей версии Delphi. Попробуйте деинсталлировать чужеродные компоненты один за другим (или пакет за пакетом), пока проблема не будет устранена.

Контролируйте все программные продукты, установленные на вашей машине и деинсталлируйте те из них, которые сбоят. Фаворитами AV среди них являются шароварные утилиты и программы и бета версии программных продуктов.

Все вышеперечисленное в основном не касалось самого процесса программирования и в малой степени зависит от разработчика. Теперь же обратимся к теме, как не допустить при разработке программного продукта ситуации, при которой он сам будет являться причиной ошибки.

Вы могли бы рассмотреть компилирование вашего приложения с директивой {$D}, данная директива компилятора может создавать файлы карты (файлы с расширением map, которые можно найти в том же каталоге, что и файлы проекта), которые могут послужить большой справкой в локализации источника подобных ошибок. Для лучшего «контроля» за своим приложением компилируйте его с директивой {$D}. Таким образом, вы заставите Delphi генерировать информацию для отладки, которая может послужить подспорьем при выявление возникающих ошибок.

Следующая позиция в Project Options — Linker & Compiler позволяет вам, определить все для последующей отладки. Лучше всего, если помимо самого выполняемого кода будет доступна и отладочная информация — это поможет при поиске ошибок. Отладочная информация увеличивает размер файла и занимает дополнительную память при компилировании программ, но непосредственно на размер или быстродействие выполняемой программы не влияет. Включение опций отладочной информации и файла карты дают детальную информацию только если вы компилируете программу с директивой {$D+}.

Эта информация состоит из таблицы номеров строк для каждой процедуры, которая отображает адреса объектных кодов в номера строк исходного текста. Директива $D обычно используется совместно с другой директивой — $L, что позволяет или запрещает генерацию информации о локальных символах для отладки.

Таким образом вы без труда сможете найти точный адрес той подпрограммы, которая была ответственна за ошибку. Одна из наиболее общих причин ошибок выполнения — использование объекта, который еще не был создан. Если второй адрес при выдачи ошибки — FFFFFFF (или 0000000) Вы можете почти утверждать, что было обращение к объекту, который еще не был создан. Например, вызов метода формы, которая не была создана.

procedure TfrMain.OnCreate(Sender: TObject);
 var BadForm: TBadForm;
 begin
   BadForm.Refresh; // причина  ошибки
 end;

Попытаемся разобратся в этой ситуации. Предположим, что BadForm есть в списке «Available forms» в окне Project Options|Forms. В этом списке находятся формы, которые должны быть созданы и уничтожены вручную. В коде выше происходит вызов метода Refresh формы BadForm, что вызывает нарушение доступа, так как форма еще не была создана, т.е. для объекта формы не было выделено памяти.

Если вы установите «Stop on Delphi Exceptions» в Language Exceptions tab в окне Debugger Options, возможно возникновение сообщения об ошибке, которое покажет, что произошло ошибка типа EACCESSVIOLATION. EACCESSVIOLATION — класс исключение для недопустимых ошибок доступа к памяти. Вы будете видеть это сообщение при разработке вашего приложения, т.е. при работе приложения, которое было запущено из среды Delphi.

Следующее окно сообщения будет видеть пользователь — и программа будет закрыта при совершение недопустимой операции:

 Access violation at address 0043F193
 in module 'Project1.exe'
 Read of address 000000.

Первое шестнадцатиричное число (‘0043F193’) — адрес ошибки во время выполнения программы. Выберите опцию меню ‘Search|Find Error’, введите адрес, в котором произошла ошибка (‘0043F193’) в диалоге и нажмите OK. Теперь Delphi перетранслирует ваш проект и покажет вам строку исходного текста, где произошла ошибка во время выполнения программы, то есть BadForm.Refresh.

Естественно, что списка наиболее общих причин ошибок, вызывающих аварийное завершение работы программы, написанной в Delphi, в чистом виде нет. Есть несколько общих «узких мест» в коде и структуре программы, когда подобная ошибка может произойти. Перечислим наиболее распространенные.

Недопустимый параметр API

Если вы пытаетесь передать недопустимый параметр в процедуру Win API, может произойти ошибка. Необходимо отслеживать все нововведения в API при выходе новых версий операционных систем и их обновлений.

Уничтожение исключения

Никогда не уничтожайте временный объект исключения. Обработка исключения автоматически уничтожает объект исключения. Если вы уничтожите объект самостоятельно, то приложение попытается уничтожать объект снова, и произойдет ошибка.

 Zero:=0;
 try
    dummy:= 10 / Zero;
 except on E: EZeroDivide do
    MessageDlg('Can not divide by zero!', mtError, [mbOK], 0);
    E.free. // причина ошибки
 end;

Индексация пустой строки

Пустая строка не имеет никаких достоверных данных. Следовательно, попытка индексировать пустую строку — подобно попытке обратиться к нулю, что приведет также к ошибке:

 var s: string;
 begin
    s:='';
    s[1]:='a'; // причина ошибки
 end;

Обращение к динамической переменной

Вы должны строить обращение к динамической переменной корректно, иначе вы перемещаете адреса указателей и возможно разрушаете другие выделенные ячейки памяти.

procedure TForm1.Button1Click(Sender: TObject);
 var
   p1 : pointer;
   p2 : pointer;
 begin
   GetMem(p1, 128);
   GetMem(p2, 128);
  {эта строка может быть причиной ошибки}
   Move(p1, p2, 128);
  {данная строка корректна }
   Move(p1^, p2^, 128);
   FreeMem(p1, 128);
   FreeMem(p2, 128);
 end;

Перечисленные подходы позволят избежать наиболее частых недочетов в разработке, которые могут вызвать столь неприятное как для пользователя, так и для разработчика сообщение о том, что программа выполнила «недопустимую операцию».

Удачной вам ловли багов, господа!

>
Access Violation at address при загрузке дочерней формы

  • Подписаться на тему
  • Сообщить другу
  • Скачать/распечатать тему

  


Сообщ.
#1

,
13.03.08, 07:02

    Может мой вопрос покажется «ламерским» :whistle: но может кто нибудь сталкивался с такой проблемой.
    Когда вызываю из главной формы дочернюю форму:

    ExpandedWrap disabled

      implementation

       uses Unit2;

      procedure TForm1.CustomItem4Click(Sender: TObject);

      begin

      form2:=tform2.Create (self);

      end;

    то вылетают следующие ошибки
    Ошибка: Access Violation at address xxxxxxxx in module ‘XXXXXXX.exe’. Read of address xxxxxxxx.,
    xxxxxxxx — любые значения адреса,
    или
    Ошибка: External Exception C000001D.
    после чего приходится нажимать ОК и только после этого создается дочернее окно и в конце концов всё это виснет.
    Дочернюю форму закрываю так:

    ExpandedWrap disabled

      procedure TForm2.SCButton1Click(Sender: TObject);

      begin

      close;

      end;

      procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);

      begin

      action:=cafree;

      end;

      procedure TForm2.FormCloseQuery(Sender: TObject; var CanClose: Boolean);

      begin

       canclose:=true;

      end;

    Как можно обойти данную проблему :(
    Подскажите плз.. Если можно то с примерами…


    vladimir74



    Сообщ.
    #2

    ,
    13.03.08, 07:28

      добавь еще проверку, скорее всего эта форма уже существует…

      ExpandedWrap disabled

        procedure TForm1.CustomItem4Click(Sender: TObject);

        begin

          if not assigned(form2) then

            form2:=tform2.Create (self);

          form2.Show();

        end;

      ну и закрывай ….

      ExpandedWrap disabled

        procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);

        begin

             Action := caFree;

             Form2 := nil;

        end;


      Felan



      Сообщ.
      #3

      ,
      13.03.08, 10:12

        А еще лучше не проверку делать, а в свойствах проекта убрать форму, которая только динамически создается/уничтожается из автосоздания.


        Demon:-)

          


        Сообщ.
        #4

        ,
        14.03.08, 08:03

          Цитата Felan @ 13.03.08, 10:12

          А еще лучше не проверку делать, а в свойствах проекта убрать форму, которая только динамически создается/уничтожается из автосоздания.

          Это интересно как? :huh: Как можно реализовать Ваш пример.

          -Added 14.03.08, 08:14

          Цитата vladimir74 @ 13.03.08, 07:28

          добавь еще проверку, скорее всего эта форма уже существует…

          ExpandedWrap disabled

            procedure TForm1.CustomItem4Click(Sender: TObject);

            begin

              if not assigned(form2) then

                form2:=tform2.Create (self);

              form2.Show();

            end;

          ну и закрывай ….

          ExpandedWrap disabled

            procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);

            begin

                 Action := caFree;

                 Form2 := nil;

            end;

          Да странно он уже вызывает эти ошибки как в предыдущем и новую другую ошибку

          Ошибка: Abstract error!

          В чем бага не пойму :huh:

          Сообщение отредактировано: Demon:-) — 14.03.08, 08:15


          vladimir74



          Сообщ.
          #5

          ,
          14.03.08, 08:29

            Цитата Demon:-) @ 14.03.08, 08:03

            Это интересно как? :huh: Как можно реализовать Ваш пример.

            зайди в файл проекта (project1.dpr) и убери от туда все Application.CreateForm(TForm2, Form2); кроме стартовой…

            Цитата Demon:-) @ 14.03.08, 08:03

            Да странно он уже вызывает эти ошибки как в предыдущем и новую другую ошибку
            Ошибка: Abstract error!

            непонял что и где вызывает ошибки?


            Demon:-)

              


            Сообщ.
            #6

            ,
            14.03.08, 08:33

              Цитата vladimir74 @ 14.03.08, 08:29

              Цитата Demon:-) @ 14.03.08, 08:03

              Это интересно как? :huh: Как можно реализовать Ваш пример.

              зайди в файл проекта (project1.dpr) и убери от туда все Application.CreateForm(TForm2, Form2); кроме стартовой…

              Цитата Demon:-) @ 14.03.08, 08:03

              Да странно он уже вызывает эти ошибки как в предыдущем и новую другую ошибку
              Ошибка: Abstract error!

              непонял что и где вызывает ошибки?

              А эту я сразу же надпись снёс, иначе она бы вместе с главной загружалась (а это мне ненужно ;) )

              а ошибки…

              …Ошибка: Access Violation at address xxxxxxxx in module ‘XXXXXXX.exe’. Read of address xxxxxxxx.,
              xxxxxxxx — любые значения адреса,
              или
              Ошибка: External Exception C000001D.
              после чего приходится нажимать ОК и только после этого создается дочернее окно и в конце концов всё это виснет….

              Сообщение отредактировано: Demon:-) — 14.03.08, 08:34


              vladimir74



              Сообщ.
              #7

              ,
              14.03.08, 08:41

                Demon:-)
                мне чего то кажется что у тебя ошибка где то в другом…..
                посмотри в Create и Show вызываемой формы может у тебя там что то не так… ты где то обращаешься к обьекту который в данный момент не существует (еще не создан или уже уничтожен)
                поставь в дебагере брейкпоинты в первой форме там где ты открываешь вторую, и во второй форме в методах Create и Show (если существуют) и посмотри где конкретно вылетает ошибка…


                Demon:-)



                Сообщ.
                #8

                ,
                14.03.08, 08:58

                  Цитата vladimir74 @ 14.03.08, 08:41

                  Demon:-)
                  мне чего то кажется что у тебя ошибка где то в другом…..
                  посмотри в Create и Show вызываемой формы может у тебя там что то не так… ты где то обращаешься к обьекту который в данный момент не существует (еще не создан или уже уничтожен)
                  поставь в дебагере брейкпоинты в первой форме там где ты открываешь вторую, и во второй форме в методах Create и Show (если существуют) и посмотри где конкретно вылетает ошибка…

                  вот сам дпр

                  ExpandedWrap disabled

                    program Project1;

                    uses

                      Forms,

                      Unit1 in ‘Unit1.pas’ {Form1},

                      Unit2 in ‘Unit2.pas’ {Form2};

                    …..

                    {$R *.res}

                    begin

                      Application.Initialize;

                      Application.Title := »;

                      Application.CreateForm(TForm1, Form1);

                      Application.Run;

                    end.

                  Унит1 это главная
                  Унит 2 дочка

                  Вот в Уните1 я я вызываю дочку Унит2 таким образом (в первом сообщении было описано)

                  ExpandedWrap disabled

                     form2:=tform2.Create (self);

                  вылетали ошибки, и вставил Ваш код

                  ExpandedWrap disabled

                    if not assigned(form2) then

                        form2:=tform2.Create (self);

                      form2.Show();

                  ошибки остались и возникла новая вот


                  vladimir74



                  Сообщ.
                  #9

                  ,
                  14.03.08, 09:01

                    ах да может тебе вместо tform2.Create(self) создавать tform2.Create(Application) ?
                    разница между вызовами описана в DRKB кстати очень рекомендую….

                    Добавлено 14.03.08, 09:11
                    Demon:-)
                    что к тебя происходит в form2.FormCreate(Sender: TObject) ?

                    я только что сделал проект (пятница подействовала)

                    ExpandedWrap disabled

                      program Project1;

                      uses

                        Forms,

                        Unit1 in ‘Unit1.pas’ {Form1},

                        Unit2 in ‘Unit2.pas’ {Form2};

                      {$R *.res}

                      begin

                        Application.Initialize;

                        Application.CreateForm(TForm1, Form1);

                        Application.Run;

                      end.

                    ExpandedWrap disabled

                      unit Unit1;

                      interface

                      uses

                        Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

                        Dialogs, StdCtrls;

                      type

                        TForm1 = class(TForm)

                          Button: TButton;

                          procedure ButtonClick(Sender: TObject);

                        private

                          { Private-Deklarationen }

                        public

                          { Public-Deklarationen }

                        end;

                      var

                        Form1: TForm1;

                      implementation

                      uses

                        Unit2;

                      {$R *.dfm}

                      procedure TForm1.ButtonClick(Sender: TObject);

                      begin

                      if not assigned(form2) then

                          form2:=tform2.Create(self);

                        form2.Show();

                      end;

                      end.

                    ExpandedWrap disabled

                      unit Unit2;

                      interface

                      uses

                        Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

                        Dialogs, StdCtrls;

                      type

                        TForm2 = class(TForm)

                          Edit: TEdit;

                          procedure FormClose(Sender: TObject; var Action: TCloseAction);

                        private

                          { Private-Deklarationen }

                        public

                          { Public-Deklarationen }

                        end;

                      var

                        Form2: TForm2;

                      implementation

                      {$R *.dfm}

                      procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);

                      begin

                           Action := caFree;

                           Form2 := nil;

                      end;

                      end.

                    у меня никаких ошибок нет, следовательно я так понимаю проблема ни в самом открытии а в том что ты при этом открытии делаешь дополнительно. Пройдись дебагером от начала создания до конца открытия формы…


                    AnalogXP



                    Сообщ.
                    #10

                    ,
                    14.03.08, 09:58

                      Full Member

                      ***

                      Рейтинг (т): 8

                      Вот так совпадение. Прям сейчас с той же проблемой сталкнулся. Код точно такой же.
                      AV появляется если я в TForm2.FormCreate хочу задать этой же форме позицию на экране или же присвоить ей другие значения.

                      ExpandedWrap disabled

                        Ini := TIniFile.Create(ShellDir(‘AppData’)+’LinkCollectorConfig.ini’);

                            try

                               form2.Top:=ini.ReadInteger(‘Program’,’Top’, 100); // Если эти две сторочки убать

                               form2.Left:=ini.ReadInteger(‘Program’,’Left’,100); // то проблема исчезает

                            finally

                             ini.Free;

                            end;

                      Но как в таком случае регулировать ее позицию?

                      Все вроде, решил проблему заменив код создания формы с

                      ExpandedWrap disabled

                               if not assigned(form2) then

                                    form2:=tform2.Create(self);

                               form2.Show;

                      на этот

                      ExpandedWrap disabled

                               if not assigned(form2) then

                                    Application.CreateForm(TForm2, Form2);

                               Form2.Show;

                      Сообщение отредактировано: AnalogXP — 14.03.08, 10:13


                      Demon:-)



                      Сообщ.
                      #11

                      ,
                      14.03.08, 10:46

                        Цитата AnalogXP @ 14.03.08, 09:58

                        Вот так совпадение. Прям сейчас с той же проблемой сталкнулся. Код точно такой же.
                        AV появляется если я в TForm2.FormCreate хочу задать этой же форме позицию на экране или же присвоить ей другие значения.

                        ExpandedWrap disabled

                          Ini := TIniFile.Create(ShellDir(‘AppData’)+’LinkCollectorConfig.ini’);

                              try

                                 form2.Top:=ini.ReadInteger(‘Program’,’Top’, 100); // Если эти две сторочки убать

                                 form2.Left:=ini.ReadInteger(‘Program’,’Left’,100); // то проблема исчезает

                              finally

                               ini.Free;

                              end;

                        Но как в таком случае регулировать ее позицию?

                        Все вроде, решил проблему заменив код создания формы с

                        ExpandedWrap disabled

                                 if not assigned(form2) then

                                      form2:=tform2.Create(self);

                                 form2.Show;

                        на этот

                        ExpandedWrap disabled

                                 if not assigned(form2) then

                                      Application.CreateForm(TForm2, Form2);

                                 Form2.Show;

                        да реально «тупость» почему-то дочерней форме нельзя задавать размер :wall:

                        Вызывает ошибку вот эта запись: :whistle:

                        ExpandedWrap disabled

                          procedure TForm2.FormCreate(Sender: TObject);

                          begin

                          form2.Width:=540;

                          form2.Height:=360;

                          end;

                        гы :lol: комментируешь и всё работает :lol:
                        так

                        ExpandedWrap disabled

                          procedure TForm2.FormCreate(Sender: TObject);

                          begin

                          //form2.Width:=540;

                          //form2.Height:=360;

                          end;

                        всем псб… ;)


                        leo



                        Сообщ.
                        #12

                        ,
                        14.03.08, 11:31

                          AnalogXP, Demon:-)
                          Ну вы и чудаки, однако :D
                          Кто же в FormCreate обращается к «левой» переменной form2 ?! Нужно же с self работать, т.е. просто писать

                          ExpandedWrap disabled

                            procedure TForm2.FormCreate(Sender: TObject);

                            begin

                              {form2.}Width:=540;

                              {form2.}Height:=360;

                            end;

                          PS. Вот вам и разница между TForm2.Create и Application.CreateForm:
                          TForm2.Create вызывает TObject.NewInstance, затем Create и FormCreate, и только после этого происходит присвоение значения переменной form2. Т.е. во время выполнения FormCreate form2 еще не присвоено и = Nil !!!
                          А вот Application.CreateForm вызывает TObject.NewInstance, сразу присваивает значение form2 и только после этого вызывает Create и FormCreate с уже установленным значением form2

                          Сообщение отредактировано: leo — 14.03.08, 11:36

                          0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)

                          0 пользователей:

                          • Предыдущая тема
                          • Delphi: Общие вопросы
                          • Следующая тема

                          [ Script execution time: 0,0567 ]   [ 16 queries used ]   [ Generated: 29.01.23, 06:48 GMT ]  

                          0 0 голоса
                          Рейтинг статьи
                          Подписаться
                          Уведомить о
                          guest

                          0 комментариев
                          Старые
                          Новые Популярные
                          Межтекстовые Отзывы
                          Посмотреть все комментарии

                          А вот еще интересные материалы:

                        • Яшка сломя голову остановился исправьте ошибки
                        • Ясность цели позволяет целеустремленно добиваться намеченного исправьте ошибки
                        • Ясность цели позволяет целеустремленно добиваться намеченного где ошибка
                        • Делонги кофемашина ошибка накипи
                        • Диагностика дисков windows обнаружила ошибку s m a r t на диске