PDA

View Full Version : خطای ناشناخته



joker
دوشنبه 31 خرداد 1395, 10:21 صبح
سلام
ی نرم افزار دارم که گاهی وقتها خطای زیر را میده
متاسفانه زمان و مکان خاصی نداره این خطا
یهو 2 روز هیچ خبری ازش نیست
یهو بعد 2 روز کارکردن سیستم یهو پیام میده
از XE5 استفاده میکنم
ارتباط با دیتابیسم ado هست
سیستمم مولتی ترد هست و ازQueue صف استفاده میکنم.

تمام فانکشن ها و پروسیجر ها را در tRY EXCEPT قراردادم ولی متاسفانه هیچ وقت نفهمیدم این خطا از کجا میاد :افسرده:

Mahmood_M
دوشنبه 31 خرداد 1395, 13:44 عصر
لیست فیلدهای یک Query یا Table رو از جایی میخونید و Add می کنید ؟
مثلا به عنوان پارامتر درون یک Function یا Procedure یا اینکه مثلا Result یک Function رو بهش اختصاص بدید

joker
دوشنبه 31 خرداد 1395, 16:52 عصر
من توی این سیستم کلا دو حالت دسترسی به دیتابیس دارم
یکی تابع نوشتم برای اجرای دستورات sql

Procedure QUERY_EXEC(sql:string);
var
QUERY:TADOQuery;
begin
try
Query := TADOQuery.Create(nil);
QUERY.Connection:= DM.ADOConnection1;
QUERY.Close;
QUERY.SQL.Clear;
QUERY.SQL.Add(SQL);
QUERY.ExecSQL;
QUERY.Close;
FreeAndNil(QUERY);
except


end;




end;


و حالت دوم دسترسی به دیتابیس که برای وقتیه که میخوام اطلاعاتی را مقدارشو داشته باشم :
ی چیزی تو مایه زیر

var
XQuery:TADOQuery;
begin



XQuery := TADOQuery.Create(nil);
XQUERY.Connection:= DM.ADOConnection1;
XQUERY.Close;
XQUERY.SQL.Clear;
XQUERY.SQL.Add(SQL);
XQUERY.Open;
sleep(1);




if XQuery.RecordCount>0 then
begin
XQuery.Close;
FreeAndNil(XQuery);


result :='OK';
exit;


end




این دستورات در حالت نرمال دارند کار میکنند .... ولی بعضی وقتا این ارور را میده و اصلا نمیدونم چرا ، روتین ها ثابتن :( دستورات تکرارین :(

:اشتباه:

Mahmood_M
دوشنبه 31 خرداد 1395, 18:20 عصر
از این دستورات داخل Thread استفاده می کنید ؟ ( چون گفتید که برنامه MultiThread هستش )
برای ارتباط با DataBase درون Thread باید یک Connection جداگانه Create کنید، درواقع هر Thread باید کانکشن مخصوص به خودش رو داشته باشه یا به عبارت دیگه، نباید از طریق چند Thread به یک Connection دسترسی داشته باشید
علت اون پیغام می تونه این باشه که List فیلدهای ADOQuery داره Nil میشه ولی خود ADOQuery هنوز Nil نشده
دستور زیر همچین خطایی برای یک TStrings ایجاد می کنه :


procedure TMainFrm.Button1Click(Sender: TObject);
begin
SetItems(ComboBox1.Items);
end;

procedure TMainFrm.SetItems(SList: TStrings);
var
S : TStrings;
begin
S := nil;
SList.Assign(S);
end;

با توحه به اینکه این پیغام فقط در بعضی مواقع اتفاق می افته حدس میزنم علتش استفاده یک کانکشن در چند Thread باشه، این موضوع باعث تداخل کار Connection و ارتباط اون با Query هایی که بهش وصل شدن میشه و مشکلات دیگه ای رو هم ایجاد خواهد کرد
یا ممکنه خطا در جای دیگه ای غیر از این کدهایی که قرار دادید اتفاق می افته
برای هر Thread یک Connection جدا بسازید و یا Query رو با مقداردهی خاصیت ConnectionString مستقیما به بانک وصل کنید

چند نکته در مورد کدها :
Object ها رو در خارج Try بسازید ( Create رو قبل از Try بذارید )
در دستور اول در ExecSQL خطایی رخ بده، Query ساخته شده Free نمیشه
Exception ها رو به اون صورت که در کد اول قرار دادید اصطلاحا قورت ندید
در کد دوم اگر RecordCount برابر صفر باشه، Query ساخته شده Free نمیشه

برای کنترل Create و Free کردن و Handle کردن Error های Query ، کدهاتون رو به صورت زیر تغییر بدید :


Procedure QUERY_EXEC(SQL : String);
var
QUERY : TADOQuery;
begin
Query := TADOQuery.Create(nil);
try
QUERY.ConnectionString := '...';
QUERY.Close;
QUERY.SQL.Clear;
QUERY.SQL.Add(SQL);
try
QUERY.ExecSQL;
except
on E:Exception do
begin
ShowMessage('Execution Error : ' + E.Message);
end;
end;
finally
FreeAndNil(QUERY);
end;
end;

...

var
XQuery:TADOQuery;
begin
XQuery := TADOQuery.Create(nil);
try
XQUERY.ConnectionString := '...';
XQUERY.Close;
XQUERY.SQL.Clear;
XQUERY.SQL.Add(SQL);
try
XQUERY.Open;
except
on E:Exception do
ShowMessage('Execution Error : ' + E.Message);
end;
finally
if XQuery.Active = True then
begin
if XQuery.RecordCount > 0 then
begin
XQuery.Close;
Result := 'OK';
end;
end;

FreeAndNil(XQuery);
end;

golbafan
دوشنبه 31 خرداد 1395, 18:40 عصر
سلام
اینکه کانکشن در ترد اصلی باشه معمولا مشکلی ایجاد نمیکنه
خطای ایجاد شده هم مربوط میشه به این جا:
XQUERY.SQL.Add(SQL);
XQUERY.Open;

احتمالا گاهی کوئری هات مشکل میخورن
یک ApplicationEvent براز توی برنامه و اون try ها رو هم برشون دار
بعد توی رویداد onException یک فایل لاگ بنویس بصورت متن با درج تاریخ/ساعت برای هر خطا
ببین چه اتفاقاتی میوفته :لبخندساده:

درضمن بعد از تولید کوئری میتونی خلاصه تر و با امنیت بهتری هم عمل کنی:
XQuery := TADOQuery.Create(nil);
XQUERY.Connection:= DM.ADOConnection1;
XQUERY.SQL.Text:=SQL;
XQUERY.Open;

Mahmood_M
دوشنبه 31 خرداد 1395, 19:20 عصر
اینکه کانکشن در ترد اصلی باشه معمولا مشکلی ایجاد نمیکنه
استفاده از کانکشن جداگانه برای هر Thread یکی از اصول کار با Thread هاست، ممکنه در برخی موارد مشکلی "مشاهده" نشه، وقتی کانکشن توسط Query دیگه ای در صفحه اصلی داره استفاده میشه و شما قصد دارید از همون کانکشن در داخل Thread دیگه ای استفاده کنید، مشکلاتش نمایان میشه
چیزی که مشکل ایجاد می کنه "استفاده همزمان دو Thread از یک Connection هستش"، اما اگر Connection در Thread اصلی هیچ استفاده ای نشه، معمولا مشکلی هم مشاهده نمیشه
یکی از مواردی که در کار با Thread ها باید همیشه رعایت بشه استفاده از Connection جداگانه هستش
قبلا هم در چند تاپیک در این مورد صحبت شده اما به عنوان یک مثال ساده در اینجا (http://delphi.about.com/od/kbthread/a/query_threading.htm) یک نمونه وجود داره و به این مورد هم اشاره کرده :


There are 3 traps you need to know how to solve when creating multithreaded Delphi ADO database applications:

CoInitialize and CoUninitialize must be called manually before using any of the dbGo objects. Failing to call CoInitialize will result in the "CoInitialize was not called" exception. The CoInitialize method initializes the COM library on the current thread. ADO is COM.
You *cannot* use the TADOConnection object from the main thread (application). Every thread needs to create its own database connection.
You must use the Synchronize procedure to "talk" to the main thread and access any controls on the main form.

...


خطای ایجاد شده هم مربوط میشه به این جا:
Open کردن Query و مشکل داشتن Query نوشته شده، به نظر نمیرسه باعث Nil شدن لیست فیلدها بشه

به هر حال، بهترین کار اینه که در حالت دیباگ وقتی که خطا اتفاق افتاد اطلاعات دیباگ رو بررسی کنید
باید مشخص بشه که در کدوم دستور این خطا اتفاق می افته
شاید در جایی غیر از این دستوراتی که قرار دادید باشه

golbafan
سه شنبه 01 تیر 1395, 12:49 عصر
اوه...
درسته من یادم رفت بگم نیاز به سینکرونایز داره :لبخند:
به نظر میرسه استفاده از کانکشن استرینگ خود کوئری اینجا مفیدتر باشه


مطالعه بیشتر:
http://delphi.about.com/od/kbthread/a/query_threading.htm
(http://delphi.about.com/od/kbthread/a/query_threading.htm)



ویرایش:
راستی یه جا خوندم وقتی از COM در ترد استفاده میکنید باید از تابع CoInitialize هم برای کارکرد صحیح برنامه COM استفاده کنی

joker
سه شنبه 01 تیر 1395, 13:38 عصر
من الان استفاده از کانکشن استرینگ را جایگزین استفاده از کانکشن اصلی کردم.

CoInitialize را هنوز استفاده نکردم . ( تاحالا خطایی که بخاطر استفاده از com ابجکتم باشه نگرفتم ( یکبار طی 2ماه گذشته اونم احتمالا به خاطر قطعی ارتباط با سرور بود وسط کار)

create و free را بردم بیرون از try except

MadExcept را هم فعال کردم ببینم چی میشه سرانجام این داستان :)

برنامه را فعلا اجرا کردم تا ببینم این دوسه روزه قات میزنه یا نه - اگه زد ، منو حلال کنید ممکنه رگمو بزنم دیگه :)))

ممنون از دوستان
:قلب::قلب:

Mahmood_M
سه شنبه 01 تیر 1395, 14:54 عصر
را هنوز استفاده نکردم . ( تاحالا خطایی که بخاطر استفاده از com ابجکتم باشه نگرفتم ( یکبار طی 2ماه گذشته اونم احتمالا به خاطر قطعی ارتباط با سرور بود وسط کار)
CoInitialize رو باید فراخوانی کنید
من فرض رو بر این گذاشتم که قبل از فراخوانی تابع اون رو فراخوانی می کنید، چون فراخوانی نشه پیغام خطا میده
احتمالا چون کانکشن از خارج Thread متصل شده پیغام خطا نداده
اون تغییرات رو که اعمال کردید، CoInitialize رو هم فراخوانی کنید :)
رعایت نکردن اصول کار با Thread ها مشکلات نامشخصی ایجاد می کنه که معمولا پیدا کردن منبع خطا هم بسیار مشکل میشه

پیشنهاد می کنم از ADO استفاده نکنید و به جاش از UniDAC استفاده کنید که به صورت مستقیم به بانک وصل میشه و به واسط COM نیاز نداره، UniDAC کاملا Thread-Safe هستش و خیلی راحت می تونید ازش استفاده کنید، علاوه بر این امکانات بسیار بیشتری نسبت به ADO داره، به طور خلاصه با وجود UniDAC ( یا FireDAC ) دلیلی برای استفاده از ADO نمی مونه !

joker
سه شنبه 01 تیر 1395, 17:18 عصر
خب mad exception ی چیزایی بهم گفت -
عکس ضمیمه
چندتایی از try except ها را کم کردم ببینم نقطه اصلی را بهم میگه کجاست ( محل خطا هم جالبه - یکی از فایلهای اصلی دلفی - چون احتمال دادن بخاطر try نتونسته دقیق بگه چی به چیه از پردازش خطای دستی خارج کردم روتین را و فعلا منتظرم دوباره خطا را بده...

و مورد عجب برام اینکه قستمی که ارور داده یعنی ساخته شدن استرینگ لیست بسیار ساده و بدون هیچ کد خاصی هست ( data هم یک رشته ساده متنی هست)

Stdata := TStringList.Create;
STdata.Clear;
STdata.Text:= data;



CoInitialize را هم اضافه کردم ولی هنوز اجرا نکردم منتظرم این فایلی که اجرا کردم قات اولشو بزنه تا یباره آپدیت کنم.

BORHAN TEC
چهارشنبه 02 تیر 1395, 00:17 صبح
پیشنهاد میکنم این سه مطلب رو به ترتیب بخونید:
http://chrisbensen.blogspot.com/2007/06/delphi-tips-and-tricks.html
http://chrisbensen.blogspot.com/2007/06/delphi-tips-and-tricks_20.html
http://chrisbensen.blogspot.com/2007/06/delphi-tips-and-tricks_26.html