async void _ConvertTypeLibrary(object tlDef, object button) { string comDll = null; switch (tlDef) { case string path: comDll = path; break; case _RegTypelib r: //can be several locales var aloc = new List <string>(); //registry keys like "0" or "409" var aloc2 = new List <string>(); //locale names for display in the list dialog using (var verKey = Registry.ClassesRoot.OpenSubKey($@"TypeLib\{r.guid}\{r.version}")) { foreach (var s1 in verKey.GetSubKeyNames()) { int lcid = s1.ToInt(0, out int iEnd, STIFlags.IsHexWithout0x); if (iEnd != s1.Length) { continue; //"FLAGS" etc; must be hex number without 0x } aloc.Add(s1); var s2 = "Neutral"; if (lcid > 0) { try { s2 = new CultureInfo(lcid).DisplayName; } catch { s2 = s1; } } aloc2.Add(s2); } } string locale; if (aloc.Count == 1) { locale = aloc[0]; } else { int i = ADialog.ShowList(aloc2, "Locale", owner: this); if (i == 0) { return; } locale = aloc[i - 1]; } comDll = r.GetPath(locale); if (comDll == null || !AFile.ExistsAsFile(comDll)) { ADialog.ShowError(comDll == null ? "Failed to get file path." : "File does not exist.", owner: this); return; } break; } await Task.Run(() => { this.Enabled = false; AOutput.Write($"Converting COM type library to .NET assembly."); try { if (_convertedDir == null) { _convertedDir = AFolders.Workspace + @".interop\"; AFile.CreateDirectory(_convertedDir); } List <string> converted = new List <string>(); Action <string> callback = s => { AOutput.Write(s); if (s.Starts("Converted: ")) { s.RegexMatch(@"""(.+?)"".$", 1, out s); converted.Add(s); } }; int rr = AExec.RunConsole(callback, AFolders.ThisAppBS + "Au.Net45.exe", $"/typelib \"{_convertedDir}|{comDll}\"", encoding: Encoding.UTF8); if (rr == 0) { foreach (var v in converted) { if (!_meta.com.Contains(v)) { _meta.com.Add(v); } } AOutput.Write(@"<>Converted and saved in <link>%AFolders.Workspace%\.interop<>."); _Added(button, _meta.com); } } catch (Exception ex) { ADialog.ShowError("Failed to convert type library", ex.ToStringWithoutStack(), owner: this); } this.Enabled = true; }); }
static bool _Compile(bool forRun, FileNode f, out CompResults r, FileNode projFolder) { var p1 = APerf.Create(); r = new CompResults(); var m = new MetaComments(); if (!m.Parse(f, projFolder, EMPFlags.PrintErrors)) { return(false); } var err = m.Errors; p1.Next('m'); bool needOutputFiles = m.Role != ERole.classFile; //if for run, don't compile if f role is classFile if (forRun && !needOutputFiles) { r.role = ERole.classFile; return(false); } XCompiled cache = XCompiled.OfCollection(f.Model); string outPath = null, outFile = null, fileName = null; if (needOutputFiles) { if (m.OutputPath != null) { outPath = m.OutputPath; fileName = m.Name + ".dll"; } else { outPath = cache.CacheDirectory; fileName = f.IdString; } outFile = outPath + "\\" + fileName; AFile.CreateDirectory(outPath); } if (m.PreBuild.f != null && !_RunPrePostBuildScript(false, m, outFile)) { return(false); } var po = m.CreateParseOptions(); var trees = new CSharpSyntaxTree[m.CodeFiles.Count]; for (int i = 0; i < trees.Length; i++) { var f1 = m.CodeFiles[i]; trees[i] = CSharpSyntaxTree.ParseText(f1.code, po, f1.f.FilePath, Encoding.UTF8) as CSharpSyntaxTree; //info: file path is used later in several places: in compilation error messages, run time stack traces (from PDB), Visual Studio debugger, etc. // Our AOutputServer.SetNotifications callback will convert file/line info to links. It supports compilation errors and run time stack traces. } //p1.Next('t'); string asmName = m.Name; if (m.Role == ERole.editorExtension) { asmName = asmName + "|" + (++c_versionCounter).ToString(); //AssemblyLoadContext.Default cannot load multiple assemblies with same name } var compilation = CSharpCompilation.Create(asmName, trees, m.References.Refs, m.CreateCompilationOptions()); //p1.Next('c'); #if PDB string pdbFile = null; #endif MemoryStream pdbStream = null; string xdFile = null; Stream xdStream = null; Stream resNat = null; ResourceDescription[] resMan = null; EmitOptions eOpt = null; if (needOutputFiles) { _AddAttributes(ref compilation, needVersionEtc: m.Role == ERole.miniProgram || m.Role == ERole.exeProgram); //create debug info always. It is used for run-time error links. #if PDB pdbStream = new MemoryStream(); if (m.OutputPath != null) { pdbFile = Path.ChangeExtension(outFile, "pdb"); eOpt = new EmitOptions(debugInformationFormat: DebugInformationFormat.Pdb, pdbFilePath: pdbFile); //eOpt = new EmitOptions(debugInformationFormat: DebugInformationFormat.PortablePdb, pdbFilePath: pdbFile); //smaller, but not all tools support it } else { //eOpt = new EmitOptions(debugInformationFormat: DebugInformationFormat.Embedded); //no, it is difficult to extract, because we load assembly from byte[] to avoid locking. We instead append portable PDB stream to the assembly stream. eOpt = new EmitOptions(debugInformationFormat: DebugInformationFormat.PortablePdb); //adds < 1 KB; almost the same compiling speed. Separate pdb file is 14 KB; 2 times slower compiling, slower loading. } #else if (m.OutputPath != null) { //we don't use classic pdb file becouse of this error after switching to .NET Core: // Unexpected error writing debug information -- 'The version of Windows PDB writer is older than required: 'diasymreader.dll'' eOpt = new EmitOptions(debugInformationFormat: DebugInformationFormat.Embedded); } else { pdbStream = new MemoryStream(); //eOpt = new EmitOptions(debugInformationFormat: DebugInformationFormat.Embedded); //no, it is difficult to extract, because we load assembly from byte[] to avoid locking. We instead append portable PDB stream to the assembly stream. eOpt = new EmitOptions(debugInformationFormat: DebugInformationFormat.PortablePdb); //adds < 1 KB; almost the same compiling speed. Separate pdb file is 14 KB; 2 times slower compiling, slower loading. } #endif if (m.XmlDocFile != null) { xdStream = AFile.WaitIfLocked(() => File.Create(xdFile = APath.Normalize(m.XmlDocFile, outPath))); } resMan = _CreateManagedResources(m); if (err.ErrorCount != 0) { err.PrintAll(); return(false); } if (m.Role == ERole.exeProgram || m.Role == ERole.classLibrary) { resNat = _CreateNativeResources(m, compilation); } if (err.ErrorCount != 0) { err.PrintAll(); return(false); } //EmbeddedText.FromX //it seems we can embed source code in PDB. Not tested. } //p1.Next(); var asmStream = new MemoryStream(8000); var emitResult = compilation.Emit(asmStream, pdbStream, xdStream, resNat, resMan, eOpt); if (needOutputFiles) { xdStream?.Dispose(); resNat?.Dispose(); //info: compiler disposes resMan } //p1.Next('e'); var diag = emitResult.Diagnostics; if (!diag.IsEmpty) { foreach (var d in diag) { if (d.Severity == DiagnosticSeverity.Hidden) { continue; } err.AddErrorOrWarning(d, f); if (d.Severity == DiagnosticSeverity.Error && d.Id == "CS0009") { MetaReferences.RemoveBadReference(d.GetMessage()); } } err.PrintAll(); } if (!emitResult.Success) { if (needOutputFiles) { AFile.Delete(outFile); #if PDB if (pdbFile != null) { AFile.Delete(pdbFile); } #endif if (xdFile != null) { AFile.Delete(xdFile); } } return(false); } if (needOutputFiles) { //If there is no [STAThread], will need MTA thread. if (m.Role == ERole.miniProgram || m.Role == ERole.exeProgram) { bool hasSTAThread = compilation.GetEntryPoint(default)?.GetAttributes().Any(o => o.ToString() == "System.STAThreadAttribute") ?? false;