Thursday, November 06, 2008
So a few months ago we released our CAB Installer SDK, and we decided to try out value-based pricing as a social experiment.  Our thinking was that developers make their living - and typically a more comfortable living than flipping burgers - and that the would a) understand the value of code and time saved and b) be willing to compensate us for the SDK based on their perceived value of the product.

We here we are a full four months later, and how is this experiment going?  Well here's a graph that says it all:




What this says is that we've sold 51 "value units" to 37 customers, meaning that over 80% of you who bought it only paid $5.  Now assuming you're a low-paid, entry-level guy making only $40k a year that means you felt it's worth just over 15 minutes of your time (and keep in mind this thing comes with full source code).

What this tells me is that one of these must be the case:

1) The SDK sucks and has no value
2) People don't understand "value-based" pricing
3) Developers are cheap bastards who will pay as little as possible for something

Well #1 is probably not true, as we've used it on a few projects and it works well.  I think we descibed value-based pricing pretty clearly, and it's not a tough concept.  So all I can conclude is, well, #3 must be true.  Now we can't really hold it against you, after all we did allow you to buy it for $5 and some people simply have low moral standards.  I'm just surprised it's so many of you.

Will we change the pricing model?  Well I have two thoughts on that.

1) the current pricing includes zero support, so it's no "work" for us to just leave it as-is
2) moving it to fully open source might increase the number of people using it

I'm inclined to go with #1, simply because moving it to open source requires a bit of work on our part.  In short, we'll keep it out there as an apparent $5 product because I'm too busy to do anything else with it, but the likelihood of it getting any additional features is pretty slim.  It was an experiment that yielded data, and as such I'd say that it was valuable.  It certainly shows that it's a pricing model that can't be used to support a business.

11/6/2008 10:13:06 AM (Eastern Standard Time, UTC-05:00)  #    Comments [6]  | 
 Monday, October 20, 2008
As with most developers, I have a large pool of code snippets from work, tests and investigations I've done in the past.  Sometimes these grow into products, other times they just sit on a hard drive waiting for a time for me to actually use them.

Over the coming weeks I plan to blow the dust off of some of these and see what looks worthwhile.  Simple stuff might get rolled into the SDF (I rolled in a keyboard hook a couple weeks ago) and some stuff will get pushed out as open source libraries because we simply don't need any more products generating any more support load on us at the moment.

Hopefully we'll come up with a coherent place to put all of these in the near future, but for now I'll just post them here on my blog.

The first is the series is called POOMHelper.  I put this together back when WinMo 5.0 first came out while doing work that required that I detect when a POOM item was modified or deleted.  It's a fairly straightforward piece of code that hooks into the eventing mechanism of POOM, plus it also allows you to set some properties of POOM items (mostly Contacts IIRC) that didn't exist in the POOM object model at the time (they may have since been added).

Here's what the public object model looks like:

POOMHelper.PNG

The code is released under our MIT X11 license, so do with it what you wish.  If you need support or help, we offer consulting services.

Download the POOMHelper source here
Download the Interop.PocketOutlook.dll assemly(64 KB) here.
10/20/2008 10:44:21 AM (Eastern Daylight Time, UTC-04:00)  #    Comments [0]  | 
 Thursday, October 02, 2008

One of the things we tend to take for granted and assume to be true is that identical code will produce identical behaviors across different platforms.  It only seems sensible that if we write some C# code that compiles and runs for the full framework as well as the compact framework that the resulting behavior, provided the code isn't obviously platform dependent, should behave the same.  Right?

Apparently not.

While porting some networking code today from the device to the desktop, I was having failures in code that I was certain worked.  It turns out that sockets don't behave the same.  Since a Socket class is an abstraction of something that is pretty damned standard, the fact that we have this disparity surprises and alarms me.  It smells an awful lot like a bug.

Want to try it yourself?  I put together some pretty basic repro code:

class Program

{

private Socket m_serverSocket;

private ManualResetEvent m_requestDoneEvent = new ManualResetEvent(false);

public static void Main()

{

Program p = new Program();

p.Run();

}

public void Run()

{

IPEndPoint localEndpoint = new IPEndPoint(IPAddress.Any, 90);

m_serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

m_serverSocket.Bind(localEndpoint);

Thread serverThread = new Thread(ServerThreadProc);

serverThread.IsBackground = true;

serverThread.Priority = ThreadPriority.AboveNormal;

serverThread.Start();

Thread.Sleep(100);

Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

EndPoint ep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 90);

clientSocket.Connect(ep);

m_requestDoneEvent.WaitOne(10000, false);

}

private void ServerThreadProc()

{

m_serverSocket.Listen(10);

m_requestDoneEvent.Reset();

m_serverSocket.BeginAccept(AcceptRequest, m_serverSocket);

// wait for the async accept to complete

m_requestDoneEvent.WaitOne(10000, false);

}

private void AcceptRequest(IAsyncResult result)

{

Debug.WriteLine("\r\n\n -------------------------------------------");

Debug.WriteLine("| The current Platform is " + Environment.OSVersion.Platform.ToString());

Debug.WriteLine("| The server socket reports Connected == " + m_serverSocket.Connected.ToString());

Debug.WriteLine(" -------------------------------------------\r\n\n");

m_requestDoneEvent.Set();

}

}


And the outputs:

On the desktop:

 -------------------------------------------
|  The current Platform is Win32NT
|  The server socket reports Connected == False
 -------------------------------------------

On the device:

 -------------------------------------------
|  The current Platform is WinCE
|  The server socket reports Connected == True
 -------------------------------------------

WTF?!


	
10/2/2008 6:40:17 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [6]  | 
Our push toward Continuous Integration, Test Driven Development and overall code quality here at OpenNETCF is a continuous learning experience.  One of the issues for unit/integration tests we've run into is that many SDF classes require an application message pump in order to "operate" properly.  A classic case it the FileSystemWatcher (FSW).  The FSW handles all of its eventing using the underlying OS architecture, which in turn uses windows messages (wouldn't have been my choice, but hey, I didn't architect it).

Well for windows messages to get dispatched, you need a message pump.  In a normal application, this is set up for you when you call Application.Run but in a unit test there isn't such a call, and MSTEST certainly doesn't spin up a pump for you.  We also don't really want to create a Form just for our test (Application.Run requires a Form instance be passed in) and Application.Run blocks until the Form is closed so how wopuld we run our test and the pump anyway?

The solution I came up with is to use the OpenNETCF.Windows.Forms.Application2.Run method, which doesn't require a Form, and to spawn it in a background thread at the start of the test, then at the end of the test call Application2.Exit, something like this:


      ThreadPool.QueueUserWorkItem(delegate(object o)
        {
          OpenNETCF.Windows.Forms.Application2.Run();
        });

      // run test here

      OpenNETCF.Windows.Forms.Application2.Exit();

Of course just having a Pump doesn't necessarily mean that your messages will be dispatched when you want, and keep in mind that Windows messages are very low priority, so they might not get posted or dispatched immediately, so that adds more complexty to the test.  After you perform an action that causes a message (like creating a file) you want to call Application2.DoEvents to force messages to be dispatched, and you probably want to do it a few times after having your thread yield to make sure that the scheduler actually gets around to posting the things to the queue in the first place.

In the end it all works, it's just not near as clean as one would hope for in a unit test.  Here's an example of an FSW test from our production system:


AutoResetEvent m_testEvent = new AutoResetEvent(false);

[TestMethod()]
public void FileSystemWatcherCreatedWatchDirEventTestPositive()
{
  ThreadPool.QueueUserWorkItem(delegate(object o)
    {
      OpenNETCF.Windows.Forms.Application2.Run();
    });

  FileSystemWatcher fsw = null;
 
  string filename = "\\Temp\\Test.txt";
  if (File.Exists(filename)) File.Delete(filename);

  try
  {
    fsw = new FileSystemWatcher("\\Temp", "*.*");
    fsw.Created += new FileSystemEventHandler(fsw_Created);
    fsw.EnableRaisingEvents = true;

    m_testEvent.Reset();

    File.CreateText(filename).Close();
    // give time for the message to get queued, and flush the queue a few times
    for (int i = 0; i < 10; i++)
    {
      Thread.Sleep(20);
      OpenNETCF.Windows.Forms.Application2.DoEvents();
    }

    // check for an event
    Assert.IsTrue(m_testEvent.WaitOne(1000, false), "Create Event did not fire");
  }
  finally
  {
    // clean up
    OpenNETCF.Windows.Forms.Application2.Exit();
    File.Delete("\\Test.txt");
    if( fsw != null) fsw.Dispose();
  }
}

void fsw_Created(object sender, FileSystemEventArgs e)
{
  m_testEvent.Set();
}
10/2/2008 12:04:26 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [1]  | 
 Friday, September 26, 2008
So yesterday I decided that I'd start a longer-term project of creating a decent WiFi viewer application for PPC/WinMo.  Basically something to use as a good sample for the Smart Device Framework, as well as to help test out several of the features (those classes are nearly impossible to automate testing for).  I've decided to use it as a showcase for everything I can think of as long as it isn't superfluous - so using OpenNETCF UI controls, etc.

One of the first snags I hit is with the test device I just happen to be using - an Axim x51.  I ran the app and it finds the wireless NIC, no problem.  I then added some code to handle when the adapter comes or goes (like when it's powered on and off).  Again, fine.  I then set out to do the list of nearby APs, so I set up an open AP and connected to it with the built-in utility.

Now all of a sudden my app doesn't see the wireless NIC.  WTF?  Gotta be a bug in the SDF, right?  So I trace it down and it turns out that there doesn't seem to be a bug.  a call to IPHLPAPI.DLL's GetInterfaceInfo() method simply says there's only one adapter - the USB connection.  Strange.  Maybe it's some state problem.  I power the radio off and back on (again using the built-in tool) while running my app.  The adapter shows up again (hooray) but only briefly, then it disappears again.  WTF?  So after several of these cycles, I find that as soon as the adapter connects to an AP, it no longer shows up as an adapter using the IPHLPAPI function. 

It appears that somehow they've decided that either when they connect or when they bind that the interface needs to be hidden - maybe to prevent other apps from interfering with theirs.  Yet another genius decision by an OEM.  Why would anyone ever want to do something that's not built in?  Off to eBay to find another device on which this will (hopefully) work I guess.

9/26/2008 12:54:03 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [1]  | 
 Friday, September 19, 2008
Smart Device Framework 2.3 is really close to release - we've frozen the code base and have been working on the installer (a large chunk of work since we're getting it fully automated).  The hope is that with all of this CI infrastructure in place, we'll be able to turn SDF releases far more frequently (right now it's taken over a month to go from the decision to release to get where we are - and the release still hasn't shipped). 

In order to turn out releases more often, we need ideas for features to add - after all we need a reason to release.  This is where you come in.  Navigate over to the SDF Product Page and you'll see a new "Feedback" widget over on the side (courtesy of UserVoice).Click on it and create or vote for feature ideas.

We've also added the widget to the Padarn page, so if you've a Padarn user, let us know what you'd like to see there too.


Smart Device Framework Suggestions

Padarn Suggestions

9/19/2008 12:54:42 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [1]  |