# Thursday, October 18, 2012

A new customer came to me late last week with an interesting problem.  They have hundreds of Motorola/Symbol MC70 barcode scanners in the field and occasionally the flash memory on the devices gets corrupted. 

The current “solution” these guys are using involves removing the flash chips from the board, reprogramming it in a separate device, re-balling the BGA pins, and re-soldering it to the board. That explains why they desperately want an application that can do it.

They know the range where the corruption occurs and wanted an application that would allow a user to read, update and rewrite the corrupted bytes in flash.  They had talked with five separate developers before they found me, and all 5 had agreed that it was impossible, so naturally I took that as a challenge.

First, there are lots of things to know about how flash access works.  Most importantly, it’s not like RAM.  You can’t just map to it, then go about your merry way doing 32-bit reads and writes.  You can read it that way, sure, but writing is a whole new game.  Flash is broken up into “blocks” (which aren’t even always the same size - in the case of the MC70, the first 4 blocks are 64k long, and the rest are 256k long.) and writes must be done following this general procedure:

  1. Read the *entire* block that contains the byte you want to change into RAM
  2. Change to flash to Lock mode (a flash register write)
  3. Unlock the block of flash (another register write)
  4. Change the flash to erase mode (register write)
  5. Erase the *entire* block of flash (which writes all FF’s to it)
  6. Change the flash to write mode (register write)
  7. Update the RAM buffer with your changes
  8. Write in the *entire* block block to flash
  9. Tell the flash to commit (register write)
  10. Wait for the flash to finish (register read)
  11. Put the flash back into read mode (register write)

Oh, and if you get any of this wrong, you’ve made yourself an expensive brick.  The only solution at that point is the de-soldering and reprogramming route, and I don’t have that kind of hardware in my office.

So I started writing the app Monday morning, using C# since I had to create a UI for the editor, and on Wednesday morning this is what I delivered:

FlashEdit

So, in just 2 days I did what was “impossible”. I not only wrote all of the flash access code, I also wrote a hex editor control and an app UI to make use of the flash library.

Thursday, October 18, 2012 11:47:16 AM (Central Daylight Time, UTC-05:00)  #     | 
# Monday, September 17, 2012

I’ve recently started refactoring a customer’s code base for a working application.  They recognize the need to make their code more extensible and maintainable so I’m helping to massage the existing code into something that they will be able to continue shipping and upgrading for years to come without ending up backed into a corner.

One of my first suggestions was to start eliminating the abundance of static variables in the code base.  In this case, static classes and methods abound, and it looks like it was used as a quick-and-dirty mechanism to provide singleton behavior.  Now I’m not going to go into depth on why an actual singleton might have been better, or the pitfalls of all of these statics.  Write-ups on that kind of thing about in books and on line.

Instead, let’s look at what it means to migrate from a static, an instance or a singleton over to using a DI container, specifically OpenNETCF’s IoC framework.

First, let’s look at a “service” class that exposes a single integer and how we might consume it.

   1: class MyStaticService
   2: {
   3:     public static int MyValue = 1;
   4: }

And how we’d get a value from it:

   1: var staticValue = MyStaticService.MyValue;

Simple enough.  Some of the down sides here are:

  • There’s no way to protect the Field value from unwanted changes
  • To use the value, I have to have a reference to the assembly containing the class
  • It’s really hard to mock and cannot be moved into an interface

Now let’s move that from a static to an instance Field in a constructed class:

   1: class MyInstanceService
   2: {
   3:     public MyInstanceService()
   4:     {
   5:         MyValue = 1;
   6:     }
   7:  
   8:     public int MyValue { get; set; }
   9: }

Now we have to create the class instance and later retrieve the value.

   1: var service = new MyInstanceService();
   2:  
   3: // and at a later point....
   4: var instanceValue = service.MyValue;

We’ve got some benefit from doing this.  We can now control access to the underlying value, making the setter protected or private, and we’re able to do bounds checking, etc.  All good things.  Still, there are downsides:

  • I have to keep track of the instance I created, passing it between consumers or maintaining a reachable reference
  • I have no protection from multiple copies being created
  • The consumer must have a reference to the assembly containing the class (making run-time plug-ins very hard)

Well let’s see what a Singleton pattern buys us:

   1: class MySingletonService
   2: {
   3:     private static MySingletonService m_instance;
   4:  
   5:     private MySingletonService()
   6:     {
   7:         MyValue = 1;
   8:     }
   9:  
  10:     public static MySingletonService Instance
  11:     {
  12:         get
  13:         {
  14:             if (m_instance == null)
  15:             {
  16:                 m_instance = new MySingletonService();
  17:             }
  18:             return m_instance;
  19:         }
  20:     }
  21:  
  22:     public int MyValue { get; set; }
  23: }

And now the consumer code:

   1: var singleTonValue = MySingletonService.Instance.MyValue;

That looks nice from a consumer perspective.  Very clean.  I’m not overly thrilled about having the Instance accessor property, but it’s not all that painful.  Still, there are drawbacks:

  • The consumer must have a reference to the assembly containing the class (making run-time plug-ins very hard)
  • If I want to mock this or swap implementations, I’ll got to go all over my code base replacing the calls to the new instance (or implement a factory).

How would all of this look with a DI container?

   1: interface IService
   2: {
   3:     int MyValue { get; }
   4: }
   5:  
   6: class MyDIService : IService
   7: {
   8:     public MyDIService()
   9:     {
  10:         MyValue = 1;
  11:     }
  12:  
  13:     public int MyValue { get; set; }
  14: }

Note that the class is interface-based and we register the instance with the DI container by *interface* type.  This allows us to pull it back out of the container later by that interface type.  The consumer doesn’t need to know anything about the actual implementation.

   1: // Note that the Services collection holds (conceptually) singletons.  Only one instance per registered type is allowed.
   2: // If you need multiple instances, use the Items collection, which requires a unique identifier string key for each instance
   3: RootWorkItem.Services.AddNew<MyDIService, IService>();
   4:  
   5: // and at a later point....
   6: var diValue = RootWorkItem.Services.Get<IService>().MyValue;

Mocks, implementation changes based on environment (like different hardware) and testing become very easy.  Plug-in and run-time feature additions based on configuration or license level also are simplified.

Monday, September 17, 2012 1:07:54 PM (Central Daylight Time, UTC-05:00)  #     | 
# Wednesday, September 12, 2012

Yes, VS 2012 will support CE development, including Compact Framework 3.9.  Support is scheduled to be released in Q1 of next year.  See this video for more details.

Wednesday, September 12, 2012 6:23:08 PM (Central Daylight Time, UTC-05:00)  #     | 
# Wednesday, August 29, 2012

I’ve just checked in new code changes and rolled a full release for the OpenNETCF ORM.  The latest code changes add transaction support.  This new release adds a load of features since the last (the last was way back in February), most notably full support for SQLite on all of the following platforms: Windows Desktop, Windows CE, Windows Phone and Mono for Android.

Wednesday, August 29, 2012 12:12:37 PM (Central Daylight Time, UTC-05:00)  #     | 
# Wednesday, August 08, 2012

I’ve released version 1.0.12221 of the OpenNETCF IoC framework.  Not a whole lot new going on – it was primarily bug fixes – but binaries for all supported Platforms (Full framework, Compact Framework, Windows Phone and Mono for Android) are included.  If you’re using it, how about showing me some love and rating it (other than “Have not used it yet” – what kind of review is that?).

http://ioc.codeplex.com/releases/view/82674#ReviewsAnchor

Wednesday, August 08, 2012 10:38:34 AM (Central Daylight Time, UTC-05:00)  #     | 
# Friday, June 15, 2012

It was easy to miss, but Microsoft publicly announced that Smart Device development projects will, in fact, be released in Visual Studio 2012 in Q1 of 2013.  It’s not news to me, but it is to most, and it’s way, way past due.

Friday, June 15, 2012 9:50:07 AM (Central Daylight Time, UTC-05:00)  #     | 
# Friday, March 30, 2012

Ages ago I created an SDK that allows you to extract device CAB files and create a replacement for wceload if you wanted (we used this on a couple customer applications).  We also tried to sell it as an experiment in "value-based pricing".  Well the experiment showed, largely, that people would pay the minimum and very, very rarely come back and pay anything more so either it was of low value, or people are just cheap.

At any rate, I don't feel like maintaining it internally any longer so it has become yet another project that I've open sourced for the community at large.  The full download is now available over on Codeplex.

Friday, March 30, 2012 10:55:07 AM (Central Standard Time, UTC-06:00)  #     | 
# Tuesday, March 06, 2012

I just got an email in the Support inbox over at OpenNETCF asking about the libraries support (or really lack thereof) for Wireless Networking in Windows Embedded Compact 7 (rolls off the tongue, doesn’t it?).  While this is a question about a specific feature of the SDF, it really opens up the broader question of what our plans are for the future of the whole product.  In fact you could say it really opens up the question on what our general company plans and philosophy are.

First, let’s address this specific issue, as it deserves answering and has a little bit of a back story leading to a minor rant which is always fun. 

The SDF supports an object model for wireless networks that is based around the Wireless Zero Config (WZC) API.  Microsoft introduced WZC in CE 5.0 (IIRC) in an attempt to “standardize” the way the platform and apps would communicate with the wireless drivers, since NDIS doesn’t really have any specification for a lot of the wireless properties.  While I applaud the spirit, WZC is convoluted at best, and was definitely not designed to make the life of someone who has to use P/Invoke to communicate with it any easier.  Still, it was some sort of standardization so we rolled up our sleeves and created a managed interface for it. 

I’ll readily admit that what we ended with isn’t ideal, but I’m a fierce self critic and I rarely think what we do is as good as it could be.  Still it provided a programmatic interface for something that a *lot* of people wanted and that Microsoft had not implemented in managed code.

Well with Compact 7 (I’ll just call it CE7) Microsoft decided that WZC wasn’t the way to go, but instead Native WiFi was.  Painfully they didn’t follow a sensible “deprecated but supported” track for WZC and support side-by-side, they just dropped WZC support altogether.  That meant that we could not interface with a wireless adapter under CE7 using our existing code base at all – it was a flat-out break.

To make things worse, Microsoft went radio-silent for about 2 years (and still counting – yes I’m looking at you Redmond!) on what, if any, future the Compact Framework, or CE for that matter, might have.  The original WZC work was probably 4-6 weeks of development and it required that we buy several devices for testing.  Believe me, it was a real pain in the ass.  Do we (more specifically do *I*) really feel like doing that all again for the Native WiFi interfaces?  If I do, where do I get a CE7 device with WiFi support?  If I do all of that work, what’s the ROI if Microsoft kills the Compact Framework?  What’s the ROI if they revive it and implement Wireless themselves?

It’s really difficult to answer those questions, and we’re not the only ones who are wondering these things.  I can say, though, that we’ve very recently decided that yes, we will continue to do both support and new development for the SDF.  What that new development will entail I can’t say.  Not because it’s some big secret, but because I can’t make those plans until Microsoft tells us their plans (and they haven’t).  If they decide to release a new version of the Compact Framework, I’d like to not have a load of functionality duplication between it and the SDF.

So, is the SDF a dead product?  No.  We will continue to support it and have plans for feature additions.  We just don’t know exactly what those features will be or when they will be written (confidence inspiring, I know). 

Will we provide Wireless support for CE7?  If Microsoft does not, then yes, we will.  Again, we don’t know if they will or not.  If they did, we don’t know when that would be.  Ideally, though, programming for WZC and Native WiFi should be the same, so if they don’t do it, we’ll add support that looks and feels just like what’s in the SDF today.  If they do add it, I’d be inclined to update our object model to match theirs (keeping the old stuff for compatibility though).

Don’t read too much into this.  Yes, I’m optimistic about the Compact Framework and Windows CE Windows Embedded Compact but I’ve been using them for over a decade and I’ve based a whole lot of my knowledge, business and life on them.  I almost have to be optimistic.  But let’s face it, CE and the CF are still great tools for delivering products.  We’re still shipping products based on them.  Still doing new development and new installs.  Still writing proposals for them. 

Yes, we’ve tested the waters with Android and iOS.  We’ve even delivered finished products for them both, but using those tolls only reinforced my feelings about the strength and possibilities of the CF and CE and we’re still committed to using and supporting them.

Tuesday, March 06, 2012 5:25:57 PM (Central Standard Time, UTC-06:00)  #     | 
# Thursday, February 16, 2012

We've started doing work creating client applications for a few of our products to run on Android and iOS devices.  Since I'm a huge fan of code re-use, I took the time this week to port the OpenNETCF.ORM over to Xamarin's Mono for Android using a SQLite backing store implementation.  The biggest challenge was that SQLite doesn't support TableDirect not ResultSets, so it took a bit of code to get running.  Still, it took only a day and a half to get what I feel is pretty good support up and running.  I've not yet tested it through all of the possible permutations of queries, etc, but basic, single-table CRUD operations all test out fine.

So now a single code base can work on the Windows Desktop, Windows CE and Android (probably iOS and Windows Phone as well with very little work). If you're doing MonoDroid work, give it a try.

Thursday, February 16, 2012 3:48:39 PM (Central Standard Time, UTC-06:00)  #     | 
# Tuesday, February 14, 2012

Occasionally I'm surprised at how old software never seems to really die off.  Take for example an old port I did of the zlib compression library to Windows CE.  Back in probably '05 I built the original sources for the Windows CE platform.  In '07 I created a managed wrapper for the library and some samples on how to use that library.  I put it on the web and then pretty much forgot about it.

Well when we migrated our web site to a new server, some old links died and this just happened to be one of them (if you know of others, please let me know) and today I got an email from someone asking if I still had the code.  Sure, it wasn't the day things went down, but it still means not only was someone looking for it, the cared enough to email me and ask.

For those looking for it, it's got a new, hoipefully more permanent home on Codeplex:  http://zlibce.codeplex.com

 

Tuesday, February 14, 2012 1:04:43 PM (Central Standard Time, UTC-06:00)  #     | 
# Monday, November 28, 2011

Lately it seems that I’m been doing a lot of posts referring to “a customer”.  Often they are different customers, but it really sounds impersonal, so in this one I’m going to name names and point fingers – in a friendly way, of course.

Last week Nicolas emailed me to get some advice on the Visual Studio 2008 Forms designer and the Compact Framework.  Specifically he wanted to know if it’s possible to get a custom TabControl to be designable.  My assumption is that either he’s inheriting from the stock version or they’ve created a fully custom control inheriting from ContainerControl – I didn’t ask because in either case the answer is simple: it’s not going to work in the designer.

I told him that it won’t work, but in retrospect I feel I’ve short-changed him a bit.  As a developer, I like to hear a bit more than an “abandon all hope, ye who sail here for there be sea monsters” kind of response.  Why doesn’t it work?  More importantly, what are my options for a workaround?

In this case it’s interesting to note that even if he could get the designer to work for this control, I’d recommend that he not do it.  And they why to that recommendation is far more interesting.

So, Nicolas, here’s the longer answer to your question.

First, you have to understand that the Studio Designer is very, very brittle.  Any little quirkiness tends to send it into fits, and once broke you often spend large amounts of time unloading things, resetting the toolbox, restarting Studio or even restarting the PC.  This is a huge time drain, so avoiding situations that break the designer is the first order of business.

Getting the designer working for a CF component is even worse.  CF controls often make use of things that the desktop doesn’t even have, which the designer really hates.  You also have to build the Controls for both versions of the CF (for Nicolas this is important since, at least last time I was with his team, they had versions of the application targeting CF 2.0 and CF 3.5).  A control built against CF 3.5 will never show up in the designer of a CF 2.0 project.  There’s also a major bug in the designer that limits CF 3.5 controls that I’ve written about before.

For designer support you also have to maintain an XMTA file and often end up having condition compiler statements which really make the code less readable.  Workarounds for most of these issues exist.  You can create separate set of “designer” assemblies that provide only stuff for the designer and minimal actual control logic.  The problem with this is that you then have to maintain these things, which sucks up developer time that should be spent actually solving your business problem.

You can’t create a desktop assembly and use it for the designer because the designer will then suck in the full .NET framework as a reference and attempt to deploy that to the device when you want to debug.

So if the designer is so brittle and worthless, what’s a developer to do?  Well, my general attitude is to provide “support” only for basic Controls, and by “support” I mean the inherent capability of the designer to show a box with the location and bounds of your control.  No rendering.  No styling.  No collection editing. 

Really the designer’s primary use for me is to aid in basic layout.  I need to put a Control on a Form, set it’s location and Size and that’s it anyway.  Anything else is done via code, and there’s nothing wrong with this.  Embrace it as how you must work and your life becomes much simpler.

That’s all well and good for a simple Control, but what about a container like Nicolas is after?  His team would like to be able to drop on a Tab control and design each individual Tab, right?  So what are they to do?

Well, I think they’re looking at the problem wrong to begin with.  I don’t blame them, people have been looking at this wrong for some time, and the simple existence of the TabControl tend to push people to look at it wrong.  In general, my recommendation is to not use the TabControl in the first place. 

First of all, it’s 2011.  That ass-looking TabControl would have been ok back in 1995 (if the devices had existed then) since it looks just like the ass-looking Windows 95 interface. 

It’s was an ok solution in 2000 when these devices were new and people thought of them as just “mini PCs” so extending the desktop paradigm to the device was what we did.    Yes, we wished we could do a little more to make it pretty by adding icons and custom drawing, but it worked and users really didn’t expect anything more.

But it’s 2011.  Users don’t use a stylus if they can avoid it.  They know an elegant UI when they see one because they’ve been using a smartphone.  Just because they have no choice in the app they use (this is an LOB app, so the user is locked in) is no excuse to not deliver something that is actually nice to use.  The TabControl does not meet that.  It’s tiny, it scrolls weird, and it’s ugly with no options to make it non-ugly.  Plus it’s  Tab motif.  Name the last mobile app you used on a phone that used tabs.  That’s what I thought. Abandon the piece of crap.

So what would I use?  Obviously this will be subjective, as it’s what I would personally do given a blank slate.  Nicolas is trying to put something into an already-existing, very large code base, so his mileage may vary, but I’m betting it won’t be too bad, and implementing this might actually do a lot toward getting other gains they really need like better transition time between views.

I’d start using the OpenNETCF IoC project.  I’d split each of the existing “tabs” into individual SmartParts, which are now fully designable since they’re just UserControls.  So there you are – the designer requirement is solved.  I’d then place on the Form a pair of DeckWorkspaces.  One would display the “selected” view and the other would replace the “tabs” of old with buttons or clickable images that would be used to select the current view.  It would look something like this in the designer for the Form:

tabs

And then the SmartPart that goes into the bottom workspace might be something like this in the designer (well it would probably be a bit different, but you get the idea):

tabs2

See, the designer works.  It meets the business requirement that it provides a “tab-like” capability where the user selects an item across the bottom and the upper view changes.  But now it’s not just usable with a stylus but it’s also likely to be usable with a finger (gasp!) and aesthetically it doesn’t make my want to poke my own eyes out.  It’s a win-win.  Plus now you can lazy-load the views *on demand* rather than loading up every damned control on every tab when the Form is brought up, so the user is going to be pleasantly surprised there too.  Oh, and now it’s easy to change out “tabs” based on user rights or workflow and probably other benefits that I’m not thinking of.

Occasionally we’ve got to break out of the “what we know” and the “what we’ve always done” routine and look out at the horizon.  By doing so we can often make some simple changes and make things easier for both the developer and the user, and that is how we make progress.

Monday, November 28, 2011 12:24:00 PM (Central Standard Time, UTC-06:00)  #     | 
# Tuesday, November 22, 2011

So a customer asked me last week if I could provide them some managed code to allow them to insert a certificate and a private key into the certificate store on a Windows CE device.  A simple enough request, right?  Heh, isn’t that how they all start?

To be honest, I’ve done very little work with certificates in the past – basically I’ve created certs for use with Padarn and SSL but that’s about it. So my first order of business was to do some due diligence to try to get an idea of the scope of the problem.  I pulled up the MSDN docs of the Compact Framework’s support for X509 stuff.  It shows that there is device support for the X509Store as well as the X509Certificate so I told them it should be pretty easy to achieve their goal.  I mean the docs sure look like it would be easy.

Next I needed to write a little code to familiarize myself with the object model.  I fired up a device that most definitely has several certificates installed.

CECerts

Then I wrote some simple code to give me an idea which of the six stores these fall into:

public void DumpCertCounts() 
{ 
    Debug.WriteLine("        Current User     Local Machine");

    var userStore = new X509Store(StoreName.Root, StoreLocation.CurrentUser); 
    var deviceStore = new X509Store(StoreName.Root, StoreLocation.LocalMachine); 
    Debug.WriteLine(string.Format("Root:          {0}              {1}", 
        userStore.Certificates.Count, 
        deviceStore.Certificates.Count)); 
    
    userStore = new X509Store(StoreName.My, StoreLocation.CurrentUser); 
    deviceStore = new X509Store(StoreName.My, StoreLocation.LocalMachine); 
    Debug.WriteLine(string.Format("My:            {0}              {1}", 
        userStore.Certificates.Count, 
        deviceStore.Certificates.Count));

    userStore = new X509Store(StoreName.CertificateAuthority, StoreLocation.CurrentUser); 
    deviceStore = new X509Store(StoreName.CertificateAuthority, StoreLocation.LocalMachine); 
    Debug.WriteLine(string.Format("CA:            {0}              {1}", 
        userStore.Certificates.Count, 
        deviceStore.Certificates.Count)); 
} 

Easy enough.  The problem was that then I actually ran the code.  This is the output.

CECertDbg

Really?  Zero certificates in any store?  Was my code right?  Yes.  Did I target the right device from the debugger?  Yes.  Alrighty, so it seems that “existence of a class in the BCL” doesn’t necessarily mean “actual functionality is implemented.”  This doesn’t bode well.

Technically I don’t need to know what certificates are installed on the device for the customer’s request, right.  Sure, it would be nice to see if a cert is there before trying to install it, but we can always just wrap the call in a try/catch and ignore any exception thrown when we try to install a duplicate.  Not the ideal solution, but workable since this will be done infrequently.

I then generated some test certs with makecert.exe and pushed them to the device.  Now I needed to create code to import them, hopefully an import works better than enumerating existing certs. 

Let’s see, how would we bring in a cert?  How about the Import method on the X509CerticateCollection?  Oh, not supported in the CF.

Ah, but the Add method is, so I just need to create an X509Certificate and Add it.  Let’s see, the X509Certifiacte class has two static methods – CreateFromCertFile and CreateFromSignedFile.  Of course those aren’t supported in the CF.  That would be too easy.

How about from a constructor? Looks like the only constructor supported is the one that takes in a byte array.  What on earth is that byte array.  Oh, the example shows that it’s easy to get by Exporting from a certificate you already have.  How un-useful is that?

So it appears that the entire namespace in the Compact Framework is useless.  Actually worse than useless because I can’t create a direct replacement or I’ll end up with naming conflicts with the existing useless garbage.

So now I have to create the objects in the namespace and implement the methods required to achieve “import a certificate into the device store”.  The starting point is the C source code for the Certificates control panel applet you see above, which ships with Platform Builder.  It’s an ugly load of P/Invoking methods that deal with lots of pointers to structs that contains pointers to structs that contain pointers to structs that contain…you get the idea.

So I spent a day just getting the import of a CER file working (much easier than the PVK import is going to be) and my exact same test code now outputs this:

CECertDbg2

See, an implementation that actually does something.  I have no idea what was going on at Microsoft when this namespace was “implemented” or how it got out the door.  It definitely wouldn’t pass even the most rudimentary of functional tests.

This is a classic case of why I’d love to see the Compact Framework open/shared sourced.  I could actually go in and fix these things.

Tuesday, November 22, 2011 10:41:17 AM (Central Standard Time, UTC-06:00)  #     | 
# Monday, November 21, 2011

This post is part of my "Software Development" series. The TOC for the entire series can be found here.


Let’s start this post with a couple questions.  What is an ORM and why would you use one?  If you don’t know the answer to both of these questions, then this post is for you.  If you do know the answers, feel free to skip to the next post (after I’ve created it of course).

First, let’s look at what an ORM is. And ORM is an object-relational mapping.  According to Wikipedia, which we all know is the infallible and definitive resource for all knowledge, and ORM is “a programming technique for converting data between incompatible type systems in object-oriented programming languages.”  Ok, what the hell does that mean?  My definition is a little less “scholarly.”  I’d say that

An ORM is a way to abstract your data storage into simple class objects (POCOs if you’d like) so that you don’t have to worry about all the crap involved in actually writing data fields, rows, tables and the like.  As a developer, I want to deal with a “Person” class.  I don’t want to think about SQL statements.  I don’t want to have to worry about indexes or tables or even how my “Person” gets stored to or read from disk.  I just want to say “Save this Person instance” and have it done.  THAT is what an ORM is.  A framework that lets me concentrate on solving my business problem instead of spending days writing bullshit, mind-numbing, error-prone data access layer code.

Why would we use an ORM?  I think I’ve been fairly upfront that I consider myself to be a lazy developer.  No, I don’t mean that I take shortcuts or do shoddy work, I mean that I hate doing things more than once.  I hate having to write reams of code to solve problems that have already been solved.  I’ve been writing applications that consume data for years, and as a consequence, I’ve been writing data access code for years.  If you’re not using an ORM, you probably know down in your soul that this type of work flat-out sucks.  Anything that simplifies data access to me is a win.

Of course there are other, more tangible benefits as well.  If you’re using an ORM, it’s often possible to swap out data stores – so maybe you could write to SQL Server then swap a line of two in configuration and write to MySQL or an XML file.  It also allows you to mock things or create stubs to remove data access (really handy when someone tells you that your data access is what’s slowing things down when you’re pretty sure it’s not).

I can see that some of you still need convincing.  That’s good – you shouldn’t ever just take someone’s word for it that they know what they’re doing.  Ask for proof.  Well let’s look at a case of my own code, why I built the OpenNETCF ORM and how I was recently reminded why it’s a good thing.

A couple years ago I wrote an application for a time and attendance device (i.e. a time clock) that, not surprisingly, stored data about employees, punches, schedules, etc.  on the device.  It also has the option to store it on a server and synchronize the data from clock to server, but that’s not core to our discussion today.  The point is that we were storing a fair bit of data and this was a project I did right before I create the ORM framework.  It was, in fact, the project that made it clear to me that I needed to write an ORM.

Just about a month ago the customer wanted to extend the application, adding a couple features to the time clock that required updates to how the data was stored.  It took me very little time to realize that the existing DAL code was crap.  Crap that I architected and wrote.  Sure, it works.  They’ve shipped thousands of these devices and I’ve heard no complaints and had no bug reports, so functionally it’s fine and it does exactly what was required.  Nonetheless the code is crap and here’s why.

First, let’s look at a table class in the DAL, like an Employee (the fat that the DAL knows about tables is the first indication there’s a problem):

internal class EmployeeTable : Table 
{ 
    public override string Name { get { return "Employees"; } }

    public override ColumnInfo KeyField 
    { 
        get  
        { 
            return new ColumnInfo { Name = "EmployeeID", DataType = SqlDbType.Int }; 
        } 
    }

    internal protected override ColumnInfo[] GetColumnInfo() 
    { 
        return new ColumnInfo[] 
        { 
            KeyField, 
            new ColumnInfo { Name = "BadgeNumber", DataType = SqlDbType.NVarChar, Size = 50 }, 
            new ColumnInfo { Name = "BasePay", DataType = SqlDbType.Money }, 
            new ColumnInfo { Name = "DateOfHire", DataType = SqlDbType.DateTime }, 
            new ColumnInfo { Name = "FirstName", DataType = SqlDbType.NVarChar, Size = 50 }, 
            new ColumnInfo { Name = "LastName", DataType = SqlDbType.NVarChar, Size = 50 }, 
            // ... lots more column definitions ... 
        }; 
    }

    public override Index[] GetIndexDefinitions() 
    { 
        return new Index[] 
        { 
            new Index 
            { 
                Name = "IDX_EMPLOYEES_EMPLOYEEID_GUID",  
                SQL = "CREATE INDEX IDX_EMPLOYEES_EMPLOYEEID_GUID ON Employees (EmployeeID, GUID DESC)" 
            }, 
        }; 
    } 
}

So every table for an entity derives from Table, which looks like this (shorted a load for brevity in this post):

public abstract class Table : ITable 
{ 
    internal protected abstract ColumnInfo[] GetColumnInfo();

    public abstract string Name { get; } 
    public abstract ColumnInfo KeyField { get; }

    public virtual string GetCreateTableSql() 
    { 
        // default implementation - override for different versions 
        StringBuilder sb = new StringBuilder(); 
        sb.Append(string.Format("CREATE TABLE {0} (", Name));

        ColumnInfo[] infoset = GetColumnInfo(); 
        int i; 
        for (i = 0; i < (infoset.Length - 1); i++) 
        { 
            sb.Append(string.Format("{0},", infoset[i].ToColumnDeclaration())); 
        }

        sb.Append(string.Format("{0}", infoset[i].ToColumnDeclaration())); 
        sb.Append(")");

        return sb.ToString(); 
    }

    public virtual Index[] GetIndexDefinitions() 
    { 
        return null; 
    }

    public virtual IDbCommand GetInsertCommand() 
    { 
        SqlCeCommand cmd = new SqlCeCommand(); 

        // long, manual generation of the SQL and then creating the command

        return cmd; 
    }

    public IDbCommand GetUpdateCommand() 
    { 
        SqlCeCommand cmd = new SqlCeCommand(); 

        // long, manual generation of the SQL and then creating the command 

        return cmd; 
    }

    public IDbCommand GetDeleteCommand() 
    { 
        SqlCeCommand cmd = new SqlCeCommand(); 

        // long, manual generation of the SQL and then creating the command 

        return cmd; 
    }


Sure, I get a few bonus points for using inheritance so that each table doesn’t have to do all of this work, but it’s still a pain.  Adding a new Table required that I understand all of this goo, create the ColumnInfo right, know what the index stuff is, etc.  And what happens when I need to add a field to an existing table?  It’s not so clear.

Now how about consuming this from the app?  When the app needs to get an Employee  you have code like this:

public IEmployee[] GetAllEmployees(IDbConnection connection) 
{ 
    List list = new List();

    using (SqlCeCommand command = new SqlCeCommand()) 
    { 
        command.CommandText = EMPLOYEES_SELECT_SQL2; 
        command.Connection = connection as SqlCeConnection;

        using (var rs = command.ExecuteResultSet(ResultSetOptions.Scrollable | ResultSetOptions.Insensitive)) 
        { 
            GetEmployeeFieldOrdinals(rs, false);

            while (rs.Read()) 
            { 
                IEmployee employee = EntityService.CreateEmployee();

                employee.BadgeNumber = rs.IsDBNull(m_employeeFieldOrdinals["BadgeNumber"]) 
                    ? null : rs.GetString(m_employeeFieldOrdinals["BadgeNumber"]); 
                employee.BasePay = rs.IsDBNull(m_employeeFieldOrdinals["EmployeeNumber"]) 
                    ? 0 : rs.GetDecimal(m_employeeFieldOrdinals["BasePay"]); 
                employee.DateOfHire = rs.IsDBNull(m_employeeFieldOrdinals["DateOfHire"]) 
                    ? DateTime.MinValue : rs.GetDateTime(m_employeeFieldOrdinals["DateOfHire"]); 
                employee.EmployeeID = rs.GetInt32(m_employeeFieldOrdinals["EmployeeID"]); 
                employee.FirstName = rs.GetString(m_employeeFieldOrdinals["FirstName"]); 
                employee.LastName = rs.GetString(m_employeeFieldOrdinals["LastName"]); 
                string middleInitialStr = !rs.IsDBNull(m_employeeFieldOrdinals["MiddleInitial"]) 
                    ? rs.GetString(m_employeeFieldOrdinals["MiddleInitial"]) : String.Empty; 
                employee.MiddleInitial = String.IsNullOrEmpty(middleInitialStr) ? '\0' : middleInitialStr[0]; 

                 // on and on for another 100 lines of code) 

                 list.Add(employee); 
            } 
        } 
    }

    return list.ToArray(); 
} 

Nevermind the fact that this could be improved a little with GetFields – the big issues here are that you have to hard-code the SQL to get the data, then you have to parse the results and fill out the Employee entity instance.  You do this for every table.  You change a table, you then have to go change the SQL and every method that touches the table.  The process is error prone, time consuming and just not fun.  It also makes me uneasy because the test surface area needs to be big.  How do I ensure that all places that access the table were fixed?  Unit tests help give me some comfort, but really it has to go through full integration testing of all features (since Employees are used by just about every feature on the clock).

Now what would the ORM do for me here?  Without going into too much detail on exactly how to use the ORM (we’ll look at that in another blog entry), let’s just look at what the ORM version of things would look like.

We’d not have any “Table” crap.  No SQL.  No building Commands and no parsing Resultsets.  We’d just define an Entity like this:

[Entity] 
internal class Employee 
{ 
    [Field(IsPrimaryKey = true)] 
    public int EmployeeID { get; set; }        
    [Field] 
    public DateTime DateOfHire { get; set; } 
    [Field] 
    public string FirstName { get; set; } 
    [Field] 
    public string LastName { get; set; }

    // etc. 
}

Note how much cleaner this is than the Table code I had previously.  Also note that this one class replaces *both* the Table class and the Business Object class.  So this is much shorter.

What about all of that create table, insert, update and delete SQL and index garbage I had to know about, write and maintain?  Well, it’s replaced with this:

m_store = new DataStore(databasePath);

if (!m_store.StoreExists) 
{ 
    m_store.CreateStore(); 
}

m_store.AddType<Employee>(); 

That’s it.  Adding another Entity simply requires adding just one more line of code – a call to AddType for the new Entity type.  In fact the ORM can auto-detect all Entity types in an assembly with a single call if you want.  So that’s another big win.  The base class garbage gets shifted into a framework that’s already tested.  Less code for me to write means more time to solve my real problems and less chance for me to add bugs.

What about the long, ugly, unmaintainable query though?  Well that’s where the ORM really, really pays off.  Getting all Employees becomes stupid simple.

var allEmployees = m_store.Select<Employee>();  

Yep, that’s it. There are overloads that let you do filtering.  There are other methods that allow you to do paging.  Creates, updates and deletes are similarly easy. 

Why did I create my own instead of using one that already exists?  Simple – there isn’t one for the Compact Framework.  I also find that, like many existing IoC frameworks, they try to be everything to everyone and end up overly complex.  Another benefit to the OpenNETCF ORM is that it is fully supported on both the Compact and Full Frameworks, so I can use it in desktop and device projects and not have to cloud my brain with knowing multiple frameworks.  I even have a partial port to Windows Phone (it just needs a little time to work around my use of TableDirect in the SQL Compact implementation). 

Oh, and it’s fast.  Really fast.  Since my initial target was a device with limited resources, I wrote the code for that environment.  The SQL Compact implementation avoids using the query parser whenever possible because experience has taught me that as soon as you write actual SQL, you’re going to pay an order of magnitude performance penalty (yes, it really is that bad).  It uses TableDirect whenever possible.  It caches type info so reflection use is kept to a bare minimum.  It caches common commands so if SQL was necessary, it at least can reuse query plans.

So that’s why I use an ORM. Doing data access in any other way has become insanity.

Monday, November 21, 2011 11:08:06 AM (Central Standard Time, UTC-06:00)  #     | 
# Tuesday, November 15, 2011

Transferring data between processes in Windows CE is not difficult, and you can avoid using passing data through the message pump using WM_COPYDATA (which I've never considered a good idea, BTW).  Sockets and point to point queues are pretty straightforward and well documented for IPC. 

One mechanism that isn't well covered, and that works well for sharing larger blobs of data, is a memory-mapped file.  The SDF contains a MemoryMappedFile class (in the OpenNETCF.IO namespace) that allows you to use a shared memory region (in ram or on disk - your choice) and to access it just like you would any other stream with Write, Read and Seek.  Here's a simple example. Yes I realize my synchronization mechanism here isn't the cleanest, but it conveys the idea with the least code.

 

using System;
using System.Text;
using OpenNETCF.IO;
using OpenNETCF.Threading;
using System.Diagnostics;
using System.Reflection;
using System.IO;

namespace MMFPeer
{
    class Program
    {
        public const string SharedMapName = "MMF_PEER_NAME";
        public const long MaxMapSize = 1024;
        public const string SharedMutexName = "MMF_PEER_MUTEX";
        public const string DataReadyEventName = "MMF_PEER_DATA_READY";

        private MemoryMappedFile m_mmf;
        private NamedMutex m_mutex;
        private EventWaitHandle m_dataReady;

        private bool Sending { get; set; }

        static void Main(string[] args)
        {
            new Program().Run();
        }

        public void Run()
        {
            var processName = Path.GetFileNameWithoutExtension(Assembly.GetExecutingAssembly().GetName().CodeBase);

            // create the MMF
            m_mmf = MemoryMappedFile.CreateInMemoryMap(SharedMapName, MaxMapSize);

            // create a shared mutex
            m_mutex = new NamedMutex(false, SharedMutexName);

            // create a data-ready event
            m_dataReady = new EventWaitHandle(false, EventResetMode.ManualReset, DataReadyEventName);

            // fire up a "listener"
            new System.Threading.Thread(ReadProc)
            {
                IsBackground = true,
                Name = "MMF Peer Reader"
            }
            .Start();

            Console.WriteLine("Memory Mapped File Created.  Enter text to send to peer(s)");

            // wait for user input
            while (true)
            {
                var input = Console.ReadLine();

                if (input == null)
                {
                    Thread2.Sleep(5000);
                    input = GetMockInput();
                    Debug.WriteLine(string.Format("Platform does not have a Console installed. Sending mock data '{0}'", input));
                }

                if (input == "exit") break;

                // prefix our process name so we can tell who sent the data
                input = processName + ":" + input;

                // grab the mutex
                if (!m_mutex.WaitOne(5000, false))
                {
                    Console.WriteLine("Unable to acquire mutex.  Send Abandoned");
                    Debug.WriteLine("Unable to acquire mutex.  Send Abandoned");
                    continue;
                }

                // mark as "sending" so the listener will ignore what we send
                Sending = true;

                // create a "packet" (length + data)
                var packet = new byte[4 + input.Length];
                Buffer.BlockCopy(BitConverter.GetBytes(input.Length), 0, packet, 0, 4);
                Buffer.BlockCopy(Encoding.ASCII.GetBytes(input), 0, packet, 4, input.Length);

                // write the packet at the start
                m_mmf.Seek(0, System.IO.SeekOrigin.Begin);
                m_mmf.Write(packet, 0, packet.Length);

                // notify all clients that data is ready (manual reset events will release all waiting clients)
                m_dataReady.Set();

                // yield to allow the receiver to unblock and check the "Sending" flag
                Thread2.Sleep(1);

                // reset the event
                m_dataReady.Reset();

                // unmark "sending" 
                Sending = false;

                // release the mutex
                m_mutex.ReleaseMutex();
            }            
        }

        private int m_inputIndex = -1;
        private string[] m_inputs = new string[]
            {
                "Hello",
                "World"
            };

        private string GetMockInput()
        {
            if (++m_inputIndex >= m_inputs.Length) m_inputIndex = 0;
            return m_inputs[m_inputIndex];
        }

        private void ReadProc()
        {
            var lengthBuffer = new byte[4];
            var dataBuffer = new byte[m_mmf.Length - 4];

            while (true)
            {
                // use a timeout so if the app ends, this thread can exit
                if(!m_dataReady.WaitOne(1000, false)) continue;

                // avoid receiving our own data
                if (Sending)
                {
                    // wait long enough for the sender to reset the m_dataReady flag
                    Thread2.Sleep(5);
                    continue;
                }

                // grab the mutex to prevent concurrency issues
                m_mutex.WaitOne(1000, false);

                // read from the start
                m_mmf.Seek(0, System.IO.SeekOrigin.Begin);

                // get the length
                m_mmf.Read(lengthBuffer, 0, 4);
                var length = BitConverter.ToInt32(lengthBuffer, 0);

                // get the data
                m_mmf.Read(dataBuffer, 0, length);

                // release the mutex so any other clients can receive
                m_mutex.ReleaseMutex();

                // convert to a string
                var received = Encoding.ASCII.GetString(dataBuffer, 0, length);

                Console.WriteLine("Received: " + received);
                Debug.WriteLine("Received: " + received);
            }
        }
    }
}
Tuesday, November 15, 2011 12:18:09 PM (Central Standard Time, UTC-06:00)  #     | 
# Friday, October 28, 2011

One of the more common question groups I see about the Smart Device Framework really revolves around connecting to an access point.  Connecting to an Open AP is pretty straightforward, but as soon as you need to start adding encryption, privacy, passcodes and all that fun stuff, people tend to go off the rails.  And I can't blame them, the WirelessZeroConfigNetworkInterface class isn't the most intuitive interface.  What I've done in my own projects is to create a WiFiService class that wraps all of this (and a lot of other stuff too).  I'll try to get that full service to a publishable state in the not-too-distant future, but the core pieces for connectivity boil down to these few methods:

 

public bool ConnectToOpenNetwork(string ssid)
{
    if (!WiFiSupported) throw new NotSupportedException("No WZC WiFi Adapter detected on this device");

    return ConnectToNetwork(ssid, null, false, AuthenticationMode.Open, WEPStatus.WEPDisabled);
}

public bool ConnectToOpenNetwork(IAccessPoint accessPoint)
{
    if (!WiFiSupported) throw new NotSupportedException("No WZC WiFi Adapter detected on this device");

    return m_wzc.AddPreferredNetwork(accessPoint);
}

public bool ConnectToWEPNetwork(string ssid, string wepKey)
{
    if (!WiFiSupported) throw new NotSupportedException("No WZC WiFi Adapter detected on this device");

    return ConnectToNetwork(ssid, wepKey, false, AuthenticationMode.Open, WEPStatus.WEPEnabled);
}

public bool ConnectToWEPNetwork(IAccessPoint accessPoint, string wepKey)
{
    if (!WiFiSupported) throw new NotSupportedException("No WZC WiFi Adapter detected on this device");

    return ConnectToWEPNetwork(accessPoint.Name, wepKey);
}

/// 
/// This connects to a WPA network using TKIP encryption.
/// 
/// 
/// 
/// 
public bool ConnectToWPANetwork(string ssid, string passphrase)
{
    if (!WiFiSupported) throw new NotSupportedException("No WZC WiFi Adapter detected on this device");

    return ConnectToNetwork(ssid, passphrase, false, AuthenticationMode.WPAPSK, WEPStatus.TKIPEnabled);
}

/// 
/// This method will only connect to a WPA2 network using AES.
/// If you need fallback to WPA (using TKIP), use the overload that takes in an AccessPoint
/// 
/// 
/// 
/// 
public bool ConnectToWPA2Network(string ssid, string passphrase)
{
    if (!WiFiSupported) throw new NotSupportedException("No WZC WiFi Adapter detected on this device");

    return ConnectToNetwork(ssid, passphrase, false, AuthenticationMode.WPA2PSK, WEPStatus.AESEnabled);
}

/// 
/// Connects to a WPA or WPA2 (AES or TKIP)
/// 
/// 
/// 
/// 
public bool ConnectToWPANetwork(IAccessPoint accessPoint, string passphrase)
{
    if (!WiFiSupported) throw new NotSupportedException("No WZC WiFi Adapter detected on this device");

    // quick validation
    var valid = accessPoint.AuthenticationMode == AuthenticationMode.WPA
        || accessPoint.AuthenticationMode == AuthenticationMode.WPAPSK
        || accessPoint.AuthenticationMode == AuthenticationMode.WPA2
        || accessPoint.AuthenticationMode == AuthenticationMode.WPA2PSK;

    if (!valid)
    {
        throw new InvalidOperationException("The provided AP is not set up for WPA");
    }

    return ConnectToNetwork(accessPoint.Name, passphrase, false, accessPoint.AuthenticationMode, accessPoint.Privacy);
}

private bool ConnectToNetwork(string ssid, string passphrase, bool adhoc, AuthenticationMode mode, WEPStatus encryption)
{
    EAPParameters eap = null;

    switch (mode)
    {
        case AuthenticationMode.WPA:
        case AuthenticationMode.WPAPSK:
        case AuthenticationMode.WPA2:
        case AuthenticationMode.WPA2PSK:
            eap = new EAPParameters()
            {
                Enable8021x = true,
                EapType = EAPType.Default,
                EapFlags = EAPFlags.Enabled,
            };
            break;
    }

    // stop scanning while connecting
    var wasScanning = Scanning;
    StopScanning();

    try
    {
             lock (m_syncRoot)
             {
        return m_wzc.AddPreferredNetwork(ssid,
            !adhoc,
            passphrase,
            1,
            mode,
            encryption,
            eap);
            }
    }
    catch (ArgumentException ae)
    {
        return false;
    }
    finally
    {
        if (wasScanning) StartScanning();
    }
}

I apologize that the above code won't just compile right out of the box - it's got some dependencies to other bits in the Service class, but my hope is that it at least gives the essence of what needs to be done to get the connectvitiy you need working.

Friday, October 28, 2011 11:41:48 AM (Central Daylight Time, UTC-05:00)  #     | 
# Thursday, July 28, 2011

Today someone asked over on StackOverflow how to compile Bouncy Castle for Compact Framework 3.5.  Since SO has no way to add attachments, I'm parking the solution here.  Basically here are the steps:

  1. Download the latest source (v 1.7 as of this writing)
  2. Download my Visual Studio 2008 solution and project files
  3. Put the csharp.cf.sln in the same location as the existing csharp.sln file
  4. Put the crypto.cf.csproj in the same location as the existing crypto.csproj file
  5. Open the csharp.cf.sln file and build
Thursday, July 28, 2011 4:08:46 PM (Central Daylight Time, UTC-05:00)  #     | 
# Tuesday, June 28, 2011

I'm still working on cleaning out our code archive and publishing things I feel might still be useful.  Today, I published the OpenNETCF Calendar controls, which provide some funtionality that has the look and feel of the Pocket Outlook UI.

Get the source over on Codeplex.

Tuesday, June 28, 2011 9:56:46 AM (Central Daylight Time, UTC-05:00)  #     | 
# Saturday, June 11, 2011

Recently I've seen some activity on Stack Overflow where some industrious people are attempting to get Studio 2010 to build non-Phone CF applications.  I assume this is for Continuous Integration/Build Server stuff, but it's a start toward a community workaround to the long-standing problem of Microsoft not giving us any help.  Part of the solution appears to be to copy over some of the targets files from an earlier version of Studio.  In an effort to aid in that, I've attached those files below.

msbuild.targets.cf3.5.rar (240.41 KB)
msbuild.targets.cf1_2.rar (255.88 KB)

Saturday, June 11, 2011 7:10:36 PM (Central Daylight Time, UTC-05:00)  #     | 
# Thursday, April 28, 2011

 

I've again refreshed OpenNETCF's MTConnect Managed SDK with a few changes, including:

  • Added support for some current and sample filtering (not all filtering is supported, but I've added device name and data item ID filtering)
  • Added AgentInformation to the EntityClient so you get information about the agent returning a data set for a probe
  • Miscellaneous bug fixes and refactoring

As always, if you find a bug or would like me to work on implementing a specific feature from the specification, add it to the lists over on the Codeplex site.

Thursday, April 28, 2011 2:00:50 PM (Central Daylight Time, UTC-05:00)  #     | 
# Thursday, March 17, 2011

I "own" a whole lot of open/shared source projects over on Codeplex (MTConnect, IoC, ORM, FTP, RAS, TAPI, RAPI, Serial, Barcode and a few others).  I also have a job that requires that I do work for paying customers, so often the open source projects tend to languish with little attention.  How do I decide when to allocate time to one of those open source projects?  Well a really good way to get me to do work on it is to pay me to fix a bug.  Shocking, I know.

Earlier this week a customer asked me to help them out with some RAS problems they were having.  The library worked fine on their Pocket PC 2003 devices but failed on the CE 4.2 devices (yes, both old, but when someone owns a lot of old devices that function, it's hard to argue that they should upgrade).  They overnighted me a device and said they'd pay for me to try to get it working.  One thing that I like about the shared projects is that everyone benefits from the fruits of that effort.  I've rolled the fixes back into the public source for the OpenNETCF.Net.Ras library.

Thursday, March 17, 2011 9:30:55 AM (Central Standard Time, UTC-06:00)  #     | 
# Monday, March 14, 2011

One complaint I see a lot about apps built using the Compact Framework is that they look terrible.  It doesn't have to be that way, and honestly the Compact Framework really isn't at fault here.  Generally speaking the problem is that developers are lazy and want to just use what's in the off-the-shelf toolbox.  If you do that, then yes, you'll have something that looks like a Windows 95 application.  But again, I don't fault the CF for that, I fault developers (or more likely their managers).

If you want an app to have good aesthetics you need two things:

  1. To hire a real graphic designer to at least produce collaterals
    At the risk of making broad generalizations, I'll state that developers suck at producing graphics (it's OK, graphic designers suck at writing code).  You wouldn't hire a plumber to hang drywall, why on earth are you having developers create UI?  Hire a designer that has experience building UI collaterals and you'll find that not only will your app look a thousand times better, it will get done faster than having your devs hack at it.
  2. To break your developer's reliance on the off-the-shelf controls
    Using the controls that install with the CF is a sure-fire way to end up with a dated-looking app.  This doesn't mean you have to go buy a 3rd-party set of controls; that may help, but don't just assume it's going to solve the issue.  What we've done internally is to build a set of controls that we use.  Most people would be surprised to know that we have a very small set. We wrote the entire device application for a commercially-available time clock that you'd never know was based on CE by looking at it and I believe that the number of Control types used (other than a Form and SmartPart) was one.  That's right - we used a single control type for everything.  We use it as a Button, a Label, a CheckBox and stacked together to make a List (it's a touch device with only 5 or 6 items visible at a time).

There's not much I can do for you on point #1, but for #2 I can at least give you the control we use.  It's called ImageButton due to it's genesis as a button that, surprisingly, held an image.  It got more complex as we needed and added new capabilities and features.  You can set the up and down images (to get that nice "inverted" look on click), put align text left, center or right (with an appropriate margin if you'd like), put text and images together, and several other features.

Here are some quick screen shots:

The first screen shows rounded buttons (the change to a blue background when clicked) with the grey ">" and the text aligned in different ways.  Note that the right and left justified text have a fairly large margin; this is settable.

The second is probably not the greatest picture as it looks a lot like a normal checkbox, but those checkboxes are actually images.  You could easily change them to a round "bubble" or a box with a check that extends beyond the box border.

The third is a simple drawn-gradient background.  Note that the button in the upper middle (which again is an ImageButton) has rounded corners and the gradient background is visible through the transparent corners of the button (this was a real challenge in the CF).  The "State1" text on this view is actually another ImageButton control so it can respond to clicks.  Again note that the gradient background is visible through the control.

The final view shows yet another use of the control.  The two-state capability is actually done with two images. The control is a single ImageButton and when it gets clicked, we swap the image to toggle the "on" or "off" selection. Notice that the text in the images is less pixelated becasue we don't have ClearType turned on.

Since I didn't really have a better place to put the code, and since the sample app showing it was using the IoC framework, I've simply put the code in the OpenNETCF IoC Framework tree on Codeplex (under Samples). Also note that the code for this is not yet in the release. That means you need to pull it from the source view (starting with change set 59250)

Monday, March 14, 2011 8:29:51 AM (Central Standard Time, UTC-06:00)  #     | 
# Thursday, March 10, 2011

I've published a major upgrade to the OpenNETCF MTConnect Managed SDK over on Codeplex.  Earlier releases only had Agent support, but this release adds Client support.  The SDK supports both the Compact Framework and the Full Framework (version 3.5 of each).

If you're wondering exactly what MTConnect is, it is a standard for how data should be published from machine tools as XML over a simple Web interface.  More information on the standard can be found on the MTConnect Institute's web site, including the full specification.  This SDK allows managed developers to publish and/or consume data according to the standard.

Thursday, March 10, 2011 12:11:23 PM (Central Standard Time, UTC-06:00)  #     | 
# Friday, March 04, 2011

In case you missed the pretty quiet announcement this week, Windows CE Windows Embedded Compact (love those marketers) 7.0 was released to manufacturers this week.  You can download the eval as well.

There are some nice new features in this release, but what's more important is that it's actually been released.  There seems to have been a lot of speculation lately about the demise of CE, and this concretely says "no, CE is not dead".  This release means that it is officially a supported product for the next 5 years with extended support for another 5 after that.

I spent most of this week in Redmond talking with some of the teams and I came away with a pretty positive impression about the future of CE Compact.  Microsoft has failed lately to provide information about the future and direction of Compact, and that lack of communication has led to a lot of people making implicit assumptions that we've been reassured are not true.

I don't have any specifics that I can share right now, but I can say that I personally would have no qualms about recommending CE 7 Embedded Compact 7 and the Compact Framework as a base for new products that you intend to ship for the next decade. 

I'll share more news as I get it.

Friday, March 04, 2011 12:46:38 PM (Central Standard Time, UTC-06:00)  #     | 
# Thursday, December 02, 2010

I do quite a bit of my work in the area of industrial automation, and one of the more interesting initiatives to come along in a while is called MTConnect ("MT" presumably for "Machine Tool").  MTConnect is an open standard for communicating with a machine tool to retrieve information about its current or past status - everything from executing controller line of code to tool head positions.  It's interesting enough that I joined the Technical Advisory Group and am helping to review and improve the specification (version 1.1.0 was just ratified last week).

As frequently seems to be the case, there was a bit of interest from other members on exposing MTConnect data from tools running Windows OSes (both CE and XPe), but no one working on providing an actual SDK to allow a standardized way to actually do it.  Well implementing code from nothing but a spec is actually something I find pretty fun and interesting, so I took up the challenge.  My first attempt was, as is typical, more of a throw-away attempt at writing code to help me understand how the spec itself works.  I spent a couple weeks on it then tossed it all out.

I then took the knowledge I gained from that first effort and put together a more coherent SDK that I'm releasing a loose Beta of today over on Codeplex.  It's not done - in fact I wanted to get a few more features in and get it to a releasable point before publishing it, but I've already gotten inquiries and volunteers to test out the beta work, so I'm publishing early to facilitate sharing the code between teams and to allow me to track bugs and discuss the SDK in a publicly searchable location.

I'm working on a specific project that is going to use this SDK, so I have a specific set of funtionality that I'm driving toward, but that doesn't mean I'm not open to making it easier for everyone to use. If you're working on machine tool controllers and are interested in exposing data meeting the MTConnect standard, I encourage you to take a look at the Agent SDK, post questions in the Discussions area and report bugs/desired features to help drive the effort forward.

Thursday, December 02, 2010 4:16:34 PM (Central Standard Time, UTC-06:00)  #     | 
# Monday, May 17, 2010

I'm working on a Windows CE project where we're storing an MD5 hash of a password in a SQL Compact database.  The solution is a mix of both native and managed code, and moth need to have access to both generating and verifying a password hash, so it's pretty important that we have functions for both that generate the same hash output given the same string input.  Here are the functions I'm using:

Managed

private static string MD5Hash(string str)
{
  MD5 hash = MD5.Create();
  byte[] h = hash.ComputeHash(Encoding.Unicode.GetBytes(str));

  StringBuilder sb = new StringBuilder();
  foreach (byte b in h)
  {
    sb.Append(b.ToString("x2"));
  }

  return sb.ToString();
}

Native

#include "wincrypt.h"

#define MD5LEN 16

DWORD MD5Hash(TCHAR *input, DWORD inLength, TCHAR *output, DWORD *outLength)
{
  HCRYPTPROV hContext = 0;
  HCRYPTHASH hHash = 0;
  DWORD status = 0;
  TCHAR hexDigits[] = _T("0123456789abcdef");
  DWORD hashLen = MD5LEN;
  BYTE hash[MD5LEN];
  TCHAR pair[2];

  if(*outLength < (MD5LEN + 1) * sizeof(TCHAR)) return ERROR_INSUFFICIENT_BUFFER;

  // Get handle to the crypto provider
  if (!CryptAcquireContext(&hContext,
                           NULL,
                           NULL,
                           PROV_RSA_FULL,
                           CRYPT_VERIFYCONTEXT))
  {
    status = GetLastError();
    return status;
  }

  if (!CryptCreateHash(hContext, CALG_MD5, 0, 0, &hHash))
  {
    status = GetLastError();
    CryptReleaseContext(hContext, 0);
    return status;
  }

  if (!CryptHashData(hHash, (BYTE*)input, inLength, 0))
  {
    status = GetLastError();
    CryptReleaseContext(hContext, 0);
    CryptDestroyHash(hHash);
    return status;
  }

  if (CryptGetHashParam(hHash, HP_HASHVAL, hash, &hashLen, 0))
  {
    memset(output, 0, *outLength);
    for (DWORD i = 0; i < hashLen; i++)
    {
      _stprintf(pair, _T("%c%c"), hexDigits[hash[i] >> 4], hexDigits[hash[i] & 0x0F]);
      _tcsncat(output, pair, 2);
    }

    *outLength = (_tcslen(output) + 1) * sizeof(TCHAR);
  }
  else
  {
    status = GetLastError();
  }

  DEBUGMSG(TRUE, (_T("MD5 hash of %s: %s\r\n"), input, output));

  CryptDestroyHash(hHash);
  CryptReleaseContext(hContext, 0);

  return status;
}

Both of these will probably work on the desktop as well, but I haven't actually tested there, so YMMV. 

This is also a great example of why I prefer C# for helping keep things simple.  Generally speaking, fewer lines of code means fewer bugs.

Monday, May 17, 2010 12:04:21 PM (Central Daylight Time, UTC-05:00)  #     | 
# Thursday, May 06, 2010

We recently ported a desktop SCSF/CAB application over to use the OpenNETCF.IoC framework.  The primary thinking here was that the SCSF has way, way more stuff than we really need, want or use and the SCSF wizards had done wacky things to a few Studio installations, so we wanted to get rid of all of that as well.

I've checked in the fruits of the labors into the Codeplex project, including new desktop project and solution files.

Now you can be even closer to one codebase for both desktop and device projects.  If nothing else, it allows you to not have to remember two different injection frameworks if, like me, you end up doing a lot of work on both platforms.

Thursday, May 06, 2010 5:48:28 PM (Central Daylight Time, UTC-05:00)  #     | 
# Wednesday, April 28, 2010

Somehow we'd missed migrating one of our shared source libraries - the Mobile Ink library - over to CodePlex.  I got that done this afternoon.  More info on using the library can be found here.

 

Wednesday, April 28, 2010 5:23:01 PM (Central Daylight Time, UTC-05:00)  #     | 
# Wednesday, January 06, 2010

For those following the OpenNETCF IoC Framework, I've checked in another set of fixes.  So yes, it's still an active project.

Wednesday, January 06, 2010 12:42:12 PM (Central Standard Time, UTC-06:00)  #     | 
# Tuesday, January 05, 2010

Ages ago I did a science project where I was reading and writing registers from managed code.  It worked well and there is absolutely no reason that you shouldn't be able to do this kind of thing.  Windows CE is an embedded platform, and affecting hardware is what we do in the embedded world.

Well, as with any code posted on the web (and in fact this got rolled into the Smart Device Framework), it got used in some actual shipping products.  Great!  i'm not the only one who thinks we should be able to do this stuff.

Well, over the past 6 months or so I had a few different people contact me saying that the code either didn't work, or did work on their earlier hardware, but was now giving strange behavior.

Most telling was that if you hooked up a scope to the memory, you would see 4 write pulses when writing to a register.  Furthermore, all 4 bytes in the register ended up getting set to the last byte in the array you wanted to write.  For example, if you wrote 0x12345678 to a register, it would actually get set to 0x56565656.

Now I know that this code originally worked.  I wrote it using actual hardware (PXA255) so the behavior was new.  The fact that there were 4 strobes on the memory strongly suggested that the code was actually doing 4 individual writes for the 4 bytes, not a single, atomic 4-byte write.

I looked at the code and it couldn't be more simple.  A write boiled down to this:

public void WriteInt32(int data)
{
   Marshal.WriteInt32(m_addressPointer, data);
}

A bit more investigation found that the behavior was fine under CF 1.0, but started failing in CF 2.0.  What that means is that Microsoft changed the underlying implementation of the Marshal class, and in a bad way.  Why would they do such a stupid, stupid thing?  The new mechanism is going to not only cause a break for the writes we're looking at, but it's also a lot slower.

Since I don't know who did this, I can only guess as to what happened.  If you write a 4-byte value to an address that is not DWORD aligned (i.e. not evenly divisible by 4) then an ARM processor with throw a fault and puke on you (x86 throws, but handles it internally).  My bet is that some edge case got reported that the CF was throwing an unaligned exception, and some idiot developer decided that the heavy-handed solution of changing the write to happen byte-by-byte would be the solution.  Yes, it prevents the error, but it causes bugs and is bad, bad form.  Personally I'd like to slap the persone who made the change and the person who reviewed the change and thought it was ok.

So how do you get around this?  Well by doing what the CF itself should have done.  Instead of using Marshal, you use unsafe code and pointers, and check address alignnment before writing.  Something like this:

public unsafe void WriteInt32(int data, int offset)
{
  int baseAddr = (m_addressPointer.ToInt32() + offset);
  if (baseAddr % 4 == 0)
  {
    // dword aligned
    uint* pDest = (uint*)(baseAddr);
    *pDest = (uint)data;
  }
  else if (baseAddr % 2 == 0)
  {
    // word aligned
    ushort* pDest = (ushort*)(baseAddr);
    *pDest = (ushort)(data >> 0x10);
    pDest += 2;
    *pDest = (ushort)(data & 0xFFFF);
  }
  else
  {
    // byte aligned
    byte* pDest = (byte*)(baseAddr);
    foreach (byte b in BitConverter.GetBytes(data))
    {
      *pDest = b;
      pDest++;
    }
  }
}

This is a classic case of someone not understanding the problem they are solving.  This logic should have been done well below us - that's the whole point of using managed code, right?  To simplify things.  Unfortunately it also allows many developers to write code without understanding what it's really doing, and in my mind that's crazy risky. 

Testing also shows that the Read and Copy methods are similarly broken and all of these bugs still exist in CF 3.5, and I strongly suspect it will remain broken in future versions.  So beware, if you're using Marshal for moving data, you could probably get 4x performance improvement by using a pointer instead.

Tuesday, January 05, 2010 11:53:28 AM (Central Standard Time, UTC-06:00)  #     | 
# Friday, November 20, 2009

If you've ever done mstest unit testing with a Smart Device project, then you're painfully aware of how badly Microsoft dropped the ball on this one.  Debugging a unit test requires making device registry modifications, adding a call to Debugger.Break in your code, then telling Studio to Attach to Remote Process once the breakpoint has been hit.  Seriously, that's their officially published answer to how you debug a Smart Device unit test!

If you know anything about testing, you know that keeping the cycle time for a test to a minimum.  The longer it takes a developer to go from "start testing" to a break point where they can step, then the less productive they're going to be.  Even worse, if the process is painful, slow and convoluted (check, check and check for Microsoft's recommendation), they're likely to just skip writing tests altogether.

Internally we get around this by using our own test runner which uses Reflection to load up and run tests.  I've decided to once again give back to the community and publish this gem as part of Project Resistance (it will get checked in to the IoC Framework as well).

It does not support everything that mstest does, but it's got enough to get you going, and I think it's at least reasonably easy to modify if it doesn't meet your needs.  The currently supported attributes are:

It also might now be obvious how to set it up for your own app.  You need to add a reference to your test assemblies (so VS will deploy them - for some stupid reason you can't tell it to do so via the Configuration Manager) and make sure all projects are set to deploy to the same place.

As usual, if you have feedback or updates, please let me know.  Submitting a patch right on one of the project portals is probably the easiest way (hint, hint).

It's probably worth noting here that the code for this is the CFTestRunner project, and you have to pull it from the source tab on the project site (it's not in the release download yet).

Friday, November 20, 2009 12:36:06 PM (Central Standard Time, UTC-06:00)  #     | 
# Tuesday, October 06, 2009

When you hire a consultant you're not simply getting someone to sling code (if that's what you want, hire a contractor).  Sure we do that, but we bring along a lot of experience, and quite often a plethora of code libraries.  We tend to see a wide variety of applications and tend to build up wide repositories of knowledge.  For example we're just putting finishing touches on a C# wrapper for using the Anviz SM2000 fingerprint reader.  It's a low-cost OEM module that seems to perform pretty well.  So if you're building a custom Windows CE (now Windows Embedded Compact to be true to Microsoft's marketing engine) device where you need fingerprint authentication, we could have you enrolling and authenticating fingerprints from a managed application very, very quickly instead of you spending a week or more trying to build up code from a not-so-clear spec document.

Tuesday, October 06, 2009 9:34:25 AM (Central Daylight Time, UTC-05:00)  #     | 
# Friday, August 14, 2009

Consider the following code:

public class MyAttribute : Attribute
{
  public MyAttribute(UnmanagedType foo)
  {
  }

  public int Bar { get; set; }
}

[StructLayout(LayoutKind.Sequential)]
public struct Test
{
  [CLSCompliant(false)]
  [MyAttribute(UnmanagedType.ByValArray, Bar = 4)]
  [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
  public ushort[] ArrayShorts;
}

class Program
{
  static void Main(string[] args)
  {
    FieldInfo field_info = typeof(Test).GetField("ArrayShorts");
    object[] custom_attributes = field_info.GetCustomAttributes(typeof(MarshalAsAttribute), false);
    Debug.WriteLine("Attributes: " + custom_attributes.Length.ToString());
    custom_attributes = field_info.GetCustomAttributes(typeof(MyAttribute), false);
    Debug.WriteLine("Attributes: " + custom_attributes.Length.ToString());
    custom_attributes = field_info.GetCustomAttributes(typeof(CLSCompliantAttribute), false);
    Debug.WriteLine("Attributes: " + custom_attributes.Length.ToString());
  }
}

The code defines a custom attribute, then defices a struct that uses that attribute, along with some BCL-provided attributes.  It then uses reflection to get back those attributes. What would you expect the output to be?  Probably this:

Attributes: 1

Attributes: 1

Attributes: 1


And if you run it against the full framework, that's exactly what you get.  But if you run it against the Compact framework, you instead get this:

Attributes: 0

Attributes: 1

Attributes: 1

Yes, you're seeing it correctly, the MarshalAsAttribute doesn't show up.  So immediately I call this a bug since the frameworks differ and there is no documentation that says they should (even if it were documented I'd call it a bug).  So I did a little asking around and a little research.  It turns out that there is a difference between MarshalAs and the other attributes.  According to the ECMA-335 spec for the Common Language Interface, section 21.2

There are two kinds of custom attributes, called genuine custom attributes, and pseudo custom attributes.

Custom attributes and pseudo custom attributes are treated differently, at the time they are defined, as follows:

  • A custom attribute is stored directly into the metadata; the‘blob’ which holds its defining data is stored as-is. That ‘blob’ can be retrieved later.
  • A pseudo custom attribute is recognized because its name is one of a short list. Rather than store its ‘blob’ directly in metadata, that ‘blob’ is parsed, and the information it contains is used to set bits and/or fields within metadata tables. The ‘blob’ is then discarded; it cannot be retrieved later.

The spec goes on to say that the MarshalAsAttribute is a pseudo custom attribute, so it would fall into the second bullet above.  If you re-read the last sentence in that bullet  you'll see that "it cannot be retrieved later."  So, in fact, it seems like the full framework is the one in error here, at least per the spec! This attribute should not be readable at all at run time.

Now why the authors of the spec would have made such a strange exception is beyond me.  THe method in the language is clearly called GetCustomattributes, not "GetOnlySomeCustomAttributes" and this is the first I've even heard of pseudo cutom attributes, which tells me it's not well documented and likely not well known.  So while the Compact Framework follows the spec to the letter, if you go with reasonable expectations as a guideline, I'm going to have to say that it's behavior is wrong and that the spec is incorrect and needs revising.

Friday, August 14, 2009 10:15:23 AM (Central Daylight Time, UTC-05:00)  #     | 
# Wednesday, July 15, 2009

I've been using the OpenNETCF IoC framework for several new projects lately, and I really like it.  I think my favorite feature is the fact it gives you the ability to subscribe to events from other objects without requiring an instance of the event publisher.  In fact you don't even need to know the publishers type or even of its existence - you simply need to know the name of the event that might get raised.

The only downside I've hit lately is that the performance for creating objects is not fantastic.  Of course that's to be expected.  To achieve all of this really loosely coupled architecture, we have to rely heavily on Reflection.  Not only do we have to do object creation through Reflection, but we have to do a lot of complex querying looking for type names and attributes, and this can take a while.

In an effort to improve things a bit, I'm working on updating the internal ObjectFactory class that is responsible for all of this heavy lifting.  I've added caches for most of the items that get queried through Reflection.  For example now when we want to find all of the published events from a specific Type, we use reflection the first time, but every subsequent time we pull the list from a cache.

A unit test for creating a single object that both publishes and subscribes to events yields nearly a 5,000% improvement in creating speed for the second object of the same type.  Yes, you read that right - it is 50 times faster creating the exact same object type the second time around.  There's no avoiding the pain the first time we have to inspect a Type, but I've already noticed a marked improvement in app startup time using the new changes.

Wednesday, July 15, 2009 12:27:27 PM (Central Daylight Time, UTC-05:00)  #     | 
# Friday, June 26, 2009

As you may have guessed from some of my recent blog entries, we're making a push to get a lot of our shared source code out to the Codeplex servers.  Our serial library is the latest one to make the move.  It's now available at http://serial.codeplex.com.  As with all of these libraries, if you have the desire to contribute, fix, extend, or whatever just let us know and we'll add you as a developer.

Friday, June 26, 2009 9:24:33 AM (Central Daylight Time, UTC-05:00)  #     | 
# Thursday, June 25, 2009

In my last blog entry, I showed a workaround for getting mouse events for the CF ListView.  Since I can't leave well enough alone, I decided I'd actually try implementing the HitTest method (now that we have a valid x,y click coordinate).  Here's the result:

namespace OpenNETCF.Windows.Forms
{
  [Flags]
  public enum ListViewHitTestLocations
  {
    None = 0x01,
    Image = 0x02,
    Label = 0x04,
    StateImage = 0x08,

    AboveClientArea = 0x08,
    BelowClientArea = 0x10,
    RightOfClientArea = 0x20,
    LeftOfClientArea = 0x40,
  }

  public class ListViewHitTestInfo
  {
    public ListViewItem Item { get; set; }
    public ListViewHitTestLocations Location { get; set; }
    public ListViewItem.ListViewSubItem SubItem { get; set; }
  }
}

namespace OpenNETCF.Core
{
  using OpenNETCF.Windows.Forms;
  using System.Runtime.InteropServices;
  using System.Diagnostics;

  public static partial class Extensions
  {
    private const int LVM_FIRST = 0x1000;
    private const int LVM_GETCOUNTPERPAGE = LVM_FIRST + 40;
    private const int LVM_SUBITEMHITTEST = LVM_FIRST + 57;

    private struct LVHITTESTINFO
    {
      public int x;
      public int y;
      public ListViewHitTestLocations flags;
      public int iItem;
      public int iSubItem;
    }

    public static int GetVisibleRowCount(this ListView lv)
    {
      return Win32Window.SendMessage(lv.Handle, LVM_GETCOUNTPERPAGE, 0, 0).ToInt32();
    }

    public static ListViewHitTestInfo HitTest(this ListView lv, int x, int y)
    {
      LVHITTESTINFO info = new LVHITTESTINFO();
      info.x = x;
      info.y = y;
      GCHandle pInfo = GCHandle.Alloc(info, GCHandleType.Pinned);
      try
      {
        Win32Window.SendMessage(lv.Handle, LVM_SUBITEMHITTEST, 0, pInfo.AddrOfPinnedObject());
        LVHITTESTINFO result = (LVHITTESTINFO)pInfo.Target;
        ListViewHitTestInfo lvhti = new ListViewHitTestInfo();

        lvhti.Location = result.flags;
        switch (lvhti.Location)
        {
          case ListViewHitTestLocations.Image:
          case ListViewHitTestLocations.Label:
          case ListViewHitTestLocations.StateImage:
            lvhti.Item = lv.Items[result.iItem];
            lvhti.SubItem = lvhti.Item.SubItems[result.iSubItem];
            break;
        }
        return lvhti;
      }
      finally
      {
        pInfo.Free();
      }
    }

    public static ListViewHitTestInfo HitTest(this ListView lv, Point point)
    {
      return lv.HitTest(point.X, point.Y);
    }
  }
}

And usage looks like this:

public partial class Foo : Form
{
  public Foo()
  {
    InitializeComponent();
    listView.Items.Add(new ListViewItem(new string[] { "Item A", "Sub A1", "Sub A2", "Sub A3" }));
    listView.Items.Add(new ListViewItem(new string[] { "Item B", "Sub B1", "Sub B2", "Sub B3" }));
    listView.Items.Add(new ListViewItem(new string[] { "Item C", "Sub C1", "Sub C2", "Sub C3" }));
    listView.Items.Add(new ListViewItem(new string[] { "Item D", "Sub D1", "Sub D2", "Sub D3" }));
    listView.Items.Add(new ListViewItem(new string[] { "Item E", "Sub E1", "Sub E2", "Sub E3" }));

    ClickFilter filter = new ClickFilter(listView);
    Application2.AddMessageFilter(filter);
    filter.MouseDown += new MouseEventHandler(filter_MouseDown);

  }

  void filter_MouseDown(object sender, MouseEventArgs e)
  {
    ListView lv = sender as ListView;
    ListViewHitTestInfo hti = lv.HitTest(e.X, e.Y);

    if (hti.Item != null)
    {
      Debug.WriteLine(string.Format("Item: {0}, SubItem: {1}", hti.Item.Text, hti.SubItem.Text));
    }
  }
}

All of this will end up in SDF v. Next, but why do we have to implement fundamental things like this that should already be there?  It was excusable to be missing in v 1.0.  Maybe even in v 2.0, but in 3.5?  Really?

 

Thursday, June 25, 2009 11:38:57 AM (Central Daylight Time, UTC-05:00)  #     | 
# Wednesday, June 24, 2009

It took a long time to get here, but we've finally release what I'm calling the version 1.0 (pervious version were 0.9.x) release of the OpenNETCF.IoC Framework.  In case you've not been tracking this project, it is a public-domain-licensed (you can't get any more free and unencumbered than that) framework that provides both inversion of control and dependency injection for .NET Compact Framework applications (it can be used on the desktop as well).  It's roughly modelled after Microsoft's SCSF and CAB frameworks, but it's scaled down and optimized for running on mobile and embedded devices, plus I "fixed" stuff that I think the SCSF got wrong (like having a static, globally available RootWorkItem and the ability to insert IMessageFilters into the application's message pump).

This framework is in use in a couple of commercial applications already, so it's been pretty heavily tested and vetted.  I still want to add a few more features as well and go back through it looking for performance optimizations, but it certainly has enough features to be used in applications today.

This release also ships with a full-blown, real-world sample application, not just the typical "Northwind" type of application.  The sample is called WiFiSurvey and it can be used to survey WiFi AP coverage of a site and to monitor associated AP changes as well as network addressability of a device.

WiFiSurvey has a Configuration service, a SQL CE 3.5-backed Data Access Layer, an Infrastructure module and a an application shell all of which are fully decoupled from one another and that are all loaded dynamically using an XML definition file.  The shell makes use of both a DeckWorkspace and a TabWorkspace, showing you not just how to use them, but also how to create your own workspaces if need be.

The WiFiSurvey application has a single source base for all target platforms and has been tested on the following platforms:

  • ARM-based CE 6.0 with a 320x240 (landscape) display.
  • Pocket PC 2003 240x320 (portrait)
  • WinMo 5.0 240x320 (portrait and landscape)

The IoC framework has additionally been tested on x86-based CE 5.0 and CE 6.0 devices.

As a side note, the WiFiSurvey sample application is also a good example of using the OpenNETCF Smart Device Framework for getting wireless information.

Wednesday, June 24, 2009 1:54:24 PM (Central Daylight Time, UTC-05:00)  #     | 
# Friday, June 05, 2009
I've pushed up a new set of features and fixes for the OpenNETCF.IoC Framework as well as rolled a new release. See the Project Site for full details on what changed, but the general feature additions are that the framework now supports loading modules from configuration XML (you can now really decouple your app modules) and I added support for a DeckWorkspace and all the trappings around starting your app from a workspace.
Friday, June 05, 2009 1:21:56 PM (Central Daylight Time, UTC-05:00)  #     | 
# Wednesday, April 15, 2009

If you've ever needed to automatically launch an application under Windows CE then you probably know that one of the most common ways is to put an entry into the device registry under HKLM\Init.  This works great for native applications, but for CF applications success is hit or miss.  The reason is that CF applications require certain system APIs to be up and running before they can run.  For a native app that has this requirement, the solution is simple: you call either IsApiReady or WaitForAPIReady (depending on the OS version) and then continue when you're satisfied.  For CF apps it won't work.  The APIs need to be ready long before Main is entered and any of your managed code is running.

Of course the "right" solution is that the CF team should have put that check into the loader so we could launch this way, but they didn't so we have to work around it.

So how do we get around this?  Well we leverage the initialization process itself.  When an app is launched from HKLM\Init, it is responsible for calling SignalStarted once it is running.  This allows any other items launching from HKLM\Init to set up dependencies, for example if item 60 depends on item 50, item 60 won't launch until item 50 has called SignalStarted.  What we can do is create a native application that acts as a launch gate.  This gate app will call the appropriate wait function, and only after the APIs we need are available does it call SignalStarted.  We can then launch any CF application using HKLM\Init by simply having the gate application launch first, and then having the CF app depend on the gate.

So in the registry, it would look like this:

[HKEY_LOCAL_MACHINE\Init]
    "Launch90"="gateapp.exe"
    "Depend90"=hex:1e,00 ; depend on GWES, which is at 30
    "Launch91"="MyCFApp.exe"
    "Depend91"=hex:5a,00 ; depend on gateapp

And here's the source code for a basic gate app:

extern "C" DWORD WaitForAPIReady(DWORD, DWORD);
extern "C" BOOL IsAPIReady(DWORD hAPI);

int _tmain(int argc, _TCHAR* argv[])
{
    // quick sanity check - HKLM\Init will send in our order number
    if(argc == 0) return 0;

    BOOL success = FALSE;

    // wait for window manager - that should be enough for us
    #if _WIN32_WCE > 0x500
        success = (WaitForAPIReady(SH_WMGR, 5000) == WAIT_OBJECT_0);
    #else
        int i = 0;
        while((! IsAPIReady(SH_WMGR)) && (i++ < 50))
        {
             Sleep(100);
        }

        success = (i < 50);
    #endif

    if(success)
    {
        int launchCode = _ttoi(argv[1]);
        SignalStarted(launchCode);
    }
    else
    {
        RETAILMSG(TRUE, (_T("CFInitGate timed out - SH_WMGR was not ready after 5 seconds\r\n")));
    }

    return 0;
}

Wednesday, April 15, 2009 12:46:38 PM (Central Daylight Time, UTC-05:00)  #     | 
# 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?!


	
Thursday, October 02, 2008 5:40:17 PM (Central Daylight Time, UTC-05:00)  #     | 
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();
}
Thursday, October 02, 2008 11:04:26 AM (Central Daylight Time, UTC-05:00)  #     | 
# 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)]
Thursday, August 14, 2008 9:40:38 AM (Central Daylight Time, UTC-05:00)  #     | 
# 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.

Tuesday, August 12, 2008 11:03:11 AM (Central Daylight Time, UTC-05:00)  #     | 
# 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.

 

Wednesday, May 28, 2008 11:46:42 AM (Central Daylight Time, UTC-05:00)  #     | 
# 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.

Monday, May 05, 2008 7:14:17 AM (Central Daylight Time, UTC-05:00)  #     | 
# 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;
    }
}

Saturday, April 12, 2008 2:11:20 PM (Central Daylight Time, UTC-05:00)  #     | 
# 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.

Saturday, January 26, 2008 12:11:37 AM (Central Standard Time, UTC-06:00)  #     | 
# 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.

Tuesday, January 22, 2008 4:12:07 PM (Central Standard Time, UTC-06:00)  #     | 
# Thursday, January 03, 2008
Thursday, January 03, 2008 4:51:06 PM (Central Standard Time, UTC-06:00)  #     | 
# 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.

Monday, December 31, 2007 2:25:40 PM (Central Standard Time, UTC-06:00)  #     | 
# 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.

 

Thursday, December 06, 2007 5:15:00 PM (Central Standard Time, UTC-06:00)  #     | 
# Thursday, November 29, 2007
We just published a new article on using WCF from device applications on the Community siteRead it here.

Thursday, November 29, 2007 1:40:14 PM (Central Standard Time, UTC-06:00)  #     | 
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.

Thursday, November 29, 2007 10:20:53 AM (Central Standard Time, UTC-06:00)  #     | 
# 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.

Tuesday, November 13, 2007 11:10:29 AM (Central Standard Time, UTC-06:00)  #     | 
# Tuesday, October 30, 2007
OpenNETCF is proud to announce that we've released Padarn - an ASP.NET Web Server designed to run under the limited resource environment of Windows CE.  For more information on Padarn, visit our web site.

Tuesday, October 30, 2007 10:51:03 AM (Central Standard Time, UTC-06:00)  #     | 
# Friday, September 28, 2007
Mark has just published a couple articles with just about everything you could want to know about inking:

Using the OpenNETCF Mobile Ink Library for Windows Mobile 6
Sharing Windows Mobile Ink with the Desktop

Friday, September 28, 2007 3:37:14 PM (Central Daylight Time, UTC-05:00)  #     | 
# Friday, September 21, 2007

If you write applications that query a database - any database - then you might want to check out my new article on data caching.

Friday, September 21, 2007 11:09:11 AM (Central Daylight Time, UTC-05:00)  #     | 
# Thursday, September 20, 2007

In case you've missed it, the OpenNETCF Community Site publishes articles relevent to mobile and embedded developers.  We just published the latest entitled Developing Connected Smart Device Application with SqlClient. Check it (and the previous articles) out.

Thursday, September 20, 2007 5:19:52 PM (Central Daylight Time, UTC-05:00)  #     | 
# Tuesday, September 04, 2007

Today we were working on some final testing of a new app and I was irritated by the About Form's behavior of generice CE devices.  When we went to display the form, we'd get the ugly "expanding rectangle" animation of the Form opening, and even worse, when we closed it it would animate the close, then animate the re-opeing of the main form.

Unfortunately the Compact Framework give us absolutely nothing for controlling this behavior (hello?  Windows Mobile isn't the only platform the CF is used on....).  So we have to work around it.

Add the following to your form (or a utility class is what we actually used, as we called this from multiple places):

private const int WS_EX_NOANIMATION = 0x04000000;
private const int GWL_EX_STYLE = -20;

[DllImport("Coredll.dll", SetLastError=true)]
private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);

[DllImport("coredll.dll", SetLastError=true)]
private static extern int GetWindowLong(IntPtr hWnd, int nIndex);

internal static void StopAnimation(Form form)
{
    int style = GetWindowLong(form.Handle, GWL_EX_STYLE);
    style |= WS_EX_NOANIMATION;
    SetWindowLong(form.Handle, GWL_EX_STYLE, style);
}

Then simply call StopAnimation in your Form's constructor immediately after InitializeComponent is called:

public Form1()
{
    InitializeComponent();
    StopAnimation(this);
}

We'll probably add this to the next release of the Smart Device Framework.

Tuesday, September 04, 2007 3:41:52 PM (Central Daylight Time, UTC-05:00)  #     | 

This morning OpenNETCF announced a new web initiative - our Community Web Site. I'll spare you explaining it in detail here, as it's on the front page of the site, but we've got articles and white papers, a public SVN server for shared-source projects and a monthly coding competition. This month we're giving away a copy of Studio 2005 professional and a Windows Mobile 5.0 device of your choice.

To come are Forums and a Wiki.

Let us know what you think.

Tuesday, September 04, 2007 9:42:06 AM (Central Daylight Time, UTC-05:00)  #     | 
# Tuesday, May 15, 2007

We got no direct mention, but there's a real nice live video of the OpenNETCF RoundGauge control in action at 1:16 into this video:

http://www.youtube.com/watch?v=2AavbEAUHAg

In fact that's the exact app I used for testing and developing the RoundGauge way back when.  As you can see, it's running 4 gauges, all updating real time, on a Cirrus 200MHz processor and there is zero hesitation or flicker so yes, fast, smooth graphics *are* possible with the CF.

Tuesday, May 15, 2007 12:18:22 PM (Central Daylight Time, UTC-05:00)  #     | 
# Friday, May 11, 2007

First of all, let's answer the question. Yes, FindWindow can find any Window that exists, regarless of the style bits or state, provided it is given enough information to identify the Window. This often is as simple as the Window's caption (or Text property in C#).

In a recent newsgroup thread, a user argues vehemently that calling the FindWindow API can't find a window that has been hidden (specifically in C# using the Hide() method).  Since my word is not enough to convince him, and since he apparently won't show the code that's failing, I decided to put it to rest with a test application (download the test app here).

Friday, May 11, 2007 8:37:31 AM (Central Daylight Time, UTC-05:00)  #     | 
# Monday, May 07, 2007

Fabien Decret of Adeneo now has a CF Blog.  It's in French so I can't tell you much about it, but the item titles look good and if nothing else he's got a lot of activity.

Monday, May 07, 2007 11:25:07 AM (Central Daylight Time, UTC-05:00)  #     | 
# Wednesday, April 18, 2007

Today someone asked how they might enumerate all files and folders on a device.  The concept is pretty simple, and implementing it with a recursive algorithm is straightforward.  To make it a bit more useful I'm adding the items to a TreeView and saving the full path to the display file/folder in the TreeNode's Tag property, so when a node is selected, you don't have to reassemble the path.

Code:

public void GetListOfAllFilesAndFolders(TreeView view)
{
    view.BeginUpdate();
    view.Nodes.Clear();
    TreeNode node = new TreeNode("My Device");

    AddFolderToTreeNode("\\", node);
    view.Nodes.Add(node);
    view.EndUpdate();
}

private void AddFolderToTreeNode(string rootPath, TreeNode rootNode)
{
    // add in all directories
    string[] dirList = Directory.GetDirectories(rootPath);

    foreach (string dir in dirList)
    {
        TreeNode node = new TreeNode(Path.GetFileName(dir));
        node.Tag = dir;
        AddFolderToTreeNode((string)node.Tag, node);
        rootNode.Nodes.Add(node);
    }

    // add all files
    string[] fileList = System.IO.Directory.GetFiles(rootPath);

    foreach (string file in fileList)
    {
        TreeNode node = new TreeNode(Path.GetFileName(file));
        node.Tag = file;
        rootNode.Nodes.Add(node);
    }
}

Usage:

GetListOfAllFilesAndFolders(treeView1);

Yes, It's that simple.

NOTE: I tested this on an OLDI 56SAM-400 800MHz x86 CE device running CE 5.0 so your mileage may vary.

Wednesday, April 18, 2007 11:41:01 AM (Central Daylight Time, UTC-05:00)  #     | 
# Friday, April 06, 2007

If you've done much development on non-Windows Mobile CE devices I'm sure your painfully aware of the pain in the ass process of getting Visual Studio 2005 to actually attach to the device.  Lately I've been doing nothing but non-WM development and deviced to automate and solidify the process, plus document it while I was at it.

We've got a (native) tool called CEDbgSetup that you run on your device (x86 or ARMv4I supported) and then you set up Studio once and only once, then you can debug every time using wired Ethernet, 802.11, USB RNDIS (and likely any other transport that uses TCP/IP) and without ActiveSync.

The tool supports auto-launching from \Windows\Startup or by setting up an LaunchXX entry in HKLM\Init in the device registry, or just manual click.  It should work headless too, though I've not specifically tested it.

It works with native or managed apps.

The tool, along with full source can be downloaded here (607k zip).

A white paper on how to set up Visual Studio 2005 can be viewed here (144k PDF).

Feedback is appreciated, but keep in mind that its free and completely unsupported (read: don't ask me for support).

Friday, April 06, 2007 4:49:54 PM (Central Daylight Time, UTC-05:00)  #     | 
# Thursday, April 05, 2007

Another feature that missed the 2.1 cutoff - we've added a couple things to our existing EventWaitHandle:

A Set() overload:
public bool Set(int data)

A GetData method:
public int GetData()

For simple, fast, and really easy to implement IPC you can just do this:

Process A
EventWaitHandle wh = new EventWaitHandle(false, EventResetMode.AutoReset, "MY_EVENT_NAME");

int myData = 10;
wh.Set(myData);

Process B
EventWaitHandle wh = new EventWaitHandle(false, EventResetMode.AutoReset, "MY_EVENT_NAME");

if (m_eventsHandle.WaitOne())
{
    int myData = m_eventsHandle.GetData();
}

No MessageWindows, no queues - no ugliness at all - and it works with really minimal headless CE systems.

Thursday, April 05, 2007 10:42:43 AM (Central Daylight Time, UTC-05:00)  #     | 

I know, SDF 2.1 has only been out for a day, but we're always working on the code base.  I just wrapped up a namespace that didn't make the 2.1 cutoff: OpenNETCF.WindowsCE.Services.  It allows you to enumerate, start, stop, refresh, load, unload, set the debug mask and register for autostart any service on the device.

Moving forward we're trying to componentize the SDF, so this one is stand-alone with no additional dependencies (not even the core OpenNETCF.dll library)

Here's a screen shot of my test/sample harness:

 

Thursday, April 05, 2007 10:05:41 AM (Central Daylight Time, UTC-05:00)  #     | 
# Friday, March 23, 2007

SDF 2.1 Preview - Get LIT

Ever have a business scenario where you want your application to perform some task on a periodic basis but the period will be very long - maybe days, weeks or even months?  You want it to occur if the device is asleep or aware at the time?  Well we've got a new SDF class that's designed for this: the LargeIntervalTimer (or LIT).

Let's look at a simple scenario.  Let's say I'm writing a custom calendar.  I want to set up a recurring meeting.  Well let's say the first meeting will be tomorrow at the same time as it is now (for simplicity) and then every 7 days after that.  We want the device to do something on that period (presumably make a noise, vibrate or whatever. 

The LIT is specially designed for this type of scenario.  The code to do achieve that scenario would look like this:

// we want the first timer event o happen 1 day from now
m_lit.FirstEventTime = DateTime.Now.AddDays(1);

// after the first event, we want it to fire every 7 days
m_lit.Interval = new TimeSpan(7, 0, 0, 0);

// we want it to be recurring
m_lit.OneShot = false;

// wire up a handler
m_lit.Tick += new EventHandler(LIT_Tick);

// start the timer
m_lit.Enabled = true;

That's it.  No P/Invokes.  No ugly Notifications, threads, events or whatever.  We've swept all that ugliness under the rug for you so all you need to do it implement your app.

Friday, March 23, 2007 2:56:43 PM (Central Standard Time, UTC-06:00)  #     | 
# Wednesday, February 28, 2007

I've been writing apps in mostly C# for several years now.  Of course I've also been doing drivers and kernel work in C or C++, and that has only improved my ability to write, debug and understand managed code in ways that pure managed developers will never really get.  Hell, I pretty much believe that if your managed code doesn't require allowing unsafe code then it could probably be done better.

Well for a few months now I've been neck-deep in a C++ application.  It was started using MFC, which tends to give me a rash just by saying the letters, but it was a customer decision not mine.  At any rate, I was doing some work with a RAS class where I needed any number of consumers to be able to be notified of any changes in the RAS dial status (dialing, connecting, authenticating, etc.).

Imagine a C# class for RAS that will expose events for when the dial staus changes.  THe first thing you'd need is a delegate, right?

In C# you'd have this (assuming DialStatus is an enum):

delegate void DialStatusChange(DialStatus dialStatus);

Well the c++ I wrote (without even thinking about C#) looked eerily familiar:

typedef void(*DIAL_STATUS_DELEGATE)(DialStatus newStatus);

In C# you'd use the += or -= operators for adding an event handler to a classes event, so we get spoiled.  In VB, you call AddHandler with the event name and then the address of the handler.  Well my C++ had a private vector of function pointers (which the CF maintains internally):

std::vector <DIAL_STATUS_DELEGATE> ConnectionManager::m_statusCallbackList;

And then I added methods for adding and removing handlers to the class:

 void ConnectionManager::StatusAddHandler(DIAL_STATUS_DELEGATE callback)
 {
   m_statusCallbackList.push_back(callback);
 }
 
 void ConnectionManager::StatusRemoveHandler(DIAL_STATUS_DELEGATE callback)
 {
   std::vector<DIAL_STATUS_DELEGATE>::iterator iterator;
 
   for(iterator = m_statusCallbackList.begin() ; iterator != m_statusCallbackList.end() ; iterator++)
   {
     if((*iterator) == callback)
     {
        m_statusCallbackList.erase(iterator);
        break;
     }
   }
 }

And how about usage?  In C#, our event-exposing class would have a defined event:

 event DialStatusChange OnStatusChange;

and then when we want to raise the event (assume it's multicast) from our app, we'd do something like this:

 if (OnStatusChange != null)
 {
     foreach (DialStatusChange dsc in OnStatusChange.GetInvocationList())
     {
         dsc(newStatus);
     }
 }

Well my C++ didn't need the event declaration, but raising the "event" (which is simply calling a function pointer callback) looked like this:

 std::vector<DIAL_STATUS_DELEGATE>::iterator iterator;
 
 recheck2:
 for(iterator = m_statusCallbackList.begin() ; iterator != m_statusCallbackList.end() ; iterator++)
 {
   if(IsBadCodePtr((FARPROC)(*iterator)))
   {
      // invalid callback found (someone hooked us then died without unhooking)
      m_statusCallbackList.erase(iterator);
      goto recheck2;
   }
      (*iterator)(status);
 }

Now this was just off the top of my head, so maybe there are improvements that could be made, but it kind of surprised me how I'd taken the concepts I really learned in C# and translated them back to my C++ code.  So yes, writing C# can make you a better C++ developer.

Wednesday, February 28, 2007 11:02:32 PM (Central Standard Time, UTC-06:00)  #     | 
# Thursday, February 22, 2007

For all those naysayers who complain that we don't do enough free stuff for the community, here's another contribution:

We're now hosting the latest zlib source for Windows CE and a managed wrapper for it.  Click the bitmap or here for more info.

Thursday, February 22, 2007 10:49:30 AM (Central Standard Time, UTC-06:00)  #     | 
# Saturday, January 13, 2007

A guy in the newsgroups who is new to .NET, coming from VB6, is having a hard time drawing on a Form.  The problem with VB6 was it had the Shape controls which provided a crutch for a developer to never actually understand how drawing worked, and now that crutch has been removed.

Drawing is simple - you need a Graphics object to paint on.  The easiest way to get that for your Form is to just override OnPaint - you'll get it as an input parameter.  The other nice effect is that you don't have to do anything other than refresh the Form to get your code to run.

Now normally I don't like to just give out the answer - no one learns much that way - but he seems to genuinely have spent several hours trying to get this, so I figured I'd throw him a bone so he doesn't get frustrated and give up altogether.  So the goal is to draw a "crosshair" on the Form that the user can move around with the D-Pad on the device.  Again, this is a very, very basic example - it took me roughly 15 minutes to do (and that's becasue my VB is very rusty).

Create a device WinForms app.  Select the Form and make sure KeyPreview is True.  Then add this code:

Public Class Form1
   Private crosshairs As New Rectangle(0, 0, 20, 20)
   Private bluePen As New Pen(Color.Blue)
   Private Const StepBy As Int32 = 3

Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
   Dim b As New SolidBrush(Color.Blue)

   ' let the system do it's normal drawing
   MyBase.OnPaint(e)

   'now draw our crosshairs
   DrawCrosshairs(e.Graphics)
End Sub

Private Sub DrawCrosshairs(ByRef g As Graphics)
   g.DrawEllipse(bluePen, crosshairs)
   g.DrawLine( bluePen, _
               crosshairs.Left + crosshairs.Width / 2, _
               
crosshairs.Top - 10, _
               crosshairs.Left + crosshairs.Width / 2, _
               crosshairs.Bottom + 10)
   g.DrawLine( bluePen, _
               crosshairs.Left - 10, _
               crosshairs.Top + crosshairs.Height / 2, _
               crosshairs.Right + 10, _
               crosshairs.Top + crosshairs.Height / 2)
End Sub

Private Sub Form1_KeyDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles MyBase.KeyDown
   If (e.KeyCode = System.Windows.Forms.Keys.Up) Then
      If crosshairs.Top >= StepBy Then
         crosshairs.Offset(0, -StepBy)
      End If
   End If
   If (e.KeyCode = System.Windows.Forms.Keys.Down) Then
      If crosshairs.Bottom <= Me.Height - StepBy Then
         crosshairs.Offset(0, StepBy)
     End If
   End If
   If (e.KeyCode = System.Windows.Forms.Keys.Left) Then
      If crosshairs.Left >= StepBy Then
         crosshairs.Offset(-StepBy, 0)
      End If
   End If
   If (e.KeyCode = System.Windows.Forms.Keys.Right) Then
      If crosshairs.Right <= Me.Width - StepBy Then
         crosshairs.Offset(StepBy, 0)
      End If
   End If
   'this forces OnPaint to be called
   Me.Refresh()
End Sub
End Class

That's all there is to it.

Saturday, January 13, 2007 5:18:19 PM (Central Standard Time, UTC-06:00)  #     | 
# Friday, January 12, 2007

This question comes up a lot, and the well-known and documented workaround is to set your Form's Text property to an empty string to hide it from the Running Programs applet so I won't go into that.  And before reading further, if you're after hiding a Form, then go Google for that info - everything I present here are ways that are proven to not work.  It's simply info for those who are tempted to try another route.

We know that the Running Programs applet simply enumerates all top-level windows with text.  We also know that a lot of native apps don't have the problem of child windows showing up, so I decided to try to mimic that behavior with some P/Invoke shenanigans.  The test app simply had 2 forms - Form1 would create a Form2 instance on a button click and then call ShowDialog on the new Form2 instance.  After that (if it got that far), I'd check the applet to see if my code worked.

Method 1: Reparent the Form with SetParent

[DllImport("coredll.dll", SetLastError = true)]
internal static extern IntPtr SetParent(IntPtr hwndChild, IntPtr hwndNewParent);

  • Tried calling this after creating the Form2 and before calling ShowDialog.  ShowDialog subsequently throws an ArgumentException
  • Tried calling it in the ctor of Form2. ShowDialog subsequently throws an ArgumentException
  • Tried passing Form1's Handle to Form 2, then calling SetParent in a Form2.Activate handler. ShowDialog subsequently throws an ArgumentException
  • Tried calling SetParent from a button click in Form2, so after the dialog is loaded and shown.  No exception now, but when SetParent runs, Form1 gains scrollbars, Form2's controls end up in Form1 along with Form1's controls (so a mash up of both) and all controls are non-responsive.

Method 2: Change the Window Style bits

internal const int GWL_STYLE = -16;
internal const int GWL_EXSTYLE = -20;
internal const uint WS_CHILD = 0x40000000;
internal const uint WS_EX_APPWINDOW = 0x00040000;

[DllImport("coredll.dll", SetLastError = true)]
internal static extern uint SetWindowLong(IntPtr hWnd, int nIndex, uint dwNewLong);

[DllImport("coredll.dll", SetLastError = true)]
internal static extern uint GetWindowLong(IntPtr hWnd, int nIndex);

  • Tried setting style to WS_CHILD after creation and before ShowDialog.  Still appears in applet.
  • Tried setting style to WS_CHILD in Form2 ctor.  Still appears in applet.
  • Tried setting style to WS_CHILD in Form2 Activate handler.  Still appears in applet.
  • Tried setting style to WS_CHILD in Form2 button click handler.  Still appears in applet.
  • Tried unsetting extended style to WS_EX_APPWINDOW after creation and before ShowDialog.  Still appears in applet.
  • Tried unsetting extended style to WS_EX_APPWINDOW in Form2 button handler.  Still appears in applet.

So what's the take away lesson?  CF Forms like to be shown in that damned applet, and the simplest mechanism is still to just set the text to an empty string when you want to hide it.

Friday, January 12, 2007 1:10:24 PM (Central Standard Time, UTC-06:00)  #     | 

When we use a language, often we tend to overlook some of the more obvious constructs or be frustrated by what we think should work.  For example, assume we have this simple problem - our function receives a value and based on that value we run through a switch, but we have code that will be run for multiple cases.  Explaining what I mean in words is tough - so let's look at what I'm trying to say in code.  Assume we have these enums:

[Flags]
enum Foo
{
  NoFoo = 0,
  FooA = 1,
  FooB = 2,
  FooC = 4
}

enum Bar
{
  A,
  B,
  C
}

We want a function that will take in a Bar, and based on that create a Foo.  If Bar is A, the the resulting Foo is a combination of FooA, FooB and FooC.  If Bar is B, then it's a combination of FooB and FooC.  If Bar is C, then the result is just FooC.  In C, we'd just do this:

Foo FooBar(Bar bar)
{
  Foo foo = Foo.NoFoo;

  switch (bar)
  {
    case Bar.A:
      foo |= Foo.FooA;
    case Bar.B:
      foo |= Foo.FooB;
    case Bar.C:
      foo |= Foo.FooC;
    break;
  }
  return foo;
}

Letting each case fall into the next intentionally. Yes it's a contrived example, but you get the idea.  There are cases when we need to do processing like this (like a project I'm doing right now).

Well C# doesn't like this type of construct - I'm not certain why it's illegal (other than missing breaks are common bugs) - but the compiler will say 'Error: Control cannot fall through from one case label ('case 0:') to another'.  So you might code a 'fix' like this:

Foo FooBar(Bar bar)
{
   Foo foo = Foo.NoFoo;

   switch (bar)
   {
      case Bar.A:
         foo |= Foo.FooA;
      break;
      case Bar.B:
         foo |= Foo.FooA;
         foo |= Foo.FooB;
      break;
      case Bar.C:
         foo |= Foo.FooA;
         foo |= Foo.FooB;
         foo |= Foo.FooC;
      break;
   }
   return foo;
}

Not too bad, but if you have to do more processing than a single line it gets ugly and maintainability goes downhill fast.

The thing to keep in mind in C# is that those case statements are actually labels, so you can use them as such, meaning they are valid goto targets, so this code is perfectly valid:

Foo FooBar3(Bar bar)
{
   Foo foo = Foo.NoFoo;

   switch (bar)
   {
      case Bar.A:
         foo |= Foo.FooA;
         goto case Bar.B;
      case Bar.B:
         foo |= Foo.FooB;
         goto case Bar.C;
      case Bar.C:
         foo |= Foo.FooC;
      break;
   }
   return foo;
}

Friday, January 12, 2007 11:34:40 AM (Central Standard Time, UTC-06:00)  #     | 
# Wednesday, January 03, 2007

For those who missed it, my MSDN Webcast is now available as an on-demand download.

Wednesday, January 03, 2007 10:27:46 AM (Central Standard Time, UTC-06:00)  #     | 
# Tuesday, December 19, 2006

If you did not attend my session at last year's MEDC or at MobileConnections then you have yet another chance to learn about memory management in the Compact Framework.  Tomorrow I'll be presenting it again in an MSDN Webcast.

Tuesday, December 19, 2006 8:31:30 PM (Central Standard Time, UTC-06:00)  #     | 
# Monday, December 11, 2006

Over the last week of so I've noticed several questions in the newsgroups about how to synchronize a CE device's time with a time server.  Once again, it's not something I'd done, but I knew there were servers out there that provide the time publicly, so it couldn't bee too tough.

First stop was to see if Wikipedia gave a simple explanation.  Useful, but not exactly what we need - we want to know exactly what the server expects, and what exactly it returns.  It does give us the RFCs.

So a little looking at RFCs and we see that RFC 2030 is very applicable, and gives us all the info we need about the protocol.

A little more looking with Google found a public server domain name from NTP.org that actually rotates through public NTP servers, so we don't have to worry about one being up or not.

Armed with nothing but the second two links I wrote a little code.  This could be far more robust, with fractional seconds. mode, stratum, precision info and all that, but I just wanted to get a reasonable time - so to the second is all I was after.

public DateTime GetNTPTime()
{
   // 0x1B == 0b11011 == NTP version 3, client - see RFC 2030
   byte[] ntpPacket = new byte[] { 0x1B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                                   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                                   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                                   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                                   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                                   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

   IPAddress[] addressList = Dns.GetHostEntry("pool.ntp.org").AddressList;

   if (addressList.Length == 0)
   {
      // error
      return DateTime.MinValue;
   }

   IPEndPoint ep = new IPEndPoint(addressList[0], 123);
   UdpClient client = new UdpClient();
   client.Connect(ep);
   client.Send(ntpPacket, ntpPacket.Length);
   byte[] data = client.Receive(ref ep);

   // receive date data is at offset 32
   // Data is 64 bits - first 32 is seconds - we'll toss the fraction of a second
   // it is not in an endian order, so we must rearrange
   byte[] endianSeconds = new byte[4];
   endianSeconds[0] = (byte)(data[32 + 3] & (byte)0x7F); // turn off MSB (some servers set it)
   endianSeconds[1] = data[32 + 2];
   endianSeconds[2] = data[32 + 1];
   endianSeconds[3] = data[32 + 0];
   uint seconds = BitConverter.ToUInt32(endianSeconds, 0);

   return (new DateTime(1900, 1, 1)).AddSeconds(seconds);
}

Monday, December 11, 2006 9:59:10 AM (Central Standard Time, UTC-06:00)  #     | 
# Wednesday, November 08, 2006

If you attended one of my presentations at the MobileConnections conference, below are direct links to the PPTs and the Sample Applications I showed (if the links aren't active try again in a while as I FTP them up).

Compact Framework 2.0 and the Smart Device Framework 2.0
Presentation PPT
Sample Apps
Get the SDF here

Memory Management in the Compact Framework 2
Presentation PPT
Sample App
Blog Entry on Bitmaps and OOM

Sharing Assets between the CF and the Full Framework
Presentation PPT
ADT CodePlex project

Update (Nov 13, 06): The 'Sharing Assets..." presentation material originally came from Daniel Moth.  Unfortunately several very useful links in the footer of a few slides did not make the transition into the template for this show.  This omission does a disservice to those looking at the material, as they point to far deeper looks at some of the individual topics discussed.  Below are the links that I highly recommend that you visit, as they give much better insight than just the slides.

Slide 3: Visual Studio For Devices - See Resx compatibility for Smart Device projects
Slide 9: CF-Specific classes v2.0 - See Not a Strict subset
Slide 10: Windows Mobile 5.0 - See Microsoft.Windows.Mobile
Slide 11: CF CLR - See JIT
Slide 12: PPC versus Desktop - See Desktop to PPC (Part A)
Slide 16: CF Assemblies on PC - See Retagetable (=256)
Slide 17: Share the code, not the binary - See Share Code (#if FULL_FRAME)
Slide 20: How VS2005 & CF v2.0 improved the Everett story - See Deploy to My Computer

Wednesday, November 08, 2006 11:42:08 AM (Central Standard Time, UTC-06:00)  #     | 
# Tuesday, October 31, 2006

Some info that's worth reading:

Tuesday, October 31, 2006 10:45:58 AM (Central Standard Time, UTC-06:00)  #     | 

NETCFRPM is a great tool, but it's kind of obvious that it was a late addition to the SP1 SDK.  One big problem is the process of deploying.  There are a couple files that must be pushed to the device in order to run, and these are documented only in Steven Pratchner's blog (maybe elsewhere too, but they aren't in the RPM readme deployed with the SDK). Don't get me wrong, I'd rather have it as an afterthought than not at all.

At any rate, without these files you'll launch RPM, but a device connected via ActiveSync won't show up in the Device ComboBox. Once deployed, RPM pushes uses RAPI to do it's work.  What surprises me is that RPM uses RAPI for data transfer, so why doesn't it use RAPI at the start to find the device and push these files to begin with?  The likely answer is that the team just didn't have time to get it done.  Well, since my general philosophy is any time that I do the same operation more than three times, it needs to be automated.  This is especially true when I have to go hunting for files, (or even the list of files that I can't always recall).

So I created a simple bootstrap that does all this for you.  It requires the OpenNETCF.Desktop.Communication library, but that's about it.  It could be extended to correctly detect target processor architecture (currently just uses ARM), and even do the RAPI provisioning (see Steven Blog entry) for WM 5.0 devices that need it, but at the current moment it works with my devices, so it's unlikely that I'll make changes unless I start testing another device.

At any rate, here's the first cut (download the binary here):

namespace OpenNETCF.RPMBootstrap
{
  class Program
  {
    static void Main(string[] args)
    {
      RapiBootstrap();
    }

    private static OpenNETCF.Desktop.Communication.RAPI m_rapi;

    private static string[] m_filnames = new string[] 
   
      @"netcfrtl.dll",
      @"netcflaunch.exe"
    };

    private const string SDK_REG_KEY = @"SOFTWARE\Microsoft\.NETCompactFramework\v2.0.0.0\InstallRoot";
    private const string SOURCE_PATH = @"\WindowsCE\wce400\armv4";
    private const string TARGET_PATH = @"\Windows";
    private const string CE4_ARM_PATH = @"WindowsCE\wce400\armv4";
    private const string RPM_PATH = @"bin\NetCFRPM.exe";

    public static void RapiBootstrap()
    {
      System.Console.WriteLine("OpenNETCF RPMBootstrap Bootstrap");
      System.Console.WriteLine("================================");

      m_rapi = new OpenNETCF.Desktop.Communication.RAPI();

      System.Console.WriteLine("Connecting via ActiveSync...");
      m_rapi.Connect(true, 10000);

      if (!m_rapi.Connected)
      {
        System.Console.WriteLine("Connection FAILED");
        return;
      }
      System.Console.WriteLine("Connected");

      // determine SDK install folder
      Microsoft.Win32.RegistryKey key = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(SDK_REG_KEY);
      string sdkPath = (string)key.GetValue(null);
      key.Close();
      System.Console.WriteLine("CF 2.0 SDK is installed at:\n\t" + sdkPath);
      string localFilePath = System.IO.Path.Combine(sdkPath, CE4_ARM_PATH);

      foreach (string fileName in m_filnames)
      {
        string targetFile = System.IO.Path.Combine(TARGET_PATH, fileName);

        if (!m_rapi.DeviceFileExists(targetFile))
        {
            System.Console.WriteLine(string.Format("{0} not on device", fileName));
            System.Console.Write("Copying...");
            string localFile = System.IO.Path.Combine(localFilePath, fileName);
            m_rapi.CopyFileToDevice(localFile, targetFile);
            System.Console.WriteLine("ok");
        }
        else
        {
            System.Console.WriteLine(string.Format("{0} already on device", fileName));
        }
      }
      System.Console.Write("Disconnecting...");
      m_rapi.Disconnect();
      System.Console.WriteLine("ok");

      // find RPM
      string rpmPath = System.IO.Path.Combine(sdkPath, RPM_PATH);

      if (!System.IO.File.Exists(rpmPath))
      {
        System.Console.WriteLine("Local RPM Binary not found at:\r\t" + rpmPath);
        return;
      }

      // launch
      System.Console.WriteLine("Launching RPM...");
      System.Diagnostics.Process p = System.Diagnostics.Process.Start(rpmPath);
    }
  }
}

Tuesday, October 31, 2006 10:35:18 AM (Central Standard Time, UTC-06:00)  #     | 
# Thursday, October 26, 2006

If you know much about me, you probably know that simple doesn't excite me.  I like problems that are interesting yet tough to solve.  A year or more ago or so I decided that I really needed to know how CAB files work.  If you do much work creating CE devices you're familiar with the frustration of trying to include some third party package in your device image, but all you have is a CAB.  So you have to find all of the files, folders registry entries and the like that the CAB creates - usually through a process of elimination.

Well I knew that a CAB file has to contain that info, and experience had told me that just unpacking the CAB gives you all of the files (though with mangled names).  So I started playing in my free time reverse engineering how they're packed up.  Like many of my side projects, I'd tabled the project a couple months ago due to a lack of time and paying work that needed doing.  That was until this week, when I needed to combine a third-party CAB with a customer's app to give them a single-CAB deployment.  I spent a couple days working on the tool to get it to generate an INF file that was compatible with CABWIZ.  And that brings us to the quiz.  The following is an output from the tool - right from the source 3rd party CAB.  If I run this and the files into CABWIZ I get the came CAB back out that installs on the device as expected.  Any guess on what the CAB is (yes it's so easy it's a rhetorical question)?

; +-------------------------------------------------+
; | INF Generated by OpenNETCF CABConstructor       |
; | visit
http://www.OpenNETCF.com for product info |
; +-------------------------------------------------+

[Version]
Signature="$Windows NT$"
CESignature="$Windows CE$"
Provider="Microsoft"

[CEStrings]
AppName=".NET CF 2.0"
InstallDir="%CE2%"

[DefaultInstall]
AddReg="RegKeys"
CESetupDLL="NETCF_~1.dll"
CopyFiles="Destination3","Destination2"

[CEDevice]
VersionMin="4.0"
VersionMax="4.999"
BuildMax="0xE0000000"
UnsupportedPlatforms="HPC","JUPITER","SMARTPHONE"

[SourceDisksNames]
1=,"SRCFILES",,SourceFiles

[SourceDisksFiles]
"00System.008"=1
"0mscoree.006"=1
"0NETCFv2.000"=1
"cgacutil.005"=1
"CGACUT~1.021"=1
"CUSTOM~1.020"=1
"MICROS~1.018"=1
"MICROS~2.017"=1
"MICROS~4.019"=1
"MSCORE~1.002"=1
"MSCORE~1.022"=1
"mscorlib.007"=1
"NETCF2~1.001"=1
"NETCFA~1.003"=1
"NETCFD~1.004"=1
"NETCF_~1.dll"=1
"NETCF_~1.dll"=1
"SY40C7~1.014"=1
"SY4317~1.016"=1
"SY726F~1.010"=1
"SY9B57~1.013"=1
"SYC6B2~1.011"=1
"SYSTEM~1.015"=1
"SYSTEM~3.009"=1
"SYSTEM~4.012"=1
"_setup.xml"=1

[DestinationDirs]
Destination3=0,"\%CE2%\.NET CF 2.0"
Destination2=0,"\%CE2%"

[Destination3]
"mscorlib.dll","mscorlib.007",,0x40000001
"System.dll","00System.008",,0x40000001
"System.Drawing.dll","SYSTEM~3.009",,0x40000001
"System.Messaging.dll","SY726F~1.010",,0x40000001
"System.Web.Services.dll","SYC6B2~1.011",,0x40000001
"System.Windows.Forms.dll","SYSTEM~4.012",,0x40000001
"System.Windows.Forms.DataGrid.dll","SY9B57~1.013",,0x40000001
"System.Xml.dll","SY40C7~1.014",,0x40000001
"System.Net.IrDA.dll","SYSTEM~1.015",,0x40000001
"System.Data.dll","SY4317~1.016",,0x40000001
"Microsoft.VisualBasic.dll","MICROS~2.017",,0x40000001
"Microsoft.WindowsCE.Forms.dll","MICROS~1.018",,0x40000001
"Microsoft.WindowsMobile.DirectX.dll","MICROS~4.019",,0x40000001
"CustomMarshalers.dll","CUSTOM~1.020",,0x40000001
"cgacutil.exe.-410~-410~ARMV4","CGACUT~1.021",,0x40000001
"mscoree.dll.-410~-410~ARMV4","MSCORE~1.022",,0x40000001

[Destination2]
"netcf2_0license.txt","NETCF2~1.001",,0x40000001
"MSCOREE2_0.dll","MSCORE~1.002",,0x40000001
"netcfagl2_0.dll","NETCFA~1.003",,0x40000001
"netcfd3dm2_0.dll","NETCFD~1.004",,0x40000001
"cgacutil.exe","cgacutil.005",,0xA0000001
"mscoree.dll","0mscoree.006",,0xA0000001

[RegKeys]
HKLM,"GACPath",""%InstallDir%"",0x00000000,"%InstallDir%"

 

Thursday, October 26, 2006 9:33:22 PM (Central Daylight Time, UTC-05:00)  #     | 
# Wednesday, October 25, 2006

So a couple of us at OpenNETCF have been heads down coding in a project for the last couple months.  It's got some interesting aspects and I'll be posting lessons learned over the coming weeks.  One of the biggest problems is that the deployment device for testing is the HP6315, which I'll go on record as saying sucks.  Working around its flakiness is a real pain.  Another problem is the customer's desire to run on the device out-of-the-box to minimize deployment.  That means running with the version of the CF in ROM (CF 1.0 SP2 in this case).

Well the last few bugs in the project's bug database are that the app can't restart once it's been closed.  The first suspect is obviously that the app didn't really shut down all the way, and the CF is preventing new instances from running.

First step is to repro.  It's odd becasue we've been developing for a couple months and neitehr of us have seen this.  We run through the reported repro steps from the customer's functional testing and can't get it to fail.  The customer can repro it every time, and on multiple devices.  It then dawned on me to try a hard-reset, clean device.  Blam!  Sure enough, it failed.

So what's different?  Well in development we use Studio, which pushes down the latest CF (SP3) the first time and then we forget about it.  SP3 fixed something in SP2.  Of course the requirement is to use what's in ROM, so it's time to debug a little.

Next step is to use Remote Process Viewer and confirm that the process is indeed still running before digging into the code.

Well RPC shows that the second instance did in fact launch - now we have 2 instances.  Further, tapping the icon starts more instances.  If you don't understand the implications of this, let me explain a little.  The CF (on PPC devices) enforces singleton app behavior (a bad idea IMHO, but that's a side topic).  Well if we're getting multiple instances, then this "enforcement" is failing  The CF is not only not shutting the first down, but the second is being prevented from fully coming up (maybe the assembly is locked in some way?).

Well, added a little debug code and we find that Application.Run is exiting and Main is running out it's course.  Again, this points at a zombie thread, but that doesn't explain the multiple instance issue.

I spent a couple hours walking our code and a 3rd party library, trying to find a zombie thread or anything that might be doing this to no avail, and then it dawned on me - the app is closing.  We don't need any resources, we just want to end.  Well, let's just be heavy handed

I looked up ExitProcess in the platform headers, and it's and inline that calls TerminateProcess with GetCurrentProcess for the handle.  Digging further leads you to find that GetCurrentProcess() is really just the constant 0x42 (on ARM anyway).  So this is what I ended up with (well trimmed down anyway):

static void Main()
{
    Main main = new Main();
    Application.Run(main);

    if((System.Environment.Version.Major == 1) && (System.Environment.Version.Build < 4292))
    {
        // NOTE:
        // This is a heavy-handed workaround for a bug this app exposes in pre-SP3 CF 1.0 devices
        // For those interested, 0x42 is the expansion of GetCurrentProcess() from the platform SDK headers
        // and ExitProcess is simply an inline calling TerminateProcess with GetCurrentProcess(). 
        TerminateProcess(0x42, 0);
    }
}

[DllImport("coredll.dll", SetLastError=true)]
private static extern int TerminateProcess(uint handle, int uExitCode);

Long live the kludge! 

Wednesday, October 25, 2006 5:34:11 PM (Central Daylight Time, UTC-05:00)  #     | 
# Friday, August 25, 2006

If you read much of what I write, you know that performance is something I like to always keep in mind, and I'm also a big fan of quantitative analysis.  It all started ages ago with a look at the real time capabilities of Windows CE, but most recently I've been looking at managed code.  This entry is the second in a series of looks at performance of managed code, specifically the .NET Compact Framework.  For the first entry, see this blog entry (which became this MSDN article).

Act II - Method calls are Expensive (or Stay in the Shallow End of the Stack!)

As with many of my diatribes into technical problems, this all started with a newsgroup post.  SOmeone posted that the had done a Towers of Hanoi implementation in VB.NET, C# and C++ and that the C# implementation was faster than VB, but got slower in CF 2.0.  They also said that native code was much faster than both (I think he may have said an order of magnitude, but don't quote me on that).

Well he didn't post any code so we could reproduce his results, and I never take anyone on their word on something like this, so I decided to put it to the test.  Before going any further, you may need some background information on what the Towers of Hanoi problem is.  You're probably familiar with it - you just didn't know what it was called.  Go read this Wikipedia entry and come back.

I decide to do a recursive solution, so the meat of the problem is that we have an exponential number of method calls, making the call stack very, very deep.  Just from a memory point of view this is a bad idea (see my MEDC presentation on memory management if you want to know more on the whys of that).  But you'll also see that the expense of method calls in the CF (see Act I) really bites you here as it bites hard.

First, let's look at the code.  In C#, it looks like this:

public class Hanoi
{
  private int[] pegs = new int[3];

  public Hanoi(int totalDisks)
  {
    // start with all disks on peg 0
    pegs[0] = totalDisks;
    pegs[1] = 0;
    pegs[2] = 0;
  }

  public int Solve()
  {
    int et = Environment.TickCount;
    Move(0, 2, 1, pegs[0]);
    return Environment.TickCount - et;
  }

  private void Move(int fromPeg, int toPeg, int intermediatePeg, int disks)
  {
    if(disks == 0) return;

    // move all but one disk to the intermediate peg
    Move(fromPeg, intermediatePeg, toPeg, disks - 1);

    // move the last remaining disk to the destination - no need for the intermediate
    pegs[fromPeg] -= 1;
    pegs[toPeg] += 1;

    // now move all but one off the intermediate peg to the destination peg
    Move(intermediatePeg, toPeg, fromPeg, disks - 1);
  }
}

You can see that Move calls itself twice.  Now try tracing the code in your head if I call Solve when totalDisks is 30.  Ugly.

Now lets look at the C++ implementation.

class Hanoi
{
    private:
        int pegs[3];
        void Move(int fromPeg, int toPeg, int intermediatePeg, int disks);

    public:
        Hanoi(int totalDisks);
        int Solve();
};

Hanoi::Hanoi(int totalDisks)
{
    // start with all disks on peg 0
    pegs[0] = totalDisks;
    pegs[1] = 0;
    pegs[2] = 0;
}

void Hanoi::Move(int fromPeg, int toPeg, int intermediatePeg, int disks)
{
    if(disks == 0) return;

    // move all but one disk to the intermediate peg
    Move(fromPeg, intermediatePeg, toPeg, disks - 1);

    // move the last remaining disk to the destination - no need for the intermediate
    pegs[fromPeg] -= 1;
    pegs[toPeg] += 1;

    // now move all but one off the intermediate peg to the destination peg
    Move(intermediatePeg, toPeg, fromPeg, disks - 1);
}

int Hanoi::Solve()
{
    int et = GetTickCount();
    Move(0, 2, 1, pegs[0]);
    return GetTickCount() - et;
}

You can see that it's really not much different - in fact it's really, really close.  So what do we see for results when we run these? Look at the table below (my device was an Axim X30 with a PXA270 processor)

disks C# - CF 2.0 C++ Diff % improvement
20 744 461 283 38%
21 1518 1148 370 24%
22 2997 2068 929 31%
23 5964 4006 1958 33%
24 11892 7646 4246 36%
25 23942 14875 9067 38%
26 47945 29423 18522 39%
27 95674 58496 37178 39%
28 190917 116837 74080 39%

The C++ version performed nearly 40% better.  Of course the C# JIT compiler is running on a mobile device, with presumably much less power than a desktop machine, so the JITter is built to optimize for compile speed, not execution speed.  To try to level the playing field, I compiled the C++ version in Debug mode to turn off all compiler optimizations.  I can't actually see what the CF JITter creates for assembly, so we can't be sure if the resulting code is the same, but that's the nearest I can come.

So we know that the implementation code is near identical.  We also know from Act I that identical code in a single method has no performance difference between native and managed code.  We also know from Act I that method calls are quite expensive.  A reasonable conclusion then is that the performance degradation we're seeing here is nearly all in the cost of method calls.  Does this mean that managed code performance is terrible?  The answer is "it can be if you don't fully understand what the CLR is doing." The lesson learned here then is to either refactor the algorithm to keep your call stack short (there are non-recursive solutions to this problem - maybe another day I'll test that), or put heavily recursive stuff into a native library and P/Invoke to it.

If you want to try these out on your own device, you can download the source code here (post your results in the comments if you'd like).  The source actually points out another lesson, this time in UI development speed. The C# version has a nice UI that I put together in about 10 minutes.  Writing a nice UI would have taken quite a bit longer in C++ so I didn't even bother. With the C++ version you have to get the results from by running in eVC or Studio and setting a breakpoint in the calling loop and writing down the number after each iteration.

Friday, August 25, 2006 11:09:58 AM (Central Daylight Time, UTC-05:00)  #     | 
# Wednesday, August 23, 2006

We've had an idea for a new project for some time, and we've finally decided to put out a code seed for it to see if the community wants to get involved.

The idea is this - Create a native coredll.dll library for the desktop that exactly matches the one exposed by Windows CE (same funtions at the same ordinals).  In theory, this would allow you to run Compact Framework applications against the full framework including code that P/Invokes.  The long-term goal is to implement every funtion (there are about 1800 of them, we've seeded the project with 50), but the milestone I'm shooting for is to get this library to a point that the SDF will run on the desktop.

Why would you want this library you ask?  The answer is fairly simple - to help in debugging and unit testing.  I don't envision you shipping a product that runs on CE and XP, but I do see great value in being able to run your CF assemblies through NUnit or Team Suite unit tests, which today cannot be done on a device.  This project is an enabler for that.

THe project is located at CodePlex as the OpenNETCF Advanced Debugging Toolkit.  Look for more pieces to the toolkit as time progresses.

Wednesday, August 23, 2006 1:00:11 PM (Central Daylight Time, UTC-05:00)  #     | 
# Tuesday, August 22, 2006

The Bitmap class in the Compact Framework is a confusing thing, largely because it has abstracted what the OS is doing underneath a little too far.  For example, look at the following code:

Bitmap bmp1 = new Bitmap(fileStream);
Bitmap bmp2 = new Bitmap(200, 200);

Let's assume that fileStream is a valid stream to a resource bitmap file that is 100x100 in size.  So is there any difference between bmp1 and bmp2, other than the fact bmp1 presumably has some color data in it?  The answer is yes - there's a very big difference, and that difference can have a huge impact on application performace as well as cause exceptions.

So let's look at this a little deeper with some examples.  Here's the first:

int iterations = 0;

while (true)
{
  try
  {
    iterations++;
    Bitmap b = new Bitmap(GetImageStream());
    if (iterations % 100 == 0)
    {
      Debug.WriteLine(string.Format("{0} objects", iterations));
    }
  }
  catch
  {
    Debug.WriteLine(string.Format("Failed after {0} objects", iterations));
    Debugger.Break();
  }
}

If I run this code (GetImageStream() just pulls an image from an embedded resource), the app will run forever, occasionally spitting out the debug port how many hundreds of objects it's created.  If you run RPM on it you'll see memory getting allocated, the GC firing occasionally and resources being freed up.  All is well in the world of managed code and everything is working as expected.  Hooray.

Now let's change that ever so slightly to this:

int iterations = 0;

while (true)
{
  try
  {
    iterations++;
    Bitmap b = new Bitmap(200, 200); // <--- CHANGED HERE
    if (iterations % 100 == 0)
    {
      Debug.WriteLine(string.Format("{0} objects", iterations));
    }
  }
  catch
  {
    Debug.WriteLine(string.Format("Failed after {0} objects", iterations));
    Debugger.Break();
  }
}

Note the single change where the bitmap is created.  Try running this and after a few iterations - the exact number depends on available device memory - it will OOM (throw an out of memory exception).  On the device in front of me it was about 40.

So the first thing to do is theorize why this would happen.  Seems like the Bitmap's resources aren't getting freed after it goes out of scope at the end of the while block.  An explicit call to Dispose() may solve it if that's the case, so let's try another test.

int iterations = 0;

Bitmap b = null;
while (true)
{
  if (b != null)  // explicit disposal
    b.Dispose();

  try
  {
    iterations++;
    b = new Bitmap(200, 200);
    if (iterations % 100 == 0)
    {
      Debug.WriteLine(string.Format("{0} objects", iterations));
    }
  }
  catch
  {
    Debug.WriteLine(string.Format("Failed after {0} objects", iterations));
    Debugger.Break();
  }
}

Sure enough, when we run this, it behaves like the first.  Strange that the Bitmap behaves differently depending on which constructor we use - this is contrary to common sense, right? 

So let's think a little more.  A Bitmap has a large area of unmanaged resources and some managed resources.  It seems that when we create a bitmap using the size ctor, the finalizer doesn't get run when an OOM happens.  Let's test again and see if that really is what's going on. We'll remove the explicit Dispose call and wait for the finalizers and try again when we OOM.

int iterations = 0;

while (true)
{
  try
  {
    iterations++;
    Bitmap b = new Bitmap(200, 200);
    if (iterations % 100 == 0)
    {
      Debug.WriteLine(string.Format("{0} objects", iterations));
    }
  }
  catch (OutOfMemoryException)
  {
    Debug.WriteLine("Waiting for finalizers to run..."));
    GC.WaitForPendingFinalizers();
    Bitmap b = new Bitmap(GetImageStream());
  }
}

When we run this one, again all is well in managed code land, though we see it waiting for finalizers to run a lot, and that catch is an expensive one for perf (as all exceptions are). At this point I think "well that surely has to be a bug" but I often like a second opinion, so I went right to the source and asked the CF team about the behavior.  The response from them is actually quite informative.  Their response in in italics below.

I think you are probably seeing is several interactions that can be quite confusing.

  1. Creating a bitmap using the stream constructor will construct a DIB (Device Independent Bitmap).
  2. Creating a bitmap using the width/height constructor will construct a DDB (Device Dependent Bitmap).
  3. DIB's are allocated out of the virtual address space of the application.
  4. DDB's are allocated by the driver. This typically means that they are allocated in the virtual address space of gwes.exe. Alternatively, the driver could allocate these in dedicated video ram.
  5. Creating a bitmap with the stream constructor will generate a fair amount of garbage as it copies data from one buffer to the other.

When we perform a GC because of an OOM in the stream constructor case, we will almost certainly have some amount of garbage that we can free back to the OS immediately. This will also trigger the finalizer to run on another thread as soon as possible. That should help the next call to bitmap creation.

When we perform a GC because of an OOM in the width/height constructor case, it is fairly likely that the OOM is caused because of virtual memory exhaustion in gwes.exe. Thus freeing memory in our process will not help the memory condition in gwes.exe. We need the bitmap finalizer to run before this would actually free memory in a way that would help this scenario. While the finalizer thread would certainly have been triggered to start, it most likely will not get a chance to free bitmaps before we OOM while trying to allocate a bitmap immediately after triggering a GC on the initial thread.

In short, we have 2 different types of Bitmap in our runtime with varying performance and allocation characteristics. DDBs are generally faster to manipulate and draw to the screen than DIBs, but they are constructed in an external memory space that can cause allocation confusion and cause the performance of calls to LockBits or Save to be slow. If a DIB is desired and you wish to construct it based on width and height, we provide a function that constructs a Bitmap with a width, height, and pixelformat specified. This function will construct a DIB instead of a DDB.

I personally still consider this a bug in the implementation - the CF should catch these occasions and handle it for us rather than OOMing all the way back to the app to wait for the Finalizers and retry - that's an implementation that should be done below us. 

Still the answer sheds light on the fact that how we create a Bitmap should be highly dependent on how we intend to use that Bitmap.

Tuesday, August 22, 2006 1:24:50 PM (Central Daylight Time, UTC-05:00)  #     | 
# Friday, June 30, 2006

It occurred to me today that I never posted my presentation on Memory Management in the CF from MEDC 2006, so here it is.

Friday, June 30, 2006 10:42:23 AM (Central Daylight Time, UTC-05:00)  #     | 
# Thursday, June 29, 2006

Since most people like to try before they buy, we've released Evaluation Versions of our Calendar Controls.  Download the evaluation binaries and a sample project using them here.

Thursday, June 29, 2006 11:37:14 AM (Central Daylight Time, UTC-05:00)  #     | 
# Friday, June 23, 2006

I recently worked on a project that required me to connect to a printer from a Pocket PC with the Widcomm bluetooth stack on it.  Frustrating as it was, one positive thing came from it - I generated a nice start to a set of classes for using High-Point Software's BTConnect.

Of course it means that you'll need to buy BTConnect to use this library without the "evaluation mode" popup, but it abstracts the ugliness of sommand-line parameters away from the developer so you can focus on creating your app.

I've parked the library here:

www.opennetcf.org/shared

Friday, June 23, 2006 3:31:35 PM (Central Daylight Time, UTC-05:00)  #     | 
# Thursday, June 22, 2006

So I spent all of today fighting with a Dell Axim X30, and I've again come to the conclusion that companies who keep things proprietary really suck.

My task was to print over Bluetooth.  I decided that I'd use PrinterCE from Field Software for simplicity, and all worked well if the radio was already on when I tried to print.  If the radio was off, however, things went bad quickly and the device had to be soft reset to recover.  Not a good thing for usability.

Since they use the Widcomm/Broadcomm stack, I decided to look at the BTConnect stuff from High-Point software.  After four or five hours of creating a nice class wrapper for the app (which is going to become a shared-source library from OpenNETCF shortly) I found that the radio is on whenever I am actively searching or connecting, but as soon as it's done, the on-board BT manager shuts the radio off.  That means I can find the printer and connect with it, but as soon as I had off printing to PrinterCE, the radio shuts off and the device hangs.  Beautiful.  A plague upon the engineers at Dell.

Searching the web I find nothing hinting at how to programmatically do this.  Curses on the yet again.

I use Spy++ to see if I'm lucky and any messages might jump out when I use the BT Manager.  Nothing.  My hatred for Dell increases.

I search the registry for anything that might affect behavior and I find only one key that reports the current state.  Damn you Dell!

So I'm now a day into just turning on a Radio for a job I've quoted at 2 days (which I've already used doing the reporting code).  Now not only am I highly irritated, I'm highly irrated on my own dime.

So I make a last ditch effort to get something that at least functions.  I decided for a kludge, and a really nasty one at that.  As soon as I thought of it, I was disgusted by the idea, but I've really been left with no choice.  I'd simulate tapping the screen to turn the radio on.

And without further ado, I give unto you the following ugliness.  Hopefully no one elese ever has to use it, but since I've stooped this far, someone else probably will have to as well.  If you do, please curse Dell as well.

using System;
using Microsoft.Win32;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace OpenNETCF.Devices
{
  public static class AximX30
  {
    public static bool BluetoothRadioState
    {
      get
      {
        RegistryKey key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\WIDCOMM\BtConfig\General");
        bool currentState = (((int)key.GetValue("StackMode")) == 1);
        key.Close();
        return currentState;
      }
      set
      {
        // see if any action is needed
        if (BluetoothRadioState == value)
        {
          return;
        }

        // save where we were
        IntPtr topWindow = GetForegroundWindow();

        int bticonX = Screen.PrimaryScreen.Bounds.Width - 10;
        int bticonY = Screen.PrimaryScreen.Bounds.Height - 10;
        int btmenuX = Screen.PrimaryScreen.Bounds.Width - 20;
        int btmenuY = Screen.PrimaryScreen.Bounds.Height - 80;
        int starticonX = 10;
        int starticonY = 10;
        int todayX = 10;
        int todayY = 60;

        // start
         SendTap(starticonX, starticonY);

         // let the menu draw
         System.Threading.Thread.Sleep(300);

         // today screen
         SendTap(todayX, todayY);

         // let the screen draw
         System.Threading.Thread.Sleep(300);

         // Blutooth icon tap
         SendTap(bticonX, bticonY);

         // let the menu draw
         System.Threading.Thread.Sleep(300);

         // bt menu tap
         SendTap(btmenuX, btmenuY);

         System.Threading.Thread.Sleep(300);

         // restore where we were
         ShowWindow(topWindow, SW.NORMAL);
         UpdateWindow(topWindow);
       }
      }

    [DllImport("coredll")]
    private static extern void mouse_event(
            MOUSEEVENTF dwFlags, int dx, int dy, int dwData, int dwExtraInfo);

    [DllImport("coredll")]
    private static extern IntPtr GetForegroundWindow();


    [DllImport("coredll")]
    private static extern bool ShowWindow(IntPtr hWnd, SW nCmdShow);

    [DllImport("coredll")]
    private static extern bool UpdateWindow(IntPtr hWnd);


    [Flags]
    private enum HWND
    {
    TOP = 0,
    BOTTOM = 1,
    TOPMOST = -1,
    NOTOPMOST = -2
    }

    private enum SW
    {
    HIDE = 0,
    SHOWNORMAL = 1,
    NORMAL = 1,
    SHOWMINIMIZED = 2,
    SHOWMAXIMIZED = 3,
    MAXIMIZE = 3,
    SHOWNOACTIVATE = 4,
    SHOW = 5,
    MINIMIZE = 6,
    SHOWMINNOACTIVE = 7,
    SHOWNA = 8,
    RESTORE = 9,
    SHOWDEFAULT = 10,
    FORCEMINIMIZE = 11,
    MAX = 11
    }

    [Flags]
    private enum MOUSEEVENTF
    {
        MOVE = 0x1, /* mouse move */
        LEFTDOWN = 0x2, /* left button down */
        LEFTUP = 0x4, /*left button up */
        RIGHTDOWN = 0x8, /*right button down */
        RIGHTUP = 0x10, /*right button up */
        MIDDLEDOWN = 0x20, /*middle button down */
        MIDDLEUP = 0x40, /* middle button up */
        WHEEL = 0x800, /*wheel button rolled */
        VIRTUALDESK = 0x4000, /* map to entrire virtual desktop */
        ABSOLUTE = 0x8000, /* absolute move */
        TOUCH = 0x100000, /* absolute move */
    }

    private static void SendTap(int x, int y)
    {
      mouse_event(MOUSEEVENTF.LEFTDOWN | MOUSEEVENTF.ABSOLUTE, 
            (int)((65535 / Screen.PrimaryScreen.Bounds.Width) * x), 
            (int)((65535 / Screen.PrimaryScreen.Bounds.Height) * y), 0, 0);
      mouse_event(MOUSEEVENTF.LEFTUP, 0, 0, 0, 0);
    }
  }
}

Thursday, June 22, 2006 4:02:54 PM (Central Daylight Time, UTC-05:00)  #     | 
# Monday, June 05, 2006

I may be a little biased because they're ours, but our new Calendar Controls really are the best looking ones I've seen.  You can now add a calendar to your managed app that looks just like the Outlook calendar.

Monday, June 05, 2006 12:30:35 PM (Central Daylight Time, UTC-05:00)  #     | 

Seems like it took forever, but we've finally released the SDF 2.0 Extensions for Visual Studio 2005.

For more info on what exatly comes with the new SDF Extensions, click here.

Monday, June 05, 2006 12:26:58 PM (Central Daylight Time, UTC-05:00)  #     | 
# Friday, May 26, 2006

Ever have to do manual serialization of data in a DataSet?  The challenge is being able to call BitConverter.GetBytes on the data element when at compile time you have no idea what it's type is, and GetBytes doesn't accept an Object (though I'd argue maybe it should and attempt to convert the underlying type if it's resolvable).

Here's my solution:

byte[] elementBytes = (byte[])typeof(BitConverter).GetMethod(
   
"GetBytes", new Type[] { dataRow[columnNumber].GetType() }).Invoke(
    null, new object[] { dataRow[columnNumber] });

Friday, May 26, 2006 12:09:56 PM (Central Daylight Time, UTC-05:00)  #     | 
# Saturday, April 22, 2006

In case you hadn't heard, CF 2.0 SP1 Beta is now available.

Saturday, April 22, 2006 12:26:29 AM (Central Daylight Time, UTC-05:00)  #     | 
# Monday, February 27, 2006

So the OpenNETCF.Desktop.Communication library has been used in the "first malware to cross-infect a handheld phone or PDA from a desktop PC binary file...." (reported by the Mobile Antivirus Research Association).

The quote from the author is as follows:

The crossover virus was written in C# (C Sharp) using Visual Studio .NET 2003, the Communications Library of openNETCF.org was used and a great help

As part of such a "first" I'm not so sure I'm proud of, it does show how pervasive something as useful as our libraries can be.

 

Monday, February 27, 2006 6:41:29 PM (Central Standard Time, UTC-06:00)  #     | 
# Thursday, February 23, 2006

If you're building a CE image and want to include CF 1.0 or CF 2.0, then it's pretty simple - just drag the component from the catalog into your workspace.  But what if you don't want to include the CF at all, but still have the plumbing there so your end-user can install the CF themselves and have it work?  One could look through the cesysgen.bat files and decipher what needs to be set or, if you're lazy like me, you could ask the CF team.

Here's the answer, courtesy of Jim Suplizio:

The appropriate support sysgen will have to be added to the image depending on what version of CF is going to be installed.

  • SYSGEN_DOTNET_SUPPORT is the CF 1.0 support sysgen.
  • SYSGEN_DOTNETV2_SUPPORT is the CF 2.0 support sysgen. SYSGEN_DOTNETV2_SUPPORT is a super-set of the CF 1.0 support.

Effectively if SYSGEN_DOTNETV2_SUPPORT is added to the image then the end user can deploy either 1.0 or 2.0 and all of the required underlying CE OS pieces will be there.

Keep in mind that required means "base functionality". CF 2.0 has optional functionality that requires other SYSGENS and they are as follows:

  • Message Queuing - SYSGEN_MSMQ
  • Soap Reliable Messaging Protocol - SYSGEN_MSMQ_SRMP
  • SQLMobile (2005) requires CoCreateGuid functionality - SYSGEN_OLE_GUIDS
  • IPv6 - SYSGEN_TCPIP6
  • IE, PIE, HTMLView (htmlview.dll) or nothing - SYSGEN_IE, SYSGEN_PIE, SYSGEN_HELP or simply nothing.
  • The following only apply to the MainstoneII, other devices will only have null driver sets:
    • D3D Mobile - BSP_D3DM_XSCALE
    • D3D Mobile - SYSGEN_D3DMXSCALE

 

Thursday, February 23, 2006 3:47:21 PM (Central Standard Time, UTC-06:00)  #     | 
# Wednesday, February 22, 2006

It's been a while since we visited the OpenNETCF.Deskop.Communication library, but there have been lingering issues that ActiveSync 4.x have exposed (a stack imbalance exception).  So today I revisited the library and did some fixes to it as well as to the sample app (bad, bad me for not marshaling calls to the UI from a thread.  It's now a Studio 2005 project.

The latest code is in Vault and the downloads on the site are updated to this new version (2.9) as well.

Wednesday, February 22, 2006 1:09:33 PM (Central Standard Time, UTC-06:00)  #     | 
# Sunday, February 19, 2006

SDF 2.0 continues to have functionality for working with the time and time zones for devices.  The differences from SDF 1.x are a namespace change, a classname change (don't believe the beta docs or the beta, these have changed since that drop), and under the hood a lot more internal checks are going on to head off errors and to provide correct device support.  Basically this one has been pretty heavily tested across a broad range of devices.

Some quick highlights:

Getting and Displaying a List of Time Zones

using OpenNETCF.WindowsCE;
...
// get and display all available zones
TimeZoneCollection tzc = new TimeZoneCollection();
tzc.Initialize();

foreach (TimeZoneInformation tzi in tzc)
{
    lstZones.Items.Add(tzi);
}

Displaying the Currently Set Time Zone

using OpenNETCF.WindowsCE;
...
// get and display the currrent zone

TimeZoneInformation currentTz = new TimeZoneInformation();
DateTimeHelper.GetTimeZoneInformation(ref currentTz);
lblCurrentZone.Text = currentTz.StandardName;

Setting the Current Time Zone (from the Listing above)

using OpenNETCF.WindowsCE;
...
if
(lstZones.SelectedItem != null)
{
    TimeZoneInformation tzi = (TimeZoneInformation)lstZones.SelectedItem;
    DateTimeHelper.SetTimeZoneInformation(tzi);

    // this verifies that the time zone did indeed get changed
    TimeZoneInformation tz = new TimeZoneInformation(
        (byte[])Registry.LocalMachine.OpenSubKey("Time").GetValue(
            "TimeZoneInformation"));
    MessageBox.Show("Current Timezone in Registry is:\r\n" 
        + tz.StandardName, "Verified");
}

Displaying the Current Time (without DateTime.Now)

using OpenNETCF.WindowsCE;
...
DateTime dt = DateTimeHelper.SystemTime;

txtHour.Text = dt.Hour.ToString();
txtMinute.Text = string.Format("{0:00}", dt.Minute);
txtSecond.Text = string.Format("{0:00}", dt.Second);

Setting The Current System Time (Local Time is Analogous)

using OpenNETCF.WindowsCE;
...
// get the current time so we can copy the date part

DateTime dt = DateTimeHelper.SystemTime;

DateTimeHelper.SystemTime = new DateTime(dt.Year, dt.Month, dt.Day,
    int.Parse(txtHour.Text), int.Parse(txtMinute.Text), int.Parse(txtSecond.Text));

 

Sunday, February 19, 2006 5:17:03 PM (Central Standard Time, UTC-06:00)  #     | 
# Sunday, February 12, 2006

CE supports a Multimedia timer, though it's not in CF 1.0 or 2.0.  We've rectified that in SDF 2.0 (though your platform must support the Multimedia timer to use it - meaning WM and PPC are out).

So what do high-performance timers buy you?  Well if you look at how a regular timer works, they run at a really low priority and are horrible if you want anything that resembles deterministic behavior.  If you set the interval to say 1000ms, it's guaranteed to not fire in less than 1000ms, but there's actually no upper bound at all.  Jitter of 50ms (5%) would not be exceptional and in fact I've seen substantially worse on systems with a high load.

Our Timer2 class (name is still not finalized, so don't finalize on it) is based on the desktop's Timer class and provides things like a one-shot capability (timer fires once and never again without you having to disable it) and if you derive from it you can have it run a callback instead of raising an event.

Here's a quick example of usage:

void StartMyOneshotTimer
{
  // create a timer
  Timer2 oneShot = new Timer2();

  // make it a one-shot timer
  oneShot.AutoReset = false;

  // fire 3 seconds from enabling
  oneShot.Interval = 3000;

  // allow 10ms latitude for when it fires
  // so it will fire between now + 2995 and now + 3005
  oneShot.Resolution = 10;

  oneShot.Elapsed += new ElapsedEventHandler(oneShot_Elapsed);

  oneShot.Start();
}

void oneShot_Elapsed(object sender, ElapsedEventArgs e)
{
  // do something here
}

Sunday, February 12, 2006 1:39:02 PM (Central Standard Time, UTC-06:00)  #     | 
# Saturday, February 04, 2006

You might have noticed that SDF 2.0 no longer has serial or GPS classes.  This is intentional.  CF 2.0 now has serial classes and the Windows Mobile AKU includes GPS support.  We realize that there are some of you out there that are using GPSes on non-WM 5.0 devices and that you'd like to be able to use our stuff under Studio 2005.  For those people, we've spun the Serial and GPS classes into a stand-alone assembly: OpenNETCF.IO.Serial.  There are no plans for an installer for this assembly, and unless we get strong feedback it probably won't make it into the SDF either.

Saturday, February 04, 2006 1:41:38 PM (Central Standard Time, UTC-06:00)  #     | 
# Thursday, February 02, 2006

If you're a Windows CE device developer and you've got a device with a persistent registry, you're probably already aware that the CF Registry class doesn't help much for saving the registry, restoring branches from a file or creating volatile keys.  Once again the SDF is here to help with CreateVolatileSubkey, RestoreHiveBasedKey, RestoreRamBasedRegistry, SaveHiveBasedKey and SaveRamBasedRegistry.

One note - the doc says they're in the Registry2 class - that's already been changed to RegistryHelper.

 

Thursday, February 02, 2006 10:31:28 PM (Central Standard Time, UTC-06:00)  #     | 
# Sunday, January 29, 2006

Making apps power aware, especially through the device Power Manager works, but the code is kind of ugly and a pain to implement.  Once again SDF 2.0 simplifies things:

using System;
using System.Windows.Forms;
using OpenNETCF.WindowsCE;

namespace WindowsCETest
{
  public partial class MyPowerAwareClass
  {
    public MyPowerAwareClass()
    {
      DeviceManagement.DeviceWake +=
       new DeviceNotification(
DeviceManagement_DeviceWake);

      PowerManagement.PowerUp += new DeviceNotification(PowerManagement_PowerUp);
    }

    void PowerManagement_PowerUp()
    {
      MessageBox.Show("The Power Manager says I'm awake!");
    }

    void DeviceManagement_DeviceWake()
    {
      MessageBox.Show("Device notifications say I'm awake!");
    }
  }
}

Sunday, January 29, 2006 8:56:53 AM (Central Standard Time, UTC-06:00)  #     | 
# Saturday, January 28, 2006

The Windows CE operating system supports several notifications for common device events liek changes in AC power, network status changes and time changes.  They are exposed by using the CeRunAppAtEvent or CeSetUserNotification APIs.  While the Windows Mobile Notification Broker provides an interface for some of these, it doesn't provide access to all of them, nor is it available for general Windows CE developers.

SDF 1.4 provided a set of Notification classes that could be used to get these (and those classes are still there in SDF 2.0) but we felt that a simple object model around these would really be a nice thing to have.  So we created the OpenNETCF.WindowsCE.DeviceManagement class.  Now subscribing to the notifications is as simple as this example of detecting when the device time has been modified:

using System;
using System.Windows.Forms;
using OpenNETCF.WindowsCE;

namespace WindowsCETest
{

public class MyClass
{

public MyClass()
{

    DeviceManagement.TimeChanged +=
       
new DeviceNotification(DeviceManagement_TimeChanged);
}

void
DeviceManagement_TimeChanged()
{
    MessageBox.Show(
"The time was just changed.");
}

}

}

Saturday, January 28, 2006 5:26:51 PM (Central Standard Time, UTC-06:00)  #     | 
# Saturday, January 21, 2006

We're extremely close to a beta release of SDF 2.0.  To give you a taste of what's in it, we've posted the online documentation.  Any feedback is appreciated.  Enjoy.

Saturday, January 21, 2006 10:49:17 PM (Central Standard Time, UTC-06:00)  #     | 
# Tuesday, December 20, 2005

So if you're working with Microsoft's new Microsoft.WindowsMobile.PocketOutlook namespace and trying to use the OutlookCollection.Restrict method to filter by a Contact's (or any PimItem's) ItemId field, you will likely run into a problem.

First, let me say the ItemID Class sucks.  It exposes basically nothing useful but a ToString() method, even though it holds numeric data.  And the first item added to POOM gets the ItemId of 0x80000001, which you might note is an *unsigned* number (again, the CLS is a pain for not allowing unsigned numbers).  So if you have an ItemId class and you want to use it, you have to do something like this:

unchecked
{
    int myId = int
.Parse(m_outlookSession.Contacts.Items[0].ItemId.ToString())
}

Really, that's how you have to do it.

So, let's say we have a Contact's ItemId and we want to see if the current session has said Contact.  One might try this:

m_outlookSession.Contacts.Items.Restrict("[ItemId]=" + itemId.ToString());

A nice try, but that gives you the not-so-helpful exception:

The query string is incorrectly formatted.
Parameter name: [ItemId]=-2147483647

Alright, so ItemId only exposes itself as a string, and Restrict has little in the way of useful documentation or samples, so maybe we can try it as a string like so:

m_outlookSession.Contacts.Items.Restrict("[ItemId]='" + itemId.ToString() + “'“);

Well that gives a similar exception, just adding the single quotes:

The query string is incorrectly formatted.
Parameter name: [ItemId]=-'2147483647'

Because I've used POOM from C++, I know that ItemId seems new to me, so just as a lark I figure I'll try 'Oid' as a field name instead, and keep it as a numeric:

m_outlookSession.Contacts.Items.Restrict("[Oid]=" + itemId.ToString());

Lo and behold, success!

Now technically this might not be a true bug, but it's sure not documented anywhere, nor would it be at all intuitive to guess if you'd not used C++ to access an IContact (which exports an Oid field, *not* an ItemId) before.  Bad Windows Mobile Team.  BAD!

Things needed:

  • The ItemID needs to have an explicit operator for conversion to an int at the very least.  A ToInt32 or ToIntPtr or something of the sort would be useful.
  • Either name the field Oid like it's stored in the database, or provide some sort of substitution so when I filter by ItemId the underlying class converts that to Oid.
  • The least they could have done was give exception text like “The field cannot be found in the collection“
  • Documenting how to remove a Restrict once set would be useful (I used Restrict(“[Oid]<>0“) for lack of any better idea)
Tuesday, December 20, 2005 9:15:43 PM (Central Standard Time, UTC-06:00)  #     | 
# Tuesday, December 13, 2005

Steven Pratschner has posted a really good blog entry on how the CF 2.0 CLR works.  Take a look.

Tuesday, December 13, 2005 5:11:42 PM (Central Standard Time, UTC-06:00)  #     | 
# Friday, December 09, 2005

The final version of my article on deployment has been published on MSDN.

Friday, December 09, 2005 6:52:37 PM (Central Standard Time, UTC-06:00)  #     | 
# Tuesday, November 29, 2005

The Crime:  My CE device runs a CF app just fine when I deploy it manually, but once I connect to it from Studio for deployment and then cycle the power, not just my app, but all CF apps no longer work.  Reflashing the image on the device repairs the sitation.  What the hell is going on?

The Evidence:
Article #1: Studio saves connection security info in the device registry
Article #2: Studio saves connection security info in RAM of the device (in the root folder)
Article #3: When a CAB file is expanded, RegFlushKey is called
Article #4: When Studio deploys the CF, it does so as a CAB
Article #5: When a device is reset all RAM is lost
Article #6: The CF requires that the info in RAM and the registry match to launch

Verdict:
When I make a connection some info is stored on the device in RAM and the registry.  When I deploy, half that is persisted.  When I reset the device or pull power the other half is lost, causing all CF apps to no longer run on the device unless I erase the persistent registry (which depending on your device may mean reflashing the whole damned thing).
 
Sentence:
Run DelCryptoKeysDevice.exe (part of Windows CE Utilities for Visual Studio .NET 2003 Add-on Pack 1.1) to erase the info from the registry.  Doing it on startup by adding it to your image and this to platform.reg means you can forget about it:
[HKEY_LOCAL_MACHINE\init]
        "Launch55"="DelCryptoKeys.exe"
        "Depend55"=hex:14,00, 1e,00

Tuesday, November 29, 2005 3:50:47 PM (Central Standard Time, UTC-06:00)  #     | 
# Wednesday, November 23, 2005

I was playing around with PlaySound today (no pun intended) and was kind of surprised to find that I could play an embedded resource wave file with one line of code (well one line past the P/Invoke declaration):

using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;

....

// P/Invoke declarations....
[DllImport("CoreDll.DLL", EntryPoint="PlaySound", SetLastError=true)]
private extern static int PlaySound(byte
[] szSound, IntPtr hMod, SoundFlags flags);

[Flags]
enum SoundFlags
{
  Alias = 0x00010000,
  Filename = 0x00020000,
  Synchronous = 0x00000000,
  Asynchronous = 0x00000001,
  Memory = 0x00000004,
  Loop = 0x00000008,
  NoStop = 0x00000010
}

...

PlaySound(((MemoryStream)Assembly.GetExecutingAssembly(
  ).GetManifestResourceStream(
  "AudioTest.tada.wav")).GetBuffer(),
  IntPtr.Zero,
  SoundFlags.Synchronous | SoundFlags.Memory);

Wednesday, November 23, 2005 11:41:54 AM (Central Standard Time, UTC-06:00)  #     | 
# Tuesday, November 22, 2005

A couple new articles from OpenNETCF have finally gone public:

Device Debugging and Emulation in Visual Studio 2005 from Alex Feinman
Using Visual Studio 2005 to Design User Interfaces and Data for Device Applications from Maarten Struys

Tuesday, November 22, 2005 12:02:12 PM (Central Standard Time, UTC-06:00)  #     | 
# Monday, November 14, 2005

I just got word today from the Group Program Manager of the Compact Framework team that Compact Famework 2.0 Service Pack 1 (due out next year - dates not finalized, so I won't speculate) will have the following support for general Windows CE 4.2:

  • Runtime support only
  • Supported for all WinCE processor types (i.e., not just ARM family)

The picture for integration into Studio 2005, debugging, emulators and all of that is still fuzzy, so for now I'd plan pessimistically and assume that the two points above are all you'll get, but at least it lets you move forward.

 

Monday, November 14, 2005 4:08:19 PM (Central Standard Time, UTC-06:00)  #     | 
# Friday, November 04, 2005

Once again the OpenNETCF SDF has won Pocket PC Magazine's Best Software Award in the “.NET Developer Package” category for both the SmartPhone and Pocket PC categories in Pocket PC Magazine's Best Software.  Kudos to the team and thanks to the judges and community for making it a success.

SDF 2.0 will bring even more excitement - more on that later.... 

Friday, November 04, 2005 4:58:39 PM (Central Standard Time, UTC-06:00)  #     | 
# Saturday, October 29, 2005

If you've not heard here are a few bits:

  1. Studio 2005 RTM is available for download for those with MSDN subscriptions.  If you've got a subscription, don't wait until the 7th, go get it now!
  2. CF 2.0 distributables are available here
  3. A Platform Builder 5.0 QFE for CF 2.0 is available here.
Saturday, October 29, 2005 9:11:41 PM (Central Daylight Time, UTC-05:00)  #     | 
# Thursday, September 01, 2005

OpenNETCF.org has been around for a few years now, but it still surprises me how much we've accomplished with so little.  Our flagship product, the Smart Device Framework is extremely popular, and in total we've had over 200,000 combined downloads of binary and source packages of all of our offerings.  Imagine what we could accomplish if we actually did this full time.  Anyone know where we could get angel funding or someone with deep pockets and a love for Compact Framework R&D?

Thursday, September 01, 2005 10:33:45 AM (Central Daylight Time, UTC-05:00)  #     | 
# Sunday, August 21, 2005

Here's yet another article I'm working on for MSDN and again, comments are welcome.  A printer friendly version is here.  The source is available here.

Sunday, August 21, 2005 11:16:38 PM (Central Daylight Time, UTC-05:00)  #     | 
# Monday, August 15, 2005

Here's another article I'm working on for MSDN.  Again, comments are welcome.  A printer friendly version is here.  The source is available here.

Monday, August 15, 2005 8:25:29 PM (Central Daylight Time, UTC-05:00)  #     | 
# Sunday, August 14, 2005

Here's a draft of an article I'm putting together for MSDN.  Comments are welcome.  A printer friendly version is here.

Sunday, August 14, 2005 11:20:26 AM (Central Daylight Time, UTC-05:00)  #     | 
# Friday, August 05, 2005

Finally!  We've released SDF 1.4.  What's new you ask?  Well here's a quick list (which I extracted from check-in comments from the source Vault - you can do the same to see exactly what the changes are):

  • Bluetooth support added
  • Serial.GPS mods to standardize naming
  • Diagnostics.DebugMessage and RetailMessage
  • Bug fixes in TimeZoneCollection and DateTimeEx
  • Bug Fix in XmlSerializer for GUIDs
  • EventWaitHandle.WaitOne bug fix
  • Additions and fixes for AccessPoint and Adapter classes
  • Bug fix in StreamInterfaceDriver.DeviceIoControl
  • ThreadEx.RealTimePrioority added
  • Updated BatteryLife designer
  • Updated DateTimePicker Designer
  • Fixes and Updates to Win32Window
  • ApplicationEx fix for modal Forms and thread safety added
  • FTP bug fixes
  • ControlEx added support for WM_COMMAND
  • DeviceMonitor adds RequestDeviceNotifications
  • SelectedIndexChanged fix in ComboBoxEx
  • WS and WS_EX updated to remove unsupported styles
Friday, August 05, 2005 9:57:21 AM (Central Daylight Time, UTC-05:00)  #     | 
# Sunday, May 29, 2005

At MEDC I had a conversation with Seth Demsey about making a guitar tuner with a Pocket PC.  The challenge is getting analog data into the device and I proposed using an external ADC with an I2C interface using the serial port control lines for the I2C bus.  I'd spent a considerable amount of time over the last couple months working with the SPOT processor  and writing synchronous serial code and our conversation got me to thinking that the work was actually salvageable. 

I refactored a lot of the code so that now it can be used for the Compact Framework or even the Full Framework.  Couple this with the SDF's serial port classes and you could have an I2C bus running in under 5 minutes on a device or the desktop.  What fun!

Before you say “well the docs are nice, but where do I get the binaries?” let me preemptively answer.  I'll post binary downloads in the near future as soon as we get the licensing model ironed out.  These classes are not going to use the OpenNETCF Shared Source License.  Instead they will be commercial, for pay items.

Because I still think community access and learning is important for the community, I can tell you that the current plan is to offer the binaries as free for personal, non-commercial use.  Sorry, but only paying customers will get the source.

Sunday, May 29, 2005 9:35:34 PM (Central Daylight Time, UTC-05:00)  #     | 
# Wednesday, May 25, 2005

Are you working on a Compact Framework application and need some assistance?  Looking to start a new project and need an experienced resource?  Do you want the bragging rights of having industry leading experts to work on your application?  Well you've come to the right place!  The one and only Neil Cowburn is looking for contract work.  If you're in the UK great, so is he.  If not you're still in luck, we've got experience in remote development as well.

Click here for his resume and contact info.

Wednesday, May 25, 2005 6:01:03 PM (Central Daylight Time, UTC-05:00)  #     | 
# Thursday, April 07, 2005

As is typical, I started what I thought would be a simple project - providing managed access to a Sierra Wireless AirCard 555 CDMA modem from managed code. I did a basic eVB wrapper in COM for it about a year ago and figured it was going to be cake. Little did I know...

The first problem is that it needed a native callback.  Second, that callback takes a union parameter, which has some 50+ members.  Third, the few pieces of info retrieved without the callback would be used to update a UI and had long retrieval times, so retrieval must be on a Thread, yet most users won't want to deal with using Invoke for everything.

So the short story is that it took a lot longer to develop than originally planned.  I guess that's how development typically works.

It's only been through light testing, and I still need to add a lot of XML comments and run it through NDOC, but it's pretty much complete.  This is going to be a for-sale product through OpenNETCF Consulting, the “commercial arm“ of the OpenNETCF project (we've got to pay the bills somehow).  If you're interested, email us.

Here's a snapshot of my test harness, which gives you an idea of the properties exposed.

Thursday, April 07, 2005 7:58:01 PM (Central Daylight Time, UTC-05:00)  #     | 
# Sunday, April 03, 2005

Anyone taken the time to test out this sample code in the CF and see how it compares to the full framework?  I was surprised at the desktop results, and if it's true for the CF then I'd like to be aware of it.  Does the JITter use registers for locals when available?  If so, how many registers (the FFW has 64 of them)?  Is it processor dependent?

Sunday, April 03, 2005 9:47:03 PM (Central Daylight Time, UTC-05:00)  #     | 
# Tuesday, March 29, 2005

You're probably aware that using dumpbin.exe is very useful if you need to P/InvokedumpbinGUI.exe is a tool that I've been using for years that you might be interested in.  It adds a simply context menu to the Windows shell, so you can right click any DLL or LIB and view it's exports (among other things).

The install leaves a bit to be desired.  You have to have a dev tool installed, and you must modify your path to allow dumpbinGUI to find a couple files.  If you have Studio 2003 installed in the default location, add these to your system path:

C:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\bin
C:\Program Files\Microsoft Visual Studio .NET 2003\Common7\IDE

Tuesday, March 29, 2005 5:47:42 AM (Central Standard Time, UTC-06:00)  #     | 
# Wednesday, February 23, 2005
Wednesday, February 23, 2005 9:06:23 AM (Central Standard Time, UTC-06:00)  #     | 
# Sunday, February 13, 2005

I see requests for CF encryption examples on a regular basis in the newsgroups and the Forums.  Well, here's one I used in an app for storing a password in the registry using the OpenNETCF Encryption stuff :

internal static byte[] GetIV(string keyString)
{
 byte[] bytes = Encoding.Unicode.GetBytes(keyString);
 byte[] iv = new byte[24];
 if(bytes.Length < 24)
 {
  for(int i = 0 ; i < 24 ; i++)
  {
   iv[i] = bytes[i % bytes.Length];
  }
 }
 else
 {
  Array.Copy(bytes, 0, iv, 0, 24);
 }
 return iv;
}
internal static byte[] Encrypt(string toEncrypt, byte[] iv)
{
 TripleDESCryptoServiceProvider des = null;
 try
 {
  des = new TripleDESCryptoServiceProvider();
 }
 catch(Exception)
 {
  MessageBox.Show("The high encryption pack must be installed.  Please install and try again.",
   "Crypto Failure",
   MessageBoxButtons.OK,
   MessageBoxIcon.Exclamation,
   MessageBoxDefaultButton.Button1);
  return null;
 }
 des.Key = PRIVATE_KEY;
 des.IV = iv;
 return des.EncryptValue(Encoding.Unicode.GetBytes(toEncrypt));
}
internal static string Decrypt(byte[] toDecrypt, byte[] iv)
{
 TripleDESCryptoServiceProvider des = null;
 try
 {
  des = new TripleDESCryptoServiceProvider();
 }
 catch(Exception)
 {
  MessageBox.Show("The high encryption pack must be installed.  Please install and try again.",
   "Crypto Failure",
   MessageBoxButtons.OK,
   MessageBoxIcon.Exclamation,
   MessageBoxDefaultButton.Button1);
  return null;
 }
 des.Key = PRIVATE_KEY;
 des.IV = iv;
 byte[] decBytes = des.DecryptValue(toDecrypt);
 return Encoding.Unicode.GetString(decBytes, 0, decBytes.Length);
}
Usage is simple:
byte[] encryptedPwd = AppGlobal.Encrypt(password, GetIV(PUBLIC_KEY));
string  previousPwd = Decrypt(encryptedPwd , GetIV(PUBLIC_KEY));
Sunday, February 13, 2005 6:13:36 AM (Central Standard Time, UTC-06:00)  #     | 
# Wednesday, February 02, 2005

A fairly common complaint from the CF community is the time gap between SDF source releases.  Well with the generous donation from SourceGear of Vault, we're now able to give the community direct read-only access to the source in real time.  Of course this added access means that what you get out of source control may not work properly, but there's no more complaining about not being able to get the latest code.

To get the source, navigate over to http://vault.netcf.tv/VaultService/VaultWeb/login.aspx and login with a username and password of 'guest'

Oh, and keep in mind that we now accept donations, so if you'd like to support us it's quick and simple.

Wednesday, February 02, 2005 8:07:40 AM (Central Standard Time, UTC-06:00)  #     | 
# Tuesday, August 24, 2004

So today I gave Scott Holden of Compact Framework Dev Team fame to discuss my obsession with the GC.  These were my take-aways from the call:

  1. The GC actually runs *on* the executing thread, so there's no specific GC thread to get ahold of.  So much for that idea (at least I got a real Process Viewer out of it, not that piece of crap Running Programs thing).
  2. Since GC won't happen during a P/Invoke call I was considering calling an unmanaged DLL that would host the EE, pass it a function pointer (evidently available in CF 2.0 - been too busy to actually investigate), and have the DLL run the ISR.  Evidently when you step across the managed/unmanaged boundary in either direction, the GC is given a chance to run.  There goes that idea.
  3. The GC has no mechanisms for telling it not to run.  The only way to prevent it's running in any piece of code is to not make an allocation (simple for the ISR itself).  You must be guaranteed that no allocation happens in the process.  Since the ISR really must run in its own thread, you'd have to have some sort of locking mechanism against *all* memory allocations during the ISR execution.  Ugly if possible at all.  There goes that idea.
  4. Scott believes that “impossible“ is rare in software, though this one borders on very, very difficult.  He said getting a driver hosted in device.exe might be more difficult, but the two are probably the closest to the impossible line he can think of.

So the call left me a bit disappointed.

Since nothing fun ever comes easy, I back-burnered the idea for a while - until I was washing dishes tonight to be precise - and then I had an epiphany - “what alcoholics refer to as a moment of clarity” if you will.

Given that the GC runs on the current thread and given that if you make no allocations during the execution of a section of code (your ISR) then the GC won't run, then all we have to do is to make sure that our ISR makes no allocations and is guaranteed to be the running thread, right?  Well why not use CeSetThreadPriority to set our ISR/IST thread priority higher (well lower numerically) than any other managed thread can go? 

Think of it this way: you have your main thread at priority 251, it creates an IST and sets it's priority to 245 or so.  The IST allocates whatever it needs for the ISR execution and then blocks on a WaitForSingleObject waiting for the interrupt event.  The main thread then runs along merrily until blam!  We get an interrupt.  The IST unblocks and interrupts the primary thread because of its priority (setting a high quantum may be a good idea too?).  The ISR runs, does its thing, then blocks again at WaitForSingleObject.

In theory it sounds good, but right now it's just an idea.  I'll attempt to find time to give it a try in the next few days.  The only problem I see right now is what if the GC is already collecting at the time of the interrupt?  Will the IST be blocked until the GC is done, or will the GC be running on the primary thread, so it in turn will be interrupted by the higher priority IST?  Only experimentation will tell.

Tuesday, August 24, 2004 8:58:38 PM (Central Daylight Time, UTC-05:00)  #     | 
# Saturday, August 21, 2004

Here's a great look from Steven at how POOM will be accessed (most likely anyway) through version 2.0 of the CF.

Saturday, August 21, 2004 8:41:05 PM (Central Daylight Time, UTC-05:00)  #     | 
# Friday, August 20, 2004

So I've been playing with the idea of how you could get deterministic behaviour out of managed code.  Of course the party line is that it's not possible, but to me that's just throwing down the gauntlet.  They also say you can't suspend and resume a managed thread, to which I replied “Yeah? Watch me!”

This was fueled when I was at DevCon and sat in on a lab of Nat Frampton's where you simply create an ISR that responds to a contact on a serial port pin.  I wondered “well why the hell can't I do that in managed code?”  No reason really - I mean it's simply setting up an event to fire when an interrupt is received, and all of that can be P/Invoked (I haven't actually written the code yet, but there's nothing magic about it).  The problem comes with determinism.

Conventional wisdom says that the GC suspends all managed threads when it's doing its work (which can be annoying in most cases).  If this were to happen while you were doing your ISR work, you could get a really bad outlier.  So your system responds in milliseconds most of the time, but occasionally it takes a few seconds.  That's unacceptable - a box flies of the conveyor and hits the floor, making a mess.

So how do we handle this?  How about setting the GC thread priority to 255 at the start of your ISR, then back to it's original priority afterward?  To even better handle it, your ISR priority needs to be set high.

How do we proceed then?  We need the GC thread handle to manipulate its priority.  In comes the toolhelp libraries.  I wrote a ToolHelp wrapper for the SDF earlier in this quest, but I just realized yesterday I only had the PROCESSENTRY32 stuff done, not the THREADENTRY32, so I spent last night doing that piece.

Still, the THREADENTRY32 only has a ThreadID - how are we to determine which thread is the GC as opposed to all the other threads we may have in our process?  Well, I ran a quick check with a slap-together process viewer app based on my new toolhelp wrappers and I see this:

Notice the Priority 248 thread then think about managed priorities - there are five ranging from Lowest (0) to Highest(4), which presumably equate to 255 to 251, though I've yet to check.

My initial thought was that the 248 priority might be the GC, after all it would make sense that it's higher priority, though I also recall that all manged apps need 3 threads.  So why do I have 5?  Well the answer is that this was running through the debugger.  When run stand-alone, I get 3 threads, all with a priority of 251.

So where does that leave us?  Well first, it's evident that 251 is “Normal” priority, so I'll need to map out the actual values for the managed equivalents, but more importantly we still have no way to uniquely identify the GC thread (that I've found anyway).

Right now that's the show stopper.  If you've got any ideas on how to reliably identify the GC thread handle, I'd love to hear them. 

I guess on the plus side, outside of the designer work, it's only about 10 lines of code to create the process viewer you see here.  I'll probably add a few features and turn it into a sample for using the SDF.

Friday, August 20, 2004 12:49:41 PM (Central Daylight Time, UTC-05:00)  #     | 
# Tuesday, August 10, 2004

Man, I've been asking for it for sometime and finally here it is, a full explanation of how the GC works in the CF!  Yet more required reading for the CF developer.

Tuesday, August 10, 2004 9:18:58 PM (Central Daylight Time, UTC-05:00)  #     | 
# Monday, August 02, 2004

Wondering what the CF 2.0 brings to the table?  Wondering how the SDF enhances it and bridges the gap from 1.0?  Take a look at Peter's new poster.  A picture is worth a thousand words, but a diagram tells an entire story.

Monday, August 02, 2004 10:09:41 PM (Central Daylight Time, UTC-05:00)  #     | 
# Sunday, July 25, 2004

Well we started the coding competition a few weeks ago now, and the suspense is killing me.  We seem to have a lot of interest (over 4000 hits on the main Wiki page for the competition) but since it'll be a while until submissions start coming in I've got no idea how successful this is going to be.  Is anyone actually writing something for it?

Sunday, July 25, 2004 12:11:17 PM (Central Daylight Time, UTC-05:00)  #     | 
# Thursday, July 15, 2004
Just in time for the OpenNETCF Coding Competition, OpenNETCF.org's SDF 1.2 has been released!

Among the new features in version 1.2 are:
 
OpenNETCF
 - EnumEx replaces OpenNETCF.Enum
 - EnvironmentEx replaces OpenNETCF.Environment, adds MachineName,
   UserName, and GetLogicalDrives
 - MathEx - New Constants and FFT implementations
 
OpenNETCF.Diagnostics
 - EventLog - Logging component for help with debugging and
   development OpenNETCF.Drawing
 - Added GraphicsEx, PenEx, and BitmapEx classes that wrap native
   drawing API's.
 
OpenNETCF.Net
 - Bug fixes and feature enhancements
 
OpenNETCF.Phone
 - Sms class
 
OpenNETCF.Runtime.InteropServices
 - AdvancedMarshaler - Better support for complex data marshaling
 - MarshalEx - Added ReAllocHGlobal method
 
OpenNETCF.ToolHelp
 - Get real-time information about running processes and threads
 
OpenNETCF.Tracing
 - Added a much more robust set of tracing classes akin to the full
   framework
 
OpenNETCF.Web.Services2 (yes WSE 2.0!):
 - Provides implementations for:
   * WS-Addressing
   * WS-Attachments
   * WS-Security
   * WS-SecureConversation
 
OpenNETCF.Windows.Forms
 - Help class now modelled more closely on FFW
 - New ProgressBarEx control
 - Added SoundPlayer and SystemSounds classes modeled after the FFW
 - Enhanced SmartList features
Thursday, July 15, 2004 4:18:55 PM (Central Daylight Time, UTC-05:00)  #     | 
# Friday, July 09, 2004

Jono's blog entry has the list of fixes and the download details.

Friday, July 09, 2004 10:45:16 AM (Central Daylight Time, UTC-05:00)  #     | 
# Thursday, July 08, 2004

A new interest group called WE-DIG was launched at DevCon last week.  They've put quite a bit of work into it and it looks promising.  They're starting in the Seattle area, but there's already been a lot of interest for other regions, so it may grow quickly.  Check them out.

Thursday, July 08, 2004 12:03:38 PM (Central Daylight Time, UTC-05:00)  #     | 

If you're a fan of data mining, then you'll love this cool newsgroup mining tool done by Microsoft Research (pointed out by Jono).  You'll see the compactframework newsgroup is #6 for activity out of all of them - pretty impressive!

Thursday, July 08, 2004 11:48:04 AM (Central Daylight Time, UTC-05:00)  #     | 
# Wednesday, June 09, 2004

For those curious about what I've been up to, I'm working on a thing - can't really name it, but it's longer than an article, but not quite a book.  Think of a 3-chapter technical book.

The working title is “Communication with VB.NET” but it's somewhat misleading.  When I say “communication” I mean data marshalling.  It's going to cover intra-thread, inter-thread and inter-process data marshalling.  Basically if you have one Form and you need to get data to another, how do you do it?  How about from one thread to another?  Or one process to another?  These kind of questions come up frequently in the newsgroups, so I'm putting together a definitive guide. 

As you may know, I think print media is dead for technical subjects, so I'll be PDFing it and selling it through OpenNETCF Consulting.  It'll probably be in the $5-$10 range.  If it's successful, we'll try to do a series of them on several topics and in several languages (think C# or VB, not English v. French).  Any topics you think need to be covered?

This one will be unusual in that it will apply to desktop developers in many instances, but all code will be targeted to run under the CF.  It's a real pain when CF developers have to “down-port” desktop code in a book to use it.

Wednesday, June 09, 2004 11:12:03 AM (Central Daylight Time, UTC-05:00)  #     | 
# Wednesday, May 19, 2004

Ever want to have an application idle timer for you CF app?  Maybe to pop up a login screen, power down a peripheral or log off a network?  A recent newsgroup post prompted me to write one, and in VB of all things.  The thing that took longest was figuring out that VB.NET doesn't actually have an application entry point and call to Application.Run unless you modify the project settings.  And this is supposed to be easier than C#?

At any rate, the code is in the OpenNETCF Wiki.  Expect a C# version in the near term.  In fact I'm going to work on a set of IMessageFilter implementation examples, so if you have any ideas or things you'd like to see, let me know.

Wednesday, May 19, 2004 1:03:03 PM (Central Daylight Time, UTC-05:00)  #     | 
# Monday, May 10, 2004
This article predates my days as a developer (I was a seismic geologist in those days) but it's unbelievably valuable.  Sure, it talks about Windows 3.1 and DOS and 95 was the latest, but if you develop on any windows platform you should read an understand it.
Monday, May 10, 2004 4:23:46 PM (Central Daylight Time, UTC-05:00)  #     | 
# Monday, April 26, 2004

I finally broke down and did it. I've implemented real threading for the CF.

The ThreadEx class, which will be released with version 2.0 of the SDF, has the following (plus what was already in Thread):

  • Name
  • Abort
  • Join
  • Suspend
  • Resume
  • IsAlive
  • ThreadState

Depending on the release schedule for the SDF, I may release the source file for it sooner.

Of course like always it needs thorough testing, but peliminary work looks good.  If you're interested in testing it, let us know.

Monday, April 26, 2004 4:22:36 PM (Central Daylight Time, UTC-05:00)  #     | 
# Friday, April 23, 2004

At 1:00 am, it's never a positive thing to see a message like this.  Sure, it's not a BSOD, but it sounds rather ominous.

<--- Click me

On the positive side, you can see I'm working again on the Pocket Excel project.

Friday, April 23, 2004 8:14:14 AM (Central Daylight Time, UTC-05:00)  #     | 
# Wednesday, April 21, 2004

With the globe getting smaller, it makes good sense to test your products against multiple language versions of a target platform.  Fortunately Microsoft is finally providing Pocket PC developers an easier way to do so with several available PPC 2003 SE Emulator images:

Wednesday, April 21, 2004 8:52:28 AM (Central Daylight Time, UTC-05:00)  #     | 
# Saturday, March 27, 2004

If you do *any* CF coding, this new article by Dan Fox and Jon Box is required reading.

Keep in mind that the QueryPerfCounter/Frequency functions are not just processor but OEM dependent.  It may not be implemented at all on your platform, though if it's not the calls will return the GetTickCount value.

I'm thinking the perf statistics features are important enough that I'll add methods to the OpenNETCF.Desktop.Communication library to turn on/off collection and to pull back a Statistics collection.

Saturday, March 27, 2004 12:59:30 PM (Central Standard Time, UTC-06:00)  #     | 
# Saturday, February 28, 2004

You may recall that recently I said I was going to write a wrapper around the ADS CAN bus driver.  Well I got it done and like most work I do, when it was finished I realized there was a better way to do it.  This donned on me when I started writing a wrapper for the I2C bus and I started typing nearly identical code.

So instead of moving forward with the I2C wrapper, I've gone back and written a generic stream interface driver wrapper in an abstract (MustInherit for you VBers) class.  I will then derive both the CAN and I2C drivers from it.  It will become part of the OpenNETCF.IO namespace when complete.

Look for an MSDN article covering it in the future.  I may even get crazy and do both a C# and VB version....

Saturday, February 28, 2004 10:49:33 AM (Central Standard Time, UTC-06:00)  #     | 
# Friday, February 27, 2004

As promised, I've released the source for the RoundGauge control, along with some bug fixes and other new stuff, with a full drop of the Smart Device Framework source.

Keep in mind that this is an inter-release drop, so the Smart Device Framework Binary Download,  the Full SDF Release Source Download and the online help remain unchanged.

Friday, February 27, 2004 1:06:17 PM (Central Standard Time, UTC-06:00)  #     | 
# Wednesday, February 25, 2004

Here's a project I started ages ago - a dial gauge.  It's going to be the basis of my digital, CF-based dashboard in my 1967 Dodge Dart if and when I ever get around to writing the software and installing the sensors.

I should be publishing the source with the Smart Device Framework source soon, but this pic is a preview.

 

 

 

It's got a *lot* of features, so I won't list them all, but some important items are:

  • A composite control (the pic doesn't show available indicator lamps)
  • Full Designer support with BeginUpdate and EndUpdate
  • Component serialization (implemented CodeDomSerializer)
  • Lots of drawing examples, including arcs
  • Fun with trigonometry
  • Double (well actually triple) buffering of graphics
  • Low and high warning areas with separate colors and events

 

Wednesday, February 25, 2004 5:28:37 PM (Central Standard Time, UTC-06:00)  #     | 
# Thursday, February 12, 2004

Why is it that the CF doesn't expose the Set/GetAttributes methods on the File class?  Who knows - but here's a (completely untested) implementation that will someday work its way into the SDF....

Thursday, February 12, 2004 12:17:41 PM (Central Standard Time, UTC-06:00)  #     | 
# Monday, February 02, 2004

If you've done any PPC UI development you're probably aware that you cannot make a Form appear less than full screen, unless you make it borderless, which isn't terribly useful.  The ability to do a popup like the MessageBox would be really useful, but actually implementing it is a bitch.

The full-screen behavior is not something enforced by the CF, but is actually enforced by the Pockety PC itself.  To confirm this for yourself, simply make an app with a less-than-fullscreen Form and run it on the vanilla CE emulator.  On another of my infamous commuter thinking sessions, I starting thinking about how the PPC enforces this and how to circumvent it.

My initial thought was that the PPC simply modified the window position and style to enforce the behavior and that by modifying them after the fact would be a plausible workaround.

I decided to run some quick tests this morning to confirm my suspicions.  I created an app that created two Forms - one with the standard border, title bar, etc. and one that was borderless.  I resized both in the designer to be less than full screen, and then I used the OpenNETCF.Win32Window class to look at the Forms' properties with GetWindowLong and move them around with SetWindowPos.

To my surprise, the style of the normal window was 0x1600 0000 - meaning it had no title bar! As confirmation, if I moved the Form with SetWindowPos , the Form's  contents moved, but the title bar stayed at the top of the screen.  Interesting.  It seems that the PPC itself must be stripping the title bar from the Form and creating its own system-wide title bar.  This actually explains some weird behavior seen in the past, such as when you close a Form and the title bar remains.

I modified the style bits and position of the "normal" form and was able to get the Form's actual title bar to appear, allowing me to drag and resize the Form, but the PPC still enforced the size of the Form on initialization.  No matter what I set in the Form's ctor, it always ended up as 240x294.  I could resize it manually afterward, but that wasn't desireable.

I then tried starting with a borderlesss Form and manipulating it's bits and I found that I could get it to start up in the right place with the right size.  The only problem is that there seems to be a "hidden" minimize box (even though I explicitly remove it) that allow the form to be minimized by clicking in the upper left of the title bar.

By moving the manipulation code in the normal Form to an Activated event handler I got the same behavior.

So in short, I've come close - I've got a non-fullscreen form that you can move and size.  The only remaining issue is to get rid of the pesky minimize button.  If you want to play with it, here's the code to get you started.  Start wiht a Form with no ControlBox and the BorderStyle set to none.

public FormB()
{
    InitializeComponent();

    IntPtr hWnd = Win32Window.FindWindow(null, this.Text);

    int style = Win32Window.GetWindowLong(hWnd, Win32Window.GetWindowLongParam.GWL_STYLE);

    style |= (int)(
            Win32Window.WindowStyle.WS_BORDER
            | Win32Window.WindowStyle.WS_CAPTION
            & ~Win32Window.WindowStyle.WS_MINIMIZEBOX
            );

    Win32Window.SetWindowLong(hWnd, (int)Win32Window.GetWindowLongParam.GWL_STYLE, style);

    Win32Window.SetWindowPos(hWnd, Win32Window.SetWindowPosZOrder.HWND_TOP, 25, 25, this.Width, this.Height, Win32Window.SetWindowPosFlags.SWP_SHOWWINDOW);
}

Monday, February 02, 2004 12:01:29 PM (Central Standard Time, UTC-06:00)  #     | 
# Monday, January 12, 2004

I've seen a few threads where developers wish to use purely .NET classes for their entire application.  While this is a laudible goal (I just wrote an entire FTP client that uses absolutely no P/Invokes - it will be released as part of  IDSSAPI .NET hopefully by the week's end), it's unrealistic in most situations and avoiding P/Invokes simply because one doesn't like or understand them is a shortcut to disaster.

Scott Hanselman has a great article in his blog that talks about ".NET purity" and is well worth the read.

Monday, January 12, 2004 3:14:26 PM (Central Standard Time, UTC-06:00)  #     | 
# Thursday, December 04, 2003

So yesterday on my commute home I was thinking (it's an hour each way, so there's lots of time for it). 

Many developers have asked about getting the name of a control in the CF, and it's just not nicely exposed.  I thought, wouldn't it be nice to be able to get the name of a control if I have an instance of it (like in an event handler) or to be able to get an instance of a control if I know the string name? 

With a little work using reflection I came up with this (and for the record, reflection f'ing rocks!), which will be in the SDF release:

public class ControlEx
{
	public static Control GetControlByName(Control Parent, string Name)
	{
		FieldInfo info = Parent.GetType().GetField(Name,
			BindingFlags.NonPublic | BindingFlags.Instance |
			BindingFlags.Public | BindingFlags.IgnoreCase);

		if(info == null) return null;

		object o = info.GetValue(Parent);

		if(o == null) return null;

		return (Control)o;
	}

	public static string GetControlName(object SourceControl)
	{
		FieldInfo[] fi = ((Control)SourceControl).Parent.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance |
			BindingFlags.Public | BindingFlags.IgnoreCase);

		foreach(FieldInfo f in fi)
		{
			if(f.GetValue(((Control)SourceControl).Parent).Equals(SourceControl))
				return f.Name;
		}

		return null;
	}
}
Thursday, December 04, 2003 11:16:34 AM (Central Standard Time, UTC-06:00)  #     |