//----------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. // //----------------------------------------------------------------------- namespace Microsoft.Isam.Esent.Interop { using System; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Runtime.CompilerServices; using System.Security.Permissions; using Microsoft.Isam.Esent.Interop.Vista; using Microsoft.Win32.SafeHandles; /// /// A class that encapsulates a in a disposable object. The /// instance must be closed last and closing the instance releases all other /// resources for the instance. /// public class Instance : SafeHandleZeroOrMinusOneIsInvalid { /// /// Parameters for the instance. /// private readonly InstanceParameters parameters; /// /// The name of the instance. /// private readonly string name; /// /// The display name of the instance. /// private readonly string displayName; /// /// The TermGrbit to be used at JetTerm time. /// private TermGrbit termGrbit; /// /// Initializes a new instance of the Instance class. The underlying /// JET_INSTANCE is allocated, but not initialized. /// /// /// The name of the instance. This string must be unique within a /// given process hosting the database engine. /// [SecurityPermissionAttribute(SecurityAction.LinkDemand)] public Instance(string name) : this(name, name, TermGrbit.None) { } /// /// Initializes a new instance of the Instance class. The underlying /// JET_INSTANCE is allocated, but not initialized. /// /// /// The name of the instance. This string must be unique within a /// given process hosting the database engine. /// /// /// A display name for the instance. This will be used in eventlog /// entries. /// [SecurityPermissionAttribute(SecurityAction.LinkDemand)] public Instance(string name, string displayName) : this(name, displayName, TermGrbit.None) { } /// /// Initializes a new instance of the Instance class. The underlying /// JET_INSTANCE is allocated, but not initialized. /// /// /// The name of the instance. This string must be unique within a /// given process hosting the database engine. /// /// /// A display name for the instance. This will be used in eventlog /// entries. /// /// /// The TermGrbit to be used at JetTerm time. /// [SecurityPermissionAttribute(SecurityAction.LinkDemand)] public Instance(string name, string displayName, TermGrbit termGrbit) : base(true) { this.name = name; this.displayName = displayName; this.termGrbit = termGrbit; JET_INSTANCE instance; RuntimeHelpers.PrepareConstrainedRegions(); try { this.SetHandle(JET_INSTANCE.Nil.Value); } finally { // This is the code that we want in a constrained execution region. // We need to avoid the situation where JetCreateInstance2 is called // but the handle isn't set, so the instance is never terminated. // This would happen, for example, if there was a ThreadAbortException // between the call to JetCreateInstance2 and the call to SetHandle. // // If an Esent exception is generated we do not want to call SetHandle // because the instance isn't valid. On the other hand if a different // exception (out of memory or thread abort) is generated we still need // to set the handle to avoid losing track of the instance. The call to // JetCreateInstance2 is in the CER to make sure that the only exceptions // which can be generated are from ESENT failures. Api.JetCreateInstance2(out instance, this.name, this.displayName, CreateInstanceGrbit.None); this.SetHandle(instance.Value); } this.parameters = new InstanceParameters(instance); } /// /// Gets the JET_INSTANCE that this instance contains. /// public JET_INSTANCE JetInstance { [SecurityPermissionAttribute(SecurityAction.LinkDemand)] get { this.CheckObjectIsNotDisposed(); return this.CreateInstanceFromHandle(); } } /// /// Gets the InstanceParameters for this instance. /// public InstanceParameters Parameters { [SecurityPermissionAttribute(SecurityAction.LinkDemand)] get { this.CheckObjectIsNotDisposed(); return this.parameters; } } /// /// Gets or sets the TermGrbit for this instance. /// public TermGrbit TermGrbit { [SecurityPermissionAttribute(SecurityAction.LinkDemand)] get { this.CheckObjectIsNotDisposed(); return this.termGrbit; } [SecurityPermissionAttribute(SecurityAction.LinkDemand)] set { this.CheckObjectIsNotDisposed(); this.termGrbit = value; } } /// /// Provide implicit conversion of an Instance object to a JET_INSTANCE /// structure. This is done so that an Instance can be used anywhere a /// JET_INSTANCE is required. /// /// The instance to convert. /// The JET_INSTANCE wrapped by the instance. [SecurityPermissionAttribute(SecurityAction.LinkDemand)] public static implicit operator JET_INSTANCE(Instance instance) { return instance.JetInstance; } /// /// Returns a that represents the current . /// /// /// A that represents the current . /// public override string ToString() { return string.Format(CultureInfo.InvariantCulture, "{0} ({1})", this.displayName, this.name); } /// /// Initialize the JET_INSTANCE. /// [SecurityPermissionAttribute(SecurityAction.LinkDemand)] public void Init() { this.Init(InitGrbit.None); } /// /// Initialize the JET_INSTANCE. /// /// /// Initialization options. /// [SecurityPermissionAttribute(SecurityAction.LinkDemand)] public void Init(InitGrbit grbit) { this.CheckObjectIsNotDisposed(); JET_INSTANCE instance = this.JetInstance; // Use a constrained region so that the handle is // always set after JetInit2 is called. RuntimeHelpers.PrepareConstrainedRegions(); try { // Remember that a failure in JetInit can zero the handle // and that JetTerm should not be called in that case. Api.JetInit2(ref instance, grbit); } finally { this.SetHandle(instance.Value); } } /// /// Initialize the JET_INSTANCE. This API requires at least the /// Vista version of ESENT. /// /// /// Additional recovery parameters for remapping databases during /// recovery, position where to stop recovery at, or recovery status. /// /// /// Initialization options. /// [SecurityPermissionAttribute(SecurityAction.LinkDemand)] public void Init(JET_RSTINFO recoveryOptions, InitGrbit grbit) { this.CheckObjectIsNotDisposed(); JET_INSTANCE instance = this.JetInstance; // Use a constrained region so that the handle is // always set after JetInit3 is called. RuntimeHelpers.PrepareConstrainedRegions(); try { // Remember that a failure in JetInit can zero the handle // and that JetTerm should not be called in that case. VistaApi.JetInit3(ref instance, recoveryOptions, grbit); } finally { this.SetHandle(instance.Value); } } /// /// Terminate the JET_INSTANCE. /// [SuppressMessage( "Microsoft.StyleCop.CSharp.MaintainabilityRules", "SA1409:RemoveUnnecessaryCode", Justification = "CER code belongs in the finally block, so the try clause is empty")] [SecurityPermissionAttribute(SecurityAction.LinkDemand)] public void Term() { // Use a constrained region so that the handle is // always set as invalid after JetTerm is called. RuntimeHelpers.PrepareConstrainedRegions(); try { // This try block deliberately left blank. } finally { // This is the code that we want in a constrained execution region. // We need to avoid the situation where JetTerm is called // but the handle isn't invalidated, so the instance is terminated again. // This would happen, for example, if there was a ThreadAbortException // between the call to JetTerm and the call to SetHandle. // // If an Esent exception is generated we do not want to invalidate the handle // because the instance isn't necessarily terminated. On the other hand if a // different exception (out of memory or thread abort) is generated we still need // to invalidate the handle. try { Api.JetTerm2(this.JetInstance, this.termGrbit); } catch (EsentDirtyShutdownException) { this.SetHandleAsInvalid(); throw; } this.SetHandleAsInvalid(); } } /// /// Release the handle for this instance. /// /// True if the handle could be released. protected override bool ReleaseHandle() { // The object is already marked as invalid so don't check var instance = this.CreateInstanceFromHandle(); return (int)JET_err.Success == Api.Impl.JetTerm2(instance, this.termGrbit); } /// /// Create a JET_INSTANCE from the internal handle value. /// /// A JET_INSTANCE containing the internal handle. private JET_INSTANCE CreateInstanceFromHandle() { return new JET_INSTANCE { Value = this.handle }; } /// /// Check to see if this instance is invalid or closed. /// [SecurityPermissionAttribute(SecurityAction.LinkDemand)] private void CheckObjectIsNotDisposed() { if (this.IsInvalid || this.IsClosed) { throw new ObjectDisposedException("Instance"); } } } }