TLB to XML
September 7th, 2006

I wrote a small program that generates xml from a type library. The type library can be a .tlb, or embedded as a resource in a PE (.dll, .ocx, .exe, etc).

I’m using this as a build tool—basically it’s the glue that makes a big project using C#, C++, VB6, WiX and NAnt hold together.

The source can be downloaded from this link. This doesn’t grab everything from the tlb, but it’s pretty simple and not hard to extend.

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

    Copyright (c) 2005, Dan McKinley
    All rights reserved.

    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions
    are met:

    -    Redistributions of source code must retain the above copyright notice,
        this list of conditions and the following disclaimer. 

    -   Redistributions in binary form must reproduce the above
        copyright notice, this list of conditions and the following
        disclaimer in the documentation and/or other materials
        provided with the distribution.

    -    The name of Dan McKinley may not be used to endorse or promote products
        derived from this software without specific prior written permission. 

    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
    CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
    WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
    OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
    BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
    EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
    TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
    ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
    TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
    THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
    SUCH DAMAGE.

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

using System;
using System.Collections.Generic;
using System.Text;
using isvc = System.Runtime.InteropServices;
using System.IO;
using System.Xml;
using System.Runtime.InteropServices.ComTypes;

namespace tlbspit
{
    class Program
    {
        [isvc.DllImport("oleaut32.dll", PreserveSig = false)]
        static extern void LoadTypeLib(
            [isvc.MarshalAs(isvc.UnmanagedType.LPWStr)] string szFile,
            [isvc.MarshalAs(isvc.UnmanagedType.Interface)] ref ITypeLib pLib);

        static int Main(string[] args)
        {
            if (args.Length == 0)
            {
                Usage();
                return -1;
            }
            try
            {
                string path = string.Join(" ", args);
                using (XmlTextWriter w = new XmlTextWriter(Console.Out))
                {
                    w.Formatting = Formatting.Indented;
                    Run(path, w);
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
                return -1;
            }
            return 0;
        }

        static void Run(string path, XmlWriter w)
        {
            w.WriteStartElement("library");
            ITypeLib tlb = null;
            LoadTypeLib(path, ref tlb);
            WriteTlbAttribs(tlb, w);
            int ct = tlb.GetTypeInfoCount();
            for (int i = 0; i < ct; i++)
            {
                ITypeInfo t = null;
                tlb.GetTypeInfo(i, out t);
                WriteType(t, w);
            }
            w.WriteEndElement();
        }

        static void WriteType(ITypeInfo t, XmlWriter w)
        {
            w.WriteStartElement("type");
            ITypeInfo2 t2 = (ITypeInfo2)t;
            TYPEKIND k;
            t2.GetTypeKind(out k);
            w.WriteAttributeString("name", NameOf(t));
            w.WriteElementString("guid", GuidOf(t));
            w.WriteElementString("kind", k.ToString());
            w.WriteEndElement();    // type
        }

        static unsafe string GuidOf(ITypeInfo t)
        {
            IntPtr pAttr = IntPtr.Zero;
            try
            {
                t.GetTypeAttr(out pAttr);
                TYPEATTR* attr = (TYPEATTR*)pAttr;
                Guid g = attr->guid;
                return g.ToString();
            }
            finally
            {
                if (pAttr != IntPtr.Zero)
                    t.ReleaseTypeAttr(pAttr);
            }
        }

        static string NameOf(ITypeInfo t)
        {
            string name = null, doc = null, hlpfile = null;
            int i = 0;
            t.GetDocumentation(-1, out name, out doc, out i, out hlpfile);
            return name;
        }

        static string NameOf(ITypeLib tlb)
        {
            string name = null, doc = null, hlp = null;
            int hc = 0;
            tlb.GetDocumentation(-1, out name, out doc, out hc, out hlp);
            return name;
        }

        static unsafe void WriteTlbAttribs(ITypeLib tlb, XmlWriter w)
        {
            IntPtr pAttr = IntPtr.Zero;
            tlb.GetLibAttr(out pAttr);
            try
            {
                TYPELIBATTR* attr = (TYPELIBATTR*)pAttr;
                w.WriteAttributeString("guid", attr->guid.ToString());
                w.WriteAttributeString("version",
                    string.Format("{0}.{1}", attr->wMajorVerNum, attr->wMinorVerNum));
                w.WriteAttributeString("name", NameOf(tlb));
            }
            finally
            {
                if (pAttr != IntPtr.Zero)
                    tlb.ReleaseTLibAttr(pAttr);
            }
        }

        static void Usage()
        {
            Console.WriteLine(@"
TLBSPIT by Dan McKinley

  Writes out (some) of the attributes of a type library as xml to
  standard output.

  Usage: tlbspit.exe <type library file>

  The file can be a dll/ocx/etc. or a tlb. It can optionally include a
  slash followed by the number of a resource.

  Examples:
    tlbspit foo.tlb
    tlbspit bar.dll\3
");
        }
    }
}

Wheels I Have Reinvented
September 3rd, 2006

This site has been silent for quite a while. My lame excuse for this is as follows.

Sometime around the beginning of the summer I started trying to learn functional programming in earnest. I had some exposure to this in school (ML, if I remember correctly), but not nearly enough, as it turns out. C#’s anonymous methods provided the “eureka” moment that made me want to revisit the topic. I had also been asked to contribute to The Hub, but I had felt like I was lost in the world of F# and that fizzled out altogether (maybe now I might have a few things to say).

So that’s my story—I have been exploring strange new(-ish) topics and I’ve found that I haven’t had anything insightful to contribute to the world.

Recently I’ve been trying to complete a few large projects in Common Lisp. I’m pleasantly surprised to find myself saying that most or all of the highfalutin crap that is spouted about Lisp is true. As to why it isn’t the most-used language in the world right now, I am too much of a novice to speculate. But purely from a language standpoint it is certainly making all of the others feel, well, tedious.

Lisp seems to supersede every single neat trick I have ever come up with programming professionally. It either contains them outright, or it makes them trivial through macros or completely unnecessary for some other reason. Let me give you one simple example.

Writing applications in C#, I’ve occasionally had a singleton or some kind of global object that I wanted to be redefined within a certain call stack. Let’s say I have this API:

class Foo
{
    public static Foo Default { get; }
}

And I want this to be redefined for a little while while I call some method called Bar(). Impersonation of users is one concrete example of where this is useful.

My neat-trick solution to this problem is to make the implementation of Default use thread local storage (and maybe fall back on a static instance too), then create a “scope” class that allows me to write code that looks like this:

// Original value of SomeOtherFoo out here
using(FooScope s = new FooScope(someOtherFoo))
{
    // In here, Foo.Default gives someOtherFoo
    Bar();
}
// Original value of SomeOtherFoo out here

(I am skipping over a lot of irritating details here and let me be clear, I don’t claim to have invented this. I just mean that I burned a lot of hours arriving at it basically independently of the ten million other programmers who probably recognized the same need and did the same thing. It is purely my own ignorance and stupidity that made “re-discovering” it necessary.)

Anyway, I thought that was clever at the time. But with dynamic scope (as opposed to global scope, which is what static fields have in C#) Lisp has this as part of the language. Writing this:

(let ((*default-foo* some-other-foo))
    (bar))

Is basically the same thing with no effort. The Lisp syntax has the added advantage of working with objects other than the ones I author myself.

This reminds me of the first time I was exposed to .NET events. I realized that I had implemented the same god-damned thing in other languages a handful of times (albeit as a less-seasoned programmer) and it never worked as beautifully or as simply.

Naturally, CLOS seems to have some features that I now realize I have been mimicking imperfectly with events. It also has features that will probably replace events in many of the problems where I have applied them. That is an altogether different story.


First Impressions of CCR
June 24th, 2006

I got a chance to mess around with the Concurrency and Coordination Runtime (CCR) bits recently. Before I get into that, first check out this real-life code I wrote this week.

public static class NotificationQueue
{
    private static Queue<Notification> _queue;
    private static Semaphore _work;

    static NotificationQueue()
    {
        _queue = new Queue<Notification>();
        _work = new Semaphore(0, int.MaxValue);
    }

    public static void Enqueue(Notification n)
    {
        lock (_queue)
        {
            _queue.Enqueue(n);
        }
        _work.Release();
    }

    public static Notification Dequeue()
    {
        _work.WaitOne();
        lock (_queue)
        {
            return _queue.Dequeue();
        }
    }
}

The idea here is to post notifications to the queue from many threads, and have a single notification thread sending the messages out from something like this:

    private static void NotifyThreadProc()
    {
        while(!Abort())
            NotificationQueue.Dequeue().Send();
    }

The documentation for CCR is pretty sparse at this point, limited to just this paper, the Channel9 video, and this Wiki. Some of the particulars seem to have changed significantly, but I was able to figure out how to replicate my explicitly-threaded notification queue:

public class NotificationService : CcrServiceBase
{
    private Port<Notification> _port;

    public NotificationService()
        : base(new DispatcherQueue("foo"))
    {
        _port = new Port<Notification>();
        Activate(Arbiter.Receive(true, _port,
            delegate(Notification n)
            {
                n.Send();
            }));
    }

    public void Post(Notification n)
    {
        _port.Post(n);
    }
}

That’s pretty awesome: notice that no explicit locks or waits are necessary, and I don’t need to write the NotificationThreadProc or the code required to start it. I just need to make a NotificationService and start posting to it.

I can’t say it’s the most immediately comprehensible API I’ve ever seen, but hopefully that will change with more documentation and some polish. It’s also possible that I am just warped from years of using the lower-level concepts. Overall this is awfully impressive for a library.