using System; using System.Runtime.InteropServices; using Vanara.InteropServices; using LSN = System.Int64; namespace Vanara.PInvoke; public static partial class Ole32 { /// Specifies a hint about the order in which records are to be read from a log. // https://docs.microsoft.com/en-us/windows/win32/api/txlogpub/ne-txlogpub-record_reading_policy typedef enum RECORD_READING_POLICY { // RECORD_READING_POLICY_FORWARD = 1, RECORD_READING_POLICY_BACKWARD = 2, RECORD_READING_POLICY_RANDOM = 3 } ; [PInvokeData("txlogpub.h", MSDNShortId = "NE:txlogpub.RECORD_READING_POLICY")] public enum RECORD_READING_POLICY { /// /// Value: /// 1 /// Indicates that records will be read in order of increasing LSN (from least recent to most recent). /// RECORD_READING_POLICY_FORWARD = 1, /// /// Value: /// 2 /// Indicates that records will be read in order of decreasing LSN (from most recent to least recent). /// RECORD_READING_POLICY_BACKWARD, /// /// Value: /// 3 /// Indicates that records may be read in any order. /// RECORD_READING_POLICY_RANDOM, } /// Initializes an instance of a file based implementation of ILog. // https://docs.microsoft.com/en-us/windows/win32/api/txlogpub/nn-txlogpub-ifilebasedloginit [PInvokeData("txlogpub.h", MSDNShortId = "NN:txlogpub.IFileBasedLogInit")] [ComImport, Guid("00951E8C-1294-11d1-97E4-00C04FB9618A"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), CoClass(typeof(SimpleFileBasedLog))] public interface IFileBasedLogInit { /// Create a new log instance on the specified file. If a file with that name already exists, it is overwritten. /// The absolute path of the file to be created. /// A hint to the implementation about the total capacity that will be needed. // https://docs.microsoft.com/en-us/windows/win32/api/txlogpub/nf-txlogpub-ifilebasedloginit-initnew HRESULT InitNew( [in] LPCWSTR // filename, [in] ULONG cbCapacityHint ); void InitNew([MarshalAs(UnmanagedType.LPWStr)] string filename, uint cbCapacityHint); } /// /// Provides generic low-level logging functionality. /// The Common Log File System (CLFS), provides functionality that is a superset of that provided by ILog. /// /// /// /// WAL is a technique used by certain applications, such as database management systems, to implement atomic and isolated transactions. /// This technique involves writing records of changes to the application's resources to a log before you make these changes. This way /// the changes can be reverted if they are required, for example if the transaction fails or is interrupted. In order for applications /// to provide transactions that are robust against interruptions such as system crash or power failure, the logging implementation must /// provide a method for forcing the log; that is, to make sure that previously written records are on disk before continuing. /// /// /// Writing records that use ILog is a sequential operation; that is, new records are always appended to the end of the log. Each /// record appended to the log is assigned a log sequence number (LSN), a numeric identifier which may be used to retrieve the record /// later. The data type LSN is a typedef for LARGE_INTEGER, a signed 64-bit value; however, ILog uses only LSNs with nonnegative /// values. In addition, LSNs must satisfy the following conditions: /// /// /// /// /// LSNs are monotonically increasing; if record B is written to the log after record A, the LSN of record B must be larger than the LSN /// of record A. /// /// /// /// /// Values of zero and MAXLSN (0x7FFFFFFFFFFFFFFF) must never be used as the LSN of a record, as they have special meaning to some of the /// methods of ILog. /// /// /// /// /// Other than the conditions here, no assumptions should be made about how LSNs are assigned by an implementation of ILog. In /// particular, it is not safe to assume that records will be assigned sequential values for LSNs. /// /// /// After a record is appended to the log, it may not be modified. However, when previously written records are no longer needed, for /// example records of changes in a transaction that has already been committed, ILog supports truncating the log. This way, disk /// space that was used for nonessential records may be reused. Truncating the log consists of deleting all records with an LSN less than /// a specified value. /// /// /// As a performance optimization, some implementations of ILog may buffer records in memory until the log is forced. If this is /// the case, special you must consider error control and recovery. Consider the following situation: /// /// /// /// /// Record A is appended to the log, but the log is not forced. The ILog implementation copies the record to a buffer in memory /// and returns a success code. /// /// /// /// /// Record B is appended to the log, and the ILog implementation decides to force the log to disk. This is either because the /// caller asked the log to be forced or because the memory buffer is full. However, the write operation fails, for example because of /// low disk space. /// /// /// /// /// In this situation, it would be inappropriate for the ILog implementation to enable additional records to be appended to the /// log, unless it can guarantee that all records for which it returned a success code are first written to disk. One possible method of /// error control would be to pin the log in an error state when this situation occurs, permanently disallowing additional writes to the /// log instance. Callers that do not force the log to disk for each appended record should realize that this situation may occur and be /// able to handle it appropriately. /// /// ILog File-based Implementation /// /// The Windows operating system provides a file-based implementation of ILog, which enables you to create a log suited for /// write-ahead logging on a file. The log uses a file as a circular buffer, which enables unused space to be reused. This may also /// increase the size of the file that may be needed to fit additional records when the log is full. Changes to the log are made /// atomically, so that the contents of the log may be recovered after a crash. This implementation uses a buffer in memory for appending /// log records. As a result, records are not guaranteed to be written to disk when the ILog::AppendRecord method returns, unless the /// caller requests that the log be forced. /// /// Use the following CLSID to create an instance of a file based log (see CoCreateInstance): /// CLSID_SimpleFileBasedLog ({E16C0593-128F-11D1-97E4-00C04FB9618A} ). /// /// The file based implementation of ILog additionally supports the IFileBasedLogInit and IPersistFile interfaces. Use /// IFileBasedLogInit::InitNew to create a new log file. Use IPersistFile::Load to open an existing log file. /// /// /// This implementation uses a simple error control policy. If any one of the methods fails because of an error on the file-system level, /// which includes a disk full error, the log is pinned in an error state. This prevents clients from appending additional records to the /// file or reading potentially bad records. To continue to use the log file, you must create a new instance of the log. /// /// // https://docs.microsoft.com/en-us/windows/win32/api/txlogpub/nn-txlogpub-ilog [PInvokeData("txlogpub.h", MSDNShortId = "NN:txlogpub.ILog")] [ComImport, Guid("FF222117-0C6C-11d2-B89A-00C04FB9618A"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] public interface ILog { /// Forces the contents of the log to disk, at least up through the specified LSN. /// /// At the very least, all records that have not yet been written to disk with an LSN less than or equal to lsnMinToForce must /// be written to disk now. An implementation may, however, choose to write more records than what is strictly required. For example, /// an implementation is allowed to force all records to disk, regardless of the value of lsnMinToForce. Passing 0 as /// lsnMinToForce indicates that the entire log is to be forced to disk. /// /// /// The log may also be forced to disk after appending individual records. See ILog::AppendRecord. /// Notes to Callers /// /// A failure return value indicates that any records appended to the log since the last time it was successfully forced are not /// guaranteed to be on disk. The ILog interface does not provide a method to determine which records have been successfully written /// to disk. If you need to know which records were successfully written to disk, you must force the log for each record. See ILog::AppendRecord. /// /// Notes to Implementers /// /// It is recommended that you flush file buffers (for example, using the FlushFileBuffers function) before returning from this method. /// /// // https://docs.microsoft.com/en-us/windows/win32/api/txlogpub/nf-txlogpub-ilog-force HRESULT Force( [in] LSN lsnMinToForce ); void Force(LSN lsnMinToForce); /// Write a new record to the end of the log. /// A pointer to an array of BLOBs of data to be written. /// The size of the rgBlob array, in elements. /// /// Indicates whether to force the data to disk. If TRUE, the contents of the log up to this record must be forced to disk /// before the call returns. If FALSE, this record may be buffered in memory to be written after the call returns successfully. /// /// /// A pointer to the LSN of the newly appended record. If the LSN of the newly appended record is not needed, this parameter can be NULL. /// /// /// /// Each log record written or read by ILog is an opaque BLOB of data. As a convenience to callers, AppendRecord allows /// multiple BLOBs to be concatenated into a single record; because many implementations of ILog will copy records to a buffer /// in memory, it may be inefficient for the caller to allocate memory for concatenating the parts of a record. However, once a /// record is appended to the log, ILog provides no method to extract individual BLOBs from the record. It is the /// responsibility of the caller to parse the data in records read from the log. See ILog::ReadRecord. /// /// Notes to Callers /// /// A failure return value indicates that any records appended to the log since the last time it was successfully forced are not /// guaranteed to be on disk. The ILog interface does not provide a method to determine which records have been successfully written /// to disk. If you need to know which records were successfully written to disk, you must force the log for each record. /// /// Notes to Implementers /// /// If fForceNow is TRUE, it is recommended that you flush file buffers (for example, using the FlushFileBuffers /// function) before returning from this method. /// /// // https://docs.microsoft.com/en-us/windows/win32/api/txlogpub/nf-txlogpub-ilog-appendrecord HRESULT AppendRecord( [in] BLOB *rgBlob, // [in] ULONG cBlob, [in] BOOL fForceNow, [in, out] LSN *plsn ); void AppendRecord([In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] BLOB[] rgBlob, uint cBlob, [MarshalAs(UnmanagedType.Bool)] bool fForceNow, out LSN plsn); /// Read a record from the log. /// The LSN of the record to be read. /// /// A pointer to the LSN of the previous record (the record immediately preceding the record to be read). This parameter can be /// NULL if the LSN of the previous record is not needed. This parameter is 0 if there is no previous record in the log, or if /// an error occurs. /// /// /// A pointer to the LSN of the next record (the record immediately following the record to read). This parameter can be NULL /// if the LSN of the next record is not needed. This parameter is MAXLSN (0x7FFFFFFFFFFFFFFF) if there is no next record in the log. /// This parameter is 0 if an error occurs. /// /// /// A pointer to a variable that will contain a pointer to the record data on return. The memory for this data is allocated by /// ReadRecord and freed by the caller (see CoTaskMemFree). This parameter is NULL if an error occurs. /// /// A pointer to a variable that receives the size of the record data, in bytes, on return. /// /// /// Although records appended to the log using ILog::AppendRecord may be concatenated from multiple BLOBs, ReadRecord returns /// the record as a single opaque blob of data. ILog provides no method to extract individual BLOBs from the record. It is the /// responsibility of the caller to parse the data in records returned by ReadRecord. /// /// Notes to Callers /// /// If the log contains very large records, this method may fail because ReadRecord was unable to allocate sufficient memory /// for the record data. If the size of records is bounded or if you only need an initial part of the record, it may be more /// efficient to call ILog::ReadRecordPrefix. /// /// // https://docs.microsoft.com/en-us/windows/win32/api/txlogpub/nf-txlogpub-ilog-readrecord HRESULT ReadRecord( [in] LSN lsnToRead, // [in, out] LSN *plsnPrev, [in, out] LSN *plsnNext, [out] BYTE **ppbData, [out] ULONG *pcbData ); unsafe void ReadRecord(LSN lsnToRead, [In, Out, Optional] LSN* plsnPrev, [In, Out, Optional] LSN* plsnNext, out SafeCoTaskMemHandle ppbData, out uint pcbData); /// Reads an initial part of a record from the log. /// The LSN of the record to be read. /// /// A pointer to the LSN of the previous record (the record immediately preceding the record to read). You may pass NULL if /// the LSN of the previous record is not needed. This parameter is 0 if there is no previous record in the log or if an error occurs. /// /// /// A pointer to the LSN of the next record (the record immediately following the record to read). You may pass NULL if the /// LSN of the next record is not needed. This parameter is MAXLSN (0x7FFFFFFFFFFFFFFF) if there is no next record in the log. This /// parameter is 0 if an error occurs. /// /// A pointer to a buffer into which the record data is to be read. /// /// A pointer to a variable that contains the size in bytes of the buffer on input, and will contain the size in bytes of the record /// data read on return. /// /// /// A pointer to a variable that will contain the size in bytes of the entire record on return. You may pass NULL if the size /// of the entire record is not needed. /// /// /// Although records appended to the log using ILog::AppendRecord may be concatenated from multiple BLOBs, ReadRecordPrefix /// returns the record as a single opaque blob of data. ILog provides no method to extract individual BLOBs from the record. It is /// the responsibility of the caller to parse the data in records returned by ReadRecordPrefix. /// // https://docs.microsoft.com/en-us/windows/win32/api/txlogpub/nf-txlogpub-ilog-readrecordprefix HRESULT ReadRecordPrefix( [in] LSN // lsnToRead, [in, out] LSN *plsnPrev, [in, out] LSN *plsnNext, [out] BYTE *pbData, [in, out] ULONG *pcbData, [out] ULONG *pcbRecord ); unsafe void ReadRecordPrefix(LSN lsnToRead, [In, Out, Optional] LSN* plsnPrev, [In, Out, Optional] LSN* plsnNext, [Out] IntPtr pbData, ref uint pcbData, out uint pcbRecord); /// Retrieves information about the current bounds of the log. /// /// A pointer to the LSN of the first record in the log. This parameter can be NULL if the LSN of the first record is not needed. /// /// /// A pointer to the LSN of the last record in the log. This parameter can be NULL if the LSN of the last record is not needed. /// /// The limits returned by this method may include records that have not yet been written to disk. // https://docs.microsoft.com/en-us/windows/win32/api/txlogpub/nf-txlogpub-ilog-getloglimits HRESULT GetLogLimits( [in, out] LSN // *plsnFirst, [in, out] LSN *plsnLast ); unsafe void GetLogLimits([In, Out, Optional] LSN* plsnFirst, [In, Out, Optional] LSN* plsnLast); /// Throws away the specified prefix of the log, making it no longer retrievable. /// The LSN of the first record not to be thrown away. If this parameter is 0, the entire log is emptied. /// /// This request is only a hint to the log implementation. The log is free to ignore the request, or to retain more than was strictly /// requested. Many ILog implementations will follow this latter option. /// // https://docs.microsoft.com/en-us/windows/win32/api/txlogpub/nf-txlogpub-ilog-truncateprefix HRESULT TruncatePrefix( [in] LSN // lsnFirstToKeep ); void TruncatePrefix(LSN lsnFirstToKeep); /// Provides a hint to the implementation about the pattern in which records will be read. /// /// The pattern in which records will most often be read. For more information, see the RECORD_READING_POLICY enumeration. /// /// /// Not all implementations of ILog will be optimized for reading records in a particular pattern. An implementation may choose to /// ignore this request and return S_OK. /// // https://docs.microsoft.com/en-us/windows/win32/api/txlogpub/nf-txlogpub-ilog-setaccesspolicyhint HRESULT SetAccessPolicyHint( [in] // RECORD_READING_POLICY policy ); void SetAccessPolicyHint(RECORD_READING_POLICY policy); } /// CLSID_SimpleFileBasedLog [ComImport, Guid("e16c0593-128f-11d1-97e4-00c04fb9618a"), ClassInterface(ClassInterfaceType.None)] public class SimpleFileBasedLog { } }