I have made a custom messagebox (discussed here) that shows localized quit prompt.
protected override void OnBackKeyPress(CancelEventArgs e)
{
//some conditions
e.Cancel = true;
string quitText = DeviceWrapper.Localize("QUIT_TEXT");
string quitCaption = DeviceWrapper.Localize("QUIT_CAPTION");
string quitOk = DeviceWrapper.Localize("DISMISS");
string quitCancel = DeviceWrapper.Localize("MESSAGEBOX_CANCEL");
IAsyncResult asyncResult = Guide.BeginShowMessageBox(
quitCaption, quitText, new List<string> { quitOk, quitCancel },
0, MessageBoxIcon.Error, null, null);
asyncResult.AsyncWaitHandle.WaitOne();
int? result = Guide.EndShowMessageBox(asyncResult);
if (result.HasValue && result.Value == 0)
e.Cancel = false;
//some more features
}
It works fine, but it does crash when it's run as stand-alone (without Visual Studio) after few seconds if user doesn't press anything.
I tried reproducing the same crash with phone attached using Release and Debug from Visual Studion, but it's as stable as it can possibly be. It only crashes when app is run from the phone itself.
How do I find the issue? What could be the reason for it? As far as I can tell, I cannot access crash logs on the device.
There should be something with the messagebox logic perhaps?
UPD
Few notes on your suggestion, Neil:
first of all VS warns me "the async method lacks await" stuff.
secondly I am not sure how to return info to "BackKeyPressed" that e.Cancel should equal "false". This should be the safe way to quit an app AFAIK.
And wouldn't we quit the "OnBackKeyPress" method if we run async method from it? That would mean that we can't let it know about our decision (e.Cancel = false).
Or if we don't quit the "OnBackKeyPress" then it could mean this event will stay for too long and the whole purpose is lost - app will be killed, right?
UPD2:
It seems that I am between Scylla and Charybdis: either I show a messagebox and experience crashes in runtime which is discouraged by guidelines, or I don't show a messagebox at all.
I've tried both native MessageBox and my implementation of custom messagebox with localized "Ok" and "Cancel" buttons. They behave the same: either they are synchronous and hang OnBackKeyPress, or they are async and OnBackKeyPress exits before we can let it know about user's decision.
Final decision
Apparently the guidelines state that we shouldn't ask user's confirmation at all.
Since there is no viable way to implement a working quit confirmation messagebox without crashing I've decided to not show it at all.
If you block or delay certain page events for too long, the OS will kill your app as it assumes its crashed...
OnNavigatedTo
OnNavigatedFrom
OnBackKeyPress
In your case, I would recommend putting the custom MessageBox in it's own method, then calling it on the UI thread.
private void customMessageBox()
{
// custom message box code
// use NavigationService.GoBack() if you need to exit
}
protected override void OnBackKeyPress(CancelEventArgs e)
{
e.Cancel = true;
Deployment.Current.Dispatcher.BeginInvoke(() => customMessageBox() );
}
I also found another answer which accomplishes the thing by making the method Async.
Debugger attached (or not)
When the Visual Studio debugger is attached, the OS does not warn you of certain errors such as high memory usage, page init delays, etc - so it is very important to test your app without the Visual Studio debugger attached either on device or in the emulator (Debug or Release)
Windows Phone 8.1
Be aware that the handling of the Back button has changed in WP8.1 XAML/WinRT apps. It might be something to consider if you're upgrading the app project in future.
I implemented this in my project and it worked. Try it!
protected override void OnBackKeyPress(CancelEventArgs e)
{
base.OnBackKeyPress(e);
e.Cancel = true;
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
MessageBoxResult result = MessageBox.Show("Hello", "Msg Box", MessageBoxButton.OKCancel);
if (result == MessageBoxResult.OK)
{
//Do something
}
else
{
//Do something
}
});
}
Related
Note: I have only tested this using the emulator, and pushing toasts using the built in functionality. I am assuming this isn't an emulator issue.
I followed this guide in order to intercept push toast notifications while the app is running. However, I only want to suppress the toast notification when the app is in the foreground. It should still display when another app is in the foreground. So I wrote the following handler in App.xaml.cs (and subscribed to the PushNotificationReceived event):
private async void OnPushNotification(PushNotificationChannel sender, PushNotificationReceivedEventArgs e)
{
string msg = "";
if (e.NotificationType == PushNotificationType.Toast)
{
await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
if (Window.Current.Visible)
{
msg += " Toast canceled.";
e.ToastNotification.SuppressPopup = true;
}
});
if (true) // actually determines if it's a certain type of toast
{
await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
{
ConfirmationContentDialog confirmationDialog = new ConfirmationContentDialog();
confirmationDialog.SetMessage("Please confirm that you like turtles." + msg);
await confirmationDialog.ShowAsync();
});
}
}
}
So this works, in the sense that I only see the "toast canceled" message when the app was in the foreground when receiving the push notification. When I'm on the start screen or somewhere else I always get the toast. This is good. However, when the app is in the foreground, sometimes (usually after sending the second push) the toast shows up anyway (even though "Toast canceled" displays). But sometimes it doesn't. It's rather inconsistent.
This is leading me to believe that due to the await, sometimes the toast gets through before the code gets run on the UI thread to check whether the app is visible or not. However, I can't access Window.Current.Visible from here without using the dispatcher. I even tried CoreApplication.MainView.CoreWindow.Visible but that gives me "interface marshalled for different thread etc" exception. Speaking of which, I don't understand how CoreApplication.MainView.CoreWindow.Dispatcher can be called from anywhere but CoreApplication.MainView.CoreWindow.Visible not? How does that even work.
Anyway, how do I fix this? I would like to keep this within App.xaml.cs because I have a number of pages in this app, but this content dialog needs to be shown no matter which page the user is on, and without the user being redirected to a different page. However, I am of course open for new suggestions.
I fixed this as per Kai Brummund's suggestion by using a simple boolean toggle in the App class, and subscribing to the VisibilityChanged event like so:
private bool APP_VISIBLE = true;
protected override async void OnLaunched(LaunchActivatedEventArgs e)
{
// Stuff put here by Visual Studio
Window.Current.VisibilityChanged += OnVisibilityChanged;
Window.Current.Activate();
}
private void OnVisibilityChanged(object sender, VisibilityChangedEventArgs e)
{
if (e.Visible)
APP_VISIBLE = true;
else
APP_VISIBLE = false;
}
That way I can use APP_VISIBLE to suppress the popup without having to use the dispatcher and the toast is suppressed immediately.
Can we work together to come up with something that works for control-c, control-break, log off, window X button pressed, etc?
Here is what I have so far:
class Program
{
private static ConsoleEventHandlerDelegate consoleHandler;
delegate bool ConsoleEventHandlerDelegate(CtrlTypes eventCode);
static void Main(string[] args)
{
consoleHandler = new ConsoleEventHandlerDelegate(ConsoleCtrlCheck);
SetConsoleCtrlHandler(consoleHandler, true);
System.Diagnostics.Process.GetCurrentProcess().Exited
+= delegate(object sender, EventArgs e)
{
GeneralManager.Stop();
};
Console.CancelKeyPress += delegate(object sender,
ConsoleCancelEventArgs e)
{
e.Cancel = false;
GeneralManager.Stop();
};
GeneralManager.Start();
}
private static bool ConsoleCtrlCheck(CtrlTypes ctrlType)
{
switch (ctrlType)
{
case CtrlTypes.CTRL_C_EVENT:
Console.WriteLine("CTRL+C received!");
GeneralManager.Stop();
break;
case CtrlTypes.CTRL_BREAK_EVENT:
isclosing = true;
Console.WriteLine("CTRL+BREAK received!");
GeneralManager.Stop();
break;
case CtrlTypes.CTRL_CLOSE_EVENT:
Console.WriteLine("Program being closed!");
GeneralManager.Stop();
break;
case CtrlTypes.CTRL_LOGOFF_EVENT:
case CtrlTypes.CTRL_SHUTDOWN_EVENT:
Console.WriteLine("User is logging off!");
GeneralManager.Stop();
break;
}
return true;
}
#region unmanaged
[DllImport("kernel32.dll")]
static extern bool SetConsoleCtrlHandler(ConsoleEventHandlerDelegate
handlerProc, 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
}
#endregion
}
Two problems:
In the Managed Control-Break handler, if we set e.Cancel = true it fails with an exception for .Net4. This is noted in the MSDN article with no work-around: http://msdn.microsoft.com/en-us/library/system.consolecanceleventargs.cancel.aspx
I don't know how to cancel the close in the ConsoleCtrlCheck. I get a second or two to do some cleanup, but I'd rather cancel and make sure it all gets done properly.
UPDATE:
Thanks for the replies. Upvoted both. Will wait to see if anyone can come up with a reply that directly solves what I asked for, otherwise will accept one of the "use NT services" answers.
I need to wait for pending user requests to complete, disconnect them cleanly, run a few queries on the database to reflect the change(s) in state and so forth. It's a TCP server.
Then don't run it as a Console or any other kind of Client app.
Just run it as a Windows (NT) Service and the only events you'll have to worry about are Power loss and a stop signal.
Use a UPS and make sure you can close in a reasonable timespan.
I have not tried to do this kind of thing with a console app, but you may do better with a Windows Forms (or WCF app). They will give you a FormClosing event which is cancellable. Alternately, use a Windows Service if you are writing a network service, it provides an interface to cleanly stop your application.
If you are really keen on a console app, perhaps a try {} finally {} clause around all your code or something more exotic like a critical finaliser may allow you to run clean up code. But this is really not the right tool for the job.
And there are cases which you cannot prevent you app being closed, eg: power failure, or Task Manager kill command (and if an app didn't close via the X, Task Manager is the first tool I'd reach for).
So, code your service application such that all client requests are logged to a transaction log (like SQL server does). If you are unexpectedly interrupted (by whatever circumstance) anything which has happened up until that point is in the log. When your service next starts, replay that log.
One of your things to log will be "I was shutdown cleanly at time T". If you restart and don't find that item at the end of your log, you know something went wrong, and you can take whatever action is required.
If you need to know what your service is doing, use one of the many logging frameworks to pipe events to a second app, which just displays activity.
I spent couple hours looking at this and as I don't have time now to build a working code; as while it's probably short, getting it right would take a while. I'll just give you link to the various stuff that's needed to get this done:
http://pastebin.com/EzX3ezrf
Summarizing the lessons from the code in the paste:
Need a message pump to handle some/all of WM_QUERYENDSESSION, WM_ENDSESSION, CTRL_SHUTDOWN_EVENT (in c# SystemEvents.SessionEnding may cover some/all of these)
Easiest way to get a message pump is to make it a hidden form/window app, but I recall it's possible to build as a console app and add a message pump also. I didn't include that code in the paste though.
"If an application must block a potential system shutdown, it can call the ShutdownBlockReasonCreate function"
As AllocConsole is used to create the console, you need to use SetConsoleCtrlHandler and use ExitThread(1) in the handler. This is a "hack" that kills off the thread that would close the console otherwise. It's used in FarManager. see interf.cpp for example
You need to also initialize and clean up the console when using AllocConsole.
Pressing CTRL+C is reported to mess up the input. I'm not sure if FarManager is handling this scenario. There's some code in the CTRL_BREAK_EVENT handler in interf.cpp that I'm not sure what it does.
FarManager also handles WM_POWERBROADCAST, probably to do with suspending
If all that isn't enough (should be), you can also add the console into another process and IPC your messages to it like shown here. Why does closing a console that was started with AllocConsole cause my whole application to exit? Can I change this behavior?
RMTool can be used to simulate logoff/shutdown messages for testing: http://download.microsoft.com/download/d/2/5/d2522ce4-a441-459d-8302-be8f3321823c/LogoToolsv1.0.msi
MSDN has some C# code also at microsoft.win32.systemevents.sessionending.aspx
and microsoft.win32.systemevents.aspx (hidden form example)
The mischel.com/pubs/consoledotnet/consoledotnet.zip has a sample winTest project with AllocConsole being used and some of the events handled.
How to display a message box in windows 8 apps using c# like calling MessageBox.Show() in windows phone 7?
MessageDialog msgDialog = new MessageDialog("Your message", "Your title");
//OK Button
UICommand okBtn = new UICommand("OK");
okBtn.Invoked = OkBtnClick;
msgDialog.Commands.Add(okBtn);
//Cancel Button
UICommand cancelBtn = new UICommand("Cancel");
cancelBtn.Invoked = CancelBtnClick;
msgDialog.Commands.Add(cancelBtn);
//Show message
msgDialog.ShowAsync();
And your call backs
private void CancelBtnClick(IUICommand command)
{
}
private void OkBtnClick(IUICommand command)
{
}
P.S. You can follow this tutorial for more.
The MessageDialog class should fit your needs.
My simpler way, for confirmation type message boxes:
var dlg = new MessageDialog("Are you sure?");
dlg.Commands.Add(new UICommand("Yes", null, "YES"));
dlg.Commands.Add(new UICommand("No", null, "NO"));
var op = await dlg.ShowAsync();
if ((string)op.Id == "YES")
{
//Do something
}
For simpler way, Just to display the message text and OK button. Use Windows.UI.Popups namespace. Create a method messagebox() that method should be
using Windows.UI.Popups;
protected async void messageBox(string msg)
{
var msgDlg = new Windows.UI.Popups.MessageDialog(msg);
msgDlg.DefaultCommandIndex = 1;
await msgDlg.ShowAsync();
}
Then call this method in your code like
messageBox("Unexpected error held");
Additional tidbit:
It appears in a modern Windows App a MessageDialog will not show prior to your app making its Window.Current.Active() call, which usually happens in the app class' OnLaunched() method. If you're trying to use MessageDialog to display something like a start-up exception, that's important to keep in mind.
My testing indicates MessageDialog.ShowAsync() may actually await but without the dialog being shown if Window.Current.Active() hasn't been called yet, so from a code execution standpoint it'll look like everything is working but yet no dialog is displayed.
If the goal is to display an exception during start-up, I can think of two options (there may be more).
Capture the exception information and then wait to display it until after Window.Current.Activate(). This can work if the exception is such that the application can recover from it and continue with start-up. For example, if there is an error restoring saved state information the app might want to report that to the user but then continue to start-up as if there was no saved state.
If the situation is such that the app is throwing up its hands and intending to terminate, but wants to let the user know what happened, then another solution might be to have a separate dedicated code block/method that plugs a new clean frame into Windows.Current.Content, activates it using Windows.Current.Activate(), and then invokes MessageDialog.ShowAsync(). I haven't experimented with this approach so I'm not sure if other conditions also need to be met like possibly loading a page into the frame.
use for page like:
private async void AppBarButton_Click(object sender, RoutedEventArgs e)
{
Windows.UI.Popups.MessageDialog a = new Windows.UI.Popups.MessageDialog("hello this is awth");
await a.ShowAsync();
}
Or not!
I have a fairly simple application timer program. The program will launch a user selected (from file dialog) executable and then terminate the process after the user specified number of minutes. During testing I found that a crash occurs when I call the Process.Kill() method and the application is minimized to the system tray.
The executable in question is Frap.exe which I use frequently and is the reason I wrote the app timer in the first place. I always minimize fraps to the tray, and this is when the crash occurs.
My use of Kill() is straight forward enough...
while (true)
{
//keep checking if timer expired or app closed externally (ie. by user)
if (dtEndTime <= DateTime.Now || p.HasExited)
{
if (!p.HasExited)
p.Kill();
break;
}
System.Threading.Thread.Sleep(500);
}
In searching for alternatives methods to close an external application programmatically, I found only Close() and Kill() (CloseMainWindow is not helpful to me at all). I tried using Close(), which works providing the application is minimized the tray. If the app is minimized, Close() doesn't cause a crash but the app remains open and active.
One thing I noticed in a few posts posts regarding closing external applications was the comment: "Personally I'd try to find a more graceful way of shutting it down though." made in THIS thread found here at stack flow (no offense to John). Thing is, I ran across comments like that on a few sites, with no attempt at what a graceful or elegant (or crash-free!!) method might be.
Any suggestions?
The crash experienced is not consistant and I've little to offer as to details. I am unable to debug using VS2008 as I get message - cant debug crashing application (or something similar), and depending on what other programs I have running at the time, when the Kill() is called some of them also crash (also programs only running in the tray) so I'm thinking this is some sort of problem specifically related to the system tray.
Is it possible that your code is being executed in a way such that the Kill() statement could sometimes be called twice? In the docs for Process.Kill(), it says that the Kill executes asynchronously. So, when you call Kill(), execution continues on your main thread. Further, the docs state that Kill will throw a Win32Exception if you call it on an app that is already in the process of closing. The docs state that you can use WaitForExit() to wait for the process to exit. What happens if you put a call to WaitForExit() immediately following the call to Kill(). The loop looks ok (with the break statement). Is it possible that you have code entering that loop twice?
If that's not the problem, maybe there is another way to catch that exception:
Try hooking the AppDomain.CurrentDomain.UnhandledException event
(currentDomain is a static member)
The problem is that Kill runs asynchronously, so if it's throwing an exception, it's occurring on a different thread. That's why your exception handler doesn't catch it. Further (I think) that an unhandled async exception (which is what I believe you have) will cause an immediate unload of your application (which is what is happening).
Edit: Example code for hooking the UnhandledExceptionEvent
Here is a simple console application that demonstrates the use of AppDomain.UnhandledException:
using System;
public class MyClass
{
public static void Main()
{
System.AppDomain.CurrentDomain.UnhandledException += MyExceptionHandler;
System.Threading.ThreadPool.QueueUserWorkItem(DoWork);
Console.ReadLine();
}
private static void DoWork(object state)
{
throw new ApplicationException("Test");
}
private static void MyExceptionHandler(object sender, System.UnhandledExceptionEventArgs e)
{
// get the message
System.Exception exception = e.ExceptionObject as System.Exception;
Console.WriteLine("Unhandled Exception Detected");
if(exception != null)
Console.WriteLine("Message: {0}", exception.Message);
// for this console app, hold the window open until I press enter
Console.ReadLine();
}
}
My first thought is to put a try/catch block around the Kill() call and log the exception you get, if there is one. It might give you a clue what's wrong. Something like:
try
{
if(!p.HasExited)
{
p.Kill();
}
break;
}
catch(Exception ex)
{
System.Diagnostics.Trace.WriteLine(String.Format("Could not kill process {0}, exception {1}", p.ToString(), ex.ToString()));
}
I dont think I should claim this to be "THE ANSWER" but its a decent 'work around'. Adding the following to lines of code...
p.WaitForInputIdle(10000);
am.hWnd = p.MainWindowHandle;
...stopped the crashing issue. These lines were placed immediately after the Process.Start() statement. Both lines are required and in using them I opened the door to a few other questions that I will be investigating over the next few days. The first line is just an up-to 10 second wait for the started process to go 'idle' (ie. finish starting). am.hWnd is a property in my AppManagement class of type IntPtr and this is the only usage of both sides of the assignment. For lack of better explaination, these two lines are analguous to a debouncing method.
I modified the while loop only slightly to allow for a call to CloseMainWindow() which seems to be the better route to take - though if it fails I then Kill() the app:
while (true)
{
//keep checking if timer expired or app closed externally (ie. by user)
if (dtEndTime <= DateTime.Now || p.HasExited) {
try {
if (!p.HasExited) // if the app hasn't already exitted...
{
if (!p.CloseMainWindow()) // did message get sent?
{
if (!p.HasExited) //has app closed yet?
{
p.Kill(); // force app to exit
p.WaitForExit(2000); // a few moments for app to shut down
}
}
p.Close(); // free resources
}
}
catch { // blah blah }
break;
}
System.Threading.Thread.Sleep(500);
}
My initial intention for getting the MainWindowHandle was to maximize/restore an app if minimized and I might implement that in the near future. I decided to see if other programs that run like Fraps (ie, a UI but mostly run in the system tray (like messanger services such as Yahoo et al.)). I tested with XFire and nothing I could do would return a value for the MainWindowHandle. Anyways, this is a serperate issue but one I found interesting.
PS. A bit of credit to JMarsch as it was his suggestion RE: Win32Exception that actually lead me to finding this work around - as unlikely as it seems it true.
I have an application that has been getting strange errors when canceling out of a dialog box. The application can't continue if the box is cancelled out of, so it exits, but it is not working for some reason, and thus it keeps running and crashes.
I debugged this problem, and somehow the application runs right past the Application.Exit call. I'm running in Debug mode, and this is relevant because of a small amount of code that depends on the RELEASE variable being defined. Here is my app exit code. I have traced the code and it entered the ExitApp method, and keeps on going, returning control to the caller and eventually crashing.
This is an application which provides reports over a remote desktop connection, so that's why the exit code is a bit weird. Its trying to terminate the remote session, but only when running under release because I don't want to shut down my dev machine for every test run.
private void ExitApp()
{
HardTerminalExit();
Application.Exit();
}
// When in Debug mode running on a development computer, this will not run to avoid shutting down the dev computer
// When in release mode the Remote Connection or other computer this is run on will be shut down.
[Conditional("RELEASE")]
private void HardTerminalExit()
{
WTSLogoffSession(WTS_CURRENT_SERVER_HANDLE, WTS_CURRENT_SESSION, false);
}
I've run a debugger right past the Application.Exit line and nothing happens, then control returns to the caller after I step past that line.
What's going on? This is a Windows Forms application.
This is an article which expands on the same train of thought you are going through: http://www.dev102.com/2008/06/24/how-do-you-exit-your-net-application/
Basically:
Environment.Exit - From MSDN: Terminates this process and gives the
underlying operating system the
specified exit code. This is the code
to call when you are using console
application.
Application.Exit - From MSDN: Informs all message pumps that they
must terminate, and then closes all
application windows after the messages
have been processed. This is the code
to use if you are have called
Application.Run (WinForms
applications), this method stops all
running message loops on all threads
and closes all windows of the
application. There are some more
issues about this method, read about
it in the MSDN page.
Another discussion of this: Link
This article points out a good tip:
You can determine if System.Windows.Forms.Application.Run has been called by checking the System.Windows.Forms.Application.MessageLoop property. If true, then Run has been called and you can assume that a WinForms application is executing as follows.
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);
}
Having had this problem recently (that Application.Exit was failing to correctly terminate message pumps for win-forms with Application.Run(new Form())), I discovered that if you are spawning new threads or starting background workers within the constructor, this will prevent Application.Exit from running.
Move all 'RunWorkerAsync' calls from the constructor to a form Load method:
public Form()
{
this.Worker.RunWorkerAsync();
}
Move to:
public void Form_Load(object sender, EventArgs e)
{
this.Worker.RunWorkerAsync();
}
Try Environment.Exit(exitCode).
I have went though this situation in many cases I use Thread.CurrentThread.Abort()
and the process is closed. It seems that Application.Exit isn't hooking up properly with current thread.
Also ensure any threads running in your application have the IsBackground property set to true. Non-background threads will easily block the application from exiting.
Make sure you have no Console.ReadLine(); in your app and Environment.Exit(1); will work and close your app.
I created the following that will exit the app anywhere. You don't have to worry if the Form is running or not, the test determines that and calls appropriate Exit.
public void exit(int exitCode)
{
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(exitCode);
}
} //* end exit()
Is this application run (in the Main method) using Application.Run()? Otherwise, Application.Exit() won't work.
If you wrote your own Main method and you want to stop the application, you can only stop by returning from the Main method (or killing the process).
Try this :
in Program.cs file :
after Application.Run(new form());
add Application.Exit();
private void frmLogin_FormClosing(object sender, FormClosingEventArgs e)
{
if (e.CloseReason == CloseReason.UserClosing)
{
DialogResult result = MessageBox.Show("Do you really want to exit?", "Dialog Title", MessageBoxButtons.YesNo);
if (result == DialogResult.Yes)
{
Environment.Exit(0);
}
else
{
e.Cancel = true;
}
}
else
{
e.Cancel = true;
}
}