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).

4/6/2007 5:49:54 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [2]  | 
 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.

4/5/2007 11:42:43 AM (Eastern Daylight Time, UTC-04:00)  #    Comments [0]  | 

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:

 

4/5/2007 11:05:41 AM (Eastern Daylight Time, UTC-04:00)  #    Comments [0]  | 
 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.

3/23/2007 4:56:43 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [1]  | 
 Thursday, March 08, 2007

And so ends the debate of whether you can time an engine by ear or not.

 

3/8/2007 9:00:38 AM (Eastern Standard Time, UTC-05:00)  #    Comments [0]  | 
 Thursday, March 01, 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.

3/1/2007 12:02:32 AM (Eastern Standard Time, UTC-05:00)  #    Comments [0]  |