memcmp из PInvoke в C# не работает должным образом для массивов больше, чем 4x4

Часть PInkove была взята из какого-то SO ответа (извините, я потерял ссылку на оригинал).

Ниже приведена полная программа. Выход 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));
        }
    }
}

Если я изменю размер массива на 4x4, то вывод получится true

Почему memcmp ведет себя так?

1 ответ

У вашего кода много проблем. Вот что я вижу:

  1. msvcrt.dll Среда выполнения C++ подвержена изменениям, поэтому любой код, основанный на ней, рискует в будущем сломаться.
  2. Конечный параметр memcmp имеет тип size_t, Это тот же размер, что и указатель, и поэтому отображается на UIntPtr, Помните, что C# long имеет ширину 64 бита.
  3. Использование Array в a p/invoke в лучшем случае сомнительно. Кто знает, что это за маршалы? Я ожидаю, что это маршалы как указатель на внутреннее представление класса. Это точно не маршал как указатель на начало массива.
  4. memcpy ожидает количество байтов для сравнения, а не длину массива. Вам нужно умножить длину массива на размер элементов, чтобы получить количество байтов.

Чтобы ваша программа работала, вам нужно решить эти проблемы. Оставляя в стороне использование msvcrt, вы можете исправить свой код следующим образом:

[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();
    }
}

Этот код, безусловно, не очень эффективен, не говоря уже о том, что он очень хакерский. Я предлагаю вам придерживаться чистого кода.net.

Другие вопросы по тегам