# Native Code > برنامه نویسی در Delphi > مقالات مرتبط با Delphi/Win32 >  What access violations are and how to prevent AVs

## m-khorsandi

Access Violation چیست و چگونه می‌توان در زمان طراحی/اجرا از بوجود آمدن آن جلوگیری کرد؟

Access Violation، General Protection Fault یا Invalid Page Fault؛ نام‌ها تغییر می‌کنند، اما طبیعت و ذات خطاها یکی ست. Access Violation عبارتی ست که کاربران کامپیوترهای شخصی (PC) موقعی می‌بینند که برنامه‌ی اجرا شده توسط آنها، سعی در دستیابی به محلی دارد که به آن برنامه تخصیص داده نشده ست و در واقع در اختیار آن برنامه قرار ندارد. 

Access violation at address 
in module .
Read of address


اگر برنامه‌های ویندوزی قادر می‌بودند اطلاعاتی را در خارج از ناحیه‌ی تخصیص داده شده به آن‌ها بنویسند، می‌توانستند دستورات و اطلاعات برنامه‌های دیگر و حتی سیستم عامل را رونویسی کنند. اگر چنین چیزی اتفاق می‌افتاد سیستم عامل به نحوی از کار می‌افتاد و می‌بایست آن را دوباره راه‌اندازی کنید. برای مثال، وقتی در برنامه‌ای در ویندوز 2000 یا NT  خطایی روی می‌دهد، Dr.Watson مداخله کرده و برنامه را متوقف می‌کند، چندتا عیب‌یابی سریع را انجام می‌دهد و آنها را در یک فایل متنی ذخیره می‌کند. برنامه Dr.Watson را می‌توانید در شاخه‌ی Windows\System32 و با نام drwatson بیابید، در توضیحات فایل نیز، Windows Fault detection utility را مشاهده می‌کنید.

Access Violation تقریباً بیشترین خطایی ست که در برنامه‌های ویندوزی مشاهده می‌شود. هدف از این مقاله، کمک به شما در یافتن راه‌حلی برای Access Violation با دلفی هست. اول از همه، *Access* Violation هیچ ارتباطی با Microsoft *Access* ندارد. 

توجه : برای سهولت در خواندن و نوشتن Access Violation، از این پس عبارت AV را جایگزین Access Violation می‌کنم.

ما می‌توانیم مواجهات‌مان با AV در دلفی را به دو بخش عمده تقسیم کنیم : Runtime و Design time یا به بیانی دیگر خطای AV در زمان طراحی و در زمان اجرا.


Access Violation در زمان طراحی
AVهای زمان طراحی در موقع شروع یا بستن دلفی یا Build کردن یک پروژه اتفاق می‌افتد.

*Hardware* – سخت افزار
زمانی‌که کامپیوترتان مشغول کار کردن هست، پیغام‌های AVیی که ظاهر می‌شوند، می‌توانند چندین منشاء مختلف داشته باشند، که می‌توان بایوس سیستم، سیستم عامل یا روتین‌های درایورهای سخت افزاری را نام برد. بعضی از کارت‌های ویدئویی، صدا یا شبکه واقعاً می‌توانند سبب بروز AVها باشند. چرا؟ هر کارتی در کامپیوتر به همراه راه‌انداز آن به نتیجه‌ی مطلوب می‌رسد. تجربه‌ی این مشکل و خطا می‌تواند مشروط بر سازنده وسیله یا کارت، نسخه‌ی ویندوز و نسخه‌ی دلفی مورد استفاده باشد.
در اینجا چند مرحله که می‌توانند کمکی در حل این مشکلات باشند، ارائه شده ست:
•	مراحل لازم جهت بازبینی ناسازگاری و تداخل بین دستگاه‌های نصب شده را طی کنید.
•	گاهی اوقات کم کردن وضوح تصویری به تثبیت کردن درایور معیوب کارت گرافیکی کمک خواهد کرد.
•	همیشه از آخرین نسخه‌ی درایورها برای اجزاء سیستم‌تان استفاده کنید.


*Software* – نرم افزار
بااینکه ویندوز، محبوب‌ترین سیستم عامل برای ماشین‌های اینتل هست، به دلیل شکنندگی مفرط و اشکالات زیاد آن، برنامه‌های نامناسب می‌توانند به راحتی سبب از کار افتادن سیستم عامل شوند(سیستم‌عامل بعضی اوقات خود به خود نیز از کار می‌افتد). در اینجا راه‌هایی که می‌توانند به شانس شما در جهت برخورداری از یک محیط برنامه‌نویسی استوار و باثبات کمک کنند، وجود دارد، که در جلوگیری از AVهای مشخص، شما را راهنمایی می‌کنند.

•	مطمئن باشید که از آخرین Service Pack منتشر شده برای ویندوز استفاده می‌کنید.
•	یکی از بهترین روش‌ها، استفاده از آخرین بروزرسانی‌ها و Patchها برای دلفی مورد استفاده‌ی خود هست. با استفاده از آخرین Updateها و Patchها برای دلفی، تعداد AVها، خصوصاً AVهای زمان طراحی، شدیداً کاهش پیدا می‌کند.
•	اگر به صورت تصادفی در محیط توسعه دلفی، پیغام‌ Access Violation دریافت می‌کنید، احتمالاً کامپوننت‌ها و بسته‌هایی(Components and Packages)  را به صورت نادرست نصب کرده‌اید. سعی کنید یکی یکی کامپوننت‌ها یا بسته‌های نصب شده را حذف کنید، آزمایش کنید، تا مشکل برطرف شود، سپس با فروشنده‌ی کامپوننت راجع به این موضوع و مشکل صحبت کنید، البته این بخش خیلی برای ما که عموماً از کامپوننتهای Crack شده استفاده می‌کنیم، کارایی ندارد مگر از طریق بعضی انجمن‌ها.
•	هر چیز غیر معمولی که روی کامپیوتر نصب شده را با برنامه‌ای که پیغام خطا می‌دهد و یا از کار می‌افتد بررسی کنید و تطبیق دهید. برنامه‌های کمکی Shareware  غیر معمول و برنامه‌هایی که نسخه نهایی آنها منتشر نشده ، برای این گونه پیغام‌های خطا مستعد هستند.
•	یک Access Violation می‌تواند براثر تنظیمات غلط سیستم نیز روی دهد. اگر مکرراً با پیغام‌ خطای یکسان مواجه شدید، جزئیات خطا را یادداشت کنید و با کمپانی سازنده‌ی نرم‌افزار در مورد پیغام تماس بگیرید.




Access Violationهای عمومی دلفی در زمان اجرای برنامه
مشکل زیر می‌تواند برای هر توسعه‌ای از نرم‌افزار اتفاق بیفتد:
برنامه‌ای را نوشته‌اید، آزمایش کرده‌اید و آن را برای استفاده‌ی عمومی منتشر می‌کنید. سپس کاربری تلفنی درباره‌ی از کار افتادن برنامه با شما صحبت می‌کند و این ناراحت کننده ست.

ممکن ست بخواهید به برنامه‌ی کامپایل شده‌‌تان با رهنمود کامپایلر {$D} نگاهی بیندازید. دلفی می‌تواند map fileهایی ایجاد کند که کمک بزرگی به یافتن منشاء خطاهای AV می‌کنند. فرم Project Options(از منوی Project|Options|Linker & Compiler) در این باره هر چه که نیاز دارید در اختیارتان می‌گذارد.برای یونیت‌ها، اطلاعات معمولی debug به همراه یونیت کد، ثبت و ذخیره می‌شود. اطلاعات debug، سایز فایل یونیت را افزایش می‌دهد و زمانی که می‌خواهید پروژه را کامپایل کنید نیز حافظه‌ی اضافی نیاز دارد، اما هیچ تاثیری روی سرعت و سایز فایل اجرایی ندارد. 

map file، فایلی با پسوند map هست که کامپایلر آن را تولید می‌کند، و نمایش‌دهنده‌ی لایه‌های فایل اجرایی ست. برخلاف فرمت باینری فایل‌های اجرایی و فایل‌های dcu، map file یک فایل متنی خوانا و واضح ست که می‌توانید آن را چاپ کنید یا در یک ویرایشگر ساده ببینید. برای هر پروژه یک map file تولید می‌شود. برای فعال کردن آن باید به Project|Options|Linker بروید و در قسمت Map file آن را فعال کنید. می‌توانید به یک پروژه‌ی تستی ایجاد و آن را ذخیره کنید، Map File را فعال کنید، پروژه را کامپایل کنید و map file ساخته شده را در notepad باز کنید و ببینید.



معمولاً Access Violationها خودشان‌را در اولین اجرای برنامه نشان می‌دهند. مهم ست درباره‌ی این فکر کنید که کاربر چه عملی را انجام می‌دهد تا این مشکل برای بار اول نمود پیدا می‌کند و سپس برای رفع مشکل اقدام کنید، در واقع در اینجا شما نیاز به برنامه و طرحی برای تست دارید. از نظر کاربر، برنامه‌ی شما باعث ایجاد وقفه‌ای در کار او شده، و همچنین بیان مشکل توسط کاربر برای حل آن، زمان‌بر هست، هرچند که می‌توان گفت صحبت با کاربر، بهترین و شاید تنهاترین راه برای یافتن مشکل می‌باشد.

در ادامه‌ی مطلب می‌بینید که چطور می‌توان به راحتی فایل سورس، متد، و خطی که دقیقاً AV در آنجا اتفاق افتاده را یافت.

'*Search - Find Error...*'
وقتی یک AVی زمان اجرا اتفاق می‌افتد، کاربر پیغام خطایی شبیه به پیغام زیر دریافت می‌کند :

Access violation at address 
in module 
Read of address


اگر برنامه‌ی شما به همراه اطلاعات debug در محیط دلفی کامپایل شده باشد، می‌توانید خطی از سورس کد متناظر با کد کامپایل شده‌، که علت خطای AVست را بیابید.

*Non-existing object*
یکی از عمومی‌ترین علل AVها در دلفی، استفاده از اشیایی ست که هنوز ساخته نشده‌اند. اگر آدرس دوم FFFFFFF یا 000000 بود، تقریباً می‌توانید مطمئن باشید که می‌خواهید از شیئی استفاده کنید که هنوز ساخته نشده ست. برای مثال، از فرمی که نه به صورت خودکار (auto-created) ساخته شده و نه با استفاده از کد ساخته شده، متدی را فراخوانی می‌کنیم :


procedure TfrMain.OnCreate(Sender: TObject);
var 
  BadForm: TBadForm;
begin
  // this will generate an AV
  BadForm.Refresh;
end;

فرض کنید که فرم BadForm در "Available Forms" در پنجره‌ی Project Options قرار دارد( فرم می‌بایست که به صورت دستی، هم ساخته و هم آزاد شود)، در فرم بالا، فراخوانی متد Refresh از فرم BadForm، باعث یک Access Violation می‌شود.
اگر قابلیت "Stop on Delphi Exceptions" درTab  Language Exceptions در پنجره‌ی Debugger Options فعال باشد، پیغام زیر نمایش داده می‌شود :



پیغام بیان می‌کند که EaccessViolation اتفاق افتاده ست. EaccessViolation کلاس خطایی1 ست برای خطاهای دسترسی به حافظه‌ی نامعتبر2(یا پوچ). پیغام بالا چیزی ست که شما در زمان توسعه‌ی برنامه خواهید دید و کاربر پیغام خطای بعدی را می‌بیند و برنامه از کار می‌افتد :




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


در AVیی که رخ داده، اولین عدد هگزادسیمال ('0043F193')، آدرس خطای زمان اجرا در کد کامپایل شده ست. در محیط دلفی، از منوی Search گزینه‌ی Find Error… را انتخاب کنید، آدرس خطایی که اتفاق افتاده را وارد کنید و دکمه‌ی Ok را بزنید. الان دلفی پروژه را دوباره کامپایل می‌کند و خطی که خطای زمان اجرا در آن اتفاق افتاده را به شما نشان می‌دهد، که این خط، BadForm.Refresh هست.



آنچه در ادامه می‌آید لیستی از عمومی‌ترین علل وقوع AVها در محیط توسعه‌ی دلفی هست. این لیست، شامل تمام مواقع بروز AVها نیست و نمی‌تواند باشد. شما می‌توانید AVهای دریافتی که در این لیست نیستند را در انجمن برنامه‌نویس مطرح کنید، تا به همراه هم، راه حلی برای آنها بیابیم.


*Calling a non-existing object*
همچون حالت قبل، بیشترین احتمال ایجاد AVها، استفاده از شیئی هست که هنوز ایجاد نشده و یا قبلاً ایجاد شده و از بین رفته است و می‌خواهید به آن رجوع کنید. برای جلوگیری از این نوع AV، مطمئن باشید که هر شیئی که به آن رجوع می‌کنید، ابتدا ساخته شده باشد. برای مثال، می‌خواهید جدولی را در رویداد OnCreate فرم، باز کنید، این جدول برروی Data Moduleیی قرار دارد که هنوز ساخته نشده ست(قبلاً از لیست فرم‌های Auto-Create حذف شده). 

در کد زیر، بعد از فراخوانی متد شیئی (b: TBitmap) که از حافظه‌ی مربوط به آن آزاد شده(شیئ از بین رفته)، یک AV اتفاق می‌افتد :

var b:TBitmap;
begin
 b:=TBitmap.Create;
 try
  //do something with b
 finally
  b.free;
 end; 
 ...
 //this will cause an AV - b does not longer exist
 b.Canvas.TextOut(0,0,'this is an Access Violation');
end;


*Invalid API parameter*
اگر سعی کنید که یک پارامتر نامعتبر به یک تابع API ویندوز ارسال کنید، یک AV اتفاق می‌افتد. بهترین راه برای حل این نوع AV، رجوع به راهنمای Win API برای کسب اطلاعات دقیق جهت فراخونی تابع و نوع پارامتری که تابع انتظار آن را دارد، هست. برای مثال، همواره مطمئن باشید که یک اشاره‌گر نامعتبر را برای پارامتر بافر ارسال نکنید.


*Let Delphi Free*
موقعی‌که یک شیئ، مالک شیئ دیگری ست، بگذارید شیئ مالک، حافظه‌ی مورد استفاده‌ی آن شیئ را از بین ببرد. از آنجائیکه به صورت پیش‌فرض، مالک همه‌ی فرم‌ها، شیئ Application هست، وقتی برنامه خاتمه پیدا می‌کند، شیئ Application نیز آزاد می‌شود، بنابراین تمام اشیائی که مالکشان Application هست نیز آزاد می‌شوند. برای مثال، اگر شما دو تا فرم دارید(Form1/Unit1 and Form2/Unit2)  و هر دو نیز به صورت خودکار ساخته می‌شوند (Auto-Create)، وقتی برنامه را اجرا کنید، کد زیر سبب بروز یک AV می‌شود :

unit Unit1;
...
uses unit2;
...
procedure TForm1.Call_Form2
begin
 Form2.ShowModal;
 Form2.Free;
 Form2.ShowModal;  //AV
end;


*Killing the exception*
هرگز به صورت واضح، شیئ موقتی خطا (E) را از بین نبرید، به این دلیل که بعد از کنترل کردن خطا، این شیئ به صورت خودکار از بین می‌رود. و اگر سعی کنید این شیئ را از بین ببرید، برنامه نیز سعی در از بین بردن دوباره‌ی این شیئ دارد(شیئی که دیگر وجود ندارد)، بنابراین یک Access Violation بوجود می‌آید :

Zero:=0;
try
 dummy:= 10 / Zero;
except
 on E: EZeroDivide do
   MessageDlg('Can not divide by zero!',
               mtError, [mbOK], 0);
 E.free; // causes an access violation
end;


*Indexing an empty string*
یک رشته خالی حاوی اطلاعات معتبری نیست، بنابراین، استفاده از اندیس یکی از خانه‌های یک رشته‌ی خالی، شبیه به دستیابی به مقدار nil و در نتیجه وقوع یک Access Violation هست.

var s: string;
begin
 s:='';
 s[1]:='a';   //AV
end;


*Dereferencing pointers*
برای رسیدن به مقدار یک اشاره‌گر باید از dereferencing برای اشاره‌گر استفاده کنید، به این شکل که علامت ^ را در انتهای اشاره‌گر قرار دهید تا بتوانید مقدار آن را بگیرید، در غیر اینصورت، در مثال زیر، شما آدرس اشاره‌گر را خواهید گرفت(خط قرمزرنگ) و قصد دارید آدرس آن را در اشاره‌گر دیگری کپی! کنید، که این نیز باعث ایجاد یک AV می‌شود.


procedure TForm1.Button1Click(Sender: TObject);
var
  p1 : pointer;
  p2 : pointer;
begin
  GetMem(p1, 128);
  GetMem(p2, 128);
 {This line may cause an access violation}
  Move(p1, p2, 128);
 {This line is correct}
  Move(p1^, p2^, 128);
  FreeMem(p1, 128);
  FreeMem(p2, 128);
end;

----------


## AlirezaBahredar

با تشکر از ترجمه شما آقای خرسندی.
لازم می دونم به شخصه لینک منبع این مطلب رو اینجا بزارم....
http://delphi.about.com/od/objectpas.../aa052201b.htm

----------

