命名调整,修复无法调试的问题

This commit is contained in:
ShrlAlgo
2026-01-20 16:03:27 +08:00
parent 681681c7ed
commit 766fa23cc0
9 changed files with 48 additions and 87 deletions

3
.gitignore vendored
View File

@@ -360,4 +360,5 @@ MigrationBackup/
.ionide/ .ionide/
# Fody - auto-generated XML schema # Fody - auto-generated XML schema
FodyWeavers.xsd FodyWeavers.xsd
/Setup

View File

@@ -89,8 +89,7 @@ namespace AddInManager
} }
else else
{ {
var externalCommand = instanceObj as IExternalCommand; if (instanceObj is not IExternalCommand externalCommand)
if (externalCommand == null)
{ {
message = $"{className} 没有实现 IExternalCommand 接口"; message = $"{className} 没有实现 IExternalCommand 接口";
result = Result.Failed; result = Result.Failed;
@@ -110,13 +109,8 @@ namespace AddInManager
} }
finally finally
{ {
// 确保清理工作安全执行 assemLoader.UnhookAssemblyResolve();
try assemLoader.CopyGeneratedFilesBack();
{
assemLoader.UnhookAssemblyResolve();
assemLoader.CopyGeneratedFilesBack();
}
catch { /* 忽略清理时的错误,防止掩盖主异常 */ }
} }
return result; return result;
} }

View File

@@ -42,7 +42,7 @@ namespace AddInManager
} }
} }
public void SaveToLocalIni(IniFile file) public void SaveToLocalIni(InitFile file)
{ {
if (ItemList == null || ItemList.Count == 0) if (ItemList == null || ItemList.Count == 0)
{ {
@@ -74,7 +74,7 @@ namespace AddInManager
file.Write("ExternalApplications", "EACount", num2); file.Write("ExternalApplications", "EACount", num2);
} }
private void WriteExternalCommand(IniFile file, AddinItem item, int number) private void WriteExternalCommand(InitFile file, AddinItem item, int number)
{ {
file.Write("ExternalCommands", $"ECName{number}", item.Name); file.Write("ExternalCommands", $"ECName{number}", item.Name);
file.Write("ExternalCommands", $"ECClassName{number}", item.FullClassName); file.Write("ExternalCommands", $"ECClassName{number}", item.FullClassName);
@@ -82,7 +82,7 @@ namespace AddInManager
file.Write("ExternalCommands", $"ECDescription{number}", item.Description); file.Write("ExternalCommands", $"ECDescription{number}", item.Description);
} }
private void WriteExternalApplication(IniFile file, AddinItem item, int number) private void WriteExternalApplication(InitFile file, AddinItem item, int number)
{ {
file.Write("ExternalApplications", $"EAClassName{number}", item.FullClassName); file.Write("ExternalApplications", $"EAClassName{number}", item.FullClassName);
file.Write("ExternalApplications", $"EAAssembly{number}", item.AssemblyName); file.Write("ExternalApplications", $"EAAssembly{number}", item.AssemblyName);

View File

@@ -28,34 +28,27 @@ namespace AddInManager
ReadAddinsFromAimIni(); ReadAddinsFromAimIni();
} }
public IniFile AimIniFile { get; set; } public InitFile AimJsonFile { get; set; }
public IniFile RevitIniFile { get; set; } public InitFile RevitIniFile { get; set; }
private void GetIniFilePaths() private void GetIniFilePaths()
{ {
//var folderPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); //var folderPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
var folderPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); var folderPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
var appFolder = Path.Combine(folderPath, Resources.AppFolder); var appFolder = Path.Combine(folderPath, Resources.AppFolder);
// switch from INI to JSON storage var jsonFilePath = Path.Combine(appFolder, "AimInternal.json");
var iniFilePath = Path.Combine(appFolder, "AimInternal.json"); AimJsonFile = new InitFile(jsonFilePath);
AimIniFile = new IniFile(iniFilePath);
// If an old INI exists, migrate it to JSON (one-time)
try try
{ {
var oldIniPath = Path.Combine(appFolder, "AimInternal.ini"); var oldIniPath = Path.Combine(appFolder, "AimInternal.ini");
if (File.Exists(oldIniPath) && !File.Exists(iniFilePath)) if (File.Exists(oldIniPath) && !File.Exists(jsonFilePath))
{ {
var oldIni = new IniFile(oldIniPath); var oldIni = new InitFile(oldIniPath);
// populate commands/applications from old INI
Commands.ReadItems(oldIni); Commands.ReadItems(oldIni);
Applications.ReadItems(oldIni); Applications.ReadItems(oldIni);
// save to new JSON store SaveToPersistentStore(jsonFilePath);
SaveToPersistentStore(iniFilePath);
// backup old INI
try try
{ {
var backupPath = oldIniPath + ".bak"; var backupPath = oldIniPath + ".bak";
@@ -77,16 +70,15 @@ namespace AddInManager
var currentProcess = Process.GetCurrentProcess(); var currentProcess = Process.GetCurrentProcess();
var fileName = currentProcess.MainModule.FileName; var fileName = currentProcess.MainModule.FileName;
var revitIniFilePath = fileName.Replace(".exe", ".ini"); var revitIniFilePath = fileName.Replace(".exe", ".ini");
RevitIniFile = new IniFile(revitIniFilePath); RevitIniFile = new InitFile(revitIniFilePath);
} }
public void ReadAddinsFromAimIni() public void ReadAddinsFromAimIni()
{ {
// try load from persistent JSON store; if fails, fall back to legacy INI-format reader if (!LoadFromPersistentStore(AimJsonFile.FilePath))
if (!LoadFromPersistentStore(AimIniFile.FilePath))
{ {
Commands.ReadItems(AimIniFile); Commands.ReadItems(AimJsonFile);
Applications.ReadItems(AimIniFile); Applications.ReadItems(AimJsonFile);
} }
} }
@@ -152,11 +144,11 @@ namespace AddInManager
// ensure file exists // ensure file exists
try try
{ {
if (!File.Exists(AimIniFile.FilePath)) if (!File.Exists(AimJsonFile.FilePath))
{ {
new FileInfo(AimIniFile.FilePath).Directory?.Create(); new FileInfo(AimJsonFile.FilePath).Directory?.Create();
FileUtils.CreateFile(AimIniFile.FilePath); FileUtils.CreateFile(AimJsonFile.FilePath);
FileUtils.SetWriteable(AimIniFile.FilePath); FileUtils.SetWriteable(AimJsonFile.FilePath);
} }
} }
catch (Exception) catch (Exception)
@@ -165,10 +157,10 @@ namespace AddInManager
} }
// save to persistent JSON store; if fails, fall back to legacy INI writer // save to persistent JSON store; if fails, fall back to legacy INI writer
if (!SaveToPersistentStore(AimIniFile.FilePath)) if (!SaveToPersistentStore(AimJsonFile.FilePath))
{ {
Commands.Save(AimIniFile); Commands.Save(AimJsonFile);
Applications.Save(AimIniFile); Applications.Save(AimJsonFile);
} }
} }

View File

@@ -2,7 +2,7 @@
{ {
public class AddinsApplication : Addins public class AddinsApplication : Addins
{ {
public void ReadItems(IniFile file) public void ReadItems(InitFile file)
{ {
var num = file.ReadInt("ExternalApplications", "EACount"); var num = file.ReadInt("ExternalApplications", "EACount");
var i = 1; var i = 1;
@@ -13,7 +13,7 @@
SortAddin(); SortAddin();
} }
private bool ReadExternalApplication(IniFile file, int nodeNumber) private bool ReadExternalApplication(InitFile file, int nodeNumber)
{ {
var text = file.ReadString("ExternalApplications", $"EAClassName{nodeNumber}"); var text = file.ReadString("ExternalApplications", $"EAClassName{nodeNumber}");
var text2 = file.ReadString("ExternalApplications", $"EAAssembly{nodeNumber}"); var text2 = file.ReadString("ExternalApplications", $"EAAssembly{nodeNumber}");
@@ -30,7 +30,7 @@
return true; return true;
} }
public void Save(IniFile file) public void Save(InitFile file)
{ {
file.WriteSection("ExternalApplications"); file.WriteSection("ExternalApplications");
file.Write("ExternalApplications", "EACount", m_maxCount); file.Write("ExternalApplications", "EACount", m_maxCount);
@@ -52,7 +52,7 @@
file.Write("ExternalApplications", "EACount", num); file.Write("ExternalApplications", "EACount", num);
} }
private bool WriteExternalApplication(IniFile file, AddinItem item, int number) private bool WriteExternalApplication(InitFile file, AddinItem item, int number)
{ {
file.Write("ExternalApplications", $"EAClassName{number}", item.FullClassName); file.Write("ExternalApplications", $"EAClassName{number}", item.FullClassName);
file.Write("ExternalApplications", $"EAAssembly{number}", item.AssemblyPath); file.Write("ExternalApplications", $"EAAssembly{number}", item.AssemblyPath);

View File

@@ -2,7 +2,7 @@
{ {
public class AddinsCommand : Addins public class AddinsCommand : Addins
{ {
public void ReadItems(IniFile file) public void ReadItems(InitFile file)
{ {
var num = file.ReadInt("ExternalCommands", "ECCount"); var num = file.ReadInt("ExternalCommands", "ECCount");
var i = 1; var i = 1;
@@ -13,7 +13,7 @@
SortAddin(); SortAddin();
} }
private bool ReadExternalCommand(IniFile file, int nodeNumber) private bool ReadExternalCommand(InitFile file, int nodeNumber)
{ {
var text = file.ReadString("ExternalCommands", $"ECName{nodeNumber}"); var text = file.ReadString("ExternalCommands", $"ECName{nodeNumber}");
var text2 = file.ReadString("ExternalCommands", $"ECAssembly{nodeNumber}"); var text2 = file.ReadString("ExternalCommands", $"ECAssembly{nodeNumber}");
@@ -33,7 +33,7 @@
return true; return true;
} }
public void Save(IniFile file) public void Save(InitFile file)
{ {
file.WriteSection("ExternalCommands"); file.WriteSection("ExternalCommands");
file.Write("ExternalCommands", "ECCount", m_maxCount); file.Write("ExternalCommands", "ECCount", m_maxCount);
@@ -55,7 +55,7 @@
file.Write("ExternalCommands", "ECCount", num); file.Write("ExternalCommands", "ECCount", num);
} }
private bool WriteExternalCommand(IniFile file, AddinItem item, int number) private bool WriteExternalCommand(InitFile file, AddinItem item, int number)
{ {
file.Write("ExternalCommands", $"ECName{number}", item.Name); file.Write("ExternalCommands", $"ECName{number}", item.Name);
file.Write("ExternalCommands", $"ECClassName{number}", item.FullClassName); file.Write("ExternalCommands", $"ECClassName{number}", item.FullClassName);

View File

@@ -17,7 +17,6 @@ namespace AddInManager
private Dictionary<string, DateTime> m_copiedFiles; private Dictionary<string, DateTime> m_copiedFiles;
private bool m_parsingOnly; private bool m_parsingOnly;
// 移除硬编码的 .NET 2.0 路径改用更通用的方式虽然在Revit中通常不依赖这个
private static string m_dotnetDir = System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory(); private static string m_dotnetDir = System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory();
public static string m_resolvedAssemPath = string.Empty; public static string m_resolvedAssemPath = string.Empty;
@@ -30,26 +29,27 @@ namespace AddInManager
m_copiedFiles = new Dictionary<string, DateTime>(); m_copiedFiles = new Dictionary<string, DateTime>();
} }
// ... CopyGeneratedFilesBack 保持不变 ...
public void CopyGeneratedFilesBack() public void CopyGeneratedFilesBack()
{ {
if (string.IsNullOrEmpty(TempFolder) || !Directory.Exists(TempFolder)) return;
var files = Directory.GetFiles(TempFolder, "*.*", SearchOption.AllDirectories); var files = Directory.GetFiles(TempFolder, "*.*", SearchOption.AllDirectories);
foreach (var text in files) foreach(var text in files)
{ {
if (m_copiedFiles.ContainsKey(text)) if(m_copiedFiles.ContainsKey(text))
{ {
var dateTime = m_copiedFiles[text]; var dateTime = m_copiedFiles[text];
var fileInfo = new FileInfo(text); var fileInfo = new FileInfo(text);
if (fileInfo.LastWriteTime > dateTime) if(fileInfo.LastWriteTime > dateTime)
{ {
var text2 = text.Remove(0, TempFolder.Length); var text2 = text.Remove(0, TempFolder.Length);
var text3 = OriginalFolder + text2; var text3 = OriginalFolder + text2;
FileUtils.CopyFile(text, text3); FileUtils.CopyFile(text, text3);
} }
} else
{
var text4 = text.Remove(0, TempFolder.Length);
var text5 = OriginalFolder + text4;
FileUtils.CopyFile(text, text5);
} }
// 新生成的文件不建议盲目拷回,可能会污染源目录,视需求而定
} }
} }
@@ -77,10 +77,6 @@ namespace AddInManager
TempFolder = FileUtils.CreateTempFolder(stringBuilder.ToString()); TempFolder = FileUtils.CreateTempFolder(stringBuilder.ToString());
// 【建议】在此处除了复制主DLL最好把同目录下的所有 .dll 都复制过去
// 这样可以避免 AssemblyResolve 频繁触发,解决大部分 NuGet 依赖问题
// CopyAllDllsToTemp(OriginalFolder, TempFolder); // 这是一个建议实现的辅助方法
var assembly = CopyAndLoadAddin(originalFilePath, parsingOnly); var assembly = CopyAndLoadAddin(originalFilePath, parsingOnly);
if (null == assembly || !IsAPIReferenced(assembly)) if (null == assembly || !IsAPIReferenced(assembly))
{ {
@@ -99,29 +95,17 @@ namespace AddInManager
{ {
m_refedFolders.Add(directoryName); m_refedFolders.Add(directoryName);
} }
var list = new List<FileInfo>(); var list = new List<FileInfo>();
// 假设 FileUtils.CopyFileToFolder 会处理文件复制
// 关键点:如果你的 FileUtils 不支持复制子文件夹(如 zh-CN资源加载依然会失败
destPath = FileUtils.CopyFileToFolder(srcFilePath, TempFolder, onlyCopyRelated, list); destPath = FileUtils.CopyFileToFolder(srcFilePath, TempFolder, onlyCopyRelated, list);
if (string.IsNullOrEmpty(destPath)) if (string.IsNullOrEmpty(destPath))
{ {
return null; return null;
} }
foreach (var fileInfo in list) foreach (var fileInfo in list)
{ {
if (!m_copiedFiles.ContainsKey(fileInfo.FullName)) m_copiedFiles.Add(fileInfo.FullName, fileInfo.LastWriteTime);
m_copiedFiles.Add(fileInfo.FullName, fileInfo.LastWriteTime);
} }
} }
else
{
// 如果文件已存在,计算目标路径
string fileName = Path.GetFileName(srcFilePath);
destPath = Path.Combine(TempFolder, fileName);
}
return LoadAddin(destPath); return LoadAddin(destPath);
} }
@@ -131,10 +115,7 @@ namespace AddInManager
try try
{ {
Monitor.Enter(this); Monitor.Enter(this);
// 【关键修改 1】使用 LoadFrom 而不是 LoadFile assembly = Assembly.LoadFile(filePath);
// LoadFrom 会自动在 filePath 所在的目录中查找依赖项,这解决了大部分 NuGet 包加载失败的问题
// LoadFile 这是一个纯粹的文件加载,不带上下文,不会去旁边找依赖
assembly = Assembly.LoadFrom(filePath);
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -157,9 +138,6 @@ namespace AddInManager
var assemblyNameObj = new AssemblyName(args.Name); var assemblyNameObj = new AssemblyName(args.Name);
var simpleName = assemblyNameObj.Name; var simpleName = assemblyNameObj.Name;
// 【关键修改 2】绝对不要在 AssemblyResolve 中手动处理 .resources
// 那个 "长度不能小于0" 的错误就是因为这里返回了错误的东西或者试图去加载主程序集
// 如果请求的是 .resources直接返回 null让 CLR 自己去临时目录的子文件夹里找
if (simpleName.EndsWith(".resources", StringComparison.OrdinalIgnoreCase) || if (simpleName.EndsWith(".resources", StringComparison.OrdinalIgnoreCase) ||
args.Name.Contains(".resources")) args.Name.Contains(".resources"))
{ {
@@ -183,8 +161,6 @@ namespace AddInManager
return assembly; return assembly;
} }
// 4. 如果还没找到,处理一些特殊情况(比如 XMLSerializers
// 这里的逻辑可以保留,但通常用处不大
if (simpleName.EndsWith(".XmlSerializers", StringComparison.OrdinalIgnoreCase)) if (simpleName.EndsWith(".XmlSerializers", StringComparison.OrdinalIgnoreCase))
{ {
// 忽略序列化程序集请求 // 忽略序列化程序集请求
@@ -222,14 +198,12 @@ namespace AddInManager
{ {
var extensions = new string[] { ".dll", ".exe" }; var extensions = new string[] { ".dll", ".exe" };
// 1. 在 .NET 框架目录找 (通常不需要System库会自动加载但保留也没事)
/*
foreach (var ext in extensions) foreach (var ext in extensions)
{ {
string path = Path.Combine(m_dotnetDir, simpleName + ext); string path = Path.Combine(m_dotnetDir, simpleName + ext);
if (File.Exists(path)) return path; if (File.Exists(path)) return path;
} }
*/
// 2. 在所有引用过的源目录中找 // 2. 在所有引用过的源目录中找
foreach (var ext in extensions) foreach (var ext in extensions)

View File

@@ -8,14 +8,14 @@ using System.Runtime.Serialization;
namespace AddInManager namespace AddInManager
{ {
public class IniFile public class InitFile
{ {
public string FilePath { get; } public string FilePath { get; }
private readonly bool m_isJson; private readonly bool m_isJson;
private Dictionary<string, Dictionary<string, string>> m_jsonData; private Dictionary<string, Dictionary<string, string>> m_jsonData;
public IniFile(string filePath) public InitFile(string filePath)
{ {
FilePath = filePath; FilePath = filePath;
m_isJson = string.Equals(Path.GetExtension(FilePath), ".json", StringComparison.OrdinalIgnoreCase); m_isJson = string.Equals(Path.GetExtension(FilePath), ".json", StringComparison.OrdinalIgnoreCase);

View File

@@ -4,7 +4,7 @@
; 定义应用程序的名称 ; 定义应用程序的名称
#define MyAppName "RevitAddinManager" #define MyAppName "RevitAddinManager"
; 定义应用程序的版本号 ; 定义应用程序的版本号
#define MyAppVersion "2.0.0" #define MyAppVersion "3.0.0"
; 定义应用程序的发布者 ; 定义应用程序的发布者
#define MyAppPublisher "ShrlAlgo" #define MyAppPublisher "ShrlAlgo"
; 定义应用程序的网址 ; 定义应用程序的网址