Files
ShrlAlgoToolkit/AddinManager/Services/AddinManagerService.cs
2025-09-04 22:38:49 +08:00

1502 lines
54 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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
{
/// <summary>
/// 插件管理服务
/// </summary>
public class AddinManagerService
{
private readonly string _revitVersion;
private readonly List<string> _addinFolderPaths;
private readonly string _configPath;
private readonly Dictionary<string, Assembly> _loadedAssemblies = new Dictionary<string, Assembly>();
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();
}
/// <summary>
/// 动态检测当前运行的Revit版本
/// </summary>
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";
}
/// <summary>
/// 从进程信息中提取Revit版本
/// </summary>
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;
}
/// <summary>
/// 获取系统中安装的所有Revit版本
/// </summary>
private List<string> GetInstalledRevitVersions()
{
var versions = new HashSet<string>();
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();
}
/// <summary>
/// 获取所有可能的插件目录
/// </summary>
private List<string> GetAllAddinDirectories(string revitVersion)
{
var directories = new List<string>();
// 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();
}
/// <summary>
/// 获取Revit的安装路径
/// </summary>
private List<string> GetRevitInstallPaths(string revitVersion)
{
var installPaths = new List<string>();
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();
}
/// <summary>
/// 获取所有已安装的插件包括已禁用的和Bundle格式的
/// </summary>
public List<AddinInfo> GetInstalledAddins()
{
var addins = new List<AddinInfo>();
// 扫描所有插件目录
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<string> { $"无法访问目录: {ex.Message}" },
Status = "访问错误",
IsEnabled = false
};
addins.Add(errorAddin);
}
}
return addins;
}
/// <summary>
/// 扫描传统.addin文件格式的插件
/// </summary>
private List<AddinInfo> ScanTraditionalAddins(string addinFolderPath)
{
var addins = new List<AddinInfo>();
// 扫描所有.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<string> { ex.Message },
Status = "解析错误",
IsEnabled = false
};
addins.Add(errorAddin);
}
}
return addins;
}
/// <summary>
/// 扫描Bundle格式的插件.bundle文件夹
/// </summary>
private List<AddinInfo> ScanBundlePlugins(string appPluginsPath)
{
var addins = new List<AddinInfo>();
// 查找所有.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<string> { "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<string> { $"Bundle解析错误: {ex.Message}" },
Status = "Bundle解析错误",
IsEnabled = false
};
addins.Add(errorAddin);
}
}
return addins;
}
/// <summary>
/// 解析Bundle格式的PackageContents.xml文件
/// </summary>
private List<AddinInfo> ParseBundlePackage(string packageContentsPath, string bundleFolder)
{
var addins = new List<AddinInfo>();
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<string>();
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<XElement>())
{
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;
}
/// <summary>
/// 启用插件 - 支持传统.addin文件和Bundle格式
/// </summary>
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;
}
}
/// <summary>
/// 禁用插件 - 支持传统.addin文件和Bundle格式
/// </summary>
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;
}
}
/// <summary>
/// 检查是否是Bundle格式插件
/// </summary>
private bool IsBundlePlugin(AddinInfo addin)
{
return addin.AddinFilePath.Contains("ApplicationPlugins") &&
addin.AddinFilePath.EndsWith("PackageContents.xml");
}
/// <summary>
/// 启用传统.addin文件格式插件
/// </summary>
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;
}
/// <summary>
/// 禁用传统.addin文件格式插件
/// </summary>
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;
}
/// <summary>
/// 启用Bundle格式插件
/// </summary>
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;
}
/// <summary>
/// 禁用Bundle格式插件
/// </summary>
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;
}
/// <summary>
/// 卸载插件
/// </summary>
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;
}
}
/// <summary>
/// 热重载插件 - 增强版本
/// </summary>
public bool HotReloadAddin(AddinInfo addin)
{
try
{
// 先禁用插件
if (addin.IsEnabled)
{
DisableAddin(addin);
}
// 卸载已加载的程序集
if (_loadedAssemblies.ContainsKey(addin.AssemblyPath))
{
_loadedAssemblies.Remove(addin.AssemblyPath);
}
// 等待一段时间确保<E7A1AE><E4BF9D><EFBFBD>件释放
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;
}
}
/// <summary>
/// 安装插件
/// </summary>
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;
}
}
/// <summary>
/// 检查插件兼容性
/// </summary>
public bool CheckCompatibility(AddinInfo addin)
{
return addin.SupportedRevitVersions.Contains(_revitVersion) ||
addin.SupportedRevitVersions.Count == 0;
}
/// <summary>
/// 获取插件错误日志
/// </summary>
public List<string> GetAddinErrors(AddinInfo addin)
{
return addin.ErrorMessages;
}
/// <summary>
/// 安装临时调试插件 - 直接加载到Revit中
/// </summary>
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;
}
}
/// <summary>
/// 创建临时.addin文件作为备用方案
/// </summary>
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;
}
}
/// <summary>
/// 直接执行命令(用于调试测试)
/// </summary>
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;
}
}
/// <summary>
/// 卸载临时调试插件
/// </summary>
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;
}
}
/// <summary>
/// 创建调试插件的XML内容
/// </summary>
private string CreateDebugAddinXml(CommandInfo commandInfo, string commandName, string addinId)
{
var xml = $@"<?xml version=""1.0"" encoding=""utf-8""?>
<RevitAddIns>
<AddIn Type=""Command"">
<Name>{commandName}</Name>
<Assembly>{commandInfo.AssemblyPath}</Assembly>
<FullClassName>{commandInfo.ClassName}</FullClassName>
<Description>临时调试插件 - {commandName}</Description>
<VendorId>AddinManager.Debug</VendorId>
<VendorDescription>Revit插件管理器调试功能</VendorDescription>
<SupportedVersion>{_revitVersion}</SupportedVersion>
<Transaction>{commandInfo.TransactionMode}</Transaction>
</AddIn>
</RevitAddIns>";
return xml;
}
/// <summary>
/// 从DLL中获取命令信息
/// </summary>
public List<CommandInfo> GetCommandsFromDll(string dllPath)
{
var commands = new List<CommandInfo>();
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;
}
/// <summary>
/// 获取命令的事务模式
/// </summary>
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";
}
}
/// <summary>
/// 附加到Revit进程进行调试
/// </summary>
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;
}
}
/// <summary>
/// 获取已安装的Visual Studio实例
/// </summary>
private List<string> GetVisualStudioInstances()
{
var instances = new List<string>();
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;
}
/// <summary>
/// 清理临时调试插件
/// </summary>
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
{
// 忽略清理错误
}
}
/// <summary>
/// 释放程序集引用以便重新编译
/// </summary>
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<string, bool>();
if (File.Exists(_configPath))
{
var json = File.ReadAllText(_configPath);
states = JsonConvert.DeserializeObject<Dictionary<string, bool>>(json) ?? new Dictionary<string, bool>();
}
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<Dictionary<string, bool>>(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
{
// 忽略错误
}
}
/// <summary>
/// 解析.addin文件
/// </summary>
private List<AddinInfo> ParseAddinFile(string addinFilePath)
{
var addins = new List<AddinInfo>();
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;
}
/// <summary>
/// 获取正在运行的Revit进程
/// </summary>
public List<ProcessInfo> GetRevitProcesses()
{
var processes = new List<ProcessInfo>();
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;
}
/// <summary>
/// 清理调试插件
/// </summary>
public void CleanupDebugAddins()
{
try
{
// 清理临时调试文件
CleanupTemporaryDebugAddins();
// 释放程序集引用
ReleaseDllReferences();
System.Diagnostics.Debug.WriteLine("调试插件清理完成");
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"清理调试插件失败: {ex.Message}");
}
}
}
/// <summary>
/// 进程信息模型
/// </summary>
public class ProcessInfo
{
public int Id { get; set; }
public string ProcessName { get; set; }
public string WindowTitle { get; set; }
public DateTime StartTime { get; set; }
}
}