AXForum  
Вернуться   AXForum > Microsoft Dynamics AX > DAX: Программирование
All
Забыли пароль?
Зарегистрироваться Правила Справка Пользователи Сообщения за день Поиск

 
 
Опции темы Поиск в этой теме Опции просмотра
Старый 05.09.2011, 12:46   #1  
gl00mie is offline
gl00mie
Участник
MCBMSS
Most Valuable Professional
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
3,684 / 5798 (201) ++++++++++
Регистрация: 28.11.2005
Адрес: Москва
Записей в блоге: 3
Lightbulb Взаимодействие с Excel через .NET (семейство классов SysExcel)
Во вложении - проект с семейством классов SysExcel, переписанным на работу с Excel через .NET (Microsoft.Office.Interop.Excel). Модификация возникла как решение проблемы взаимодействия ядра AX 2009 с офисными приложениями через COM, что подробно описано в темах Ошибка времени выполнения в ComExcelDocument_RU.findRange() и Ошибка чтения файлов XLS под Windows 7. Через .NET экспорты/импорты стали работать намного стабильнее и субъективно чуть быстрее.
Чтобы проект скомпилировался, дополнительно нужны модификации, выложенные в темах
Вспомогательные классы проверки условий и утверждений
Класс для преобразования значений между различными значимыми типами
Также для работы модификации, разумеется, нужны установленные сборки, в которых реализованы классы и enum'ы из пространства имен Microsoft.Office.Interop.Excel. Проверялось всё на Ms Office 2010 и ядре AX 2009 SP1 RU6/RU7.

Внимание! Работу с Excel вашего или чужого кода через .NET нужно тщательно тестировать, к примеру, у меня не заработало объединение ячеек, используемое в \Classes\SysDataExcelCOM\addLookup, которое на ура отрабатывает через COM. Включается работа сеймейства SysExcel через .NET в методе SysExcel::mustInteropViaNET(). Я лично не решился пока на тотальное включение взаимодействия с Excel через .NET и сделал метод, который анализирует стек вызовов и включает работу через .NET в зависимости от того, видит ли он там "разрешенные" классы или нет. За счет этого для включения/выключения работы через .NET не пришлось как-то специально править код, работающий с классами SysExcel, хотя другого рода правки потребовались: пришлось вычистить те места, где работа шла напрямую с COM-объектами, обертками для которых служит семейство SysExcel, в результате пришлось добавить несколько новых классов-оберток.
Для удобства импорта из Excel в SysExcelRange был добавлен метод valueTyped(), возвращающий значение ячейки, приведенное к требуемому базовому аксаптовскому типу. Вся логика преобразования типов реализована в отдельном классе. Также в SysExcelApplication был добавлен метод findRange(), позволяющий найти SysExcelRange из произвольного листа, - по аналогии с одноименным методом из ComExcelDocument_RU.
По непонятным для меня причинам всё семейство SysExcel объявлено как выполняющееся сугубо на клиенте. В моем случае это ограничение снято, чтобы можно было формировать файлы Excel в пакетных заданиях, выполняющихся на сервере, однако, в выложенном проекте эти изменения не отражены.
Вложения
Тип файла: rar SysExcel_via_NET.rar (17.4 Кб, 1031 просмотров)
За это сообщение автора поблагодарили: mazzy (5), eugene egorov (2), raz (15), ZVV (5), naPmu3aH (1), Logger (18), lev (10), ziva (2), AvrDen (1), Bega (10), perestoronin (1), MikeR (10), IvanS (1), Stainless (1), S.Kuskov (15), suicest (1), Kabardian (4), Dark Light (2), Melkiades (1).
Старый 05.09.2011, 13:51   #2  
Logger is offline
Logger
Участник
Лучший по профессии 2015
Лучший по профессии 2014
 
3,940 / 3229 (115) ++++++++++
Регистрация: 12.10.2004
Адрес: Москва
Записей в блоге: 2
Кстати, а кто нибудь решил проблему с некорректной работой объединения ячеек через .Net ?
(Метод Union из Excel в \Classes\SysDataExcelCOM\ )
Старый 05.09.2011, 15:47   #3  
Stainless is offline
Stainless
Участник
MCBMSS
Columbus IT
 
34 / 114 (4) +++++
Регистрация: 26.01.2007
Как раз очень интересует серверный режим работы, какие были подводные камни при переводе? (кроме изменения режима выполнения SysExcel на CalledFrom и простановке необходимых Permissions)
Старый 05.09.2011, 18:01   #4  
gl00mie is offline
gl00mie
Участник
MCBMSS
Most Valuable Professional
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
3,684 / 5798 (201) ++++++++++
Регистрация: 28.11.2005
Адрес: Москва
Записей в блоге: 3
Подводный камень только один: решить, когда создавать SysExcelApplication нужно все-таки именно на клиенте, поскольку один и тот же серверный код может работать как в пакете, так и интерактивно, и в последнем случае, очевидно, Excel должен запускаться на клиентской машине. Я это решил так:
X++:
public static client server SysExcelApplication_NET construct(ClassRunMode _contructOnTier = ClassRunMode::Client)
{
    SysExcelApplication_NET ret;
    ;
    switch (_contructOnTier)
    {
        case ClassRunMode::Called :
        case ClassRunMode::ClientOrServer :
            ret = new SysExcelApplication_NET();
            break;
        case ClassRunMode::Client :
            ret = classFactory::makeObjectOnClient( classnum(SysExcelApplication_NET) );
            break;
        case ClassRunMode::Server :
            ret = classFactory::makeObjectOnServer( classnum(SysExcelApplication_NET) );
            break;
        default :
            throw error( Error::unsupportedEnumValue( _contructOnTier ) );
    }
    return ret;
}
public static client server SysExcelApplication construct()
{
    SysExcelApplication     ret;
    COM                     excel;
    real                    excelVersion;
    ;
    if (SysExcel::mustInteropViaNET())
    {
        return SysExcelApplication_NET::construct( clientKind() == ClientType::Server ? ClassRunMode::Server : ClassRunMode::Client );
    }
    // ...
Запрос всех InteropPermission'ов для взаимодействия с CLR на сервере в модификации реализован, так что вызывающий код об этом может не заботиться.
Старый 08.10.2012, 09:41   #5  
Masel is offline
Masel
Участник
 
39 / 537 (18) +++++++
Регистрация: 19.09.2007
Цитата:
Сообщение от gl00mie Посмотреть сообщение
Подводный камень только один: решить, когда создавать SysExcelApplication нужно все-таки именно на клиенте, поскольку один и тот же серверный код может работать как в пакете, так и интерактивно, и в последнем случае, очевидно, Excel должен запускаться на клиентской машине. Я это решил так:
X++:
public static client server SysExcelApplication_NET construct(ClassRunMode _contructOnTier = ClassRunMode::Client)
{
    SysExcelApplication_NET ret;
    ;
    switch (_contructOnTier)
    {
        case ClassRunMode::Called :
        case ClassRunMode::ClientOrServer :
            ret = new SysExcelApplication_NET();
            break;
        case ClassRunMode::Client :
            ret = classFactory::makeObjectOnClient( classnum(SysExcelApplication_NET) );
            break;
        case ClassRunMode::Server :
            ret = classFactory::makeObjectOnServer( classnum(SysExcelApplication_NET) );
            break;
        default :
            throw error( Error::unsupportedEnumValue( _contructOnTier ) );
    }
    return ret;
}
public static client server SysExcelApplication construct()
{
    SysExcelApplication     ret;
    COM                     excel;
    real                    excelVersion;
    ;
    if (SysExcel::mustInteropViaNET())
    {
        return SysExcelApplication_NET::construct( clientKind() == ClientType::Server ? ClassRunMode::Server : ClassRunMode::Client );
    }
    // ...
Запрос всех InteropPermission'ов для взаимодействия с CLR на сервере в модификации реализован, так что вызывающий код об этом может не заботиться.
При создании книги (workbook) из шаблона на сервере аксапта кидает Exception::ClrError. Что я делаю не так? Кстати если создавать книгу не из шаблона, то все отрабатывает корректно.
Права на шаблон у учетки АОСа FullControl.
Старый 08.10.2012, 11:17   #6  
Masel is offline
Masel
Участник
 
39 / 537 (18) +++++++
Регистрация: 19.09.2007
Нашел решение в интернете.

Решение:
для Windows 2008 Server x64 создать папку C:\Windows\SysWOW64\config\systemprofile\Desktop
для Windows 2008 Server x86 создать папку C:\Windows\System32\config\ systemprofile\Desktop


Я даже не знаю, что сказать на этот счет, у меня просто нет слов. Microsoft ***
Как я рад, что нашел это решение на 5-й странице поиска, с ужасом думаю что мог ее пролистать
За это сообщение автора поблагодарили: Logger (3), Denicce (2), propeller (1).
Старый 08.10.2012, 20:38   #7  
gl00mie is offline
gl00mie
Участник
MCBMSS
Most Valuable Professional
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
3,684 / 5798 (201) ++++++++++
Регистрация: 28.11.2005
Адрес: Москва
Записей в блоге: 3
Можно было ограничиться поиском по форуму: Сохранение документа Excel
Старый 22.10.2012, 08:37   #8  
pasha_isaev is offline
pasha_isaev
Участник
 
12 / 12 (1) ++
Регистрация: 21.03.2006
Адрес: Барнаул
Не работает Microsoft.Office.Interop.Excel._Application.Union()
Цитата:
Внимание! Работу с Excel вашего или чужого кода через .NET нужно тщательно тестировать, к примеру, у меня не заработало объединение ячеек, используемое в \Classes\SysDataExcelCOM\addLookup, которое на ура отрабатывает через COM
Уважаемые, решил ли кто-нибудь эту проблему? Какие танцы с бубном требуются для объединения разрозненных ячеек через .NET?
Старый 06.03.2013, 11:36   #9  
Bega is offline
Bega
Участник
Аватар для Bega
 
382 / 444 (15) +++++++
Регистрация: 18.08.2005
Адрес: Москва
Почему-то при работе через .NET на клиенте, процесс Excel остается в памяти, даже когда пользователь закрывает Excel. Этот процесс исчезает только с выходом из DAX. Так что если пользователь весь день запускает отчеты, то у него может закончится память, да и некрасиво это.

Сравнивал с аналогичным вызовом через COM, там процесс сразу исчезает после закрытия Excel.

Есть идеи, как это побороть?
Вот код, который воспроизводит проблему (специально вытащил вызовы .NET из классов gl00mie для наглядности):
X++:
Microsoft.Office.Interop.Excel.ApplicationClass application_net = new Microsoft.Office.Interop.Excel.ApplicationClass();
Microsoft.Office.Interop.Excel.Workbooks        workbooks_net;
;
workbooks_net = application_net.get_Workbooks();
workbooks_net.Add("");
application_net.set_Visible(true);
Старый 06.03.2013, 11:55   #10  
Logger is offline
Logger
Участник
Лучший по профессии 2015
Лучший по профессии 2014
 
3,940 / 3229 (115) ++++++++++
Регистрация: 12.10.2004
Адрес: Москва
Записей в блоге: 2
Может попробовать принудительно запустить .Net сборку мусора ?
Обычно помогает.
Старый 06.03.2013, 11:57   #11  
Bega is offline
Bega
Участник
Аватар для Bega
 
382 / 444 (15) +++++++
Регистрация: 18.08.2005
Адрес: Москва
Цитата:
Сообщение от Logger Посмотреть сообщение
Может попробовать принудительно запустить .Net сборку мусора ?
Обычно помогает.
Это как?
Старый 06.03.2013, 12:31   #12  
Logger is offline
Logger
Участник
Лучший по профессии 2015
Лучший по профессии 2014
 
3,940 / 3229 (115) ++++++++++
Регистрация: 12.10.2004
Адрес: Москва
Записей в блоге: 2
Ошибка чтения файлов XLS под Windows 7
https://groups.google.com/forum/?fro...ng/eNozbbwCZF0
и.т.п.
За это сообщение автора поблагодарили: Bega (5).
Старый 06.03.2013, 12:36   #13  
Bega is offline
Bega
Участник
Аватар для Bega
 
382 / 444 (15) +++++++
Регистрация: 18.08.2005
Адрес: Москва
Действительно, вот такой вариант работает.
X++:
Microsoft.Office.Interop.Excel.ApplicationClass application_net = new Microsoft.Office.Interop.Excel.ApplicationClass();
    Microsoft.Office.Interop.Excel.Workbooks        workbooks_net;
    ;
    workbooks_net = application_net.get_Workbooks();
    workbooks_net.Add("");
    application_net.set_Visible(true);
    
    workbooks_net = null;
    application_net = null;
    
    System.GC::Collect();
За это сообщение автора поблагодарили: Ace of Database (2).
Старый 06.03.2013, 13:08   #14  
LeonDerCom is offline
LeonDerCom
Участник
 
45 / 20 (1) +++
Регистрация: 08.10.2012
Bega
Попробуйте
X++:
application_net.finalize
Старый 06.03.2013, 14:16   #15  
Bega is offline
Bega
Участник
Аватар для Bega
 
382 / 444 (15) +++++++
Регистрация: 18.08.2005
Адрес: Москва
Цитата:
Сообщение от LeonDerCom Посмотреть сообщение
Bega
Попробуйте
X++:
application_net.finalize
Нет такого метода.
Старый 06.03.2013, 15:21   #16  
LeonDerCom is offline
LeonDerCom
Участник
 
45 / 20 (1) +++
Регистрация: 08.10.2012
Bega
В данном случае не вчитался и подумал о ком объекте. А раз тут дотНет, то есть еще вот такой момент, в C# после Collect вызывал метод WaitForPendingFinalizers.
Старый 06.03.2013, 15:23   #17  
Bega is offline
Bega
Участник
Аватар для Bega
 
382 / 444 (15) +++++++
Регистрация: 18.08.2005
Адрес: Москва
Цитата:
Сообщение от LeonDerCom Посмотреть сообщение
Bega
В данном случае не вчитался и подумал о ком объекте. А раз тут дотНет, то есть еще вот такой момент, в C# после Collect вызывал метод WaitForPendingFinalizers.
Да, видел этот метод, но без него тоже работает, посчитал что "wait" тут не нужно.
Старый 07.03.2013, 16:30   #18  
Bega is offline
Bega
Участник
Аватар для Bega
 
382 / 444 (15) +++++++
Регистрация: 18.08.2005
Адрес: Москва
Есть какой-то способ вставки в range массива, как это было возможно с COM?
Вот пример для COM, он работает:
X++:
ComExcelDocument_RU         comExcel;
    ArrayExtend_RU              arrayStr = new ArrayExtend_RU(Types::String, 10);
    ;
    comExcel = new ComExcelDocument_RU();
    comExcel.newFile("", false);
    
    arrayStr = new ArrayExtend_RU(Types::String, 2);
    arrayStr.value(1, "AAA");
    arrayStr.value(2, "БББ");
    
    comExcel.insertValue("A1:B1", arrayStr);
    
    comExcel.visible(true);
А для .NET не работает:
X++:
SysExcelApplication_NET     sysExcelApplication;
    SysExcelWorksheet_NET       sysExcelWorksheet;
    SysExcelRange_NET           range;
    ArrayExtend_RU              arrayStr = new ArrayExtend_RU(Types::String, 10);
    ;
    sysExcelApplication = SysExcelApplication_NET::construct();
    sysExcelApplication.workbooks().add("");
    sysExcelWorksheet = sysExcelApplication.worksheets().itemFromNum(1);
    arrayStr = new ArrayExtend_RU(Types::String, 2);
    arrayStr.value(1, "AAA");
    arrayStr.value(2, "БББ");
    range = sysExcelWorksheet.range("A1:B1");

    range.value2(arrayStr);

    sysExcelApplication.visible(true);
Пытался различными способами передать массив, прямая передача или упаковка через CLRInterop::getObjectForAnyType(arrayStr ) выдает ошибку о невозможности преобразования.
Старый 07.03.2013, 16:46   #19  
Bega is offline
Bega
Участник
Аватар для Bega
 
382 / 444 (15) +++++++
Регистрация: 18.08.2005
Адрес: Москва
Нашел способ через System.Array:
X++:
SysExcelApplication_NET     sysExcelApplication;
    SysExcelWorksheet_NET       sysExcelWorksheet;
    SysExcelRange_NET           range;
    System.Array                array;
    System.Type                 type;
    ;
    sysExcelApplication = SysExcelApplication_NET::construct();
    sysExcelApplication.workbooks().add("");
    sysExcelWorksheet = sysExcelApplication.worksheets().itemFromNum(1);
    
    type = System.Type::GetType('System.String');
    array = System.Array::CreateInstance(type, 2);
    
    array.SetValue(CLRInterop::getObjectForAnyType("AAA"), 0);
    array.SetValue(CLRInterop::getObjectForAnyType("БББ"), 1);
    
    range = sysExcelWorksheet.range("A1:B1");
    range.value2(array);
    
    sysExcelApplication.visible(true);
За это сообщение автора поблагодарили: BOAL (2), Pustik (2), Logger (5), IvanS (1).
Старый 09.10.2013, 07:31   #20  
IvanS is offline
IvanS
Участник
Аватар для IvanS
 
241 / 44 (2) +++
Регистрация: 30.06.2006
Адрес: Екатеринбург
При открытии шаблона через workBooks_net у меня возникает сообщение, что файл был восстановлен. С чем это может быть связано?
Выяснил, что такое поведение связано с шаблонами 2007 офиса. С 2003 нормально работает

Последний раз редактировалось IvanS; 09.10.2013 в 08:24.
Теги
.net, ax2009, excel, законченный пример, полезное

 

Похожие темы
Тема Автор Раздел Ответов Посл. сообщение
проблема использования Excel через clr Batuev Artem DAX: Программирование 8 22.08.2011 18:01
AX.NET: интеграция .NET-приложений с Аксаптой и (будущие) возможности облачных вычислений gl00mie DAX: Программирование 2 23.04.2010 00:47
Импорт даты через Excel OliaM DAX: Функционал 2 13.12.2007 10:32
Экспорт в Excel через WorkBooks.OpenText() Владимир Максимов DAX: Программирование 2 09.04.2004 17:16
Чтение Excel-ячейки в Аксапте (2.5) через COM AKIS DAX: Программирование 3 25.03.2004 20:18

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход

Рейтинг@Mail.ru
Часовой пояс GMT +3, время: 05:08.