Code stops executing when a user clicks on the console window - c#

I've got a console application that executes my code without user interaction. If the user clicks within the console window, on purpose or on accident, all execution stops.
This has something to do with copying text from the console window. The only way for the application to start executing again is if the user selects text and then right-clicks on the console window, copying it to the clipboard.
To see this in action, create a console application and add the following code.
class Program
{
static void Main(string[] args)
{
var task = Task.Run(async () =>
{
int i = 0;
while (true)
{
Console.WriteLine(i++);
await Task.Delay(1000);
}
});
Console.ReadLine();
}
}
When you click on the console window, the Task thread stops executing. This is not desirable behavior at all, and I want to prevent this from happening in my console application.
How can I prevent this? None of the properties/events on the console window have anything to do with controlling this behavior, as far as I can see.
As you can see, when i'm click within window appear cursor. When i press any key - cursor gone and app continue working

This happens if you have Quick Edit Mode enabled on the console window. If you right-click on the title bar and select Properties, then select the Options tab, you can check to see if Quick Edit Mode is enabled. If you disable Quick Edit Mode, then the scrolling doesn't stop when you click in the window.
The reason scrolling stops is because a mouse clicked in the window is used to select text.
You can disable Quick Edit Mode on the console in your program, but doing so requires calling the GetConsoleMode and SetConsoleMode API functions. Here's how you would do it:
[DllImport("kernel32.dll", SetLastError=true)]
public static extern IntPtr GetConsoleWindow();
[DllImport("kernel32.dll", SetLastError=true)]
public static extern bool GetConsoleMode(
IntPtr hConsoleHandle,
out int lpMode);
[DllImport("kernel32.dll", SetLastError=true)]
public static extern bool SetConsoleMode(
IntPtr hConsoleHandle,
int ioMode);
/// <summary>
/// This flag enables the user to use the mouse to select and edit text. To enable
/// this option, you must also set the ExtendedFlags flag.
/// </summary>
const int QuickEditMode = 64;
// ExtendedFlags must be combined with
// InsertMode and QuickEditMode when setting
/// <summary>
/// ExtendedFlags must be enabled in order to enable InsertMode or QuickEditMode.
/// </summary>
const int ExtendedFlags = 128;
void DisableQuickEdit()
{
IntPtr conHandle = GetConsoleWindow();
int mode;
if (!GetConsoleMode(conHandle, out mode))
{
// error getting the console mode. Exit.
return;
}
mode = mode & ~(QuickEditMode | ExtendedFlags);
if (!SetConsoleMode(conHandle, mode))
{
// error setting console mode.
}
}
void EnableQuickEdit()
{
IntPtr conHandle = GetConsoleWindow();
int mode;
if (!GetConsoleMode(conHandle, out mode))
{
// error getting the console mode. Exit.
return;
}
mode = mode | (QuickEditMode | ExtendedFlags);
if (!SetConsoleMode(conHandle, mode))
{
// error setting console mode.
}
}
If you go down this route, it's probably a good idea to save the original console mode setting when your program starts, and restore it when your program exits. So at startup:
GetConsoleMode(GetConsoleWindow(), ref saveConsoleMode);
and when your program terminates:
SetConsoleMode(GetConsoleWindow(), saveConsoleMode);
With appropriate error handling, of course. You wouldn't want to restore the console mode if the call to GetConsoleMode failed.

I just saw that this answer linked in the comments of OP's question contained what I found by myself. I will keep my answer because people might not see it, just like me, and it would spare them a lot of time.
Jim's answer did not work for me, I couldn't figure out why.
I dug around and found a solution that works, so I'll share my findings, hopefully helping someone in the same situation.
The problem was with the handle that I got from GetConsoleWindow(), it gave a Win32 error (0x6) where the handle is invalid when I tried to use it. The call to SetConsoleMode() did nothing.
To get a working handle, I used GetStdHandle() to get the Input handle for the console. Add this to Jim's code :
public const int STD_INPUT_HANDLE = -10;
[DllImport("Kernel32.dll", SetLastError = true)]
public static extern IntPtr GetStdHandle(int nStdHandle);
Then replace GetConsoleWindow() by GetStdHandle(STD_INPUT_HANDLE) in DisableQuickEdit() and EnableQuickEdit() in Jim's code.
After calling DisableQuickEdit(), the selection is disabled in the console.
Thanks Jim !

Related

How to change back and forth application focus with global hotkey in C#

I am creating my own C# clipboard manager and I have a global hotkey, ALT+H, that will trigger removal of text formatting from the clipboard. My application runs in the backend with a tray icon only. As such this is working fine but my problem is that when I am inside an application, e.g. Word and I am pressing my hotkey, then it will show me all kind of menus inside those applications and I do not want that.
Just for info then this is very related to another question I have on SO currently, How to stop further processing global hotkeys in C#. In the other question, this is based on finding a solution for the hotkey but another approach, which I think could even be better, could be to temporary switch focus to my application and once my hotkey is no longer applicable then the focus can be switched back to the originating application.
However then I have no idea how to switch back to the originating application again!?
My code so far, only focussing on the application changing mechanishmn:
Programs.cs:
// Import the required "SetForegroundWindow" method
[DllImport("user32.dll")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
Form1.cs:
// Change focus to my application (when hotkey is active)
Program.SetForegroundWindow(this.Handle);
I am not fully sure if this actually does work but I can see that the originating application (e.g. Word) looses focus and my application works fine still so I do expect it works fine.
My problem is - how to get the hWnd(?) handle from the originating application so I can switch back to it once done? And what if I am not within any application but e.g. just on the WIndows desktop? What will then happen - can it change back to that?
I would appreciate any hints that can help as I am by far no real C# developer ;-)
I have found the solution myself and will explain what worked out for me. I have this code here:
using System.Runtime.InteropServices;
[DllImport("user32.dll")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]
public static extern IntPtr GetForegroundWindow();
// I get in to here when the clipboard has changed and I need to find the application that has changed the clipboard
// Get the active/originating application handle
IntPtr originatingHandle = GetForegroundWindow();
// ------------------
// Do "some stuff" - this is not required for the application switching but it will get the application process name for the active/originating application
// Get the process ID from the active application
uint processId = 0;
GetWindowThreadProcessId(originatingHandle, out processId);
// Get the process name from the process ID
string appProcessName = Process.GetProcessById((int)processId).ProcessName;
// End "some stuff"
// ------------------
// Change focus to my application - this code is inside my main form (Form1)
SetForegroundWindow(this.Handle);
// Do some more stuff - whatever is required for my application to do
// ...
// Change focus back to the originating application again
SetForegroundWindow(originatingHandle);
At least the above code works for me.

FindWindow in C#(via pinvoke) finds desired window handle, but not in the desired conditions. How do I fix it?

I'm trying to get certain window handle. I was searching for solution for many hours and I understand that my question sounds similar to this one:
FindWindow() doesn't find my window [C++]
But that discussion didn't help.
I was trying to use both FindWindow() and FindWindowEx() like these two:
IntPtr SysPropWndHandler = FindWindow("#32770", "Параметри продуктивності");
IntPtr SysPropWndHandler = FindWindowEx(IntPtr.Zero, IntPtr.Zero, "#32770", "Параметри продуктивності");
Weird part is that when I run the program, it starts new process for certain system settings program from system32 folder and it can't find it's handle during same launch time (if that's correct to say so). I tried to pause it to give it time to create window and assign handle, but that doesn't help.
But! If that system program is launched first and then I run my program - it finds it's handle right away.
Two ways for that "external launch":
I run system program manually before launching my program
I run my program, which launches that system program, then I close my program, system program doesn't close then. After that I run my program again.
But what I'm actually trying to make my program do is this:
launch system program (some productivity settings)
hide window
change some settings via WinApi (kind of checkbox clicking emulation)
click ok
close it
Since my code works, at least in some conditions, looks like it has nothing to do with encoding, which was mansioned in that similar question. Otherwise it wouldn't work at all.
I was trying to launch it hidden, but it didn't work. I tried the same code for notepad for debugging it - it works.
string prog_path = #"C:\Windows\System32\SystemPropertiesPerformance.exe";
Process process = new Process();
process.StartInfo.FileName = prog_path;
process.StartInfo.CreateNoWindow = true; // no need for that, but I tried with it and without it just in case it works
process.StartInfo.UseShellExecute = true;
process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
process.Start();
According to Microsoft documentation you need to set UseShellExecute to true in order to use StartInfo.WindowStyle = ProcessWindowStyle.Hidden (which I did), but program still can choose to ignore that. Looks like that's exactly what's happening there.
But I tried to get exect window handle via Spy++ and try to hide it - it works, so I can manipulate it from there and do my thing. The only problem is to find it's handle...
How do I find that handle in this case?
P.S.
Windows 10 x64 Pro Ukrainian (for other languages that window title in the code won't work)
.NET Framework 4.7.2
Code is inside .NET Framework Class Library, which is launched from C# Console Application.
For me this one works fine(on Windows 7):
using System;
using System.Diagnostics;
using System.Text;
using System.Runtime.InteropServices;
namespace findwindow
{
class Program
{
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
public static void Main(string[] args)
{
Process.Start(new ProcessStartInfo(){FileName=#"C:\Windows\System32\SystemPropertiesPerformance.exe"});
System.Threading.Thread.Sleep(100);
IntPtr hwnd = FindWindow("#32770", "Параметры быстродействия");
var sb = new StringBuilder(50);
GetWindowText(hwnd, sb, 49);
Console.WriteLine("hwnd:"+hwnd+", title:"+sb);
Console.ReadKey(true);
}
}
}
Outputs:
hwnd:5636204, title:Параметры быстродействия
Try with that code your title, and say if it works.
Also there is a different approach like in this answer.
Another approach is to use the UI Automation technology that's built in Windows. For example, this sample Console app should work. And because it's event-based, it does not need to use timers which can be context-dependent:
public static void Main(string[] args)
{
Automation.AddAutomationEventHandler(WindowPattern.WindowOpenedEvent, AutomationElement.RootElement, TreeScope.Children, (sender, e) =>
{
var element = sender as AutomationElement;
if (element.Current.Name == "Параметры быстродействия")
{
Console.WriteLine("hwnd:" + element.Current.NativeWindowHandle);
}
});
Process.Start("SystemPropertiesPerformance.exe");
Console.ReadLine(); // wait ...
Automation.RemoveAllEventHandlers(); // cleanup
}
It works fine on my Windows 10 x64 machine. If this doesn't work, make sure your program and SystemPropertiesPerformance.exe run at the same UAC level.

close a message box from another program using c#

Here's my problem: we have an automated build process for our product. During the compilation of one of the VB6 projects a message box is spit out that requires the user to click 'ok' before it can move on. Being an automated process this is a bad thing because it can end up sitting there for hours not moving until someone clicks ok. We've looked into the VB6 code to try and suppress the message box, but no one can seem to figure out how to right now. So as a temp fix I'm working on a program that will run in the background and when the message box pops up, closes it. So far I'm able to detect when the message pops up but I can't seem to find a function to close it properly. The program is being written in C# and I'm using the FindWindow function in user32.dll to get a pointer to the window. So far I've tried closeWindow, endDialog, and postMessage to try and close it but none of them seem to work. closeWindow just minimizes it, endDialog comes up with a bad memory exception, and postMessage does nothing. Does anyone know of any other functions that will take care of this, or any other way of going about getting rid of this message? Thanks in advance.
here's the code I have currently:
class Program
{
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
static void Main(string[] args)
{
IntPtr window = FindWindow(null, "Location Browser Error");
while(window != IntPtr.Zero)
{
Console.WriteLine("Window found, closing...");
//use some function to close the window
window = IntPtr.Zero;
}
}
}
You have to found the window, that is the first step. After you can send the SC_CLOSE message using SendMessage.
Sample
[DllImport("user32.dll")]
Public static extern int SendMessage(int hWnd,uint Msg,int wParam,int lParam);
public const int WM_SYSCOMMAND = 0x0112;
public const int SC_CLOSE = 0xF060;
IntPtr window = FindWindow(null, "Location Browser Error");
if (window != IntPtr.Zero)
{
Console.WriteLine("Window found, closing...");
SendMessage((int) window, WM_SYSCOMMAND, SC_CLOSE, 0);
}
More Information
Find and Close the window using WIN API
When you find the message box, try sending it WM_NOTIFY with a BN_CLICKED type and the ID of the OK button.

Dispose the handle on exit of application using c#

I am using the below code to block the taskbar which is working perfectly.
But since my application is running in background, the only way to exit the application
is by killing the .exe from task manager. So while exiting like this, the blocked task bar remains at the same state. But actually it shud resume the taskbar on exiting the application.
The reason i am doing this is, it is a kiosk application.
what is the way to overcome this.
public class Taskbar
{
[DllImport("user32.dll")]
public static extern int FindWindow(string className, string windowText);
[DllImport("user32.dll")]
public static extern int ShowWindow(int hwnd, int command);
public const int SW_HIDE = 0;
public const int SW_SHOW = 1;
public int _taskbarHandle;
protected static int Handle
{
get
{
return FindWindow("Shell_TrayWnd", "");
}
}
public Taskbar()
{
_taskbarHandle = FindWindow("Shell_TrayWnd", "");
}
public static void Show()
{
ShowWindow(Handle, SW_SHOW);
}
public static void Hide()
{
ShowWindow(Handle, SW_HIDE);
}
}
Why not just use this implementation to run completely fullscreen?
http://www.codeproject.com/KB/cs/FullScreenDotNetApp.aspx
Like the others have said when you killin the application no.
Your post is a bit sparse on why you cannot close your application gracefully, so il suggest this method.
1)
Hotkeys ( http://www.codeproject.com/KB/system/Hotkeys.aspx ) that can be pressed that will close down your application gracefully. I personaly like this method, as i use hotkeys in many of my apps.
2)
Starting a seperate application that will wake up every XXX and check if the main application is running, if its not running run the Show code and then kill itself. This method is very simular to how viruses often work, so its tried and works :)
If your only way to exit is by killing it, then I am afraid you can't reset the property back to normal.
There can be a workaround to this.
Create a service which monitors your application through polling and when it finds your application as 'not running', it restores the TaskBar to normal.
There can be other similar workarounds to this but I can't think of a way of doing this from within your application given the limitation.
For exiting the application, i am registering a hot key combination and resuming the task bar and kill the process from taskbar programatically.
you can make your application check for the existance of a file (or any other thing you can control from outside and the app have access to it), if found: dispose & exit.
it's dirty but gives a little bit more control of how your application terminate than killing it from the task manager.

How do I get the active ChildWindow of an application?

I want to send a pressKey event to a certain application which is not the active application IN Windows so I have to use the sendMessage/postMessage api calls.
However, I need to know the exact child window that is active IN the application and send the pressKey message to it...
I was using GetTopWindow and GetWindow(GW_CHILD) api calls to get the top child window of the main window, and do it again with the obtained child window to get the top grandchildWindow, and keep doing it until I found a childwindow with no more childwindows. This works great for some applications but in some cases it doesn't. Sometimes the parent window is the active window, not one of its childwindows, so getting the parent's top child window will not work cause I will be sending a message to the wrong window.
The only way I found of doing this (getting the handler of the actual active window) was using the GuiThreadInfo api call but it only works if the target application is the active one IN Windows. As I mentioned in the beginning, it isn't so the handler comes null.
I can bring the application to the top using setForegroundWindow api call but I DON'T want to do this. I also tried the AttachThreadInput and GetFocus api calls, but again, they only work if the target application is the active application IN windows.
Any ideas? Thanks
I assume from the things that you have tried that you know how to get a handle to your main window, but if you don't just leave a comment and I will post a snippet for that.
I combined a few things that I found on the net to figure this out, but the main one is this one. I don't have a great app to test this with, but it works in a simple case. One exception is that I think if you use tool windows in your application it will not find that as it is coded because I think the GetLastActivePopup method doesn't include them (not sure about that, and didn't test that case).
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool IsWindowVisible(IntPtr hWnd);
[DllImport("user32.dll")]
static extern IntPtr GetLastActivePopup(IntPtr hWnd);
[DllImport("user32.dll", ExactSpelling = true)]
static extern IntPtr GetAncestor(IntPtr hwnd, uint gaFlags);
const uint GA_PARENT = 1;
const uint GA_ROOT = 2;
const uint GA_ROOTOWNER = 3;
public static IntPtr GetAppActiveWindow(IntPtr hwnd)
{
IntPtr activeAppWindow = IntPtr.Zero;
if (hwnd != IntPtr.Zero)
{
//Get the root owner window (make sure we are at the app window
//if you already have a handle to the main window shouldn't have
//to do this but I put it in just in case
hwnd = GetAncestor(hwnd, GA_ROOTOWNER);
while ((activeAppWindow =
GetLastActivePopup(hwnd)) != activeAppWindow)
{
if (IsWindowVisible(activeAppWindow))
break;
hwnd = activeAppWindow;
}
}
return activeAppWindow;
}
If you know the Window title and the Window class name, take a look at FindWindow() and FindWindowEx() and see if those meet your needs.
FindWindow(): http://msdn.microsoft.com/en-us/library/ms633499.aspx
FindWindowEx(): http://msdn.microsoft.com/en-us/library/ms633500(VS.85).aspx

Categories

Resources