Время на прочтение
7 мин
Количество просмотров 8.3K
Введение
Представляю вашему вниманию пошаговое руководство по разработке add-in’а для Excel.
Excel-DNA это бесплатная открытая библиотека для создания Excel расширений. Сайт проекта excel-dna.net
На протяжении данного руководства мы разработаем add-in, который позволяет по нажатию кнопки загружать данные со стороннего сайта в текущую страницу. Итак, начнем.
Разработка add-in’а
Для начала создадим новый проект типа Class Library, назовем его cryptostar. Подключим библиотеку excel-dna:
Install-Package ExcelDna.AddIn
Install-Package ExcelDna.Integration
Install-Package ExcelDna.Interop
Теперь можем приступать к реализации. В качестве источника данных будем использовать API api.coinmarketcap.com/v1/ticker, запрос возвращает массив объектов содержащих информацию о различных цифровых валютах.
[
{
"id": "bitcoin",
"name": "Bitcoin",
"symbol": "BTC",
"rank": "1",
"price_usd": "4512.7",
"price_btc": "1.0",
"24h_volume_usd": "2711790000.0",
"market_cap_usd": "74640450605.0",
"available_supply": "16540087.0",
"total_supply": "16540087.0",
"percent_change_1h": "0.3",
"percent_change_24h": "-7.03",
"percent_change_7d": "3.95",
"last_updated": "1504391067"
},
{
"id": "ethereum",
"name": "Ethereum",
"symbol": "ETH",
"rank": "2",
"price_usd": "336.689",
"price_btc": "0.0740905",
"24h_volume_usd": "1402470000.0",
"market_cap_usd": "31781255657.0",
"available_supply": "94393508.0",
"total_supply": "94393508.0",
"percent_change_1h": "2.36",
"percent_change_24h": "-13.01",
"percent_change_7d": "0.84",
"last_updated": "1504391070"
}
]
Первым делом напишем загрузчик данных:
public class Ticker
{
public string id { get; set; }
public string name { get; set; }
public string symbol { get; set; }
public decimal? rank { get; set; }
public string price_usd { get; set; }
public decimal? price_btc { get; set; }
public string market_cap_usd { get; set; }
public decimal? available_supply { get; set; }
public decimal? total_supply { get; set; }
public string percent_change_1h { get; set; }
public string percent_change_24h { get; set; }
public string percent_change_7d { get; set; }
public long last_updated { get; set; }
}
public class DataLoader
{
public Ticker[] LoadTickers()
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://api.coinmarketcap.com/v1/ticker/");
request.Method = "GET";
request.ContentType = "application/json";
using (var response = request.GetResponse())
using (var stream = response.GetResponseStream())
using (var responseReader = new StreamReader(stream))
{
string data = responseReader.ReadToEnd();
using (var sr = new StringReader(data))
using (var jsonReader = new JsonTextReader(sr))
{
var items = JsonSerializer.CreateDefault()
.Deserialize<Ticker[]>(jsonReader);
return items;
}
}
}
}
Пояснять данный код я не буду, так как он довольно простой и к нашей теме отношение имеет довольно опосредованное.
Теперь мы умеем получать данные в виде массива объектов класса Ticker. Пришло время научиться отображать эти данные на текущей странице.
Что бы отобразить данные, нам понадобится экземпляр класса Microsoft.Office.Interop.Excel.Application. Он предоставляет доступ к объектной модели Excel, через него мы сможем получить объект-страницу(worksheet) и записать наши данные в нужные ячейки. Давайте напишем класс для записи данных на страницу.
public class DataRender
{
public void RenderData(Ticker[] tickers)
{
// используем dynamic что бы не привязываться к конкретной версии Excel
dynamic xlApp = ExcelDnaUtil.Application;
// получаем активную страницу
var ws = xlApp.ActiveSheet;
// если страница не открыта ничего не делаем
if (ws == null)
return;
// очищаем содержимое страницы
ws.Cells.Clear();
// с использованием reflection заполняем страницу данными
var props = typeof(Ticker).GetProperties();
for (var j = 0; j < props.Length; j++)
{
var prop = props[j];
var cell = ws.Cells[1, j + 1];
cell.Value2 = prop.Name;
cell.Font.Bold = true;
}
// предварительно запишем данные в двумерный массив, а затем присвоим этот массив объекту Range. Это позволит значительно ускорить работу плагина по сравнению с вариантом, в котором каждое значение по отдельности устанавливается в отдельную ячейку.
object[,] data = new object[tickers.Length, props.Length];
for (var i = 0; i < tickers.Length; i++)
{
for (var j = 0; j < props.Length; j++)
{
var val = props[j].GetValue(tickers[i], null);
data[i, j] = val;
}
}
var startCell = ws.Cells[2, 1];
var endCell = ws.Cells[1 + tickers.Length, props.Length];
var range = ws.Range[startCell, endCell];
range.Value2 = data;
var firstCell = ws.Cells[1, 1];
// выравниваем колонки, чтобы все данные были на виду
ws.Range[firstCell, endCell].Columns.AutoFit();
}
}
При работе с объектной моделью надо помнить о том, что работаем со ссылками на COM объекты. В основном потоке Excel мы можем спокойно использовать эти объекты и не заботиться об освобождении ссылок (Marshal.ReleaseComObject), однако, если мы захотим использовать объектную модель из отдельного потока, у нас есть два варианта:
- Самостоятельно отслеживать все используемые объекты и очищать ссылки на них. Этот подход чреват ошибками и я не рекомендую его использовать.
- ExcelDna предоставляет возможность добавить задание на выполнение в основном потоке, для этого предназначен метод ExcelAsyncUtil.QueueAsMacro, пример использования:
ExcelAsyncUtil.QueueAsMacro(() =>{ Excel.Application xlApp = (Excel.Application)ExcelDnaUtil.Appplication; xlApp.StatusBar="Sending request..."; });
Таким образом, мы научились отображать данные на странице. Приступим к работе с пользовательским интерфейсом. ExcelDna позволяет вносить изменения в стандартный Ribbon, добавлять в него новые вкладки и кнопки. Создадим собственную вкладку и разместим на ней кнопку. По нажатию на кнопку будет происходить загрузка данных на текущую страницу. Для этого мы должны отнаследоваться от класса ExcelRibbon и переопределить метод GetCustomUI, метод возвращает RibbonXML с описанием интерфейса нашего add-in’а.
[ComVisible(true)]
public class RibbonController : ExcelRibbon
{
public override string GetCustomUI(string RibbonID)
{
return @"
<customUI xmlns='http://schemas.microsoft.com/office/2006/01/customui' loadImage='LoadImage'>
<ribbon>
<tabs>
<tab id='tab1' label='Cryptostar'>
<group id='group1' label='Cryptostar'>
<button id='button1' image='bitcoin' label='Get Data' onAction='OnButtonPressed'/>
</group >
</tab>
</tabs>
</ribbon>
</customUI>";
}
public void OnButtonPressed(IRibbonControl control)
{
try
{
var dataLoader = new DataLoader();
var tickers = dataLoader.LoadTickers();
var dataRender = new DataRender();
dataRender.RenderData(xlApp, tickers);
}
catch(Exception e)
{
MessageBox.Show(e.ToString());
}
}
}
Мы объявили кнопку, располагающуюся на закладке и группе с названием cryptostar. У кнопки задан обработчик onAction=’OnButtonPressed’, при нажатии на кнопку будет вызван метод OnButtonPressed в классе RibbonController.
Помимо обработчика мы указали изображение для кнопки: image=’bitcoin’. Имя изображения задается в конфигурационном файле — Cryptostar-AddIn.dna. Данный файл автоматически добавляется в проект при подключении nuget’a. Пример:
<Image Name="bitcoin" Path="bitcoin.png" Pack="true" />
Сборка и Отладка
Наш плагин готов, давайте попробуем его собрать. Нажимаем F5. После чего получаем набор файлов *.xll:
Cryptostar-AddIn64-packed.xll, Cryptostar-AddIn-packed.xll, Cryptostar-AddIn.xll, Cryptostar-AddIn64.xll
Видим, что полученные файлы отличаются как по разрядности, так и по наличию слова packed. С разрядностью все понятно, выбирать нужно тот, который совпадает по разрядности с Excel. А чем же отличаются packed и не packed add-in’ы? ExcelDNA позволяет упаковывать зависимости плагина в .xll файл. Зависимостями могут являться любые файлы, используемые в проекте, например внешние библиотеки или картинки. Зависимости задаются в конфигурационном файле, выглядит это так:
<DnaLibrary Name="Cryptostar Add-In" RuntimeVersion="v4.0">
<ExternalLibrary Path="Cryptostar.dll" ExplicitExports="false" LoadFromBytes="true" Pack="true" />
<Reference Path="Newtonsoft.Json.dll" ExplicitExports="false" LoadFromBytes="true" Pack="true" />
<Image Name="bitcoin" Path="bitcoin.png" Pack="true" />
</DnaLibrary>
Обратите внимание на атрибут Pack=”true”, он указывает, что данный файл должен быть упакован.
Если мы используем неупакованный add-in, то в одной директории с ним должны находиться и все его зависимости.
Теперь выбираем подходящий .xll файл и запускаем его. Если вы все сделали правильно, то после открытия Excel увидите новую вкладку Cryptostart и кнопку Get Data, а по нажатию на нее страница наполнится данными по валютам:
К сожалению, программы редко работают с первого раза, поэтому нам может потребоваться отладчик. Настроить отладку ExcelDna add-in’а просто. Для этого в свойствах проекта на закладке Debug выбираем Start External Program и прописываем путь к Excel.exe, в моем случае это G:Program FilesMicrosoft OfficeOffice14Excel.exe. В start options пишем название упакованного файла add-in’a с учетом разрядности Excel. Например, Cryptostar-AddIn64-packed.xll. Все, теперь мы можем нажать F5 и полноценно отлаживать add-in.
Делаем установщик
Итак, add-in сделан, отлажен, протестирован и готов к работе. Вопрос в том, в каком виде его распространять. Один из вариантов доверить установку add-in’a пользователю. Делается это через интерфейс Excel, на закладке developer tab->Add-ins->Browse указываем путь к .xll файлу. Данный способ будет работать, только если .xll файл подписан сертификатом и сертификат присутствует в trusted root certification authorities store. Как создать сертификат и подписать им файл хорошо описано здесь.
Альтернативный способ – написать свою программу для установки add-in’a, которая бы прописывала необходимые ключи в реестре и таким образом регистрировала наш add-in. Задача эта не из легких, т.к. необходимо учитывать различные версии Excel у которых пути и ключи в реестре различаются. Но к счастью эта задача уже решена и существует шаблон проекта — инсталлятора, выполняющего необходимые действия. Шаблон можно взять здесь.
Заключение
В результате мы познакомились с библиотекой Excel-DNA и прошли полный путь от разработки add-in’a до его отладки и создания установщика.
Исходный код проекта доступен по ссылке.
Содержание
- Excel-DNA
- Use .NET
- Make User-Defined Functions
- Create Custom Ribbon Add-ins
- Getting Started
- Create a Project in Visual Studio
- Write the Addin Code
- Compile and Run
- Debug
- Distribution
- Getting Help
- Supporting Excel-DNA
- Sponsorship
- Corporate Support Agreements
- Testimonials
- What is excel dna
- What is Excel-DNA?
- Aren’t there other ways to create Excel add-ins with .NET? Why should I use Excel-DNA?
- Shouldn’t I just stick to the official Microsoft tools for making Excel add-ins, rather than relying on a third-party tool?
- What about VBA? Can Excel-DNA help me use my VBA skills and still move to .NET?
- Разработка Microsoft Excel add-in’а с использованием библиотеки Excel-DNA
- Введение
- Разработка add-in’а
- Сборка и Отладка
- Делаем установщик
- Заключение
Excel-DNA
Free and easy .NET for Excel
Excel-DNA is an independent project that integrates .NET into Microsoft Excel to extend its native capabilities. Using C#, Visual Basic.NET or F#, it is possible to create a standalone add-in file (.xll) with high-performance user-defined functions (UDFs), custom ribbon interfaces, and much more!
Use .NET
Excel-DNA is a library that enables creation of Excel add-ins with .NET. Add-ins can be written in VB.NET, C# or F# (or a combination of these), using the Visual Studio IDE or a just a text editor.
Make User-Defined Functions
With Excel-DNA it is possible to create new worksheet functions that integrate with Excel’s calculation model.
Create Custom Ribbon Add-ins
Excel-DNA add-ins can also extend the Excel user interface with ribbon enhancements and custom task panes.
Getting Started
The easiest way to make an Excel-DNA addin is to create to follow these simple steps:
Create a Project in Visual Studio
- Select Create a new project and then select Class Library in either Visual Basic, C# or F#.
- Enter a name for the project.
- Under Framework, select the .NET 6.0 (Long-term support) option.
Write the Addin Code
- Depending on the language of choice, in the .csproj, .vbproj, or .fsproj file, change the value between the TargetFramework tags to net6.0-windows.
- Add the following under
:
Depending on the language of choice (C#, Visual Basic.NET or F#), add the following code to the class file (.cs, .vb or .fs):
Compile and Run
- To compile the solution, ensure to explicitly select Build Solution, under the Build menu item at the top menu bar. Alternatively, press the Ctrl+Shift+B key combination.
- To run the code after compilation, select Start Debugging, under the Debug menu item at the top menu bar. Alternatively, press F5.
- When the solution is running, Excel will open and a security notice will pop-up. Select the Enable this add-in for this session only. option.
- In Excel, open a new workbook and use the newly created function:
Debug
It is possible to debug the solution through Visual Studio. To do so, follow these simple steps while the solution is running:
- In Visual Studio, navigate to the line of code that is required debugging.
- Create a breakpoint by selecting Toggle Breakpoint, under the Debug menu item at the top menu bar. Alternatively, press F9. The line of code would be highlighted in red.
- In Excel, use the function that is needed to be debugged. The execution of the function will be caught by Visual Studio at the breakpoint. The line of code would be highlighted in yellow.
- In Visual Studio, inspect the code and change it as required. Once done, select Continue, under the Debug menu item at the top bar. Alternatively, press F5.
- Finally, see the new results reflect in Excel upon completion of execution of the debugged function.
Distribution
In order to use the newly created add-in, users would require the .NET 6 runtime to be installed. Additionally, the correct architecutre (32bit or 64bit) of the installation should be taken into consideration.
Getting Help
For further help with Excel-DNA or to report an issue with the library, please feel free to contact us via our Excel-DNA Google Group, which is our primary support channel. Within the Google Group, it is possible to find detailed, responsive help to queries and a searchable archive with over 5000 messages.
Excel-DNA was made freely available because of our enthusiasm for Excel and the .NET Framework. We are looking forward to help you get started, knowing that the initial steps could be daunting to some. All Excel-DNA questions are welcome!
Supporting Excel-DNA
Encouragement of future development of Excel-DNA, and access to direct support can be achieved through:
- Sponsoring the project via GitHub Sponsors, or
- entering into a Corporate Support Agreement.
Sponsoring the Excel-DNA project is greatly appreciated and a monthly or once-off payment can be made through GitHub at: https://github.com/sponsors/Excel-DNA
Corporate Support Agreements
Corporate users who are using the Excel-DNA library as part of their critical infrastructure, may want to enter a more formal and direct Corporate Support Agreement. For an annual subscription fee this will ensure:
- Continuity of the Excel-DNA project, with ongoing development and maintenance, adapting to new versions of Excel and .NET.
- Access to direct assistance and support in using the library.
- Priority for bug-fixes and feature requests.
For more details, please contact us by email: govert@dnakode.com
Testimonials
Jane Street uses Excel-DNA.
«We migrated to Excel-DNA, which has simplified greatly our Excel interfacing. It is a superb product, superior to others we have used previously, and Govert is incredibly responsive and helpful in maintaining and improving it.» — Alberto Cherubini, EQ Finance
«Excel-DNA is in use in our major locations around the world and in many mission critical scenarios. Usage covers the full gamut of features from simple UDF add-ins to managing real-time trading flow, interacting with order management systems. The open source Excel-DNA project is incredibly useful to us. Thanks very much to Govert and contributors.» — Paul Gresham, Hong Kong.
«I have never programmed with Excel and Excel-DNA allowed me build Calcbench’s Excel Add-In in two weeks.» — Andrew Kittredge, Calcbench
«Excel-DNA is incredibly versatile and stable framework for building Excel add-ins and user-defined functions. With such a pivotal element in our project, we were very concerned about committing to Excel-DNA that was developed by an enthusiast. However, we quickly learned that Govert has created an outstanding open source project; that he is keen on helping with any challenging questions and that the community is active enough to sustain a healthy evolution of Excel-DNA.» — Ilya Vadeiko, FinDynamics
«Excel-DNA is the open source project that quietly delivers a robust and simple to use extensibility framework for Microsoft Excel. When we felt we had outgrown VBA, no longer wanted the complexity of C++ and were thoroughly fed up with VSTO, Excel-DNA seemed a great choice and we haven’t been let down.
In Excel-DNA, combined with his incredible understanding of Excel integration, the .Net framework and passion for wanting to help others, Govert continues to deliver a project that makes Excel-DNA a ‘no brainer’ choice.
Thank you to Govert and to everyone involved in Excel-DNA — well done.» — Simon Miles, Solution 7
Источник
What is excel dna
Welcome to the Excel-DNA wiki!
The wiki in this project will be used to document the internal implementation of the Excel-DNA core library (the native code .xll, ExcelDna.Loader and ExcelDna.Integration). Documentation for those using Excel-DNA to build add-in should go into the https://github.com/Excel-DNA/Excel-DNA.github.io repository.
What is Excel-DNA?
Excel-DNA is a library to help you make Excel add-ins with .NET. Your add-in can be written in VB.NET, C# or F# (or a combination of these), using the Visual Studio IDE or a just a text editor.
Aren’t there other ways to create Excel add-ins with .NET? Why should I use Excel-DNA?
There are a few different ways of making Excel add-ins with .NET, but Excel-DNA has unique advantages. First, let me explain the different kind of Excel add-ins.
Now let’s put together a list of the different ways to make an Excel add-in with .NET:
- VSTO
- COM add-in
- C API
- Other libraries — NetOffice, Add-In Express, FCell.
Finally, where does Excel-DNA fit in? Excel-DNA brings together all three parts we need to make a great Excel add-in with .NET — the native Excel C API, the COM object model and the .NET runtime.
Shouldn’t I just stick to the official Microsoft tools for making Excel add-ins, rather than relying on a third-party tool?
Using only the Microsoft tools, it has been hard to make powerful and full-featured Excel add-ins with .NET, that work in different Excel versions, and are easy to deploy. Some of the problems are:
- Microsoft has no official support for using the native Excel C API in .NET add-ins
- VSTO has no support for making user-defined worksheet functions
- Automation add-ins can provide UDF, but have poor performance, and allow limited customization
- VSTO and regular COM-based add-ins require administrative rights to install
What if I want to make an Excel add-in with Python, C or C++?
Excel-DNA is used for making Excel add-ins with .NET. There are similar libraries that integrate with the native Excel C API, for making add-ins with other languages.
What about VBA? Can Excel-DNA help me use my VBA skills and still move to .NET?
VB.NET is the newest member of the Visual Basic family. While sometimes overshadowed in popularity by C#, VB.NET is as powerful as C# (sometimes more!), can access all the same .NET libraries, and is fully supported for making Excel-DNA add-ins. While VB.NET gives a familiar syntax if you’re coming from VBA, there are still a few changes that you need to get used to. But rest assured that VB.NET gives you access to the full power of .NET and Excel-DNA.
Excel-DNA add-ins can also integrate with VBA code by creating your own COM libraries that can be called from VBA. One advantage in putting these libraries inside an Excel-DNA add-in is that they can be deployed without requiring registration with administrator privileges.
Источник
Разработка Microsoft Excel add-in’а с использованием библиотеки Excel-DNA
Введение
Представляю вашему вниманию пошаговое руководство по разработке add-in’а для Excel.
Excel-DNA это бесплатная открытая библиотека для создания Excel расширений. Сайт проекта excel-dna.net
На протяжении данного руководства мы разработаем add-in, который позволяет по нажатию кнопки загружать данные со стороннего сайта в текущую страницу. Итак, начнем.
Разработка add-in’а
Для начала создадим новый проект типа Class Library, назовем его cryptostar. Подключим библиотеку excel-dna:
Install-Package ExcelDna.AddIn
Install-Package ExcelDna.Integration
Install-Package ExcelDna.Interop
Теперь можем приступать к реализации. В качестве источника данных будем использовать API api.coinmarketcap.com/v1/ticker, запрос возвращает массив объектов содержащих информацию о различных цифровых валютах.
Первым делом напишем загрузчик данных:
Пояснять данный код я не буду, так как он довольно простой и к нашей теме отношение имеет довольно опосредованное.
Теперь мы умеем получать данные в виде массива объектов класса Ticker. Пришло время научиться отображать эти данные на текущей странице.
Что бы отобразить данные, нам понадобится экземпляр класса Microsoft.Office.Interop.Excel.Application. Он предоставляет доступ к объектной модели Excel, через него мы сможем получить объект-страницу(worksheet) и записать наши данные в нужные ячейки. Давайте напишем класс для записи данных на страницу.
При работе с объектной моделью надо помнить о том, что работаем со ссылками на COM объекты. В основном потоке Excel мы можем спокойно использовать эти объекты и не заботиться об освобождении ссылок (Marshal.ReleaseComObject), однако, если мы захотим использовать объектную модель из отдельного потока, у нас есть два варианта:
- Самостоятельно отслеживать все используемые объекты и очищать ссылки на них. Этот подход чреват ошибками и я не рекомендую его использовать.
- ExcelDna предоставляет возможность добавить задание на выполнение в основном потоке, для этого предназначен метод ExcelAsyncUtil.QueueAsMacro, пример использования:
Таким образом, мы научились отображать данные на странице. Приступим к работе с пользовательским интерфейсом. ExcelDna позволяет вносить изменения в стандартный Ribbon, добавлять в него новые вкладки и кнопки. Создадим собственную вкладку и разместим на ней кнопку. По нажатию на кнопку будет происходить загрузка данных на текущую страницу. Для этого мы должны отнаследоваться от класса ExcelRibbon и переопределить метод GetCustomUI, метод возвращает RibbonXML с описанием интерфейса нашего add-in’а.
Мы объявили кнопку, располагающуюся на закладке и группе с названием cryptostar. У кнопки задан обработчик onAction=’OnButtonPressed’, при нажатии на кнопку будет вызван метод OnButtonPressed в классе RibbonController.
Помимо обработчика мы указали изображение для кнопки: image=’bitcoin’. Имя изображения задается в конфигурационном файле — Cryptostar-AddIn.dna. Данный файл автоматически добавляется в проект при подключении nuget’a. Пример:
Сборка и Отладка
Наш плагин готов, давайте попробуем его собрать. Нажимаем F5. После чего получаем набор файлов *.xll:
Cryptostar-AddIn64-packed.xll, Cryptostar-AddIn-packed.xll, Cryptostar-AddIn.xll, Cryptostar-AddIn64.xll
Видим, что полученные файлы отличаются как по разрядности, так и по наличию слова packed. С разрядностью все понятно, выбирать нужно тот, который совпадает по разрядности с Excel. А чем же отличаются packed и не packed add-in’ы? ExcelDNA позволяет упаковывать зависимости плагина в .xll файл. Зависимостями могут являться любые файлы, используемые в проекте, например внешние библиотеки или картинки. Зависимости задаются в конфигурационном файле, выглядит это так:
Обратите внимание на атрибут Pack=”true”, он указывает, что данный файл должен быть упакован.
Если мы используем неупакованный add-in, то в одной директории с ним должны находиться и все его зависимости.
Теперь выбираем подходящий .xll файл и запускаем его. Если вы все сделали правильно, то после открытия Excel увидите новую вкладку Cryptostart и кнопку Get Data, а по нажатию на нее страница наполнится данными по валютам:
К сожалению, программы редко работают с первого раза, поэтому нам может потребоваться отладчик. Настроить отладку ExcelDna add-in’а просто. Для этого в свойствах проекта на закладке Debug выбираем Start External Program и прописываем путь к Excel.exe, в моем случае это G:Program FilesMicrosoft OfficeOffice14Excel.exe. В start options пишем название упакованного файла add-in’a с учетом разрядности Excel. Например, Cryptostar-AddIn64-packed.xll. Все, теперь мы можем нажать F5 и полноценно отлаживать add-in.
Делаем установщик
Итак, add-in сделан, отлажен, протестирован и готов к работе. Вопрос в том, в каком виде его распространять. Один из вариантов доверить установку add-in’a пользователю. Делается это через интерфейс Excel, на закладке developer tab->Add-ins->Browse указываем путь к .xll файлу. Данный способ будет работать, только если .xll файл подписан сертификатом и сертификат присутствует в trusted root certification authorities store. Как создать сертификат и подписать им файл хорошо описано здесь.
Альтернативный способ – написать свою программу для установки add-in’a, которая бы прописывала необходимые ключи в реестре и таким образом регистрировала наш add-in. Задача эта не из легких, т.к. необходимо учитывать различные версии Excel у которых пути и ключи в реестре различаются. Но к счастью эта задача уже решена и существует шаблон проекта — инсталлятора, выполняющего необходимые действия. Шаблон можно взять здесь.
Заключение
В результате мы познакомились с библиотекой Excel-DNA и прошли полный путь от разработки add-in’a до его отладки и создания установщика.
Источник
- What if we could write plugins for Excel using the power of C# instead of VBA?
- What if we could target and maintain a plugin that is compatible with multiple excel versions, at the same time?
- What if we could provide our users with their familiar Excel workflow but add custom options, template generation, database access, and dynamic menus?
- What if I could create user defined worksheet functions (UDFs) that run fast and asynchronously?
… Enter Excel-DNA.
In this blog I give a tutorial on getting started with Excel-DNA, a project to build excel add-ins using .NET. By the end you will have a new custom tab in your excel ribbon that contains a button. I will also show you how to debug this excel plugin by running the project directly from Visual Studio.
All code can be found at https://bitbucket.org/andysprague44/excel-dna-examples/HelloWorld.
What is Excel-DNA?
Simply put, Excel-DNA is an independent open-source project to integrate .NET into Excel. If you were wondering, the ‘DNA’ stands for DotNet for Applications, as opposed to VBA standing for Visual Basic for Applications. The full introduction can be found at the home page on https://exceldna.codeplex.com/. Some getting started instructions can be found at https://exceldna.codeplex.com/wikipage?title=Getting%20Started.
If the above has stoked your curiosity, but you are already tied to a VBA add-in, this blog is not for you. Instead you need to switch, and for this you should visit the excellent blog at https://sysmod.wordpress.com/2012/11/06/migrating-an-excel-vba-add-in-to-a-vb-net-xll-with-excel-dna-update/
Getting Set-Up
Install Visual Studios, and add a new Class Library (.NET Framework) project called MyExcelAddin. (Note I’m using .net framework 4.7.2, but I’d be surprised if it didn’t work from 4.5 onwards).
Using the package manager console (Tools –> NuGet Package Manager –> Package Manager console) run the following:
Install-Package ExcelDna.AddIn Install-Package ExcelDna.Integration Install-Package NetOfficeFw.Excel
This should have resulted in a new file being created at the project root called “MyExcelAddin-AddIn.dna”. Leave it alone, but you don’t need to understand the contents yet 🙂
Building the ribbon
To add a custom ribbon we need 2 additional files – an XML description of the new custom ribbon element, and a class that implements ExcelDna.Integration.CustomUI.ExcelRibbon.
Add these both to the root folder of the project and call them ‘CustomRibbon.xml’ and ‘CustomRibbon.cs’
CustomRibbon.xml:
<?xml version="1.0" encoding="utf-8" ?> <customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui" onLoad="OnLoad"> <ribbon> <tabs> <tab id="MyFirstExcelRibbonTab" label="My Custom Tab"> <group id="HelpGroup" label="My Group"> <button id="PressMeButton" label="Press Me!" imageMso="FastForwardShort" size="large" onAction="OnPressMe" /> </group> </tab> </tabs> </ribbon> </customUI>
We also have to add the XML file as an embedded resource. To do this go to the file in Solution Explorer, right click and go to properties. Then set the Build Action to ‘Embedded Resource’.
CustomRibbon.cs:
using System; using System.IO; using System.Resources; using System.Reflection; using System.Runtime.InteropServices; using Application = NetOffice.ExcelApi.Application; using ExcelDna.Integration.CustomUI; namespace MyExcelAddin { [ComVisible(true)] public class CustomRibbon : ExcelRibbon { private Application _excel; private IRibbonUI _thisRibbon; public override string GetCustomUI(string ribbonId) { _excel = new Application(null, ExcelDna.Integration.ExcelDnaUtil.Application); string ribbonXml = GetCustomRibbonXML(); return ribbonXml; } private string GetCustomRibbonXML() { string ribbonXml; var thisAssembly = typeof(CustomRibbon).Assembly; var resourceName = typeof(CustomRibbon).Namespace + ".CustomRibbon.xml"; using (Stream stream = thisAssembly.GetManifestResourceStream(resourceName)) using (StreamReader reader = new StreamReader(stream)) { ribbonXml = reader.ReadToEnd(); } if (ribbonXml == null) { throw new MissingManifestResourceException(resourceName); } return ribbonXml; } public void OnLoad(IRibbonUI ribbon) { if (ribbon == null) { throw new ArgumentNullException(nameof(ribbon)); } _thisRibbon = ribbon; _excel.WorkbookActivateEvent += OnInvalidateRibbon; _excel.WorkbookDeactivateEvent += OnInvalidateRibbon; _excel.SheetActivateEvent += OnInvalidateRibbon; _excel.SheetDeactivateEvent += OnInvalidateRibbon; if (_excel.ActiveWorkbook == null) { _excel.Workbooks.Add(); } } private void OnInvalidateRibbon(object obj) { _thisRibbon.Invalidate(); } } }
This file has a bit of magic in it, but the method descriptions should be pretty self-explanatory. You are unlikely to need to change the above methods initially. What we will do is to add new methods to handle events raised from COM elements. You might have noticed there is an action on the button defined in the XML file called “OnPressMe” that currently does nothing. So lets make it do something!
First though, let’s take a short side-track and get our new excel add-in working in Excel.
Debugging the add-in from Visual Studio
To view the ribbon in Excel, we could set this up manually. First build the project, then open Excel and go to File –> Options –> Add-ins –> Manage: Go… –> Browse, and add the dll at MyExcelAddin/bin/Debug/MyExcelAddin-AddIn64.xll.
However, during development this loop is pretty slow. We can instead use Visual Studio so that Excel is launched with the add-in automatically attached when clicking Start. You might have been lucky enough that this was automatic (it was for me). But if not go the properties of the project (Alt + Enter from Solution Explorer) and choose ‘Start external program’ from the Debug settings and point to your excel location [on my machine, this was C:Program FilesMicrosoft OfficerootOffice16EXCEL.EXE].
Also add ‘MyExcelAddin-AddIn.xll’ to the Command line arguments.
Now click ‘Start’. Excel should load up and have the plugin attached. You will need to click ‘Enable Plugin’ on launch if prompted.
You should now see your new ribbon element in Excel!
Doing something useful
The last step is to hook up the button to perform an action. In the spirit of all beginner tutorials the world over lets get it to write “Hello, World!” to cell A1.
To do this, we need to add an event handler for the action to CustomRibbon.cs. Add the following snippet to the end of the class:
public void OnPressMe(IRibbonControl control) { using (var controller = new ExcelController(_excel, _thisRibbon)) { controller.PressMe(); } }
Now, let’s add our controller. Add a new class to the project called ExcelController, and copy in the following code:
using System; using Application = NetOffice.ExcelApi.Application; using ExcelDna.Integration.CustomUI; using NetOffice.ExcelApi; namespace MyExcelAddin { class ExcelController : IDisposable { private readonly IRibbonUI _modelingRibbon; protected readonly Application _excel; public ExcelController(Application excel, IRibbonUI modelingRibbon) { _modelingRibbon = modelingRibbon; _excel = excel; } public void PressMe() { var activeSheet = _excel.ActiveSheet as Worksheet; activeSheet.Range("A1").Value = "Hello, World!"; } public void Dispose() { } } }
Now, restart your application and click the button, voila!
excel-dna / exceldna
Goto Github
PK
View Code? Open in Web Editor
NEW
93.0
248.0
59.41 MB
Excel-DNA — Free and easy .NET for Excel. This repository contains the core Excel-DNA library.
Home Page: https://excel-dna.net
License: zlib License
C# 68.04%
PowerShell 0.07%
C++ 7.68%
C 1.39%
Assembly 22.40%
Batchfile 0.31%
Visual Basic .NET 0.07%
F# 0.04%
excel
hacktoberfest
exceldna’s Introduction
exceldna’s People
exceldna’s Issues
Normalize line-endings to match .gitattributes
When cloning the main repository, a lot of files immediately appear as being modified because they are currently stored using Linux line-endings, and the .gitattributes
file is set to make the transformations based on platform (LF => CRLF).
Create an .xsd file for the .dna files
Creating an .xsd schema file for the .dna files should lead to an improved editing experience in Visual Studio.
All public static methods getting registered as functions
Hey Govert,
I recently updated from 0.32 to 0.33 to see if I can get that sweet Intellisense working. In doing so I noticed it’s now registering all public static methods in my assembly as excel functions.
My expectation (and I believe the prior behaviour) was that only methods with the [ExcelFunction()]
attribute or similar would get registered. Is there an easy way to get back to that?
Fix AppVeyor build
The part of the build that managed output files into NuGet packages is not working right, causing old files to be put into the NuGet packages.
After Unregistering an add-in with COM object, Excel crashes when closing
Due to DllCanUnload() attempting to reload the add-in during shutdown.
Reproduced in SamplesMasterSlave
Replace the post-build commands with custom MSBuild tasks + custom build target
As discussed in #15 we should write one or more custom MSBuild tasks, ship them into one assembly, and have a custom build target to run them as part of the build.
These MSBuild tasks would be responsible for copying the XLL to the bin folder, as well as running ExcelDnaPack based on a conditional compilation symbol.
Add a check to ExcelReference for valid SheetId
Improve the way we determine the path for Excel and add-in platform, for debugging
This is somewhat related to issue #18, where I’m proposing that we move the Excel.exe path and add-in to the project file, so that the debugging information is persisted in source control, and any developer that clones the repository would then be able to debug the add-in without having to configure anything.
We know that we can’t control what version of Excel each developer has installed on his/her machine… Some developers run Excel 2010 32-bit, others run Excel 2010 64-bit, others Excel 2013 64-bit, etc.
I’d like a developer to be able to clone my repository, open my solution in Visual Studio, and just hit F5 and be able to Debug it, starting an instance of the Excel installed on his/her machine, no matter what version is there, and with the right add-in XLL file (x64 or x86) for that debugging session.
Whilst I don’t have a perfect solution for that yet, I do have a workaround that have been working well for most cases:
Inside the project file, I add a few conditional property groups that define the StartProgram
variable with the correct path of the Excel on the machine (if matches any, of course)
<PropertyGroup Condition="'$(StartProgram)' == '' AND Exists('C:Program FilesMicrosoft OfficeOffice15EXCEL.EXE')">
<StartProgram>C:Program FilesMicrosoft OfficeOffice15EXCEL.EXE</StartProgram>
</PropertyGroup>
<PropertyGroup Condition="'$(StartProgram)' == '' AND Exists('C:Program FilesMicrosoft OfficeOffice14EXCEL.EXE')">
<StartProgram>C:Program FilesMicrosoft OfficeOffice14EXCEL.EXE</StartProgram>
</PropertyGroup>
<!-- etc... -->
I also have the similar conditionals for 32-bit Excel installed on Windows 64-bit machines:
<PropertyGroup Condition="'$(StartProgram)' == '' AND Exists('C:Program Files (x86)Microsoft OfficeOffice15EXCEL.EXE')">
<StartProgram>C:Program Files (x86)Microsoft OfficeOffice15EXCEL.EXE</StartProgram>
</PropertyGroup>
<PropertyGroup Condition="'$(StartProgram)' == '' AND Exists('C:Program Files (x86)Microsoft OfficeOffice14EXCEL.EXE')">
<StartProgram>C:Program Files (x86)Microsoft OfficeOffice14EXCEL.EXE</StartProgram>
</PropertyGroup>
<!-- etc... -->
And a similar kind of test is applied to determine the right add-in to execute (32-bit or 64-bit)
<PropertyGroup Condition="'$(StartProgram)' != '' AND $(StartProgram.Contains('Program Files (x86)'))">
<StartArguments>"MyXL-AddIn.xll"</StartArguments>
</PropertyGroup>
<PropertyGroup Condition="'$(StartProgram)' != '' AND (! $(StartProgram.Contains('Program Files (x86)')) )">
<StartArguments>"MyXL-AddIn64.xll"</StartArguments>
</PropertyGroup>
And finally, conditional to set the StartAction
property
<PropertyGroup Condition="'$(StartProgram)' != ''">
<StartAction>Program</StartAction>
</PropertyGroup>
What are your thoughts on this (or something like this)?
ps: These MSBuild instructions don’t necessarily need to be on the .csproj
(could be a custom .targets file added to the project to make it easier to edit from within Visual Studio).
ExcelDna ribbon doesn’t show in another office versions.
I have compile my project and works good with Excel 2010 but its ribbon not showing in Excel 2007 version.
Is there any workaround to compstible ExcelDna with multiple Excel version?
Diagnostic Logging : «default configuration» does not work
Hello,
I added the following lines to an Excel Command (a brand new Excel-DNA project).
Trace.TraceInformation(«Trace information!»);
Trace.TraceWarning(«Trace warning!»);
Trace.TraceError(«Trace error!»);
I expect to see the LogDisplay opens (because of TraceError) and I expect to see the two last messages (because TraceInformation should not be logged by default).
The documentation states:
«If there is no .xll.config configuration file for the add-in, or there are no entries related to the ExcelDna.Integration TraceSource in the .config file, the following configuration is made»
Nonetheless, when I debug the running code, the Trace class has only the DefaultTraceListener.
Am I missing something or is this a bug?
Thanks in advance.
NB: I am running Excel 2013 64 bit
Working with App.Config files
I find working with the App.Config file a really hard task. I have been wondering on how to actually ease my pain and I found a solution which seems to work partially : It gets done through a class that I found on SO :
Is there a better way to actually work with an app config file that is already built in? If not, would you consider integrating the class I have been using? The problem is that whenever I use the ConfigurationManager Excel looks for the config file in MyDocuments since that is the default working directory for Excel.
Add TlbExp option to build targets
For add-ins that expose the COM Server, there is an additional step in the build process, to run TlbExp to generate a .tlb file from the compiled .dll.
It would be nice to have that as an option in the build process.
Last argument description of every function has «. » added to it, even if it already ends that way.
Minor issue worth resolving in the next release.
The code here meant to fix the «Excel Function Description bug» is resulting in already well formatted function argument descriptions getting two periods:
The solution is probably as simple as trimming off any trailing period and space before adding it back:
argumentDescriptions[j] = argumentDescriptions[j].TrimEnd(new[] { ' ', '.' }) + ". ";
This will play nice with people whose argument descriptions already end in «. » or just «.»;
If we want to be more sensitive to people who choose to have things like ellipses at the end of their descriptions (and we probably do), maybe something more long winded, like:
if (!argumentDescriptions[j].EndsWith(". "))
{
if (argumentDescriptions[j].EndsWith("."))
argumentDescriptions[j] += " ";
else
argumentDescriptions[j] += ". ";
}
That way descriptions like:
"Custom tags, metadata, etc..."
will be converted to:
"Custom tags, metadata, etc... "
rather than
"Custom tags, metadata, etc.... "
or
"Custom tags, metadata, etc. "
Excel call on non-UI thread throws an exception
Running something like this on non-UI thread would cause Excel-Dna to throw an exception wihout any inner exception details.
var addinPath = (string)XlCall.Excel(XlCall.xlGetName); // exception gets thrown over here.
var addinDir = addinPath.Substring(0, addinPath.LastIndexOf('\'));
var configPath = @"Resourcesconfig.xml";
var filePath = Path.Combine(addinDir, configPath);
return filePath;
ExcelDnaPack does not preserve original encoding of source code files
The source file is not guaranteed to be always be UTF8 (which is what File.ReadAllText
) assumes. Instead, ExcelDnaPack
should read source code files as binary before packing (just like it does with images) to preserve whatever encoding is being used by the developer, in the file.
Make ExplicitExports «true” by default (or at least make it default in the .dna template)
I think ExplicitExports
should be true
by default as IMO this is what most ExcelDna developers would want/expect.
I’ve been bitten by this before I knew this attribute existed, and leaked a few static classes as formulas in an add-in, which caused me some pain.
I realize this could break existing add-ins, but sometimes breaking changes are necessary — and if documented well, should not cause too many issues.
If introducing this change is not an option in the short-term, I’d suggest at least update the ExcelDna-Template.dna
to declare ExplicitExports="true”
there so that anyone creating a new add-in will get the expected behavior (and also makes this attribute more discoverable).
<DnaLibrary Name="%ProjectName% Add-In" RuntimeVersion="v4.0">
<ExternalLibrary Path="%OutputFileName%" LoadFromBytes="true" Pack="true" ExplicitExports="true” />
</DnaLibrary>
code changes necessary for x64 xll packing
documentation suggestion
In Excel-DNA — Step-by-step C# add-in.doc, in addition to Load the corresponding ExcelDna64.xll file instead. can you please add Also, ensure that the ExcelDna64.xll file is used in the packing process if applicable? This would make it absolutely clear that this is necessary.
code changes necessary for x64 xll packing
The following code in SourceExcelDnaPackPackProgram.cs had to be changed from
xllInputPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, «ExcelDna.xll»);
to
xllInputPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, «ExcelDna64.xll»);
XlCall.Excel(XlCall.xlGetName) from Ribbon throws unhandled exception
I get an ExcelDna.Integration.XlCallException after trying to access XlCall.Excel(XlCall.xlGetName) from a ribbon.
The same accessing technique works if I use it from an UDF.
The error comes from over here :
Automatically replace XLL version information with data read from an assembly
This is a (slightly modified) feature request that was asked in the mailing-list, so just adding as an issue so we can track this better and possibly encourage someone to implement and send a PR.
Original requests:
- Exposing resource info in ExcelDNAPack
- Versioning the xll for Windows Installer
My assumption is that the developer (or ideally the continuous integration system) should be responsible for setting the right version information about the add-in, in the main assembly (or in all of them). With that said, we should be able to describe in the Excel-DNA XML file which assembly should be used to read version information about the add-in (version number, copyright, product name, etc.) and any information available in this assembly should automatically replace what is in the XLL file, during the build of the project.
I would suggest this to be implemented as a regular MSBuild Task, rather than implementing this as part of the ExcelDnaPack utility given that people might not care about packing their add-ins — and still would like to get version information in the XLL that gets copied during the build.
Allow complete unloading of add-in — implement DllCanUnloadNow()
This is a follow-up to the issue #61 . We have fixed the crash, but still con’t allow the .xll to unload completely if COM features were used.
Revisit this and implement DllCanUnloadNow() if possible.
Build event commands should not use hard-coded paths
They should use macros to determine the path based on the project (or solution path).
This would enhance the experience of developing an Excel add-in with a team or developing an open-source add-in, as any person would be able to clone the repository and compile the project, no matter what folder structure they used.
Detailed registration logging
Add a way to monitor registration progress and particularly failures. It can be hard to see why some functions are not registered — perhaps because of incompatible signatures, scope or visibility. It would be useful to have a general logging mechanism to monitor the loading and registration, mainly for trouble shooting.
This should be an opt-in mechanism, perhaps defining an ILogging interface, and having a place to register a logger implementation early in the load sequence (AutoOpen might be too late).
The custom functions with DNA runs more than once
I don’t know why when I use a function with DNA from Excel, it’s launch 2 o 3 times.
AddInReloader Rtd Issue
@AlanStubbs wrote:
I’ve been working on an adaptation of the AddInReloader project and it all works fine until I have RtdServer-based UDF calls in the workbook. In this case, my «master» addin is unable to fully unregister the current «slave» addin (ExcelIntegration.UnregisterXLL(path)) so that subsequently trying to delete the current slave xll produces a System.UnauthorizedAccessException. Strangely, it allows me to rename the slave xll file to slave.backup but then when I register the new slave addin (RegisterXLL(slave)), it actually registers slave.backup again.
I’ve tried adding calls to ComServer.DllUnregisterServer() in various locations — the slave’s AutoClose, before/after the call to UnregisterXLL, etc — all to no avail. All the problem code is running inside QueueAsMacro.
Do you expect this behaviour — and do you know what I can do to fix it? With a blank workbook, everything works perfectly. I’m using Excel-DNA.0.32.0.
Many thanks,
Alan
Issue originally reported in the ExcelDna mailing-list:
https://groups.google.com/forum/#!topic/exceldna/LHsR2dZkl8g
Ignore IsExceptionSafe=true when there is a bool parameter
A function like this
[ExcelFunction(IsExceptionSafe = true)]
public static string GetBool(bool b)
{
return b.ToString();
}
called from Excel with an unrecognized string parameter like «a» will cause Excel to crash.
This is due to the marshaling failure in Excel-DNA causing an unhandled exception which is leaked to Excel (since the IsExceptionSafe=true inhibits generation of the exception handling wrapper).
The fix would be to ignore the IsExceptionSafe=true flag for functions that have a boolean parameter.
Excel-DNA 33.9 and Excel 16 /2015 64 bit
I am updating a client UDF package that included some ribbon menus and built in documentation so the function would show up in the function directory during spreadsheet entry and the types would validate.
The demo software DOES NOT FUNCTION out of the box on 64 bit.
I had to fall back to compileing 32 bit and it seems to work on 64 bit excel.
Implement continuous integration builds with Appveyor
Add automated builds through AppVeyor, and packages published through a MyGet feed, for Excel-DNA and the Registration extension.
I have registered accounts with both in the past, but I still have to figure out how the permissions and accounts should work to allow others to work on this.
There also a tool to help with the NuGet package configurations, but I can’t find it now…
Framework update 4.6.1 and Excel DNA
Hi Govert,
We have been using ExcelDNA for a while now and it has been working brilliantly. Microsoft released the Framework update 4.6.1, and now 64bit Excel with ExcelDNA does not work. The sample code returns #num when the function is being executed.
Our code — which is slightly modified crashes excel and the exception is occurring in the CLR (using windbg to trace). Uninstalling the 4.6.1 update does not fix the issue. A complete uninstall of Framework 4.0 and subsequent re-install of 4.5.2 fixes it.
I am certain that ExcelDNA is not the culprit but that Microsoft update is. Any advice on how to pursue to get a fix?
ExcelDnaPack crashes during build — needs to catch exceptions before MSBuild does
When an unhandled exception occurs in ExcelDnaPack
, it lets it bubble up to the caller, and crashes. This causes MSBuild/Visual Studio to show the dialogs below:
We need to catch any unhandled exceptions in Main, and return a Environment.ExitCode >= 1 to signal that it failed — so that MSBuild can fail the build too.
Add support for unit testing in Excel-DNA
Currently it is not possible to unit test Excel add-ins built with Excel DNA, because pretty much all interactions one does with the Excel DNA framework requires calling methods in static classes, which in turn call Excel APIs/features and because these are static classes, it is not possible to mock these classes.
I’ve made a good process with my ExcelDna.Abstractions project which addresses many of these issues, but it is not ideal and it is harder to maintain as Excel DNA moves and new methods are added.
I’d like all static classes that relies on Excel functionality to be implemented as regular classes that also implement interfaces (so that we can depend on these interfaces instead, and be able to mock) and then mark the static classes Obsolete
, to encourage devs to use the new regular classes/interfaces.
This issue is somewhat related to issue #20.
COM registration may fail with roaming profiles
Investigate Excel 2016 RTD server startup for persistent RTD server
Debug instructions to run Excel should be stored in the project file
When I install the Excel-DNA NuGet package, it adds the instructions to allow me to debug the add-in by starting Excel and passing the add-in file as a parameter, which is great.
Unfortunately it doesn’t make this change in the project file (e.g. .csproj
). Instead, it updates the user’s file related to the project (e.g. .csproj.user
) which is individual for each developer, and therefore is not committed to the source control.
Example of a .csproj.user
file:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/ ...">
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'">
<StartAction>Program</StartAction>
<StartProgram>C:Program FilesMicrosoft OfficeOffice15EXCEL.EXE</StartProgram>
<StartArguments>"MyExcelAddIn-AddIn.xll"</StartArguments>
</PropertyGroup>
</Project>
In practice, what happens is that when a developer clones a repository from source control and tries to debug it, it never works…
The workaround so far, is to manually copy/paste the contents of the .csproj.user
file into the .csproj
file.
Example of a manually changed .csproj
file:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- ... -->
<PropertyGroup>
<!-- ... -->
<TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<StartAction>Program</StartAction>
<StartProgram>C:Program FilesMicrosoft OfficeOffice15EXCEL.EXE</StartProgram>
<StartArguments>"MyExcelAddIn-AddIn.xll"</StartArguments>
</PropertyGroup>
<!-- ... -->
What I would like, is for the NuGet package to update the .csproj
file with the StartAction
, StartProgram
and StartArguments
, instead of updating the .csproj.user
file (and equivalent for other languages).
Running on Linux/Mono
Do you think there’s any way to pack XLLs on Linux?
When I try, I get the following error:
Using base add-in Blah-AddIn.xll
Unhandled Exception:
System.EntryPointNotFoundException: BeginUpdateResource
at (wrapper managed-to-native) ResourceHelper:BeginUpdateResource (string,bool)
at ResourceHelper+ResourceUpdater..ctor (System.String fileName) [0x00000] in <filename unknown>:0
at ExcelDnaPack.PackProgram.Main (System.String[] args) [0x00000] in <filename unknown>:0
[ERROR] FATAL UNHANDLED EXCEPTION: System.EntryPointNotFoundException: BeginUpdateResource
at (wrapper managed-to-native) ResourceHelper:BeginUpdateResource (string,bool)
at ResourceHelper+ResourceUpdater..ctor (System.String fileName) [0x00000] in <filename unknown>:0
at ExcelDnaPack.PackProgram.Main (System.String[] args) [0x00000] in <filename unknown>:0
Same error wether I download the binaries or build from source.
RTD-based ExcelAsyncUtil.Observe does not restart when reopening book
Investigate assembly redirection in .xll.config files
It seems that assembly redirection specified in .xll.config files is not applied.
Investigate and document or fix.
Exception handling
Hi,
I’m quite new to Excel-DNA but already find it really powerful. I seem to me exception handling mechanism in Excel-DNA is not able to catch an exception thrown in C# within user’s code. For instance, I have a dummy function named «divide» which takes as argument 2 doubles. In practice, the exception thrown by the null division returns #Value, which does not give me much information about what’s happening.
I would expect Excel-DNA to return the actual Exception message and the name of the main method which throws the exception.
I took a look at the source code and a quick fix in the method HandleUnhandledException could do the trick instead of just returning ExcelError.ExcelErrorValue. What do you think ?
Rgds,
Yenyenx
XlCustomMarshal::MarshalManagedToNative memory corrupted
After a long hour running, always crash the Excel with «System.NullReferenceException»
The following are the variable info. Strangely the pOper is 0x0000000c0 for the last string value of the ManagedObj.
LogDisplayTraceListener can’t be used in the top trace listener
With the below config, app will get the NULL exception because of the Debug.print() output in initialization.
All the Debug methods won’t check the TraceFilter. So there is no way to directly use the «LogDisplay» for error message popup.
The NULL exception is caused by the «LogDisplay.RecordLine(message);» That function doesn’t check whether the «LogStrings» is null.
If add the following check, LogDisplay can be used directly.
if (LogStrings == null)
return;
App.config
<sharedListeners>
<add name="LogDisplay" type="ExcelDna.Logging.LogDisplayTraceListener,ExcelDna.Integration"/>
</sharedListeners>
<trace autoflush="false" indentsize="4">
<listeners>
<remove name="Default"/>
<add name="LogDisplay"/>
</listeners>
</trace>
xlfRegister call failed for function or command
Fix NuGet install script for F# in Visual Studio 2015
When installing the NuGet package into an F# project in VS 2015, there is an error:
Exception setting "Value": "Object of type 'Microsoft.VisualStudio.FSharp.ProjectSystem.BuildAction' cannot be converted to type
'VSLangProj.prjBuildAction'."
At C:TempFs2015TestpackagesExcelDna.AddIn.0.33.7-rc1toolsinstall.ps1:56 char:13
+ $newDnaFile.Properties.Item("BuildAction").Value = ([Microsoft.Visua ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], SetValueInvocationException
+ FullyQualifiedErrorId : ExceptionWhenSetting
This is not a serious setting to change, and everything else still seems to work.
Another F#-related problem in the NuGet install script is to configure the Debug executable. F# uses a different project system and this setting is not (easily) accessible.
XlCall.Excel(XlCall.xlUDF, «Resize»… throws AccessViolationException
I have a following function and it does not return in XlCall.Excel
and throws an exception,
private static object PrintArray()
{
object[,] retArray = new object[3, 1];
for (int i = 0; i < 3; i++)
{
retArray[i, 0] = DateTime.Now;
}
var returnArray = XlCall.Excel(XlCall.xlUDF, "Resize", retArray);
return returnArray;
}
Exception:
System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
at ExcelDna.Loader.XlCallImpl.TryExcelImpl12(Int32 xlFunction, Object& result, Object[] parameters)
at ExcelDna.Loader.XlCallImpl.TryExcelImpl(Int32 xlFunction, Object& result, Object[] parameters)
at ExcelDna.Integration.ExcelIntegration.TryExcelImpl(Int32 xlFunction, Object& result, Object[] parameters)
at ExcelDna.Integration.XlCall.TryExcel(Int32 xlFunction, Object& result, Object[] parameters)
at ExcelDna.Integration.XlCall.Excel(Int32 xlFunction, Object[] parameters)
at AsyncFunctions.ArrayResizer.Resize(Object[,] array)
at Wrapped_f12_Resize(Object[,] )
at ExcelDna.Loader.XlCallImpl.TryExcelImpl12(Int32 xlFunction, Object& result, Object[] parameters)
at ExcelDna.Loader.XlCallImpl.TryExcelImpl(Int32 xlFunction, Object& result, Object[] parameters)
at ExcelDna.Integration.ExcelIntegration.TryExcelImpl(Int32 xlFunction, Object& result, Object[] parameters)
at ExcelDna.Integration.XlCall.TryExcel(Int32 xlFunction, Object& result, Object[] parameters)
at ExcelDna.Integration.XlCall.Excel(Int32 xlFunction, Object[] parameters)
at PlantSiSReport.Functions.PrintArray()
Add support for dependency injection in Excel-DNA
I’d like to be able to setup my IoC container via an entry-point of the add-in (my understanding is that the AutoOpen
of a class implementing IExcelAddIn
will be the very first thing to run when Excel loads the add-in)
public class AddIn : IExcelAddIn
{
public void AutoOpen()
{
// Setup my IoC container
myContainer.Register<AwesomeService>.As<IMyService>();
myContainer.Register<GreatLogger>.As<ILog>();
}
}
And then be able to have dependency resolution on any of my Ribbons in the add-in, ideally via constructor injection (*).
[ComVisible(true)]
public class ModelingRibbon : ExcelRibbon
{
public ModelingRibbon(IMyService service, ILog logger)
{
// ...
}
}
(*) Not sure if constructor injection would not be possible… Looks like the Ribbon instance is constructed before the AutoOpen
runs, which would invalidate the idea of setting up the container in the AutoOpen
.
Anyway, goal is to have an entry point that allows the opportunity to setup an IoC container, and have dependency resolution across the different instances created by the framework.
Multithreaded packing fails for large number of assemblies
When packing a large number of assemblies (about 80), the new multi-threaded packing fails with a message
System.NotSupportedException: The number of WaitHandles must be less than or equal to 64.
at System.Threading.WaitHandle.WaitAll(WaitHandle[] waitHandles, Int32 millisecondsTimeout, Boolean exitContext)
at System.Threading.WaitHandle.WaitAll(WaitHandle[] waitHandles)
at ResourceHelper.ResourceUpdater.EndUpdate(Boolean discard)
at ResourceHelper.ResourceUpdater.EndUpdate()
at ExcelDnaPack.PackProgram.Pack(String[] args)
at ExcelDnaPack.PackProgram.Main(String[] args)
There is a limit on the number of events we can wait for, and currently we make an event per assembly that is packed.
@konne If you’re still around, would you perhaps like to look at this?
A workaround is to run the ExcelDnaPack without multi-threading, by adding the /NoMultiThreading
switch to the command line.
ExcelRtdServer objects are leaked
Initially reported on CodePlex: https://exceldna.codeplex.com/discussions/581717#
RTD server objects derived from the ExcelRtdServer
base class are not released by the registration mechanism after ServerTerminate()
. As a result, the server instances live as long as the Excel process. This may have to do with the COM server support implemented by Excel-DNA.
A partial workaround to mitigate the memory leak might be to set all references to further managed objects in the ExcelRtdServer-derived class are set to null in the ServerTerminate()
implementation.
Exception when call RTD in ExcelRibbon
It is a strange error. It is all right to invoke the RTD in the UDF, which call the UDF in the excel sheet cell . But if call it in the ribbon, the exception will be raised. In the debugger, I can see the request is processed properly by RTD. Just before return, the following exception will be raised.
UDF:
public static class UserDefinedFunctions
{
[ExcelFunction(Description = "start the RTD server for order submission & price data")]
public static object startServer(String IP, int port)
{
return XlCall.RTD("ExcelTradeGateway.FIXRTDServer", null, new string[] { "CONFIG", IP, port.ToString()});
}
}
UI:
<group id="dtExcelSetttings" label="Settings">
<editBox id="rtdPeriod" label="Refresh period" onChange="OnPeriodChange"/>
<editBox id="rtdServer" label="FIXGateWay" onChange="OnServerChange"/>
</group>
Code to call RTD, which will cause the exception
[ComVisible(true)]
public class Ribbon : ExcelRibbon
{
public void OnServerChange(IRibbonControl control, string text)
{
object val = UserDefinedFunctions.startServer(text, 123);
}
}
Exception detail:
Exception thrown: 'System.AccessViolationException' in ExcelDna.Loader.dll
ExcelDna.Integration Error: 5 : The RTD server of type ExcelTradeGateway.FIXRTDServer required by add-in ExcelTradeGateway could not be registered.
This is an unexpected error.
Error message: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
RtdRegistration.RTD exception: System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
at ExcelDna.Loader.XlCallImpl.TryExcelImpl12(Int32 xlFunction, Object& result, Object[] parameters) in V:GitHubExcelDnaSourceExcelDna.LoaderXlCallImpl.cs:line 154
at ExcelDna.Loader.XlCallImpl.TryExcelImpl(Int32 xlFunction, Object& result, Object[] parameters) in V:GitHubExcelDnaSourceExcelDna.LoaderXlCallImpl.cs:line 80
at ExcelDna.Integration.ExcelIntegration.TryExcelImpl(Int32 xlFunction, Object& result, Object[] parameters) in V:GitHubExcelDnaSourceExcelDna.IntegrationExcelIntegration.cs:line 43
at ExcelDna.Integration.XlCall.TryExcel(Int32 xlFunction, Object& result, Object[] parameters) in V:GitHubExcelDnaSourceExcelDna.IntegrationXlCall.cs:line 1113
at ExcelDna.Integration.Rtd.RtdRegistration.TryCallRTD(Object& result, String progId, String server, String[] topics) in V:GitHubExcelDnaSourceExcelDna.IntegrationExcelRtd.cs:line 176
at ExcelDna.Integration.Rtd.RtdRegistration.TryRTD(Object& result, String progId, String server, String[] topics) in V:GitHubExcelDnaSourceExcelDna.IntegrationExcelRtd.cs:line 142
Align ExcelDnaDoc build with new ExcelDna.AddIn build
The ExcelDnaDoc package (http://mndrake.github.io/ExcelDnaDoc/index.html and https://github.com/mndrake/ExcelDnaDoc) provide automatic help file generation for Excel-DNA add-ins. It also installs some post-build events (and assumes that these run before the ExcelDna.AddIn build events).
We need to check how it is impacted by the new ExcelDna.AddIn build targets approach, and whether the package needs to be updated to align the build steps from ExcelDnaDoc with ExcelDna.AddIn.
@mndrake might be able to able to help, otherwise it might be suggested as a pull request to the ExcelDnaDoc project.
ExcelDnaPack has a problem with output file paths without directory component
from https://exceldna.codeplex.com/workitem/9366
Me:
I had to be very careful with the command-line arguments to ExcelDnaPack. My previous comment about packing failing was due to omitting leading ‘. ‘ from the paths and had nothing to do with mixed-mode assemblies (I incorrectly thought it had something to do with this as I had a reference to a mixed mode assembly in the solution but was not referencing it in the anywhere else in the code nor in the .dna)
Govert:
Are you saying that it makes a difference to ExcelDnaPack whether put the arguments as «.XXX.dna» vs. just «XXX.dna»? Do you have any idea why that would be?
Reproduce by omitting the leading .
C:workDummyCTPDummyCTPbinDebug>ExcelDnaPack.exe DummyCTP.dna /O DummyCTP.xll /Y
Output directory could not be created. Error: Path cannot be the empty string or all whitespace.
Exiting ExcelDnaPack.
The problem appears to be with this line:
string outputDirectory = Path.GetDirectoryName(xllOutputPath);
I found that specifying a leading . on the output argument is sufficient.
C:workDummyCTPDummyCTPbinDebug>ExcelDnaPack.exe DummyCTP.dna /O .DummyCTP.xll /Y
Using base add-in C:toolsExcelDna.xll
-> Updating resource: Type: ASSEMBLY_LZMA, Name: EXCELDNA.INTEGRATION, Length: 60651
-> Updating resource: Type: CONFIG, Name: __MAIN__, Length: 524
~~> ExternalLibrary path DummyCTP.dll resolved to C:workDummyCTPDummyCTPbinDebugDummyCTP.dll.
-> Updating resource: Type: ASSEMBLY_LZMA, Name: REINSCTP, Length: 4959
~~> Assembly path NLog.dll resolved to C:workDummyCTPDummyCTPbinDebugNLog.dll.
-> Updating resource: Type: ASSEMBLY_LZMA, Name: NLOG, Length: 119256
-> Updating resource: Type: DNA, Name: __MAIN__, Length: 1035
Completed Packing .DummyCTP.xll.
I will check out a couple of these suggestions for command-line processing (not my favorite topic!) and see if any of them look appropriate
http://stackoverflow.com/questions/491595/best-way-to-parse-command-line-arguments-in-c
Duplicate ambiguous method names
Hi,
I’m currently using Excel-DNA to interface my current C# project with excel. When compiling my own project, I have no restriction on the public static method names as far as C# compiler is happy, excel-dna is too. I mean that nothing prevents the user from defining multiple public static methods with the exact same names and different arguments. The method the XLL will/should call, is ambiguous. In practice, one of the methods is picked by Excel-DNA… but which one ?
I would be nice to detect such cases at compile time or at run-time and to return an exception such as «Ambiguous function call». What do you think ?
Rgds,
Yenyenx
System format exception in the LogDisplay class
LogDisplay.cs incorrectly using String.Format with params when there is no parameter passed to the method.