diff --git a/AddinManager/AddinManager.csproj b/AddinManager/AddinManager.csproj
new file mode 100644
index 0000000..82d0340
--- /dev/null
+++ b/AddinManager/AddinManager.csproj
@@ -0,0 +1,76 @@
+
+
+
+ net48
+ true
+ x64
+ x64
+ 7.3
+ Revit Addin Manager
+ Revit插件管理器,提供插件的安装、卸载、启用/禁用、热重载等功能
+ ShrlAlgo
+ RevitAddinManager
+ Copyright © 2024
+ 1.0.0.0
+ 1.0.0.0
+ true
+ false
+ false
+ REVIT_API_AVAILABLE
+
+
+
+
+
+ ..\libs\2020\RevitAPI.dll
+ False
+
+
+ ..\libs\2020\RevitAPIUI.dll
+ False
+
+
+
+
+
+
+ C:\Program Files\Autodesk\Revit 2020\RevitAPI.dll
+ False
+
+
+ C:\Program Files\Autodesk\Revit 2020\RevitAPIUI.dll
+ False
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/AddinManager/Services/AddinManagerService.cs b/AddinManager/Services/AddinManagerService.cs
new file mode 100644
index 0000000..46ff63a
--- /dev/null
+++ b/AddinManager/Services/AddinManagerService.cs
@@ -0,0 +1,1501 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Xml.Linq;
+
+using AddinManager.Models;
+
+using Newtonsoft.Json;
+
+namespace AddinManager.Services
+{
+ ///
+ /// 插件管理服务
+ ///
+ public class AddinManagerService
+ {
+ private readonly string _revitVersion;
+ private readonly List _addinFolderPaths;
+ private readonly string _configPath;
+ private readonly Dictionary _loadedAssemblies = new Dictionary();
+
+ public AddinManagerService(string revitVersion = null)
+ {
+ _revitVersion = revitVersion ?? DetectCurrentRevitVersion();
+ _addinFolderPaths = GetAllAddinDirectories(_revitVersion);
+
+ // 配置文件放在用户目录下
+ var userAddinPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
+ "Autodesk", "Revit", "Addins", _revitVersion);
+ _configPath = Path.Combine(userAddinPath, "AddinManagerConfig.json");
+
+ EnsureDirectoriesExist();
+ }
+
+ ///
+ /// 动态检测当前运行的Revit版本
+ ///
+ private string DetectCurrentRevitVersion()
+ {
+ try
+ {
+ // 方法1:从当前运行的Revit进程检测
+ var revitProcesses = Process.GetProcessesByName("Revit");
+ if (revitProcesses.Length > 0)
+ {
+ var process = revitProcesses[0];
+ var version = ExtractVersionFromProcess(process);
+ if (!string.IsNullOrEmpty(version))
+ {
+ return version;
+ }
+ }
+
+ // 方法2:从Revit API获取版本(如果在Revit环境中运行)
+ try
+ {
+ var app = AddinManagerApp.UiApplication.Application;
+ return app.VersionNumber;
+ }
+ catch
+ {
+ // 忽略Revit API获取失败
+ }
+ // 方法3:从注册表获取最新安装的版本
+ var installedVersions = GetInstalledRevitVersions();
+ if (installedVersions.Count > 0)
+ {
+ return installedVersions.OrderByDescending(v => v).First();
+ }
+ }
+ catch
+ {
+ // 忽略检测失败
+ }
+
+ // 默认返回2024版本
+ return "2024";
+ }
+
+ ///
+ /// 从进程信息中提取Revit版本
+ ///
+ private string ExtractVersionFromProcess(Process process)
+ {
+ try
+ {
+ var fileName = process.MainModule?.FileName;
+ if (!string.IsNullOrEmpty(fileName))
+ {
+ var directory = Path.GetDirectoryName(fileName);
+ if (directory != null)
+ {
+ // 从路径中提取版本号
+ for (int year = 2018; year <= 2030; year++)
+ {
+ if (directory.Contains(year.ToString()))
+ {
+ return year.ToString();
+ }
+ }
+ }
+
+ // 从文件版本信息中获取
+ var versionInfo = FileVersionInfo.GetVersionInfo(fileName);
+ if (versionInfo.ProductVersion != null)
+ {
+ var match = System.Text.RegularExpressions.Regex.Match(versionInfo.ProductVersion, @"(\d{4})");
+ if (match.Success)
+ {
+ return match.Groups[1].Value;
+ }
+ }
+ }
+ }
+ catch
+ {
+ // 忽略错误
+ }
+ return null;
+ }
+
+ ///
+ /// 获取系统中安装的所有Revit版本
+ ///
+ private List GetInstalledRevitVersions()
+ {
+ var versions = new HashSet();
+
+ try
+ {
+ // 从注册表获取
+ using (var key = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Autodesk\Revit"))
+ {
+ if (key != null)
+ {
+ foreach (var subKeyName in key.GetSubKeyNames())
+ {
+ if (int.TryParse(subKeyName, out int year) && year >= 2018 && year <= 2030)
+ {
+ versions.Add(subKeyName);
+ }
+ }
+ }
+ }
+
+ // 从WOW6432Node获取
+ using (var key = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(@"SOFTWARE\WOW6432Node\Autodesk\Revit"))
+ {
+ if (key != null)
+ {
+ foreach (var subKeyName in key.GetSubKeyNames())
+ {
+ if (int.TryParse(subKeyName, out int year) && year >= 2018 && year <= 2030)
+ {
+ versions.Add(subKeyName);
+ }
+ }
+ }
+ }
+
+ // 从文件系统检查
+ var programFiles = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
+ var programFilesX86 = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86);
+
+ foreach (var baseDir in new[] { programFiles, programFilesX86 })
+ {
+ if (string.IsNullOrEmpty(baseDir)) continue;
+
+ var autodeskDir = Path.Combine(baseDir, "Autodesk");
+ if (Directory.Exists(autodeskDir))
+ {
+ foreach (var dir in Directory.GetDirectories(autodeskDir, "Revit *"))
+ {
+ var dirName = Path.GetFileName(dir);
+ var match = System.Text.RegularExpressions.Regex.Match(dirName, @"Revit (\d{4})");
+ if (match.Success)
+ {
+ versions.Add(match.Groups[1].Value);
+ }
+ }
+ }
+ }
+ }
+ catch
+ {
+ // 忽略错误
+ }
+
+ return versions.ToList();
+ }
+
+ ///
+ /// 获取所有可能的插件目录
+ ///
+ private List GetAllAddinDirectories(string revitVersion)
+ {
+ var directories = new List();
+
+ // 1. 用户级插件目录 (当前用户) - .addin文件
+ var userAddinPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
+ "Autodesk", "Revit", "Addins", revitVersion);
+ directories.Add(userAddinPath);
+
+ // 2. 用户级ApplicationPlugins目录 - .bundle文件夹
+ var userAppPluginsPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
+ "Autodesk", "ApplicationPlugins");
+ directories.Add(userAppPluginsPath);
+
+ // 3. 全局插件目录 (所有用户) - .addin文件
+ var globalAddinPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData),
+ "Autodesk", "Revit", "Addins", revitVersion);
+ directories.Add(globalAddinPath);
+
+ // 4. 全局ApplicationPlugins目录 - .bundle文件夹
+ var globalAppPluginsPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData),
+ "Autodesk", "ApplicationPlugins");
+ directories.Add(globalAppPluginsPath);
+
+ // 5. 程序文件目录下的通用插件目录
+ var programFilesAddinPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles),
+ "Autodesk", $"Revit {revitVersion}", "AddIns");
+ if (Directory.Exists(programFilesAddinPath))
+ {
+ directories.Add(programFilesAddinPath);
+ }
+
+ // 6. 程序文件(x86)目录下的插件目录
+ var programFilesX86 = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86);
+ if (!string.IsNullOrEmpty(programFilesX86))
+ {
+ var programFilesX86AddinPath = Path.Combine(programFilesX86, "Autodesk", $"Revit {revitVersion}", "AddIns");
+ if (Directory.Exists(programFilesX86AddinPath))
+ {
+ directories.Add(programFilesX86AddinPath);
+ }
+ }
+
+ // 去重并返回存在的目录
+ return directories.Distinct().Where(Directory.Exists).ToList();
+ }
+
+ ///
+ /// 获取Revit的安装路径
+ ///
+ private List GetRevitInstallPaths(string revitVersion)
+ {
+ var installPaths = new List();
+
+ try
+ {
+ // 从注册表查找Revit安装路径
+ using (var key = Microsoft.Win32.Registry.LocalMachine.OpenSubKey($@"SOFTWARE\Autodesk\Revit\{revitVersion}"))
+ {
+ if (key != null)
+ {
+ var installPath = key.GetValue("InstallationPath") as string;
+ if (!string.IsNullOrEmpty(installPath) && Directory.Exists(installPath))
+ {
+ installPaths.Add(installPath);
+ }
+ }
+ }
+
+ // 从注册表查找其他可能的路径 (WOW6432Node for 32-bit apps on 64-bit Windows)
+ using (var key = Microsoft.Win32.Registry.LocalMachine.OpenSubKey($@"SOFTWARE\WOW6432Node\Autodesk\Revit\{revitVersion}"))
+ {
+ if (key != null)
+ {
+ var installPath = key.GetValue("InstallationPath") as string;
+ if (!string.IsNullOrEmpty(installPath) && Directory.Exists(installPath))
+ {
+ installPaths.Add(installPath);
+ }
+ }
+ }
+ }
+ catch
+ {
+ // 忽略注册表读取错误
+ }
+
+ // 如果注册表查找失败,尝试常见的安装路径
+ var commonPaths = new[]
+ {
+ Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "Autodesk", $"Revit {revitVersion}"),
+ Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), "Autodesk", $"Revit {revitVersion}"),
+ Path.Combine("C:", "Program Files", "Autodesk", $"Revit {revitVersion}"),
+ Path.Combine("C:", "Program Files (x86)", "Autodesk", $"Revit {revitVersion}")
+ };
+
+ foreach (var path in commonPaths)
+ {
+ if (Directory.Exists(path))
+ {
+ installPaths.Add(path);
+ }
+ }
+
+ return installPaths.Distinct().ToList();
+ }
+
+ ///
+ /// 获取所有已安装的插件(包括已禁用的和Bundle格式的)
+ ///
+ public List GetInstalledAddins()
+ {
+ var addins = new List();
+
+ // 扫描所有插件目录
+ foreach (var addinFolderPath in _addinFolderPaths)
+ {
+ if (!Directory.Exists(addinFolderPath))
+ continue;
+
+ try
+ {
+ // 检查是否是ApplicationPlugins目录(Bundle格式)
+ if (addinFolderPath.Contains("ApplicationPlugins"))
+ {
+ // 扫描Bundle格式插件
+ var bundleAddins = ScanBundlePlugins(addinFolderPath);
+ addins.AddRange(bundleAddins);
+ }
+ else
+ {
+ // 扫描传统.addin文件格式
+ var traditionalAddins = ScanTraditionalAddins(addinFolderPath);
+ addins.AddRange(traditionalAddins);
+ }
+ }
+ catch (Exception ex)
+ {
+ // 记录目录访问错误
+ var errorAddin = new AddinInfo
+ {
+ Name = $"目录访问错误: {Path.GetFileName(addinFolderPath)}",
+ AddinFilePath = addinFolderPath,
+ SourceDirectory = addinFolderPath,
+ HasErrors = true,
+ ErrorMessages = new List { $"无法访问目录: {ex.Message}" },
+ Status = "访问错误",
+ IsEnabled = false
+ };
+ addins.Add(errorAddin);
+ }
+ }
+
+ return addins;
+ }
+
+ ///
+ /// 扫描传统.addin文件格式的插件
+ ///
+ private List ScanTraditionalAddins(string addinFolderPath)
+ {
+ var addins = new List();
+
+ // 扫描所有.addin文件(包括.disabled文件)
+ var addinFiles = Directory.GetFiles(addinFolderPath, "*.addin", SearchOption.AllDirectories)
+ .Concat(Directory.GetFiles(addinFolderPath, "*.addin.disabled", SearchOption.AllDirectories))
+ .ToArray();
+
+ foreach (var addinFile in addinFiles)
+ {
+ try
+ {
+ var addinInfos = ParseAddinFile(addinFile);
+ foreach (var addinInfo in addinInfos)
+ {
+ // 标记插件来源目录
+ addinInfo.SourceDirectory = addinFolderPath;
+ addins.Add(addinInfo);
+ }
+ }
+ catch (Exception ex)
+ {
+ // 记录解析错误
+ var errorAddin = new AddinInfo
+ {
+ Name = Path.GetFileNameWithoutExtension(addinFile),
+ AddinFilePath = addinFile,
+ SourceDirectory = addinFolderPath,
+ HasErrors = true,
+ ErrorMessages = new List { ex.Message },
+ Status = "解析错误",
+ IsEnabled = false
+ };
+ addins.Add(errorAddin);
+ }
+ }
+
+ return addins;
+ }
+
+ ///
+ /// 扫描Bundle格式的插件(.bundle文件夹)
+ ///
+ private List ScanBundlePlugins(string appPluginsPath)
+ {
+ var addins = new List();
+
+ // 查找所有.bundle文件夹
+ var bundleFolders = Directory.GetDirectories(appPluginsPath, "*.bundle", SearchOption.TopDirectoryOnly);
+
+ foreach (var bundleFolder in bundleFolders)
+ {
+ try
+ {
+ // 查找PackageContents.xml文件
+ var packageContentsPath = Path.Combine(bundleFolder, "PackageContents.xml");
+ if (File.Exists(packageContentsPath))
+ {
+ var bundleAddins = ParseBundlePackage(packageContentsPath, bundleFolder);
+ addins.AddRange(bundleAddins);
+ }
+ else
+ {
+ // 如果没有PackageContents.xml,记录为错误
+ var errorAddin = new AddinInfo
+ {
+ Name = Path.GetFileNameWithoutExtension(bundleFolder),
+ AddinFilePath = bundleFolder,
+ SourceDirectory = appPluginsPath,
+ HasErrors = true,
+ ErrorMessages = new List { "Bundle文件夹中缺少PackageContents.xml文件" },
+ Status = "Bundle格式错误",
+ IsEnabled = false
+ };
+ addins.Add(errorAddin);
+ }
+ }
+ catch (Exception ex)
+ {
+ // 记录Bundle解析错误
+ var errorAddin = new AddinInfo
+ {
+ Name = Path.GetFileNameWithoutExtension(bundleFolder),
+ AddinFilePath = bundleFolder,
+ SourceDirectory = appPluginsPath,
+ HasErrors = true,
+ ErrorMessages = new List { $"Bundle解析错误: {ex.Message}" },
+ Status = "Bundle解析错误",
+ IsEnabled = false
+ };
+ addins.Add(errorAddin);
+ }
+ }
+
+ return addins;
+ }
+
+ ///
+ /// 解析Bundle格式的PackageContents.xml文件
+ ///
+ private List ParseBundlePackage(string packageContentsPath, string bundleFolder)
+ {
+ var addins = new List();
+ var doc = XDocument.Load(packageContentsPath);
+
+ // 获取Bundle的基本信息
+ var packageElement = doc.Element("ApplicationPackage");
+ if (packageElement == null) return addins;
+
+ var bundleName = packageElement.Attribute("Name")?.Value ?? Path.GetFileNameWithoutExtension(bundleFolder);
+ var bundleVersion = packageElement.Attribute("Version")?.Value ?? "Unknown";
+ var bundleDescription = packageElement.Attribute("Description")?.Value ?? "";
+ var bundleAuthor = packageElement.Attribute("Author")?.Value ?? "Unknown";
+
+ // 查找支持的Revit版本
+ var supportedVersions = new List();
+ var componentsElement = packageElement.Element("Components");
+ if (componentsElement != null)
+ {
+ foreach (var runtimeRequirements in componentsElement.Descendants("RuntimeRequirements"))
+ {
+ var platform = runtimeRequirements.Attribute("Platform")?.Value;
+ var version = runtimeRequirements.Attribute("Version")?.Value;
+
+ if (platform == "Revit" && !string.IsNullOrEmpty(version))
+ {
+ supportedVersions.Add(version);
+ }
+ }
+ }
+
+ // 查找所有的AddIn元素
+ foreach (var componentElement in componentsElement?.Elements("ComponentEntry") ?? Enumerable.Empty())
+ {
+ var moduleType = componentElement.Attribute("ModuleType")?.Value;
+ if (moduleType != "Managed") continue; // 只处理托管代码插件
+
+ var appName = componentElement.Attribute("AppName")?.Value ?? bundleName;
+ var appDescription = componentElement.Attribute("AppDescription")?.Value ?? bundleDescription;
+
+ // 检查是否被禁用(通过重命名bundle文件夹)
+ bool isDisabled = bundleFolder.EndsWith(".bundle.disabled") || bundleFolder.EndsWith(".disabled");
+
+ var addin = new AddinInfo
+ {
+ Name = appName,
+ Description = appDescription,
+ Version = bundleVersion,
+ Developer = bundleAuthor,
+ AddinFilePath = packageContentsPath,
+ SourceDirectory = Path.GetDirectoryName(bundleFolder),
+ Type = AddinType.Application, // Bundle通常是Application类型
+ CurrentRevitVersion = _revitVersion,
+ LastModified = File.GetLastWriteTime(packageContentsPath),
+ IsEnabled = !isDisabled,
+ Status = isDisabled ? "已禁用" : "已启用"
+ };
+
+ // 添加支持的版本
+ foreach (var version in supportedVersions)
+ {
+ addin.SupportedRevitVersions.Add(version);
+ }
+
+ // 查找程序集路径
+ var appModule = componentElement.Element("AppModule");
+ if (appModule != null)
+ {
+ var relativePath = appModule.Value;
+ if (!string.IsNullOrEmpty(relativePath))
+ {
+ var assemblyPath = Path.Combine(bundleFolder, "Contents", relativePath);
+ addin.AssemblyPath = assemblyPath;
+
+ // 尝试获取程序集版本信息
+ if (File.Exists(assemblyPath))
+ {
+ try
+ {
+ var assembly = Assembly.LoadFrom(assemblyPath);
+ var assemblyVersion = assembly.GetName().Version?.ToString();
+ if (!string.IsNullOrEmpty(assemblyVersion))
+ {
+ addin.Version = assemblyVersion;
+ }
+
+ // 查找主类
+ var types = assembly.GetTypes();
+ var appType = types.FirstOrDefault(t => t.GetInterfaces()
+ .Any(i => i.Name.Contains("IExternalApplication")));
+ if (appType != null)
+ {
+ addin.ClassName = appType.FullName;
+ }
+ }
+ catch
+ {
+ // 忽略程序集加载错误
+ }
+ }
+ }
+ }
+
+ addins.Add(addin);
+ }
+
+ return addins;
+ }
+
+ ///
+ /// 启用插件 - 支持传统.addin文件和Bundle格式
+ ///
+ public bool EnableAddin(AddinInfo addin)
+ {
+ try
+ {
+ // 检查是否是Bundle格式插件
+ if (IsBundlePlugin(addin))
+ {
+ return EnableBundlePlugin(addin);
+ }
+ else
+ {
+ return EnableTraditionalPlugin(addin);
+ }
+ }
+ catch (Exception ex)
+ {
+ addin.ErrorMessages.Add($"启用失败: {ex.Message}");
+ return false;
+ }
+ }
+
+ ///
+ /// 禁用插件 - 支持传统.addin文件和Bundle格式
+ ///
+ public bool DisableAddin(AddinInfo addin)
+ {
+ try
+ {
+ // 检查是否是Bundle格式插件
+ if (IsBundlePlugin(addin))
+ {
+ return DisableBundlePlugin(addin);
+ }
+ else
+ {
+ return DisableTraditionalPlugin(addin);
+ }
+ }
+ catch (Exception ex)
+ {
+ addin.ErrorMessages.Add($"禁用失败: {ex.Message}");
+ return false;
+ }
+ }
+
+ ///
+ /// 检查是否是Bundle格式插件
+ ///
+ private bool IsBundlePlugin(AddinInfo addin)
+ {
+ return addin.AddinFilePath.Contains("ApplicationPlugins") &&
+ addin.AddinFilePath.EndsWith("PackageContents.xml");
+ }
+
+ ///
+ /// 启用传统.addin文件格式插件
+ ///
+ private bool EnableTraditionalPlugin(AddinInfo addin)
+ {
+ if (!File.Exists(addin.AddinFilePath))
+ return false;
+
+ // 如果文件以.disabled结尾,重命名回.addin
+ if (addin.AddinFilePath.EndsWith(".disabled"))
+ {
+ string enabledPath = addin.AddinFilePath.Replace(".addin.disabled", ".addin");
+ File.Move(addin.AddinFilePath, enabledPath);
+ addin.AddinFilePath = enabledPath;
+ }
+
+ addin.IsEnabled = true;
+ SaveAddinState(addin);
+ return true;
+ }
+
+ ///
+ /// 禁用传统.addin文件格式插件
+ ///
+ private bool DisableTraditionalPlugin(AddinInfo addin)
+ {
+ if (!File.Exists(addin.AddinFilePath))
+ return false;
+
+ // 如果文件以.addin结尾,重命名为.addin.disabled
+ if (addin.AddinFilePath.EndsWith(".addin"))
+ {
+ string disabledPath = addin.AddinFilePath + ".disabled";
+ File.Move(addin.AddinFilePath, disabledPath);
+ addin.AddinFilePath = disabledPath;
+ }
+
+ addin.IsEnabled = false;
+ SaveAddinState(addin);
+ return true;
+ }
+
+ ///
+ /// 启用Bundle格式插件
+ ///
+ private bool EnableBundlePlugin(AddinInfo addin)
+ {
+ var bundleFolder = Path.GetDirectoryName(addin.AddinFilePath);
+ if (!Directory.Exists(bundleFolder))
+ return false;
+
+ // 如果Bundle文件夹以.disabled结尾,重命名回.bundle
+ if (bundleFolder.EndsWith(".disabled"))
+ {
+ string enabledPath = bundleFolder.Replace(".bundle.disabled", ".bundle");
+ Directory.Move(bundleFolder, enabledPath);
+
+ // 更新插件信息中的路径
+ addin.AddinFilePath = Path.Combine(enabledPath, "PackageContents.xml");
+ addin.SourceDirectory = Path.GetDirectoryName(enabledPath);
+ }
+ else if (bundleFolder.EndsWith(".bundle.disabled"))
+ {
+ string enabledPath = bundleFolder.Replace(".bundle.disabled", ".bundle");
+ Directory.Move(bundleFolder, enabledPath);
+
+ // 更新插件信息中的路径
+ addin.AddinFilePath = Path.Combine(enabledPath, "PackageContents.xml");
+ addin.SourceDirectory = Path.GetDirectoryName(enabledPath);
+ }
+
+ addin.IsEnabled = true;
+ SaveAddinState(addin);
+ return true;
+ }
+
+ ///
+ /// 禁用Bundle格式插件
+ ///
+ private bool DisableBundlePlugin(AddinInfo addin)
+ {
+ var bundleFolder = Path.GetDirectoryName(addin.AddinFilePath);
+ if (!Directory.Exists(bundleFolder))
+ return false;
+
+ // 如果Bundle文件夹以.bundle结尾,重命名为.bundle.disabled
+ if (bundleFolder.EndsWith(".bundle"))
+ {
+ string disabledPath = bundleFolder + ".disabled";
+ Directory.Move(bundleFolder, disabledPath);
+
+ // 更新插件信息中的路径
+ addin.AddinFilePath = Path.Combine(disabledPath, "PackageContents.xml");
+ addin.SourceDirectory = Path.GetDirectoryName(disabledPath);
+ }
+
+ addin.IsEnabled = false;
+ SaveAddinState(addin);
+ return true;
+ }
+
+ ///
+ /// 卸载插件
+ ///
+ public bool UninstallAddin(AddinInfo addin)
+ {
+ try
+ {
+ // 删除.addin文件
+ if (File.Exists(addin.AddinFilePath))
+ {
+ File.Delete(addin.AddinFilePath);
+ }
+
+ // 删除程序集文件(如果在插件目录下)
+ if (File.Exists(addin.AssemblyPath) &&
+ _addinFolderPaths.Any(path => addin.AssemblyPath.StartsWith(path)))
+ {
+ File.Delete(addin.AssemblyPath);
+ }
+
+ RemoveAddinState(addin);
+ return true;
+ }
+ catch (Exception ex)
+ {
+ addin.ErrorMessages.Add($"卸载失败: {ex.Message}");
+ return false;
+ }
+ }
+
+ ///
+ /// 热重载插件 - 增强版本
+ ///
+ public bool HotReloadAddin(AddinInfo addin)
+ {
+ try
+ {
+ // 先禁用插件
+ if (addin.IsEnabled)
+ {
+ DisableAddin(addin);
+ }
+
+ // 卸载已加载的程序集
+ if (_loadedAssemblies.ContainsKey(addin.AssemblyPath))
+ {
+ _loadedAssemblies.Remove(addin.AssemblyPath);
+ }
+
+ // 等待一段时间确保���件释放
+ System.Threading.Thread.Sleep(500);
+
+ // 重新启用插件
+ EnableAddin(addin);
+
+ addin.LastModified = File.GetLastWriteTime(addin.AssemblyPath);
+ return true;
+ }
+ catch (Exception ex)
+ {
+ addin.ErrorMessages.Add($"热重载失败: {ex.Message}");
+ return false;
+ }
+ }
+
+ ///
+ /// 安装插件
+ ///
+ public bool InstallAddin(string addinFilePath, string targetDirectory = null)
+ {
+ try
+ {
+ if (targetDirectory == null)
+ {
+ // 默认安装到用户插件目录(第一个目录)
+ targetDirectory = _addinFolderPaths.FirstOrDefault() ??
+ Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
+ "Autodesk", "Revit", "Addins", _revitVersion);
+ }
+
+ var fileName = Path.GetFileName(addinFilePath);
+ var targetPath = Path.Combine(targetDirectory, fileName);
+
+ File.Copy(addinFilePath, targetPath, true);
+ return true;
+ }
+ catch (Exception)
+ {
+ return false;
+ }
+ }
+
+ ///
+ /// 检查插件兼容性
+ ///
+ public bool CheckCompatibility(AddinInfo addin)
+ {
+ return addin.SupportedRevitVersions.Contains(_revitVersion) ||
+ addin.SupportedRevitVersions.Count == 0;
+ }
+
+ ///
+ /// 获取插件错误日志
+ ///
+ public List GetAddinErrors(AddinInfo addin)
+ {
+ return addin.ErrorMessages;
+ }
+
+ ///
+ /// 安装临时调试插件 - 直接加载到Revit中
+ ///
+ public bool InstallTemporaryDebugAddin(CommandInfo commandInfo, string commandName)
+ {
+ try
+ {
+#if REVIT_API_AVAILABLE
+ // 直接通过Revit API加载插件
+ var uiApp = AddinManagerApp.UiApplication;
+ if (uiApp == null)
+ {
+ System.Diagnostics.Debug.WriteLine("Revit应用程序未初始化");
+ return false;
+ }
+
+ // 加载程序集
+ var assembly = Assembly.LoadFrom(commandInfo.AssemblyPath);
+ var commandType = assembly.GetType(commandInfo.ClassName);
+
+ if (commandType == null)
+ {
+ System.Diagnostics.Debug.WriteLine($"无法找到类型: {commandInfo.ClassName}");
+ return false;
+ }
+
+ // 创建外部命令数据
+ var commandData = new Autodesk.Revit.UI.ExternalCommandData();
+
+ // 将命令添加到临时调试命令列表
+ var tempCommandId = $"DebugCmd_{DateTime.Now:yyyyMMdd_HHmmss}_{Guid.NewGuid().ToString("N")[..8]}";
+
+ // 创建临时按钮(可选,用于UI访问)
+ try
+ {
+ var ribbonTab = uiApp.GetRibbonTabs().FirstOrDefault(t => t == "调试") ??
+ uiApp.CreateRibbonTab("调试");
+
+ var panel = uiApp.GetRibbonPanels("调试").FirstOrDefault(p => p.Name == "临时命令") ??
+ uiApp.CreateRibbonPanel("调试", "临时命令");
+
+ var buttonData = new Autodesk.Revit.UI.PushButtonData(
+ tempCommandId,
+ commandName,
+ commandInfo.AssemblyPath,
+ commandInfo.ClassName);
+
+ var button = panel.AddItem(buttonData) as Autodesk.Revit.UI.PushButton;
+ if (button != null)
+ {
+ button.ToolTip = $"临时调试命令: {commandName}";
+ button.LongDescription = $"从 {Path.GetFileName(commandInfo.AssemblyPath)} 加载的调试命令";
+ }
+ }
+ catch (Exception ex)
+ {
+ System.Diagnostics.Debug.WriteLine($"创建临时按钮失败: {ex.Message}");
+ // 即使按钮创建失败,命令仍然可以通过其他方式调用
+ }
+
+ // 记录已加载的临时命令
+ if (!_loadedAssemblies.ContainsKey(commandInfo.AssemblyPath))
+ {
+ _loadedAssemblies.Add(commandInfo.AssemblyPath, assembly);
+ }
+
+ return true;
+#else
+ // 在没有Revit API的环境中,仍然创建.addin文件作为fallback
+ return CreateTemporaryAddinFile(commandInfo, commandName);
+#endif
+ }
+ catch (Exception ex)
+ {
+ System.Diagnostics.Debug.WriteLine($"直接加载插件失败: {ex.Message}");
+ return false;
+ }
+ }
+
+ ///
+ /// 创建临时.addin文件作为备用方案
+ ///
+ private bool CreateTemporaryAddinFile(CommandInfo commandInfo, string commandName)
+ {
+ try
+ {
+ // 确保用户插件目录存在
+ var userAddinPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
+ "Autodesk", "Revit", "Addins", _revitVersion);
+
+ if (!Directory.Exists(userAddinPath))
+ {
+ Directory.CreateDirectory(userAddinPath);
+ }
+
+ // 生成临时插件的唯一标识
+ var tempAddinId = $"DebugAddin_{DateTime.Now:yyyyMMdd_HHmmss}_{Guid.NewGuid().ToString("N")[..8]}";
+ var tempAddinFileName = $"{tempAddinId}.addin";
+ var tempAddinFilePath = Path.Combine(userAddinPath, tempAddinFileName);
+
+ // 创建临时.addin文件内容
+ var addinXml = CreateDebugAddinXml(commandInfo, commandName, tempAddinId);
+
+ // 写入.addin文件
+ File.WriteAllText(tempAddinFilePath, addinXml);
+
+ return true;
+ }
+ catch (Exception ex)
+ {
+ System.Diagnostics.Debug.WriteLine($"创建临时addin文件失败: {ex.Message}");
+ return false;
+ }
+ }
+
+ ///
+ /// 直接执行命令(用于调试测试)
+ ///
+ public bool ExecuteCommand(CommandInfo commandInfo)
+ {
+ try
+ {
+#if REVIT_API_AVAILABLE
+ var uiApp = AddinManagerApp.UiApplication;
+ if (uiApp == null) return false;
+
+ // 加载程序集
+ var assembly = Assembly.LoadFrom(commandInfo.AssemblyPath);
+ var commandType = assembly.GetType(commandInfo.ClassName);
+
+ if (commandType == null) return false;
+
+ // 创建命令实例
+ var commandInstance = Activator.CreateInstance(commandType) as Autodesk.Revit.UI.IExternalCommand;
+ if (commandInstance == null) return false;
+
+ // 准备命令数据
+ var commandData = new Autodesk.Revit.UI.ExternalCommandData
+ {
+ Application = uiApp,
+ JournalData = new Autodesk.Revit.DB.NameValueMap()
+ };
+
+ var message = string.Empty;
+ var elementSet = new Autodesk.Revit.DB.ElementSet();
+
+ // 执行命令
+ var result = commandInstance.Execute(commandData, ref message, elementSet);
+
+ return result == Autodesk.Revit.UI.Result.Succeeded;
+#else
+ return false;
+#endif
+ }
+ catch (Exception ex)
+ {
+ System.Diagnostics.Debug.WriteLine($"执行命令失败: {ex.Message}");
+ return false;
+ }
+ }
+
+ ///
+ /// 卸载临时调试插件
+ ///
+ public bool UnloadTemporaryDebugAddin(string assemblyPath)
+ {
+ try
+ {
+ // 从已加载程序集列表中移除
+ if (_loadedAssemblies.ContainsKey(assemblyPath))
+ {
+ _loadedAssemblies.Remove(assemblyPath);
+ }
+
+ // 释放程序集引用
+ ReleaseDllReferences();
+
+ return true;
+ }
+ catch (Exception ex)
+ {
+ System.Diagnostics.Debug.WriteLine($"卸载临时插件失败: {ex.Message}");
+ return false;
+ }
+ }
+
+ ///
+ /// 创建调试插件的XML内容
+ ///
+ private string CreateDebugAddinXml(CommandInfo commandInfo, string commandName, string addinId)
+ {
+ var xml = $@"
+
+
+ {commandName}
+ {commandInfo.AssemblyPath}
+ {commandInfo.ClassName}
+ 临时调试插件 - {commandName}
+ AddinManager.Debug
+ Revit插件管理器调试功能
+ {_revitVersion}
+ {commandInfo.TransactionMode}
+
+";
+
+ return xml;
+ }
+
+ ///
+ /// 从DLL中获取命令信息
+ ///
+ public List GetCommandsFromDll(string dllPath)
+ {
+ var commands = new List();
+
+ try
+ {
+ if (!File.Exists(dllPath))
+ {
+ return commands;
+ }
+
+ // 加载程序集
+ var assembly = Assembly.LoadFrom(dllPath);
+
+ // 查找实现IExternalCommand接口的类
+ foreach (var type in assembly.GetTypes())
+ {
+ if (type.IsClass && !type.IsAbstract)
+ {
+ // 检查是否实现了IExternalCommand接口
+ var interfaces = type.GetInterfaces();
+ bool implementsIExternalCommand = false;
+
+ foreach (var interfaceType in interfaces)
+ {
+ if (interfaceType.Name == "IExternalCommand" ||
+ interfaceType.FullName?.Contains("IExternalCommand") == true)
+ {
+ implementsIExternalCommand = true;
+ break;
+ }
+ }
+
+ if (implementsIExternalCommand)
+ {
+ var commandInfo = new CommandInfo
+ {
+ ClassName = type.FullName,
+ AssemblyPath = dllPath,
+ Assembly = assembly,
+ Type = type,
+ TransactionMode = GetTransactionMode(type)
+ };
+
+ commands.Add(commandInfo);
+ }
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ // 返回错误信息
+ var errorCommand = new CommandInfo
+ {
+ ClassName = "Error",
+ AssemblyPath = dllPath,
+ HasErrors = true,
+ ErrorMessage = ex.Message
+ };
+ commands.Add(errorCommand);
+ }
+
+ return commands;
+ }
+
+ ///
+ /// 获取命令的事务模式
+ ///
+ private string GetTransactionMode(Type type)
+ {
+ try
+ {
+ // 查找Transaction特性
+ var transactionAttribute = type.GetCustomAttributes(false)
+ .FirstOrDefault(attr => attr.GetType().Name.Contains("Transaction"));
+
+ if (transactionAttribute != null)
+ {
+ // 尝试获取事务模式值
+ var modeProperty = transactionAttribute.GetType().GetProperty("Mode");
+ if (modeProperty != null)
+ {
+ var modeValue = modeProperty.GetValue(transactionAttribute);
+ return modeValue?.ToString() ?? "Manual";
+ }
+ }
+
+ return "Manual"; // 默认事务模式
+ }
+ catch
+ {
+ return "Manual";
+ }
+ }
+
+ ///
+ /// 附加到Revit进程进行调试
+ ///
+ public bool AttachToRevitProcess()
+ {
+ try
+ {
+ var revitProcesses = Process.GetProcessesByName("Revit");
+ if (revitProcesses.Length == 0)
+ {
+ return false;
+ }
+
+ // 如果有多个Revit进程,选择第一个
+ var revitProcess = revitProcesses[0];
+ var processId = revitProcess.Id;
+
+ // 尝试启动Visual Studio并附加到进程
+ var vsInstances = GetVisualStudioInstances();
+ if (vsInstances.Count > 0)
+ {
+ var vsPath = vsInstances.First();
+ var startInfo = new ProcessStartInfo
+ {
+ FileName = vsPath,
+ Arguments = $"/debugexe",
+ UseShellExecute = true
+ };
+
+ Process.Start(startInfo);
+ return true;
+ }
+
+ return false;
+ }
+ catch
+ {
+ return false;
+ }
+ }
+
+ ///
+ /// 获取已安装的Visual Studio实例
+ ///
+ private List GetVisualStudioInstances()
+ {
+ var instances = new List();
+
+ try
+ {
+ // 查找Visual Studio的常见安装路径
+ var possiblePaths = new[]
+ {
+ @"Microsoft Visual Studio\2022\Enterprise\Common7\IDE\devenv.exe",
+ @"Microsoft Visual Studio\2022\Professional\Common7\IDE\devenv.exe",
+ @"Microsoft Visual Studio\2022\Community\Common7\IDE\devenv.exe",
+ @"Microsoft Visual Studio\2019\Enterprise\Common7\IDE\devenv.exe",
+ @"Microsoft Visual Studio\2019\Professional\Common7\IDE\devenv.exe",
+ @"Microsoft Visual Studio\2019\Community\Common7\IDE\devenv.exe"
+ };
+
+ var programFiles = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
+ var programFilesX86 = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86);
+
+ foreach (var basePath in new[] { programFiles, programFilesX86 })
+ {
+ if (string.IsNullOrEmpty(basePath)) continue;
+
+ foreach (var relativePath in possiblePaths)
+ {
+ var fullPath = Path.Combine(basePath, relativePath);
+ if (File.Exists(fullPath))
+ {
+ instances.Add(fullPath);
+ }
+ }
+ }
+ }
+ catch
+ {
+ // 忽略错误
+ }
+
+ return instances;
+ }
+
+ ///
+ /// 清理临时调试插件
+ ///
+ public void CleanupTemporaryDebugAddins()
+ {
+ try
+ {
+ var userAddinPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
+ "Autodesk", "Revit", "Addins", _revitVersion);
+
+ if (!Directory.Exists(userAddinPath)) return;
+
+ // 查找所有临时调试插件文件
+ var tempFiles = Directory.GetFiles(userAddinPath, "DebugAddin_*.addin");
+
+ foreach (var tempFile in tempFiles)
+ {
+ try
+ {
+ // 检查文件是否超过1天,如果是则删除
+ var fileInfo = new FileInfo(tempFile);
+ if (DateTime.Now - fileInfo.CreationTime > TimeSpan.FromDays(1))
+ {
+ File.Delete(tempFile);
+ }
+ }
+ catch
+ {
+ // 忽略单个文件删除失败
+ }
+ }
+ }
+ catch
+ {
+ // 忽略清理错误
+ }
+ }
+
+ ///
+ /// 释放程序集引用以便重新编译
+ ///
+ public void ReleaseDllReferences()
+ {
+ try
+ {
+ _loadedAssemblies.Clear();
+
+ // 强制垃圾回收
+ GC.Collect();
+ GC.WaitForPendingFinalizers();
+ GC.Collect();
+ }
+ catch
+ {
+ // 忽略错误
+ }
+ }
+
+ private void EnsureDirectoriesExist()
+ {
+ foreach (var directory in _addinFolderPaths)
+ {
+ if (!Directory.Exists(directory))
+ {
+ try
+ {
+ Directory.CreateDirectory(directory);
+ }
+ catch
+ {
+ // 忽略创建目录失败
+ }
+ }
+ }
+ }
+
+ private void SaveAddinState(AddinInfo addin)
+ {
+ try
+ {
+ var states = new Dictionary();
+
+ if (File.Exists(_configPath))
+ {
+ var json = File.ReadAllText(_configPath);
+ states = JsonConvert.DeserializeObject>(json) ?? new Dictionary();
+ }
+
+ var key = $"{addin.ClassName}|{Path.GetFileNameWithoutExtension(addin.AddinFilePath)}";
+ states[key] = addin.IsEnabled;
+
+ var updatedJson = JsonConvert.SerializeObject(states, Formatting.Indented);
+ File.WriteAllText(_configPath, updatedJson);
+ }
+ catch
+ {
+ // 忽略保存错误
+ }
+ }
+
+ private void RemoveAddinState(AddinInfo addin)
+ {
+ try
+ {
+ if (!File.Exists(_configPath))
+ return;
+
+ var json = File.ReadAllText(_configPath);
+ var states = JsonConvert.DeserializeObject>(json);
+
+ if (states != null)
+ {
+ var key = $"{addin.ClassName}|{Path.GetFileNameWithoutExtension(addin.AddinFilePath)}";
+ if (states.ContainsKey(key))
+ {
+ states.Remove(key);
+ var updatedJson = JsonConvert.SerializeObject(states, Formatting.Indented);
+ File.WriteAllText(_configPath, updatedJson);
+ }
+ }
+ }
+ catch
+ {
+ // 忽略错误
+ }
+ }
+
+ ///
+ /// 解析.addin文件
+ ///
+ private List ParseAddinFile(string addinFilePath)
+ {
+ var addins = new List();
+ var doc = XDocument.Load(addinFilePath);
+
+ // 判断文件是否被禁用(通过文件扩展名)
+ bool isDisabled = addinFilePath.EndsWith(".disabled");
+
+ foreach (var addinElement in doc.Descendants("AddIn"))
+ {
+ var addin = new AddinInfo
+ {
+ AddinFilePath = addinFilePath,
+ CurrentRevitVersion = _revitVersion,
+ LastModified = File.GetLastWriteTime(addinFilePath),
+ IsEnabled = !isDisabled
+ };
+
+ // 解析类型
+ var typeAttr = addinElement.Attribute("Type")?.Value;
+ switch (typeAttr)
+ {
+ case "Command":
+ addin.Type = AddinType.Command;
+ break;
+ case "Application":
+ addin.Type = AddinType.Application;
+ break;
+ case "ExternalDBApplication":
+ addin.Type = AddinType.ExternalDBApplication;
+ break;
+ default:
+ addin.Type = AddinType.Unknown;
+ break;
+ }
+
+ addin.Name = addinElement.Element("Name")?.Value ?? "Unknown";
+ addin.Description = addinElement.Element("Description")?.Value ?? "";
+ addin.AssemblyPath = addinElement.Element("Assembly")?.Value ?? "";
+ addin.ClassName = addinElement.Element("FullClassName")?.Value ?? "";
+ addin.Developer = addinElement.Element("VendorId")?.Value ?? "Unknown";
+
+ // 解析版本兼容性
+ var versionElements = addinElement.Elements("SupportedVersion");
+ foreach (var versionElement in versionElements)
+ {
+ addin.SupportedRevitVersions.Add(versionElement.Value);
+ }
+
+ // 如果程序集路径是相对路径,转换为绝对路径
+ if (!Path.IsPathRooted(addin.AssemblyPath))
+ {
+ addin.AssemblyPath = Path.Combine(Path.GetDirectoryName(addinFilePath), addin.AssemblyPath);
+ }
+
+ // 尝试获取程序集版本信息
+ if (File.Exists(addin.AssemblyPath))
+ {
+ try
+ {
+ var assembly = Assembly.LoadFrom(addin.AssemblyPath);
+ addin.Version = assembly.GetName().Version?.ToString() ?? "Unknown";
+ }
+ catch
+ {
+ addin.Version = "Unknown";
+ }
+ }
+
+ addins.Add(addin);
+ }
+
+ return addins;
+ }
+
+ ///
+ /// 获取正在运行的Revit进程
+ ///
+ public List GetRevitProcesses()
+ {
+ var processes = new List();
+
+ try
+ {
+ var revitProcesses = Process.GetProcessesByName("Revit");
+
+ foreach (var process in revitProcesses)
+ {
+ try
+ {
+ var processInfo = new ProcessInfo
+ {
+ Id = process.Id,
+ ProcessName = process.ProcessName,
+ WindowTitle = process.MainWindowTitle,
+ StartTime = process.StartTime
+ };
+ processes.Add(processInfo);
+ }
+ catch
+ {
+ // 忽略无法访问的进程
+ }
+ }
+ }
+ catch
+ {
+ // 忽略获取进程失败
+ }
+
+ return processes;
+ }
+
+ ///
+ /// 清理调试插件
+ ///
+ public void CleanupDebugAddins()
+ {
+ try
+ {
+ // 清理临时调试文件
+ CleanupTemporaryDebugAddins();
+
+ // 释放程序集引用
+ ReleaseDllReferences();
+
+ System.Diagnostics.Debug.WriteLine("调试插件清理完成");
+ }
+ catch (Exception ex)
+ {
+ System.Diagnostics.Debug.WriteLine($"清理调试插件失败: {ex.Message}");
+ }
+ }
+ }
+
+ ///
+ /// 进程信息模型
+ ///
+ public class ProcessInfo
+ {
+ public int Id { get; set; }
+ public string ProcessName { get; set; }
+ public string WindowTitle { get; set; }
+ public DateTime StartTime { get; set; }
+ }
+}