Notes from Daily Encounters with Technology RSS 2.0
 
# Thursday, April 19, 2012

File IO in .NET for Metro style apps (aka .NET Core) can be a challenge for seasoned .NET developers. The classes in Windows.Storage namespace are different from both System.IO.IsolatedStorage and System.IO and require some getting used to. On top of that even the remaining classes in System.IO are missing some of properties and methods.

One such method is Stream.Close() (notice the remark at the top of the linked page). This doesn’t mean that you can forget about closing or disposing streams once you’re done with them, though. Try calling the following piece of code twice in a row and you’ll see what I mean:

StorageFolder folder = ApplicationData.Current.LocalFolder;
StorageFile file = await folder.CreateFileAsync("file.xml", CreationCollisionOption.ReplaceExisting);
IRandomAccessStream randomStream = await file.OpenAsync(FileAccessMode.ReadWrite);
IOutputStream outStream = randomStream.GetOutputStreamAt(0);
XmlSerializer serializer = new XmlSerializer(typeof(string));
Stream stream = outStream.AsStreamForWrite();
serializer.Serialize(stream, "Test");
await outStream.FlushAsync();

The second call to CreateFileAsync() ends up with an UnauthorizedAccessException: Access is denied, the reason being that the file is still being locked by the objects from the first call. To avoid that you need to call Dispose() on either the Stream class:

StorageFolder folder = ApplicationData.Current.LocalFolder;
StorageFile file = await folder.CreateFileAsync("file.xml", CreationCollisionOption.ReplaceExisting);
IRandomAccessStream randomStream = await file.OpenAsync(FileAccessMode.ReadWrite);
IOutputStream outStream = randomStream.GetOutputStreamAt(0);
XmlSerializer serializer = new XmlSerializer(typeof(string));
using (Stream stream = outStream.AsStreamForWrite())
{
    serializer.Serialize(stream, "Test");
    await outStream.FlushAsync();
}

or the IOutputStream interface:

StorageFolder folder = ApplicationData.Current.LocalFolder;
StorageFile file = await folder.CreateFileAsync("file.xml", CreationCollisionOption.ReplaceExisting);
IRandomAccessStream randomStream = await file.OpenAsync(FileAccessMode.ReadWrite);
using (var outStream = randomStream.GetOutputStreamAt(0))
{
    XmlSerializer serializer = new XmlSerializer(typeof(string));
    Stream stream = outStream.AsStreamForWrite();
    serializer.Serialize(stream, "Test");
    await outStream.FlushAsync();
}

For some reason calling Dispose() only on the IRandomAccessStream interface is NOT enough:

StorageFolder folder = ApplicationData.Current.LocalFolder;
StorageFile file = await folder.CreateFileAsync("file.xml", CreationCollisionOption.ReplaceExisting);
using (IRandomAccessStream randomStream = await file.OpenAsync(FileAccessMode.ReadWrite))
{
    IOutputStream outStream = randomStream.GetOutputStreamAt(0);
    XmlSerializer serializer = new XmlSerializer(typeof(string));
    Stream stream = outStream.AsStreamForWrite();
    serializer.Serialize(stream, "Test");
    await outStream.FlushAsync();
}

Keep that in mind when working with streams in WinRT.

Thursday, April 19, 2012 10:25:14 PM (Central European Daylight Time, UTC+02:00)  #    Comments [0] - Trackback
Development | C# | Metro
# Saturday, April 07, 2012

Since Metro applications are running in a sandboxed environment they only have limited file handling capabilities. The private storage area for each application is conceptually very similar to isolated storage as we already know it from Silverlight and Windows Phone 7, only with its own API. The actual storage location is slightly different as well (although it is abstracted away, it’s useful to be able to look into it during development):

  • Metro application files are located in C:\Users\<ProfileName>\AppData\Local\Packages
  • .NET isolated storage files are located in C:\Users\<ProfileName>\AppData\Local\Isolated Storage
  • Silverlight isolated storage files are located in C:\Users\<ProfileName>\AppData\Local\Microsoft\Silverlight

This still doesn’t solve the problem of opening a file in a standard format (PDF for instance) with the associated application. In .NET you could simply use the Process type:

Process.Start(filename);

Unfortunately it isn’t available for Metro style applications, therefore a different solution is required. My first instinct was to activate a Windows 8 contract but it turned out none of them can be used for this purpose. It turns out the solution is even simpler - Launcher class:

StorageFile file = await ApplicationData.LocalFolder.GetFileAsync(filename);
await Launcher.LaunchFileAsync(file);

There are also a couple of overloads available for setting additional options and opening files from Uris.

Saturday, April 07, 2012 12:28:06 PM (Central European Daylight Time, UTC+02:00)  #    Comments [0] - Trackback
Development | C# | Metro
# Monday, March 26, 2012

Probably the most interesting error I had to troubleshoot since I started developing my first Windows Metro application happened to me after doing some restructuring and cleaning up of the existing code base. Almost immediately after starting the application the debugger stopped its execution somewhere in App.g.i.cs (an auto generated file in obj folder, not part of the code I’ve written):

if (Debugger.IsAttached) Debugger.Break();

Trying to continue execution from this point on resulted only in another even more vague error message:

Unable to activate Windows Metro style application

Fortunately the fix turned out to be really simple once it dawned on me how to get more information: the second argument of UnhandledException event handler inside which the execution stopped was UnhandledExceptionEventArgs. Peeking inside revealed the mystery:

XAML parsing failed.

There was an error in App.xaml which for same strange reason didn’t cause the build to fail nor did it prevent the app from starting. It was listed in the Error List though:

An error occured while finding the resource dictionary

At least now I know what to look for when it happens the next time.

Monday, March 26, 2012 9:16:20 PM (Central European Daylight Time, UTC+02:00)  #    Comments [0] - Trackback
Development | C# | Metro
# Wednesday, March 21, 2012

It seems that Visual Studio 11 Beta works better and more reliable in Windows 7 than it does in Windows 8 Consumer Preview. In Windows 7 I’ve been running it without problems, using Slovenian regional settings. In Windows 8 Consumer Preview on the other hand it doesn’t even even start properly when regional settings are set to Slovenian. It just shows a bunch of error messages while loading and continues the trend with almost every command you click on afterwards.

Error message on Visual Studio 11 startup

Fortunately the solution is simple: just switch the regional settings to English (United States) and the problems will be gone (it might work with some of the other settings as well, I didn’t check). This might not be enough if you have started Visual Studio 11 Beta for the first time with Slovenian regional settings. In this case even after switching back to English regional settings the problems will persist. To solve this problem you need to delete all the Visual Studio related settings from your profile. You can find them in two folders:

  • C:\Users\<Username>\AppData\Local\Microsoft\VisualStudio\11.0
  • C:\Users\<Username>\AppData\Roaming\Microsoft\VisualStudio\11.0

Doing this is certainly simpler and quicker than reinstalling Visual Studio 11 with correct regional settings as I did the first time.

Wednesday, March 21, 2012 8:02:05 PM (Central European Standard Time, UTC+01:00)  #    Comments [0] - Trackback
Software | VisualStudio | Windows
# Monday, March 19, 2012

Windows Workflow Foundation (WF) is an often overlooked part of .NET framework but its declarative approach to defining workflow logic can of prove useful in spite of the steep learning curve and some unfortunate limitations. Once you get to know it, you might even come up with new creative ways of using it. Let’s take a look at one such use case.

If you’ve at least tried out WF, you probably know that there is built in support for Visual Basic expressions which is being used by several activities. It doesn’t take ingenuity to come up with a simple workflow which can be used to calculate a value of an expression.

Simple workflow for evaluating an expression

Using the simplest of the workflow hosting classes, it’s easy to evaluate this expression inside your application for any input value:

private bool Invoke()
{
    var invoker = new WorkflowInvoker(new TimeTravel());
    var inputs = new Dictionary<string, object> { { "Speed", 80 } };
    var outputs = invoker.Invoke(inputs);
    return (bool)outputs["Result"];
}

A less known fact is that workflows can also be defined in code. Using this approach, it’s easy to create a generic method for evaluating runtime defined expressions:

public TResult Evaluate<TArg, TResult>(TArg input, string expression)
{
    var vb = new VisualBasicValue<TResult>(expression);

    var wf = new DynamicActivity<TResult>
    {
        Properties = 
        {
            new DynamicActivityProperty
            {
                Name = "Input",
                Type = typeof(InArgument<TArg>)
            }
        },
        Implementation = () => new Assign<TResult>
        {
            Value = new InArgument<TResult>(vb),
            To = new ArgumentReference<TResult> { ArgumentName = "Result" }
        }
    };

    var inputs = new Dictionary<string, object>
    {
        { "Input", input }
    };

    return WorkflowInvoker.Invoke(wf, inputs);
}

The above code assumes that the input value will be referenced in the expression with the name “Input”. The generic arguments need not be only basic types but in this case it is necessary to import additional Visual Basic references. With only a minor change in the workflow definition a similar approach can be used for validating the expression when the user enters it:

public string Validate<TArg, TResult>(string expression)
{
    var vb = new VisualBasicValue<TResult>(expression);

    var wf = new DynamicActivity<TResult>
    {
        Properties = 
        {
            new DynamicActivityProperty
            {
                Name = "Input",
                Type = typeof(InArgument<TArg>)
            }
        },
        Implementation = () => new If()
        {
            Condition = new InArgument<bool>(false),
            Then = new Assign<TResult>
            {
                Value = new InArgument<TResult>(vb),
                To = new ArgumentReference<TResult> { ArgumentName = "Result" }
            }
        }
    };

    try
    {
        WorkflowInvoker.Invoke(wf);
    }
    catch (InvalidWorkflowException e)
    {
        return e.Message;
    }
    return null;
}

We are taking into account that the expression is getting compiled when WorkflowInvoker.Invoke is called. Since we don’t have an instance of the input class available, we use the If statement to avoid evaluating the expression and causing a runtime exception. The only expected cause of the exception is therefore expression compilation error. The message returned by the method could even be parsed further to only include the actual error emitted by the compiler.

New features in .NET framework 4.5 (currently in beta) allow us to do even more:

  • It includes support not only for Visual Basic expressions but for C# expressions as well. CSharpValue<T> can be used just like VisualBasicValue<T>.
  • Both classes include additional GetExpressionTree method which can be used to retrieve and further process the expression tree parsed from the expression text. This offers many new possibilities, like modifying the expression to evaluate it without invoking the workflow or analyzing it to determine which input argument properties it actually depends on.

And no, GetExpressionTree method is not available in .NET 4 although the documentation is saying otherwise.

Monday, March 19, 2012 8:50:53 PM (Central European Standard Time, UTC+01:00)  #    Comments [0] - Trackback
Development | .NET 4.5 | WF4
Sponsored Ads

About Me
Currently Reading

Entity Framework 4.1: Expert's Cookbook

Twitter
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

All Content © 2012, Damir Arh, M. Sc. Send mail to the author(s) - Privacy Policy - Sign In
Based on DasBlog theme 'Business' created by Christoph De Baene (delarou)
Social Network Icon Pack by Komodo Media, Rogie King is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License.