Skip to content

SendKeys.ParseKeys rejects "^a^c" on .NET 10 although it works on .NET Framework #14690

Description

@cuiliang

.NET version

.net 10

Did it work in .NET Framework?

Yes

Did it work in any of the earlier releases of .NET Core or .NET 5+?

No response

Issue description

Description:
System.Windows.Forms.SendKeys.SendWait("^a^c") works on .NET Framework 4.7.2/4.8, but throws ArgumentException on .NET 10.

Expected:
"^a^c" should be parsed as Ctrl+A followed by Ctrl+C, consistent with .NET Framework behavior.

Actual:
.NET 10 throws:
System.ArgumentException: SendKeys string "^a^c" is not valid.

Impact:
This breaks existing WinForms/WPF desktop applications that migrated from .NET Framework and stored SendKeys scripts using this syntax.

Minimal repro:
Use reflection to call ParseKeys, or call SendKeys.SendWait("^a^c") in a WinForms/WPF app.

Observed:
net472: OK
net10.0-windows: ArgumentException

Steps to reproduce

using System;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows.Forms;

internal static class Program
{
    private static readonly string[] Cases =
    {
        "^a",
        "^c",
        "^a^c",
        "^(a)^(c)",
        "^(AC)",
        "+a+c",
        "%f%t",
        "^+{END}^{HOME}",
        "^{HOME}^+{END}{DEL}"
    };

    private static int Main()
    {
        Console.WriteLine("Framework: " + RuntimeInformation.FrameworkDescription);
        Console.WriteLine("OS: " + RuntimeInformation.OSDescription);
        Console.WriteLine("SendKeys assembly: " + typeof(SendKeys).Assembly.FullName);
        Console.WriteLine("SendKeys location: " + typeof(SendKeys).Assembly.Location);

        var parseKeys = typeof(SendKeys).GetMethod(
            "ParseKeys",
            BindingFlags.NonPublic | BindingFlags.Static);

        if (parseKeys == null)
        {
            Console.WriteLine("ParseKeys method not found.");
            return 2;
        }

        Console.WriteLine("ParseKeys signature: " + parseKeys);
        Console.WriteLine();

        var hwndArg = CreateDefaultArgument(parseKeys.GetParameters().Last().ParameterType);
        foreach (var text in Cases)
        {
            var error = TryParse(parseKeys, hwndArg, text);
            Console.WriteLine(error == null
                ? $"OK   {text}"
                : $"ERR  {text}  {error.GetType().FullName}: {error.Message}");
        }

        return 0;
    }

    private static object? CreateDefaultArgument(Type parameterType)
    {
        return parameterType.IsValueType ? Activator.CreateInstance(parameterType) : null;
    }

    private static Exception? TryParse(MethodInfo parseKeys, object? hwndArg, string text)
    {
        try
        {
            parseKeys.Invoke(null, new[] { text, hwndArg });
            return null;
        }
        catch (TargetInvocationException ex)
        {
            return ex.InnerException ?? ex;
        }
    }
}
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFrameworks>net472;net10.0-windows10.0.19041.0</TargetFrameworks>
    <UseWindowsForms>true</UseWindowsForms>
    <LangVersion>latest</LangVersion>
    <Nullable>enable</Nullable>
  </PropertyGroup>
</Project>

Results:
.net 10

Framework: .NET 10.0.9
OS: Microsoft Windows 10.0.26200
SendKeys assembly: System.Windows.Forms, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
SendKeys location: C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App\10.0.9\System.Windows.Forms.dll
ParseKeys signature: Void ParseKeys(System.String, Windows.Win32.Foundation.HWND)

OK   ^a
OK   ^c
ERR  ^a^c  System.ArgumentException: SendKeys 字符串“^a^c”无效。
OK   ^(a)^(c)
OK   ^(AC)
ERR  +a+c  System.ArgumentException: SendKeys 字符串“+a+c”无效。
ERR  %f%t  System.ArgumentException: SendKeys 字符串“%f%t”无效。
OK   ^+{END}^{HOME}
OK   ^{HOME}^+{END}{DEL}

D:\Work\Quicker\Tools\temp\SendKeysParseProbe\bin\Debug\net10.0-windows10.0.19041.0\SendKeysParseProbe.exe (process 76692) exited with code 0 (0x0).
To automatically close the console when debugging stops, enable Tools->Options->Debugging->Automatically close the console when debugging stops.
Press any key to close this window . . .

net472:

Framework: .NET Framework 4.8.9325.0
OS: Microsoft Windows 10.0.26200
SendKeys assembly: System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
SendKeys location: C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Windows.Forms\v4.0_4.0.0.0__b77a5c561934e089\System.Windows.Forms.dll
ParseKeys signature: Void ParseKeys(System.String, IntPtr)

OK   ^a
OK   ^c
OK   ^a^c
OK   ^(a)^(c)
OK   ^(AC)
OK   +a+c
OK   %f%t
OK   ^+{END}^{HOME}
OK   ^{HOME}^+{END}{DEL}

D:\Work\Quicker\Tools\temp\SendKeysParseProbe\bin\Debug\net472\SendKeysParseProbe.exe (process 42812) exited with code 0 (0x0).
To automatically close the console when debugging stops, enable Tools->Options->Debugging->Automatically close the console when debugging stops.
Press any key to close this window . . .

Metadata

Metadata

Assignees

Labels

untriagedThe team needs to look at this issue in the next triage

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions