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

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

GetMessage в отдельном потоке


Страницы:   1 2 3 4

msg ©   (2006-03-09 12:11:00) 

В отдельном потоке (TThread) создаю форму:

procedure TTest.Execute;
var
msg:TMsg;
wnd:HWND;
wc:WNDCLASS;
begin
wc.style:=0;
wc.cbClsExtra:=0;
wc.cbWndExtra:=0;
wc.hInstance:=hInstance;
wc.hIcon:=0;
wc.hCursor:=0;
wc.hbrBackground:=GetStockObject(LTGRAY_BRUSH);
wc.lpszMenuName:=nil;
wc.lpszClassName:='asdasd';
wc.lpfnWndProc:=@WndProc;

Windows.RegisterClass(wc);
wnd:=CreateWindow(
  'asdasd',
  'Hello world',
  WS_CAPTION,
  10,
  10,
  200,
  80,
  HWND_DESKTOP,
  0,
  hInstance,
  nil
);

 while GetMessage(Msg,wnd,0,0) do
 begin
   TranslateMessage(Msg);
   DispatchMessage(Msg);
 end;

DestroyWindow(wnd);
Windows.UnRegisterClass('asdasd', hInstance);
end;

WndProc выглядит примерно так:

function WndProc(wnd:HWND; msg:UINT; w:UINT; l:UINT):UINT;stdcall;
var
begin
Result:=0;
case msg of
  WM_QUIT: ;
  WM_PAINT: ;
else
  Result:=DefWindowProc(wnd,msg,w,l);
end;
end;

В основном потоке создаю отдельный поток:

var
T: TTest;
procedure TForm1.Button1Click(Sender: TObject);
begin
T := TTest.Create(true);
with T do
begin
  FreeOnTerminate:=true;
  Resume;
end;
end;

и посылаю сообщение:

procedure TForm1.Button2Click(Sender: TObject);
var
h:hWnd;
begin
 h := FindWindow('asdasd', nil);
 SendMessage(h, WM_QUIT, 0, 0);
end;

GetMessage ни в какую не желает принимать сообщение, даже если написать GetMessage(Msg,0,0,0), в то же время WndProc сообщение принимает. Я так понимаю, что посланные мной сообщения принимает обработчик главного потока. Так ли это? И нужен ли вообще, в таком случае, GetMessage в отдельном потоке?


Leonid Troyanovsky   (2006-03-13 09:06:00) [1]


> msg   (10.03.06 21:21) [68]

> Если же использовать WaitForSingleObject, то нужен дополнительный
> поток. Хотя в принципе, ожидание сообщений консольного буфера
> у меня построено на WaitForSingleObjectEx, можно попробовать
> объединить ожидание event и буфера функцией WaitForMultipleObjects,
>  как считаете? Оба события же должны ожидаться бесконечно


Конечно, WaitForMultipleObjects.
Если же используется WaitForSingleObjectEx, то можно извещать
ожидающий поток не с помощью event (SetEvent), а QueueUserAPC.

--
Regards, LVT.


begin...end   (2006-03-11 08:51:00) [2]

К [74]:

> Сообщения, посылаемые окну консоли...

Разумеется, имеется в виду стандартное консольное окно. Если создать дополнительное своё, то сообщения, адресованные ему, вроде бы, нормально принимаются в цикле (тоже на первый взгляд).


begin...end   (2006-03-11 08:27:00) [3]

> Игорь Шевченко ©   (10.03.06 22:11) [72]

Сообщения, посылаемые окну консоли через PostMessage, собственноручно организованным GetMessage-циклом не выбираются. Но у меня в [71] была мысль посылать не окнам (стандартному консольному или самостоятельно созданному), а потоку (через PostThreadMessage). Эти сообщения, на первый взгляд, нормально доходят.


msg   (2006-03-10 23:51:00) [4]

У консольного приложения же как такового нет своего окна, консольное окно создаеться вызовом AllocConsole и заним следит система, к консольному окну может "приотачится" сколь угодо процессов (AttachConsole), и уничтожается оно после того, как все процессы от него отключатся вызвав FreeConsole.

Ну покрайней мере вот такой код не дает ни каких результатов:
в таймере (timeSetEvent):
 h := FindWindow('ConsoleWindowClass', nil);
 PostMessage(h, WM_PAINT, 0, 0);
или
 SetConsoleTitle('{9F0C9451-118F-4A3E-9170-6C1989797463}');
 h := FindWindow(nil, '{9F0C9451-118F-4A3E-9170-6C1989797463}');
 PostMessage(h, WM_PAINT, 0, 0);

в цикле:
 if PeekMessage(Mess,0,0,0,PM_REMOVE) then
 begin
   case Mess.message of
     WM_PAINT: beep;
   end;
 end;


Игорь Шевченко   (2006-03-10 22:11:00) [5]

begin...end ©   (10.03.06 21:54) [71]

А насколько я знаю, этот цикл уже организовывает тот, кто поддерживает ConsoleWindowClass для окна консоли, то есть, winsrv.dll

Я вот не знаю (если ты попробуешь, напиши, интересно), как интерференция с ним себя покажет, поэтому и спросил автора о необходимости именно консольного приложения.


begin...end   (2006-03-10 21:54:00) [6]

> Игорь Шевченко ©   (10.03.06 21:29) [70]

Я, возможно, глупость сморожу, но у консоли разве затруднительно организовать цикл с GetMessage подобно тому, как это делается в GUI? Я, собсно, к тому, что не совсем понятно, зачем ещё окно создавать :)


Игорь Шевченко   (2006-03-10 21:29:00) [7]

msg   (10.03.06 21:23) [69]


> Основное приложение: да.


Собственно, как уже отмечал LVT, в консольном приложении создать окно тоже труда не составит. Единственное, на мой взгляд, неудобное место будет с организацией выборки сообщений.


msg   (2006-03-10 21:23:00) [8]

>А что, консоль является необходимым внешним условием ?

Основное приложение: да. Приложение отдающее команды: нет, хотя оно и не важно от куда SendMessage слать.


msg   (2006-03-10 21:21:00) [9]

>Читатель ждет (периодически проверяет) состояние event, и при
>сигнале читает проекцию файла. Писатель пишет и устанавливает
>событие по окончанию. Всякое чтение-запись в проекцию начинается
>с захвата мьютекса и завершается его release. Вот, собс-но, и все.
>Отладить оное взамодействие можно в обычном гуевом приложении
>с кодом консоли, вынесенным в поток.

Здесь плохо, что "Читатель ждет (периодически проверяет)", потому как происходит дополнительная нагрузка на процессор (ни кто не спорт, она не значительна) и в зависимости от частоты проверки, получение сообщения у нас может занять какое-то время. Так же увеличение частоты проверки, ради сокращения времени получения сообщения, все больше увеличивает нагрузку на процессор.

Если же использовать WaitForSingleObject, то нужен дополнительный поток. Хотя в принципе, ожидание сообщений консольного буфера у меня построено на WaitForSingleObjectEx, можно попробовать объединить ожидание event и буфера функцией WaitForMultipleObjects, как считаете? Оба события же должны ожидаться бесконечно (INFINITE).

>Хотя, конечно, возможно, что лучше его сделать Message-Only Window
>(see SetParent with HWND_MESSAGE)

Кстати, действительно замечательный флаг. Благодарю.


Игорь Шевченко   (2006-03-10 20:42:00) [10]


> Так бы и сделал если бы было бы гуишное приложение, но у
> консоли то ведь нет своей очереди оконных сообщений...


А что, консоль является необходимым внешним условием ?


Leonid Troyanovsky   (2006-03-10 18:32:00) [11]


> msg   (10.03.06 17:37) [64]

> AllocateHWND, ага, что может быть проще, вот только бы еще
> класс окна можно было бы определять самому.


Особенной нужды в этом нет. Для того, чтобы его можно было проще
найти среди дельфирожденных окон ему можно сделать SetWindowText
or SetProp с уникальным идентификатором.

Хотя, конечно, возможно, что лучше его сделать Message-Only Window
(see SetParent with HWND_MESSAGE)

--
Regards, LVT.


Leonid Troyanovsky   (2006-03-10 18:19:00) [12]


> msg   (10.03.06 17:37) [64]

> В данном случае команды оч. простые и даже если будет какое-
> то расширение команд, ничего сложного не будет, максимум
> передача строки(пути), но эта проблема решается в моем случае
> с помощью CopyDataStruct, так же ни когда не потребуется
> удаленное управление. В связи с этим есть желание cделать
> все настолько просто, насколько это возможно (легче отлаживать,
>  писать приложение(я) отдающее(ие) команды, модифицировать.
> ..).


С помощью mmf оно решается не сложней.
Дополнительно потребуется лишь (тоже именованные) mutex и, возможно,
event (CreateEvent).
Читатель ждет (периодически проверяет) состояние event, и при
сигнале читает проекцию файла. Писатель пишет и устанавливает
событие по окончанию. Всякое чтение-запись в проекцию начинается
с захвата мьютекса и завершается его release. Вот, собс-но, и все.
Отладить оное взамодействие можно в обычном гуевом приложении
с кодом консоли, вынесенным в поток.

Т.е., для консольного приложения - это самое то, ничего лишнего
и громоздкого (включая TThread ;).

--
Regards, LVT.


msg   (2006-03-10 17:37:00) [13]

Сергей М.
несомненно, VCL-ные библиотеки используются, но дело все в том, что само приложение мало по килобайтам, поэтому просто не хотелось бы его "утяжелять" использованием к примеру таких модулей как: Controls, Forms, Menus, Dialogs ...

Leonid Troyanovsky
В данном случае команды оч. простые и даже если будет какое-то расширение команд, ничего сложного не будет, максимум передача строки(пути), но эта проблема решается в моем случае с помощью CopyDataStruct, так же ни когда не потребуется удаленное управление. В связи с этим есть желание cделать все настолько просто, насколько это возможно (легче отлаживать, писать приложение(я) отдающее(ие) команды, модифицировать...).

а к примеру с тем же MMF надо решать проблему отслеживания изменившейся/пополнившейся информации, проблему синхронизации приложений, структуры размещаемой информации и пр.

AllocateHWND, ага, что может быть проще, вот только бы еще класс окна можно было бы определять самому.

Crash Coredump
Это понятно. Так бы и сделал если бы было бы гуишное приложение, но у консоли то ведь нет своей очереди оконных сообщений...


Crash Coredump   (2006-03-10 15:32:00) [14]

msg   (10.03.06 14:58) [59]


> А вообще задача такая: есть консольное приложение в момент
> работы оно должно принимать и обрабатывать ряд команд данных
> сторонними программами. Т.е., к примеру, примерно так будет
> выглядеть команда моей программе из стороннего процесса


Не обязательно консольное:

unit WorkerMain;

interface

uses
 Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;

type
 TfWorker = class(TForm)
   procedure FormCreate(Sender: TObject);
 public
   procedure DefaultHandler(var Message); override;
 end;

var
 fWorker: TfWorker;

implementation
uses
 WorkerMessages;

{$R *.DFM}

{ TfWorker }

{ TfWorker }

procedure TfWorker.DefaultHandler(var Message);
begin
 with TMessage(Message) do
   if Msg = UM_WORKER1 then
     MessageBeep (0)
   else if Msg = UM_WORKER2 then
     MessageBox(0, 'message 2', 'Worker', MB_OK)
   else
     inherited;
end;

procedure TfWorker.FormCreate(Sender: TObject);
begin
 Application.ShowMainForm := false;
 ShowWindow (Application.Handle, SW_HIDE);
end;

end.


unit WorkerMessages;

interface
uses
 Messages;

const
 UM_WORKER1 = WM_USER + 100;
 UM_WORKER2 = WM_USER + 101;

implementation

end.


Это один EXEшник.

unit ControlMain;

interface

uses
 Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
 StdCtrls, ActnList;

type
 TfWorkerControl = class(TForm)
   Button1: TButton;
   Button2: TButton;
   Button3: TButton;
   ActionList: TActionList;
   actMessage1: TAction;
   actMessage2: TAction;
   actTerminate: TAction;
   procedure FormCreate(Sender: TObject);
   procedure ActionListUpdate(Action: TBasicAction; var Handled: Boolean);
   procedure actMessage1Execute(Sender: TObject);
   procedure actMessage2Execute(Sender: TObject);
   procedure actTerminateExecute(Sender: TObject);
 private
   FWorkerWnd: HWND;
 end;

var
 fWorkerControl: TfWorkerControl;

implementation
uses
 WorkerMessages;

{$R *.DFM}

procedure TfWorkerControl.FormCreate(Sender: TObject);
begin
 FWorkerWnd := FindWindow ('TfWorker', 'fWorker');
end;

procedure TfWorkerControl.ActionListUpdate(Action: TBasicAction;
 var Handled: Boolean);
begin
 actMessage1.Enabled := IsWindow(FWorkerWnd);
 actMessage2.Enabled := IsWindow(FWorkerWnd);
 actTerminate.Enabled := IsWindow(FWorkerWnd);
end;

procedure TfWorkerControl.actMessage1Execute(Sender: TObject);
begin
 SendMessage(FWorkerWnd, UM_WORKER1, 0, 0);
end;

procedure TfWorkerControl.actMessage2Execute(Sender: TObject);
begin
 SendMessage(FWorkerWnd, UM_WORKER2, 0, 0);
end;

procedure TfWorkerControl.actTerminateExecute(Sender: TObject);
begin
 PostMessage(FWorkerWnd, WM_CLOSE, 0, 0);
end;

end.


Это второй EXEшник - та самая стороняя программа


Leonid Troyanovsky   (2006-03-10 15:19:00) [15]


> msg   (10.03.06 14:58) [59]

> А вообще задача такая: есть консольное приложение в момент
> работы оно должно принимать и обрабатывать ряд команд данных
> сторонними программами. Т.е., к примеру, примерно так будет
> выглядеть команда моей программе из стороннего процесса:


Для взаимодействия с консольным приложением можно взять, например,
pipes, memory mapped files, APC & etc.

Ну, а если уж так хочется GUI-thread можно, например,

http://www.rsdn.ru/Forum/?mid=883505

только, пользы от конгуевых гибридов значительно меньше.

--
Regards, LVT.


Сергей М.   (2006-03-10 15:03:00) [16]


> msg


Ведь так или иначе классы ты задействуешь, коль скоро задействовал TThread ..


Сергей М.   (2006-03-10 15:02:00) [17]


> msg   (10.03.06 14:58) [59]


Насколько принципиален при решении этой задачи отказ от борландовских классов в пользу "чистого WinAPI" ?


msg   (2006-03-10 14:58:00) [18]

Сергей М., Crash Coredump
а, да, точно, понял. этот вопрос снят.

Crash Coredump
Если вы спрашиваете о T.ExitTest;, то это просто эксперимент.

А вообще задача такая: есть консольное приложение в момент работы оно должно принимать и обрабатывать ряд команд данных сторонними программами. Т.е., к примеру, примерно так будет выглядеть команда моей программе из стороннего процесса:

const
 WM_ASDASD_COMMAND = WM_USER + 3;
 _ASDASD_TEST      = 1;

...

h := FindWindow('asdasd', nil);
SendMessage(h, WM_ASDASD_COMMAND, _ASDASD_TEST, 0);


Crash Coredump   (2006-03-10 12:50:00) [19]


> и из основного потока вызвать ExitTest:
>
> T.ExitTest;


То процедура будет выполнена в контексте основного потока, что неудивительно.

msg   (10.03.06 12:45) [56]

А что ты вообще хочешь сделать, если не секрет, в связи с чем вопрос ?
Может есть готовое решение.


Сергей М.   (2006-03-10 12:49:00) [20]


> ПОТОК (в нашем случае не основной)


Ну как же не основной-то ?

Сам же говоришь , что


> и из основного потока вызвать ExitTest


Страницы:   1 2 3 4

Наверх

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