From 5dad85375d15aca586a0c52dd2f1b6e3f1e64455 Mon Sep 17 00:00:00 2001 From: David Hall Date: Thu, 19 Oct 2023 17:47:07 -0600 Subject: [PATCH] Added nullability to QoS and test. Fixed bug in TC_GEN_FLOW. --- PInvoke/QoS/QoS.cs | 9 +++---- PInvoke/QoS/QoSSP.cs | 12 ++++----- PInvoke/QoS/Traffic.cs | 52 +++++++++++++++++++++++++++------------ PInvoke/Ws2_32/QoS.cs | 27 +++++++++++++++++--- UnitTests/PInvoke/QoS/QoSTests.cs | 18 ++++++++------ 5 files changed, 81 insertions(+), 37 deletions(-) diff --git a/PInvoke/QoS/QoS.cs b/PInvoke/QoS/QoS.cs index 47857d4a..1687e140 100644 --- a/PInvoke/QoS/QoS.cs +++ b/PInvoke/QoS/QoS.cs @@ -1,7 +1,3 @@ -global using System; -global using System.Runtime.InteropServices; -global using Vanara.Extensions; -global using Vanara.InteropServices; global using static Vanara.PInvoke.Ws2_32; global using IN_ADDR_IPV4 = Vanara.PInvoke.Ws2_32.IN_ADDR; global using IN_ADDR_IPV6 = Vanara.PInvoke.Ws2_32.IN6_ADDR; @@ -118,7 +114,7 @@ public static partial class Qwave // QOS_OBJECT_HDR, *LPQOS_OBJECT_HDR; [PInvokeData("qos.h", MSDNShortId = "NS:qos.__unnamed_struct_0")] [StructLayout(LayoutKind.Sequential)] - public struct QOS_OBJECT_HDR + public struct QOS_OBJECT_HDR : IQoSObjectHdr { /// /// Specifies the type of object to which QOS_OBJECT_HDR is attached. The following values are valid for QOS_OBJECT_HDR: @@ -145,6 +141,9 @@ public static partial class Qwave var ot = CorrespondingTypeAttribute.CanGet(typeof(T), out var e) ? e : throw new ArgumentException(); return new() { ObjectType = ot, ObjectLength = (uint)Marshal.SizeOf(typeof(T)) }; } + + /// An instance of used for the end of a list. + public static readonly QOS_OBJECT_HDR EndOfList = Init(); } /// The QOS object QOS_SD_MODE defines the behavior of the traffic control-packet shaper component. diff --git a/PInvoke/QoS/QoSSP.cs b/PInvoke/QoS/QoSSP.cs index eec2282a..9aff3cc7 100644 --- a/PInvoke/QoS/QoSSP.cs +++ b/PInvoke/QoS/QoSSP.cs @@ -202,7 +202,7 @@ public static partial class Qwave SizeT IVanaraMarshaler.GetNativeSize() => Marshal.SizeOf(typeof(ACTUAL)); - SafeAllocatedMemoryHandle IVanaraMarshaler.MarshalManagedToNative(object managedObject) + SafeAllocatedMemoryHandle IVanaraMarshaler.MarshalManagedToNative(object? managedObject) { if (managedObject is not CONTROL_SERVICE cs) { @@ -224,13 +224,13 @@ public static partial class Qwave } else { - native.union.ParamBuffer = new() { ParameterId = ParamBuffer.Value.ParameterId, Length = 8U + (uint)bufLen }; + native.union.ParamBuffer = new() { ParameterId = ParamBuffer!.Value.ParameterId, Length = 8U + (uint)bufLen }; } ret.Write(native); - if (bufLen > 0) + if (bufLen > 0 && ParamBuffer.HasValue) { - ret.Write(ParamBuffer.Value.Buffer, false, bufOffset); + ret.Write(ParamBuffer!.Value.Buffer!, false, bufOffset); } return ret; @@ -239,7 +239,7 @@ public static partial class Qwave private static readonly int bufOffset = Marshal.OffsetOf(typeof(ACTUAL), "union").ToInt32() + Marshal.OffsetOf(typeof(UNION.DUMMYBUF), "Buffer").ToInt32(); - object IVanaraMarshaler.MarshalNativeToManaged(IntPtr pNativeData, SizeT allocatedBytes) + object? IVanaraMarshaler.MarshalNativeToManaged(IntPtr pNativeData, SizeT allocatedBytes) { if (pNativeData == IntPtr.Zero || allocatedBytes == 0) { @@ -255,7 +255,7 @@ public static partial class Qwave else { PARAM_BUFFER pb = new() { ParameterId = actual.union.ParamBuffer.ParameterId, Length = actual.union.ParamBuffer.Length }; - pb.Buffer = pNativeData.ToByteArray((int)pb.Length - 8, bufOffset, allocatedBytes); + pb.Buffer = pNativeData.ToByteArray((int)pb.Length - 8, bufOffset, allocatedBytes)!; ret.ParamBuffer = pb; } return ret; diff --git a/PInvoke/QoS/Traffic.cs b/PInvoke/QoS/Traffic.cs index cfd04c8d..26447998 100644 --- a/PInvoke/QoS/Traffic.cs +++ b/PInvoke/QoS/Traffic.cs @@ -172,7 +172,7 @@ public static partial class Traffic // Buffer ) {...} [PInvokeData("traffic.h", MSDNShortId = "NC:traffic.TCI_NOTIFY_HANDLER")] [UnmanagedFunctionPointer(CallingConvention.Winapi, SetLastError = false)] - public delegate void TCI_NOTIFY_HANDLER([In] IntPtr ClRegCtx, [In] IntPtr ClIfcCtx, TC_NOTIFY Event, [In] IntPtr SubCode, + public delegate void TCI_NOTIFY_HANDLER([In] IntPtr ClRegCtx, [In, Optional] IntPtr ClIfcCtx, TC_NOTIFY Event, [In] IntPtr SubCode, uint BufSize, [In] IntPtr Buffer); /// Describes the notification event. @@ -697,7 +697,7 @@ public static partial class Traffic /// Note Use of the TcEnumerateFlows function requires administrative privilege. /// [PInvokeData("traffic.h", MSDNShortId = "NF:traffic.TcEnumerateFlows")] - public static ENUMERATION_BUFFER_MGD TcEnumerateFlows(HIFC IfcHandle, uint pFlowCount = 0xFFFF) + public static ENUMERATION_BUFFER_MGD? TcEnumerateFlows(HIFC IfcHandle, uint pFlowCount = 0xFFFF) { HFLOWENUM hEnum = default; uint bufSz = 1024, cnt; @@ -1484,7 +1484,7 @@ public static partial class Traffic public IntPtr pFlow; /// The corresponding structure. - public TC_GEN_FLOW Flow => pFlow.ToStructure(); + public TC_GEN_FLOW Flow => pFlow.ToStructure()!; /// Specifies the number of filters associated with the flow. public uint NumberOfFilters; @@ -1511,7 +1511,7 @@ public static partial class Traffic [PInvokeData("traffic.h", MSDNShortId = "NS:traffic._ENUMERATION_BUFFER")] public class ENUMERATION_BUFFER_MGD { - private ENUMERATION_BUFFER_MGD() { } + private ENUMERATION_BUFFER_MGD() => throw new NotImplementedException(); internal ENUMERATION_BUFFER_MGD(IntPtr ptr) { @@ -1972,6 +1972,25 @@ public static partial class Traffic /// criteria). Note that the Mask member must be of the same type as the Pattern member. /// public IntPtr Mask; + + /// Creates a instance. + /// The type of the pattern. + /// The filter type to be applied with the filter. + /// Indicates the specific format of the pattern to be applied to the filter, such as IP_PATTERN. + /// A bitmask applied to the bits designated in the Pattern member. + /// The memory allocated too the and fields. + /// + /// A complete instance. Do not dispose the value returned in until this + /// structure is no longer needed. + /// + public static TC_GEN_FILTER Create(NDIS_PROTOCOL_ID AddressType, in T Pattern, in T Mask, out SafeHandle memAlloc) where T : struct + { + var sz = Marshal.SizeOf(typeof(T)); + SafeNativeArray parts = new(new T[] { Pattern, Mask }); + memAlloc = parts; + var ptrs = parts.GetPointers(); + return new TC_GEN_FILTER { AddressType = AddressType, PatternSize = (uint)sz, Pattern = ptrs[0], Mask = ptrs[1] }; + } } /// @@ -1998,7 +2017,7 @@ public static partial class Traffic public class TC_GEN_FILTER_MGD_UNK : TC_GEN_FILTER_MGD_BASE { /// - /// Indicates the specific format of the pattern to be applied to the filter, such as IP_PATTERN. The pattern specifies which bits of + /// Indicates the specific format of the pattern to be applied to the filter, such as . The pattern specifies which bits of /// a given packet should be evaluated when determining whether a packet is included in the filter. /// public byte[] Pattern; @@ -2013,8 +2032,8 @@ public static partial class Traffic internal TC_GEN_FILTER_MGD_UNK(in TC_GEN_FILTER f) { AddressType = f.AddressType; - Pattern = f.Pattern.ToByteArray((int)f.PatternSize); - Mask = f.Mask.ToByteArray((int)f.PatternSize); + Pattern = f.Pattern.ToByteArray((int)f.PatternSize) ?? new byte[0]; + Mask = f.Mask.ToByteArray((int)f.PatternSize) ?? new byte[0]; } } @@ -2076,30 +2095,31 @@ public static partial class Traffic /// QOS_SHAPING_RATE /// QOS_OBJECT_END_OF_LIST /// - public IQoSObjectHdr[] TcObjects; + public IQoSObjectHdr[]? TcObjects; SizeT IVanaraMarshaler.GetNativeSize() => Marshal.SizeOf(typeof(INT_TC_GEN_FLOW)); - SafeAllocatedMemoryHandle IVanaraMarshaler.MarshalManagedToNative(object managedObject) + SafeAllocatedMemoryHandle IVanaraMarshaler.MarshalManagedToNative(object? managedObject) { if (managedObject is not TC_GEN_FLOW f) throw new ArgumentException("Only objects of type TC_GEN_FLOW can be marshaled."); int objLen = f.TcObjects?.Length ?? 0; - SafeCoTaskMemStruct pFlow = new(new INT_TC_GEN_FLOW() { SendingFlowspec = f.SendingFlowspec, ReceivingFlowspec = f.ReceivingFlowspec, TcObjectsLength = objLen }); - pFlow.Size += f.TcObjects?.Sum(o => Marshal.SizeOf(o.GetType())) ?? 0; + int objByteLen = f.TcObjects?.Sum(o => Marshal.SizeOf(o.GetType())) ?? 0; + SafeCoTaskMemStruct pFlow = new(new INT_TC_GEN_FLOW() { SendingFlowspec = f.SendingFlowspec, ReceivingFlowspec = f.ReceivingFlowspec, TcObjectsLength = objByteLen }); + pFlow.Size += objByteLen; if (objLen > 0) { var oPtr = pFlow.GetFieldAddress(nameof(TcObjects)); for (int i = 0; i < objLen; i++) { - oPtr.Write(f.TcObjects[i]); + oPtr.Write(f.TcObjects![i]); oPtr = oPtr.Offset(Marshal.SizeOf(f.TcObjects[i].GetType())); } } return pFlow; } - object IVanaraMarshaler.MarshalNativeToManaged(IntPtr pNativeData, SizeT allocatedBytes) + object? IVanaraMarshaler.MarshalNativeToManaged(IntPtr pNativeData, SizeT allocatedBytes) { var f = pNativeData.ToStructure(allocatedBytes); TC_GEN_FLOW ret = new() { SendingFlowspec = f.SendingFlowspec, ReceivingFlowspec = f.ReceivingFlowspec, TcObjects = new IQoSObjectHdr[f.TcObjectsLength] }; @@ -2171,15 +2191,15 @@ public static partial class Traffic /// Pointer to the client-callback function ClAddFlowComplete. [MarshalAs(UnmanagedType.FunctionPtr)] - public TCI_ADD_FLOW_COMPLETE_HANDLER ClAddFlowCompleteHandler; + public TCI_ADD_FLOW_COMPLETE_HANDLER? ClAddFlowCompleteHandler; /// Pointer to the client-callback function ClModifyFlowComplete. [MarshalAs(UnmanagedType.FunctionPtr)] - public TCI_MOD_FLOW_COMPLETE_HANDLER ClModifyFlowCompleteHandler; + public TCI_MOD_FLOW_COMPLETE_HANDLER? ClModifyFlowCompleteHandler; /// Pointer to the client-callback function ClDeleteFlowComplete. [MarshalAs(UnmanagedType.FunctionPtr)] - public TCI_DEL_FLOW_COMPLETE_HANDLER ClDeleteFlowCompleteHandler; + public TCI_DEL_FLOW_COMPLETE_HANDLER? ClDeleteFlowCompleteHandler; } /// Provides a for that is disposed using . diff --git a/PInvoke/Ws2_32/QoS.cs b/PInvoke/Ws2_32/QoS.cs index 1cf767ec..8c3575c0 100644 --- a/PInvoke/Ws2_32/QoS.cs +++ b/PInvoke/Ws2_32/QoS.cs @@ -169,9 +169,30 @@ public static partial class Ws2_32 public struct FLOWSPEC { /// Represents a FLOWSPEC with all unspecified values. - public static readonly FLOWSPEC NotSpecified = new() { DelayVariation = QOS_NOT_SPECIFIED, Latency = QOS_NOT_SPECIFIED, MaxSduSize = QOS_NOT_SPECIFIED, - MinimumPolicedSize = QOS_NOT_SPECIFIED, PeakBandwidth = QOS_NOT_SPECIFIED, TokenBucketSize = QOS_NOT_SPECIFIED, TokenRate = QOS_NOT_SPECIFIED, - ServiceType = SERVICETYPE.SERVICETYPE_BESTEFFORT }; + public static readonly FLOWSPEC NotSpecified = new(SERVICETYPE.SERVICETYPE_BESTEFFORT); + + /// Initializes a new instance of the struct. + /// The delay variation. + /// The latency. + /// Maximum size of the sdu. + /// Minimum size of the policed. + /// The peak bandwidth. + /// Size of the token bucket. + /// The token rate. + /// Type of the service. + public FLOWSPEC(SERVICETYPE serviceType, uint tokenRate = QOS_NOT_SPECIFIED, uint delayVariation = QOS_NOT_SPECIFIED, + uint latency = QOS_NOT_SPECIFIED, uint maxSduSize = QOS_NOT_SPECIFIED, uint minimumPolicedSize = QOS_NOT_SPECIFIED, + uint peakBandwidth = QOS_NOT_SPECIFIED, uint tokenBucketSize = QOS_NOT_SPECIFIED) + { + DelayVariation = delayVariation; + Latency = latency; + MaxSduSize = maxSduSize; + MinimumPolicedSize = minimumPolicedSize; + PeakBandwidth = peakBandwidth; + TokenBucketSize = tokenBucketSize; + TokenRate = tokenRate; + ServiceType = serviceType; + } /// /// diff --git a/UnitTests/PInvoke/QoS/QoSTests.cs b/UnitTests/PInvoke/QoS/QoSTests.cs index 7216730d..e6482026 100644 --- a/UnitTests/PInvoke/QoS/QoSTests.cs +++ b/UnitTests/PInvoke/QoS/QoSTests.cs @@ -7,6 +7,7 @@ using static Vanara.PInvoke.Traffic; using static Vanara.PInvoke.Ws2_32; namespace Vanara.PInvoke.Tests; + [TestFixture] public class QoSTests { @@ -67,16 +68,19 @@ public class QoSTests { TC_GEN_FLOW flow = new() { - SendingFlowspec = FLOWSPEC.NotSpecified, - ReceivingFlowspec = FLOWSPEC.NotSpecified, - TcObjects = new IQoSObjectHdr[] { new QOS_DS_CLASS { ObjectHdr = QOS_OBJECT_HDR.Init(), DSField = 0x28 } } + SendingFlowspec = new FLOWSPEC(SERVICETYPE.SERVICETYPE_GUARANTEED, 10000, maxSduSize: 344, minimumPolicedSize: 12, peakBandwidth: 32000, tokenBucketSize: 680), + ReceivingFlowspec = new(SERVICETYPE.SERVICETYPE_GUARANTEED, 10000), + TcObjects = new IQoSObjectHdr[] { new QOS_DS_CLASS { ObjectHdr = QOS_OBJECT_HDR.Init(), DSField = 0x2E }, QOS_OBJECT_HDR.EndOfList } }; Assert.That(TcAddFlow(hIfc, default, default, flow, out var hFlow), ResultIs.Successful); try { - TC_GEN_FILTER filter = new() { }; + IP_PATTERN pattern = new() { ProtocolId = (byte)IPPROTO.IPPROTO_UDP, tcDstPort = 5000 }; + IP_PATTERN mask = new() { ProtocolId = 0xFF, tcDstPort = 0xFFFF }; + TC_GEN_FILTER filter = TC_GEN_FILTER.Create(NDIS_PROTOCOL_ID.NDIS_PROTOCOL_ID_TCP_IP, pattern, mask, out var mem); Assert.That(TcAddFilter(hFlow, filter, out SafeHFILTER hFilt), ResultIs.Successful); hFilt.Dispose(); + mem.Dispose(); } finally { @@ -119,12 +123,12 @@ public class QoSTests using var pin = new PinnedObject(dscp); Assert.That(QOSSetFlow(hQos, flowId, QOS_SET_FLOW.QOSSetOutgoingDSCPValue, 4, pin), ResultIs.Successful); - uint[] flowIds = null; + uint[]? flowIds = null; Assert.That(() => flowIds = QOSEnumerateFlows(hQos), Throws.Nothing); Assert.That(flowIds, Is.Not.Empty); - flowIds.WriteValues(); + flowIds!.WriteValues(); - Assert.That(() => QOSQueryFlow(hQos, flowIds[0], QOS_QUERY_FLOW.QOSQueryOutgoingRate, false).WriteValues(), Throws.Nothing); + Assert.That(() => QOSQueryFlow(hQos, flowIds![0], QOS_QUERY_FLOW.QOSQueryOutgoingRate, false).WriteValues(), Throws.Nothing); Assert.That(QOSRemoveSocketFromFlow(hQos, socket.Handle, flowId), ResultIs.Successful);