M.kazem Akhgary
M.kazem Akhgary

Reputation: 19179

How to Console.ReadLine from one thread and consume ConsoleKeys from another thread?

I'm making a test console application. This application runs a void task (I can't change this fact), and for it to remain opened I insert Console.ReadLine at end of Main method.

Is there any way to consume each key being pressed from other threads? I tried the following but call to Peek is blocking the thread.

loop = Task.Run(async () =>
{
    var input = Console.In;
    while (running)
    {
        int key = input.Peek(); // blocks here forever
        if (key == -1)
        {
            await Task.Delay(50);
        }
        else
        {
            input.Read();
            if ((ConsoleKey)key == ConsoleKey.Enter)
            {
                Completed?.Invoke();
            }
            else
            {
                OnKeyDown((ConsoleKey)key);
            }
            // todo how to intercept keyup?
        }
    }
});

This is the main method

static void Main(string[] args)
{
    GrpcEnvironment.SetLogger(new Grpc.Core.Logging.ConsoleLogger());

    //setup MagicOnion and option.
    var service = MagicOnionEngine.BuildServerServiceDefinition(isReturnExceptionStackTraceInErrorDetail: true);

    var server = new global::Grpc.Core.Server
    {
        Services = { service },
        Ports = { new ServerPort("localhost", 12345, ServerCredentials.Insecure) }
    };

    // launch gRPC Server.
    server.Start();

    // and wait.
    Console.ReadLine();
}

what I want is basically to have a keyboard key pressed event listener on another thread.


I also tried global keyboard hooks but that does not work for console application.

Upvotes: 2

Views: 2440

Answers (2)

Mirko
Mirko

Reputation: 4282

You consider just trying something like this?

Make sure to try running this from an actual console as my mileage with VS 2017 varied on CTRL-C working in the IDE. (I should have mentioned this uses C# 7.2 - for async main)

    class Program
    {
        static async Task Main()
        {
            CancellationTokenSource cts = new CancellationTokenSource();

            Console.CancelKeyPress += (sender, args) => cts.Cancel();

            Console.WriteLine("Press CTRL-C to Exit");

            // Start you server here

            while (!cts.IsCancellationRequested)
            {

                if (Console.KeyAvailable)
                {
                    var key = Console.ReadKey(true);

                    Console.WriteLine($"Read: {key.KeyChar}");
                }

                await Task.Delay(50, cts.Token);
            }
        }
    }

Upvotes: 1

M.kazem Akhgary
M.kazem Akhgary

Reputation: 19179

I decided to put this instead of Console.ReadLine at end of Main method.

while (true) Task.Delay(1000).Wait(); // console.ReadLine doesn't let us to read from console in other threads.

And then I can do

loop = Task.Run(() =>
{
    while (running)
    {
        var key = Console.ReadKey(true).Key;
        if (key == ConsoleKey.Enter)
        {
            Completed?.Invoke();
        }
        else
        {
            OnKeyDown(key);
        }
        // todo how to intercept keyup?
    }
});

by pressing enter, our application wont close but this is a test application and exit with enter is not our requirement.

but If someone still knows an anwer with Console.ReadLine I appreciate to know it.

Upvotes: 1

Related Questions