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
ECHO OFF 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.0.0.0"/^> >> %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 GOTO :End :NoParams ECHO Usage: RegFreeMe DLLFilename :End
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) objSpy.Quit
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 { [STAThread] static void Main(string[] args) { if (!string.IsNullOrEmpty(args[0])) { Console.Out.WriteLine(Assembly.LoadFile(args[0]).GetName().Version.ToString()); } else { Console.WriteLine("Usage: GetAssembly AssemblyFilename"); } } } }