Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

In this Discussion

SCRIPT PACK and variantarrays


I am trying to call inside a script functions that belong to an automation server like Excel or Word. The exact code that runes in Delphi in late binding is like this:


   ModValue:= VarArrayCreate([0,7], varDouble);

   For i:=0 To 7 do begin
        ModValue[i]:= 1;

   ret:= SapModel.PropFrame.SetModifiers('R1',  VarArrayRef(ModValue));

because open array parameters are not supported I created these functions that I wrapped for the script in another unit:

function Create1DArrayDouble(const L,U:Integer):variant;
     Result:=VarArrayCreate([L,U], varDouble);

function ArrayRef(obj:variant):IUnknown ;

and then I write in the script:


   For i:=0 To 7 do begin
        ModValue[i]:= 1;


   ret:= SapModel.PropFrame.SetModifiers('R1', ArrayRef(ModValue));

However it complains about data mismatch in the variable passed by ArrayRef(ModValue) and if I used olevariant instead of variant the application just end abruptly


  • 34 Comments sorted by Votes Date Added
  • Hard to tell you what may be wrong.
    Can you try to declare some test function in Delphi instead of SapModel.PropFrame.SetModifiers, import it into script, and try to pass array as you do now via script? 
    If it will run ok, then you can try to call SapModel.PropFrame.SetModifiers from this wrapper function.
  • The fist example is in Delphi and it goes ok. It only fails in the script because it receives a COM error. 
    Do you have any example at all of using automation server that receives an array as argument? This is basically the only problem.  
  • I tried many possible ways of passing the arguments and wrapping it but nothing worked...I really need help on this. Otherwise automation become impossible to use, as many automation servers receive and output arrays safearrays, that in latebinding are managed as variant arrays
  • However I cannot wrap the function SapModel.PropFrame.SetModifiers as that would mean I would wrap thousands of API calls
  • edited February 17 Posts: 0 Accepted Answer Vote Up0Vote Down
    1) Why you use IUnknown as a return type of ArrayRef? Use Variant.
    2) "obj" parameter of ArrayRef function is temporary value, just like any other parameter or local variable. And so, it will be destroyed after function execution. So, its definitely wrong to return a reference (pointer really) to it. I think the fact that it worked in Delphi is a pure luck. Not sure, whether this will work, but just for debug tryto use var parameter.

    ArrayRef(var obj: Variant): Variant;

  • IUnknown was there because I made an experiment and I accidentally left it in the code the moment I pasted here. I used variant and also olevariant
  • I've checked additionally, and both of the following ways work fine:

    ArrayRef(var obj: Variant): Variant;
    ArrayRef(const obj: Variant): Variant;
  • Thank you. It works. 

    I have a new problem that is after I get an active object under a Idispatch variable using a function in delphi that will be wrapped in the script

            ClassID := ProgIDToClassID(ClassName);
           nresult:=  GetActiveObject(ClassID, nil, Unknown);

            nresult:=Unknown.QueryInterface(CLSIDfromTheInterface), ret);

    I get a very nice IDispatch that works nice if I cast it with an interface in Delphi. However if I want to pass it as a variable to be understood by the script I must pass it to variant or olevariant. However, when I do that, it gives error and does not work.

    Any Idea on how to get an active instance of a COM object that supports DUAL interface using olevariant to expose it to the scripter?

  • ScriptPack understand only Variants, IDispatch can be converted to Variant as well. All other interfaces do not really relate to scripting.
  • Thank you. I am having this type of memory leaks just after running one script once


    Any idea what to do?
  • Could you provide a tool to parse the wrapper and generate a simple reference guide to all exposed classes, functions, procedures and variables? Thank you. 
  • Also another question. Whats it the fastest way to call thousands of times a given function or procedure that has input and output arguments?

    Imagine I want to call from Delphi a function in the script that does:

    DOSomething(Input1,Input2,Output1,Output2 )


  • 1) About memory leak: please provide a script text.
    2) "tool to parse the wrapper" - no.
    3) Keep script control active and use RunProc.

  • procedure Main;
        // variable declaration. < All variables need to be declared >
        INPUT_SAPVERSION;   // a number between 19 and 22
        INPUT_SAPPATH;      // The full path where the SAP2000.EXE is located
        INPUT_SAVEDMODEL;   // The full path of the saved model
        FRM;                // avariable to hold a form object where our log text will be output
        SAP;                // The Automation Object used to acess the SAP2000 Model
        SAPModel;           // The SAP Model variable
        ret,ret1,ret2,ret3; // Return values from most API functions. Returning zero = success
        eMatType_Concrete;  // A constant to define the material enumeration index. Check CSI OAPI reference
        ModValue;           // An array that is used to hold the section property modifiers
        eUnits_kip_ft_F;    // A constant to define the required units enumeration. Check CSI OAPI reference 
        eUnits_kip_in_F;    // A constant to define the required units enumeration. Check CSI OAPI reference 
        i,j;                // Necessary variables to hold cycles
        Restraint;          // An array that is used to hold the restraint boolean options for the 6 dof of a joint
        FrameName0,FrameName1,FrameName2; // Variables used to hold the frame names
        PointName0,PointName1;            // Variables used to hold the point names
        eLoadPatternType_Other;  // A constant to define Load type enumeration. Check CSI OAPI reference 
        PointLoadValue;     // An array that is used to apply forces on points
        SapResult;          // a normal array used to store displacements
        NumberResults;      // value to store the number of retrieved results  
        Obj;                // String Array to be used retrieving results
        Elm;                // String Array to be used retrieving results
        LoadCase;           // String Array to be used retrieving results
        StepType;           // String Array to be used retrieving results
        StepNum;            // double Array to be used retrieving results
        U1;                 // double Array to be used retrieving results
        U2;                 // double Array to be used retrieving results
        U3;                 // double Array to be used retrieving results
        R1;                 // double Array to be used retrieving results
        R2;                 // double Array to be used retrieving results
        R3;                 // double Array to be used retrieving results
        eItemTypeElm_ObjectElm;// A constant to define result type enumeration. Check CSI OAPI reference 
        s;                  // a useful string

        // Define input
        // CHANGE HERE ->->->->->->->->
        INPUT_SAPPATH:='C:\Program Files\Computers and Structures\SAP2000 22\SAP2000.EXE';
        INPUT_SAVEDMODEL:='D:\temp\SAP Temp\MyModel1.sdb';
        // <-<-<-<-<-<-<-<-< CHANGE HERE 
        // Starting API
        SAP.ApplicationStart; SapModel:= SAP.SapModel;
        ret:= SapModel.InitializeNewModel;
        ret:= SapModel.File.NewBlank;

        // Define the concrete type of material
        eMatType_Concrete:=2; // check SAP2000 OAPI manual to find this number assignment
        ret:= SapModel.PropMaterial.SetMaterial('CONC', eMatType_Concrete);
        ret:= SapModel.PropMaterial.SetMPIsotropic('CONC', 3600, 0.2, 0.0000055);

        // Define rectangular frame section property
        ret:= SapModel.PropFrame.SetRectangle('R1', 'CONC', 12, 12);
        ModValue:=CreateDoubleArray;  RedimArray(ModValue,8);
        For i:=0 To 7 do begin
        ModValue[0]:=1000;  // Area
        ModValue[1]:=0;     // Shear 2
        ModValue[2]:=0;     // Shear 3
        ret:= SapModel.PropFrame.SetModifiers('R1', ArrayRef(Modvalue));
        // Switch to k-ft units
        eUnits_kip_ft_F:=4;  // check SAP2000 OAPI manual to find this number assignment
        ret:= SapModel.SetPresentUnits(eUnits_kip_ft_F);

        // Add frame object by coordinates
        ret1:= SapModel.FrameObj.AddByCoord(0, 0, 0, 0, 0, 10, FrameName0, 'R1', '1');
        ret2:= SapModel.FrameObj.AddByCoord(0, 0, 10, 8, 0, 16, FrameName1, 'R1', '2');
        ret3:= SapModel.FrameObj.AddByCoord(-4, 0, 10, 0, 0, 10, FrameName2, 'R1', '3');
        ret:=concat(concat(ret1,' - '),concat(ret2,' - '),ret3);

        // Assign point object restraint at base and top
        Restraint:=CreateBooleanArray;  RedimArray(Restraint,6);
        Restraint[0]:=True;   Restraint[1]:=True;   Restraint[2]:=True;
        Restraint[3]:=True;   Restraint[4]:=False;  Restraint[5]:=False;
        ret:= SapModel.FrameObj.GetPoints(FrameName0, PointName0, PointName1);
        ret:= SapModel.PointObj.SetRestraint(PointName0, ArrayRef(Restraint));
        Restraint[0]:=True;   Restraint[1]:=True;   Restraint[2]:=false;
        Restraint[3]:=false;   Restraint[4]:=False;  Restraint[5]:=False;
        ret:= SapModel.FrameObj.GetPoints(FrameName1, PointName0, PointName1);
        ret:= SapModel.PointObj.SetRestraint(PointName1, ArrayRef(Restraint));

        // Refresh view, update (initialize) zoom
        ret:= SapModel.View.RefreshView(0, False);

        // Add Load patterns
        eLoadPatternType_Other:=8; // check SAP2000 OAPI manual to find this number assignment

        // Add the load patterns
        ret:= SapModel.LoadPatterns.Add('1', eLoadPatternType_Other, 1);
        ret:= SapModel.LoadPatterns.Add('2', eLoadPatternType_Other);
        ret:= SapModel.LoadPatterns.Add('3', eLoadPatternType_Other);
        ret:= SapModel.LoadPatterns.Add('4', eLoadPatternType_Other);                           
        ret:= SapModel.LoadPatterns.Add('5', eLoadPatternType_Other);
        ret:= SapModel.LoadPatterns.Add('6', eLoadPatternType_Other);
        ret:= SapModel.LoadPatterns.Add('7', eLoadPatternType_Other);

        // Assigning Load to load pattern 2
        ret:= SapModel.FrameObj.GetPoints(FrameName2, PointName0, PointName1);
        PointLoadValue:=CreateDoubleArray;  RedimArray(PointLoadValue,6);
        For i:=0 to 5 do PointLoadValue[i]:=0;
        PointLoadValue[2]:= -10;
        ret:=SapModel.PointObj.SetLoadForce(PointName0, '2', ArrayRef(PointLoadValue));
        ret:=SapModel.FrameObj.SetLoadDistributed(FrameName2, '2', 1, 10, 0, 1, 1.8, 1.8);

        // Assigning Load to load pattern 3
        ret:= SapModel.FrameObj.GetPoints(FrameName2, PointName0, PointName1);
        For i:=0 to 5 do PointLoadValue[i]:=0; 
        PointLoadValue[2]:= -17.2;
        PointLoadValue[4]:= -54.4;
        ret:=SapModel.PointObj.SetLoadForce(PointName1, '3', ArrayRef(PointLoadValue));

        // Assigning Load to load pattern 4
        ret:= SapModel.FrameObj.SetLoadDistributed(FrameName1, '4', 1, 11, 0, 1, 2, 2);

        // Assigning Load to load pattern 5
        ret:= SapModel.FrameObj.SetLoadDistributed(FrameName0, '5', 1, 2, 0, 1, 2, 2, 'Local');
        ret:= SapModel.FrameObj.SetLoadDistributed(FrameName1, '5', 1, 2, 0, 1, -2, -2, 'Local');

        // Assigning Load to load pattern 6
        ret:= SapModel.FrameObj.SetLoadDistributed(FrameName0, '6', 1, 2, 0, 1, 0.9984, 0.3744, 'Local');
        ret:= SapModel.FrameObj.SetLoadDistributed(FrameName1, '6', 1, 2, 0, 1, -0.3744, 0, 'Local');

        // Assigning Load to load pattern 7
        ret:= SapModel.FrameObj.SetLoadPoint(FrameName1, '7', 1, 2, 0.5, -15, 'Local');

        // Free the array that was initially used in the begging of the load assignment

        // Switch to k-in units
        eUnits_kip_in_F:=3;  // check SAP2000 OAPI manual to find this number assignment
        ret:= SapModel.SetPresentUnits(eUnits_kip_in_F);

        // Save the Model;
        // Run the Model

        // Get results for load patterns 1 through 7
        SapResult:=Array[0..6] of double;
        ret:= SapModel.FrameObj.GetPoints(FrameName1, PointName0, PointName1);

        // Cycle the Load patterns
        For i:= 0 to 6 do begin

            ret:= SapModel.Results.Setup.DeselectAllCasesAndCombosForOutput;
            ret:= SapModel.Results.Setup.SetCaseSelectedForOutput(inttostr(i + 1));
            If i <= 3 then begin

                ret:= SapModel.Results.JointDispl(
                                                    ArrayRef(Obj) ,

                // Store result
                SapResult[i]:= U3[0];

            end else begin

                ret:= SapModel.Results.JointDispl(
                                                    ArrayRef(Obj) ,

                // Store result
                SapResult[i]:= U1[0];



                    floattostr(SAPResult[0])+' | '+
                    floattostr(SAPResult[1])+' | '+
                    floattostr(SAPResult[2])+' | '+
                    floattostr(SAPResult[3])+' | '+
                    floattostr(SAPResult[4])+' | '+
                    floattostr(SAPResult[5])+' | '+

        // Clear the arrays


  • Hi. Any comment?
  • Checked your code. See no memory leaks. I remeber some time ago we fixes the bug like this, so, probably you use not the latest version.
  • Yes, I do not have the latest version...How Can I upgrade the code?
  • I see that our old bug was related to global contants initializer, but your script code does not declare global contants. Are you sure that erorr is produces by this code?
    Can you do the following for debugging purposes: 

    1) Open HelloWorld demo
    2) Comment our the line: LMDScriptControl1.RunProc('Main', [7, 'asdsa']);
    3) Add ReportMemoryLeaksOnShutdown := True in main form unit initialization.
    4) Run demo, copy your script code into the memo and click Run (this will compile the code).
    5) Close the demo and look whether some memory leaks are reported.

  • Yes. The same memory leaks

    TLMDDesignerExpr x 4  Tlist x 8
    unknown x 4
    TLMDDesignatorItem x 4
  • I just added two simple wrapper units that add a few functions I need in the script. That is the only diference between the original helloworls
  • Ok. So, leak happens exactly during mentioned script code compiling (wrapper units are not used at this stage).

    Than I have no advice other than upgrading to latest version, because designator expression is any identifier reference: variable reference, like simple X, any chain of dot references, like X.Y.Z, any calls with parameters, like X(7, 8).Y('sdfdf'), any array member access: X[7].

    So, I have no idea what are special four cases in your script, which leads to memory leaks. Your script actually contains 333 designator expressions.
  • I see four "for" loops. If you have sources, can you look at TLMDForStmt.Destroy method in LMDSctPasCompiler.pas unit. It should look like this:

    destructor TLMDForStmt.Destroy;
  • Thanks. that solved. the first line was missing.

    I am still finding a problem that is for some reason this library i am using when trying to get Active OLE object return an object that ony works if it is casted to an interface. If casted to Olevariant, it does not work..Do you have any way to wrap a COM interface as a variable? I can't make it work in Delphi even if it is olevariant. Only if it is the real ActiveX class
  • Try cast to IDispatch using YourComObject.QueryInterface(...), may be it implemented.
  • Hi. 




    I get Invalid typecast or type mismatch n the mat argument of the length function..Do you know why?
  • Hi, any news about this bug? I can't find a way to get the length of an array
  • And I want to pass these variables as arguments between functions
  • Please use Low and High functions for arrays. Length and SetLength curently works only for strings in script code.
  • How can I pass arrays between Delphi and the script?
  • Arrays of records or of objects?
  • And how can I pass lists or arrays of objects or records as arguments . From Delphi to the script?
  • Delphi arrays are not supported. In script you can define arrays of objects (which are records too).
    You can learn docs here:
  • But how can I pass them from Delphi to Script and from script to Delphi?
  • Delphi arrays are not supported. No way. Implement your own collection classes.
Sign In or Register to comment.