Thursday, December 07, 2006

I was doing a search for some info just now and came across a really well done article describing drivers in Windows CE.  Check it out.

12/7/2006 6:17:45 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0]  | 
 Wednesday, December 06, 2006

We got a request today from someone wondering if the SDF would help send a Wake-on-LAN or Magic Packet.  Well I've never had to do it before, so I looked it up on Wikipedia. The short answer is no, but that's because all of the required pieces are already there in the CF.

Here's my guess on it - keep in mind that I don't have a WOL-capable PC lying around to test this with (if you test it and can confirm if it does or does not work, by all means let me know).

UPDATE (Dec 7, 06): Alex Feinman took the time to test the original code and the broadcast didn't work.  The code has been updated with working, tested code.

/// <summary>
/// Wakes a remote PC
/// </summary>
/// <param name="targetMAC">MAC address of target. Must be 6 bytes and MUST be in network order (reversed)</param>
/// <param name="password">Optional password. Must be null or 4 or 6 bytes.</param>
public static void WOL(byte[] targetMAC, byte[] password)
{
  // target mac must be 6-bytes!
  if (targetMAC.Length != 6)
  {
    throw new ArgumentException();
  }

  // check password
  if((password != null) && 
      (password.Length != 4) && 
      (password.Length != 6))
  {
    throw new ArgumentException();
  }

  int packetLength = 6 + (20 * 6);
  if (password != null)
  {
    packetLength += password.Length;
  }

 
byte
[] magicPacket = new byte[packetLength];

  // has a 6-byte header of 0xFF
  byte[] header = new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
  Buffer.BlockCopy(header, 0, magicPacket, 0, header.Length);

  // repeat the destination MAC 16 times

  // your MAC *is* in network (reverse) order, right??
 
int
offset = 6;
  for(int i = 0 ; i < 16 ; i++)
  {
    Buffer.BlockCopy(targetMAC, 0, magicPacket, offset, targetMAC.Length);
    offset += 6;
  }

  if (password != null)
  {
    Buffer.BlockCopy(password, 0, magicPacket, offset, password.Length);
  }

  IPEndPoint ep = new IPEndPoint(IPAddress.Broadcast, 9);
  UdpClient c = new UdpClient();
 
c.Send(magicPacket, magicPacket.Length, ep);
}

12/6/2006 5:29:35 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0]  | 
 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

11/8/2006 12:42:08 PM (Eastern Standard Time, UTC-05:00)  #    Comments [2]  | 
 Tuesday, October 31, 2006

Some info that's worth reading:

10/31/2006 12:45:58 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [0]  | 

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);
    }
  }
}

10/31/2006 12:35:18 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [0]  | 
 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%"

 

10/26/2006 10:33:22 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [4]  |