Tuesday 20 November 2012

DateTime.ParseExact can be misleading

Last night, I was made aware of a crash in our Payslips for PAYE Tools program that occured pretty much every time it ran.

It turns out the user had changed the short date format on their system, so it included either a . or a – as the separator.

I had this line in my code:

Date = DateTime.ParseExact(value, "dd/MM/yyyy", null);

While this looks like it might do the right thing, it turns out it doesn’t as I didn’t use the invariant culture for the conversion.  The word exact is a bit misleading, as I’d assumed (incorrectly) that / was a character.  In fact, it’s replaced with the date separator, and no longer matches the string that came out of the database with a /

Tuesday 13 November 2012

Be very careful saving data in Windows 8

Windows 8 presents a number of new challenges for developers.

My challenge today is how can I save what the user is doing, and not lose it.

Best practice for Windows 8 apps suggests you save early, and often.  From the time a user closes your app you have about 10 seconds until it’s definitely not running.  For those of you who aren’t totally up to speed on the details of the Windows Store Application Lifecycle I suggest you have a look here first.

Now, the problem is in that article, on the section on App Close

App close

Generally, users don't need to close apps, they can let Windows manage them. However, users can choose to close an app using the close gesture or by pressing Alt+F4. You can't include any UI in your app to enable the user to close your app, or it won't pass the Store certification process.

Given the above, you can see that there is no way to be notified of a close event, however your OnSuspending event will still fire, after about 5 seconds.

So, assuming you busily began to save when you’re app received the VisibilityChanged event, you have maybe 5 seconds left to save the remainder of your state when you get to OnSuspending.

I say maybe, because, if you’re really unlucky, you’ll find that the user restarted your application from the start menu, after beginning your OnSuspending code.  In that case, your currently running thread will be terminated and your app will restart.  The file you were in the middle of saving could now be in any state.

If you don’t believe me, you can read the details here

Saving the application data to file in the Application.OnSuspending method does not work?

In a nutshell, this quote from the bottom of the thread sums up the problem
"To summarize, if the user closes the app and then immediately restarts before the first instance has finished closing then the first instance is forcibly terminated so the user runs in a clean state."

So, how do we build an application to demonstrate the problem?  First, in App.xaml.cs, wire up the VisibilityChanged event when you create your root frame.

Window.Current.VisibilityChanged += Current_VisibilityChanged;
this.Suspending += OnSuspending;

Next, implement the event handler like this and add a couple of members.


Task currentSaveTask;
void Current_VisibilityChanged(object sender, WIndows.UI.Core.VisibilityChangedEventArgs e)
{
if (!e.Visible && currentSaveTask == null)
{
CurrentSaveTask = SaveOurData();
}
}
async Task RenameFile(string tempFile, string newName)
{
StorageFolder localFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
StorageFile sf = await localFolder.GetFileAsync(tempFile);
await sf.RenameAsync(newName, NameCollisionOption.ReplaceExisting);
}

async Task WriteData(string filename, string content)
{
StorageFolder localFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
// on my machine, this is C:\Users\Tony\AppData\Local\Packages\2da24013-8fd9-49f2-8067-778dece884d6_6j3fz8tfj8x4m\LocalState

StorageFile sf = await localFolder.CreateFileAsync(filename, CreationCollisionOption.ReplaceExisting);

using (Stream s = await sf.OpenStreamForWriteAsync())
using (TextWriter output = new StreamWriter(s))
{
output.WriteLine(content);
}
}

async Task BeginSaving()
{
await WriteData("startup.txt", "We started saving");
}

async Task ContinueSaving()
{
await WriteData("temporary.txt", "The data file is incomplete");
}

async Task FinishSaving()
{
await WriteData("temporary.txt", "The data file was written successfully");
}

async Task SaveOurData()
{
await BeginSaving(); // show some progress to the user.
await Task.Delay(5500); // simulate extra work
await ContinueSaving();
await Task.Delay(4000); // simulate still more work.
await FinishSaving();
await RenameFile("temporary.txt", "final.txt");
currentSaveTask = null; // remove reference to our pending save
}


This writes a file in to our local storage, named startup.txt, and then delays long enough to guarantee we’re in the Suspend code, it then creates a file temporary.txt, delays a little longer, and finally writes the finished version of the temporary.txt file.


Finally it proceeds to rename the file to final.txt, and cleans up the handle to the outstanding task.


Now implement our OnSuspending handler like this:


 


async private void OnSuspending(object sender, SuspendingEventArgs e)
{
var deferral = e.SuspendingOperation.GetDeferral();
if (currentSaveTask != null) await currentSaveTask;
// you may want to use Task.WhenAll here to rendezvous multiple tasks...
// Task[] OutstandingSave = new Task[] { currentSaveTask };
//if (OutstandingSave[0] != null)
//{
// await Task.WhenAll(OutstandingSave);
//}
deferral.Complete();
}


This code waits for the currentSaveTask we kicked off if it exists.





Testing


To test this, do the following.


Fire up your app, and and open a window to wherever your App’s StorageFolder.LocalFolder resolves to.


Now press F4 on the app, to close it.


You should see a startup.txt file appear, followed after about 5.5 seconds a temporary.txt file appear. When you see that appear, quickly press the windows key, and click to restart your app.


You’ll notice that the remainder of the code never executes, and when you examine temporary.txt, it will contain “The data file is incomplete”.


Takeaway


If you were saving directly to a file (in XML format) and hadn’t yet completed by the time you relaunched, your file would be incomplete or corrupt.  This method allows you to check that the temporary.txt file does not exist at startup.  If it does exist, you can be pretty certain that final.txt file is incomplete.  Of course, temporary.txt file may be incomplete as well, so you’ll need to code in an additional step to allow you to check that it too is complete, (possibly by renaming to another intermediate file, temporaryfinal.txt?)

Thursday 1 November 2012

HP Machines and Windows 8

I’ve had a bad 24 hours.  I’ve been trying to get started developing for Windows Phone 8, and as a result of the new outlandish hardware requirements to do so have had to purchase a new PC.

So, I wandered down to Curry’s and picked up an  HPPavilion g6-2241sa 15.6 Laptop – Blue for £350.  I then had to get the windows pro pack to enable Hyper-V, (which I still haven’t, at the time of writing this, managed)

Alas, I’ve stumbled into the problem that Windows Update on HP machines doesn't actually work very well (if at all).  The big 190 MB update that needs to be done locks up after reboot at 13%

After nearly 12 hours talking to support, I came across this:

http://support.microsoft.com/kb/2777330

What I did was:

go to the windows update site, and download the updates manually: KB2764870, KB2761094, and 2756872 as well as delmigprov.exe

http://www.microsoft.com/en-us/download/details.aspx?id=34908

I then ran delmigprov

did the clean boot thing

copied the files to the desktop

and then went to device manager, and uninstalled all devices that might cause a problem (2x network, card reader, 2xaudio, and web cam).

I also manually stopped the audio services in case it was the IDT audio.After running the first 2 kb updates, I needed to reboot.

Alas, that meant I had to remove all of the devices again, and stop the services after they were installed.

Now my machine has finally booted back into windows!

I am now going to create another system restore point

Since then, I’ve added another 5 windows updates, but this time without disabling the devices.

And now another restore point, and finally install of pro-pack.

All of this seems to have worked, and I’m finally up and running after only 19 hours of work.

If this has saved you time, please consider purchasing some of our software at http://www.wieser-software.com/