1
votes

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.

1
You need to show your code (in your q), because it is not at all clear what you mean by "the database object is inside my main procedure".MartynA
I added some code, let me know if it's more clear now.lorife
Please add actual code, not something you made up on the fly. The code you posted does not compile. Also, if the code above can't access the dataset, then there's no way you can use the dataset within the event handler. You have no control over when the event is called, so you can't modify its parameters.Ken White

1 Answers

2
votes

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;