Example #1
0
        /// <summary>
        /// Executes assembly in this thread.
        /// Handles exceptions.
        /// </summary>
        /// <param name="asmFile">Full path of assembly file.</param>
        /// <param name="args">To pass to Main.</param>
        /// <param name="pdbOffset">0 or offset of portable PDB in assembly file.</param>
        /// <param name="flags"></param>
        /// <param name="fullPathRefs">Paths of assemblies specified using full path.</param>
        public static void Run(string asmFile, string[] args, int pdbOffset, RAFlags flags = 0, string fullPathRefs = null)
        {
            ADebug.PrintIf(pdbOffset == 0, "pdbOffset 0");

            bool inEditorThread       = 0 != (flags & RAFlags.InEditorThread);
            bool findLoaded           = inEditorThread;
            _LoadedScriptAssembly lsa = default;
            Assembly asm = findLoaded ? lsa.Find(asmFile) : null;

            if (asm == null)
            {
#if true
                //var p1 = APerf.Create();
                var alc = System.Runtime.Loader.AssemblyLoadContext.Default;
                //SHOULDDO: try to unload. It seems AssemblyLoadContext supports it. Not tested. I guess it would create more problems than is useful.
                //p1.Next();
                using (var stream = AFile.WaitIfLocked(() => File.OpenRead(asmFile))) {
                    //p1.Next();
                    if (pdbOffset > 0)
                    {
                        var b = new byte[pdbOffset];
                        stream.Read(b, 0, b.Length);
                        using var msAsm = new MemoryStream(b);
                        b = new byte[stream.Length - pdbOffset];
                        stream.Read(b, 0, b.Length);
                        using var msDeb = new MemoryStream(b);
                        //p1.Next('f');
                        asm = alc.LoadFromStream(msAsm, msDeb);
                        //p1.Next();
                    }
                    else
                    {
                        asm = alc.LoadFromStream(stream);
                    }
                }
                //p1.NW();
                //APerf.Next('a');

                if (fullPathRefs != null)
                {
                    var fpr = fullPathRefs.SegSplit("|");
                    alc.Resolving += (System.Runtime.Loader.AssemblyLoadContext alc, AssemblyName an) => {
                        //AOutput.Write(an, an.Name, an.FullName);
                        foreach (var v in fpr)
                        {
                            var s1    = an.Name;
                            int iName = v.Length - s1.Length - 4;
                            if (iName <= 0 || v[iName - 1] != '\\' || !v.Eq(iName, s1, true))
                            {
                                continue;
                            }
                            if (!AFile.ExistsAsFile(v))
                            {
                                continue;
                            }
                            //try {
                            return(alc.LoadFromAssemblyPath(v));
                            //} catch(Exception ex) { ADebug.Print(ex.ToStringWithoutStack()); break; }
                        }
                        return(null);
                    };
                }

                //ADebug.PrintLoadedAssemblies(true, true);

                //AOutput.Write(asm);
#else
                byte[] bAsm, bPdb = null;
                using (var stream = AFile.WaitIfLocked(() => File.OpenRead(asmFile))) {
                    bAsm = new byte[pdbOffset > 0 ? pdbOffset : stream.Length];
                    stream.Read(bAsm, 0, bAsm.Length);
                    try {
                        if (pdbOffset > 0)
                        {
                            bPdb = new byte[stream.Length - pdbOffset];
                            stream.Read(bPdb, 0, bPdb.Length);
                        }
                        else
                        {
                            var s1 = Path.ChangeExtension(asmFile, "pdb");
                            if (AFile.ExistsAsFile(s1))
                            {
                                bPdb = File.ReadAllBytes(s1);
                            }
                        }
                    }
                    catch (Exception ex) { bPdb = null; ADebug.Print(ex); }                    //not very important
                }
                //APerf.Next('f');
                //APerf.First();
                asm = Assembly.Load(bAsm, bPdb);
#endif
                //APerf.Next('A');
                //APerf.NW(); //without AV 7 ms. With Windows Defender 10 ms, but first time 20-900 ms.
                if (findLoaded)
                {
                    lsa.Add(asmFile, asm);
                }

                //never mind: it's possible that we load a newer compiled assembly version of script than intended.
            }

            try {
                var entryPoint = asm.EntryPoint ?? throw new InvalidOperationException("assembly without entry point (function Main)");

                bool useArgs = entryPoint.GetParameters().Length != 0;
                if (useArgs)
                {
                    if (args == null)
                    {
                        args = Array.Empty <string>();
                    }
                }

                //APerf.Next('1');
                if (!inEditorThread)
                {
                    Util.Log_.Run.Write("Task started.");
                }

                //APerf.Next('2');
                if (useArgs)
                {
                    entryPoint.Invoke(null, new object[] { args });
                }
                else
                {
                    entryPoint.Invoke(null, null);
                }

                if (!inEditorThread)
                {
                    Util.Log_.Run.Write("Task ended.");
                }
            }
            catch (TargetInvocationException te) {
                var e = te.InnerException;

                if (!inEditorThread)
                {
                    Util.Log_.Run.Write($"Unhandled exception: {e.ToStringWithoutStack()}");
                }

                if (0 != (flags & RAFlags.DontHandleExceptions))
                {
                    throw e;
                }
                //AOutput.Write(e);
                AScript.OnHostHandledException(new UnhandledExceptionEventArgs(e, false));
            }

            //see also: TaskScheduler.UnobservedTaskException event.
            //	tested: the event works.
            //	tested: somehow does not terminate process even with <ThrowUnobservedTaskExceptions enabled="true"/>.
            //		Only when ADialog.Show called, the GC.Collect makes it to disappear but the process does not exit.
            //	note: the terminating behavior also can be set in registry or env var. It overrides <ThrowUnobservedTaskExceptions enabled="false"/>.
        }
Example #2
0
 ///
 protected AScript()
 {
     s_instance = this;
 }