How To Generate Native Machine Images for a C# Application (ngen)
// This script runs the NET NGen utility against a series of DLLs. In MSI, it should be run as a Deferred Mode In System Context Custom Action,
// with a companion SetProperty CA that sets CustomActionData for this CA to [INSTALLDIR].
// It is written so it can be run outside of MSI to [vastly] simplify debugging.
var ClassName_ScriptingRuntimeShell = "WScript.Shell";
var ClassName_ScriptingRuntimeFilesystem = "Scripting.FileSystemObject";
var argumentString = "";
var arguments = null;
var commandLineToRun = "";
var netFrameworkRoot = "";
var net2Directory = "";
var ngenPathname = "";
var installDir = "";
// CustomActionData format: [INSTALLDIR]<?>
// If running in MSI, use arguments via standard CustomActionData interface, otherwise assume running outside MSI in debugger
if (DetectRunningInMSI())
argumentString = Session.Property("CustomActionData");
else
argumentString = "c:\\service\\";
arguments = argumentString.split("<?>");
if (arguments.length == 1)
{
installDir = arguments[0];
}
else
{
LogMessage("Argument string invalid (" +arguments.length+ "): " +argumentString);
throw 3;
}
installDir = EnsureEndsInBackslash(installDir);
// Locate native image generation tool (ngen)
netFrameworkRoot = ReadRegistryValue("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\.NETFramework\\InstallRoot");
netFrameworkRoot = EnsureEndsInBackslash(netFrameworkRoot);
net2Directory = netFrameworkRoot +"v2.0.50727\\";
ngenPathname = net2Directory +"ngen.exe";
LogMessage("GenerateNativeImages: NGen located at "+ngenPathname);
// Process only EXEs. NGen will automatically recurse into all referenced assemblies.
LogMessage("GenerateNativeImages: Generating images for EXEs...");
CreateNativeImagesForFiles(installDir, "exe", ngenPathname);
function CreateNativeImagesForFiles( targetDirectory, targetExtension, ngenPathname )
// Compiles native machine images for all files meeting in the specified directory meeting the specified filename specification
{
var targetDirectoryBackslash = "";
var allFiles = null;
var filesToProcess = new Array();
var currFileFullPathname = "";
var commandLineToRun = "";
// Find all files in the specified directory with the specified extension
targetDirectoryBackslash = EnsureEndsInBackslash(targetDirectory);
allFiles = GetDirectoryFiles(targetDirectory);
for (currFileNo = 0; currFileNo < allFiles.length; currFileNo++)
{
if (GetFileExtension(allFiles[currFileNo]).toUpperCase() == targetExtension.toUpperCase())
{
currFileFullPathname = targetDirectoryBackslash + allFiles[currFileNo];
filesToProcess.push(currFileFullPathname);
}
}
// NGen the filtered file list
for (currFileNo = 0; currFileNo < filesToProcess.length; currFileNo++)
{
commandLineToRun = "\""+ ngenPathname +"\" install \"" +filesToProcess[currFileNo]+ "\"";
RunCommand(commandLineToRun);
// Ignore Run errors, this operation is non-critical
}
}
function GetDirectoryFiles( targetDirectory )
// Returns an array with all filenames in the specified directory. Filenames are relative to targetDirectory.
{
var fsUtil = null;
var directoryContents = null;
var targetFilesEnumerator = null;
var filesArray = null;
fsUtil = new SafeGetActiveXObject(ClassName_ScriptingRuntimeFilesystem);
if (fsUtil != null)
{
try
{
directoryContents = fsUtil.GetFolder(targetDirectory);
targetFilesEnumerator = new Enumerator(directoryContents.files);
filesArray = new Array();
for (; !targetFilesEnumerator.atEnd(); targetFilesEnumerator.moveNext())
filesArray.push(targetFilesEnumerator.item().Name);
}
catch (unexpectedException)
{
LogMessage("GetDirectoryFiles: Error " +unexpectedException.number+ ": " +unexpectedException.message);
}
}
return filesArray;
}
function GetFileExtension( x )
// Returns the extension of a filename (no leading .)
{
var separatorPos = -1;
separatorPos = x.lastIndexOf(".");
if (separatorPos > 0)
return x.substr(separatorPos+1);
else
return "";
}
function ReadRegistryValue( valuePath )
// Reads the specified registry value and returns it, or "" if not found
{
var shellObject = null;
var returnVal = "";
shellObject = SafeGetActiveXObject(ClassName_ScriptingRuntimeShell);
if (shellObject != null)
{
try
{
returnVal = shellObject.RegRead(valuePath);
}
catch (ignoreException)
{
// Ignore it
returnVal = "";
}
shellObject = null;
}
return returnVal;
}
function RunCommand( commandLine )
// Runs the specified command line and returns the process exit code or failure code
{
var returnVal = -1;
var shellUtil = null;
// Validate parameters
if (commandLine == "")
return -1;
shellUtil = SafeGetActiveXObject(ClassName_ScriptingRuntimeShell);
if (shellUtil != null)
{
try
{
LogMessage("RunCommand: Running "+commandLine);
returnVal = shellUtil.Run(commandLine, 0, true);
if (returnVal != 0)
LogMessage("RunCommand: Error " +returnVal+ " returned by command line "+commandLine);
}
catch (runException)
{
if (runException.number == -2147024894)
LogMessage("RunCommand: File not found error running command " +commandLine+ ": " +runException.message);
else
LogMessage("RunCommand: Error " +runException.number+ " running command " +commandLine+ ": " +runException.message);
returnVal = runException.number;
}
shellUtil = null;
}
return returnVal;
}
function LogMessage( messageString )
// Writes a line to the MSI log, or the console if running outside MSI.
{
var customActionName = "CreateRemoteExecutionService";
if (DetectRunningInMSI())
{
var msiMessageTypeInfo = 0x04000000
var logRecord = null;
var currTime = new Date();
logRecord = Installer.CreateRecord(1);
logRecord.StringData(0) = currTime.toTimeString() +" "+ customActionName +": [1]";
logRecord.StringData(1) = messageString;
Session.Message(msiMessageTypeInfo, logRecord);
logRecord = null;
}
else
{
var currTime = new Date();
messageString = currTime.toTimeString() +" "+ customActionName +": "+ messageString;
WScript.Echo(messageString);
}
}
function DetectRunningInMSI()
// Returns TRUE if running in MSI, else FALSE
{
var returnVal = true;
var stringVal = "";
try
{
// Try to reference the Session object, which is only provided by the MSI runtime engine
stringVal = Session.Property("ProductCode");
}
catch (thisException)
{
// An exception is only thrown if the runtime couldn't resolve Session, which means
// this script is not running under MSI
returnVal = false;
}
return returnVal;
}
function SafeGetActiveXObject( className )
// Creates an instance of className and returns it, or null if an error occurs, and logs the error
{
var returnVal = null;
try
{
returnVal = new ActiveXObject(className);
}
catch (createException)
{
LogMessage("SafeGetActiveXObject: Error " +createException.number+ " creating object of class " +className+ ": " +createException.message);
}
return returnVal;
}
function EnsureEndsInBackslash( directoryPath )
// Returns directoryPath ending in a backslash if it does not already end in one.
{
var returnVal = directoryPath;
if (returnVal.substring(returnVal.length, returnVal.length-1) != "\\")
returnVal = returnVal + "\\";
return returnVal;
}