воскресенье, 29 декабря 2013 г.

Локализация плагинов AutoCAD: динамическое переключение локализаций интерфейса и справки

В данной заметке показан способ локализации  графического интерфейса плагина и его справочной системы. Помимо этого, продемонстрирован способ динамического переключения локализаций отдельно для графического пользовательского интерфейса плагина и отдельно для его справочной системы.

К сожалению, по умолчанию в AutoCAD не реализован механизм динамического переключения локализаций пользовательского интерфейса плагинов, а так же напрочь отсутствует механизм локализации справочной системы (текущая реализация предоставляет лишь одну общую справку на все локализации), хотя данная возможность достаточно востребована среди пользователей AutoCAD. В нашей организации к использованию утверждён AutoCAD 2009 SP3 Enu, т. к. печальная практика показывает, что локализованные версии AutoCAD, как правило, имеют дополнительные баги помимо тех, которые изначально присутствуют в "родной", английской версии. Поскольку пользователи неоднократно натыкались на подобные "грабли", руководством было принято решение использовать только английскую версию AutoCAD.

Дополнительные пользовательские плагины порой могут поддерживать различные локализации (если разработчик плагина позаботился об этом). Подгружая такие дополнительные приложения в AutoCAD, пользователь порой может предпочесть, чтобы локализация диалоговых окон плагина, а так же локализация сообщений и опций, выводимых плагином в консоль AutoCAD имели иную локализацию, отличную от текущей локализации самого AutoCAD (например русскую, вместо английской). Есть и другая категория пользователей, для которой интерфейс плагина предпочтителен так же английский, в то время как предпочитаемая локализация справочной системы плагина - русская.

Данная заметка демонстрирует самостоятельную реализацию обоих, обозначенных выше пользовательских предпочтений. В обозначенном мною ниже решении происходит переключение локализации Thread.CurrentUICulture, но локализацию Thread.CurrentCulture я не трогаю, т. к. в её переключении нет необходимости. Такой подход позволяет избежать потенциально возможных проблем с форматированием по умолчанию для даты, времени, валюты и чисел. Я создал простой класс UICultureSwitcher, реализующий интерфейс IDisposable. Посредством этого класса в контексте работы команд можно производить временное переключение локализации пользовательского интерфейса команд на нужную. По завершении работы команды происходит автоматическое восстановление предыдущей локализации (см. коды примеров ниже).

Для того, чтобы в каждом новом плагине повторно не писать код, позволяющий управлять локализацией плагина отдельно для его пользовательского интерфейса и отдельно для его справки - я написал библиотеку NetExtension. В её составе реализован следующий функционал:
  1. Получение полного перечня всех команд и примечаний к ним, определённых в составе любой .NET сборки, загружаемой в AutoCAD. Этот перечень, при желании, можно вывести в консоль AutoCAD, дабы пользователь ознакомился с ним.
  2. Связь команд, определённых в составе .NET плагина с соответствующим разделом локализованной справочной системы (предпочитаемую локализацию указывает пользователь).

    Если во время работы такой команды пользователь нажимает кнопку F1, то открывается раздел справочной системы, содержащий информацию по обозначенной команде. При этом нет нужды добавлять в Support File Search Path дополнительные каталоги поиска, дабы AutoCAD мог найти нужный файл справки плагина (как это обычно требуется в AutoCAD).

    В AutoCAD 2014 можно набрать команду в консоли AutoCAD и вместо Enter нажать F1, что является достаточно удобным, т. к. далеко не все команды ведут с пользователем диалог, во время которого тот может успеть нажать клавишу F1. В AutoCAD 2009 такой способ вызова справки, к сожалению, не работает.
  3. Если программист добавляет очередную локализацию в свой плагин, то нет необходимости производить его повторной перекомпиляции, но вместо этого достаточно лишь скопировать дополнительную локализацию в соответствующий подкаталог и она будет автоматически подхватываться в процессе работы.
  4. Реализована возможность переключения пользователем отдельно локализации интерфейса команд, определённых в составе плагина, и отдельно - переключение локализации справочной системы плагина. Эти настройки храняться в конфигурационном файле плагина.

    Обозначенное разделение управления обусловлено тем, что порой интерфейс команд предпочтителен английский, в то время как справка по ним предпочитается русская.
  5. Настройки и предпочтения каждого пользователя индивидуальны и хранятся в его личном переносимом профиле Windows.
  6. Переключение пользователем локализации пользовательского интерфейса и\или справки плагина применяется мгновенно для всех команд, определённых в составе сборки, не требуя перезапуска acad.exe.
  7. В том случае, когда нужная локализация справки отсутствует, подставляться файл той локализации, которая используется по умолчанию (default).
  8. Реализована возможность создавать как одну общую настройку локализаций плагина для всех установленных на компьютере AutoCAD, так и управлять локализацией этого плагина под каждую версию AutoCAD индивидуально (на усмотрение пользователя).
  9. Все используемые в классе команд ресурсы, подлежащие локализации, вынесены в отдельный RESX файл этого же класса.
  10. Если XML файл пользовательских настроек отсутствует - он создаётся автоматически (в переносимом пользовательском профиле Windows).
  11. Если XML файл общих настроек отсутствует, то он создаётся автоматически (рядом с DLL файлом сборки).
Для того, чтобы программист мог воспользоваться библиотекой NetExtension, ему необходимо следовать ряду простых правил:
  1. Имя файла справочной системы, используемого по умолчанию, совпадает с именем файла сборки, но имеет расширение CHM. Т. е. если сборка имеет имя MyAssembly.dll, то имя файла справки, используемой по умолчанию будет MyAssembly.chm.
  2. Если файл локализованной справочной системы находится в том же каталоге, где и сама сборка, то имя такого файла будет иметь дополнительный суффикс в виде имени локализации. Т. е. MyAssembly.Ru-ru.xml.
  3. Если файл локализованной справочной системы находится в каталоге, отличном от того, который указан в п.2, то имя такого файла полностью совпадает с именем файла справки, используемой по умолчанию: MyAssembly.chm.
  4. Поиск нужного файла справочной системы происходит следующим образом (все относительные пути расчитываются от каталога сборки, используется первый найденный файл):
    4.1. Сначала выполняется поиск локализованной версии справки:
    4.1.1. Поиск файла .\MyAssembly.[Ru-ru].xml. Вместо [Ru-ru] - соответствующая локализация.
    4.1.2. Поиск файла ..\help\[Ru-ru]\MyAssembly.chm. Вместо [Ru-ru] - соответствующая локализация.
    4.1.3. Поиск файла ..\..\help\[Ru-ru]\MyAssembly.chm. Вместо [Ru-ru] - соответствующая локализация.
    4.2. Если искомый локализованный файл справки не найден, то начинается поиск справки, используемой по умолчанию:
    4.2.1. Поиск файла .\MyAssembly.chm.
    4.2.2. Поиск файла ..\help\MyAssembly.chm.
    4.2.3. Поиск файла ..\..\help\MyAssembly.chm.
  5. Идентификатор раздела справочной системы, описывающего команду AutoCAD, совпадает с глобальным именем этой команды.
  6. Идентификатор примечания о команде, которое должно отображаться в консоли AutoCAD при распечатке списка обнаруженных команд, должен формироваться по правилу: ГлобальноеИмяКомандыDescription. Текст примечания, которое будет использоваться на основании этого идентификатора, должен быть определён в составе файла ресурсов RESX класса команд.
  7. Наименование компании, разработавшей плагин, должно указываться в свойствах сборки (см. настройки решения).
  8. Если указанное в п.7 свойство не будет заполнено, то при формировании полного имени файла пользовательских настроек, текст "\%Company%" при разворачивании строки в полное имя файла, будет "вырезаться", как будто его и не было указано изначально.
Помимо этого, я так же написал небольшой тестовый проект LocalizedPluginSample_Acad2014, на примере которого показываю работу NetExtension.

Итак, поехали...

В проекте LocalizedPluginSample_Acad2014 созданы два файла: ExtensionApplication.cs и Commands.cs. Первый реализует IExtensionApplication, а второй определяет некоторый набор команд AutoCAD.

   1:  /* ExtensionApplication.cs
   2:   * © Andrey Bushman, 2013
   3:   * Пример локализации плагинов AutoCAD и их справки
   4:   */
   5:   
   6:  // Microsoft
   7:  using System;
   8:  using System.Collections.Generic;
   9:  using System.Linq;
  10:  using System.Text;
  11:  using System.IO;
  12:  using System.Resources;
  13:  using System.Reflection;
  14:  using System.Runtime.InteropServices;
  15:  using System.Globalization;
  16:  using System.Xml.Linq;
  17:   
  18:  // Autodesk
  19:  using cad = Autodesk.AutoCAD.ApplicationServices.Application;
  20:  using App = Autodesk.AutoCAD.ApplicationServices;
  21:  using Db = Autodesk.AutoCAD.DatabaseServices;
  22:  using Ed = Autodesk.AutoCAD.EditorInput;
  23:  using Rtm = Autodesk.AutoCAD.Runtime;
  24:   
  25:  // Bushman
  26:  using Bushman.CAD.PluginServices;
  27:   
  28:  [assembly: Rtm.ExtensionApplication(typeof(Bushman.CAD.Samples.ExtensionApplication))]
  29:   
  30:  namespace Bushman.CAD.Samples {
  31:   
  32:      public sealed class ExtensionApplication : Rtm.IExtensionApplication {
  33:   
  34:          #region IExtensionApplication Members
  35:   
  36:          /// <summary>
  37:          /// Этот метод будет запущен на исполнение при загрузке плагина в AutoCAD.
  38:          /// </summary>
  39:          public void Initialize() {
  40:              Ed.Editor ed = cad.DocumentManager.MdiActiveDocument.Editor;
  41:              String assemblyFileFullName = GetType().Assembly.Location;
  42:              String assemblyName = Path.GetFileName(GetType().Assembly.Location);
  43:              ResourceManager resMng = new ResourceManager(GetType());
  44:   
  45:              Assembly assembly = GetType().Assembly;
  46:              // Получаю значение культуры, предпочитаемой пользователем
  47:              CultureInfo uiCulture = LocalizationServices.GetCustomUICulture(assembly);
  48:   
  49:              using (UICultureSwitcher switcher = new UICultureSwitcher(uiCulture)) {
  50:                  // Сообщаю о том, что произведена загрузка сборки и указываю полное имя файла,
  51:                  // дабы было видно, откуда он загружен
  52:                  ed.WriteMessage("\n{0} {1} {2}.\n{3}: {4}\n{5}\n",
  53:                      resMng.GetString("Assembly"),
  54:                      assemblyName, resMng.GetString("Loaded"),
  55:                      resMng.GetString("AssemblyFileFullName"),
  56:                      assemblyFileFullName,
  57:                      resMng.GetString("Copyright"));
  58:   
  59:                  const Char _char = '*';
  60:                  const Int32 charCount = 10;
  61:                  ed.WriteMessage(new String(_char, charCount));
  62:   
  63:   
  64:                  // Получаю информацию обо всех командах AutoCAD, определённых в составе текущей сборки
  65:                  Dictionary<Type, List<Rtm.CommandMethodAttribute>> commandsInfo =
  66:                      LocalizationServices.GetCommands(assembly);
  67:   
  68:                  // Вывожу в консоль AutoCAD локализованную информацию о найденных командах AutoCAD
  69:                  String report = LocalizationServices.PrintCommands(commandsInfo, uiCulture);
  70:                  ed.WriteMessage(report);
  71:   
  72:                  // Регистрирую справочную систему для локализации, предпочитаемой пользователем
  73:                  CultureInfo helpCulture = LocalizationServices.GetCustomHelpCulture(assembly);
  74:                  LocalizationServices.HelpInfoRegistration(assembly, helpCulture, commandsInfo);
  75:   
  76:                  ed.WriteMessage(new String(_char, charCount));
  77:                  resMng.ReleaseAllResources();
  78:              }
  79:          }
  80:   
  81:          public void Terminate() {
  82:              // nothing
  83:          }
  84:   
  85:          #endregion
  86:      }
  87:  }

Как видим, в методе Initialize я вывожу на консоль полный перечень команд AutoCAD, определённых в составе текущей сборки. При этом используется та культура, которая указана в файле пользовательских настроек плагина. Кроме того, выполняется регистрация локализованной версии справочной системы.

   1:  /* Commands.cs
   2:   * © Andrey Bushman, 2013
   3:   * Пример локализации плагинов AutoCAD и их справки
   4:   */
   5:   
   6:  // Microsoft
   7:  using System;
   8:  using System.Collections.Generic;
   9:  using System.Linq;
  10:  using System.Text;
  11:  using System.Resources;
  12:   
  13:  // Autodesk
  14:  using cad = Autodesk.AutoCAD.ApplicationServices.Application;
  15:  using App = Autodesk.AutoCAD.ApplicationServices;
  16:  using Db = Autodesk.AutoCAD.DatabaseServices;
  17:  using Ed = Autodesk.AutoCAD.EditorInput;
  18:  using Rtm = Autodesk.AutoCAD.Runtime;
  19:  using System.Globalization;
  20:  using System.Threading;
  21:   
  22:  // Bushman
  23:  using Bushman.CAD.PluginServices;
  24:  using System.Reflection;
  25:   
  26:  /* Если сборке не назначен атрибут CommandClass, то AutoCAD ищет public определения команд во всех
  27:   * public классах.
  28:   * 
  29:   * Если сборке назначен один или несколько атрибутов CommandClass, то AutoCAD ищет команды только
  30:   * в указанных классах, при условии, что область их видимости - public.
  31:   * 
  32:   * Если в сборке будут определены команды с одинаковым именем, то при регистрации второго определения
  33:   * повторяющейся команды возникнет исключение. Те команды, которые до этого события были успешно 
  34:   * зарегистрированы в AutoCAD, будут доступны для использования.
  35:   */
  36:   
  37:  [assembly: Rtm.CommandClass(typeof(Bushman.CAD.Samples.Commands))]
  38:   
  39:  namespace Bushman.CAD.Samples {
  40:   
  41:      public sealed class Commands {
  42:   
  43:          const String cmdNamespace = "Bushman";
  44:   
  45:          [Rtm.CommandMethod(cmdNamespace, "cmd1", "cmd1Id", Rtm.CommandFlags.Modal)]
  46:          public void Command1() {
  47:              // На время работы нашей команды устанавливаем нужную нам культуру
  48:              using (UICultureSwitcher switcher = new UICultureSwitcher(LocalizationServices
  49:                  .GetCustomUICulture(GetType().Assembly))) {
  50:   
  51:                  Ed.Editor ed = cad.DocumentManager.MdiActiveDocument.Editor;
  52:                  ResourceManager resMng = new ResourceManager(typeof(Bushman.CAD.Samples.Commands));
  53:                  ed.WriteMessage("{0}\n", resMng.GetString("Command1_Msg"));
  54:                  resMng.ReleaseAllResources();
  55:              }
  56:          }
  57:   
  58:          [Rtm.CommandMethod(null, "cmd2", "cmd2Id", Rtm.CommandFlags.Modal)]
  59:          public void Command2() {
  60:              // На время работы нашей команды устанавливаем нужную нам культуру
  61:              using (UICultureSwitcher switcher = new UICultureSwitcher(LocalizationServices
  62:                  .GetCustomUICulture(GetType().Assembly))) {
  63:   
  64:                  Ed.Editor ed = cad.DocumentManager.MdiActiveDocument.Editor;
  65:                  ResourceManager resMng = new ResourceManager(typeof(Bushman.CAD.Samples.Commands));
  66:                  ed.WriteMessage("{0}\n", resMng.GetString("Command2_Msg"));
  67:                  resMng.ReleaseAllResources();
  68:              }
  69:          }
  70:   
  71:          [Rtm.CommandMethod("cmd3", Rtm.CommandFlags.Modal)]
  72:          public void Command3() {
  73:              // На время работы нашей команды устанавливаем нужную нам культуру
  74:              using (UICultureSwitcher switcher = new UICultureSwitcher(LocalizationServices
  75:                  .GetCustomUICulture(GetType().Assembly))) {
  76:   
  77:                  Ed.Editor ed = cad.DocumentManager.MdiActiveDocument.Editor;
  78:                  ResourceManager resMng = new ResourceManager(typeof(Bushman.CAD.Samples.Commands));
  79:                  ed.WriteMessage("{0}\n", resMng.GetString("Command3_Msg"));
  80:                  resMng.ReleaseAllResources();
  81:              }
  82:          }
  83:   
  84:          [Rtm.CommandMethod("cmd4", Rtm.CommandFlags.Modal)]
  85:          public void Command4() {
  86:              // На время работы нашей команды устанавливаем нужную нам культуру
  87:              using (UICultureSwitcher switcher = new UICultureSwitcher(LocalizationServices
  88:                  .GetCustomUICulture(GetType().Assembly))) {
  89:   
  90:                  Ed.Editor ed = cad.DocumentManager.MdiActiveDocument.Editor;
  91:                  ResourceManager resMng = new ResourceManager(typeof(Bushman.CAD.Samples.Commands));
  92:                  ed.WriteMessage("{0}\n", "Hello from cmd4 (UNLOCALIZED) from the Commands class!");
  93:                  resMng.ReleaseAllResources();
  94:              }
  95:          }
  96:   
  97:          /// <summary>
  98:          /// Изменить локализацию графического интерфейса команд, определённых в составе данной сборки
  99:          /// </summary>
 100:          /// <param name="culture">Нужная культура</param>
 101:          private void SetUILocalization(CultureInfo culture) {
 102:              Ed.Editor ed = cad.DocumentManager.MdiActiveDocument.Editor;
 103:              ResourceManager resMng = new ResourceManager(typeof(Bushman.CAD.Samples.Commands));
 104:   
 105:              // Меняем значения культур пользовательского интерфейса и справки в файле настроек
 106:              Assembly assembly = GetType().Assembly;
 107:              CultureInfo helpCulture = LocalizationServices.GetCustomHelpCulture(assembly);
 108:              LocalizationServices.SaveCustomCultures(assembly, culture, helpCulture);
 109:   
 110:              resMng.ReleaseAllResources();
 111:          }
 112:   
 113:          /// <summary>
 114:          /// Изменить локализацию справочной системы плагина
 115:          /// </summary>
 116:          /// <param name="culture">Нужная культура</param>
 117:          private void SetHelpLocalization(CultureInfo culture) {
 118:              Ed.Editor ed = cad.DocumentManager.MdiActiveDocument.Editor;
 119:              ResourceManager resMng = new ResourceManager(typeof(Bushman.CAD.Samples.Commands));
 120:   
 121:              // Меняем значения культур пользовательского интерфейса и справки в файле настроек
 122:              Assembly assembly = GetType().Assembly;
 123:              CultureInfo uiCulture = LocalizationServices.GetCustomUICulture(assembly);
 124:              LocalizationServices.SaveCustomCultures(assembly, uiCulture, culture);
 125:   
 126:              // Получаю словарь команд AutoCAD, сгруппированных по классам, в составе которых они определены
 127:              Dictionary<Type, List<Rtm.CommandMethodAttribute>> commandsInfo =
 128:                  LocalizationServices.GetCommands(assembly);
 129:   
 130:              // Обновляю регистрационные данные справки
 131:              LocalizationServices.HelpInfoRegistration(assembly, culture, commandsInfo);
 132:              resMng.ReleaseAllResources();
 133:          }
 134:   
 135:          /// <summary>
 136:          /// Установить английский интерфейс плагина
 137:          /// </summary>
 138:          [Rtm.CommandMethod("setUI_En-us", Rtm.CommandFlags.Modal)]
 139:          public void SetUI_Enu() {
 140:              SetUILocalization(new System.Globalization.CultureInfo("En-us"));
 141:          }
 142:   
 143:          /// <summary>
 144:          /// Установить русский интерфейс плагина
 145:          /// </summary>
 146:          [Rtm.CommandMethod("setUI_Ru-ru", Rtm.CommandFlags.Modal)]
 147:          public void SetUI_Rus() {
 148:              SetUILocalization(new System.Globalization.CultureInfo("Ru-ru"));
 149:          }
 150:   
 151:          /// <summary>
 152:          /// Использовать в плагине русскую справку
 153:          /// </summary>
 154:          [Rtm.CommandMethod("setHelp_Ru-ru", Rtm.CommandFlags.Modal)]
 155:          public void SetHelp_Rus() {
 156:              SetHelpLocalization(new System.Globalization.CultureInfo("Ru-ru"));
 157:          }
 158:   
 159:          /// <summary>
 160:          /// Использовать в плагине английскую справку
 161:          /// </summary>
 162:          [Rtm.CommandMethod("setHelp_En-us", Rtm.CommandFlags.Modal)]
 163:          public void SetHelp_Enu() {
 164:              SetHelpLocalization(new System.Globalization.CultureInfo("En-us"));
 165:          }
 166:   
 167:      }
 168:  }

В этом файле мы видим четыре тестовых команды, а так же две команды переключения локализаций пользовательского интерфейса (русский\английский) и ещё две команды, переключающие локализацию используемой справочной системы. Показываю несколько скринов работы:




Русский вариант выглядит так:




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

Если текущей локализацией для справочной системы мы назначаем английскую, то справка открывается английская:




Давайте вызовем команду cmd2_en-us, после чего изменим локализацию пользовательского интерфейса плагина, и вызовем команду повторно. В нашем примере переключение пользовательского интерфейса выполняется командами setUI_En-us и  setUI_Ru-ru:




Как видите, сначала команда cmd2_en-us выводила сообщения на английском, но после переключения на русскую локализацию, последующие сообщения так же стали выполняться на русском.

Для того, чтобы переключить локализацию справки, в нашем примере следует воспользоваться командами setHelp_En-us и setHelp_Ru-ru. После этого справка станет русской:




Непосредственно возле плагина должен находиться файл [ИмяСборки].CommonSettings.xml. В случае отсутствия, он будет автоматически создан. Вот пример его содержимого:

   1:  <?xml version="1.0" encoding="utf-8" ?>
   2:  <Settings>
   3:    <!--
   4:    Путь к файлу пользовательских настроек можно указывать как в относительной, так и в абсолютной форме. 
   5:    Так же допускается (и даже рекомендуется) использование системных переменных. Дополнительно
   6:    предоставляются следующие переменные:
   7:    
   8:    %Assembly% - имя файла сборки без расширения.
   9:    %Platform% - разрядность AutoCAD (x86 или x64).
  10:    %Company% - компания-разработчик плагина (значение читается из свойств сборки).
  11:    %CoreMajorVersion% - первое число в версии ядра AutoCAD текущего приложения.
  12:    %CoreMinorVersion% - второе число в версии ядра AutoCAD текущего приложения.
  13:    
  14:    Например в значении 17.2 (AutoCAD 2009), 17 - это %CoreMajorVersion%, а 2 - это %CoreMinorVersion%.
  15:    Переменные %CoreMajorVersion% и %CoreMinorVersion% позволяют создавать настройки плагина, отдельно для
  16:    каждой версии AutoCAD (если такое потребуется в связи со спецификой приложения).    
  17:    -->
  18:    <CustomSettingsFile>%AppData%\AcadPlugins\%Company%\%Assembly%\%Assembly%.CustomSettings.xml</CustomSettingsFile>
  19:    <!--Значения, используемые по умолчанию, если они не переопределены в файле пользовательских настроек.-->
  20:    <DefaultValues>
  21:      <!--Предпочитаемая локализация пользовательского интерфейса плагина. Если значение не указано,
  22:      то используется текущая в AutoCAD.-->
  23:      <UICulture>En-us</UICulture>
  24:      <!--Предпочитаемая локализация справочной системы плагина. Если значение не указано, 
  25:      то используется текущая в AutoCAD.-->
  26:      <HelpCulture>En-us</HelpCulture>
  27:    </DefaultValues>
  28:  </Settings>

Пользовательский же файл настроек, хранящийся в пользовательском профиле Windows, выглядит проще и вы, при желании, можете размещать в нём дополнительные настройки, специфичные для вашего плагина:

   1:  <?xml version="1.0" encoding="utf-8"?>
   2:  <Settings>
   3:    <Localization>
   4:      <!--Preferred culture of the user interface-->
   5:      <UICulture>ru-RU</UICulture>
   6:      <!--Preferred culture of the help file-->
   7:      <HelpCulture>en-US</HelpCulture>
   8:    </Localization>
   9:  </Settings>

Этот файл так же будет создаваться автоматически, в случае его отсутствия. Исходный код проектов в формате MS Visual Studio 2012 находится здесь.

P.S. Выше обозначенная возможность динамического переключения локализаций справочной системы AutoCAD реализована с помощью функции acedSetFunHelp. В nanoCAD имеется свой аналог функции acedSetFunHelp - это функция ncedSetFunHelp. Сигнатура функции следующия:

   1:  int acedSetFunHelp(
   2:      const ACHAR* pszFunctionName, // Имя команды
   3:      const ACHAR* pszHelpfile, // Имя файла помощи
   4:      const ACHAR* pszTopic, // Имя раздела файла помощи
   5:      int iCmd // неиспользуемый параметр
   6:  )

Комментариев нет: