From d5295aa8795b2cbaac360701d0cae1031e20dd93 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 12 Aug 2025 19:56:34 +0000 Subject: [PATCH 01/12] Initial plan From 3bbba6b066429ca532b3688f0649b9f265d230cd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 12 Aug 2025 20:05:42 +0000 Subject: [PATCH 02/12] Update .NET 10 documentation for Preview 7 release Co-authored-by: BillWagner <493969+BillWagner@users.noreply.github.com> --- docs/core/whats-new/dotnet-10/libraries.md | 364 ++++++++++++++++++++- docs/core/whats-new/dotnet-10/overview.md | 8 +- docs/core/whats-new/dotnet-10/runtime.md | 7 +- docs/core/whats-new/dotnet-10/sdk.md | 32 +- 4 files changed, 401 insertions(+), 10 deletions(-) diff --git a/docs/core/whats-new/dotnet-10/libraries.md b/docs/core/whats-new/dotnet-10/libraries.md index d160657de7993..fafc50a47e988 100644 --- a/docs/core/whats-new/dotnet-10/libraries.md +++ b/docs/core/whats-new/dotnet-10/libraries.md @@ -2,14 +2,14 @@ title: What's new in .NET libraries for .NET 10 description: Learn about the updates to the .NET libraries for .NET 10. titleSuffix: "" -ms.date: 07/16/2025 +ms.date: 08/12/2025 ms.topic: whats-new ai-usage: ai-assisted --- # What's new in .NET libraries for .NET 10 -This article describes new features in the .NET libraries for .NET 10. It's been updated for Preview 6. +This article describes new features in the .NET libraries for .NET 10. It's been updated for Preview 7. ## Cryptography @@ -328,3 +328,363 @@ A community contribution improved the performance of to launch a process in a separate process group. This allows you to send isolated signals to child processes which could otherwise take down the parent without proper handling. Sending signals is convenient to avoid forceful termination. + +```csharp +using System; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using System.Threading; + +class Program +{ + static void Main(string[] args) + { + bool isChildProcess = args.Length > 0 && args[0] == "child"; + if (!isChildProcess) + { + var psi = new ProcessStartInfo + { + FileName = Environment.ProcessPath, + Arguments = "child", + CreateNewProcessGroup = true, + }; + + using Process process = Process.Start(psi)!; + Thread.Sleep(5_000); + + GenerateConsoleCtrlEvent(CTRL_C_EVENT, (uint)process.Id); + process.WaitForExit(); + + Console.WriteLine("Child process terminated gracefully, continue with the parent process logic if needed."); + } + else + { + // If you need to send a CTRL+C, the child process needs to re-enable CTRL+C handling, if you own the code, you can call SetConsoleCtrlHandler(NULL, FALSE). + // see https://learn.microsoft.com/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw#remarks + SetConsoleCtrlHandler((IntPtr)null, false); + + Console.WriteLine("Greetings from the child process! I need to be gracefully terminated, send me a signal!"); + + bool stop = false; + + var registration = PosixSignalRegistration.Create(PosixSignal.SIGINT, ctx => + { + stop = true; + ctx.Cancel = true; + Console.WriteLine("Received CTRL+C, stopping..."); + }); + + StreamWriter sw = File.AppendText("log.txt"); + int i = 0; + while (!stop) + { + Thread.Sleep(1000); + sw.WriteLine($"{++i}"); + Console.WriteLine($"Logging {i}..."); + } + + // Clean up + sw.Dispose(); + registration.Dispose(); + + Console.WriteLine("Thanks for not killing me!"); + } + } + + private const int CTRL_C_EVENT = 0; + private const int CTRL_BREAK_EVENT = 1; + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool SetConsoleCtrlHandler(IntPtr handler, [MarshalAs(UnmanagedType.Bool)] bool Add); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool GenerateConsoleCtrlEvent(uint dwCtrlEvent, uint dwProcessGroupId); +} +``` + +### AES KeyWrap with Padding (IETF RFC 5649) + +AES-KWP is an algorithm that is occasionally used in constructions like Cryptographic Message Syntax (CMS) EnvelopedData, where content is encrypted once, but the decryption key needs to be distributed to multiple parties, each one in a distinct secret form. + +.NET now supports the AES-KWP algorithm via instance methods on the class: + +```csharp +private static byte[] DecryptContent(ReadOnlySpan kek, ReadOnlySpan encryptedKey, ReadOnlySpan ciphertext) +{ + using (Aes aes = Aes.Create()) + { + aes.SetKey(kek); + + Span dek = stackalloc byte[256 / 8]; + int length = aes.DecryptKeyWrapPadded(encryptedKey, dek); + + aes.SetKey(dek.Slice(0, length)); + return aes.DecryptCbc(ciphertext); + } +} +``` + +### Post-quantum cryptography updates + +#### ML-DSA + +The class gained ease-of-use updates in this release, allowing some common code patterns to be simplified: + +```diff +private static byte[] SignData(string privateKeyPath, ReadOnlySpan data) +{ + using (MLDsa signingKey = MLDsa.ImportFromPem(File.ReadAllBytes(privateKeyPath))) + { +- byte[] signature = new byte[signingKey.Algorithm.SignatureSizeInBytes]; +- signingKey.SignData(data, signature); ++ return signingKey.SignData(data); +- return signature; + } +} +``` + +Additionally, this release added support for HashML-DSA, which we call "PreHash" to help distinguish it from "pure" ML-DSA. As the underlying specification interacts with the Object Identifier (OID) value, the SignPreHash and VerifyPreHash methods on this `[Experimental]` type take the dotted-decimal OID as a string. This may evolve as more scenarios using HashML-DSA become well-defined. + +```csharp +private static byte[] SignPreHashSha3_256(MLDsa signingKey, ReadOnlySpan data) +{ + const string Sha3_256Oid = "2.16.840.1.101.3.4.2.8"; + return signingKey.SignPreHash(SHA3_256.HashData(data), Sha3_256Oid); +} +``` + +#### Composite ML-DSA + +This release also introduces new types to support ietf-lamps-pq-composite-sigs (currently at draft 7), and an implementation of the primitive methods for RSA variants. + +```csharp +var algorithm = CompositeMLDsaAlgorithm.MLDsa65WithRSA4096Pss; +using var privateKey = CompositeMLDsa.GenerateKey(algorithm); + +byte[] data = [42]; +byte[] signature = privateKey.SignData(data); + +using var publicKey = CompositeMLDsa.ImportCompositeMLDsaPublicKey(algorithm, privateKey.ExportCompositeMLDsaPublicKey()); +Console.WriteLine(publicKey.VerifyData(data, signature)); // True + +signature[0] ^= 1; // Tamper with signature +Console.WriteLine(publicKey.VerifyData(data, signature)); // False +``` + +### PipeReader support for JSON serializer + + now supports , complementing the existing support. Previously, deserializing from a `PipeReader` required converting it to a , but the new overloads eliminate that step by integrating `PipeReader` directly into the serializer. As a bonus, not having to convert from what you're already holding can yield some efficiency benefits. + +This shows the basic usage: + +```csharp +var pipe = new Pipe(); + +// Serialize to writer +await JsonSerializer.SerializeAsync(pipe.Writer, new Person("Alice")); +await pipe.Writer.CompleteAsync(); + +// Deserialize from reader +var result = await JsonSerializer.DeserializeAsync(pipe.Reader); +await pipe.Reader.CompleteAsync(); + +Console.WriteLine($"Your name is {result.Name}."); +// Output: Your name is Alice. + +record Person(string Name); +``` + +Here is an example of a producer that produces tokens in chunks and a consumer that receives and displays them. + +```csharp +var pipe = new Pipe(); + +// Producer writes to the pipe in chunks +var producerTask = Task.Run(async () => +{ + async static IAsyncEnumerable GenerateResponse() + { + yield return new Chunk("The quick brown fox", DateTime.Now); + await Task.Delay(500); + yield return new Chunk(" jumps over", DateTime.Now); + await Task.Delay(500); + yield return new Chunk(" the lazy dog.", DateTime.Now); + } + + await JsonSerializer.SerializeAsync>(pipe.Writer, GenerateResponse()); + await pipe.Writer.CompleteAsync(); +}); + +// Consumer reads from the pipe and outputs to console +var consumerTask = Task.Run(async () => +{ + var thinkingString = "..."; + var clearThinkingString = new string("\b\b\b"); + var lastTimestamp = DateTime.MinValue; + + // Read response to end + Console.Write(thinkingString); + await foreach (var chunk in JsonSerializer.DeserializeAsyncEnumerable(pipe.Reader)) + { + Console.Write(clearThinkingString); + Console.Write(chunk.Message); + Console.Write(thinkingString); + lastTimestamp = DateTime.Now; + } + + Console.Write(clearThinkingString); + Console.WriteLine($" Last message sent at {lastTimestamp}."); + + await pipe.Reader.CompleteAsync(); +}); + +await producerTask; +await consumerTask; + +record Chunk(string Message, DateTime Timestamp); + +// Output (500ms between each line): +// The quick brown fox... +// The quick brown fox jumps over... +// The quick brown fox jumps over the lazy dog. Last message sent at 8/1/2025 6:41:35 PM. +``` + +Note that all of this is serialized as JSON in the (formatted here for readability): + +```json +[ + { + "Message": "The quick brown fox", + "Timestamp": "2025-08-01T18:37:27.2930151-07:00" + }, + { + "Message": " jumps over", + "Timestamp": "2025-08-01T18:37:27.8594502-07:00" + }, + { + "Message": " the lazy dog.", + "Timestamp": "2025-08-01T18:37:28.3753669-07:00" + } +] +``` + +### WebSocketStream + +This release introduces `WebSocketStream`, a new API designed to simplify some of the most common—and previously cumbersome— scenarios in .NET. + +Traditional `WebSocket` APIs are low-level and require significant boilerplate: handling buffering and framing, reconstructing messages, managing encoding/decoding, and writing custom wrappers to integrate with streams, channels, or other transport abstractions. These complexities make it difficult to use WebSockets as a transport, especially for apps with streaming or text-based protocols, or event-driven handlers. + +**WebSocketStream** addresses these pain points by providing a -based abstraction over a WebSocket. This enables seamless integration with existing APIs for reading, writing, and parsing data, whether binary or text, and reduces the need for manual plumbing. + +#### Common usage patterns + +Here are a few examples of how `WebSocketStream` simplifies typical WebSocket workflows: + +##### Streaming text protocol (for example, STOMP) + +```csharp +using Stream transportStream = WebSocketStream.Create( + connectedWebSocket, + WebSocketMessageType.Text, + ownsWebSocket: true); +// Integration with Stream-based APIs +// Don't close the stream, as it's also used for writing +using var transportReader = new StreamReader(transportStream, leaveOpen: true); +var line = await transportReader.ReadLineAsync(cancellationToken); // Automatic UTF-8 and new line handling +transportStream.Dispose(); // Automatic closing handshake handling on `Dispose` +``` + +##### Streaming binary protocol (for example, AMQP) + +```csharp +Stream transportStream = WebSocketStream.Create( + connectedWebSocket, + WebSocketMessageType.Binary, + closeTimeout: TimeSpan.FromSeconds(10)); +await message.SerializeToStreamAsync(transportStream, cancellationToken); +var receivePayload = new byte[payloadLength]; +await transportStream.ReadExactlyAsync(receivePayload, cancellationToken); +transportStream.Dispose(); +// `Dispose` automatically handles closing handshake +``` + +##### Reading a single message as a stream (for example, JSON deserialization) + +```csharp +using Stream messageStream = WebSocketStream.CreateReadableMessageStream(connectedWebSocket, WebSocketMessageType.Text); +// JsonSerializer.DeserializeAsync reads until the end of stream. +var appMessage = await JsonSerializer.DeserializeAsync(messageStream); +``` + +##### Writing a single message as a stream (for example, binary serialization) + +```csharp +public async Task SendMessageAsync(AppMessage message, CancellationToken cancellationToken) +{ + using Stream messageStream = WebSocketStream.CreateWritableMessageStream(_connectedWebSocket, WebSocketMessageType.Binary); + foreach (ReadOnlyMemory chunk in message.SplitToChunks()) + { + await messageStream.WriteAsync(chunk, cancellationToken); + } +} // EOM sent on messageStream.Dispose() +``` + +**WebSocketStream** enables high-level, familiar APIs for common WebSocket consumption and production patterns—reducing friction and making advanced scenarios easier to implement. + +### TLS 1.3 for macOS (client) + +This release adds client-side TLS 1.3 support on macOS by integrating Apple's Network.framework into and . Historically, macOS used Secure Transport which doesn't support TLS 1.3; opting into Network.framework enables TLS 1.3. + +#### Scope and behavior + +- macOS only, client-side in this release. +- Opt-in. Existing apps continue to use the current stack unless enabled. +- When enabled, older TLS versions (TLS 1.0 and 1.1) may no longer be available via Network.framework. + +#### How to enable + +Use an AppContext switch in code: + +```csharp +// Opt in to Network.framework-backed TLS on Apple platforms +AppContext.SetSwitch("System.Net.Security.UseNetworkFramework", true); + +using var client = new HttpClient(); +var html = await client.GetStringAsync("https://example.com"); +``` + +Or use an environment variable: + +```bash +# Opt-in via environment variable (set for the process or machine as appropriate) +DOTNET_SYSTEM_NET_SECURITY_USENETWORKFRAMEWORK=1 +# or +DOTNET_SYSTEM_NET_SECURITY_USENETWORKFRAMEWORK=true +``` + +#### Notes + +- Applies to and APIs built on it (for example, /). +- Cipher suites are controlled by macOS via Network.framework. +- Underlying stream behavior may differ when Network.framework is enabled (for example, buffering, read/write completion, cancellation semantics). +- Zero-byte reads: semantics may differ. Avoid relying on zero-length reads for detecting data availability. +- Internationalized domain names (IDN): certain IDN hostnames may be rejected by Network.framework. Prefer ASCII/Punycode (A-label) hostnames or validate names against macOS/Network.framework constraints. +- If your app relies on specific edge-case behavior, validate it under Network.framework. diff --git a/docs/core/whats-new/dotnet-10/overview.md b/docs/core/whats-new/dotnet-10/overview.md index 2b70efb375c74..16ca1f5117600 100644 --- a/docs/core/whats-new/dotnet-10/overview.md +++ b/docs/core/whats-new/dotnet-10/overview.md @@ -2,14 +2,14 @@ title: What's new in .NET 10 description: Learn about the new features introduced in .NET 10 for the runtime, libraries, and SDK. Also find links to what's new in other areas, such as ASP.NET Core. titleSuffix: "" -ms.date: 07/16/2025 +ms.date: 08/12/2025 ms.topic: whats-new ai-usage: ai-assisted --- # What's new in .NET 10 -Learn about the new features in .NET 10 and find links to further documentation. This page has been updated for Preview 6. +Learn about the new features in .NET 10 and find links to further documentation. This page has been updated for Preview 7. .NET 10, the successor to [.NET 9](../dotnet-9/overview.md), is [supported for three years](https://dotnet.microsoft.com/platform/support/policy/dotnet-core) as a long-term support (LTS) release. You can [download .NET 10 here](https://get.dot.net/10). @@ -23,14 +23,14 @@ For more information, see [What's new in the .NET 10 runtime](runtime.md). ## .NET libraries -The .NET 10 libraries introduce new APIs in cryptography, globalization, numerics, serialization, collections, and diagnostics, and when working with ZIP files. New JSON serialization options include disallowing duplicate properties and strict serialization settings. Post-quantum cryptography support has been expanded with Windows Cryptography API: Next Generation (CNG) support. +The .NET 10 libraries introduce new APIs in cryptography, globalization, numerics, serialization, collections, and diagnostics, and when working with ZIP files. New JSON serialization options include disallowing duplicate properties and strict serialization settings. Post-quantum cryptography support has been expanded with Windows Cryptography API: Next Generation (CNG) support. Preview 7 adds Windows process group support, AES KeyWrap with Padding, enhanced post-quantum cryptography with ML-DSA updates, PipeReader support for JSON serialization, WebSocketStream for simplified WebSocket usage, and TLS 1.3 support for macOS clients. For more information, see [What's new in the .NET 10 libraries](libraries.md). For details on JSON serialization, see [System.Text.Json overview](/dotnet/standard/serialization/system-text-json/overview). ## .NET SDK -The .NET 10 SDK includes support for [Microsoft.Testing.Platform](../../testing/microsoft-testing-platform-intro.md) in `dotnet test`, standardizes CLI command order, and updates the CLI to generate native tab-completion scripts for popular shells. For containers, console apps can natively create container images, and a new property lets you explicitly set the format of container images. The SDK also supports platform-specific .NET tools, one-shot tool execution with `dotnet tool exec`, the new `dnx` tool execution script, CLI introspection with `--cli-schema`, and enhanced file-based apps with publish support and native AOT. +The .NET 10 SDK includes support for [Microsoft.Testing.Platform](../../testing/microsoft-testing-platform-intro.md) in `dotnet test`, standardizes CLI command order, and updates the CLI to generate native tab-completion scripts for popular shells. For containers, console apps can natively create container images, and a new property lets you explicitly set the format of container images. The SDK also supports platform-specific .NET tools, one-shot tool execution with `dotnet tool exec`, the new `dnx` tool execution script, CLI introspection with `--cli-schema`, and enhanced file-based apps with publish support and native AOT. Preview 7 adds support for using the `any` RuntimeIdentifier with platform-specific .NET tools for maximum compatibility. For more information, see [What's new in the SDK for .NET 10](sdk.md). For details on .NET tools, see [Manage .NET tools](/dotnet/core/tools/global-tools). diff --git a/docs/core/whats-new/dotnet-10/runtime.md b/docs/core/whats-new/dotnet-10/runtime.md index 36e804b2ae8f9..cfcc46773d5c6 100644 --- a/docs/core/whats-new/dotnet-10/runtime.md +++ b/docs/core/whats-new/dotnet-10/runtime.md @@ -2,13 +2,16 @@ title: What's new in .NET 10 runtime description: Learn about the new features introduced in the .NET 10 runtime. titleSuffix: "" -ms.date: 07/16/2025 +ms.date: 08/12/2025 ms.topic: whats-new ai-usage: ai-assisted --- # What's new in the .NET 10 runtime -This article describes new features and performance improvements in the .NET runtime for .NET 10. It has been updated for Preview 6. +This article describes new features and performance improvements in the .NET runtime for .NET 10. It has been updated for Preview 7. + +> [!NOTE] +> .NET 10 Preview 7 does not include new runtime features. The runtime improvements and features described below were introduced in earlier previews. ## JIT compiler improvements diff --git a/docs/core/whats-new/dotnet-10/sdk.md b/docs/core/whats-new/dotnet-10/sdk.md index 4e9670b714c64..1a88d55cc34a9 100644 --- a/docs/core/whats-new/dotnet-10/sdk.md +++ b/docs/core/whats-new/dotnet-10/sdk.md @@ -2,14 +2,14 @@ title: What's new in the SDK and tooling for .NET 10 description: Learn about the new .NET SDK features introduced in .NET 10. titleSuffix: "" -ms.date: 07/16/2025 +ms.date: 08/12/2025 ms.topic: whats-new ai-usage: ai-assisted --- # What's new in the SDK and tooling for .NET 10 -This article describes new features and enhancements in the .NET SDK for .NET 10. It has been updated for Preview 6. +This article describes new features and enhancements in the .NET SDK for .NET 10. It has been updated for Preview 7. ## .NET tools enhancements @@ -202,3 +202,31 @@ name = "Microsoft.Testing.Platform" ``` For more details, see [Testing with `dotnet test`](../../testing/unit-testing-with-dotnet-test.md). + +## Preview 7 additions + +The following feature was added in Preview 7: + +### Use the `any` RuntimeIdentifier with platform-specific .NET tools + +The [platform-specific .NET tools](#platform-specific-net-tools) feature released in Preview 6 is great for making sure your tools are optimized for specific platforms that you target ahead-of-time. However, there are times where you won't know all of the platforms that you'd like to target, or sometimes .NET itself will learn how to support a new platform, and you'd like your tool to be runnable there too. + +The good news is that .NET is great at this - the platform at its heart is meant to support this kind of platform-agnostic execution. To make your new platform-specific .NET tools work this way, you only need to add one thing to your project file: the `any` Runtime Identifier. + +```diff + + + linux-x64; + linux-arm64; + macos-arm64; + win-x64; +- win-arm64 ++ win-arm64; ++ any + + +``` + +This RuntimeIdentifier is at the 'root' of our platform-compatibility checking, and since it declares support for, well, _any_ platform, the tool that we package for you will be the most compatible kind of tool - a framework-dependent, platform-agnostic .NET dll, which requires a compatible .NET Runtime in order to execute. When you perform a `dotnet pack` to create your tool, you'll see a new package for the `any` RuntimeIdentifier appear alongside the other platform-specific packages and the top-level manifest package. + +The eagle-eyed among you will note that this is the exact same kind of tool that you would make in .NET 9 and earlier, but now it fits into the overall goal of enabling platform-specific .NET tools! From 16b5fb6ffd03607c549ac504715c95b38f151b04 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 12 Aug 2025 21:26:46 +0000 Subject: [PATCH 03/12] Restructure documentation and address all review feedback Co-authored-by: BillWagner <493969+BillWagner@users.noreply.github.com> --- docs/core/whats-new/dotnet-10/libraries.md | 414 +++++------------- docs/core/whats-new/dotnet-10/overview.md | 6 +- docs/core/whats-new/dotnet-10/runtime.md | 5 +- docs/core/whats-new/dotnet-10/sdk.md | 54 ++- .../snippets/csharp/PipeReaderBasic.cs | 19 + .../snippets/csharp/PipeReaderChunks.cs | 51 +++ .../dotnet-10/snippets/csharp/ProcessGroup.cs | 73 +++ .../snippets/csharp/WebSocketStreamBinary.cs | 16 + .../snippets/csharp/WebSocketStreamRead.cs | 8 + .../snippets/csharp/WebSocketStreamText.cs | 15 + .../snippets/csharp/WebSocketStreamWrite.cs | 15 + 11 files changed, 344 insertions(+), 332 deletions(-) create mode 100644 docs/core/whats-new/dotnet-10/snippets/csharp/PipeReaderBasic.cs create mode 100644 docs/core/whats-new/dotnet-10/snippets/csharp/PipeReaderChunks.cs create mode 100644 docs/core/whats-new/dotnet-10/snippets/csharp/ProcessGroup.cs create mode 100644 docs/core/whats-new/dotnet-10/snippets/csharp/WebSocketStreamBinary.cs create mode 100644 docs/core/whats-new/dotnet-10/snippets/csharp/WebSocketStreamRead.cs create mode 100644 docs/core/whats-new/dotnet-10/snippets/csharp/WebSocketStreamText.cs create mode 100644 docs/core/whats-new/dotnet-10/snippets/csharp/WebSocketStreamWrite.cs diff --git a/docs/core/whats-new/dotnet-10/libraries.md b/docs/core/whats-new/dotnet-10/libraries.md index fafc50a47e988..cd5bfb7478b6c 100644 --- a/docs/core/whats-new/dotnet-10/libraries.md +++ b/docs/core/whats-new/dotnet-10/libraries.md @@ -9,7 +9,7 @@ ai-usage: ai-assisted # What's new in .NET libraries for .NET 10 -This article describes new features in the .NET libraries for .NET 10. It's been updated for Preview 7. +This article describes new features in the .NET libraries for .NET 10. ## Cryptography @@ -113,6 +113,73 @@ private static bool ValidateMLDsaSignature(ReadOnlySpan data, ReadOnlySpan The PQC algorithms are available on systems where the system cryptographic libraries are OpenSSL 3.5 (or newer) or Windows CNG with PQC support. Also, the new classes are all marked as [`[Experimental]`](../../../fundamentals/syslib-diagnostics/experimental-overview.md) under diagnostic `SYSLIB5006` until development is complete. +#### ML-DSA enhancements + +The class gained ease-of-use updates, allowing some common code patterns to be simplified: + +```diff +private static byte[] SignData(string privateKeyPath, ReadOnlySpan data) +{ + using (MLDsa signingKey = MLDsa.ImportFromPem(File.ReadAllBytes(privateKeyPath))) + { +- byte[] signature = new byte[signingKey.Algorithm.SignatureSizeInBytes]; +- signingKey.SignData(data, signature); ++ return signingKey.SignData(data); +- return signature; + } +} +``` + +Additionally, this release added support for HashML-DSA, which is called "PreHash" to help distinguish it from "pure" ML-DSA. As the underlying specification interacts with the Object Identifier (OID) value, the SignPreHash and VerifyPreHash methods on this `[Experimental]` type take the dotted-decimal OID as a string. This might evolve as more scenarios using HashML-DSA become well-defined. + +```csharp +private static byte[] SignPreHashSha3_256(MLDsa signingKey, ReadOnlySpan data) +{ + const string Sha3_256Oid = "2.16.840.1.101.3.4.2.8"; + return signingKey.SignPreHash(SHA3_256.HashData(data), Sha3_256Oid); +} +``` + +#### Composite ML-DSA + +This release also introduces new types to support ietf-lamps-pq-composite-sigs (currently at draft 7), and an implementation of the primitive methods for RSA variants. + +```csharp +var algorithm = CompositeMLDsaAlgorithm.MLDsa65WithRSA4096Pss; +using var privateKey = CompositeMLDsa.GenerateKey(algorithm); + +byte[] data = [42]; +byte[] signature = privateKey.SignData(data); + +using var publicKey = CompositeMLDsa.ImportCompositeMLDsaPublicKey(algorithm, privateKey.ExportCompositeMLDsaPublicKey()); +Console.WriteLine(publicKey.VerifyData(data, signature)); // True + +signature[0] ^= 1; // Tamper with signature +Console.WriteLine(publicKey.VerifyData(data, signature)); // False +``` + +### AES KeyWrap with Padding (IETF RFC 5649) + +AES-KWP is an algorithm that is occasionally used in constructions like Cryptographic Message Syntax (CMS) EnvelopedData, where content is encrypted once, but the decryption key needs to be distributed to multiple parties, each one in a distinct secret form. + +.NET now supports the AES-KWP algorithm via instance methods on the class: + +```csharp +private static byte[] DecryptContent(ReadOnlySpan kek, ReadOnlySpan encryptedKey, ReadOnlySpan ciphertext) +{ + using (Aes aes = Aes.Create()) + { + aes.SetKey(kek); + + Span dek = stackalloc byte[256 / 8]; + int length = aes.DecryptKeyWrapPadded(encryptedKey, dek); + + aes.SetKey(dek.Slice(0, length)); + return aes.DecryptCbc(ciphertext); + } +} +``` + ## Globalization and date/time - [New method overloads in ISOWeek for DateOnly type](#new-method-overloads-in-isoweek-for-dateonly-type) @@ -183,6 +250,7 @@ This new API is already used in and improves the p - [Allow specifying ReferenceHandler in `JsonSourceGenerationOptions`](#allow-specifying-referencehandler-in-jsonsourcegenerationoptions) - [Option to disallow duplicate JSON properties](#option-to-disallow-duplicate-json-properties) - [Strict JSON serialization options](#strict-json-serialization-options) +- [PipeReader support for JSON serializer](#pipereader-support-for-json-serializer) ### Allow specifying ReferenceHandler in `JsonSourceGenerationOptions` @@ -224,6 +292,37 @@ These options are read-compatible with now supports , complementing the existing support. Previously, deserializing from a `PipeReader` required converting it to a , but the new overloads eliminate that step by integrating `PipeReader` directly into the serializer. As a bonus, not having to convert from what you're already holding can yield some efficiency benefits. + +This shows the basic usage: + +:::code language="csharp" source="snippets/csharp/PipeReaderBasic.cs"::: + +Here is an example of a producer that produces tokens in chunks and a consumer that receives and displays them: + +:::code language="csharp" source="snippets/csharp/PipeReaderChunks.cs"::: + +Note that all of this is serialized as JSON in the (formatted here for readability): + +```json +[ + { + "Message": "The quick brown fox", + "Timestamp": "2025-08-01T18:37:27.2930151-07:00" + }, + { + "Message": " jumps over", + "Timestamp": "2025-08-01T18:37:27.8594502-07:00" + }, + { + "Message": " the lazy dog.", + "Timestamp": "2025-08-01T18:37:28.3753669-07:00" + } +] +``` + ## System.Numerics - [More left-handed matrix transformation methods](#more-left-handed-matrix-transformation-methods) @@ -329,270 +428,23 @@ A community contribution improved the performance of to launch a process in a separate process group. This allows you to send isolated signals to child processes which could otherwise take down the parent without proper handling. Sending signals is convenient to avoid forceful termination. -```csharp -using System; -using System.Diagnostics; -using System.IO; -using System.Runtime.InteropServices; -using System.Threading; - -class Program -{ - static void Main(string[] args) - { - bool isChildProcess = args.Length > 0 && args[0] == "child"; - if (!isChildProcess) - { - var psi = new ProcessStartInfo - { - FileName = Environment.ProcessPath, - Arguments = "child", - CreateNewProcessGroup = true, - }; - - using Process process = Process.Start(psi)!; - Thread.Sleep(5_000); - - GenerateConsoleCtrlEvent(CTRL_C_EVENT, (uint)process.Id); - process.WaitForExit(); - - Console.WriteLine("Child process terminated gracefully, continue with the parent process logic if needed."); - } - else - { - // If you need to send a CTRL+C, the child process needs to re-enable CTRL+C handling, if you own the code, you can call SetConsoleCtrlHandler(NULL, FALSE). - // see https://learn.microsoft.com/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw#remarks - SetConsoleCtrlHandler((IntPtr)null, false); - - Console.WriteLine("Greetings from the child process! I need to be gracefully terminated, send me a signal!"); - - bool stop = false; - - var registration = PosixSignalRegistration.Create(PosixSignal.SIGINT, ctx => - { - stop = true; - ctx.Cancel = true; - Console.WriteLine("Received CTRL+C, stopping..."); - }); - - StreamWriter sw = File.AppendText("log.txt"); - int i = 0; - while (!stop) - { - Thread.Sleep(1000); - sw.WriteLine($"{++i}"); - Console.WriteLine($"Logging {i}..."); - } - - // Clean up - sw.Dispose(); - registration.Dispose(); - - Console.WriteLine("Thanks for not killing me!"); - } - } - - private const int CTRL_C_EVENT = 0; - private const int CTRL_BREAK_EVENT = 1; - - [DllImport("kernel32.dll", SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - private static extern bool SetConsoleCtrlHandler(IntPtr handler, [MarshalAs(UnmanagedType.Bool)] bool Add); - - [DllImport("kernel32.dll", SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - private static extern bool GenerateConsoleCtrlEvent(uint dwCtrlEvent, uint dwProcessGroupId); -} -``` - -### AES KeyWrap with Padding (IETF RFC 5649) - -AES-KWP is an algorithm that is occasionally used in constructions like Cryptographic Message Syntax (CMS) EnvelopedData, where content is encrypted once, but the decryption key needs to be distributed to multiple parties, each one in a distinct secret form. - -.NET now supports the AES-KWP algorithm via instance methods on the class: - -```csharp -private static byte[] DecryptContent(ReadOnlySpan kek, ReadOnlySpan encryptedKey, ReadOnlySpan ciphertext) -{ - using (Aes aes = Aes.Create()) - { - aes.SetKey(kek); - - Span dek = stackalloc byte[256 / 8]; - int length = aes.DecryptKeyWrapPadded(encryptedKey, dek); - - aes.SetKey(dek.Slice(0, length)); - return aes.DecryptCbc(ciphertext); - } -} -``` - -### Post-quantum cryptography updates - -#### ML-DSA - -The class gained ease-of-use updates in this release, allowing some common code patterns to be simplified: - -```diff -private static byte[] SignData(string privateKeyPath, ReadOnlySpan data) -{ - using (MLDsa signingKey = MLDsa.ImportFromPem(File.ReadAllBytes(privateKeyPath))) - { -- byte[] signature = new byte[signingKey.Algorithm.SignatureSizeInBytes]; -- signingKey.SignData(data, signature); -+ return signingKey.SignData(data); -- return signature; - } -} -``` - -Additionally, this release added support for HashML-DSA, which we call "PreHash" to help distinguish it from "pure" ML-DSA. As the underlying specification interacts with the Object Identifier (OID) value, the SignPreHash and VerifyPreHash methods on this `[Experimental]` type take the dotted-decimal OID as a string. This may evolve as more scenarios using HashML-DSA become well-defined. - -```csharp -private static byte[] SignPreHashSha3_256(MLDsa signingKey, ReadOnlySpan data) -{ - const string Sha3_256Oid = "2.16.840.1.101.3.4.2.8"; - return signingKey.SignPreHash(SHA3_256.HashData(data), Sha3_256Oid); -} -``` - -#### Composite ML-DSA - -This release also introduces new types to support ietf-lamps-pq-composite-sigs (currently at draft 7), and an implementation of the primitive methods for RSA variants. - -```csharp -var algorithm = CompositeMLDsaAlgorithm.MLDsa65WithRSA4096Pss; -using var privateKey = CompositeMLDsa.GenerateKey(algorithm); - -byte[] data = [42]; -byte[] signature = privateKey.SignData(data); - -using var publicKey = CompositeMLDsa.ImportCompositeMLDsaPublicKey(algorithm, privateKey.ExportCompositeMLDsaPublicKey()); -Console.WriteLine(publicKey.VerifyData(data, signature)); // True - -signature[0] ^= 1; // Tamper with signature -Console.WriteLine(publicKey.VerifyData(data, signature)); // False -``` - -### PipeReader support for JSON serializer - - now supports , complementing the existing support. Previously, deserializing from a `PipeReader` required converting it to a , but the new overloads eliminate that step by integrating `PipeReader` directly into the serializer. As a bonus, not having to convert from what you're already holding can yield some efficiency benefits. - -This shows the basic usage: - -```csharp -var pipe = new Pipe(); - -// Serialize to writer -await JsonSerializer.SerializeAsync(pipe.Writer, new Person("Alice")); -await pipe.Writer.CompleteAsync(); - -// Deserialize from reader -var result = await JsonSerializer.DeserializeAsync(pipe.Reader); -await pipe.Reader.CompleteAsync(); - -Console.WriteLine($"Your name is {result.Name}."); -// Output: Your name is Alice. - -record Person(string Name); -``` - -Here is an example of a producer that produces tokens in chunks and a consumer that receives and displays them. - -```csharp -var pipe = new Pipe(); - -// Producer writes to the pipe in chunks -var producerTask = Task.Run(async () => -{ - async static IAsyncEnumerable GenerateResponse() - { - yield return new Chunk("The quick brown fox", DateTime.Now); - await Task.Delay(500); - yield return new Chunk(" jumps over", DateTime.Now); - await Task.Delay(500); - yield return new Chunk(" the lazy dog.", DateTime.Now); - } - - await JsonSerializer.SerializeAsync>(pipe.Writer, GenerateResponse()); - await pipe.Writer.CompleteAsync(); -}); - -// Consumer reads from the pipe and outputs to console -var consumerTask = Task.Run(async () => -{ - var thinkingString = "..."; - var clearThinkingString = new string("\b\b\b"); - var lastTimestamp = DateTime.MinValue; - - // Read response to end - Console.Write(thinkingString); - await foreach (var chunk in JsonSerializer.DeserializeAsyncEnumerable(pipe.Reader)) - { - Console.Write(clearThinkingString); - Console.Write(chunk.Message); - Console.Write(thinkingString); - lastTimestamp = DateTime.Now; - } - - Console.Write(clearThinkingString); - Console.WriteLine($" Last message sent at {lastTimestamp}."); +:::code language="csharp" source="snippets/csharp/ProcessGroup.cs"::: - await pipe.Reader.CompleteAsync(); -}); - -await producerTask; -await consumerTask; - -record Chunk(string Message, DateTime Timestamp); - -// Output (500ms between each line): -// The quick brown fox... -// The quick brown fox jumps over... -// The quick brown fox jumps over the lazy dog. Last message sent at 8/1/2025 6:41:35 PM. -``` - -Note that all of this is serialized as JSON in the (formatted here for readability): - -```json -[ - { - "Message": "The quick brown fox", - "Timestamp": "2025-08-01T18:37:27.2930151-07:00" - }, - { - "Message": " jumps over", - "Timestamp": "2025-08-01T18:37:27.8594502-07:00" - }, - { - "Message": " the lazy dog.", - "Timestamp": "2025-08-01T18:37:28.3753669-07:00" - } -] -``` +## WebSocket enhancements ### WebSocketStream -This release introduces `WebSocketStream`, a new API designed to simplify some of the most common—and previously cumbersome— scenarios in .NET. +.NET 10 introduces `WebSocketStream`, a new API designed to simplify some of the most common—and previously cumbersome— scenarios in .NET. Traditional `WebSocket` APIs are low-level and require significant boilerplate: handling buffering and framing, reconstructing messages, managing encoding/decoding, and writing custom wrappers to integrate with streams, channels, or other transport abstractions. These complexities make it difficult to use WebSockets as a transport, especially for apps with streaming or text-based protocols, or event-driven handlers. -**WebSocketStream** addresses these pain points by providing a -based abstraction over a WebSocket. This enables seamless integration with existing APIs for reading, writing, and parsing data, whether binary or text, and reduces the need for manual plumbing. +`WebSocketStream` addresses these pain points by providing a -based abstraction over a WebSocket. This enables seamless integration with existing APIs for reading, writing, and parsing data, whether binary or text, and reduces the need for manual plumbing. #### Common usage patterns @@ -600,64 +452,33 @@ Here are a few examples of how `WebSocketStream` simplifies typical WebSocket wo ##### Streaming text protocol (for example, STOMP) -```csharp -using Stream transportStream = WebSocketStream.Create( - connectedWebSocket, - WebSocketMessageType.Text, - ownsWebSocket: true); -// Integration with Stream-based APIs -// Don't close the stream, as it's also used for writing -using var transportReader = new StreamReader(transportStream, leaveOpen: true); -var line = await transportReader.ReadLineAsync(cancellationToken); // Automatic UTF-8 and new line handling -transportStream.Dispose(); // Automatic closing handshake handling on `Dispose` -``` +:::code language="csharp" source="snippets/csharp/WebSocketStreamText.cs"::: ##### Streaming binary protocol (for example, AMQP) -```csharp -Stream transportStream = WebSocketStream.Create( - connectedWebSocket, - WebSocketMessageType.Binary, - closeTimeout: TimeSpan.FromSeconds(10)); -await message.SerializeToStreamAsync(transportStream, cancellationToken); -var receivePayload = new byte[payloadLength]; -await transportStream.ReadExactlyAsync(receivePayload, cancellationToken); -transportStream.Dispose(); -// `Dispose` automatically handles closing handshake -``` +:::code language="csharp" source="snippets/csharp/WebSocketStreamBinary.cs"::: ##### Reading a single message as a stream (for example, JSON deserialization) -```csharp -using Stream messageStream = WebSocketStream.CreateReadableMessageStream(connectedWebSocket, WebSocketMessageType.Text); -// JsonSerializer.DeserializeAsync reads until the end of stream. -var appMessage = await JsonSerializer.DeserializeAsync(messageStream); -``` +:::code language="csharp" source="snippets/csharp/WebSocketStreamRead.cs"::: ##### Writing a single message as a stream (for example, binary serialization) -```csharp -public async Task SendMessageAsync(AppMessage message, CancellationToken cancellationToken) -{ - using Stream messageStream = WebSocketStream.CreateWritableMessageStream(_connectedWebSocket, WebSocketMessageType.Binary); - foreach (ReadOnlyMemory chunk in message.SplitToChunks()) - { - await messageStream.WriteAsync(chunk, cancellationToken); - } -} // EOM sent on messageStream.Dispose() -``` +:::code language="csharp" source="snippets/csharp/WebSocketStreamWrite.cs"::: + +`WebSocketStream` enables high-level, familiar APIs for common WebSocket consumption and production patterns—reducing friction and making advanced scenarios easier to implement. -**WebSocketStream** enables high-level, familiar APIs for common WebSocket consumption and production patterns—reducing friction and making advanced scenarios easier to implement. +## TLS enhancements ### TLS 1.3 for macOS (client) -This release adds client-side TLS 1.3 support on macOS by integrating Apple's Network.framework into and . Historically, macOS used Secure Transport which doesn't support TLS 1.3; opting into Network.framework enables TLS 1.3. +.NET 10 adds client-side TLS 1.3 support on macOS by integrating Apple's Network.framework into and . Historically, macOS used Secure Transport which doesn't support TLS 1.3; opting into Network.framework enables TLS 1.3. #### Scope and behavior - macOS only, client-side in this release. - Opt-in. Existing apps continue to use the current stack unless enabled. -- When enabled, older TLS versions (TLS 1.0 and 1.1) may no longer be available via Network.framework. +- When enabled, older TLS versions (TLS 1.0 and 1.1) might no longer be available via Network.framework. #### How to enable @@ -684,7 +505,8 @@ DOTNET_SYSTEM_NET_SECURITY_USENETWORKFRAMEWORK=true - Applies to and APIs built on it (for example, /). - Cipher suites are controlled by macOS via Network.framework. -- Underlying stream behavior may differ when Network.framework is enabled (for example, buffering, read/write completion, cancellation semantics). -- Zero-byte reads: semantics may differ. Avoid relying on zero-length reads for detecting data availability. -- Internationalized domain names (IDN): certain IDN hostnames may be rejected by Network.framework. Prefer ASCII/Punycode (A-label) hostnames or validate names against macOS/Network.framework constraints. +- Underlying stream behavior might differ when Network.framework is enabled (for example, buffering, read/write completion, cancellation semantics). +- Zero-byte reads: semantics might differ. Avoid relying on zero-length reads for detecting data availability. +- Internationalized domain names (IDN): certain IDN hostnames might be rejected by Network.framework. Prefer ASCII/Punycode (A-label) hostnames or validate names against macOS/Network.framework constraints. - If your app relies on specific edge-case behavior, validate it under Network.framework. + diff --git a/docs/core/whats-new/dotnet-10/overview.md b/docs/core/whats-new/dotnet-10/overview.md index 16ca1f5117600..cf1be4dcc5bb7 100644 --- a/docs/core/whats-new/dotnet-10/overview.md +++ b/docs/core/whats-new/dotnet-10/overview.md @@ -9,7 +9,7 @@ ai-usage: ai-assisted # What's new in .NET 10 -Learn about the new features in .NET 10 and find links to further documentation. This page has been updated for Preview 7. +Learn about the new features in .NET 10 and find links to further documentation. .NET 10, the successor to [.NET 9](../dotnet-9/overview.md), is [supported for three years](https://dotnet.microsoft.com/platform/support/policy/dotnet-core) as a long-term support (LTS) release. You can [download .NET 10 here](https://get.dot.net/10). @@ -23,14 +23,14 @@ For more information, see [What's new in the .NET 10 runtime](runtime.md). ## .NET libraries -The .NET 10 libraries introduce new APIs in cryptography, globalization, numerics, serialization, collections, and diagnostics, and when working with ZIP files. New JSON serialization options include disallowing duplicate properties and strict serialization settings. Post-quantum cryptography support has been expanded with Windows Cryptography API: Next Generation (CNG) support. Preview 7 adds Windows process group support, AES KeyWrap with Padding, enhanced post-quantum cryptography with ML-DSA updates, PipeReader support for JSON serialization, WebSocketStream for simplified WebSocket usage, and TLS 1.3 support for macOS clients. +The .NET 10 libraries introduce new APIs in cryptography, globalization, numerics, serialization, collections, and diagnostics, and when working with ZIP files. New JSON serialization options include disallowing duplicate properties, strict serialization settings, and PipeReader support for improved efficiency. Post-quantum cryptography support has been expanded with Windows Cryptography API: Next Generation (CNG) support, enhanced ML-DSA with simplified APIs and HashML-DSA support, plus Composite ML-DSA. Additional cryptography enhancements include AES KeyWrap with Padding support. New networking capabilities include WebSocketStream for simplified WebSocket usage and TLS 1.3 support for macOS clients. Process management gains Windows process group support for better signal isolation. For more information, see [What's new in the .NET 10 libraries](libraries.md). For details on JSON serialization, see [System.Text.Json overview](/dotnet/standard/serialization/system-text-json/overview). ## .NET SDK -The .NET 10 SDK includes support for [Microsoft.Testing.Platform](../../testing/microsoft-testing-platform-intro.md) in `dotnet test`, standardizes CLI command order, and updates the CLI to generate native tab-completion scripts for popular shells. For containers, console apps can natively create container images, and a new property lets you explicitly set the format of container images. The SDK also supports platform-specific .NET tools, one-shot tool execution with `dotnet tool exec`, the new `dnx` tool execution script, CLI introspection with `--cli-schema`, and enhanced file-based apps with publish support and native AOT. Preview 7 adds support for using the `any` RuntimeIdentifier with platform-specific .NET tools for maximum compatibility. +The .NET 10 SDK includes support for [Microsoft.Testing.Platform](../../testing/microsoft-testing-platform-intro.md) in `dotnet test`, standardizes CLI command order, and updates the CLI to generate native tab-completion scripts for popular shells. For containers, console apps can natively create container images, and a new property lets you explicitly set the format of container images. The SDK also supports platform-specific .NET tools with enhanced compatibility via the `any` RuntimeIdentifier, one-shot tool execution with `dotnet tool exec`, the new `dnx` tool execution script, CLI introspection with `--cli-schema`, and enhanced file-based apps with publish support and native AOT. For more information, see [What's new in the SDK for .NET 10](sdk.md). For details on .NET tools, see [Manage .NET tools](/dotnet/core/tools/global-tools). diff --git a/docs/core/whats-new/dotnet-10/runtime.md b/docs/core/whats-new/dotnet-10/runtime.md index cfcc46773d5c6..2231693b867d2 100644 --- a/docs/core/whats-new/dotnet-10/runtime.md +++ b/docs/core/whats-new/dotnet-10/runtime.md @@ -8,10 +8,7 @@ ai-usage: ai-assisted --- # What's new in the .NET 10 runtime -This article describes new features and performance improvements in the .NET runtime for .NET 10. It has been updated for Preview 7. - -> [!NOTE] -> .NET 10 Preview 7 does not include new runtime features. The runtime improvements and features described below were introduced in earlier previews. +This article describes new features and performance improvements in the .NET runtime for .NET 10. ## JIT compiler improvements diff --git a/docs/core/whats-new/dotnet-10/sdk.md b/docs/core/whats-new/dotnet-10/sdk.md index 1a88d55cc34a9..727d0b94dd151 100644 --- a/docs/core/whats-new/dotnet-10/sdk.md +++ b/docs/core/whats-new/dotnet-10/sdk.md @@ -9,7 +9,7 @@ ai-usage: ai-assisted # What's new in the SDK and tooling for .NET 10 -This article describes new features and enhancements in the .NET SDK for .NET 10. It has been updated for Preview 7. +This article describes new features and enhancements in the .NET SDK for .NET 10. ## .NET tools enhancements @@ -59,6 +59,30 @@ The actual implementation of the `dnx` command is in the `dotnet` CLI itself, al For more information about managing .NET tools, see [Manage .NET tools](../../tools/global-tools.md). +### Use the `any` RuntimeIdentifier with platform-specific .NET tools + +The [platform-specific .NET tools](#platform-specific-net-tools) feature is great for making sure tools are optimized for specific platforms that you target ahead-of-time. However, there are times where you won't know all of the platforms that you'd like to target, or sometimes .NET itself will learn how to support a new platform, and you'd like your tool to be runnable there too. + +.NET is great at this - the platform at its heart is meant to support this kind of platform-agnostic execution. To make platform-specific .NET tools work this way, you only need to add one thing to your project file: the `any` Runtime Identifier. + +```diff + + + linux-x64; + linux-arm64; + macos-arm64; + win-x64; +- win-arm64 ++ win-arm64; ++ any + + +``` + +This RuntimeIdentifier is at the 'root' of the platform-compatibility checking, and since it declares support for _any_ platform, the tool that gets packaged will be the most compatible kind of tool - a framework-dependent, platform-agnostic .NET dll, which requires a compatible .NET Runtime to execute. When you perform a `dotnet pack` to create your tool, you'll see a new package for the `any` RuntimeIdentifier appear alongside the other platform-specific packages and the top-level manifest package. + +This is the exact same kind of tool that you would make in .NET 9 and earlier, but now it fits into the overall goal of enabling platform-specific .NET tools! + ### CLI introspection with `--cli-schema` A new `--cli-schema` option is available on all CLI commands. When used, it outputs a JSON representation of the CLI command tree for the invoked command or subcommand. This is useful for tool authors, shell integration, and advanced scripting. @@ -202,31 +226,3 @@ name = "Microsoft.Testing.Platform" ``` For more details, see [Testing with `dotnet test`](../../testing/unit-testing-with-dotnet-test.md). - -## Preview 7 additions - -The following feature was added in Preview 7: - -### Use the `any` RuntimeIdentifier with platform-specific .NET tools - -The [platform-specific .NET tools](#platform-specific-net-tools) feature released in Preview 6 is great for making sure your tools are optimized for specific platforms that you target ahead-of-time. However, there are times where you won't know all of the platforms that you'd like to target, or sometimes .NET itself will learn how to support a new platform, and you'd like your tool to be runnable there too. - -The good news is that .NET is great at this - the platform at its heart is meant to support this kind of platform-agnostic execution. To make your new platform-specific .NET tools work this way, you only need to add one thing to your project file: the `any` Runtime Identifier. - -```diff - - - linux-x64; - linux-arm64; - macos-arm64; - win-x64; -- win-arm64 -+ win-arm64; -+ any - - -``` - -This RuntimeIdentifier is at the 'root' of our platform-compatibility checking, and since it declares support for, well, _any_ platform, the tool that we package for you will be the most compatible kind of tool - a framework-dependent, platform-agnostic .NET dll, which requires a compatible .NET Runtime in order to execute. When you perform a `dotnet pack` to create your tool, you'll see a new package for the `any` RuntimeIdentifier appear alongside the other platform-specific packages and the top-level manifest package. - -The eagle-eyed among you will note that this is the exact same kind of tool that you would make in .NET 9 and earlier, but now it fits into the overall goal of enabling platform-specific .NET tools! diff --git a/docs/core/whats-new/dotnet-10/snippets/csharp/PipeReaderBasic.cs b/docs/core/whats-new/dotnet-10/snippets/csharp/PipeReaderBasic.cs new file mode 100644 index 0000000000000..2087bc145425d --- /dev/null +++ b/docs/core/whats-new/dotnet-10/snippets/csharp/PipeReaderBasic.cs @@ -0,0 +1,19 @@ +using System; +using System.IO.Pipelines; +using System.Text.Json; +using System.Threading.Tasks; + +var pipe = new Pipe(); + +// Serialize to writer +await JsonSerializer.SerializeAsync(pipe.Writer, new Person("Alice")); +await pipe.Writer.CompleteAsync(); + +// Deserialize from reader +var result = await JsonSerializer.DeserializeAsync(pipe.Reader); +await pipe.Reader.CompleteAsync(); + +Console.WriteLine($"Your name is {result.Name}."); +// Output: Your name is Alice. + +record Person(string Name); \ No newline at end of file diff --git a/docs/core/whats-new/dotnet-10/snippets/csharp/PipeReaderChunks.cs b/docs/core/whats-new/dotnet-10/snippets/csharp/PipeReaderChunks.cs new file mode 100644 index 0000000000000..75ca0ea8e95e5 --- /dev/null +++ b/docs/core/whats-new/dotnet-10/snippets/csharp/PipeReaderChunks.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.IO.Pipelines; +using System.Text.Json; +using System.Threading.Tasks; + +var pipe = new Pipe(); + +// Producer writes to the pipe in chunks +var producerTask = Task.Run(async () => +{ + async static IAsyncEnumerable GenerateResponse() + { + yield return new Chunk("The quick brown fox", DateTime.Now); + await Task.Delay(500); + yield return new Chunk(" jumps over", DateTime.Now); + await Task.Delay(500); + yield return new Chunk(" the lazy dog.", DateTime.Now); + } + + await JsonSerializer.SerializeAsync>(pipe.Writer, GenerateResponse()); + await pipe.Writer.CompleteAsync(); +}); + +// Consumer reads from the pipe and outputs to console +var consumerTask = Task.Run(async () => +{ + var thinkingString = "..."; + var clearThinkingString = new string("\b\b\b"); + var lastTimestamp = DateTime.MinValue; + + // Read response to end + Console.Write(thinkingString); + await foreach (var chunk in JsonSerializer.DeserializeAsyncEnumerable(pipe.Reader)) + { + Console.Write(clearThinkingString); + Console.Write(chunk.Message); + Console.Write(thinkingString); + lastTimestamp = DateTime.Now; + } + + Console.Write(clearThinkingString); + Console.WriteLine($" Last message sent at {lastTimestamp}."); + + await pipe.Reader.CompleteAsync(); +}); + +await producerTask; +await consumerTask; + +record Chunk(string Message, DateTime Timestamp); \ No newline at end of file diff --git a/docs/core/whats-new/dotnet-10/snippets/csharp/ProcessGroup.cs b/docs/core/whats-new/dotnet-10/snippets/csharp/ProcessGroup.cs new file mode 100644 index 0000000000000..fce205adc4386 --- /dev/null +++ b/docs/core/whats-new/dotnet-10/snippets/csharp/ProcessGroup.cs @@ -0,0 +1,73 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using System.Threading; + +class Program +{ + static void Main(string[] args) + { + bool isChildProcess = args.Length > 0 && args[0] == "child"; + if (!isChildProcess) + { + var psi = new ProcessStartInfo + { + FileName = Environment.ProcessPath, + Arguments = "child", + CreateNewProcessGroup = true, + }; + + using Process process = Process.Start(psi)!; + Thread.Sleep(5_000); + + GenerateConsoleCtrlEvent(CTRL_C_EVENT, (uint)process.Id); + process.WaitForExit(); + + Console.WriteLine("Child process terminated gracefully, continue with the parent process logic if needed."); + } + else + { + // If you need to send a CTRL+C, the child process needs to re-enable CTRL+C handling, if you own the code, you can call SetConsoleCtrlHandler(NULL, FALSE). + // see https://learn.microsoft.com/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw#remarks + SetConsoleCtrlHandler((IntPtr)null, false); + + Console.WriteLine("Greetings from the child process! I need to be gracefully terminated, send me a signal!"); + + bool stop = false; + + var registration = PosixSignalRegistration.Create(PosixSignal.SIGINT, ctx => + { + stop = true; + ctx.Cancel = true; + Console.WriteLine("Received CTRL+C, stopping..."); + }); + + StreamWriter sw = File.AppendText("log.txt"); + int i = 0; + while (!stop) + { + Thread.Sleep(1000); + sw.WriteLine($"{++i}"); + Console.WriteLine($"Logging {i}..."); + } + + // Clean up + sw.Dispose(); + registration.Dispose(); + + Console.WriteLine("Thanks for not killing me!"); + } + } + + private const int CTRL_C_EVENT = 0; + private const int CTRL_BREAK_EVENT = 1; + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool SetConsoleCtrlHandler(IntPtr handler, [MarshalAs(UnmanagedType.Bool)] bool Add); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool GenerateConsoleCtrlEvent(uint dwCtrlEvent, uint dwProcessGroupId); +} \ No newline at end of file diff --git a/docs/core/whats-new/dotnet-10/snippets/csharp/WebSocketStreamBinary.cs b/docs/core/whats-new/dotnet-10/snippets/csharp/WebSocketStreamBinary.cs new file mode 100644 index 0000000000000..be91067addbc7 --- /dev/null +++ b/docs/core/whats-new/dotnet-10/snippets/csharp/WebSocketStreamBinary.cs @@ -0,0 +1,16 @@ +using System; +using System.IO; +using System.Net.WebSockets; +using System.Threading; +using System.Threading.Tasks; + +// Streaming binary protocol (for example, AMQP) +Stream transportStream = WebSocketStream.Create( + connectedWebSocket, + WebSocketMessageType.Binary, + closeTimeout: TimeSpan.FromSeconds(10)); +await message.SerializeToStreamAsync(transportStream, cancellationToken); +var receivePayload = new byte[payloadLength]; +await transportStream.ReadExactlyAsync(receivePayload, cancellationToken); +transportStream.Dispose(); +// `Dispose` automatically handles closing handshake \ No newline at end of file diff --git a/docs/core/whats-new/dotnet-10/snippets/csharp/WebSocketStreamRead.cs b/docs/core/whats-new/dotnet-10/snippets/csharp/WebSocketStreamRead.cs new file mode 100644 index 0000000000000..6f8a41def2452 --- /dev/null +++ b/docs/core/whats-new/dotnet-10/snippets/csharp/WebSocketStreamRead.cs @@ -0,0 +1,8 @@ +using System.IO; +using System.Net.WebSockets; +using System.Text.Json; + +// Reading a single message as a stream (for example, JSON deserialization) +using Stream messageStream = WebSocketStream.CreateReadableMessageStream(connectedWebSocket, WebSocketMessageType.Text); +// JsonSerializer.DeserializeAsync reads until the end of stream. +var appMessage = await JsonSerializer.DeserializeAsync(messageStream); \ No newline at end of file diff --git a/docs/core/whats-new/dotnet-10/snippets/csharp/WebSocketStreamText.cs b/docs/core/whats-new/dotnet-10/snippets/csharp/WebSocketStreamText.cs new file mode 100644 index 0000000000000..ccb396383c8d9 --- /dev/null +++ b/docs/core/whats-new/dotnet-10/snippets/csharp/WebSocketStreamText.cs @@ -0,0 +1,15 @@ +using System.IO; +using System.Net.WebSockets; +using System.Threading; +using System.Threading.Tasks; + +// Streaming text protocol (for example, STOMP) +using Stream transportStream = WebSocketStream.Create( + connectedWebSocket, + WebSocketMessageType.Text, + ownsWebSocket: true); +// Integration with Stream-based APIs +// Don't close the stream, as it's also used for writing +using var transportReader = new StreamReader(transportStream, leaveOpen: true); +var line = await transportReader.ReadLineAsync(cancellationToken); // Automatic UTF-8 and new line handling +transportStream.Dispose(); // Automatic closing handshake handling on `Dispose` \ No newline at end of file diff --git a/docs/core/whats-new/dotnet-10/snippets/csharp/WebSocketStreamWrite.cs b/docs/core/whats-new/dotnet-10/snippets/csharp/WebSocketStreamWrite.cs new file mode 100644 index 0000000000000..2cd3302664fad --- /dev/null +++ b/docs/core/whats-new/dotnet-10/snippets/csharp/WebSocketStreamWrite.cs @@ -0,0 +1,15 @@ +using System; +using System.IO; +using System.Net.WebSockets; +using System.Threading; +using System.Threading.Tasks; + +// Writing a single message as a stream (for example, binary serialization) +public async Task SendMessageAsync(AppMessage message, CancellationToken cancellationToken) +{ + using Stream messageStream = WebSocketStream.CreateWritableMessageStream(_connectedWebSocket, WebSocketMessageType.Binary); + foreach (ReadOnlyMemory chunk in message.SplitToChunks()) + { + await messageStream.WriteAsync(chunk, cancellationToken); + } +} // EOM sent on messageStream.Dispose() \ No newline at end of file From 52a2ab4326ba60281aa0589e4c9a9fd828534655 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Wed, 13 Aug 2025 10:01:06 -0400 Subject: [PATCH 04/12] Apply suggestions from code review Co-authored-by: Genevieve Warren <24882762+gewarren@users.noreply.github.com> --- docs/core/whats-new/dotnet-10/libraries.md | 6 +++--- docs/core/whats-new/dotnet-10/overview.md | 4 ++-- docs/core/whats-new/dotnet-10/runtime.md | 2 +- docs/core/whats-new/dotnet-10/sdk.md | 4 ++-- .../dotnet-10/snippets/csharp/PipeReaderChunks.cs | 6 +++--- .../dotnet-10/snippets/csharp/WebSocketStreamBinary.cs | 4 ++-- .../dotnet-10/snippets/csharp/WebSocketStreamRead.cs | 2 +- .../dotnet-10/snippets/csharp/WebSocketStreamText.cs | 10 +++++----- .../dotnet-10/snippets/csharp/WebSocketStreamWrite.cs | 4 ++-- 9 files changed, 21 insertions(+), 21 deletions(-) diff --git a/docs/core/whats-new/dotnet-10/libraries.md b/docs/core/whats-new/dotnet-10/libraries.md index cd5bfb7478b6c..c5786d9d3a35d 100644 --- a/docs/core/whats-new/dotnet-10/libraries.md +++ b/docs/core/whats-new/dotnet-10/libraries.md @@ -9,7 +9,7 @@ ai-usage: ai-assisted # What's new in .NET libraries for .NET 10 -This article describes new features in the .NET libraries for .NET 10. +This article describes new features in the .NET libraries for .NET 10. It's been updated for Preview 7. ## Cryptography @@ -142,7 +142,7 @@ private static byte[] SignPreHashSha3_256(MLDsa signingKey, ReadOnlySpan d #### Composite ML-DSA -This release also introduces new types to support ietf-lamps-pq-composite-sigs (currently at draft 7), and an implementation of the primitive methods for RSA variants. +This release also introduces new types to support [ietf-lamps-pq-composite-sigs](https://datatracker.ietf.org/doc/draft-ietf-lamps-pq-composite-sigs/) (currently at draft 7), and an implementation of the primitive methods for RSA variants. ```csharp var algorithm = CompositeMLDsaAlgorithm.MLDsa65WithRSA4096Pss; @@ -448,7 +448,7 @@ Traditional `WebSocket` APIs are low-level and require significant boilerplate: #### Common usage patterns -Here are a few examples of how `WebSocketStream` simplifies typical WebSocket workflows: +Here are a few examples of how `WebSocketStream` simplifies typical `WebSocket` workflows: ##### Streaming text protocol (for example, STOMP) diff --git a/docs/core/whats-new/dotnet-10/overview.md b/docs/core/whats-new/dotnet-10/overview.md index cf1be4dcc5bb7..02d2f1a8b4592 100644 --- a/docs/core/whats-new/dotnet-10/overview.md +++ b/docs/core/whats-new/dotnet-10/overview.md @@ -9,7 +9,7 @@ ai-usage: ai-assisted # What's new in .NET 10 -Learn about the new features in .NET 10 and find links to further documentation. +Learn about the new features in .NET 10 and find links to further documentation. This page has been updated for Preview 7. .NET 10, the successor to [.NET 9](../dotnet-9/overview.md), is [supported for three years](https://dotnet.microsoft.com/platform/support/policy/dotnet-core) as a long-term support (LTS) release. You can [download .NET 10 here](https://get.dot.net/10). @@ -23,7 +23,7 @@ For more information, see [What's new in the .NET 10 runtime](runtime.md). ## .NET libraries -The .NET 10 libraries introduce new APIs in cryptography, globalization, numerics, serialization, collections, and diagnostics, and when working with ZIP files. New JSON serialization options include disallowing duplicate properties, strict serialization settings, and PipeReader support for improved efficiency. Post-quantum cryptography support has been expanded with Windows Cryptography API: Next Generation (CNG) support, enhanced ML-DSA with simplified APIs and HashML-DSA support, plus Composite ML-DSA. Additional cryptography enhancements include AES KeyWrap with Padding support. New networking capabilities include WebSocketStream for simplified WebSocket usage and TLS 1.3 support for macOS clients. Process management gains Windows process group support for better signal isolation. +The .NET 10 libraries introduce new APIs in cryptography, globalization, numerics, serialization, collections, and diagnostics, and when working with ZIP files. New JSON serialization options include disallowing duplicate properties, strict serialization settings, and `PipeReader` support for improved efficiency. Post-quantum cryptography support has been expanded with Windows Cryptography API: Next Generation (CNG) support, enhanced ML-DSA with simplified APIs and HashML-DSA support, plus Composite ML-DSA. Additional cryptography enhancements include AES KeyWrap with Padding support. New networking capabilities include `WebSocketStream` for simplified `WebSocket` usage and TLS 1.3 support for macOS clients. Process management gains Windows process group support for better signal isolation. For more information, see [What's new in the .NET 10 libraries](libraries.md). For details on JSON serialization, see [System.Text.Json overview](/dotnet/standard/serialization/system-text-json/overview). diff --git a/docs/core/whats-new/dotnet-10/runtime.md b/docs/core/whats-new/dotnet-10/runtime.md index 2231693b867d2..a7a3beaba248b 100644 --- a/docs/core/whats-new/dotnet-10/runtime.md +++ b/docs/core/whats-new/dotnet-10/runtime.md @@ -8,7 +8,7 @@ ai-usage: ai-assisted --- # What's new in the .NET 10 runtime -This article describes new features and performance improvements in the .NET runtime for .NET 10. +This article describes new features and performance improvements in the .NET runtime for .NET 10. It's been updated for Preview 7. ## JIT compiler improvements diff --git a/docs/core/whats-new/dotnet-10/sdk.md b/docs/core/whats-new/dotnet-10/sdk.md index 727d0b94dd151..cca62dce66bf5 100644 --- a/docs/core/whats-new/dotnet-10/sdk.md +++ b/docs/core/whats-new/dotnet-10/sdk.md @@ -9,7 +9,7 @@ ai-usage: ai-assisted # What's new in the SDK and tooling for .NET 10 -This article describes new features and enhancements in the .NET SDK for .NET 10. +This article describes new features and enhancements in the .NET SDK for .NET 10. It's been updated for Preview 7. ## .NET tools enhancements @@ -65,7 +65,7 @@ The [platform-specific .NET tools](#platform-specific-net-tools) feature is grea .NET is great at this - the platform at its heart is meant to support this kind of platform-agnostic execution. To make platform-specific .NET tools work this way, you only need to add one thing to your project file: the `any` Runtime Identifier. -```diff +```xml linux-x64; diff --git a/docs/core/whats-new/dotnet-10/snippets/csharp/PipeReaderChunks.cs b/docs/core/whats-new/dotnet-10/snippets/csharp/PipeReaderChunks.cs index 75ca0ea8e95e5..8c043ddcb591b 100644 --- a/docs/core/whats-new/dotnet-10/snippets/csharp/PipeReaderChunks.cs +++ b/docs/core/whats-new/dotnet-10/snippets/csharp/PipeReaderChunks.cs @@ -6,7 +6,7 @@ var pipe = new Pipe(); -// Producer writes to the pipe in chunks +// Producer writes to the pipe in chunks. var producerTask = Task.Run(async () => { async static IAsyncEnumerable GenerateResponse() @@ -22,14 +22,14 @@ async static IAsyncEnumerable GenerateResponse() await pipe.Writer.CompleteAsync(); }); -// Consumer reads from the pipe and outputs to console +// Consumer reads from the pipe and outputs to console. var consumerTask = Task.Run(async () => { var thinkingString = "..."; var clearThinkingString = new string("\b\b\b"); var lastTimestamp = DateTime.MinValue; - // Read response to end + // Read response to end. Console.Write(thinkingString); await foreach (var chunk in JsonSerializer.DeserializeAsyncEnumerable(pipe.Reader)) { diff --git a/docs/core/whats-new/dotnet-10/snippets/csharp/WebSocketStreamBinary.cs b/docs/core/whats-new/dotnet-10/snippets/csharp/WebSocketStreamBinary.cs index be91067addbc7..286c1b253ac22 100644 --- a/docs/core/whats-new/dotnet-10/snippets/csharp/WebSocketStreamBinary.cs +++ b/docs/core/whats-new/dotnet-10/snippets/csharp/WebSocketStreamBinary.cs @@ -4,7 +4,7 @@ using System.Threading; using System.Threading.Tasks; -// Streaming binary protocol (for example, AMQP) +// Streaming binary protocol (for example, AMQP). Stream transportStream = WebSocketStream.Create( connectedWebSocket, WebSocketMessageType.Binary, @@ -13,4 +13,4 @@ var receivePayload = new byte[payloadLength]; await transportStream.ReadExactlyAsync(receivePayload, cancellationToken); transportStream.Dispose(); -// `Dispose` automatically handles closing handshake \ No newline at end of file +// `Dispose` automatically handles closing handshake. \ No newline at end of file diff --git a/docs/core/whats-new/dotnet-10/snippets/csharp/WebSocketStreamRead.cs b/docs/core/whats-new/dotnet-10/snippets/csharp/WebSocketStreamRead.cs index 6f8a41def2452..5ecce13b17819 100644 --- a/docs/core/whats-new/dotnet-10/snippets/csharp/WebSocketStreamRead.cs +++ b/docs/core/whats-new/dotnet-10/snippets/csharp/WebSocketStreamRead.cs @@ -2,7 +2,7 @@ using System.Net.WebSockets; using System.Text.Json; -// Reading a single message as a stream (for example, JSON deserialization) +// Reading a single message as a stream (for example, JSON deserialization). using Stream messageStream = WebSocketStream.CreateReadableMessageStream(connectedWebSocket, WebSocketMessageType.Text); // JsonSerializer.DeserializeAsync reads until the end of stream. var appMessage = await JsonSerializer.DeserializeAsync(messageStream); \ No newline at end of file diff --git a/docs/core/whats-new/dotnet-10/snippets/csharp/WebSocketStreamText.cs b/docs/core/whats-new/dotnet-10/snippets/csharp/WebSocketStreamText.cs index ccb396383c8d9..6295107f6af9b 100644 --- a/docs/core/whats-new/dotnet-10/snippets/csharp/WebSocketStreamText.cs +++ b/docs/core/whats-new/dotnet-10/snippets/csharp/WebSocketStreamText.cs @@ -3,13 +3,13 @@ using System.Threading; using System.Threading.Tasks; -// Streaming text protocol (for example, STOMP) +// Streaming text protocol (for example, STOMP). using Stream transportStream = WebSocketStream.Create( connectedWebSocket, WebSocketMessageType.Text, ownsWebSocket: true); -// Integration with Stream-based APIs -// Don't close the stream, as it's also used for writing +// Integration with Stream-based APIs. +// Don't close the stream, as it's also used for writing. using var transportReader = new StreamReader(transportStream, leaveOpen: true); -var line = await transportReader.ReadLineAsync(cancellationToken); // Automatic UTF-8 and new line handling -transportStream.Dispose(); // Automatic closing handshake handling on `Dispose` \ No newline at end of file +var line = await transportReader.ReadLineAsync(cancellationToken); // Automatic UTF-8 and new line handling. +transportStream.Dispose(); // Automatic closing handshake handling on `Dispose`. \ No newline at end of file diff --git a/docs/core/whats-new/dotnet-10/snippets/csharp/WebSocketStreamWrite.cs b/docs/core/whats-new/dotnet-10/snippets/csharp/WebSocketStreamWrite.cs index 2cd3302664fad..58ad0b9b090f1 100644 --- a/docs/core/whats-new/dotnet-10/snippets/csharp/WebSocketStreamWrite.cs +++ b/docs/core/whats-new/dotnet-10/snippets/csharp/WebSocketStreamWrite.cs @@ -4,7 +4,7 @@ using System.Threading; using System.Threading.Tasks; -// Writing a single message as a stream (for example, binary serialization) +// Writing a single message as a stream (for example, binary serialization). public async Task SendMessageAsync(AppMessage message, CancellationToken cancellationToken) { using Stream messageStream = WebSocketStream.CreateWritableMessageStream(_connectedWebSocket, WebSocketMessageType.Binary); @@ -12,4 +12,4 @@ public async Task SendMessageAsync(AppMessage message, CancellationToken cancell { await messageStream.WriteAsync(chunk, cancellationToken); } -} // EOM sent on messageStream.Dispose() \ No newline at end of file +} // EOM sent on messageStream.Dispose(). \ No newline at end of file From 2b7c6283645d27df24a06c4c2900d1c46f621487 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 13 Aug 2025 14:17:26 +0000 Subject: [PATCH 05/12] Fix XML code formatting and add snippets project file Co-authored-by: BillWagner <493969+BillWagner@users.noreply.github.com> --- docs/core/whats-new/dotnet-10/sdk.md | 13 +++++----- .../dotnet-10/snippets/csharp/snippets.csproj | 26 +++++++++++++++++++ 2 files changed, 32 insertions(+), 7 deletions(-) create mode 100644 docs/core/whats-new/dotnet-10/snippets/csharp/snippets.csproj diff --git a/docs/core/whats-new/dotnet-10/sdk.md b/docs/core/whats-new/dotnet-10/sdk.md index cca62dce66bf5..a7cf6337ab089 100644 --- a/docs/core/whats-new/dotnet-10/sdk.md +++ b/docs/core/whats-new/dotnet-10/sdk.md @@ -68,13 +68,12 @@ The [platform-specific .NET tools](#platform-specific-net-tools) feature is grea ```xml - linux-x64; - linux-arm64; - macos-arm64; - win-x64; -- win-arm64 -+ win-arm64; -+ any + linux-x64; + linux-arm64; + macos-arm64; + win-x64; + win-arm64; + any ``` diff --git a/docs/core/whats-new/dotnet-10/snippets/csharp/snippets.csproj b/docs/core/whats-new/dotnet-10/snippets/csharp/snippets.csproj new file mode 100644 index 0000000000000..6f0d5c788bb02 --- /dev/null +++ b/docs/core/whats-new/dotnet-10/snippets/csharp/snippets.csproj @@ -0,0 +1,26 @@ + + + + net8.0 + Library + enable + enable + preview + false + + + + + + + + + + + + + + + + + \ No newline at end of file From d6db43f44489a56cea43cc1baf6d81ea78899dbb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 13 Aug 2025 14:21:59 +0000 Subject: [PATCH 06/12] Fix remaining writing style issues in runtime documentation Co-authored-by: BillWagner <493969+BillWagner@users.noreply.github.com> --- docs/core/whats-new/dotnet-10/runtime.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/core/whats-new/dotnet-10/runtime.md b/docs/core/whats-new/dotnet-10/runtime.md index a7a3beaba248b..174f9937fd37e 100644 --- a/docs/core/whats-new/dotnet-10/runtime.md +++ b/docs/core/whats-new/dotnet-10/runtime.md @@ -48,7 +48,7 @@ private static void Main() } ``` -On x64, we pass the members of `Point` to `Consume` in separate registers, and since physical promotion kicked in for the local `p`, we don't allocate anything on the stack first: +On x64, the members of `Point` are passed to `Consume` in separate registers, and since physical promotion kicked in for the local `p`, nothing is allocated on the stack first: ```asm Program:Main() (FullOpts): @@ -57,7 +57,7 @@ Program:Main() (FullOpts): tail.jmp [Program:Consume(Program+Point)] ``` -Now, suppose we changed the type of the members of `Point` to `int` instead of `long`. Because an `int` is four bytes wide, and registers are eight bytes wide on x64, the calling convention requires us to pass the members of `Point` in one register. Previously, the JIT compiler would first store the values to memory, and then load the eight-byte chunk into a register. With the .NET 10 improvements, the JIT compiler can now place the promoted members of struct arguments into shared registers directly: +Now, suppose the type of the members of `Point` was changed to `int` instead of `long`. Because an `int` is four bytes wide, and registers are eight bytes wide on x64, the calling convention requires the members of `Point` to be passed in one register. Previously, the JIT compiler would first store the values to memory, and then load the eight-byte chunk into a register. With the .NET 10 improvements, the JIT compiler can now place the promoted members of struct arguments into shared registers directly: ```asm Program:Main() (FullOpts): From b74118d49ce60e803ed339914b10d64bfa93503a Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Wed, 13 Aug 2025 10:37:49 -0400 Subject: [PATCH 07/12] Update libraries.md --- docs/core/whats-new/dotnet-10/libraries.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/core/whats-new/dotnet-10/libraries.md b/docs/core/whats-new/dotnet-10/libraries.md index c5786d9d3a35d..bf990258b109c 100644 --- a/docs/core/whats-new/dotnet-10/libraries.md +++ b/docs/core/whats-new/dotnet-10/libraries.md @@ -509,4 +509,3 @@ DOTNET_SYSTEM_NET_SECURITY_USENETWORKFRAMEWORK=true - Zero-byte reads: semantics might differ. Avoid relying on zero-length reads for detecting data availability. - Internationalized domain names (IDN): certain IDN hostnames might be rejected by Network.framework. Prefer ASCII/Punycode (A-label) hostnames or validate names against macOS/Network.framework constraints. - If your app relies on specific edge-case behavior, validate it under Network.framework. - From f6a248f75cd7f13496c7e1b3870cb551d47559fd Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 14 Aug 2025 08:25:12 -0400 Subject: [PATCH 08/12] Apply suggestions from code review Co-authored-by: Genevieve Warren <24882762+gewarren@users.noreply.github.com> --- docs/core/whats-new/dotnet-10/libraries.md | 16 ++++++++-------- docs/core/whats-new/dotnet-10/sdk.md | 6 ++---- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/docs/core/whats-new/dotnet-10/libraries.md b/docs/core/whats-new/dotnet-10/libraries.md index bf990258b109c..6798b423b699f 100644 --- a/docs/core/whats-new/dotnet-10/libraries.md +++ b/docs/core/whats-new/dotnet-10/libraries.md @@ -304,7 +304,7 @@ Here is an example of a producer that produces tokens in chunks and a consumer t :::code language="csharp" source="snippets/csharp/PipeReaderChunks.cs"::: -Note that all of this is serialized as JSON in the (formatted here for readability): +All of this is serialized as JSON in the (formatted here for readability): ```json [ @@ -432,7 +432,7 @@ A community contribution improved the performance of to launch a process in a separate process group. This allows you to send isolated signals to child processes which could otherwise take down the parent without proper handling. Sending signals is convenient to avoid forceful termination. +For Windows, you can now use to launch a process in a separate process group. This allows you to send isolated signals to child processes that could otherwise take down the parent without proper handling. Sending signals is convenient to avoid forceful termination. :::code language="csharp" source="snippets/csharp/ProcessGroup.cs"::: @@ -440,7 +440,7 @@ For Windows, you can now use scenarios in .NET. +.NET 10 introduces `WebSocketStream` , a new API designed to simplify some of the most common—and previously cumbersome— scenarios in .NET. Traditional `WebSocket` APIs are low-level and require significant boilerplate: handling buffering and framing, reconstructing messages, managing encoding/decoding, and writing custom wrappers to integrate with streams, channels, or other transport abstractions. These complexities make it difficult to use WebSockets as a transport, especially for apps with streaming or text-based protocols, or event-driven handlers. @@ -458,11 +458,11 @@ Here are a few examples of how `WebSocketStream` simplifies typical `WebSocket` :::code language="csharp" source="snippets/csharp/WebSocketStreamBinary.cs"::: -##### Reading a single message as a stream (for example, JSON deserialization) +##### Read a single message as a stream (for example, JSON deserialization) :::code language="csharp" source="snippets/csharp/WebSocketStreamRead.cs"::: -##### Writing a single message as a stream (for example, binary serialization) +##### Write a single message as a stream (for example, binary serialization) :::code language="csharp" source="snippets/csharp/WebSocketStreamWrite.cs"::: @@ -503,9 +503,9 @@ DOTNET_SYSTEM_NET_SECURITY_USENETWORKFRAMEWORK=true #### Notes -- Applies to and APIs built on it (for example, /). +- TLS 31.3 applies to and APIs built on it (for example, /). - Cipher suites are controlled by macOS via Network.framework. - Underlying stream behavior might differ when Network.framework is enabled (for example, buffering, read/write completion, cancellation semantics). -- Zero-byte reads: semantics might differ. Avoid relying on zero-length reads for detecting data availability. -- Internationalized domain names (IDN): certain IDN hostnames might be rejected by Network.framework. Prefer ASCII/Punycode (A-label) hostnames or validate names against macOS/Network.framework constraints. +- Semantics might differ for zero-byte reads. Avoid relying on zero-length reads for detecting data availability. +- Certain internationalized domain names (IDN) hostnames might be rejected by Network.framework. Prefer ASCII/Punycode (A-label) hostnames or validate names against macOS/Network.framework constraints. - If your app relies on specific edge-case behavior, validate it under Network.framework. diff --git a/docs/core/whats-new/dotnet-10/sdk.md b/docs/core/whats-new/dotnet-10/sdk.md index a7cf6337ab089..6eb8d37e46818 100644 --- a/docs/core/whats-new/dotnet-10/sdk.md +++ b/docs/core/whats-new/dotnet-10/sdk.md @@ -63,7 +63,7 @@ For more information about managing .NET tools, see [Manage .NET tools](../../to The [platform-specific .NET tools](#platform-specific-net-tools) feature is great for making sure tools are optimized for specific platforms that you target ahead-of-time. However, there are times where you won't know all of the platforms that you'd like to target, or sometimes .NET itself will learn how to support a new platform, and you'd like your tool to be runnable there too. -.NET is great at this - the platform at its heart is meant to support this kind of platform-agnostic execution. To make platform-specific .NET tools work this way, you only need to add one thing to your project file: the `any` Runtime Identifier. +To make your tool work this way, add the `any` runtime identifier to your project file: ```xml @@ -78,9 +78,7 @@ The [platform-specific .NET tools](#platform-specific-net-tools) feature is grea ``` -This RuntimeIdentifier is at the 'root' of the platform-compatibility checking, and since it declares support for _any_ platform, the tool that gets packaged will be the most compatible kind of tool - a framework-dependent, platform-agnostic .NET dll, which requires a compatible .NET Runtime to execute. When you perform a `dotnet pack` to create your tool, you'll see a new package for the `any` RuntimeIdentifier appear alongside the other platform-specific packages and the top-level manifest package. - -This is the exact same kind of tool that you would make in .NET 9 and earlier, but now it fits into the overall goal of enabling platform-specific .NET tools! +This RuntimeIdentifier is at the 'root' of the platform-compatibility checking, and since it declares support for _any_ platform, the tool that gets packaged will be the most compatible kind of tool - a framework-dependent, platform-agnostic .NET DLL, which requires a compatible .NET Runtime to execute. When you perform a `dotnet pack` to create your tool, you'll see a new package for the `any` RuntimeIdentifier appear alongside the other platform-specific packages and the top-level manifest package. ### CLI introspection with `--cli-schema` From 32ee79cbc1cf07c9453dc07ccc5e420fd339f7e3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 14 Aug 2025 12:36:25 +0000 Subject: [PATCH 09/12] Address reviewer feedback: fix ML-DSA description, clarify Composite ML-DSA types, move WebSocketStream description, fix TLS typo, and remove extra blank lines Co-authored-by: BillWagner <493969+BillWagner@users.noreply.github.com> --- docs/core/whats-new/dotnet-10/libraries.md | 10 +++++----- docs/core/whats-new/dotnet-10/sdk.md | 2 -- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/docs/core/whats-new/dotnet-10/libraries.md b/docs/core/whats-new/dotnet-10/libraries.md index 6798b423b699f..27956e652df47 100644 --- a/docs/core/whats-new/dotnet-10/libraries.md +++ b/docs/core/whats-new/dotnet-10/libraries.md @@ -115,7 +115,7 @@ The PQC algorithms are available on systems where the system cryptographic libra #### ML-DSA enhancements -The class gained ease-of-use updates, allowing some common code patterns to be simplified: +The class includes ease-of-use features that simplify common code patterns: ```diff private static byte[] SignData(string privateKeyPath, ReadOnlySpan data) @@ -142,7 +142,7 @@ private static byte[] SignPreHashSha3_256(MLDsa signingKey, ReadOnlySpan d #### Composite ML-DSA -This release also introduces new types to support [ietf-lamps-pq-composite-sigs](https://datatracker.ietf.org/doc/draft-ietf-lamps-pq-composite-sigs/) (currently at draft 7), and an implementation of the primitive methods for RSA variants. +.NET 10 introduces new types to support [ietf-lamps-pq-composite-sigs](https://datatracker.ietf.org/doc/draft-ietf-lamps-pq-composite-sigs/) (currently at draft 7), including `CompositeMLDsa` and `CompositeMLDsaAlgorithm` types with implementation of the primitive methods for RSA variants. ```csharp var algorithm = CompositeMLDsaAlgorithm.MLDsa65WithRSA4096Pss; @@ -446,6 +446,8 @@ Traditional `WebSocket` APIs are low-level and require significant boilerplate: `WebSocketStream` addresses these pain points by providing a -based abstraction over a WebSocket. This enables seamless integration with existing APIs for reading, writing, and parsing data, whether binary or text, and reduces the need for manual plumbing. +`WebSocketStream` enables high-level, familiar APIs for common WebSocket consumption and production patterns. These APIs reduce friction and make advanced scenarios easier to implement. + #### Common usage patterns Here are a few examples of how `WebSocketStream` simplifies typical `WebSocket` workflows: @@ -466,8 +468,6 @@ Here are a few examples of how `WebSocketStream` simplifies typical `WebSocket` :::code language="csharp" source="snippets/csharp/WebSocketStreamWrite.cs"::: -`WebSocketStream` enables high-level, familiar APIs for common WebSocket consumption and production patterns—reducing friction and making advanced scenarios easier to implement. - ## TLS enhancements ### TLS 1.3 for macOS (client) @@ -503,7 +503,7 @@ DOTNET_SYSTEM_NET_SECURITY_USENETWORKFRAMEWORK=true #### Notes -- TLS 31.3 applies to and APIs built on it (for example, /). +- TLS 1.3 applies to and APIs built on it (for example, /). - Cipher suites are controlled by macOS via Network.framework. - Underlying stream behavior might differ when Network.framework is enabled (for example, buffering, read/write completion, cancellation semantics). - Semantics might differ for zero-byte reads. Avoid relying on zero-length reads for detecting data availability. diff --git a/docs/core/whats-new/dotnet-10/sdk.md b/docs/core/whats-new/dotnet-10/sdk.md index 6eb8d37e46818..c0d8f4d6b38a7 100644 --- a/docs/core/whats-new/dotnet-10/sdk.md +++ b/docs/core/whats-new/dotnet-10/sdk.md @@ -79,9 +79,7 @@ To make your tool work this way, add the `any` runtime identifier to your projec ``` This RuntimeIdentifier is at the 'root' of the platform-compatibility checking, and since it declares support for _any_ platform, the tool that gets packaged will be the most compatible kind of tool - a framework-dependent, platform-agnostic .NET DLL, which requires a compatible .NET Runtime to execute. When you perform a `dotnet pack` to create your tool, you'll see a new package for the `any` RuntimeIdentifier appear alongside the other platform-specific packages and the top-level manifest package. - ### CLI introspection with `--cli-schema` - A new `--cli-schema` option is available on all CLI commands. When used, it outputs a JSON representation of the CLI command tree for the invoked command or subcommand. This is useful for tool authors, shell integration, and advanced scripting. ```bash From 5899b682d74354d77b8a01b86fd7db3f0b5f227e Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 14 Aug 2025 08:44:25 -0400 Subject: [PATCH 10/12] Update docs/core/whats-new/dotnet-10/sdk.md --- docs/core/whats-new/dotnet-10/sdk.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/core/whats-new/dotnet-10/sdk.md b/docs/core/whats-new/dotnet-10/sdk.md index c0d8f4d6b38a7..6eb8d37e46818 100644 --- a/docs/core/whats-new/dotnet-10/sdk.md +++ b/docs/core/whats-new/dotnet-10/sdk.md @@ -79,7 +79,9 @@ To make your tool work this way, add the `any` runtime identifier to your projec ``` This RuntimeIdentifier is at the 'root' of the platform-compatibility checking, and since it declares support for _any_ platform, the tool that gets packaged will be the most compatible kind of tool - a framework-dependent, platform-agnostic .NET DLL, which requires a compatible .NET Runtime to execute. When you perform a `dotnet pack` to create your tool, you'll see a new package for the `any` RuntimeIdentifier appear alongside the other platform-specific packages and the top-level manifest package. + ### CLI introspection with `--cli-schema` + A new `--cli-schema` option is available on all CLI commands. When used, it outputs a JSON representation of the CLI command tree for the invoked command or subcommand. This is useful for tool authors, shell integration, and advanced scripting. ```bash From eaf1c3d1477a13f03be99dfeeb7c38320704dc0c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 14 Aug 2025 12:49:25 +0000 Subject: [PATCH 11/12] Address final reviewer feedback: change ML-DSA heading and add blank line before CLI introspection Co-authored-by: BillWagner <493969+BillWagner@users.noreply.github.com> --- docs/core/whats-new/dotnet-10/libraries.md | 2 +- docs/core/whats-new/dotnet-10/sdk.md | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/core/whats-new/dotnet-10/libraries.md b/docs/core/whats-new/dotnet-10/libraries.md index 27956e652df47..0c9ac5d159867 100644 --- a/docs/core/whats-new/dotnet-10/libraries.md +++ b/docs/core/whats-new/dotnet-10/libraries.md @@ -113,7 +113,7 @@ private static bool ValidateMLDsaSignature(ReadOnlySpan data, ReadOnlySpan The PQC algorithms are available on systems where the system cryptographic libraries are OpenSSL 3.5 (or newer) or Windows CNG with PQC support. Also, the new classes are all marked as [`[Experimental]`](../../../fundamentals/syslib-diagnostics/experimental-overview.md) under diagnostic `SYSLIB5006` until development is complete. -#### ML-DSA enhancements +#### ML-DSA The class includes ease-of-use features that simplify common code patterns: diff --git a/docs/core/whats-new/dotnet-10/sdk.md b/docs/core/whats-new/dotnet-10/sdk.md index 6eb8d37e46818..a4ebf7f5284a8 100644 --- a/docs/core/whats-new/dotnet-10/sdk.md +++ b/docs/core/whats-new/dotnet-10/sdk.md @@ -80,6 +80,7 @@ To make your tool work this way, add the `any` runtime identifier to your projec This RuntimeIdentifier is at the 'root' of the platform-compatibility checking, and since it declares support for _any_ platform, the tool that gets packaged will be the most compatible kind of tool - a framework-dependent, platform-agnostic .NET DLL, which requires a compatible .NET Runtime to execute. When you perform a `dotnet pack` to create your tool, you'll see a new package for the `any` RuntimeIdentifier appear alongside the other platform-specific packages and the top-level manifest package. + ### CLI introspection with `--cli-schema` A new `--cli-schema` option is available on all CLI commands. When used, it outputs a JSON representation of the CLI command tree for the invoked command or subcommand. This is useful for tool authors, shell integration, and advanced scripting. From b69a4fdf4fcb4cc67ff1734d98b42ba9277bd9c2 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 14 Aug 2025 08:57:48 -0400 Subject: [PATCH 12/12] Update docs/core/whats-new/dotnet-10/sdk.md --- docs/core/whats-new/dotnet-10/sdk.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/core/whats-new/dotnet-10/sdk.md b/docs/core/whats-new/dotnet-10/sdk.md index a4ebf7f5284a8..6eb8d37e46818 100644 --- a/docs/core/whats-new/dotnet-10/sdk.md +++ b/docs/core/whats-new/dotnet-10/sdk.md @@ -80,7 +80,6 @@ To make your tool work this way, add the `any` runtime identifier to your projec This RuntimeIdentifier is at the 'root' of the platform-compatibility checking, and since it declares support for _any_ platform, the tool that gets packaged will be the most compatible kind of tool - a framework-dependent, platform-agnostic .NET DLL, which requires a compatible .NET Runtime to execute. When you perform a `dotnet pack` to create your tool, you'll see a new package for the `any` RuntimeIdentifier appear alongside the other platform-specific packages and the top-level manifest package. - ### CLI introspection with `--cli-schema` A new `--cli-schema` option is available on all CLI commands. When used, it outputs a JSON representation of the CLI command tree for the invoked command or subcommand. This is useful for tool authors, shell integration, and advanced scripting.