ADN Open CIS
Сообщество программистов Autodesk в СНГ

07/02/2014

Программное определение дублированных имен .NET команд

Неприятен факт связанный с атрибутом CommandMethod в AutoCAD .NET сборке: в нем может быть две разных команды имеющих одно имя но разные методы. Например, как в следующем коде:

Код - C#: [Выделить]
  1. [CommandMethod("Test")]
  2. public void SomeMethod()
  3. {
  4.     // Реализация ...
  5. }
  6.  
  7. [CommandMethod("Test")]
  8. public void SomeOtherMethod()
  9. {
  10.     // Реализация ...
  11. }

Что будет если мы попробуем загрузить dll-файл, содержащий этот код, в AutoCAD? Этот код прекрасно скомпилируется, но при загрузке появятся сообщения об ошибке:

Cannot load assembly. Error details: Autodesk.AutoCAD.Runtime.Exception: eDuplicateKey
   at Autodesk.AutoCAD.Runtime.CommandClass.AddCommand(ICommandLineCallable ca, MethodInfo mi)
   at Autodesk.AutoCAD.ApplicationServices.AutoCADApplicationHolder.Initialize(Assembly assembly)
   at Autodesk.AutoCAD.ApplicationServices.ExtensionLoader.ProcessAssembly(Assembly assembly)

Но нет способа узнать какая команда привела к сообщению об исключении. Несложно определить это в данном сценарии, но в моем случае я работаю с .NET-файлом, в котором определены сотни команд, распределенных среди десятков файлов. Так что если я для тестирования дублирую часть кода, я могу случайно продублировать и имя команды, которую затем сложно найти.

В качестве решения для поиска дубликатов можно предложить использовать возможности .NET Reflection. Это делается следующим куском кода: необходимо создать отдельный .NET dll-файл и загрузить dll-файл содержащий дубликаты, используя FindCmdDuplicates (path):

Код - C#: [Выделить]
  1. [CommandMethod("FindCmdDuplicates")]
  2. public void FindCmdDuplicatesCmd()
  3. {
  4.     string asmPath = SelectAssembly();
  5.  
  6.     if (asmPath == null)
  7.         return;
  8.  
  9.     FindCmdDuplicates(asmPath);
  10. }
  11.  
  12. private string SelectAssembly()
  13. {
  14.     System.Windows.Forms.OpenFileDialog dlg =
  15.         new System.Windows.Forms.OpenFileDialog();
  16.  
  17.     dlg.Title = "Загрузка файла сборки ";
  18.  
  19.     dlg.InitialDirectory = Environment.GetFolderPath(
  20.         Environment.SpecialFolder.Desktop);
  21.  
  22.     dlg.Filter = ".Net сборка (*.dll)|*.dll";
  23.     dlg.FilterIndex = 1;
  24.     dlg.RestoreDirectory = true;
  25.  
  26.     while (dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK)
  27.     {
  28.         try
  29.         {
  30.             AssemblyName asmName =
  31.                 AssemblyName.GetAssemblyName(dlg.FileName);
  32.  
  33.             return dlg.FileName;
  34.         }
  35.         catch (BadImageFormatException ex)
  36.         {
  37.             System.Windows.Forms.MessageBox.Show(
  38.                 "Извините, но это неверная .NET сборка...",
  39.                 "Неверная сборка",
  40.                 System.Windows.Forms.MessageBoxButtons.OK,
  41.                 System.Windows.Forms.MessageBoxIcon.Error);
  42.         }
  43.     }
  44.  
  45.     return null;
  46. }
  47.  
  48. public void FindCmdDuplicates(string asmPath)
  49. {
  50.     Dictionary<string, List<MethodInfo>> map =
  51.         new Dictionary<string, List<MethodInfo>>();
  52.  
  53.     Assembly asm = Assembly.LoadFile(asmPath);
  54.  
  55.     Type[] expTypes = asm.GetTypes();
  56.  
  57.     foreach (Type type in expTypes)
  58.     {
  59.         MethodInfo[] methods = type.GetMethods();
  60.  
  61.         foreach (MethodInfo method in methods)
  62.         {
  63.             CommandMethodAttribute attribute =
  64.                 GetCommandMethodAttribute(method);
  65.  
  66.             if (attribute == null)
  67.                 continue;
  68.  
  69.             if (!map.ContainsKey(attribute.GlobalName))
  70.             {
  71.                 var methodInfo = new List<MethodInfo>();
  72.                        
  73.                 map.Add(attribute.GlobalName, methodInfo);
  74.             }
  75.  
  76.             map[attribute.GlobalName].Add(method);
  77.         }
  78.     }
  79.  
  80.     Document doc = Application.DocumentManager.MdiActiveDocument;
  81.     Editor ed = doc.Editor;
  82.  
  83.     foreach (var keyValuePair in map)
  84.     {
  85.         if (keyValuePair.Value.Count > 1)
  86.         {
  87.             ed.WriteMessage(
  88.                 "\nДублированный атрибут: " + keyValuePair.Key);
  89.  
  90.             foreach (var method in keyValuePair.Value)
  91.             {
  92.                 ed.WriteMessage(
  93.                     "\n – Метод: " + method.Name);               
  94.             }
  95.         }
  96.     }
  97. }
  98.  
  99. public CommandMethodAttribute GetCommandMethodAttribute(
  100.         MethodInfo method)
  101. {
  102.     object[] attributes = method.GetCustomAttributes(true);
  103.  
  104.     foreach (object attribute in attributes)
  105.     {
  106.         if (attribute is CommandMethodAttribute)
  107.         {
  108.             return attribute as CommandMethodAttribute;
  109.         }
  110.     }
  111.  
  112.     return null;
  113. }

Источник: http://adndevblog.typepad.com/autocad/2014/01/detecting-net-command-duplicates-programmatically.html

Обсуждение: http://adn-cis.org/forum/index.php?topic=513

Опубликовано 07.02.2014
Отредактировано 07.02.2014 в 03:18:12