[.net] PInvoke and fixed sized arrays

Started by
3 comments, last by mutex 17 years ago
Hi, here's the problem. I have a DllImport of a C function that takes a pointer to a structure. Also within the C structure is a fixed sized arrays of 4 floats. I'm wondering what the correct way to use DllImport with C# and these functions is. For the pointer to the struct is it OK to just use ref? I've been using C# references to substitute for pointers in Tao and it seems to work. The real problem comes with the array, I've googled and everyone seems to have a different idea on how to do it. Some use MarshalAs, others create a C# style array, which I don't think will work since the C function calls malloc, and some use fixed unsafe arrays. Anyone know the correct method?
Advertisement
I think you're not really supposed to just use ref, because if the GC invokes while your code inside the external library is executing the GC may decide to move that memory around. Of course, the pointer inside the unmanaged code won't be updated, so then the C routine will suddenly be reading/writing memory which is unallocated. However, I *think* that won't be a problem if you aren't doing any multithreaded stuff in your program, because IIRC the GC will only begin collection when managed code requests new memory through the new operator. (I'm not certain on this though.)

The correct way for the struct involves using Marshal.AllocHGlobal to allocate an unmanaged block of memory that's of size Marshal.SizeOf(mystruct), and then use Marshal.PtrToStructure (or is it StructureToPtr?) to copy the data from the managed struct into the unmanaged memory, and pass the pointer to the unmanaged memory to the C routine.

One approach with your array is to just do four consecutive floats:
float v1,v2,v3,v4;
I don't know if this is the best way though.
Exactly when the GC will run is not explicitly defined. With the current implementation, it will typically run when gen N gets full, at which point it will collect up to gen N. Allocations can happen concurrently with a collection, provided that the gen 0 block is not full (in which case the allocating thread will block until a gen0 collection has completed).

Ideally you shouldn't be using ref or out but fixed, and passing in that pointer. That is the purpose of the fixed keyword, you can also use fixed to allocate a fixed length array in a managed structure. You will be required to obtain a fixed pointer to that location in order to access it if you must read from that data in your managed application. You can also use the StructLayoutAttribute to arrange things so that you have enough extra space for a fixed length array (for instance by ensuring that you have 4 floats sequentially placed at the end of your structure).

If you are just passing around the object to various unmanaged APIs (but not explicitly accessing it in your managed application (i.e. a handle to an OpenGL context), then you can use a chunk of unmanaged memory (as the previous poster above mentioned). If you do need to access the structure, I would tend to favore using structlayout than marshalling back and forth, it's simply cleaner, although the performance will be aproximately the same...

In time the project grows, the ignorance of its devs it shows, with many a convoluted function, it plunges into deep compunction, the price of failure is high, Washu's mirth is nigh.

so if I have this C struct and function:

struct SomeStruct{     float SomeArray[4];     int* SomeIntPtr;};void SomeFunction( SomeStruct* StructPtr );


then the correct way to import it into C# would be:

[StructLayout(LayoutKind.Sequential)]public unsafe struct SomeStruct{     public fixed float SomeArray[4];     public int* SomeIntPtr;}//DllImport stuffpublic static extern void SomeFunction( [MarshalAs(UnmanagedType.LPStruct)]SomeStruct StructPtr ); 


?

I'm not sure about MarshalAs I got it from another wrppaer. The current library I'm trying to import already has a bunch of wrappers, but they're half-done or have a lot of problems. Plus each one does PInvoke differently which causes some confusion.
This page may have relevant info: Default Marshaling for Arrays. At the bottom is this sample:
Quote:
Unmanaged representation:

struct MyStruct {
short s1[128];
}

Arrays can be marshaled as UnmanagedType.ByValArray, which requires you to set the MarshalAsAttribute.SizeConst field. The size can be set only as a constant. The following code shows the corresponding managed definition of MyStruct.

C#
[StructLayout(LayoutKind.Sequential)]
public struct MyStruct {
[MarshalAs(UnmanagedType.ByValArray, SizeConst=128)] public short[] s1;
}

This topic is closed to new replies.

Advertisement