Thursday, 29 March 2012

Automatic Reg Free COM Script

Here's a windows batch file I wrote that automatically takes a .NET Assembly who's classes have been suitably decorated with COM visibility tags and embeds all the information necessary for Reg Free COM to work.

UPDATE1: OK, some fixes added. But this one really works.
However, I wrote a command line app to get the version number of an assembly called GetAssemblyVersion.
Source code is at the end of the post, unless you know of a better way, in which case let me know!
It also doesn't work for signed assemblies, you'll need to add another command to add something like %PublicToken% to the client manifest

REM Show usage if no parameters are passed in
IF %1==[] GOTO :NoParams
SET DLLFilename=%1
REM Kill any old manifests, mt won't overwrite one
IF EXIST %DLLFilename%.manifest DEL %DLLFilename%.manifest
REM Get MT to autogenerate a reg free COM manifest from the COM decorated classes in the assembly
mt -nologo -managedassemblyname:%DLLFilename% -nodependency -out:%DLLFilename%.manifest
REM Optional VB Script that uses XMLSpy to auto-format the Xml into something readable
WSCRIPT PrettyPrint.vbs //NOLOGO //B //T:60 %~dp0%DLLFilename%.manifest
REM Now poke the manifest file into the target DLL at the right location
reshacker -addoverwrite %DLLFilename%,%DLLFilename%,%DLLFilename%.manifest,24,1,1033
REM Generate the COM interface that isn't present in .NET assemblies.
tlbexp /silent %DLLFilename%
REM Again, use reghacker to poke it into the DLL
reshacker -addoverwrite %DLLFilename%,%DLLFilename%,%~n1.tlb,TYPELIB,1,1033
REM Clear up all the rubbish, because we don't need all these files hanging around anymore!! 8D
DEL %DLLFilename%.manifest

FOR /F "tokens=*" %%i in ('GetAssemblyVersion %~dp0\%DLLFilename%') do SET AssemblyVersion=%%i
REM Now generate a client manigest for the EXE/DLL that's going to be referencing our component
IF EXIST %1.client.manifest DEL %1.client.manifest
ECHO ^<?xml version="1.0" encoding="UTF-8" standalone="yes"?^> >> %1.client.manifest
ECHO ^<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"^> >> %1.client.manifest
ECHO ^<assemblyIdentity type="win32" name="client" version=""/^> >> %1.client.manifest
ECHO  ^<dependency^> >> %1.client.manifest
ECHO   ^<dependentAssembly^> >> %1.client.manifest
ECHO       ^<assemblyIdentity name="%~n1" version="%AssemblyVersion%" processorArchitecture="msil"/^> >> %1.client.manifest
ECHO   ^</dependentAssembly^> >> %1.client.manifest
ECHO  ^</dependency^> >> %1.client.manifest
ECHO ^</assembly^> >> %1.client.manifest



ECHO Usage: RegFreeMe DLLFilename


If you're not using Xml pretty printing, remote the WSCRIPT... line

There are some pre-requisites for that, you need the following software installed and in your PATH variable:

mt (provided by the MS Windows SDK or Visual Studio)
rc (provided by the MS Windows SDK or Visual Studio)
tlbexp (provided by the MS Windows SDK or Visual Studio)
reshacker (Follow the link, download and install)

Optional (pretty prints the Xml manifest embedded in the DLL to allow easy reading)

wscript, the Windows scripting host
Altova XMLSpy

The following VB Script:

'Pretty Print XML
Dim objSpy ' As XMLSpyLib.Application
Set objSpy = GetObject("", "XMLSpy.Application")
'objSpy.ShowApplication (False)
Dim objDoc' As XMLSpyLib.Document
Set objDoc = objSpy.Documents.OpenFile(Wscript.Arguments(0), False)
objDoc.SwitchViewMode 0
objDoc.SwitchViewMode 1
objDoc.SaveAs Wscript.Arguments(0)

TBD: Publish the script to auto-merge many client manifests into one and embed it in a target EXE (embedding is required for this to work in Win7, side by side manifests don't work anymore)

Embed this in your build scripts.
There, I've saved the world from reg free COM.
But will anybody ever read it?
Or buy me beer?

Source code for GetAssemblyVersion:

using System;
using System.Reflection;

namespace GetAssemblyVersion
    static class Program
        static void Main(string[] args)
            if (!string.IsNullOrEmpty(args[0]))
                Console.WriteLine("Usage: GetAssembly AssemblyFilename");


No comments:

Post a Comment