Files
AddinManager/AddInManager/AssemLoader.cs

242 lines
8.7 KiB
C#
Raw Normal View History

2025-09-04 09:53:20 +08:00
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Text;
using System.Threading;
namespace AddInManager
{
public class AssemLoader
{
public string OriginalFolder { get; set; }
public string TempFolder { get; set; }
2026-01-02 11:40:39 +08:00
private List<string> m_refedFolders;
private Dictionary<string, DateTime> m_copiedFiles;
private bool m_parsingOnly;
2026-01-23 18:07:21 +08:00
// 获取 .NET 运行时目录
2026-01-02 11:40:39 +08:00
private static string m_dotnetDir = System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory();
private string m_revitAPIAssemblyFullName;
2025-09-04 09:53:20 +08:00
public AssemLoader()
{
TempFolder = string.Empty;
m_refedFolders = new List<string>();
m_copiedFiles = new Dictionary<string, DateTime>();
}
2026-01-23 18:07:21 +08:00
// 保持原有的文件回写逻辑不变
2025-09-04 09:53:20 +08:00
public void CopyGeneratedFilesBack()
{
2026-01-23 18:07:21 +08:00
if (!Directory.Exists(TempFolder)) return;
2025-09-04 09:53:20 +08:00
var files = Directory.GetFiles(TempFolder, "*.*", SearchOption.AllDirectories);
2026-01-23 18:07:21 +08:00
foreach (var text in files)
2025-09-04 09:53:20 +08:00
{
2026-01-23 18:07:21 +08:00
if (m_copiedFiles.ContainsKey(text))
2025-09-04 09:53:20 +08:00
{
var dateTime = m_copiedFiles[text];
var fileInfo = new FileInfo(text);
2026-01-23 18:07:21 +08:00
if (fileInfo.LastWriteTime > dateTime)
2025-09-04 09:53:20 +08:00
{
var text2 = text.Remove(0, TempFolder.Length);
var text3 = OriginalFolder + text2;
FileUtils.CopyFile(text, text3);
}
}
2026-01-23 18:07:21 +08:00
// 注意:通常我们不希望把临时文件夹产生的所有垃圾文件都拷回源目录,视需求而定
2025-09-04 09:53:20 +08:00
}
}
public void HookAssemblyResolve()
{
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
}
public void UnhookAssemblyResolve()
{
AppDomain.CurrentDomain.AssemblyResolve -= CurrentDomain_AssemblyResolve;
}
public Assembly LoadAddinsToTempFolder(string originalFilePath, bool parsingOnly)
{
2026-01-23 18:07:21 +08:00
if (string.IsNullOrEmpty(originalFilePath) || !File.Exists(originalFilePath))
2025-09-04 09:53:20 +08:00
{
return null;
}
m_parsingOnly = parsingOnly;
OriginalFolder = Path.GetDirectoryName(originalFilePath);
2026-01-02 11:40:39 +08:00
2025-09-04 09:53:20 +08:00
var stringBuilder = new StringBuilder(Path.GetFileNameWithoutExtension(originalFilePath));
2026-01-02 11:40:39 +08:00
stringBuilder.Append(parsingOnly ? "-Parsing-" : "-Executing-");
2026-01-23 18:07:21 +08:00
// 1. 创建全新的临时文件夹 (基于时间戳,确保唯一)
2025-09-04 09:53:20 +08:00
TempFolder = FileUtils.CreateTempFolder(stringBuilder.ToString());
2026-01-02 11:40:39 +08:00
2026-01-23 18:07:21 +08:00
// 2. 复制并加载
2025-09-04 09:53:20 +08:00
var assembly = CopyAndLoadAddin(originalFilePath, parsingOnly);
if (null == assembly || !IsAPIReferenced(assembly))
{
return null;
}
return assembly;
}
private Assembly CopyAndLoadAddin(string srcFilePath, bool onlyCopyRelated)
{
2026-01-23 18:07:21 +08:00
string destPath = string.Empty;
// 复制文件到临时目录
2025-09-04 09:53:20 +08:00
if (!FileUtils.FileExistsInFolder(srcFilePath, TempFolder))
{
var directoryName = Path.GetDirectoryName(srcFilePath);
if (!m_refedFolders.Contains(directoryName))
{
m_refedFolders.Add(directoryName);
}
var list = new List<FileInfo>();
2026-01-02 11:40:39 +08:00
destPath = FileUtils.CopyFileToFolder(srcFilePath, TempFolder, onlyCopyRelated, list);
2026-01-23 18:07:21 +08:00
if (string.IsNullOrEmpty(destPath)) return null;
2025-09-04 09:53:20 +08:00
foreach (var fileInfo in list)
{
2026-01-23 18:07:21 +08:00
m_copiedFiles[fileInfo.FullName] = fileInfo.LastWriteTime;
2025-09-04 09:53:20 +08:00
}
}
2026-01-23 18:07:21 +08:00
else
{
// 如果文件已存在极少情况因为是新Temp目录构造目标路径
destPath = Path.Combine(TempFolder, Path.GetFileName(srcFilePath));
}
// 加载复制后的文件
2026-01-02 11:40:39 +08:00
return LoadAddin(destPath);
2025-09-04 09:53:20 +08:00
}
private Assembly LoadAddin(string filePath)
{
Assembly assembly = null;
try
{
2026-01-23 18:07:21 +08:00
// 【核心修改】:使用字节流加载,而不是 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);
}
2026-01-02 11:40:39 +08:00
}
catch (Exception ex)
{
Debug.WriteLine($"LoadAddin Failed: {filePath}, Error: {ex.Message}");
throw;
2025-09-04 09:53:20 +08:00
}
return assembly;
}
private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
2026-01-23 18:07:21 +08:00
// 防止递归或死循环
string assemblyName = new AssemblyName(args.Name).Name;
2026-01-02 11:40:39 +08:00
2026-01-23 18:07:21 +08:00
// 忽略资源文件
if (assemblyName.EndsWith(".resources") || assemblyName.EndsWith(".XmlSerializers"))
return null;
2026-01-02 11:40:39 +08:00
2026-01-23 18:07:21 +08:00
// 1. 在临时文件夹中寻找依赖项
// 因为主程序是字节流加载的,它不知道自己在 TempFolder必须我们告诉它去那里找
string foundPath = SearchAssemblyFileInTempFolder(assemblyName);
2026-01-02 11:40:39 +08:00
2026-01-23 18:07:21 +08:00
if (!string.IsNullOrEmpty(foundPath))
{
// 找到依赖项后,同样使用字节流加载!
// 这样保证主程序集和依赖程序集都在“无上下文”的环境中匹配
return LoadAddin(foundPath);
}
2026-01-02 11:40:39 +08:00
2026-01-23 18:07:21 +08:00
// 2. 如果临时文件夹没有,去源文件夹找 (并复制过来)
foundPath = SearchAssemblyFileInOriginalFolders(assemblyName);
if (!string.IsNullOrEmpty(foundPath))
{
return CopyAndLoadAddin(foundPath, true);
2025-09-04 09:53:20 +08:00
}
2026-01-23 18:07:21 +08:00
return null;
2025-09-04 09:53:20 +08:00
}
2026-01-02 11:40:39 +08:00
private string SearchAssemblyFileInTempFolder(string simpleName)
2025-09-04 09:53:20 +08:00
{
2026-01-02 11:40:39 +08:00
var extensions = new string[] { ".dll", ".exe" };
foreach (var ext in extensions)
2025-09-04 09:53:20 +08:00
{
2026-01-02 11:40:39 +08:00
string path = Path.Combine(TempFolder, simpleName + ext);
if (File.Exists(path)) return path;
2025-09-04 09:53:20 +08:00
}
2026-01-23 18:07:21 +08:00
return null;
2025-09-04 09:53:20 +08:00
}
2026-01-02 11:40:39 +08:00
private string SearchAssemblyFileInOriginalFolders(string simpleName)
2025-09-04 09:53:20 +08:00
{
2026-01-02 11:40:39 +08:00
var extensions = new string[] { ".dll", ".exe" };
2026-01-23 18:07:21 +08:00
// 1. 系统目录 (通常不需要System dll 会自动解析,但保留以防万一)
2026-01-02 11:40:39 +08:00
foreach (var ext in extensions)
2025-09-04 09:53:20 +08:00
{
2026-01-02 11:40:39 +08:00
string path = Path.Combine(m_dotnetDir, simpleName + ext);
if (File.Exists(path)) return path;
2025-09-04 09:53:20 +08:00
}
2026-01-02 11:40:39 +08:00
2026-01-23 18:07:21 +08:00
// 2. 所有引用过的源文件夹
2026-01-02 11:40:39 +08:00
foreach (var ext in extensions)
2025-09-04 09:53:20 +08:00
{
2026-01-02 11:40:39 +08:00
foreach (var folder in m_refedFolders)
2025-09-04 09:53:20 +08:00
{
2026-01-02 11:40:39 +08:00
string path = Path.Combine(folder, simpleName + ext);
2026-01-23 18:07:21 +08:00
if (File.Exists(path)) return path;
2025-09-04 09:53:20 +08:00
}
}
2026-01-02 11:40:39 +08:00
2025-09-04 09:53:20 +08:00
return null;
}
private bool IsAPIReferenced(Assembly assembly)
{
if (string.IsNullOrEmpty(m_revitAPIAssemblyFullName))
{
foreach (var assembly2 in AppDomain.CurrentDomain.GetAssemblies())
{
if (string.Compare(assembly2.GetName().Name, "RevitAPI", true) == 0)
{
m_revitAPIAssemblyFullName = assembly2.GetName().Name;
break;
}
}
}
2026-01-23 18:07:21 +08:00
// 如果还没加载 RevitAPI (极其罕见),通过
2026-01-02 11:40:39 +08:00
if (string.IsNullOrEmpty(m_revitAPIAssemblyFullName)) return true;
2025-09-04 09:53:20 +08:00
foreach (var assemblyName in assembly.GetReferencedAssemblies())
{
if (m_revitAPIAssemblyFullName == assemblyName.Name)
{
return true;
}
}
return false;
}
}
2026-01-02 11:40:39 +08:00
}