BinaryFormatter выдает проблемы при конвертации внутренних объектов, таких как Int32 и Double

Я пишу DLL C ++ в смешанном режиме и преобразую объекты, считанные из базы данных, в неуправляемые переменные кода. Я пытаюсь преобразовать 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[] только со значением объекта, чтобы я мог копировать его побайтово в неуправляемые данные.

ЕСЛИ oVal является Int32 а также *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 читается из базы данных, я не могу быть уверен в ее типе данных во время компиляции.

Как я могу получить значение объекта, а не всю структуру данных объекта, в byte [] ab?

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

ОБНОВЛЕНИЕ 12 июня 2014

Это обновление старого кода MFC, использующего DAO, и я хочу обновить старое 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()

0

Решение

Использование BinaryFormatter — неудачный кит. Он не просто записывает объект, который вы передаете, он также добавляет метаданные, которые описывает предмет. Таким образом, он может быть снова надежно десериализован, восстанавливая точно такой же объект. Вы можете ясно видеть это в своей свалке. Он начинается с заголовка «Я бинарные сериализованные данные», за которым следует описание типа объекта. ИНТ в этом случае. «m_Value» — это поле, в котором хранится значение в упакованном объекте Int32.

Вы делать знать тип объекта, он задается по столбцу в базе данных. Что исправлено для всех намерений и целей, изменение его заставляет вас также изменить свой код. Так что байт пули и преобразовать объект:

  int limit = safe_cast<int>(oVal);

Но я думаю, вы не хотите этого делать, потому что преобразование типов действительно существует в вашем родном коде. Вы можете опередить отражение, напишите switch () для oVal-> GetType (). Теперь вы можете написать правильные приведения в каждом операторе case и использовать BitConverter.GetBytes () для получения array<Byte>^, Остерегайтесь строк, кодирования вопросов.

Преобразование из необработанной строки базы данных в безопасные управляемые объекты, обратно в необработанные данные и преобразование в исходные значения, очевидно, является непродуктивным. Вы немного обречены снять код и выполнить преобразование данных раньше. Надеюсь, у вас есть собственная структура, соответствующая строке dbase, вы также можете заполнить ее C ++ / CLI.

1

Другие решения


По вопросам рекламы [email protected]