/// <summary> /// Finds accessible object (AO) in the specified control of window w. /// Returns true if found. The <see cref="Result"/> property will be the found AO. /// </summary> /// <param name="w">Window that contains the control.</param> /// <param name="controls">Control properties. This functions searches in all matching controls.</param> /// <exception cref="Exception">Exceptions of <see cref="Find(AWnd)"/>.</exception> /// <remarks> /// Alternatively you can specify control class name or id in role. How this function is different: 1. Allows to specify more control properties. 2. Works better/faster when the control is of a different process or thread than the parent window; else slightly slower. /// </remarks> public bool Find(AWnd w, AWnd.ChildFinder controls) { w.ThrowIfInvalid(); foreach (var c in controls.FindAll(w)) { try { if (_FindOrWait(c, 0, false)) { controls.Result = c; return(true); } } catch (AuException ex) when(!c.IsAlive) { ADebug.Print(ex.Message); } //don't throw AuWndException/AuException if the window or a control is destroyed while searching, but throw AuException if eg access denied } return(false); }
Bitmap _GetImage(string file, IconGetFlags giFlags, Func <Bitmap> callback, Action <Bitmap, object> autoUpdate, object auParam, bool auDispose) { bool cached = true; lock (this) { Bitmap R = null; //is in memory cache? if (_table == null) { _table = new Hashtable(StringComparer.OrdinalIgnoreCase); } else { R = _table[file] as Bitmap; } if (R == null) { //is in file cache? try { if (_x == null && AFile.ExistsAsFile(_cacheFile)) { _x = AExtXml.LoadElem(_cacheFile); if (_iconSize != _x.Attr("size", 0) || ADpi.BaseDPI != _x.Attr("dpi", 0)) { _x = null; ADebug.Print("info: cleared icon cache"); } //FUTURE: Delete unused entries. Maybe try to auto-update changed icons. // Not very important, because there is ClearCache. } if (_x != null) { var x = _x.Elem("i", "name", file, true); if (x != null) { using var ms = new MemoryStream(Convert.FromBase64String(x.Value), false); R = new Bitmap(ms); } } } catch (Exception ex) { ADebug.Print(ex.Message); } if (R != null) { _table[file] = R; //add to memory cache } else if (_LoadImage(out R, file, giFlags, callback)) //get file icon { _AddImage(file, R, false); //add to file cache and memory cache cached = false; } } //auto-update if (cached && autoUpdate != null) { var d = new _AUData() { cache = this, oldImage = R, file = file, callback = callback, autoUpdated = autoUpdate, auParam = auParam, giFlags = giFlags, canDispose = auDispose, }; _AutoUpdateAdd(d); } return(R); } }
/// <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"/>. }