Thursday, August 14, 2008
If you're using the sample code from Alex Feinman's MSDN article on hosting ActiveX controls, then you might be interested to know that we've found and fixed a bug in it.  The original code doesn't properly clean up and destroy the native control instances, so the native destructor is never called and you leak objects.  For many things like Media Player, where you create one control and use it for the life of your app it's not much of a problem, but if you're creating and disposing a lot of controls in your app, it is a problem.

The fixed file is available here [AxHost.zip (7.89 KB)]
8/14/2008 10:40:38 AM (Eastern Daylight Time, UTC-04:00)  #    Comments [0]  | 
 Tuesday, August 12, 2008
We've been getting a lot of inquiries lately about our plans for releasing the SDF built for Visual Studio 2008 and against CF 3.5, so I'll lay out our current status and short-term plan.

I realize we're a bit late in releasing a version for Studio '08.  Now you might say to yourself "how hard can it be?  Just open the solution in Studio '08, let it upgrade, recompile and release."  Sure, it could be that simple if we wer content with just tossing it out there, but we're not.  With the move to Studio '08 we decided to take advantage of some of the new tools we have.  First we migrated the entire SDF source tree from Vault to TFS.  Vault worked just fine, but we wanted to take advantage of TFS and integrate both continuous integration, automated testing and test-driven development into the product. 

That mean rearchitecting the solution and projecy layouts and then writing tests.  Lots of tests.  Of course writing tests leads to finding bugs, which then leads to fixing bugs.  We started by looking at reported bugs but also looking at some use cases and testing classes we know get the most use.  We have no delusion that we're going to have even close to full code coverage (or even 50%) by our next release, but we want to get off on the right foot and at least have some coverage for the next release.

Of course we've also added some new features like the OpenNETCF.Net.Mail namespace and all of this takes time.  As of right now we have less than 40 hours of test writing left to hit our release milestone.  Once we hit that, we then have to build the Help and installation package and release.  My hope is to have something ready in early September, but that's not a guarantee.  We know you want the release - we do - we just want to make sure it's right.

An ancillary question that also comes up is "when will we be releasing a version compiled for CF 3.5?"  As of right now we have no plans to release a CF 3.5-targeted version of the SDF.  Yes, you read that right.  We have no plan for a CF 3.5 release.  "Why is that?" you might ask, after all CF 3.5 is the latest and greatest, right?  Sure, it is, and we think that when possible you should use it.  However the SDF has historically been used by developers using older versions of the CF and is already rolled out in a *lot* of CF 2.0 projects.  If we moved to 3.5, none of those 2.0 project would be able to use the SDF without recompiling themselves.  If we moved to 3.5, then we'd also be tempted to use 3.5 features, which would then even make the source incompatible with 2.0 and a recompile wouldn't even be an option. I, for one, don't really want to leave all of those CF 2.0 developer's high and dry. 

Since CF 3.5 assemblies are unusable in CF 2.0 projects, but CF 2.0 assemblies can be used without a problem in CF 3.5 applications it makes the decision pretty simple.  If we stay with CF 2.0 as a target, then far more people can use the library.  If you absolutely must have it built targeting CF 3.5, you can always recompile the source yourself.

8/12/2008 12:03:11 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [0]  | 
 Wednesday, May 28, 2008

Since the Compact Framework doesn't have support for App.Config file, we created our own implementation that follows the full framework model.  It requires that the config file be named “MyApp.exe.config” (which is how it works on the desktop) with a subset of the desktop functionality and it's worked well for some time.  Until recently that is.

Yesterday I set out to create some unit tests for some of our OpenNETCF.Rss namespace objects.  Well the FeedEngine object requires information from an app.config file on construction, so I figured it would be simple - I'd just add an app.config file to the test project and mark it as a DeploymentItem.  After a little investigation I found that the calling assembly for a device unit test is SmartDeviceTestHost.exe, which by default runs out of \Program Files\SmartDeviceTest on the target device.  That means that to use your own app config file from a unit test it would need to be named SmartDeviceTestHost.exe.config. 

Interestingly (or frustratingly, depending on when you asked me yesterday), this test host deploys *its own* version of a config file with the same name with some info on what framework it’s running against.  The test framework just heavy-handedly overwrites any existing file rather than merging its contents into the existing one, and it overwrites *after* it deploys all of the test pieces, so you can’t just merge its contents into your own app config and use it.

As a workaround I actually modified the OpenNETCF implementation for app config files.  I didn't really want to, but the only other solution I could think of was to write code that would open the MS-deployed version and do a manual merge in the unit  test code before the test is run, and that seemed like a much uglier route. The OpenNETCF Configuration implementation now looks for a file named MyApp.exe.config.unittest before looking for MyApp.exe.config and uses the "unittest"-suffixed version if it’s there.  I then modified my TestBase class (from which all of my unit tests derive) to add this:

 

 

        [TestInitialize]

        public virtual void TestInitialize()

        {

            CopyTestConfigFile();

        }

 

        private void CopyTestConfigFile()

        {

            // copy the config file to the test host folder

            string src = Path.Combine(TestContext.TestDeploymentDir, "SmartDeviceTestHost.exe.config");

            string dest = Path.Combine(TestHostFolder, "SmartDeviceTestHost.exe.config.unittest");

            if ((File.Exists(src)) && (!File.Exists(dest)))

            {

                File.Copy(src, dest);

            }

        }

 

        public string TestHostFolder

        {

            get

            {

                return  Path.GetDirectoryName(

                        Path.GetDirectoryName(

                        Path.GetDirectoryName(

                        Assembly.GetCallingAssembly().GetName().CodeBase)));

            }

        }

 

Now I simply add SmartDeviceTestHost.exe.config to the unit test project, mark it as a deployment item and voila - it works as expected.  Just how it should have yesterday morning when I set out to write a couple simple tests.

And for the record - the current "solution" for debugging device unit tests (which involves putting in a Debugger.Break() call in the unit test and then doing an "attach to process" from another instance of Studio) is an unweildy pain in the ass.  It takes no less than a minute just te get a unit test running and in a state that you can step through code.  That might not sound like a lot, but try this: put a breakpoint in your code and when the debugger hit is, wait a full minute before you step or look at the Locals window.  Now do this every time you want to debug.

 

5/28/2008 12:46:42 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [2]  | 
 Monday, May 05, 2008

Finally!  Someone has created a code profiler for the .NET Compact Framework.  And what's even more exciting is that it's free.  I've not used it, so I can't vouch for how well it works, or how easy it is to use, but the fact it exists is quite promising.

5/5/2008 8:14:17 AM (Eastern Daylight Time, UTC-04:00)  #    Comments [1]  | 
 Saturday, April 12, 2008

In a recent project we decided to migrate the application's data engine from SQL CE 3.1 to SQL CE 3.5.  The challenge with this is that the file formats themselves are not compatible and trying to connect to a 3.1 database usign the 3.5 objects will cause an Exception.  3.5 provides a method to upgrade 3.1 database files, but to use it you really need to know that the file is in 3.1 format to begin with.

Of course you could always put in logic to connect and catch the exception and then upgrade, but let's face it - using exceptions for flow control is a bad practice and any ime it can be avoided it should be. 

Surprisingly, the SQL CE team didn't give us any mechanism otherwise to determine a database file's version, so I put together the following (which should work on the desktop as well as the device):

internal enum SSCEVersion
{
    Unknown,
    v3_1,
    v3_5
}

internal static SSCEVersion GetDatabaseVersion(string path)
{
    uint signature = 0;

    using (FileStream stream = new FileStream(path, FileMode.Open))
    {
        using (BinaryReader reader = new BinaryReader(stream))
        {
            stream.Seek(16, SeekOrigin.Begin);

            signature = reader.ReadUInt32();
        }
    }

    switch (signature)
    {
        case 0x00357b9d: // 3.5
            return SSCEVersion.v3_5;
        case 0x002dd714: // 3.1
            return SSCEVersion.v3_1;
        default:
            return SSCEVersion.Unknown;
    }
}

4/12/2008 3:11:20 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [0]  | 
 Saturday, January 26, 2008
If you follow my blog much, then you know performance is a bit of an interest of mine.  Well here's one that pretty much pulls the pants down on the CF loader.  Nothing like a 3 second load time for some classes.  Admittedly it's an edge case and there seems to be some wacky logic in the repro code, but it's still pretty interesting. And kudos to Noah for looking into it, finding the problem and at least explaining why it's happening and how to mitigate it at least to a degree (and to his managers for letting him do it).  Without doubt the CF team is the most transparent team I've encountered at Microsoft.  They readily admit where they've got shorcomings and are happy to explain why we see problems.  One can only hope this practice continues.

1/26/2008 1:11:37 AM (Eastern Standard Time, UTC-05:00)  #    Comments [0]  | 
 Tuesday, January 22, 2008
It took me 5 months to actually get it out the door, but I've just published a new white paper on the performance implications of P/Invoking in the COmpact Framework.  Check it out here.

1/22/2008 5:12:07 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0]  | 
 Thursday, January 03, 2008
 Monday, December 31, 2007
Before I go any further, let me state for the record that this library does not provide any ability to play MP3 files, so if that's what you were thinking from the name, then don't get too excited.

The OpenNETCF.Media.MP3 library is a library that allows you to read and write ID3 tags to MP3 files.  It's both Compact Framework 2.0 and Full Framework 2.0 compliant.  For a usage sample, see the AudioTagR project.

The general object model looks like this:



It's licensed under our very open MIT X11 license.
Download the full source code (C#) here.

12/31/2007 3:25:40 PM (Eastern Standard Time, UTC-05:00)  #    Comments [1]  | 
 Thursday, December 06, 2007

So I picked up a cheap PTZ (pan/tilt/zoom) camera off eBay a couple weeks ago for doing some R&D on a project I'm working on.  My hope was I'd be able to hook it up and talk to it from a device and get streaming video into an app.  Well it turns out that it's not so simple.  All cameras appear to have some form of proprietary interface that obviously varies from OEM to OEM.

I hooked up WireShark in hopes that I could reverse engineer the network commands fromthe packets, but it looks like it would take me weeks to get it figured out, and I don't have that kind of time (or desire) so I shot off an email to the OEM.  While I wait for them to reply (if they ever do) I went about reverse-engineering a kludge.

The device has a built in web server that allows you to control the camera, view video, etc.  So I opened up a page and looked at the source.  It contained a frameset, so I started navigating through frame pages and deconstructing the html to see what commands did what.  Unfortunately the video streaming piece appears to be in a compiled Java applet so getting at that turned out to be a dead-end, however it wasn't a total loss.  I did figure out how to send it commands to pan, tilt and capture single frames (well I figured out a lot more, but I limited my implementation to those functions for now).

So armed with what I knew, I slapped together the following class, basically simulating myself as a browser:

using System;
using System.Net;
using System.Net.Sockets;
using System.Drawing;
using System.IO;
using System.Threading;
using OpenNETCF.Peripherals.Camera;

namespace OpenNETCF.Peripherals
{
  public class MegaTecCamera : ICamera
  {
    private const int CMD_UP = 1;
    private const int CMD_DOWN = 2;
    private const int CMD_LEFT = 3;
    private const int CMD_RIGHT = 4;

    private string m_ip;
    private string m_username;
    private string m_password;

    public MegaTecCamera(IPAddress address, string username, string password)
    {
        m_ip = address.ToString();
        m_username = username;
        m_password = password;
    }

    public Image GetImage()
    {
        Image img = null;

        string command = string.Format(http://{0}/pda.cgi?user={1}&password={2}&page=image&cam=1
            m_ip, m_username, m_password);

        byte[] buffer = new byte[10000];
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(command);
        HttpWebResponse response = (HttpWebResponse)request.GetResponse();
        using (Stream stream = response.GetResponseStream())
        {
            try
            {
                img = new Bitmap(stream);
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine("Capture failed: " + ex.Message);
            }

            response.Close();
        }
        return img;
    }

    public void Pan(PanDirection direction)
    {
        if (direction == PanDirection.Left)
            SendHttpCommand(GetDirectionCommand(CMD_LEFT));
        else
            SendHttpCommand(GetDirectionCommand(CMD_RIGHT));
    }

    public void Tilt(TiltDirection direction)
    {
        if (direction == TiltDirection.Up)
            SendHttpCommand(GetDirectionCommand(CMD_UP));
        else
            SendHttpCommand(GetDirectionCommand(CMD_DOWN));
    }

    private string GetDirectionCommand(int direction)
    {
        return string.Format(http://{0}/pda.cgi?user={1}&password={2}&page=execute&cam=1&command={3},
            m_ip, m_username, m_password, direction);
    }

    private void SendHttpCommand(string command)
    {
        ThreadPool.QueueUserWorkItem(new WaitCallback(delegate(object o)
        {
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(command);
            try
            {
                request.GetResponse().Close();
            }
            catch { }
        }));
    }
  }
}

The next obvious question is "What the hell do I do with this class that is of any use?"

Well that's the fun part!  I integrated it into a sample page on our demo Padarn server, so we now have images captured from an IP camera streamed back to a 200MHz Windows CE device that in turn serves up those images (and control of the camera) using an ASP.NET server.

The next step I'll add is the ability to turn on and off the light in the room via a web page.

 

12/6/2007 6:15:00 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0]  | 
 Thursday, November 29, 2007
We just published a new article on using WCF from device applications on the Community siteRead it here.

11/29/2007 2:40:14 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0]  | 
In case you missed it, I published a new article on our community site last week on how to get a DateTime.Now equivalent with the milliseconds field filled in.  Read it here.

11/29/2007 11:20:53 AM (Eastern Standard Time, UTC-05:00)  #    Comments [0]  | 
 Tuesday, November 13, 2007
We've proactively pulled (meaning no one threatened us or asked us to do it) our QRCodeCF Library from public availability due to internal concerns about GPL.  Personally I dislike GPL due to its viral nature - it makes using any GPL code in any software that you intend to use for commercial or proprietary systems extremely risky, and we simply don't want to perpetuate that.  We write software to solve problems, and not surprisingly I feel those who write software should be able to get paid for doing so and not worry that their entire business might have to be given away because of some insane licensing issue.