2

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.

M.kazem Akhgary
  • 18,645
  • 8
  • 57
  • 118
  • Is your intention, just to keep the application open, receiving input, while some other task is running – TheGeneral Jan 28 '19 at 02:39
  • Well the application thread must remain open and should have the longest lifecycle than other threads. somewhere in another task I want to do `ReadKey` but the `ReadLine` in Main method blocks the `ReadKey` in other threads. I was thinking if its possible to Read keys while also having `ReadLine`. – M.kazem Akhgary Jan 28 '19 at 02:46
  • @TheGeneral I've updated the question. As you can see we start the server. for it to remain open we insert `Console.ReadLine`. but somewhere in another thread I want to do `ReadKey`. I hope that's clear. If you know other tricks I appreciate to know. Thanks a lot – M.kazem Akhgary Jan 28 '19 at 02:52
  • As soon as you pressed the button on any other thread the `Console.ReadLine();` on main thread will activate and finish the application, no? – Hasan Emrah Süngü Jan 28 '19 at 02:53
  • @HasanEmrahSüngü If I press Enter, yes. the application will terminate. But if i press any other key, I can see them in the console but it has no effects on `input.Peek();` or `input.Read();` – M.kazem Akhgary Jan 28 '19 at 02:55
  • Yeah without getting too fancey you could read the input from a loop in the main thread and use a signaling mechanism like a resetEvents, this assuming you really need a new task to do the other work (as you said you did) however you need to be certain this other task needs to exist and read input, seems a little constraining – TheGeneral Jan 28 '19 at 02:55
  • 2
    @TheGeneral I think this was a XY problem. my apologize but honestly i only came up with this after i tried to explain it here! check out my answer. – M.kazem Akhgary Jan 28 '19 at 03:10

2 Answers2

1

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.

M.kazem Akhgary
  • 18,645
  • 8
  • 57
  • 118
1

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);
            }
        }
    }
Mirko
  • 4,284
  • 1
  • 22
  • 19