Tom Torell
Tom Torell

Reputation: 51

Marshalling an array of strucs to call an unmanaged function from C#

I have to call an unmanaged function from C# and must provide an array of coordinates (doubles) to it. How does the marshalling work for this case correctly?

On the unmanaged side:

typedef struct dPoint3dTag
{
  double x, y, z;
} dPoint3d;

void UnmanagedModifyGeometry(char *strFeaId, dPoint3d *pnts, int iNumPnts);

I defined a managed Structure for DPoint3d on the managed side:

[StructLayout(LayoutKind.Sequential)]
public struct DPoint3d
{
// Constructor
public DPoint3d(double x, double y, double z)
{
this.x = x;
this.y = y;
this.z = z;
}

public double x, y, z;
}

I'm trying to call the unmanaged function from C# in this way:

// Import of the unmanaged function
[DllImport("Unmanaged.dll")]
public static extern void UnmanagedModifyGeometry([MarshalAs(UnmanagedType.LPStr)] string strFeaId, DPoint3d[] pnts, int iNumPnts);

// Using the unmanaged function from C#
// Allocating points
DPoint3d[] pnts = new DPoint3d[iPntCnt];
String strFeaId = "4711";

// After filling in the points call the unmanaged function
UnmanagedModifyGeometry(strFeaId, pnts, iPntCnt);

Is this workflow correct?

Regards tomtorell

Upvotes: 2

Views: 94

Answers (1)

David Heffernan
David Heffernan

Reputation: 613562

First of all on the unmanaged side, char* is a modifiable string. You should use const here to indicate that the data flows from caller to callee. And it makes sense to do the same for the other parameters:

void UnmanagedModifyGeometry(
    const char *strFeaId, 
    const dPoint3d *pnts, 
    const int iNumPnts
);

Now it is clear to all how the data flows.

On the managed side, there is one obvious problem with the declaration which is that you don't specify the calling convention. The default is stdcall, but your unmanaged code will be cdecl, assuming that the declaration in the question is accurate.

The struct declarations that you show match perfectly. There is nothing more to say on that subject.

You can also make use of the default marshalling to simplify the p/invoke. I'd write it like this:

[DllImport("Unmanaged.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void UnmanagedModifyGeometry(
    string strFeaId, 
    [In] DPoint3d[] pnts, 
    int iNumPnts
);

And call it like this:

DPoint3d[] pnts = new DPoint3d[...]; // supply appropriate value for array length
// populate pnts
UnmanagedModifyGeometry("4711", pnts, pnts.Length);

Upvotes: 1

Related Questions