修复无法二次调试

This commit is contained in:
ShrlAlgo
2026-01-23 18:07:21 +08:00
parent 766fa23cc0
commit 9b7a6e2772
2 changed files with 71 additions and 82 deletions

View File

@@ -17,9 +17,9 @@ namespace AddInManager
private Dictionary<string, DateTime> m_copiedFiles;
private bool m_parsingOnly;
// 获取 .NET 运行时目录
private static string m_dotnetDir = System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory();
public static string m_resolvedAssemPath = string.Empty;
private string m_revitAPIAssemblyFullName;
public AssemLoader()
@@ -29,27 +29,25 @@ namespace AddInManager
m_copiedFiles = new Dictionary<string, DateTime>();
}
// 保持原有的文件回写逻辑不变
public void CopyGeneratedFilesBack()
{
if (!Directory.Exists(TempFolder)) return;
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 fileInfo = new FileInfo(text);
if(fileInfo.LastWriteTime > dateTime)
if (fileInfo.LastWriteTime > dateTime)
{
var text2 = text.Remove(0, TempFolder.Length);
var text3 = OriginalFolder + text2;
FileUtils.CopyFile(text, text3);
}
} else
{
var text4 = text.Remove(0, TempFolder.Length);
var text5 = OriginalFolder + text4;
FileUtils.CopyFile(text, text5);
}
// 注意:通常我们不希望把临时文件夹产生的所有垃圾文件都拷回源目录,视需求而定
}
}
@@ -65,7 +63,7 @@ namespace AddInManager
public Assembly LoadAddinsToTempFolder(string originalFilePath, bool parsingOnly)
{
if (string.IsNullOrEmpty(originalFilePath) || originalFilePath.StartsWith("\\") || !File.Exists(originalFilePath))
if (string.IsNullOrEmpty(originalFilePath) || !File.Exists(originalFilePath))
{
return null;
}
@@ -75,8 +73,10 @@ namespace AddInManager
var stringBuilder = new StringBuilder(Path.GetFileNameWithoutExtension(originalFilePath));
stringBuilder.Append(parsingOnly ? "-Parsing-" : "-Executing-");
// 1. 创建全新的临时文件夹 (基于时间戳,确保唯一)
TempFolder = FileUtils.CreateTempFolder(stringBuilder.ToString());
// 2. 复制并加载
var assembly = CopyAndLoadAddin(originalFilePath, parsingOnly);
if (null == assembly || !IsAPIReferenced(assembly))
{
@@ -87,7 +87,9 @@ namespace AddInManager
private Assembly CopyAndLoadAddin(string srcFilePath, bool onlyCopyRelated)
{
var destPath = string.Empty;
string destPath = string.Empty;
// 复制文件到临时目录
if (!FileUtils.FileExistsInFolder(srcFilePath, TempFolder))
{
var directoryName = Path.GetDirectoryName(srcFilePath);
@@ -97,15 +99,21 @@ namespace AddInManager
}
var list = new List<FileInfo>();
destPath = FileUtils.CopyFileToFolder(srcFilePath, TempFolder, onlyCopyRelated, list);
if (string.IsNullOrEmpty(destPath))
{
return null;
}
if (string.IsNullOrEmpty(destPath)) return null;
foreach (var fileInfo in list)
{
m_copiedFiles.Add(fileInfo.FullName, fileInfo.LastWriteTime);
m_copiedFiles[fileInfo.FullName] = fileInfo.LastWriteTime;
}
}
else
{
// 如果文件已存在极少情况因为是新Temp目录构造目标路径
destPath = Path.Combine(TempFolder, Path.GetFileName(srcFilePath));
}
// 加载复制后的文件
return LoadAddin(destPath);
}
@@ -114,73 +122,60 @@ namespace AddInManager
Assembly assembly = null;
try
{
Monitor.Enter(this);
assembly = Assembly.LoadFile(filePath);
// 【核心修改】:使用字节流加载,而不是 LoadFile
// 这样可以避免文件锁定,并且每次 Load 都会视为新的程序集实例
if (File.Exists(filePath))
{
byte[] assemblyBytes = File.ReadAllBytes(filePath);
byte[] pdbBytes = null;
// 尝试加载 PDB 以支持调试
string pdbPath = Path.ChangeExtension(filePath, "pdb");
if (File.Exists(pdbPath))
{
pdbBytes = File.ReadAllBytes(pdbPath);
}
// 使用 Load(bytes, pdbBytes)
assembly = Assembly.Load(assemblyBytes, pdbBytes);
}
}
catch (Exception ex)
{
// 增加简单的错误输出,方便调试
Debug.WriteLine($"LoadAddin Failed: {filePath}, Error: {ex.Message}");
throw;
}
finally
{
Monitor.Exit(this);
}
return assembly;
}
private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
Assembly assembly = null;
lock (this)
// 防止递归或死循环
string assemblyName = new AssemblyName(args.Name).Name;
// 忽略资源文件
if (assemblyName.EndsWith(".resources") || assemblyName.EndsWith(".XmlSerializers"))
return null;
// 1. 在临时文件夹中寻找依赖项
// 因为主程序是字节流加载的,它不知道自己在 TempFolder必须我们告诉它去那里找
string foundPath = SearchAssemblyFileInTempFolder(assemblyName);
if (!string.IsNullOrEmpty(foundPath))
{
var assemblyNameObj = new AssemblyName(args.Name);
var simpleName = assemblyNameObj.Name;
if (simpleName.EndsWith(".resources", StringComparison.OrdinalIgnoreCase) ||
args.Name.Contains(".resources"))
{
return null;
}
// 1. 先在临时目录找
var text = SearchAssemblyFileInTempFolder(simpleName);
if (File.Exists(text))
{
return LoadAddin(text);
}
// 2. 临时目录没有,去源目录找
text = SearchAssemblyFileInOriginalFolders(simpleName);
// 3. 如果源目录找到了,复制到临时目录并加载
if (!string.IsNullOrEmpty(text))
{
assembly = CopyAndLoadAddin(text, true);
return assembly;
}
if (simpleName.EndsWith(".XmlSerializers", StringComparison.OrdinalIgnoreCase))
{
// 忽略序列化程序集请求
return null;
}
// 5. 【可选】最后尝试手动弹窗选择(原代码逻辑),
// 但通常对于依赖项来说弹窗很烦人建议只针对主程序集弹窗或者直接返回null
// 如果这是为了解决找不到依赖的问题,上面 LoadFrom 改好后这里应该很少进来了
// 只有当你确实需要弹窗时保留下面代码
/*
var assemblySelector = new Wpf.AssemblySelectorWindow(args.Name);
if (assemblySelector.ShowDialog() == true)
{
text = assemblySelector.ResultPath;
assembly = CopyAndLoadAddin(text, true);
}
*/
// 找到依赖项后,同样使用字节流加载!
// 这样保证主程序集和依赖程序集都在“无上下文”的环境中匹配
return LoadAddin(foundPath);
}
return assembly;
// 2. 如果临时文件夹没有,去源文件夹找 (并复制过来)
foundPath = SearchAssemblyFileInOriginalFolders(assemblyName);
if (!string.IsNullOrEmpty(foundPath))
{
return CopyAndLoadAddin(foundPath, true);
}
return null;
}
private string SearchAssemblyFileInTempFolder(string simpleName)
@@ -191,41 +186,35 @@ namespace AddInManager
string path = Path.Combine(TempFolder, simpleName + ext);
if (File.Exists(path)) return path;
}
return string.Empty;
return null;
}
private string SearchAssemblyFileInOriginalFolders(string simpleName)
{
var extensions = new string[] { ".dll", ".exe" };
// 1. 系统目录 (通常不需要System dll 会自动解析,但保留以防万一)
foreach (var ext in extensions)
{
string path = Path.Combine(m_dotnetDir, simpleName + ext);
if (File.Exists(path)) return path;
}
// 2. 所有引用过的源目录中找
// 2. 所有引用过的源文件夹
foreach (var ext in extensions)
{
foreach (var folder in m_refedFolders)
{
string path = Path.Combine(folder, simpleName + ext);
if (File.Exists(path))
{
return path;
}
if (File.Exists(path)) return path;
}
}
// 3. 原代码中关于 Regression 的逻辑(看起来是特定环境的,如果不需要建议删除)
return null;
}
private bool IsAPIReferenced(Assembly assembly)
{
// 保持原逻辑不变
if (string.IsNullOrEmpty(m_revitAPIAssemblyFullName))
{
foreach (var assembly2 in AppDomain.CurrentDomain.GetAssemblies())
@@ -237,7 +226,7 @@ namespace AddInManager
}
}
}
// 防止未加载 RevitAPI 时崩溃
// 如果还没加载 RevitAPI (极其罕见),通过
if (string.IsNullOrEmpty(m_revitAPIAssemblyFullName)) return true;
foreach (var assemblyName in assembly.GetReferencedAssemblies())

View File

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