![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
GetMessage в отдельном потоке
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