Bonjour à tous,

langage: C#
Framework .NET: 2.0 et sup.
O.S: Windows XP et sup.

Je dispose d'une bibliothèque dynamique (DLL) programmée en C. L'appel d'une des fonctions exportées par cette DLL me retourne une structure.

Grâce à Pinvoke, je "marshal" ma structure pour qu'elle soit compréhensible en C#. Jusque là pas de problème.

Un bout de la structure C:

Code C : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
 
#pragma pack(1)
typedef struct  tag_TRPACKET{
   long Flag;
   long Transition;
   char Provider[16];
   //[...]
} TRPACKET;
#pragma pack()

La même en C#:

Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
 
    [StructLayout(LayoutKind.Sequential, Pack=1, CharSet=CharSet.Ansi)] //size: 60
    public struct TRPACKET{        
        public int Flag;        
        public int Transition;        
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16, ArraySubType= UnmanagedType.LPStr)]
        public byte[] Provider; // size: 16
        //[...]
}

N.B: Notez que le membre "Provider" est de type byte[], mais le mettre en char[] ne change rien au problème...

Problématique

La fonction C me rempli le tableau de caractère "Provider" avec une chaîne ANSI (i.e. un octet par caractère). Notez que chacun des caractères retournés dans le buffer "Provider" est obligatoirement dans l'espace imprimable ( 0x20 >= caractère <= 0x7E). Le caractère final de la chaine est toujours un '\0'

Le problème est que les appels subséquent à la fonction ne nettoie pas le buffer complètement. Ça ne pose pas de problème en C, mais en C#, cela m'en pose un. Exemples:

- 1er appel à la fonction. La fonction C me retourne "ABCDE\0" dans le buffer "Provider":
Provider vaut {0x41, 0x42, 0x43, 0x44, 0x45, 0x00} soit "ABCDE\0".
- 2eme appel, la fonction me retourne "FGH\0":
Provider vaut {0x46, 0x47, 0x48, 0x00, 0x45, 0x00, /*... */}
Comme on le voit ci-dessus, la fonction ne nettoie pas le buffer, puisque le 0x45 ('E') de l'appel précédent est encore présent.

Lorsque j'imprime la chaine de caractère (via console.WriteLine()) la chaîne imprimée pour le deuxième cas vaut : "FGH E", ce qui n'est pas ce que j'attends.

Notez que le conversion byte[] => string est réalisée avec: ASCIIEncoding.ASCII.GetString() ou Encoding.UTF8.GetString()

J'ai essayé pas mal de chose outre les deux méthodes ci-dessus pour qu'un '\0' exprime bien une fin de chaîne pour C# mais je n'ai rien trouvé de satisfaisant.

Du coup j'ai pondu une fonction mais ça me semble être une rustine plutôt que quelque chose d'acceptable en l'état:

Code C# : 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
 
        //search for the first '\0' and truncate the buffer at this offset
        static string StringFromByteArray(byte[] array)
        {
            int i;
            bool found = false;
            for (i = 0; i < array.Length; i++)
                if (array[i] == '\0')
                {
                    found = true;
                    break;
                }
            string str = null;
            if(found)
                str = Encoding.UTF8.GetString(array, 0, i);            
 
            return str;
        }

Existe-t-il un moyen propre de faire une conversion de ce genre, c'est à dire que C# prenne le premier '\0' dans le buffer comme le caractère terminateur de chaine ?

Merci à vous.