Link to home
Start Free TrialLog in
Avatar of werehamster-
werehamster-

asked on

Adding VBScript and JavaScript to Delphi 7 (Active Script)

Greetings,

I have a client program, and I want the ability for users to add either VBScript or JavaScript custom scripts to it.

Basically I want the ability to start them off with a scripts.txt file that lays out what events are supported.

Also, I need to add a function in the scripts that allows them to output to the chat box.

Sub Event_UserTalk(Username, Flags, Message, Ping)
  AddQ "/w "&Username&" Hi!"
End Sub

Currently, I am playing with psvActiveScript, but not sure how to use it.

I need example code on how to get data from an event and trigger a sub in the script, and how to have the script send data back to my application.  Please don't just give links.  Thanks.
Avatar of ginsonic
ginsonic
Flag of Romania image

Add a TWebBrowser and run it from there.
Avatar of werehamster-
werehamster-

ASKER

ginsonic,
  -  Don't take this the wrong way, but I don't see how that comment helps me at all.  Could you elaborate or provide some sample code?  I do appreciate it though.

everyone else,
  -  Basically what I am looking for is something that I can do...

procedure DisplayString(S:String);
begin
  Form1.Memo1.Lines.Add(S);
end;

function Blah : string;
begin
  result := 'Blah';
end;


In a script...

Sub Event_UserTalk(Username, Flags, Message, Ping)
  DisplayString "Hi "&Username&" Hi!"
  Call DisplayString("My favorite word is "&Blah&"!")
End Sub
p/s: if you are using windows 98 and below, you should go and download windows script host in www.microsoft.com

(base on delphi 7)
1) click component/import active-x component/Microsoft Script control 1.0/install.
2) put MSScriptControl_TLB under your uses section. Put in the function below to your program

function  SMT_MCCScript(FunctionName : String; FunctionParam : String) : integer;
var
  ScriptControl  : TScriptControl;
  ScriptCode     : TStringList;
  ScriptText     : String;
  ScriptFilename : TextFile;
  ProcessFile    : Boolean;
  varScriptControl : Variant;
  LoadedScript : TStringList;
begin
  Result := TargetUnit;

  ScriptControl := TScriptControl.create(application);
  ScriptControl.Language := 'VBScript';
  ScriptControl.Reset;
  ScriptCode := TStringList.Create;

  Scriptcode.Clear;
  ScriptCode := ExportToStringList(TargetUnit);

  AssignFile(ScriptFilename,extractfilepath(application.ExeName) + localsetting + '.mcc');
  {$I-}
  ProcessFile := false;

  if fileexists(extractfilepath(application.ExeName) + 'kenny.mcc') then
  begin
    FileMode := 0;
    reset(ScriptFileName);
    ProcessFile := true;
  end
  else
  begin
     showmessage('need keny.mcc included with your exe file');
    application.terminate;
  end;

  ' to load the vb script into the system
  LoadedScript := TStringList.create;
  LoadedScript.Clear;
  if ProcessFile then
  begin
    while not eof(ScriptFilename) do
    begin
      readln(ScriptFilename, ScriptText);
      LoadedScript.add(ScriptText);
    end;
    close(ScriptFilename);
  end;
  {$I+}  

  ScriptCode.AddStrings(LoadedScript);
  ScriptCode.SaveToFile('c:\mcc_script_debug.txt');
  ScriptControl.AddCode(ScriptCode.Text);

  varScriptControl := ScriptControl.ControlInterface;
  try
    varScriptControl.Run(FunctionName);

  except

    'if the function is not exist... create a template for it

    if ScriptCode.IndexOf('    function ' + functionname) = -1 then
    begin
      AssignFile(ScriptFilename,extractfilepath(application.ExeName) + localsetting + '.mcc');
      {$I-}
      append(ScriptFilename);
      writeln(ScriptFilename,'');
      writeln(ScriptFilename,'    function ' + functionname);
      writeln(ScriptFilename,'    end function');
      writeln(ScriptFilename,'');
      flush(ScriptFilename);
      close(ScriptFilename);
      {$I+}
    end;
  end;

 
end;

3) create kenny.mcc into the same folder where you exe located (usually the same place with your source code)
4) copy this command into the kenny.mcc file

function test
  msgbox("hello world!")
end fucntion

5) save kenny.mcc file
6) put a button into your delphi program
7) under your button command... type this

SMT_MCCScript('test','');

8) please take note... this is modified version from the original which I used over my project. Please do drop me an email so that I can update any mistake along the way.
oh yeah... my email is kennyk@teleplan-my.com
I already have a copy of MSScriptControl_TLB from another component I added.

This just looks like it reads and edits a file and adds the function if it does not exist.  How do I run the script and stuff?  Is it possible for you to make a compilable example of this.  I am still a little confused.

Thanks.
anyway... my msn is kennyk@teleplan-my.com

It will execute it too.... trust me

varScriptControl.Run(FunctionName); <--- this is where the command it will execute the function... if it fail, it will create the function template in kenny.mcc


I just saw how you loaded it.  should have read more carefully, but still kinda confused.  :)
what it does is very simpe

1) load the script
2) run the script
3) if the script fail, create a template for the programmer to create
What is TargetUnit supposed to be?
delete the targetunit...

it was from the original software.
How do I add functions for the script to use, and have the script pass back information?  I'm all confused.  I can't even get it to run.
SOLUTION
Avatar of entrapnet
entrapnet

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
If you are using psvActiveScript the solutions depends from your Delphi version. The most easy will be implementation for Delphi 7
I made a generic example for, it should works for Delphi 5-7

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  psvActiveScript, StdCtrls, ActiveX;

type
  TfrmScriptDemo = class(TForm)
    Memo1: TMemo;
    Button1: TButton;
    psvActiveScriptWindow: TpsvActiveScriptWindow;
    Memo2: TMemo;
    procedure Button1Click(Sender: TObject);
    procedure psvActiveScriptWindowError(Sender: TObject; Line,
      Pos: Integer; ASrc, ADescription: String);
  private
    { Private declarations }
  public
    { Public declarations }
    Username, Flags, _Message, Ping : string;
    end;

var
  frmScriptDemo: TfrmScriptDemo;

implementation

{$R *.DFM}

type
  TDispatchProc = procedure of object;
  TListObj = class
   Proc : TDispatchProc;
  end;

  TAdditionalScriptProcedures = class(TInterfacedObject, IDispatch)
  private
    FunctionList : TStringList;
    FRetValue : variant;
    FParams : PDispParams;
    function GetInputArg(AIndex: integer): variant;
  protected
    procedure DisplayString;
    procedure Blah;
  public
      { IDispatch }
    function GetIDsOfNames(const IID: TGUID; Names: Pointer;
      NameCount: Integer; LocaleID: Integer; DispIDs: Pointer): HRESULT;
      virtual; stdcall;
    function GetTypeInfo(Index: Integer; LocaleID: Integer;
      out TypeInfo): HRESULT; stdcall;
    function GetTypeInfoCount(out Count: Integer): HRESULT; stdcall;
    function Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer;
      Flags: Word; var Params; VarResult: Pointer; ExcepInfo: Pointer;
      ArgErr: Pointer): HRESULT; virtual; stdcall;

    constructor Create;
    Destructor  Destroy; override;
  end;

{ TAdditionalScriptProcedures }

procedure TAdditionalScriptProcedures.Blah;
begin
 FRetValue := 'Blah';
end;

constructor TAdditionalScriptProcedures.Create;
var
 p : TListObj;
begin
  inherited;
  FunctionList := TStringList.Create;

  //Add functions
  P := TListObj.Create;
  P.Proc := DisplayString;
  FunctionList.AddObject('DisplayString', P);

  P := TListObj.Create;
  P.Proc := Blah;
  FunctionList.AddObject('Blah', P);
end;

destructor TAdditionalScriptProcedures.Destroy;
var
 cnt : integer;
begin
  for cnt := 0 to FunctionList.Count - 1 do
   FunctionList.Objects[cnt].Free;
  FunctionList.Free;
  inherited;
end;

procedure TAdditionalScriptProcedures.DisplayString;
var
 S : string;
begin
 S := GetInputArg(0);
 frmScriptDemo.Memo1.Lines.Add(S);
end;

function TAdditionalScriptProcedures.GetIDsOfNames(const IID: TGUID;
  Names: Pointer; NameCount, LocaleID: Integer; DispIDs: Pointer): HRESULT;
type
  TDispIDsArray = array[0..0] of TDISPID;
  PDispIDsArray = ^TDispIDsArray;
var
  IDs: PDispIDsArray absolute DispIDs;
  i: integer;
  Name: WideString;
begin
  if NameCount > 1 then Result := DISP_E_UNKNOWNNAME
  else
    if NameCount < 1 then Result := E_INVALIDARG
    else Result := S_OK;
  for i := 0 to NameCount - 1 do
    IDs[i] := DISPID_UNKNOWN;
  if NameCount = 1 then
    begin
      Name := PWideChar(Names^);
      IDs[0] := FunctionList.IndexOf(Name);
       if IDs[0] = -1 then
        Result := DISP_E_UNKNOWNNAME;
    end;
end;

function TAdditionalScriptProcedures.GetInputArg(AIndex: integer): variant;
begin
  if (AIndex < 0) or (AIndex >= FParams^.cArgs) then
        TList.Error('Index Error', AIndex);
  Result := Variant(FParams^.rgvarg^[FParams^.cArgs - 1 - AIndex])
end;

function TAdditionalScriptProcedures.GetTypeInfo(Index, LocaleID: Integer;
  out TypeInfo): HRESULT;
begin
  Result := E_NOTIMPL;
end;

function TAdditionalScriptProcedures.GetTypeInfoCount(
  out Count: Integer): HRESULT;
begin
  Result := E_NOTIMPL;
end;

function TAdditionalScriptProcedures.Invoke(DispID: Integer;
  const IID: TGUID; LocaleID: Integer; Flags: Word; var Params; VarResult,
  ExcepInfo, ArgErr: Pointer): HRESULT;

var
 P : TDispatchProc;
begin
  if not VarIsEmpty(FRetValue) then
   VarClear(FRetValue);

  Result := DISP_E_MEMBERNOTFOUND;
  if DispID >= FunctionList.Count then
   Exit;
  try
  FParams := @Params;
  if DispID <> -1 then
    if (Flags = DISPATCH_METHOD) or (Flags and not DISPATCH_METHOD = DISPATCH_PROPERTYGET) then
      begin
        p := TListObj(FunctionList.Objects[DispID]).Proc;
        if Assigned(P) then
         P;
      end;

  if not VarIsEmpty(FRetValue) then
   POleVariant(VarResult)^ := FRetValue;
  Result := S_OK;
  except
    on E : Exception do
     begin
       Result := DISP_E_EXCEPTION;
     end;
  end;
end;

procedure TfrmScriptDemo.Button1Click(Sender: TObject);
var
 ScriptText : string;
 MyProcedures : TAdditionalScriptProcedures;
begin
  MyProcedures := TAdditionalScriptProcedures.Create;
  psvActiveScriptWindow.AddNamedItem('Basic', Myprocedures);
   UserName := 'ginsonic';
   Flags := '0';
   _Message := 'Hello';
   Ping := 'Ping';


  //just for demo
  Memo2.Clear;
  Memo2.Lines.Add('Event_UserTalk "' + UserName +
  '", "' + Flags + '", "' + _Message + '", "' + Ping + '"');
  Memo2.Lines.Add('Sub Event_UserTalk(Username, Flags, Message, Ping)');
  Memo2.Lines.Add('DisplayString "Hi "&Username&" Hi!"');
  Memo2.Lines.Add('Call DisplayString("My favorite word is "&Blah&"!")');
  Memo2.Lines.Add('End Sub');


  //event occurs, execute script
  ScriptText := Memo2.Text;
  psvActiveScriptWindow.Execute(ScriptText);
end;

procedure TfrmScriptDemo.psvActiveScriptWindowError(Sender: TObject; Line,
  Pos: Integer; ASrc, ADescription: String);
begin
  ShowMessage('Line: ' + IntToStr(Line +1) + ' ' + ADescription);
end;

end.


psvActiveScript works without windows script host and allows real integration with your Delphi application.
php4delphi -- Yeah, I was messing with it a bit, but could not understand it all too well.  Found some other source that did the same thing that messes with a lot of interface stuff that I have no clue how to do as well using VBA or something.

I assume you forgot to add uses Variants as I coudn't compile.

Also, I am not getting it to add anything to memo1 as I think you intended.
Would this be something that would have to be synchronized normally?
More simple way to use TAutoIntfObject + Type library instead of own IDispatch implementation.
You create type library in application and add something like this

IMyInterface = interface(IDispatch)
['{9275B45A-2187-4793-B8CF-16CF8F2A6708}']
  procedure DisplayString(const S:WideString);
  function Blah : WideString;
end;

In you code

TMyObject = class(TAutoIntfObject, IMyInterface)
  procedure DisplayString(const S:WideString);
  function Blah : WideString;
end;

....
  MyInterface := TMyObject.Create(IMyInterface, ComServer.TypeLib);
Schertkov, could you possibly give me a sample?  I'm not good with this stuff at all, and the only way I learn is by example.  Soon as I see ['{...}'] my head starts to hurt.  :)

I dunno, but it looks like he had to do it that way because of having to call GetInputArg(0).

If I could get one solid working example, I'd be all peachy.  :)
Ok, basically I would just copy ActiveScriptObj apparently.  Haven't tested it yet though.
I do have a major question to ask here though.  If I run multiple copies of the applications, are the messages going to get mixed up between them all?
I think you can modify php4delphi example.
In delphi environment choose New | Other... ActiveX Type Library

In type library editor click "New Interface" and create methods DisplayString
and Balah. Save TypeLib and use generated unit YouTypeLib_TLB and
units ComObj, ComServ in you test form.

Next simple inherit TIntfAutoObject with you interface and replace
string
 MyProcedures := TAdditionalScriptProcedures.Create;
on
 MyInterface := TMyObject.Create(IMyInterface, ComServer.TypeLib);
in previos example.
This is what I came up with.  How would I change it to do the...

IMyInterface = interface(IDispatch)
['{9275B45A-2187-4793-B8CF-16CF8F2A6708}']''

... thing?



unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, psvActiveScript, comobj;

type
  TForm1 = class(TForm)
    btnExecute: TButton;
    pw: TpsvActiveScriptWindow;
    Memo1: TMemo;
    Memo2: TMemo;
    procedure btnExecuteClick(Sender: TObject);
    procedure pwError(Sender: TObject; Line, Pos: Integer; ASrc,
      ADescription: String);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

type
  TActiveBlahInfo = class(TInterfacedObject, IDispatch)
  protected
    function GetTypeInfoCount(out Count: Integer): HResult; stdcall;
    function GetTypeInfo(Index, LocaleID: Integer; out TypeInfo): HResult; stdcall;
    function GetIDsOfNames(const IID: TGUID; Names: Pointer;
      NameCount, LocaleID: Integer; DispIDs: Pointer): HResult; stdcall;
    function Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer;
      Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult; stdcall;
  end;


var
  Form1: TForm1;

implementation

uses
  ActiveScriptObj,  ActiveX;

{$R *.DFM}

procedure TForm1.btnExecuteClick(Sender: TObject);
var
 W,W2 : IDispatch;
 O : TActiveAppInfo;
 O2 : TActiveBlahInfo;
begin
   O := TActiveAppInfo.Create;
   W := O;

   O2 := TActiveBlahInfo.Create;
   W2 := O2;

   pw.AddNamedItem('App', W);
   pw.AddNamedItem('Blah', W2);

   pw.execute(Memo1.text);
end;


procedure TForm1.pwError(Sender: TObject; Line, Pos: Integer; ASrc,
  ADescription: String);
begin
  Showmessage(ADescription + ': ' + ASrc);

end;

const
  DISPID_ADDQ  = 3;
  DISPID_BLAH = 4;

function TActiveBlahInfo.GetIDsOfNames(const IID: TGUID; Names: Pointer; NameCount,
  LocaleID: Integer; DispIDs: Pointer): HResult;
type
  TDispIDsArray = array[0..0] of TDISPID;
  PDispIDsArray = ^TDispIDsArray;
var
  IDs: PDispIDsArray absolute DispIDs;
  i: integer;
  Name: WideString;
begin
  if NameCount > 1 then Result := DISP_E_UNKNOWNNAME
  else
    if NameCount < 1 then Result := E_INVALIDARG
    else Result := S_OK;
  for i := 0 to NameCount - 1 do
    IDs[i] := DISPID_UNKNOWN;
  if NameCount = 1 then
    begin
      Name := PWideChar(Names^);
      if UpperCase(Name) = 'ADDQ' then IDs[0] := DISPID_ADDQ else
      if UpperCase(Name) = 'BLAH' then IDs[0] := DISPID_BLAH else
       Result := DISP_E_UNKNOWNNAME;
    end;
end;

function TActiveBlahInfo.GetTypeInfo(Index, LocaleID: Integer; out TypeInfo): HResult;
begin
  Pointer(TypeInfo) := nil;
  Result := E_NOTIMPL;
end;

function TActiveBlahInfo.GetTypeInfoCount(out Count: Integer): HResult;
begin
  Count := 0;
  Result := S_OK;
end;

function TActiveBlahInfo.Invoke(DispID: Integer; const IID: TGUID;
  LocaleID: Integer; Flags: Word; var Params; VarResult, ExcepInfo,
  ArgErr: Pointer): HResult;

type
  PVariantArray = ^TVariantArray;
  TVariantArray = array[0..65535] of Variant;
  PIntegerArray = ^TIntegerArray;
  TIntegerArray = array[0..65535] of Integer;
var
  Parms: PDispParams;
  FRetValue : Variant;
begin
  if (DispID = DISPID_BLAH) then
    begin
      if VarResult = nil then
        VarResult := @FRetValue;
      POleVariant(VarResult)^ := 'Blah';
      Result := S_OK;
    end
  else
  if (DispID = DISPID_ADDQ) then
    begin
      Parms := @Params;
      Form1.Memo2.Lines.Add(PVariantArray(Parms.rgvarg)^[0]);
      Result := S_OK;
    end
  else

Result := DISP_E_MEMBERNOTFOUND;
end;


end.
OK, lets make this more clear.

First, you need a special class to implement your idea.
Copy CustomProcedures.pas unit to your application folder and leave it as is.
It implements the basic custom class for all future manipulations.

unit CustomProcedures;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  psvActiveScript, StdCtrls, ActiveX, Variants;

type
  TDispatchProc = procedure of object;
  TListObj = class
   Proc : TDispatchProc;
  end;

  TCustomScriptProcedures = class(TInterfacedObject, IDispatch)
  protected
    FunctionList : TStringList;
    FRetValue : variant;
    FParams : PDispParams;
    function GetInputArg(AIndex: integer): variant;
    function GetIDsOfNames(const IID: TGUID; Names: Pointer;
      NameCount: Integer; LocaleID: Integer; DispIDs: Pointer): HRESULT;
      virtual; stdcall;
    function GetTypeInfo(Index: Integer; LocaleID: Integer;
      out TypeInfo): HRESULT; stdcall;
    function GetTypeInfoCount(out Count: Integer): HRESULT; stdcall;
    function Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer;
      Flags: Word; var Params; VarResult: Pointer; ExcepInfo: Pointer;
      ArgErr: Pointer): HRESULT; virtual; stdcall;
  public
    constructor Create; virtual;
    Destructor  Destroy; override;
  end;

implementation

{ TCustomScriptProcedures }

constructor TCustomScriptProcedures.Create;
begin
  inherited;
  FunctionList := TStringList.Create;
end;

destructor TCustomScriptProcedures.Destroy;
var
 cnt : integer;
begin
  for cnt := 0 to FunctionList.Count - 1 do
   FunctionList.Objects[cnt].Free;
  FunctionList.Free;
  inherited;
end;

function TCustomScriptProcedures.GetIDsOfNames(const IID: TGUID;
  Names: Pointer; NameCount, LocaleID: Integer; DispIDs: Pointer): HRESULT;
type
  TDispIDsArray = array[0..0] of TDISPID;
  PDispIDsArray = ^TDispIDsArray;
var
  IDs: PDispIDsArray absolute DispIDs;
  i: integer;
  Name: WideString;
begin
  if NameCount > 1 then Result := DISP_E_UNKNOWNNAME
  else
    if NameCount < 1 then Result := E_INVALIDARG
    else Result := S_OK;
  for i := 0 to NameCount - 1 do
    IDs[i] := DISPID_UNKNOWN;
  if NameCount = 1 then
    begin
      Name := PWideChar(Names^);
      IDs[0] := FunctionList.IndexOf(Name);
       if IDs[0] = -1 then
        Result := DISP_E_UNKNOWNNAME;
    end;
end;

function TCustomScriptProcedures.GetInputArg(AIndex: integer): variant;
begin
  if (AIndex < 0) or (AIndex >= FParams^.cArgs) then
        TList.Error('Index Error', AIndex);
  Result := Variant(FParams^.rgvarg^[FParams^.cArgs - 1 - AIndex])
end;

function TCustomScriptProcedures.GetTypeInfo(Index, LocaleID: Integer;
  out TypeInfo): HRESULT;
begin
  Result := E_NOTIMPL;
end;

function TCustomScriptProcedures.GetTypeInfoCount(
  out Count: Integer): HRESULT;
begin
  Result := E_NOTIMPL;
end;

function TCustomScriptProcedures.Invoke(DispID: Integer; const IID: TGUID;
  LocaleID: Integer; Flags: Word; var Params; VarResult, ExcepInfo,
  ArgErr: Pointer): HRESULT;
var
 P : TDispatchProc;
begin
  if not VarIsEmpty(FRetValue) then
   VarClear(FRetValue);

  Result := DISP_E_MEMBERNOTFOUND;
  if DispID >= FunctionList.Count then
   Exit;
  try
  FParams := @Params;
  if DispID <> -1 then
    if (Flags = DISPATCH_METHOD) or (Flags and not DISPATCH_METHOD = DISPATCH_PROPERTYGET) then
      begin
        p := TListObj(FunctionList.Objects[DispID]).Proc;
        if Assigned(P) then
         P;
      end;

  if not VarIsEmpty(FRetValue) then
   POleVariant(VarResult)^ := FRetValue;
  Result := S_OK;
  except
    on E : Exception do
     begin
       Result := DISP_E_EXCEPTION;
     end;
  end;
end;

end.




Now you have to create your own class inherited from TCustomscriptProcedures to implement the functions you want to add.

unit MyProcedures;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  psvActiveScript, StdCtrls, ActiveX, Variants, CustomProcedures;

type
  TMyProcedures = class(TCustomScriptProcedures)
  protected
    procedure DisplayString;
    procedure Blah;
  public
    constructor Create; override;
  end;  


implementation

uses
  Unit1;

{ TMyProcedures }

procedure TMyProcedures.Blah;
begin
 //if you implement a function always assign result to FRetValue
 FRetValue := 'Blah';
end;

constructor TMyProcedures.Create;
var
 p : TListObj;
begin
  inherited;
  //Add functions
  P := TListObj.Create;
  P.Proc := DisplayString;
  FunctionList.AddObject('DisplayString', P);

  P := TListObj.Create;
  P.Proc := Blah;
  FunctionList.AddObject('Blah', P);
end;

procedure TMyProcedures.DisplayString;
var
 S : string;
begin
//retrieve argument
 S := GetInputArg(0);
 Form1.Memo1.Lines.Add(S);
end;

end.


You can change this class as you want to achieve your goal.
And now you can add it to the application

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, psvActiveScript, CustomProcedures, MyProcedures;

type
  TForm1 = class(TForm)
    psvActiveScriptWindow: TpsvActiveScriptWindow;
    Memo1: TMemo;
    Memo2: TMemo;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    Username, Flags, _Message, Ping : string;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
 ScriptText : string;
 MyProcedures : TMyProcedures;
begin
  MyProcedures := TMyProcedures.Create;
  psvActiveScriptWindow.AddNamedItem('Basic', Myprocedures);
   UserName := 'ginsonic';
   Flags := '0';
   _Message := 'Hello';
   Ping := 'Ping';


  //just for demo
  Memo2.Clear;
  Memo2.Lines.Add('Event_UserTalk "' + UserName +
  '", "' + Flags + '", "' + _Message + '", "' + Ping + '"');
  Memo2.Lines.Add('Sub Event_UserTalk(Username, Flags, Message, Ping)');
  Memo2.Lines.Add('Basic.DisplayString "Hi "&Username&" Hi!"');
  Memo2.Lines.Add('Call Basic.DisplayString("My favorite word is "&Basic.Blah&"!")');
  Memo2.Lines.Add('End Sub');

  //event occurs, execute script
  ScriptText := Memo2.Text;
  psvActiveScriptWindow.Execute(ScriptText);
end;

end.


If you want to use your function names directly, e.g. DisplayString, instead of Basic.DisplayString
change the following code in psvActiveScript.pas:

  for I := 0 to FGlobalObjects.NamedItemCount - 1 do
    FEngine.AddNamedItem(PWideChar(WideString(FGlobalObjects.NamedItemName[I])), SCRIPTITEM_ISVISIBLE);


to:

  for I := 1 to FGlobalObjects.NamedItemCount - 1 do
   begin
    if SameText(FGlobalObjects.NamedItemName[I], 'Basic') then
    FEngine.AddNamedItem(PWideChar(WideString(FGlobalObjects.NamedItemName[I])), SCRIPTITEM_ISVISIBLE  or SCRIPTITEM_GLOBALMEMBERS )
     else
       FEngine.AddNamedItem(PWideChar(WideString(FGlobalObjects.NamedItemName[I])), SCRIPTITEM_ISVISIBLE);
   end;


If you run multiple copies of the application each application will displays own messages.
TActiveBlahInfo is not necessary because delphi ComObj
contains standard IDispatch implementation in TAutoObject and TAutoIntfObject
Wow, this helps a lot.
Anyway, I would prefer not to use automation, if you are not planning to create COM-server. Is it really nessesary to add type library to you application ?

I can propose also a completely generic solution that gives a possibility to
automate any object or component using psvActiveScript.

unit psvObjAuto;

interface
 uses
  Windows, SysUtils, Classes, ActiveX, TypInfo, Consts, Controls, Graphics,
  Dialogs, Forms, Variants, RTLConsts;


type

  TDispatchProc = procedure of object;

  TpsvObjectInfo = class
   public
     ParamCount : integer;
     GetProc : TDispatchProc;
     SetProc : TDispatchProc;
   end;


  TpsvObjectDispatch = class(TInterfacedObject, IDispatch)
  private
    FInstance: TObject;
    FOwned: Boolean;
    FObjectInfo : TStringList;
    FRetValue : variant;
    FParams : PDispParams;
    procedure ObjectClassNameProc;
    procedure ObjectClassnameIsProc;
  protected
    function GetInputArg(AIndex:integer):variant;
    function GetInputArgAsString(AIndex:integer):string;
    function GetInputArgAsInteger(AIndex:integer):integer;
    function GetInputArgAsBoolean(AIndex:integer):boolean;
    function GetInputArgAsFloat(AIndex:integer):double;
    function GetInputArgAsDateTime(AIndex:integer):TDateTime;
    procedure ReturnOutputArg(AValue:variant);
    procedure RegisterMethod(AName : string; AParamCount : integer; AMethodProc : TDispatchProc);
    procedure RegisterProperty(AName : string; AGet : TDispatchProc; ASet : TDispatchProc);
  public
    { IDispatch }
    function GetIDsOfNames(const IID: TGUID; Names: Pointer;
      NameCount: Integer; LocaleID: Integer; DispIDs: Pointer): HRESULT;
      virtual; stdcall;
    function GetTypeInfo(Index: Integer; LocaleID: Integer;
      out TypeInfo): HRESULT; stdcall;
    function GetTypeInfoCount(out Count: Integer): HRESULT; stdcall;
    function Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer;
      Flags: Word; var Params; VarResult: Pointer; ExcepInfo: Pointer;
      ArgErr: Pointer): HRESULT; virtual; stdcall;
  public
    constructor Create(Instance: TObject; Owned: Boolean = True);
    destructor Destroy; override;
    procedure Initialize; virtual;
    property CurrentObject : TObject read FInstance;
  end;


  TpsvComponentDispatch = class(TpsvObjectDispatch, IDispatch)
  private
    FCurrentProp : string;
  protected
    procedure GetPublishedProp; virtual;
    procedure SetPublishedProp; virtual;
    procedure RegisterPublishedProps;
  public
    function Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer;
      Flags: Word; var Params; VarResult: Pointer; ExcepInfo: Pointer;
      ArgErr: Pointer): HRESULT; override; stdcall;
    procedure Initialize; override;
  end;


  TpsvSystemDispatch = class(TpsvComponentDispatch)
  private
   Consts : array of variant;
  protected
     {System procedures}
     procedure VarIsNullProc;
     procedure RoundProc;
     procedure TruncProc;
     procedure CopyProc;
     procedure PosProc;
     procedure LengthProc;
     {SysUtils procedures}
     procedure UpperCaseProc;
     procedure LowerCaseProc;
     procedure CompareStrProc;
     procedure CompareTextProc;
     procedure AnsiUpperCaseProc;
     procedure AnsiLowerCaseProc;
     procedure AnsiCompareStrProc;
     procedure AnsiCompareTextProc;
     procedure IsValidIdentProc;
     procedure IntToStrProc;
     procedure IntToHexProc;
     procedure StrToIntProc;
     procedure StrToIntDefProc;
     procedure FloatToStrProc;
     procedure FormatFloatProc;
     procedure StrToFloatProc;
     procedure EncodeDateProc;
     procedure EncodeTimeProc;
     procedure DayOfWeekProc;
     procedure DateProc;
     procedure TimeProc;
     procedure NowProc;
     procedure IncMonthProc;
     procedure IsLeapYearProc;
     procedure DateToStrProc;
     procedure TimeToStrProc;
     procedure DateTimeToStrProc;
     procedure StrToDateProc;
     procedure StrToTimeProc;
     procedure StrToDateTimeProc;
     procedure BeepProc;
     procedure FormatProc;
     procedure RandomProc;
     {Dialogs procedures}
     procedure ShowMessageProc;
     procedure GetPublishedProp; override;
     procedure AddConstant(AName : string; AValue : variant);
  public
    procedure Initialize; override;
  end;

 TpsvFontDispatch = class(TpsvObjectDispatch)
 protected
   procedure SetNameProp;
   procedure GetNameProp;
   procedure SetStyleProp;
   procedure GetStyleProp;
   procedure SetSizeProp;
   procedure GetSizeProp;
   procedure SetColorProp;
   procedure GetColorProp;
 public
   procedure Initialize;  override;
 end;

 TpsvControlDispatch = class(TpsvComponentDispatch)
  protected
    procedure GetFontProp;
  public
    procedure Initialize; override;
  end;


implementation



function VarToInteger(v:variant):integer;
begin
   case VarType(v) of
      varSmallint, varInteger, varByte, varError: result:=v;
      varSingle, varDouble, varCurrency, varDate: result:=round(v);
      varBoolean: if v=true then result:=1 else result:=0;
      varString, varOleStr: result:=round(StrToFloat (v));
      varUnknown, varDispatch : result := 0;
      else
         if VarIsNull(v) then
            result := 0
         else
            result := VarAsType(v,varInteger);
   end;
end;

function VarToFloat(v:variant):double;
begin
   case VarType(v) of
    varSmallint,
    varInteger,
    varByte,
    varError,
    varSingle,
    varDouble,
    varCurrency,
    varDate:   Result:=v;
    varBoolean: if v=true then result:=1 else result:=0;
    varString,varOleStr: result:= StrToFloat(v);
    varUnknown, varDispatch : result := 0;
      else
         if VarIsNull(v) then
            result := 0
         else
            result := VarAsType(v,varDouble);
   end;
end;

function VarToBoolean(v:variant):boolean;
begin
   result:=(VarToInteger(v)<>0);
end;


{ TpsvObjectDispatch }

constructor TpsvObjectDispatch.Create(Instance: TObject; Owned: Boolean);
begin
  inherited Create;
  FInstance := Instance;
  FOwned := Owned;
  FObjectInfo := TStringList.Create;
  Initialize;
end;

destructor TpsvObjectDispatch.Destroy;
var
 i : integer;
begin
  for i := 0 to FObjectInfo.Count - 1 do
   begin
     FObjectInfo.Objects[i].Free;
   end;
  FObjectInfo.Free;
  if FOwned then
    FInstance.Free;
  inherited;
end;

function TpsvObjectDispatch.GetIDsOfNames(const IID: TGUID; Names: Pointer;
  NameCount, LocaleID: Integer; DispIDs: Pointer): HRESULT;
type
  TDispIDsArray = array[0..0] of TDISPID;
  PDispIDsArray = ^TDispIDsArray;
var
  IDs: PDispIDsArray absolute DispIDs;
  i: integer;
  Name: WideString;
begin
  if NameCount > 1 then Result := DISP_E_UNKNOWNNAME
  else
    if NameCount < 1 then Result := E_INVALIDARG
    else Result := S_OK;
  for i := 0 to NameCount - 1 do
    IDs[i] := DISPID_UNKNOWN;
  if NameCount = 1 then
    begin
      Name := PWideChar(Names^);
      IDs[0] := FObjectInfo.IndexOf(Name);
       if IDs[0] = -1 then
        Result := DISP_E_UNKNOWNNAME;
    end;
end;

function TpsvObjectDispatch.GetInputArg(AIndex: integer): variant;
begin
  if (AIndex < 0) or (AIndex >= FParams^.cArgs) then
        TList.Error(SListIndexError, AIndex);
  Result := Variant(FParams^.rgvarg^[FParams^.cArgs - 1 - AIndex])
end;

function TpsvObjectDispatch.GetInputArgAsBoolean(AIndex: integer): boolean;
begin
  Result := VarToBoolean(GetInputArg(AIndex));
end;

function TpsvObjectDispatch.GetInputArgAsDateTime(
  AIndex: integer): TDateTime;
begin
   Result := VarToDateTime(GetInputArg(AIndex));
end;

function TpsvObjectDispatch.GetInputArgAsFloat(AIndex: integer): double;
begin
  Result := VarToFloat(GetInputArg(AIndex));
end;

function TpsvObjectDispatch.GetInputArgAsInteger(AIndex: integer): integer;
begin
  Result := VarToInteger(GetInputArg(AIndex));
end;

function TpsvObjectDispatch.GetInputArgAsString(AIndex: integer): string;
begin
  Result := VarToStr(GetInputArg(AIndex));
end;

function TpsvObjectDispatch.GetTypeInfo(Index, LocaleID: Integer;
  out TypeInfo): HRESULT;
begin
  Result := E_NOTIMPL;
end;

function TpsvObjectDispatch.GetTypeInfoCount(out Count: Integer): HRESULT;
begin
  Result := E_NOTIMPL;
end;

procedure TpsvObjectDispatch.Initialize;
begin
  RegisterMethod('ClassNameIs', 1, ObjectClassnameIsProc);
  RegisterMethod('ClassName', 0,  ObjectClassNameProc);
end;

function TpsvObjectDispatch.Invoke(DispID: Integer; const IID: TGUID;
  LocaleID: Integer; Flags: Word; var Params; VarResult, ExcepInfo,
  ArgErr: Pointer): HRESULT;

var
  OI : TpsvObjectInfo;
begin
  if not VarIsEmpty(FRetValue) then
   VarClear(FRetValue);

  Result := DISP_E_MEMBERNOTFOUND;
  if DispID >= FObjectInfo.Count then
   Exit;
  try
  FParams := @Params;
  if DispID <> -1 then
    if Flags = DISPATCH_METHOD then
      begin
        OI := TpsvObjectInfo(FObjectInfo.Objects[DispID]);
        if Assigned(OI.GetProc) then
         OI.GetProc;
      end
       else
         case Flags and not DISPATCH_METHOD of
          DISPATCH_PROPERTYGET :
            begin
              OI := TpsvObjectInfo(FObjectInfo.Objects[DispID]);
              if Assigned(OI.GetProc) then
                OI.GetProc;
            end;
          DISPATCH_PROPERTYPUT,
          DISPATCH_PROPERTYPUTREF,
          DISPATCH_PROPERTYPUT + DISPATCH_PROPERTYPUTREF:
            begin
              OI := TpsvObjectInfo(FObjectInfo.Objects[DispID]);
              if Assigned(OI.SetProc) then
                OI.SetProc;
            end;
        end;

  if not VarIsEmpty(FRetValue) then
   POleVariant(VarResult)^ := FRetValue;
  Result := S_OK;
  except
    on E : Exception do
     begin
       Result := DISP_E_EXCEPTION;
     end;
  end;
end;

procedure TpsvObjectDispatch.ObjectClassnameIsProc;
begin
  ReturnOutputArg(FInstance.ClassNameIs(GetInputArgAsString(0)) );
end;

procedure TpsvObjectDispatch.ObjectClassNameProc;
begin
  ReturnOutputArg(FInstance.ClassName);
end;


procedure TpsvObjectDispatch.RegisterMethod(AName: string;
  AParamCount: integer; AMethodProc: TDispatchProc);
var
 OI : TpsvObjectInfo;
 i : integer;
begin
 OI := TpsvObjectInfo.Create;
 OI.GetProc := AMethodProc;
 OI.ParamCount := AParamCount;
 i := FObjectInfo.Add(AName);
 FObjectInfo.Objects[i] := OI;
end;

procedure TpsvObjectDispatch.RegisterProperty(AName: string; AGet,
  ASet: TDispatchProc);
var
 OI : TpsvObjectInfo;
 i : integer;
begin
  OI := TpsvObjectInfo.Create;
  OI.GetProc := AGet;
  OI.SetProc := ASet;
  i := FObjectInfo.Add(AName);
  FObjectInfo.Objects[i] := OI;
end;

procedure TpsvObjectDispatch.ReturnOutputArg(AValue: variant);
begin
  FRetValue := AValue;
end;

{ TpsvComponentDispatch }

procedure TpsvComponentDispatch.GetPublishedProp;
begin
  ReturnOutputArg(GetPropValue(FInstance, FCurrentProp));
end;

procedure TpsvComponentDispatch.Initialize;
begin
  inherited;
  RegisterPublishedProps;
end;

function TpsvComponentDispatch.Invoke(DispID: Integer; const IID: TGUID;
  LocaleID: Integer; Flags: Word; var Params; VarResult, ExcepInfo,
  ArgErr: Pointer): HRESULT;
begin
  FCurrentProp := FObjectInfo[DispID];
  Result := inherited Invoke(DispID,  IID,
  LocaleID, Flags, Params, VarResult, ExcepInfo, ArgErr);
end;

procedure TpsvComponentDispatch.RegisterPublishedProps;
var
 PropList : PPropList;
 FCount : integer;
 Filter : TTypeKinds;
 FSize  : integer;
 C : integer;
begin
  if not Assigned(FInstance) then
   Exit;
  Filter := [tkInteger, tkChar, tkFloat, tkString,  tkEnumeration,
   tkWChar, tkLString, tkWString, tkVariant];
  FCount := GetPropList(FInstance.ClassInfo, Filter, nil);
  FSize := FCount * SizeOf(Pointer);
  GetMem(PropList, FSize);
  GetPropList(FInstance.ClassInfo, Filter , PropList);
  for c:=0 to FCount-1 do
   begin
     RegisterProperty(PropList^[c].Name, GetPublishedProp, SetPublishedProp);
   end;
  FreeMem(PropList, FSize);
end;

procedure TpsvComponentDispatch.SetPublishedProp;
var
 Arg : Variant;
begin
  Arg := GetInputArg(0);
  if PropIsType(FInstance, FCurrentProp, tkEnumeration) then
   begin
     if VarType(Arg) = varBoolean then
      if Arg = true then
       Arg := 1
        else
          Arg := 0;
   end;
  SetPropValue(FInstance, FCurrentProp, Arg);
end;

{ TpsvControlDispatch }

type
  TMControl = class(TControl);

procedure TpsvControlDispatch.GetFontProp;
begin
  ReturnOutputArg(TpsvFontDispatch.Create(TMControl(FInstance).Font, False) as IDispatch);
end;

procedure TpsvControlDispatch.Initialize;
begin
  inherited;
  RegisterProperty('Font', GetFontProp, nil);
end;

{ TpsvFontDispatch }

procedure TpsvFontDispatch.GetColorProp;
begin
  ReturnOutputArg(TFont(FInstance).Color);
end;

procedure TpsvFontDispatch.GetNameProp;
begin
  ReturnOutputArg(TFont(FInstance).Name);
end;

procedure TpsvFontDispatch.GetSizeProp;
begin
  ReturnOutputArg(TFont(FInstance).Size);
end;

procedure TpsvFontDispatch.GetStyleProp;
begin
  ReturnOutputArg(GetOrdProp(FInstance, 'Style'));
end;

procedure TpsvFontDispatch.Initialize;
begin
  inherited;
  RegisterProperty('Name', GetNameProp, SetNameProp);
  RegisterProperty('Size', GetSizeProp, SetSizeProp);
  RegisterProperty('Style', GetStyleProp, SetStyleProp);
  RegisterProperty('Color', GetColorProp, SetColorProp);
end;

procedure TpsvFontDispatch.SetColorProp;
begin
  TFont(FInstance).Color := GetInputArgAsInteger(0);
end;

procedure TpsvFontDispatch.SetNameProp;
begin
  TFont(FInstance).Name := GetInputArgAsString(0);
end;

procedure TpsvFontDispatch.SetSizeProp;
begin
  TFont(FInstance).Size := GetInputArgAsInteger(0);
end;

procedure TpsvFontDispatch.SetStyleProp;
begin
  SetOrdProp(FInstance, 'Style', GetInputArgAsInteger(0));
end;

{ TpsvSystemDispatch }

procedure TpsvSystemDispatch.AddConstant(AName: string; AValue: variant);
var
 L : integer;
begin
  L := Length(Consts);
  SetLength(Consts, L + 1);
  Consts[L] := AValue;
  RegisterProperty(AName, GetPublishedProp, nil);
end;


procedure TpsvSystemDispatch.GetPublishedProp;
var
 I : integer;
begin
 I := FObjectInfo.IndexOf(FCurrentProp);
 if I > -1 then
  ReturnOutputArg(Consts[i])
   else
     ReturnOutputArg(-1);
end;

procedure TpsvSystemDispatch.Initialize;
begin
  AddConstant('clScrollBar',clScrollBar);
  AddConstant('clBackground',clBackground);
  AddConstant('clActiveCaption',clActiveCaption);
  AddConstant('clInactiveCaption',clInactiveCaption);
  AddConstant('clMenu',clMenu);
  AddConstant('clWindow',clWindow);
  AddConstant('clWindowFrame',clWindowFrame);
  AddConstant('clMenuText',clMenuText);
  AddConstant('clWindowText',clWindowText);
  AddConstant('clCaptionText',clCaptionText);
  AddConstant('clActiveBorder',clActiveBorder);
  AddConstant('clInactiveBorder',clInactiveBorder);
  AddConstant('clAppWorkSpace',clAppWorkSpace);
  AddConstant('clHighlight',clHighlight);
  AddConstant('clHighlightText',clHighlightText);
  AddConstant('clBtnFace',clBtnFace);
  AddConstant('clBtnShadow',clBtnShadow);
  AddConstant('clGrayText',clGrayText);
  AddConstant('clBtnText',clBtnText);
  AddConstant('clInactiveCaptionText',clInactiveCaptionText);
  AddConstant('clBtnHighlight',clBtnHighlight);
  AddConstant('cl3DDkShadow',cl3DDkShadow);
  AddConstant('cl3DLight',cl3DLight);
  AddConstant('clInfoText',clInfoText);
  AddConstant('clInfoBk',clInfoBk);
  AddConstant('clBlack',clBlack);
  AddConstant('clMaroon',clMaroon);
  AddConstant('clGreen',clGreen);
  AddConstant('clOlive',clOlive);
  AddConstant('clNavy',clNavy);
  AddConstant('clPurple',clPurple);
  AddConstant('clTeal',clTeal);
  AddConstant('clGray',clGray);
  AddConstant('clSilver',clSilver);
  AddConstant('clRed',clRed);
  AddConstant('clLime',clLime);
  AddConstant('clYellow',clYellow);
  AddConstant('clBlue',clBlue);
  AddConstant('clFuchsia',clFuchsia);
  AddConstant('clAqua',clAqua);
  AddConstant('clLtGray',clLtGray);
  AddConstant('clDkGray',clDkGray);
  AddConstant('clWhite',clWhite);
  AddConstant('clNone',clNone);
  AddConstant('clDefault',clDefault);

  AddConstant('vbEmpty', 0);
  AddConstant('vbNull',  1);
  AddConstant('vbInteger', 2);
  AddConstant('vbLong', 3);
  AddConstant('vbSingle', 4);
  AddConstant('vbDouble', 5);
  AddConstant('vbCurrency', 6);
  AddConstant('vbDate', 7);
  AddConstant('vbString', 8);
  AddConstant('vbObject', 9);
  AddConstant('vbError', 10);
  AddConstant('vbBoolean', 11);
  AddConstant('vbVariant', 12);
  AddConstant('vbDataObject', 13);
  AddConstant('vbDecimal', 14);
  AddConstant('vbByte', 17);
  AddConstant('vbArray', 8192);



  RegisterMethod( 'UpperCase',          1,  UpperCaseProc ) ;
  RegisterMethod( 'LowerCase',          1,  LowerCaseProc );
  RegisterMethod( 'CompareStr',         2,  CompareStrProc );
  RegisterMethod( 'CompareText',        2,  CompareTextProc );
  RegisterMethod( 'AnsiUpperCase',      1,  AnsiUpperCaseProc );
  RegisterMethod( 'AnsiLowerCase',      1,  AnsiLowerCaseProc );
  RegisterMethod( 'AnsiCompareStr',     2,  AnsiCompareStrProc );
  RegisterMethod( 'AnsiCompareText',    2,  AnsiCompareTextProc );
  RegisterMethod( 'IsValidIdent',       1,  IsValidIdentProc );
  RegisterMethod( 'IntToStr',           1,  IntToStrProc );
  RegisterMethod( 'IntToHex',           2,  IntToHexProc );
  RegisterMethod( 'StrToInt',           1,  StrToIntProc );
  RegisterMethod( 'StrToIntDef',        2,  StrToIntDefProc );
  RegisterMethod( 'FloatToStr',         1,  FloatToStrProc );
  RegisterMethod( 'Format',             2,  FormatProc );
  RegisterMethod( 'FormatFloat',        2,  FormatFloatProc );
  RegisterMethod( 'StrToFloat',         1,  StrToFloatProc );
  RegisterMethod( 'EncodeDate',         3,  EncodeDateProc );
  RegisterMethod( 'EncodeTime',         4,  EncodeTimeProc );
  RegisterMethod( 'DayOfWeek',          1,  DayOfWeekProc );
  RegisterMethod( 'Date',               0,  DateProc );
  RegisterMethod( 'Time',               0,  TimeProc );
  RegisterMethod( 'Now',                0,  NowProc );
  RegisterMethod( 'IncMonth',           2,  IncMonthProc );
  RegisterMethod( 'IsLeapYear',         1,  IsLeapYearProc );
  RegisterMethod( 'DateToStr',          1,  DateToStrProc );
  RegisterMethod( 'TimeToStr',          1,  TimeToStrProc );
  RegisterMethod( 'DateTimeToStr',      1,  DateTimeToStrProc );
  RegisterMethod( 'StrToDate',          1,  StrToDateProc );
  RegisterMethod( 'StrToTime',          1,  StrToTimeProc );
  RegisterMethod( 'StrToDateTime',      1,  StrToDateTimeProc );
  RegisterMethod( 'Beep',               0,  BeepProc );
  RegisterMethod( 'VarIsNull',          1,  VarIsNullProc );
  RegisterMethod( 'Round',              1,  RoundProc );
  RegisterMethod( 'Trunc',              1,  TruncProc );
  RegisterMethod( 'Copy',               3,  CopyProc );
  RegisterMethod( 'Pos',                2,  PosProc );
  RegisterMethod( 'Length',             1,  LengthProc );
  RegisterMethod( 'ShowMessage',        1,  ShowMessageProc );
  RegisterMethod( 'Random',             0,  RandomProc );

  inherited;
end;



procedure TpsvSystemDispatch.UpperCaseProc;
begin
   ReturnOutputArg( UpperCase( GetInputArgAsString( 0 ) ) );
end;

procedure TpsvSystemDispatch.LowerCaseProc;
begin
   ReturnOutputArg( LowerCase( GetInputArgAsString( 0 ) ) );
end;

procedure TpsvSystemDispatch.CompareStrProc;
begin
  ReturnOutputArg( CompareStr( GetInputArgAsString( 0 ),GetInputArgAsString( 1 ) ) );
end;

procedure TpsvSystemDispatch.CompareTextProc;
begin
   ReturnOutputArg( CompareText( GetInputArgAsString( 0 ),GetInputArgAsString( 1 ) ) );
end;

procedure TpsvSystemDispatch.AnsiUpperCaseProc;
begin
  ReturnOutputArg( AnsiUpperCase( GetInputArgAsString( 0 ) ) );
end;

procedure TpsvSystemDispatch.AnsiLowerCaseProc;
begin
  ReturnOutputArg( AnsiLowerCase( GetInputArgAsString( 0 ) ) );
end;

procedure TpsvSystemDispatch.AnsiCompareStrProc;
begin
  ReturnOutputArg( AnsiCompareStr( GetInputArgAsString( 0 ),GetInputArgAsString( 1 ) ) );
end;

procedure TpsvSystemDispatch.AnsiCompareTextProc;
begin
   ReturnOutputArg( AnsiCompareText( GetInputArgAsString( 0 ),GetInputArgAsString( 1 ) ) );
end;


procedure TpsvSystemDispatch.IsValidIdentProc;
begin
  ReturnOutputArg( IsValidIdent( GetInputArgAsString( 0 ) ) );
end;

procedure TpsvSystemDispatch.IntToStrProc;
begin
  ReturnOutputArg( IntToStr( GetInputArgAsInteger( 0 ) ) );
end;

procedure TpsvSystemDispatch.IntToHexProc;
begin
  ReturnOutputArg( IntToHex( GetInputArgAsInteger( 0 ),GetInputArgAsInteger( 1 ) ) );
end;

procedure TpsvSystemDispatch.StrToIntProc;
begin
  ReturnOutputArg( StrToInt( GetInputArgAsString( 0 ) ) );
end;

procedure TpsvSystemDispatch.StrToIntDefProc;
begin
  ReturnOutputArg( StrToIntDef( GetInputArgAsString( 0 ),GetInputArgAsInteger( 1 ) ) );
end;

procedure TpsvSystemDispatch.FloatToStrProc;
begin
  ReturnOutputArg( FloatToStr( GetInputArgAsFloat( 0 ) ) );
end;

procedure TpsvSystemDispatch.FormatFloatProc;
begin
  ReturnOutputArg( FormatFloat( GetInputArgAsString( 0 ),GetInputArgAsFloat( 1 ) ) );
end;

procedure TpsvSystemDispatch.StrToFloatProc;
begin
  ReturnOutputArg( StrToFloat( GetInputArgAsString( 0 ) ) );
end;

procedure TpsvSystemDispatch.EncodeDateProc;
begin
  ReturnOutputArg( EncodeDate(
         GetInputArgAsInteger( 0 ),
         GetInputArgAsInteger( 1 ),
         GetInputArgAsInteger( 2 ) ) );
end;

procedure TpsvSystemDispatch.EncodeTimeProc;
begin
  ReturnOutputArg( EncodeTime(
         GetInputArgAsInteger( 0 ),
         GetInputArgAsInteger( 1 ),
         GetInputArgAsInteger( 2 ),
         GetInputArgAsInteger( 3 ) ) );
end;



procedure TpsvSystemDispatch.DayOfWeekProc;
begin
  ReturnOutputArg( DayOfWeek( GetInputArgAsDateTime( 0 ) ) );
end;

procedure TpsvSystemDispatch.DateProc;
begin
  ReturnOutputArg( Date );
end;

procedure TpsvSystemDispatch.TimeProc;
begin
  ReturnOutputArg( Time );
end;

procedure TpsvSystemDispatch.NowProc;
begin
  ReturnOutputArg( Now );
end;

procedure TpsvSystemDispatch.IncMonthProc;
begin
  ReturnOutputArg( IncMonth( GetInputArgAsDateTime( 0 ),GetInputArgAsInteger( 1 ) ) );
end;

procedure TpsvSystemDispatch.IsLeapYearProc;
begin
  ReturnOutputArg( IsLeapYear( GetInputArgAsInteger( 0 ) ) );
end;

procedure TpsvSystemDispatch.DateToStrProc;
begin
  ReturnOutputArg( DateToStr( GetInputArgAsDateTime( 0 ) ) );
end;

procedure TpsvSystemDispatch.TimeToStrProc;
begin
  ReturnOutputArg( TimeToStr( GetInputArgAsDateTime( 0 ) ) );
end;

procedure TpsvSystemDispatch.DateTimeToStrProc;
begin
  ReturnOutputArg( DateTimeToStr( GetInputArgAsDateTime( 0 ) ) );
end;

procedure TpsvSystemDispatch.StrToDateProc;
begin
  ReturnOutputArg( StrToDate( GetInputArgAsString( 0 ) ) );
end;

procedure TpsvSystemDispatch.StrToTimeProc;
begin
  ReturnOutputArg( StrToTime( GetInputArgAsString( 0 ) ) );
end;

procedure TpsvSystemDispatch.StrToDateTimeProc;
begin
  ReturnOutputArg( StrToDateTime( GetInputArgAsString( 0 ) ) );
end;


procedure TpsvSystemDispatch.BeepProc;
begin
  Beep;
end;

procedure TpsvSystemDispatch.VarIsNullProc;
begin
  ReturnOutputArg( VarIsNull( GetInputArg( 0 ) ) );
end;

procedure TpsvSystemDispatch.RoundProc;
begin
  ReturnOutputArg( Integer(Round( GetInputArgAsFloat( 0 ) )) );
end;

procedure TpsvSystemDispatch.TruncProc;
begin
  ReturnOutputArg( Integer(Trunc( GetInputArgAsFloat( 0 ) )) );
end;

procedure TpsvSystemDispatch.CopyProc;
begin
  ReturnOutputArg( Copy(
         GetInputArgAsString( 0 ),
         GetInputArgAsInteger( 1 ),
         GetInputArgAsInteger( 2 ) ) );
end;


procedure TpsvSystemDispatch.PosProc;
begin
  ReturnOutputArg( pos(GetInputArgAsString( 0 ),GetInputArgAsString( 1 )) );
end;

procedure TpsvSystemDispatch.LengthProc;
begin
  ReturnOutputArg( Length( GetInputArgAsString( 0 ) ) );
end;

procedure TpsvSystemDispatch.ShowMessageProc;
begin
   ShowMessage( GetInputArgAsString( 0 ) );
end;

procedure TpsvSystemDispatch.RandomProc;
begin
  ReturnOutputArg( Random );
end;

procedure TpsvSystemDispatch.FormatProc;
var args  : array of TVarRec;
    v     : variant;
    lo,hi : integer;
    c     : integer;
    pvar  : ^variant;
begin
   begin
      v:=GetInputArg(1);
      if VarArrayDimCount(v)=1 then
      begin
         lo:=VarArrayLowBound(v,1);
         hi:=VarArrayHighBound(v,1);
         SetLength(args,hi-lo+1);
         pvar := VarArrayLock(v);
         try
            for c:=lo to hi do
            begin
               with TVarData(pvar^) do
                  case VType of
                     varByte,
                     varSmallint,
                     varInteger :
                        begin
                           args[c-lo].VInteger:=pvar^;
                           args[c-lo].VType:=vtInteger;
                        end;
                     varDate,
                     varSingle,
                     varDouble :
                        begin
                           New(args[c-lo].VExtended);
                           args[c-lo].VExtended^:=VDouble;
                           args[c-lo].VType:=vtExtended;
                        end;
                     varCurrency :
                        begin
                           args[c-lo].VCurrency:=@VCurrency;
                           args[c-lo].VType:=vtCurrency;
                        end;
                     varBoolean :
                        begin
                           args[c-lo].VBoolean:=pvar^;
                           args[c-lo].VType:=vtBoolean;
                        end;
                     varOleStr,
                     varStrArg :
                        begin
                           args[c].VPWideChar := VOleStr;
                           args[c].VType := vtPWideChar;
                        end;
                     varString :
                        begin
                           args[c-lo].VAnsiString := VString;
                           args[c-lo].VType := vtAnsiString;
                        end;
                     else begin Raise Exception.Create('Illegal argument to Format function'); end;
                  end;
               inc(pvar);
            end;

            ReturnOutputArg(Format(GetInputArgAsString(0),args));
         finally
            for c:=hi downto lo do
            begin
               dec(pvar);
               with TVarData(pvar^) do
                  case VType of
                     varDate, varSingle, varDouble :
                        Dispose(args[c-lo].VExtended);
                  end;
            end;
            VarArrayUnlock(v);
         end;
      end
      else
         begin  raise Exception.Create('Format function expects second argument as a variant vector'); end;
   end;
end;


end.

Regards,
Serhiy Perevoznyk, author of psvActiveScript
I think this will solve my problems, but going to leave up the question for a little longer case anyone else has any quick tips they can give me.

Also, with this, I can hav emore than one script going at once I assume?  Or should I queue it?  Basically going to have a whole bunch of events such as...

UserTalk
UserWhisper
UserEmote
SytemMessage
OnTimer
Etc.

And things will be going by VERY fast.  Most of this stuff will be triggered from an indy TCP reader thread and a timer.  Is this supposed to be Thread safe or should I use synchronize?  I probalbly need it synchronized for the memo.lines.add anyway.
OK, I have very easy solution, but strongly Delphi 7 oriented

unit myunit;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, psvActiveScript;

type
  {$METHODINFO ON}
  TDemo = class(TForm)
    psvActiveScriptWindow: TpsvActiveScriptWindow;
    Button1: TButton;
    Memo1: TMemo;
    Memo2: TMemo;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    Username, Flags, _Message, Ping : string;
    procedure DisplayString(S : String);
    function Blah : string;
  end;
  {$METHODINFO OFF}

var
  Demo: TDemo;

implementation

{$R *.dfm}

uses
 ObjComAuto;

function TDemo.Blah: string;
begin
  Result := 'Blah';
end;

procedure TDemo.Button1Click(Sender: TObject);
var
 DOB : TObjectDispatch;
begin
   UserName := 'ginsonic';
   Flags := '0';
   _Message := 'Hello';
   Ping := 'Ping';

  DOB := TObjectDispatch.Create(Demo, false);
  psvActiveScriptWindow.AddNamedItem('Demo', DOB);

  //just for demo
  Memo2.Clear;
  Memo2.Lines.Add('Event_UserTalk "' + UserName +
  '", "' + Flags + '", "' + _Message + '", "' + Ping + '"');
  Memo2.Lines.Add('Sub Event_UserTalk(Username, Flags, Message, Ping)');
  Memo2.Lines.Add('Demo.DisplayString "Hi "&Username&" Hi!"');
  Memo2.Lines.Add('Call Demo.DisplayString("My favorite word is "&Demo.Blah&"!")');
  Memo2.Lines.Add('End Sub');


  //event occurs, execute script

  psvActiveScriptWindow.Execute(Memo2.Text);

end;

procedure TDemo.DisplayString(S: String);
begin
  Memo1.Lines.Add(S);
end;

end.

If you are going to use a threads in your application you have to synchronize for memo.lines.add
You can create an instance of psvActiveScriptWindow for every thread to avoid synchronization of the scripting engine (anyway the values of the variables in your script must be unique for every thread)
Alright, I'm still missing one crucial piece of information....


How do I execute a specific function or sub with parameters without having to rewrite the script each time?

I mean, if I have an OnChat event, how do I call a sub inside the script

Sub OnChat(Name, Text)
End Sub

Where I give it a name and text during runtime?  All the examples I've seen, run with the variables inside the script already and not passing anything into the script.
psvActiveScript has a property ScriptInterface. It is a Dispatch interface of script engine. You can use it to assign value to the variables and to call specific functions and procedures. In this case you can write one long script with all functions you will need and call specific functions in your event handlers at run-time.

Yes, but how do I pass values to those functions at run-time?

Do I have to make functions that don't have parameters using globals that I change or something to simulate it, and then call the OnEvent(params) functions from there?
I just need to know how to call a specific sub/function in the script with PARAMETERS during RUN-TIME.  Is this possible?  It is really what I've been asking from the start.  I don't mean to seem rude, just trying to get this done.  :)
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Now that component looks more complete.  I've been awake too long.  I'll mess with it when I wake up and give points appropietly.  Thanks for all the help.
Ok.  Cool.  I tested it out and it looks like it works great.  Now I can proceed on implementing it in my application.  Thanks for all the help.  :)
If you happen to still be around, let me know.  :)