10

With great help of the stackoverflow community, I've managed to call a native DLL function. However, I can't modify the values of ID or intersects array. No matter what I do with it on the DLL side, the old value remains. It seems read-only.

Here are some code fragments:

C++ struct:

typedef struct _Face { int ID; int intersects[625]; } Face; 

C# mapping:

[StructLayout(LayoutKind.Sequential)] public struct Face { public int ID; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 625)] public int[] intersects; } 

C++ method (type set to DLL in VS2010):

extern "C" int __declspec(dllexport) __stdcall solve(Face *faces, int n){ for(int i =0; i<n; i++){ for(int r=0; r<625; r++){ faces[i].intersects[r] = 333; faces[i].ID = 666; } } 

C# method signature:

[DllImport("lib.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] public static extern int solve(Face[] faces, int len); 

C# method invocation:

Face[] faces = new Face[10]; faces[0].intersects = new int[625]; faces[0].ID = -1; //.. and add 9 more .. solve(faces, faces.Length); // faces[0].ID still equals -1 and not 666 

Kindest regards, e.

3
  • 1
    I had a similar question about a year ago. I ended up marshalling a pointer to an array. Take a look as it might help you. stackoverflow.com/questions/4464532/… Commented Dec 2, 2011 at 20:32
  • I could swear this was asked just a day or 2 ago (not by @Queequeg though). Commented Dec 2, 2011 at 20:41
  • By the way, in your previous question I told you that you should not use SetLastError on your functions. That is for Windows API functions only. That same advice still applies. The CharSet and CallingConvention settings are option in C# since you are using the C# defaults. I'd remove them too since they just add clutter. Commented Dec 2, 2011 at 20:59

2 Answers 2

8

You have to tell the pinvoke marshaller explicitly that the array needs to be marshaled back. You do this with the [In] and [Out] attributes. Like this:

 [DllImport("...")] public static extern int solve([In, Out] Face[] faces, int len); 
Sign up to request clarification or add additional context in comments.

3 Comments

this does not change the behavior.. :(
I tested this before I posted, using the exact declarations and a DLL written in C++ compiled with VS2008. I'm quite sure it works.
It works. I also have run this code. I was 99% of the way there with my answer, but Hans beat me to it (again!) Please try again.
0

This is an output field only? To get to the bottom of this, I'd try substituting your Face[] parameter with a large-enough a byte[] and see if the byte array gets filled with anything (you'll have to change your [DllExport] a bit).

Also, one other thing I used to experience when doing this with char*'s is that I had to pre-allocate the buffer in C#. For example:

StringBuilder theString=new StringBuilder(); MyUnmanagedFunction(theString); 

would not work. But assuming that returned theString was a max 256 characters, I would do this:

StringBuilder theString=new StringBuilder(256); MyUnmanagedFunction(theString); 

And I'd be in business. I'd recommend trying the byte[] substitution, if that doesn't work, try the pre-allocated byte array. Once you are seeing the byte array actually get changed by your C++ code, then you can figure out how to marshal that thing into your C# struct.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.