IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C# Discussion :

Marshalling struct décallage 2 bytes.


Sujet :

C#

  1. #1
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Février 2005
    Messages
    41
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Février 2005
    Messages : 41
    Points : 28
    Points
    28
    Par défaut Marshalling struct décallage 2 bytes.
    Bonjour,

    J'utilise actuellement un API (WvAPI.dll) natif (écrit en Ansi C) dans un développement dotNet frmaework 3.5.
    J'ai déjà construit une partie du marshalling.
    Cela semble bien fonctionner pour des structures simples mais j'ai quelques soucis pour cette structure:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    typedef enum WV_OPERATING_MODE
    {	
       WV_OPERATING_MODE_INVALID = 0,
       WV_OPERATING_MODE_MONITORING,
       WV_OPERATING_MODE_STANDBY,
       WV_OPERATING_MODE_READY,
       WV_OPERATING_MODE_BUSY,
       WV_OPERATING_MODE_PAPEROUT,
       WV_OPERATING_MODE_DOOR_OPEN,
       WV_OPERATING_MODE_COMM_FAILURE,
       WV_OPERATING_MODE_DEVICE_FAILURE,
       WV_OPERATING_MODE_NA,
       WV_OPERATING_MODE_DISCHARGE = WV_OPERATING_MODE_NA,
       WV_OPERATING_MODE_LAST = WV_OPERATING_MODE_NA
    } WV_OPERATING_MODE ;
     
    typedef int WV_CONNECT_ID ;
     
    typedef struct {
       TCHAR             PatientName  [WV_PATIENT_NAME_SIZE] ;
       TCHAR             PatientID    [WV_PATIENT_ID_SIZE] ;
       TCHAR             BedLabel     [WV_BED_LABEL_SIZE];
       TCHAR             CareUnit     [WV_CARE_UNIT_SIZE];
       TCHAR             FileName     [WV_FILE_NAME_SIZE];
       TCHAR             IPAddress    [WV_IP_ADDRESS_SIZE];
       TCHAR             MulticastIP  [WV_MULTICAST_IP_SIZE];
       TCHAR             DeviceType   [WV_DEVICE_TYPE_SIZE];
       WV_OPERATING_MODE DeviceStatus ;
       WV_CONNECT_ID     ConnectID ;  // 0 if not connected
       BOOL				 Wireless;
       TCHAR             HostName     [WV_HOSTNAME_SIZE] ;
    } WV_BED_DESCRIPTION ;
     
     
    typedef struct {
       WV_BED_DESCRIPTION WvBeds[WV_MAX_BEDS_PER_SERVER] ;
    } WV_BED_LIST ;
     
    IMPORT_FUNCTION int WINAPI WvListBeds(const TCHAR *pServerName, const TCHAR *pUserName, const TCHAR *pPassword, WV_BED_LIST *pBedList, int *pNumberOfBeds) ;
    Que j'ai mappé par l’intermédiaire du marshalling de la façon suivante:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
     
            public enum WV_OPERATING_MODE
            {
                WV_OPERATING_MODE_INVALID = 0,
                WV_OPERATING_MODE_MONITORING,
                WV_OPERATING_MODE_STANDBY,
                WV_OPERATING_MODE_READY,
                WV_OPERATING_MODE_BUSY,
                WV_OPERATING_MODE_PAPEROUT,
                WV_OPERATING_MODE_DOOR_OPEN,
                WV_OPERATING_MODE_COMM_FAILURE,
                WV_OPERATING_MODE_DEVICE_FAILURE,
                WV_OPERATING_MODE_NA,
                WV_OPERATING_MODE_DISCHARGE = WV_OPERATING_MODE_NA,
                WV_OPERATING_MODE_LAST = WV_OPERATING_MODE_NA
            };
     
            [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
            public struct WV_BED_DESCRIPTION
            {
                [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = WV_PATIENT_NAME_SIZE)]
                public string PatientName;
                [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = WV_PATIENT_ID_SIZE)]
                public string PatientID;
                [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = WV_BED_LABEL_SIZE)]
                public string BedLabel;
                [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = WV_CARE_UNIT_SIZE)]
                public string CareUnit;
                [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = WV_FILE_NAME_SIZE)]
                public string FileName;
                [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = WV_IP_ADDRESS_SIZE)]
                public string IPAddress;
                [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = WV_MULTICAST_IP_SIZE)]
                public string MulticastIP;
                [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = WV_DEVICE_TYPE_SIZE)]
                public string DeviceType;
                public WV_OPERATING_MODE DeviceStatus;
                public int ConnectID;  // 0 if not connected
                public int Wireless;
                [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = WV_HOSTNAME_SIZE)]
                public string HostName;
            };
     
            [StructLayoutAttribute(LayoutKind.Sequential)]
            public struct WV_BED_LIST
            {
                [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = WV_MAX_BEDS_PER_SERVER, ArraySubType = UnmanagedType.Struct)]
                public WV_BED_DESCRIPTION[] WvBeds;
            };
     
            [DllImportAttribute(Constants.NATIVE_DLL, EntryPoint = "WvListBeds", CharSet = CharSet.Ansi)]
            public static extern int WvListBeds(
                [MarshalAsAttribute(UnmanagedType.LPStr)] string pServerName,
                [MarshalAsAttribute(UnmanagedType.LPStr)] string pUserName,
                [MarshalAsAttribute(UnmanagedType.LPStr)] string pPassword,
                ref WV_BED_LIST pBedList,
                ref int pNumberOfBeds);
    Ce que je constate c'est qu'à partir du champ DeviceStatus (qui est une énumération) la valeur retournée ne correspond pas à celle de l'application d'exemple fournie avec l'API. Tous les champs suivant sont affectés par cette erreur et on observe un décalage de 2 bytes.
    En effet, le champ Wireless vaut 0x50430000 or il doit être à 0x00000000.
    le valeurs 0x50 et 0x43 correspondent à de l'ASCII du string suivant HostName.
    Ainsi le HostName vaut "S207" alors qu'il doit valoir "CPS207".
    Les caractères C et P correspondent respectivement à 0x43 et 0x50 en ASCII.

    Pourriez-vous m'éclairer dans ce problème?
    Est-ce au niveau de l'énumération?

    Un tout grand merci pour la grande richesse de votre site web.

    KINDT Raphaël.

  2. #2
    Rédacteur
    Avatar de Nathanael Marchand
    Homme Profil pro
    Expert .Net So@t
    Inscrit en
    Octobre 2008
    Messages
    3 615
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Expert .Net So@t
    Secteur : Conseil

    Informations forums :
    Inscription : Octobre 2008
    Messages : 3 615
    Points : 8 082
    Points
    8 082
    Par défaut
    En ANSI C, quelle est la taille d'une enum ?
    En C#, c'est codé sur un Int32 mais tu peux demander à l'avoir sur un Int16 ou même un byte.

  3. #3
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Février 2005
    Messages
    41
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Février 2005
    Messages : 41
    Points : 28
    Points
    28
    Par défaut
    J'ai déjà vérifié que la size était la même des deux côtés en faisant simplement ceci du côté C:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    int sz = sizeof(WV_OPERATING_MODE)
    puis du côté C#:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    int sz = sizeof(Wv.WV_OPERATING_MODE);
    Des deux côtés j'ai 4 bytes.
    J'ai même forcé l'usage d'un int à la place de l'enum dans le marshalling.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    public int DeviceStatus;
    à la place de:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    public WV_OPERATING_MODE DeviceStatus;
    J'ai même tenté un short pour voir mais le problème est toujours le même.
    J'ai créé une nouvelle solution avec deux projets: l'un qui génère une DLL native avec la même fonction et les mêmes structures et enum. L'autre qui effectue le marshaling et qui test le remplissage de la structure.
    Dans cette solution, tout semble fonctionner correctement, je n'ai pas de décalage de 2 bytes.

  4. #4
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Février 2005
    Messages
    41
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Février 2005
    Messages : 41
    Points : 28
    Points
    28
    Par défaut
    J'imagine qu'une structure dot NET n'est pas du tout contenu dans une mémoire linéaire?
    Ainsi, après son mapping, je ne peux pas me fier au décalage de bytes qui affecterait d'autres champ de ma structure.

    En C#, c'est codé sur un Int32 mais tu peux demander à l'avoir sur un Int16 ou même un byte.
    Puis-je savoir comment faire?

  5. #5
    Rédacteur
    Avatar de Nathanael Marchand
    Homme Profil pro
    Expert .Net So@t
    Inscrit en
    Octobre 2008
    Messages
    3 615
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Expert .Net So@t
    Secteur : Conseil

    Informations forums :
    Inscription : Octobre 2008
    Messages : 3 615
    Points : 8 082
    Points
    8 082
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    public enum WV_OPERATING_MODE : Int32 // ou Int16, ou byte
            {
    //blabla
    }
    Cela dit tu peux essayer ca:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    [StructLayout(LayoutKind.Explicit)]
    public struct Rect 
    {
       [FieldOffset(0)] public int left;
       [FieldOffset(4)] public int top;
       [FieldOffset(8)] public int right;
       [FieldOffset(12)] public int bottom;
    }
    http://msdn.microsoft.com/en-us/libr...kind.aspx#Y513

  6. #6
    Membre confirmé
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mars 2011
    Messages
    269
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2011
    Messages : 269
    Points : 460
    Points
    460
    Par défaut
    Bonjour,

    Attention le marshalling C# est un peu spécial au niveau des alignements.
    Un Int32 se positionne (en octets) a un multiple de 4.
    La position de "DeviceID" est donc dépendante de la position de "DeviceType", si "DeviceType" est à l'octet 13 par exemple, et qu'il est codé sur 5 octets, et bien "DeviceID" n'est pas à l'octet 18 (13+5=18) mais à l'octet 20, 20 étant le prochain multiple de 4.
    De la même façon un Int16 se positionne sur un multiple de 2.

    Vérifies la longueur de tes chaines de caractère pour voir si elle n'induisent pas un décalage.

  7. #7
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Février 2005
    Messages
    41
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Février 2005
    Messages : 41
    Points : 28
    Points
    28
    Par défaut
    Cette réponse est très intéressante.
    J'ai en effet un décalage de 2 bytes.
    En fait, voici la taille des différents champs de la structure:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    typedef struct {
       TCHAR             PatientName  [WV_PATIENT_NAME_SIZE] ;  // 26
       TCHAR             PatientID    [WV_PATIENT_ID_SIZE] ;    // 20
       TCHAR             BedLabel     [WV_BED_LABEL_SIZE];      // 20
       TCHAR             CareUnit     [WV_CARE_UNIT_SIZE];      // 20
       TCHAR             FileName     [WV_FILE_NAME_SIZE];      // 20
       TCHAR             IPAddress    [WV_IP_ADDRESS_SIZE];     // 20
       TCHAR             MulticastIP  [WV_MULTICAST_IP_SIZE];   // 20
       TCHAR             DeviceType   [WV_DEVICE_TYPE_SIZE];    // 20
       WV_OPERATING_MODE DeviceStatus ;                         //  4
       WV_CONNECT_ID     ConnectID ;  // 0 if not connected     //  4
       BOOL              Wireless;                              //  4
       TCHAR             HostName     [WV_HOSTNAME_SIZE] ;      // 20
    } WV_BED_DESCRIPTION ;
    Ainsi, l'offset de DeviceStatus est de 166. Il manque donc 2 octets pour être aligné au bloc suivant de 4 octets.
    Comment faire pour corriger ce problème côté marshalling?
    En attendant, est-il possible de forcer le comptage au bytes près?

    Merci pour cette réponse.

  8. #8
    Membre confirmé
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mars 2011
    Messages
    269
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2011
    Messages : 269
    Points : 460
    Points
    460
    Par défaut
    Malheureusement pour toi, c'est pas possible.
    Soit tu as accès a la structure C++, et tu insères du padding.
    Soit tu n'y as pas accès et dans ce cas, il faudra reconstitué ta structure à la main.

  9. #9
    Membre confirmé
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mars 2011
    Messages
    269
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2011
    Messages : 269
    Points : 460
    Points
    460
    Par défaut
    En fait, je viens de penser à une solution
    Mettre 4 champs de type byte.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
            public struct WV_BED_DESCRIPTION
            {
                [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = WV_PATIENT_NAME_SIZE)]
                public string PatientName;
                [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = WV_PATIENT_ID_SIZE)]
                public string PatientID;
                [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = WV_BED_LABEL_SIZE)]
                public string BedLabel;
                [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = WV_CARE_UNIT_SIZE)]
                public string CareUnit;
                [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = WV_FILE_NAME_SIZE)]
                public string FileName;
                [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = WV_IP_ADDRESS_SIZE)]
                public string IPAddress;
                [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = WV_MULTICAST_IP_SIZE)]
                public string MulticastIP;
                [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = WV_DEVICE_TYPE_SIZE)]
                public string DeviceType;
                private byte _deviceStatus1;
                private byte _deviceStatus2;
                private byte _deviceStatus3;
                private byte _deviceStatus4;
                private byte _connectID1;
                private byte _connectID2;
                private byte _connectID3;
                private byte _connectID4;
                private byte _wireless1;
                private byte _wireless2;
                private byte _wireless3;
                private byte _wireless4;
                [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = WV_HOSTNAME_SIZE)]
                public string HostName;
     
               public WV_OPERATING_MODE DeviceStatus
               {
                   get { // agregation des 4 bytes, puis cast en enum }
               }
     
               public int ConnectID  // 0 if not connected
               {
                   get { // agregation des 4 bytes }
               }
               public int Wireless
               {
                   get { // agregation des 4 bytes }
               }
            };

  10. #10
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Février 2005
    Messages
    41
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Février 2005
    Messages : 41
    Points : 28
    Points
    28
    Par défaut
    Bonjour et merci pour la réponse.
    Je n'aurais pas le temps de tester cette solution dans les 2 semaines.
    Toutefois, je me pose une question.
    Pourquoi ce problème n'apparaît pas lorsque je compile moi-même une dll native de test? Ce que j'ai expliqué précédement:
    J'ai créé une nouvelle solution avec deux projets: l'un qui génère une DLL native avec la même fonction et les mêmes structures et enum. L'autre qui effectue le marshaling et qui test le remplissage de la structure.
    Dans cette solution, tout semble fonctionner correctement, je n'ai pas de décalage de 2 bytes.

  11. #11
    Membre confirmé
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mars 2011
    Messages
    269
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2011
    Messages : 269
    Points : 460
    Points
    460
    Par défaut
    Et bien parce que cette dll est native.
    Il n'y a pas d’opération de marshalling, le framework effectue des opérations de marshalling quand on passe d'une zone mémoire managé, à une zone non managé, et inversement.
    Donc par définition, il n'y a pas de marshalling avec une dll native, et comme c'est surement le marshalling qui induit ce décallage, sans marshalling pas de décallage.

    Je ne sais pas si j'ai été clair.

  12. #12
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Février 2005
    Messages
    41
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Février 2005
    Messages : 41
    Points : 28
    Points
    28
    Par défaut
    Je reprend la discussion.
    DLL Native signifie pour moi que ce fichier est construit à partir d'instruction machine (pour un CPU particulier).
    Les DLL managées sont pour moi celles qui utilisent du pseudo-code et donc ont besoin d'une machine virtuelle pour fonctionner. Cette machine virtuelle va digérer ce pseudo-code et construire en temps réelle (JIT) du language machine.

    Or c'est bien ce que j'ai créé dans ma solution de test.
    - Une DLL Native
    - Une application managé qui utilise cette DLL Native (d'où le marshaling)

    Je vais tenter en convertissant tout en array de byte puis type cast...

    Si quelqu'un a une autre solution.

    Encore merci pour vos conseils.

Discussions similaires

  1. Byte[] to struct
    Par kickoune dans le forum C#
    Réponses: 2
    Dernier message: 18/06/2014, 08h19
  2. Marshalling C struct en C#
    Par ncheboi dans le forum C#
    Réponses: 1
    Dernier message: 05/09/2011, 20h28
  3. modifier une struct marshallée en C#
    Par small_frenchy dans le forum C#
    Réponses: 1
    Dernier message: 19/09/2010, 14h55
  4. [Dvp.NET|A intégrer] [C#]Conversion struct <-> byte[]
    Par smyley dans le forum Contribuez
    Réponses: 2
    Dernier message: 14/12/2008, 14h04
  5. erreur IDL:omg.org/CORBA/MARSHAL:1.0
    Par Pinggui dans le forum CORBA
    Réponses: 3
    Dernier message: 13/05/2002, 15h05

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo