Excel-DNA
Excel-DNA - библиотека для .NET, с помощью которой можно написать и собрать полностью самодостаточный файл надстройки для Excel с расширением .xll
Этот файл достаточно положить в пользовательскую папку, без прав администратора, и просто включить в настройках Excel.
Такая надстройка будет иметь доступ к COM-модели Excel, C API Excel - позволяет взаимодействовать с интерфейсом программы и рабочими книгами. Сможет реализовать пользовательские формулы и добавить свое меню на интерфейсную ленту Ribbon UI.
В статье я расскажу как написать свою формулу для Excel на C#. Собрать и установить свою первую надстройку.
Подготовка
Создадим новый проект библиотеки классов для версии .NET 6
dotnet new classlib --framework net6.0 -o ExcelAddIn
В файле .csproj
нужно изменить значение версии фреймворка на
<TargetFramework>net6.0-windows</TargetFramework>
Теперь установим базовый пакет ExcelDna.AddIn
dotnet add package ExcelDna.AddIn
Писать можно и в Visual Studio, и в VSCode.
Чтобы дебажить код в VSCode нужно создать файл launch.json
и в нем изменить две строки, "program"
и "args"
:
{
"version": "0.2.0",
"configurations": [
{
"name": ".NET Core Launch (console)",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "C:\\Program Files\\Microsoft Office\\root\\Office16\\EXCEL.EXE",
"args": [
"${workspaceFolder}\\bin\\Debug\\net6.0-windows\\ExcelAddIn-AddIn64.xll"
],
"cwd": "${workspaceFolder}",
"console": "internalConsole",
"stopAtEntry": false
},
{
"name": ".NET Core Attach",
"type": "coreclr",
"request": "attach"
}
]
}
Пути к файлу программы Excel и собранной надстройки нужно поставить свои.
Первая формула
Напишем простую формулу, которая складывает два числа:
global using ExcelDna.Integration;
namespace ExcelAddIn;
public static class ExcelFunctions
{
[ExcelFunction]
public static double DNASUM(double a, double b)
{
return a + b;
}
}
Все статические методы помеченные атрибутом [ExcelFunction]
теперь принимаются Excel как пользовательские формулы.
Нажмем F5, откроется Excel, который уже откроет файл нашей надстройки. Поскольку у нас нет цифровой подписи появится предупреждение.
Нажимаем "Включить" и проверяем работу нашей формулы
Интерфейс IExcelAddIn
Для работы нашей надстройки может понадобится, чтобы она при открытии и закрытии выполняла какие-либо полезные вещи. Например читала настройки из реестра, подключала базы данных.
Cоздадим класс MyAddIn
, реализующий интерфейс IExcelAddIn
public class MyAddIn : IExcelAddIn
{
public void AutoClose()
{
throw new NotImplementedException();
}
public void AutoOpen()
{
throw new NotImplementedException();
}
}
При открытии надстройки будет создан экземпляр класса MyAddIn
и выполнен метод AutoOpen()
Делаем формулу запроса курса валют с сайта ЦБ
Напишем теперь формулу, которая делает что-то полезное, например запрашивает курс валют с сайта ЦБ.
Интерфейс для клиента:
namespace ExcelAddIn.Services;
public interface ICurrencyClient
{
public Task<decimal?> GetExchangeRate(DateTime date);
}
Класс клиента API сайта ЦБ. Парсим XML с курсами на нужную дату:
using System.Diagnostics;
using System.Text;
using System.Xml.Linq;
namespace ExcelAddIn.Services;
public class CurrencyClient : ICurrencyClient
{
private readonly HttpClient _httpClient;
private const string _requestAddress = @"https://www.cbr.ru/scripts/XML_daily.asp?date_req=";
public CurrencyClient(HttpClient httpClient)
{
_httpClient = httpClient;
}
public async Task<decimal?> GetExchangeRate(DateTime date)
{
string request = $"{_requestAddress}{date.Date:dd/MM/yyyy}";
HttpResponseMessage response = await _httpClient.GetAsync(request);
try
{
response.EnsureSuccessStatusCode();
var bytes = await response.Content.ReadAsByteArrayAsync();
var xml = Encoding.GetEncoding(1251).GetString(bytes);
XElement valCourses = XElement.Parse(xml);
decimal? exchangeRate = decimal.Parse(valCourses.Elements("Valute")
.Where(e => e.Element("Name").Value == "Евро")
.FirstOrDefault()
.Element("Value").Value);
return exchangeRate;
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
return null;
}
}
}
Добавляем нужные сервисы и создаем провайдер сервисов
using Microsoft.Extensions.DependencyInjection;
using ExcelAddIn.Services;
using System.Text;
public class MyAddIn : IExcelAddIn
{
public static ServiceProvider ServiceProvider { get; private set; }
public void AutoOpen()
{
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
IServiceCollection Services = new ServiceCollection();
Services.AddHttpClient()
.AddSingleton<ICurrencyClient, CurrencyClient>();
ServiceProvider = Services.BuildServiceProvider();
}
public void AutoClose()
{
}
}
И, наконец, определяем метод для формулы:
using ExcelAddIn.Services;
using Microsoft.Extensions.DependencyInjection;
namespace ExcelAddIn;
public static class ExcelFunctions
{
[ExcelFunction]
public static object ExchangeRate(double dateField)
{
ICurrencyClient currencyClient = MyAddIn.ServiceProvider.GetService<ICurrencyClient>();
DateTime date = dateField == 0 ? DateTime.Today : DateTime.FromOADate(dateField);
if (ExcelAsyncUtil.Run(nameof(ExchangeRate), dateField, delegate
{
return currencyClient.GetExchangeRate(date)
.GetAwaiter()
.GetResult() ?? -1m;
}) is not decimal requestResult)
{
return "Загрузка...";
}
else if (requestResult < 0)
{
return ExcelError.ExcelErrorNA;
}
else
{
return Math.Round(requestResult, 2);
}
}
}
Теперь формула =ExchangeRate( )
возвращает сегодняшний курс евро, а если сослаться на ячейку с датой в формате Excel, на нужную дату.
Сборка и установка
Чтобы собрать нашу надстройку в один файл со всеми зависимостями, нужно добавить в файл .csproj
строки
<Target Name="PackedReferences" AfterTargets="AfterBuild" BeforeTargets="ExcelDnaBuild">
<ItemGroup>
<References Include="$(OutDir)*.dll" Exclude="$(OutDir)$(TargetFileName)" />
</ItemGroup>
<PropertyGroup>
<ExcelAddInInclude>@(References)</ExcelAddInInclude>
</PropertyGroup>
</Target>
Теперь делаем
dotnet build
и в папке \bin\Debug\net6.0-windows
забираем два собранных файла для 32 и 64-разрядных версий Excel
Эти файлы нужно поместить в папку %AppData%\Microsoft\AddIns
и в настройках Excel поставить галочку напротив нашего файла. Готово!
Заключение
В этой статье описывается как написать и собрать простую надстройку для Excel средствами .NET.
Код можно посмотреть по ссылке https://gitea.cebotari.ru/chebser/ExcelAddIn/
Комментарии (13)
AlexeyK77
10.06.2023 19:31+1Вирусописатели говорят большое спасибо корпорации микрософт еще за один сопособ незметного протаскивания и запуска всяких зловредов в обход систем безопасности.
vitesse
10.06.2023 19:31+5В статье написано "Поскольку у нас нет цифровой подписи появится предупреждение" и если вы скачали расширение без подписи и установили себе, а потом подтвердили запуск с "возможным нарушением безопасности", то "вирусописатели говорят большое спасибо" вам.
Microsoft не может заблокировать полностью запуск без подписи - тестировать же нужно как-то расширения, да и для личных нужд можно без подписи (мы когда делали код для автоматического заполнения таблиц по XML-datasource, сделали расширение чтоб выводить все биндинги в виде tooltip для помощи себе в отладке).Skykharkov
10.06.2023 19:31+1Вот именно. Если пользователь видит окно где явно написано - "Нет подписи\сертификата" или там "Издатель неизвестен" то он сам себе злобный буратино. А если админы такое разрешают запускать в корпоративной середе, то гнать таких админов ссаными тряпками на мороз.
AlexeyK77
10.06.2023 19:31Немножко соц.инженерии и пользователь нажмет ОК хоть сто раз. Основная проблема, что исполняемый код можно поместить в пользователский каталог, а потом офис его сам подгрузит в свое пространство. Это же классика жанра.
schebotar Автор
10.06.2023 19:31Один из побочных эффектов на самом деле, это то что Windows Defender очень не любит такие надстройки и может видеть в них вирус даже если ты их пишешь сам и знаешь что там зловреда нет.
BasiC2k
10.06.2023 19:31+1Не совсем понятно, зачем использовать эту библиотеку, если стандартный проект VSTO может все то-же самое. И пользовательскую функцию тоже.
aborouhin
10.06.2023 19:31+1Сам c Excel-DNA не работал (меня больше Word интересует), но, я так понимаю, из плюсов у неё остался более низкий порог входа для VBA-макрописателей и поддержка .NET Core, а не только Framework. Раньше, вроде, VSTO ещё требовало платной Visual Studio и не умело в UDF, но сие в прошлом.
Но вообще и то, и то, увы, deprecated. Для поддержки веб- и мак-версий Office (по моим впечатлениям, первыми пользуются не так часто, а вот маков расплодилось множество) остаются только Office Add-Ins на JS (причём интерфейс add-in'а можно на Blazor Webassembly написать, а вот взаимодействие с Office API - только JS без вариантов, печаль).
Sazonov
А под мак оно будет работать?
schebotar Автор
Windows-only, насколько мне известно.