Fórum Identificar mensagens de erro dentro de uma thread, usando tpapro. #442872
14/05/2013
0
É o seguinte, fiz um programinha no delphi conforme indicado no manual Apro_DevGuide.pdf, para enviar mensagens via sms e funcionou perfeitamente, inclusive a parte de mensagens do componente, me informando quando ocorreu tudo certo ou quando deu algum erro.
O problema é que preciso usar esse programa como uma thread e tentei de várias formas, mas as mensagens não são geradas dentro do thread.
Vou colocar as units e solicito que me ajudem, por favor.
Primeira unit, do formulário principal.
unit uSms;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, adgsm, OoMisc, AdPort;
type
TForm1 = class(TForm)
Edit1: TEdit;
Edit2: TEdit;
Button1: TButton;
ListBox1: TListBox;
Label1: TLabel;
procedure Button1Click(Sender: TObject);
{linhas comentadas para teste com a thread}
// procedure ApdGSMPhone1GSMComplete(Pager: TApdCustomGSMPhone;
// State: TGSMStates; ErrorCode: Integer);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
uses uThreadSMS;
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
Var
envia : TThreadSMS;
begin
{linhas comentadas para teste com a thread}
// ApdGSMPhone1.SMSAddress := Edit1.Text;
// ApdGSMPhone1.SMSMessage := Edit2.Text;
// ApdGSMPhone1.SendMessage;
envia := TThreadSMS.Create;
envia.Resume;
end;
{linhas comentadas para teste com a thread}
{esse procedimento funciona perfeitamente aqui, mas não na thread}
//procedure TForm1.ApdGSMPhone1GSMComplete(Pager: TApdCustomGSMPhone;
// State: TGSMStates; ErrorCode: Integer);
//begin
// ListBox1.Items.Add('Erro :' + IntToStr(ErrorCode) + '-' + ErrorMsg(ErrorCode));
// ListBox1.Items.Add('Error number ' + IntToStr(ErrorCode));
// case State of
// gsNone: ListBox1.Items.Add('idle');
// gsConfig: ListBox1.Items.Add('Configuration finished');
// gsSendAll: ListBox1.Items.Add('Enviando todas as mensagens armazenadas');
// gsSend: ListBox1.Items.Add('Message was sent');
// gsListAll: ListBox1.Items.Add('List all messages finished');
// gsWrite: ListBox1.Items.Add('Escrevendo uma mensagem no armazenamento');
// end;
//end;
end.e agora a unit da thread.
unit uThreadSMS;
interface
uses
Classes {$IFDEF MSWINDOWS} , Windows {$ENDIF}, adgsm, OoMisc, AdPort, AdExcept, Forms;
type
TThreadSMS = class(TThread)
ApdGSMPhone2: TApdGSMPhone;
ApdComPort2: TApdComPort;
procedure ApdGSMPhone2GSMComplete(Pager: TApdCustomGSMPhone;
State: TGSMStates; ErrorCode: Integer);
procedure SmsgsNone;
Procedure SmsgsConfig;
procedure SmsgsSendAll;
procedure SmsgsWrite;
procedure SmsgsListAll;
procedure SmsgsSend;
procedure PegaTelefone;
procedure PegaMensagem;
procedure PegaPorta;
Procedure PegaVelocidade;
Procedure AtualizaForm1;
Constructor Create;
private
procedure SetName;
protected
procedure Execute; override;
end;
implementation
uses uSms;
{ Important: Methods and properties of objects in visual components can only be
used in a method called using Synchronize, for example,
Synchronize(UpdateCaption);
and UpdateCaption could look like,
procedure TThreadSMS.UpdateCaption;
begin
Form1.Caption := 'Updated in a thread';
end; }
{$IFDEF MSWINDOWS}
type
TThreadNameInfo = record
FType: LongWord; // must be 0x1000
FName: PChar; // pointer to name (in user address space)
FThreadID: LongWord; // thread ID (-1 indicates caller thread)
FFlags: LongWord; // reserved for future use, must be zero
end;
{$ENDIF}
{ TThreadSMS }
procedure TThreadSMS.SetName;
{$IFDEF MSWINDOWS}
var
ThreadNameInfo: TThreadNameInfo;
{$ENDIF}
begin
{$IFDEF MSWINDOWS}
ThreadNameInfo.FType := $1000;
ThreadNameInfo.FName := 'SMS';
ThreadNameInfo.FThreadID := $FFFFFFFF;
ThreadNameInfo.FFlags := 0;
try
RaiseException( $406D1388, 0, sizeof(ThreadNameInfo) div sizeof(LongWord), @ThreadNameInfo );
except
end;
{$ENDIF}
end;
procedure TThreadSMS.Execute;
begin
SetName;
{ Place thread code here }
ApdComPort2 := TApdComPort.Create(nil);
ApdGSMPhone2 := TApdGSMPhone.Create(nil);
ApdComPort2.ComNumber := 5;
ApdComPort2.Baud := 115000;
ApdComPort2.Parity := pNone;
ApdComPort2.DataBits := 8;
ApdComPort2.StopBits := 1;
ApdComPort2.Open := True;
ApdGSMPhone2.ComPort := ApdComPort2;
ApdGSMPhone2.GSMMode := gmDetect;
Synchronize(PegaTelefone);
Synchronize(PegaMensagem);
ApdGSMPhone2.QuickConnect := True;
ApdGSMPhone2.SendMessage;
end;
{é essa parte que quero que informe as mensagens, mas não funciona dentro da thread}
procedure TThreadSMS.ApdGSMPhone2GSMComplete(Pager: TApdCustomGSMPhone;
State: TGSMStates; ErrorCode: Integer);
begin
Synchronize(AtualizaForm1);
// ListBox1.Items.Add('Erro :' + IntToStr(ErrorCode) + '-' + ErrorMsg(ErrorCode));
// ListBox1.Items.Add('Error number ' + IntToStr(ErrorCode));
case State of
gsNone: Synchronize(SmsgsNone);
gsConfig: Synchronize(SmsgsConfig);
gsSendAll: Synchronize(SmsgsSendAll);
gsSend: Synchronize(SmsgsSend);
gsListAll: Synchronize(SmsgsListAll);
gsWrite: Synchronize(SmsgsWrite);
end;
end;
procedure TThreadSMS.SmsgsConfig;
begin
Form1.ListBox1.Items.Add('Configuration finished');
end;
procedure TThreadSMS.SmsgsListAll;
begin
Form1.ListBox1.Items.Add('List all messages finished');
end;
procedure TThreadSMS.SmsgsNone;
begin
Form1.ListBox1.Items.Add('Idle');
end;
procedure TThreadSMS.SmsgsSend;
begin
Form1.ListBox1.Items.Add('Message was sent');
end;
procedure TThreadSMS.SmsgsSendAll;
begin
Form1.ListBox1.Items.Add('Enviando todas as mensagens armazenadas');
end;
procedure TThreadSMS.SmsgsWrite;
begin
Form1.ListBox1.Items.Add('Escrevendo uma mensagem no armazenamento');
end;
procedure TThreadSMS.PegaMensagem;
begin
ApdGSMPhone2.SMSMessage := Form1.Edit2.Text;
end;
procedure TThreadSMS.PegaTelefone;
begin
ApdGSMPhone2.SMSAddress := Form1.Edit1.Text;
end;
constructor TThreadSMS.Create;
begin
inherited Create(True); { Chama o contrutor herdado. Ele irá temporariamente colocar o thread em estado de espera para depois executá-lo. }
FreeOnTerminate := True;
end;
procedure TThreadSMS.PegaPorta;
begin
// ApdComPort2.ComNumber := Form1.ApdComPort1.ComNumber;
end;
procedure TThreadSMS.PegaVelocidade;
begin
// ApdComPort2.Baud := Form1.ApdComPort1.Baud;
end;
procedure TThreadSMS.AtualizaForm1;
begin
Form1.ListBox1.Items.Add('Chegou no GSMComplete');
Form1.Label1.Caption := 'chegou no GSMComplete';
Application.ProcessMessages;
end;
end.
Obrigado pela atenção.
Marcos Saffran
Curtir tópico
+ 0Posts
15/05/2013
Alex Constâncio
Por favor, seja mais específico quando diz "não funciona". Você que dizer que recebe uma mensagem de erro (e nesse caso seria bom colocá-la aqui) ou que simplesmente nada acontece?
Independentemente do caso, vejo no teu código uma prática a ser evitada: o acesso á variável Form1. Mais interessante e seguro é você criar uma propriedade na classe da thread que receba o Form e passar a usar esta propriedade. Quando o Teu form mudar de nome, o código da thread não vai sofrer.
Alex
Gostei + 0
15/05/2013
Marcos Saffran
Por favor, seja mais específico quando diz "não funciona". Você que dizer que recebe uma mensagem de erro (e nesse caso seria bom colocá-la aqui) ou que simplesmente nada acontece?
Independentemente do caso, vejo no teu código uma prática a ser evitada: o acesso á variável Form1. Mais interessante e seguro é você criar uma propriedade na classe da thread que receba o Form e passar a usar esta propriedade. Quando o Teu form mudar de nome, o código da thread não vai sofrer.
Alex
Olá Alex,
simplesmente nada acontece, o evento GSMComplete não ocorre, ou não sei como capturá-lo, dentro da thread.
Obrigado pela dica da prática a ser evitada, farei a sua recomendação.
Gostei + 0
15/05/2013
Marcos Saffran
Gostei + 0
17/05/2013
Alex Constâncio
Este componente é assíncrono? Quando acontece o evento Complete? Percebi que o Execute da tua Thread não fica em loop, ela inicia, faz várias coisas e termina. Quando o Execute termina, a thread pára de executar, eventualmente sendo destruída. Para operar o thread, a tua lógica tem que mudar um pouco para levar este comportamento em conta.
Alex
Gostei + 0
23/05/2013
Marcos Saffran
Eu não sei se o componente é assíncrono.
O evento complete ocorre no término do envio da mensagem para o modem.
A thread não está em loop, pois ela é executada a cada clique do botão de envio, enviando uma mensagem por vez de forma direta, ou seja, envia a mensagem diretamente através do modem, sem usar a memória do mesmo.
Obrigado.
Gostei + 0
28/05/2013
Guilherme Wiethaus
Algumas coisas você precisa rever das suas ideias. Vi alguns problemas, mas
são muitas as coisas a serem discutidas. Vou colocar algumas delas aqui para
te esclarecer um mais de como melhor será sua estratégia.
A forma que estas declarado o código de Execute() não surtirá muito efeito
pois irá executar em primeira instância sua Thread, passará pelo Synchronize()
e encerrará a execução da Thread. Para que a execução não termine necessita
dentro da Thread um laço while. a propriedade Terminated() indica que a sua
thread foi terminada e ela sairá do laço. Mas não se baseie apenas neste, pode
e deve ser usado com outros sinalizadores declarados por você que vão dizer
outros momentos de se encerrar a execução do laço na thread e consequentemente
a finalziar thread.
procedure TWMinhaThread.Execute;
begin
while (not Terminated) begin
Synchronize (ExecutaUmaTarefa_SMS);
end;
end;
Outro ponto é evitar de criar criar objetos dentro de Execute() a não ser
que eles sejam destruídos logo abaixo. Se obviamente inserir a criação de
objetos dentro do laço irá criar infinitos objetos em memória até estourar
sua pilha e travamentos do sistema operacional (não deve, mas acontece em
alguns casos).
O ideal é criar fora da thread e configurar suas propriedades. A parte de
aquisição de dados você deve por dentro da Thread, mas tem um porém ai.
Se a transmissão/recepção de dados for mais lenta em relação a execução
de cada linha na thread (isto é o mais comum) irá executar o comando sem
ter transmitido/recebido completamente o pacote anterior, ocasionará assim,
dependendo da tecnologia do dispositivo, falhas na transmissão/recepção.
Geralmente o fabricante disponibiliza funções que avisa no modo assíncrono,
quando o dispositivo terminou de processar um comando. O Status do dispositivo
deve ser sempre lido.
Portanto, para contornar isto precisas usar sinalizadores que indicam
que a transmissão foi feita ou recebida, para que novamente a linha seja
executada. Assim deverá esperar por esta conclusão.
Existe outra questão ai no meio. Vai que seu dispositivo falhe e não
retorne o que espera....O thread ficará bloqueado intermitentemente esperando
em algum laço ou procedimento Wait() por alguma coisa que nunca irá receber.
Para tanto, se utiliza uma técnica de contagem de tempo de inicio e de fim da
execução. Se demorar mais doque o fabricante menciona necessário na execução de
um comando, este então deverá sair do laço de espera por timeout.
Outros recursos que precisa usar geralmente é o controle de seção crítica,
através de Semáforos e Mutex.
Vou colocar algumas referências aqui que pode ajudar a esclarecer mais
os conceitos de Thread:
Trabalhando com Threads:
[url]http://balaiotecnologico.blogspot.com.br/2009/08/trabalhando-com-threads.html[/url]
Trabalhando com Threads em Delphi - exemplo básico:
[url]http://balaiotecnologico.blogspot.com.br/2009/08/trabalhando-com-threads-em-delphi.html[/url]
TThread - Melhores práticas:
[url]http://eugostododelphi.blogspot.com.br/2009/11/tthread-melhores-praticas.html[/url]
Threads no Delphi, por onde começar ? – Parte I:
[url]http://drgarcia1986.wordpress.com/2013/01/31/threads-no-delphi-por-onde-comecar-parte-i/[/url]
Threads no Delphi, por onde começar ? – Parte II:
[url]http://drgarcia1986.wordpress.com/2013/02/08/threads-no-delphi-por-onde-comecar-parte-ii/[/url]
Threads no Delphi, por onde começar ? – Parte III:
[url]http://drgarcia1986.wordpress.com/2013/02/11/threads-no-delphi-por-onde-comecar-parte-iii/[/url]
Threads no Delphi, por onde começar ? – Parte IV:
[url]http://drgarcia1986.wordpress.com/2013/02/13/threads-no-delphi-por-onde-comecar-parte-iv/[/url]
Abraços
Gostei + 0
28/05/2013
Marcos Saffran
agradeço pelo ensinamento e estudarei os links, depois alterarei a thread e postarei os resultados.
Até mais,
Gostei + 0
28/05/2013
Tiago Nunes
type
TMinhaThread = class(TThread)
protected
procedure Execute; override;
end;
...
procedure TMinhaThread.Execute;
begin
while True do
begin
Sleep(100);
//Codigo
end;
end;
procedure TForm1.ExecuteMinhaThread;
var oThread: TMinhaThread;
begin
Screen.Cursor := crHourGlass;
try
Application.ProcessMessages;
oThread := TMinhaThread(True);
oThread.FreeOnTerminate := True;
oThread.Resume;
finally
Screen.Cursor := crDefault;
end;
end;
end.
Gostei + 0
Clique aqui para fazer login e interagir na Comunidade :)