none
Marshalling complex C# struct to C DLL. Error marshalling and unmarshalling. RRS feed

  • Question

  • SO here is my problem. I have a tree structure with several level with complex data type. But here is the simple snippet of code that I prototype and it is not working. The problem is my internal structure YStruct in X has datatype of UINT32 and all the other are UINT16 for simplicity and it seems marshalling and unmarshalling is having issue. One can see the result in console. 
    I have a structure in C# and it when passed to unmanaged code, some of the parameters get changed. And when I get the result back i do not get the required result. In fact the structure is messed up and I get garbage data.

    Here is the C dll code

    <!****************DLLCode.h***************************!>

    typedef unsigned short word;

    typedef unsigned __int64 qword;

    typedef unsigned long dword ;

    typedef unsigned char byte;

    typedef struct
    {

      word a;
      word b;
       word c;
        YStruct y;
       word d;
       word e;
       word f;

    } X;

    typedef struct
    {
      dword g;
    } YStruct;

    *****************************DLLCode.cpp*******************************************

    #include "DLLCode.h"

    #include "string.h";

    #include "stdio.h";

    #pragma pack(1)   

    extern "C" 

    {

      __declspec(dllexport) void UpdateCalRecord(void* rec)

    {

    X* calRec = (X*) rec;

          printf("*******************************Input to C Code******************\n");

            printf("U2: b = %d \n",calRec->b);

    printf("U2: c= %d \n", calRec->c);

      printf("*******************************Input to C Code Ended******************\n");

    calRec->e = 999;
    calRec->b = 444;
    calRec->f = 55555;
    calRec->a = 6;

    calRec->d = 0xD;
    calRec->c = 22222;
    calRec->y.g = 30;
    printf("*******************************C Code parameters set******************\n");
    printf("U2: a= %d\n", calRec->a); 
    printf("U2: b= %d \n",calRec->b);
    printf("U2: c= %d \n", calRec->c);
    printf("U4: g= %d \n",calRec->g);
    printf("U2: d: %d\n", calRec->d);
    printf("U2: e= %d \n",calRec->e);
    printf("U2: f= %d \n",calRec->f);
    printf("*****************************C code parameter end************************\n");
    }
    }


    Here is the C# code::
    ************************************ C# code****************************************

    using System;

    using System.Collections.Generic;

    using System.Linq;

    using System.Text;

    using System.Runtime.InteropServices;

    class Program
        {
            [DllImport("Win32DLL.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
            public extern static void UpdateCalRecord(ref X rec);

            static void Main(string[] args)
            {
                X record = new X();
                record.a = 2;

                record.b = 111;
             //   record.y.g = 20;
                record.c = 40;
                Console.WriteLine("**********************Before API call********************");

                Console.WriteLine("b: " + record.b);
                Console.WriteLine("c: " + record.c);
                UpdateCalRecord(ref record);
                Console.WriteLine("******************After API call*****************");
                Console.WriteLine("U2: a: " + record.a);
                Console.WriteLine("U2: b: " + record.b);
                Console.WriteLine("U2: c: " + record.c);
                Console.WriteLine("U4: g: " + record.y.g);
                Console.WriteLine("U2: d: " + record.d);
                Console.WriteLine("U2: e: " + record.e);
                Console.WriteLine("U2: f: " + record.f);

                Console.ReadLine();
            }

           [Serializable]
            [StructLayout(LayoutKind.Sequential, Pack=1)]

            public struct X
            {

                 [MarshalAs(UnmanagedType.U2)]
                 public UInt16 a;

                [MarshalAs(UnmanagedType.U2)]
                public UInt16 b;

                [MarshalAs(UnmanagedType.U2)]
                public UInt16 c;

               public YStruct y;

            [MarshalAs(UnmanagedType.U2)]
                public UInt16 d;
                [MarshalAs(UnmanagedType.U2)]
                public UInt16 e;

                [MarshalAs(UnmanagedType.U2)]

                public UInt16 f;

            }

         public struct YStruct

    {

                  [MarshalAs(UnmanagedType.U4)]
                    public UInt32 f;

    }

                                                                                  

    Nadeem


    • Edited by callnadeem Thursday, October 10, 2013 7:36 PM Typo in YStruct it is UINt32
    Thursday, October 10, 2013 4:33 PM

All replies

  • Since YStruct includes a dword, which is a 32-bit value, fix the marshaling definition. Use for example UnmanagedType.U4 and UInt32.

    Show the wrong results displayed on your console.


    • Edited by Viorel_MVP Thursday, October 10, 2013 7:23 PM
    Thursday, October 10, 2013 7:22 PM
  • We found this issue is resolved when we remove struct layout or change Pack to 0 or 4 in managed code. Dont know why though even in unmanged code we set the "pragma Pack(1)".

    However we now run into union problem.

    So our YStruct in C# looks like this now

    public struct YStruct 

    {

         public Z z;

         [MarshalAs(UnmanagedType.U4) 

         public UInt32 f;

         

    }

         [StructLayout(LayoutKind.Explicit)]
            public struct Z
            {
                [FieldOffset(0)]
                [MarshalAs(UnmanagedType.U2)]
                public UInt16 m;

                [FieldOffset(2)]
                [MarshalAs(UnmanagedType.U2)]
                public UInt16 n;
                [FieldOffset(2)]
                [MarshalAs(UnmanagedType.U8)]
                public UInt64 p;

                [FieldOffset(2)]
                [MarshalAs(UnmanagedType.U8)]
                public UInt64 q;
                [FieldOffset(2)]
                [MarshalAs(UnmanagedType.U8)]
                public UInt64 r;
                [FieldOffset(2)]
                [MarshalAs(UnmanagedType.U8)]
                public UInt64 s;

            }

    And in the C code it looks like 

     typedef struct

    {

         word m;

        union

       {

             word n;

             qword p;

             qword q;

              qword r;

               qword s;

        }

    }Z;

    So here what I did I set the value of the one of the union to a number say 100 before calling the unamanged code. Once the unmanged code is invoked i check the value union value it is changed to 0. Again I set that value to say 500 in unmanged code, but when the method from unmanaged code return I get 100 so the value did not changed for the union.

    I do not know what I am doing wrong here. 


    Nadeem

    Thursday, October 10, 2013 8:02 PM