-1

I want to terminate thread by clicking the button. If the thread normally works without user interruption it is OK but sometimes user needs to abort thread and that's the question that how user abort the thread.

Here is my code that I tested:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, mmsystem, ExtCtrls;

type
  TForm1 = class(TForm)
    Image1: TImage;
    procedure Image1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  end;

type
  hangth = class(TThread)
  private
    { Private declarations }
  protected
    procedure Execute; override;
    procedure play;
  end;

var
  Form1: TForm1;
  played: boolean;
  szalhang: hangth;

implementation

{$R *.dfm}

procedure hangth.play;
begin
  played := true;
  szalhang.Terminate;
end;

procedure hangth.Execute;
begin
  played := false;
  SndPlaySound(pchar('hang.wav'), SND_SYNC);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  played := true;
end;

procedure TForm1.Image1Click(Sender: TObject);
begin
  if played = true then begin
    szalhang := hangth.Create(true);
    szalhang.Resume;
  end else begin
    szalhang.Terminate();
    // here i want to terminate thread, but it doesn't want to be killed.
  end;
end;

end.
Jerry Dodge
  • 26,858
  • 31
  • 155
  • 327
user3880482
  • 35
  • 2
  • 9
  • And yet we still don't know what the question is. What does it mean to "terminate a thread directly outside of thread"? – Lasse V. Karlsen Jul 26 '14 at 20:32
  • I want to terminate thread by clicking the button. If the thread normally works without user interruption it is OK but sometimes user needs to abort thread and that's the question that how user abort the thread. – user3880482 Jul 26 '14 at 20:42
  • Okay, that's a good start. But could you fix the above code so that it compiles? You say it works, but we all see proof that it does not work. – Jerry Dodge Jul 26 '14 at 20:49
  • Code was re-attached. It can be compiled now. – user3880482 Jul 26 '14 at 20:59
  • It didn't compile, I made the fix to make it compile as well as cleaned it up for you. Now can you be a bit more clear how it's not working? What does/doesn't it do that you don't expect? – Jerry Dodge Jul 26 '14 at 21:03
  • Click to button where written code: szalhang.Terminate(); but thread is not terminated, keeps running. – user3880482 Jul 26 '14 at 21:09
  • Are you sure this isn't a code mix-up? Just the fact that you had both `TForm1` and `TForm4` in the same block of code tells me you have two different forms. By the way, your above code doesn't even have a button. – Jerry Dodge Jul 26 '14 at 21:11
  • You seem to be confusing the way threads work. I thread isn't forcefully terminated with the Terminate command. That's an instruction to the thread to terminate itself. Your thread should check for the terminated property and when it's set then it should exit itself. If you want to forcefully terminate the thread then you have to use TerminateThread(szalhang.ThreadId) instead of szalhang.Terminate but thats not advisable at all. You should rather change the SndPlaySound to check for the termination. Look at this question http://stackoverflow.com/questions/4044855/how-to-kill-a-thread-in-delphi – Graymatter Jul 26 '14 at 21:19
  • Also look at this question. It might be a better approach. http://stackoverflow.com/questions/13123587/how-to-stop-music-started-via-sndplaysound You shouldn't need threads at all. – Graymatter Jul 26 '14 at 21:22
  • ok, I solved it another way but it is weird why it cannot be terminated inside of the thread. – user3880482 Jul 26 '14 at 21:55
  • To stop the sound played by the `sndPlaySound` function you'd need to call `sndPlaySound` with the first parameter set to `nil`. The thread is terminated when its `Execute` method finishes. So your usage of the `Terminated` flag is wrong. Finally, it would be better to keep that thread alive waiting for an event and play the sound when the event is signalled. – TLama Jul 27 '14 at 09:48
  • @TLama: Setting the first parameter to nil only works when using `SND_ASYNC`, not when using `SND_SYNC`. – Remy Lebeau Jul 27 '14 at 19:54
  • @Remy, d'oh, I was just hoping that what MS describes as *"if this parameter is NULL, any currently playing sound is stopped"* includes also the sound played with the `SND_SYNC` flag. Absence of a note about termination at the `SND_SYNC` flag description points out that I was wrong in that. Thanks! – TLama Jul 27 '14 at 20:03

1 Answers1

0

When you call TThread.Terminate(), it sets the TThread.Terminated property to true and does nothing else. It is the responsibility of your TThread.Execute() code to look at the TThread.Terminated property periodically and exit gracefully when it is True. However, in this situation, that is not possible because SndPlaySound() is blocking the thread, and there is no way to interrupt SndPlaySound() when it is running in SND_SYNC mode. Your only option would be to use the Win32 API TerminateThread() function to perform a brute-force termination of the thread.

Since you obviously need more control over the playback of the audio, and detection of when the audio is finished playing, then SndPlaySound() is not the best solution for your needs. You have a TForm, you might consider using Delphi's TMediaPlayer component, for example:

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls, Vcl.MPlayer;

type
  MPlayerState = (mpsClosed, mpsOpened, mpsPlaying);

  TForm1 = class(TForm)
    Image1: TImage;
    MediaPlayer1: TMediaPlayer;
    procedure MediaPlayer1Notify(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure Image1Click(Sender: TObject);
  private
    { Private declarations }
    State: MPlayerState;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm56.FormCreate(Sender: TObject);
begin
  State := mpsClosed;
  MediaPlayer1.FileName := 'C:\full path to\hang.wav';
end;

procedure TForm56.MediaPlayer1Notify(Sender: TObject);
begin
  case MediaPlayer1.Mode of
    mpStopped, mpPlaying:
      State := mpsOpened;
  end;
end;

procedure TForm1.Image1Click(Sender: TObject);
begin
  if State = mpsClosed then
  begin
    MediaPlayer1.Notify := False;
    MediaPlayer1.Wait := True;
    MediaPlayer1.Open;
    State := mpsOpened;
  end;

  if State = mpsOpened then
  begin
    MediaPlayer1.Notify := True;
    MediaPlayer1.Wait := False;
    MediaPlayer1.Play;
    if MediaPlayer1.Error = 0 then
      State := mpsPlaying
  end else
  begin
    MediaPlayer1.Notify := False;
    MediaPlayer1.Wait := True;
    MediaPlayer1.Stop;
    State := mpsOpened;

    MediaPlayer1.Notify := False;
    MediaPlayer1.Wait := True;
    MediaPlayer1.Close;
    State := mpsClosed;
  end;
end;

end.
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770