-1

I have the following C function declaration which I would like to call from C# (Note I cannot access the source code of the dll):

int setData(OuterStruct *data);

And the relevant structs for this C function are as follows:

typedef struct InnerStruct
{
    unsigned int a
    unsigned int b
    unsigned char *c
} InnerStruct;

typedef struct OuterStruct
{
    unsigned int d
    InnerStruct *e
} OuterStruct;

So far what I have done is:

Created my managed struct variants

[StructLayout(LayoutKind.Sequential)]
public struct OuterStruct
{
    public uint d;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
    public InnerStruct[] e
}

[StructLayout(LayoutKind.Sequential)]
public struct InnerStruct
{
    public uint a;
    public uint b;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
    public byte[] c
}

created a DllImport method

[DllImport("myDll.dll", EntryPoint ="setData", CallingConvention= CallingConvention.StdCall]
public static extern int setData(ref OuterStruct data);

Called my method and ended up with a constant "AccessViolationException". At the moment I'm not sure if it's an issue on my side or with the .dll and how it processes the input, but want to cover any loose ends I might have first.

Note I have created a variety of other P/Invoke methods for this .dll which use simple structs, only this one fails. Additionally both the .dll and the C# project is running as 32bit

5
  • InnerStruct * e is size of 32 (on 32-bit machine)... And on c# side you have 40 * size of InnerStruct... Same story with InnerStruct char * ... Commented Apr 11, 2024 at 17:54
  • @Selvin Are you able to explain a bit further what you mean? I'm not quite following where those numbers have come from (besides perhaps the memory size of the struct) Commented Apr 11, 2024 at 18:10
  • Not 32 but 4...if you have pointer on c side then if you pass value from c#, c side is trying to interpret this as pointer... Commented Apr 11, 2024 at 18:21
  • It's being passed by reference however, therefore a pointer is being passed? Note I have used the ref for a range of other functions, and it's worked for those. Commented Apr 11, 2024 at 19:15
  • Why have you got it as UnmanagedType.ByValArray? All of these are using pointers. Commented Apr 11, 2024 at 21:15

1 Answer 1

0

Your declarations are not quite right:

  • c is declared as a pointer, so you would have needed UnmanagedType.LPArray
  • e is declared as a pointer, so it should also use have used LPArray.
    • If it's actually a single object and not an array then InnerStruct needs to be a class and the declaration should be public InnerStruct e;
  • But you can't embed arrays in structs. You'd need to marshal them manually. Make sure to use Marshall.FreeHGlobal in a finally or using.
  • Exactly where the size of the array is coming from is not clear.
  • CallingConvention.StdCall is probably wrong: most C code uses CallingConvention.Cdecl.
[StructLayout(LayoutKind.Sequential)]
public struct OuterStruct
{
    public uint d;
    public IntPtr e;
}

[StructLayout(LayoutKind.Sequential)]
public struct InnerStruct
{
    public uint a;
    public uint b;
    public IntPtr c;
}

Marshalling manually looks like this

var o = new OuterStruct();
var array = new InnerStruct[5];
// set values etc
var arrayPtr = IntPtr.Zero;
var bytePtr = IntPtr.Zero;
try
{
    for (var i = 0; i < array.Length; i++)
    {
        array[i].c = Marshal.AllocHGlobal(SomeByteArray.Length);
        Marshal.Copy(SomeByteArray, 0, array[i].c, SomeByteArray.Length);
    }
    arrayPtr = Marshal.AllocHGlobal(array.Length * Marshal.SizeOf<InnerStruct>());
    Marshal.Copy(array, 0, arrayPtr, array.Length * Marshal.SizeOf<InnerStruct>());

    // make call
}
finally
{
    Marshal.FreeHGlobal(arrayPtr);
    foreach (var x in array)
        Marshal.FreeHGlobal(x.c);
}
Sign up to request clarification or add additional context in comments.

6 Comments

Given this a go. Using the new declaration as you've said as well as the [In] parameter for the C# setData parameter causes a TypeLoadException: 'e' can only be marshaled in restricted ways. I'm assuming this is because it's within a struct.
Is InnerStruct *e actually supposed to be an array? Or is it actually just one object? And what is the actual memory management supposed to look like, who allocates and frees these pointers?
This dll was used for an old LabView CVI app, can confirm innerstruct *e is for an array of variable length. With memory management, the old app as the caller managed the memory so I believe the C# code should manage the memory as it is acting like the old app would've.
Hmmm you can't marshal an array inside a struct unless you use SAFEARRAY. You'd need to marshal it yourself.
Implemented the manual marshalling, had to use an extension method from this post: stackoverflow.com/questions/25311361/… so that I could do Marshal.Copy on the inner struct as it only accepts a byte[] for the source parameter. Unfortunately still getting the accessViolationException.
|

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.