首页 > 解决方案 > 对象的 C# 和 C++ 字节数组转换的区别

问题描述

我正在尝试将一些 C++ 代码转换为 C# 以用于应用程序。我尝试转换的函数计算对象的校验和,其中包括 MAC 地址以及其他详细信息。C++ 中的 Checksum 函数定义为:

unsigned short CalculateCheckSum(unsigned char* p, int n)
{
    unsigned short x, checksum = 0;

    for (unsigned long i = 0; i < n; ++i)
    {
        x = p[i];
        x <<= i % 8;
        checksum += x;
    }

    return checksum != 0 ? checksum : 51;

我编写了在 C# 中定义的相同函数是:

 public static ushort CalculateCheckSum(byte[] p, int n)
        {
            ushort x, checksum = 0;

            for (int i = 0; i < n; ++i)
            {
                x = p[i];
                x <<= i % 8;
                checksum += x;
            }

            return (ushort)(checksum != 0 ? checksum : 51);
        }

这是在 C++ 中计算校验和的代码:

    PCInfoClass pcInfo;
    char nicIDStr[1024];
    strcpy_s(nicIDStr, "34-29-8f-93-16-61");
    NICAddressStrToBinary(nicIDStr, pcInfo.nicID);
    char        outbuf[1000];
    pcInfo.timeStamp = 1234;
    pcInfo.expDate = 0;
    I32 pcInfoSz = 20;
    pcInfo.checksum = 0;
    unsigned char* byteStr;
    byteStr = (unsigned char*)&pcInfo;
    pcInfo.checksum = CalculateCheckSum(byteStr, pcInfoSz);

由于CalculateCheckSum方法采用Byte 数组作为参数,因此我使用了System.Runtime 附带的BinaryFormatter类。我试图用以下几行在 C# 中复制相同的功能:

            PCInfoClass pcInfo =  new PCInfoClass();
            char[] nicIDStr = new char[1024];
            string str = "34-29-8f-93-16-61";
            for (int i = 0; i < str.Length; i++)
            {
                nicIDStr[i] = str[i];
            }
            NICAddressStrToBinary(nicIDStr, pcInfo.nicID);
            pcInfo.timeStamp = 1234;
            pcInfo.expDate = 0;
            int pcInfoSz = 20;
            pcInfo.checksum = 0;
            pcInfo.checksum = CalculateCheckSum(ObjectToByteArray1(pcInfo), pcInfoSz);



public static  byte[] ObjectToByteArray1(Object obj)
        {
            if (obj == null)
                return null;

            BinaryFormatter bf = new BinaryFormatter();
            MemoryStream ms = new MemoryStream();
            bf.Serialize(ms, obj);

            return ms.ToArray();
        }

不幸的是,两种方法的校验和值都不同,因此转换在这一点上被卡住了。

此代码中使用的另一种方法是NICAddressStrToBinary,在 C++ 中其定义为:

bool NICAddressStrToBinary(const char* nicIDStr, unsigned char* outbuf)
{
    int     c, i, dgt;

    if (nicIDStr == NULL) return false;

    //converted char to integer as ascii number.
    for (dgt = 0, i = 0; (c = nicIDStr[i]) != '\0'; ++i)
    {
        //if it is 45 '-' then the loop will continue;
        if (c == '-') continue;

         //if the ascii value is between 48 to 57 then we will decrrease with 48 of given integer
        if ('0' <= c && c <= '9')
        {
            c -= '0';
        }
        else
            if ('a' <= c && c <= 'f')
            {
                c -= 'a' - 10;
            }
            else
                if ('A' <= c && c <= 'F')
                {
                    c -= 'A' - 10;
                }
                else
                {
                    return false;
                }

        if (dgt >= 6 * 2)
        {
            return false;
        }

        if (outbuf != NULL)
        {
            if ((dgt & 1) == 0)
            {
                //// it means c<<4 is c*2power4
                outbuf[dgt / 2] = c << 4;
            }
            else
            {
                outbuf[dgt / 2] |= c;
            }
        }

        dgt++;
    }


    if (dgt < 6  * 2)
    {
        return false;
    }

    return true;
}

在 C# 中,它被重写为:

 public static void NICAddressStrToBinary(char[] nicIDStr, byte[] outbuf)
        {
            int c, i, dgt;
            if (nicIDStr == null) return ;
            for (dgt = 0, i = 0; i<=nicIDStr.Length-1; ++i)
            {
                c = nicIDStr[i];
                if (c == '-') continue;
                if ('0' <= c && c <= '9')
                {
                    c -= '0';
                }
                else if ('a' <= c && c <= 'f')
                {
                    c -= 'a' - 10;
                }
                else if ('A' <= c && c <= 'F')
                {
                    c -= 'A' - 10;
                }
                else
                {
                    return;
                }


                /* make sure there aren't too many digits
                 */
                if (dgt >= 6 * 2)
                {
                    return ;
                }

                /* accumulate the binary NIC ID
                 *  remembering that we're starting
                 *  with the most significant digits first
                 */
                if (outbuf != null)
                {
                    if ((dgt & 1) == 0)
                    {
                        //// it means c<<4 is c*2power4
                        outbuf[dgt / 2] = (byte)(c << 4);
                    }
                    else
                    {
                        outbuf[dgt / 2] |= (byte)c;

                    }
                }

                /* advance the digit index
                 */
                dgt++;
            }


            /* make sure I have enough digits
             */
            if (dgt < 6 * 2)
            {
                return ;
            }
            return ;
        }

有人可以告诉我在 C++ 和 C# 中计算不同值的原因是什么?

标签: c#c++

解决方案


由于CalculateCheckSum 方法采用Byte 数组作为参数,因此我使用了System.Runtime 附带的BinaryFormatter 类。

单独来看,这将是一个合理的选择,但 BinaryFormatter 使用一种复杂的格式,而不仅仅是“对象的字节”。这些字节可能在某处,但还有很多其他的东西。所以在这种情况下它不起作用。

即使在 C# 中,也有一些方法可以获取给定对象的原始字节,但您必须为此专门设计PCInfoClass:使其成为结构,使用fixed-size 数组nicID(引用是不行的)。然后您可以使用一些技巧(您可以使用的技巧取决于您所针对的 .NET 版本)来获取该结构的原始字节。

我的建议是使用 aBinaryWriter将每个字段手动写入 a MemoryStream,然后ToArray像您一样使用。要非常小心地调用 的正确重载Write,并明确写入填充字节。如果不知道 C++ 中的类定义是什么样的,我无法为您编写该代码。


推荐阅读