Console.ReadLine()を中断する

2023/02/18に公開

Console.ReadLineをタイムアウトや、外部の要因なんかで中断したい場合、.NETの標準APIだけでは実装できません。

てことで、つぎのようにすると実現できます。

using System.Runtime.InteropServices;

if (TryReadLine(TimeSpan.FromSeconds(3), out var line))
{
    Console.WriteLine($"ReadLine was completed. : {line}");
}
else
{
    Console.WriteLine($"ReadLine was interrupted.");
}


static bool TryReadLine(TimeSpan timeSpan, out string line)
{
    var isRead = false;
    Task.Delay(timeSpan).ContinueWith(_ =>
    {
        // 読み込みが未完了の場合だけ中断する。
        if (!isRead)
        {
            const int standardInputHandle = -10;
            var handle = GetStdHandle(standardInputHandle);
            CancelIoEx(handle, IntPtr.Zero);
        }
    });

    try
    {
        line = Console.ReadLine()!;
        isRead = true;
        return true;
    }
    catch (InvalidOperationException)
    {
    }
    catch (OperationCanceledException)
    {
    }

    line = default!;
    return false;
}

[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr GetStdHandle(int nStdHandle);

[DllImport("kernel32.dll", SetLastError = true)]
static extern bool CancelIoEx(IntPtr handle, IntPtr lpOverlapped);

上記はタイマーでキャンセルしていますが、たとえばCancellationTokenを渡すなんかも考えられますね。

以上です。

参考資料:Cancelling Console.Read

Discussion