/// <summary> /// Creates SQLite databases containing design-time assemblies and XML documentation files of a .NET Core runtime. The SDK must be installed. /// </summary> /// <remarks> /// Shows a list dialog. /// If selected All, creates for all runtime versions starting from 3.1, with names ref.version.db (eg ref.3.1.0.db) and doc.version.db, in AFolders.ThisAppBS. /// Else creates only for the selected runtime version, with names ref.db and doc.db, in dataDir, and sets to copy to AFolders.ThisAppBS when opening next time after process restarts. /// We ship and at run time load databases of single version, named ref.db and doc.db. In the future should allow to download and use multiple versions. /// Also this function allows users to create databases from SDKs installed on their PC, but currently this feature is not exposed. Would need to add UI and exception handling. /// ref.db contains dlls from 'dotnet\packs' folder. They contain only metadata of public API, not all code like dlls in the 'dotnet\shared' folder. /// Why need it when we can load PortableExecutableReference from 'dotnet\shared' folder? Because: /// 1. They are big and may add 100 MB of process memory. We need to load all, because cannot know which are actually used in various stages of compilation. /// 2. When loading from dll files, Windows Defender makes it as slow as 2.5 s or more, unless the files already are in OS file buffers. /// 3. Better compatibility. See https://github.com/dotnet/standard/blob/master/docs/history/evolution-of-design-time-assemblies.md /// doc.db contains XML documentation files of .NET Core assemblies. From the same 'dotnet\packs' folder. /// Why need it: /// 1. Else users would have to download whole .NET Core SDK. Now need only runtimes. /// 2. Parsed XML files can use eg 200 MB of process memory. Now we get doc of a single type/method/etc from database only when need; all other data is not in memory. /// /// Need to run this after changing Core version of C# projects (<TargetFramework>netcoreapp3.1</TargetFramework>). Also update COREVER2 etc in AppHost.cpp. /// </remarks> public static void CreateRefAndDoc(string dataDir = @"Q:\app\Au\Other\Data") { Cursor.Current = Cursors.WaitCursor; if (0 == AFolders.NetRuntimeBS.RegexReplace(@"(?i)\\shared\\(Microsoft\.NETCore\.App)\\.+", @"\packs\$1.Ref", out var refDir, 1)) { throw new AuException(); } //AOutput.Write(refDir); var a = new List <string>(); foreach (var f in AFile.EnumDirectory(refDir, FEFlags.UseRawPath)) //for each version { if (!f.IsDirectory) { continue; } var s = f.Name; int v1 = s.ToInt(0, out int ne), v2 = s.ToInt(ne + 1); if (v1 < 3 || v2 < 1) { continue; //must be 3.1 or later } a.Add(s); } a.Add("All"); int i = ADialog.ShowList(a, "Create database", "For runtime") - 1; if (i < 0) { return; } int n = a.Count - 1; if (i < n) { _CreateRefAndDoc(refDir, a[i], false, dataDir); } else { for (i = 0; i < n; i++) { _CreateRefAndDoc(refDir, a[i], true, dataDir); } } AOutput.Write("CreateRefAndDoc done."); Cursor.Current = Cursors.Arrow; }
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; }); }