Friday, September 26, 2008
So yesterday I decided that I'd start a longer-term project of creating a decent WiFi viewer application for PPC/WinMo.  Basically something to use as a good sample for the Smart Device Framework, as well as to help test out several of the features (those classes are nearly impossible to automate testing for).  I've decided to use it as a showcase for everything I can think of as long as it isn't superfluous - so using OpenNETCF UI controls, etc.

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

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

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

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

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

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


Smart Device Framework Suggestions

Padarn Suggestions

9/19/2008 12:54:42 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [2]  | 
 Thursday, September 18, 2008
As part of our push to implement continuous integration, we need to have TFS generate CAB files for us.  Unfortunately the only tool we have for this is the archaic piece of crap CABWIZ and it's not a very friendly tool for automating.  What we did was create a custom build task that calls cabwiz for you and will output the error file back to the build log on failure. Now all we have to do is add this to our build script:

    <CabWiz
      InfPath="$(CABFolder)\SDF.INF"
        ErrorLog="Error.log"
        OutputPath="$(CABFolder)" />


The custom build task looks like this:

namespace OpenNETCF.Build.Utilities
{
  using System;
  using System.IO;
  using System.Text;
  using Microsoft.Build.Framework;
  using Microsoft.Build.Utilities;
  using Microsoft.Win32;
  using System.Diagnostics;

  /// <summary>
  /// Uses CabWiz to create a Smart Device CAB installer.
  /// </summary>
  /// <example>
  /// <code><![CDATA[
  /// <CabWiz
  ///   InfPath="MyApp.inf"
  ///   ErrorLog="Error.log"
  ///   WorkingFolder="..\Build"
  ///   Cpu="x86"
  ///   Compress="true"
  ///   NoUninstall="false"
  ///   OutputPath="..\Release"
  ///   Platform="wm"
  ///   PreXml="Pre.xml"
  ///   PostXml="Post.xml"
  /// />
  /// ]]></code>
  /// </example>
  public sealed class CabWiz : ToolTask
  {
    [Required]
    public string InfPath { get; set; }

    public bool Compress { get; set; }
    public string Cpu { get; set; }
    public string ErrorLog { get; set; }
    public bool NoUninstall { get; set; }
    public string OutputPath { get; set; }
    public string Platform { get; set; }
    public string PostXml { get; set; }
    public string PreXml { get; set; }

    private static string CabWizSubPath
    {
      get { return @"SmartDevices\SDK\SDKTools"; }
    }

    protected override string ToolName
    {
      get { return "cabwiz.exe"; }
    }

    public override bool Execute()
    {
      bool ret = base.Execute();

      if (!ret)
      {
        string path = Path.GetDirectoryName(InfPath);
        path = Path.Combine(path, ErrorLog);

        if (File.Exists(path))
        {
          Log.LogMessageFromText(string.Format("!!! BEGIN CABWIZ ERROR FILE DUMP !!!", path), MessageImportance.High);
          Log.LogMessagesFromFile(path, MessageImportance.High);
          Log.LogMessageFromText(string.Format("!!! END CABWIZ ERROR FILE DUMP !!!", path), MessageImportance.High);
        }
        else
        {
          Log.LogMessageFromText(string.Format("CABWIZ: Couldn't find error log at '{0}'", path), MessageImportance.High);
        }
      }
      return ret;
    }

    protected override string GenerateFullPathToTool()
    {
      if (String.IsNullOrEmpty(ToolPath))
      {
        string path = string.Empty;
        using (RegistryKey regKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\VisualStudio\9.0\Setup\VS", false))
        {
          if (regKey != null)
          {
            path = (string)regKey.GetValue("ProductDir");
          }
        }
        ToolPath = Path.Combine(path, CabWizSubPath);
      }

      return Path.Combine(ToolPath, ToolName);
    }

    protected override string GenerateCommandLineCommands()
    {
      GenerateFullPathToTool();
      StringBuilder cabWizArgs = new StringBuilder();
      cabWizArgs.AppendFormat("\"{0}\" ", InfPath);

      if (!string.IsNullOrEmpty(OutputPath))
      {
        cabWizArgs.AppendFormat("/dest \"{0}\" ", OutputPath);
      }

      if (!string.IsNullOrEmpty(ErrorLog))
      {
        cabWizArgs.AppendFormat("/err \"{0}\" ", ErrorLog);
      }

      if (!string.IsNullOrEmpty(PreXml))
      {
        cabWizArgs.AppendFormat("/prexml \"{0}\" ", PreXml);
      }

      if (!string.IsNullOrEmpty(PostXml))
      {
        cabWizArgs.AppendFormat("/postxml \"{0}\" ", PostXml);
      }

      if (!string.IsNullOrEmpty(Cpu))
      {
        cabWizArgs.AppendFormat("/cpu {0} ", Cpu);
      }

      if (!string.IsNullOrEmpty(Platform))
      {
        cabWizArgs.AppendFormat("/platform {0} ", Platform);
      }

      if (Compress)
      {
        cabWizArgs.Append("/compress ");
      }

      if (NoUninstall)
      {
        cabWizArgs.Append("/nouninstall");
      }

      Log.LogMessage(MessageImportance.High, cabWizArgs.ToString());
      return cabWizArgs.ToString();
    }
  }
}


9/18/2008 10:22:34 AM (Eastern Daylight Time, UTC-04:00)  #    Comments [1]  | 
 Wednesday, September 10, 2008
Every now and then (much less now that .NET languages have been around are are pretty mature) I see people who are moving into .NET programming and they ask "which is better, C# or VB.NET."  Generally speaking there is no "better" but there are some things available in one language but not the other.  Typically I've always thought that C# had just a little more - it has the ability to support unsafe code, which I like and use occasionally.  I could never come up with something VB had that C# didn't.  Until today.

A friend asked me how he could use the Contains() method of a string inside a case statement, and it reminded me of an old VB 6 construct that I'd used, so I tried it to be sure VB.NET still supported it, and sure enough, it works fine:

        Dim myvar As String = "My Test String"

        Select Case True
            Case myvar.Contains("not there")
                Debug.WriteLine("Contains 'not there'")
            Case myvar.Contains("Test")
                Debug.WriteLine("Contains 'Test'")
            Case myvar.Contains("Other")
                Debug.WriteLine("Contains 'Other'")
        End Select

However the construct won't work in C#.  It won't even compile because C# expects case labels to be constants.

        string myvar = "My Test String";

        switch (true)
        {
          case myvar.Contains("not there"):
            Debug.WriteLine("Contains 'not there'");
            break;
          case myvar.Contains("Test"):
            Debug.WriteLine("Contains 'Test'");
            break;
          case myvar.Contains("Other"):
            Debug.WriteLine("Contains 'Other'");
            break;
        }

So there you go VB lovers - score on point for your side.  I'm not saying that I'm going to start writing all my code in VB now (not that I have anything against VB, I mean I did co-author a book on it, I'm just really rusty) but here's some fodder for what some consider a religious debate.

9/10/2008 11:55:08 AM (Eastern Daylight Time, UTC-04:00)  #    Comments [0]  | 
 Wednesday, September 03, 2008
Here's another quick peek at an upcoming class in the next release of the SDF:

Object Model

keyhook2.PNG

Usage

    private KeyboardHook m_keyHook;
    
    public Form1()
    {
      m_keyHook = new KeyboardHook();
      m_keyHook.KeyDetected += OnKeyDetected;
      m_keyHook.Enabled = true;
    }

    void OnKeyDetected(OpenNETCF.Win32.WM keyMessage, KeyData keyData)
    {
      // Do Stuff
    } 


Sample App in the SDF

keyhook1.PNG

9/3/2008 1:40:08 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [4]  |