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;

}