1 //------------------------------------------------------------------------------
2 // <copyright file="DBPropSet.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 // <owner current="true" primary="true">Microsoft</owner>
6 // <owner current="true" primary="false">Microsoft</owner>
7 //------------------------------------------------------------------------------
11 using System.Data.Common;
12 using System.Diagnostics;
13 using System.Globalization;
14 using System.Runtime.CompilerServices;
15 using System.Runtime.InteropServices;
16 using System.Security;
17 using System.Security.Permissions;
18 using System.Threading;
19 using System.Runtime.ConstrainedExecution;
21 namespace System.Data.OleDb {
23 sealed internal class DBPropSet : SafeHandle {
25 private readonly Int32 propertySetCount;
27 // VSDD 621427: stores the exception with last error.HRESULT from IDBProperties.GetProperties
28 private Exception lastErrorFromProvider;
30 private DBPropSet() : base(IntPtr.Zero, true) {
34 internal DBPropSet(int propertysetCount) : this() {
35 this.propertySetCount = propertysetCount;
36 IntPtr countOfBytes = (IntPtr)(propertysetCount * ODB.SizeOf_tagDBPROPSET);
37 RuntimeHelpers.PrepareConstrainedRegions();
39 base.handle = SafeNativeMethods.CoTaskMemAlloc(countOfBytes);
40 if (ADP.PtrZero != base.handle) {
41 SafeNativeMethods.ZeroMemory(base.handle, countOfBytes);
44 if (ADP.PtrZero == base.handle) {
45 throw new OutOfMemoryException();
49 internal DBPropSet(UnsafeNativeMethods.IDBProperties properties, PropertyIDSet propidset, out OleDbHResult hr) : this() {
50 Debug.Assert(null != properties, "null IDBProperties");
52 int propidsetcount = 0;
53 if (null != propidset) {
54 propidsetcount = propidset.Count;
56 Bid.Trace("<oledb.IDBProperties.GetProperties|API|OLEDB>\n");
57 hr = properties.GetProperties(propidsetcount, propidset, out this.propertySetCount, out base.handle);
58 Bid.Trace("<oledb.IDBProperties.GetProperties|API|OLEDB|RET> %08X{HRESULT}\n", hr);
61 // VSDD 621427: remember the last HRESULT. Note we do not want to raise exception now to avoid breaking change from Orcas RTM/SP1
66 internal DBPropSet(UnsafeNativeMethods.IRowsetInfo properties, PropertyIDSet propidset, out OleDbHResult hr) : this() {
67 Debug.Assert(null != properties, "null IRowsetInfo");
69 int propidsetcount = 0;
70 if (null != propidset) {
71 propidsetcount = propidset.Count;
73 Bid.Trace("<oledb.IRowsetInfo.GetProperties|API|OLEDB>\n");
74 hr = properties.GetProperties(propidsetcount, propidset, out this.propertySetCount, out base.handle);
75 Bid.Trace("<oledb.IRowsetInfo.GetProperties|API|OLEDB|RET> %08X{HRESULT}\n", hr);
78 // VSDD 621427: remember the last HRESULT. Note we do not want to raise exception now to avoid breaking change from Orcas RTM/SP1
83 internal DBPropSet(UnsafeNativeMethods.ICommandProperties properties, PropertyIDSet propidset, out OleDbHResult hr) : this() {
84 Debug.Assert(null != properties, "null ICommandProperties");
86 int propidsetcount = 0;
87 if (null != propidset) {
88 propidsetcount = propidset.Count;
90 Bid.Trace("<oledb.ICommandProperties.GetProperties|API|OLEDB>\n");
91 hr = properties.GetProperties(propidsetcount, propidset, out this.propertySetCount, out base.handle);
92 Bid.Trace("<oledb.ICommandProperties.GetProperties|API|OLEDB|RET> %08X{HRESULT}\n", hr);
95 // VSDD 621427: remember the last HRESULT. Note we do not want to raise exception now to avoid breaking change from Orcas RTM/SP1
100 private void SetLastErrorInfo(OleDbHResult lastErrorHr) {
101 // note: OleDbHResult is actually a simple wrapper over HRESULT with OLEDB-specific codes
102 UnsafeNativeMethods.IErrorInfo errorInfo = null;
103 string message = String.Empty;
105 OleDbHResult errorInfoHr = UnsafeNativeMethods.GetErrorInfo(0, out errorInfo); // 0 - IErrorInfo exists, 1 - no IErrorInfo
106 if ((errorInfoHr == OleDbHResult.S_OK) && (errorInfo != null)) {
107 ODB.GetErrorDescription(errorInfo, lastErrorHr, out message);
108 // note that either GetErrorInfo or GetErrorDescription might fail in which case we will have only the HRESULT value in exception message
110 lastErrorFromProvider = new COMException(message, (int)lastErrorHr);
113 public override bool IsInvalid {
115 return (IntPtr.Zero == base.handle);
119 override protected bool ReleaseHandle() {
120 // NOTE: The SafeHandle class guarantees this will be called exactly once and is non-interrutible.
121 IntPtr ptr = base.handle;
122 base.handle = IntPtr.Zero;
123 if (ADP.PtrZero != ptr) {
125 int count = this.propertySetCount;
126 for (int i = 0, offset = 0; i < count; ++i, offset += ODB.SizeOf_tagDBPROPSET) {
128 IntPtr rgProperties = Marshal.ReadIntPtr(ptr, offset);
129 if(ADP.PtrZero != rgProperties) {
130 int cProperties = Marshal.ReadInt32(ptr, offset + ADP.PtrSize);
132 IntPtr vptr = ADP.IntPtrOffset(rgProperties, ODB.OffsetOf_tagDBPROP_Value);
133 for (int k = 0; k < cProperties; ++k, vptr = ADP.IntPtrOffset(vptr, ODB.SizeOf_tagDBPROP)) {
134 SafeNativeMethods.VariantClear(vptr);
136 SafeNativeMethods.CoTaskMemFree(rgProperties);
139 SafeNativeMethods.CoTaskMemFree(ptr);
144 internal int PropertySetCount {
146 return this.propertySetCount;
150 internal tagDBPROP[] GetPropertySet(int index, out Guid propertyset) {
151 if ((index < 0) || (PropertySetCount <= index)) {
152 if (lastErrorFromProvider != null)
154 // VSDD 621427: add extra error information for CSS/stress troubleshooting.
155 // We need to keep same exception type to avoid breaking change with Orcas RTM/SP1.
156 throw ADP.InternalError(ADP.InternalErrorCode.InvalidBuffer, lastErrorFromProvider);
159 throw ADP.InternalError(ADP.InternalErrorCode.InvalidBuffer);
163 tagDBPROPSET propset = new tagDBPROPSET();
164 tagDBPROP[] properties = null;
166 bool mustRelease = false;
167 RuntimeHelpers.PrepareConstrainedRegions();
169 DangerousAddRef(ref mustRelease);
170 IntPtr propertySetPtr = ADP.IntPtrOffset(DangerousGetHandle(), index * ODB.SizeOf_tagDBPROPSET);
171 Marshal.PtrToStructure(propertySetPtr, propset);
172 propertyset = propset.guidPropertySet;
174 properties = new tagDBPROP[propset.cProperties];
175 for(int i = 0; i < properties.Length; ++i) {
176 properties[i] = new tagDBPROP();
177 IntPtr ptr = ADP.IntPtrOffset(propset.rgProperties, i * ODB.SizeOf_tagDBPROP);
178 Marshal.PtrToStructure(ptr, properties[i]);
189 internal void SetPropertySet(int index, Guid propertySet, tagDBPROP[] properties) {
190 if ((index < 0) || (PropertySetCount <= index)) {
191 if (lastErrorFromProvider != null) {
192 // VSDD 621427: add extra error information for CSS/stress troubleshooting.
193 // We need to keep same exception type to avoid breaking change with Orcas RTM/SP1.
194 throw ADP.InternalError(ADP.InternalErrorCode.InvalidBuffer, lastErrorFromProvider);
197 throw ADP.InternalError(ADP.InternalErrorCode.InvalidBuffer);
200 Debug.Assert(Guid.Empty != propertySet, "invalid propertySet");
201 Debug.Assert((null != properties) && (0 < properties.Length), "invalid properties");
203 IntPtr countOfBytes = (IntPtr)(properties.Length * ODB.SizeOf_tagDBPROP);
204 tagDBPROPSET propset = new tagDBPROPSET(properties.Length, propertySet);
206 bool mustRelease = false;
207 RuntimeHelpers.PrepareConstrainedRegions();
209 DangerousAddRef(ref mustRelease);
211 IntPtr propsetPtr = ADP.IntPtrOffset(DangerousGetHandle(), index * ODB.SizeOf_tagDBPROPSET);
213 RuntimeHelpers.PrepareConstrainedRegions();
215 // must allocate and clear the memory without interruption
216 propset.rgProperties = SafeNativeMethods.CoTaskMemAlloc(countOfBytes);
217 if (ADP.PtrZero != propset.rgProperties) {
218 // clearing is important so that we don't treat existing
219 // garbage as important information during releaseHandle
220 SafeNativeMethods.ZeroMemory(propset.rgProperties, countOfBytes);
222 // writing the structure to native memory so that it knows to free the referenced pointers
223 Marshal.StructureToPtr(propset, propsetPtr, false/*deleteold*/);
226 if (ADP.PtrZero == propset.rgProperties) {
227 throw new OutOfMemoryException();
230 for(int i = 0; i < properties.Length; ++i) {
231 Debug.Assert(null != properties[i], "null tagDBPROP " + i.ToString(CultureInfo.InvariantCulture));
232 IntPtr propertyPtr = ADP.IntPtrOffset(propset.rgProperties, i * ODB.SizeOf_tagDBPROP);
233 Marshal.StructureToPtr(properties[i], propertyPtr, false/*deleteold*/);
243 static internal DBPropSet CreateProperty(Guid propertySet, int propertyId, bool required, object value) {
244 tagDBPROP dbprop = new tagDBPROP(propertyId, required, value);
245 DBPropSet propertyset = new DBPropSet(1);
246 propertyset.SetPropertySet(0, propertySet, new tagDBPROP[1] { dbprop });