Без дополнительной информации будет сложно помочь вам отладить это, особенно почему это работает в одном сервисе, а не в другом. Тем не мение:
Вместо того, чтобы пытаться исправить проблему в коде, вы можете полностью удалить окна и использовать PostThreadMessage () вместо PostMessage (). Для правильной работы отправки сообщений вам нужен цикл сообщений, но не обязательно получение окон.
Изменить: я пытаюсь ответить на все ваши ответы сразу.
Во-первых, если вы хотите упростить себе жизнь, вам действительно стоит проверить OmniThreadLibrary от gabr. Я не знаю, работает ли это в служебном приложении Windows, я даже не знаю, пробовали ли это еще. Вы можете спросить на форуме. Однако у него много замечательных функций, и на него стоит обратить внимание хотя бы ради обучающего эффекта.
Но, конечно, вы также можете запрограммировать это для себя, и вам придется это сделать для версий Delphi до Delphi 2007. Я просто добавлю несколько фрагментов из нашей внутренней библиотеки, которая развивалась с годами и работает в нескольких десятках программ. Я не утверждаю, что в нем нет ошибок. Вы можете сравнить это со своим кодом, и если что-то выпадет, не стесняйтесь спрашивать, и я постараюсь уточнить.
Это упрощенный метод Execute () базового класса рабочего потока:
procedure TCustomTestThread.Execute;
var
Msg: TMsg;
begin
try
while not Terminated do begin
if (integer(GetMessage(Msg, HWND(0), 0, 0)) = -1) or Terminated then
break;
TranslateMessage(Msg);
DispatchMessage(Msg);
if Msg.Message = WM_USER then begin
// handle differently according to wParam and lParam
// ...
end;
end;
except
on E: Exception do begin
...
end;
end;
end;
Важно не допускать необработанных исключений, поэтому для всего есть обработчик исключений верхнего уровня. То, что вы делаете с исключением, - ваш выбор и зависит от приложения, но все исключения должны быть перехвачены, иначе приложение будет прекращено. В сервисе ваш единственный вариант - это, вероятно, их регистрировать.
Существует специальный метод для инициирования завершения работы потока, поскольку поток необходимо разбудить, когда он находится внутри GetMessage ():
procedure TCustomTestThread.Shutdown;
begin
Terminate;
Cancel; // internal method dealing with worker objects used in thread
DoSendMessage(WM_QUIT);
end;
procedure TCustomTestThread.DoSendMessage(AMessage: Cardinal;
AWParam: integer = 0; ALParam: integer = 0);
begin
PostThreadMessage(ThreadID, AMessage, AWParam, ALParam);
end;
Публикация WM_QUIT вызовет выход из цикла обработки сообщений. Однако существует проблема, заключающаяся в том, что код в классах-потомках может полагаться на правильную обработку сообщений Windows во время выключения потока, особенно при использовании интерфейсов COM. Вот почему вместо простого WaitFor () используется следующий код для освобождения всех запущенных потоков:
procedure TCustomTestController.BeforeDestruction;
var
i: integer;
ThreadHandle: THandle;
WaitRes: LongWord;
Msg: TMsg;
begin
inherited;
for i := Low(fPositionThreads) to High(fPositionThreads) do begin
if fPositionThreads[i] <> nil then try
ThreadHandle := fPositionThreads[i].Handle;
fPositionThreads[i].Shutdown;
while TRUE do begin
WaitRes := MsgWaitForMultipleObjects(1, ThreadHandle, FALSE, 30000,
QS_POSTMESSAGE or QS_SENDMESSAGE);
if WaitRes = WAIT_OBJECT_0 then begin
FreeAndNil(fPositionThreads[i]);
break;
end;
if WaitRes = WAIT_TIMEOUT then
break;
while PeekMessage(Msg, 0, 0, 0, PM_REMOVE) do begin
TranslateMessage(Msg);
DispatchMessage(Msg);
end;
end;
except
on E: Exception do
// ...
end;
fPositionThreads[i] := nil;
end;
end;
Это в переопределенном методе BeforeDestruction (), потому что все потоки должны быть освобождены до того, как деструктор дочернего класса контроллера начнет освобождать любые объекты, которые потоки могут использовать.
person
mghie
schedule
24.02.2009