2

I need to include a .dll which contains the following C/C++ function prototype and struct definition:

typedef struct { unsigned short us1; unsigned short us2; unsigned short data[16]; } myStruct; int myFunc(myStruct *MYSTRUCT); 

To use this Function i created a new Class:

static class DLL { public struct MyStruct { public ushort us1; public ushort us2; public ushort[] data; public PDPORT(ushort[] temp1, ushort temp2, ushort temp3) { us1 = temp2; us2 = temp3; data = temp1; } }; [DllImport("PDL65DLL.dll")] public static extern int mvb_PutPort(ref MyStruct CommData); } 

In my main class I initiliaze the struct variable and call the Function like that:

 DLL.MyStruct communicationData = new DLL.MyStruct(new ushort[16], new ushort(), new ushort()); DLL.MyFunc( ref communicationData); 

However, this does not seem to work. The Function is passing something but not the right values, I suggest it is something with the pointer usage. Maybe a struct* is not the same as ref struct... Can someone explain what the problem is?

1
  • slight observation: the export is myFunc, and the extern is mvb_PutPort with nothing to tell it the new name; is that just a copy/paste thing? because: that also won't make things happy Commented Aug 26, 2020 at 8:11

2 Answers 2

2

You might need to specify the packing of the struct (depending on the default packing used by the C++ code). You can also specify a fixed-sized array for the marshalling to take care of using [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)].

You also don't need the C# implementation to use a struct and indeed for the fairly large size of the data I would recommend using a class.

Here's an example:

[StructLayout(LayoutKind.Sequential, Pack=4)] public sealed class MyStruct { public ushort us1; public ushort us2; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] public ushort[] data; public MyStruct(ushort[] temp1, ushort temp2, ushort temp3) { us1 = temp2; us2 = temp3; data = new ushort[16]; Array.Copy(temp1, data, Math.Min(temp1.Length, data.Length)); } } 

Note how the constructor ensures that the size of the array is correct. You must always ensure that the size of the array matches the SizeConst declaration.

With these changes, the Marshaller should take care of things for you.

Sign up to request clarification or add additional context in comments.

2 Comments

The program crashes if i use a class object pointer as a parameter (ref Object).
Nevermind I realized that class objects are already references and needs to be passed by value. Thank you very much!
1

The C/C++ struct is more like a "fixed buffer":

 unsafe struct MyStruct { ushort us1, us2; fixed ushort data[16]; } 

This is not the same as an array, and is a little more awkward to work with in the C# code, but: it is possible.

I'm not 100% sure, but I believe you should also use an unmanaged pointer in the P/Invoke layer:

public static extern int mvb_PutPort(MyStruct* CommData); 

which means you need to call it via:

fixed (MyStruct* ptr = whateverYouWereGoingToPass) { mvb_PutPort(ptr); } 

5 Comments

I would avoid using unsafe and instead use [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] for the fixed-size array.
@MatthewWatson then post that as an answer :) personally, I'd use a fixed buffer
I wouldn't use fixed because then you have to enable unsafe for the project - and then it will spread to all the other projects that use that class.
@MatthewWatson yup, I'm familiar with the implications (all good reasons why I'd keep those types localized to whatever uses them directly), and again: if you have a better approach - that'd make a great second answer
Lets say I have a function where I initialise "MyStruct test_struct" and set test_struct.data[0] to 10. How do I pass it as a pointer to the fixed-block?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.