Tuesday, June 24, 2008

TypeLib or not TypeLib

That is the question.
Whether tis nobler in the mind to suffer
the slings and arrows of outrageous fortune,
or just use the bloody Registry table?


It seems that Microsoft have effectively deprecated the TypeLib table.

From the msdn docs:

Installation package authors are strongly advised against using the TypeLib table. Instead, they should register type libraries by using the Registry table. Reasons for avoiding self registration include:

  • If an installation using the TypeLib table fails and must be rolled back, the rollback may not restore the computer to the same state that existed prior to the rollback. Type libraries registered prior to rollback may not be registered after rollback.
Additionally, if you compare (using InstEd of course) the _BadRegData tables from early darice.cub files with that in the latest Windows Server 2008 SDK, all the references to TypeLib subkeys have been removed. This means they no longer show up as ICE33 errors. Microsoft really want you to put TypeLib registration into the Registry table.

But why is this? Their justification is that registry keys created/overwritten by the RegisterTypeLibraries action cannot be restored to their previous state upon rollback. With the Registry table entries, the Windows Installer engine guarantees that registry keys modified during an installation are returned to their previous state upon rollback.

However the RegisterTypeLibraries action most likely calls the RegisterTypeLib api. The Windows Installer engine has no control over what that api does, and cannot therefore manage the registry keys so that they are restored upon rollback.

Hence Microsoft's strong suggestion to use the Registry table.

But a closer examination shows that the suggestion solves a problem only when things go wrong, and leave a gaping problem when things go right.

Consider the automobile airbag. It's a fantastic device for saving the average driver's face of average attractiveness in the case of an accident. We are all grateful they exist. The Registry table is the Windows Installer airbag. It saves the registry from deformity in the case of an accident by ensuring that, upon rollback, the registry is restored to its state prior to the installation.

However, imagine that whenever the driver turned the ignition off, the steering wheel whacked them in the face, airbag safely tucked inside it's protective casing.

This is the problem with declaring the typelib table unsafe. Sure, the airbag will save you when you have an accident, something most drivers don't set out to do. But the Windows Installer engine can't save the registry when everything goes well, the package is installed perfectly, and then is uninstalled for whatever reason, regardless of whether registry keys are set via the Registry or the TypeLib tables.

The Windows Installer engine only holds the registry state for the duration of the installation, allowing restoration only upon rollback. Upon successful installation and uninstallation, both the Registry table and the TypeLib table will result in keys/values being deleted, regardless of their state prior to the installation. It's the whack in the face for turning the ignition off.

Addressing the airbag issue, it seems that given they have the code for the RegisterTypeLib api (and code for rollback of the registry), they could supplant it with one that successfully restores the registry table upon rollback, just like the Registry table does. This would provide all the same protection as the Registry table, and not require modification of all the existing msi's out there.

So, should you use the TypeLib table? Well, given that MS haven't done the work to allow successful rollback on the TypeLib table, if you have the tools to automatically generate the Registry table entries, then I can't see why not. But the extra work may not be worth it, considering that the majority of cases where things go as intended (install/unsintall) will suffer the same problems as the minority of cases when things have gone wrong (rollback).

The only caveat to this is that most conflict checking utilities will not look inside a typelib for conflicts between typelibs (e.g. this interface's typelib info is provided by this file, no this file, no this file). In this case, using the Registry table probably provides a more significant benefit than using the TypeLib table (note to software vendors, your packages will likely be used in enterprises where they are conflict managed, so help them out).

But this of course isn't Microsoft's justification, and their justification seems a little weak for such a strong recommendation.

Tuesday, June 10, 2008

What! Are you blind?

I didn't say it out loud, but I was thinking something similar.
Personification of inanimate objects like compilers is daft,
but it feels good. That's my excuse anyway.

Here's the situation.

Given this C++/CLI code:

template< typename T >
ref struct A
{
~A()
{
}
};

ref struct B
{
A<int>^ a;
}

The linker generates this error:
error LNK2020: unresolved token (06000414) A<int>::Dispose

Knowing that in C++/CLI, the destructor syntax generates a Dispose function for ref classes/structs, the usual resolution is to ensure that A class has a destructor defined.

Which is where you say "What! Are you blind?". Because there it is. And no matter how many times you check it to make sure it exists and hit the recompile button, the linker still spits out the same error. I know. I tried a lot of times.

The solution? Change the member declaration in B to:
A<int> a;


The reason? There are conflicting behaviours between the C++ template compilation and the C++/CLI MSIL generator. By declaring a destructor, the MSIL generation insists that the class must have a Dispose method. But the C++ template compilation will not emit definitions of functions in template classes unless the function is actually used.

By using the handle syntax (^), but never manually calling a->Dispose(), the compiler will never emit the Dispose function. Removing the handle syntax, such that the "a" variable exists on the stack (at least notionally), means that behind the scenes the compiler generates the call to a->Dispose() when the variable goes out of scope.

In native C++, the template optimisation is exactly what most programmers want. Why have a function defined if it is never called?

However an MSIL class in an assembly must be fully defined, regardless of what functions are called.

Hence, unless you manually call Dispose, or use the stack based syntax, the compiler never generates the Dispose function for the linker to find, which is invalid for an MSIL class in an assembly.

Of course, the resulting error is useful because I realised that I hadn't managed the object properly. Unfortunately this can only be considered serendipitous, because should many objects of type A exist, but only one of them get Disposed properly (or even improperly), the unresolved external error won't show.

The unanswered question. It seems that there is something special about the Dispose function (or how it is generated with the destructor syntax), because the same is not true of an ordinary function. E.g.

template< typename T >
ref struct A
{
~A()
{
}

void f()
{
}
};

ref struct B
{
A<int> a;
}

This code does not generate an unresolved external error for the f function. Further investigation with ILDasm is required.

Note: This post is relevant to Visual Studio 2005. I am not sure of how VS 2008 behaves.

Friday, June 6, 2008

InstEd 1.5.2.3 Released!

1.5.2.3 has been released. With huge performance improvements, the addition of forward and back navigation, and quite a few bug fixes, it is now better than ever, making packaging even more productive.

Release notes here.
Download here.