Umsetzung C++-Struktur



  • Hi,

    also ich habe folgende C++-Struktur vor mir liegen und muss diese in C# übertragen:

    #define CTRLTELE_MAX_GUID 10
    typedef SQLGUID CTRLTELE_GUID[CTRLTELE_MAX_GUID];
    typedef struct
    {
    	DWORD dwCrc32,
    	      dwVersion,
    	      dwSize;
    	char  cType;
    	union
    	{
    		struct // cType 'M'
    		{
                        ULONG ulFlags;
                        CTRLTELE_GUID gGUID;
    		};
    		struct // cType 'W'
    		{
                        long iFlags;
                        TCHAR szMsg[513];
    		};
    		struct // cType 'R'
    		{
                        long iDelay;
    		};
    		struct // cType 'A'
    		{
                        long lAuftragID;
    		};
    	};
    }CTRLTELE;
    
    [StructLayoutAttribute(LayoutKind.Sequential)]
    		public struct CTRLTELE
    		{
    			public uint dwCrc32;
    			public uint dwVersion;
    			public uint dwSize;
    			public byte cType;
    
    			public CtrlTypes Types;
    		}
    
    		[StructLayoutAttribute(LayoutKind.Explicit)]
    		public struct CtrlTypes
    		{
    			[FieldOffsetAttribute(0)]
    			public TypeM M;
    
    			[FieldOffsetAttribute(0)]
    			public TypeW W;
    
    			[FieldOffsetAttribute(0)]
    			public TypeR R;
    
    			[FieldOffsetAttribute(0)]
    			public TypeA A;
    		}
    
    		[StructLayoutAttribute(LayoutKind.Sequential)]
    		public struct TypeM
    		{
    			public uint ulFlags;
    			public GUID[] gGUID;
    		}
    
    		[StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    		public struct TypeW
    		{
    			public int iFlags;
    			[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 513)]
    			public string szMsg;
    		}
    
    		[StructLayoutAttribute(LayoutKind.Sequential)]
    		public struct TypeR
    		{
    			public int iDelay;
    		}
    
    		[StructLayoutAttribute(LayoutKind.Sequential)]
    		public struct TypeA
    		{
    			public int lAuftragID;
    		}
    
    		[StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    		public struct GUID
    		{
    			public uint Data1;
    			public ushort Data2;
    			public ushort Data3;
    			[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 8)]
    			public string Data4;
    		}
    

    530 Bytes groß ist die "originale" Struktur, bis 560 hab ich die C#-Variante gekriegt.
    Und eine Exception fliegt wegen der CtrlTypes-Struct: "...konnte nicht geladen werden, da sie bei Offset 0 ein Objektfeld enthält, das falsch ausgerichtet ist oder von einem Feld überlappt wird, das kein Objektfeld ist."

    Ich verstehe das schon ungefähr, weiß nur nicht, wie ich das beheben kann bzw. wie die Umsetzung der Struktur wirklich aussehen soll.

    Wer kann mir da helfen ? 😕


  • Administrator

    Eine C++ Struktur ist das nicht wirklich, eher eine äusserst hässliche C Struktur 😉
    Habe mich kurz mit dem Problem beschäftigt, bin aber vorerst essen und danach noch mit anderem beschäftigt. Die "einfachste" Lösung, welche ich bis jetzt finden konnte, wäre:

    [StructLayout(LayoutKind.Sequential)]
    public struct CtrlTele
    { 
        public uint dwCrc32;
        public uint dwVersion;
        public uint dwSize;
        public uint cType;
    
        [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.U1, SizeConst = 517)]
        public byte[] Types;
    }
    

    Unter der Voraussetzung, dass es ein Padding bei cType gibt (deswegen der uint ) und dass TCHAR im Typ W zu einem char expandiert. Danach kann man mit einem MemoryStream und einem BinaryReader die Sache auslesen und konvertieren. Du könntest entsprechende Methoden in CtrlTele zur Verfügung stellen, welche dann auch gleich die Typprüfung mit cType durchführen. Nicht wirklich ideal, aber die Struktur ist auch alles andere als ideal für P/Invoke 🙂

    Grüssli

    PS: Bei Attributen kannst du das "Attribute" am Ende des Namen weglassen.



  • Hi,

    ja, C++-Struktur war tatsächlich übertrieben. :):)

    Grundsätzlich ist der Aufbau der Struktur nicht ideal für C#, ich will in einer Assembly mit meinen nativen Windows Services per Socket kommunizieren und dazu gibt es dieses Telegramm.

    Notwendig von den Typen ist nur 'W', der Rest interessiert mich nicht. Jedoch sollte es korrekt auf der anderen Seite aussehen (deshalb die Angabe der Größe vorhin).
    Ich muss also setzen:

    • dwVersion
    • dwSize
    • cType (= 'W')
    • iFlags
    • szMsg (tatsächlich mehr als 1 Zeichen)

    Wenn szMsg nicht wäre, wär' der Vorschlag von dir ideal...
    Das ist irgendwie doof hier.


  • Administrator

    R3dNeXX schrieb:

    Notwendig von den Typen ist nur 'W', der Rest interessiert mich nicht.

    Sag das doch gleich! Das macht die Sache EXTREM viel einfacher!

    [StructLayout(LayoutKind.Sequential)] 
    public struct CtrlTele
    { 
        public uint dwCrc32; 
        public uint dwVersion; 
        public uint dwSize; 
        public byte cType; 
        public TypeW W;
    }
    
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] 
    public struct TypeW 
    { 
        public int iFlags; 
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 513)] 
        public string szMsg;
    }
    

    So funktioniert es. CharSet.Unicode oder CharSet.Ansi musst du noch auswählen. Auf einem Windows NT und später ist TCHAR meistens WCHAR. Aber kommt halt darauf an, wie die DLL kompiliert wurde, bzw. mit UNICODE oder nicht.

    Grüssli



  • Oh, entschuldige, hab das echt vergessen zu erwähnen. 🙂

    Super, funktioniert einwandfrei !! Nur ein Pack = 1 bei CtrlTele und TypeW hat noch gefehlt, damit ich bei 530 Bytes ankomme, aber ich habe dafür auch das #pragma pack(1) im 1. Post vergessen.
    Gut, in meinem Fall noch Ansi, dann ist alles ok.

    Vielen Dank für deine Hilfe !!!!!!! 👍
    Schönes Wochenende 😉


Anmelden zum Antworten