573 lines
25 KiB
C#
573 lines
25 KiB
C#
|
//------------------------------------------------------------------------------
|
||
|
// <copyright file="RowBinding.cs" company="Microsoft">
|
||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
// </copyright>
|
||
|
// <owner current="true" primary="true">[....]</owner>
|
||
|
// <owner current="true" primary="false">[....]</owner>
|
||
|
//------------------------------------------------------------------------------
|
||
|
|
||
|
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("<oledb.IAccessor.CreateAccessor|API|OLEDB>\n");
|
||
|
hr = iaccessor.CreateAccessor(flags, (IntPtr)rowBindStatus.Length, this, (IntPtr)_dataLength, out _accessorHandle, rowBindStatus); // MDAC 69530
|
||
|
Bid.Trace("<oledb.IAccessor.CreateAccessor|API|OLEDB|RET> %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("<oledb.IChapteredRowset.ReleaseChapter|API|OLEDB> Chapter=%Id\n", chapter);
|
||
|
OleDbHResult hr = chapteredRowset.ReleaseChapter(chapter, out refCount);
|
||
|
Bid.Trace("<oledb.IChapteredRowset.ReleaseChapter|API|OLEDB|RET> %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();
|
||
|
}
|
||
|
}
|
||
|
}
|