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

 
 
Опции темы Поиск в этой теме Опции просмотра
Старый 30.03.2007, 11:21   #1  
gl00mie is offline
gl00mie
Участник
MCBMSS
Most Valuable Professional
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
3,684 / 5798 (201) ++++++++++
Регистрация: 28.11.2005
Адрес: Москва
Записей в блоге: 3
Работа с Excel через COM и ошибка 0x800A03EC (Range.AutoFilter)
Пытаюсь сделать простую, казалось бы, вещь: установить автофильтры на некоторые колонки листа Excel, в которые предварительно выводятся данные. В самом Excel вроде бы все просто:
PHP код:
Range("J5:J23").AutoFilter 
Пытаюсь сделать то же самое в X++, получаю Range через ComExcelDocument_RU.findRange() (точнее через метод-обертку, предоставляющий доступ к findRange() «извне»), вызываю AutoFilter() - и получаю ошибку:
Цитата:
Метод "autoFilter" в COM-объекте класса "Range" возвратил код ошибки 0x800A03EC (<неизвестно>), который означает: Метод AutoFilter из класса Range завершен неверно.
Почему она вылезает - ума не приложу. В отладчике все выглядит нормально, объекты инициализированы, диапазон ячеек для Range передается корректный, заголовок в первой ячейке диапазона есть. Параметры никакие не передаю, поскольку в MSDN написано, что все параметры метода Range.AutoFilter() - необязательные. Пробовал диапазон ячеек, включая заголовок, заполнять без пропусков и только латиницей. Пробовал копировать %ProgramFiles%\Microsoft Office\Office11\1049\xllex.dll в ..\1033\ и даже, по всяким шаманским советам, копировать сам excel.exe в .\1033\xllex.dll. На всякий случай пробовал также делать через Selection:
X++:
COM app;
COM sel;
COM range;
;
// ...
range.select();
app = range.application();
sel = app.selection();
sel.autoFilter();
Диапазон нормально выделяется, но при вызове sel.autoFilter() вылезает такая же фигня. Причем объект sel, если верить отладчику, - это тоже Range, так что разницы по идее никакой и не должно быть. Кто-нить с таким сталкивался?..

Ms Office Excel 2003 (11.8120.8122) SP2 Rus, Axapta 3.0 SP5 KR2
Старый 30.03.2007, 11:31   #2  
Gustav is offline
Gustav
Moderator
Аватар для Gustav
SAP
Лучший по профессии 2009
 
1,858 / 1152 (42) ++++++++
Регистрация: 24.01.2006
Адрес: Санкт-Петербург
Записей в блоге: 19
Цитата:
Сообщение от gl00mie Посмотреть сообщение
Пытаюсь сделать простую, казалось бы, вещь: установить автофильтры на некоторые колонки листа Excel, в которые предварительно выводятся данные.
А эти некоторые колонки они подряд идут? А на самом VBA есть отлаженная процедурка, которая бы работала так как надо или Вы сразу на X++ делаете?
Старый 30.03.2007, 11:50   #3  
AndyD is offline
AndyD
Участник
КОРУС Консалтинг
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
 
2,560 / 2479 (88) +++++++++
Регистрация: 20.08.2005
Попробуйте autoFilter(1);
__________________
Axapta v.3.0 sp5 kr2
За это сообщение автора поблагодарили: Ace of Database (5), gl00mie (5), pedrozzz (1).
Старый 30.03.2007, 11:54   #4  
belugin is offline
belugin
Участник
Аватар для belugin
Сотрудники Microsoft Dynamics
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии 2011
Лучший по профессии 2009
 
4,622 / 2925 (107) +++++++++
Регистрация: 16.01.2004
Записей в блоге: 5
http://support.microsoft.com/kb/320369/en-us
X++:
static void Job29(Args _args)
{
    COM App = new Com("Excel.Application");
    COM wb;
    COM wbs;
    COM sh;
    COM r;
    Com c;
    Com s;
    COMDispFunction f;
;
    App.Visible(true);
    wbs = App.WorkBooks();
    wb = Wbs.Add();
    sh = wb.ActiveSheet();
    r = sh.Range("A1");
    r.value2("1");
    r = sh.Range("A2");
    r.value2("2");
    sh.AutoFilterMode(false);
    r = sh.Range("A1:A2");
    f = new COMDispFunction(r, "AutoFilter", COMDispContext::METHOD);
    f.call();
    //r.AutoFilter();
}

Последний раз редактировалось belugin; 30.03.2007 в 11:54. Причина: xpp
Старый 30.03.2007, 11:57   #5  
Gustav is offline
Gustav
Moderator
Аватар для Gustav
SAP
Лучший по профессии 2009
 
1,858 / 1152 (42) ++++++++
Регистрация: 24.01.2006
Адрес: Санкт-Петербург
Записей в блоге: 19
Цитата:
Сообщение от AndyD Посмотреть сообщение
Попробуйте autoFilter(1);
Точно. Нашёл у себя в залежах такую же строку. Припоминаю, что что-то было, но строка не прокомментирована - поэтому точно не помню.
Старый 30.03.2007, 12:01   #6  
gl00mie is offline
gl00mie
Участник
MCBMSS
Most Valuable Professional
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
3,684 / 5798 (201) ++++++++++
Регистрация: 28.11.2005
Адрес: Москва
Записей в блоге: 3
Цитата:
Сообщение от AndyD Посмотреть сообщение
Попробуйте autoFilter(1);
Ура!!! Заработало!..
Цитата:
Сообщение от Gustav Посмотреть сообщение
Точно. Нашёл у себя в залежах такую же строку. Припоминаю, что что-то было, но строка не прокомментирована - поэтому точно не помню.
Оказывается, надо было номер поля, по которому фильтр задается, явно указать
PHP код:
Function AutoFilter_
    
<InAttribute()> Optional ByVal Field As Object_
    
<InAttribute()> Optional ByVal Criteria1 As Object_
    
<InAttribute()> Optional ByVal Operator As XlAutoFilterOperator_
    
<InAttribute()> Optional ByVal Criteria2 As Object_
    
<InAttribute()> Optional ByVal VisibleDropDown As Object _
) As Object 
Цитата:
Parameters
Field
Optional Object. The integer offset of the field on which you want to base the filter (from the left of the list; the leftmost field is field one).

Последний раз редактировалось gl00mie; 30.03.2007 в 12:08.
Старый 30.03.2007, 12:04   #7  
gl00mie is offline
gl00mie
Участник
MCBMSS
Most Valuable Professional
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
3,684 / 5798 (201) ++++++++++
Регистрация: 28.11.2005
Адрес: Москва
Записей в блоге: 3
Цитата:
Сообщение от Gustav Посмотреть сообщение
А эти некоторые колонки они подряд идут? А на самом VBA есть отлаженная процедурка, которая бы работала так как надо или Вы сразу на X++ делаете?
Автофильтр на колонки ставится отдельно на каждую. В качестве "отлаженной процедуры" я, по правде сказать, ограничился кодом в консоли отладчика Excel, который и привел в первом сообщении.
Старый 30.03.2007, 12:22   #8  
belugin is offline
belugin
Участник
Аватар для belugin
Сотрудники Microsoft Dynamics
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии 2011
Лучший по профессии 2009
 
4,622 / 2925 (107) +++++++++
Регистрация: 16.01.2004
Записей в блоге: 5
Интересно, что вот так тоже работает без указания дополнительных параметров
X++:
 f = new COMDispFunction(r, "AutoFilter", COMDispContext::METHOD);
f.call()
Старый 30.03.2007, 12:25   #9  
Gustav is offline
Gustav
Moderator
Аватар для Gustav
SAP
Лучший по профессии 2009
 
1,858 / 1152 (42) ++++++++
Регистрация: 24.01.2006
Адрес: Санкт-Петербург
Записей в блоге: 19
Цитата:
Сообщение от gl00mie Посмотреть сообщение
Автофильтр на колонки ставится отдельно на каждую.
Не очень догоняю... Речь идет о том, чтобы наложить условия на отдельные колонки после того, как режим автофильтра включен в принципе? Т.е. у всех колонок области уже есть "стрелочки" и мы некоторые из них делаем "голубыми". Так?
Старый 30.03.2007, 12:32   #10  
gl00mie is offline
gl00mie
Участник
MCBMSS
Most Valuable Professional
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
3,684 / 5798 (201) ++++++++++
Регистрация: 28.11.2005
Адрес: Москва
Записей в блоге: 3
Цитата:
Сообщение от Gustav Посмотреть сообщение
Не очень догоняю... Речь идет о том, чтобы наложить условия на отдельные колонки после того, как режим автофильтра включен в принципе?
Пардон, это я напутал Колонки идут подряд, и автофильтр накладывается на диапазон, включающий эти колонки. Т.е. мне просто нужно было, чтобы у колонок появились «стрелочки».
Старый 30.03.2007, 12:43   #11  
Gustav is offline
Gustav
Moderator
Аватар для Gustav
SAP
Лучший по профессии 2009
 
1,858 / 1152 (42) ++++++++
Регистрация: 24.01.2006
Адрес: Санкт-Петербург
Записей в блоге: 19
Цитата:
Сообщение от gl00mie Посмотреть сообщение
Пардон, это я напутал
Спасибо. Ну, слава Богу! А то я уж запереживал, что может я чего-то пропустил в жизни эксельной - вдруг уже разрешили ставить автофильтры на несвязные области (типа на 1-ю, 4-ю, 7-ю колонки, например)?!
Старый 30.03.2007, 16:28   #12  
gl00mie is offline
gl00mie
Участник
MCBMSS
Most Valuable Professional
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
3,684 / 5798 (201) ++++++++++
Регистрация: 28.11.2005
Адрес: Москва
Записей в блоге: 3
В продолжение темы взаимодействия с Excel... Захотел я вычислять диапазон ячеек для Range в формате A1 на основании адреса левой верхней ячейки и количества строк/столбцов в диапазоне. Конечно, удобнее это делать для адресов в формате R1C1, но у Excel.Application есть чудесный метод ConvertFormula(), позволяющий в т.ч. преобразовывать адреса между форматами A1 и R1C1.
PHP код:
Function ConvertFormula_
    
<InAttribute()> ByVal Formula As Object_
    
<InAttribute()> ByVal FromReferenceStyle As XlReferenceStyle_
    
<InAttribute()> Optional ByVal ToReferenceStyle As Object_
    
<InAttribute()> Optional ByVal ToAbsolute As Object_
    
<InAttribute()> Optional ByVal RelativeTo As Object _
) As Object 
Цитата:
Parameters

Formula
Required Object. A String that contains the formula you want to convert. This must be a valid formula, and it must begin with an equal sign.
FromReferenceStyle
Required XlReferenceStyle. The reference style of the formula.
XlReferenceStyle can be one of these XlReferenceStyle constants:
xlA1
xlR1C1


ToReferenceStyle
Optional XlReferenceStyle. The reference style you want returned. If this argument is omitted, the reference style isn't changed; the formula stays in the style specified by FromReferenceStyle.
Если заглянуть в Excel 11.0 Object Library (или просто нажать print xlR1C1 в отладчике Excel), то можно увидеть, что значение этой константы почему-то равно -4150 (0xffffefca). Непонятно, конечно, ну да ладно. Пишу такой вот код
X++:
#define.xlRefStyleA1    (1)
#define.xlRefStyleR1C1  (-4150)    //(0xffffefca)

// _rows, _cols - dimensions of the range area
// _bookmark    - the upper-left corner of the range
protected Bookmark getXlRangeAddress(Bookmark _bookmark, int _rows, int _cols)
{
    COM range;
    COM app;
    str strAddr;
    str strRet;
    ;
    if(!excel)              throw error("@SYS54192");
    if(_rows<1 || _cols<1)  throw error(error::wrongUseOfFunction(funcname()));
    range   = excel.my_findRange(_bookmark);
    strRet  = range.address();
    if(_rows>1 || _cols>1)
    {
        app     = range.application();
        // convert _bookmark's upper-left cell address to R1C1 format
        strAddr = app.convertFormula(strRet,  #xlRefStyleA1, #xlRefStyleR1C1);
        if(!strAddr) throw error(@"Ошибка при вызове Application.ConvertFormula!");
        strAddr = strAddr + @":R" + int2str(range.row()+_rows-1) + @"C" + int2str(range.column()+_cols-1);
        strRet  = app.convertFormula(strAddr, #xlRefStyleR1C1, #xlRefStyleA1);
    }
    return strRet;
}
Здесь excel.my_findRange() - обертка для ComExcelDocument_RU.findRange(). Но app.convertFormula() всегда возвращает пустую строку! Если просто вызвать
X++:
strAddr = app.convertFormula(strRet,  #xlRefStyleA1);
то возвращает strRet, как и положено, не преобразуя из формата A1. При всем при этом тестовый VBScript замечательно отрабатывает
PHP код:
const xlRefStyleA1      1
const xlRefStyleR1C1    = -4150
const sBmk  "$J$4"
const cRows 24
const cCols 2
set oApp    
CreateObject("Excel.Application")
strAddr     oApp.ConvertFormula(sBmkxlRefStyleA1xlRefStyleR1C1)
wscript.echo sBmk    " == " strAddr
strAddr     
strAddr ":R" & (4+cRows-1) & "C" & (10+cCols-1)
wscript.echo strAddr " == " oApp.ConvertFormula(strAddrxlRefStyleR1C1xlRefStyleA1
Код:
$J$4 == R4C10
R4C10:R27C11 == $J$4:$K$27
Пробовал дополнительно указывать значение xlAbsolute (1) параметра ToAbsolute - эффект нулевой. Я что, неправильно задаю xlRefStyleR1C1? Может, отрицательное значение надо как-то по-другому передавать?..

Последний раз редактировалось gl00mie; 30.03.2007 в 16:32.
Старый 30.03.2007, 16:50   #13  
gl00mie is offline
gl00mie
Участник
MCBMSS
Most Valuable Professional
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
3,684 / 5798 (201) ++++++++++
Регистрация: 28.11.2005
Адрес: Москва
Записей в блоге: 3
Нашелся вроде бы обходной вариант с использованием методов colName2Num() и num2NameCell() класса ComExcelDocument_RU, но все же интересно было бы разобраться с этой Application.ConvertFormula()
Старый 30.03.2007, 17:07   #14  
Gustav is offline
Gustav
Moderator
Аватар для Gustav
SAP
Лучший по профессии 2009
 
1,858 / 1152 (42) ++++++++
Регистрация: 24.01.2006
Адрес: Санкт-Петербург
Записей в блоге: 19
А что сделать в задачке надо физически? Реанимировать формулы в нотации R1C1 после вывода из Аксапты в Excel? Если да, то я использую вот такую мулю:
X++:
rng.FormulaR1C1( rng.Value2() );  
// кстати, повторным использованием можно превратить формулы в значения
Старый 30.03.2007, 17:19   #15  
AndyD is offline
AndyD
Участник
КОРУС Консалтинг
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
 
2,560 / 2479 (88) +++++++++
Регистрация: 20.08.2005
А тут, как раз таки, надо воспользоваться COMDispFunction
X++:
    Com sheet;
    Com Range;
    COMDispFunction f;
    str s;

    COMVariant varArg1 = new COMVariant();
    COMVariant varArg2 = new COMVariant();
    COMVariant varArg3 = new COMVariant();
    COMVariant varArg4 = new COMVariant();
    COMVariant varArg5 = COMVariant::createNoValue();
    COMVariant varRet  = new COMVariant(COMVariantInOut::OUT_RETVAL, ComVariantType::VT_ERROR);
;
    Range = sheet.range("D10:AK258");
    s = Range.Address(true, true, -4150);
    info(s);

    f  = new COMDispFunction(app, "ConvertFormula", ComDispContext::Method);
    varArg1.bStr(s);
    varArg2.int(-4150);
    varArg3.int(1);
    varArg4.int(1);
    f.call(varArg1, varArg2, varArg3, varArg4, varArg5, varRet);
    info(varRet.bStr());
__________________
Axapta v.3.0 sp5 kr2
Старый 30.03.2007, 18:37   #16  
Gustav is offline
Gustav
Moderator
Аватар для Gustav
SAP
Лучший по профессии 2009
 
1,858 / 1152 (42) ++++++++
Регистрация: 24.01.2006
Адрес: Санкт-Петербург
Записей в блоге: 19
Хм... а у меня как-то просто через COMVariant всё получилось..
X++:
static void Job_TestExcelConvertFormula(Args _args)
{
    COM         xlApp; // Excel.Application
    COMVariant  cv;
    ;

    xlApp = new COM('Excel.Application');

    cv = xlApp.ConvertFormula('$A$1:$B$20', 1, -4150);
    info(cv.bStr()); // R1C1:R20C2

    cv = xlApp.ConvertFormula('R1C1:R20C2', -4150, 1);
    info(cv.bStr()); // $A$1:$B$20
}
За это сообщение автора поблагодарили: gl00mie (5).
 

Похожие темы
Тема Автор Раздел Ответов Посл. сообщение
Построчный импорт из Excel через COM olesh DAX: Программирование 20 23.10.2008 14:01
Ошибка COM-объекта longson DAX: Программирование 15 21.04.2008 13:03
Ошибка com 0x800A9C68 NetBus DAX: Программирование 2 12.09.2007 17:52
Работа с Excel через COM в DAX 4.0 Paul_ST DAX: Программирование 4 17.07.2007 16:45
DLL (BarCode) через COM Antant DAX: Программирование 4 04.02.2007 16:02
Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск
Опции просмотра

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

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

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