3

I have the following C struct

struct XYZ
{
void            *a;
char            fn[MAX_FN];     
unsigned long   l;          
unsigned long   o;  
};

And I want to call the following function from C#:

extern "C"  int     func(int handle, int *numEntries, XYZ *xyzTbl);

Where xyzTbl is an array of XYZ of size numEntires which is allocated by the caller

I have defined the following C# struct:

[StructLayoutAttribute(Sequential, CharSet = CharSet.Ansi)]
public struct XYZ
{
   public System.IntPtr rva;
   [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 128)]
   public string fn;
   public uint l;
   public uint o;
}

and a method:

 [DllImport(@"xyzdll.dll", CallingConvention = CallingConvention.Cdecl)]
 public static extern Int32 func(Int32 handle, ref Int32 numntries,
     [MarshalAs(UnmanagedType.LPArray)] XYZ[] arr);

Then I try to call the function :

XYZ xyz = new XYZ[numEntries];
for (...) xyz[i] = new XYZ();
func(handle,numEntries,xyz);

Of course it does not work. Can someone shed light on what I am doing wrong ?

Simon Mourier
  • 132,049
  • 21
  • 248
  • 298
lifey
  • 87
  • 1
  • 9

4 Answers4

1
[StructLayoutAttribute(Sequential, CharSet = CharSet.Ansi)]
public struct XYZ
{
   public System.IntPtr rva;
   [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 128)]
   public string fn;
   public uint l;
   public uint o;
}

Shouldn't those uint be ulong ? Also, MAX_FN is 128 right ?

XYZ xyz = new XYZ[numEntries];
for (...) xyz[i] = new XYZ(); 

XYZ is a value type (struct), so the second line here is redundant (structs are always initialized)

 [DllImport(@"xyzdll.dll", CallingConvention = CallingConvention.Cdecl)]
 public static extern Int32 func(Int32 handle, ref Int32 numntries,
 [MarshalAs(UnmanagedType.LPArray)] XYZ[] arr);

[MarshalAs(UnmanagedType.LPArray)] is redundant, the compiler will see it's a struct array.

Ohad Schneider
  • 36,600
  • 15
  • 168
  • 198
0

I don't believe you can use LPArray when you have a managed structure. Just take that out, and use [In] and [Out] (if needed) instead.

IIRC, if you use LPArray, that would try to pass a pointer to the first element, which is illegal because the structure isn't blittable. You'd need to remove the [MarshalAs(...)] entirely.


Edit:

I don't remember, but you might need to initialize the string fields before passing them... I'll check that when I get the chance.

user541686
  • 205,094
  • 128
  • 528
  • 886
  • Hi, I tried your suggestion but it does not work. I think that Marshalling is required since C# and C++ represent arrays differently. Can I create C# array as contigious block of structs ? – lifey Jan 02 '11 at 13:13
  • @lifey: Hm... what is the error you get? Knowing that would really help. And yes, marshaling *does* need to happen, it's just that I don't think you specify it explicitly as `LPArray`. – user541686 Jan 02 '11 at 15:21
0

Check this one: Marshal C++ struct array into C# , probably it would help.

Community
  • 1
  • 1
Nickolay Olshevsky
  • 13,706
  • 1
  • 34
  • 48
  • Thanks Nickolay the post you are talking about is different. I need to allocate the struct array in C#.... – lifey Jan 02 '11 at 09:04
  • Yes, actually. I also found http://msdn.microsoft.com/en-us/library/z6cfh6e6.aspx#cpcondefaultmarshalingforarraysanchor1, MSDN tells that you should (probably) also pass SizeConst parameter. – Nickolay Olshevsky Jan 02 '11 at 09:14
0

I would marshal this manually. First, make xyzTbl an IntPtr.

[DllImport(@"xyzdll.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern Int32 func(Int32 handle, ref Int32 numntries, IntPtr xyzTb);

Rather than allocating the XYZ array like you are doing - allocate enough unmanaged memory to store the table.

IntPtr unmanaged = 
    Marshal.AllocHGlobal(Marshal.SizeOf(typeof(XYZ)) * numEntries);

Call your func(handle, ref numEntries, unmanaged); The job then is to unmarshal the unmanaged memory back into managed types.

IntPtr[] entries = new IntPtr[numEntries];
List<XYZ> xyz = new List<XYZ>();
Marshal.Copy(unmanaged, entries, 0, numEntries);
foreach (IntPtr entry in entries)
    xyz.Add(Marshal.PtrToStructure(entry, typeof(XYZ)));

Marsha.FreeHGlobal(unmanaged);
Mark H
  • 13,797
  • 4
  • 31
  • 45
  • 1
    This kind of manual marshalling is only necessary if the unmanaged code keeps a reference to the memory passed in beyond the point where the called function returns. – CodesInChaos Feb 13 '11 at 11:10
  • And your finalization code isn't very robust either. But most likely that only matters in few applications. Typically those that require clean unloads of AppDomains. – CodesInChaos Feb 13 '11 at 11:12