diff --git a/Interop.Windows/Dde/DdeSettings.cs b/Interop.Windows/Dde/DdeSettings.cs index ed75d2f9..84cd732b 100644 --- a/Interop.Windows/Dde/DdeSettings.cs +++ b/Interop.Windows/Dde/DdeSettings.cs @@ -7,23 +7,39 @@ using Ecng.Common; using Ecng.Serialization; + /// + /// Represents the settings for DDE (Dynamic Data Exchange) communication. + /// [DisplayName("DDE settings")] public class DdeSettings : Cloneable, IPersistable { + /// + /// Initializes a new instance of the class with default values. + /// public DdeSettings() { Server = "EXCEL"; Topic = "[Book1.xlsx]Sheet1"; } + /// + /// Gets or sets the DDE server name. + /// [Display(Name = "Server", Description = "DDE server name.", Order = 0)] public string Server { get; set; } + /// + /// Gets or sets the DDE topic name (for example, "[Book1.xlsx]Sheet1"). + /// [Display(Name = "Topic", Description = "Topic name (like [Book1.xlsx].Sheet1).", Order = 1)] public string Topic { get; set; } private int _columnOffset; + /// + /// Gets or sets the column offset from the left top corner. + /// + /// Thrown when a negative value is assigned. [Display(Name = "Column offset", Description = "Column offset from left top corner.", Order = 2)] public int ColumnOffset { @@ -39,6 +55,10 @@ public int ColumnOffset private int _rowOffset; + /// + /// Gets or sets the row offset from the left top corner. + /// + /// Thrown when a negative value is assigned. [Display(Name = "Row offset", Description = "Row offset from left top corner.", Order = 2)] public int RowOffset { @@ -52,19 +72,34 @@ public int RowOffset } } + /// + /// Gets or sets a value indicating whether header names should be displayed. + /// [Display(Name = "Headers", Description = "Show headers name.", Order = 2)] public bool ShowHeaders { get; set; } + /// + /// Applies the settings from the specified instance. + /// + /// The instance containing the new settings. public void Apply(DdeSettings clone) { PersistableHelper.Apply(this, clone); } + /// + /// Creates a deep copy of the current instance. + /// + /// A new instance that is a deep copy of this instance. public override DdeSettings Clone() { return PersistableHelper.Clone(this); } + /// + /// Loads the settings from the provided . + /// + /// The storage from which to load the settings. public void Load(SettingsStorage storage) { Server = storage.GetValue(nameof(Server)); @@ -74,6 +109,10 @@ public void Load(SettingsStorage storage) ShowHeaders = storage.GetValue(nameof(ShowHeaders)); } + /// + /// Saves the current settings to the provided . + /// + /// The storage to which the settings will be saved. public void Save(SettingsStorage storage) { storage diff --git a/Interop.Windows/Dde/XlsDdeClient.cs b/Interop.Windows/Dde/XlsDdeClient.cs index d57cbf35..be8efb2a 100644 --- a/Interop.Windows/Dde/XlsDdeClient.cs +++ b/Interop.Windows/Dde/XlsDdeClient.cs @@ -7,20 +7,36 @@ using NDde.Client; + /// + /// A client for interacting with Excel via Dynamic Data Exchange (DDE), providing methods to start, stop, and send data. + /// public class XlsDdeClient(DdeSettings settings) : Disposable { private DdeClient _client; + /// + /// Gets a value indicating whether the DDE client is currently started and connected. + /// public bool IsStarted => _client != null; + /// + /// Gets the settings used to configure the DDE client. + /// + /// Thrown when the settings provided during construction are null. public DdeSettings Settings { get; } = settings ?? throw new ArgumentNullException(nameof(settings)); + /// + /// Starts the DDE client and establishes a connection to the specified server and topic. + /// public void Start() { _client = new DdeClient(Settings.Server, Settings.Topic); _client.Connect(); } + /// + /// Stops the DDE client and disconnects from the server if currently connected. + /// public void Stop() { if (_client.IsConnected) @@ -29,6 +45,12 @@ public void Stop() _client = null; } + /// + /// Sends a block of data to Excel via DDE using the specified row and column offsets. + /// + /// A list of rows, where each row is a list of cell values to send. + /// Thrown when is null. + /// Thrown when is empty. public void Poke(IList> rows) { if (rows is null) @@ -48,6 +70,9 @@ public void Poke(IList> rows) XlsDdeSerializer.Serialize(rows), 0x0090 | 0x4000, (int)TimeSpan.FromSeconds(10).TotalMilliseconds); } + /// + /// Releases managed resources by stopping the DDE client if it is started. + /// protected override void DisposeManaged() { if (IsStarted) diff --git a/Interop.Windows/Dde/XlsDdeSerializer.cs b/Interop.Windows/Dde/XlsDdeSerializer.cs index 9bfb3155..bdff471e 100644 --- a/Interop.Windows/Dde/XlsDdeSerializer.cs +++ b/Interop.Windows/Dde/XlsDdeSerializer.cs @@ -7,6 +7,9 @@ using Ecng.Common; + /// + /// Provides methods for serializing and deserializing data to and from a format compatible with Excel DDE. + /// static class XlsDdeSerializer { private enum DataTypes : short @@ -54,6 +57,12 @@ private static DataTypes GetXlsType(object cell) throw new ArgumentException($"Unknown cell value type '{cell.GetType()}'.", nameof(cell)); } + /// + /// Serializes a table of data into a byte array compatible with Excel DDE. + /// + /// A list of rows, where each row is a list of cell values to serialize. + /// A byte array representing the serialized data. + /// Thrown when an unsupported cell type is encountered. public static byte[] Serialize(IList> rows) { var stream = new MemoryStream(); @@ -101,6 +110,12 @@ public static byte[] Serialize(IList> rows) return stream.ToArray(); } + /// + /// Deserializes a byte array from Excel DDE format into a table of data. + /// + /// The byte array containing the serialized DDE data. + /// A list of rows, where each row is a list of cell values. + /// Thrown when is null. public static IList> Deserialize(byte[] data) { if (data is null) diff --git a/Interop.Windows/Dde/XlsDdeServer.cs b/Interop.Windows/Dde/XlsDdeServer.cs index 2fd819f4..afc391c4 100644 --- a/Interop.Windows/Dde/XlsDdeServer.cs +++ b/Interop.Windows/Dde/XlsDdeServer.cs @@ -9,19 +9,34 @@ namespace Ecng.Interop.Dde using NDde.Server; + /// + /// Provides a DDE server for Excel that handles poke requests and advises clients of updated data. + /// [CLSCompliant(false)] public class XlsDdeServer(string service, Action>> poke, Action error) : DdeServer(service) { + /// + /// Private helper class that dispatches events on dedicated threads. + /// private class EventDispatcher(Action errorHandler) : Disposable { private readonly Action _errorHandler = errorHandler ?? throw new ArgumentNullException(nameof(errorHandler)); private readonly SynchronizedDictionary> _events = []; + /// + /// Adds an event to be executed. + /// + /// The event action to add. public void Add(Action evt) { Add(evt, string.Empty); } + /// + /// Adds an event to be executed with a synchronization token. + /// + /// The event action to add. + /// The synchronization token to group events. public virtual void Add(Action evt, string syncToken) { if (evt is null) @@ -63,6 +78,9 @@ private static BlockingQueue CreateNewThreadQueuePair(string syncToken) return queue; } + /// + /// Disposes the managed resources by closing all event queues. + /// protected override void DisposeManaged() { _events.SyncDo(d => d.ForEach(p => p.Value.Close())); @@ -76,6 +94,9 @@ protected override void DisposeManaged() private readonly Action>> _poke = poke ?? throw new ArgumentNullException(nameof(poke)); private readonly Action _error = error ?? throw new ArgumentNullException(nameof(error)); + /// + /// Starts the DDE server and initializes the timer to advise clients. + /// public void Start() { Exception error = null; @@ -125,6 +146,14 @@ public void Start() .Interval(TimeSpan.FromSeconds(1)); } + /// + /// Handles poke requests from DDE conversations. + /// + /// The DDE conversation instance. + /// The item name requested. + /// The data payload in byte array format. + /// The format of the data received. + /// A indicating that the poke has been processed. protected override PokeResult OnPoke(DdeConversation conversation, string item, byte[] data, int format) { _dispather.Add(() => @@ -136,6 +165,10 @@ protected override PokeResult OnPoke(DdeConversation conversation, string item, return PokeResult.Processed; } + /// + /// Releases the unmanaged resources and, optionally, the managed resources. + /// + /// True to release both managed and unmanaged resources; false to release only unmanaged resources. protected override void Dispose(bool disposing) { _dispather.Dispose(); diff --git a/Interop.Windows/WinApi.cs b/Interop.Windows/WinApi.cs index 8a4a838f..51de6c30 100644 --- a/Interop.Windows/WinApi.cs +++ b/Interop.Windows/WinApi.cs @@ -5,11 +5,20 @@ using Microsoft.Win32; - /// - /// + /// + /// Provides Windows API utility methods. + /// [CLSCompliant(false)] public static class WinApi { + /// + /// Retrieves the screen boundaries (left, top, width, height) for the screen that contains the specified window handle. + /// + /// The handle of the window. + /// Output parameter that returns the left coordinate of the screen. + /// Output parameter that returns the top coordinate of the screen. + /// Output parameter that returns the width of the screen. + /// Output parameter that returns the height of the screen. public static void GetScreenParams(IntPtr hwnd, out int left, out int top, out int width, out int height) { var activeScreen = Screen.FromHandle(hwnd); @@ -25,6 +34,13 @@ public static void GetScreenParams(IntPtr hwnd, out int left, out int top, out i private static RegistryKey BaseKey => Registry.CurrentUser; + /// + /// Updates the auto-run registry entry for a given application. + /// + /// The name of the application. + /// The full path to the application executable. + /// True to enable auto-run; false to disable it. + /// Thrown when the autorun registry key cannot be found. public static void UpdateAutoRun(string appName, string path, bool enabled) { using var key = BaseKey.OpenSubKey(_path, true) ?? throw new InvalidOperationException($"autorun not found ({_path})"); diff --git a/Interop.Windows/WindowsGrandAccess.cs b/Interop.Windows/WindowsGrandAccess.cs index 00781a6b..89efa578 100644 --- a/Interop.Windows/WindowsGrandAccess.cs +++ b/Interop.Windows/WindowsGrandAccess.cs @@ -11,9 +11,13 @@ using Ecng.Common; // https://stackoverflow.com/a/30687230/8029915 + +/// +/// Provides methods to modify access rights for the Windows window station and desktop. +/// public static class WindowsGrandAccess { - private class Token(WindowsGrandAccess.GenericSecurity wsSecurity, WindowsGrandAccess.GenericSecurity dsSecurity, int? oldWindowStationMaskm, int? oldDesktopMask) : Disposable + private class Token(GenericSecurity wsSecurity, WindowsGrandAccess.GenericSecurity dsSecurity, int? oldWindowStationMaskm, int? oldDesktopMask) : Disposable { private readonly GenericSecurity _wsSecurity = wsSecurity ?? throw new ArgumentNullException(nameof(wsSecurity)); private readonly GenericSecurity _dsSecurity = dsSecurity ?? throw new ArgumentNullException(nameof(dsSecurity)); @@ -35,6 +39,13 @@ protected override void DisposeManaged() private const int _desktopRightsAllAccess = 0x000f01ff; private const int _windowStationAllAccess = 0x000f037f; + /// + /// Grants full access rights to the current process window station and thread desktop for the specified user. + /// + /// The user account to which access rights are to be granted. + /// + /// An token that, when disposed, restores the original access rights. + /// public static IDisposable GrantAccessToWindowStationAndDesktop(string username) { var wsHandle = new NoopSafeHandle(PInvoke.GetProcessWindowStation()); diff --git a/Interop.Windows/WindowsThreadingHelper.cs b/Interop.Windows/WindowsThreadingHelper.cs index 2dcb322c..a42600cf 100644 --- a/Interop.Windows/WindowsThreadingHelper.cs +++ b/Interop.Windows/WindowsThreadingHelper.cs @@ -5,8 +5,17 @@ using Ecng.Common; + /// + /// Provides helper methods to run code on threads with specific apartment states. + /// public static class WindowsThreadingHelper { + /// + /// Sets the apartment state of the specified thread to single-threaded apartment (STA). + /// + /// The thread to set the apartment state for. + /// The same thread with the updated apartment state. + /// Thrown when the thread is null. public static Thread STA(this Thread thread) { if (thread is null) @@ -16,6 +25,12 @@ public static Thread STA(this Thread thread) return thread; } + /// + /// Sets the apartment state of the specified thread to multi-threaded apartment (MTA). + /// + /// The thread to set the apartment state for. + /// The same thread with the updated apartment state. + /// Thrown when the thread is null. public static Thread MTA(this Thread thread) { if (thread is null) @@ -25,6 +40,11 @@ public static Thread MTA(this Thread thread) return thread; } + /// + /// Invokes the specified action on a new STA (single-threaded apartment) thread. + /// + /// The action to invoke. + /// Thrown when the action is null. public static void InvokeAsSTA(this Action action) { if (action is null) @@ -38,6 +58,15 @@ public static void InvokeAsSTA(this Action action) } // http://stackoverflow.com/questions/518701/clipboard-gettext-returns-null-empty-string + + /// + /// Invokes the specified function on a new STA (single-threaded apartment) thread and returns a result. + /// + /// The type of the return value. + /// The function to invoke. + /// The result returned by the function. + /// Thrown when the function is null. + /// Throws any exception that occurs during the function execution. public static T InvokeAsSTA(this Func func) { if (func is null) diff --git a/Interop/BlittableDecimal.cs b/Interop/BlittableDecimal.cs index d8c6f3c6..745aa0ea 100644 --- a/Interop/BlittableDecimal.cs +++ b/Interop/BlittableDecimal.cs @@ -4,6 +4,9 @@ using Ecng.Common; + /// + /// Represents a blittable version of a decimal value. + /// [StructLayout(LayoutKind.Sequential)] public struct BlittableDecimal { @@ -12,6 +15,9 @@ public struct BlittableDecimal private int _bit2; private int _bit3; + /// + /// Gets or sets the decimal value. + /// public decimal Value { get => new[] { _bit0, _bit1, _bit2, _bit3 }.To(); @@ -26,16 +32,30 @@ public decimal Value } } + /// + /// Converts a decimal value to a . + /// + /// The decimal value to convert. + /// A representing the decimal value. public static explicit operator BlittableDecimal(decimal value) { return new BlittableDecimal { Value = value }; } + /// + /// Converts a to a decimal value. + /// + /// The to convert. + /// A decimal value. public static implicit operator decimal(BlittableDecimal value) { return value.Value; } + /// + /// Returns a string that represents the decimal value. + /// + /// A string representation of the decimal value. public override string ToString() => Value.ToString(); } } \ No newline at end of file diff --git a/Interop/DllLibrary.cs b/Interop/DllLibrary.cs index f57761e0..57cfdc93 100644 --- a/Interop/DllLibrary.cs +++ b/Interop/DllLibrary.cs @@ -5,21 +5,54 @@ namespace Ecng.Interop using Ecng.Common; + /// + /// Represents a base class that manages a DLL library and provides access to its exported functions. + /// + /// The file path to the DLL. public abstract class DllLibrary(string dllPath) : Disposable { + /// + /// Gets the file path to the DLL. + /// public string DllPath { get; private set; } = dllPath.ThrowIfEmpty(nameof(dllPath)); private Version _dllVersion; - public Version DllVersion => _dllVersion ??= FileVersionInfo.GetVersionInfo(DllPath).ProductVersion?.Replace(',', '.')?.RemoveSpaces()?.To(); + /// + /// Gets the version of the DLL by retrieving product information. + /// + public Version DllVersion => _dllVersion ??= + FileVersionInfo.GetVersionInfo(DllPath).ProductVersion? + .Replace(',', '.') + ?.RemoveSpaces() + ?.To(); + + /// + /// Gets the pointer to the loaded DLL. + /// protected IntPtr Handler { get; } = Marshaler.LoadLibrary(dllPath); + /// + /// Retrieves a function pointer from the DLL and casts it to the specified delegate type. + /// + /// The type of the delegate. + /// The name of the procedure to retrieve. + /// A delegate of type . protected T GetHandler(string procName) => Handler.GetHandler(procName); + /// + /// Attempts to retrieve a function pointer from the DLL as the specified delegate type. Returns null if not found. + /// + /// The type of the delegate. + /// The name of the procedure to retrieve. + /// A delegate of type , or null if the procedure is not found. protected T TryGetHandler(string procName) where T : Delegate => Handler.TryGetHandler(procName); + /// + /// Disposes native resources associated with the DLL. + /// protected override void DisposeNative() { Handler.FreeLibrary(); diff --git a/Interop/GCHandle.cs b/Interop/GCHandle.cs index 651ff8a6..5672d840 100644 --- a/Interop/GCHandle.cs +++ b/Interop/GCHandle.cs @@ -41,6 +41,10 @@ public GCHandle(T value, GCHandleType type, int? size) _size = size; } + /// + /// Create safe pointer. + /// + /// public SafePointer CreatePointer() => new(Value.AddrOfPinnedObject(), _size); /// diff --git a/Interop/HGlobalSafeHandle.cs b/Interop/HGlobalSafeHandle.cs index a31dd8e9..cdca5915 100644 --- a/Interop/HGlobalSafeHandle.cs +++ b/Interop/HGlobalSafeHandle.cs @@ -4,14 +4,25 @@ using Microsoft.Win32.SafeHandles; + /// + /// Represents a safe handle for unmanaged memory allocated with HGlobal. + /// public class HGlobalSafeHandle : SafeHandleZeroOrMinusOneIsInvalid { + /// + /// Initializes a new instance of the class with the specified pointer. + /// + /// An that represents the allocated unmanaged memory. public HGlobalSafeHandle(IntPtr ptr) : base(true) { SetHandle(ptr); } + /// + /// Releases the unmanaged memory by freeing the HGlobal allocation. + /// + /// true if the handle is released successfully; otherwise, false. protected override bool ReleaseHandle() { DangerousGetHandle().FreeHGlobal(); diff --git a/Interop/HardwareInfo.cs b/Interop/HardwareInfo.cs index d4876650..a37acb41 100644 --- a/Interop/HardwareInfo.cs +++ b/Interop/HardwareInfo.cs @@ -14,11 +14,23 @@ namespace Ecng.Interop using WmiLight; + /// + /// Provides methods to generate a hardware-based identifier. + /// public static class HardwareInfo { + /// + /// Gets the hardware identifier. + /// + /// A string representing the hardware identifier. public static string GetId() => AsyncContext.Run(() => GetIdAsync()); + /// + /// Asynchronously gets the hardware identifier. + /// + /// A token to monitor for cancellation requests. + /// A task that represents the asynchronous operation. The task result contains a string representing the hardware identifier. public static async Task GetIdAsync(CancellationToken cancellationToken = default) { string id; diff --git a/Interop/IExcelWorker.cs b/Interop/IExcelWorker.cs index f58b68b9..6f88a5cf 100644 --- a/Interop/IExcelWorker.cs +++ b/Interop/IExcelWorker.cs @@ -5,28 +5,115 @@ using Ecng.Common; + /// + /// Defines a contract for working with Excel files, providing methods to manipulate cells, styles, sheets, and formatting. + /// public interface IExcelWorker : IDisposable { + /// + /// Sets the value of a cell at the specified column and row. + /// + /// The type of the value to set. + /// The column index (1-based). + /// The row index (1-based). + /// The value to set in the cell. + /// The current instance for method chaining. IExcelWorker SetCell(int col, int row, T value); + + /// + /// Gets the value of a cell at the specified column and row. + /// + /// The type of the value to retrieve. + /// The column index (1-based). + /// The row index (1-based). + /// The value of the cell cast to type . T GetCell(int col, int row); + /// + /// Sets the style of a column based on a specified type. + /// + /// The column index (1-based). + /// The that determines the style. + /// The current instance for method chaining. IExcelWorker SetStyle(int col, Type type); + + /// + /// Sets the style of a column using a custom format string. + /// + /// The column index (1-based). + /// The format string to apply to the column. + /// The current instance for method chaining. IExcelWorker SetStyle(int col, string format); + /// + /// Sets conditional formatting for a column based on a condition. + /// + /// The column index (1-based). + /// The to use for the condition. + /// The condition value as a string. + /// The background color to apply if the condition is met (e.g., hex code or name). + /// The foreground (text) color to apply if the condition is met (e.g., hex code or name). + /// The current instance for method chaining. IExcelWorker SetConditionalFormatting(int col, ComparisonOperator op, string condition, string bgColor, string fgColor); + /// + /// Renames the current sheet to the specified name. + /// + /// The new name for the sheet. + /// The current instance for method chaining. IExcelWorker RenameSheet(string name); + + /// + /// Adds a new sheet to the workbook. + /// + /// The current instance for method chaining. IExcelWorker AddSheet(); + + /// + /// Checks if a sheet with the specified name exists in the workbook. + /// + /// The name of the sheet to check. + /// true if the sheet exists; otherwise, false. bool ContainsSheet(string name); + + /// + /// Switches the active sheet to the one with the specified name. + /// + /// The name of the sheet to switch to. + /// The current instance for method chaining. IExcelWorker SwitchSheet(string name); + /// + /// Gets the total number of columns in the current sheet. + /// + /// The number of columns. int GetColumnsCount(); + + /// + /// Gets the total number of rows in the current sheet. + /// + /// The number of rows. int GetRowsCount(); } + /// + /// Defines a contract for creating and opening Excel worker instances from streams. + /// public interface IExcelWorkerProvider { + /// + /// Creates a new Excel workbook and returns an instance to interact with it. + /// + /// The stream to write the new workbook to. + /// If true, the workbook is opened in read-only mode; otherwise, it is writable. + /// An instance for the new workbook. IExcelWorker CreateNew(Stream stream, bool readOnly = false); + + /// + /// Opens an existing Excel workbook from a stream and returns an instance to interact with it. + /// + /// The stream containing the existing workbook data. + /// An instance for the opened workbook. IExcelWorker OpenExist(Stream stream); } } \ No newline at end of file diff --git a/Interop/Marshaler.cs b/Interop/Marshaler.cs index bd68dd1f..a7f61017 100644 --- a/Interop/Marshaler.cs +++ b/Interop/Marshaler.cs @@ -12,15 +12,16 @@ namespace Ecng.Interop #endif /// - /// Provides a collection of extended methods, that manipulate with class . + /// Provides a collection of extended methods that manipulate and extend the functionality of the class for interoperating with unmanaged memory and libraries. /// public static class Marshaler { /// /// Marshals data from an unmanaged block of memory to a newly allocated managed object of the specified type. /// - /// A pointer to an unmanaged block of memory. - /// A managed object containing the data pointed to by the ptr parameter. + /// The type of the structure to marshal. Must be a value type. + /// The pointer to the unmanaged block of memory. + /// A managed object of type containing the data from the unmanaged memory. public static T ToStruct(this IntPtr ptr) where T : struct { @@ -28,15 +29,23 @@ public static T ToStruct(this IntPtr ptr) } /// - /// Marshals data from a managed object to an unmanaged block of memory. + /// Marshals data from a managed object to an unmanaged block of memory and returns the pointer. /// - /// A managed object holding the data to be marshaled. This object must be an instance of a formatted class. - /// - /// A pointer to an unmanaged block of memory. + /// The type of the structure to marshal. Must be a value type. + /// The managed object to marshal. + /// The optional size of the unmanaged memory block. If null, the size of is used. + /// A pointer to the allocated unmanaged memory containing the marshaled data. public static IntPtr StructToPtr(this T structure, int? size = default) where T : struct => structure.StructToPtrEx(size).ptr; + /// + /// Marshals data from a managed object to an unmanaged block of memory and returns the pointer along with the size. + /// + /// The type of the structure to marshal. Must be a value type. + /// The managed object to marshal. + /// The optional size of the unmanaged memory block. If null, the size of is used. + /// A tuple containing the pointer to the unmanaged memory and its size in bytes. public static (IntPtr ptr, int size) StructToPtrEx(this T structure, int? size = default) where T : struct { @@ -47,10 +56,12 @@ public static (IntPtr ptr, int size) StructToPtrEx(this T structure, int? siz } /// - /// Writes a value to unmanaged memory. + /// Writes a value to the specified unmanaged memory location. /// - /// The address in unmanaged memory from which to write. + /// The type of the value to write. Must be a supported primitive type (e.g., byte, short, int, long, IntPtr). + /// The address in unmanaged memory to write to. /// The value to write. + /// Thrown when is not a supported type. public static void Write(this IntPtr ptr, T value) where T : struct { @@ -69,10 +80,12 @@ public static void Write(this IntPtr ptr, T value) } /// - /// Reads a value from an unmanaged pointer. + /// Reads a value from the specified unmanaged memory location. /// - /// The address in unmanaged memory from which to read. - /// The value read from the ptr parameter. + /// The type of the value to read. Must be a supported primitive type (e.g., byte, short, int, long, IntPtr). + /// The address in unmanaged memory to read from. + /// The value read from the unmanaged memory, cast to type . + /// Thrown when is not a supported type. public static T Read(this IntPtr ptr) where T : struct { @@ -95,41 +108,123 @@ public static T Read(this IntPtr ptr) } /// - /// Converts an unmanaged function pointer to a delegate. + /// Converts an unmanaged function pointer to a delegate of the specified type. /// - /// The type of the delegate to be returned. - /// An type that is the unmanaged function pointer to be converted. - /// A delegate instance that can be cast to the appropriate delegate type. + /// The type of the delegate to create. + /// The unmanaged function pointer to convert. + /// A delegate of type that wraps the unmanaged function. public static T GetDelegateForFunctionPointer(this IntPtr ptr) { return Marshal.GetDelegateForFunctionPointer(ptr, typeof(T)).To(); } + /// + /// Converts an unmanaged ANSI string pointer to a managed string. + /// + /// The pointer to the ANSI string in unmanaged memory. + /// The managed string representation of the ANSI string. public static string ToAnsi(this IntPtr ptr) => Marshal.PtrToStringAnsi(ptr); + + /// + /// Converts an unmanaged ANSI string pointer to a managed string with a specified length. + /// + /// The pointer to the ANSI string in unmanaged memory. + /// The length of the string to read. + /// The managed string representation of the ANSI string. public static string ToAnsi(this IntPtr ptr, int len) => Marshal.PtrToStringAnsi(ptr, len); + + /// + /// Converts a managed string to an unmanaged ANSI string and returns a pointer to it. + /// + /// The managed string to convert. + /// A pointer to the unmanaged ANSI string. public static IntPtr FromAnsi(this string str) => Marshal.StringToHGlobalAnsi(str); + /// + /// Converts an unmanaged string pointer (platform-dependent encoding) to a managed string. + /// + /// The pointer to the string in unmanaged memory. + /// The managed string representation. public static string ToAuto(this IntPtr ptr) => Marshal.PtrToStringAuto(ptr); + + /// + /// Converts an unmanaged string pointer (platform-dependent encoding) to a managed string with a specified length. + /// + /// The pointer to the string in unmanaged memory. + /// The length of the string to read. + /// The managed string representation. public static string ToAuto(this IntPtr ptr, int len) => Marshal.PtrToStringAuto(ptr, len); + + /// + /// Converts a managed string to an unmanaged string (platform-dependent encoding) and returns a pointer to it. + /// + /// The managed string to convert. + /// A pointer to the unmanaged string. public static IntPtr FromAuto(this string str) => Marshal.StringToHGlobalAuto(str); + /// + /// Converts an unmanaged BSTR pointer to a managed string. + /// + /// The pointer to the BSTR in unmanaged memory. + /// The managed string representation of the BSTR. public static string ToBSTR(this IntPtr ptr) => Marshal.PtrToStringBSTR(ptr); + + /// + /// Converts a managed string to an unmanaged BSTR and returns a pointer to it. + /// + /// The managed string to convert. + /// A pointer to the unmanaged BSTR. public static IntPtr FromBSTR(this string str) => Marshal.StringToBSTR(str); + /// + /// Converts an unmanaged Unicode string pointer to a managed string. + /// + /// The pointer to the Unicode string in unmanaged memory. + /// The managed string representation of the Unicode string. public static string ToUnicode(this IntPtr ptr) => Marshal.PtrToStringUni(ptr); + + /// + /// Converts an unmanaged Unicode string pointer to a managed string with a specified length. + /// + /// The pointer to the Unicode string in unmanaged memory. + /// The length of the string to read. + /// The managed string representation of the Unicode string. public static string ToUnicode(this IntPtr ptr, int len) => Marshal.PtrToStringUni(ptr, len); + + /// + /// Converts a managed string to an unmanaged Unicode string and returns a pointer to it. + /// + /// The managed string to convert. + /// A pointer to the unmanaged Unicode string. public static IntPtr FromUnicode(this string str) => Marshal.StringToHGlobalUni(str); + /// + /// Allocates unmanaged memory of the specified size and wraps it in a safe handle. + /// + /// The size of the memory to allocate, interpreted as an integer. + /// A wrapping the allocated unmanaged memory. public static HGlobalSafeHandle ToHGlobal(this int ptr) { return ((IntPtr)ptr).ToHGlobal(); } + /// + /// Allocates unmanaged memory of the specified size and wraps it in a safe handle. + /// + /// The size of the memory to allocate, as an . + /// A wrapping the allocated unmanaged memory. public static HGlobalSafeHandle ToHGlobal(this IntPtr ptr) { return new HGlobalSafeHandle(Marshal.AllocHGlobal(ptr)); } + /// + /// Encodes a string using the specified encoding, allocates unmanaged memory for it, and returns a safe handle. + /// + /// The encoding to use for the string. + /// The string to encode and allocate. + /// A wrapping the allocated unmanaged memory containing the encoded string. + /// Thrown when is null. public static HGlobalSafeHandle ToHGlobal(this Encoding encoding, string data) { if (encoding is null) @@ -144,6 +239,13 @@ public static HGlobalSafeHandle ToHGlobal(this Encoding encoding, string data) return pData; } + /// + /// Decodes an unmanaged ANSI string from a pointer into a managed string using the specified encoding. + /// + /// The encoding to use for decoding the string. + /// The pointer to the ANSI string in unmanaged memory. + /// The decoded managed string. + /// Thrown when is null. public static string ToString(this Encoding encoding, IntPtr pData) { if (encoding is null) @@ -158,9 +260,24 @@ public static string ToString(this Encoding encoding, IntPtr pData) return encoding.GetString(data); } + /// + /// Retrieves a delegate for a named procedure from an unmanaged library. + /// + /// The type of the delegate to retrieve. + /// The handle to the loaded unmanaged library. + /// The name of the procedure to retrieve. + /// A delegate of type for the specified procedure. + /// Thrown when the procedure cannot be found in the library. public static T GetHandler(this IntPtr library, string procName) => GetDelegateForFunctionPointer(GetProcAddress(library, procName)); + /// + /// Attempts to retrieve a delegate for a named procedure from an unmanaged library. + /// + /// The type of the delegate to retrieve. Must inherit from . + /// The handle to the loaded unmanaged library. + /// The name of the procedure to retrieve. + /// A delegate of type if found; otherwise, null. public static T TryGetHandler(this IntPtr library, string procName) where T : Delegate { @@ -181,6 +298,13 @@ public static T TryGetHandler(this IntPtr library, string procName) private static extern IntPtr Kernel32GetProcAddress([In] IntPtr hModule, [In] string procName); #endif + /// + /// Loads an unmanaged library from the specified path. + /// + /// The file path to the unmanaged library (DLL). + /// A handle to the loaded library. + /// Thrown when is null or empty. + /// Thrown when the library cannot be loaded. public static IntPtr LoadLibrary(string dllPath) { if (dllPath.IsEmpty()) @@ -198,6 +322,11 @@ public static IntPtr LoadLibrary(string dllPath) return handler; } + /// + /// Frees a previously loaded unmanaged library. + /// + /// The handle to the library to free. + /// true if the library was successfully freed; otherwise, false. public static bool FreeLibrary(this IntPtr hModule) { #if NETCOREAPP @@ -208,6 +337,13 @@ public static bool FreeLibrary(this IntPtr hModule) #endif } + /// + /// Retrieves the address of a named procedure from an unmanaged library. + /// + /// The handle to the loaded unmanaged library. + /// The name of the procedure to retrieve. + /// The address of the procedure in unmanaged memory. + /// Thrown when the procedure cannot be found in the library. public static IntPtr GetProcAddress(this IntPtr hModule, string procName) { if (TryGetProcAddress(hModule, procName, out var addr)) @@ -216,6 +352,13 @@ public static IntPtr GetProcAddress(this IntPtr hModule, string procName) throw new ArgumentException($"Error load procedure {procName}.", nameof(procName), new Win32Exception()); } + /// + /// Attempts to retrieve the address of a named procedure from an unmanaged library. + /// + /// The handle to the loaded unmanaged library. + /// The name of the procedure to retrieve. + /// When this method returns, contains the address of the procedure if found; otherwise, . + /// true if the procedure address was found; otherwise, false. public static bool TryGetProcAddress(this IntPtr hModule, string procName, out IntPtr address) { #if NETCOREAPP @@ -226,6 +369,15 @@ public static bool TryGetProcAddress(this IntPtr hModule, string procName, out I #endif } + /// + /// Converts an unmanaged byte reference to a managed string using the specified encoding, with a maximum byte length. + /// + /// The encoding to use for decoding the string. + /// A reference to the starting byte in unmanaged memory. + /// The maximum number of bytes to read. + /// The decoded managed string, or null if the source is zero. + /// Thrown when is null. + /// Thrown when is negative. public static unsafe string GetUnsafeString(this Encoding encoding, ref byte srcChar, int maxBytes) { if (encoding is null) @@ -246,6 +398,17 @@ public static unsafe string GetUnsafeString(this Encoding encoding, ref byte src } } + /// + /// Writes a managed string to an unmanaged byte reference using the specified encoding, with a maximum byte length. + /// + /// The encoding to use for encoding the string. + /// A reference to the target byte in unmanaged memory. + /// The maximum number of bytes to write. + /// The string to write. + /// Thrown when is null. + /// + /// Thrown when is negative or the string length exceeds . + /// public static unsafe void SetUnsafeString(this Encoding encoding, ref byte tgtChar, int maxBytes, string value) { if (encoding is null) @@ -257,6 +420,9 @@ public static unsafe void SetUnsafeString(this Encoding encoding, ref byte tgtCh if (value.IsEmpty()) return; + if (value.Length >= maxBytes) + throw new ArgumentOutOfRangeException(nameof(maxBytes), maxBytes, "Invalid value."); + var charBuffer = stackalloc char[maxBytes]; fixed (byte* ptr8 = &tgtChar) @@ -264,9 +430,6 @@ public static unsafe void SetUnsafeString(this Encoding encoding, ref byte tgtCh for (var b = 0; b < maxBytes; b++) ptr8[b] = 0; - if (value.Length >= maxBytes) - throw new ArgumentOutOfRangeException(); - for (var c = 0; c < value.Length; c++) charBuffer[c] = value[c]; @@ -274,16 +437,32 @@ public static unsafe void SetUnsafeString(this Encoding encoding, ref byte tgtCh } } + /// + /// Copies data from an unmanaged pointer to a byte array. + /// + /// The pointer to the unmanaged memory to copy from. + /// The byte array to copy the data into. public static void CopyTo(this IntPtr ptr, byte[] buffer) { ptr.CopyTo(buffer, 0, buffer.Length); } + /// + /// Copies a specified amount of data from an unmanaged pointer to a byte array. + /// + /// The pointer to the unmanaged memory to copy from. + /// The byte array to copy the data into. + /// The starting index in the buffer where data should be copied. + /// The number of bytes to copy. public static void CopyTo(this IntPtr ptr, byte[] buffer, int offset, int length) { Marshal.Copy(ptr, buffer, offset, length); } + /// + /// Frees an unmanaged memory block previously allocated with . + /// + /// The pointer to the unmanaged memory to free. public static void FreeHGlobal(this IntPtr ptr) => Marshal.FreeHGlobal(ptr); } } \ No newline at end of file diff --git a/Interop/ProcessExtensions.cs b/Interop/ProcessExtensions.cs index e6c1fef8..0888da74 100644 --- a/Interop/ProcessExtensions.cs +++ b/Interop/ProcessExtensions.cs @@ -11,10 +11,24 @@ using Microsoft.Win32.SafeHandles; +/// +/// Provides extension methods for the class to manage processor affinity, memory limits, and job behavior on Windows. +/// public unsafe static class ProcessExtensions { #pragma warning disable CA1416 // Validate platform compatibility + /// + /// Sets the processor affinity for the process, allowing control over which CPU cores the process can execute on. + /// + /// The process whose processor affinity is to be set. + /// + /// The bitmask representing the allowed CPU cores. The mask is applied to the process's current affinity. + /// + /// Thrown when the provided process is null. + /// + /// Thrown when the current operating system is not Windows or Linux. + /// public static void SetProcessorAffinity(this Process process, long cpu) { if (process is null) @@ -30,6 +44,22 @@ public static void SetProcessorAffinity(this Process process, long cpu) throw new PlatformNotSupportedException(); } + /// + /// Limits the process memory usage by assigning it to a Windows Job Object with a specified process memory limit. + /// + /// The process to limit memory usage for. + /// The maximum allowed memory in bytes. + /// + /// A representing the job object that manages the memory limit. + /// + /// Thrown when the provided process is null. + /// Thrown when the provided limit is less than or equal to zero. + /// + /// Thrown when the current operating system is not Windows. + /// + /// + /// Thrown when setting the job object information or assigning the process to the job object fails. + /// public static SafeFileHandle LimitByMemory(this Process process, long limit) { if (process is null) @@ -70,6 +100,21 @@ public static SafeFileHandle LimitByMemory(this Process process, long limit) return jobHandle; } + /// + /// Configures the process to automatically kill its child processes when the process is closed by + /// assigning it to a Windows Job Object with the kill-on-job-close flag. + /// + /// The process whose child processes should be terminated on close. + /// + /// A representing the job object that enforces the kill-on-job-close behavior. + /// + /// Thrown when the provided process is null. + /// + /// Thrown when the current operating system is not Windows. + /// + /// + /// Thrown when setting the job object information or assigning the process to the job object fails. + /// public static SafeFileHandle SetKillChildsOnClose(this Process process) { if (process is null) diff --git a/Interop/PtrReader.cs b/Interop/PtrReader.cs index 8387284f..aa71c96c 100644 --- a/Interop/PtrReader.cs +++ b/Interop/PtrReader.cs @@ -3,8 +3,16 @@ using System; using System.Runtime.InteropServices; + /// + /// Provides methods to read data from an unmanaged memory pointer. + /// public class PtrReader { + /// + /// Initializes a new instance of the class with the specified pointer. + /// + /// The unmanaged memory pointer to read from. + /// Thrown if is . public PtrReader(IntPtr ptr) { Ptr = ptr; @@ -12,6 +20,10 @@ public PtrReader(IntPtr ptr) private IntPtr _ptr; + /// + /// Gets or sets the unmanaged memory pointer. + /// + /// Thrown if the value is . public IntPtr Ptr { get { return _ptr; } @@ -24,6 +36,10 @@ public IntPtr Ptr } } + /// + /// Reads a byte from the current pointer and advances the pointer by the size of a byte. + /// + /// The byte read from the pointer. public byte GetByte() { var ret = Marshal.ReadByte(_ptr); @@ -32,6 +48,10 @@ public byte GetByte() return ret; } + /// + /// Reads a 32-bit integer from the current pointer and advances the pointer by the size of an integer. + /// + /// The 32-bit integer read from the pointer. public int GetInt() { var ret = Marshal.ReadInt32(_ptr); @@ -40,6 +60,10 @@ public int GetInt() return ret; } + /// + /// Reads a 64-bit integer from the current pointer and advances the pointer by the size of a long. + /// + /// The 64-bit integer read from the pointer. public long GetLong() { var ret = Marshal.ReadInt64(_ptr); @@ -48,6 +72,10 @@ public long GetLong() return ret; } + /// + /// Reads a 16-bit integer from the current pointer and advances the pointer by the size of a short. + /// + /// The 16-bit integer read from the pointer. public short GetShort() { var ret = Marshal.ReadInt16(_ptr); @@ -56,6 +84,10 @@ public short GetShort() return ret; } + /// + /// Reads an from the current pointer and advances the pointer by the size of an . + /// + /// The read from the pointer. public IntPtr GetIntPtr() { var ret = Marshal.ReadIntPtr(_ptr); @@ -64,6 +96,10 @@ public IntPtr GetIntPtr() return ret; } + /// + /// Reads a null-terminated ANSI string from the current pointer and advances the pointer by the size of an . + /// + /// The ANSI string read from the pointer. If the string is null, an empty string is returned. public string GetString() { var str = _ptr.ToAnsi(); @@ -72,6 +108,11 @@ public string GetString() return str is null ? string.Empty : str.Trim(); } + /// + /// Reads an ANSI string of the specified length from the current pointer and advances the pointer by the specified length. + /// + /// The length of the string to read. + /// The ANSI string read from the pointer. If the string is null, an empty string is returned. public string GetString(int length) { var str = _ptr.ToAnsi(length); diff --git a/Interop/SafePointer.cs b/Interop/SafePointer.cs index 92e05690..aa5a21a3 100644 --- a/Interop/SafePointer.cs +++ b/Interop/SafePointer.cs @@ -4,12 +4,23 @@ using Ecng.Common; + /// + /// Represents a safe wrapper around an unmanaged memory pointer with bounds checking and shifting capabilities. + /// public struct SafePointer { private IntPtr _pointer; private readonly int? _size; private int _origin; + /// + /// Initializes a new instance of the struct with a specified pointer and optional size. + /// + /// The unmanaged memory pointer to wrap. Must not be . + /// The optional size of the memory block in bytes. If specified, must be non-negative. + /// + /// Thrown when is or is negative. + /// public SafePointer(IntPtr pointer, int? size) { if (pointer == default) @@ -23,6 +34,9 @@ public SafePointer(IntPtr pointer, int? size) _origin = 0; } + /// + /// Gets the current unmanaged memory pointer. + /// public IntPtr Pointer => _pointer; private void CheckBorder() @@ -37,6 +51,15 @@ private void CheckBorder(int offset) throw new ArgumentOutOfRangeException(nameof(offset)); } + /// + /// Reads a structure of type from the current pointer position. + /// + /// The type of the structure to read. Must be a value type. + /// If true, shifts the pointer by the size of after reading. + /// The structure read from the pointer. + /// + /// Thrown when the operation exceeds the defined size boundary. + /// public TStruct ToStruct(bool autoShift = false) where TStruct : struct { @@ -50,12 +73,26 @@ public TStruct ToStruct(bool autoShift = false) return value; } + /// + /// Shifts the pointer forward by the size of the specified structure type. + /// + /// The type of the structure whose size determines the shift. Must be a value type. + /// + /// Thrown when the shift exceeds the defined size boundary. + /// public void Shift() where TStruct : struct { Shift(typeof(TStruct).SizeOf()); } + /// + /// Shifts the pointer forward by the specified offset in bytes. + /// + /// The number of bytes to shift the pointer. + /// + /// Thrown when the shift exceeds the defined size boundary. + /// public void Shift(int offset) { CheckBorder(offset); @@ -64,11 +101,29 @@ public void Shift(int offset) _pointer += offset; } + /// + /// Copies data from the current pointer position to a byte array. + /// + /// The target byte array to copy data into. + /// If true, shifts the pointer by the length of the copied data after copying. + /// + /// Thrown when the operation exceeds the defined size boundary. + /// public void CopyTo(byte[] buffer, bool autoShift = false) { CopyTo(buffer, 0, buffer.Length, autoShift); } + /// + /// Copies a specified amount of data from the current pointer position to a byte array. + /// + /// The target byte array to copy data into. + /// The starting index in the buffer where data should be copied. + /// The number of bytes to copy. + /// If true, shifts the pointer by after copying. + /// + /// Thrown when the operation exceeds the defined size boundary. + /// public void CopyTo(byte[] buffer, int offset, int length, bool autoShift = false) { CheckBorder(length); @@ -79,6 +134,15 @@ public void CopyTo(byte[] buffer, int offset, int length, bool autoShift = false Shift(length); } + /// + /// Reads a value of type from the current pointer position. + /// + /// The type of the value to read. Must be a value type. + /// If true, shifts the pointer by the size of after reading. + /// The value read from the pointer. + /// + /// Thrown when the operation exceeds the defined size boundary. + /// public TValue Read(bool autoShift = false) where TValue : struct { @@ -92,6 +156,11 @@ public TValue Read(bool autoShift = false) return value; } + /// + /// Implicitly converts a to an . + /// + /// The instance to convert. + /// The underlying value. public static implicit operator IntPtr(SafePointer pointer) => pointer.Pointer; } } \ No newline at end of file diff --git a/common.props b/common.props index f404005f..fa4d791c 100644 --- a/common.props +++ b/common.props @@ -44,8 +44,6 @@ '$(MSBuildProjectName)' == 'Net.HPSocket' or '$(MSBuildProjectName)' == 'SmartFormat' or '$(MSBuildProjectName)' == 'StringSearch' or - '$(MSBuildProjectName)' == 'Interop' or - '$(MSBuildProjectName)' == 'Interop.Windows' or '$(MSBuildProjectName)' == 'Tests' "> false