Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

DLL & Threads [D6, Win2k]


Dynamit ©   (2002-10-22 16:57:00) 

Может знает кто, помогите. Проблема такая - в DLL создается поток в котором происходит запрос к БД. Сигнал готовности - сообщение приложению. В принципе все отрабатывает, кроме выгрузки библиотеки. Создание потока происходит таким образом:


Function QueryExec(AHndl: THandle; Number: Integer; ASQL, AFNum: PChar): THandle; SafeCall;
var
QueryForm: TFQThread;
begin
Application.Handle:=AHndl;
QueryForm := TFQThread.Create(Application);
QueryForm.Visible:=False;

With TQueryThread.Create(QueryForm) Do
Begin
Result:=ThreadId;
QueryForm.UNum:=Number;
QueryForm.USQL:=String(ASQL);
QueryForm.UFName:=String(AFNum);
QueryForm.UHndl:=AHndl;
OnTerminate:=Term;
Resume;
End;
//QueryForm.Free;
end;


Если раскоментировать освобождение QueryForm - будет ошибка при выполнении тела потока.
Если же не создавать поток (убрать блок With) и раскоментировать освобождение QueryForm, то функция выгрузки библиотеки отрабатывает нормально.

Метод Create для TThread:


constructor TQueryThread.Create(AQueryForm: TFQThread);
begin
QueryForm := AQueryForm;
FreeOnTerminate := True;
inherited Create(True);
end;


????


Dynamit   (2002-10-24 09:35:00) [1]

Ураааа!

Все заработало. Объединение последнего совета Набережных С. с предудущими советами (не всеми), пара тройка десятков опытов - вот и все, что нужно было для успеха.

Всем участникам - огромное человеческое спасибо!

Отдельное спасибо господину Набережных С.

До встречи.


Dynamit   (2002-10-24 08:55:00) [2]

При выгрузке библиотеки осталась ошибка Access violation.

Но был странный момент - не знаю связано это или нет:

При первом запуске после загрузки компа при появлении данных в таблице (Table1.Open) ошибок не было, и можно было работать с таблицей, но при закрытии библиотеки произошла ошибка доступа (Access violation) с зависанием проги (была закрыта с помощью диспетчера). При последующих запусках программа зависала при выгрузке библиотеки (т.е. таблицу я видел, но сделать ничего не успевал). Поставил showMessage после FreeLibrary(LibHandle) - ошибку выдал раньше, чем мое сообщение.

Есть идея попробовать блок Try ... Exception End для удаления ошибки - но ведь это не правильно!

Других идей нет :( и все опускается вместе с руками!


Набережных С.   (2002-10-23 18:04:00) [3]

Например, так:

Function QueryExec(AHndl: THandle; Number: Integer; ASQL, AFNum: PChar): THandle; SafeCall
begin
...
With TQueryThread.Create(QueryForm) Do
Begin
Result:=Handle;
....
end;

procedure TForm1.Button2Click(Sender: TObject);
Var
LibHandle: THandle;
QExec: TQueryExec;
CurHndl: LongInt;

HRes: integer;

begin
LibHandle:=LoadLibrary("ASQuery.Dll");
Try
If LibHandle=0 Then
Raise EDLLLoadError.Create("Не могу загрузить DLL");
@QExec:=GetProcAddress(LibHandle,"QueryExec");
If @QExec <> nil Then

begin
CurHndl:=QExec(Handle, 1, PChar(Query), PChar("Tmp\Proba.db"));
Button2.Enabled:=False;
Button1.Enabled:=True;
fReady:=False;

repeat
Application.ProcessMessages;
HRes:=WaitForSingleObject(CurHandle,100);
until (HRes <> WAIT_TIMEOUT) or fReady;
if HRes = WAIT_ABANDONED then RaiseLastWin32Error
else if fReady then (отменено)

@QExec:=nil;
Table1.Open;
end;
Finally
FreeLibrary(LibHandle);
End;
end;


Dynamit   (2002-10-23 16:37:00) [4]


> Набережных С.


Маленькая просьба, если не трудно, покажи реализацию любого из описанных тобой методов ожидания завершения потока. Обещаю проработать и сообщить результаты :)
А в плане того, где жду - это здесь:

procedure TForm1.Button2Click(Sender: TObject);
Var
LibHandle: THandle;
QExec: TQueryExec;
CurHndl: LongInt;
begin
LibHandle:=LoadLibrary("ASQuery.Dll");
Try
If LibHandle=0 Then
Raise EDLLLoadError.Create("Не могу загрузить DLL");
@QExec:=GetProcAddress(LibHandle,"QueryExec");
If Not (@QExec=nil) Then
CurHndl:=QExec(Handle, 1, PChar(Query), PChar("Tmp\Proba.db"));
Button2.Enabled:=False;
Button1.Enabled:=True;
fReady:=False;
While Not fReady Do
Begin
Application.ProcessMessages;
Sleep(100);
End;
Sleep(10);
@QExec:=nil;
Table1.Open;
Finally
FreeLibrary(LibHandle);
End;
end;


Это тестовый вариант, так что строго не судите ;)


Набережных С.   (2002-10-23 16:21:00) [5]

Вынужден настаивать(хотя, разумеется, могу и ошибиться) - наиболее вероятно, что при освобождении DLL ошибка возникает из-за того, что код, находящийся в библиотеке, в это время выполняется. "жду еще Sleep(100)" - не понятно, где. Лучше покажи, как ждешь. А ждать лучше не сообщения, а изменения состояния хендла - это гарантирует окончание потока. И форму можно освободить по окончании ожидания. Но возникновение OnTerminate, как и факт уничтожения TThread вызовом деструктора по флагу FreeOnTerminate - еще не гарантия, что код потока завершился. Точнее, обработчик OnTerminate вызывается, когда поток еще исполняется, но после возврата из Execute. Единственная, повторюсь, гарантия завершения кода потока - ожидание хендла(можно и через TThread.WaitFor, хотя имхо лучше через API).

>Кстати насколько верно утверждение, что после завершения метода Execute освобождается сам объект потока?
Дельфийская реализация гарантирует вызов деструктора TThread по окончании Execute, если FreeOnTerminate = true, но это еще не гарантия завершения потока в понимании ОС. Далее следует еще выполнение некоторого завершающего кода в контексте потока.


Dynamit   (2002-10-23 10:30:00) [6]


> Bis

Уже сделал - результат тот же. Главное ведь что обидно - данные на экране я получаю, а работать могу только до FreeLibrary :(


Dynamit   (2002-10-23 10:28:00) [7]


> panov

Извини, начало просмотрел :)

Но все равно нет в жизни счастья - глюк остался.

Есть у меня еще одна мысль - ведь в функции QueryExec переменная QueryForm - локальная, а значит по завершении будет недоступна...

Нет, перенес ее во внешний блок Var - глобальный для DLL - глюк остался.


Bis   (2002-10-23 10:22:00) [8]

сделай глобальную переменную и напиши уничтожение в OnTerminate, больше идей нету ...


panov   (2002-10-23 10:06:00) [9]

>Dynamit (23.10.02 09:56)
Прочитай внимательно то, что написано у меня...
Уничтожай форму в методе Execute, а не в основном потоке.


Dynamit   (2002-10-23 09:56:00) [10]


> panov

Еще раз повторяю - это для експеримента, потому и закоментировано.


> Bis

caFree не помогает :(


panov   (2002-10-23 09:37:00) [11]

QueryForm.Free;вызывай в коде Execute- в конце.

With TQueryThread.Create(QueryForm) Do
Begin
Result:=ThreadId;
QueryForm.UNum:=Number;
QueryForm.USQL:=String(ASQL);
QueryForm.UFName:=String(AFNum);
QueryForm.UHndl:=AHndl;
OnTerminate:=Term;
Resume;
End;
QueryForm.Free;

//здесь у тебя форма уничтожается доокончания работы потока, поэтому и возникает exception


Bis   (2002-10-23 09:11:00) [12]

Я тут не то в прошлый раз написал, каюсь

Про освобождение формы уже писали, у тебя она автоматически убъется Application. Если хочешь сам, то в методе OnClose формы поставь значение caFreе


Dynamit   (2002-10-23 08:37:00) [13]


> Игорь Шевченко


Вот метод Execute:

procedure TQueryThread.Execute;
begin
Try
try
with QueryForm do
begin
SThr.SessionName := Format("A%s%x", [SThr.Name,UNum]);
DB1.SessionName := SThr.SessionName;
DB1.DatabaseName := Format("A%s%x", [DB1.Name,UNum]);
QInd.SessionName := DB1.SessionName;
QInd.DatabaseName := DB1.DatabaseName;
QInd.SQL.Text:=USQL;
If (Not Terminated) Then
Begin
Table1.TableName:=Format(UFName,[UNum]);
Table1.SessionName:=SThr.SessionName;
CrtRcv; // Создание таблицы-приемника
UCnt:=BathMv; // Получение данных
SendMsg(PM_DATAREADY,UNum,UCnt) //Данные готовы
End;
End;
except
on E: Exception do
begin
{ Display any error we receive on the status line }
MessageText := Format("Хмм "+QueryForm.QInd.SQL.Text+#13+" %s: %s.", [E.ClassName, E.Message]);
Synchronize(DisplayMessage);
end;
End;
Finally
QueryForm.QInd.Close;
QueryForm.SThr.Active:=False;
QueryForm.DB1.Connected:=False;
End;
end;



> Bis (22.10.02 17:27)
> ты кажется дважды пытаешь освободить поток
> выбирай или FreeOnTerminate или сам вызывай Free


Я освобождаю форму, а не поток.


> NailS


Дк я ведь согласен, но делать то что-то надо, а освобождал форму только ради эксперимента, ведь где-то ее надо освобождать!


> Набережных С.


Из метода Execute для потока (приведено выше), видно, что я ловлю сообщение о готовности данных. Вот по его приходу я жду еще Sleep(100), и только затем выгружаю библиотеку. (Кстати насколько верно утверждение, что после завершения метода Execute освобождается сам объект потока?).

Так что, пока ситуация темна как ночь!


Набережных С.   (2002-10-22 17:57:00) [14]

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


NailS   (2002-10-22 17:53:00) [15]


> Если раскоментировать освобождение QueryForm - будет ошибка
> при выполнении тела потока.

Не удивительно.
У тебя же поток стал работать после Resume, и небось в работе использует QueryForm, которой ты следом за вызовом Resume делаешь QueryForm.Free.

QueryExec - это функция экспортируемая из длл?


Bis   (2002-10-22 17:27:00) [16]

ты кажется дважды пытаешь освободить поток
выбирай или FreeOnTerminate или сам вызывай Free


Игорь Шевченко   (2002-10-22 17:22:00) [17]

А Execute для TThread ?


Наверх

Титульная страница Поиск, карта сайта Написать письмо