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.

Browser War Continued

analytics

If you run a web site, I highly recommending using Google Analytics and Google Webmaster Tools. They're pretty freakin' amazing. I was checking out my site traffic and statistics from the last month and was pretty surprised to find that Chrome is already at 4.26% of my traffic (it's currently my primary browser while I patiently wait for my Google neural implant). That and Firefox surpasses IE? I was under the impression IE had an insane market share. But then again, this site is directed towards more technology savvy people, who are the minority of the total users who brows the web. So this sampling probably isn't representative of the whole.

Klaxon += New Features

Screen02

  • Added options to disable each sensor action while snoozed. For example, if your Flip action snoozes your phone, and you don't want to let it resnooze while snoozing (by just picking up your phone in some cases), check this box!
  • Added the ability to select a Windows Media Player playlist. You can find playlists in your \Playlists or \Internal Storage\Playlists folders. You can use Windows Media Player on your desktop to create and sync playlists.
  • When you snooze your device, the screen is automatically turned off to save power.
  • The default sound file is now \Windows\Alarm5.wav. It is not configurable in the UI yet, but you can configure it manually setting the \HKCU\Software\Klaxon\DefaultSoundFile value in the registry. (It does not exist by default, but Klaxon will use it if it is there.)

Download here.

Windows Mobile Video Power States

All in all, development on Klaxon has given me some great insight into how power states affect program execution. One of the changes I am making to the newest version of Klaxon is to turn the device's video off when the user hits snooze.

Since the earliest versions of Klaxon, I have been using the SystemIdleTimerReset call to prevent the device from going to sleep. This behavior is necessary because the application needs to continue running to pick up sensor events. However, using SystemIdleTimerReset has the unintended effect of leaving the display on: it should be turned off so as not to drain the battery or have a distracting bright light on in the room while the user is trying to sleep. So I did some searching to see if there was a way to explicitly turn off the video, but allow program execution to continue. The behavior I wanted is similar to how Windows Media Player can turn off the video but the songs keep playing, and the device does not go to sleep until Windows Media Player is stopped.

It turns out that Windows Mobile has several video power states, which are described quite nicely in an MSDN article. I ended up wrapping these native power management calls into a bow tied package for usage in C# for your reusing pleasure:

using System;
using System.Runtime.InteropServices;

namespace WindowsMobile.Utilities
{
public enum VideoPowerState
{
VideoPowerOn = 1,
VideoPowerStandBy,
VideoPowerSuspend,
VideoPowerOff
};

public static class Device
{
const int SETPOWERMANAGEMENT = 6147;
const int GETPOWERMANAGEMENT = 6148;

[DllImport("coredll")]
extern static IntPtr GetDC(IntPtr hwnd);

[DllImport("coredll")]
extern static int ExtEscape(IntPtr hdc, int nEscape, int cbInput, ref VideoPowerManagement vpm, int zero, IntPtr empty);

[DllImport("coredll")]
extern static int ExtEscape(IntPtr hdc, int nEscape, int zero, IntPtr empty, int cbOutput, ref VideoPowerManagement outData);

struct VideoPowerManagement
{
public int Length;
public int DPMSVersion;
public VideoPowerState PowerState;
}

public static VideoPowerState VideoPowerState
{
get
{
IntPtr hdc = GetDC(IntPtr.Zero);
VideoPowerManagement ret = new VideoPowerManagement();
ExtEscape(hdc, GETPOWERMANAGEMENT, 0, IntPtr.Zero, 12, ref ret);
return ret.PowerState;
}
set
{
IntPtr hdc = GetDC(IntPtr.Zero);
VideoPowerManagement vpm = new VideoPowerManagement();
vpm.Length = 12;
vpm.DPMSVersion = 1;
vpm.PowerState = value;
ExtEscape(hdc, SETPOWERMANAGEMENT, vpm.Length, ref vpm, 0, IntPtr.Zero);
}
}
}
}

Usage:

Just get or set the Device.VideoPowerState property to do whatever you need!

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.

Bullshit Bingo

bsbingo

This gave me a good laugh. Check out the Bullshit Bingo web site.

And Another Klaxon Update

Screen01

Updates and fixes:

  • Implemented new skin for the actual Alarm form
  • Possibly fixed bug where WAV files were not looping.
  • Fixed bug where editing alarms that took place on the noon or midnight hours were bugged
  • Implemented 24 hour mode (on the new UI only)
  • Vibrations are now 150 and 300 ms instead of 500ms
  • Windows Alarms are now schedule with no sound, so all the user may see is a notification (if Home Screen integration is enabled)
  • Fixed bug where One Time alarms did not show up on the Home Screen

Download the Klaxon update here.

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.

Woe is Me

I've got a ton of stuff I want to blog about, but man, I can't seem to find the time. This post is mostly just a mental note of stuff I've been working on or want to write about in the near future:

  • Object Models, XML, XSLT, DataSets, SQL, and how to make them all play nice on Windows Mobile
  • Extending the XSD tool to customize code generation
    1. Supply an XSD
    2. Implement/Override a "CodeGenerator"
    3. Generate a custom object model really easily (Let's face it, the XSD tool sucks)
    4. Profit!?
    5. (And write an MSBuild task and do all your coding in XML and the process is automated)
  • "Smart" key navigation within a form - This is an algorithm I wrote that analyzes the controls on your form and computes the best destination control given a source control and a navigation event (up, down, left, right, scroll). It's very handy in that I never need to write any key handling code; it always works, no matter the size, layout, or contents of the form.
  • Texture Loading Problem
    • Consider the following:
      • OpenGL/Direct3D textures can only be 2^N by 2^N in dimensions (let's refer to them as "square" textures)
      • Actual user textures may be any dimension (let's refer to them as user textures)
      • Loading a user texture into a square texture results in wasted memory due to empty/unused space
    • Challenge: Write an algorithm that takes user textures and creates a mosaic inside square textures to waste as little possible space.
    • Difficulty: Make it fast on a mobile device.

Windows Mobile Screen Capture

I often find myself needing to query the phone's screen dimensions or needing to take a screen shot for demo purposes. This usually resulted in me recycling some of the screen scraping code I wrote for Omnipresence (a prototype cross platform remote desktop type client). I figured I may as well post this code since it's been pretty handy to me, and that others may find it handy as well.

The code below lets you do just that:

  • PixelDimensions - Returns the device's resolution.
  • GetScreenCapture - Capture the current contents of a screen to a bitmap that can then be saved/manipulated however you please.
using System;
using System.Runtime.InteropServices;
using System.Drawing;
using System.Drawing.Imaging;

namespace WindowsMobile.Utilities
{
    public class DeviceScreen
    {
        enum RasterOperation : uint
        {
            SRC_COPY = 0x00CC0020
        }

        [DllImport("coredll.dll")]
        static extern int BitBlt(IntPtr hdcDest, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, RasterOperation rasterOperation);

        [DllImport("coredll.dll")]
        private static extern IntPtr GetDC(IntPtr hwnd);

        [DllImport("coredll.dll")]
        private static extern int GetDeviceCaps(IntPtr hdc, DeviceCapsIndex nIndex);

        enum DeviceCapsIndex : int
        {
            HORZRES = 8,
            VERTRES = 10,
        }

        public static Size PixelDimensions
        {
            get
            {
                IntPtr hdc = GetDC(IntPtr.Zero);
                return new Size(GetDeviceCaps(hdc, DeviceCapsIndex.HORZRES), GetDeviceCaps(hdc, DeviceCapsIndex.VERTRES));
            }
        }

        public static Bitmap GetScreenCapture()
        {
            IntPtr hdc = GetDC(IntPtr.Zero);
            Size size = PixelDimensions;
            Bitmap bitmap = new Bitmap(size.Width, size.Height, PixelFormat.Format16bppRgb565);
            using (Graphics graphics = Graphics.FromImage(bitmap))
            {
                IntPtr dstHdc = graphics.GetHdc();
                BitBlt(dstHdc, 0, 0, size.Width, size.Height, hdc, 0, 0, RasterOperation.SRC_COPY);
                graphics.ReleaseHdc(dstHdc);
            }
            return bitmap;
        }
    }
}

Klaxon 2.0

Klaxon2Screenshot

Oh man, I'm tired, so I'll make this quick.

New features:

  • If you have a 480x640+ screen, you will see the new UI. It's pretty. Still have to skin the alarm form, but besides that, it is done.
  • Possibly fixed bug where Klaxon would stay resident in memory.
  • The number controls are now gesture based: you can flick them and they will scroll through their range quite rapidly!
  • You can now preview the alarm using the play button. (Only available in the new UI)

To Do:

  • Time localization (such as 24 hour mode).
  • Volume control.
  • Figure out a way to turn off the backlight, but also not allow the device to go into sleep mode when snoozing.
  • Fix sound looping issue.

Thanks to expo7 from XDA-Developers for the UI design!

Download the new Klaxon here.

Windows Live Writer and Windows Server 2008

Many months ago when I discovered Windows Server 2008 with it's Hyper-V, stability, etc, I made the switch to that as the operating system on my primary workstation. However, the one thing I've really been missing is Windows Live Writer: for whatever, reason it is not able to install on Windows Server 2008. So, for the time being, I continued blogging from my Vista machine that was still tasked with being my media center.

Recently I discovered that you can install the Technical Preview of Live Writer on Windows Server 2008. Then I tried just overwriting the Writer directory from my Vista machine to Server 2008, and it all works fine! So this is my first official blog from Windows Server 2008.

Bandwidth Problems Solved!

Thanks to a kind soul from XDA-Developers, I've been able to mirror my various downloads on their hosting service, for free! Thanks again to Rich from BlurryFox! Most of that bandwidth was eaten by Klaxon, so if that's what you are looking for, the download is available at the new mirror.

Browser War: Google Chrome, IE8, Firefox 3 + Ubiquity

Been doing a lot of work related research around mobile browsers and usability lately. After reading the 38ish page comic about Chrome, watching videos about Ubiquity, and hearing Microsofties talk about how awesome IE8 was, I decided to just try all of them myself.

Initial impressions:

Firefox 3 + Ubiquity

I'd played around with Firefox 3 earlier this year, and although it is better than IE7, I'm a creature of habit, and it didn't offer me anything compelling enough to switch completely.

I installed Ubiquity as well as a bunch of commands from the Ubiquity herd. The Mozilla Labs video makes this this thing look sweet, but in reality, the "Natural Language Input" felt more like I was back in the early 80s playing Zork. Maybe I'll get used to it after a while. It has a lot of potential, but I don't know if I really find value in trying to make already simple tasks even simpler.

Chrome

Fast... very fast. V8 is pretty impressive. Puts even Firefox to shame. And there's so much screen real estate, which is something the other browsers could learn to value. It's still a pretty early beta, so there aren't many fancy features to write about. The main "new and different" thing that caught my eye is that you can undock tabs into their own windows. And you can move tabs or windows into other windows. Pretty handy. I often find myself with several browsers open, with pages I want to keep open, but want to move them all into the same window so I save on task bar real estate. And Incognito Mode would be handy for any young man wishing to visit questionable sites on their computer.

Internet Explorer 8

I was pretty scared to install this. IE8 is supposedly uncoupled from the shell and operating system, but I don't know if I buy that. And installing a beta product that can potentially make your whole system go haywire just gives me the willies. So I chose to install this on a VM. It's the same old same old for the most part. Three things caught my eye though.

  • The search bar anticipates what you are going to type and shows common searches in a drop down box, similar to how Google does on their home page. Not even Chrome does this!
  • It groups related tabs by color; if you open a new tab from a link, it will group those two tabs together.
  • Accelerators are kind of neat. It's sort of like having a list of all Ubiquity commands available up front. It would be nice if the Accelerators were smart enough to know when it is applicable to a highlighted selection, and filter themselves accordingly.

But all in all, the stability of IE8 was pretty solid from my experience.

 

I'm probably going to stick to playing with Chrome for a while. The extra screen real estate and fancy dockable tabs make me happy. Maybe as Ubiquity develops, I'll switch to that, but at the moment it is not compelling enough. And I've already resigned to the fact that we'll be putting Google Chips (TM) into our head by 2020.

Where oh Where has My Bandwidth Gone

Well, koushikdutta.com is averaging around 1000 unique hits a day. And that is really taxing the free hosting provided courtesy Google Pages. As a result, a lot of the download links go down intermittently as they reach their page view/bandwidth maximum. From the looks of it, Klaxon, Sensory Overload, and Outlook Notes Mobile are the bandwidth hogs. I'll look into an alternate hosting service. But, I'm too cheap to pay anything significant for hosting a simple blog site. If anyone knows of any decent hosting services, let me know!

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.