Reputation: 387
I'm using Delphi 10.3, and a component which reads SMS from a phone. This components works like this:
cmp.ReadSms;
After that, there is an event onReadSMS
which is called for every message present.
I need to save each message in a database, but the database object is inside my main procedure. I cannot reach my database from the event.
I was wondering if there is a way to pass my database object to the event so that I can do my inserting there.
EDIT:
Here is some code:
unit myUnit;
TEventHandlers = class
procedure ReadMessage(Sender: TObject; MessageText: WideString; PhoneNumber, CenterNumber: AnsiString; TimeStamp: TDateTime; TimeZone, Status, Index: Integer);
end;
var myEvents: TEventHandlers;
function SMSReceive(dbMain: TFDConnection): Boolean;
var cSMS: TGSMObj;
begin
cSMS: TGSMObj.create(nil);
cSMS.OnReadMessage := myEvents.ReadMessage;
cSMS.ReadSms;
end;
procedure TEventHandlers.ReadMessage(Sender: TObject; MessageText: WideString; PhoneNumber, CenterNumber: AnsiString; TimeStamp: TDateTime; TimeZone, Status, Index: Integer);
begin
// Here I can read the SMS.
// I would like to do:
// dbMain.ExecSQL('INSERT INTO ...')
// But dbMain is unknown here.
end;
Let me add also that this unit is called from some external thread, so I cannot have a global database object, but I need to pass it when calling the SMSReceive
function.
Upvotes: 1
Views: 175
Reputation: 595837
There is no way you can add custom parameters to a pre-existing event (without altering the component's source code). But there are some options you can try to manipulate how your event handler operates.
The easiest solution would be to just derive a new class from TGSMObj
and add a TFDConnection
field to it, eg:
type
TMyGSMObj = class(TGSMObj)
DB: TFDConnection;
end;
// 'class' methods can be used as event handlers, so you don't
// need to create a global object instance of this class...
TEventHandlers = class
class procedure ReadMessage(Sender: TObject; MessageText: WideString; PhoneNumber, CenterNumber: AnsiString; TimeStamp: TDateTime; TimeZone, Status, Index: Integer);
end;
function SMSReceive(dbMain: TFDConnection): Boolean;
var
cSMS: TMyGSMObj;
begin
cSMS := TMyGSMObj.Create(nil);
cSMS.DB := dbMain;
cSMS.OnReadMessage := TEventHandlers.ReadMessage;
cSMS.ReadSms;
end;
class procedure TEventHandlers.ReadMessage(Sender: TObject; MessageText: WideString; PhoneNumber, CenterNumber: AnsiString; TimeStamp: TDateTime; TimeZone, Status, Index: Integer);
begin
TMyGSMObj(Sender).DB.ExecSQL('INSERT INTO ...');
end;
Or, if TGSMObj
is a TComponent
descendant, you can use its Tag
property to hold the TFDConnection
pointer, eg:
type
TEventHandlers = class
class procedure ReadMessage(Sender: TObject; MessageText: WideString; PhoneNumber, CenterNumber: AnsiString; TimeStamp: TDateTime; TimeZone, Status, Index: Integer);
end;
function SMSReceive(dbMain: TFDConnection): Boolean;
var
cSMS: TGSMObj;
begin
cSMS := TGSMObj.Create(nil);
cSMS.Tag := NativeInt(dbMain);
cSMS.OnReadMessage := TEventHandlers.ReadMessage;
cSMS.ReadSms;
end;
class procedure TEventHandlers.ReadMessage(Sender: TObject; MessageText: WideString; PhoneNumber, CenterNumber: AnsiString; TimeStamp: TDateTime; TimeZone, Status, Index: Integer);
begin
TFDConnection(TGSMObj(Sender).Tag).ExecSQL('INSERT INTO ...');
end;
Or, you can use the TMethod
record to coerce the Self
parameter of your event handler to point to whatever you want, eg:
type
TEventHandlers = class
class procedure ReadMessage(Sender: TObject; MessageText: WideString; PhoneNumber, CenterNumber: AnsiString; TimeStamp: TDateTime; TimeZone, Status, Index: Integer);
end;
function SMSReceive(dbMain: TFDConnection): Boolean;
var
cSMS: TGSMObj;
Handler: TWhateverTypeOnReadMessageUses;
begin
Handler := TEventHandlers.ReadMessage;
TMethod(Handler).Data := dbMain;
cSMS := TGSMObj.Create(nil);
cSMS.OnReadMessage := Handler;
cSMS.ReadSms;
end;
class procedure TEventHandlers.ReadMessage(Sender: TObject; MessageText: WideString; PhoneNumber, CenterNumber: AnsiString; TimeStamp: TDateTime; TimeZone, Status, Index: Integer);
begin
TFDConnection(Self).ExecSQL('INSERT INTO ...');
end;
Alternatively, using TMethod
anyway, you can use a standalone procedure rather than a class method, eg:
procedure ReadMessage(Self: Pointer; Sender: TObject; MessageText: WideString; PhoneNumber, CenterNumber: AnsiString; TimeStamp: TDateTime; TimeZone, Status, Index: Integer);
begin
TFDConnection(Self).ExecSQL('INSERT INTO ...');
end;
function SMSReceive(dbMain: TFDConnection): Boolean;
var
cSMS: TGSMObj;
Handler: TWhateverTypeOnReadMessageUses;
begin
TMethod(Handler).Data := dbMain;
TMethod(Handler).Code := @ReadMessage;
cSMS := TGSMObj.Create(nil);
cSMS.OnReadMessage := Handler;
cSMS.ReadSms;
end;
Or, a class helper might work (I haven't tried this one myself), eg:
type
TFDConnectionHelper = class helper for TFDConnection
public
procedure ReadMessage(Sender: TObject; MessageText: WideString; PhoneNumber, CenterNumber: AnsiString; TimeStamp: TDateTime; TimeZone, Status, Index: Integer);
end;
function SMSReceive(dbMain: TFDConnection): Boolean;
var
cSMS: TGSMObj;
begin
cSMS := TGSMObj.Create(nil);
cSMS.OnReadMessage := dbMain.ReadMessage;
cSMS.ReadSms;
end;
procedure TFDConnectionHelper.ReadMessage(Sender: TObject; MessageText: WideString; PhoneNumber, CenterNumber: AnsiString; TimeStamp: TDateTime; TimeZone, Status, Index: Integer);
begin
Self.ExecSQL('INSERT INTO ...');
end;
Upvotes: 2