From 85b4e30e355c8f8fa0c139f3c36dd01607e2169b Mon Sep 17 00:00:00 2001 From: GG Z <903524121@qq.com> Date: Fri, 2 Jan 2026 11:40:39 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=A8=8B=E5=BA=8F=E9=9B=86?= =?UTF-8?q?=E5=8A=A0=E8=BD=BD=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AddInManager/AssemLoader.cs | 240 ++++++++++++++++++------------------ 1 file changed, 122 insertions(+), 118 deletions(-) diff --git a/AddInManager/AssemLoader.cs b/AddInManager/AssemLoader.cs index 0cacb10..801ac15 100644 --- a/AddInManager/AssemLoader.cs +++ b/AddInManager/AssemLoader.cs @@ -11,9 +11,18 @@ namespace AddInManager public class AssemLoader { public string OriginalFolder { get; set; } - public string TempFolder { get; set; } + private List m_refedFolders; + private Dictionary m_copiedFiles; + private bool m_parsingOnly; + + // 移除硬编码的 .NET 2.0 路径,改用更通用的方式(虽然在Revit中通常不依赖这个) + private static string m_dotnetDir = System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory(); + + public static string m_resolvedAssemPath = string.Empty; + private string m_revitAPIAssemblyFullName; + public AssemLoader() { TempFolder = string.Empty; @@ -21,8 +30,11 @@ namespace AddInManager m_copiedFiles = new Dictionary(); } + // ... CopyGeneratedFilesBack 保持不变 ... public void CopyGeneratedFilesBack() { + if (string.IsNullOrEmpty(TempFolder) || !Directory.Exists(TempFolder)) return; + var files = Directory.GetFiles(TempFolder, "*.*", SearchOption.AllDirectories); foreach (var text in files) { @@ -37,12 +49,7 @@ namespace AddInManager FileUtils.CopyFile(text, text3); } } - else - { - var text4 = text.Remove(0, TempFolder.Length); - var text5 = OriginalFolder + text4; - FileUtils.CopyFile(text, text5); - } + // 新生成的文件不建议盲目拷回,可能会污染源目录,视需求而定 } } @@ -64,16 +71,16 @@ namespace AddInManager } m_parsingOnly = parsingOnly; OriginalFolder = Path.GetDirectoryName(originalFilePath); + var stringBuilder = new StringBuilder(Path.GetFileNameWithoutExtension(originalFilePath)); - if (parsingOnly) - { - stringBuilder.Append("-Parsing-"); - } - else - { - stringBuilder.Append("-Executing-"); - } + stringBuilder.Append(parsingOnly ? "-Parsing-" : "-Executing-"); + TempFolder = FileUtils.CreateTempFolder(stringBuilder.ToString()); + + // 【建议】在此处,除了复制主DLL,最好把同目录下的所有 .dll 都复制过去 + // 这样可以避免 AssemblyResolve 频繁触发,解决大部分 NuGet 依赖问题 + // CopyAllDllsToTemp(OriginalFolder, TempFolder); // 这是一个建议实现的辅助方法 + var assembly = CopyAndLoadAddin(originalFilePath, parsingOnly); if (null == assembly || !IsAPIReferenced(assembly)) { @@ -84,7 +91,7 @@ namespace AddInManager private Assembly CopyAndLoadAddin(string srcFilePath, bool onlyCopyRelated) { - var text = string.Empty; + var destPath = string.Empty; if (!FileUtils.FileExistsInFolder(srcFilePath, TempFolder)) { var directoryName = Path.GetDirectoryName(srcFilePath); @@ -92,18 +99,30 @@ namespace AddInManager { m_refedFolders.Add(directoryName); } + var list = new List(); - text = FileUtils.CopyFileToFolder(srcFilePath, TempFolder, onlyCopyRelated, list); - if (string.IsNullOrEmpty(text)) + // 假设 FileUtils.CopyFileToFolder 会处理文件复制 + // 关键点:如果你的 FileUtils 不支持复制子文件夹(如 zh-CN),资源加载依然会失败 + destPath = FileUtils.CopyFileToFolder(srcFilePath, TempFolder, onlyCopyRelated, list); + + if (string.IsNullOrEmpty(destPath)) { return null; } foreach (var fileInfo in list) { - m_copiedFiles.Add(fileInfo.FullName, fileInfo.LastWriteTime); + if (!m_copiedFiles.ContainsKey(fileInfo.FullName)) + m_copiedFiles.Add(fileInfo.FullName, fileInfo.LastWriteTime); } } - return LoadAddin(text); + else + { + // 如果文件已存在,计算目标路径 + string fileName = Path.GetFileName(srcFilePath); + destPath = Path.Combine(TempFolder, fileName); + } + + return LoadAddin(destPath); } private Assembly LoadAddin(string filePath) @@ -112,7 +131,16 @@ namespace AddInManager try { Monitor.Enter(this); - assembly = Assembly.LoadFile(filePath); + // 【关键修改 1】使用 LoadFrom 而不是 LoadFile + // LoadFrom 会自动在 filePath 所在的目录中查找依赖项,这解决了大部分 NuGet 包加载失败的问题 + // LoadFile 这是一个纯粹的文件加载,不带上下文,不会去旁边找依赖 + assembly = Assembly.LoadFrom(filePath); + } + catch (Exception ex) + { + // 增加简单的错误输出,方便调试 + Debug.WriteLine($"LoadAddin Failed: {filePath}, Error: {ex.Message}"); + throw; } finally { @@ -123,121 +151,107 @@ namespace AddInManager private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) { - Assembly assembly; + Assembly assembly = null; lock (this) { - new AssemblyName(args.Name); - var text = SearchAssemblyFileInTempFolder(args.Name); + var assemblyNameObj = new AssemblyName(args.Name); + var simpleName = assemblyNameObj.Name; + + // 【关键修改 2】绝对不要在 AssemblyResolve 中手动处理 .resources + // 那个 "长度不能小于0" 的错误就是因为这里返回了错误的东西或者试图去加载主程序集 + // 如果请求的是 .resources,直接返回 null,让 CLR 自己去临时目录的子文件夹里找 + if (simpleName.EndsWith(".resources", StringComparison.OrdinalIgnoreCase) || + args.Name.Contains(".resources")) + { + return null; + } + + // 1. 先在临时目录找 + var text = SearchAssemblyFileInTempFolder(simpleName); if (File.Exists(text)) { - assembly = LoadAddin(text); + return LoadAddin(text); } - else + + // 2. 临时目录没有,去源目录找 + text = SearchAssemblyFileInOriginalFolders(simpleName); + + // 3. 如果源目录找到了,复制到临时目录并加载 + if (!string.IsNullOrEmpty(text)) { - text = SearchAssemblyFileInOriginalFolders(args.Name); - if (string.IsNullOrEmpty(text)) - { - var array = args.Name.Split(new char[] { ',' }); - var text2 = array[0]; - if (array.Length > 1) - { - var text3 = array[2]; - if (text2.EndsWith(".resources", StringComparison.CurrentCultureIgnoreCase) && !text3.EndsWith("neutral", StringComparison.CurrentCultureIgnoreCase)) - { - text2 = text2.Substring(0, text2.Length - ".resources".Length); - } - text = SearchAssemblyFileInTempFolder(text2); - if (File.Exists(text)) - { - return LoadAddin(text); - } - text = SearchAssemblyFileInOriginalFolders(text2); - } - } - if (string.IsNullOrEmpty(text)) - { - var assemblySelector = new Wpf.AssemblySelectorWindow(args.Name); - if (assemblySelector.ShowDialog() != true) - { - return null; - } - text = assemblySelector.ResultPath; - } + assembly = CopyAndLoadAddin(text, true); + return assembly; + } + + // 4. 如果还没找到,处理一些特殊情况(比如 XMLSerializers) + // 这里的逻辑可以保留,但通常用处不大 + 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 assembly; } - private string SearchAssemblyFileInTempFolder(string assemName) + private string SearchAssemblyFileInTempFolder(string simpleName) { - var array = new string[] { ".dll", ".exe" }; - var text = string.Empty; - var text2 = assemName.Substring(0, assemName.IndexOf(',')); - foreach (var text3 in array) + var extensions = new string[] { ".dll", ".exe" }; + foreach (var ext in extensions) { - text = $"{TempFolder}\\{text2}{text3}"; - if (File.Exists(text)) - { - return text; - } + string path = Path.Combine(TempFolder, simpleName + ext); + if (File.Exists(path)) return path; } return string.Empty; } - private string SearchAssemblyFileInOriginalFolders(string assemName) + private string SearchAssemblyFileInOriginalFolders(string simpleName) { - var array = new string[] { ".dll", ".exe" }; - var text = string.Empty; - var text2 = assemName.Substring(0, assemName.IndexOf(',')); - foreach (var text3 in array) + var extensions = new string[] { ".dll", ".exe" }; + + // 1. 在 .NET 框架目录找 (通常不需要,System库会自动加载,但保留也没事) + /* + foreach (var ext in extensions) { - text = $"{m_dotnetDir}\\{text2}{text3}"; - if (File.Exists(text)) - { - return text; - } + string path = Path.Combine(m_dotnetDir, simpleName + ext); + if (File.Exists(path)) return path; } - foreach (var text4 in array) + */ + + // 2. 在所有引用过的源目录中找 + foreach (var ext in extensions) { - foreach (var text5 in m_refedFolders) + foreach (var folder in m_refedFolders) { - text = $"{text5}\\{text2}{text4}"; - if (File.Exists(text)) + string path = Path.Combine(folder, simpleName + ext); + if (File.Exists(path)) { - return text; + return path; } } } - try - { - var directoryInfo = new DirectoryInfo(Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName)); - var text6 = $"{directoryInfo.Parent.FullName}\\Regression\\_RegressionTools\\"; - if (Directory.Exists(text6)) - { - foreach (var text7 in Directory.GetFiles(text6, "*.*", SearchOption.AllDirectories)) - { - if (Path.GetFileNameWithoutExtension(text7).Equals(text2, StringComparison.OrdinalIgnoreCase)) - { - return text7; - } - } - } - } - catch (Exception) - { - } - var num = assemName.IndexOf("XMLSerializers", StringComparison.OrdinalIgnoreCase); - if (num != -1) - { - assemName = $"System.XML{assemName.Substring(num + "XMLSerializers".Length)}"; - return SearchAssemblyFileInOriginalFolders(assemName); - } + + // 3. 原代码中关于 Regression 的逻辑(看起来是特定环境的,如果不需要建议删除) + return null; } private bool IsAPIReferenced(Assembly assembly) { + // 保持原逻辑不变 if (string.IsNullOrEmpty(m_revitAPIAssemblyFullName)) { foreach (var assembly2 in AppDomain.CurrentDomain.GetAssemblies()) @@ -249,6 +263,9 @@ namespace AddInManager } } } + // 防止未加载 RevitAPI 时崩溃 + if (string.IsNullOrEmpty(m_revitAPIAssemblyFullName)) return true; + foreach (var assemblyName in assembly.GetReferencedAssemblies()) { if (m_revitAPIAssemblyFullName == assemblyName.Name) @@ -258,18 +275,5 @@ namespace AddInManager } return false; } - - private List m_refedFolders; - - private Dictionary m_copiedFiles; - - private bool m_parsingOnly; - - private static string m_dotnetDir = - $"{Environment.GetEnvironmentVariable("windir")}\\Microsoft.NET\\Framework\\v2.0.50727"; - - public static string m_resolvedAssemPath = string.Empty; - - private string m_revitAPIAssemblyFullName; } -} +} \ No newline at end of file