среда, 23 апреля 2014 г.

Excel COM Add-in с нуля


Введение

В статье описывается процесс создания COM надстройки для Microsoft Excel средствами .NET Framework. Описаны этапы создания настройки, регистрации в системе и управления объектной моделью Excel.

Для создания надстройки и тестирования понадобится:
  • Visual Studio, в моем случае 2012;
  • Microsoft Excel, любая версия начиная с 2003;
  • Primary Interop Assemblies (PIA).

Что такое Primary Interop Assemblies или сборки взаимодействия

Excel представляет собой неуправляемую среду и ничего не знает о существовании .NET Framework, ровно как и наоборот. Для реализации взаимодействия между этими компонентами используются так называемые сборки взаимодействия, которые представляют собой обертку над COM интерфейсами и тем самым могут быть использованы .NET Framework для управления объектной моделью Excel.

Для Excel 2003/2007 сборки взаимодействия устанавливаются отдельно. После установки эти сборки появляются в GAC. Начиная с Excel 2010 сборки взаимодействия являются частью Excel и их установка отдельно не требуется.

Сборки взаимодействия используются исключительно в процессе разработке. По умолчанию, начиная с .NET Framework 4.0, все классы из сборок взаимодействия встраиваются в целевую .NET сборку, таким образом их физическое наличие на компьютере пользователя не требуется. Однако в более ранних версиях .NET Framework возможности встраивать типы не было, в результате чего сборки взаимодействия должны были устанавливаться на каждом компьютере, где использовалась надстройка.

Создание надстройки

Для начала создадим проект типа Class Library в Visual Studio.



Первым делом добавим к проекту две ссылки:
  • Microsoft.Office.Interop.Excel
  • Extensibility



Заметим, что Microsoft.Office.Interop.Excel имеет несколько версий, при этом главный номер соответствует версии Excel. Например, 11 – это Excel 2003, 12 – Excel 2007 и т.д.. С высокой долей вероятности, сборки взаимодействия для Excel 2007 будут работать и в других версиях, однако не стоит забывать, что различия в функциональности этих сборок все же существуют.

Для создания надстройки необходимо создать класс и реализовать интерфейс ITDExtensibility2. Для последующей регистрации надстройки необходимо пометить класс атрибутом ComVisible(true).

[ComVisible(true)]
public class ComAddin : IDTExtensibility2
{
    private _Application _xlApp;

    public void OnConnection(object application, ext_ConnectMode connectMode,  object addInInst, ref Array custom)
    {
        _xlApp = (_Application) application;
        _xlApp.StatusBar = "Hello, world";

        Debug.WriteLine("OnConnection");
    }

    public void OnDisconnection(ext_DisconnectMode removeMode, ref Array custom)
    {
        Debug.WriteLine("OnDisconnection");
    }

    public void OnAddInsUpdate(ref Array custom)
    {
        Debug.WriteLine("OnAddInsUpdate");
    }

    public void OnStartupComplete(ref Array custom)
    {
        Debug.WriteLine("OnStartupComplete");
    }

    public void OnBeginShutdown(ref Array custom)
    {
        Debug.WriteLine("OnBeginShutdown");
    }
}

Интерфейс предоставляет 5 методов. OnConnection вызывается при загрузки надстройки в Excel. При этом параметр connectMode указывает каким именно образом надстройка была загружена, при запуске Excel автоматически или же вручную, путем активации соответствующего флажка в окне надстроек. Интересным параметром здесь также является application типа object, который содержит ссылку на объектную модель Excel. Приведение этой переменной к типу _Application позволяет управлять объектной моделью. В нашем случае, мы изменяем строку состояния и устанавливаем в нее текст Hello, world.

_xlApp = (_Application) application; //Получаем ссылку на объектную модель Excel,
_xlApp.StatusBar = "Hello, world";   //изменяем строку состояния.

Метод OnDisconnection вызывается при выгрузки надстройки, т.е. когда соответствующий флажок снимается в окне надстроек или при закрытии Excel.
Метод OnStartupComplete срабатывает только в том случае, если надстройка настроена на загрузку при запуске Excel, и происходит это после того как Excel загружен. В противном, случае метод не вызывается.
Метод OnBeginShutdown вызывается при закрытии Excel. Метод OnAddinsUpdate срабатывает при загрузке или выгрузке надстройки в Excel.

Регистрация надстройки

Надстройка готова, теперь необходимо зарегистрировать ее соответствующим образом. Для этого необходимо выполнить следующие шаги:
  1. Зарегистрировать полученную DLL с помощью утилиты regasm.exe;
  2. Добавить несколько записей в реестр.
Regasm.exe позволяет зарегистрировать .NET сборку в качестве COM компонента. В нашем случае будет зарегистрирован класс ComAddin, отмеченный атрибутом ComVisible(true).
Необходимо помнить, что Excel бывает как 32-, так и 64-битный. Таким образом .NET сборку необходимо регистрировать дважды, используя как 32-, так и 64-битную версию утилиты regasm.exe.
Прежде чем зарегистрировать сборку с помощью этой утилиты, посмотрим второй пункт. Для того, чтобы Excel понял, что COM компонент представляет собой именно надстройку для Excel, необходимо добавить новую запись в раздел реестра HKLM\Software\Microsoft\Office\Excel\AddIns\. При этом имя нового раздела совпадает с именем класса, реализующего надстройку, включая namespace. Автоматизируем этот процесс. Для этого, добавим к нашему классу два новых статических метода.

[ComRegisterFunction]
private static void Register(Type type)
{
    var path = Registry.LocalMachine.Name + @"\Software\Microsoft\Office\Excel\AddIns\" + type.FullName;

    Registry.SetValue(path, "CommandLineSafe", 0, RegistryValueKind.DWord);
    Registry.SetValue(path, "Description", "This is the first COM Add-in", RegistryValueKind.String);
    Registry.SetValue(path, "FriendlyName", "First COM Add-in", RegistryValueKind.String);
    Registry.SetValue(path, "LoadBehavior", 3, RegistryValueKind.DWord);
}

[ComUnregisterFunction]
private static void Unregister(Type type)
{
    var comAddinsKey = Registry.LocalMachine.OpenSubKey(@"Software\Microsoft\Office\Excel\AddIns\", true);
    if (comAddinsKey == null)
    {
        return;
    }

    comAddinsKey.DeleteSubKeyTree(type.FullName);
    comAddinsKey.Close();
}

Обратим внимание, что методы отмечены аттрибутами ComRegisterFunction и ComUnregisterFunction. Согласно документации, методы, отмеченные этими атрибутами будут вызваны при регистрации сборки с помощью утилиты regasm.exe или с помощью метода RegistrationServices.RegisterAssembly. При этом методы должны быть статичными, возвращать тип void и принимать объект типа Type в качестве единственного аргумента.
Таким образом, метод Register будет вызван при регистрации сборки с помощью утилиты regasm.exe. Сам метод, в свою очередь, добавляет новый подраздел в раздел реестра HKLM\Software\Microsoft\Office\Excel\AddIns\, который содержит информацию о всех COM надстройках.

Registry.SetValue(path, "CommandLineSafe", 0, RegistryValueKind.DWord);
Registry.SetValue(path, "Description""This is the first COM Add-in"RegistryValueKind.String);
Registry.SetValue(path, "FriendlyName""First COM Add-in"RegistryValueKind.String);
Registry.SetValue(path, "LoadBehavior", 3, RegistryValueKind.DWord);

Ключ FriendlyName содержит имя надстройки, а ключ Description – его описание. Здесь можно указать любой текст. Наиболее интересным является ключ LoadBehavior, который указывает способ загрузки надстройки в Excel. Этот ключ имеет несколько возможных значений, однако наиболее интересными являются значения 2 и 3. В первом случает надстройка не будет загружена в Excel при запуске, во втором – будет. Установим этот ключ в значение 3 для того чтобы загружать надстройку каждый раз при запуске Excel автоматически. Заметим, что LoadBehavior является единственным обязательным ключом. В случае его отсутствия, надстройка не будет загружена в Excel и также не будет доступна для загрузки в окне Add-ins.
Метод Unregister, отмеченный атрибутом ComUnregisterFunction, соответственно, удаляет информацию о надстройке из списка, таким образом в Excel она доступна не будет. Метод будет вызван при вызове утилиты regasm.exe с ключом /unregister.
Соберем проект и пробуем зарегистрировать полученную сборку. Для удобства, создадим в папке с надстройкой .bat файл со следующими командами

C:\Windows\Microsoft.NET\Framework\v4.0.30319\regasm.exe "%~dp0\ComAddin.dll" /codebase
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\regasm.exe "%~dp0\ComAddin.dll" /codebase
pause


Ключ /codebase указывает на то, что необходимо добавить полный путь к сборке в реестр. В противном случае, если ключ не указан, сборка будет также успешно зарегистрирована, однако при последующем запуске Excel он попытается найти сборку в GAC. В результате надстройка не будет загружена, потому что ее просто нет в GAC.
Соответственно, если сборка предварительно добавлена в GAC, ключ /codebase необходимо опустить.

Сразу заметим, что для регистрации сборки необходимы права администратора. Если вы работаете под обычным пользователем, необходимо запускать скрипт от имени администратора.

Для удаления надстройки, необходимо использовать ключ /unregister. При этом скрипт принимает следующий вид.

C:\Windows\Microsoft.NET\Framework\v4.0.30319\regasm.exe "%~dp0\ComAddin.dll" /unregister
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\regasm.exe "%~dp0\ComAddin.dll" /unregister
pause

После вызова скрипта имеем следующее


Сборка зарегистрирована успешно, однако regasm.exe указывает на отсутствие подписи у сборки. В данном случае проигнорируем это предупреждение.
После регистрации в реестре создан новый раздел.


Запускаем Excel. В строке состояния видим ‘Hello, world’, что свидетельствует о том, что надстройка была успешно загружена.


Откроем окно надстроек


При этом видим, что имя и описание надстройки имеют значения, добавленные ранее в реестр, а сама надстройка расположена в группе “Active Application Add-ins”, т.е. является активной.

Отладка

Отладка надстройки осуществляется достаточно просто. Для этого необходимо зарегистрировать надстройку, после чего в настройках проекта в разделе Debug указать Excel в качестве программы для запуска.


После чего запускаем отладчик. При этом в окне Output в Visual Studio видим последовательность вызова методов класса.


С исходниками и готовой сборкой можно ознакомиться тут.

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

Отправить комментарий