I am writing a c++ mixed mode DLL and converting objects read from a database to unmanaged code variables. I am trying to convert the object
into a byte array so I can copy its value byte by byte to unmanaged memory.
When I do the conversion I get more than the object's data in the byte array.
Here is an example of the code I have:
// 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];
}
// ...
}
The array ab
that is returned looks like a struct for an Int32
object, not just it's value.
I want to get a byte[]
with just the value of the object so that I can copy it byte by byte to the unmanaged data.
IF oVal
is Int32
and *oVal = 0x12345678
then here's a memory dump of 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..
The value for oVal
is on the last line following the fieldname m_value
.
Since oVal
is read from a database I can't be sure of its datatype at compile time.
How can I get the object's value, not the entire object data structure, into the byte[] ab?
=========================================================
UPDATE June 12, 2014
This is an update of old MFC code that uses DAO and I want to update the old 32 bit app to be a 64 bit app. To do that I have to rewrite the DAO code to use ADO.Net since DAO is only available in 32 bit.
I really wanted to avoid putting in a switch for memory copy but looks like I'm forced to live within the restrictions of .Net. The old MFC code let me use memcpy() to transfer the DAO recordset data to the appropriate variable. This is a straight forward way to update the data.
Using a switch statement seems unnecessary and complex. I was hoping to avoid it.
Does anyone have a better idea?
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()