Пока JclShell.ShellExecAndWait
ожидает выхода порожденного процесса, он использует следующий основной насос сообщений:
while WaitForSingleObject(Sei.hProcess, 10) = WAIT_TIMEOUT do
repeat
Msg.hwnd := 0;
Res := PeekMessage(Msg, Sei.Wnd, 0, 0, PM_REMOVE);
if Res then
begin
TranslateMessage(Msg);
DispatchMessage(Msg);
end;
until not Res;
CloseHandle(Sei.hProcess);
Я говорю «базовый», потому что зацикленная часть потока сообщений VCL выглядит так:
if PeekMessage(Msg, 0, 0, 0, PM_NOREMOVE) then
begin
Unicode := (Msg.hwnd = 0) or IsWindowUnicode(Msg.hwnd);
if Unicode then
MsgExists := PeekMessageW(Msg, 0, 0, 0, PM_REMOVE)
else
MsgExists := PeekMessageA(Msg, 0, 0, 0, PM_REMOVE);
if MsgExists then
begin
Result := True;
if Msg.Message <> WM_QUIT then
begin
Handled := False;
if Assigned(FOnMessage) then FOnMessage(Msg, Handled);
if not IsPreProcessMessage(Msg) and not IsHintMsg(Msg) and
not Handled and not IsMDIMsg(Msg) and
not IsKeyMsg(Msg) and not IsDlgMsg(Msg) then
begin
TranslateMessage(Msg);
if Unicode then
DispatchMessageW(Msg)
else
DispatchMessageA(Msg);
end;
end
else
begin
{$IF DEFINED(CLR)}
if Assigned(FOnShutDown) then FOnShutDown(self);
DoneApplication;
{$IFEND}
FTerminate := True;
end;
end;
Строка кода
if Assigned(FOnMessage) then FOnMessage(Msg, Handled);
это то, что передаст сообщение вашему TApplicationEvents.OnMessage
через TMultiCaster
, который TApplicationEvents
назначил Application.OnMessage
. Это не делается в исходном коде JCL. Дополнительные проблемы могут быть связаны с тем, что на данный момент JCL не поддерживает UniCode в отношении сообщений и не обрабатывает WM_QUIT
.
Что с этим делать, тоже зависит от того, чего вы хотите достичь. Почему вы хотите получать эти сообщения в первую очередь?
Я имею в виду, что можно изменить исходный код JCL - если вы хотите это сделать - и добавить VCL.Forms
к использованию, а затем вызвать обработчик события, если он назначен, как это делает VCL:
while WaitForSingleObject(Sei.hProcess, 10) = WAIT_TIMEOUT do
repeat
Msg.hwnd := 0;
Res := PeekMessage(Msg, Sei.Wnd, 0, 0, PM_REMOVE);
if Res then
begin
Handled := False;
if Assigned(Application.OnMessage) then
Application.OnMessage(Msg, Handled);
if not Handled then
begin
TranslateMessage(Msg);
DispatchMessage(Msg);
end;
end;
until not Res;
CloseHandle(Sei.hProcess);
Или даже вызовите Application.ProcessMessages
, чтобы иметь ту же обработку сообщений, что и VCL:
while WaitForSingleObject(Sei.hProcess, 10) = WAIT_TIMEOUT do
Application.ProcessMessages;
CloseHandle(Sei.hProcess);
Это работает, я не видел никаких побочных эффектов, как предполагали комментаторы. Но прежде чем изменить исходный код JCL таким образом, я, вероятно, реализовал бы свой собственный ShellExecAndWait
. В зависимости от того, чего вы хотите добиться, обычная отправка сообщений все равно должна работать. Итак, если ваш TFrom имеет, например.
procedure WMLButtonDown(var Message: TWMLButtonDown); message WM_LBUTTONDOWN;
реализовано, его следует вызвать. Это, однако, только в том случае, если сообщение было направлено на вашу форму. Если кнопка нажата, результирующие сообщения будут направлены на саму кнопку. Тогда вам нужно будет реализовать свой собственный класс-потомок.
Может быть, совсем другое предложение?
Если ваша цель состоит в том, чтобы определенные элементы управления не нажимались, пока JclShell.ShellExecAndWait
ожидает, и использование обработки сообщений является вашим подходом к тому, чтобы сделать это возможным, вы можете попробовать другие вещи.
Почему бы не отключить их? Это также даст пользователю видимую индикацию того, что кликать нельзя.
Button1.Enabled := False;
try
JclShell.ShellExecAndWait('C:\Windows\system32\notepad.exe');
Self.Caption := 'Notepad closed';
finally
Button1.Enabled := True;
end;
Если вас беспокоит наличие нескольких элементов управления, создайте TAction
, назначьте его каждому элементу управления, который хотите отключить, и отключите их все вместе со свойством Enabled
элемента TAction
.
Или просто скрыть форму, пока открыт notepad.exe?
Hide;
try
JclShell.ShellExecAndWait('C:\Windows\system32\notepad.exe');
Self.Caption := 'Notepad closed';
finally
Show;
end;
Или вы можете удалить обработчики событий:
var
ATmpOnClick: TNotifyEvent;
begin
ATmpOnClick := Button1.OnClick;
Button1.OnClick := nil;
try
JclShell.ShellExecAndWait('C:\Windows\system32\notepad.exe');
Self.Caption := 'Notepad closed';
finally
Button1.OnClick := ATmpOnClick;
end;
end;
person
nil
schedule
16.11.2017