C++ Union nach C# migrieren.



  • Liebe Forumianer,

    nach langem Stöbern bin ich zu keinem vernünftigen Ergebnis gekommen. Hier meine Frage:
    ... wie bilde ich folgendes Union-Konstrukt in C# nach???

    typedef union
    {
        struct
        {
            unsigned char  reserve;
            unsigned char  ain[12];
            unsigned char  aout[8];
            unsigned char  cntr;
            unsigned char  fef_zyklus;
            unsigned char  fef_projekt;
        }
        rd;
        unsigned char  wr[12 + 8 + 4];
    }
    PRM_t;
    

    mein Ansatz ist

    public struct Union_PRM_t
    {
        public struct rd
        {
            public byte  reserve;
            public static byte[]  ain = new byte[12];
            public static byte[]  aout = new byte[8];
            public byte  cntr;
            public byte  fef_zyklus;
            public byte  fef_projekt;
        }
    
        public static byte[]  wr = new byte[12 + 8 + 4];
    }
    
    //Deklaration
    Union_PRM_t data;
    

    Die Funktion in einer DLL sieht so aus:

    DPR_DWORD DP_read_slv_par (DPR_DWORD user_handle,// in
                                 DPR_WORD slv_add, // in
                                 DPR_WORD type, // in
                                 DPR_WORD *data_len; // out
                                 DPR_BYTE *data; // out
                                 DP_ERROR_T *error ); // out
    

    Wie übergebe ich an *data die Adresse von data.wr???

    Vielen-vielen Dank für Eure unterstützung!



  • Kannst du ein bisschen näher beschreiben was du vor hast?



  • ganz allgemein: ich muss über Profibus-Kommunikationskarte eine SPS-Baugruppe ansprechen.

    Ich habe:
    - eine DLL (c++)
    - ein paar Header, die die Strukturen der Parameter und die Deklarationen der Funktionen für diese DLL beschreiben (c++)

    In einem Header wird eine Funktion so beschrieben:

    DPR_DWORD DP_read_slv_par (DPR_DWORD user_handle,// in
                                 DPR_WORD slv_add, // in
                                 DPR_WORD type, // in
                                 DPR_WORD *data_len, // out
                                 DPR_BYTE *data, // out
                                 DP_ERROR_T *error ); // out
    

    Diese habe ich folgendermaßen importiert:

    [DllImport("dp_base.dll", CharSet = CharSet.Ansi)]
            public static extern DPR_DWORD DP_read_slv_par  (DPR_DWORD user_handle,// in 
                                                            DPR_WORD slv_add, // in 
                                                            DPR_WORD type,  // in 
                                                            out DPR_WORD data_len, // out 
                                                            out DPR_BYTE data, // out 
                                                            out DP_ERROR_T error ); // out
    

    Für DPR_BYTE *data habe ich folgende Struktur als Vorgabe:

    typedef union
    {
        struct
        {
            unsigned char  reserve;
            unsigned char  ain[12];
            unsigned char  aout[8];
            unsigned char  cntr;
            unsigned char  fef_zyklus;
            unsigned char  fef_projekt;
        }
        rd;
        unsigned char  wr[12 + 8 + 4];
    }
    PRM_t;
    

    Diese habe ich folgendermaßen versucht mit C# nachzubilden:

    public struct Union_PRM_t
    {
        public struct rd
        {
            public byte  reserve;
            public static byte[]  ain = new byte[12];
            public static byte[]  aout = new byte[8];
            public byte  cntr;
            public byte  fef_zyklus;
            public byte  fef_projekt;
        }
    
        public static byte[]  wr = new byte[12 + 8 + 4];
    }
    

    Ich deklariere eine Variable der Struktur Union_PRM_t:

    //Deklaration
    Union_PRM_t data;
    

    im Anschluss muss ich das Member wr an DP_read_slv_par übergeben:

    dpd_RetErrClass = DP_Master.DP_read_slv_par(glb_dpdUserHandle,
                                                            glb_SlvAdr[0],
                                                            DP_IF.DP._SLV_PRM,
                                                            out DP_parlen,
                                                            out data.wr,
                                                            out dpt_ErrStruct);
    

    Nun was mache ich falsch? 😕



  • Mach einfach 2 structs raus 😉
    (StructLayout würde ich immer angeben)


  • Administrator

    In C# kann man Unions mit LayoutKind.Explicit und FieldOffsetAttribute nachbauen. Dies ist aber in deinem Fall nicht nötig, wenn ich das richtig sehe. Du wirst im C# Programm sowieso nie auf das Array wr zugreifen. Daher mach es doch gleich so:

    [StructLayout(LayoutKind.Sequential)]
    public struct Union_PRM_t
    {
      public byte reserve;
    
      [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)]
      public byte[] ain;
    
      [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
      public byte[] aout;
    
      public byte cntr;
      public byte fef_zyklus;
      public byte fef_projekt;
    }
    
    [DllImport("dp_base.dll", CharSet = CharSet.Ansi)]
    public static extern DPR_DWORD DP_read_slv_par(
      DPR_DWORD user_handle,
      DPR_WORD slv_add,
      DPR_WORD type,
      out DPR_WORD data_len,
      ref Union_PRM_t data,
      out DP_ERROR_T error);
    

    Warnung: Dieser User ist sehr müde und kann daher für die Korrektheit der Informationen nicht garantieren.

    Grüssli



  • Hallo Dravere,

    interessanter Ansatz. Funktioniert das auch bei Unions, die unterschiedliche Große elemente im Speicher liegen haben? Eine Enum orientiert sich ja an dem größtmöglichen Wert, damit es beim Iterieren durch ein Array keine Schwierigkeiten gibt.

    Grüße



  • Hi Dravere,

    Danke, es hat geklappt!

    Kannst du mir bitte vielleicht in einem anderen Thread weiterhelfen:

    http://www.c-plusplus.net/forum/285493

    ???


  • Administrator

    @2AndAHalfBit,
    Ich würde aufpassen, wenn es unterschiedliche Grössen drin hat und zusätzlich nicht primitive Typen. Aber mit dem FieldOffsetAttribute kann man einiges erreichen. Z.B. sowas ist kein Problem:

    [StructLayout(LayoutKind.Explicit)]
    struct Test
    {
      [FieldOffset(0)]
      public int a;
    
      [FieldOffset(0)]
      public long b;
    }
    

    Dies ist sogar eine Union in C# selber. Also das folgende Program gibt 10 aus:

    Test t = new Test();
    t.a = 10;
    Console.WriteLine(t.b);
    

    Grüssli



  • Hallo Dravere,

    das dein Beispiel funktioniert, ist unkritisch. Aber was wäre, wenn ich ein Array von deinem Enum habe? Bei der ausgabe des zweiten Arrayelements würde das auch noch passen? Ich habs nun nicht ausprobiert, also glaub ichs dir erstmal.

    Greez.


  • Administrator

    2AndAHalfBit schrieb:

    das dein Beispiel funktioniert, ist unkritisch. Aber was wäre, wenn ich ein Array von deinem Enum habe? Bei der ausgabe des zweiten Arrayelements würde das auch noch passen? Ich habs nun nicht ausprobiert, also glaub ichs dir erstmal.

    Enum ist in C# standardmässig Int32 . Falls du einen anderen Typen willst, dann musst du dies angeben.
    http://msdn.microsoft.com/en-us/library/sbbt4032.aspx

    2AndAHalfBit schrieb:

    Eine Enum orientiert sich ja an dem größtmöglichen Wert, ...

    In C# nicht.

    Grüssli


Log in to reply