![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Многопоточность в DLL, отрисовка в приложении
sally © (2006-01-12 18:40:00)Есть класс от TThread. Класс простейший: поиск файлов по диску. Отображать необходимо текущий файл и отрисовывать необходимую информацию. Но есть "но": передавать при создании объетка Canvas нельзя. Для "визуализации" процесса нужно использовать интерфейсы.
IVisualizer = interface
procedure Progress(AFile: string);
end;
TFileSearch = class(TThread)
private
FVisualizer: IVisualizer;
protected
procedure DoVisualize;
procedure BuildFileList(const APath: string);
procedure Execute; override;
public
constructor Create(AVisualizer: IVisualizer; const APath: string);
end;
TVisualizer = class(TForm, IVisualizer)
procedure Progress(AFile: string);
end;
procedure TFileSearch.Execute;
begin
BuildFileList(FPath);
end;
procedure TFileSearch.BuildFileList(const APath: string);
...
begin
....
/// Поиск файлов
Synchronize(Draw);
....
end;
procedure TFileSearch.Draw;
begin
// Если использовать просто напрямую Canvas, то все ок
// Canvas.TextOut(10, 100, CurrentFile);
//
FVisualizer.Progress(CurrentFile);
// При таком варианте возникает Exception
end;
procedure TForm1.Progress(AFile: string);
begin
Canvas.TextOut(10, 100, AFile);
end;
Реально ли эту проблему обойти? Если да, то как?
sally (2006-01-24 16:58:00) [1]>simpson
>Кто отменил необходимость синхронизации
А поподробней пожалуйста. Где в главной форме нужно делать синхронизацию и как?
simpson (2006-01-16 23:31:00) [2]1. Непонятно назначение свойства
property CurrentFile: string read FCurrentFile write FCurrentFile;
К этому свойству есть 2 обращения, причем из объекта того же класса, где свойство объявлено. При этом read- и write-методов как таковых нет.
Т. о., обращения к полю класса хватило бы:
FCallBack.Progress(AFileName);
2. Перегрузка деструктора, у которого тело состоит из вызова деструктора предка, не оправдана:
destructor TFileSearch.Destroy;
begin
inherited;
end;
3. Обработку исключений в потоках никто не отменял. В Execute должна присутствовать конструкция вида
try
...
except
...
on E: ....
...
end
4. Зачем создавать suspended-поток в данном случае?
5. Вообще, странный способ создавать объекты:
with TFileSearch(GetProc(Self, 'c:\')) do
Так будет лучше:
with TFileSearch.Create(GetProc(Self, 'c:\')) do
6. Кто отменил необходимость синхронизации и для чего вызов Application.ProcessMessages??
procedure TVisualizer.Progress(AFile: string);
begin
if SameText(AFile[1], 'C') then
Canvas.TextOut(10, 100, AFile)
else
Canvas.TextOut(10, 200, AFile);
Application.ProcessMessages;
end;
Где хотя бы вызов Lock и UnLock для Canvas?
Canvas.Lock;
try
Canvas.TextOut(...);
finally
Canvas.Unlock;
end;
Читаем справку по Canvas и Application.ProcessMessages.
В общем, до DLL пока не дошли ))). Рекомендую сделать это пока в одном проекте.
sally (2006-01-16 19:00:00) [3]>simpson
Согласен, что с Callback - не лучший вариант.
>Автор? Приведи весь проблемный код
Код DLL
unit Unit2;
interface
uses
Classes, untIntf, Windows;
type
TCallback = procedure(AFile: string);
TFileSearch = class(TThread)
private
FPath: string;
FCurrentFile: string;
FCallBack: IVisualizer;
protected
procedure Draw;
procedure BuildFileList(const APath: string);
procedure Execute; override;
public
constructor Create(ACallBack: IVisualizer; const APath: string);
destructor Destroy; override;
property CurrentFile: string read FCurrentFile write FCurrentFile;
end;
implementation
{ TFileSearch }
procedure TFileSearch.BuildFileList(const APath: string);
var
AFindFileData: TWin32FindData;
AFileName: string;
AFile: cardinal;
begin
AFile := FindFirstFile(PAnsiChar(APath + '*'), AFindFileData);
try
if AFile <> INVALID_HANDLE_VALUE then
repeat
if AFindFileData.cFileName[0] = '.' then Continue;
AFileName := APath + string(AFindFileData.cFileName);
CurrentFile := AFileName;
FCallBack.Progress(CurrentFile);
if (AFindFileData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) <> 0 then
BuildFileList(AFileName + '\');
until not FindNextFile(AFile, AFindFileData);
finally
Windows.FindClose(AFile);
end;
end;
constructor TFileSearch.Create(ACallBack: IVisualizer; const APath: string);
begin
FPath := APath;
FCallBack := ACallback;
inherited Create(True);
end;
destructor TFileSearch.Destroy;
begin
inherited;
end;
procedure TFileSearch.Draw;
begin
//FVisualizer.Progress(CurrentFile);
end;
procedure TFileSearch.Execute;
begin
BuildFileList(FPath);
end;
end.
unit untIntf;
interface
type
IVisualizer = interface
['{2C787F2F-C3AF-4B0E-977E-15CC0E1C8B24}']
procedure Progress(AFile: string);
end;
implementation
end.
type
TVisualizer = class(TForm, IVisualizer)
Button1: TButton;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
FFileSearch: TFileSearch;
procedure Progress(AFile: string);
public
{ Public declarations }
end;
var
Visualizer: TVisualizer;
implementation
{$R *.dfm}
uses
untLoadLib;
{ TForm1 }
procedure TVisualizer.Button1Click(Sender: TObject);
begin
with TFileSearch(GetProc(Self, 'c:\')) do
Resume;
end;
procedure TVisualizer.Button2Click(Sender: TObject);
begin
with TFileSearch(GetProc(Self, 'd:\')) do
Resume;
end;
procedure TVisualizer.Progress(AFile: string);
begin
if SameText(AFile[1], 'C') then
Canvas.TextOut(10, 100, AFile)
else
Canvas.TextOut(10, 200, AFile);
Application.ProcessMessages;
end;
Все собственно. Это только заготовка. На ней я и застопорился.
simpson (2006-01-13 19:54:00) [4]sally (13.01.06 10:52) [3]
В чем изврат, описано в моем посте. Даже с примером.
Ошибка в проектировании. Если есть "нечто", предназначенное для выполнения "чего-то", то пусть это "нечто" и занимается своими делами.
Поток ищет файлы - пусть ищет. Его задача - найти файл и "дернуть" метод интерфейса. Все. Что делает интерфейс, поток волновать недолжно. И как он это делает, тоже потоку фиолетово.
Реализация интерфейса должна рисовать статистику в GUI - вот пусть рисует и самазаботится о том, как обеспечить потокобезопасность.
В твоем же примере это делается в потоке. Почему так делать не нужно, я пояснил на примере с сервисом.
Почему так делать необходимо:
1. Соблюдается принцип модульности проекта.
2. Функционал модулей четко разделен по области применения и не зависит от реализации самих модулей.
3. Synchronize - это в VCL. Завтра тебе нужно будет сделать то же самое без VCL. Либо используя другой framework. Или вообще не на Delphi. Нужна универсальность.
Не согласен с критиками, предлагающими использовать коллбэки или механизм оконных сообщений. Интерфейсы - подход более универсальный, и, как показала практика, более жизнеспособный (.NET).
Автор вопроса, безусловно, не Нострадамус, и не может предсказать, что будет с проектом и как он будет развиваться. Поэтому механизм взаимодействия модулей проекта нужно продумать изначально. Если это - просто "для себя", то даже в этом случае нужно учиться грамотно проектировать приложения - дальше будет проще.
Автору рекомендую почитать это:
http://rsdn.ru/article/patterns/patterns.xml
Да и вообще будут не лишними все статьи этого раздела.
ЗЫ. Автор? Приведи весьпроблемный код. Почему у тебя это уже как минимум два человека выпрашивают?
han_malign (2006-01-13 17:34:00) [5]Какой Canvas, какой Draw - это и без потоков работать не будет...
SendMessage(<control>.Handle, CM_INVALIDATE, 0, 0) без всяких Syncronize, и в OnDraw доступ к защищенному критической секцией полю(SendMessage вне критической секции, иначе deadlock)...
Digitman (2006-01-13 13:30:00) [6]
> в чем проблема с использованием интерфейсов?
проблема - в ошибке в 17-й строке
sally (2006-01-13 13:11:00) [7]>Digitman
Спасибо. Но в чем проблема с использованием интерфейсов?
Digitman (2006-01-13 12:59:00) [8]TMyCallback = procedure(AFile: string);
TFileSearch = class(TThread)
private
FPath: String;
FCallback: TMyCallback;
protected
procedure Execute; override;
public
constructor Create(ACallback: TMyCallback; const APath: string);
end;
..
constructor TFileSearch.Create;
begin
FPath := APath;
FCallback := ACallback;
inherited Create(False);
end;
procedure TFileSearch.Execute;
begin
..
/// Поиск файлов
FCallback(Currentfile);
..
end;
sally (2006-01-13 12:34:00) [9]Как сделать не "выежываясь"?
И в чем заключается "выеживание"? Что нужно сделать, чтобы этот метод заработал?
Digitman (2006-01-13 12:31:00) [10]
> в чем принципиальная разница?
ради одного-единственного класса, реализующего тот самый интерфейс. выёживаться с интерфейсами нет никакого резона.
sally (2006-01-13 12:20:00) [11]А в чем принципиальная разница? Несколько
Digitman (2006-01-13 12:06:00) [12]В твоих хост-приложениях, потенциально использующих эту ДЛЛ, могут фигурировать множество форм, класс каждой из которых реализует этот интерфейс ?
sally (2006-01-13 11:59:00) [13]>Digitman
Только Delphi-программами. Я бы даже сказал, что только своей программой(90%).
Digitman (2006-01-13 11:57:00) [14]я еще раз спрашиваю : твоя ДЛЛ по твоей идее м.б. использована НЕ только Делфи-программами?
это КРАЙНЕ важно.
sally (2006-01-13 11:48:00) [15]смысл в том, чтобы при изменении интерфейса основного приложения, не нужно было бы изменять DLL.
Digitman (2006-01-13 11:43:00) [16]каков смысл вынесения сабжа в ДЛЛ ?
твоя ДЛЛ по твоей идее м.б. использована НЕ только Делфи-программами ?
sally (2006-01-13 11:35:00) [17]>Digitman
Да
Digitman (2006-01-13 11:30:00) [18]
> В DLL реализован поиск файлов
т.е. класс TFileSearch объявлен и реализован в DLL ?
sally (2006-01-13 10:52:00) [19]> simpson
>IMHO, изврат полный.
Изврат в чем? В том как устроен обмен между DLL и приложением?
Exception: Canvas does not allow drawing
>код реализации IVisualizer
procedure TForm1.Progress(AFile: string);
begin
Canvas.TextOut(10, 100, AFile);
end;
>Digitman
В DLL реализован поиск файлов.
Каким образом еще можно реализовать обмен данными между DLL(Service, Application)? Кроме разве что SendMessage ничего в голову не приходит. И имеет ли этот вариант(с Interface) право на жизнь?
Digitman (2006-01-13 08:12:00) [20]А DLL здесь причем ?
Всуе упомянута или как ?