Skip to content

net

Visible != You can see it

The Visible property on Controls is a bit misleading. Aside from the fact that setting it to true, is really only an advisory setting, since it's truth is also dependent on all its parents, there is the issue that is has nothing to do with whether it's actually visible when the form is painted.

Simple example: You have a Control A in a panel and that panel is half off-screen (or at least half across the edge of its container. And A is in the part of the panel that's obscured. A is going to be Visible, even though its not visible.

The interesting thing is that the paint cycle knows this, and OnPaint won't be called on the Control. Now wouldn't it be nice if there was a property that told you that your Visible control won't be drawn? If there is a simple way to determine this, I haven't found it (especially not on .NETCF 1.0).

The way I do it now is that i use RectangleToScreen on the Control and then recursively walk up the parents, get their screen rectangle and call IntersectsWith on the control's Rectangle and each Parent. As soon as i find one that doesn't intersect, I know the Control is not going to be drawn.

It's always the Invoke

If you are working on the .NET Compact Framework 1.0 and you get an ArgumentException trying to manipulate your UI (such as adding a Control to a form), you are likely doing this operation from a thread other than you UI thread and should be using Invoke. Thanks to jonfroelich's post "Controls.Add ArgumentException" for putting me on the right path. With all my frustrating debugging and unexplained freezes, i'd completely forgot that the particular code branch I was looking at was initiated by a callback from a WorkerThread.

Of course the Invoke facilities of .NETCF 1.0 are pitiful, lacking the things that make this whole single-threaded UI business bearable in regular Windows.Forms. There is not InvokeRequired for one thing, nor is there BeginInvoke. And even Invoke just provides the Invoke(Delegate) signature, not the much more useful Invoke(Delegate, Object[]).

Add to that that the only valid Delegate is an EventHandler, which takes two arguments, the exclusion of Invoke(Delegate, Object[]) really is painful.

Of System.Diagnostics.Debug, ActiveSync & Sockets

Today started simple enough. I needed more debug info from my Smartphone app than just using the debugger could give me in a reasonable fashion. My usual solution is log4net for getting a debug spew, but trying to stay as simple as possible on the .NETCF 1.0, I decided to hit up System.Diagnostics instead. More to the point, System.Diagnostics.Debug.WriteLine() and its brethren.

Except, nothing showed up in the output window. Checking whether there was a config setting, then making sure that .NETCF 1.0 supported it, I was stumped. Finally I find out that the default TraceListener is only supported by VS.NET 2k5 if you use .NETCF 2.0. Now I don't know if you've looked at deploying 2.0 apps in a world where even the most modern phones still ship with 1.0, but I certainly couldn't find a clean and simple solution. Especially not one that didn't require being tethered and downloading 40MB from MS, just so they can run a couple hundred kilobytes of App. And it's a pity, because I run into something every day to which the answer is "Use .NETCF 2.0". I want to, really, I do, but deployment convenience trumps developer convenience.

Next up was writing a custom TraceListener and wanting to be as unobtrusive to my Apps operation, I built it on UDP, i.e. a UdpTraceListener. Console based prototype code was up in about 20 minutes for the UdpTraceListener and the console app for listening to the spew. Then followed a couple of hours of testing and stepping through debug and not being able to get anything to be sent from my Emulator cradled via ActiveSync.

I even thought it might be because the phone Emulator couldn't talk to the hosting computer and moved the console app to Mono on my server. That part worked flawlessly--copy the binary call mono UdpTraceConsole.exe and it was up and running. But it also received no data.

Then finally I find a chat log with the .NETCF team that reveals that UDP is not supported over ActiveSync. Nice.

Another 20 minutes of coding and i had a TcpTraceListener up in running in my console test harness. Woot. Time to put test on the phone, so i can finally get back to real coding....

Right. That would have been nice. The same game as before. Tried all sorts of permutations and received no data. Sometimes the debugger would throw a Socket Exception, usually it wouldn't. It made no sense. I knew that TCP worked because I use HttpWebRequest extensively. So I tried putting my console listener on port 80. Nothing. Finally, I decided to open up a port on my firewall and forward it back to my dev machine so that I could try to contact my console listener via a network address from the outside world. And that finally worked. Only theory I have right now is that the DHCP setup via ActiveSync is screwy and it just can't see any of my local net. I'll investigate that later, for now I'm just happy to have a working debug spew.

And here is the exceedingly simple TcpTraceListener. Note: I use '\0' to denote the end of any message so my debug messages can include linefeeds.

using System;
using System.Diagnostics;
using System.Net.Sockets;
using System.Net;
using System.Text;

namespace Droog.Diagnostics
{
  public class TcpTraceListener : TraceListener
  {
    IPEndPoint endPoint;
    Socket console;

    public TcpTraceListener(IPEndPoint consoleEndPoint)
    {
      this.endPoint = consoleEndPoint;
    }

    private Socket ConsoleSocket
    {
      get
      {
        if (console == null || !console.Connected)
        {
          console = new Socket(
            AddressFamily.InterNetwork,
            SocketType.Stream,
            ProtocolType.Tcp);
          console.Connect(endPoint);
        }
        return console;
      }
    }

    public override void Write(string message)
    {
      try
      {
        ConsoleSocket.Send(Encoding.ASCII.GetBytes(message));
      }
      catch
      {
        // this is for a debug spew. I'd rather swallow the exceptions
        // than have my debug code mess with my App.
      }
    }

    public override void WriteLine(string message)
    {
      Write(message + "\\r\\n\\0");
    }

    public override void Close()
    {
      if (console != null)
      {
        if (console.Connected)
        {
          console.Close();
        }
        console = null;
      }
      base.Close();
    }
  }
}

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

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

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!

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.

C# closures

Just after starting to play with closures in javascript (to fake delegates), i run across an excellent series of articles on closures and anonymous functions in C# 2.0, complete with pitfalls. Cool stuff...

The implementation of anonymous methods in C# and its consequences

Part 1

Part 2

Part 3

ThreadSingleton revisited

I've been using the CallContext for a while now to create thread singletons, especially under ASP.NET since it's a single process and you need to work in a thread context when dealing with per request singletons.

Today a co-worker pointed out this post. Apparently you can't trust CallContext under ASP.NET. You really should stuff your singletons into HttpContext when running under ASP.NET, just to be sure. It's a good read.