我在代码中使用Thread发送短信。
发送短信我使用MCoreComponent
类;
首先,覆盖Create
函数并创建一个objSMS1
对象,
然后在objSMS1.connect()
函数
Execute
constructor ReceiveThread.create;
begin
Inherited Create(True);
objSMS1 := TSMS.Create(nil);
end;
procedure ReceiveThread.Execute();
begin
if Not objSMS1.IsError(true, strMyAppName) then
begin
objSMS1.Connect();
if Not objSMS1.IsError(true, strMyAppName) then
ShowMessage('Connection successful');
end;
while not Terminated do
begin
CoInitialize(nil);
DoShowData;//Recieved Message
end;
end;
这两个功能正常工作,连接模块成功完成,每次都检查收件箱。
但我需要发送消息。我的Send Message
功能是:
procedure ReceiveThread.SendSMS(phoneno, txt: String);
var strSendResult :String;
begin
objSMS1.Validity := Trim('24') + LeftStr('Hour', 1);//Access Violation Error
strSendResult := objSMS1.SendSMS(phoneno, txt, False);
if Not objSMS1.IsError(true, strMyAppName) then
MessageDlg('Message sent!', mtInformation, [mbOK], 0);
end;
当我在按钮中调用SendSMS
功能时单击主窗体,应用程序遇到访问冲突错误。如何在线程中调用发送消息?
其他设置
var
RTh : ReceiveThread;//Global Var
//Run Tread
RTh := ReceiveThread.Create();
RTh.FreeOnTerminate := True;
//Send Message From Button Click
RTh.SendSMS(Phoneno,Msg);//Access Violation Error
答案 0 :(得分:3)
根据这个问题,主要的可见问题是在没有同步块的情况下从线程的方法内部调用MessageDlg
但是代码本身还有许多其他问题,并且对你的问题的评论已经指向了你朝着正确的方向前进。
对DoShowData
的调用可能是另一个问题,但问题并没有提供更多细节。
另一个奇怪的事情是对CoInitialize
的反复拨打电话。即使这不是一个大问题,因为后续调用返回False
,调用必须由CoUninitialize
平衡。
引用评论:“ SendSMS是否是线程安全的?”你知道。
我试图在您的代码中添加一些订单 - 我希望......
该主题使用TThreadList<TSMSInfo>
类型的列表,并将其视为queue来存储并获取要发送的SMS:通过其Locklist
方法访问该列表为了避免并发访问。
SMS发送通知实现为类型TSMSSentEvent
的自定义通知事件:如果已分配,则事件在synchronized block之间触发,以便在主线程中执行( GUI应用程序中的VCL线程。
Sleep(1)
会在队列为空时减少CPU费用* - 从我的PC上的50%
到2%
。
谨防objSMS1
对象的创建及其处置,因为我所说的可能不是正确的地方;也许你每次发送队列时都需要调用objSMS1.Connect
并objSMS1.Disconnect
- 这个方法应该可用 - 就在那之后,你应该知道它。
{$DEFINE FAKESMS}
编译器指令允许我测试应用程序,因为我没有任何MCoreComponent
库:我已将其保留为测试目的。
SMSSender.pas
unit:线程类和朋友
unit SMSSender;
{.$DEFINE FAKESMS}
interface
uses
System.SysUtils,
System.Classes,
System.Generics.Collections,
Winapi.ActiveX;
const
StrMyAppName = '';
type
{$IFDEF FAKESMS}
TSMS = class
public
Validity: string;
function IsError(a: Boolean; b: string): Boolean;
procedure Connect;
function SendSMS(phoneNo, text: string; bBool: Boolean): string;
constructor Create(AObj: TObject);
end;
{$ENDIF}
TSMSInfo = record
id: Integer;
phoneNo: string;
text: string;
end;
TSMSSentEvent = procedure (Sender: TObject; AId: Integer; AIsError: Boolean; AResult: string) of object;
TSMSSender = class(TThread)
private
FSMSList: TThreadList<TSMSInfo>;
FSentCount: Integer;
function GetQueueCount: Integer;
protected
procedure Execute; override;
public
OnSMSSent: TSMSSentEvent;
procedure AddSMS(const ASMSInfo: TSMSInfo);
constructor Create(CreateSuspended: Boolean = False);
destructor Destroy; override;
property QueueCount: Integer read GetQueueCount;
property SentCount: Integer read FSentCount;
end;
implementation
{$IFDEF FAKESMS}
{ TSMS }
procedure TSMS.Connect;
begin
end;
constructor TSMS.Create(AObj: TObject);
begin
end;
function TSMS.IsError(a: Boolean; b: string): Boolean;
begin
Result := False;
end;
function TSMS.SendSMS(phoneNo, text: string; bBool: Boolean): string;
begin
Result := 'message sent';
Sleep(300);//simulates the SMS sent
end;
{$ENDIF}
{ TReceiveThread }
constructor TSMSSender.Create(CreateSuspended: Boolean);
begin
inherited Create(CreateSuspended);
FSentCount := 0;
FSMSList := TThreadList<TSMSInfo>.Create;
end;
destructor TSMSSender.Destroy;
begin
FSMSList.Free;
inherited;
end;
function TSMSSender.GetQueueCount: Integer;
begin
Result := FSMSList.LockList.Count;
FSMSList.UnlockList;
end;
procedure TSMSSender.AddSMS(const ASMSInfo: TSMSInfo);
begin
FSMSList.Add(ASMSInfo);
end;
procedure TSMSSender.Execute;
var
objSMS1: TSMS;
SMSInfo: TSMSInfo;
strSendResult: string;
lst: TList<TSMSInfo>;
begin
CoInitialize(nil);
try
objSMS1 := TSMS.Create(nil);
try
if objSMS1.IsError(True, StrMyAppName) then
raise Exception.Create('Error Message 1');
objSMS1.Connect;
if objSMS1.IsError(True, StrMyAppName) then
raise Exception.Create('Error Message 2');
objSMS1.Validity := '24H';
while not Terminated do begin
while GetQueueCount > 0 do begin
lst := FSMSList.LockList;
try
SMSInfo := lst.First;
lst.Delete(0);
finally
FSMSList.UnlockList;
end;
//maybe the following has to be synchronized in order to work properly?
//Synchronize(procedure
// begin
strSendResult := objSMS1.SendSMS(SMSInfo.phoneNo, SMSInfo.text, False);
// end);
Inc(FSentCount);
if Assigned(OnSMSSent) then
Synchronize(procedure
begin
OnSMSSent(Self, SMSInfo.id, objSMS1.IsError(true, StrMyAppName), strSendResult);
end);
if Terminated then
Break;
end;
Sleep(1);
end;
finally
objSMS1.Free;
end;
finally
CoUninitialize;
end;
end;
end.
Unit1.pas
单位:表格单位
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, System.UITypes,
SMSSender;
type
TForm1 = class(TForm)
btnAddSMS: TButton;
Memo1: TMemo;
btnTerminate: TButton;
btnStart: TButton;
procedure btnAddSMSClick(Sender: TObject);
procedure btnTerminateClick(Sender: TObject);
procedure btnStartClick(Sender: TObject);
private
{ Private declarations }
FReceiver: TSMSSender;
procedure ReceiverSMSSent(Sender: TObject; AId: Integer; AIsError: Boolean; AResult: string);
procedure ReceiverTerminate(Sender: TObject);
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
uses
System.Math;
{$R *.dfm}
procedure TForm1.btnAddSMSClick(Sender: TObject);
var
sms: TSMSInfo;
begin
with sms do begin
id := Random(65535);
phoneNo := '+39' + IntToStr(RandomRange(111111111, 999999999));
text := 'You won nothing at all, as usual';
end;
FReceiver.AddSMS(sms);
end;
procedure TForm1.btnStartClick(Sender: TObject);
begin
Memo1.Lines.Clear;
FReceiver := TSMSSender.Create(True);
FReceiver.FreeOnTerminate := True;
FReceiver.OnSMSSent := ReceiverSMSSent;
FReceiver.OnTerminate := ReceiverTerminate;
FReceiver.Start;
btnStart.Enabled := False;
btnAddSMS.Enabled := True;
btnTerminate.Enabled := True;
end;
procedure TForm1.btnTerminateClick(Sender: TObject);
begin
FReceiver.Terminate;
end;
procedure TForm1.ReceiverSMSSent(Sender: TObject; AId: Integer; AIsError: Boolean;
AResult: string);
begin
Memo1.Lines.Add(Format('id = %d'#9'isError = %s'#9'result = %s', [AId, BoolToStr(AIsError), AResult]));
end;
procedure TForm1.ReceiverTerminate(Sender: TObject);
var
receiver: TSMSSender;
ex: Exception;
begin
btnStart.Enabled := True;
btnAddSMS.Enabled := False;
btnTerminate.Enabled := False;
receiver := TSMSSender(Sender);
ex := Exception(receiver.FatalException);
if Assigned(ex) then begin
MessageDlg(ex.Message, mtError, [mbOK], 0);
Exit;
end;
MessageDlg(Format('Thread %d has finished, %d SMS sent, queue count is %d.', [receiver.ThreadID, receiver.SentCount, receiver.QueueCount]), mtInformation, [mbOK], 0);
end;
end.
Unit1.dfm
单位
object Form1: TForm1
Left = 0
Top = 0
Caption = 'Form1'
ClientHeight = 277
ClientWidth = 527
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
DesignSize = (
527
277)
PixelsPerInch = 96
TextHeight = 13
object btnAddSMS: TButton
Left = 440
Top = 209
Width = 75
Height = 25
Anchors = [akRight, akBottom]
Caption = 'Add SMS'
Enabled = False
TabOrder = 0
OnClick = btnAddSMSClick
end
object Memo1: TMemo
Left = 8
Top = 8
Width = 417
Height = 257
Anchors = [akLeft, akTop, akRight, akBottom]
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Lucida Console'
Font.Style = []
Lines.Strings = (
'Memo1')
ParentFont = False
ScrollBars = ssVertical
TabOrder = 1
end
object btnTerminate: TButton
Left = 440
Top = 240
Width = 75
Height = 25
Anchors = [akRight, akBottom]
Caption = 'Terminate'
Enabled = False
TabOrder = 2
OnClick = btnTerminateClick
end
object btnStart: TButton
Left = 440
Top = 178
Width = 75
Height = 25
Anchors = [akRight, akBottom]
Caption = 'Start'
TabOrder = 3
OnClick = btnStartClick
end
end