Search code examples
c#.netvb.netbuildngen

Autorun Ngen.exe on First Run


My app doesn't have an installer. It is portable but I need to run ngen.exe on it because it runs on startup.

Is it recommended to autorun ngen.exe on the first run of the app? Will it cause any problems later? Is there a built in way to do it?


Solution

  • Is it recommended to autorun ngen.exe on the first run of the app?

    I have never read or heard any such recommendation, but it is an interesting idea.

    I say go for it and test whether it works for your situation (which is interesting insofar as your "portable"/"no-installer" requirement).

    Is there a built in way to do it?

    There is not a built-in way to run NGen on the first run of an app; but it can be done as the PoC below demonstrates.

    PoC code

    The following PoC code incorporates code from a related SO answer.

    using System;
    using System.Diagnostics;
    using System.IO;
    using System.Linq;
    using System.Threading;
    
    namespace SelfNgenPoC
    {
        class Program
        {
            static void Main(string[] args)
            {
                /*
                 * Check whether the app has been NGen'd with code adapted from
                 * https://stackoverflow.com/a/20593260/1810429, which also outlines
                 * an alternative approach - by running...
                 *     ngen.exe display <assemblyPath>
                 * ...and checking the result - 0 if the app is already NGen'd and
                 * -1 if it is not.
                 */
    
                Process process = Process.GetCurrentProcess();
    
                ProcessModule[] modules = new ProcessModule[process.Modules.Count];
                process.Modules.CopyTo(modules, 0);
    
                var niQuery =
                    from m in modules
                    where m.FileName.Contains(@"\" + process.ProcessName + ".ni")
                    select m.FileName;
                bool ni = niQuery.Count() > 0 ? true : false;
    
                // FORNOW: for PoC debugging and sanity checking
                if (ni)
                    Console.WriteLine("Native Image: " + niQuery.ElementAt(0));
                else
                    Console.WriteLine("IL Image: " + process.MainModule.FileName);
    
                /*
                 * NGen the app if not.
                 */
    
                if (!ni)
                {
                    // FORNOW: for PoC debugging and sanity checking
                    Console.WriteLine("The app is not NGen'd.");
                    Console.WriteLine("NGen'ing the app...");
    
                    var assemblyPath = process.MainModule.FileName;
    
                    ProcessStartInfo startInfo = new ProcessStartInfo();
                    // TODO: Determine the path to (the appropriate version of)
                    // ngen.exe.
                    // FORNOW: Just use a hardcoded path to ngen.exe for PoC.
                    startInfo.FileName =
                        @"C:\Windows\Microsoft.NET\Framework\v4.0.30319\ngen.exe";
                    startInfo.Arguments = "install \"" + assemblyPath + "\"";
                    // TBD: process options that you think make sense
                    startInfo.CreateNoWindow = false;
                    startInfo.UseShellExecute = false;
                    startInfo.WindowStyle = ProcessWindowStyle.Hidden;
    
                    try
                    {
                        using (Process exeProcess = Process.Start(startInfo))
                        {
                            exeProcess.WaitForExit();
                        }
                    }
                    catch
                    {
                        // TBD: error handling that you think makes sense - e.g.
                        // logging or displaying the error, moving on regardless
                        // etcetera.
                    }
                }
                else
                {
                    // FORNOW: for PoC debugging and sanity checking
                    Console.WriteLine("The app is already NGen'd.");
                }
    
                /*
                 * Carry on with whatever your app does.
                 */
            }
        }
    }
    

    first run

    C:\bin\SelfNgenPoC>.\SelfNgenPoC.exe
    IL Image: C:\bin\SelfNgenPoC.exe
    The app is not NGen'd.
    NGen'ing the app...
    Microsoft (R) CLR Native Image Generator - Version 4.0.30319.18408
    Copyright (c) Microsoft Corporation.  All rights reserved.
    1>    Compiling assembly C:\bin\SelfNgenPoC.exe (CLR v4.0.30319) ...
    
    C:\bin\SelfNgenPoC>
    

    subsequent run

    C:\bin\SelfNgenPoC>.\SelfNgenPoC.exe
    Native Image: C:\Windows\assembly\NativeImages_v4.0.30319_32\SelfNgenPoC\a461633
    0444188e116025424c70d15f1\SelfNgenPoC.ni.exe
    The app is already NGen'd.
    
    C:\SelfNgenPoC>
    

    Will it cause any problems later?

    Whether it makes sense to NGen an assembly depends on many factors, which you can review in an MSDN blog post, NGen documentation on MSDN, an older but relevant "MSDN Magazine" article, and elsewhere.

    Ultimately only you know enough about your app to determine whether it makes sense to NGen (any, some, or all of) its assemblies.

    Assuming it does make sense in your situation, I don't expect it will cause any distinct problems based on what you have described.

    Just be sure to keep standard NGen responsibilities in mind - particularly this one noted in the NGen MSDN documentation...

    Images need to be regenerated when the original assembly or one of its dependencies is serviced.

    ...which should be manageable with a little fancier self-NGen'ing logic.