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