BinaryFormatter在转换Int32和Double等内部对象时出现问题

BinaryFormatter issues converting intrinsic objects like Int32 and Double

本文关键字:内部对象 问题 Double 转换 Int32 BinaryFormatter      更新时间:2023-10-16

我正在编写c++混合模式DLL并将从数据库读取的对象转换为非托管代码变量。我试图将object转换为字节数组,以便我可以将其值逐字节复制到非托管内存。

当我进行转换时,我得到的不仅仅是字节数组中对象的数据。

下面是我的代码示例:

// Convert an object to a byte array
static array<System::Byte>^ ObjectToByteArray(Object^ obj)
{
    if (obj->Equals(nullptr))
        return nullptr;
    BinaryFormatter^ bf = gcnew BinaryFormatter();
    MemoryStream^ ms = gcnew MemoryStream();
    bf->Serialize(ms, obj);
    return ms->ToArray();
}
void dtmControlLimits() {
    OdbcDataReader^ reader;
    // ...
    Object oVal = reader->GetValue(ordinalPositionColumn);
    array<System::Byte, 1>^ ab = ObjectToByteArray(oVal);
    pin_ptr<System::Byte> pp = &ab[0];
    char *p = (char *)pz->zMap.pValue;
    for (size_t i = 0; i < knSizeOf; i++, p++){
      *p = ab[i];
    }
    // ...
}

返回的数组ab看起来像是Int32对象的结构体,而不仅仅是它的值。

我想获得一个byte[]与对象的值,以便我可以将其逐字节复制到非托管数据。

如果oValInt32*oVal = 0x12345678,那么这里是ab的内存转储:

0x03A45954  00 01 00 00 00 ff ff ff ff 01 00 00 00 00 00 00 00 04  .....ÿÿÿÿ.........
0x03A45966  01 00 00 00 0c 53 79 73 74 65 6d 2e 49 6e 74 33 32 01  .....System.Int32.
0x03A45978  00 00 00 07 6d 5f 76 61 6c 75 65 00 08 78 56 34 12 0b  ....m_value..xV4..

oVal的值在字段名m_value后面的最后一行。由于oVal是从数据库中读取的,因此在编译时无法确定其数据类型。

我怎么能得到对象的值,而不是整个对象的数据结构,到字节[]ab?

=========================================================

2014年6月12日更新

这是使用DAO的旧MFC代码的更新,我想将旧的32位应用程序更新为64位应用程序。要做到这一点,我必须重写DAO代码以使用ADO。Net,因为DAO只有32位可用。

我真的很想避免为内存复制设置开关,但看起来我被迫生活在。net的限制中。旧的MFC代码允许我使用memcpy()将DAO记录集数据传输到适当的变量。这是一种更新数据的直接方法。

使用switch语句似乎没有必要且复杂。我本想避免的。

谁有更好的主意?

static ViStatus ReaderToTag(
    pzDataTag_t pz
    , String^ sColumnName
    , IDataReader^ reader)
{
    ViStatus errStatus = VI_SUCCESS;
    array<System::Byte, 1>^ ab;
    if (pz) {
        const MM_eVppType_t keVppType = pz->zMap.eVppType;
        const size_t knSizeOf = MM_VPPTYPE_SIZEOF(keVppType);
        Object^ oVal = reader[sColumnName];
        Type^ typ = oVal->GetType();
        switch (Type::GetTypeCode(typ)) {
        case System::TypeCode::Byte:
            ab = BitConverter::GetBytes((Byte)oVal);
            break;
        case System::TypeCode::Char:
            ab = BitConverter::GetBytes((Char)oVal);
            break;
        case System::TypeCode::DateTime:
        {
            DateTime^ date = (DateTime)oVal;
            TimeSpan^ diff = date->ToUniversalTime() - DateTime(1970, 1, 1);
            *(time_t *)pz->zMap.pValue = static_cast<std::time_t>(diff->TotalMilliseconds);
            break;
        }
        case System::TypeCode::Decimal:
        {
            array<int>^ bits = Decimal::GetBits((Decimal)oVal);
            System::Array::Resize(ab, sizeof(Decimal));
            System::Buffer::BlockCopy(bits, 0, ab, 0, ab->Length);
            break;
        }
        case System::TypeCode::Double:
            ab = BitConverter::GetBytes((Double)oVal);
            break;
        case System::TypeCode::Int16:
            ab = BitConverter::GetBytes((Int16)oVal);
            break;
        case System::TypeCode::Int32:
            ab = BitConverter::GetBytes((Int32)oVal);
            break;
        case System::TypeCode::Int64:
            ab = BitConverter::GetBytes((Int64)oVal);
            break;
        case System::TypeCode::Object:
            break;
        case System::TypeCode::SByte:
            ab = BitConverter::GetBytes((SByte)oVal);
            break;
        case System::TypeCode::Single:
            ab = BitConverter::GetBytes((Single)oVal);
            break;
        case System::TypeCode::String:
        {
            String ^ str = Convert::ToString(oVal);
            char *pcSrc = (char*)(void*)Marshal::StringToHGlobalAnsi(str);
            char *pcDest = (char *)pz->zMap.pValue;
            for (int i = 0; i < min(str->Length, pz->zMap.nArySize - 1); i++, pcSrc++, pcDest++) {
                *pcDest = *pcSrc;
            }
            *pcDest = 0;
            break;
        }
        case System::TypeCode::UInt16:
            ab = BitConverter::GetBytes((UInt16)oVal);
            break;
        case System::TypeCode::UInt32:
            ab = BitConverter::GetBytes((UInt32)oVal);
            break;
        case System::TypeCode::UInt64:
            ab = BitConverter::GetBytes((UInt64)oVal);
            break;
        case System::TypeCode::Empty:
        case System::TypeCode::DBNull:
        default:
            goto AbortOnError;
        }
        if (ab && ab->Length) {
            pin_ptr<System::Byte> pp = &ab[0];
            char *p = (char *)pz->zMap.pValue;
            for (size_t i = 0; i < min((size_t)ab->Length, knSizeOf); i++, p++){
                *p = ab[i];
            }
            //memcpy(pz->zMap.pValue, (char*)pp, knSizeOf);
        }
        Debug::WriteLine(sColumnName->ToString()
            + "=" + oVal->ToString()
            + ", typ=" + typ->ToString());
    }
    return errStatus;
AbortOnError:
    if (VI_SUCCESS == errStatus) errStatus = mmGrasp_eErr_Unspecified;
    if (MM_pfnRunStatus) MM_pfnRunStatus("... mmDotNet_dtmControlLimitsToTags: Error");
    return errStatus;
} // ReaderToTag()

在这里使用BinaryFormatter是失败的。它不仅编写您传递给它的对象,还添加了描述对象的元数据。因此,它可以可靠地再次反序列化,重建完全相同的对象。你可以在你的垃圾场里清楚地看到这一点。它以一个头开始,上面写着"我是二进制序列化的数据",后面是对象类型的描述。在这里是int。"m_Value"是存储在盒装Int32对象中的值的字段。

确实知道对象类型,它是由数据库中的列设置的。这对于所有意图和目的来说都是固定的,更改它也会迫使您更改代码。将项目符号写入字节并转换为对象:

  int limit = safe_cast<int>(oVal);

但我猜你不想那样做,因为类型转换实际上存在于你的本地代码中。您可以继续使用反射,为oVal->GetType()编写switch()。现在可以在每个case语句中编写适当的类型转换,并使用BitConverter.GetBytes()来获得array<Byte>^。注意字符串,编码很重要。

从原始数据库行到安全托管对象的转换,再转换回原始数据并转换为本机值显然是无效的。您注定要提前提取代码并进行数据转换。希望你有一个与数据库行匹配的本地结构体,你也可以用c++/CLI填充它。