Catching ctrl+c event in console application (multi-threaded) - c#

I have a main thread of a Console Application that runs few external processes this way
private static MyExternalProcess p1;
private static MyExternalProcess p2;
private static MyExternalProcess p3;
public void Main() {
p1 = new MyExternalProcess();
p2 = new MyExternalProcess();
p3 = new MyExternalProcess();
p1.startProcess();
p2.startProcess();
p3.startProcess();
}
public static void killEveryoneOnExit() {
p1.kill();
p2.kill();
p3.kill();
}
class MyExternalProcess {
private Process p;
...
public void startProces() {
// do some stuff
PlayerProcess = new Process();
....
PlayerProcess.Start();
// do some stuff
}
public void kill() {
// do some stuff
p.Kill();
}
}
What I need to do is: when the Main thread is interrupted (exit button or ctrl+c), the other processes should be killed.
How do I trigger my method killEveryoneOnExit on CTRL+C or Exit (X) button?

Based on your question there are two events you need to catch.
First there is the console close event which is explained here: "On Exit" for a Console Application
Second you want to catch control c which is explained here: How do I trap ctrl-c in a C# console app
If you put these two together with your example you get something like this:
static ConsoleEventDelegate handler;
private delegate bool ConsoleEventDelegate(int eventType);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetConsoleCtrlHandler(ConsoleEventDelegate callback, bool add);
private static MyExternalProcess p1;
public static void Main()
{
Console.CancelKeyPress += delegate
{
killEveryoneOnExit();
};
handler = new ConsoleEventDelegate(ConsoleEventCallback);
SetConsoleCtrlHandler(handler, true);
p1 = new MyExternalProcess();
p1.startProcess();
}
public static void killEveryoneOnExit()
{
p1.kill();
}
static bool ConsoleEventCallback(int eventType)
{
if (eventType == 2)
{
killEveryoneOnExit();
}
return false;
}
For a working ctrl c (fun intended) paste example: http://pastebin.com/6VV4JKPY

Related

SendKeys not working with VisualBoy Advance

I am attempting to simulate keyboard input to programmatically play a game in VisualBoy Advance. There is no response from VisualBoy Advance when SendKeys.SendWait() is used.
private const string VBA_PROCESS_NAME = "VBA-rr-svn480";
public void Up()
{
PressButton("{UP}");
}
public void Down()
{
PressButton("{DOWN}");
}
public void Left()
{
PressButton("{LEFT}");
}
public void Right()
{
PressButton("{RIGHT}");
}
public void A()
{
PressButton("z");
}
public void B()
{
PressButton("x");
}
public void LShoulder()
{
PressButton("a");
}
public void RShoulder()
{
PressButton("s");
}
public void Start()
{
PressButton("~");
}
public void Select()
{
PressButton("+");
}
private void PressButton(string Button)
{
var VBAProcess = GetVBAProcess();
// Verify that VBA is a running process.
if (VBAProcess == null)
throw new Exception("Visual Boy Advance could not be found.");
IntPtr VBAHandle = VBAProcess.MainWindowHandle;
// Make sure that VBA is running and that we have a valid handle.
if (VBAHandle == IntPtr.Zero)
throw new Exception("Visual Boy Advance is not running.");
// Make VBA the foreground application and send it the button press.
SetForegroundWindow(VBAHandle);
SendKeys.SendWait(Button);
}
// Activate an application window.
[DllImport("USER32.DLL")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
private Process GetVBAProcess()
{
return Process.GetProcessesByName(VBA_PROCESS_NAME).FirstOrDefault();
}
If I swap out the process name for a different process (such as notepad++) the key presses work perfectly. This leads me to believe that I must have the wrong process or window for VisualBoy Advance, but I haven't found one that looks correct when I grab all processes and look through them.

InternetExplorer DocumentCompleteEventHandler not firing in C#

I am new to C#, I am writing code to open an IEBrowser and do some stuff once its loaded.
If you see the code in Main below
Here is my code :
public delegate void DocumentCompleteEventHandler(SHDocVw.InternetExplorer IE);
class Program{
private static string m_autoLoginFormContents = null;
private static SHDocVw.InternetExplorer m_autologinIEWindow;
static SHDocVw.DWebBrowserEvents2_DocumentCompleteEventHandler m_AutoLoginDocCompleteHandler;
private static SHDocVw.DWebBrowserEvents2_DocumentCompleteEventHandler m_documentCompleteEventHandler;
public static event DocumentCompleteEventHandler DocumentComplete;
static void Main(string[] args)
{
m_documentCompleteEventHandler = new SHDocVw.DWebBrowserEvents2_DocumentCompleteEventHandler(DocumentCompleteEventHandler);
m_autologinIEWindow = OpenIEWindowToURL("about:blank");
m_autologinIEWindow.DocumentComplete += m_AutoLoginDocCompleteHandler;
m_AutoLoginDocCompleteHandler = new SHDocVw.DWebBrowserEvents2_DocumentCompleteEventHandler(URLAutologinDocumentCompleteEventHandler);
System.Console.Read();
}
public static void URLAutologinDocumentCompleteEventHandler(object senderObject, ref object objectTwo /* not sure what this argument is for */)
{
//Something
}
private static void DocumentCompleteEventHandler(object senderObject, ref object objectTwo /* not sure what this argument is for */ )
{
//Something
}
}
The IE Window opens up with blank page as needed but the event is never fired up, ofcourse I am doing something wrong as I am super new and probably my first code in C#.
You can make the code simple and try as said below. It is working...
You can notice the document complete event handling.
Look into comments for explanation.
static void Main()
{
//DECLARE INTERNET EXPLORER OBJECT
SHDocVw.InternetExplorer m_autologinIEWindow = new SHDocVw.InternetExplorer();
//ASSOCIATE HANDLER TO DOCUMENT COMPLETE EVENT
m_autologinIEWindow.DocumentComplete += URLAutologinDocumentCompleteEventHandler;
//NAVIGATE THE URL
m_autologinIEWindow.Navigate("about:blank");
m_autologinIEWindow.AddressBar = true;
m_autologinIEWindow.Visible = true;
}
//HANDLER DEFINITION
public static void URLAutologinDocumentCompleteEventHandler(object senderObject, ref object objectTwo /* not sure what this argument is for */)
{
//Something
}

helper method checking if window is open on another thread

I have found this solution for checking if a window is open:
How do I know if a WPF window is opened
It's throwing an error back at me since my wpf window is on another thread. Is there a way to still use it?
Solution:
public static bool IsWindowOpen<T>(string name = "") where T : Window
{
return string.IsNullOrEmpty(name)
? Application.Current.Windows.OfType<T>().Any()
: Application.Current.Windows.OfType<T>().Any(w => w.Name.Equals(name));
}
if (Helpers.IsWindowOpen<Window>("MyWindowName"))
{
// MyWindowName is open
}
if (Helpers.IsWindowOpen<MyCustomWindowType>())
{
// There is a MyCustomWindowType window open
}
if (Helpers.IsWindowOpen<MyCustomWindowType>("CustomWindowName"))
{
// There is a MyCustomWindowType window named CustomWindowName open
}
I have created a sample application solving your problem after spending entire day.
Solution can be downloaded here
What it does :
Click button to create window on new thread. A new window is created for you on new thread. The moment this new window is created, this button in your mainwindow is disabled. When you close your new window, creation button in your mainwindow is enabled again.
If it doesn't fit your needs, tell your requirements, I will improve it. Same can be done using pure Win32 functions too without using our event bridge class. I am working on it. And I will post win32 version soon.
I am creating NewWindow on a separate thread. If you close MainWindow, NewWindow still runs as it is on new thread.
I am keeping it completely separate as no instance is used in MainWindow to point to NewWindow. To solve this issue I am using a Win32 handle.
For NewWindow to send notifications to MainWindow, I am using a static class WindowNotifier with static events. This class acts as the bridge between the two. In NewWindow Closing/Closed/Loaded events are used to fire events.
MainWindow handle various events of this static class to remain updated about NewWindow.
Win32 functions used :
[DllImport("user32.dll")]
public static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
public static extern bool IsWindowVisible(IntPtr hWnd);
ThreadCreator.cs
public static class ThreadCreator
{
private static NewWindow W;
public static void CreateWindow()
{
Thread t = new Thread(ThreadProc);
t.SetApartmentState(ApartmentState.STA);
t.Start();
}
private static void ThreadProc(object obj)
{
W = new NewWindow();
W.ShowDialog();
}
}
WindowNotifier.cs
public static class WindowNotifier
{
public static event CreatedDelegateCallback IamCreatedEvent;
public delegate void CreatedDelegateCallback(IntPtr handle);
public static event ClosingDelegateCallback IamClosingEvent;
public delegate void ClosingDelegateCallback (IntPtr handle);
public static event ClosedDelegateCallback IamClosedEvent;
public delegate void ClosedDelegateCallback(IntPtr handle);
public static void OnIamCreated(IntPtr handle)
{
if (IamCreatedEvent != null)
IamCreatedEvent(handle);
}
public static void OnIamClosing(IntPtr handle)
{
if (IamClosingEvent != null)
IamClosingEvent(handle);
}
public static void OnIamClosed(IntPtr handle)
{
if (IamClosedEvent != null)
IamClosedEvent(handle);
}
}
MainWindow.xaml.cs
...
void WindowNotifier_IamCreatedEvent(IntPtr handle)
{
HandleOfWindowOnNewThread = handle;
Debug.WriteLine(string.Format("I am created : {0}", handle));
btnCreateNewWindow.Dispatcher.Invoke(() => btnCreateNewWindow.IsEnabled = false);
}
void WindowNotifier_IamClosedEvent(IntPtr handle)
{
if (HandleOfWindowOnNewThread == handle)
HandleOfWindowOnNewThread = IntPtr.Zero;
Debug.WriteLine(string.Format("I am closed : {0}", handle));
btnCreateNewWindow.Dispatcher.Invoke(() => btnCreateNewWindow.IsEnabled = true);
}
...
NewWindow.xaml.cs
...
private void Window_Closed(object sender, EventArgs e)
{
WindowNotifier.OnIamClosed(Handle);
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
WindowNotifier.OnIamClosing(Handle);
}
// To get correct handle we need to ensure Window is fully created and active
private void Window_Loaded(object sender, RoutedEventArgs e)
{
_handle = GetForegroundWindow();
WindowNotifier.OnIamCreated(Handle);
}
...
Dispatcher does not help here because when window is created on a different thread, it's not contained in the Application.Windows collection, but in a collection which for some reason is not exposed (called NonAppWindowsInternal). Shortly, there is no official way to do that. Of course you can use reflection on your own risk.
But if your window is on UI thread and you just want to call the function from another thread, then you can use something like this
Application.Current.Dispatcher.Invoke(() => IsWindowOpen<...>(...))
or better change the helper method to be
public static bool IsWindowOpen<T>(string name = "") where T : Window
{
return Application.Current.Dispatcher.Invoke(() => string.IsNullOrEmpty(name)
? Application.Current.Windows.OfType<T>().Any()
: Application.Current.Windows.OfType<T>().Any(w => w.Name.Equals(name)));
}
EDIT Here is something that works currently, but may change in the future, so as mentioned above, use it on your own risk
public static class WindowUtils
{
public static bool IsWindowOpen<T>(string name = "") where T : Window
{
return FindWindow<T>(name) != null;
}
public static T FindWindow<T>(string name = "") where T : Window
{
return FindWindow<T>(WindowsInternal, name) ?? FindWindow<T>(NonAppWindowsInternal, name);
}
private static T FindWindow<T>(Func<Application, WindowCollection> windowListAccessor, string name = "") where T : Window
{
bool matchName = !string.IsNullOrEmpty(name);
var windowList = windowListAccessor(Application.Current);
for (int i = windowList.Count - 1; i >= 0; i--)
{
var window = windowList[i] as T;
if (window != null && (!matchName || window.Name == name)) return window;
}
return null;
}
private static readonly Func<Application, WindowCollection> WindowsInternal = GetWindowCollectionAccessor("WindowsInternal");
private static readonly Func<Application, WindowCollection> NonAppWindowsInternal = GetWindowCollectionAccessor("NonAppWindowsInternal");
private static Func<Application, WindowCollection> GetWindowCollectionAccessor(string propertyName)
{
var property = typeof(Application).GetProperty(propertyName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
return (Func<Application, WindowCollection>)Delegate.CreateDelegate(
typeof(Func<Application, WindowCollection>), property.GetMethod);
}
}
If you return the window from your IsWindowOpen method. U can use the Invoke or BeginInvoke on the window, to dispatch the work on the thread where the window was created on.

console catch close event thread

I have a console application which uses a BackgroundWorker to run an infinite loop.
I'm trying to catch the close event and do some stuff.
I used some googled solutions, and came up with the following:
class Program
{
private static bool keepAlive = false;
private static BackgroundWorker bgWorker = new BackgroundWorker();
private static void runThread ()
{
while (keepAlive) {}
}
private bool ConsoleClosingCheck(CtrlTypes ctrlType)
{
switch (ctrlType)
{
case CtrlTypes.CTRL_CLOSE_EVENT:
keepAlive = false;
break;
}
return true;
}
[DllImport("Kernel32")]
public static extern bool SetConsoleCtrlHandler(HandlerRoutine Handler, bool Add);
public delegate bool HandlerRoutine(CtrlTypes CtrlType);
public enum CtrlTypes
{
CTRL_C_EVENT = 0,
CTRL_BREAK_EVENT,
CTRL_CLOSE_EVENT,
CTRL_LOGOFF_EVENT = 5,
CTRL_SHUTDOWN_EVENT
}
public static void Start ()
{
keepAlive = true;
bgWorker.DoWork += (sender, e) => runThread();
bgWorker.RunWorkerAsync();
}
public static void Main(string[] args)
{
Program p = new Program();
SetConsoleCtrlHandler(new HandlerRoutine(p.ConsoleClosingCheck), true);
p.Start();
}
}
When I debug it and close the console using the "X" (normal close) it doesn't stop at the case, instead the application crashes.
Any ideas?
The example code doesn't compile for me:
Member 'Program.Start()' cannot be accessed with an instance reference;
I suspect that the problem is just because the 'p' object that you create does not have a managed root (no managed class keeps a reference to it), there is only an unmanaged callback that refers to it.
This means that p can get garbage collected, causing a failure when calling the callback.
I tried converting the example Program class to be all static. It didn't wait in the Start() method so I added a wait on BackgroundWorker.IsBusy at the end of the start method.
I also added such a wait in the callback; otherwise, its just a race to see if the rest of the doWork method gets to execute:
case CtrlTypes.CTRL_CLOSE_EVENT:
{
keepAlive = false;
while (bgWorker.IsBusy)
{
Thread.Sleep(100);
}
break;
}

Windows Forms - How to kick of a seperate thread and hold current thread

I have a windows app and an an dll(windows form) that im trying to open (ActivationCheck), im trying to pause the current thread open a new thread (ActivationCheck) wait for that form event to return true then continue the main thread.
Could someone explain\show me what im doing wrong - thanks.
static class Program
{
private static SplashScreen splash;
private static bool quitApp;
private static bool activationFinished;
[STAThread]
static void Main()
{
Thread thread = new Thread(ActivationCheck);
thread.Start();
do
{
Thread.Sleep(1000);
} while (activationFinished);
if (!quitApp)
{
Thread.Sleep(0);
// WizardRun();
Application.Run(new Main(ref splash));
}
}
}
private static void ActivationCheck()
{
splash.SetStatus = "Checking License...";
Guid productId = new Guid(Properties.Settings.Default.ProductId);
Guid versionId = new Guid(Properties.Settings.Default.VersionId);
Client.UI.EntryPoint entryPoint = new EntryPoint();
activationFinished = false;
Client.BLL.ProductActivation.GenerateTrialLicense(productId1, versionId2, EditionId3);
entryPoint.IniatePlugin(productId, versionId);
entryPoint.PluginFinished += new EventHandlers.PluginFinishEventHandler(entryPoint_PluginFinished);
}
static void entryPoint_PluginFinished(bool forceQuit)
{
quitApp = forceQuit;
activationFinished = true;
}
You could just do thread.Join()? To be honest, though, I'm not quite sure what the point is of starting a second thread and pausing the first; just do the work on the original thread?
The problem with the code is possibly that activationFinished is being held in a register; try marking it as volatile, or alternatively use a lock at both places that access this variable. Even better would be to use a ManualResetEvent or similar, and open it from the activation code.
using System;
using System.Threading;
static class Program
{
static void Main()
{
new Thread(DoActivation).Start();
Console.WriteLine("Main: waiting for activation");
activation.WaitOne();
Console.WriteLine("Main: and off we go...");
}
static void DoActivation(object state)
{
Console.WriteLine("DoActivation: activating...");
Thread.Sleep(2000); // pretend this takes a while
Console.WriteLine("DoActivation: activated");
activation.Set();
// any other stuff on this thread...
}
static ManualResetEvent activation = new ManualResetEvent(false);
}

Categories

Resources