1

I am rewrite lib from delphi to c#. The library method is called in the delphi client with the following parameter

T:=VarArrayCreate([1,2,1,1],varDouble);
T[1,1]:=Tmin;
T[2,1]:=Tmax;

I tried to pass the same array to c # like this:

 Array tVector = Array.CreateInstance(typeof(double), new[] { 2, 1 }, new[] { 1, 1 });
 tVector.SetValue(Tmin, 1, 1);
 tVector.SetValue(Tmax, 2, 1);

 object T = tVector;

Then thrown out

System.Runtime.InteropServices.SEHException: "External component has thrown an exception."

And tried like this:

 Array tVector1 = Array.CreateInstance(typeof(double), new[] { 1 }, new[] { 1 });
 Array tVector2 = Array.CreateInstance(typeof(double), new[] { 1 }, new[] { 1 });
 Array tVector = Array.CreateInstance(tVector1.GetType(), new[] { 2 }, new[] { 1 });

 tVector.SetValue(tVector1, 1);
 tVector.SetValue(tVector2, 2);

 object T = tVector;

Then thrown out

System.Runtime.InteropServices.SafeArrayTypeMismatchException: "Specified array was not of the expected type."

Init conditions:

c# calls the delphi method:

function  call (hModel: TModelHandle; const FunName: variant; var Params: variant; var Res: variant): TRetCode; stdcall;

Params is a 1d variant array. I prepare it as follows:

public object Call(string functionName, params object[] parameters)
{
        object res = new();

        object funName = functionName;
        
        Array endParamsArray = Array.CreateInstance(typeof(object), new[] {parameters.Length}, new[] {1});
        Array.Copy(parameters, endParamsArray, parameters.Length);
        object endParams = endParamsArray;
        
        var rc = call(hModel, ref funName, ref endParams, ref res);

        if (rc != TRetCode.rcOK)
            error(rc);

        return res;
}

And then I call:

 object T = tVector;

 PM.Call("funname", ID, Name, T, RamStab, Ktrans, sec_overcoming);

When the passed parameters are simple: int, string, etc. everything works well. But passing an array I get an exception

And this is the client code on Delphi, in which everything works:

 w:='funname'; FunName:=w;
Params:=VarArrayCreate([1,6],varVariant);
Params[1]:=ID;
Params[2]:=Name;
T:=VarArrayCreate([1,2,1,1],varDouble);
T[1,1]:=Tmin;
T[2,1]:=Tmax;
Params[3]:=T;
Params[4]:=RamStab;
Params[5]:=Ktrans;
Params[6]:=sec_overcoming;
rc:=PM.call(PM.hModel,FunName,Params,Res);
Bar:=PM.getObject(Name);
if Bar=nil then
  error('create');
7
  • @Renat An exception is thrown when this parameter is passed to the delphi library Commented Feb 4, 2021 at 18:53
  • 2
    Unclear who is calling who... If it is delphi that is calling c#, you could try to define your method as receiving [MarshalAs(UnmanagedType.SafeArray)] Array ar and see if it breaks. If it doesn't break then we can work on how to extract the data, or even [MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_R8)] Array ar Commented Feb 4, 2021 at 19:13
  • @xanatos In contrast, c# calls the delphi method. The signature is as follows: function call (hModel: TModelHandle; const FunName: variant; var Params: variant; var Res: variant): TRetCode; stdcall; Params is object T in example. Commented Feb 4, 2021 at 19:46
  • 2
    @Egorosh It is passing variants, not safearrays.... You can create the array as you were doing in the first example, and then pass it like: [MarshalAs(UnmanagedType.LPStruct) object obj]... But I'm quite unsure about the first parameter. (technically it is passing a safearray INSIDE a variant) Commented Feb 4, 2021 at 19:49
  • @xanatos I Changed the description of the question - added initial conditions Commented Feb 4, 2021 at 20:18

1 Answer 1

1

Found what you are referencing: https://www.mvstudium.com/eng/RMDmanual-en.pdf

function call (hModel: TModelHandle; const FunName: variant; var Params: variant; var Res: variant): TRetCode; stdcall;

TRetCode __stdcall call (TModelHandle hModel, variant *FunName, variant *Params, variant *Res);

Where

TRetCode = integer – completion code.

and

TModelHandle = THandle – pointer to a model;

So I would sai TModelHandle is a IntPtr (32 bits with 32 bits processes, 64 bits with 64 bits processes). So:

[DllImport(@"Yourdll", CallingConvention = CallingConvention.StdCall)]
public static extern int call(IntPtr hModel, ref object funName, ref object @params, ref object res);

public static object Call(string functionName, params object[] parameters)
{
    object res = null;

    object funName = functionName;

    Array endParamsArray = Array.CreateInstance(typeof(object), new[] { parameters.Length }, new[] { 1 });
    Array.Copy(parameters, endParamsArray, parameters.Length);
    object endParams = endParamsArray;

    var rc = call(hModel, ref funName, ref endParams, out res);

    return res;
}

I've built a C#->C++ code with the signature proposed for C++ and it works correctly. The parameters are correctly passed, res is correctly passed out.

double Tmin = double.MinValue;
double Tmax = double.MaxValue;

int ID = 1;
string Name = "Foo";
double RamStab = 5;
long Ktrans = 3;
int sec_overcoming = 2;

Array tVector = Array.CreateInstance(typeof(double), new[] { 2, 1 }, new[] { 1, 1 });
tVector.SetValue(Tmin, 1, 1);
tVector.SetValue(Tmax, 2, 1);

object response = Call("Hello world", ID, Name, tVector, RamStab, Ktrans, sec_overcoming);
Sign up to request clarification or add additional context in comments.

4 Comments

It still doesn't work for me(( I checked on delphi and everything works there:yadi.sk/d/mN2QlIpL1KkuGQ I will write to the RMD developers....
@Egorosh At this point I'm curious of what the problem is :-) So when you discover it, write about it :-)
This was an internal model error. I didn't set the desired variable inside the model before calling call and ended up throwing an unhandled exception there=))
@Egorosh 🤣🤣🤣🤣🤣🤣🤣🤣

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.