internal void MarshalToNative(object value, ODBC32.SQL_C sqlctype, byte precision) { IntPtr buffer = this.Ptr; switch (sqlctype) { /* #if DEBUG * case ODBC32.SQL_C.CHAR: * Debug.Assert(false, "should have bound as SQL_C.WCHAR"); * goto default; #endif */ case ODBC32.SQL_C.WCHAR: { //Note: We always bind as unicode //Note: StructureToPtr fails indicating string it a non-blittable type //and there is no MarshalStringTo* that moves to an existing buffer, //they all alloc and return a new one, not at all what we want... //So we have to copy the raw bytes of the string ourself?! Char[] rgChars = ((string)value).ToCharArray(); EnsureAlloc((rgChars.Length + 1) * 2); buffer = this.Ptr; // Realloc may have changed buffer address Marshal.Copy(rgChars, 0, buffer, rgChars.Length); Marshal.WriteInt16(buffer, rgChars.Length * 2, 0); // Add the null terminator break; } case ODBC32.SQL_C.BINARY: case ODBC32.SQL_C.CHAR: { Byte[] rgBytes = (Byte[])value; EnsureAlloc(rgBytes.Length + 1); buffer = this.Ptr; // Realloc may have changed buffer address Marshal.Copy(rgBytes, 0, buffer, rgBytes.Length); break; } case ODBC32.SQL_C.UTINYINT: Debug.Assert((Length >= 1), "Native buffer too small "); Marshal.WriteByte(buffer, (Byte)value); break; case ODBC32.SQL_C.SSHORT: //Int16 Debug.Assert((Length >= 2), "Native buffer too small "); Marshal.WriteInt16(buffer, (Int16)value); break; case ODBC32.SQL_C.SLONG: //Int32 case ODBC32.SQL_C.REAL: //float Debug.Assert((Length >= 4), "Native buffer too small "); Marshal.StructureToPtr(value, buffer, false /*deleteold*/); break; case ODBC32.SQL_C.SBIGINT: //Int64 case ODBC32.SQL_C.DOUBLE: //Double Debug.Assert((Length >= 8), "Native buffer too small "); Marshal.StructureToPtr(value, buffer, false /*deleteold*/); break; case ODBC32.SQL_C.GUID: //Guid //All of these we can just delegate Debug.Assert(16 <= Length, "Native buffer too small "); Marshal.StructureToPtr(value, buffer, false /*deleteold*/); break; case ODBC32.SQL_C.BIT: Debug.Assert((Length >= 1), "Native buffer too small "); Marshal.WriteByte(buffer, (Byte)(((bool)value) ? 1 : 0)); break; case ODBC32.SQL_C.TYPE_TIMESTAMP: { //typedef struct tagTIMESTAMP_STRUCT //{ // SQLSMALLINT year; // SQLUSMALLINT month; // SQLUSMALLINT day; // SQLUSMALLINT hour; // SQLUSMALLINT minute; // SQLUSMALLINT second; // SQLUINTEGER fraction; (billoniths of a second) //} //We have to map this ourselves, due to the different structures between //ODBC TIMESTAMP and URT DateTime, (ie: can't use StructureToPtr) Debug.Assert(16 <= Length, "Native buffer too small "); DateTime datetime = (DateTime)value; Marshal.WriteInt16(buffer, 0, (short)datetime.Year); //year Marshal.WriteInt16(buffer, 2, (short)datetime.Month); //month Marshal.WriteInt16(buffer, 4, (short)datetime.Day); //day Marshal.WriteInt16(buffer, 6, (short)datetime.Hour); //hour Marshal.WriteInt16(buffer, 8, (short)datetime.Minute); //minute Marshal.WriteInt16(buffer, 10, (short)datetime.Second); //second Marshal.WriteInt32(buffer, 12, datetime.Millisecond * 1000000); //fraction break; } // Note: System does not provide a date-only type case ODBC32.SQL_C.TYPE_DATE: { // typedef struct tagDATE_STRUCT // { // SQLSMALLINT year; // SQLUSMALLINT month; // SQLUSMALLINT day; // } DATE_STRUCT; Debug.Assert(6 <= Length, "Native buffer too small "); DateTime datetime = (DateTime)value; Marshal.WriteInt16(buffer, 0, (short)datetime.Year); //year Marshal.WriteInt16(buffer, 2, (short)datetime.Month); //month Marshal.WriteInt16(buffer, 4, (short)datetime.Day); //day break; } // Note: System does not provide a date-only type case ODBC32.SQL_C.TYPE_TIME: { // typedef struct tagTIME_STRUCT // { // SQLUSMALLINT hour; // SQLUSMALLINT minute; // SQLUSMALLINT second; // } TIME_STRUCT; Debug.Assert(6 <= Length, "Native buffer too small "); TimeSpan timespan = (TimeSpan)value; Marshal.WriteInt16(buffer, 0, (short)timespan.Hours); //hours Marshal.WriteInt16(buffer, 2, (short)timespan.Minutes); //minutes Marshal.WriteInt16(buffer, 4, (short)timespan.Seconds); //seconds break; } case ODBC32.SQL_C.NUMERIC: { //Note: Unfortunatly the ODBC NUMERIC structure and the URT DECIMAL structure do not //align, so we can't so do the typical "PtrToStructure" call (below) like other types //We actually have to go through the pain of pulling our raw bytes and building the decimal // Marshal.PtrToStructure(buffer, typeof(decimal)); //So we are mapping this ourselves //typedef struct tagSQL_NUMERIC_STRUCT //{ // SQLCHAR precision; // SQLSCHAR scale; // SQLCHAR sign; // SQLCHAR val[SQL_MAX_NUMERIC_LEN]; //} SQL_NUMERIC_STRUCT; int[] parts = Decimal.GetBits((Decimal)value); byte[] bits = BitConverter.GetBytes(parts[3]); Debug.Assert(19 <= Length, "Native buffer too small "); Marshal.WriteByte(buffer, 0, (Byte)precision); //precision Marshal.WriteByte(buffer, 1, bits[2]); //Bits 16-23 scale Marshal.WriteByte(buffer, 2, (Byte)((0 == bits[3]) ? 1 : 0)); //Bit 31 - sign(isnegative) Marshal.WriteInt32(buffer, 3, parts[0]); //val(low) Marshal.WriteInt32(buffer, 7, parts[1]); //val(mid) Marshal.WriteInt32(buffer, 11, parts[2]); //val(hi) Marshal.WriteInt32(buffer, 15, 0); //val(xhi) break; } default: throw ODC.UnknownSQLCType(sqlctype); } ; }
public object MarshalToManaged(ODBC32.SQL_C sqlctype, int cb) { IntPtr buffer = this.Ptr; switch (sqlctype) { /* #if DEBUG * case ODBC32.SQL_C.CHAR: * Debug.Assert(false, "should have bound as WCHAR"); * goto default; #endif */ case ODBC32.SQL_C.WCHAR: //Note: We always bind as unicode if (cb < 0) { String retstr = Marshal.PtrToStringUni(buffer); Debug.Assert(retstr.Length <= this.Length / 2); return(retstr); } Debug.Assert((Length >= cb), "Native buffer too small "); cb = Math.Min(cb / 2, (Length - 2) / 2); return(Marshal.PtrToStringUni(buffer, cb)); case ODBC32.SQL_C.CHAR: case ODBC32.SQL_C.BINARY: { Debug.Assert((Length >= cb), "Native buffer too small "); cb = Math.Min(cb, Length); Byte[] rgBytes = new Byte[cb]; Marshal.Copy(buffer, rgBytes, 0, cb); return(rgBytes); } case ODBC32.SQL_C.SSHORT: Debug.Assert((Length >= 2), "Native buffer too small "); return(Marshal.PtrToStructure(buffer, typeof(Int16))); case ODBC32.SQL_C.SLONG: Debug.Assert((Length >= 4), "Native buffer too small "); return(Marshal.PtrToStructure(buffer, typeof(int))); case ODBC32.SQL_C.SBIGINT: Debug.Assert((Length >= 8), "Native buffer too small "); return(Marshal.PtrToStructure(buffer, typeof(Int64))); case ODBC32.SQL_C.BIT: { Debug.Assert((Length >= 1), "Native buffer too small "); Byte b = Marshal.ReadByte(buffer); return(b != 0x00); } case ODBC32.SQL_C.REAL: Debug.Assert((Length >= 8), "Native buffer too small "); return(Marshal.PtrToStructure(buffer, typeof(float))); case ODBC32.SQL_C.DOUBLE: Debug.Assert((Length >= 8), "Native buffer too small "); return(Marshal.PtrToStructure(buffer, typeof(double))); case ODBC32.SQL_C.UTINYINT: Debug.Assert((Length >= 1), "Native buffer too small "); return(Marshal.ReadByte(buffer)); case ODBC32.SQL_C.GUID: Debug.Assert((Length >= 16), "Native buffer too small "); return(Marshal.PtrToStructure(buffer, typeof(Guid))); case ODBC32.SQL_C.TYPE_TIMESTAMP: { //So we are mapping this ourselves. //typedef struct tagTIMESTAMP_STRUCT //{ // SQLSMALLINT year; // SQLUSMALLINT month; // SQLUSMALLINT day; // SQLUSMALLINT hour; // SQLUSMALLINT minute; // SQLUSMALLINT second; // SQLUINTEGER fraction; (billoniths of a second) //} // return (DateTime)Marshal.PtrToStructure(buffer, typeof(DateTime)); Debug.Assert(16 <= Length, "Native buffer too small "); return(new DateTime( Marshal.ReadInt16(buffer, 0), //year Marshal.ReadInt16(buffer, 2), //month Marshal.ReadInt16(buffer, 4), //day Marshal.ReadInt16(buffer, 6), //hour Marshal.ReadInt16(buffer, 8), //mintue Marshal.ReadInt16(buffer, 10), //second Marshal.ReadInt32(buffer, 12) / 1000000 //milliseconds )); } // Note: System does not provide a date-only type case ODBC32.SQL_C.TYPE_DATE: { // typedef struct tagDATE_STRUCT // { // SQLSMALLINT year; // SQLUSMALLINT month; // SQLUSMALLINT day; // } DATE_STRUCT; return(new DateTime( Marshal.ReadInt16(buffer, 0), //year Marshal.ReadInt16(buffer, 2), //month Marshal.ReadInt16(buffer, 4) //day )); } // Note: System does not provide a date-only type case ODBC32.SQL_C.TYPE_TIME: { // typedef struct tagTIME_STRUCT // { // SQLUSMALLINT hour; // SQLUSMALLINT minute; // SQLUSMALLINT second; // } TIME_STRUCT; return(new TimeSpan( Marshal.ReadInt16(buffer, 0), //hours Marshal.ReadInt16(buffer, 2), //minutes Marshal.ReadInt16(buffer, 4) //seconds )); } case ODBC32.SQL_C.NUMERIC: { //Note: Unfortunatly the ODBC NUMERIC structure and the URT DECIMAL structure do not //align, so we can't so do the typical "PtrToStructure" call (below) like other types //We actually have to go through the pain of pulling our raw bytes and building the decimal // Marshal.PtrToStructure(buffer, typeof(decimal)); //So we are mapping this ourselves //typedef struct tagSQL_NUMERIC_STRUCT //{ // SQLCHAR precision; // SQLSCHAR scale; // SQLCHAR sign; /* 1 if positive, 0 if negative */ // SQLCHAR val[SQL_MAX_NUMERIC_LEN]; //} SQL_NUMERIC_STRUCT; Debug.Assert(19 <= Length, "Native buffer too small "); return(new Decimal( Marshal.ReadInt32(buffer, 3), //val(low) Marshal.ReadInt32(buffer, 7), //val(mid) Marshal.ReadInt32(buffer, 11), //val(hi) Marshal.ReadByte(buffer, 2) == 0, //sign(isnegative) Marshal.ReadByte(buffer, 1) //scale // Marshal.ReadByte(buffer, 0), //precision )); } default: throw ODC.UnknownSQLCType(sqlctype); } ; }