I am currently writing system tests for a legacy windows forms application.
In my test, I call the Program.Main() method, which in turn calls Application.Run(new MainForm()); at some point.
Is there a way to replace Application.Run() with something that I can control?
Specifically, I need to be able to stop the execution and catch unhandled exceptions.
You could modify Program.Main to accept a form as an input parameter, with a default of MainForm. Reference types can't have non-null defaults but we can accomplish the same thing by using two prototypes:
class Program
{
//Normal entry point
static public void Main()
{
Main(new MainForm());
}
//Internal & test entry point
static public void Main(Form form)
{
DoSomeSetup();
Application.Run(form);
}
}
When you run the program in the normal way, it'll use a MainForm.
But when you run it from your test project, you can call it like this:
Program.Main(new FormICanControl());
And then you can control it.
//Arrange
var t = new TestForm();
//Act
Program.Main(t);
t.ExecuteSomeTest();
//Assert
Assert.AreEqual(t.ResultCode, 0, "Test failed.");
To "stop the execution", you can ask Main thread to exit by calling Application.ExitThread() and in dreadful situations when it does't respond you may kill the Main thread with .Abort() as a last resort.
The exceptions won't be automatically collected for you anywhere, but you can catch them in a process of testing using
Application.ThreadExceptionHandler
AppDomain.CurrentDomain.UnhandledException
Testing a real-life app without Application might be a problem because people often do some tricks by calling Application.DoEvents(..) and doing other stuff with messages pump.
Related
I am trying to keep the (beta version of my) application running as much as possible,So I placed another try-catch inside Program.cs as well in cases where some critical errors occur and shut the application down unexpectedly.And in the catch i rewrote the Application.Run() method so that the application can resume itself after being terminated for what ever reason.
Is it right to have such a plan for this specific scenario?
If it is not right,Then what else is recommended in order to keep the program running?
This is the sample code demonstrating what i mean:
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using Hossein;
using Pishro.Classes;
namespace Pishro
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
try
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new frmMain());
}
catch(Exception exc)
{
API.SaveAndShowLog(exc);
Application.Run(new frmMain());
}
}
}
}
Globally handling exceptions is a good idea for logging and alerting.
An automatic restart policy like yours can be useful, yes. There is a risk however: If the crash has corrupted global data-structures restarting the app can have unpredictable results like silent data corruption. For example, files might still be open and locked. Locks might not have been released. Static variables might be in an undefined state. Rogue threads might still be running, unaware that the application UI was destroyed.
I recommend that you restart the app by starting a new process of your application. Let the old process die.
I think your question involves a deeper question.. Should I catch all the exceptions?
To keep going you could catch them all.. but all those possible exceptions that you don't expect or not know are probably bugs.
Maybe you should try to implement better error handling approach within your app. So all the exceptions are known or expected.
Instead of wrapping a try catch around your application run method, consider handling the exceptions with events.
static void Main()
{
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)
{
MessageBox.Show("Exception handled");
}
static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
}
Mainly the thread exception is the one you want but ideally - you would want to set up some form of logging/flagging the error to the user and still dispose of the program because it may cause the program to continue in an unfit state. Please put a button on your form and in the click event throw new Exception(""); and then the message box should display.
So I have a huge program and decided I should make one of the methods run in a separate thread. So I put the method in a separate class, an activated it on my form. It seemed to worked just how I wanted it to until it got to part where it gave me this error:
SendKeys cannot run inside this application because the application
is not handling Windows messages. Either change the application to
handle messages, or use the SendKeys.SendWait method.
I tried looking for the answer online. I think I saw something about how SendKeys only works in a Form or something.
Can anyone tell me a way to simulate a keystroke without using SendKeys, OR a way to get SendKeys to work in a different, non-form thread?
Your console application needs a message loop. This is done through the Application class. You will need to call Application.Run(ApplicationContext).
class MyApplicationContext : ApplicationContext
{
[STAThread]
static void Main(string[] args)
{
// Create the MyApplicationContext, that derives from ApplicationContext,
// that manages when the application should exit.
MyApplicationContext context = new MyApplicationContext();
// Run the application with the specific context. It will exit when
// the task completes and calls Exit().
Application.Run(context);
}
Task backgroundTask;
// This is the constructor of the ApplicationContext, we do not want to
// block here.
private MyApplicationContext()
{
backgroundTask = Task.Factory.StartNew(BackgroundTask);
backgroundTask.ContinueWith(TaskComplete);
}
// This will allow the Application.Run(context) in the main function to
// unblock.
private void TaskComplete(Task src)
{
this.ExitThread();
}
//Perform your actual work here.
private void BackgroundTask()
{
//Stuff
SendKeys.Send("{RIGHT}");
//More stuff here
}
}
I Know this not an answer, but this how i used to do using ActiveX and Script
Set ws = CreateObject("WScript.Shell")
str = "Hi there... ~ Dont click your mouse while i am typing." & _
" ~~This is a send key example, using which you can send your keystrokes"
ws.Run("notepad.exe")
WScript.Sleep(1000)
For c=1 To Len(str)
WScript.Sleep(100) 'Increase the value for longer delay
ws.SendKeys Mid(str,c,1)
Next
Save this code as file.vbs and double click to execute in windows machine.
I was looking at some other question and then I had this question:
Who calls the Main() method?
If I want to exit/quit the application (when I get some exception in the Main() method itself), is it a good idea to use return; statement from the catch block in the Main() method?
Please note that I am not starting any thread in the Main() method explicitly. When we start the application, are there any threads start automatically in the background?
Application.Exit() does not guarantee an application to quit
(EDITED point) Environment.Exit() is another option.
Can use of return; statement to quit the application be a good idea? If no, what are those (subtle) things because of which it may not be good idea?
In comparison, which is the best approach to quit?
If your application is going to terminate due to an unexpected error, you may wish to use Environment.FailFast which will "crash" the application with a specified message that gets written to the event log and offers the user an opportunity to submit the crash data to Microsoft. As the developer, Microsoft can make the crash data available to you.
But if you just want to return an error condition to the calling process (such as in the case of a console application) you can modify your Main method signature to return an int and then return a non-zero value which by convention implies an error condition.
Who calls the Main() method?
CLR
If I want to exit/quit the application (when I get some exception in the Main() method itself), is it a good idea to use return; statement from the catch block in the Main() method?
Yes, with the caveat that you don't have other foreground threads.
You could put a giant try/catch block around all of Main(), or set up the AppDomain.UnhandledException event. This is not directly relevant, but look at Environment.Exit, you're right about Application.Exit since all it does is close all windows. Environment.Exit also allows you to specify a value other than 0 as the exit status (errorlevel) if you have declared void Main() instead of int Main().
Application.Exit() is for Form based applications. The best way to handle an exception in Main:
static void Main(string[] args) {
try {
// code here
} catch {
// do any clean up and return
// optionally specify an exit code
Environment.Exit(1 /* or any number other than zero since this is an error condition */);
}
}
1) The OS basically calls the Main method. It start address is specified PE header of the .exe (if I remember correctly).
2) Yes use the return statement from the catch block.
I understand that there are a few ways to exit an application, such as Application.Exit(), Application.ExitThread(), Environment.Exit(), etc.
I have an external "commons" library, and I'm trying to create a generic FailIf method that logs the failure to the logs, does this and that and this and that, then finally exits the application... here's a short version of it.
public static void FailIf(Boolean fail, String message, Int32 exitCode = 1)
{
if (String.IsNullOrEmpty(message))
throw new ArgumentNullException("message");
if (fail)
{
//Do whatever I need to do
//Currently Environment.Exit(exitCode)
Environment.Exit(exitCode);
}
}
I have read that using Environment.Exit isn't the best way to handle things when it comes to WinForm apps, and also when working with WPF apps and Silverlight there are different ways to exit... My question is really:
What do I put to exit gracefully to cover all application types?
Read this about the difference between using Environment and Application :
Application.Exit Vs Environment.Exit
There's an example of what you want to do in the bottom of that page:
if (System.Windows.Forms.Application.MessageLoop)
{
// Use this since we are a WinForms app
System.Windows.Forms.Application.Exit();
}
else
{
// Use this since we are a console app
System.Environment.Exit(1);
}
If it's just an abort, use Environment.Exit(). If it's something very critical (that can't handle any sort of cleanup), use Environment.FailFast().
I would recommend using basic exception handling, so instead of System.Environment.Exit(1) throw new ApplicationException(message) which bubbles up the exception to the main method, in your case something like this:
try{
Application.Run(new MyForm());
}
catch(ApplicationException){
// do custom cleanup/reporting
}
Just make sure you throw the exception from the main thread, else invoke on it before throwing ofcourse
Can you exit an application before the constructor is finished and the main form is loaded?
At startup, I have a loading screen that displays before the main form is loaded. The loading screen is displayed from the constructor before the constructor has finished.
I do something similar with an exit screen by using a variable between the main form and the exit screen. I have an application exit in the main form if the exit screen returns true.
Finally, should all the thread/class/loading/program setup be done in the main constructor or am I doing it wrong?
Update:
I mean after the program.cs and in the static main
namespace app
{
public partial class app1 : Form
{
public app1()
{
InitializeComponent();
// open loading screen
// initialize vars
// create objects
}
// form opens when app1() finishes
Is app1() the right place to initialize everything?
If I try to send a "close" message back from the loading screen before app1() is finished, it doesn't work - the process still runs even though nothing is open.
I've found that if I try to kill my application from the main form constructor when I still have the splash screen showing on a different thread (which looks similar to what you are doing), that Application.Exit() does not work, but Environment.Exit(-1) does.
try this,
public partial class MyForm : Form
{
public MyForm()
{
if (MyFunc())
{
this.Shown += new EventHandler(MyForm_CloseOnStart);
}
}
private void MyForm_CloseOnStart(object sender, EventArgs e)
{
this.Close();
}
}
it will work well...
Which main constructor of which class?
Are you talking about the static method Main that has a default location in the Program class?
You use that method to do initialization that needs to occur before you open any windows on screen.
Obviously, if you need to use a loading screen, you will probably want to move some code somewhere else, as you need a message loop around forms, and the message loop will block until your form closes.
If you return from the Main method before you open any form, then no form will be shown obviously.
Having said all that, I feel your question is a bit vague and I'm pretty sure I didn't understand exactly what it is that you're asking.
First and foremost, Main is not a constructor, it's just a static method.
When main thread ends:
background threads are "killed/abandoned"
foreground threads (the default when creating threads) are waited till they finish.
you can break constructor only via throwing an exception. To do that surreptitiously, throw you own specific exception.
class ConstructorAbortedException : Exception { }
class Foo
{
public Foo()
{
if(goesWrong)
{
throw new ConstructorAbortedException();
}
}
}
void Bar()
{
try
{
Foo f = new Foo();
}
catch(ConstructorAbortedException)
{
//..
}
}
As jontsnz answered, the code with
Environment.Exit(-1)
works fine in the constructor, but this causes the application to throw an "Application Hang" event, which can be seen as an error in the Windows Event Viewer. Using
Environment.Exit(0)
exits without registering an error though, so I prefer that one.