Showing posts with label 30 Days of Bitching about .NET CF. Show all posts
Showing posts with label 30 Days of Bitching about .NET CF. Show all posts

Post 11: Android versus Windows Mobile Development

I wanted to refrain from blogging about this until I had at least somewhat delved into Android development. And although I am by no means a guru at the Android platform yet, I feel I can safely make some broad statements, step on some toes, and be able to sufficiently back up my opinions. [0] :)

 

The Byte Code

Google made some interesting architectural and strategic decisions regarding Android. The most notable being Java as the development language and Dalvik as the underlying bytecode. Dalvik, in short, is a byte code that is interpreted at runtime, similar to Sun's Java byte code or Microsoft's Intermediate Language (aka IL)[1]. I didn't really understand why Google would go and create another byte code specification for Java until I read a great article by Stefano Mazzochi. The long and short of it is, Sun has some nasty licensing around Java ME, and the "Open" Handset Alliance can't really have an Open platform with the possibility of some mega-corporation trying to wet their beaks at their expense. [2]

So, still begs the same question: why invent a whole new byte code when Intermediate Language has no such licensing (it is an ECMA approved standard)?  Maybe Google took a look at it and decided it wasn't suitable for a mobile platform, or maybe they didn't want to jump into bed with Microsoft. I'm guessing the latter.

 

The Language

At the end of the day, the byte code generated behind the scenes is about as important as the color of my socks. I generally don't think about it on a daily basis, and what really matters is the language you are writing in. I can understand that Google chose Java to ease their target developer base's transition the new platform, but still... I'm going to just go ahead and say it. Java sucks.

How much does Java suck? Let me count the ways.

  1. Terrible constraints around package/file/directory/class names.
  2. Lack of anonymous methods.
  3. Lack of closures.
  4. No lambda expressions (or LINQ).
  5. Lack of events/delegates.
  6. Lack of partial classes.
  7. Ridiculous implementation of enumerations.
  8. Terrible implementation of generics (type erasure).
  9. No user defined value-types: no "struct" and thus no capability of having objects that live purely on the stack. This is nasty on performance when it counts (such as in games).
  10. Strings are immutable, but not interned.
  11. No access to "unsafe" code.
  12. No get/set accessor methods.
  13. No operator overloading.
  14. No extension methods.
  15. Last but not least, stupid and unintuitive brace positioning.

How many ways? 13, 14, 15, Ha, Ha, Ha, Ha, Ha!

 

I'm not trying to be a nit-picky zealot here; everything I listed is a feature I use on a regular basis. I will admit though, that I am enjoying anonymous classes to some degree, but they would be mostly pointless with the availability of events/delegates. I realize that most of what I listed is just syntactic sugar, and neither Java or C# is more "powerful" than the other; they can each do anything you need in their own way. Except C# lets you do it more quickly, intuitively, and elegantly. [3]

 

The Development Environments

Eclipse is a giant steaming pile of... yeah. Yes, very capable, very flexible, but whoever is doing usability testing on this junk needs to get a job that involves testing the constricting force of a rope around their neck.

My biggest pet peeve is the fact that you can't simply click in the Expressions view and edit or add something inline. You have to right click it and click add/edit. That takes you to a vanilla text editor (which has no Intellisense/Content Assist), type in your expression, pray it is parsable, and then click ok. Otherwise you are having to go through that zillion step process yet again.

Earlier I gave some kudos to Java's inner class capabilities. As neat as they are, Eclipse makes them damn near unusable: you can't watch a value of a member variable in the parent class of an inner class. That makes debugging a giant pain: you need to load all those members into local variables if something is going wrong, otherwise it is impossible to find and fix even simple issues, such as a null reference. I'm not sure if this is a problem with Eclipse, Java, or Android though.

The auto-complete in Eclipse is painfully annoying. Auto-complete should be optional, and not forcibly inserted with no regard to what the developer wants. I ended up turning it all off, because what it was inserting was generally useless. And even though the auto-complete is off, Eclipse is still inserting parenthesis for me...

 

The SDKs 

Though I have been complaining a lot about developing on Android, I do want to point out that I haven't actually complained about Android. Basically every API I could think of was standardized and implemented.

My litmus test in this regard was a port of Klaxon to Android. It really helped underscore the huge differences between the two platforms.

For me, most notable was the APIs to access device sensors. Version 1 of the Android SDK exposes a simple SensorManager class that took me 5 minutes to figure out and access. Microsoft has had 6ish versions of Windows Mobile with no Sensor API in sight.

Secondly, scheduling processes to start on Android is infinitely easier than in Windows Mobile. Here's how that learning process worked for me on Windows Mobile:

  • First I started with CeRunAppAtTime. I write a gross PInvoke for this. This works for a while, then stops working completely. Others have had this issue as well. I need to switch to another mechanism.
  • Then I switched to CeSetUserNotificationEx. I write yet another gross PInvoke for this.
  • This involves filling out some elaborate UserNotificationTrigger structure. I eventually get this working.
  • Then I find that if the device is sleeping, Klaxon sometimes does not start properly.
  • I solve this issue by digging through some documentation and randomly figuring out the problem: I need to force the device to turn on or it goes back to sleep.
  • Klaxon works again for a while, until daylight savings occurs. On some phones, it is now firing an hour late. I have no idea why. I have given up.

Let's compare this to Android:

  • Access the AlarmManager.
  • Tell it to turn on and fire an intent at a certain time.
  • Handle the intent.

This just works, no hassle, no fuss. I must say, intents are easy to use, amazingly flexible, and equally powerful.

So, two of the "difficult" parts for creating Klaxon on Windows Mobile were not so difficult at all on Android. The final piece: UI implementation.

Windows Forms code versus Android's XML Layout. Windows Forms which works with absolute sizing and positioning, and Android which uses relative sizing and positioning. Windows "Static" Controls versus Android's Animatable Controls. There really is no comparison, so I won't even bother.

Simply put, the relevant Android SDK blows the Windows Mobile SDK out of the water (language and framework features aside).

Windows Mobile does have far better documentation (MSDN) than Android, but I consider that more of a teething issue that would occur with any new SDK.

 

The Platforms

Android is pretty heavily locked down from a security standpoint. Applications must register what capabilities they wish to have, and the user must approve of them. And though this security is all in all a positive thing, it does have it's downsides; for example, it is impossible to write an application that screen shots a screen other than its own, as this is considered a security risk. [4]

And though this is not really a fault of Android per se, the phones that are actually being released are not as "open" as the name Open Handset Alliance would suggest. Carriers always have and will continue to lock down phones so long as they subsidize them. For example, the user does not have access to the root account on the G1; this makes removal/tweaking of the standard Android applications impossible. But at least on Android you can tweak the standard applications due to it being open source (after getting root).

On the flip side of the coin, Windows Mobile doesn't have a VM hiding all the nitty gritty details from you. Developers can easily get low level access to anything, without having to worry about Microsoft filling the holes. For example, if Android were to have shipped without a Sensor API, there is no way (outside of manually installing native binaries through ADB) that a developer could implement it. And that is not a comforting feeling. For example: Android does not support streaming video through the MediaPlayer yet, and as far as I know, nothing can be done about that unless the native android binaries are patched through an OTA update.

Oh, and the Android Market. Not going to bother elaborating on this point, as the statement itself is pretty self explanatory.

 

Summary

I don't really have anything insightful to say here, and that may be because it is 4AM. I'll wrap it up by saying, I really enjoy developing for Windows Mobile. The language and tools are great. However, the platform and applications simply are not very usable. On the other hand, though I'm not completely discontent with Android development, the platform, applications, and general usability is fantastic; for once I don't have to fight my phone to do simple things like browse the web. So you can guess which phone is always in my pocket. And then you can infer which once I am more interested in developing in.

That said, I hope Microsoft can produce a usable platform in the near future; as that would make me a very happy user and developer. I can't believe I'm going to say this, but they should take a page out of Apple's book and just say "screw backwards compatibility, screw existing applications, we're going to develop a brand new platform that works". They've been catering to the backwards compatibility mantra for so long that it is impossible for them to move forward. And all the while, they are alienating the very market share they are so afraid to lose.

 

 

[0] I've released 2 applications on the Market, Telnet and Klaxon, so I feel I have a decent grasp of developing for the Android platform. Klaxon has really helped underscore the huge differences in the platform.

[1] Yes, I realize that IL is not interpreted on the Windows Platform, it is JIT compiled.

[2] As "evil" as Microsoft may be connoted to be, you don't see them on the news exercising their patent rights. Unlike Sun.

[3] I'm thinking about looking into Ruby.

[4] Maybe Google will implement this in the future.

Post 10: Not Just a Windows Mobile Blog Anymore

522482

I just got my T-Mobile G1 this morning. Initial thoughts from a developer's standpoint:

Pros:

  • It's not Windows Mobile.
  • Richer UI development tools.
  • Android Market

Cons:

  • It's not Windows Mobile.
  • Java. (C# makes Java look like Corky from Life Goes On)
  • No MSDN.

I'm going to be switching gears and doing primarily Android development until Microsoft figures out how to ship a real mobile OS.

Post 9, The Rant Continues: TabIndex is Terrible. Windows Mobile needs "Smart" Key Navigation.

Yeah, so it's been well over 30 Days as I originally predicted, so I'm renaming this series 30 Posts of Bitching about .NET Compact Framework.

Windows Mobile "Standard" [0] device development is a pain. I've never owned one, because I can't really live without a touch screen.

I still fondly remember many years ago, when I first fired up the Visual Studio Form designer with the Smartphone form factor. I started started dragging my controls out, doing the usual, then I noticed something odd: I didn't have a Button control on my toolbox. I searched around for a bit, and assumed that my Visual Studio installation was hosed. I created a new Pocket PC project, and the Button control was there. So I dragged one out, and then changed the target platform to Smartphone. I reopened the form and saw this intimidating hazard sign:


BrokenSmartphone

Attempting to run the application resulted in a NotSupported exception. Apparently simple Button controls aren't supported on Windows Mobile Standard for whatever reason. I guess this is because there is no touch screen, and thus no mouse clicks, so they just dropped support for the Button control and left the developer with LinkLabels. That really brings application development to a grinding halt if you are trying to target both Smartphone and Pocket PC. [1]

Man, I've really strayed from the point of this article. Getting back on track: The purpose of this post is to discuss something I mentioned briefly in a previous post. One of the annoying issues associated with Smartphone development is that since you are restricted to only keyboard input, your application has to be very aware of key navigation. The user should be able to intuitively navigate through all the forms quickly and easily. However, from an API level, all the developer is given is TabIndex. TabIndex is a property that you can set on any control that let the Windows API know the tab order of controls. For example, the Up button on a Smartphone would take you back to the control with the previous TabIndex, while the Down button would take you to the next TabIndex.

All is well in the world of TabIndex and simple 5 minute applications. TabIndex works great if all your controls are laid out vertically, with one control per line. But if you need to do any real Windows Mobile application development, chances are that TabIndex will not cut it. Take for example the following Form:

TabIndexSucks

 

For visual reference, the yellow region is a panel housing 4 LinkLabels, and the white region is the Form housing the other 6. The TabIndex of each Link is shown.

Navigating through this form is gross. For one, the Left and Right keys don't actually navigate left or right to other Controls. And the Up and Down Keys simply go through the Controls in TabIndex order. So, if "Panel TabIndex 0" had focus, pressing up would take you to "Form TabIndex 5" and not "Form TabIndex 2". Conversely, if "Form TabIndex 2" had focus, pressing down would take you to "Form TabIndex 3" and not "Panel TabIndex 0".

Basically, once you introduce any sort of grid or panels, TabIndex probably will not work for you. At that point, you need to hook the KeyDown events and handle the navigation to and from each Control... manually... for every Form. And as development progesses, and the UI changes, you better make sure that you update all those nasty KeyDown event hooks so your navigation doesn't break!

This is not ideal; it introduces code bloat, bugs, and is in general just tedious to do. To that end, I wrote an algorithm that would analyze the Controls in a Form and figure out the best Navigation target from any given Control (if there was one).

I'm not going to really get into the algorithm in great detail. Basically the way it works is that it looks at the key that was pressed and finds the corresponding edge of the Control. It uses that edge to find the opposite edge of all the other controls that has the best "score". A score is comprised of 2 factors: how close are the edges to each other and how much of the edge is "shared". Yeah, that probably made no sense. Maybe this picture will help:

EdgeAlgorithm

 

So, yeah, Edge Distance and Shared Edge Length are the two weighting factors in determining the best navigation target.

This navigation code is actually a small part of the overall UI framework I use. I've stripped out the algorithm and repackaged it so that it can be used by anyone. Usage is pretty straightforward. The Navigation class has two members:

  • FindFocusedControl - Find the control on the Form that is currently focused.
  • GetBestNavigationTarget - Given a Control and a navigation Key (Up, Down, Left, or Right), find the best navigation target.

To catch navigation events easily, you can do it at a form level by enabling KeyPreview. Then override OnKeyDown and use those two methods to find the Control that should receive focus. For example, here's the code from the sample:

using System;
using System.Windows.Forms;

namespace KeyNavigationDemo
{
public partial class NavigationDemoForm : Form
{
public NavigationDemoForm()
{
InitializeComponent();

// By enabling key preview, the form receives all key events
// that would otherwise be received and processed by a control.
// We can choose to handle these events.
KeyPreview = true;
}

private void myExitMenuItem_Click(object sender, EventArgs e)
{
Close();
}

private void mySmartNavigationMenuItem_Click(object sender, EventArgs e)
{
mySmartNavigationMenuItem.Checked = !mySmartNavigationMenuItem.Checked;
}

protected override void OnKeyDown(KeyEventArgs e)
{
if (mySmartNavigationMenuItem.Checked)
{
switch (e.KeyCode)
{
case Keys.Up:
case Keys.Down:
case Keys.Right:
case Keys.Left:
{
Control focused = Navigation.FindFocusedControl(this);
if (focused != null)
{
Control navigateTarget = Navigation.GetBestNavigationTarget(focused, e.KeyCode);
e.Handled = true;
if (navigateTarget != null)
navigateTarget.Focus();
}
}
break;
}
}
base.OnKeyDown(e);
}
}
}

Here's the full source code to the navigation algorithm and the sample demonstrating how to use it. The sample application is the same one you see in the above image with 10 LinkLabels. By default, Smart Navigation is disabled, use the arrow keys to navigate around the form using TabIndex. Then enable Smart Navigation in the Menu and use the arrow keys to navigate intuitively.


The sample is just a demonstration of the navigation; keep in mind that some Controls may want to handle certain key presses. For example, text boxes should generally receive the left and right key presses to navigate within the entered text and not to another Control.


[0] "Standard" is the official Microsoft term for a Windows Mobile non touch screen device. Professional is the term for touch screen devices. It used to go by "Smartphone" and "Pocket PC" back in 2003, and then they changed it to Standard and Professional, so that's how it is now. I think. Don't quote me. In fact, forget you ever read this.


[1] I should mention, there is nothing to stop a developer from basically inheriting from Control and overriding OnClick and thus creating a Button control. This works just fine, and it is just benign code on a Smartphone; behind the scenes those mouse methods are just a result of receiving WM_LBUTTONDOWN/UP messages. So, I ended up creating my own ButtonBase class in the UI framework I wrote/use.

Day 8: VARIANT_BOOL, BOOL, bool, HRESULTs, and managed Exceptions

God, what a mess.

  true false
VARIANT_BOOL -1 0
bool (C#/C++) true false
BOOL (C++) not 0 0
HRESULT >= 0 < 0

I ran into this mess while creating COM interfaces in C# that were being used in C++ (via tlbexp). I discovered some silly behavior with regards to how C# or tlbexp marshals bool return types, which was the cause of a couple bugs:

C# COM Interface:

[Guid("91B57DDB-5CCF-4cb5-9A26-A7F9559BAFFF")]
public interface IFoo
{
    // by default bool is marshalled as VARIANT_BOOL
    bool Bar();
    void Goo();
}

Seriously? Why is it defaulted to VARIANT_BOOL and not BOOL? VARIANT_BOOL is a Visual Basic concept (and a retarded one at that). Looking at the table above, it is the complete opposite behavior of COM HRESULTs. The fix:

[Guid("91B57DDB-5CCF-4cb5-9A26-A7F9559BAFFF")]
public interface IFoo
{
    [return: MarshalAs(UnmanagedType.Bool)]
    bool Bar();
    void Goo();
}

Another issue that bothered me was that these COM calls actually look like the following:

virtual HRESULT __stdcall raw_Bar (
  /*[out,retval]*/long* pRetVal ) = 0;

virtual HRESULT __stdcall Goo () = 0;

All COM calls are returning HRESULTs behind the scenes, which is expected. However, what happens when the C# code throws an exception? You would expect the marshaller to maybe catch it and return an HRESULT failure? Nope. On .NET CF (and maybe even in the desktop version too), the application crashes (without any chance for recovery) in native code. Beautiful. This basically requires that your C# COM methods have a try/catch wrap around all operation, as an exception would be fatal. I guess I can understand why you wouldn't want to have the COM interop handling arbitrarily catch all exceptions, but it is quite tedious to have to do it yourself.

Day 7 or 37, who knows: .Net CF and Marshalling ANSI strings in structures

Consider the following code:

using System;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.Text;

namespace SmartDeviceProject5
{
class Program
{
static void Main(string[] args)
{
Foo foo = new Foo();
try
{
SomeMethod(foo); // KABOOM! NotSupportedException
}
catch (Exception e)
{
}
try
{
AnotherMethod("hello"); // KABOOM! NotSupportedException
}
catch (Exception e)
{
}
}

[DllImport("SomeDLL")]
extern static void SomeMethod(Foo foo);

[DllImport("SomeDLL")]
extern static void AnotherMethod([MarshalAs(UnmanagedType.LPStr)] string someString);
}

public struct Foo
{
public string Bar;
public string Borked;
}

}

When attempting to run this program, it will throw an exception upon calling the SomeMethod PInvoke. Attempting to call the "AnotherMethod" function will fail just as well.
This happens because C# automatically marshals all strings in structures as LPTStr (which for some reason is LPStr in Windows Mobile). However, in methods, strings are marshalled as LPWStr. I don't get it. Anyways, .Net Compact Framework does not support marshalling as LPStr in structures or method calls for whatever reason, even though the code to make it happen is all there (as I will show you).
Changing the code by adding the MarshalAs attribute to explicitly Marshal them as LPWStr would make it work:



    [DllImport("SomeDLL")]
extern static void AnotherMethod([MarshalAs(UnmanagedType.LPWStr)] string someString);
}

public struct Foo
{
[MarshalAs(UnmanagedType.LPWStr)]
public string Bar;
[MarshalAs(UnmanagedType.LPWStr)]
public string Borked;
}


Although LPWStr (Unicode) is more or less the standard now, but if you really really want/need to Marshal it as a LPStr, tough cookies, you get a NotSupportedException.
Generally, if you need to marshal a structure with strings, it's highly unlikely that you are going to need to mix LPWStr and LPStr in the same structure. To that end, I wrote a custom MarshalAnsi class that marshals a structure and all its contents; any strings found are marshalled as LPStr:


using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Reflection;

namespace System.Runtime.InteropServices
{
    /// <summary>
    /// .NET Compact framework does not support marshalling strings to ascii.
    /// Need to do it manually.
    /// </summary>
    static class MarshalAnsi
    {
        /// <summary>
        /// This Dictionary maintains all the strings allocated by an IntPtr that a structure
        /// was Marshalled to.
        /// </summary>
        static Dictionary<IntPtr, List<IntPtr>> myStringsForObject = new Dictionary<IntPtr,List<IntPtr>>();

        public static IntPtr StructureToPtr(object structure)
        {
            Type type = structure.GetType();
            var fieldInfos = type.GetFields(BindingFlags.Instance | BindingFlags.GetField | BindingFlags.Public | BindingFlags.NonPublic);
            
            // determine the total size of the structure. Need to special case strings and bools
            int totalSize = 0;
            foreach (FieldInfo field in fieldInfos)
            {
                totalSize += field.FieldType == typeof(string) ? Marshal.SizeOf(typeof(IntPtr)) :
                              field.FieldType == typeof(bool) ? Marshal.SizeOf(typeof(int)) : Marshal.SizeOf(field.FieldType);
            }

            // allocate the pointer, and create it's list of allocated strings
            IntPtr ret = Marshal.AllocHGlobal(totalSize);
            List<IntPtr> strings = new List<IntPtr>();
            myStringsForObject.Add(ret, strings);
            // structure pointer offset, which is incremented as we write to the structure
            int ofs = 0;
            foreach (FieldInfo field in fieldInfos)
            {
                object toWrite = null;

                if (field.FieldType == typeof(string))
                {
                    // allocate memory for the string if need be, and add it to the 
                    // pointers string allocation list
                    string str = field.GetValue(structure) as string;
                    IntPtr strPtr;
                    if (str == null)
                        strPtr = IntPtr.Zero;
                    else
                    {
                        byte[] bytes = Encoding.ASCII.GetBytes(str);
                        strPtr = Marshal.AllocHGlobal(bytes.Length + 2);
                        strings.Add(strPtr);
                        Marshal.Copy(bytes, 0, strPtr, bytes.Length);
                        Marshal.WriteInt16(strPtr, bytes.Length, 0);
                    }
                    toWrite = strPtr;
                }
                else if (field.FieldType == typeof(bool))
                {
                    // need to write this as an int, not a bool. 
                    // BOOL in C/C++ is really an int, which is of size 4.
                    toWrite = (bool)field.GetValue(structure) ? 1 : 0;
                }
                else
                {
                    // just do the default behavior
                    toWrite = field.GetValue(structure);
                }
                Marshal.StructureToPtr(toWrite, (IntPtr)((int)ret + ofs), false);
                // increment the structure pointer offset
                ofs += Marshal.SizeOf(toWrite);
            }

            return ret;
        }

        /// <summary>
        /// Destroy the memory allocated by a structure, and all strings as well.
        /// </summary>
        /// <param name="ptr"></param>
        public static void DestroyStructure(IntPtr ptr)
        {
            List<IntPtr> strings = myStringsForObject[ptr];
            myStringsForObject.Remove(ptr);
            foreach (IntPtr strPtr in strings)
            {
                Marshal.FreeHGlobal(strPtr);
            }
            Marshal.FreeHGlobal(ptr);
        }
    }
}

Usage:
Simply pass a structure into this class and it will return a pointer with the marshalled data. All strings are marshalled as Ansi strings.
Remember to call MarshalAnsi.DestroyStructure with the pointer returned from MarshalAnsi.StructureToPtr when it is no longer in use.

Day 6?: Mergebin - Combine your Unmanaged and Mananaged DLLs into one Handy Package

MC++ is nice. It lets you mix unmanaged and managed code and allows you to easily create assemblies where you need to do a lot of interop work. Unfortunately, MC++ is not available for .NET CF. So, whenever you need to do any significant degree of interop, you usually end up doing one of the following:

  • Doing a lot of PInvokes into code that really isn't PInvoke friendly. This generally results in a lot of manual marshalling of arguments and unsafe code. This doesn't work if what you need to PInvoke is a .lib or a non-flat/C style DLL.
  • Writing a unmanaged DLL that does all the heavy lifting, and creating managed PInvokes to access it.
  • Writing a COM object for Windows Mobile and referencing that in your C# project.

The first solution is pretty kludgy, but it works. The latter two solutions result in two outputs: a managed assembly and an interop DLL. The DLLs must always be deployed with each other to function properly. Not a very elegant solution either.

I've been playing with SQLite recently. SQLite is the most used database engine in the world, and it runs on basically any device imaginable, including Windows Mobile. SQLite is written entirely in C/C++, so naturally someone wrote a managed wrapper for it called System.Data.SQLite.

One of the interesting things I noticed about System.Data.SQLite is that it somehow manages to deploy as one assembly, even though one of the source DLLs is managed and the other is an unmanaged interop. After diving into the managed code, I noticed something quite peculiar: it was calling PInvokes contained within itself! Somehow a single DLL contained both managed and unmanaged code!

Turns out that the creator of SQLite also created a tool called Mergebin that allows you to merge your managed assembly and unmanaged interop assembly into one nice bow tied package. Very elegant! The full source to the Mergebin tool is included with System.Data.SQLite. I looked over it, and although I have a very rough idea of how DLLs work, so the actual technical implementation of it is all Greek to me. Interesting stuff nonetheless!

Note:

Just a few posts ago, I talked about how I managed to combine two .NET assemblies into one. It involved embedding one assembly as a resource of it's dependent assembly. Pretty kludgy. I probably should have Googled to see if there was a tool possible to merge two .NET assemblies, and there is: ILMerge. I haven't looked into it at all though, so I'm not sure if it will work for .NET CF.

Day 5: XslCompiledTransform, I miss you.

Oh man, Day 5? I'm really bad at this. But hey, I was up front that I wouldn't actually be posting on a daily basis.

It's pretty hard to write an application that doesn't touch XML in some way these days. A while ago, I found myself in several situations where I was wishing I could run an XML document through an XSLT before processing it. As a workaround, I'd load the document into an XmlDocument and do the transformation programmatically.

That's obviously a bit of a bitch, so after some thorough digging, I found that Windows Mobile 6+ ships with the unmanaged MSXML API, and that you can create wrappers for these COM objects using the MIDL compiler and adding a reference to the type library file (TLB).

My deterrence from using this is that for everything I write I need to remember to add the COM wrapper, Interop.MSXML2.dll, to my CAB setup files. This isn't a huge deal when it's a single file, obviously. But updating my setup projects every time I add a COM wrapper is very tedious.

Normally, I could just embed that DLL as a resource in my assembly, and call Assembly.Load on the resource stream. However, .NET CF does not have that particular overload of Assembly.Load. So my horrible solution: embed it as a resource, and when the static constructor on XslCompiledTransform is called, write that file out to the current directory and load it:


static XslCompiledTransform()
{
    try
    {
        Assembly.LoadFrom("Interop.MSXML2.dll");
    }
    catch (Exception)
    {
        string cwd = Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase);
        using (FileStream fs = new FileStream(Path.Combine(cwd, "Interop.MSXML2.dll"), FileMode.Create))
        {
            using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("CompactFrameworkExtensions.Interop.MSXML2.dll"))
            {
                fs.Copy(stream);
            }
        }

    }
}

Note: This may cause "dirty" uninstallation issues.

Oh, that reminds me, the "fs.Copy(stream)" is a handy extension method that allows you to copy the contents of a stream into another.

Usage is pretty straightforward:

class Program
{
    static void Main(string[] args)
    {
        XslCompiledTransform trans = new XslCompiledTransform();
        trans.LoadString(Resource1.XSL);
        string ret = trans.TransformString(Resource1.XML);
    }
}

Resource1.XML and Resource1.XSL are simply strings that contain the documents.

Click here for the full source to XslCompiledTransform and some other handy extension methods.

Day 4: System.Windows.Threading.Dispatcher; Same Great Taste but half the Fat

With the introduction of WPF, Microsoft provided a couple very useful class for ensuring thread safety and affinity: System.Windows.Threading.Dispatcher and DispatcherObject. At the heart of it, a dispatcher class is just a standard Windows Get/Translate/Dispatch message pump with some prioritized delegate queuing and object orientation goodness added to the mix.

Unfortunately, WPF in all it's glory is not available on .NET CF; including this handy utility class. So, as you guessed, I ended up implementing it. Here's the full source to my implementation of the Dispatcher and its related classes.

Not yet Implemented:

Although the delegates are processed in prioritized order relative to each other, at the moment, the Background, ApplicationIdle, ContextIdle, and SystemIdle are not processed at what should probably be the correct time: after other Windows Message events. Exactly when they are supposed to be executed is something I'll need to look into and address.

Day 3: Outlook Notes Mobile

outlooknotes1 outlooknotes2

This isn't really so much a complaint about Windows Mobile development, but more so about hole in the platform's user experience.

If you're an avid Windows Mobile user, you are familiar with the Notes application: you can create a short text or image note and have it sync to your desktop. Then the desktop will sync it to Exchange. However, there's a couple problems:

  1. Only Pocket PCs come with the notes applications: Smartphones do not.
  2. You must use your desktop ActiveSync connection to sync with Exchange; it does not sync directly.

To resolve this, a few months ago I wrote an application that allows you to view, edit, and create notes on your Windows Mobile device, and sync directly with Exchange.

Download Outlook Notes Mobile.

Usage:

  1. Start Outlook Notes Mobile.
  2. Go to settings.
  3. Enter the same login information you provided ActiveSync. (The server will be in the format of "mydomain.com" or "mail.mydomain.com", ask your SysAdmin).
  4. Synchronize. (There is no automatic synchronization).

Note:

If you have problems getting this application to work, it may be due to your Exchange server setup. The Microsoft "recommended" Exchange deployment does not expose the Exchange Web Service (it redirects all requests to the OWA client).

Day 2: Serialization of Complex Types and XmlAttributes

This isn't really a rant about Windows Mobile per se, but it's an issue I seem to run into on a consistent basis: you can't deserialize an XmlAttribute into a struct. Consider the following code:

using System;
using System.Linq;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Xml.Serialization;
using System.IO;

namespace SmartDeviceProject2
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            XmlSerializer ser = new XmlSerializer(typeof(Foo));

            string xml = "<Foo Moo=\"2,3\"/>";
            MemoryStream mem = new MemoryStream();
            StreamWriter writer = new StreamWriter(mem);
            writer.Write(xml);
            writer.Flush();
            mem.Seek(0, SeekOrigin.Begin);
            Foo f = (Foo)ser.Deserialize(mem);
        }
    }

    public struct Bar : IFormattable
    {
        public int X;
        public int Y;
        public static Bar Parse(string s)
        {
            string[] strings = s.Split(',');
            Bar ret = new Bar();
            ret.X = int.Parse(strings[0]);
            ret.Y = int.Parse(strings[1]);
            return ret;
        }
        public override string ToString()
        {
            return string.Format("{0},{1}", X, Y);
        }

        #region IFormattable Members

        public string ToString(string format, IFormatProvider formatProvider)
        {
            return ToString();
        }

        #endregion
    }

    public struct Foo
    {
        [XmlAttribute]
        public Bar Moo;
    }
}

Attempting to run this code on the desktop would fail on the line that instantiates the XmlSerializer: "Cannot serialize member 'Moo' of type SmartDeviceProject2.Foo. XmlAttribute/XmlText cannot be used to encode complex types.".

However, on .NET CF 2.0, it fails on deserialization with the cryptic message: "The type SmartDeviceProject2.Bar was not expected. Use the XmlInclude or SoapInclude attribute to specify types that are not known statically."

But my gripe is the following: I have a struct, it is IFormattable, and it can be Parsed (like int.Parse, DateTime.Parse, et al). Why is that not sufficient enough for serialization to and from an XmlAttribute string?

Anyways, here is the workaround:

  1. For all types that you want to be deserialized from attributes, explicitly give them the [XmlElement] attribute. Yes, you read that right.
  2. Create an XmlSerializer. Attach a new method to the UnknownAttribute event.
  3. Whenever this event is fired, use a combination of the attribute name and reflection to find the PropertyInfo/FieldInfo for what XmlSerializer can't deserialize. Then find that object type's static Parse method, and call it on the attribute string. Then use that PropertyInfo/FieldInfo to set the property.

The new code would look something like this (you will need to add your own code to handle if it is a Property, since my sample is only looking for fields):

private void button1_Click(object sender, EventArgs e)
{
    XmlSerializer ser = new XmlSerializer(typeof(Foo));

    string xml = "<Foo Moo=\"2,3\"/>";
    MemoryStream mem = new MemoryStream();
    StreamWriter writer = new StreamWriter(mem);
    writer.Write(xml);
    writer.Flush();
    mem.Seek(0, SeekOrigin.Begin);
    ser.UnknownAttribute += new XmlAttributeEventHandler(ser_UnknownAttribute);
    Foo f = (Foo)ser.Deserialize(mem);
}

void ser_UnknownAttribute(object sender, XmlAttributeEventArgs e)
{
    FieldInfo field = e.ObjectBeingDeserialized.GetType().GetField(e.Attr.Name);
    MethodInfo parse = field.FieldType.GetMethod("Parse");
    field.SetValue(e.ObjectBeingDeserialized, parse.Invoke(null, new object[] { e.Attr.Value }));
}
public struct Foo
{
    [XmlElement]
    public Bar Moo;
}

Unfortunately I have not figured out a similar workaround for serialization...

Day 1 - Windows Mobile OpenFileDialog and SaveFileDialog, why are you so terrible?

Yeah, so whoever the UX person is behind the Windows Mobile version OpenFileDialog and SaveFileDialog, they need to reevaluate their career choice. Here's a screen shot of these beauties in action:

save open

What the hell is with the OpenFileDialog? You can't browse through the file system to find what you want. You gotta bust out the stylus (problem #1) and search for the file. And even then it has to be in the path of your My Documents folder (problem #2).

I try to avoid these dialogs like the plague, but finally ended up needing an OpenFileDialog. I ended up writing my own out of principle:

goodopen

Don't get me wrong, this isn't the end all be all of Windows Mobile OpenFileDialogs, but it is infinitely better than the stock version. It works basically like the standard OpenFileDialog:

  1. Create an instance.
  2. Set a filter if need be.
  3. Set the initial directory if need be.
  4. ShowDialog

I still want to tweak it a bit more so that the fonts and such are all customizable via the developer, and eventually write a SaveFileDialog (probably just throw a TextBox at the bottom). But this will do for now; you can download the assemblies for the Windows Mobile OpenFileDialog here. You'll need to deploy all the assemblies to use it, but the type in particular you should be interested in is WindowsMobile.CommonDialogs.OpenFileDialog.

30 Days of Bitching about .NET Compact Framework

If you're an aficionado of .NET Compact Framework development, you've probably stumbled across Chris Craft's blog and read 30 Days of .NET [Windows Mobile Applications].

All in all it was a great series. There were a good number of "fillers" so to speak, but the gems more than made up for it.

The tag line of my blog is "I like Windows Mobile development. Does that make me a masochist?"; and it is quite amazing how often that statement really rings true. Windows Mobile is by necessity a subset of the fully featured desktop version of Windows. And the same goes for .NET and it's little brother .NET Compact Framework. I retract that statement; "little brother" does not do .NET justice when comparing it to CF. I think "adopted bastard red headed stepchild" seems more fitting, don't you? [0][1]

Anyhow, the purpose of this post was to announce that in a sort of satirical homage to Chris' series I will be blogging/bitching the next 30 days about all the pitfalls of Windows Mobile and .NET CF. And if we're lucky, I'll describe any workarounds I may have found. Ok, I'll be up front: don't check this page every day expecting an update; I've been rather busy lately. However, I have more than enough to work with that I can catch up with any amount backlog should I fall behind.

And with that; I'll start with an easy one!

Day 0:

WPF or Silverlight. I want it on Windows Mobile. One of the more annoying aspects of Windows Mobile development is that there are so many screen resolutions to support. Designing an application that looks good on any screen resolution is quite a chore. To implement this properly, one would need a set of classes that allow you to lay out your screen elements in relative measurements rather than absolute dimensions.

The full version of .NET has a TableLayoutPanel class which has the aforementioned behavior. However, that class does not exist on .NET CF. In fact, no intelligent layout code exists from anything released by Microsoft. The Android SDK comes with this sort of UI framework in their V1 product. And I'm not too familiar with the iPhone SDK, but my guess is that when you are supporting a single screen size, it's probably not a huge issue.

Solutions:

Though I can't release the source, I basically wrote a bunch of WPF style classes to do intelligent layout. These classes include StackPanel, DockPanel, WrapPanel, Grid, and ItemsControl. You can probably just grab the WindowlessControls assembly (don't ask me why I called it that) out of any number of my released projects and try to figure out how to use it (hello ILDASM). I should also mention that all the controls support transparencies and "bubbling" up events to its parents; similar to WPF. The ItemsControl is also data driven and rendered using a ContentPresenter similar to WPF.

 

I somewhat lied about Silverlight not being available on Windows Mobile. It is, but it's in beta. The problem is that it requires it to be hosted in a browser. Unfortunately the WebBrowser control in Windows Mobile is not as fully featured, and does not give you access to the DOM. That makes reacting to events on the Silverlight control in non JavaScript code impossible; or at least I thought.

Recently I figured out a way how to invoke C# code through JavaScript. The first step is to get from JavaScript into unmanaged code:

  1. Create an ActiveX control project.
  2. Embed a WebBrowser in your Windows Forms application.
  3. Embed that ActiveX control in your browser HTML. (using <object>)
  4. Funnel all Silverlight events from JavaScript into your ActiveX control.  Now you are in unmanaged code.

Now to get from unmanaged code into managed code:

  1. In your ActiveX control project, create a DLL export that accepts and stores a function pointer or a COM object. This function pointer or COM object will be passed in from managed code.
  2. Create a DLLImport to that function. Pass in a pointer to a method/COM object in managed code.
  3. When JavaScript is invoked, your ActiveX is being called into. Now from that point, you can then call into the managed function pointer you provided earlier. I'll leave the exercise of passing and parsing arbitrary arguments up to the reader. You can basically invoke any method name and any method signatures of any C# function from JavaScript by using a crafty combination of string parsing and reflection. The solution I had was quite elegant for being a giant hack in the grand scheme of things.

And there you go! You are now creating Silverlight thick client applications just like on the desktop!

 

[0] I speak in jest. I do not mean any offense to anyone who is adopted, a bastard, or a step child. I did mean the red head remark though.

[1] Real developers zero index their blog footnotes.