From 85435fb67675fdb07d172e22d2581328c76d2f55 Mon Sep 17 00:00:00 2001 From: GG Z <903524121@qq.com> Date: Thu, 4 Sep 2025 22:38:49 +0800 Subject: [PATCH] =?UTF-8?q?addin=E9=A1=B9=E7=9B=AE=E6=B7=BB=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AddinManager/AddinManager.csproj | 76 + AddinManager/Services/AddinManagerService.cs | 1501 ++++++++++++++++++ 2 files changed, 1577 insertions(+) create mode 100644 AddinManager/AddinManager.csproj create mode 100644 AddinManager/Services/AddinManagerService.cs 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; } + } +}