I've got code in a button click like so:
try
{
Cursor = Cursors.WaitCursor;
GenerateReports();
}
finally
{
Cursor = Cursors.Default;
GC.Collect();
GenPacketBtn.Enabled = true;
}
Nowhere else but in the finally block is the cursor set back to default, yet it does "get tired" and revert to its default state for some reason. Why would this be, and how can I assure that it will not stop "waiting" until the big daddy of all the processes (GenerateReports()) has completed?
Use instead Control.UseWaitCursor = true, this does not time out.
If an expensive operation is being executed then Windows will take over and it will change the Cursor.WaitCursor to whatever it deems necessary. So with Cursor.WaitCursor it will either due to a timeout (but not fully sure about this) or because of Windows simply claiming ownership of the cursor without regards to its previous state. We also had a similar situation with where the Cursor did not behave as expected when performing an expensive task that involved called 3rd party PDF converters but we did not investigate more on the nature of the issue as it was not a priority.
After a bit of reading, it turned out setting the Hourglass cursor is a bit more complicated than it seems:
.net WaitCursor: how hard can it be to show an hourglass?
Also as a side note: you should use Cursor.Current = Cursors.WaitCursor as this forces the cursor to change to busy immediately , more details at : https://stackoverflow.com/a/302865/1463733
Related
I have a notifyIcon that I want to use in WinForms app, to show balloonTips to the user on certain events / issues.
It worked fine, and then I made changes to the application that I cannot see could have influenced the workings of the BalloonTip, but now the balloonTip does not pop up.
The NotifyIcon.Icon appears in the task bar and if you keep looking at the taskbar you briefly see the icon in there (like it flashes), but the actual BalloonTipText and title don't appear
here is the code I am using.
private void SetBalloonTip(String BalloonMessage, bool isError)
{
AppTips.Icon = SystemIcons.Exclamation;
AppTips.BalloonTipTitle = "Attention";
AppTips.BalloonTipText = BalloonMessage;
AppTips.Visible = true;
if (isError)
{
AppTips.BalloonTipIcon = ToolTipIcon.Error;
}
else
{
AppTips.BalloonTipIcon = ToolTipIcon.Info;
}
AppTips.ShowBalloonTip(300000);
}
whenever I want to show a BalloonTip Message I call the above method.
I have played around with the timeout as I read on other threads it could be a possible issue, but increasing nor decreasing it made any difference.
I also added Thread.Sleep before calling the SetBalloonTip method, thinking that maybe the method is called too quickly in succession, but that did not make a difference either.
There are limits on the length of BalloonTipText and BalloonTipTitle on some versions of Windows (but not on others, Windows 10 supports any length for example):
NotifyIcon.BalloonTipText max length of 255 characters
NotifyIcon.BalloonTipTitle max length of 63 characters
Make sure your text does not violate these limitations to have it display properly.
I'm writing a UWP file manager and I've come to a problem with drag&drop. I'm probably just beating my head against the wall since this is obviously another bug in the platform, but this time I can't find any workaround.
When dragging files and dropping them into File Explorer everything is fine. I fill up the DataPackage and listen to the OperationCompleted event, which happens when the files finished moving to another folder. When I drop them into another view within my app, I can call DataPackageView.ReportOperationCompleted, which does work (sort of). The problem is, it's also called AUTOMATICALLY at the same time the drop happens, even though the operation is not finished yet - and I can't do anything about it. The call stack is completely empty when I hit a breakpoint in the event handler.
On top of that, when I actually look into the arguments of OperationCompleted, the Operation in OperationCompletedEventArgs is ALWAYS None! It's None when File Explorer does the job, it's None when it gets called automatically, it's None when I call it manually, NO MATTER WHAT argument I pass in. Any explanation for this, Microsoft? I'm tired of fixing your bugs, especially when I can't actually do it since the platform is so limited.
one other "curiosity" with drag&dropping files in UWP is that if you get files dropped in your app and a requested operation set to move - you can't actually move them - the files are read-only. Try explaining that to the user.
Not sure how you to move files. In general, you should use StorageFile.CopyAsync method. You could use try/catch block to wrap this operation like the follwing:
try
{
var operation = appFile.File.CopyAsync(ApplicationData.Current.LocalFolder, appFile.File.DisplayName, NameCollisionOption.GenerateUniqueName);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine("exception msg: "+ex.Message);
}
Then if the file is readonly, you will get exception message, you should use this message to notify user.
I wanted a notification for when the operation is actually done.
You should implemente AsyncOperationWithProgressCompletedHandler for your async operation, then you will get notification when it's completed.
operation.Completed = (tresult,tprogress) => { System.Diagnostics.Debug.WriteLine("progress msg: "+tprogress); };
I am building a document management system currently and I was trying to change the cursor to a "waiting" cursor while the document is loading, pretty standard.
As per the MSDN documentation, I am using the following code:
System.Windows.Input.Mouse.OverrideCursor = System.Windows.Input.Cursors.Wait;
try
{
newPage.LoadForm(data);
}
finally
{
System.Windows.Input.Mouse.OverrideCursor = null;
}
The problem is, after LoadForm is finished, the cursor doesn't return to its normal state. I have debugged the program and the "null" line is being run so I have no idea what the problem is.
Any ideas?
If this is a long-running operation, you might consider moving this whole code to a Task (though in that case you'd have to dispatch the changes to the OverrideCursor property back to the main thread).
I tested this quickly with a Sleep simulating a long-running application and it seemed to work fine (I put this code in the window's constructor in an empty WPF application for testing).
Task.Factory.StartNew(() =>
{
Application.Current.Dispatcher.Invoke(() =>
System.Windows.Input.Mouse.OverrideCursor = System.Windows.Input.Cursors.Wait);
try
{
Thread.Sleep(5000);
}
finally
{
Application.Current.Dispatcher.Invoke(() =>
System.Windows.Input.Mouse.OverrideCursor = null);
}
});
WORKAROUND
You must set it to the cursor type you want instead of setting it to null. So, instead of setting it to null, set it to Arrow (I assume that is what you would want in Normal state).
So in finally block replace your code with this:
System.Windows.Input.Mouse.OverrideCursor = System.Windows.Input.Cursors.Arrow;
EDIT 1:
Try setting the Cursor to null at the end of try block if in case you do not want to use the workaround.
I am making an application in which i want to execute some database queries just before system get shut down. I am using this code -
static void SystemEvents_SessionEnding(object sender, SessionEndingEventArgs e)
{
e.Cancel = true;
MessageBox.Show("Shut down canceled");
}
I did execute this application and tried to shut down the system and this code captured the shut down event also but the problem is after showing message box it shows this screen also- [I can't post the image as i don't have 10 points.]
it shows the name of my application that stopped the system to get shut down and it also provide "Force Shut down button" , i dont want this screen to be displayed as user can forcefully shut down the system before completion of execution of my queries.
Need expert advices on this, thanks a lot in advance.
The Short Reliable Answer:
On any recent Windows version, you can try to cancel shutdown but Windows may decide to ignore you; this is sadly by design. If you absolutely have to finish operations before your process is terminated, the right place to do this is in the SessionEnded handler. If you have tasks that must complete before your process terminates, you must not return from your SessionEnded handler until all your work is done (so your queries, etc. have finished.)
So instead of (or as well as, if you prefer) handling SessionEnding, handle SessionEnded and do you work there:
static void SystemEvents_SessionEnded(object sender, SessionEndedEventArgs e)
{
WaitForQueriesToFinishOrSaveState(); // new code
}
How you implement that waiting will depend on your application; if you need to run queries afresh you may be able to do them therein, or you may need to Thread.Join() or otherwise wait for background tasks to complete; but it must be a blocking wait (so you need to not return from the function until you're done).
Since you can't absolutely stop shutdown, there's perhaps little point in attempting the cancellation in this case, so I'd recommend not setting e.Cancel in SessionEnding at all. On older Windows versions this was more meaningful, very much less so now unfortunately.
It's also recommended by the API docs not to do any significant work in SessionEnding (including message boxes), but to set any flags you need to return immediately and then do the work in SessionEnded. (Unproven aside: I'm suspicious that if you don't return quickly enough, this may hasten the appearance of the "programs are misbehaving, do you want to kill them" screen for the user, as Windows believes you're not playing nice any more.)
Behind the Scenes:
Setting e.Cancel indicates to Windows that you'd like the session not to end; but the user still gets a look in; and Windows may decide to ignore your request for any reason it feels pertinent. That's just the way the cookie crumbles. You may find hacks that work situationally, but there's no API or approach which is Microsoft-approved and therefore likely to work consistently now and in the future.
Under the covers, Windows is sending your process' windows a WM_QUERYENDSESSION message, which .NET receives for you and translates into the SessionEnding event) and will pass your cancellation (or lack of) back to Windows; returning TRUE if you don't cancel, FALSE if you do.
After this, Windows takes a look at all process' requests and depending on the cause of the shutdown and other factors may well still decide to go ahead despite such requests. It may also alert the user if processes are not cooperating and give them the option of killing the process.
Whether you handle WM_QUERYENDSESSION (SessionEnding) or not, you always get one last chance to clean up: you're sent a WM_ENDSESSION message (translated into SessionEnded). The tricky part is that you have to do all your vital tasks before all your SessionEnded handlers have returned!
Once Windows hears back from its WM_ENDSESSION (SessionEnded) call, all bets are off as far as your application's lifetime is concerned and Windows can terminate your process at any point.
Raymond Chen covered this quite expertly and quite recently.
http://blogs.msdn.com/b/oldnewthing/archive/2013/06/27/10429232.aspx
As a footnote, SystemEvents.SessionEnded is a convenience; if yo have a top level application window you can bypass it entirely and achieve the same via:
protected override void WndProc(ref Message m)
{
if (m.Msg == 0x16) // WM_ENDSESSION
{
WaitForQueriesToFinishOrSaveState();
m.Result = IntPtr.Zero;
return;
}
base.WndProc(ref m);
}
in shutdown command there's a switch for abort shutdown. you have to call this command by your c# code
Process cmd = new Process();
cmd.StartInfo.FileName = "cmd.exe";
cmd.StartInfo.RedirectStandardInput = true;
cmd.StartInfo.RedirectStandardOutput = true;
cmd.StartInfo.CreateNoWindow = false;
cmd.StartInfo.UseShellExecute = false;
cmd.Start();
cmd.StandardInput.WriteLine(#"shutdown -a");
cmd.StandardInput.Flush();
cmd.StandardInput.Close();
Console.WriteLine(cmd.StandardOutput.ReadToEnd());
I start the Windows On-Screen-Keyboard like that:
s_onScreenKeyboard = new Process();
s_onScreenKeyboard.StartInfo = new ProcessStartInfo("osk.exe");
s_onScreenKeyboard.EnableRaisingEvents = true;
s_onScreenKeyboard.Exited += new EventHandler(s_onScreenKeyboard_Exited);
s_onScreenKeyboard.Start();
This works fine, but when I try to stop it using the following code, it does not work, i.e. the OSK keeps running and the method returns false:
s_onScreenKeyboard.CloseMainWindow();
if (!s_onScreenKeyboard.HasExited)
{
if (!s_onScreenKeyboard.WaitForExit(1000))
{
s_onScreenKeyboard.Close();
//s_onScreenKeyboard.Kill();
}
}
When uncommenting s_onScreenKeyboard.Kill(); it is closed, but the problem is that osk.exe obviously uses another process called "msswchx.exe" which is not closed if I simply kill the OSK process. This way, I would end up with hundreds of these processes which is not what I want.
Another strange thing is that the CloseMainWindow() call worked at some time, but then it suddenly did not work anymore, and I do not remember what has changed.
Any ideas?
EDIT: I have found a solution myself. Please see my answer for details.
Background:
I am implementing an On-Screen-Keyboard for my application because it should work with a touchscreen. It is important that the keyboard layout matches the layout which is configured in Windows since the application will be shipped to many different countries. Therefore, instead of implementing a custom keyboard control with approx. 537 keyboard layouts (exaggerating a little here...), I wanted to utilize the Windows built-in On-Screen-Keyboard which adapts to the selected keyboard layout automatically, saving a lot of work for me.
I have found the/a solution myself:
When I successfully retrieve the MainWindowHandle after the process has been started, the call to CloseMainWindow() is also successful later on. I do not understand the reason for this, but the important thing is: it works!
BTW, for others having the same problem: The MainWindowHandle is not available immediately after starting the process. Obviously, it takes some milliseconds until the MainWindow is started which is why I use the following code to retrieve the handle:
DateTime start = DateTime.Now;
IntPtr handle = IntPtr.Zero;
while (handle == IntPtr.Zero && DateTime.Now - start <= TimeSpan.FromSeconds(2))
{
try
{
// sleep a while to allow the MainWindow to open...
System.Threading.Thread.Sleep(50);
handle = s_onScreenKeyboard.MainWindowHandle;
}
catch (Exception) { }
}
In this code I continuously get the MainWindowHandle every ~50ms as long as it is still equal to IntPtr.Zero. If the handle could not be retrieved after 2 seconds, I quit the loop to avoid an endless loop in case of error.
You need to wait untill the process finishes initialization, run
Process.WaitForInputIdle Method in order to do that.