Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Data / System / Data / OleDb / DBPropSet.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="DBPropSet.cs" company="Microsoft">
3 //      Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 // <owner current="true" primary="true">Microsoft</owner>
6 // <owner current="true" primary="false">Microsoft</owner>
7 //------------------------------------------------------------------------------
8
9 using System;
10 using System.Data;
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;
20
21 namespace System.Data.OleDb {
22
23     sealed internal class DBPropSet : SafeHandle {
24
25         private readonly Int32 propertySetCount;
26
27         // VSDD 621427: stores the exception with last error.HRESULT from IDBProperties.GetProperties
28         private Exception lastErrorFromProvider;
29
30         private DBPropSet() : base(IntPtr.Zero, true) {
31             propertySetCount = 0;
32         }
33
34         internal DBPropSet(int propertysetCount) : this() {
35             this.propertySetCount = propertysetCount;
36             IntPtr countOfBytes = (IntPtr)(propertysetCount * ODB.SizeOf_tagDBPROPSET);
37             RuntimeHelpers.PrepareConstrainedRegions();
38             try {} finally {
39                 base.handle = SafeNativeMethods.CoTaskMemAlloc(countOfBytes);
40                 if (ADP.PtrZero != base.handle) {
41                     SafeNativeMethods.ZeroMemory(base.handle, countOfBytes);
42                 }
43             }
44             if (ADP.PtrZero == base.handle) {
45                 throw new OutOfMemoryException();
46             }
47         }
48
49         internal DBPropSet(UnsafeNativeMethods.IDBProperties properties, PropertyIDSet propidset, out OleDbHResult hr) : this() {
50             Debug.Assert(null != properties, "null IDBProperties");
51
52             int propidsetcount = 0;
53             if (null != propidset) {
54                 propidsetcount = propidset.Count;
55             }
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);
59
60             if (hr < 0) {
61                 // VSDD 621427: remember the last HRESULT. Note we do not want to raise exception now to avoid breaking change from Orcas RTM/SP1
62                 SetLastErrorInfo(hr);
63             }
64         }
65
66         internal DBPropSet(UnsafeNativeMethods.IRowsetInfo properties, PropertyIDSet propidset, out OleDbHResult hr) : this() {
67             Debug.Assert(null != properties, "null IRowsetInfo");
68
69             int propidsetcount = 0;
70             if (null != propidset) {
71                 propidsetcount = propidset.Count;
72             }
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);
76
77             if (hr < 0) {
78                 // VSDD 621427: remember the last HRESULT. Note we do not want to raise exception now to avoid breaking change from Orcas RTM/SP1
79                 SetLastErrorInfo(hr);
80             }
81         }
82
83         internal DBPropSet(UnsafeNativeMethods.ICommandProperties properties, PropertyIDSet propidset, out OleDbHResult hr) : this() {
84             Debug.Assert(null != properties, "null ICommandProperties");
85
86             int propidsetcount = 0;
87             if (null != propidset) {
88                 propidsetcount = propidset.Count;
89             }
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);
93
94             if (hr < 0) {
95                 // VSDD 621427: remember the last HRESULT. Note we do not want to raise exception now to avoid breaking change from Orcas RTM/SP1
96                 SetLastErrorInfo(hr);
97             }
98         }
99
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;
104
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
109             }
110             lastErrorFromProvider = new COMException(message, (int)lastErrorHr);
111         }
112
113         public override bool IsInvalid {
114             get {
115                 return (IntPtr.Zero == base.handle);
116             }
117         }
118
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) {
124
125                 int count = this.propertySetCount;
126                 for (int i = 0, offset = 0; i < count; ++i, offset += ODB.SizeOf_tagDBPROPSET) {
127
128                     IntPtr rgProperties = Marshal.ReadIntPtr(ptr, offset);
129                     if(ADP.PtrZero != rgProperties) {
130                         int cProperties = Marshal.ReadInt32(ptr, offset + ADP.PtrSize);
131
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);
135                         }
136                         SafeNativeMethods.CoTaskMemFree(rgProperties);
137                     }
138                 }
139                 SafeNativeMethods.CoTaskMemFree(ptr);
140             }
141             return true;
142         }
143
144         internal int PropertySetCount {
145             get {
146                 return this.propertySetCount;
147             }
148         }
149
150         internal tagDBPROP[] GetPropertySet(int index, out Guid propertyset) {
151             if ((index < 0) || (PropertySetCount <= index)) {
152                 if (lastErrorFromProvider != null)
153                 {
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);
157                 }
158                 else {
159                     throw ADP.InternalError(ADP.InternalErrorCode.InvalidBuffer);
160                 }
161             }
162
163             tagDBPROPSET propset = new tagDBPROPSET();
164             tagDBPROP[] properties = null;
165
166             bool mustRelease = false;
167             RuntimeHelpers.PrepareConstrainedRegions();
168             try {
169                 DangerousAddRef(ref mustRelease);
170                 IntPtr propertySetPtr = ADP.IntPtrOffset(DangerousGetHandle(), index * ODB.SizeOf_tagDBPROPSET);
171                 Marshal.PtrToStructure(propertySetPtr, propset);
172                 propertyset = propset.guidPropertySet;
173
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]);
179                 }
180             }
181             finally {
182                 if (mustRelease) {
183                     DangerousRelease();
184                 }
185             }
186             return properties;
187         }
188
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);
195                 }
196                 else {
197                     throw ADP.InternalError(ADP.InternalErrorCode.InvalidBuffer);
198                 }
199             }
200             Debug.Assert(Guid.Empty != propertySet, "invalid propertySet");
201             Debug.Assert((null != properties) && (0 < properties.Length), "invalid properties");
202
203             IntPtr countOfBytes = (IntPtr)(properties.Length * ODB.SizeOf_tagDBPROP);
204             tagDBPROPSET propset = new tagDBPROPSET(properties.Length, propertySet);
205
206             bool mustRelease = false;
207             RuntimeHelpers.PrepareConstrainedRegions();
208             try {
209                 DangerousAddRef(ref mustRelease);
210
211                 IntPtr propsetPtr = ADP.IntPtrOffset(DangerousGetHandle(), index * ODB.SizeOf_tagDBPROPSET);
212
213                 RuntimeHelpers.PrepareConstrainedRegions();
214                 try {} finally {
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);
221                         
222                         // writing the structure to native memory so that it knows to free the referenced pointers
223                         Marshal.StructureToPtr(propset, propsetPtr, false/*deleteold*/);
224                     }
225                 }
226                 if (ADP.PtrZero == propset.rgProperties) {
227                     throw new OutOfMemoryException();
228                 }
229
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*/);
234                 }
235             }
236             finally {
237                 if (mustRelease) {
238                     DangerousRelease();
239                 }
240             }
241         }
242
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 });
247             return propertyset;
248         }
249     }
250 }