2015-10-05 1 views
-1

La partie PInkove a été prise d'une réponse SO (désolé, j'ai perdu le lien vers l'original).memcmp de PInvoke en C# ne fonctionne pas correctement pour les tableaux plus grands que 4x4

Ci-dessous le programme complet. La sortie est false.

using System; 
using System.Runtime.InteropServices; 

namespace Memcpy 
{ 
    class Program 
    { 
     [DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)] 
     private static extern int memcmp(Array b1, Array b2, long count); 

     public static bool CompareArrays(Array b1, Array b2) 
     { 
      // Validate buffers are the same length. 
      // This also ensures that the count does not exceed the length of either buffer. 
      return b1.Length == b2.Length && memcmp(b1, b2, b1.Length) == 0; 
     } 

     static void Main(string[] args) 
     { 
      var array1 = new int[,] 
      { 
       {0, 0, 0, 0, 0}, 
       {0, 0, 0, 0, 0}, 
       {0, 0, 0, 0, 0}, 
       {0, 0, 0, 0, 0}, 
      }; 

      var array2 = new int[,] 
      { 
       {0, 0, 0, 0, 0}, 
       {0, 0, 0, 0, 0}, 
       {0, 0, 0, 0, 0}, 
       {0, 0, 0, 0, 0}, 
      }; 

      Console.WriteLine(CompareArrays(array1, array2)); 
     } 
    } 
} 

Si je change la taille des tableaux à la sortie 4x4 se true

Pourquoi memcmp agir ainsi?

+0

Qu'attendez-vous de la valeur de b1.Length? A l'intérieur – pm100

+0

'CompareArrays' est à la fois la longueur sont égales par ailleurs, le résultat de memcmp n'est pas = 0. Il suffit de définir un point d'arrêt. ' b1.Length = '20' b2.Length = 20'' –

+1

memcmp' prend deux pointeurs vides et une longueur en octets. Qu'est-ce qui vous fait penser que la propriété 'Length' d'un tableau est une longueur en octets? Ce n'est pas le cas, c'est le nombre d'éléments dans le tableau. Aussi, vous voulez probablement appeler 'memcmp' parce que vous croyez que c'est une fonction efficace? C'est le cas, mais le code de marshaling généré peut ne pas l'être! En fait, je pense que le code de marshalling peut être le coupable ici. –

Répondre

1

Votre code comporte de nombreux problèmes. Voici ce que je peux voir:

  1. Le msvcrt.dll runtime C++ est sujette à changement, de sorte que vous tout code qui repose sur elle risque de rupture dans l'avenir.
  2. Le paramètre final de memcmp a le type size_t. C'est la même taille qu'un pointeur et ainsi mappe UIntPtr. Rappelez-vous que C# long est de 64 bits de large.
  3. L'utilisation de Array dans une invocation p est au mieux douteuse. Qui sait ce que ce marshals comme? Je m'attends à ce que marshals comme un indicateur de la représentation interne de la classe. Il est sûr que marshal pas comme un pointeur vers le début du tableau.
  4. memcpy attend le nombre d'octets à comparer, pas la longueur du tableau. Vous devez multiplier la longueur du tableau par la taille des éléments pour obtenir le nombre d'octets.

Pour que votre programme fonctionne, vous devrez résoudre ces problèmes. En laissant de côté l'utilisation de msvcrt, vous pouvez corriger votre code comme ceci:

[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)] 
private static extern int memcmp(IntPtr b1, IntPtr b2, UIntPtr count); 

public static bool CompareArrays(Array b1, Array b2) 
{ 
    if (b1.Length != b2.Length) 
     return false; 
    if (!b1.GetType().GetElementType().Equals(b2.GetType().GetElementType())) 
     return false; 

    GCHandle gch1 = GCHandle.Alloc(b1, GCHandleType.Pinned); 
    try 
    { 
     GCHandle gch2 = GCHandle.Alloc(b2, GCHandleType.Pinned); 
     try 
     { 
      return memcmp(gch1.AddrOfPinnedObject(), gch2.AddrOfPinnedObject(), 
       (UIntPtr)(b1.Length * Marshal.SizeOf(b1.GetType().GetElementType()))) == 0; 
     } 
     finally 
     { 
      gch2.Free(); 
     } 
    } 
    finally 
    { 
     gch1.Free(); 
    } 
} 

Ce code est certainement pas très efficace, sans parler étant très hacky. Je suggère que vous respectiez le code .net pur.