A collection of GCHandles for pinned objects. The handles are freed when this object is disposed.
 public void VerifyAddingNullReturnsIntPtrZero()
 {
     using (var handles = new GCHandleCollection())
     {
         IntPtr p = handles.Add(null);
         Assert.AreEqual(IntPtr.Zero, p);
     }
 }
 public void VerifyAddGivesDifferentPointers()
 {
     using (var handles = new GCHandleCollection())
     {
         IntPtr p1 = handles.Add("foo");
         IntPtr p2 = handles.Add("bar");
         Assert.AreNotEqual(p1, p2);
     }
 }
 public void VerifyAddGivesPointerToObject()
 {
     using (var handles = new GCHandleCollection())
     {
         IntPtr p = handles.Add("expected");
         string actual = Marshal.PtrToStringUni(p);
         Assert.AreEqual("expected", actual);
     }
 }
 public void VerifyAddPinsObject()
 {
     var expected = new string('x', 5);
     var weakref = new WeakReference(expected);
     using (var handles = new GCHandleCollection())
     {
         handles.Add(expected);
         expected = null;
         GC.Collect();
         Assert.IsTrue(weakref.IsAlive);
     }
 }
 public void GCHandleCollectionStress()
 {
     for (int i = 0; i < 1000; i++)
     {
         using (var handles = new GCHandleCollection())
         {
             for (int j = 0; j < 100; j++)
             {
                 IntPtr p = handles.Add(new byte[1]);
                 Assert.AreNotEqual(IntPtr.Zero, p);
             }
         }
     }
 }
        /// <summary>
        /// Creates indexes over data in an ESE database.
        /// </summary>
        /// <param name="sesid">The session to use.</param>
        /// <param name="tableid">The table to create the index on.</param>
        /// <param name="indexcreates">Array of objects describing the indexes to be created.</param>
        /// <param name="numIndexCreates">Number of index description objects.</param>
        /// <returns>An error code.</returns>
        private static int CreateIndexes3(JET_SESID sesid, JET_TABLEID tableid, IList <JET_INDEXCREATE> 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();
            }
        }
        /// <summary>
        /// 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.
        /// </summary>
        /// <param name="sesid">The session to use.</param>
        /// <param name="tableid">The table to issue the prereads against.</param>
        /// <param name="indexRanges">The key ranges to preread.</param>
        /// <param name="rangeIndex">The index of the first key range in the array to read.</param>
        /// <param name="rangeCount">The maximum number of key ranges to preread.</param>
        /// <param name="rangesPreread">Returns the number of keys actually preread.</param>
        /// <param name="columnsPreread">List of column ids for long value columns to preread.</param>
        /// <param name="grbit">Preread options. Used to specify the direction of the preread.</param>
        /// <returns>
        /// An error if the call fails.
        /// </returns>
        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();
            }
        }
        /// <summary>
        /// Make native indexcreate structures from the managed ones.
        /// </summary>
        /// <param name="managedIndexCreates">Index create structures to convert.</param>
        /// <param name="handles">The handle collection used to pin the data.</param>
        /// <returns>Pinned native versions of the index creates.</returns>
        private static unsafe NATIVE_INDEXCREATE3[] GetNativeIndexCreate3s(
            IList <JET_INDEXCREATE> 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);
        }
        /// <summary>
        /// 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.
        /// </summary>
        /// <param name="sesid">The session to use.</param>
        /// <param name="temporarytable">
        /// 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.
        /// </param>
        /// <returns>An error code.</returns>
        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);
                }
            }
        }
        /// <summary>
        /// Gets the NATIVE_indexcolumn structure that represents the object.
        /// </summary>
        /// <param name="handles">GC handle collection to add any pinned handles.</param>
        /// <returns>The NATIVE_indexcolumn structure.</returns>
        internal NATIVE_INDEX_RANGE GetNativeIndexRange(ref GCHandleCollection handles)
        {
            NATIVE_INDEX_RANGE indexRange = new NATIVE_INDEX_RANGE();
            NATIVE_INDEX_COLUMN[] nativeColumns;
            if (this.startColumns != null)
            {
                nativeColumns = new NATIVE_INDEX_COLUMN[this.startColumns.Length];
                for (int i = 0; i < this.startColumns.Length; i++)
                {
                    nativeColumns[i] = this.startColumns[i].GetNativeIndexColumn(ref handles);
                }

                indexRange.rgStartColumns = handles.Add(nativeColumns);
                indexRange.cStartColumns = (uint)this.startColumns.Length;
            }

            if (this.endColumns != null)
            {
                nativeColumns = new NATIVE_INDEX_COLUMN[this.endColumns.Length];
                for (int i = 0; i < this.endColumns.Length; i++)
                {
                    nativeColumns[i] = this.endColumns[i].GetNativeIndexColumn(ref handles);
                }

                indexRange.rgEndColumns = handles.Add(nativeColumns);
                indexRange.cEndColumns = (uint)this.endColumns.Length;
            }

            return indexRange;
        }
Exemple #11
0
        public static JET_wrn JetSetColumns(JET_SESID sesid, JET_TABLEID tableid, JET_SETCOLUMN[] setcolumns, int numColumns)
        {
            if (null == setcolumns)
            {
                throw new ArgumentNullException("setcolumns");
            }

            if (numColumns < 0 || numColumns > setcolumns.Length)
            {
                throw new ArgumentOutOfRangeException("numColumns", numColumns, "cannot be negative or greater than setcolumns.Length");
            }

            using (var gchandles = new GCHandleCollection())
            {
                unsafe
                {
                    NATIVE_SETCOLUMN* nativeSetColumns = stackalloc NATIVE_SETCOLUMN[numColumns];

                    // For performance, copy small amounts of data into a local buffer instead
                    // of pinning the data.
                    const int BufferSize = 128;
                    byte* buffer = stackalloc byte[BufferSize];
                    int bufferRemaining = BufferSize;

                    for (int i = 0; i < numColumns; ++i)
                    {
                        setcolumns[i].CheckDataSize();
                        nativeSetColumns[i] = setcolumns[i].GetNativeSetcolumn();
                        if (null == setcolumns[i].pvData)
                        {
                            nativeSetColumns[i].pvData = IntPtr.Zero;
                        }
                        else if (bufferRemaining >= setcolumns[i].cbData)
                        {
                            nativeSetColumns[i].pvData = new IntPtr(buffer);
                            Marshal.Copy(setcolumns[i].pvData, setcolumns[i].ibData, nativeSetColumns[i].pvData, setcolumns[i].cbData);
                            buffer += setcolumns[i].cbData;
                            bufferRemaining -= setcolumns[i].cbData;
                            Debug.Assert(bufferRemaining >= 0, "Buffer remaining is negative");
                        }
                        else
                        {
                            byte* pinnedBuffer = (byte*)gchandles.Add(setcolumns[i].pvData).ToPointer();
                            nativeSetColumns[i].pvData = new IntPtr(pinnedBuffer + setcolumns[i].ibData);
                        }
                    }

                    int err = Impl.JetSetColumns(sesid, tableid, nativeSetColumns, numColumns);
                    for (int i = 0; i < numColumns; ++i)
                    {
                        setcolumns[i].err = (JET_wrn)nativeSetColumns[i].err;
                    }

                    return Api.Check(err);
                }
            }
        }
        /// <summary>
        /// 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.
        /// </summary>
        /// <param name="sesid">The session to use.</param>
        /// <param name="temporarytable">
        /// 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.
        /// </param>
        /// <returns>An error code.</returns>
        public int JetOpenTemporaryTable2(JET_SESID sesid, JET_OPENTEMPORARYTABLE temporarytable)
        {
            TraceFunctionCall("JetOpenTemporaryTable2");
            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;
                }
            }
        }      
        /// <summary>
        /// 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.
        /// </summary>
        /// <param name="sesid">The session to use.</param>
        /// <param name="tableid">The table to issue the prereads against.</param>
        /// <param name="indexRanges">The key rangess to preread.</param>
        /// <param name="rangeIndex">The index of the first key range in the array to read.</param>
        /// <param name="rangeCount">The maximum number of key ranges to preread.</param>
        /// <param name="rangesPreread">Returns the number of keys actually preread.</param>
        /// <param name="columnsPreread">List of column ids for long value columns to preread.</param>
        /// <param name="grbit">Preread options. Used to specify the direction of the preread.</param>
        /// <returns>
        /// An error if the call fails.
        /// </returns>
        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("JetPrereadIndexRanges");
            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();
            }
        }
        public void VerifyDisposeUnpinsObjects()
        {
            var expected = new string('x', 5);
            var weakref = new WeakReference(expected);
            using (var handles = new GCHandleCollection())
            {
                handles.Add(expected);
                expected = null; // needed to allow GC to work
            }

            GC.Collect();
            Assert.IsFalse(weakref.IsAlive);
        }
        /// <summary>
        /// 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.
        /// </summary>
        /// <param name="sesid">The session to use.</param>
        /// <param name="tableid">The table to issue the prereads against.</param>
        /// <param name="keysStart">The start of key ranges to preread.</param>
        /// <param name="keyStartLengths">The lengths of the start keys to preread.</param>
        /// <param name="keysEnd">The end of key rangess to preread.</param>
        /// <param name="keyEndLengths">The lengths of the end keys to preread.</param>
        /// <param name="rangeIndex">The index of the first key range in the array to read.</param>
        /// <param name="rangeCount">The maximum number of key ranges to preread.</param>
        /// <param name="rangesPreread">Returns the number of keys actually preread.</param>
        /// <param name="columnsPreread">List of column ids for long value columns to preread.</param>
        /// <param name="grbit">Preread options. Used to specify the direction of the preread.</param>
        /// <returns>An error or warning.</returns>
        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("JetPrereadKeyRanges");
            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)));
                }
            }
        }
        /// <summary>
        /// Creates a table, adds columns, and indices on that table.
        /// </summary>
        /// <param name="sesid">The session to use.</param>
        /// <param name="dbid">The database to which to add the new table.</param>
        /// <param name="tablecreate">Object describing the table to create.</param>
        /// <returns>An error if the call fails.</returns>
        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();
                }
            }
        }
        /// <summary>
        /// Set an array of simple filters for <see cref="Api.JetMove(JET_SESID,JET_TABLEID,int,MoveGrbit)"/>
        /// </summary>
        /// <param name="sesid">The session to use for the call.</param>
        /// <param name="tableid">The cursor to position.</param>
        /// <param name="filters">Simple record filters.</param>
        /// <param name="grbit">Move options.</param>
        /// <returns>An error if the call fails.</returns>
        public int JetSetCursorFilter(JET_SESID sesid, JET_TABLEID tableid, JET_INDEX_COLUMN[] filters, CursorFilterGrbit grbit)
        {
            TraceFunctionCall("JetSetCursorFilter");
            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();
            }
        }
        /// <summary>
        /// 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.
        /// </summary>
        /// <param name="sesid">The session to use.</param>
        /// <param name="tableid">The table to issue the prereads against.</param>
        /// <param name="keysStart">The start of key ranges to preread.</param>
        /// <param name="keyStartLengths">The lengths of the start keys to preread.</param>
        /// <param name="keysEnd">The end of key rangess to preread.</param>
        /// <param name="keyEndLengths">The lengths of the end keys to preread.</param>
        /// <param name="rangeIndex">The index of the first key range in the array to read.</param>
        /// <param name="rangeCount">The maximum number of key ranges to preread.</param>
        /// <param name="rangesPreread">Returns the number of keys actually preread.</param>
        /// <param name="columnsPreread">List of column ids for long value columns to preread.</param>
        /// <param name="grbit">Preread options. Used to specify the direction of the preread.</param>
        /// <returns>An error or warning.</returns>
        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))));
                }
            }
        }
        /// <summary>
        /// Gets the NATIVE_indexcolumn structure that represents the object.
        /// </summary>
        /// <param name="handles">GC handle collection to add any pinned handles.</param>
        /// <returns>The NATIVE_indexcolumn structure.</returns>
        internal NATIVE_INDEX_COLUMN GetNativeIndexColumn(ref GCHandleCollection handles)
        {
            NATIVE_INDEX_COLUMN indexColumn = new NATIVE_INDEX_COLUMN();
            indexColumn.columnid = this.columnid.Value;
            indexColumn.relop = (uint)this.relop;
            indexColumn.grbit = (uint)this.grbit;
            if (this.pvData != null)
            {
                indexColumn.pvData = handles.Add(this.pvData);
                indexColumn.cbData = (uint)this.pvData.Length;
            }

            return indexColumn;
        }
        /// <summary>
        /// Creates a table, adds columns, and indices on that table.
        /// </summary>
        /// <param name="sesid">The session to use.</param>
        /// <param name="dbid">The database to which to add the new table.</param>
        /// <param name="tablecreate">Object describing the table to create.</param>
        /// <returns>An error if the call fails.</returns>
        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(nativeTableCreate.rgcolumncreate[i]);
                        }
                    }

                    if (tablecreate.rgindexcreate != null)
                    {
                        for (int i = 0; i < tablecreate.rgindexcreate.Length; ++i)
                        {
                            tablecreate.rgindexcreate[i].SetFromNativeIndexCreate(nativeIndexCreates[i]);
                        }
                    }

                    return Err(err);
                } 
                finally
                {
                    handles.Dispose();
                }
            }
        }
 /// <summary>
 /// Creates indexes over data in an ESE database.
 /// </summary>
 /// <param name="sesid">The session to use.</param>
 /// <param name="tableid">The table to create the index on.</param>
 /// <param name="indexcreates">Array of objects describing the indexes to be created.</param>
 /// <param name="numIndexCreates">Number of index description objects.</param>
 /// <returns>An error code.</returns>
 private static int CreateIndexes3(JET_SESID sesid, JET_TABLEID tableid, IList<JET_INDEXCREATE> 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();
     }
 }
        /// <summary>
        /// Make native indexcreate structures from the managed ones.
        /// </summary>
        /// <param name="managedIndexCreates">Index create structures to convert.</param>
        /// <param name="handles">The handle collection used to pin the data.</param>
        /// <returns>Pinned native versions of the index creates.</returns>
        private static unsafe NATIVE_INDEXCREATE3[] GetNativeIndexCreate3s(
            IList<JET_INDEXCREATE> 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;
        }