views:

443

answers:

3

I have a C# program that is calling into a native C++ dll.

The C# has a the following struct:

[StructLayout(LayoutKind.Sequential)]
public struct Phenomenon
{
    [MarshalAs(UnmanagedType.U1)] public char Type;
    [MarshalAs(UnmanagedType.R8)] public double Jd;
    [MarshalAs(UnmanagedType.R8)] public double Loc_jd;
    [MarshalAs(UnmanagedType.R8)] public double Angle;
    [MarshalAs(UnmanagedType.U1)] public char Tran_dir;
    [MarshalAs(UnmanagedType.I2)] public Int16 Warning;
}

The C++ program has the following struct definition:

   typedef struct
   {
      char type;
      double jd;
      double loc_jd;
      double angle;
      char tran_dir;
      short int warning;
   } phenomenon;

The C# program instantiates the Phenomenom objects has the following call to the C++ method:

Phenomenon[] phenomena = new Phenomenon[6];
short status = rsttwi_temp(phenomena);

The C++ simply updates the struct and returns. Here is the C++ function:

__declspec(dllexport) short int rsttwi_temp (phenomenon phen[1])
{
    phen[0].type = 'Z';
    phen[0].jd = 1.1;
    phen[0].loc_jd = 2.2;
    phen[0].angle = 3.3;
    phen[0].tran_dir = 4.4;
    phen[0].warning = '0';

    return 0;
}

From the C# program when I print out the values of the struct after the call to the C++ function, the struct has not been updated (i.e. it is the same as before the C++ function was called). It seems that the C++ program is able to see the values and update them, but upon return the C# never sees the updates to the values in the struct. Thinking it was a value vs. reference problem, I even tried to do this with a class in the C# program, instead of the struct. Still no luck.

Question: What marshaling do I need to allow the called C++ function to be able to update the actual struct that was passed to it?

Thank you. -Greg Edwards Total Immersion Software

+2  A: 

It's not easy to understand why this doesn't work without seeing the definition of your C# function. I'm guessing it looks something like this though

[DllImport("somedll.dll")]
public static extern Int16 rsttwi_temp(Phenomenon[] array);

I don't have much experience working pinvoking a fixed size native array. But what you could do is change the C++ signature to take a pointer instead of a fixed size array and switch the managed code to pass by reference. For example

[DllImport("somedll.dll")]
public static extern Int16 rsttwi_temp(ref Phenomenon value);

__declspec(dllexport) short int rsttwi_temp (phenomenon* phen)
JaredPar
Jared,I have been able to use the ref, but since the C++ code would have to be modified heavily to use it, I was trying to avoid using it.Thank you for your suggestion. I may ultimately have to do it this way.-Greg
@Greg, another option is to create a second method which has the signature I specified and use it to call the first. That way you avoid changing the existing code but still get the benefit of working PInvoke
JaredPar
A: 

Greg, Were you able to solve this issue afterall? I am having a similar problem. My be you can help.

-Voices.

Voices
A: 

C# char != C++ char.

C# char == C++ wchar_t

C++ char == C# byte

Ben Voigt