Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to marshal native struct array to managed array with out a for loop

I'm using a native DLL. I'm not sure, but I think I can't use PInvoke decl's with it since it does not export any functions and does not have a manifest. The DLL is delivered with a header file, explaining how to use it. The header file defines countless structs, enums and one class to be constructed using a factory method which is accessed via a Windows function ::GetProcAddress (security through obscurity). This class holds functions I would like to use in managed code.

I have successfully wrapped the class in a CLI ref class and can call trivial methods on it, wrapping those as well.

I'm going through the process of converting some structs from the header file to managed structs. For example, Native structs:

struct FooACL{
    int               action;                
    unsigned long     from,to;               
    char              comment[64]; 
    int               reserved[17];          
};

Turns into managed struct:

[StructLayout(LayoutKind::Sequential, CharSet = CharSet::Ansi)]
public value struct ManagedFooACL{
     int   action;                
     int   from,to;     
     [MarshalAs(UnmanagedType::ByValTStr, SizeConst = 64)]
     String^    comment;
     [MarshalAs(UnmanagedType::ByValArray, SizeConst = 17)]
     array<int>^ reserved;
};

As far as I can tell this should make the managed struct blittable? And any other struct that follows a similar pattern or levels of nested structure. As long as a layout is specified and none blittable are adorned with MarshalAs, will the structure as a whole be blittable?

And so, I'm attempting to see if there is a way to use Marshal::Copy or Marshal::PtrToStructure to convert an FooACL* array to array<ManagedFooACL>^.

I get the FooACL* array from a function call; I do not allocate it myself.

int total;
FooACL* foos = unamagedClass->GetFooACLS(&total);

total is an in/out that gets the size of the array returned.

What I managed to do so far, and what work is:

ManagedFooACL first = static_cast<ManagedFooACL>(Marshal::PtrToStructure(IntPtr(&foos [0]), ManagedFooACL::typeid));

What I can't wrap my mind around is why this does not:

array<ManagedFooACL>^ mfoos = gcnew array<ManagedFooACL>(total);
Marshal::PtrToStructure(IntPtr(&foos), mfoos);

This throws a:

System.ArgumentException was unhandled
  Message=The specified structure must be blittable or have layout information.
Parameter name: structure
  Source=mscorlib
  ParamName=structure

Is there a way to copy array data in one call or do I really need to do a for loop? It seems kind of silly with all this marshaling capability.

like image 594
Dmitry Avatar asked Dec 27 '12 23:12

Dmitry


1 Answers

Having done some more research it looks like the answer is no. It is not possible to auto marshal an array of structs without looping.

I think the main reason struct marshaling works with PtrToStructure is because the structure is static/predefined. The compiler knows how to lay out the memory. Since, you get a dynamic size array there's no way to specify the memory lay out ahead of time. So you must loop dynamic number of structs.

Alternatively, if you knew that you will always be getting an array of X length, you could define your own managed struct holding one element, namely the array of ManagedFooACL with a ByValArray and SizeConst value of X, and just cast the native array to the struct.

like image 50
Dmitry Avatar answered Oct 03 '22 06:10

Dmitry