Posts Tagged ‘Build’

TLB to XML

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
");
		}
	}
}

My Tentative Approval of Visual Studio 2005

Despite some initial bad press, my impression so far is that Visual Studio 2005 is a pretty nice product. I would qualify that by saying that I haven't yet used it to work on a massive web project, and as we all know web projects were definitely Visual Studio 2003 at its worst. (After two years, I will NOT use Visual Studio 2003 for web projects. I refuse. They were broken. I'm 100% text editor and NAnt from the command line now.)

I am loathe to be seen as a cheerleader, and rest assured I could point to many things about Visual Studio in general and VS2005 specifically that I can't stand. But somebody out there deserves some credit for the fact that startup performance seems to have been drastically improved. In my informal test ("One Mississippi .. two Mississippi"), VS 2005 outperforms 2003's startup by an order of magnitude. Two seconds compared to thirty seconds on the same machine. That's certainly nothing to sneeze at.

You click on the icon, it opens. All software should work this way. I'm looking directly at you, OpenOffice, Trillian, and every single product developed by Adobe. While I'm on the subject, never allowing your program to close is not an acceptable solution to this problem.

I am also a fan of this:

The "close all but this" button replaces these steps:

  1. Ctrl+Shift+S
  2. Ctrl+F4, hold for five seconds
  3. Find the original file and reopen it

NAnt Task to Enforce VB.NET Project Settings

Here is a NAnt task I am using to enforce VB.NET project settings during a build. This is useful if you can’t use <vbc> to compile your projects.

Usage example:

  <vboptions compare="Binary" strict="true" explicit="true"
      path="${projectfile}"
      if="${path::get-extension(projectfile) == '.vbproj'}" />

The code:

/// <summary>
/// Enforces specific VB settings on a particular project.
/// </summary>
[TaskName("vboptions")]
public class VbOptionsTask : Task
{
    private string _path;
    private bool _strict;
    private bool _explicit;
    private string _compare;

    [TaskAttribute("path", Required=true)]
    public string Path
    {
        get { return _path; }
        set { _path = value; }
    }

    [TaskAttribute("explicit", Required=false)]
    public bool Explicit
    {
        get { return _explicit; }
        set { _explicit = value; }
    }

    [TaskAttribute("compare", Required=false)]
    public string Compare
    {
        get { return _compare; }
        set { _compare = value; }
    }

    [TaskAttribute("strict", Required=false)]
    public bool Strict
    {
        get { return _strict; }
        set { _strict = value; }
    }

    private bool CheckOption(string opt, bool val)
    {
        bool on = (string.Compare(opt, "On", true) == 0);
        return (on == val);
    }

    private void ValidateOption(XmlNode node, string attr, bool val)
    {
        XmlAttribute a = node.Attributes[attr];
        if(a == null || !CheckOption(a.Value, val))
        {
            throw new BuildException(
                 string.Format("The build option '{0}' in project {1} is not set to '{2}.'",
                     attr, this.Path, (val ? "On" : "Off")
                     )
                 );
        }
    }

    protected override void ExecuteTask()
    {
        XmlDocument proj = ProjectFactory.LoadProjectXml(this.Path);
        if(proj == null)
        {
            throw new BuildException("Could not load project xml for: " + this.Path + ".");
        }
        XmlNode settings = proj.SelectSingleNode("//VisualBasic/Build/Settings");
        if(proj == null)
        {
            throw new BuildException(this.Path + " is not an Everett .vbproj file.");
        }
        XmlAttribute c = settings.Attributes["OptionCompare"];
        if(c == null || string.Compare(c.Value, this.Compare, true) != 0)
        {
            throw new BuildException(string.Format("The project {0} does not have Option Compare set to {1}.", this.Path, this.Compare));
        }
        ValidateOption(settings, "OptionStrict", this.Strict);
        ValidateOption(settings, "OptionExplicit", this.Explicit);
    }

    public VbOptionsTask()
    {
        _compare = "Binary";
        _explicit = true;
        _strict = true;
    }
}

Things I need

It would be really great if any of these things existed. Some of them might, but I’ve been unsuccessful in finding any of them.

  1. A utility that finds/cleans files that are in Visual Sourcesafe, but are not found in Visual Studio projects and/or solutions. When a file is ‘deleted’ in VS2003, it’s not removed from VSS. Repeated on a vast scale, this gets annoying and throws a wrench into some other things that I’m trying to do.
  2. A NAnt task that lets me use csc/vbc for all files in a Visual Studio project. I need the command line arguments (ie, /debug:pdbonly) for both of those, and the convenience of the solution task. (Of all of these, it seems like this is the most likely to exist and I just haven’t found it.)
  3. DebugEngine extensions intended for ASP.NET apps. They should be able to show me the pages that are running, the HttpContext for each request, the items that are in session, and so forth.
  4. Someone who can take difficult, complex programming tasks from me. Ideally they would be smart enough that I would trust them implicitly, rather than agonize about the projects on a daily basis.
  5. A Visual Studio project type for managing a number of XML files. By that I mean, a bunch of NAnt scripts. Actually, any tool that has a solution-like treeview and a document outline utility will work.
  6. PowerCollections, but for .NET 1.1. (Yeah, yeah, it might not be too hard to port them myself).
  7. An “Add New Item…” item in Visual Studio that has NO default name set (like “Class1.cs”), NO autogenerated comments, and has internal access by default. If I call the item IAnything, it should figure out that the item is an interface and not a class.

I’ve had some success lately eliciting comments from knowledgeable googlers, and I have high hopes for this post.