Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2006.02.26;
Скачать: CL | DM;

Вниз

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

 
sally   (2006-01-12 18:40) [0]

Есть класс от 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;

Реально ли эту проблему обойти? Если да, то как?


 
simpson ©   (2006-01-12 21:03) [1]

IMHO, изврат полный.

Есть IVisualizer. Ну и пусть его реализация думает, как вывести информацию на форму. Никакого использования Synchronize здесь быть не должно. Все действия по синхронизации - только в IVisualizer.Progress.

В качестве обоснования приведу пример - завтра тебе сказали выводить информацию не на форму, а, скажем, в файл. Здесь уже синхронизация с VCL-потоком не нужна, а никуда не денешься - она у тебя жестко прибита.

Или твой поток из GUI-приложения перекочевал в сервис. То же самое.

Ни о каких Canvas поток, производящий поиск файлов, конечно же, знать не должен. "Мухи отдельно - котлеты отдельно". (С)

По поводу Exception - давай текст исключения, тип исключения и код реализации IVisualizer. Телепатически сегодня не угадыватеся что-то.


 
Digitman ©   (2006-01-13 08:12) [2]

А DLL здесь причем ?
Всуе упомянута или как ?


 
sally   (2006-01-13 10:52) [3]

> 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 11:30) [4]


> В DLL реализован поиск файлов


т.е. класс  TFileSearch объявлен и реализован в DLL ?


 
sally   (2006-01-13 11:35) [5]

>Digitman
Да


 
Digitman ©   (2006-01-13 11:43) [6]

каков смысл вынесения сабжа в ДЛЛ ?

твоя ДЛЛ по твоей идее м.б. использована НЕ только Делфи-программами ?


 
sally   (2006-01-13 11:48) [7]

смысл в том, чтобы при изменении интерфейса основного приложения, не нужно было бы изменять DLL.


 
Digitman ©   (2006-01-13 11:57) [8]

я еще раз спрашиваю : твоя ДЛЛ по твоей идее м.б. использована НЕ только Делфи-программами ?

это КРАЙНЕ важно.


 
sally   (2006-01-13 11:59) [9]

>Digitman
Только Delphi-программами. Я бы даже сказал, что только своей программой(90%).


 
Digitman ©   (2006-01-13 12:06) [10]

В твоих хост-приложениях, потенциально использующих эту ДЛЛ, могут фигурировать множество форм, класс каждой из которых реализует этот интерфейс ?


 
sally   (2006-01-13 12:20) [11]

А в чем принципиальная разница? Несколько


 
Digitman ©   (2006-01-13 12:31) [12]


> в чем принципиальная разница?


ради одного-единственного класса, реализующего тот самый интерфейс. выёживаться с интерфейсами нет никакого резона.


 
sally   (2006-01-13 12:34) [13]

Как сделать не "выежываясь"?
И в чем заключается "выеживание"? Что нужно сделать, чтобы этот метод заработал?


 
Digitman ©   (2006-01-13 12:59) [14]

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 13:11) [15]

>Digitman
Спасибо. Но в чем проблема с использованием интерфейсов?


 
Digitman ©   (2006-01-13 13:30) [16]


> в чем проблема с использованием интерфейсов?


проблема - в ошибке в 17-й строке


 
han_malign ©   (2006-01-13 17:34) [17]

Какой Canvas, какой Draw - это и без потоков работать не будет...
SendMessage(<control>.Handle, CM_INVALIDATE, 0, 0) без всяких Syncronize, и в OnDraw доступ к защищенному критической секцией полю(SendMessage вне критической секции, иначе deadlock)...


 
simpson ©   (2006-01-13 19:54) [18]

sally   (13.01.06 10:52) [3]

В чем изврат, описано в моем посте. Даже с примером.
Ошибка в проектировании. Если есть "нечто", предназначенное для выполнения "чего-то", то пусть это "нечто" и занимается своими делами.

Поток ищет файлы - пусть ищет. Его задача - найти файл и "дернуть" метод интерфейса. Все. Что делает интерфейс, поток волновать недолжно. И как он это делает, тоже потоку фиолетово.

Реализация интерфейса должна рисовать статистику в GUI - вот пусть рисует и сама заботится о том, как обеспечить потокобезопасность.

В твоем же примере это делается в потоке. Почему так делать не нужно, я пояснил на примере с сервисом.

Почему так делать необходимо:
1. Соблюдается принцип модульности проекта.
2. Функционал модулей четко разделен по области применения и не зависит от реализации самих модулей.
3. Synchronize - это в VCL. Завтра тебе нужно будет сделать то же самое без VCL. Либо используя другой framework. Или вообще не на Delphi. Нужна универсальность.

Не согласен с критиками, предлагающими использовать коллбэки или механизм оконных сообщений. Интерфейсы - подход более универсальный, и, как показала практика, более жизнеспособный (.NET).

Автор вопроса, безусловно, не Нострадамус, и не может предсказать, что будет с проектом и как он будет развиваться. Поэтому механизм взаимодействия модулей проекта нужно продумать изначально. Если это - просто "для себя", то даже в этом случае нужно учиться грамотно проектировать приложения - дальше будет проще.

Автору рекомендую почитать это:
http://rsdn.ru/article/patterns/patterns.xml

Да и вообще будут не лишними все статьи этого раздела.

ЗЫ. Автор? Приведи весь проблемный код. Почему у тебя это уже как минимум два человека выпрашивают?


 
sally   (2006-01-16 19:00) [19]

>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-16 23:31) [20]

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-24 16:58) [21]

>simpson
>Кто отменил необходимость синхронизации

А поподробней пожалуйста. Где в главной форме нужно делать синхронизацию и как?



Страницы: 1 вся ветка

Текущий архив: 2006.02.26;
Скачать: CL | DM;

Наверх




Память: 0.53 MB
Время: 0.045 c
2-1139245393
pupapumQ
2006-02-06 20:03
2006.02.26
Массив записей


15-1138852433
Babay
2006-02-02 06:53
2006.02.26
C# для VCL.NET


6-1132258793
Иван12345
2005-11-17 23:19
2006.02.26
Передача информации о постоянной активности приложения


3-1135972536
Igorioha
2005-12-30 22:55
2006.02.26
My SQL


11-1120230528
Dodfr
2005-07-01 19:08
2006.02.26
Move TListView items with Drag&amp;Drop how to ?