2

I've got an unmanaged DLL that I am trying to use that makes use of some nested structs with reserved members at the end of them. I'm having an issue converting them to C#.

I've tried to strip down the code to what is essential. The ParentStruct has members for four instances of three different child structs. All of those three different child structs have void* arrays to reserve memory for future use.

I've tried just doing the Reserved values as IntPtrs, I've tried turning on unsafe code and using the fixed keyword (and changing them to ints). I've tried actually laying out the appropriate number of consecutive IntPtrs at the end of the struct. Nothing seems to work.

In some of those cases, I'll get an error message (an uninformative one that says that there was an error at a certain address on a certain thread). Most of the time, though, the application just dies without an error message. I'm sure that's because I'm messing with unmanaged memory here.

Just FYI, the actual Pinvoke has a signature something like

[DllImport("MyDll.dll", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
internal static extern void UseStructs([In,Out] ref ParentStruct parent);

So what's the right way to declare these structs in C# so that I can use them?

typedef struct
{
    ChildStruct1           childOne;
    ChildStruct2           childTwo;
    ChildStruct2           childTwoB;
    ChildStruct3           childThree;

#if defined K_64_BIT
    /// Reserved for future implementation
    void*          Reserved[15];
#elif defined K_32_BIT
    /// Reserved for future implementation
    void*          Reserved[14];
#endif

} ParentStruct;

typedef struct
{

    bool  Valid;
    float x;
    float y;

    /// Reserved for future implementation.
    void*               Reserved[16];
} ChildStruct3;

typedef struct
{

    bool          Found;
    XYPairFloat xyPair;


    /// Reserved for future implementation.
    void*         Reserved[16];
} ChildStruct2;


typedef struct
{
    unsigned char* imageData;
    RectInt      rectInt;

#if defined QUICK_LINK_64_BIT
    /// Reserved for future implementation
    void*          Reserved[14];
#elif defined QUICK_LINK_32_BIT
    /// Reserved for future implementation
    void*          Reserved[12];
#endif

} ChildStruct1;

typedef struct
{

    int x;
    int y;
    int width;
    int height;
} RectInt;

typedef struct
{
    float x;
    float y;
} XYPairFloat;

Edit: I think I've gotten closer, in that I'm now getting an error message that is at least human readable. But it still doesn't work. I used the fixed keyword on the Reserved members and set the SizeConst part of the attribute on the member appropriately. Everything else I did was just basic pinvoke work. However, I'm getting the error:

The type definition of this field has layout information but has an invalid managed/unmanaged type combination or is unmarshalable.

in reference to the ChildStruct1 childOne member, which is the first one so I'd venture a guess it applies to the others as well. Thoughts?

Edit 2: Per Mark Heath's request, here's the actual definition I'm using for ChildStruct1:

[StructLayout(LayoutKind.Sequential)]
internal unsafe struct ChildStruct1
{
    [MarshalAs(UnmanagedType.LPStr)]
    public string imageData;
    public RectInt rectInt;
    [MarshalAs(UnmanagedType.LPArray, SizeConst=12)]
    public fixed int Reserved[12];
}

Edit 3: I did a little more digging. That error that I'm getting disappears if I comment out the Reserved member. Obviously it doesn't work. But that particular error is caused by how I defined that member, I guess.

Tim
  • 14,999
  • 1
  • 45
  • 68
  • You also have to be aware of alignment issues. See http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.structlayoutattribute.aspx – Jim Mischel Nov 27 '12 at 17:54
  • Jim - I'm using `[StructLayout(LayoutKind.Sequential)]` on all of my structs in C#. I assumed that was the correct thing to do, but perhaps not? – Tim Nov 27 '12 at 17:58
  • can you post the interop structure you made for ChildStruct1? It is likely to be the way you declared the arrays. – Mark Heath Nov 27 '12 at 18:28
  • `LayoutKind.Sequential` is probably right, but you also have to think about packing. See the `Pack` field: http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.structlayoutattribute.pack.aspx. What you use will depend on how your C++ compiler is packing things. – Jim Mischel Nov 27 '12 at 18:30
  • Mark - I've added the struct definition for ChildStruct1. – Tim Nov 27 '12 at 18:36
  • Jim - since I don't control the code for the unmanaged DLL, how can I figure out what the correct pack value should be? – Tim Nov 27 '12 at 18:36
  • `UnmanagedType.LPArray` passes a pointer to the first element of the array. I think you want `UnmanagedType.ByValArray`, which will pass the entire array as part of the structure. See http://msdn.microsoft.com/en-us/library/System.Runtime.InteropServices.UnmanagedType.aspx – Jim Mischel Nov 27 '12 at 18:51
  • To determine packing, your best bet is to compile some C++ code that uses the structures, and turn on the options that output a listing file. You can then look at the structure layout. Or, you can use the C++ `sizeof` and `offsetof` constructs to give you the size of the struct and the offsets of the members. – Jim Mischel Nov 27 '12 at 19:01
  • Jim - I see what you mean with LPArray versus ByValArray. I think you're right that it should be ByValArray. However, switching it to that still gives me the error I mentioned in my edit. – Tim Nov 27 '12 at 19:40

1 Answers1

2

You can try changing the last value to an IntPtr array according to Essential P/Invoke

So change:

[MarshalAs(UnmanagedType.LPArray, SizeConst=12)]
public fixed int Reserved[12];

to:

[MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)]
public IntPtr[] Reserved;
SwDevMan81
  • 48,814
  • 22
  • 151
  • 184
  • Doing it that way actually just causes my executable to die without even showing me an error message. – Tim Nov 27 '12 at 19:42
  • You could run the program through [WinDbg](http://www.microsoft.com/whdc/devtools/debugging/default.mspx) to catch the exception and see where its failing. – SwDevMan81 Nov 27 '12 at 19:50
  • This ended up being the right thing to use. The problem I was seeing was actually only happening under the debugger (I hadn't even gotten to running it without that yet). Turned out to be a problem with using the Visual Studio hosting process. I was tipped off by this question here on SO: http://stackoverflow.com/questions/4532457/program-and-debugger-quit-without-indication-of-problem – Tim Nov 27 '12 at 20:05