Skip to content

2006

UserControl need to be able to run with no arguments for Designer

When you create a Custom Control or User Control in Visual Studio 2k5, it needs to be able to render its running state with no additional information. I.e. it needs to have a no argument constructor and cannot need extra data passed in at initialization, otherwise Visual Studio 2k5 Designer will die when you add the Control to another form or control, even if it comes up fine in the Designer by itself.

Ok, this one is a big Duh! but i'm sure i'll forget again some day in the future and bitch and moan about Visual Studio hating me and constantly crashing. Basically it comes down to the designer embedding the control and running it. So OnPaint will get called and any timer you have that's not disabled will run. Now if you build controls that require extra initialization data, and the running portions of the control try to use them, you'll get a NullException. Makes perfect sense really, hence the Duh. But of course, you may forget that part, because, after all, the control by itself runs fine and only crashes if the control is added to another control/form. My bet is that the designer is basically a control container and will just discard the exception, but if you add the control to another, the control being added to doesn't handle the exception and complaints.

A cool little side-effect here is that that you can put an animation in a control and that animaton will run in the Designer. :)

Put it in the Dropboks

A friend of mine created a simple web app that falls in the "yeah, i was going to do that... some time.. when i have time.. right, any day now" category. But you know how it goes, you just wish someone did it and you never get around to it.

It's a web page for uploading all those files that you need to lug around various computers you use in different locations. As a geek, i generally end up scp'ing things to my server. But that only works if the machine has scp, etc. I tried iFolder for a while, but it just never worked reliably for me and ask me for my password every time i reboot, even though i tell it to remember me each time.

And sure, there are a ton of file upload but most of them are rich on features, and with that need for configuration, and I just never bother setting up an account. If I had built my own, i'd have started down that road as well, with a nice API, advanced user and group system, ways to mount the file system on the desktop, and would have just got bogged down in the complexity.

Dropboks does just one thing, but it does it immediately and it does it extremely well. It's brilliant in its simplicty and it's gonna be immensely useful to me.

A matter of timing

You know how everything time related in .NET uses milliseconds? Kind of gives you the impression you can deal with things at a millisecond resolution. Generally not so. The internal clock seems to have a resolution just a bit above 10ms, so no matter what value you use for Thread.Sleep() or as your interval for any of the three built-in timers, you achieve about 65Hz. Now, I say "generally" because while my tests on timers have returned 65Hz regardless of which machine I tried them on, Thread.Sleep(1) seemed to give me about 500Hz (still half as fast as it should be) on one machine.

For gathering timing statistics, you could always wrap the Kernel32 counters and get amazing time resolution and with .NET 2.0 you get System.Diagnostics.Stopwatch which has that high resolution as well, if Stopwatch.IsHighResolution is true. It seems to be true on every machine i've tried. But still doesn't give me a timer with the right resolution.

Thread.Sleep(0) seems to just yield execution, so a tight loop on it gives us high resolution and should let other threads work uninterrupted. But it does drive load up regardless. I guess it's a phantom load, but i can't be sure. Using System.Diagnostics.Stopwatch and Thread.Sleep(0), I whipped up a quick-and-dirty High Resolution Timer class as follows:

public class HiResTimer
{
  static Stopwatch ticktock = new Stopwatch();
  static int counter;
  static public void Test()
  {
    HiResTimer hiTee = new HiResTimer(new TimerCallback(DoSomething), 10);
    Thread.Sleep(500);
    for (int i = 0; i < 1000; i++)
    {
      Thread.Sleep(100);
      Console.Clear();
      double hz = counter \* 1000 / ticktock.ElapsedMilliseconds;
      Console.WriteLine("hz: {0:f}", hz);
    }
    hiTee.Stop();
  }

  static void DoSomething(object nullobject)
  {
    if (!ticktock.IsRunning)
    {
      ticktock.Start();
    }
    counter++;
  }

  Thread timerLoop;
  Stopwatch watch = new Stopwatch();
  bool keepTiming = true;
  TimerCallback cycleDelegate;
  int interval;

  public HiResTimer(TimerCallback timerCycle, int interval)
  {
    this.cycleDelegate = timerCycle;
    this.interval = interval;
    timerLoop = new Thread(new ThreadStart(DoLoop));
    timerLoop.Start();
  }

  public void Stop()
  {
    keepTiming = false;
    timerLoop.Join();
  }

  private void DoLoop()
  {
    while (keepTiming)
    {
      watch.Reset();
      watch.Start();
      cycleDelegate(null);
      while (watch.ElapsedMilliseconds < interval)
      {
        Thread.Sleep(0);
      }
    }
  }
}

Seems to work and the built-in static Test() method reliably runs at 100Hz, but as i mentioned, my CPU is constantly at 50%. In theory, as the cycleDelegate increases in complexity and other threads consume processing power, the idle loop of the timer should yield it's processing hunger, but I have not given this a proper test.

The real question is, is there another sleep/yield mechanism that gives me a resoltution somewhere between Thread.Sleep(0) and Thread.Sleep(1) (where 1 really ends up being more like 12 in most cases).

Staying in sync

As I mentioned a couple of days ago, I'm trying to keep my PDA/phone and web in sync. The calendar portion is reasonably well taken care of. That leaves contacts and tasks.

I got one of my domains switched to Zimbra (fodder for another post) and looked at their address book. It seems to be just a folder with .vcf files. Using IMAP, I should be able to manipulate those fairly easily. I'll have to look whether i can access the contacts format on the xv6700 directly, because then I could just sync zimbra and my phone over the net. Alternatively, I hook into activesync on my PC and sync contacts only when I dock the phone.

And then there's Tasks. Zimbra doesn't seem to have an option for that. I know apple now stores tasks in iCal, but I guess nobody else has picked up on that yet. I looked around on the net and decided to use Voo2Do for my task keeping. It's got a nice simple REST api, that I was able to wrap in short time. Last night I moved that API to to .NET CF dll and got a little demo app running on my phone that fetches the contacts from Voo2Do. Yay! While not the most straight forward experience, I am nonetheless impressed how simple programming Pocket PC is, if you are already doing .NET stuff.

That means, tasks are well on their way to being taken care of and Contacts shouldn't be impossible to do as well.

So what were the less than straigh forward pieces in developing for the phone? Most likely it comes down to not having found any good resources for doing this and everything I've done for far has been cobbled together from various web searches and forum posts. So here's what I came across:

  • Visual Studio Windows CE 5.0 projects are always targeting 2.0, even though 2.0 doesn't appear to be standard on the platform yet
  • Initial deployment failed getting the debugging cab installed, but it does put it on the phone. Running it on the phone will fix that and then deploy works fine.
  • Need to figure out signing, so I don't get complaints every time i deploy a new build
  • I currently have two copies of a source identical lib, one for .NET and one for .NET CF. Be nice to have a single source tree with multiple targets via Visual Studio. Dunno if that's possible.
  • Debug deployment works and any exception thrown on the phone is caught in Visual Studio. Breakpoints do not, however. Everytime i set one, Visual Studio complaints that the connection to the device closed (even though debug keeps running).
  • Haven't been able to figure out how to get network connectivity on the Pocket PC 2003 SE up and running, so i can't use it for testing.
  • Can't find an emulator for Windows CE 5.0 Pocket PC, just the full CE 5.0. Dunno if one exists.

Gonna have to dig around a bit and find some better sources of documentation for doing this stuff.

Coding on the xv6700

As it turns out, my phone was not developer locked as I had assumed from some of the sites I found. It was mostly a matter of getting VS.NET 2k5 to install its code on the phone. Not that that went without problems, and I gather I still need to figure out how rapiconfig enters into this.

I created a demo on VS.NET 2k5, deployed it to the phone and it failed without any useful error. However the program was over there, so I ran it manually. Problem with my version of .NET Compact Framework, i.e. i only had 1.1 and needed 2.0 for what Visual Studio built. I searched for a while how to target 1.1 from VS.NET 2k5 and found references claiming that I could choose at project creation. However I found no such option when I tried a new project. I think have a release candidate of 2k5 on my home machine, so maybe it's supposed to be there and just not in my version. I will check on that tomorrow with my work machine, which has the official release on it.

So i fired up VS.NET 2k3. This one wouldn't connect to my phone. Played around with the settings for a while, but no go. So I hand deployed the build and voila, first test running on the phone.

Good. But I don't want to target 1.1 or use VS.NET 2k3, if i can avoid it. Can I upgrade my phone? I downloaded the .NET CF 2.o redistributable. It didn't want to install. It complained about another version being on my machine.. Grrr.. I don't care what my machine has, I want it on my phone! But I uninstalled the CF 2.0 I had, then re-installed the new one and it promptly upgraded my phone.

After the phone reboot, the phone no longer came up on my machine when i plugged it in. But that was just an artifact of the install, a rebbot fixed that. In the meantime, I fired up the binary I had previously built for 2.0 and now it worked as well. We've got a platform to play with!

Verizon xv6700 & calendaring

I got an xv6700 last week, trying to rid myself of the extra bulk of my palmpilot. The xv6700 is not too large, but has a nice large screen and a usefully sized keyboard. Plus its a full Windows CE 5.0 device, so I'll start writing apps for it as soon as i figure out how to unlock it for development.

In the meantime, my primary concern has been calendaring. That's been a pet peeve of mine for a while. I want to be able to setup meetings with people outside the boundaries of my personal calendaring program, but all the solutions used to be isolated to their servers. With iCal becoming widely accepted that's finally gone away. I can invite people that have an iCal product (zimbra, google, etc.) from the exchange server at work and vice versa and everything is happy.

However syncing to mobile still sucked, so my palm pilot and my online calendars were constantly out of sync. Switching to the xv6700 I hope to change that.

Now if I ran Exchange for personal use, all problems would be solved, but I don't. I'm currently playing with zimbra for personal use and there is still a use case that fails:

  • Creating a calendar event in zimbra and invite yourself: Mobile outlook picks it up and puts it in the calendar
  • Getting invited from someone using Exchange: Mobile outlook picks it up and puts it in the calendar
  • Creating a calendar event on the phone and inviting someone using exchange: They receive the invite and it goes into their exchange calendar
  • Creating a calendar event on the phone and inviting myself: Zimbra (or google) does not recognize the attached file

However inviting someone from exchange that uses zimbra works. So Mobile Outlook uses a different calendar file format, it would seem.. Annoying. However that still means that the only time I'm out of sync is when I create a new event on the phone. I can live with that for now.

Here's what google sees from the different type of invites:

Invite from Zimbra

Content-Type: text/calendar; name=meeting.ics; method=REQUEST; charset=utf-8
Content-Transfer-Encoding: 7bit

Invite from Exchange

Content-class: urn:content-classes:calendarmessage
Content-Type: text/calendar;
 name="meeting.ics";
 method=REQUEST
Content-Transfer-Encoding: 8bit

Invite from xv6700

Content-Type: application/ms-tnef
Content-Transfer-Encoding: base64
X-MS-Has-Attach:
X-MS-TNEF-Correlator: 221hvc0sdpi5
Content-Disposition: attachment

It would appear that at the very least, Mobile outlook doesn't set up the invite as an .ics, but instead uses MAPI and sends it as a TNEF file. So Exchange will send things out in a portable format, but Mobile Outlook won't. Pity. Can't find any options to change that behavior either.

Ah, the memories

Former High Geek of MP3, Sander van Zoest, just posted a video from the night we unleashed the bomb. Who would have thought that we'd get sued for 80 billion dollars. No seriously, those were damages sought based on $100,000 per song ripped by the bomb.

Hey, it was a worthwhile attempt to try to force record companies into licensing content in a consumer friendly manner. The most intersting lesson learned from the legal aftermath was that a) the record companies couldn't actually license us the CDs if they wanted to, so convoluted was the right holder business and the issues on whether this was mechanical or performance, and b) that the record companies don't actually know what they hold copyrights for, in detail. What a record keeping nightmare.

Sure our ploy didn't make it, but it did pave the road for licensing and pushed Sony/UMG to create a venture for legal music called Duet ne Pressplay, or as it is now called, Napster.

Apache Location-like behavior in ASP.NET (part II)

As I mentioned in my update to my last post, the custom URLs break down on postback because the form doesn't realize where it's supposed to post back to. To get around this, we need two new classes, a new Page base class and a custom HtmlTextWriter:

public class RewriteBaseClass: System.Web.UI.Page
{
 protected override void Render(HtmlTextWriter writer)
 {
   CustomActionHtmlTextWriter customWriter = new CustomActionHtmlTextWriter(writer);
   base.Render(customWriter);
 }
}

and

public class CustomActionHtmlTextWriter : HtmlTextWriter
{
 public CustomActionHtmlTextWriter(HtmlTextWriter baseWriter) : base(baseWriter)
 {
 }

 public override void WriteAttribute(string name, string value, bool fEncode)
 {
   if( name == "action")
   {
     value = HttpContext.Current.Request.RawUrl;
   }
   base.WriteAttribute (name, value, fEncode);
 }
}

This seems to work, although I'm not yet convinced it's the best way or without side-effects. Need to play with it a bit more.

Apache Location-like behavior in ASP.NET

One thing I used to do in mod_perl under apache is use the <Location> directive to feed my PerlHandlers instead of using extensions. Not only did that mean that my apps had nice URLs like

foo.com/myapp
but I would usually use paths as arguments (as is now common in REST_ful_ applications) such that

foo.com/myapp/user/add
meant that i was calling the PerlHandler myapp and the PathInfo /user/add could be interpreted as arguments. Much nicer than

foo.com/myapp.pl?mode=user&action=add
or

foo.com/myapp/useradd.pl
On the ASP.NET side, everything always seemed very file system based, giving it an almost CGI feel, even though under the hood it couldn't have been further from a CGI. Sure you could register your own extensions, but again, extensions and directories -- so filesystem.

I figured it must be possible to process request by hand and it turns out to be rather simple: In IIS just map * (not .*) to ASP.NET for your webapp and you can catch every request. And you don't have to give up on the existing ASPX, ASMX or ASHX infrastructure. Just use HttpContext.RewritePath(string path) to process the incoming requests and send them off to your regular pages or webservices.

By default you loose the path info that you'd receive in the equivalent Apache request. You can fix this with the following Application_BeginRequest code:

protected void Application_BeginRequest(Object sender, EventArgs e)
{
      int start = Request.ApplicationPath.Length+1;
      string path = Request.Path.Substring(start);
      string[] info = path.Split(new char[] {'/'},2);
      string handler = info[0];
      string pathinfo = "/"+info[1];
      string rewritePath = "~/"+handler+".aspx"+pathinfo;
      this.Context.RewritePath(rewritePath);
}

This will take a URL such as foo.com/myapp/user/add and call myapp.aspx with PathInfo of /user/add.

Update: Ok. So this doesn't work quite as well as I'd hoped, since roundtrips will change the URL around. While it doesn't error out, the URL gets ugly again and you have to do some clean-up of your path info. Basically on postback foo.com/myapp/user/add becomes foo.com/myapp/user/add/myapp.aspx.

So somehow the action of the default form needs to be changed (not legal by default). Overriding the renderer or using Javascript seem like options. I'll post again when I have a working, non-hideous solution.