Page 1 of 8 in the Coding4Fun category Next Page

A while back, the very wise Brendan Grant sent me some sample code that would use Twitter to report the health of one's Windows Home Server. I always meant to update the code to use TweetSharp to talk to Twitter, as well as add some robustness for connected/disconnected scenarios, but I'm just never going to get around to it. Instead, here it is as he sent to me.

There's a REALLY vibrant community around Windows Home Server plugins and if you've got a WHS and you want it to do something that it doesn't do, I'd encourage you to jump in.

Even as I'm posting this, I'm sure there are better and more interesting implementations. However, I like what Brendan has done to abstract away the core COM-based API of WHS for use in managed code.

Here's the full program...note again that the PostTweet() method is hacked together and should use a more robust technique:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.IO;
using System.Xml.Serialization;
using Microsoft.HomeServer.SDK.Interop.v1;

namespace Twitter_Test
{
class Program
{
static string username = "";
static string password = "";

static void Main(string[] args)
{
IWHSInfo info = new WHSInfoClass();
//Register application name
info.Init("WHS Twitter Client");

NotificationCallbackClass notificationClass = new NotificationCallbackClass();
//Register notification callback class
info.RegisterForNotifications(notificationClass);

//Check current state
Console.WriteLine("Current State: " + notificationClass.GetHealthState().ToString());

notificationClass.HealthChanged += new EventHandler<HealthChangedEventArgs>(notificationClass_HealthChanged);

Console.WriteLine("Monitoring for health changes. Press <ENTER> to exit.");
Console.ReadLine();
}

static void notificationClass_HealthChanged(object sender, HealthChangedEventArgs e)
{
Console.WriteLine("Current State " + e.Health.ToString());
PostTweet(username, password, "Your Windows Home Server's health is now: " + e.Health.ToString());
}

private static Status PostTweet(string username, string password, string message)
{
string user = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(username + ":" + password));
// determine what we want to upload as a status
byte[] bytes = System.Text.Encoding.ASCII.GetBytes("status=" + message);
// connect with the update page
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://twitter.com/statuses/update.xml");
// set the method to POST
request.Method = "POST";
// set the authorisation levels
request.Headers.Add("Authorization", "Basic " + user);
request.ContentType = "application/x-www-form-urlencoded";
// set the length of the content
request.ContentLength = bytes.Length;

request.ServicePoint.Expect100Continue = false;

// set up the stream
Stream reqStream = request.GetRequestStream();
// write to the stream
reqStream.Write(bytes, 0, bytes.Length);
// close the stream
reqStream.Close();

HttpWebResponse response = request.GetResponse() as HttpWebResponse;

StreamReader sr = new StreamReader(response.GetResponseStream());
string s = sr.ReadToEnd();


XmlSerializer ser = new XmlSerializer(typeof(Status));
object o = ser.Deserialize(new StringReader(s));
Status status = o as Status;

return status;
}
}
}

There interesting part is the Eventing part where he makes changes in your Home Server turn into .NET Events via callbacks. Check the code for details. You can get events when Physical Disks are changed, when Backup States change, or when basically anything happens. There's a number of folks on Twitter already who have their Windows Home Servers tweeting.

If you've got, or you're using a plugin to report your Home Server status on Twitter (or SMS or whatever) leave a comment and I'll update the post! I'm sure there are better solutions than this little sample.

Here's the code if you want it, and remember, it may kill your pets. If so, don't blame me as I'll deny everything. It's a sample you found on the Internet, what did you expect?



My one-hundred-and-forty-first podcast is up. I talk with Dan and Brian as they turn the successful Coding4Fun blog into a book. Brian shares how to interface with the Nintendo Wii's Wiimote, and Dan tells us how to download and convert YouTube videos in one click.

Subscribe: Subscribe to Hanselminutes Subscribe to my Podcast in iTunes

Do also remember the complete archives are always up and they have PDF Transcripts, a little known feature that show up a few weeks after each show.

Telerik is our sponsor for this show!

Building quality software is never easy. It requires skills and imagination. We cannot promise to improve your skills, but when it comes to User Interface, we can provide the building blocks to take your application a step closer to your imagination. Explore the leading UI suites for ASP.NET and Windows Forms. Enjoy the versatility of our new-generation Reporting Tool. Dive into our online community. Visit www.telerik.com.

As I've said before this show comes to you with the audio expertise and stewardship of Carl Franklin. The name comes from Travis Illig, but the goal of the show is simple. Avoid wasting the listener's time. (and make the commute less boring)



ExplorerForm I sat down with the Coding4Fun Developer Kit and immediately noticed the Preview Handler stuff. I got addicted to Preview Handlers back when Tim wrote one for PDFs. They are darned useful and it seems to me that any application that adds a new file type should add a preview handler for it. They are used in the Vista Explorer, in Outlook 2007 and in Windows Desktop Search. If you or your company makes an explorer replacement, you can also host a Preview Control and add Preview functionality to your own File Explorer application.

I wanted to make a vCard Preview Handler so I could see what's inside a vCard on systems that don't have Outlook. Here's the process to create your own Preview Handler in no time (keeping in mind that the kit is BETA).

  • After installing the C4F Developer Kit, make new Class Library project and add a reference to the PreviewHandlerFramework.
  • Create a new class like below and add the [PreviewHandler] attribute with a name for your project, the extension or extensions (like .foo;.bar), and another Guid for the COM Stuff.
    • You can get new GUIDs either via GuidGen.exe included with the Windows SDK or online at http://www.guidgen.com/ or in PowerShell via [Guid]::NewGuid().ToString()
  • Also, include a ProgId for your new class. I just used the namespace.classname.
  • Notice that I derived from FileBasedPreviewHandler. You'll need to override CreatePreviewHandlerControl and return a new instance of your own Control that is derived from FileBasedPreviewHandlerControl. The boilerplate is below. It's inside Load() where you create whatever WinForms controls you need to and add them to the this.Controls collection.
using C4F.DevKit.PreviewHandler.PreviewHandlerFramework;

namespace C4F.DevKit.PreviewHandler.PreviewHandlers
{
    [PreviewHandler("Hanselman Silly vCard Preview Handler", ".vcf", "{42810C0B-FEA8-4dbf-A711-5634DFBA9F3B}")]
    [ProgId("C4F.DevKit.PreviewHandler.PreviewHandlers.vCardPreviewHandler")]
    [Guid("D193B258-AC07-4139-B334-C20F18F4FC7C")]
    [ClassInterface(ClassInterfaceType.None)]
    [ComVisible(true)]
    public sealed class vCardPreviewHandler : FileBasedPreviewHandler
    {
        protected override PreviewHandlerControl CreatePreviewHandlerControl()
        {
            return new vCardPreviewHandlerControl();
        }

        private sealed class vCardPreviewHandlerControl : FileBasedPreviewHandlerControl
        {
            public override void Load(FileInfo file)
            {
//ADD STUFF HERE } } } }

vCards are funky things, and there's multiple versions of the format. The general format is like this:

BEGIN:VCARD
VERSION:2.1
N;LANGUAGE=en-us:Hanselman;Scott
FN:Scott Hanselman
ORG:Microsoft
TITLE:Senior Program Manager
TEL;WORK;VOICE:+1 (503) 766-2048
TEL;HOME;VOICE:+1 (503) 766-2048
TEL;CELL;VOICE:+1 (503) 766-2048
ADR;WORK;PREF:;;One Microsoft Way;Redmond;WA;11111;United States of America
LABEL;WORK;PREF;ENCODING=QUOTED-PRINTABLE:One Microsoft Way=0D=0A=
Redmond, WA  11111
ADR;HOME:;;5 Main Street;Main Town;OR,;12345;United States of America
LABEL;HOME;ENCODING=QUOTED-PRINTABLE:5 Main Street=0D=0A=
Main Town, OR, 12345
URL;WORK:
http://www.hanselman.com
EMAIL;PREF;INTERNET:firstname@lastname.com
REV:20070810T050105Z
END:VCARD

But there's a million extensions to this format and things can get very complex very fast. I set myself a goal of getting something passable working in a few hours, so I decided to preview the vCard in a DataGrid. That made Load() look like this:

public override void Load(FileInfo file)
{
    DataGridView grid = new DataGridView();
    grid.DataSource = ConvertVCardToDataTable(file);
    grid.ReadOnly = true;
    grid.Dock = DockStyle.Fill;
    grid.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
    Controls.Add(grid);
}

Next, I took the FileInfo and spun through it, breaking up each line and sticking it into a static DataTable, the format most friendly to the DataGridView.

private static DataTable ConvertVCardToDataTable(FileInfo file)
{
    DataTable table = new DataTable();
    table.Locale = System.Threading.Thread.CurrentThread.CurrentCulture;
    table.TableName = file.Name;

    using (StreamReader sr = file.OpenText())
    {
        table.Columns.Add("Data");
        table.Columns.Add("Value");

        string line;
        while ((line = sr.ReadLine()) != null)
        {
            if (line.Length > 3)
            {
                string[] parts = ProcessVCardLine(line);
                if (parts != null && parts.Length == 2)
                {
                    table.Rows.Add(parts);
                }
            }
        }
    }
    return table;
}

ProcessVCardLine just returns a string array of "name,value" given a single vCard line. Again, I never said it was pretty, I just said it worked.

private static string[] ProcessVCardLine(string line)
{
    //This is by no means a complete or even passable parsing of the fairly complex vCard format.
    List<string> nameValue = new List<string>();

    if (line.StartsWith("BEGIN:VCARD")) return null;
    if (line.StartsWith("VERSION:")) return null;
    string[] parts = line.Split(':');
    if (parts.Length == 2)
    {
        AddVCardLine(parts, ref nameValue, "TZ", "TimeZone");
        AddVCardLine(parts, ref nameValue, "NICKNAME", "Nickname");
        AddVCardLine(parts, ref nameValue, "N", "Name");
        AddVCardLine(parts, ref nameValue, "FN", "Friendly Name");
        AddVCardLine(parts, ref nameValue, "ORG", "Organization");
        AddVCardLine(parts, ref nameValue, "TITLE", "Title");
        AddVCardLine(parts, ref nameValue, "TEL", "Phone");
        AddVCardLine(parts, ref nameValue, "ADR", "Address");
        AddVCardLine(parts, ref nameValue, "URL", "Website");
        AddVCardLine(parts, ref nameValue, "EMAIL", "Email");
        AddVCardLine(parts, ref nameValue, "X-MS-IMADDRESS", "IM");
    }

    return nameValue.ToArray();
}

private static void AddVCardLine(string[] parts, ref List<string> nameValue, string name, string friendlyName)
{
    if (parts[0].StartsWith(name) && parts[1] != null)
    {
        nameValue.Add(friendlyName);
        nameValue.Add(parts[1].Replace(";", ",").Trim().Trim(','));
    }
}

Registry Editor Because this is a .NET assembly that will be called by an app expecting a COM dll, you'll need to put it in the GAC and run Regasm on it.

I made this easier during development by adding these two lines to the Post-build event command line. You may need to search your system to find where Gacutil.exe and Regasm.exe are on your system, or you can download the .NET Framework 2.0 SDK.

"C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\Gacutil.exe" /i "$(TargetPath)"
"C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\Regasm.exe" /codebase "$(TargetPath)"

Once registered, the new vCard Preview Handlers is available all over Windows to any app that cares to use it.

I've looked at this code a couple of times, not just because it's poopy, but because I felt there must be at least a few clean ways I could have made the code cleaner/terser using some of the new C# 3.0 features like Anonymous Types, and yield. Any ideas?

Here's the real tragedy. After I wrote this very sad little "just barely good enough" vCard parser, I discovered that the Coding4Fun DevKit already included a very complete vCard parsing implementation.

Curse my metal body! The vCard sample is in the Contacts project within C4FDevKit and it's scrumptious. Well, live and learn. Anyway, I had fun and it took less than an hour to get a useful (to me) PreviewHandler working. The C4FDevKit includes samples and compiled PreviewHandlers for CSV, generic binary, Icons, XML files via IE, MSIs, PDFs, Resx and Resources, SNK (keys) and Zip Files. Sweet.

You can more easily test your Preview Handlers using the PreviewHandlerHost control on a simple WinForms app, or even easier by using the already-written PreviewHandlerHost at Coding4Fun\C4FDevKit2008v1\Preview Handlers\Samples\VB\PreviewHandlerHostSample as seen in the screenshot above.



Coding4Fun Logo I've written a number of articles for Coding4Fun, in fact, there's a whole category for them on my Blog here. Dan Fernandez runs Coding4Fun and there's a LOT of Coding4Fun writers, so I can't keep track of all the cool stuff that's being done on the project.

I was looking around for some really good Source Code to read. You know the whole Be a Better Developer in 6 Months thing? We did a podcast on it, and it's being discussed in the forums. One of the things that I thought was the most important was reading other people's code. I postulated that writers don't become better just be writing, but by reading.

Anyway, I stumbled on a treasure trove of source to read. Recently released, and comparatively quietly, was the Coding4Fun Developer Kit 2008 Vol 1 (BETA) (whew!) that I will refer to as the C4F DevKit because that name is crazy long.

The "Vol 1" designation is cool because it implies it'll be a series of projects. Here's the kinds of stuff it includes (very DIY (Do It Yourself) friendly):

  • Bluetooth – Serial Port, File Transfer and Object Push; plus Device Discovery and Service Discovery
  • Windows Vista Contacts – abstraction of the Windows Contacts API 
  • Messaging – Better APIs for SMTP and POP3
  • Power Management – Access Windows Vista Power Management features like rebooting, hibernating and locking a computer; get battery status on a laptop too
  • Windows Desktop Search – Easy API to Search
  • Windows Vista Picture Acquisition – Acquire pictures and videos from common devices like digital cameras
  • Preview Handler – Resolve predefined file types such as Microsoft Word , Visual Basic and Visual C# source files, images and many others in their own applications.
  • SoundRecorder - record audio sounds and play them back information with Windows Vista's API
  • RSS ScreenSaver - WPF RSS ScreenSaver 
  • P2P Picture Share - share your pictures with family and friends

C4F Toolbox There's a LOT of source here - a bunch of it I could use in some of my on-the-side projects. Some of it is "mashups," brining different APIs together in interesting way, while much of it is very clean abstractions of COM or P/Invoke APIs for useful things like Image Acquisition and Source Recording. These are the kinds of APIs that it'd be nice to see pushed back into the BCL/platform. It also serves as a good primer on how to create a managed API on top of an unmanaged one. I think I'll do some posts on it. The Preview Handler on is very nice.

The other thing that's cool about these "components" is that a bunch of them are done as controls that live in the Visual Studio 2008 toolbox (and they all work on the Express SKUs) so in many cases using them is just a drag-and-drop operation with a Smart Tag. Apparently the plan is to add as many Smart Tags as possible.

While I was able to run the installer and compile most of the code on Windows XP, many of the projects are Vista specific. I think in the future there will be an option to select the projects that are specific to your OS. If you've got Visual Studio 2008 or the C#/VB Express version, you'll be fine. The Express 2008 SKU runs very fast under my XP SP2 VM and I've been very happy with it.

I wanted to see how fast I could get something new written with some of this source, so I set off to create a vCard Preview Handler. Remember what a Preview Handler is? It's those little in-proc plugins that can be used by Outlook or the Vista Explorer to show you what a file looks like without opening the host application. Tim Heuer made a PDF Preview Handler using some code from the obscenely talented Stephen Toub (here's a list of all his articles on MSDN).

My next post will talk about writing a vCard Preview Handler using the code in the C4F DevKit. I wanted to see just how quick and easy (or totally not at all) it would be, considering all the COM Interop and what-not.

I've really enjoyed reading the code in this kit. Nothing makes me realize what a crap programmer I am like reading a good programmer's code.



Here's a screencast demoing writing Managed .NET Plugins for the Optimus Mini Three Keyboard.

I'm a fan of the Optimus Mini-Three Keyboard for its potential. It's a harbinger of things to come, like someday a complete keyboard with screens for keys. Their hardware is wonderful. But, as a design shop with a specialization in hardware, not software, their software plugin model leaves much to be desired.

When I did my video on an Optimus Mini Three post in managed code, I ended up P/Invoking into their library, bypassing their plugin model and opting instead for a command line experience. Bummer, right?

The Optimus Configurator is pretty, to be sure, but it's programming model is obtuse and trapped in the C++ world. And not the nice OOP C++ world, the nasty part of that world.

The "implement these dozen virtual methods that tunnel strings via magic indexes" world:

virtual BOOL Paint(int button, HDC hdc) = 0;

// to collect INFO_* values from plugin 
virtual LPARAM GetInfo(int index) = 0;

 Meh. When your unmanaged C++ plugin gets called, the configuration app will call your GetInfo at least 20 times, passing in different magic numbers to get information about your plugin like name, webpage, version, etc. Not only are there these internal semantics to grok, there's a whole get/setProperties state bag thing to manage as well.

It's a common pattern for plugins though, especially when you as the host want to provide persistence to your plugins. The Windows Live Writer plugin model is similar...I had to maintain state for my Insert Amazon Links WLW Plugin via the same pattern.

On the other hand, the Paint stuff is very clever, actually. You get an HDC (handle to a device context) passed in, you paint to it, and they handle getting the resulting image over to the Optimus via their USB to Serial Bridge.

Screencast of managed Optimus Mini Three Keyboard PluginsBut, their plugins aren't managed code, and I believe that this more difficult model explains the fact that while the device is selling well, there are only (as of the writing of this post) three 3rd party plugins.

Harald Röxeisen went off (he says after reading my post...Cool!) and has released an alpha of a .NET library for Optimus Support. However, rather than supporting managed plugins in the Optimus Configurator, he's written his own configurator from scratch in .NET, and built a plugin model on top of that. It's basic, but a fantastic start. He proves my point about writing plugins, as even his alpha include THREE all new plugins written against his managed API. Excellent.

For me, I've done the inverse of Harald, and got managed plugins to work inside the existing configurator. After my last post, I got great ideas from Matt Davis of DocumentCommand and Jason Copenhaver. Jason's was managed/unmanaged C++ that would provide a bridge, and Matt's used COM and the .NET COM Interop stuff as a middleman. For me, Matt's was faster as I know that stuff pretty well.

Here's the general idea...Matt's shim implements the C++ virtuals that the configurator expects and calls CoCreateInstance on a known ProgId, in my example it's "Optimus.Nothing." Could be whatever. You'll need one shim and one ProgId per managed plugin as far as I can see. That call to CoCreateInstance is actually activating a .NET assembly that is implementing Matt's COM interface (that we might want to make even more COMish) created via TlbImp. Since we're .NET, the runtime and we get loaded, and wackiness ensues. 

We're in a no-man's land between managed and unmanaged code (and I'm sure we're leaking like a sieve) but we do things like Marshal.StringToHGlobalAnsi(managedString).ToInt32(); and

static int i = 0;
public int Paint(int button, IntPtr HDC)
{
    i++;
    DebugWrite(button);
    using (Graphics g = Graphics.FromHdc(HDC))
    {
        Brush b = ((i % 2 == 0) ? Brushes.Blue : Brushes.Red);
        g.FillRectangle(b, new Rectangle(0, 0, 96, 96));
    }
    if (i > 100) i = 0;
    return 1;
}

...for example. But, happily, we seem to not be noteworthy because the Mini Configurator loads us just fine.

I've approached Harald and perhaps we'll figure out a better bridge for .NET plugins where I could write a plugin that supports his managed API and use it in either his configurator, or the original configurator from Optimus. If I'm going to be promoting writing these plugins, we'll want the interface to hide a lot of the dispatchy stuff, more like the plugin model Bryan Batchelder and I did for the USB Security Key Fobs.

In Harald's .NET configurator, the abstract class you derive from to create a "Harald plugin" is very clean and includes new features like OnKeyHold and OnKeyDoublePress that the Optimus software doesn't make easy. He also, of course, uses BCL types like Bitmap over HDCs.

public abstract class OptimusMiniPlugin
{
    protected OptimusMiniPlugin();
    public abstract void Initialize();
    public virtual void OnKeyDoublePress();
    public virtual void OnKeyDown();
    public virtual void OnKeyHold();
    public virtual void OnKeyPress();
    public virtual void OnKeyRelease();
    public virtual void OnKeyUp();
    public abstract void Repaint();
    public void RequestNextUpdate(TimeSpan interval);
    public abstract void Terminate();
    public abstract void Update();
    public void UpdateImage(Bitmap image);
}

I'm excited to see the possibilities for this little device. Perhaps between my stuff and Haralds (and someone elses?) we can get a Windows Vista Side Show driver working for this thing...probably time for YAGCP (Yet Another Google Code Project). For now, here's what I've got. I'll do a Coding4Fun article on this in much more detail this weekend perhaps, for now, it's just scribbles and it works on my system. ;)

I wonder if the Optimus Keyboard folks care...I hope so. I'm convinced that writing managed plugins is easier than unmanaged (on Windows or in Mono). Is this even worth debating?



Page 1 of 8 in the Coding4Fun category Next Page

Contact

Sponsors

Hosting By

Hot Topics

Tags

Calendar

<November 2009>
SunMonTueWedThuFriSat
25262728293031
1234567
891011121314
15161718192021
22232425262728
293012345

Archives

November, 2009 (5)
October, 2009 (19)
September, 2009 (11)
August, 2009 (12)
July, 2009 (21)
June, 2009 (26)
May, 2009 (16)
April, 2009 (13)
March, 2009 (17)
February, 2009 (17)
January, 2009 (18)
December, 2008 (32)
November, 2008 (17)
October, 2008 (22)
September, 2008 (16)
August, 2008 (14)
July, 2008 (25)
June, 2008 (19)
May, 2008 (17)
April, 2008 (17)
March, 2008 (26)
February, 2008 (21)
January, 2008 (28)
December, 2007 (19)
November, 2007 (17)
October, 2007 (31)
September, 2007 (39)
August, 2007 (37)
July, 2007 (43)
June, 2007 (37)
May, 2007 (32)
April, 2007 (38)
March, 2007 (29)
February, 2007 (46)
January, 2007 (31)
December, 2006 (27)
November, 2006 (31)
October, 2006 (32)
September, 2006 (39)
August, 2006 (34)
July, 2006 (40)
June, 2006 (18)
May, 2006 (31)
April, 2006 (34)
March, 2006 (30)
February, 2006 (38)
January, 2006 (44)
December, 2005 (19)
November, 2005 (34)
October, 2005 (24)
September, 2005 (37)
August, 2005 (20)
July, 2005 (24)
June, 2005 (33)
May, 2005 (16)
April, 2005 (22)
March, 2005 (34)
February, 2005 (15)
January, 2005 (37)
December, 2004 (28)
November, 2004 (30)
October, 2004 (34)
September, 2004 (22)
August, 2004 (34)
July, 2004 (18)
June, 2004 (64)
May, 2004 (49)
April, 2004 (21)
March, 2004 (29)
February, 2004 (29)
January, 2004 (36)
December, 2003 (25)
November, 2003 (24)
October, 2003 (59)
September, 2003 (42)
August, 2003 (24)
July, 2003 (44)
June, 2003 (29)
May, 2003 (21)
April, 2003 (30)
March, 2003 (27)
February, 2003 (47)
January, 2003 (50)
December, 2002 (31)
November, 2002 (38)
October, 2002 (44)
September, 2002 (15)
May, 2002 (2)
April, 2002 (4)

Google Ads