none
Caling dll functions with complex struct as parameters RRS feed

  • Question

  • I am trying to call dll from the managed code.eveyrting works OK until i'm trying to use fucntions that use complex (containing arrays of structures) structures as a parameters.

     

    Let's say

    [DllImport("some.dll")]

    private static extern int myFunc(ref STRUCT2 someStrust);

    ...

     

    [StructLayout(LayoutKind.Sequential)]

    public struct STRUCT1

    {

    public int x;

    public UInt y;

     

    }

    [StructLayout(LayoutKind.Sequential)]

    public struct STRUCT2

    {

    public int xx;

    [MarshalAs (UnmanagedType.Struct, SizeConst = 4)]

    public STRUCT1[] myInsideStruct;

    }

     

    .....

    STRUCT2 myOutStruct;

    myOutStruct.xx = 1;

    myOutStruct.myInsideStruct = new STRUCT1[4];

    //assign some values to myStruct.myInsideStruct[0], [1], etc.

     

    //following line causes exception during the exectuion!!

     

    myFunct(myOutStruct);

     

    /* exception text: Cannot marshal field 'myInsideStruct' of type 'STRUCT2': Invalid managed/unmanaged type combination  (Arrays fields must be paired with ByValArray or SafeArray). */

     

    Any help?

    And - does anyone know good source for Marshling and Interoperability explanation? Everything MSDN provides focuses on simplest cases wich do not help much in situations like this.

     

    Thank you

    Monday, March 31, 2008 7:55 PM

Answers

  • Hi,

     

    I'm not sure if this will help... But attribute

    [MarshalAs (UnmanagedType.Struct, SizeConst = 4)]

    for you myInsideStruct field seems to be not in place. Have you tried to remove it?

    Here you are trying to marshal array of structs as single struct.

     

    Tuesday, April 1, 2008 7:13 AM
  • OK

    I've tried

     

    [MarshalAs(UnmanagedType.ByValArray, ArraySubType=UnmanagedType.LPStruct, SizeConst=4)]

    Struct1[] str;

     

    Are there any fallacies with this implementation?

     

    Original C function implementation:

    MyFunc(Struct2 *strct);

     

    and

     

    typedef struct{

    int x;

    Struct1[SOME_CONST];

    } Struct2;

     

    and

    #define SOME_CONST = 4;

    Wednesday, April 2, 2008 5:29 PM

All replies

  • Hi,

     

    I'm not sure if this will help... But attribute

    [MarshalAs (UnmanagedType.Struct, SizeConst = 4)]

    for you myInsideStruct field seems to be not in place. Have you tried to remove it?

    Here you are trying to marshal array of structs as single struct.

     

    Tuesday, April 1, 2008 7:13 AM
  • Vitaliy is right. UnmanagedType.Struct means a VARIANT, which is used to marshal managed formatted classes and value types to a COM server, which does not apply to your case.

    You should use UnmanagedType.ByValArray on an array that appear as fields in a structure.
    Wednesday, April 2, 2008 6:27 AM
  • Hmm,

    Looks like this does not work if this array is a structure of its own. At least I fail to find a way to do so. If I use ByValArray I will pass compilation, but during an execution I will receive an error.

     

    BTW - descripiton of UnmanagedType.Struct is so short, that it is impossible to derive any useful info. Can you reference me to the place attributes have more detailed explanation?

     

    Thank you

    Oleg

     

    Wednesday, April 2, 2008 12:52 PM
  • OK

    I've tried

     

    [MarshalAs(UnmanagedType.ByValArray, ArraySubType=UnmanagedType.LPStruct, SizeConst=4)]

    Struct1[] str;

     

    Are there any fallacies with this implementation?

     

    Original C function implementation:

    MyFunc(Struct2 *strct);

     

    and

     

    typedef struct{

    int x;

    Struct1[SOME_CONST];

    } Struct2;

     

    and

    #define SOME_CONST = 4;

    Wednesday, April 2, 2008 5:29 PM
  • Have you solved this issue?

    If not, could you please clarify what exactly you mean by "but during an execution I will receive an error."?

    To troubleshoot this issue, we really need the source code and the detailed repro steps to reproduce the problem, so that we can investigate the issue in house. It is not necessary that you send out the complete source of your project. We just need a simplest sample to reproduce the problem. You can remove any confidential information or business logic from it.


    You can send a sample project and repro steps in details to my email address which can be found in my personal profile page.


    Friday, April 4, 2008 2:00 AM
  •  

    Hi Feng,

     

    sorry for incomplete desctipiton.

    I did succeed to resolve the problem - most of my incapsulated structures a passing values to the unmanaged code (llooks like that SubType should be Struct ratehr then LPStruct, though).

    But I encounter anotehr problem - size limitation.

     

    Using me example,

     

    typedef struct {

    int x;

    int y;

    }Struct1;

     

    typedef struct {

    int xx;

     

    Struct1 myStruct[4096] ;

    }Struct2;

     

    which is translated into (thanks to MS guys who made this tool)

    [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]

    public struct Struct1 {

    /// int

    public int x;

    /// int

    public int y;

    }

    [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]

    public struct Struct2 {

    /// int

    public int x;

    /// int

    public int y;

    /// Struct1[4096]

    [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst=4096, ArraySubType=System.Runtime.InteropServices.UnmanagedType.Struct)]

    public Struct1[] myStruct;

    }

     

    [DllImport("my.dll")]

    private static extern void MyFunc(int X, ref Struct2 myStruct2);

    So, if I call it

    {

    Struct2 myStr2 = new Struct2 ();

    myStr2 .myStr1= new Struct1[4096];

     

    MyFunc(xx, ref myStr2);

    }

     

    I'm receiving an error during the excution:

    "Cannot marshal 'parameter #2': Internal limitation: structure is too complex or too large.

    Looks like the only solution is to define Struct2 as a class, but I'm not sure what perfomrance/memory management implications I should expect.

    Friday, April 4, 2008 5:42 PM
  • Could you please also provide the unmanaged signature of the function in DLL side as well as the unmanaged DLL to let me reproduce this issue?
    Monday, April 7, 2008 5:51 AM
  • OK

    void MyFunc(int x, Struct2* myStruct2)

    Friday, April 18, 2008 8:46 PM
  • The P/Invoke marshaler has a size limit on marshaled structures of a little less than 65 kilobytes.  Your structure is not quite as large at 33 KB, perhaps we're not looking at the real structure declaration.  This size limit is not present for marshaled class objects, you could simply declare Struct2 as a class and solve your problem.

    If that's not practical, you'll have to marshal the structure yourself.  Declare the function as:

        [DllImport("my.dll")]
        private static extern void MyFunc(int X, IntPtr myStruct2);

    Technically, you should now use Marshal.AllocHGlobal() to allocate the memory, fill it up with Marshal.WriteInt32() and Marshal.StructureToPtr().  That gets boring and error prone in a hurry.  You can break the rules by passing a pointer to the managed structure.  That's not really a great idea but it is likely to work:

          Struct2 sample = new Struct2();
    ...
          GCHandle hdl = GCHandle.Alloc(sample, GCHandleType.Pinned);
          IntPtr addr = hdl.AddrOfPinnedObject();
          MyFunc(0, addr);
          hdl.Free();
    • Proposed as answer by SnakeBiscuit Tuesday, March 6, 2012 1:45 PM
    • Unproposed as answer by SnakeBiscuit Tuesday, March 6, 2012 1:45 PM
    Saturday, April 19, 2008 6:06 PM
    Moderator
  •  

    Thank you for good the reply (most of your replys are quite good - I saw plenty in htis forum). You right - it wasnt a real code but simplified example. I did try class and  encountered some weird problem. In case this class contatins Struct[] StrX as one of its parameters(? or whatever it hsould be called), then this structure will not be marshalled properly.

     

    So, coming back to my example, if I call some functin with ClassWhichReplacesTheStructDueToSizeLimit Class,

    and its structure like:

    {

    Int32 xx;

    Int32 yy;

    Struct StrX[4096];

    }

     

    marshalling of the StrX will be incorrect. Let me now if more detailed description.

    Monday, April 21, 2008 12:09 AM
  • 1. One moment - why this is not a good idea?

     

     

    2. Unless there is a custom alignment  on unmanaged code sire, but then I will need to properly define my structure on managed side.. Wait, not sure wheter i cna even do this... Any way to do cutstom aligned signatures?

     

    Monday, April 21, 2008 12:51 AM
  • We are rather powerless to guess what "weird problem" or "will be incorrect" really means, especially after reposting the exact same structure we already know should marshal correctly.  You'll have to start with the "more detailed description".
    Monday, April 21, 2008 1:08 AM
    Moderator
  • Is this value (64 K) is tunable (like stack size)?

    In my project I expect structures of the size ~100-150K, so if this is not critical for the sysem ,i would like to be able to pass this limitation.

     

    Thank you

     

    Monday, April 21, 2008 2:56 PM
  • OK. I just hoped this is some known problem.

    I will use example which is much closer to the real one.

     

    // unmanaged code declarations

    int MyFunc(int x, Struct2 **str2, Struct 1 *Struct1);

     

    typedef struct

    {

    int Yx;

    int Yy;

    bool Yb[SOME_CONST];

    } Struct Y

    typedef struct{

    int num;

    StructY *strY;

    }

    Struct2;

     

    typedef struct

    {

    int x;

    int y;

    int z;

    int whatever;

    }StructZ

    typedef struct

    {

    int num;

    StructZ strZ[4096];

    }Struct1;

     

    //managed code

    [DLLImport "my.dll"]

    private static extern int MyFunc(Int32 x, out IntPtr str2, Struct 1 Struct1);

     

    //strucutres

     

    public struct Struct Y

    {

    public Int32 cY;

    public Int32 aY;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst=4)]

    public int[] zYb;

    }

    public class Struct2

    {

    public Int32 num;

    [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.LPStruct, SizeConst = MAX)]

    StructY[] strY;

    }

     

     

    public struct StructZ

    {

    public Int32  x;

    public Int32  y;

    public Int32  z;

    public Int32 whatever;

    }

    public class Struct1

    {

    public Int32 num;

    [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.Struct, SizeConst = 4096)]

    StructZ[] strZ;

    }

     

     

    So if i call

     

    int i, x;

    IntPtr pToStruct2;

     

    Struct2 str2 = new Struct2();

    for(i=0; i<MAX;i++)

    {

    str2.strY = new StructY();

    }

     

    Struct1 str1 = new Struct1();

     

     

    MyFunc(x, out pToStruct2, str1)

    Marshal.PtrToStructure(pToStructure2, str2);

     

    //then str2.strY conten filled with the right values in a wrong oder! It looks like the values are read in alphabetical order!!

    Monday, April 21, 2008 4:05 PM