//----------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. // //----------------------------------------------------------------------- namespace Microsoft.Isam.Esent.Interop.Implementation { using System; using System.Collections.Generic; using System.Globalization; using System.Runtime.InteropServices; using Microsoft.Isam.Esent.Interop.Vista; using Microsoft.Isam.Esent.Interop.Windows8; /// /// Windows8 calls to the ESENT interop layer. These calls take the managed types (e.g. JET_SESID) and /// return errors. /// internal sealed partial class JetApi : IJetApi { #region Transactions /// /// Causes a session to enter a transaction or create a new save point in an existing /// transaction. /// /// The session to begin the transaction for. /// An optional identifier supplied by the user for identifying the transaction. /// Transaction options. /// An error if the call fails. public int JetBeginTransaction3(JET_SESID sesid, long userTransactionId, BeginTransactionGrbit grbit) { TraceFunctionCall(); return Err(NativeMethods.JetBeginTransaction3(sesid.Value, userTransactionId, unchecked((uint)grbit))); } /// /// Commits the changes made to the state of the database during the current save point /// and migrates them to the previous save point. If the outermost save point is committed /// then the changes made during that save point will be committed to the state of the /// database and the session will exit the transaction. /// /// The session to commit the transaction for. /// Commit options. /// Duration to commit lazy transaction. /// Commit-id associated with this commit record. /// An error if the call fails. public int JetCommitTransaction2(JET_SESID sesid, CommitTransactionGrbit grbit, TimeSpan durableCommit, out JET_COMMIT_ID commitId) { TraceFunctionCall(); this.CheckSupportsWindows8Features("JetCommitTransaction2"); int err; uint cmsecDurableCommit = (uint)durableCommit.TotalMilliseconds; NATIVE_COMMIT_ID nativeCommitId = new NATIVE_COMMIT_ID(); unsafe { err = Err(NativeMethods.JetCommitTransaction2(sesid.Value, unchecked((uint)grbit), cmsecDurableCommit, ref nativeCommitId)); } commitId = new JET_COMMIT_ID(nativeCommitId); return err; } #endregion /// /// Gets extended information about an error. /// /// The error code about which to retrieve information. /// Information about the specified error code. /// An error code. public int JetGetErrorInfo( JET_err error, out JET_ERRINFOBASIC errinfo) { TraceFunctionCall(); this.CheckSupportsWindows8Features("JetGetErrorInfo"); NATIVE_ERRINFOBASIC nativeErrinfobasic = new NATIVE_ERRINFOBASIC(); errinfo = new JET_ERRINFOBASIC(); nativeErrinfobasic.cbStruct = checked((uint)Marshal.SizeOf(typeof(NATIVE_ERRINFOBASIC))); int nativeErr = (int)error; int err = Implementation.NativeMethods.JetGetErrorInfoW( ref nativeErr, ref nativeErrinfobasic, nativeErrinfobasic.cbStruct, (uint)JET_ErrorInfo.SpecificErr, (uint)ErrorInfoGrbit.None); errinfo.SetFromNative(ref nativeErrinfobasic); return err; } /// /// Resizes a currently open database. /// /// The session to use. /// The database to grow. /// The desired size of the database, in pages. /// The size of the database, in pages, after the call. /// Resize options. /// An error code. public int JetResizeDatabase( JET_SESID sesid, JET_DBID dbid, int desiredPages, out int actualPages, ResizeDatabaseGrbit grbit) { TraceFunctionCall(); this.CheckSupportsWindows8Features("JetResizeDatabase"); CheckNotNegative(desiredPages, "desiredPages"); uint actualPagesNative = 0; int err = Err(NativeMethods.JetResizeDatabase( sesid.Value, dbid.Value, checked((uint)desiredPages), out actualPagesNative, (uint)grbit)); actualPages = checked((int)actualPagesNative); return err; } /// /// Creates indexes over data in an ESE database. /// /// The session to use. /// The table to create the index on. /// Array of objects describing the indexes to be created. /// Number of index description objects. /// /// /// and /// are very similar, and appear to take the same arguments. The difference is in the /// implementation. JetCreateIndex2 uses LCIDs for Unicode indices (e.g. 1033), while /// JetCreateIndex4 uses Locale Names (e.g. "en-US" or "de-DE". LCIDs are older, and not as well /// supported in newer version of windows. /// /// /// An error code. /// /// public int JetCreateIndex4( JET_SESID sesid, JET_TABLEID tableid, JET_INDEXCREATE[] indexcreates, int numIndexCreates) { TraceFunctionCall(); this.CheckSupportsWindows8Features("JetCreateIndex4"); CheckNotNull(indexcreates, "indexcreates"); CheckNotNegative(numIndexCreates, "numIndexCreates"); if (numIndexCreates > indexcreates.Length) { throw new ArgumentOutOfRangeException( "numIndexCreates", numIndexCreates, "numIndexCreates is larger than the number of indexes passed in"); } return CreateIndexes3(sesid, tableid, indexcreates, numIndexCreates); } /// /// Creates a temporary table with a single index. A temporary table /// stores and retrieves records just like an ordinary table created /// using JetCreateTableColumnIndex. However, temporary tables are /// much faster than ordinary tables due to their volatile nature. /// They can also be used to very quickly sort and perform duplicate /// removal on record sets when accessed in a purely sequential manner. /// /// The session to use. /// /// Description of the temporary table to create on input. After a /// successful call, the structure contains the handle to the temporary /// table and column identifications. /// /// An error code. public int JetOpenTemporaryTable2(JET_SESID sesid, JET_OPENTEMPORARYTABLE temporarytable) { TraceFunctionCall(); this.CheckSupportsWindows8Features("JetOpenTemporaryTable2"); CheckNotNull(temporarytable, "temporarytable"); NATIVE_OPENTEMPORARYTABLE2 nativetemporarytable = temporarytable.GetNativeOpenTemporaryTable2(); var nativecolumnids = new uint[nativetemporarytable.ccolumn]; NATIVE_COLUMNDEF[] nativecolumndefs = GetNativecolumndefs(temporarytable.prgcolumndef, temporarytable.ccolumn); unsafe { using (var gchandlecollection = new GCHandleCollection()) { // Pin memory nativetemporarytable.prgcolumndef = (NATIVE_COLUMNDEF*)gchandlecollection.Add(nativecolumndefs); nativetemporarytable.rgcolumnid = (uint*)gchandlecollection.Add(nativecolumnids); if (null != temporarytable.pidxunicode) { NATIVE_UNICODEINDEX2 unicode = temporarytable.pidxunicode.GetNativeUnicodeIndex2(); unicode.szLocaleName = gchandlecollection.Add(Util.ConvertToNullTerminatedUnicodeByteArray(temporarytable.pidxunicode.GetEffectiveLocaleName())); nativetemporarytable.pidxunicode = (NATIVE_UNICODEINDEX2*)gchandlecollection.Add(unicode); } // Call the interop method int err = Err(NativeMethods.JetOpenTemporaryTable2(sesid.Value, ref nativetemporarytable)); // Convert the return values SetColumnids(temporarytable.prgcolumndef, temporarytable.prgcolumnid, nativecolumnids, temporarytable.ccolumn); temporarytable.tableid = new JET_TABLEID { Value = nativetemporarytable.tableid }; return err; } } } /// /// Creates a table, adds columns, and indices on that table. /// /// The session to use. /// The database to which to add the new table. /// Object describing the table to create. /// An error if the call fails. public int JetCreateTableColumnIndex4( JET_SESID sesid, JET_DBID dbid, JET_TABLECREATE tablecreate) { TraceFunctionCall(); this.CheckSupportsWindows8Features("JetCreateTableColumnIndex4"); CheckNotNull(tablecreate, "tablecreate"); return CreateTableColumnIndex4(sesid, dbid, tablecreate); } #region Session Parameters /// /// Gets a parameter on the provided session state, used for the lifetime of this session or until reset. /// /// The session to set the parameter on. /// The ID of the session parameter to set, see /// and . /// A 32-bit integer to retrieve. /// An error if the call fails. public int JetGetSessionParameter( JET_SESID sesid, JET_sesparam sesparamid, out int value) { TraceFunctionCall(); this.CheckSupportsWindows8Features("JetGetSessionParameter"); int err; int actualDataSize; err = NativeMethods.JetGetSessionParameter( sesid.Value, (uint)sesparamid, out value, sizeof(int), out actualDataSize); if (err >= (int)JET_err.Success) { if (actualDataSize != sizeof(int)) { throw new ArgumentException( string.Format( CultureInfo.InvariantCulture, "Bad return value. Unexpected data size returned. Expected {0}, but received {1}.", sizeof(int), actualDataSize), "sesparamid"); } } return Err(err); } /// /// Gets a parameter on the provided session state, used for the lifetime of this session or until reset. /// /// The session to set the parameter on. /// The ID of the session parameter to set, see /// and . /// A byte array to retrieve. /// AThe length of the data array. /// The actual size of the data field. /// An error if the call fails. public int JetGetSessionParameter( JET_SESID sesid, JET_sesparam sesparamid, byte[] data, int length, out int actualDataSize) { TraceFunctionCall(); this.CheckSupportsWindows8Features("JetGetSessionParameter"); CheckDataSize(data, length, "length"); int err; err = NativeMethods.JetGetSessionParameter( sesid.Value, (uint)sesparamid, data, length, out actualDataSize); return Err(err); } /// /// Sets a parameter on the provided session state, used for the lifetime of this session or until reset. /// /// The session to set the parameter on. /// The ID of the session parameter to set. /// A 32-bit integer to set. /// An error if the call fails. public int JetSetSessionParameter( JET_SESID sesid, JET_sesparam sesparamid, int valueToSet) { TraceFunctionCall(); this.CheckSupportsWindows8Features("JetSetSessionParameter"); int err; err = NativeMethods.JetSetSessionParameter(sesid.Value, (uint)sesparamid, ref valueToSet, sizeof(int)); return Err(err); } /// /// Sets a parameter on the provided session state, used for the lifetime of this session or until reset. /// /// The session to set the parameter on. /// The ID of the session parameter to set. /// Data to set in this session parameter. /// Size of the data provided. /// An error if the call fails. public int JetSetSessionParameter( JET_SESID sesid, JET_sesparam sesparamid, byte[] data, int dataSize) { TraceFunctionCall(); this.CheckSupportsWindows8Features("JetSetSessionParameter"); CheckNotNegative(dataSize, "dataSize"); CheckDataSize(data, dataSize, "dataSize"); int err; err = NativeMethods.JetSetSessionParameter(sesid.Value, (uint)sesparamid, data, dataSize); return Err(err); } #endregion #region JetGetIndexInfo overloads /// /// Retrieves information about indexes on a table. /// /// The session to use. /// The database to use. /// The name of the table to retrieve index information about. /// The name of the index to retrieve information about. /// Filled in with information about indexes on the table. /// The type of information to retrieve. /// An error if the call fails. public int JetGetIndexInfo( JET_SESID sesid, JET_DBID dbid, string tablename, string indexname, out JET_INDEXCREATE result, JET_IdxInfo infoLevel) { TraceFunctionCall(); CheckNotNull(tablename, "tablename"); int err; switch (infoLevel) { case Microsoft.Isam.Esent.Interop.Windows7.Windows7IdxInfo.CreateIndex: case Microsoft.Isam.Esent.Interop.Windows7.Windows7IdxInfo.CreateIndex2: case Microsoft.Isam.Esent.Interop.Windows8.Windows8IdxInfo.InfoCreateIndex3: break; default: throw new ArgumentException(string.Format("{0} is not a valid value JET_IdxInfo for this JET_INDEXCREATE overload.")); } if (this.Capabilities.SupportsWindows8Features) { { int bufferSize = 10 * Marshal.SizeOf(typeof(NATIVE_INDEXCREATE3)); IntPtr unmanagedBuffer = Marshal.AllocHGlobal(bufferSize); try { // var nativeIndexcreate = new NATIVE_INDEXCREATE3(); // nativeIndexcreate.cbStruct = checked((uint)bufferSize); infoLevel = Windows8IdxInfo.InfoCreateIndex3; err = Err(NativeMethods.JetGetIndexInfoW( sesid.Value, dbid.Value, tablename, indexname, unmanagedBuffer, (uint)bufferSize, (uint)infoLevel)); NATIVE_INDEXCREATE3 nativeIndexcreate = (NATIVE_INDEXCREATE3)Marshal.PtrToStructure(unmanagedBuffer, typeof(NATIVE_INDEXCREATE3)); result = new JET_INDEXCREATE(); result.SetAllFromNativeIndexCreate(ref nativeIndexcreate); } finally { Marshal.FreeHGlobal(unmanagedBuffer); } } } else { result = null; err = Err((int)JET_err.FeatureNotAvailable); } return err; } /// /// Retrieves information about indexes on a table. /// /// The session to use. /// The table to retrieve index information about. /// The name of the index to retrieve information about. /// Filled in with information about indexes on the table. /// The type of information to retrieve. /// An error if the call fails. public int JetGetTableIndexInfo( JET_SESID sesid, JET_TABLEID tableid, string indexname, out JET_INDEXCREATE result, JET_IdxInfo infoLevel) { TraceFunctionCall(); int err; switch (infoLevel) { case Microsoft.Isam.Esent.Interop.Windows7.Windows7IdxInfo.CreateIndex: case Microsoft.Isam.Esent.Interop.Windows7.Windows7IdxInfo.CreateIndex2: case Microsoft.Isam.Esent.Interop.Windows8.Windows8IdxInfo.InfoCreateIndex3: break; default: throw new ArgumentException(string.Format("{0} is not a valid value JET_IdxInfo for this JET_INDEXCREATE overload.")); } if (this.Capabilities.SupportsWindows8Features) { { int bufferSize = 10 * Marshal.SizeOf(typeof(NATIVE_INDEXCREATE3)); IntPtr unmanagedBuffer = Marshal.AllocHGlobal(bufferSize); try { // var nativeIndexcreate = new NATIVE_INDEXCREATE3(); // nativeIndexcreate.cbStruct = checked((uint)bufferSize); infoLevel = Windows8IdxInfo.InfoCreateIndex3; err = Err(NativeMethods.JetGetTableIndexInfoW( sesid.Value, tableid.Value, indexname, unmanagedBuffer, (uint)bufferSize, (uint)infoLevel)); NATIVE_INDEXCREATE3 nativeIndexcreate = (NATIVE_INDEXCREATE3)Marshal.PtrToStructure(unmanagedBuffer, typeof(NATIVE_INDEXCREATE3)); result = new JET_INDEXCREATE(); result.SetAllFromNativeIndexCreate(ref nativeIndexcreate); } finally { Marshal.FreeHGlobal(unmanagedBuffer); } } } else { result = null; err = Err((int)JET_err.FeatureNotAvailable); } return err; } #endregion #region prereading /// /// If the records with the specified key rangess are not in the buffer /// cache, then start asynchronous reads to bring the records into the /// database buffer cache. /// /// The session to use. /// The table to issue the prereads against. /// The key ranges to preread. /// The index of the first key range in the array to read. /// The maximum number of key ranges to preread. /// Returns the number of keys actually preread. /// List of column ids for long value columns to preread. /// Preread options. Used to specify the direction of the preread. /// /// An error if the call fails. /// public int JetPrereadIndexRanges( JET_SESID sesid, JET_TABLEID tableid, JET_INDEX_RANGE[] indexRanges, int rangeIndex, int rangeCount, out int rangesPreread, JET_COLUMNID[] columnsPreread, PrereadIndexRangesGrbit grbit) { TraceFunctionCall(); this.CheckSupportsWindows8Features("JetPrereadIndexRanges"); CheckNotNull(indexRanges, "indexRanges"); CheckDataSize(indexRanges, rangeIndex, "rangeIndex", rangeCount, "rangeCount"); var handles = new GCHandleCollection(); try { NATIVE_INDEX_RANGE[] nativeRanges = new NATIVE_INDEX_RANGE[rangeCount]; for (int i = 0; i < rangeCount; i++) { nativeRanges[i] = indexRanges[i + rangeIndex].GetNativeIndexRange(ref handles); } if (columnsPreread != null) { var nativecolumnids = new uint[columnsPreread.Length]; for (int i = 0; i < columnsPreread.Length; i++) { nativecolumnids[i] = (uint)columnsPreread[i].Value; } return Err(NativeMethods.JetPrereadIndexRanges(sesid.Value, tableid.Value, nativeRanges, (uint)rangeCount, out rangesPreread, nativecolumnids, (uint)columnsPreread.Length, checked((uint)grbit))); } else { return Err(NativeMethods.JetPrereadIndexRanges(sesid.Value, tableid.Value, nativeRanges, (uint)rangeCount, out rangesPreread, null, (uint)0, checked((uint)grbit))); } } finally { handles.Dispose(); } } /// /// If the records with the specified key ranges are not in the /// buffer cache then start asynchronous reads to bring the records /// into the database buffer cache. /// /// The session to use. /// The table to issue the prereads against. /// The start of key ranges to preread. /// The lengths of the start keys to preread. /// The end of key rangess to preread. /// The lengths of the end keys to preread. /// The index of the first key range in the array to read. /// The maximum number of key ranges to preread. /// Returns the number of keys actually preread. /// List of column ids for long value columns to preread. /// Preread options. Used to specify the direction of the preread. /// An error or warning. public int JetPrereadKeyRanges( JET_SESID sesid, JET_TABLEID tableid, byte[][] keysStart, int[] keyStartLengths, byte[][] keysEnd, int[] keyEndLengths, int rangeIndex, int rangeCount, out int rangesPreread, JET_COLUMNID[] columnsPreread, PrereadIndexRangesGrbit grbit) { TraceFunctionCall(); this.CheckSupportsWindows8Features("JetPrereadKeyRanges"); CheckDataSize(keysStart, rangeIndex, "rangeIndex", rangeCount, "rangeCount"); CheckDataSize(keyStartLengths, rangeIndex, "rangeIndex", rangeCount, "rangeCount"); CheckNotNull(keysStart, "keysStart"); if (keysEnd != null) { CheckNotNull(keyEndLengths, "keyEndLengths"); CheckDataSize(keysEnd, rangeIndex, "rangeIndex", rangeCount, "rangeCount"); } if (keyEndLengths != null) { CheckNotNull(keysEnd, "keysEnd"); CheckDataSize(keyEndLengths, rangeIndex, "rangeIndex", rangeCount, "rangeCount"); } grbit = grbit | PrereadIndexRangesGrbit.NormalizedKey; using (var handles = new GCHandleCollection()) { NATIVE_INDEX_COLUMN[] startColumn; NATIVE_INDEX_COLUMN[] endColumn; NATIVE_INDEX_RANGE[] ranges = new NATIVE_INDEX_RANGE[rangeCount]; for (int i = 0; i < rangeCount; i++) { startColumn = new NATIVE_INDEX_COLUMN[1]; startColumn[0].pvData = handles.Add(keysStart[i + rangeIndex]); startColumn[0].cbData = (uint)keyStartLengths[i + rangeIndex]; ranges[i].rgStartColumns = handles.Add(startColumn); ranges[i].cStartColumns = 1; if (keysEnd != null) { endColumn = new NATIVE_INDEX_COLUMN[1]; endColumn[0].pvData = handles.Add(keysEnd[i + rangeIndex]); endColumn[0].cbData = (uint)keyEndLengths[i + rangeIndex]; ranges[i].rgEndColumns = handles.Add(endColumn); ranges[i].cEndColumns = 1; } } if (columnsPreread != null) { var nativecolumnids = new uint[columnsPreread.Length]; for (int i = 0; i < columnsPreread.Length; i++) { nativecolumnids[i] = (uint)columnsPreread[i].Value; } return Err(NativeMethods.JetPrereadIndexRanges(sesid.Value, tableid.Value, ranges, (uint)rangeCount, out rangesPreread, nativecolumnids, (uint)columnsPreread.Length, checked((uint)grbit))); } else { return Err(NativeMethods.JetPrereadIndexRanges(sesid.Value, tableid.Value, ranges, (uint)rangeCount, out rangesPreread, null, (uint)0, checked((uint)grbit))); } } } /// /// Set an array of simple filters for /// /// The session to use for the call. /// The cursor to position. /// Simple record filters. /// Move options. /// An error if the call fails. public int JetSetCursorFilter(JET_SESID sesid, JET_TABLEID tableid, JET_INDEX_COLUMN[] filters, CursorFilterGrbit grbit) { TraceFunctionCall(); this.CheckSupportsWindows8Features("JetSetCursorFilter"); if (filters == null || filters.Length == 0) { return Err(NativeMethods.JetSetCursorFilter(sesid.Value, tableid.Value, null, 0, checked((uint)grbit))); } var handles = new GCHandleCollection(); try { NATIVE_INDEX_COLUMN[] nativeFilters = new NATIVE_INDEX_COLUMN[filters.Length]; for (int i = 0; i < filters.Length; i++) { nativeFilters[i] = filters[i].GetNativeIndexColumn(ref handles); } return Err(NativeMethods.JetSetCursorFilter(sesid.Value, tableid.Value, nativeFilters, (uint)filters.Length, checked((uint)grbit))); } finally { handles.Dispose(); } } #endregion #region Private utility functions /// /// Make native indexcreate structures from the managed ones. /// /// Index create structures to convert. /// The handle collection used to pin the data. /// Pinned native versions of the index creates. private static unsafe NATIVE_INDEXCREATE3[] GetNativeIndexCreate3s( IList managedIndexCreates, ref GCHandleCollection handles) { NATIVE_INDEXCREATE3[] nativeIndices = null; if (managedIndexCreates != null && managedIndexCreates.Count > 0) { nativeIndices = new NATIVE_INDEXCREATE3[managedIndexCreates.Count]; for (int i = 0; i < managedIndexCreates.Count; ++i) { nativeIndices[i] = managedIndexCreates[i].GetNativeIndexcreate3(); if (null != managedIndexCreates[i].pidxUnicode) { NATIVE_UNICODEINDEX2 unicode = managedIndexCreates[i].pidxUnicode.GetNativeUnicodeIndex2(); unicode.szLocaleName = handles.Add(Util.ConvertToNullTerminatedUnicodeByteArray(managedIndexCreates[i].pidxUnicode.GetEffectiveLocaleName())); nativeIndices[i].pidxUnicode = (NATIVE_UNICODEINDEX2*)handles.Add(unicode); nativeIndices[i].grbit |= (uint)VistaGrbits.IndexUnicode; } nativeIndices[i].szKey = handles.Add(Util.ConvertToNullTerminatedUnicodeByteArray(managedIndexCreates[i].szKey)); nativeIndices[i].szIndexName = handles.Add(Util.ConvertToNullTerminatedUnicodeByteArray(managedIndexCreates[i].szIndexName)); nativeIndices[i].rgconditionalcolumn = GetNativeConditionalColumns(managedIndexCreates[i].rgconditionalcolumn, true, ref handles); // Convert pSpaceHints. if (managedIndexCreates[i].pSpaceHints != null) { NATIVE_SPACEHINTS nativeSpaceHints = managedIndexCreates[i].pSpaceHints.GetNativeSpaceHints(); nativeIndices[i].pSpaceHints = handles.Add(nativeSpaceHints); } } } return nativeIndices; } /// /// Creates indexes over data in an ESE database. /// /// The session to use. /// The table to create the index on. /// Array of objects describing the indexes to be created. /// Number of index description objects. /// An error code. private static int CreateIndexes3(JET_SESID sesid, JET_TABLEID tableid, IList indexcreates, int numIndexCreates) { // pin the memory var handles = new GCHandleCollection(); try { NATIVE_INDEXCREATE3[] nativeIndexcreates = GetNativeIndexCreate3s(indexcreates, ref handles); return Err(NativeMethods.JetCreateIndex4W(sesid.Value, tableid.Value, nativeIndexcreates, checked((uint)numIndexCreates))); } finally { handles.Dispose(); } } /// /// Creates a table, adds columns, and indices on that table. /// /// The session to use. /// The database to which to add the new table. /// Object describing the table to create. /// An error if the call fails. private static int CreateTableColumnIndex4( JET_SESID sesid, JET_DBID dbid, JET_TABLECREATE tablecreate) { NATIVE_TABLECREATE4 nativeTableCreate = tablecreate.GetNativeTableCreate4(); unsafe { var handles = new GCHandleCollection(); try { // Convert/pin the column definitions. nativeTableCreate.rgcolumncreate = (NATIVE_COLUMNCREATE*)GetNativeColumnCreates(tablecreate.rgcolumncreate, true, ref handles); // Convert/pin the index definitions. NATIVE_INDEXCREATE3[] nativeIndexCreates = GetNativeIndexCreate3s(tablecreate.rgindexcreate, ref handles); nativeTableCreate.rgindexcreate = handles.Add(nativeIndexCreates); // Convert/pin the space hints. if (tablecreate.pSeqSpacehints != null) { NATIVE_SPACEHINTS nativeSpaceHints = tablecreate.pSeqSpacehints.GetNativeSpaceHints(); nativeTableCreate.pSeqSpacehints = (NATIVE_SPACEHINTS*)handles.Add(nativeSpaceHints); } if (tablecreate.pLVSpacehints != null) { NATIVE_SPACEHINTS nativeSpaceHints = tablecreate.pLVSpacehints.GetNativeSpaceHints(); nativeTableCreate.pLVSpacehints = (NATIVE_SPACEHINTS*)handles.Add(nativeSpaceHints); } int err = NativeMethods.JetCreateTableColumnIndex4W(sesid.Value, dbid.Value, ref nativeTableCreate); // Modified fields. tablecreate.tableid = new JET_TABLEID { Value = nativeTableCreate.tableid }; tablecreate.cCreated = checked((int)nativeTableCreate.cCreated); if (tablecreate.rgcolumncreate != null) { for (int i = 0; i < tablecreate.rgcolumncreate.Length; ++i) { tablecreate.rgcolumncreate[i].SetFromNativeColumnCreate(ref nativeTableCreate.rgcolumncreate[i]); } } if (tablecreate.rgindexcreate != null) { for (int i = 0; i < tablecreate.rgindexcreate.Length; ++i) { tablecreate.rgindexcreate[i].SetFromNativeIndexCreate(ref nativeIndexCreates[i]); } } return Err(err); } finally { handles.Dispose(); } } } // Do not add new public functions here, go above private functions above ... #endregion } }