RowBinding.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / whidbey / netfxsp / ndp / fx / src / Data / System / Data / OleDb / RowBinding.cs / 1 / RowBinding.cs

                            //------------------------------------------------------------------------------ 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// [....] 
// [....]
//----------------------------------------------------------------------------- 
 
namespace System.Data.OleDb {
 
    using System;
    using System.ComponentModel;
    using System.Diagnostics;
    using System.Data; 
    using System.Data.Common;
    using System.Globalization; 
    using System.Runtime.CompilerServices; 
    using System.Runtime.InteropServices;
    using System.Security; 
    using System.Security.Permissions;
    using System.Text;
    using System.Threading;
    using System.Runtime.ConstrainedExecution; 

    sealed internal class RowBinding : System.Data.ProviderBase.DbBuffer { 
        private readonly int _bindingCount; 
        private readonly int _headerLength;
        private readonly int _dataLength; 
        private readonly int _emptyStringOffset;

        private UnsafeNativeMethods.IAccessor _iaccessor;
        private IntPtr _accessorHandle; 

        private readonly bool _needToReset; 
        private bool _haveData; 

        // tagDBBINDING[] starting 64bit aligned 
        // all DBSTATUS values (32bit per value), starting 64bit aligned
        // all DBLENGTH values (32/64bit per value), starting 64bit alignedsa
        // all data values listed after that (variable length), each individual starting 64bit aligned
        // Int64 - zero for pointers to emptystring 

        internal static RowBinding CreateBuffer(int bindingCount, int databuffersize, bool needToReset) { 
            int headerLength = RowBinding.AlignDataSize(bindingCount * ODB.SizeOf_tagDBBINDING); 
            int length = RowBinding.AlignDataSize(headerLength + databuffersize) + 8; // 8 bytes for a null terminated string
            return new RowBinding(bindingCount, headerLength, databuffersize, length, needToReset); 
        }

        private RowBinding(int bindingCount, int headerLength, int dataLength, int length, bool needToReset) : base(length) {
            _bindingCount = bindingCount; 
            _headerLength = headerLength;
            _dataLength = dataLength; 
            _emptyStringOffset = length - 8; // 8 bytes for a null terminated string 
            _needToReset = needToReset;
 
            Debug.Assert(0 < _bindingCount, "bad _bindingCount");
            Debug.Assert(0 < _headerLength, "bad _headerLength");
            Debug.Assert(0 < _dataLength, "bad _dataLength");
            Debug.Assert(_bindingCount * 3 * IntPtr.Size <= _dataLength, "_dataLength too small"); 
            Debug.Assert(_headerLength + _dataLength <= _emptyStringOffset, "bad string offset");
            Debug.Assert(_headerLength + _dataLength + 8 <= length, "bad length"); 
        } 

        internal void StartDataBlock() { 
            if (_haveData) {
                Debug.Assert(false, "previous row not yet cleared");
                ResetValues();
            } 
            _haveData = true;
        } 
 
        internal int BindingCount() {
            return _bindingCount; 
        }

        internal IntPtr DangerousGetAccessorHandle() {
            return _accessorHandle; 
        }
 
        internal IntPtr DangerousGetDataPtr() { 
            //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            // NOTE: You must have called DangerousAddRef before calling this 
            //       method, or you run the risk of allowing Handle Recycling
            //       to occur!
            //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            return ADP.IntPtrOffset(DangerousGetHandle(), _headerLength); 
        }
 
        internal IntPtr DangerousGetDataPtr(int valueOffset) { 
            //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            // NOTE: You must have called DangerousAddRef before calling this 
            //       method, or you run the risk of allowing Handle Recycling
            //       to occur!
            //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            return ADP.IntPtrOffset(DangerousGetHandle(), valueOffset); 
        }
 
        internal OleDbHResult CreateAccessor(UnsafeNativeMethods.IAccessor iaccessor, int flags, ColumnBinding[] bindings) { 
            OleDbHResult hr = 0;
            int[] rowBindStatus = new int[BindingCount()]; 

            _iaccessor = iaccessor;

            Bid.Trace("\n"); 
            hr = iaccessor.CreateAccessor(flags, (IntPtr)rowBindStatus.Length, this, (IntPtr)_dataLength, out _accessorHandle, rowBindStatus); // MDAC 69530
            Bid.Trace(" %08X{HRESULT}\n", hr); 
 
            for (int k = 0; k < rowBindStatus.Length; ++k) {
                if (DBBindStatus.OK != (DBBindStatus)rowBindStatus[k]) { 
                    if (ODB.DBACCESSOR_PARAMETERDATA == flags) {
                        throw ODB.BadStatus_ParamAcc(bindings[k].ColumnBindingOrdinal, (DBBindStatus)rowBindStatus[k]);
                    }
                    else if (ODB.DBACCESSOR_ROWDATA == flags) { 
                        throw ODB.BadStatusRowAccessor(bindings[k].ColumnBindingOrdinal, (DBBindStatus)rowBindStatus[k]);
                    } 
                    else Debug.Assert(false, "unknown accessor buffer"); 
                }
            } 
            return hr;
        }

        internal ColumnBinding[] SetBindings(OleDbDataReader dataReader, Bindings bindings, 
                                             int indexStart, int indexForAccessor,
                                             OleDbParameter[] parameters, tagDBBINDING[] dbbindings, bool ifIRowsetElseIRow) { 
            Debug.Assert(null != bindings, "null bindings"); 
            Debug.Assert(dbbindings.Length == BindingCount(), "count mismatch");
 
            bool mustRelease = false;

            RuntimeHelpers.PrepareConstrainedRegions();
            try { 
                DangerousAddRef(ref mustRelease);
 
                IntPtr buffer = DangerousGetHandle(); 
                for(int i = 0; i < dbbindings.Length; ++i) {
                    IntPtr ptr = ADP.IntPtrOffset(buffer, (i * ODB.SizeOf_tagDBBINDING)); 
                    Marshal.StructureToPtr(dbbindings[i], ptr, false/*deleteold*/);
                }
            }
            finally { 
                if (mustRelease) {
                    DangerousRelease(); 
                } 
            }
 
            ColumnBinding[] columns = new ColumnBinding[dbbindings.Length];
            for(int indexWithinAccessor = 0; indexWithinAccessor < columns.Length; ++indexWithinAccessor) {
                int index = indexStart + indexWithinAccessor;
                OleDbParameter parameter = ((null != parameters) ? parameters[index] : null); 
                columns[indexWithinAccessor] = new ColumnBinding(
                    dataReader, index, indexForAccessor, indexWithinAccessor, 
                    parameter, this, bindings, dbbindings[indexWithinAccessor], _headerLength, 
                    ifIRowsetElseIRow);
            } 
            return columns;
        }

        static internal int AlignDataSize(int value) { 
            // buffer data to start on 8-byte boundary
            return Math.Max(8, (value + 7) & ~0x7); // MDAC 70350 
        } 

        internal object GetVariantValue(int offset) { 
            Debug.Assert(_needToReset, "data type requires reseting and _needToReset is false");
            Debug.Assert(0 == (ODB.SizeOf_Variant % 8), "unexpected VARIANT size mutiplier");
            Debug.Assert(0 == offset%8, "invalid alignment");
            ValidateCheck(offset, 2*ODB.SizeOf_Variant); 

            object value = null; 
            bool mustRelease = false; 
            RuntimeHelpers.PrepareConstrainedRegions();
            try { 
                DangerousAddRef(ref mustRelease);

                IntPtr buffer = ADP.IntPtrOffset(DangerousGetHandle(), offset);
                value = Marshal.GetObjectForNativeVariant(buffer); 
            }
            finally { 
                if (mustRelease) { 
                    DangerousRelease();
                } 
            }

            return ((null != value) ? value : DBNull.Value);
        } 

        // translate to native 
        internal void SetVariantValue(int offset, object value) { 
            // two contigous VARIANT structures, second should be a binary copy of the first
            Debug.Assert(_needToReset, "data type requires reseting and _needToReset is false"); 
            Debug.Assert(0 == (ODB.SizeOf_Variant % 8), "unexpected VARIANT size mutiplier");
            Debug.Assert(0 == offset%8, "invalid alignment");
            ValidateCheck(offset, 2*ODB.SizeOf_Variant);
 
            IntPtr buffer = ADP.PtrZero;
            bool mustRelease = false; 
            RuntimeHelpers.PrepareConstrainedRegions(); 
            try {
                DangerousAddRef(ref mustRelease); 

                buffer = ADP.IntPtrOffset(DangerousGetHandle(), offset);

                RuntimeHelpers.PrepareConstrainedRegions(); 
                try {
                    // GetNativeVariantForObject must be in try block since it has no reliability contract 
                    Marshal.GetNativeVariantForObject(value, buffer); 
                }
                finally { 
                    // safe to copy memory(dst,src,count), even if GetNativeVariantForObject failed
                    NativeOledbWrapper.MemoryCopy(ADP.IntPtrOffset(buffer,ODB.SizeOf_Variant), buffer, ODB.SizeOf_Variant);
                }
            } 
            finally {
                if (mustRelease) { 
                    DangerousRelease(); 
                }
            } 
        }

        // value
        // cached value 
        // cached zero value
        // translate to native 
        internal void SetBstrValue(int offset, string value) { 
            // two contigous BSTR ptr, second should be a binary copy of the first
            Debug.Assert(_needToReset, "data type requires reseting and _needToReset is false"); 
            Debug.Assert(0 == offset%ADP.PtrSize, "invalid alignment");
            ValidateCheck(offset, 2*IntPtr.Size);

            IntPtr ptr; 
            bool mustRelease = false;
            RuntimeHelpers.PrepareConstrainedRegions(); 
            try { 
                DangerousAddRef(ref mustRelease);
 
                RuntimeHelpers.PrepareConstrainedRegions();
                try { } finally {
                    ptr = SafeNativeMethods.SysAllocStringLen(value, value.Length);
 
                    // safe to copy ptr, even if SysAllocStringLen failed
                    Marshal.WriteIntPtr(base.handle, offset, ptr); 
                    Marshal.WriteIntPtr(base.handle, offset + ADP.PtrSize, ptr); 
                }
            } 
            finally {
                if (mustRelease) {
                    DangerousRelease();
                } 
            }
            if (IntPtr.Zero == ptr) { 
                throw new OutOfMemoryException(); 
            }
        } 

        // translate to native
        internal void SetByRefValue(int offset, IntPtr pinnedValue) {
            Debug.Assert(_needToReset, "data type requires reseting and _needToReset is false"); 
            Debug.Assert(0 == offset%ADP.PtrSize, "invalid alignment");
            ValidateCheck(offset, 2*IntPtr.Size); 
 
            if (ADP.PtrZero == pinnedValue) { // empty array scenario
                pinnedValue = ADP.IntPtrOffset(base.handle, _emptyStringOffset); 
            }
            bool mustRelease = false;
            RuntimeHelpers.PrepareConstrainedRegions();
            try { 
                DangerousAddRef(ref mustRelease);
 
                RuntimeHelpers.PrepareConstrainedRegions(); 
                try { } finally {
                    Marshal.WriteIntPtr(base.handle, offset, pinnedValue);               // parameter input value 
                    Marshal.WriteIntPtr(base.handle, offset + ADP.PtrSize, pinnedValue); // original parameter value
                }
            }
            finally { 
                if (mustRelease) {
                    DangerousRelease(); 
                } 
            }
        } 

        internal void CloseFromConnection() {
            _iaccessor = null;
            _accessorHandle = ODB.DB_INVALID_HACCESSOR; 
        }
 
        internal new void Dispose() { 
            ResetValues();
 
            UnsafeNativeMethods.IAccessor iaccessor = _iaccessor;
            IntPtr accessorHandle = _accessorHandle;

            _iaccessor = null; 
            _accessorHandle = ODB.DB_INVALID_HACCESSOR;
 
            if ((ODB.DB_INVALID_HACCESSOR != accessorHandle) && (null != iaccessor)) { 
                OleDbHResult hr;
                int refcount; 
                hr = iaccessor.ReleaseAccessor(accessorHandle, out refcount);
                if (hr < 0) { // ignore any error msgs
                    SafeNativeMethods.Wrapper.ClearErrorInfo();
                } 
            }
 
            base.Dispose(); 
        }
 
        internal void ResetValues() {
            if (_needToReset && _haveData) {
                lock(this) { // prevent Dispose/ResetValues race condition
 
                    bool mustRelease = false;
                    RuntimeHelpers.PrepareConstrainedRegions(); 
                    try { 
                        DangerousAddRef(ref mustRelease);
 
                        ResetValues(DangerousGetHandle(), _iaccessor);
                    }
                    finally {
                        if (mustRelease) { 
                            DangerousRelease();
                        } 
                    } 
                }
            } 
            else {
                _haveData = false;
            }
#if DEBUG 
            // verify types that need reseting are not forgotton, since the code
            // that sets this up is in dbbinding.cs, MaxLen { set; } 
            if (!_needToReset) { 
                Debug.Assert(0 <= _bindingCount && (_bindingCount * ODB.SizeOf_tagDBBINDING) < Length, "bad _bindingCount");
                for (int i = 0; i < _bindingCount; ++i) { 
                    short wtype = ReadInt16((i * ODB.SizeOf_tagDBBINDING) + ODB.OffsetOf_tagDBBINDING_wType);
                    switch(wtype) {
                    case (NativeDBType.BYREF | NativeDBType.BYTES):
                    case (NativeDBType.BYREF | NativeDBType.WSTR): 
                    case NativeDBType.PROPVARIANT:
                    case NativeDBType.VARIANT: 
                    case NativeDBType.BSTR: 
                    case NativeDBType.HCHAPTER:
                        Debug.Assert(false, "expected _needToReset"); 
                        break;
                    }
                }
            } 
#endif
        } 
 
        // requires ReliabilityContract to be called by ReleaseHandle
        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] 
        private void ResetValues(IntPtr buffer, object iaccessor) {
            Debug.Assert(ADP.PtrZero != buffer && _needToReset && _haveData, "shouldn't be calling ResetValues");
            for (int i = 0; i < _bindingCount; ++i) {
                IntPtr ptr = ADP.IntPtrOffset(buffer, (i * ODB.SizeOf_tagDBBINDING)); 

                int valueOffset = _headerLength + Marshal.ReadIntPtr(ptr, ODB.OffsetOf_tagDBBINDING_obValue).ToInt32(); 
                short wtype = Marshal.ReadInt16(ptr, ODB.OffsetOf_tagDBBINDING_wType); 

                switch(wtype) { 
                case (NativeDBType.BYREF | NativeDBType.BYTES):
                case (NativeDBType.BYREF | NativeDBType.WSTR):
                    ValidateCheck(valueOffset, 2*IntPtr.Size);
                    FreeCoTaskMem(buffer, valueOffset); 
                    break;
                case NativeDBType.PROPVARIANT: 
                    ValidateCheck(valueOffset, 2*NativeOledbWrapper.SizeOfPROPVARIANT); 
                    FreePropVariant(buffer, valueOffset);
                    break; 
                case NativeDBType.VARIANT:
                    ValidateCheck(valueOffset, 2*ODB.SizeOf_Variant);
                    FreeVariant(buffer, valueOffset);
                    break; 
                case NativeDBType.BSTR:
                    ValidateCheck(valueOffset, 2*IntPtr.Size); 
                    FreeBstr(buffer, valueOffset); 
                    break;
                case NativeDBType.HCHAPTER: 
                    if (null != iaccessor) {
                        // iaccessor will always be null when from ReleaseHandle
                        FreeChapter(buffer, valueOffset, iaccessor);
                    } 
                    break;
#if DEBUG 
 
                case NativeDBType.EMPTY:
                case NativeDBType.NULL: 
                case NativeDBType.I2:
                case NativeDBType.I4:
                case NativeDBType.R4:
                case NativeDBType.R8: 
                case NativeDBType.CY:
                case NativeDBType.DATE: 
                case NativeDBType.ERROR: 
                case NativeDBType.BOOL:
                case NativeDBType.DECIMAL: 
                case NativeDBType.I1:
                case NativeDBType.UI1:
                case NativeDBType.UI2:
                case NativeDBType.UI4: 
                case NativeDBType.I8:
                case NativeDBType.UI8: 
                case NativeDBType.FILETIME: 
                case NativeDBType.GUID:
                case NativeDBType.BYTES: 
                case NativeDBType.WSTR:
                case NativeDBType.NUMERIC:
                case NativeDBType.DBDATE:
                case NativeDBType.DBTIME: 
                case NativeDBType.DBTIMESTAMP:
                    break; // known, do nothing 
                case NativeDBType.IDISPATCH: 
                case NativeDBType.IUNKNOWN:
                    break; // known, releasing RowHandle will handle lifetimes correctly 
                default:
                    Debug.Assert(false, "investigate");
                    break;
#endif 
                }
            } 
            _haveData = false; 
        }
 
        // this correctly does not have a ReliabilityContract, will not be called via ReleaseHandle
        static private void FreeChapter(IntPtr buffer, int valueOffset, object iaccessor) {
            Debug.Assert (0 == valueOffset % 8, "unexpected unaligned ptr offset");
 
            UnsafeNativeMethods.IChapteredRowset chapteredRowset = (iaccessor as UnsafeNativeMethods.IChapteredRowset);
            IntPtr chapter = SafeNativeMethods.InterlockedExchangePointer(ADP.IntPtrOffset(buffer, valueOffset), ADP.PtrZero); 
            if (ODB.DB_NULL_HCHAPTER != chapter) { 
                int refCount;
                Bid.Trace(" Chapter=%Id\n", chapter); 
                OleDbHResult hr = chapteredRowset.ReleaseChapter(chapter, out refCount);
                Bid.Trace(" %08X{HRESULT}, RefCount=%d\n", hr, refCount);
            }
        } 

        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] 
        static private void FreeBstr(IntPtr buffer, int valueOffset) { 
            Debug.Assert (0 == valueOffset % 8, "unexpected unaligned ptr offset");
 
            // two contigous BSTR ptrs that need to be freed
            // the second should only be freed if different from the first
            RuntimeHelpers.PrepareConstrainedRegions();
            try {} finally { 
                IntPtr currentValue = Marshal.ReadIntPtr(buffer, valueOffset);
                IntPtr originalValue = Marshal.ReadIntPtr(buffer, valueOffset + ADP.PtrSize); 
 
                if ((ADP.PtrZero != currentValue) && (currentValue != originalValue)) {
                    SafeNativeMethods.SysFreeString(currentValue); 
                }
                if (ADP.PtrZero != originalValue) {
                    SafeNativeMethods.SysFreeString(originalValue);
                } 

                // for debugability - delay clearing memory until after FreeBSTR 
                Marshal.WriteIntPtr(buffer, valueOffset, ADP.PtrZero); 
                Marshal.WriteIntPtr(buffer, valueOffset + ADP.PtrSize, ADP.PtrZero);
            } 
        }

        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
        static private void FreeCoTaskMem(IntPtr buffer, int valueOffset) { 
            Debug.Assert (0 == valueOffset % 8, "unexpected unaligned ptr offset");
 
            // two contigous CoTaskMemAlloc ptrs that need to be freed 
            // the first should only be freed if different from the first
            RuntimeHelpers.PrepareConstrainedRegions(); 
            try {} finally {
                IntPtr currentValue = Marshal.ReadIntPtr(buffer, valueOffset);
                IntPtr originalValue = Marshal.ReadIntPtr(buffer, valueOffset + ADP.PtrSize);
 
                // originalValue is pinned managed memory or pointer to emptyStringOffset
                if ((ADP.PtrZero != currentValue) && (currentValue != originalValue)) { 
                    SafeNativeMethods.CoTaskMemFree(currentValue); 
                }
 
                // for debugability - delay clearing memory until after CoTaskMemFree
                Marshal.WriteIntPtr(buffer, valueOffset, ADP.PtrZero);
                Marshal.WriteIntPtr(buffer, valueOffset + ADP.PtrSize, ADP.PtrZero);
            } 
        }
 
        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] 
        static private void FreeVariant(IntPtr buffer, int valueOffset) {
            // two contigous VARIANT structures that need to be freed 
            // the second should only be freed if different from the first

            Debug.Assert(0 == (ODB.SizeOf_Variant % 8), "unexpected VARIANT size mutiplier");
            Debug.Assert (0 == valueOffset % 8, "unexpected unaligned ptr offset"); 

            IntPtr currentHandle = ADP.IntPtrOffset(buffer, valueOffset); 
            IntPtr originalHandle = ADP.IntPtrOffset(buffer, valueOffset+ODB.SizeOf_Variant); 
            bool different = NativeOledbWrapper.MemoryCompare(currentHandle, originalHandle, ODB.SizeOf_Variant);
 
            RuntimeHelpers.PrepareConstrainedRegions();
            try {} finally {
                // always clear the first structure
                SafeNativeMethods.VariantClear(currentHandle); 
                if (different) {
                    // second structure different from the first 
                    SafeNativeMethods.VariantClear(originalHandle); 
                }
                else { 
                    // second structure same as the first, just clear the field
                    SafeNativeMethods.ZeroMemory(originalHandle, (IntPtr)ODB.SizeOf_Variant);
                }
            } 
        }
 
        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] 
        static private void FreePropVariant(IntPtr buffer, int valueOffset) {
            // two contigous PROPVARIANT structures that need to be freed 
            // the second should only be freed if different from the first
            Debug.Assert(0 == (NativeOledbWrapper.SizeOfPROPVARIANT % 8), "unexpected PROPVARIANT size mutiplier");
            Debug.Assert (0 == valueOffset % 8, "unexpected unaligned ptr offset");
 
            IntPtr currentHandle = ADP.IntPtrOffset(buffer, valueOffset);
            IntPtr originalHandle = ADP.IntPtrOffset(buffer, valueOffset+NativeOledbWrapper.SizeOfPROPVARIANT); 
            bool different = NativeOledbWrapper.MemoryCompare(currentHandle, originalHandle, NativeOledbWrapper.SizeOfPROPVARIANT); 

            RuntimeHelpers.PrepareConstrainedRegions(); 
            try {} finally {
                // always clear the first structure
                SafeNativeMethods.PropVariantClear(currentHandle);
                if (different) { 
                    // second structure different from the first
                    SafeNativeMethods.PropVariantClear(originalHandle); 
                } 
                else {
                    // second structure same as the first, just clear the field 
                    SafeNativeMethods.ZeroMemory(originalHandle, (IntPtr)NativeOledbWrapper.SizeOfPROPVARIANT);
                }
            }
        } 

        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] 
        internal IntPtr InterlockedExchangePointer(int offset) { 
            ValidateCheck(offset, IntPtr.Size);
            Debug.Assert(0 == offset%ADP.PtrSize, "invalid alignment"); 

            IntPtr value;
            bool   mustRelease = false;
            RuntimeHelpers.PrepareConstrainedRegions(); 
            try {
                DangerousAddRef(ref mustRelease); 
 
                IntPtr ptr = ADP.IntPtrOffset(DangerousGetHandle(), offset);
                value = SafeNativeMethods.InterlockedExchangePointer(ptr, IntPtr.Zero); 
            }
            finally {
                if (mustRelease) {
                    DangerousRelease(); 
                }
            } 
            return value; 
        }
 
        override protected bool ReleaseHandle() {
            // NOTE: The SafeHandle class guarantees this will be called exactly once.
            _iaccessor = null;
            if (_needToReset && _haveData) { 
                IntPtr buffer = base.handle;
                if (IntPtr.Zero != buffer) { 
                    ResetValues(buffer, null); 
                }
            } 
            return base.ReleaseHandle();
        }
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//------------------------------------------------------------------------------ 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// [....] 
// [....]
//----------------------------------------------------------------------------- 
 
namespace System.Data.OleDb {
 
    using System;
    using System.ComponentModel;
    using System.Diagnostics;
    using System.Data; 
    using System.Data.Common;
    using System.Globalization; 
    using System.Runtime.CompilerServices; 
    using System.Runtime.InteropServices;
    using System.Security; 
    using System.Security.Permissions;
    using System.Text;
    using System.Threading;
    using System.Runtime.ConstrainedExecution; 

    sealed internal class RowBinding : System.Data.ProviderBase.DbBuffer { 
        private readonly int _bindingCount; 
        private readonly int _headerLength;
        private readonly int _dataLength; 
        private readonly int _emptyStringOffset;

        private UnsafeNativeMethods.IAccessor _iaccessor;
        private IntPtr _accessorHandle; 

        private readonly bool _needToReset; 
        private bool _haveData; 

        // tagDBBINDING[] starting 64bit aligned 
        // all DBSTATUS values (32bit per value), starting 64bit aligned
        // all DBLENGTH values (32/64bit per value), starting 64bit alignedsa
        // all data values listed after that (variable length), each individual starting 64bit aligned
        // Int64 - zero for pointers to emptystring 

        internal static RowBinding CreateBuffer(int bindingCount, int databuffersize, bool needToReset) { 
            int headerLength = RowBinding.AlignDataSize(bindingCount * ODB.SizeOf_tagDBBINDING); 
            int length = RowBinding.AlignDataSize(headerLength + databuffersize) + 8; // 8 bytes for a null terminated string
            return new RowBinding(bindingCount, headerLength, databuffersize, length, needToReset); 
        }

        private RowBinding(int bindingCount, int headerLength, int dataLength, int length, bool needToReset) : base(length) {
            _bindingCount = bindingCount; 
            _headerLength = headerLength;
            _dataLength = dataLength; 
            _emptyStringOffset = length - 8; // 8 bytes for a null terminated string 
            _needToReset = needToReset;
 
            Debug.Assert(0 < _bindingCount, "bad _bindingCount");
            Debug.Assert(0 < _headerLength, "bad _headerLength");
            Debug.Assert(0 < _dataLength, "bad _dataLength");
            Debug.Assert(_bindingCount * 3 * IntPtr.Size <= _dataLength, "_dataLength too small"); 
            Debug.Assert(_headerLength + _dataLength <= _emptyStringOffset, "bad string offset");
            Debug.Assert(_headerLength + _dataLength + 8 <= length, "bad length"); 
        } 

        internal void StartDataBlock() { 
            if (_haveData) {
                Debug.Assert(false, "previous row not yet cleared");
                ResetValues();
            } 
            _haveData = true;
        } 
 
        internal int BindingCount() {
            return _bindingCount; 
        }

        internal IntPtr DangerousGetAccessorHandle() {
            return _accessorHandle; 
        }
 
        internal IntPtr DangerousGetDataPtr() { 
            //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            // NOTE: You must have called DangerousAddRef before calling this 
            //       method, or you run the risk of allowing Handle Recycling
            //       to occur!
            //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            return ADP.IntPtrOffset(DangerousGetHandle(), _headerLength); 
        }
 
        internal IntPtr DangerousGetDataPtr(int valueOffset) { 
            //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            // NOTE: You must have called DangerousAddRef before calling this 
            //       method, or you run the risk of allowing Handle Recycling
            //       to occur!
            //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            return ADP.IntPtrOffset(DangerousGetHandle(), valueOffset); 
        }
 
        internal OleDbHResult CreateAccessor(UnsafeNativeMethods.IAccessor iaccessor, int flags, ColumnBinding[] bindings) { 
            OleDbHResult hr = 0;
            int[] rowBindStatus = new int[BindingCount()]; 

            _iaccessor = iaccessor;

            Bid.Trace("\n"); 
            hr = iaccessor.CreateAccessor(flags, (IntPtr)rowBindStatus.Length, this, (IntPtr)_dataLength, out _accessorHandle, rowBindStatus); // MDAC 69530
            Bid.Trace(" %08X{HRESULT}\n", hr); 
 
            for (int k = 0; k < rowBindStatus.Length; ++k) {
                if (DBBindStatus.OK != (DBBindStatus)rowBindStatus[k]) { 
                    if (ODB.DBACCESSOR_PARAMETERDATA == flags) {
                        throw ODB.BadStatus_ParamAcc(bindings[k].ColumnBindingOrdinal, (DBBindStatus)rowBindStatus[k]);
                    }
                    else if (ODB.DBACCESSOR_ROWDATA == flags) { 
                        throw ODB.BadStatusRowAccessor(bindings[k].ColumnBindingOrdinal, (DBBindStatus)rowBindStatus[k]);
                    } 
                    else Debug.Assert(false, "unknown accessor buffer"); 
                }
            } 
            return hr;
        }

        internal ColumnBinding[] SetBindings(OleDbDataReader dataReader, Bindings bindings, 
                                             int indexStart, int indexForAccessor,
                                             OleDbParameter[] parameters, tagDBBINDING[] dbbindings, bool ifIRowsetElseIRow) { 
            Debug.Assert(null != bindings, "null bindings"); 
            Debug.Assert(dbbindings.Length == BindingCount(), "count mismatch");
 
            bool mustRelease = false;

            RuntimeHelpers.PrepareConstrainedRegions();
            try { 
                DangerousAddRef(ref mustRelease);
 
                IntPtr buffer = DangerousGetHandle(); 
                for(int i = 0; i < dbbindings.Length; ++i) {
                    IntPtr ptr = ADP.IntPtrOffset(buffer, (i * ODB.SizeOf_tagDBBINDING)); 
                    Marshal.StructureToPtr(dbbindings[i], ptr, false/*deleteold*/);
                }
            }
            finally { 
                if (mustRelease) {
                    DangerousRelease(); 
                } 
            }
 
            ColumnBinding[] columns = new ColumnBinding[dbbindings.Length];
            for(int indexWithinAccessor = 0; indexWithinAccessor < columns.Length; ++indexWithinAccessor) {
                int index = indexStart + indexWithinAccessor;
                OleDbParameter parameter = ((null != parameters) ? parameters[index] : null); 
                columns[indexWithinAccessor] = new ColumnBinding(
                    dataReader, index, indexForAccessor, indexWithinAccessor, 
                    parameter, this, bindings, dbbindings[indexWithinAccessor], _headerLength, 
                    ifIRowsetElseIRow);
            } 
            return columns;
        }

        static internal int AlignDataSize(int value) { 
            // buffer data to start on 8-byte boundary
            return Math.Max(8, (value + 7) & ~0x7); // MDAC 70350 
        } 

        internal object GetVariantValue(int offset) { 
            Debug.Assert(_needToReset, "data type requires reseting and _needToReset is false");
            Debug.Assert(0 == (ODB.SizeOf_Variant % 8), "unexpected VARIANT size mutiplier");
            Debug.Assert(0 == offset%8, "invalid alignment");
            ValidateCheck(offset, 2*ODB.SizeOf_Variant); 

            object value = null; 
            bool mustRelease = false; 
            RuntimeHelpers.PrepareConstrainedRegions();
            try { 
                DangerousAddRef(ref mustRelease);

                IntPtr buffer = ADP.IntPtrOffset(DangerousGetHandle(), offset);
                value = Marshal.GetObjectForNativeVariant(buffer); 
            }
            finally { 
                if (mustRelease) { 
                    DangerousRelease();
                } 
            }

            return ((null != value) ? value : DBNull.Value);
        } 

        // translate to native 
        internal void SetVariantValue(int offset, object value) { 
            // two contigous VARIANT structures, second should be a binary copy of the first
            Debug.Assert(_needToReset, "data type requires reseting and _needToReset is false"); 
            Debug.Assert(0 == (ODB.SizeOf_Variant % 8), "unexpected VARIANT size mutiplier");
            Debug.Assert(0 == offset%8, "invalid alignment");
            ValidateCheck(offset, 2*ODB.SizeOf_Variant);
 
            IntPtr buffer = ADP.PtrZero;
            bool mustRelease = false; 
            RuntimeHelpers.PrepareConstrainedRegions(); 
            try {
                DangerousAddRef(ref mustRelease); 

                buffer = ADP.IntPtrOffset(DangerousGetHandle(), offset);

                RuntimeHelpers.PrepareConstrainedRegions(); 
                try {
                    // GetNativeVariantForObject must be in try block since it has no reliability contract 
                    Marshal.GetNativeVariantForObject(value, buffer); 
                }
                finally { 
                    // safe to copy memory(dst,src,count), even if GetNativeVariantForObject failed
                    NativeOledbWrapper.MemoryCopy(ADP.IntPtrOffset(buffer,ODB.SizeOf_Variant), buffer, ODB.SizeOf_Variant);
                }
            } 
            finally {
                if (mustRelease) { 
                    DangerousRelease(); 
                }
            } 
        }

        // value
        // cached value 
        // cached zero value
        // translate to native 
        internal void SetBstrValue(int offset, string value) { 
            // two contigous BSTR ptr, second should be a binary copy of the first
            Debug.Assert(_needToReset, "data type requires reseting and _needToReset is false"); 
            Debug.Assert(0 == offset%ADP.PtrSize, "invalid alignment");
            ValidateCheck(offset, 2*IntPtr.Size);

            IntPtr ptr; 
            bool mustRelease = false;
            RuntimeHelpers.PrepareConstrainedRegions(); 
            try { 
                DangerousAddRef(ref mustRelease);
 
                RuntimeHelpers.PrepareConstrainedRegions();
                try { } finally {
                    ptr = SafeNativeMethods.SysAllocStringLen(value, value.Length);
 
                    // safe to copy ptr, even if SysAllocStringLen failed
                    Marshal.WriteIntPtr(base.handle, offset, ptr); 
                    Marshal.WriteIntPtr(base.handle, offset + ADP.PtrSize, ptr); 
                }
            } 
            finally {
                if (mustRelease) {
                    DangerousRelease();
                } 
            }
            if (IntPtr.Zero == ptr) { 
                throw new OutOfMemoryException(); 
            }
        } 

        // translate to native
        internal void SetByRefValue(int offset, IntPtr pinnedValue) {
            Debug.Assert(_needToReset, "data type requires reseting and _needToReset is false"); 
            Debug.Assert(0 == offset%ADP.PtrSize, "invalid alignment");
            ValidateCheck(offset, 2*IntPtr.Size); 
 
            if (ADP.PtrZero == pinnedValue) { // empty array scenario
                pinnedValue = ADP.IntPtrOffset(base.handle, _emptyStringOffset); 
            }
            bool mustRelease = false;
            RuntimeHelpers.PrepareConstrainedRegions();
            try { 
                DangerousAddRef(ref mustRelease);
 
                RuntimeHelpers.PrepareConstrainedRegions(); 
                try { } finally {
                    Marshal.WriteIntPtr(base.handle, offset, pinnedValue);               // parameter input value 
                    Marshal.WriteIntPtr(base.handle, offset + ADP.PtrSize, pinnedValue); // original parameter value
                }
            }
            finally { 
                if (mustRelease) {
                    DangerousRelease(); 
                } 
            }
        } 

        internal void CloseFromConnection() {
            _iaccessor = null;
            _accessorHandle = ODB.DB_INVALID_HACCESSOR; 
        }
 
        internal new void Dispose() { 
            ResetValues();
 
            UnsafeNativeMethods.IAccessor iaccessor = _iaccessor;
            IntPtr accessorHandle = _accessorHandle;

            _iaccessor = null; 
            _accessorHandle = ODB.DB_INVALID_HACCESSOR;
 
            if ((ODB.DB_INVALID_HACCESSOR != accessorHandle) && (null != iaccessor)) { 
                OleDbHResult hr;
                int refcount; 
                hr = iaccessor.ReleaseAccessor(accessorHandle, out refcount);
                if (hr < 0) { // ignore any error msgs
                    SafeNativeMethods.Wrapper.ClearErrorInfo();
                } 
            }
 
            base.Dispose(); 
        }
 
        internal void ResetValues() {
            if (_needToReset && _haveData) {
                lock(this) { // prevent Dispose/ResetValues race condition
 
                    bool mustRelease = false;
                    RuntimeHelpers.PrepareConstrainedRegions(); 
                    try { 
                        DangerousAddRef(ref mustRelease);
 
                        ResetValues(DangerousGetHandle(), _iaccessor);
                    }
                    finally {
                        if (mustRelease) { 
                            DangerousRelease();
                        } 
                    } 
                }
            } 
            else {
                _haveData = false;
            }
#if DEBUG 
            // verify types that need reseting are not forgotton, since the code
            // that sets this up is in dbbinding.cs, MaxLen { set; } 
            if (!_needToReset) { 
                Debug.Assert(0 <= _bindingCount && (_bindingCount * ODB.SizeOf_tagDBBINDING) < Length, "bad _bindingCount");
                for (int i = 0; i < _bindingCount; ++i) { 
                    short wtype = ReadInt16((i * ODB.SizeOf_tagDBBINDING) + ODB.OffsetOf_tagDBBINDING_wType);
                    switch(wtype) {
                    case (NativeDBType.BYREF | NativeDBType.BYTES):
                    case (NativeDBType.BYREF | NativeDBType.WSTR): 
                    case NativeDBType.PROPVARIANT:
                    case NativeDBType.VARIANT: 
                    case NativeDBType.BSTR: 
                    case NativeDBType.HCHAPTER:
                        Debug.Assert(false, "expected _needToReset"); 
                        break;
                    }
                }
            } 
#endif
        } 
 
        // requires ReliabilityContract to be called by ReleaseHandle
        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] 
        private void ResetValues(IntPtr buffer, object iaccessor) {
            Debug.Assert(ADP.PtrZero != buffer && _needToReset && _haveData, "shouldn't be calling ResetValues");
            for (int i = 0; i < _bindingCount; ++i) {
                IntPtr ptr = ADP.IntPtrOffset(buffer, (i * ODB.SizeOf_tagDBBINDING)); 

                int valueOffset = _headerLength + Marshal.ReadIntPtr(ptr, ODB.OffsetOf_tagDBBINDING_obValue).ToInt32(); 
                short wtype = Marshal.ReadInt16(ptr, ODB.OffsetOf_tagDBBINDING_wType); 

                switch(wtype) { 
                case (NativeDBType.BYREF | NativeDBType.BYTES):
                case (NativeDBType.BYREF | NativeDBType.WSTR):
                    ValidateCheck(valueOffset, 2*IntPtr.Size);
                    FreeCoTaskMem(buffer, valueOffset); 
                    break;
                case NativeDBType.PROPVARIANT: 
                    ValidateCheck(valueOffset, 2*NativeOledbWrapper.SizeOfPROPVARIANT); 
                    FreePropVariant(buffer, valueOffset);
                    break; 
                case NativeDBType.VARIANT:
                    ValidateCheck(valueOffset, 2*ODB.SizeOf_Variant);
                    FreeVariant(buffer, valueOffset);
                    break; 
                case NativeDBType.BSTR:
                    ValidateCheck(valueOffset, 2*IntPtr.Size); 
                    FreeBstr(buffer, valueOffset); 
                    break;
                case NativeDBType.HCHAPTER: 
                    if (null != iaccessor) {
                        // iaccessor will always be null when from ReleaseHandle
                        FreeChapter(buffer, valueOffset, iaccessor);
                    } 
                    break;
#if DEBUG 
 
                case NativeDBType.EMPTY:
                case NativeDBType.NULL: 
                case NativeDBType.I2:
                case NativeDBType.I4:
                case NativeDBType.R4:
                case NativeDBType.R8: 
                case NativeDBType.CY:
                case NativeDBType.DATE: 
                case NativeDBType.ERROR: 
                case NativeDBType.BOOL:
                case NativeDBType.DECIMAL: 
                case NativeDBType.I1:
                case NativeDBType.UI1:
                case NativeDBType.UI2:
                case NativeDBType.UI4: 
                case NativeDBType.I8:
                case NativeDBType.UI8: 
                case NativeDBType.FILETIME: 
                case NativeDBType.GUID:
                case NativeDBType.BYTES: 
                case NativeDBType.WSTR:
                case NativeDBType.NUMERIC:
                case NativeDBType.DBDATE:
                case NativeDBType.DBTIME: 
                case NativeDBType.DBTIMESTAMP:
                    break; // known, do nothing 
                case NativeDBType.IDISPATCH: 
                case NativeDBType.IUNKNOWN:
                    break; // known, releasing RowHandle will handle lifetimes correctly 
                default:
                    Debug.Assert(false, "investigate");
                    break;
#endif 
                }
            } 
            _haveData = false; 
        }
 
        // this correctly does not have a ReliabilityContract, will not be called via ReleaseHandle
        static private void FreeChapter(IntPtr buffer, int valueOffset, object iaccessor) {
            Debug.Assert (0 == valueOffset % 8, "unexpected unaligned ptr offset");
 
            UnsafeNativeMethods.IChapteredRowset chapteredRowset = (iaccessor as UnsafeNativeMethods.IChapteredRowset);
            IntPtr chapter = SafeNativeMethods.InterlockedExchangePointer(ADP.IntPtrOffset(buffer, valueOffset), ADP.PtrZero); 
            if (ODB.DB_NULL_HCHAPTER != chapter) { 
                int refCount;
                Bid.Trace(" Chapter=%Id\n", chapter); 
                OleDbHResult hr = chapteredRowset.ReleaseChapter(chapter, out refCount);
                Bid.Trace(" %08X{HRESULT}, RefCount=%d\n", hr, refCount);
            }
        } 

        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] 
        static private void FreeBstr(IntPtr buffer, int valueOffset) { 
            Debug.Assert (0 == valueOffset % 8, "unexpected unaligned ptr offset");
 
            // two contigous BSTR ptrs that need to be freed
            // the second should only be freed if different from the first
            RuntimeHelpers.PrepareConstrainedRegions();
            try {} finally { 
                IntPtr currentValue = Marshal.ReadIntPtr(buffer, valueOffset);
                IntPtr originalValue = Marshal.ReadIntPtr(buffer, valueOffset + ADP.PtrSize); 
 
                if ((ADP.PtrZero != currentValue) && (currentValue != originalValue)) {
                    SafeNativeMethods.SysFreeString(currentValue); 
                }
                if (ADP.PtrZero != originalValue) {
                    SafeNativeMethods.SysFreeString(originalValue);
                } 

                // for debugability - delay clearing memory until after FreeBSTR 
                Marshal.WriteIntPtr(buffer, valueOffset, ADP.PtrZero); 
                Marshal.WriteIntPtr(buffer, valueOffset + ADP.PtrSize, ADP.PtrZero);
            } 
        }

        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
        static private void FreeCoTaskMem(IntPtr buffer, int valueOffset) { 
            Debug.Assert (0 == valueOffset % 8, "unexpected unaligned ptr offset");
 
            // two contigous CoTaskMemAlloc ptrs that need to be freed 
            // the first should only be freed if different from the first
            RuntimeHelpers.PrepareConstrainedRegions(); 
            try {} finally {
                IntPtr currentValue = Marshal.ReadIntPtr(buffer, valueOffset);
                IntPtr originalValue = Marshal.ReadIntPtr(buffer, valueOffset + ADP.PtrSize);
 
                // originalValue is pinned managed memory or pointer to emptyStringOffset
                if ((ADP.PtrZero != currentValue) && (currentValue != originalValue)) { 
                    SafeNativeMethods.CoTaskMemFree(currentValue); 
                }
 
                // for debugability - delay clearing memory until after CoTaskMemFree
                Marshal.WriteIntPtr(buffer, valueOffset, ADP.PtrZero);
                Marshal.WriteIntPtr(buffer, valueOffset + ADP.PtrSize, ADP.PtrZero);
            } 
        }
 
        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] 
        static private void FreeVariant(IntPtr buffer, int valueOffset) {
            // two contigous VARIANT structures that need to be freed 
            // the second should only be freed if different from the first

            Debug.Assert(0 == (ODB.SizeOf_Variant % 8), "unexpected VARIANT size mutiplier");
            Debug.Assert (0 == valueOffset % 8, "unexpected unaligned ptr offset"); 

            IntPtr currentHandle = ADP.IntPtrOffset(buffer, valueOffset); 
            IntPtr originalHandle = ADP.IntPtrOffset(buffer, valueOffset+ODB.SizeOf_Variant); 
            bool different = NativeOledbWrapper.MemoryCompare(currentHandle, originalHandle, ODB.SizeOf_Variant);
 
            RuntimeHelpers.PrepareConstrainedRegions();
            try {} finally {
                // always clear the first structure
                SafeNativeMethods.VariantClear(currentHandle); 
                if (different) {
                    // second structure different from the first 
                    SafeNativeMethods.VariantClear(originalHandle); 
                }
                else { 
                    // second structure same as the first, just clear the field
                    SafeNativeMethods.ZeroMemory(originalHandle, (IntPtr)ODB.SizeOf_Variant);
                }
            } 
        }
 
        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] 
        static private void FreePropVariant(IntPtr buffer, int valueOffset) {
            // two contigous PROPVARIANT structures that need to be freed 
            // the second should only be freed if different from the first
            Debug.Assert(0 == (NativeOledbWrapper.SizeOfPROPVARIANT % 8), "unexpected PROPVARIANT size mutiplier");
            Debug.Assert (0 == valueOffset % 8, "unexpected unaligned ptr offset");
 
            IntPtr currentHandle = ADP.IntPtrOffset(buffer, valueOffset);
            IntPtr originalHandle = ADP.IntPtrOffset(buffer, valueOffset+NativeOledbWrapper.SizeOfPROPVARIANT); 
            bool different = NativeOledbWrapper.MemoryCompare(currentHandle, originalHandle, NativeOledbWrapper.SizeOfPROPVARIANT); 

            RuntimeHelpers.PrepareConstrainedRegions(); 
            try {} finally {
                // always clear the first structure
                SafeNativeMethods.PropVariantClear(currentHandle);
                if (different) { 
                    // second structure different from the first
                    SafeNativeMethods.PropVariantClear(originalHandle); 
                } 
                else {
                    // second structure same as the first, just clear the field 
                    SafeNativeMethods.ZeroMemory(originalHandle, (IntPtr)NativeOledbWrapper.SizeOfPROPVARIANT);
                }
            }
        } 

        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] 
        internal IntPtr InterlockedExchangePointer(int offset) { 
            ValidateCheck(offset, IntPtr.Size);
            Debug.Assert(0 == offset%ADP.PtrSize, "invalid alignment"); 

            IntPtr value;
            bool   mustRelease = false;
            RuntimeHelpers.PrepareConstrainedRegions(); 
            try {
                DangerousAddRef(ref mustRelease); 
 
                IntPtr ptr = ADP.IntPtrOffset(DangerousGetHandle(), offset);
                value = SafeNativeMethods.InterlockedExchangePointer(ptr, IntPtr.Zero); 
            }
            finally {
                if (mustRelease) {
                    DangerousRelease(); 
                }
            } 
            return value; 
        }
 
        override protected bool ReleaseHandle() {
            // NOTE: The SafeHandle class guarantees this will be called exactly once.
            _iaccessor = null;
            if (_needToReset && _haveData) { 
                IntPtr buffer = base.handle;
                if (IntPtr.Zero != buffer) { 
                    ResetValues(buffer, null); 
                }
            } 
            return base.ReleaseHandle();
        }
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK