Friday, May 30, 2008

"This is fun!" she yelled...

I said a while ago that I was going to do it, and Wednesday night I finally got around to it.

Here's my four-year-old zooming around the end of the street without training wheels.

video

Now we'll never be able to keep up with her...

Tuesday, May 27, 2008

Conversation of the day

Product manager Bob stops by my desk to talk to me.

Bob: Who's sitting over there now? There's a kid there, I swear he's twelve years old.

Me: We did get some new co-op students.

Bob: Twelve years old! I've got underwear older than that! He must be some kind of child prodigy or some fucking thing.

Another one bites the dust

So, Steve, can we expect an apology now for the "gossipy old busybodies" line?

I thought not. That would take integrity.

Friday, May 23, 2008

Thursday, May 22, 2008

There's no wind left in my soul, and I've grown old

I was on the last multivitamin the other day, so I stopped by the drugstore on my way home from work to pick up a new bottle. Grabbed one, got home, and opened the old bottle to take out the last vitamin. The bottle looked a little different from the one I just bought. Took a closer look. Turns out, I've been taking the "Gold" version - for "Active adults over 50".

That explains why I've been driving 20 km/h under the speed limit. Yelling at the kids to turn down that crazy music. Going to bed at 8:00pm. Getting up to pee three times during the night. Hating gay marriage. Voting Conservative.

Sheesh. I should take a handful of Flintstones vitamins as an antidote.

Tuesday, May 20, 2008

My startup

I'm thinking of starting a business. Here's how I'll run it. Tell me how you think this will end up.

First off, I'll make a decent product. It won't be perfect, and I'll have to rush a little at the end to get it out the door, but it'll be "good enough." I'll sell a bunch, then I'll work on a new version. It will have some problems, mostly because I tried to get it done as fast as possible, without proper design. I'll throw everyone on it, because many hands make light work. Unfortunately, while we had "all hands on deck" working on the new version, our older versions were getting neglected, and the customers are starting to call.

Now that we have a new version, I have to sell a bunch of these things. I'll get some expensive sales people, grab a few mid- to high-level executives, throw in a token engineer and visit a few potential customers. If the customer asks a tough question, I'll just answer "Yes." It's easier than admitting I don't know the answer, or worse yet, asking the engineer.

Six months later, when the customer gets around to installing the system, I might get some phone calls. Some features don't exactly work the way they were designed, and some features that we promised to the customer are strangely missing. Chalk it up to the steep learning curve. The customer will be fine.

Three months after that, we might notice that the customer is holding the cheque in a threatening manner. They might throw us out if we don't send someone immediately to fix the problems. This is easy. I'll just throw that engineer on a plane with a laptop. How hard could it be to fix these problems? If that plane ticket is a little pricey, oh well, cost of doing business, right?

Hope that engineer hotfixes things quickly, we have a new version to work on!

Friday, May 16, 2008

I've got a bike, you can ride it if you like

Wow, I went to the well for that title...

Anyways, I biked into work today. 16km, according to The Google. The hill on the way out of St. Jacobs is a lot longer on a bike than in a car, but it will a nice coast home this afternoon.

Conversation of the day

Me: I have way too much blood in my alcohol system.

Paul: You say that a lot on Fridays.

Me: Yeah, but just until 10:00am, when I start drinking. I heard red wine is good for me, but I can't always drink a glass a day, so I save up for the weekend and have it all at once.

Jared: Alcoholics go to meetings, you're just a drunk.

Me: No, alcoholics are people who know they have a problem. I have a hobby.

Thursday, May 15, 2008

We have both kinds of music, country and western

I have a wide variety of music in my collection, mostly classic rock, some blues, some jazz, some classical, some new stuff (and by new stuff, I mean anything post-1990). I do not, however, have any country. Or western. Can't stand the stuff.

The radio station I listen to in the morning plays the occasional promo for their two sister stations, one of which is a country station. This morning they played a clip of the song, "I'd like to check you for ticks". I kid you not.


I'd like to see you out in the moonlight
I'd like to kiss you baby way back in the sticks
I'd like to walk you through a field of wildflowers
And I'd like to check you for ticks.

Wednesday, May 14, 2008

Scripting an application using the VBCodeProvider

As I explained recently, our application has a scripting capability built in. We use VB.NET as the scripting language, and we've implemented the scripting using the Microsoft.Vsa script engine.

We're still running on Visual Studio.NET 2003, which is the 1.1 framework, so until recently we've been fine with this solution. Now that we're starting to realize that we're five years and two generations behind the .NET technology, we need to step into the light. The Vsa scripting is obsolete, and has been since VS.NET 2005, so we need to replace it.

The direct replacement, according to Microsoft, is to use the compilers directly, either C# or Visual Basic. The only problem I've found with this is that it's not a simple thing to provide the scripts with access to the application objects.

What I'd like to do is have a script like this:
Option Strict Off

Imports System
Imports System.Windows.Forms

Module Script
Sub Main()
ScriptDisplay.ClearDisplay()
End Sub
End Module

and be able to compile it. The way this works in the Vsa world is that we can use the GetGlobalInstance method to provide an object that implements an interface. In the example above, I have an IScriptDisplay interface with a ClearDisplay() method. I can pass in a class that implements this method, and the script can call it.

After a few days of playing with this, I've figured out how to achieve the same thing without using the Vsa scripting engine. It's a little dirty, but it works. There are two huge wins with the new way of doing this. First of all, none of the existing scripts that are out in the real world will need to change. That's a big one, since we don't track which sites are writing scripts, and it's a pain in the ass to edit every script, for every user in the system. Second, it would be a very simple change to allow us to support C# scripts in the future (or COBOL.NET, or RPG.NET, or whatever.NET).

Here's the nuts and bolts. I've cut out the irrelevant stuff, like error checking. :)

First, create the compiler.
VBCodeProvider vbCompiler = new VBCodeProvider();
ICodeCompiler iCodeCompiler = vbCompiler.CreateCompiler();

Then, set some compiler options. You may notice that the GenerateInMemory option doesn't quite work as advertised. It still writes out the assembly to disk, at least in .NET 1.1. I'm using the TempFiles property to tell the compiler to put the generated assembly somewhere safe.
CompilerParameters compilerParams = new CompilerParameters();
compilerParams.GenerateExecutable = false;
compilerParams.GenerateInMemory = false;
compilerParams.IncludeDebugInformation = false;

compilerParams.CompilerOptions =
"/rootnamespace:SuperMegaCorp.Scripting";

compilerParams.TempFiles = new TempFileCollection(
System.IO.Path.GetTempPath(), false);

Now, add references to any assembly you might need to access from your scripts.
compilerParams.ReferencedAssemblies.Add("SuperMegaCorp.dll");
compilerParams.ReferencedAssemblies.Add("System.dll");
compilerParams.ReferencedAssemblies.Add("System.Drawing.dll");
compilerParams.ReferencedAssemblies.Add("System.Windows.Forms.dll");
Now the tricky part. Since I don't want to force every script writer out there to change every script they've written, I need to change the script for them.

To pass the application objects into the script, I'm going to edit the script on the fly. We store these scripts in a database, and they're retrieved by the client and passed into this script method that we're in now.

We need to find the Sub Main(), and insert the scripting objects. In this example, "script" is a string which contains the script text. I've also just shown one object being added, but in reality there are four.
int pos = script.ToUpper().IndexOf("Sub Main(".ToUpper());

script = script.Insert(pos + "Sub Main(".Length,
"ByRef ScriptDisplay as IScriptDisplay");

If this all works, we should have gone from this:
Sub Main()
to this:
Sub Main(ByRef ScriptDisplay as IScriptDisplay)
Now we can compile the script.
CompilerResults results =
iCodeCompiler.CompileAssemblyFromSource(
compilerParams, script);

If that works, all we have left to do is find the Main method and call it, with the application object passed as an argument.
if (results != null && results.CompiledAssembly != null)
{
// look for a method called "Main"

Type[] types = results.CompiledAssembly.GetTypes();

foreach (Type thisType in types)
{
MemberInfo[] members = thisType.GetMember("Main");

if (members.Length > 0)
{
// call it, with the script objects
// passed in as arguments

object[] args = new object[] {
(IScriptDisplay)
scriptInterfaces["ScriptDisplay"] };

thisType.InvokeMember("Main",
BindingFlags.Static |
BindingFlags.Public |
BindingFlags.InvokeMethod |
BindingFlags.IgnoreCase,
null, null, args,
CultureInfo.InvariantCulture);

break;
}
}
}

Tuesday, May 13, 2008

It's Community Helper week

This is Community Helper week at my daughter's school, and I had the chance yesterday to spend half-an-hour talking to the Junior Kindergarten class about my job, and how I help people in the community.

I brought a couple of sheets of film and a laptop with the PACS client on it. The kids were surprisingly good at identifying arms and legs in the exams I showed them. I thought it best to stick with the simple stuff - no fetal ultrasounds, as I didn't want to leave the teacher answering "where do babies come from?" all day.





They loved the angio exam I showed, when they could see what a beating heart looks like. One little girl said, "I feel my heart all the time, but that's the first time I ever saw one BEEPING!"

Mmm, pencil shavings

I knew there was a reason I don't like jelly beans that much...

From this week's News of the Weird:

A highlight of this year's Easter promotion by the Jelly Belly company (as additions to its 50 standard flavors) was its surprise BeanBoozled boxes, with odd tastes and non-standard colors. Although garlic beans, buttered-toast beans and cheese pizza beans are no longer available, connoisseurs can sample jelly beans made to taste like pencil shavings, ear wax, moldy cheese and vomit. A Jelly Belly spokeswoman told Newhouse News Service in March, "There are 20 flavors in each little box ... so you don't know what flavor you are tasting ... coconut or baby wipe." [Cleveland Plain Dealer, 3-22-08]

Friday, May 09, 2008

Happy, happy, happy, happy...

Last call for Mother's Day, everyone. And guys, don't forget, as Raymond Chen points out, that if you have kids your wife is a mother too.

I have to say, I find the period of time from late December to early May to be stressful. Birthday, Christmas, Valentine's Day, Easter, Mother's Day and Anniversary all fall in a space of four-and-a-half months.

This year, Mother's Day and our Anniversary hit on the same day. Seventeen years it is, now. That sure flew by.

And now I can't stop singing this...

Thursday, May 08, 2008

Scripting a .NET application, with COM

This week's "Software Problem of the Week" is brought to you by Visual Basic. Not any old VB, but shiny new VB.NET. Now, I'm a VB guy from waaay back, but I've been firmly in the C# world for five years. I thought there was no going back, but I was wrong.

Our application has a scripting language built in. We're using VB.NET for this, since it's easy to learn, easy to understand, and powerful enough to script the application. A requirement has come up recently that we expose the application scripts to a COM object. The COM object is home-grown Visual Basic ActiveX EXE, that basically just holds a bucket of data in static class variables.

The problem is, the VB.NET script that's running in our application needs to get a reference to the COM server at runtime, and hook up to events from it, so that we can support the full API it has.

I scoured The Google for a while, looking for someone who had done this, and finally found one:

http://www.codeproject.com/KB/cs/zetalatebindingcomevents.aspx

First step, use Reflector to take a look at the interface for the COM interop assembly. I've cut out all but one of the methods, but you'll get the idea - they're all pretty much the same.
Public Interface __context_server
Sub StudyIdentifierChange(<[In](), _ MarshalAs(UnmanagedType.BStr)> ByVal Study_id As String)
End Interface

Next step, create a class that implements this interface.
Class EventSink
Implements __context_server

Public Sub StudyIdentifierChange(ByVal Study_id As String) _
Implements __context_server.StudyIdentifierChange
MessageBox.Show("study id changed: " + Study_id)
End Sub
End Class
Now, create the class for the form that we're going to show from the script.
Public Class Form1
Inherits System.Windows.Forms.Form

Dim connectorClass As Object
Dim sink As EventSink
Dim cookie As Integer
Dim connectionPoint As UCOMIConnectionPoint

Public Sub New()
MyBase.New()
InitializeComponent()

' look up the types for the two COM classes we need

Dim connectorClassType As Type = _
Type.GetTypeFromProgID("mitra_context_server.connector_class")

Dim contextServerType As Type = _
Type.GetTypeFromProgID("mitra_context_server.context_server")

' create the connector class

Dim connectorClass As Object = _
Activator.CreateInstance(connectorClassType)

' get the reference to the class that holds the static data

Dim contextServer As Object = _
connectorClassType.InvokeMember("ContextServer", _
BindingFlags.GetProperty, Nothing, connectorClass, Nothing)

' create a connection point container

Dim connectionPointContainer As UCOMIConnectionPointContainer = _
CType(contextServer, UCOMIConnectionPointContainer)

' find the connection point for the guid of our COM server

Dim guid As New Guid("192AD08F-1213-11D4-8756-00500464D554")

connectionPointContainer.FindConnectionPoint(guid, connectionPoint)

' attach the class that handles events

sink = New EventSink
connectionPoint.Advise(sink, cookie)
End Sub

Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
If disposing Then
If Not (components Is Nothing) Then
components.Dispose()
End If

' unhook context server

connectionPoint.Unadvise(cookie)
Marshal.ReleaseComObject(connectionPoint)
End If

MyBase.Dispose(disposing)
End Sub

Private components As System.ComponentModel.IContainer

Private Sub InitializeComponent()
Me.SuspendLayout()
'
'Form1
'
Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
Me.ClientSize = New System.Drawing.Size(292, 350)
Me.Name = "Form1"
Me.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen
Me.Text = "Form1"
Me.ResumeLayout(False)
End Sub
End Class

And finally, the rest of the script. These scripts are run using the VSA scripting, which is now obsolete post-.NET 1.1. Something else to look forward to will be coming up with a way to run these in a .NET 3.5 world, if we ever get there.
Option Strict Off

Imports System
Imports System.Windows.Forms
imports system.collections
imports agfahc.pacs.framework
Imports System.Reflection
Imports System.Runtime.InteropServices
Imports System.Runtime.CompilerServices

Module Script
dim WithEvents form as Form1

Sub Main()
form = new Form1()
form.Show()
End Sub
End Module

Wednesday, May 07, 2008

Boys and girls

My seven-year-old had her first communion on Sunday, along with the rest of her Grade Two class. The difference between boys and girls at that age is amazing.

The girls all came in wearing flowing white dresses, with lots of lace trim, and jewelry. They immediately gathered in a huddle to compare hairstyles and earrings and bracelets and necklaces.

The boys staggered in with hair unbrushed, shirts untucked, some in running shoes, some wearing suits that were three sizes too big (thanks, big brother!) Not a single one seemed to care.

Friday, May 02, 2008

You are young and life is long

In Ontario, the legal age to buy alcohol is 19.

I got asked for ID at the Beer Store yesterday. "Carded," for those of you in other jurisdictions.

I stared at the guy for a second, pulled out my driver's license and handed it to him. He read it, eyes widened, and handed it back, muttering, "Sorry."

I said, "That's ok."

He said, "You know, I'm supposed to ask anyone who looks under 30 if they have ID. But I guess I was WAY OFF."

I said, "Dude, you don't have to rub it in."

I have always looked younger than I am, I suppose. But TWENTY YEARS younger? C'mon, when I was 18 years old Ronald Reagan was president. Jim Bakker was getting jiggy with Jessica Hahn. Gary Hart was getting jiggy with Donna Rice.

You get the idea.