-1

The PInkove part was taken from some SO answer (sorry, I've lost the link to original).

Below is the complete program. The output is 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));
        }
    }
}

If I change arrays' size to 4x4 the output turns true

Why does memcmp behave this way?

Pavel Murygin
  • 2,242
  • 2
  • 18
  • 26
  • what do you expect the value of b1.Length to be? – pm100 Oct 05 '15 at 22:20
  • Inside `CompareArrays` both Length's are equal, the result of memcmp is not.= 0. Just set a breakpoint: `b1.Length = 20` `b2.Length = 20` – Pavel Murygin Oct 05 '15 at 22:29
  • 1
    `memcmp` takes two void pointers and a length in bytes. What makes you think that the `Length` property of an array is a length in bytes? It's not, it's the number of elements in the array. Also, you probably want to call `memcmp` because you believe that to be an efficient function? It is, but the generated marshalling code may not be! In fact, I think the marshalling code may be the culprit here. – Kris Vandermotten Oct 05 '15 at 22:33

1 Answers1

1

Your code has many problems. Here is what I can see:

  1. The msvcrt.dll C++ runtime is subject to change, so you any code that relies on it risks breaking in the future.
  2. The final parameter of memcmp has type size_t. That is the same size as a pointer and so maps to UIntPtr. Remember that C# long is 64 bits wide.
  3. The use of Array in a p/invoke is dubious at best. Who knows what that marshals as? I expect it marshals as a pointer to the internal representation of the class. It for sure does not marshal as a pointer to the beginning of the array.
  4. memcpy expects the number of bytes to compare, not the length of the array. You need to multiply the length of the array by the size of the elements to get the number of bytes.

To make your program work you will need to tackle these issues. Leaving aside the use of msvcrt, you can correct your code like this:

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

This code is surely not very efficient, not to mention it being very hacky. I suggest that you stick to pure .net code.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490