来自C#中PInvoke的memcmp对于大于4x4的数组

时间:2015-10-05 22:02:53

标签: c# pinvoke memcmp

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 个答案:

答案 0 :(得分: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.