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

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

Многопоточность в DLL, отрисовка в приложении


Страницы:   1 2

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 здесь причем ?
Всуе упомянута или как ?


Страницы:   1 2

Наверх

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