c# - 对象的 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# 中计算不同值的原因是什么?
解决方案
由于CalculateCheckSum 方法采用Byte 数组作为参数,因此我使用了System.Runtime 附带的BinaryFormatter 类。
单独来看,这将是一个合理的选择,但 BinaryFormatter 使用一种复杂的格式,而不仅仅是“对象的字节”。这些字节可能在某处,但还有很多其他的东西。所以在这种情况下它不起作用。
即使在 C# 中,也有一些方法可以获取给定对象的原始字节,但您必须为此专门设计PCInfoClass
:使其成为结构,使用fixed
-size 数组nicID
(引用是不行的)。然后您可以使用一些技巧(您可以使用的技巧取决于您所针对的 .NET 版本)来获取该结构的原始字节。
我的建议是使用 aBinaryWriter
将每个字段手动写入 a MemoryStream
,然后ToArray
像您一样使用。要非常小心地调用 的正确重载Write
,并明确写入填充字节。如果不知道 C++ 中的类定义是什么样的,我无法为您编写该代码。