I know there is a question for Windows Forms but it doesn't work in the console, or at least I couldn't get it to work. I need to capture key presses even though the console doesn't have focus.
You can create a global keyboard hook in a console application, too.
Here's complete, working code:
https://learn.microsoft.com/en-us/archive/blogs/toub/low-level-keyboard-hook-in-c
You create a console application, but must add a reference to System.Windows.Forms for this to work. There's no reason a console app can't reference that dll.
I just created a console app using this code and verified that it gets each key pressed, whether or not the console app has the focus.
EDIT
The main thread will run Application.Run() until the application exits, e.g. via a call to Application.Exit(). The simplest way to do other work is to start a new Task to perform that work. Here's a modified version of Main() from the linked code that does this
public static void Main()
{
var doWork = Task.Run(() =>
{
for (int i = 0; i < 20; i++)
{
Console.WriteLine(i);
Thread.Sleep(1000);
}
Application.Exit(); // Quick exit for demonstration only.
});
_hookID = SetHook(_proc);
Application.Run();
UnhookWindowsHookEx(_hookID);
}
NOTE
Possibly provide a means to exit the Console app, e.g. when a special key combo is pressed depending on your specific needs. In the
Related
I'm writing an console application witch Displaying certain data on the console screen, than checking for user input from the keyboard and finally handleing it by need. all single threaded.
For that i tried using Keyboard.IsKeyDown Method from System.Windows.Input namespace. and visual studio wo'nt allow it.
Does anyone knows why and can help me?
I dont see other way implementing that logic using only one thread and no timer's.
Use Console.ReadKey() to read input from the keyboard in a console application.
Note that this is a blocking call. If you don't want to block, combine with Console.KeyAvailable. For example, this program will loop and display if a key is pressed every 10th of a second:
static void Main(string[] args)
{
do
{
if (Console.KeyAvailable)
{
var key = Console.ReadKey();
Console.WriteLine(key.Key);
}
else
{
Console.WriteLine("No key pressed");
}
System.Threading.Thread.Sleep(100);
} while (true);
}
What I want to have happen is that the console window just goes away, or better yet that it is hidden, but I want my application to keep running. Is that possible? I want to be able to use Console.WriteLine and have the console serve as an output window. I want to be able to hide and show it, and I don't want the whole app to die just because the console was closed.
EDIT
Code:
internal class SomeClass {
[DllImport("kernel32")]
private static extern bool AllocConsole();
private static void Main() {
AllocConsole();
while(true) continue;
}
}
EDIT 2
I tried the accepted solution here [ Capture console exit C# ], per the suggestion in the comments on this question. The example code is bugged in that the DLLImport needs to be "kernel32.dll" or "kernel32", not "Kernel32". After making that change, I'm getting a message to my handler for CTRL_CLOSE_EVENT when I click the X on the console window. However, calling FreeConsole and/or returning true doesn't prevent the application from terminating.
Ah, yes, this is one of the caveats of using the Windows console subsystem. When the user closes the console window (regardless of how the console was allocated), all of the processes that are attached to the console are terminated. That behavior makes obvious sense for console applications (i.e., those that specifically target the console subsystem, as opposed to standard Windows applications), but it can be a major pain in cases like yours.
The only workaround that I know of is to use the SetConsoleCtrlHandler function, which allows you to register a handler function for Ctrl+C and Ctrl+Break signals, as well as system events like the user closing the console window, the user logging off, or the system shutting down. The documentation says that if you're only interested in ignoring these events, you can pass null for the first argument. For example:
[DllImport("kernel32")]
static extern bool SetConsoleCtrlHandler(HandlerRoutine HandlerRoutine, bool Add);
delegate bool HandlerRoutine(uint dwControlType);
static void Main()
{
AllocConsole();
SetConsoleCtrlHandler(null, true);
while (true) continue;
}
That works perfectly for Ctrl+C and Ctrl+Break signals (which would have otherwise caused your application to terminate as well), but it doesn't work for the one you're asking about, which is the CTRL_CLOSE_EVENT, generated by the system when the user closes the console window.
Honestly, I don't know how to prevent that. Even the sample in the SDK doesn't actually allow you to ignore the CTRL_CLOSE_EVENT. I tried it in a little test app, and it beeps when you close the window and prints the message, but the process still gets terminated.
Perhaps more worryingly, the documentation makes me think it is not possible to prevent this:
The system generates CTRL_CLOSE_EVENT, CTRL_LOGOFF_EVENT, and CTRL_SHUTDOWN_EVENT signals when the user closes the console, logs off, or shuts down the system so that the process has an opportunity to clean up before termination. Console functions, or any C run-time functions that call console functions, may not work reliably during processing of any of the three signals mentioned previously. The reason is that some or all of the internal console cleanup routines may have been called before executing the process signal handler.
It's that last sentence that catches my eye. If the console subsystem starts cleaning up after itself immediately in response to the user attempting to close the window, it may not be possible to halt it after the fact.
(At least now you understand the problem. Maybe someone else can come along with a solution!)
Unfortunately there's nothing you can do to really alter this behaviour.
Console windows are "special" in that they're hosted by another process and do not allow sub-classing. This limits your ability to modify their behaviour.
From what I know, your two options are:
1. Disable the close button altogether. You can do this with the following code fragment:
HWND hwnd = ::GetConsoleWindow();
if (hwnd != NULL)
{
HMENU hMenu = ::GetSystemMenu(hwnd, FALSE);
if (hMenu != NULL) DeleteMenu(hMenu, SC_CLOSE, MF_BYCOMMAND);
}
2. Stop using consoles altogether, and implement your own text output solution.
Option #2 is the more complicated option but would provide you the greatest control. I found an article on CodeProject that implements a console-like application using a rich edit control to display the text (rich edit controls have the ability to stream text like the console, so they are well suited to this sort of application).
On closing the console window obtained using AllocConsole or AttachConsole, the associated process will exit. There is no escape from that.
Prior to Windows Vista, closing the console window would present a confirmation dialogue to the user asking him whether the process should be terminated or not but Windows Vista and later do not provide any such dialogue and the process gets terminated.
One possible solution to work around this is avoiding AttachConsole altogether and achieving the desired functionality through other means.
For instance in the case described by OP, console window was needed to output some text on Console using Console static class.
This can be achieved very easily using inter-process communication. For example a console application can be developed to act as an echo server
namespace EchoServer
{
public class PipeServer
{
public static void Main()
{
var pipeServer = new NamedPipeServerStream(#"Com.MyDomain.EchoServer.PipeServer", PipeDirection.In);
pipeServer.WaitForConnection();
StreamReader reader = new StreamReader(pipeServer);
try
{
int i = 0;
while (i >= 0)
{
i = reader.Read();
if (i >= 0)
{
Console.Write(Convert.ToChar(i));
}
}
}
catch (IOException)
{
//error handling code here
}
finally
{
pipeServer.Close();
}
}
}
}
and then instead of allocating/attaching a console to the current application, the echo server can be started from within the application and Console's output stream can be redirected to write to the pipe server.
class Program
{
private static NamedPipeClientStream _pipeClient;
static void Main(string[] args)
{
//Current application is a Win32 application without any console window
var processStartInfo = new ProcessStartInfo("echoserver.exe");
Process serverProcess = new Process {StartInfo = processStartInfo};
serverProcess.Start();
_pipeClient = new NamedPipeClientStream(".", #"Com.MyDomain.EchoServer.PipeServer", PipeDirection.Out, PipeOptions.None);
_pipeClient.Connect();
StreamWriter writer = new StreamWriter(_pipeClient) {AutoFlush = true};
Console.SetOut(writer);
Console.WriteLine("Testing");
//Do rest of the work.
//Also detect that the server has terminated (serverProcess.HasExited) and then close the _pipeClient
//Also remember to terminate the server process when current process exits, serverProcess.Kill();
while (true)
continue;
}
}
This is just one of the possible solutions. In essence the work around is to allot the console window to its own process so that it can terminate without affecting the parent process.
You can do this by disabling keyboard mouse input by external program called Keyfreez.
you can use it multiple times in your program where no user input required. And if any user input require u can add a process Takskkill /f /IM .
https://www.sordum.org/7921/bluelife-keyfreeze-v1-4-block-keyboard-and-mouse/
Hope this helps all of you
I have been researching how to create a hybrid winform and CLI app... I started out my app as Winforms, now I am adding CLI to it... It seems to work but has a few issues I want to figure out how to fix and have not been able to do so , probably due to my lack of experience with C#.
If i have output type in VS set to "windows application", and use the code below i am able to run the winform portion for GUI, and also from command line am able to give it parameters and it works, well at least it outputs consolewrites i have coded in, i have a seperate c# class file that has my main code so it is seperate from winform GUI and my eventual cli code, they both will just feed user input to this other "main" c# class.. anyway here is the code.
[STAThreadAttribute]
[DllImport("kernel32.dll")]
static extern bool AttachConsole(int dwProcessId);
private const int ATTACH_PARENT_PROCESS = -1;
static void Main(string[] args)
{
if (args.Length == 0)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new form_Main());
}
else
{
AttachConsole(ATTACH_PARENT_PROCESS);
cli_main cli = new cli_main();
cli.start_cli(args);
}
}
well it works in gui, i can access my menus and create different win forms, moment i click a button to perform an action i get the following exception:
System.Threading.ThreadStateException was unhandled by user code
Message=Current thread must be set to single thread apartment (STA) mode before OLE calls can be made. Ensure that your Main function has STAThreadAttribute marked on it. This exception is only raised if a debugger is attached to the process.
if i then change the output type to "console application", it works perfectly in all manners in cli and GUI, BUT when it opens the winform/GUI portion i get this ugly CMD window that will not go away.. here is the code i used, basically just what i started with before i added the above code.
static void Main(string[] args)
{
if (args.Length == 0)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new form_Main());
}
else
{
cli_main cli = new cli_main();
cli.start_cli(args);
}
}
again with my lack of knowledge on C# i am hoping someone can point me to a solution . I would prefer to keep the app as output "console application" and find a way to hide the console that is opened when i start the winform/gui portion..?? thanks in advance.
Did you do what it said in the error message?
Ensure that your Main function has STAThreadAttribute marked on it
In the code you pasted, the STAThread attribute is not marking the Main method, it is marking the AttachConsole external function. Move that to where it ought to be and you shouldn't have any problems.
If your application is a console application, it will get a console window automatically if there isn't already one attached. That's the point of making it a console application. You can use FindWindow and ShowWindow(SW_HIDE) to hide it at runtime but it will still flash on-screen briefly.
If you plan to start your application from an existing console window most of the time, you should keep it as a console application, since it will inherit the parent process's console window by default. If you plan to start your application from a UI shortcut or from other non-console processes, you should probably make it a Windows application and allocate the console as needed.
Thanks for all, but some changes for previous pattern
It works so:
static void Main(string[] args)
{
// Test pattern
args = new string[] { "-flag1", "value1", "-flag2", "value2", "-flag3", "value3" };
if (args.Length == 0)
MainWinApp();
else
MainCLI(args);
}
[STAThread]
static MainWinApp()
{
// Your code for start GUI application
}
[STAThreadAttribute]
static void MainCLI(string[] args)
{
// Your code for CLI application
}
I've got a mono app written in c# and executed on a Mac using "mono myapp.exe"
The app itself is a "Windows Application" when viewed from the project properties, but it doesn't always show a window. In program.cs, there is a static Main:
static void Main(string[] args) {
UserClient client = new UserClient();
client.Start(args);
}
public class UserClient {
public void Start(string[] args) {
// Talk to server, listen for instructions, etc.
....
// Launch the "Stay Alive" thread
// Just a thread that Sleeps/Loops watching for an exit command; mainly used to keep the process alive
}
}
Inside the UserClient's Start method, there is a piece of code that continuously monitors a server which gives it instructions to do things. One of the things it does is optionally displays a message using a windows form.
When the server instructs the process to display a message, it instantiates a form, displays it using frm.ShowDialog() and then after 30 seconds, a timer on the form runs Close() and the frm then gets disposed. However, when this happens, on my Mac I see an application title bar saying "mono" and a new icon on my dock bar for the mono app. After about 2 minutes the mono process in Activity Monitor shows "Not Responding." This eventually will prevent the user from logging out, shutting down, etc. (because Mac OS can't kill mono gracefully).
ON THE OTHER HAND... if the server never tells the process to display that form, everything runs fine and dandy: a dock icon never shows up (which is good!), mono title bar never shows up and the mono process continues to run happily, not preventing the system from shutting down or rebooting.
Anyone experienced this or have ideas on what's causing it? My guess is that it's a new GUI thread being created by the form which isn't ever being shutdown and is somehow causing a lockup, though I'm unsure of how to handle it.
Thanks for any suggestions.
Update:
Here's some code to easily reproduce and see this happening. I realize that this seems kind of "non-standard." Having said that, the below works perfectly in a Windows environment and provides the desired result of not showing an icon in the task area except when showing a message. Currently, using Application.Run and simply doing frm.ShowDialog() produce exactly the same result.
In the end what we need is to be able to display the form, then destroy the form and any associated icon from the dock. I suspect the GUI is starting a thread which isn't ever being disposed, which is why the dock icon remains. Is there a way to make sure the GUI thread is taken care of?
static class Program {
static void Main() {
StartupClass s = new StartupClass();
s.start();
}
}
public class StartupClass {
Thread stayAliveThread;
public void start() {
// Stay alive thread
stayAliveThread = new Thread(stayAliveLoop);
stayAliveThread.Start();
// This shows a form and would normally be used to display temporary and brief messages to the user. Close the message and you'll see the undesired functionality.
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
Application.Exit();
Application.ExitThread();
}
/// <summary>
/// Keep the app alive.
/// </summary>
private void stayAliveLoop() {
while (true) {
Thread.Sleep(10000);
// In the real project this method monitors the server and performs other tasks, only sometimes displaying a message.
}
}
}
I feel I'm missing several things. Most notably
[STAThread]
static void Main(string[] args) { //....
Also see this answer: Windows Forms and ShowDialog problem
I can't see anything like initializing message loop for windowed application. I.e. in windows forms case something like Application.Run(). If you do not have it, no wonder application freezes. In any case, posting more code could be helpful, as stated in comment.
In the end, I couldn't resolve this. I created a process that launched another app which displayed the message form. Not really a true answer, but the solution I had to go with.
We have a little C# startup appplication that users launch to run the latest version of our main C# WinForms application from a network share. It's kind of a simplified ClickOnce arrangement (our IT folks won't allow us to use ClickOnce).
The startup application exits after calling Process.Start("MainApplication.exe"), but the main application can take several seconds to display, leaving the user with a blank screen.
Is there a way that the starup application can poll the OS to find out if the main aplication is running before it exits? Or some other way to handle this?
You can use Process.WaitForInputIdle() to wait until your application enteres the Idle state.
Process appProcess = Process.Start("MainApplication.exe");
appProcess.WaitForInputIdle();
From MSDN:
...you have just started a process and
want to use its main window handle,
consider using the WaitForInputIdle
method to allow the process to finish
starting, ensuring that the main
window handle has been created
Remarks Section from Process.MainWindowHandle property.
You can call Process.GetProcessByName to see if the new process has been created. The other option would be to have your main application kill the startup application once it has finished loading.
Use Davids' suggestion or alternatively you can put a splash screen in your main application. It will be just a simple Form with an image running on a separate worker thread. Put this as the first item invoked on start up. Your app can continue initializing on the main thread & after some seconds or just before your Main app finishes initialization kill the worker thread.
One way to solve this easily is to use a global event to signal the startup application that the main app has reached a predetermined state. To do this create a named event handle in the startup application and wait for it to be signaled:
static void Main(string[] args)
{
const string globalName = "MyProgramName";//something unique
bool isNew = false;
ManualResetEvent mreExited = new ManualResetEvent(false);
EventWaitHandle mreStarted = new EventWaitHandle(false, EventResetMode.ManualReset, globalName, out isNew);
try
{
if (!isNew)//already running, just exit?
return;
//start and monitor for exit
Process pstarted = Process.Start("...");
pstarted.Exited += delegate(object o, EventArgs e) { mreExited.Set(); };
pstarted.EnableRaisingEvents = true;
int index = WaitHandle.WaitAny(new WaitHandle[] { mreExited, mreStarted });
if (index == 0)//mreExited signaled
throw new ApplicationException("Failed to start application.");
}
finally
{
mreExited.Close();
mreStarted.Close();
}
}
Then in the main program you signal the event once your ready for the startup application to quit:
static void Main(string[] args)
{
const string globalName = "MyProgramName";//same unique name
bool isNew = false;
EventWaitHandle mreStarted = new EventWaitHandle(false, EventResetMode.ManualReset, globalName, out isNew);
try
{
if (!isNew)
mreStarted.Set();
Application.Run(new Form());
}
finally
{
mreStarted.Close();
}
}
I think David's second option is the way to go here. The process may be running with no visible UI yet. So, once the main form is displayed in the second app, kill the first process. You could even, conceivably, pass the process id of the original process in via StartInfo when you start the new process.
There are many trade-offs involved in this, but you may be able to make start-up time fast enough to make this issue moot by using NGEN.
My suggestion is to put Splash Screen in your application it gives you a good feel rather than if you dont want to use Splash screen then kill the process when loading is finished. Or you can use Process.GetProcessByName()
For Splash Screen just make a Window Screen and in startup class just inherit the class from Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase that gives you a method OnCreateSplashScreen() Override this method like this :-
protected override void OnCreateSplashScreen()
{
base.OnCreateSplashScreen();
//yourSplashScreen is the screen you made as a window form either it would be a image or form
SplashScreen = yourSplashScreen();
}
Thanks ...Saurabh Singh Happy To Code