Monday, February 11, 2008
We were struck at least 4 times in the last 24 hours by attacks injecting into the existing Snitz forums casuing redirection to online pharma companies.  Fortunately we've been working to migrate all of the forums and content to the community site so switching over was fairly painless.  We had hoped to give users a little longer to get usernames created in the new site so they could keep attribution for posts, but sorry - the spammers accelerated that schedule.  Hopefully we'll get our redirector up and working shortly so that all of those Google and blog links to the old forums will continue to work and pass through to the new site.

Take a look at the new Forums here (and don't forget that we've re-started a Wiki as well).

2/11/2008 10:01:00 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0]  | 
 Thursday, January 31, 2008
So it seems MSDN just release a new article on Developing Stylus-Free Windows Mobile Professional Applications. I'm glad they're wisely spending the very little resources they seem to use for mobile development on getting new and fresh information out.  I mean, it's not like the same information has been available for 2 years already, and that the article was actually *submitted to them* for publication even before it was posted on the Web independently.  You go, MSDN!  If you'd like some other content you might look at any one of these (which we actually started publishing because MSDN apparently isn't interested).

1/31/2008 11:08:22 AM (Eastern Standard Time, UTC-05:00)  #    Comments [0]  | 
 Saturday, January 26, 2008
If you follow my blog much, then you know performance is a bit of an interest of mine.  Well here's one that pretty much pulls the pants down on the CF loader.  Nothing like a 3 second load time for some classes.  Admittedly it's an edge case and there seems to be some wacky logic in the repro code, but it's still pretty interesting. And kudos to Noah for looking into it, finding the problem and at least explaining why it's happening and how to mitigate it at least to a degree (and to his managers for letting him do it).  Without doubt the CF team is the most transparent team I've encountered at Microsoft.  They readily admit where they've got shorcomings and are happy to explain why we see problems.  One can only hope this practice continues.

1/26/2008 1:11:37 AM (Eastern Standard Time, UTC-05:00)  #    Comments [0]  | 
 Tuesday, January 22, 2008
It took me 5 months to actually get it out the door, but I've just published a new white paper on the performance implications of P/Invoking in the COmpact Framework.  Check it out here.

1/22/2008 5:12:07 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0]  | 
 Monday, January 21, 2008
A customer asked us to integrate a new camera with Padarn recently (the D-Link DCS-900) to try to do a similar thing that I had done in my PTZ camera demo.  Well this camera was a little different and a whole lot easier to get "streaming" data from.  I ended up writing a small wrapper class that takes the MJPG data coming from the camera, parses it into in-memory Image classes, and then those are available for use.

Once I had that I figured it would be interesting to serve up a Padarn page that did nothing but return the latest frame data from that camera - after all the camera doesn't support PTZ and just a button that takes a picture didn't seem terribly new or informative. 

The problem was that the version of Padarn I was using (1.0.5020) didn't support writing a stream of bytes to the Response.  Well as of 1.0.5030 Padarn now supports WriteBytes (and a few other things like CacheBehavior) and I created a page that dynamically loads the images into an on-page IFrame and allows you to adjust the refresh rate dynamically without ever having to send a full page request in again.

I can see that this could be useful for dynamic updates to a page for something like a control system gauge or for animating building automation status items.

Check out the new sample here.

The code for the DCS-900 looks like this (notice that the ICamera interface changed a little, so the PTZ camera also had to be altered.  I simply stubbed out the new methods).

using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Threading;
using System.IO;
using System.Net;
using System.Net.Sockets;
using OpenNETCF.Peripherals.Camera;

namespace OpenNETCF.Peripherals
{
  public class DCS900 : ICamera
  {
    private Image m_lastImage = null;
    private volatile bool m_stopThread = true;
    Thread m_grabberThread;
    private IPAddress m_ip;

    public DCS900()
    {
    }

    public void Initialize(IPAddress ipAddress, string userName, string password)
    {
        m_ip = ipAddress;
    }

    public bool SupportsPanTilt
    {
        get { return false; }
    }

    private int FindFrameBoundary(byte[] data, int offset, int maxLength)
    {
        byte[] frameDelimiter = Encoding.ASCII.GetBytes("--video boundary--");
        int n = 0;
        bool match;

        for (; offset < maxLength; offset++)
        {
            match = true;
            if (data[offset] == frameDelimiter[n])
            {
                match &= true;
                n++;
                if (n >= frameDelimiter.Length)
                {
                    // we've found the header
                    return offset + 1;
                }
            }
            else
            {
                match = false;
                n = 0;
            }
        }

        return -1;
    }

    private Image ImageFromFrameData(byte[] data, int frameLength)
    {
        int start = 50;

        // find the data start - it will be between 50 and 54 bytes in from the frame start
        while (data[start] != 0xFF)
        {
            start++;
            if (start > 54) return null;
            if (data[start + 1] == 0xD9) break;
        }

        try
        {
            using (MemoryStream stream = new MemoryStream(data, start, frameLength - start))
            {
                Image image = new Bitmap(stream);
                stream.Close();
                return image;
            }
        }
        catch (Exception ex)
        {
            System.Diagnostics.Debug.WriteLine(ex.Message);
        }

        return null;
    }

    public void Start()
    {
        m_grabberThread = new Thread(GrabberThreadProc);
        m_grabberThread.IsBackground = true;
        m_grabberThread.Start();
    }

    public void Stop()
    {
        m_stopThread = true;
    }

    public bool IsRunning
    {
        get { return !m_stopThread; }
    }

    private void GrabberThreadProc()
    {
        lock (m_grabberThread)
        {
            m_stopThread = false;

            Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            s.Connect(new IPEndPoint(m_ip, 80));

            string header = "GET /VIDEO.CGI HTTP/1.0\r\nUser-Agent: user\r\nAuthorization: Basic YWRtaW46REVVU1Q=\r\n\r\n";
            byte[] data = Encoding.ASCII.GetBytes(header);
            byte[] image = new byte[32768]; // 32k buffer is more than adequate for this camera

            s.Send(data);

            data = new byte[2048];

            int offsetStart = 0;
            int offsetEnd = 0;
            int ptr = 0;

            int length = s.Receive(data);

            do
            {
                offsetStart = FindFrameBoundary(data, 0, length);
            } while (offsetStart < 0);

            while (!m_stopThread)
            {
                offsetEnd = FindFrameBoundary(data, offsetStart, length);

                while (offsetEnd < 0)
                {
                    int copyLength = length - offsetStart;
                    Buffer.BlockCopy(data, offsetStart, image, ptr, copyLength);
                    offsetStart = 0;
                    ptr += copyLength;

                    length = s.Receive(data);
                    offsetEnd = FindFrameBoundary(data, offsetStart, length);
                }

                if (ptr > 50)
                {
                    Buffer.BlockCopy(data, offsetStart, image, ptr, offsetEnd - 18);
                    ptr += offsetEnd - 18;

                    m_lastImage = ImageFromFrameData(image, ptr);

                }
                offsetStart = offsetEnd;
                ptr = 0;

                Thread.Sleep(200);
            }

            s.Close();
        }
    }

    public Image GetImage()
    {
        return m_lastImage;
    }

    public void Pan(PanDirection direction)
    {
        throw new NotSupportedException();
    }

    public void Tilt(TiltDirection direction)
    {
        throw new NotSupportedException();
    }
  }
}


1/21/2008 7:56:46 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0]  | 
 Thursday, January 17, 2008
We've had a little bit of a brand confusion problem for a while now.  Many people incorrectly refer to our Smart Device Framework library as "OpenNETCF" - so you hear things like "I'm using OpenNETCF version 2.1" which is a bit annoying.  OpenNETCF is the company name.  We have multiple products.  You're not using Microsoft 8.0 are you?  "Hey look at how smart I am!  I listen to music on my Apple 4.0."

But it seems to have gotten worse.  A friend just sent me a clip of a resume he received.  Of course it seems to have way more on it than a person probably would know having graduated probably 2 or 3 years ago (it's clipped, but I assume that the candidate was at UT for probably 2 years) but note the list of technologies.


1/17/2008 5:48:13 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0]  |