Added nullability to QoS and test. Fixed bug in TC_GEN_FLOW.

nullableenabled
David Hall 2023-10-19 17:47:07 -06:00
parent d22ec676a0
commit 5dad85375d
5 changed files with 81 additions and 37 deletions

View File

@ -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
{
/// <summary>
/// <para>Specifies the type of object to which <c>QOS_OBJECT_HDR</c> is attached. The following values are valid for <c>QOS_OBJECT_HDR</c>:</para>
@ -145,6 +141,9 @@ public static partial class Qwave
var ot = CorrespondingTypeAttribute.CanGet<QOS_OBJ_TYPE>(typeof(T), out var e) ? e : throw new ArgumentException();
return new() { ObjectType = ot, ObjectLength = (uint)Marshal.SizeOf(typeof(T)) };
}
/// <summary>An instance of <see cref="QOS_OBJECT_HDR"/> used for the end of a list.</summary>
public static readonly QOS_OBJECT_HDR EndOfList = Init<QOS_OBJECT_HDR>();
}
/// <summary>The QOS object <c>QOS_SD_MODE</c> defines the behavior of the traffic control-packet shaper component.</summary>

View File

@ -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;

View File

@ -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);
/// <summary>Describes the notification event.</summary>
@ -697,7 +697,7 @@ public static partial class Traffic
/// <para><c>Note</c> Use of the <c>TcEnumerateFlows</c> function requires administrative privilege.</para>
/// </remarks>
[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;
/// <summary>The corresponding <see cref="TC_GEN_FLOW"/> structure.</summary>
public TC_GEN_FLOW Flow => pFlow.ToStructure<TC_GEN_FLOW>();
public TC_GEN_FLOW Flow => pFlow.ToStructure<TC_GEN_FLOW>()!;
/// <summary>Specifies the number of filters associated with the flow.</summary>
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 <c>Mask</c> member must be of the same type as the <c>Pattern</c> member.
/// </summary>
public IntPtr Mask;
/// <summary>Creates a <see cref="TC_GEN_FILTER"/> instance.</summary>
/// <typeparam name="T">The type of the pattern.</typeparam>
/// <param name="AddressType">The filter type to be applied with the filter.</param>
/// <param name="Pattern">Indicates the specific format of the pattern to be applied to the filter, such as IP_PATTERN.</param>
/// <param name="Mask">A bitmask applied to the bits designated in the <c>Pattern</c> member.</param>
/// <param name="memAlloc">The memory allocated too the <paramref name="Pattern"/> and <paramref name="Mask"/> fields.</param>
/// <returns>
/// A complete <see cref="TC_GEN_FILTER"/> instance. Do not dispose the value returned in <paramref name="memAlloc"/> until this
/// structure is no longer needed.
/// </returns>
public static TC_GEN_FILTER Create<T>(NDIS_PROTOCOL_ID AddressType, in T Pattern, in T Mask, out SafeHandle memAlloc) where T : struct
{
var sz = Marshal.SizeOf(typeof(T));
SafeNativeArray<T> 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] };
}
}
/// <summary>
@ -1998,7 +2017,7 @@ public static partial class Traffic
public class TC_GEN_FILTER_MGD_UNK : TC_GEN_FILTER_MGD_BASE
{
/// <summary>
/// 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 <see cref="IP_PATTERN"/>. The pattern specifies which bits of
/// a given packet should be evaluated when determining whether a packet is included in the filter.
/// </summary>
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
/// <para>QOS_SHAPING_RATE</para>
/// <para>QOS_OBJECT_END_OF_LIST</para>
/// </summary>
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<INT_TC_GEN_FLOW> 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<INT_TC_GEN_FLOW> 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<INT_TC_GEN_FLOW>(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
/// <summary>Pointer to the client-callback function ClAddFlowComplete.</summary>
[MarshalAs(UnmanagedType.FunctionPtr)]
public TCI_ADD_FLOW_COMPLETE_HANDLER ClAddFlowCompleteHandler;
public TCI_ADD_FLOW_COMPLETE_HANDLER? ClAddFlowCompleteHandler;
/// <summary>Pointer to the client-callback function ClModifyFlowComplete.</summary>
[MarshalAs(UnmanagedType.FunctionPtr)]
public TCI_MOD_FLOW_COMPLETE_HANDLER ClModifyFlowCompleteHandler;
public TCI_MOD_FLOW_COMPLETE_HANDLER? ClModifyFlowCompleteHandler;
/// <summary>Pointer to the client-callback function ClDeleteFlowComplete.</summary>
[MarshalAs(UnmanagedType.FunctionPtr)]
public TCI_DEL_FLOW_COMPLETE_HANDLER ClDeleteFlowCompleteHandler;
public TCI_DEL_FLOW_COMPLETE_HANDLER? ClDeleteFlowCompleteHandler;
}
/// <summary>Provides a <see cref="SafeHandle"/> for <see cref="HCLIENT"/> that is disposed using <see cref="TcDeregisterClient"/>.</summary>

View File

@ -169,9 +169,30 @@ public static partial class Ws2_32
public struct FLOWSPEC
{
/// <summary>Represents a FLOWSPEC with all unspecified values.</summary>
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);
/// <summary>Initializes a new instance of the <see cref="FLOWSPEC"/> struct.</summary>
/// <param name="delayVariation">The delay variation.</param>
/// <param name="latency">The latency.</param>
/// <param name="maxSduSize">Maximum size of the sdu.</param>
/// <param name="minimumPolicedSize">Minimum size of the policed.</param>
/// <param name="peakBandwidth">The peak bandwidth.</param>
/// <param name="tokenBucketSize">Size of the token bucket.</param>
/// <param name="tokenRate">The token rate.</param>
/// <param name="serviceType">Type of the service.</param>
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;
}
/// <summary>
/// <para>

View File

@ -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<QOS_DS_CLASS>(), 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<QOS_DS_CLASS>(), 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<ulong>(hQos, flowIds[0], QOS_QUERY_FLOW.QOSQueryOutgoingRate, false).WriteValues(), Throws.Nothing);
Assert.That(() => QOSQueryFlow<ulong>(hQos, flowIds![0], QOS_QUERY_FLOW.QOSQueryOutgoingRate, false).WriteValues(), Throws.Nothing);
Assert.That(QOSRemoveSocketFromFlow(hQos, socket.Handle, flowId), ResultIs.Successful);