lorife
lorife

Reputation: 387

is there a way to add a parameter on a component method?

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

Answers (1)

Remy Lebeau
Remy Lebeau

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

Related Questions