Problems with defined Record types.

I thank you for your help in my last post. But, I am now experiencing
the following 2 problems. I am putting them both here as they seem to be
related to each other. Please correct me if I am wrong.

With my last post about creating records, How to create record Types?
I had to get the last bit of code to bring the record within the
scripter. (Which is not even added to the source created by the importTool when using it. The
current version of importTool is the same in that regards.) At that
time, v7.2, it worked with my test record. At that time I put the coding
of my records on the backburner to deal with some other more urgent
needs. I have since then updated to v7.4 and just started working on the
record types again. And I have found that even with my original test record,
outline in my previous topic. I was not able to get it to be available
in scripter when using the

Scripter.DefineClass(TTestRecWrapper);


Even though the program would compile, and build, without any errors. I would receive the following error when trying to create/access there record from with in the script.

Unit Unit1: Unknown identifier or variable is not declared: 'TTestRec'.
Source Position: 7,23.

Even
using the DefineRecordByRtti did not work at first. And I was using the
same test record listed in my previous topic. Each time it would be defined but none of the properties were defined.

I
even tried it with the IDE demo by using the
source created by the importTool listed in my previous topic. With that
source, using the DefineClass method, I would get the same error message. But, after many times of
re-building the IDE demo with the source using the DefineRecordByRtti
did I finally get it to work. But, once I tried going back to using the
DefineClass. That error would be back. So I tried it in my custom IDE
with the Rtti version. And again after several re-builds it finally
worked. But, it still will not work with the DefineMethod. There was nothing different between sources except using the DefineClass or DefineRecordByRtti. I even left the predefined RecordWrapper in the source.

The
second problem I have is this. For some of my records I need access to
them on the Delphi side. As in some of the records will be used as
a command parameter. Since there is no information anywhere in the
atscripter.pas or atScript.pas on how to possibly go about accessing
such records on the Delphi side. I went an looked at any code that dealt
with a RecordWrapper to see how you may have done it. And I have found
that in most of the cases where a RecordWrapper is created there is no
code to access that record in the ap_*.pas file that I found the RecordWrapper in.

But, I did find 1 record type being used on the Delphi side. What it did was this:

  1. Define two objects, 1 a TObject and 1 the actual record.
  2. get the record from the script as a TObject and store it in the TObject object.
  3. check to see if the TObject object is not assigned or not the RecordWrapper
  4. If either is not true then the TObject object is then assigned the RecordWarpper with the Create(record object)
  5.  assign the record object with the TObject object as RecordWrapper typecasted with the ObjToRec.

This makes since as being the way to access record objects from the script.

example code from ap_ActnCtrls.pas
Param0Rec := TObject(integer(GetInputArg(0)));
if not Assigned(Param0Rec) or not (Param0Rec is TMessageWrapper) then 
  Param0Rec := TMessageWrapper.Create(Param0);
Param0 := TMessageWrapper(Param0Rec).ObjToRec;


Now
if I use the DefineRecordByRtti to define the record without any
predefined RecordWrapper. The above way will not compile. But, if I use
a predefined RecrodWrapper in conjunction with the DefineRecordByRtti.
This code will compile but does not seem to work. Every time the record
object is accessed on the Delphi side the record's are empty, even they are filled on the script side.

I have tried this in the IDE
demo and found it to be the same. I am sure that it is something that I
am missing or doing wrong. But, I think it has something to do with the
fact that Rtti defined records are not visible until the program is
running and then only from within the scripter. If that is the case then
I need to be able to use the DefineClass to get the records available
in scripter.

Well, now after some more testing I am now getting the following error when compiling the project if using the DefineClass to instantiate the RecordWrapper:

[dcc32 Error] ap_Test.pas(150): E2029 '(' expected but ')' found
[dcc32 Error] ap_Test.pas(151): E2010 Incompatible types: 'TClass' and 'TACTObject''
[dcc32 Error] ap_Test.pas(151): E2029 '(' expected but ')' found
[dcc32 Error] ap_Test.pas(154): E2066 Missing Operator or semicolon
[dcc32 Error] ap_Test.pas(156): E2010 Incompatible types: 'TClass' and  'procedure, untyped pointer or untyped parameter'

I'm getting the above error during compile with the following source:

Test.pas:

Unit Test;

interface

uses
  System.Types,
  System.Classes;

type
    TACTObject = record
        XCoord: integer;
        YCoord: integer;
        Def: TStringList;
    end;
    
    TWObject2 = record
      ObjectName: string;
      XCoord: integer;
      YCoord: integer;
      XSize: integer;
      YSize: integer;
      MinRed: integer;
      MinGreen: integer;
      MinBlue: integer;
      MaxRed: integer;
      MaxGreen: integer;
      MaxBlue: integer;
      Sample: string;
      SaveBMP: boolean;
      UseObject: boolean;
      Black: boolean;
      Filename: string;
    end;
    
implementation

end.



ap_Test.pas

{***************************************************************************}
{ This source code was generated automatically by                           }
{ Pas file import tool for Scripter Studio (Pro)                            }
{                                                                           }
{ Scripter Studio and Pas file import tool for Scripter Studio              }
{ written by TMS Software                                                   }
{            copyright c 1997 - 2010                                        }
{            Email : info@tmssoftware.com                                   }
{            Web : http://www.tmssoftware.com                               }
{***************************************************************************}
unit ap_Test;

interface

uses
  System.Types,
  System.Classes,
  Test,
  System.Variants,
  atScript;

{$WARNINGS OFF}

type
  TatTestLibrary = class(TatScripterLibrary)
    procedure Init; override;
    class function LibraryName: string; override;
  end;

  TACTObjectWrapper = class(TatRecordWrapper)
  private
    FXCoord: integer;
    FYCoord: integer;
    FDef: TStringList;
  public
    constructor Create(ARecord: TACTObject);
    function ObjToRec: TACTObject;
  published
    property XCoord: integer read FXCoord write FXCoord;
    property YCoord: integer read FYCoord write FYCoord;
    property Def: TStringList read FDef write FDef;
  end;

  TWObjectWrapper2 = class(TatRecordWrapper)
  private
    FObjectName: string;
    FXCoord: integer;
    FYCoord: integer;
    FXSize: integer;
    FYSize: integer;
    FMinRed: integer;
    FMinGreen: integer;
    FMinBlue: integer;
    FMaxRed: integer;
    FMaxGreen: integer;
    FMaxBlue: integer;
    FSample: string;
    FSaveBMP: boolean;
    FUseObject: boolean;
    FBlack: boolean;
    FFilename: string;
  public
    constructor Create(ARecord: TWObject2);
    function ObjToRec: TWObject2;
  published
    property ObjectName: string read FObjectName write FObjectName;
    property XCoord: integer read FXCoord write FXCoord;
    property YCoord: integer read FYCoord write FYCoord;
    property XSize: integer read FXSize write FXSize;
    property YSize: integer read FYSize write FYSize;
    property MinRed: integer read FMinRed write FMinRed;
    property MinGreen: integer read FMinGreen write FMinGreen;
    property MinBlue: integer read FMinBlue write FMinBlue;
    property MaxRed: integer read FMaxRed write FMaxRed;
    property MaxGreen: integer read FMaxGreen write FMaxGreen;
    property MaxBlue: integer read FMaxBlue write FMaxBlue;
    property Sample: string read FSample write FSample;
    property SaveBMP: boolean read FSaveBMP write FSaveBMP;
    property UseObject: boolean read FUseObject write FUseObject;
    property Black: boolean read FBlack write FBlack;
    property Filename: string read FFilename write FFilename;
  end;

implementation

constructor TACTObjectWrapper.Create(ARecord: TACTObject);
begin
  inherited Create;
  FXCoord := ARecord.XCoord;
  FYCoord := ARecord.YCoord;
  FDef := ARecord.Def;
end;

function TACTObjectWrapper.ObjToRec: TACTObject;
begin
  result.XCoord := FXCoord;
  result.YCoord := FYCoord;
  result.Def := FDef;
end;

constructor TWObjectWrapper2.Create(ARecord: TWObject2);
begin
  inherited Create;
  FObjectName := ARecord.ObjectName;
  FXCoord := ARecord.XCoord;
  FYCoord := ARecord.YCoord;
  FXSize := ARecord.XSize;
  FYSize := ARecord.YSize;
  FMinRed := ARecord.MinRed;
  FMinGreen := ARecord.MinGreen;
  FMinBlue := ARecord.MinBlue;
  FMaxRed := ARecord.MaxRed;
  FMaxGreen := ARecord.MaxGreen;
  FMaxBlue := ARecord.MaxBlue;
  FSample := ARecord.Sample;
  FSaveBMP := ARecord.SaveBMP;
  FUseObject := ARecord.UseObject;
  FBlack := ARecord.Black;
  FFilename := ARecord.Filename;
end;

function TWObjectWrapper2.ObjToRec: TWObject2;
begin
  result.ObjectName := FObjectName;
  result.XCoord := FXCoord;
  result.YCoord := FYCoord;
  result.XSize := FXSize;
  result.YSize := FYSize;
  result.MinRed := FMinRed;
  result.MinGreen := FMinGreen;
  result.MinBlue := FMinBlue;
  result.MaxRed := FMaxRed;
  result.MaxGreen := FMaxGreen;
  result.MaxBlue := FMaxBlue;
  result.Sample := FSample;
  result.SaveBMP := FSaveBMP;
  result.UseObject := FUseObject;
  result.Black := FBlack;
  result.Filename := FFilename;
end;

procedure TatTestLibrary.Init;
begin
  Scripter.DefineClass(TACTObject);  // statement is not create by importTool
  Scripter.DefineClass(TWObject2);   // statement is not create by importTool
  //Scripter.DefineRecordByRTTI(TypeInfo(TACTObject));  // statement is created by importTool  
  //Scripter.DefineRecordByRTTI(TypeInfo(TWObject2));   // statement is created by importTool
  With Scripter.DefineClass(ClassType) do
  begin
  end;
end;

class function TatTestLibrary.LibraryName: string;
begin
  result := 'Test';
end;

initialization
  RegisterScripterLibrary(TatTestLibrary, True);

{$WARNINGS ON}

end.



This makes no sense. And if I comment out the DefineClass statements and uncomment the DefineRecordByRtti statements. It will compile, but I am still getting the problem when attempting to access a instantiated record on the Delphi side. Here is the code that I am using to get the record from the script:

procedure TatTestAPILibrary.__readRecord(AMachine: TatVirtualMachine);
var
  wObject0: TObject;
  wObjectRec: TWObject2;

begin
  with AMachine do
  begin
    // get the record from the script
    wObject0 := TObject(integer(GetInputArg(0)));
    if not Assigned(wObject0) or not (wObject0 is TWObjectWrapper2) then
      wObject0 := TWObjectWrapper2.Create(wObjectRec);
    wObjectRec := TWObjectWrapper2(wObject0).ObjToRec;
    // check the record's properties to make sure they are populated.
    if wObjectRec.ObjectName = '' then
      RuntimeError('CreateObject Command Error:' + #13#13 +
        'wObject definition Error.' + #13#13 + 'ObjectName is not defined.' + #13);
    if (wObjectRec.XCoord < 0) or (wObjectRec.XCoord > Screen.Width) then
      RuntimeError('CreateObject Command Error:' + #13#13 +
        'wObject definition Error.' + #13#13 + 'XCoord is out of range!' + #13);
    if (wObjectRec.YCoord < 0) or (wObjectRec.YCoord > Screen.Height) then
      RuntimeError('CreateObject Command Error:' + #13#13 +
        'wObject definition Error.' + #13#13 + 'YCoord is out of range!' + #13);
    if (wObjectRec.XSize < 0) or (wObjectRec.XSize > Screen.Width) then
      RuntimeError('CreateObject Command Error:' + #13#13 +
        'wObject definition Error.' + #13#13 + 'XSize is out of range!' + #13);
    if (wObjectRec.YSize < 0) or (wObjectRec.YSize > Screen.Height) then
      RuntimeError('CreateObject Command Error:' + #13#13 +
        'wObject definition Error.' + #13#13 + 'YSize is out of range!' + #13);
    if (wObjectRec.MinRed < 0) or (wObjectRec.MinRed > 255) then
      RuntimeError('CreateObject Command Error:' + #13#13 +
        'wObject definition Error.' + #13#13 + 'MinRed is out of range!' + #13);
    if (wObjectRec.MinGreen < 0) or (wObjectRec.MinGreen > 255) then
      RuntimeError('CreateObject Command Error:' + #13#13 +
        'wObject definition Error.' + #13#13 + 'MinGreen is out of range!' + #13);
    if (wObjectRec.MinBlue < 0) or (wObjectRec.MinBlue > 255) then
      RuntimeError('CreateObject Command Error:' + #13#13 +
        'wObject definition Error.' + #13#13 + 'MinBlue is out of range!' + #13);
    if (wObjectRec.MaxRed < 0) or (wObjectRec.MaxRed > 255) then
      RuntimeError('CreateObject Command Error:' + #13#13 +
        'wObject definition Error.' + #13#13 + 'MaxRed is out of range!' + #13);
    if (wObjectRec.MaxGreen < 0) or (wObjectRec.MaxGreen > 255) then
      RuntimeError('CreateObject Command Error:' + #13#13 +
        'wObject definition Error.' + #13#13 + 'MaxGreen is out of range!' + #13);
    if (wObjectRec.MaxBlue < 0) or (wObjectRec.MaxBlue > 255) then
      RuntimeError('CreateObject Command Error:' + #13#13 +
        'wObject definition Error.' + #13#13 + 'MaxBlue is out of range!' + #13);
    if (wObjectRec.Sample = '') or (LowerCase(wObjectRec.Sample) <> 'full') or
      (LowerCase(wObjectRec.Sample) <> 'half') then
      RuntimeError('CreateObject Command Error:' + #13#13 +
        'wObject definition Error.' + #13#13 + 'Sample is not defined!' + #13);
    if wObjectRec.Filename = '' then
      RunTimeError('CreateObject Command Error:' + #13#13 +
        'wObject definintion Errror.' + #13#13 + 'Filename is not defined!' + #13);

    CreateObject2(wObjectRec);
  end;
end;


And here is the test script that I am using:

uses
  Classes, Test;
var
  testWObject: TWObject;
begin
  testWObject := TWObject.Create;
  testWObject.ObjectName := 'test';
  readRecord(testWObject);
end;


With the above code I receive the RunTimeError that the ObjectName property is not defined when as you can see it was spcefically defined in the script.

Okay, forget my compile error. I found the cause of the compile error
when using the DefineClass statements. Use TWObject2 instead of
TWObjectWrapper2. But even with that fix, I am still getting the
following error with the testWObject := TWObject2.Create;

.

So,
now I am down to 2 problems. Instantiating the record in scripter with
DefineClass(TWObjectWrapper2) causes the above error when instantiating
the record in a script. While instantiating the record in scripter with
DefineRecordByRtti seems to cause the record properties to be empty when
accessing the record on the Delphi side.

Okay, after some more off the wall tries. I finally got the above code to work properly with instantiating the record in scripter with DefineClass statement. It seems that you had forgotten a parameter when using the that statement to instantiate records. In my first post about records you stated that I needed to use the following statement:

Scripter.DefineClass(TWObjetWrapper2);


But, that is not so. What you need to use is:

Scripter.DefineClass(TWObjectWrapper, 'TWObject2');


This not only instantiates the record in scripter but also gives the name to use for the record type in the scripter.

With this I am able to instantiate records in scripter and access them completely from the Delphi side.

But, the Rtti is still not working. You might want to look into this. As I am sure that there might be someone that will create record types in their scripter by way of the Rtti and want access to them.

Now for all of those that may read this Topic. Here is the correct way to instantiate record types in your own scripter using the DefineClass statements:

{***************************************************************************}
{ This source code was generated automatically by                           }
{ Pas file import tool for Scripter Studio (Pro)                            }
{                                                                           }
{ Scripter Studio and Pas file import tool for Scripter Studio              }
{ written by TMS Software                                                   }
{            copyright c 1997 - 2010                                        }
{            Email : info@tmssoftware.com                                   }
{            Web : http://www.tmssoftware.com                               }
{***************************************************************************}
unit ap_Test;

interface

uses
  System.Types,
  System.Classes,
  Test,
  System.Variants,
  atScript;

{$WARNINGS OFF}

type
  TatTestLibrary = class(TatScripterLibrary)
    procedure Init; override;
    class function LibraryName: string; override;
  end;

  TACTObjectWrapper = class(TatRecordWrapper)
  private
    FXCoord: integer;
    FYCoord: integer;
    FDef: TStringList;
  public
    constructor Create(ARecord: TACTObject);
    function ObjToRec: TACTObject;
  published
    property XCoord: integer read FXCoord write FXCoord;
    property YCoord: integer read FYCoord write FYCoord;
    property Def: TStringList read FDef write FDef;
  end;

  TWObjectWrapper2 = class(TatRecordWrapper)
  private
    FObjectName: string;
    FXCoord: integer;
    FYCoord: integer;
    FXSize: integer;
    FYSize: integer;
    FMinRed: integer;
    FMinGreen: integer;
    FMinBlue: integer;
    FMaxRed: integer;
    FMaxGreen: integer;
    FMaxBlue: integer;
    FSample: string;
    FSaveBMP: boolean;
    FUseObject: boolean;
    FBlack: boolean;
    FFilename: string;
  public
    constructor Create(ARecord: TWObject2);
    function ObjToRec: TWObject2;
  published
    property ObjectName: string read FObjectName write FObjectName;
    property XCoord: integer read FXCoord write FXCoord;
    property YCoord: integer read FYCoord write FYCoord;
    property XSize: integer read FXSize write FXSize;
    property YSize: integer read FYSize write FYSize;
    property MinRed: integer read FMinRed write FMinRed;
    property MinGreen: integer read FMinGreen write FMinGreen;
    property MinBlue: integer read FMinBlue write FMinBlue;
    property MaxRed: integer read FMaxRed write FMaxRed;
    property MaxGreen: integer read FMaxGreen write FMaxGreen;
    property MaxBlue: integer read FMaxBlue write FMaxBlue;
    property Sample: string read FSample write FSample;
    property SaveBMP: boolean read FSaveBMP write FSaveBMP;
    property UseObject: boolean read FUseObject write FUseObject;
    property Black: boolean read FBlack write FBlack;
    property Filename: string read FFilename write FFilename;
  end;

implementation

constructor TACTObjectWrapper.Create(ARecord: TACTObject);
begin
  inherited Create;
  FXCoord := ARecord.XCoord;
  FYCoord := ARecord.YCoord;
  FDef := ARecord.Def;
end;

function TACTObjectWrapper.ObjToRec: TACTObject;
begin
  result.XCoord := FXCoord;
  result.YCoord := FYCoord;
  result.Def := FDef;
end;

constructor TWObjectWrapper2.Create(ARecord: TWObject2);
begin
  inherited Create;
  FObjectName := ARecord.ObjectName;
  FXCoord := ARecord.XCoord;
  FYCoord := ARecord.YCoord;
  FXSize := ARecord.XSize;
  FYSize := ARecord.YSize;
  FMinRed := ARecord.MinRed;
  FMinGreen := ARecord.MinGreen;
  FMinBlue := ARecord.MinBlue;
  FMaxRed := ARecord.MaxRed;
  FMaxGreen := ARecord.MaxGreen;
  FMaxBlue := ARecord.MaxBlue;
  FSample := ARecord.Sample;
  FSaveBMP := ARecord.SaveBMP;
  FUseObject := ARecord.UseObject;
  FBlack := ARecord.Black;
  FFilename := ARecord.Filename;
end;

function TWObjectWrapper2.ObjToRec: TWObject2;
begin
  result.ObjectName := FObjectName;
  result.XCoord := FXCoord;
  result.YCoord := FYCoord;
  result.XSize := FXSize;
  result.YSize := FYSize;
  result.MinRed := FMinRed;
  result.MinGreen := FMinGreen;
  result.MinBlue := FMinBlue;
  result.MaxRed := FMaxRed;
  result.MaxGreen := FMaxGreen;
  result.MaxBlue := FMaxBlue;
  result.Sample := FSample;
  result.SaveBMP := FSaveBMP;
  result.UseObject := FUseObject;
  result.Black := FBlack;
  result.Filename := FFilename;
end;

procedure TatTestLibrary.Init;
begin
  Scripter.DefineClass(TACTObjectWrapper, 'TACTObject');
  Scripter.DefineClass(TWObjectWrapper2, 'TWObject2');
  With Scripter.DefineClass(ClassType) do
  begin
  end;
end;

class function TatTestLibrary.LibraryName: string;
begin
  result := 'Test';
end;

initialization
  RegisterScripterLibrary(TatTestLibrary, True);

{$WARNINGS ON}

end.

Hello, I'm glad you have solved your problem. I guess the only remaining issue is some report about records being defined by RTTI not working, correct? 

However, since your posts seems to be an experience where you experimented and changed source code a lot, I would appreciate if you could e-mails us with your "final" test project that reproduces the issue with records being defined by RTTI? With that we could check what's wrong with either yours or our code, whenever the problem is. Thank you in advance.

Okay, I have sent code to support.  The archive has 3 files. The Test.pas which only has the original record definitions. the ap_Test.pas file originally created by the importTool. This was modified only to add the ReadRecord method to be able to access the record that is passed as a parameter. And script to test the creation, and population, of the record. And then the passing of the record to the method.

I also mention in the email the fact that the importTool does not include the DefineClass statement when creating the ap_*.pas file for records. But, will include the DefineRecordByRtti statement if doing the same but using the Rtti method instead.


Answered through e-mail. In case it helps other users:


this is how you receive a record created in script using DefineRecordByRTTI. When using that method, all records in scripter are represented by the TGenericRecordWrapper class. Thus when received those “records” in a Delphi method via parameter, you need to cast the object to that class and then retrieve the record pointer:

type
  PWObject = ^TWObject;

procedure TatTestLibrary.__ReadRecord(AMachine: TatVirtualMachine);
var
  wObjectRec: TWObject;
  Wrapper: TGenericRecordWrapper;
begin
  with AMachine do
  begin
    //Get the record from the script
    Wrapper := VarToObject(AMachine.GetInputArg(0)) as TGenericRecordWrapper;
    wObjectRec := PWObject(Wrapper.Rec)^;

    // At this point in my actual code would be a call to the
    // procedure use the wObjectRec for what it is needed.
  end;
end;