I have created a console application which have a menu that allow me to navigate between the menu items. I handle the navigation logic in this method:
```c#
public virtual void UpdateMenu()
{
switch (Console.ReadKey(true).Key)
{
case ConsoleKey.UpArrow:
{
if (cursor > 0)
{
cursor--;
Console.Clear();
drawWithHeader();
}
}
break;
case ConsoleKey.DownArrow:
{
if (cursor < (menuItemList.Count - 1))
{
cursor++;
Console.Clear();
drawWithHeader();
}
}
break;
case ConsoleKey.Escape:
{
if (ParentMenu != null)
{
hideMenu();
}
}
break;
case ConsoleKey.Enter:
{
Console.Clear();
drawHeader();
Console.CursorVisible = true;
menuItemList[cursor].Action();
Console.CursorVisible = false;
Console.Clear();
drawWithHeader();
}
break;
default:
{
// Unsuported key. Do nothing....
}
break;
}
}
[here](https://pastebin.com/SpkmCBnq) the full class.
Now on windows all works well, but when I run this application on my linux server with supervisor I get:
> Unhandled Exception: System.InvalidOperationException: Cannot read key when either application does not have a console or when console input has been redirected. Try Console.Read.
The stacktrace display:
at System.ConsolePal.ReadKey(Boolean intercept)
at System.Console.ReadKey();
at AppRazen.Menu.ConsoleMenu.UpdateMenu();
After some searching I discovered that this problem is related to the ReadKey() method which is not fully [compatible with linux](https://stackoverflow.com/questions/46655517/why-is-net-core-handling-readkey-differently-on-raspbian). And the solution proposed [here](https://stackoverflow.com/questions/46901071/readkey-not-working-in-net-core) simply doesn't work in my case, because the user has used OminSharp.
I also tried to set `ReadKey(false)` but this didn't fixed the problem, and I also tried to handle all the stuff inside `UpdateMenu` with `Console.Read()` but the console seems stuck.
Note that this issue happen **only** when I run my script in linux supervisor not with the default command like `dotnet AppRazen.dll`
Essentially I'm running the script with a daemon service like this:
[Unit]
Description = Daemon description
[Service]
ExecStart = /usr/bin/dotnet /home/root/Desktop/publish/AppRazen.dll
WorkingDirectory= /home/root/Desktop/publish
Restart = always
RestartSec = 3
[Install]
WantedBy = multi-user.target
```
I honestly I don't know how can I fix that. Someone have any ideas?
Thanks in advance.
Is it possible that the "linux supervisor" (whatever that is) redirects console, leading to the problem?
cc @wfurt @omajid @tmds
most certainly. In order for some console functions to work you need to TTY -> controlling terminal.
That is normally created by login or sshd and it will associate it with your session. That will not work if your stdin is not terminal.
ssh for example has -t to force pseudo-terminal allocation for cases when it is needed. Your supervisor app may need to do something similar. I'm not sure at this point if that would make it working but at least you would get over the exception.
I just answered this on stackoverflow: https://stackoverflow.com/a/53309571/3561275.
Aside from what @wfurt said, I don't even understand what the goal is here. An application can't be both interactive for users and a system deamon. These are two opposite design goals.
Thanks all you guys for the answers. I saw your answer @omajid and I'll try to explain you what the application does:
Essentially the app is a web crawler so it must run 24h on the server, initially the application worked in this way then I decided to create a menu which I can use for interactive with the app and this works as well.
The problem right now is that I need to run the crawler as a system service, so using the supervisor and I understood from you that's not possible.
So apparently right now I see only two choice:
dotnet AppRazen.dll command every time the system is not executing it.Maybe there are other ways to handle that. I added you a +1 on so.
Tell me what you think. Thanks.
I think we agree that this is not a bug in .NET Core.
Check if the input is redirected using this property and start directly the crawler method.
This should work. Personally, I would add a separate flag like --daemon or --non-interactive that gets passed by the .service file to make this explicit.
Create something like a cronjob that start the console with dotnet AppRazen.dll command every time the system is not executing it.
Is this command supposed to configure the crawler? Or just to start it if the crawler is not running?
If it's the first, then it might be worth looking into to having this console application write to a well-known path, and the crawler daemon just checking this file periodically, or on-demand using SIGHUP.
If you just want to make sure the crawler is always running, you can do that via the .service file. Maybe something like Restart=always.
@omajid
Personally, I would add a separate flag like --daemon or --non-interactive that gets passed by the .service file to make this explicit.
The flag passed can be read in the main method? I never used a similar thing, could you please show a little example, I'll be grateful.
Is this command supposed to configure the crawler? Or just to start it if the crawler is not running?
The service that I have already have Restart=always, I just need to check if the crawler is running, this is the daemon configuration:
[Unit]
Description = Daemon description
[Service]
ExecStart = /usr/bin/dotnet /home/root/Desktop/publish/AppRazen.dll
WorkingDirectory= /home/root/Desktop/publish
Restart = always
RestartSec = 3
[Install]
WantedBy = multi-user.target
I can actually use the logic of the flags, but is possible display in the journalctl only the log of a specific instance? Let me explain better: when I run AppRazen.dll the software will start 10 different tasks, each of them generate a different log in the console. Basically when I execute journalctl -fu service-name I'll get the navigation menu with the ReadKey error, using your logic I could pass a flag (even if is possible) to journalctl -fu swp and display a single instance of the task?
Because I can access to the log of a specific instance through the navigation menu, if I cannot use it I should use the flag.
Should I also manage the already running instance of the service though (?). I don't know, maybe a further explaination could help me to understand better.
Thanks in advance.
Not a bug, closing.