DBPropSet.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. //------------------------------------------------------------------------------
  2. // <copyright file="DBPropSet.cs" company="Microsoft">
  3. // Copyright (c) Microsoft Corporation. All rights reserved.
  4. // </copyright>
  5. // <owner current="true" primary="true">[....]</owner>
  6. // <owner current="true" primary="false">[....]</owner>
  7. //------------------------------------------------------------------------------
  8. using System;
  9. using System.Data;
  10. using System.Data.Common;
  11. using System.Diagnostics;
  12. using System.Globalization;
  13. using System.Runtime.CompilerServices;
  14. using System.Runtime.InteropServices;
  15. using System.Security;
  16. using System.Security.Permissions;
  17. using System.Threading;
  18. using System.Runtime.ConstrainedExecution;
  19. namespace System.Data.OleDb {
  20. sealed internal class DBPropSet : SafeHandle {
  21. private readonly Int32 propertySetCount;
  22. // VSDD 621427: stores the exception with last error.HRESULT from IDBProperties.GetProperties
  23. private Exception lastErrorFromProvider;
  24. private DBPropSet() : base(IntPtr.Zero, true) {
  25. propertySetCount = 0;
  26. }
  27. internal DBPropSet(int propertysetCount) : this() {
  28. this.propertySetCount = propertysetCount;
  29. IntPtr countOfBytes = (IntPtr)(propertysetCount * ODB.SizeOf_tagDBPROPSET);
  30. RuntimeHelpers.PrepareConstrainedRegions();
  31. try {} finally {
  32. base.handle = SafeNativeMethods.CoTaskMemAlloc(countOfBytes);
  33. if (ADP.PtrZero != base.handle) {
  34. SafeNativeMethods.ZeroMemory(base.handle, countOfBytes);
  35. }
  36. }
  37. if (ADP.PtrZero == base.handle) {
  38. throw new OutOfMemoryException();
  39. }
  40. }
  41. internal DBPropSet(UnsafeNativeMethods.IDBProperties properties, PropertyIDSet propidset, out OleDbHResult hr) : this() {
  42. Debug.Assert(null != properties, "null IDBProperties");
  43. int propidsetcount = 0;
  44. if (null != propidset) {
  45. propidsetcount = propidset.Count;
  46. }
  47. Bid.Trace("<oledb.IDBProperties.GetProperties|API|OLEDB>\n");
  48. hr = properties.GetProperties(propidsetcount, propidset, out this.propertySetCount, out base.handle);
  49. Bid.Trace("<oledb.IDBProperties.GetProperties|API|OLEDB|RET> %08X{HRESULT}\n", hr);
  50. if (hr < 0) {
  51. // VSDD 621427: remember the last HRESULT. Note we do not want to raise exception now to avoid breaking change from Orcas RTM/SP1
  52. SetLastErrorInfo(hr);
  53. }
  54. }
  55. internal DBPropSet(UnsafeNativeMethods.IRowsetInfo properties, PropertyIDSet propidset, out OleDbHResult hr) : this() {
  56. Debug.Assert(null != properties, "null IRowsetInfo");
  57. int propidsetcount = 0;
  58. if (null != propidset) {
  59. propidsetcount = propidset.Count;
  60. }
  61. Bid.Trace("<oledb.IRowsetInfo.GetProperties|API|OLEDB>\n");
  62. hr = properties.GetProperties(propidsetcount, propidset, out this.propertySetCount, out base.handle);
  63. Bid.Trace("<oledb.IRowsetInfo.GetProperties|API|OLEDB|RET> %08X{HRESULT}\n", hr);
  64. if (hr < 0) {
  65. // VSDD 621427: remember the last HRESULT. Note we do not want to raise exception now to avoid breaking change from Orcas RTM/SP1
  66. SetLastErrorInfo(hr);
  67. }
  68. }
  69. internal DBPropSet(UnsafeNativeMethods.ICommandProperties properties, PropertyIDSet propidset, out OleDbHResult hr) : this() {
  70. Debug.Assert(null != properties, "null ICommandProperties");
  71. int propidsetcount = 0;
  72. if (null != propidset) {
  73. propidsetcount = propidset.Count;
  74. }
  75. Bid.Trace("<oledb.ICommandProperties.GetProperties|API|OLEDB>\n");
  76. hr = properties.GetProperties(propidsetcount, propidset, out this.propertySetCount, out base.handle);
  77. Bid.Trace("<oledb.ICommandProperties.GetProperties|API|OLEDB|RET> %08X{HRESULT}\n", hr);
  78. if (hr < 0) {
  79. // VSDD 621427: remember the last HRESULT. Note we do not want to raise exception now to avoid breaking change from Orcas RTM/SP1
  80. SetLastErrorInfo(hr);
  81. }
  82. }
  83. private void SetLastErrorInfo(OleDbHResult lastErrorHr) {
  84. // note: OleDbHResult is actually a simple wrapper over HRESULT with OLEDB-specific codes
  85. UnsafeNativeMethods.IErrorInfo errorInfo = null;
  86. string message = String.Empty;
  87. OleDbHResult errorInfoHr = UnsafeNativeMethods.GetErrorInfo(0, out errorInfo); // 0 - IErrorInfo exists, 1 - no IErrorInfo
  88. if ((errorInfoHr == OleDbHResult.S_OK) && (errorInfo != null)) {
  89. ODB.GetErrorDescription(errorInfo, lastErrorHr, out message);
  90. // note that either GetErrorInfo or GetErrorDescription might fail in which case we will have only the HRESULT value in exception message
  91. }
  92. lastErrorFromProvider = new COMException(message, (int)lastErrorHr);
  93. }
  94. public override bool IsInvalid {
  95. get {
  96. return (IntPtr.Zero == base.handle);
  97. }
  98. }
  99. override protected bool ReleaseHandle() {
  100. // NOTE: The SafeHandle class guarantees this will be called exactly once and is non-interrutible.
  101. IntPtr ptr = base.handle;
  102. base.handle = IntPtr.Zero;
  103. if (ADP.PtrZero != ptr) {
  104. int count = this.propertySetCount;
  105. for (int i = 0, offset = 0; i < count; ++i, offset += ODB.SizeOf_tagDBPROPSET) {
  106. IntPtr rgProperties = Marshal.ReadIntPtr(ptr, offset);
  107. if(ADP.PtrZero != rgProperties) {
  108. int cProperties = Marshal.ReadInt32(ptr, offset + ADP.PtrSize);
  109. IntPtr vptr = ADP.IntPtrOffset(rgProperties, ODB.OffsetOf_tagDBPROP_Value);
  110. for (int k = 0; k < cProperties; ++k, vptr = ADP.IntPtrOffset(vptr, ODB.SizeOf_tagDBPROP)) {
  111. SafeNativeMethods.VariantClear(vptr);
  112. }
  113. SafeNativeMethods.CoTaskMemFree(rgProperties);
  114. }
  115. }
  116. SafeNativeMethods.CoTaskMemFree(ptr);
  117. }
  118. return true;
  119. }
  120. internal int PropertySetCount {
  121. get {
  122. return this.propertySetCount;
  123. }
  124. }
  125. internal tagDBPROP[] GetPropertySet(int index, out Guid propertyset) {
  126. if ((index < 0) || (PropertySetCount <= index)) {
  127. if (lastErrorFromProvider != null)
  128. {
  129. // VSDD 621427: add extra error information for CSS/stress troubleshooting.
  130. // We need to keep same exception type to avoid breaking change with Orcas RTM/SP1.
  131. throw ADP.InternalError(ADP.InternalErrorCode.InvalidBuffer, lastErrorFromProvider);
  132. }
  133. else {
  134. throw ADP.InternalError(ADP.InternalErrorCode.InvalidBuffer);
  135. }
  136. }
  137. tagDBPROPSET propset = new tagDBPROPSET();
  138. tagDBPROP[] properties = null;
  139. bool mustRelease = false;
  140. RuntimeHelpers.PrepareConstrainedRegions();
  141. try {
  142. DangerousAddRef(ref mustRelease);
  143. IntPtr propertySetPtr = ADP.IntPtrOffset(DangerousGetHandle(), index * ODB.SizeOf_tagDBPROPSET);
  144. Marshal.PtrToStructure(propertySetPtr, propset);
  145. propertyset = propset.guidPropertySet;
  146. properties = new tagDBPROP[propset.cProperties];
  147. for(int i = 0; i < properties.Length; ++i) {
  148. properties[i] = new tagDBPROP();
  149. IntPtr ptr = ADP.IntPtrOffset(propset.rgProperties, i * ODB.SizeOf_tagDBPROP);
  150. Marshal.PtrToStructure(ptr, properties[i]);
  151. }
  152. }
  153. finally {
  154. if (mustRelease) {
  155. DangerousRelease();
  156. }
  157. }
  158. return properties;
  159. }
  160. internal void SetPropertySet(int index, Guid propertySet, tagDBPROP[] properties) {
  161. if ((index < 0) || (PropertySetCount <= index)) {
  162. if (lastErrorFromProvider != null) {
  163. // VSDD 621427: add extra error information for CSS/stress troubleshooting.
  164. // We need to keep same exception type to avoid breaking change with Orcas RTM/SP1.
  165. throw ADP.InternalError(ADP.InternalErrorCode.InvalidBuffer, lastErrorFromProvider);
  166. }
  167. else {
  168. throw ADP.InternalError(ADP.InternalErrorCode.InvalidBuffer);
  169. }
  170. }
  171. Debug.Assert(Guid.Empty != propertySet, "invalid propertySet");
  172. Debug.Assert((null != properties) && (0 < properties.Length), "invalid properties");
  173. IntPtr countOfBytes = (IntPtr)(properties.Length * ODB.SizeOf_tagDBPROP);
  174. tagDBPROPSET propset = new tagDBPROPSET(properties.Length, propertySet);
  175. bool mustRelease = false;
  176. RuntimeHelpers.PrepareConstrainedRegions();
  177. try {
  178. DangerousAddRef(ref mustRelease);
  179. IntPtr propsetPtr = ADP.IntPtrOffset(DangerousGetHandle(), index * ODB.SizeOf_tagDBPROPSET);
  180. RuntimeHelpers.PrepareConstrainedRegions();
  181. try {} finally {
  182. // must allocate and clear the memory without interruption
  183. propset.rgProperties = SafeNativeMethods.CoTaskMemAlloc(countOfBytes);
  184. if (ADP.PtrZero != propset.rgProperties) {
  185. // clearing is important so that we don't treat existing
  186. // garbage as important information during releaseHandle
  187. SafeNativeMethods.ZeroMemory(propset.rgProperties, countOfBytes);
  188. // writing the structure to native memory so that it knows to free the referenced pointers
  189. Marshal.StructureToPtr(propset, propsetPtr, false/*deleteold*/);
  190. }
  191. }
  192. if (ADP.PtrZero == propset.rgProperties) {
  193. throw new OutOfMemoryException();
  194. }
  195. for(int i = 0; i < properties.Length; ++i) {
  196. Debug.Assert(null != properties[i], "null tagDBPROP " + i.ToString(CultureInfo.InvariantCulture));
  197. IntPtr propertyPtr = ADP.IntPtrOffset(propset.rgProperties, i * ODB.SizeOf_tagDBPROP);
  198. Marshal.StructureToPtr(properties[i], propertyPtr, false/*deleteold*/);
  199. }
  200. }
  201. finally {
  202. if (mustRelease) {
  203. DangerousRelease();
  204. }
  205. }
  206. }
  207. static internal DBPropSet CreateProperty(Guid propertySet, int propertyId, bool required, object value) {
  208. tagDBPROP dbprop = new tagDBPROP(propertyId, required, value);
  209. DBPropSet propertyset = new DBPropSet(1);
  210. propertyset.SetPropertySet(0, propertySet, new tagDBPROP[1] { dbprop });
  211. return propertyset;
  212. }
  213. }
  214. }