Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Code Project
CODE PROJECT For Those Who Code
  • Home
  • Articles
  • FAQ
Community
  1. Home
  2. General Programming
  3. C#
  4. Marshalling array of struct

Marshalling array of struct

Scheduled Pinned Locked Moved C#
csharpdata-structureshelptutorialannouncement
11 Posts 3 Posters 3 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • P poda

    I want to pass a struct which has a array of struct to a unmanaged code(a C DLL) and get the struct filled by the function in the DLL.Kindly help me to do this. In C DLL

    #define DLL_EXPORT extern "C" __declspec(dllexport)

    typedef struct device_Struct
    {
    int valid;
    int deviceId;
    int version;
    char flashData[100];
    } device_def;

    typedef struct module_Struct
    {
    device_def module[13];
    int testHdId;
    }module_def;

    DLL_EXPORT int _stdcall GetModules(int *TestHead,module_def *Module_Def)
    {
    *TestHead=2;

    (Module_Def+0)->module[0].valid=1;//Module_Def is array of module_def
    (Module_Def+0)->module[0].deviceId=5;
    (Module_Def+0)->module[0].version=2;
    strcpy((Module_Def+0)->module[0].flashData,"FlashData");
    Module_Def[0].testHdId=3;
    return 1;
    }

    In C# code

    [StructLayout(LayoutKind.Sequential,CharSet=CharSet.Ansi)]
    public struct device_Str
    {
    public int valid;
    public int deviceId;
    public int version;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)]
    public string flashData;
    };
    [StructLayout(LayoutKind.Sequential)]
    public struct module_Str
    {
    [MarshalAs(UnmanagedType.ByValArray,SizeConst=13)]
    public device_Str[] module;
    public int testHdId;
    };

    [DllImport("BCB_DLL.dll")]
    public static extern int GetModules(ref int TestHead, module_Str[] Module_Def);

    //Main call
    int i,j,NumOfTestHead=0,DeviceID;
    module_Str[] ModuleDef = new module_Str[4];

    for (i = 0; i < 4; i++)
    {
    ModuleDef[i] = new module_Str();
    ModuleDef[i].module = new device_Str[13];
    for (j = 0; j < 13; j++)
    {
    ModuleDef[i].module[j] = new device_Str();
    }
    }
    GetModules(ref NumOfTestHead, ModuleDef);
    DeviceID=ModuleDef[0].module[0].deviceId;//Do not get the correct value

    I know I have to do Marshalling. Help me how to do that.

    L Offline
    L Offline
    Luc Pattyn
    wrote on last edited by
    #2

    Hi, several comments: 1. I wrote an article on P/Invoke, unfortunately only part 1[^] is available so far, structs will be handled in part 2. If you own the native code, I strongly suggest you apply my logging advice, so you can observe both managed and native worlds in one listing. 2. your _def and _struct suffixes are a bit hard to get used to. I would drop them completely in the managed world. 3. If I were in charge of the code at both ends, I would go for something simpler, i.e. more function calls, less data. BTW: what happens if, suddenly, GetModules wants to return more than 4 modules? 4. there is a potential issue in

    char flashData[100];

    versus

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)]
    public string flashData;

    [MODIFIED] A managed string holds Unicode characters, taking 16-bit each. The simplest equivalent of a native char array (chars are 8-bit here) would be a byte array. [/MODIFIED] ByValTStr derives the native character type from the CharSet attribute. You'd want an explicit CharSet=CharSet.Ansi; I admit MSDN is pretty unclear on the matter. 5. except for the string issue, I would expect things to be correct without any further explicit marshaling, as you are basically passing a ref to one chunk of memory to be filled with ints and bytes. 6. I don't see why the very first few ints would not be all correct. 5. for debugging purposes, I suggest you initialize your struct members to all kinds of different silly values, so you can better see what gets filled in correctly and what doesn't. And I tend to use hex formatting while looking at such data, as that better reveals alignment problems. Hope this helps.

    Luc Pattyn [Forum Guidelines] [Why QA sucks] [My Articles] Nil Volentibus Arduum

    Please use <PRE> tags for code snippets, they preserve indentation, and improve readability.

    modified on Friday, September 24, 2010 1:07 AM

    P 1 Reply Last reply
    0
    • L Luc Pattyn

      Hi, several comments: 1. I wrote an article on P/Invoke, unfortunately only part 1[^] is available so far, structs will be handled in part 2. If you own the native code, I strongly suggest you apply my logging advice, so you can observe both managed and native worlds in one listing. 2. your _def and _struct suffixes are a bit hard to get used to. I would drop them completely in the managed world. 3. If I were in charge of the code at both ends, I would go for something simpler, i.e. more function calls, less data. BTW: what happens if, suddenly, GetModules wants to return more than 4 modules? 4. there is a potential issue in

      char flashData[100];

      versus

      [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)]
      public string flashData;

      [MODIFIED] A managed string holds Unicode characters, taking 16-bit each. The simplest equivalent of a native char array (chars are 8-bit here) would be a byte array. [/MODIFIED] ByValTStr derives the native character type from the CharSet attribute. You'd want an explicit CharSet=CharSet.Ansi; I admit MSDN is pretty unclear on the matter. 5. except for the string issue, I would expect things to be correct without any further explicit marshaling, as you are basically passing a ref to one chunk of memory to be filled with ints and bytes. 6. I don't see why the very first few ints would not be all correct. 5. for debugging purposes, I suggest you initialize your struct members to all kinds of different silly values, so you can better see what gets filled in correctly and what doesn't. And I tend to use hex formatting while looking at such data, as that better reveals alignment problems. Hope this helps.

      Luc Pattyn [Forum Guidelines] [Why QA sucks] [My Articles] Nil Volentibus Arduum

      Please use <PRE> tags for code snippets, they preserve indentation, and improve readability.

      modified on Friday, September 24, 2010 1:07 AM

      P Offline
      P Offline
      poda
      wrote on last edited by
      #3

      The size of structure sent is always 4.It is fixed. Only the ref param receives correct value from the function. But ModuleDef[0].module[0].deviceId do not get correct value. Does anyone know how to achieve this. Thanks Luc Pattyn for your reply.

      modified on Friday, September 24, 2010 3:30 AM

      L 1 Reply Last reply
      0
      • P poda

        The size of structure sent is always 4.It is fixed. Only the ref param receives correct value from the function. But ModuleDef[0].module[0].deviceId do not get correct value. Does anyone know how to achieve this. Thanks Luc Pattyn for your reply.

        modified on Friday, September 24, 2010 3:30 AM

        L Offline
        L Offline
        Luc Pattyn
        wrote on last edited by
        #4

        OK, have given this some more thought. The problem most likely is this: due to the string issue, your data really gets marshaled, i.e. copied (as it needs a conversion between 8-bit and 16-bit characters); so both sides are not working on the same chunk of memory after all. Now you did not specify in which direction the copy needs to take place, and there are some defaults, which don't suit your case. You probably need to add an [Out] ; have a look at the start of this article[^]. :)

        Luc Pattyn [Forum Guidelines] [Why QA sucks] [My Articles] Nil Volentibus Arduum

        Please use <PRE> tags for code snippets, they preserve indentation, and improve readability.

        P 1 Reply Last reply
        0
        • P poda

          I want to pass a struct which has a array of struct to a unmanaged code(a C DLL) and get the struct filled by the function in the DLL.Kindly help me to do this. In C DLL

          #define DLL_EXPORT extern "C" __declspec(dllexport)

          typedef struct device_Struct
          {
          int valid;
          int deviceId;
          int version;
          char flashData[100];
          } device_def;

          typedef struct module_Struct
          {
          device_def module[13];
          int testHdId;
          }module_def;

          DLL_EXPORT int _stdcall GetModules(int *TestHead,module_def *Module_Def)
          {
          *TestHead=2;

          (Module_Def+0)->module[0].valid=1;//Module_Def is array of module_def
          (Module_Def+0)->module[0].deviceId=5;
          (Module_Def+0)->module[0].version=2;
          strcpy((Module_Def+0)->module[0].flashData,"FlashData");
          Module_Def[0].testHdId=3;
          return 1;
          }

          In C# code

          [StructLayout(LayoutKind.Sequential,CharSet=CharSet.Ansi)]
          public struct device_Str
          {
          public int valid;
          public int deviceId;
          public int version;
          [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)]
          public string flashData;
          };
          [StructLayout(LayoutKind.Sequential)]
          public struct module_Str
          {
          [MarshalAs(UnmanagedType.ByValArray,SizeConst=13)]
          public device_Str[] module;
          public int testHdId;
          };

          [DllImport("BCB_DLL.dll")]
          public static extern int GetModules(ref int TestHead, module_Str[] Module_Def);

          //Main call
          int i,j,NumOfTestHead=0,DeviceID;
          module_Str[] ModuleDef = new module_Str[4];

          for (i = 0; i < 4; i++)
          {
          ModuleDef[i] = new module_Str();
          ModuleDef[i].module = new device_Str[13];
          for (j = 0; j < 13; j++)
          {
          ModuleDef[i].module[j] = new device_Str();
          }
          }
          GetModules(ref NumOfTestHead, ModuleDef);
          DeviceID=ModuleDef[0].module[0].deviceId;//Do not get the correct value

          I know I have to do Marshalling. Help me how to do that.

          D Offline
          D Offline
          David Knechtges
          wrote on last edited by
          #5

          I had to call a bunch of Windows APIs from C# that weren't defined in www.pinvoke.net - mostly the wifi APIs. This tool helped a lot - it helped me generate the correct C# structures for the structs that were in the native code. It also will let you generate equivalent C# structs for your own C structs, you just have to paste the C struct into it and it will generate the C# struct equivalent. http://blogs.microsoft.co.il/blogs/sasha/archive/2008/01/12/p-invoke-signature-generator.aspx[^] David

          P 1 Reply Last reply
          0
          • L Luc Pattyn

            OK, have given this some more thought. The problem most likely is this: due to the string issue, your data really gets marshaled, i.e. copied (as it needs a conversion between 8-bit and 16-bit characters); so both sides are not working on the same chunk of memory after all. Now you did not specify in which direction the copy needs to take place, and there are some defaults, which don't suit your case. You probably need to add an [Out] ; have a look at the start of this article[^]. :)

            Luc Pattyn [Forum Guidelines] [Why QA sucks] [My Articles] Nil Volentibus Arduum

            Please use <PRE> tags for code snippets, they preserve indentation, and improve readability.

            P Offline
            P Offline
            poda
            wrote on last edited by
            #6

            When I use out or ref keyword,I get System.ExecutionEngineException error. If not specified,it compiles and run smooth, but no change in the values of struct. I have modified the C# struct accordingly by the P/Invoke Interop Assistant result,but no gain. What to do?.

            L 1 Reply Last reply
            0
            • D David Knechtges

              I had to call a bunch of Windows APIs from C# that weren't defined in www.pinvoke.net - mostly the wifi APIs. This tool helped a lot - it helped me generate the correct C# structures for the structs that were in the native code. It also will let you generate equivalent C# structs for your own C structs, you just have to paste the C struct into it and it will generate the C# struct equivalent. http://blogs.microsoft.co.il/blogs/sasha/archive/2008/01/12/p-invoke-signature-generator.aspx[^] David

              P Offline
              P Offline
              poda
              wrote on last edited by
              #7

              It is a nice tool man. But unfortunately it does not work for my problem.

              1 Reply Last reply
              0
              • P poda

                When I use out or ref keyword,I get System.ExecutionEngineException error. If not specified,it compiles and run smooth, but no change in the values of struct. I have modified the C# struct accordingly by the P/Invoke Interop Assistant result,but no gain. What to do?.

                L Offline
                L Offline
                Luc Pattyn
                wrote on last edited by
                #8

                I suggest you start with something simpler. Don't do "array of structs holding array of struct holding string" right away. At the very least get rid of the string, try a fixed-length byte array. The simplest first experiment would be to simply pass just a byte array of sufficient size and see if all the data gets stored in there. Remember to log it all, preferably in hex. When the simple byte array is OK, make a struct with some actual types and the rest just a fixed-size byte array, and as long as it works let it evolve to the right thing. As soon as it fails, fix it before you add complexity. :)

                Luc Pattyn [Forum Guidelines] [Why QA sucks] [My Articles] Nil Volentibus Arduum

                Please use <PRE> tags for code snippets, they preserve indentation, and improve readability.

                P 1 Reply Last reply
                0
                • L Luc Pattyn

                  I suggest you start with something simpler. Don't do "array of structs holding array of struct holding string" right away. At the very least get rid of the string, try a fixed-length byte array. The simplest first experiment would be to simply pass just a byte array of sufficient size and see if all the data gets stored in there. Remember to log it all, preferably in hex. When the simple byte array is OK, make a struct with some actual types and the rest just a fixed-size byte array, and as long as it works let it evolve to the right thing. As soon as it fails, fix it before you add complexity. :)

                  Luc Pattyn [Forum Guidelines] [Why QA sucks] [My Articles] Nil Volentibus Arduum

                  Please use <PRE> tags for code snippets, they preserve indentation, and improve readability.

                  P Offline
                  P Offline
                  poda
                  wrote on last edited by
                  #9

                  I cannot change the struct in DLL,because it is used by many application. I tried using Marshal.PtrToStructure. But do not know how to increment the pointer. Now the value is correct for the first array element. Can you help in incrementing the pointer that points to the array of structures.

                  IntPtr pPointer;
                  module_Str AModule;
                  DeviceSize = Marshal.SizeOf(typeof(module_Str));
                  pPointer=Marshal.AllocHGlobal(DeviceSize*4);//since I pass array of 4 module_Str
                  GetModules(ref NumOfTestHead, pPointer, ref SW_Version);

                  AModule=(module_Str)Marshal.PtrToStructure(pPointer, typeof(module_Str));

                  DeviceId=AModule.module[0].deviceID//is correct

                  But how to increment the pointer to get each array element of structure, ie.AModule[0],AModule[1] and so on..

                  L 1 Reply Last reply
                  0
                  • P poda

                    I cannot change the struct in DLL,because it is used by many application. I tried using Marshal.PtrToStructure. But do not know how to increment the pointer. Now the value is correct for the first array element. Can you help in incrementing the pointer that points to the array of structures.

                    IntPtr pPointer;
                    module_Str AModule;
                    DeviceSize = Marshal.SizeOf(typeof(module_Str));
                    pPointer=Marshal.AllocHGlobal(DeviceSize*4);//since I pass array of 4 module_Str
                    GetModules(ref NumOfTestHead, pPointer, ref SW_Version);

                    AModule=(module_Str)Marshal.PtrToStructure(pPointer, typeof(module_Str));

                    DeviceId=AModule.module[0].deviceID//is correct

                    But how to increment the pointer to get each array element of structure, ie.AModule[0],AModule[1] and so on..

                    L Offline
                    L Offline
                    Luc Pattyn
                    wrote on last edited by
                    #10

                    I don't know. I never do such complex interfaces, they don't make sense to me. :|

                    Luc Pattyn [Forum Guidelines] [Why QA sucks] [My Articles] Nil Volentibus Arduum

                    Please use <PRE> tags for code snippets, they preserve indentation, and improve readability.

                    1 Reply Last reply
                    0
                    • P poda

                      I want to pass a struct which has a array of struct to a unmanaged code(a C DLL) and get the struct filled by the function in the DLL.Kindly help me to do this. In C DLL

                      #define DLL_EXPORT extern "C" __declspec(dllexport)

                      typedef struct device_Struct
                      {
                      int valid;
                      int deviceId;
                      int version;
                      char flashData[100];
                      } device_def;

                      typedef struct module_Struct
                      {
                      device_def module[13];
                      int testHdId;
                      }module_def;

                      DLL_EXPORT int _stdcall GetModules(int *TestHead,module_def *Module_Def)
                      {
                      *TestHead=2;

                      (Module_Def+0)->module[0].valid=1;//Module_Def is array of module_def
                      (Module_Def+0)->module[0].deviceId=5;
                      (Module_Def+0)->module[0].version=2;
                      strcpy((Module_Def+0)->module[0].flashData,"FlashData");
                      Module_Def[0].testHdId=3;
                      return 1;
                      }

                      In C# code

                      [StructLayout(LayoutKind.Sequential,CharSet=CharSet.Ansi)]
                      public struct device_Str
                      {
                      public int valid;
                      public int deviceId;
                      public int version;
                      [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)]
                      public string flashData;
                      };
                      [StructLayout(LayoutKind.Sequential)]
                      public struct module_Str
                      {
                      [MarshalAs(UnmanagedType.ByValArray,SizeConst=13)]
                      public device_Str[] module;
                      public int testHdId;
                      };

                      [DllImport("BCB_DLL.dll")]
                      public static extern int GetModules(ref int TestHead, module_Str[] Module_Def);

                      //Main call
                      int i,j,NumOfTestHead=0,DeviceID;
                      module_Str[] ModuleDef = new module_Str[4];

                      for (i = 0; i < 4; i++)
                      {
                      ModuleDef[i] = new module_Str();
                      ModuleDef[i].module = new device_Str[13];
                      for (j = 0; j < 13; j++)
                      {
                      ModuleDef[i].module[j] = new device_Str();
                      }
                      }
                      GetModules(ref NumOfTestHead, ModuleDef);
                      DeviceID=ModuleDef[0].module[0].deviceId;//Do not get the correct value

                      I know I have to do Marshalling. Help me how to do that.

                      P Offline
                      P Offline
                      poda
                      wrote on last edited by
                      #11

                      Finally I found a solution for my problem. Pointer increment can be done by ToInt32() function of IntPtr and adding the Offset. Here Offset is the size of the element in the array.

                      IntPtr pPointer;
                      module_Str AModule;
                      DeviceSize = Marshal.SizeOf(typeof(module_Str));
                      pPointer=Marshal.AllocHGlobal(DeviceSize*4); //since the passed struct array size is 4
                      GetModules(ref NumOfTestHead, pPointer, ref SW_Version); //call to DLL function

                      //Now the pPointer hold a long array of bytes which has the values of array of 4 module_Str
                      int Offset=0;
                      for (i = 0; i<4; i++)
                      {
                      IntPtr IncrPtr = new IntPtr(pPointer.ToInt32() + Offset);// base address + offset
                      AModule=(module_Str)Marshal.PtrToStructure(IncrPtr, typeof(module_Str));
                      Offset = Offset + Marshal.SizeOf(typeof(module_Str)); //increment the pointer by size of module_str
                      for (j = 0; j <13; j++)//the inner struct array size is 13
                      {
                      ListBox.Items.Add("DeviceID : " + AModule.module[j].deviceId);
                      ListBox.Items.Add("Version : " + AModule.module[j].version);
                      ListBox.Items.Add("Valid : " + AModule.module[j].valid);
                      ListBox.Items.Add("FlashData: " + AModule.module[j].flashData);
                      ListBox.Items.Add("TestHead : " + AModule.testHdId);
                      ListBox.Items.Add("");
                      }
                      }
                      //Finally free the allocated memory
                      Marshal.FreeHGlobal(pPointer);

                      1 Reply Last reply
                      0
                      Reply
                      • Reply as topic
                      Log in to reply
                      • Oldest to Newest
                      • Newest to Oldest
                      • Most Votes


                      • Login

                      • Don't have an account? Register

                      • Login or register to search.
                      • First post
                        Last post
                      0
                      • Categories
                      • Recent
                      • Tags
                      • Popular
                      • World
                      • Users
                      • Groups