Вызов исключения в TThread Execute?

Я только что понял, что мои исключения не отображаются пользователю в моих темах!

Сначала я использовал это в своем потоке для создания исключения, которое не работает:

except on E:Exception do
begin
  raise Exception.Create('Error: ' + E.Message);
end;

IDE показывает мне исключения, а мое приложение — нет!

Я искал решение, вот что я нашел:

Механизм исключения потоков Delphi

http://www.experts-exchange.com/Programming/Languages/Pascal/Delphi/Q_22039681.html

И ни один из них не работал для меня.

Вот мой блок Thread:

unit uCheckForUpdateThread;

interface

uses
  Windows, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient,
  IdHTTP, GlobalFuncs, Classes, HtmlExtractor, SysUtils, Forms;

type
  TUpdaterThread = class(TThread)
  private
    FileGrabber : THtmlExtractor;
    HTTP : TIdHttp;
    AppMajor,
    AppMinor,
    AppRelease : Integer;
    UpdateText : string;
    VersionStr : string;
    ExceptionText : string;
    FException: Exception;
    procedure DoHandleException;
    procedure SyncUpdateLbl;
    procedure SyncFinalize;
  public
    constructor Create;

  protected
    procedure HandleException; virtual;

    procedure Execute; override;
  end;

implementation

uses
  uMain;

{ TUpdaterThread }

constructor TUpdaterThread.Create;
begin
  inherited Create(False);
end;

procedure TUpdaterThread.Execute;
begin
  inherited;
  FreeOnTerminate := True;

  if Terminated then
    Exit;

  FileGrabber           := THtmlExtractor.Create;
  HTTP                  := TIdHTTP.Create(nil);
  try
    try
      FileGrabber.Grab('http://jeffijoe.com/xSky/Updates/CheckForUpdates.php');
    except on E: Exception do
    begin
      UpdateText := 'Error while updating xSky!';
      ExceptionText := 'Error: Cannot find remote file! Please restart xSky and try again! Also, make sure you are connected to the Internet, and that your Firewall is not blocking xSky!';
      HandleException;
    end;
    end;

    try
      AppMajor      := StrToInt(FileGrabber.ExtractValue('AppMajor[', ']'));
      AppMinor      := StrToInt(FileGrabber.ExtractValue('AppMinor[', ']'));
      AppRelease    := StrToInt(FileGrabber.ExtractValue('AppRelease[[', ']'));
    except on E:Exception do
    begin
      HandleException;
    end;
    end;

    if (APP_VER_MAJOR < AppMajor) or (APP_VER_MINOR < AppMinor) or (APP_VER_RELEASE < AppRelease) then
    begin
      VersionStr := Format('%d.%d.%d', [AppMajor, AppMinor, AppRelease]);
      UpdateText := 'Downloading Version ' + VersionStr;
      Synchronize(SyncUpdateLbl);
    end;

  finally
    FileGrabber.Free;
    HTTP.Free;
  end;
  Synchronize(SyncFinalize);
end;

procedure TUpdaterThread.SyncFinalize;
begin
  DoTransition(frmMain.TransSearcher3, frmMain.gbLogin, True, 500);
end;

procedure TUpdaterThread.SyncUpdateLbl;
begin
  frmMain.lblCheckingForUpdates.Caption := UpdateText;
end;

procedure TUpdaterThread.HandleException;
begin
  FException := Exception(ExceptObject);
  try
    Synchronize(DoHandleException);
  finally
    FException := nil;
  end;
end;

procedure TUpdaterThread.DoHandleException;
begin
  Application.ShowException(FException);
end;

end.

Если вам нужна дополнительная информация, просто дайте мне знать.

Еще раз: IDE ловит все исключения, но моя программа их не показывает.

РЕДАКТИРОВАТЬ: В конце концов сработало решение Cosmin, и причина, по которой это не сработало сначала, заключалась в том, что я не добавил переменную ErrMsg, вместо этого я просто поместил все, что будет содержать переменная, в Synchronize, что НЕ будет работать , однако я понятия не имею, почему. Я понял это, когда у меня не было других идей, и я просто возился с решениями.

Как всегда, шутка надо мной. =П


person Jeff    schedule 26.03.2011    source источник
comment
Не могли бы вы опубликовать свой источник, плз?   -  person Rafael Colucci    schedule 26.03.2011
comment
Я убрал Raise из кода, так как он не работал. Я также пытался использовать синхронизированный подъем, но это не сработало - поэтому здесь есть ExceptionText, забыл его удалить.   -  person Jeff    schedule 26.03.2011
comment
Может быть, у вас вообще нет исключений? Какие исключения у вас есть?   -  person Rafael Colucci    schedule 26.03.2011
comment
@Rafael - Исключения, вызванные элементом управления TIdHTTP, потому что я знаю, что файл на веб-сервере не существует. Я проверял, действительно ли работает логика исключения, и был потрясен, когда понял, что это не так.   -  person Jeff    schedule 26.03.2011
comment
Под не поднятием вы подразумеваете, что пользователю не отображается окно сообщения об ошибке?   -  person CodesInChaos    schedule 26.03.2011
comment
Кто бы это ни был, пожалуйста, скажите мне, почему вы проголосовали против, чтобы я знал, что я могу сделать, чтобы помочь вам, ребята, помочь мне.   -  person Jeff    schedule 26.03.2011
comment
Как уже пытались вам сказать другие, исключения во вторичных потоках не отображаются пользователю, вам нужно добавить код, чтобы сделать это самостоятельно.   -  person Lasse V. Karlsen    schedule 26.03.2011
comment
@Lasse - И я пробовал это, но это тоже не сработало, они все еще не показаны! Я попробовал код, который был опубликован, и он все еще не показывает его. Исключение перехватывает только IDE.   -  person Jeff    schedule 26.03.2011
comment
Если вы установите точку останова в методе HandleException, остановится ли она на этом?   -  person Lasse V. Karlsen    schedule 26.03.2011
comment
@Lasse - работает только второй, хотя IDE также вызывает первый, то есть я установил точку останова на HandleException; в моей процедуре Execute. Второе исключение отображается в моем приложении, но не первое — то, которое вызывает исключение EIdHTTPProtocolException.   -  person Jeff    schedule 26.03.2011
comment
Я предполагаю, что кто-то проголосовал за вас, потому что это (как сформулировано) на самом деле не вопрос. Вы можете спросить, почему я не вижу исключений, возникающих в потоках, или как показать пользователю исключение из TThread. Неопределенность затрудняет ответ. Я дал полный ответ на первый вопрос и более краткий ответ на второй, который во многом зависит от того, чего вы хотите достичь.   -  person Disillusioned    schedule 26.03.2011
comment
@Craig - ** Вы можете спросить, почему я не вижу исключений, возникающих в потоках, или как показать пользователю исключение из TThread. ** - Не вижу, в чем разница, если честно. :)   -  person Jeff    schedule 26.03.2011
comment
Джефф, если ты не видишь разницы между исключениями, которые не показываются пользователю, и исключениями, которые не вызываются, то ты также не видишь разницы между тем, что я застрял в помещении на весь день, и тем, что солнце не светило. сегодня встану. Если вас о чем-то не уведомили, это не значит, что этого не было. Пожалуйста, отредактируйте свой вопрос, чтобы более точно указать, что именно произошло или не произошло, и каковы были ваши ожидания.   -  person Rob Kennedy    schedule 26.03.2011
comment
@Rob, я уже отредактировал это - я не говорю, что исключения больше не возникают.   -  person Jeff    schedule 26.03.2011
comment
@Джефф, ты уверен? Прочитайте первое предложение вашего вопроса.   -  person Cosmin Prund    schedule 26.03.2011
comment
@Cosmin - Упс, я сосредоточился на середине - шутишь надо мной, а? :П   -  person Jeff    schedule 26.03.2011


Ответы (6)


Вот мой очень, очень краткий «взгляд» на проблему. Он работает только в Delphi 2010+ (потому что в этой версии появились анонимные методы). В отличие от уже опубликованных более сложных методов, мой показывает только сообщение об ошибке, ни больше, ни меньше.

procedure TErrThread.Execute;
var ErrMsg: string;
begin
  try
    raise Exception.Create('Demonstration purposes exception');
  except on E:Exception do
    begin
      ErrMsg := E.ClassName + ' with message ' + E.Message;
      // The following could be all written on a single line to be more copy-paste friendly  
      Synchronize(
        procedure
        begin
          ShowMessage(ErrMsg);
        end
      );
    end;
  end;
end;
person Cosmin Prund    schedule 26.03.2011
comment
Так же, как и с остальными, только IDE показывает ошибку, приложение не вызывает исключение :( - person Jeff; 26.03.2011
comment
@David - работает абсолютно идеально, останавливает выполнение и все такое! - person Jeff; 26.03.2011
comment
@David - то есть, если я делаю это вне потока, поэтому никакой синхронизации или чего-либо, связанного с TThread - person Jeff; 26.03.2011
comment
@jeff трудно сказать. Должно быть что-то, чего мы не можем видеть. - person David Heffernan; 26.03.2011
comment
@David Дэвид - похоже, я могу вызвать исключение, используя Synchronize, но ТОЛЬКО ВНЕ блока Except-End (это означает, что я могу сделать это между Try и Except). Это почему? Однако это не остановило казнь. - person Jeff; 26.03.2011
comment
@Jeff, скопируйте и вставьте мой код как есть в новое приложение Forms, сообщите нам, если он покажет вам ошибку. Этот код работает, я использую его в продакшене, а также проверял перед публикацией. И это только делает ShowMessage в Synchronize, мне, честно говоря, очень трудно поверить, что это не работает. - person Cosmin Prund; 26.03.2011
comment
@Cosmin - новый проект работает нормально. Отредактирую мой OP, чтобы показать вам код, где он НЕ работает (и ВСЕ ЕЩЕ не работает) - person Jeff; 26.03.2011
comment
@Cosmin - похоже, ваше решение все-таки сработало. :) Спасибо! - person Jeff; 27.03.2011
comment
Я не могу понять, почему этот код работает, а тот, который я разместил, - нет. Единственная разница здесь заключается в анонимном методе, и это не то, что заставляет ваш код работать. - person Rafael Colucci; 27.03.2011
comment
@Rafael, одно небольшое отличие состоит в том, что вы передаете весь объект исключения в поток VCL, и я обнаружил, что это ненадежно. Объедините то, что у вас есть, с методом Дэвида AcquireExceptionObject, и вы получите лучший результат. Как бы то ни было, я проголосовал за ваш ответ, а также за один другой, потому что считаю, что это хорошие ответы. Какая бы там ни была ошибка, она была в коде Джеффа. - person Cosmin Prund; 28.03.2011
comment
@Космин, хорошо .. понял. Но источник, который я разместил здесь, был просто примером. Я никогда не передаю все исключение в поток VCL, но я подумал, что это проще показать. На самом деле я даже не работаю с исключениями таким образом. А также я подумал, что будет легко адаптировать код для передачи строки или любого другого объекта, поскольку я просто демонстрировал, что вам нужно уведомить поток vcl, когда возникает исключение, а также вам нужно вызвать синхронизацию. - person Rafael Colucci; 28.03.2011

Что-то очень важное, что вам нужно понять о многоуровневой разработке:

Каждый поток имеет свой собственный стек вызовов, почти, как будто это отдельные программы. Это включает в себя основной поток вашей программы.

Потоки могут взаимодействовать друг с другом только определенным образом:

  • Они могут работать с общими данными или объектами. Это может привести к проблемам параллелизма, "условиям гонки", и поэтому вам нужно помочь им "хорошо обмениваться данными". Что подводит нас к следующему пункту.
  • They can "signal each other" using a variety of OS support routines. These include things like:
    • Mutexes
    • Критические секции
    • События
  • И, наконец, вы можете отправлять сообщения в другие потоки. При условии, что цепочка каким-то образом написана как получатель сообщений.

Примечание. Обратите внимание, что потоки не могут строго говоря напрямую вызывать другие потоки. Если, например, поток A попытается вызвать поток B напрямую, это будет шагом в стеке вызовов потока A!

Это подводит нас к теме вопроса: "в моих темах не возникают исключения"

Причина этого в том, что все, что делает исключение, это:

  • Запишите ошибку
  • И раскрутите стек вызовов. ‹-- NB: Ваш экземпляр TThread не может развернуть стек вызовов основного потока и не может произвольно прерывать выполнение основного потока.

Таким образом, TThread не будет автоматически сообщать об исключениях вашему основному приложению.

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

Решение

  • The first step is the same as within a single threaded application. You need to decide what the error means and how the thread should react.
    • Should the thread continue processing?
    • Должен ли поток прерваться?
    • Должна ли ошибка быть зарегистрирована/сообщена?
    • Требуется ли для ошибки решение пользователя? ‹-- Это, безусловно, сложнее всего реализовать, поэтому мы пока его пропустим.
  • Как только это будет решено, реализуйте соответствующий обработчик исключений.
  • TIP: Make sure the exception doesn't escape the thread. The OS won't like you if it does.
  • If you need the main program (thread) to report the error to the user, you have a few options.
    • If the thread was written to return a result object, then it's easy: Make a change so that it can return the error in that object if something went wrong.
    • Отправьте сообщение в основной поток, чтобы сообщить об ошибке. Обратите внимание, что основной поток уже реализует цикл сообщений, поэтому ваше приложение сообщит об ошибке, как только обработает это сообщение.

РЕДАКТИРОВАТЬ: пример кода для указанного требования.

Если все, что вы хотите сделать, это уведомить пользователя, то Cosmind Prund answer должен отлично работать в Delphi 2010. Над старыми версиями Delphi нужно немного доработать. Следующее концептуально похоже на собственный ответ Джеффа, но без ошибок :

procedure TUpdaterThread.ShowException;
begin
  MessageDlg(FExceptionMessage, mtError, [mbOk], 0);
end;

procedure TUpdaterThread.Execute;
begin
  try

    raise Exception.Create('Test Exception');
    //The code for your thread goes here
    //
    //

  except
    //Based on your requirement, the except block should be the outer-most block of your code
    on E: Exception do
    begin
      FExceptionMessage := 'Exception: '+E.ClassName+'. '+E.Message;
      Synchronize(ShowException);
    end;
  end;
end;

Некоторые важные исправления в собственном ответе Джеффа, включая реализацию, показанную в его вопросе:

Вызов Terminate актуален только в том случае, если ваш поток реализован в цикле while not Terminated do .... Посмотрите, что на самом деле делает метод Terminate.

Вызов Exit — ненужная трата времени, но вы, вероятно, сделали это из-за своей очередной ошибки.

В вашем вопросе вы оборачиваете каждый шаг в свой собственный try...except для обработки исключения. Это абсолютное нет-нет! Делая это, вы делаете вид, что даже при возникновении исключения все в порядке. Ваш поток пытается выполнить следующий шаг, но на самом деле это гарантированно потерпит неудачу! Это не способ обработки исключений!

person Disillusioned    schedule 26.03.2011
comment
Спасибо за информацию, я не знал факт о Callstack! Я уже решил, что я хочу, чтобы мой поток делал, когда возникает исключение - я хочу, чтобы он завершился, чтобы пользователь знал, что пошло не так, и также дал ему Exception.Message. Поток ничего не возвращает (я даже не знал, что он может это сделать, поэтому я мог бы сделать из него функцию??). - person Jeff; 26.03.2011
comment
Джефф, выполнение потока останавливается только тогда, когда поток завершается. Достижение блока except не приводит к завершению. Отладчик может прервать выполнение потока, но это не то же самое, что завершение, и, кроме того, отлаживаемое приложение не имеет прямых сведений о действиях отладчика над ним. Вы также можете приостановить выполнение, засыпая, ожидая или отправляя сообщение в другой поток. Это не то же самое, что завершение. - person Rob Kennedy; 26.03.2011
comment
@Jeff: В каком-то смысле ваш поток уже возвращает результаты. Это обновление метки в вашей основной форме. Что касается остановки выполнения при исключении: если вы обрабатываете исключение внутри цикла while not Terminated, оно продолжит выполнение. Однако, если вы не обрабатываете исключение, оно разворачивает ваш стек вызовов обратно к точке входа потока и завершается. Однако большинство ОС, столкнувшихся с таким плохим поведением, будут использовать GPF для всего вашего приложения. - person Disillusioned; 27.03.2011

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

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

В одном из моих собственных применений потоковой передачи, пуле потоков, потоки перехватывают и берут на себя ответственность за исключения. Это позволяет управляющему потоку обрабатывать их по своему усмотрению.

Код выглядит так.

procedure TMyThread.Execute;
begin
  Try
    DoStuff;
  Except
    on Exception do begin
      FExceptAddr := ExceptAddr;
      FException := AcquireExceptionObject;
      //FBugReport := GetBugReportCallStackEtcFromMadExceptOrSimilar.
    end;
  End;
end;

Если управляющий поток решит вызвать исключение, он может сделать это следующим образом:

raise Thread.FException at Thread.FExceptAddr;

Иногда у вас может быть код, который не может вызывать Synchronize, например. некоторые библиотеки DLL, и этот подход полезен.

Обратите внимание, что если вы не вызываете захваченное исключение, его необходимо уничтожить, иначе возникнет утечка памяти.

person David Heffernan    schedule 26.03.2011
comment
@ Дэвид, что мне тогда сказать? выкладываю имеющуюся у меня информацию - person Jeff; 26.03.2011
comment
@Джефф: просто имейте в виду, что мы не экстрасенсы и не ясновидящие, мы не знаем вашей кодовой базы, у нас нет возможности увидеть, что у вас на экране, мы не знаем, какие шаги вы предприняли, какие результаты вы получили ожидали и какие результаты получили. Если вам нужны хорошие ответы, вам нужно предоставить все это, чтобы мы хотя бы имели шанс помочь вам. В противном случае все, что мы можем сделать, это предположить, в чем может быть проблема и как вы можете ее решить... - person Marjan Venema; 26.03.2011
comment
@Jeff: Это много для чтения, но оно очень поможет вам задавать вопросы с умом: catb.org/~esr/faqs/smart-questions.html - person Marjan Venema; 26.03.2011
comment
@Marjan - кажется, я опубликовал шаги, которые я предпринял, и результаты, которые я получил. Но спасибо, буду иметь в виду. - person Jeff; 26.03.2011
comment
Дэвид, поздравляю с золотым значком Delphi. И +1 за AcquireExceptionObject, потому что я узнал кое-что новое! - person Cosmin Prund; 26.03.2011
comment
@jeff прочитайте первый комментарий к этому ответу и поставьте себя на наше место. В любом случае, похоже, что Synchronize может подойти для ваших нужд. - person David Heffernan; 26.03.2011
comment
@David - Да, я понимаю, что вы имеете в виду, однако ошибка была той же: только IDE сообщает мне, что есть исключение. Кроме того, использование Synchronize (как предполагают ответы) также не вызывает исключения в моем приложении. Я не знаю, почему он делает это со мной. Я хотел бы иметь возможность отображать пользовательское сообщение, за которым следует фактический Exception.Message, точно так же, как вы делаете это в обычном блоке TryExcept. Однако синхронизация должна выполнять эту работу, не так ли? Я попытался создать исключение с помощью Synchronize, поэтому, поскольку оно выполняется в основном потоке, оно должно работать? - person Jeff; 26.03.2011

Что ж,

Без вашего исходного кода будет сложно, но я проверил это:

Как обрабатывать исключения в объектах TThread

И это работает нормально. Возможно, вам стоит взглянуть на это.

РЕДАКТИРОВАТЬ:

Вы не следуете тому, о чем говорят нам ссылки, которые вы указываете. Проверьте мою ссылку, и вы увидите, как это сделать.

РЕДАКТИРОВАТЬ 2:

Попробуйте это и скажите мне, если это сработало:

 TUpdaterThread= class(TThread)
 private
   FException: Exception;
   procedure DoHandleException;
 protected
   procedure Execute; override;
   procedure HandleException; virtual;
 end;

procedure TUpdaterThread.Execute;
begin
  inherited;
  FreeOnTerminate := True;
  if Terminated then
    Exit;
  FileGrabber := THtmlExtractor.Create;
  HTTP := TIdHTTP.Create(Nil);
  try
    Try
      FileGrabber.Grab('http://jeffijoe.com/xSky/Updates/CheckForUpdates.php');
    Except
      HandleException;
    End;
    Try
      AppMajor := StrToInt(FileGrabber.ExtractValue('AppMajor[', ']'));
      AppMinor := StrToInt(FileGrabber.ExtractValue('AppMinor[', ']'));
      AppRelease := StrToInt(FileGrabber.ExtractValue('AppRelease[[', ']'));
    Except
      HandleException;
    End;
    if (APP_VER_MAJOR < AppMajor) or (APP_VER_MINOR < AppMinor) or (APP_VER_RELEASE < AppRelease) then begin
      VersionStr := Format('%d.%d.%d', [AppMajor, AppMinor, AppRelease]);
      UpdateText := 'Downloading Version ' + VersionStr;
      Synchronize(SyncUpdateLbl);
    end;
  finally
    FileGrabber.Free;
    HTTP.Free;
  end;
  Synchronize(SyncFinalize);

end;

procedure TUpdaterThread.HandleException;
begin
  FException := Exception(ExceptObject);
  try
    Synchronize(DoHandleException);
  finally
    FException := nil;
  end;
end;

procedure TMyThread.DoHandleException;
begin
  Application.ShowException(FException);
end;

РЕДАКТИРОВАТЬ 3:

Вы сказали, что не можете поймать EIdHTTPProtocolException. Но это работает для меня. Попробуйте этот образец и убедитесь в этом сами:

procedure TUpdaterThread.Execute;
begin
  Try
    raise EIdHTTPProtocolException.Create('test');
  Except
    HandleException;
  End;
end;
person Rafael Colucci    schedule 26.03.2011
comment
Я попробовал это - это в основном тот же код, что и в вопросе SO, на который я ссылался. - person Jeff; 26.03.2011
comment
@Rafael - я удалил методы Raise, потому что они не работали, и я пробовал другие методы и т. Д. Поверьте мне, я пробовал методы. - person Jeff; 26.03.2011
comment
ну .. тогда я действительно не знаю, потому что я пробовал этот код, и здесь он отлично работает. - person Rafael Colucci; 26.03.2011
comment
Сделал именно то, что говорит ваш код, и это не сработало, к сожалению :( На самом деле IDE ловит исключение, но программа даже не останавливается, она просто продолжается (если я нажимаю продолжить, но НАСТОЯЩЕЕ исключение не показывается пользователю). Пользователь) - person Jeff; 26.03.2011
comment
@Rafael - оказывается, он проверяет только исключение класса, а не такие вещи, как EIdHTTPProtocolException - могу ли я заставить его перехватывать все исключения? Кроме того, как мне назначить ему свое собственное сообщение? О, и программа не останавливает выполнение :S - person Jeff; 26.03.2011
comment
Он обрабатывает исключение EIdHTTPProtocolException. Я изменил источник, чтобы вызвать EIdHTTPProtocolException, и он также работает. Программа не останавливает выполнение, потому что вы работаете с потоками. Если вы хотите, чтобы программа остановилась на исключении потока, вы должны сигнализировать об ошибке потока приложению основного потока и проверять этот сигнал в основном потоке, прерывая выполнение программы. - person Rafael Colucci; 26.03.2011
comment
Возможно, вам следует закрыть этот вопрос и создать другой вопрос о том, как остановить выполнение программы, когда поток вызывает исключение. - person Rafael Colucci; 26.03.2011
comment
как мне назначить на него свое собственное сообщение? вам просто нужно повторно вызвать исключение, например raise Exception.Create('My error: ' + E.message) - person Rafael Colucci; 26.03.2011
comment
Я больше не могу вам помочь ... это все, что я знаю о вашем исходном коде. Возможно, у вас есть другой код, который не позволяет показывать сообщение. - person Rafael Colucci; 26.03.2011
comment
@Rafael - если я повторно вызову исключение в DoHandleException, оно ничего не вызовет за пределами IDE. - person Jeff; 26.03.2011
comment
Как я уже говорил вам, ваш код отлично работает для меня. Даже при повторном вызове исключения оно работает, так что это должна быть локализованная проблема. - person Rafael Colucci; 27.03.2011

Ранее я использовал SendMessge для обмена данными между потоками с использованием TWMCopyData, поэтому я думаю, что должно работать следующее:

Const MyAppThreadError = WM_APP + 1;

constructor TUpdaterThread.Create(ErrorRecieverHandle: THandle);
begin
    Inherited Create(False);
    FErrorRecieverHandle := Application.Handle;
end;

procedure TUpdaterThread.Execute;
var
    cds: TWMCopyData;
begin
  try
     DoStuff;
  except on E:Exception do
    begin
        cds.dwData := 0;
        cds.cbData := Length(E.message) * SizeOf(Char);
        cds.lpData := Pointer(@E.message[1]);         
        SendMessage(FErrorRecieverHandle, MyAppThreadError, LPARAM(@cds), 0);
    end;
  end;
end;

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

Вам нужно будет добавить Self.Handle в конструктор в форме, созданной потоком, и обработать сообщение в форме, которая его создала.

procedure HandleUpdateError(var Message:TMessage); message MyAppThreadError;
var
    StringValue: string;
    CopyData : TWMCopyData; 
begin
    CopyData := TWMCopyData(Msg);
    SetLength(StringValue, CopyData.CopyDataStruct.cbData div SizeOf(Char));
    Move(CopyData.CopyDataStruct.lpData^, StringValue[1], CopyData.CopyDataStruct.cbData);
    Message.Result := 0;
    ShowMessage(StringValue);
end;
person James Barrass    schedule 26.03.2011

Странно, что все ответили на этот вопрос, но не заметили очевидной проблемы: учитывая, что исключения, вызванные в фоновом потоке, являются асинхронными и могут возникнуть в любое время, это означает, что отображение исключений из фонового потока вызовет случайное всплывающее диалоговое окно. раз пользователю, вполне возможно, показывая исключение, которое не имеет ничего общего с тем, что пользователь делает в данный момент. Я сомневаюсь, что это может улучшить пользовательский опыт.

person Misha    schedule 26.03.2011
comment
Кто сказал, что это не связано с тем, что делает пользователь? Не все, что живет в потоке, таково. - person David Heffernan; 27.03.2011
comment
Верно, но, учитывая асинхронный характер работы с потоками, как узнать? Несомненно, часть процесса здесь заключается не только в том, чтобы слепо отвечать на вопросы пользователя, но и в том, чтобы иногда предположить, что пользователь задает неправильный вопрос? В этом случае, возможно, отображение ошибок в строке формы/состояния может быть тем, что действительно требуется, но всплывающие диалоговые окна, инициированные асинхронной обработкой на основе потоков? Кто-нибудь действительно захочет сделать это с пользователем? - person Misha; 27.03.2011
comment
@Misha Все хорошие моменты, но этот вопрос о Джеффсе является частью продолжительной серии. Насколько я понимаю, он использует потоки в ответ на действия пользователя для связи через какой-то API Skype. И он использует потоки, потому что, если бы он сделал это в основном потоке, его пользовательский интерфейс перестал бы отвечать на запросы. Таким образом, вы совершенно правы, ставя под сомнение целесообразность показа диалога, но я думаю, что в данном случае Джефф, вероятно, делает это правильно. - person David Heffernan; 27.03.2011
comment
Мы не объяснили ему этого, потому что он не задавал этот вопрос. Ему нужна помощь с его потоком, а не объяснение того, как показывать ошибки пользователю. - person Rafael Colucci; 27.03.2011
comment
Хотя это и не относится к конкретному вопросу, это отличный общий комментарий на тему ошибок. Даже в однопоточных средах диалоги ошибок могут быть весьма наглядными для пользователя, и пользователи, как правило, не читают их в любом случае. (Только взгляните, сколько вопросов типа SO: Я получил ошибку, с какой-либо подсказкой относительно самого сообщения.) В некоторых случаях может быть более практичным добавить ошибку в сообщения Окно или динамически отображать кнопку/значок, указывающую на возникновение ошибок. - person Disillusioned; 02.04.2011