Posts Tagged ‘VB’

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

A Meandering Static Locals Story

I was writing some C# code the other day and attempted to use a static local variable. This may only be because I've been spending so much time with C++ recently, but I was very surprised to find that it didn't work. "That's a shame," I thought. It's not as though I use static locals every day. I've been using C# for a few years and I was just getting around to discovering that it doesn't support them, after all. Nevertheless, I would say I use static locals exactly when I need them.

I was sure that VB.NET supported static locals. I thought that perhaps an Object Orientation zealot decreed that they weren't going into C#, even though that would not be typical of the attitude exhibited in the most recent C# whitepapers. As it turns out, that's not what happened at all. Static locals are a compiler feature, and not something implemented on the CLR level. Paul Vick explains:

Implementing static locals in the compiler was a gigantic pain in the ass, and we had a lot of arguments about whether they were worth the effort. There are times that I haven't been entirely convinced about it, but I think my thoughts on this are changing…

I suppose I have already conceded that there are more important things to spend time on. In the same post Paul discusses the relative merits of the keywords in either language.

We also felt that “Shared” is a lot more descriptive than “Static,” and I wonder whether C# would have chosen that term if they hadn't been stuck with the legacy of C, but… This is probably the most painful keyword divergence between the two languages in terms of documentation.

Something of a silly discussion, I think, since VB is teeming with its own historical oddities (to wit: Dim intArray(0) As Integer). But this does remind me of a story.

A while back I was debugging an odd issue where users of a web application were seeing each other's data "randomly." Our application has a set of links on one side of it that are customized to relate to the entity the user is viewing. A typical bug would be:

The top half of the links relate to entity 'A' (which is the right one), but the bottom half of the links relate to entity 'B,' which I was not looking at.

Compared to some other bugs I have worked on, this one was a piece of cake. I figured out pretty quickly that the developer of the control in question had written a big section of variables that looked like this:

Public Shared A As Integer
Public Shared B As String
Public Shared C As String
' …

These were used in the process of rendering the hyperlinks. I asked the developer why he'd done this, but he didn't know why. He had just seen the word "Shared" associated with member declarations in the past and thought that he needed to put it there.

He was typically more informed about keywords than this, so telling him that Shared was like "static" in other languages brought instant recognition. In fact he changed the code so that it looked like this (I'm doing this from memory, but I think this is pretty faithful to the original treasure):

' Shared == Static!! Shared variables are static!
Public A As Integer
Public B As String
Public C As String
' …

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

WTF

This Daily WTF reminded me of a guy I used to work with who would use this pattern in every single function he wrote.

Public Const cSTRINGFOO As String = "foo"

Public Function Foo() As Boolean
    Dim ret As Boolean = False
    Try
        Console.WriteLine(cSTRINGFOO)
        ret = True
    Catch ex As Exception
        ret = False
        Throw New Exception("", ex)
    End Try
    Foo = ret
    Exit Function
End Function

I’m not sure what he was expecting to happen here, but there were some clues:

  • Every function he wrote returned true or false to indicate that it had successfully completed without an exception.
  • Every exception was wrapped in another, except where he forgot and swallowed the exceptions.
  • He would always use constants for string literals, even if he only used the literal once (like above), and even if the constant was much longer than the literal.

Mind you, I discovered this self-imposed standard a while after he quit, so I never really got to the bottom of it.

Remoting Email Thread

Here is an email thread I had recently with some good information in it. Rather than try to condense it, I’ll just post the thread.

No True Object Programmer, Part Deux

Edit (2/28): What I have to say about raising an event asynchronously in VB is very incorrect. If you got here by googling “raise event async VB“ (or thereabouts), do not listen to me. Read Bill McCarthy's comment about it.


Well, Bill loves VB.NET, and knows a lot about it. I am in a slightly different camp, in that I (arguably) know a lot about it because I am forced to use it at work, but I haven’t grown to love it very much.

But as to your difficulty in parsing VB.NET code, well as you said, VB.NET is actually more natural. It is probably more that you need practice reading and writing the code.

I concede that this is probably true. I’ve been staring at C/C++/Java/C# since I was 11 years old, so it’s quite likely that’s the only reason I find it more natural. It doesn’t really help that I switch between VB.NET and C# projects on an almost hourly basis these days. I’ll jump to the VB project and start typing:

StringBuilder sb = …

Whoops. Wrong one. Apparently I can’t make the jump as effortlessly as the woman shouting into a cellphone on the train next to me last week weaved an intricate and impressive unbroken stream of Spanish/English (“Spanglish?”).

So yes. Readability is probably a personal problem.

This started out, I guess, as a discussion about which language was “more OO.” That’s may be a vague metric, so I’ll try to limit the discussion here to which language lets you accomplish the task at hand in the simplest possible way with minimal connectivity and shared knowledge between your objects.

In the case of events, I think that at best, this is a draw. This is an opinion.

VB wins when it comes to a few useful abstractions: the RaiseEvent keyword, and declarative event handling (the WithEvents/Handles keywords).

In a comment on Bill’s blog, I referred to these as “leaky abstractions” (to borrow shamelessly from Joel). What I mean by that is, they work beautifully in the simple cases, but in more sophisticated situations they break down and you’re forced to understand what is actually going on. VB may or may not leave you with a way out, once you’ve done the learning bit.

My first example here was raising an event asynchronously. Here is code that accomplishes calling event handlers on another thread in C#.

internal class MyClass
{
    public event EventHandler MyEvent;

    protected virtual void OnMyEvent(EventArgs e)
    {
        this.MyEvent.BeginInvoke(this, e, null, null);
    }
}

Here is code that accomplishes the same task in VB.NET.

Friend Class SomeClass
    Public Event MyEvent As EventHandler
    Private Delegate Sub EventRaiserDelegate(ByVal e As EventArgs)

    Protected Overridable Sub OnMyEvent(ByVal e As EventArgs)
        RaiseEvent MyEvent(Me, e)
    End Sub

    Protected Overridable Sub OnMyEventAsync(ByVal e As EventArgs)
        Dim del As EventRaiserDelegate = AddressOf OnMyEvent
        del.BeginInvoke(e, Nothing, Nothing)
    End Sub
End Class

Bill’s correct, you can raise an event asynchronously in VB.NET, albeit in a roundabout way. Unless there's a radically different method I haven't thought of, I need to write and then call OnMyEventAsync, which calls a traditional event raiser on another thread.

Here is where I think the claim that VB is “more OO” breaks down. The representation of the event as a list of functions (which is what C# gives you) was significantly simpler for us in this case. We got the job done with less code, so in my mind C# wins the OO battle here.

As to events in Vb.NET, I'm sorry but there is no "inadvertant GC rooting". I don't know who told you that, but you/they were sorely mislead.

On serialization, Vb.NET has absolutely no problems with XML serialization. There is however an issue with binary serialization and MarshalByRef classes. The real problem is MarhsalByRef classes, eg a Windows.Form, cannot be binary serialized. If you need to work around that, you can do so in Vb.NET 2005, and more elegantly than C#.

Poor terminology on my part, I guess. What I was referring to with “GC rooting” was the situations where a programmer attempts to dereference an expensive object so that it may be collected. A common mistake is leaving events wired up (and hence, a reference that has a gc root).

Is that any more difficult to deal with in VB than it is in C#? No, not really. Touché. But am I justified in calling an event a “leaky abstraction” if they require a deeper understanding of function pointers and GC in order to keep your program from hogging memory? I think so.

So that leaves the last sitch: serialization. MarshalByRef is actually not what I was thinking about, but it’s closely related. MarshalByRefObjects are at least serializable, and unless you are persisting to a storage medium you will succeed in serializing a graph if your only mistake is having a ObjRef going along for the ride.

Objects that are not [Serializable()] at all are a bit more problematic. You’ll get an exception which will be difficult to track down if you have event handlers wired up to any of these. C#’s got an easy way out (google it; my post is getting really long, dammit). The hacks VB will force you into in some cases is likely to make you cry. I know it made me cry.

So which language is “more OO?” That’s impossible to answer objectively, and it probably makes no sense to even try, but I’ll tender a personal opinion.

My feeling is that VB gives you some keywords that are useful for simple OO. Unfortunately, it obscures some of the underlying nuts and bolts, and this gets in the way of your own attempts at OO abstraction. C#, on the other hand, is a little better at ensuring that the realities of the CLR are quickly accessible if you need them.

In an ideal world, we’d have both in one language. But forced into a choice, I’ll opt for seamless access to the guts. Keeping my own objects as loosely coupled as possible is what I’m most interested in doing.

No True Object Programmer

Did you love the No True Scotsman argument? Well, get ready for the arguments!!

Explicit Interface mapping. This is incredibly cool. Unfortunately a lot of people just don’t get how important this concept is to true OO.

He continues:

As to VB.NET over C#, VB.NET is actually a far more OO language when you look at the actual implementation details.

(did I hear a record scratch?)

Far more?

At the risk of Ad Verecundiam, I’ve actually been on the phone with a Microsoft employee who said these words: “C# is a much better choice if you intend to do object development.”

Bill doesn’t expand on that argument really, other than the appeal of the End keyword. It would be a silly thing to get into an argument about, but my feeling is that End XXX makes VB code less readable. I would like to see more reasons why this could be true.

I apologize for any haughty, dismissive tone. I wrote this while working on a brick of Parmigiano Reggiano and sucking down a bottle of Chianti, so it just kind of came out that way.

Thread Safety in VB-2003 Events

Brad Abrams had a recent post about how the usual way of raising an event in C#:


protected virtual void OnMyEvent(EventArgs e)
{
    if(MyEvent != null)
    {
        MyEvent(this, e);
    }
}

is not threadsafe. Purely out of curiosity, I wondered if VB handles this the correct way:


protected virtual void OnMyEvent(EventArgs e)
{
    EventHandler handler = MyEvent;
    if(handler != null)
    {
        handler(this, e);
    }
}

Since VB abstracts this away with the RaiseEvent keyword. Unfortunately it doesn’t seem to, as this code:


Protected Overridable Sub OnMyEvent(ByVal e As EventArgs)
    RaiseEvent MyEvent(Me, e)
End Sub

Compiles to this:


.method family newslot virtual instance void OnMyEvent([mscorlib]System.EventArgs e) cil managed
{
      // Code Size: 22 byte(s)
      .maxstack 8
      L_0000: ldarg.0
      L_0001: ldfld [mscorlib]System.EventHandler VbConsoleApp.Test::MyEventEvent
      L_0006: brfalse.s L_0015
      L_0008: ldarg.0
      L_0009: ldfld [mscorlib]System.EventHandler VbConsoleApp.Test::MyEventEvent
      L_000e: ldarg.0
      L_000f: ldarg.1
      L_0010: callvirt instance void [mscorlib]System.EventHandler::Invoke(object, [mscorlib]System.EventArgs)
      L_0015: ret
}

Which has a race condition–it should store the delegate in a local. Generally, my answer to all of these questions is “it is fixed in 2005” – so I’m going to just assume that that is the case here.

Re: “Best Method Names Ever”

From Brad Abrams.

I would have to say the best function name ever is definitely Function Batman.

But if we’re limiting it to Microsoft products, I guess I’d have to say I’m often surprised by the number of internal framework methods that are labeled “hacks.”

.NET Framework hacks

The VB Compiler and Late Binding

Something interesting I came across today. Two seemingly identical loops in two different languages:

for(int i = 0; i <= 10000000; i++)
{
    TimeSpan t2 = DateTime.Now.Subtract(t);
    // assume we do something more interesting in
    // a real program...
}
Dim t As DateTime = DateTime.Now()
For i As Integer = 0 To 10000000
    Dim t2 As TimeSpan = Now.Subtract(t)
    ' assume we do something more interesting in a
    ' real program...
    t2 = Nothing
Next

There is a difference, obviously—the VB programmer has attempted to set the TimeSpan t2 (a ValueType) to Nothing. The VB compiler doesn’t prohibit you from doing this, as the C# compiler would if that programmer had tried to set t2 = null.

Programmers at my company (other than me) do this constantly. The reason is that they got used to doing it to every object variable in VB6. Whether or not that was actually necessary, I don’t know. It’s beyond the scope of this post, anyway.

At any rate, it shouldn’t matter. At most, it should end up just being one instruction and might even get tossed out by the JIT compiler. Right? Wrong.

On average, the C# loop executes more than 5 times faster. Roughly 5 seconds for VB versus roughly one second for C# on my machine. When you remove the extra line in VB, the times are comparable. If we replace TimeSpan with a reference type but leave the extra line, the times are comparable.

Why? Looking at the MSIL generated when we have gives us this in Release mode:

; Equivalent to GetType(TimeSpan)
L_001d: ldtoken [mscorlib]System.TimeSpan
L_0022: call [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle([mscorlib]System.RuntimeTypeHandle)

; This is the late-binding way of creating an empty TimeSpan.
L_0027: call object [mscorlib]System.Activator::CreateInstance([mscorlib]System.Type)
L_002c: unbox [mscorlib]System.TimeSpan
L_0031: ldobj [mscorlib]System.TimeSpan
L_0036: stloc.2

Ok, so I can see now what the compiler is trying to do. Whereas the C# compiler would make us explicitly go looking for the TimeSpan.Zero field if we wanted to reset the value of a variable, the VB compiler is trying to help us out by creating an empty structure for us.

It would have saved us a little trouble, if that was at all what we were intending to do. In my case, it just happened because a cargo-cult programmer is used to typing it. Another problem is that it happens where, even understanding this behavior, you would still not expect it. Consider:

' Reminder - DictionaryEntry is a struct.
For Each entry As DictionaryEntry In hash
    ' ...
Next

This ends up being equivalent to setting entry = Nothing with each iteration. You’d therefore get the same late binding IL and this loop is many times slower than the “same” loop in C#.

I’m not a language or compiler designer, but here’s what I would like to see done (in order of preference).

  1. Disallow this with Option Strict On, like C# does currently (a breaking change, obviously).
  2. Give a compiler warning where it will happen.
  3. Do a better job of publicizing this kind of behavior. I can’t find any documentation on it.