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

 
 
Опции темы Поиск в этой теме Опции просмотра
Старый 16.04.2009, 18:15   #1  
ivas is offline
ivas
Участник
Аватар для ivas
 
252 / 68 (3) ++++
Регистрация: 22.12.2005
Цитата:
Предлагаю Antoncheg'у и всем желающим поучаствовать в небольшом эксперименте, запустив следующий маленький джобик. Разыскиваются люди, у которых в боксе будет выдано значение 2.
выдало 2
русский офис 2007
__________________
aLL woRk aNd nO JoY MAKes jAck a dULL Boy
Старый 16.04.2009, 19:23   #2  
Gustav is offline
Gustav
Moderator
Аватар для Gustav
SAP
Лучший по профессии 2009
 
1,858 / 1152 (42) ++++++++
Регистрация: 24.01.2006
Адрес: Санкт-Петербург
Записей в блоге: 19
ivas, спасибо! Значит, всё-таки, это нам не снится, а происходит наяву... угу...

Что ж, можно попробовать поразбираться дальше, померяться региональными и иными настройками. Пока же предлагаю (надеюсь, как временное решение) написанный по горячим следам статический метод для определения языка формул:
X++:
// Определение языка формул Excel для свойства Range.Formula
// (именно Formula, а не FormulaLocal !!!)
// подробности см. здесь: 
// [noparse]http://www.axforum.info/forums/showthread.php?p=195646#post195646[/noparse]

// СПОСОБ:
// помещаем в "международное" свойство формулу на русском языке
// и проверяем последующую реакцию Excel
// по окончании проверки удаляем временные объекты
static str excelFormulaLanguage(COM _excelApplication = null)
{
    str         ret;
    COM         workbook;
    COM         range;
    COMVariant  rangeText;
    boolean     separateSession = false;
    ;

    // если сессия не передается параметром...
    if (! _excelApplication)
    {
        // ...то пробуем прицепиться к существующей
        _excelApplication = COM::getObject('Excel.Application');

        // если существующей тоже нет...
        if (! _excelApplication)
        {
            // то создаем отдельную - кратковременную - только лишь для определения языка
            _excelApplication = new COM('Excel.Application');
            separateSession = true;
        }
    }

    workbook = _excelApplication.Workbooks();
    workbook =  workbook.Add();
    range    = _excelApplication.Range('A1');

    // помещаем в "международное" свойство формулу на русском языке
    // и проверяем последующую реакцию Excel
    range.NumberFormat('0'); // на всякий случай
    range.Formula('=СУММ(1+1)');
    range.Calculate(); // на всякий случай
    rangeText = range.Text();

    if (rangeText.bStr()=='2')  // здесь ожидаются значения: #NAME? или #ИМЯ? или 2
        ret = 'Russian';
    else
        ret = 'English';

    workbook.Close(false); // закрываем без сохранения нашу временную книгу

    if (separateSession)
        _excelApplication.Quit();

    return ret;
}
Старый 17.04.2009, 07:22   #3  
ViV is offline
ViV
Axapta Retail User
Самостоятельные клиенты AX
Axapta Retail User
 
200 / 79 (3) ++++
Регистрация: 14.09.2005
Цитата:
Сообщение от Gustav Посмотреть сообщение
Предлагаю Antoncheg'у и всем желающим поучаствовать в небольшом эксперименте, запустив следующий маленький джобик. Разыскиваются люди, у которых в боксе будет выдано значение 2.
Аналогично как и у ivas - выдало 2 на русском офисе 2007.
Старый 24.06.2009, 16:34   #4  
Antoncheg is offline
Antoncheg
Участник
 
41 / 11 (1) +
Регистрация: 29.02.2008
Давно не заглядывал в этот топик, вот результат #NAME?
Офис 2003 сп3 английский

Последний раз редактировалось Antoncheg; 24.06.2009 в 17:09.
Старый 24.06.2009, 17:32   #5  
Gustav is offline
Gustav
Moderator
Аватар для Gustav
SAP
Лучший по профессии 2009
 
1,858 / 1152 (42) ++++++++
Регистрация: 24.01.2006
Адрес: Санкт-Петербург
Записей в блоге: 19
Цитата:
Сообщение от Antoncheg Посмотреть сообщение
Давно не заглядывал в этот топик, вот результат #NAME?
Офис 2003 сп3 английский
соответственно, для этого вашего случая описанный выше метод excelFormulaLanguage вернет "English" и для range.Formula() надо будет использовать идентификатор функции "CONCATENATE" (а не "СЦЕПИТЬ").

Последний раз редактировалось Gustav; 25.06.2009 в 00:36.
Старый 30.06.2009, 13:14   #6  
Antoncheg is offline
Antoncheg
Участник
 
41 / 11 (1) +
Регистрация: 29.02.2008
Цитата:
Сообщение от Gustav Посмотреть сообщение
соответственно, для этого вашего случая описанный выше метод excelFormulaLanguage вернет "English" и для range.Formula() надо будет использовать идентификатор функции "CONCATENATE" (а не "СЦЕПИТЬ").
Да, но если на разных машинах установлены разные версии офиса, становится проблематично...
Старый 01.07.2009, 16:24   #7  
tolstjak is offline
tolstjak
Участник
 
440 / 16 (1) ++
Регистрация: 05.01.2003
Недавно стал разбираться с возможностью использования данного функционала при формировании отчетов в Эксел. Столкнулся с проблемой, которая для остальных возможно не является таковой.
Ее суть:

static void aaa_TestADO_2(Args _args)
{
// ----------------------------------------------------------------------------
// ADODB.Recordset в оперативной памяти без привязки к источнику данных
// ----------------------------------------------------------------------------
// EmplTable emplTable;
InventTrans inventTrans;
InventTable inventTable;
PurchLine purchLine;
InventDim inventDim;

Voucher voucher;
AccountNum account;
TransDate date1;
// TransDate toDate;
// TransDate nDate, date1;
ItemID itemID;
str itemName;
InventQTY qty;
InventTransRefID zakaz;
ItemGroupID itemGroupID;
SysDim podraz;
InventLocationID locationID;


COM rstAxa; // ADO: Recordset
COM flds, fld; // ADO: Fields, Field
COM xlApp; // Excel.Application
COM wbks, wbk; // Workbooks, Workbook
COM wkss, wks; // Worksheets, Worksheet
COM rng, cell, rngCR; // все Range
COM font; // Range.Font
COM entCol; // Range.EntireColumn
COM actWin; // Excel.Application.ActiveWindow
int i, iMax;



// ============================================================================
// СНАЧАЛА ВЛОЖЕННАЯ ФУНКЦИЯ
// ----------------------------------------------------------------------------
// функция для задания типа поля нашего Recordset-а в оперативной памяти
// в данном демо-джобе используется для наглядности
// для простоты используем всего 3 типа данных: число, строка и дата
int adoTypeToExcel(str _type)
{
switch (_type)
{
// используются значения констант перечисления DateTypeEnum из топика TypeProperty (ADO)
// (см. справку по ADO в файле ADO210.CHM - можно поискать на своем компе)
case 'num' : return 5; // adDouble
case 'str' : return 8; // adBSTR
case 'date': return 133; // adDBDate
case 'memo': return 203; // adLongVarChar


}
return 8;
}
// ============================================================================
// ТЕПЕРЬ ОСНОВНОЙ ПРОЦЕСС
// Recordset создается в оперативной памяти - без Connection!
rstAxa = new COM('ADODB.Recordset');
// формируем структуру нашего Recordset-а в "мозгах"
flds = rstAxa.Fields();
flds.Append('Zakaz' , adoTypeToExcel('str' ));
flds.Append('Voucher' , adoTypeToExcel('str' ));
flds.Append('Цех' , adoTypeToExcel('str' ));
flds.Append('НомГруппа', adoTypeToExcel('str' ));
flds.Append('Номенклатура' , adoTypeToExcel('str' ));
flds.Append('Наименование' , adoTypeToExcel('str' ));
flds.Append('Склад' , adoTypeToExcel('str' ));


// flds.Append('BirthDate' , adoTypeToExcel('date'));

flds.Append('Кол-во' , adoTypeToExcel('num' ));
flds.Append('Сумма' , adoTypeToExcel('num' ));

// и наконец открываем его
rstAxa.Open();
// ----------------------------------------------------------------------------
while select * from inventTrans
// where ((inventTrans.DateFinancial >= fromDate && inventTrans.DateFinancial <= toDate)
where ((inventTrans.DateFinancial >= 01\01\2009 && inventTrans.DateFinancial <= 31\05\2009)

&& inventTrans.TransType == 3)
// && inventTrans.StatusReceipt == 1)
join inventTable
where (inventTrans.ItemId == inventTable.ItemId && inventTable.dataAreaId == "ssm")

join purchLine
where (inventTrans.ItemId == purchLine.ItemId && inventTrans.InventTransId == purchLine.InventTransId)
join InventDim
where (inventTrans.inventDimId == inventDim.inventDimId)
{
itemID = inventTrans.ItemId;
itemName = inventTrans.itemName();
zakaz = inventTrans.TransRefId;
qty = inventTrans.Qty;
// amount0 = inventtrans.CostAmountPhysical;
amount0 = inventTrans.CostAmountPosted;
itemGroupID = inventTable.ItemGroupId;
podraz = purchLine.Dimension[2];
voucher = inventTrans.Voucher;
locationID = inventDim.InventLocationId;





// бежим по таблице "......." Axapta
// и добавляем записи (выбранные поля) в Recordset в оперативной памяти
rstAxa.AddNew();
// fld = flds.Item('Zakaz' ); fld.Value(inventTrans.TransRefId );
// fld = flds.Item('Voucher' ); fld.Value(inventTrans.Voucher );
fld = flds.Item('Цех' ); fld.Value(purchLine.Dimension[2] );
fld = flds.Item('НомГруппа' ); fld.Value(inventTable.ItemGroupId);
fld = flds.Item('Номенклатура' ); fld.Value(inventTrans.ItemId );
fld = flds.Item('Наименование' ); fld.Value(itemName);
fld = flds.Item('Склад' ); fld.Value(inventDim.InventLocationId );

// fld = flds.Item('BirthDate' ); fld.Value(emplTable.BirthDate );
fld = flds.Item('Кол-во' ); fld.Value(inventTrans.Qty );
fld = flds.Item('Сумма' ); fld.Value(inventTrans.CostAmountPosted );


rstAxa.Update();
}
// к этому моменту в оперативной памяти сформирована НАША таблица "Список сотрудников"
// ----------------------------------------------------------------------------
// перед выгрузкой в Excel встанем на первую запись НАШЕЙ ТАБЛИЦЫ В ПАМЯТИ
// и изменим имя сотрудника, дату его рождения и ID
// НЕ БОЙТЕСЬ!!! Аксаптовская таблица EmplTable при этом НЕ ЗАТРАГИВАЕТСЯ!!! ))
// rstAxa.MoveFirst();
// fld = flds.Item('Name' ); fld.Value('Самый молодой cотрудник с очень длинным именем');
// fld = flds.Item('BirthDate'); fld.Value(1\3\2006); // 1-е марта 2006 года
// fld = flds.Item('EmplId' ); fld.Value('000333');
// потом обратите внимание, что в Excel ведущие нули у 000333 сохранятся!
// rstAxa.Update();
// ----------------------------------------------------------------------------
// готовим новую рабочую книгу Excel для приема данных из Axapta:
xlApp = new COM('Excel.Application');
xlApp.Visible(true);
wbks = xlApp.Workbooks();
wbk = wbks.Add();
wkss = wbk.Worksheets();
wks = wkss.Item(1);
wks.Name('AdoTestSheet');
rng = wks.Range('A1');
// ----------------------------------------------------------------------------
// выводим строку имен полей (1-я строка листа Excel)
flds = rstAxa.Fields();
iMax = flds.Count() - 1;
for (i = 0; i <= iMax; i += 1)
{
fld = flds.Item(i);
cell = rng.Offset(0, i);
cell.Value2(fld.Name());
}
rngCR = rng.CurrentRegion();
font = rngCR.Font();
font.Bold(true); // делаем выведенные заголовки жирным шрифтом
// ----------------------------------------------------------------------------
// выводим данные, начиная со 2-й строки листа Excel
cell = rng.Offset(1, 0);
cell.CopyFromRecordset(rstAxa);
// ----------------------------------------------------------------------------
// подгоняем ширину столбцов Excel
rngCR = rng.CurrentRegion();
entCol = rngCR.EntireColumn();
entCol.AutoFit();
// замораживаем строку заголовков Excel
cell.Select();
actWin = xlApp.ActiveWindow();
actWin.FreezePanes(true);
// ----------------------------------------------------------------------------
rstAxa.Close();
}


С комментариями все работает, а если убрать комментарии со строк

// fld = flds.Item('Zakaz' ); fld.Value(inventTrans.TransRefId );
// fld = flds.Item('Voucher' ); fld.Value(inventTrans.Voucher );

выдается ошибка: Вариантный тип, который используется методом СОМ - объекта, не поддерживается.

В чем может быть засада??

Заранее благодарен.

P.S. Ах 30 SP1
office 2003 SP3
__________________
Александр
Старый 26.06.2009, 10:48   #8  
AndyD is offline
AndyD
Участник
КОРУС Консалтинг
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
 
2,560 / 2476 (88) +++++++++
Регистрация: 20.08.2005
Проблемы со свойствами Formula и FormulaR1C1 - в способе их вызова из Ax

Все функции из Com-объектов вызываются не напрямую, а через специальный интерфейс IDispatch, в котором реализуется пара методов IDispatch::GetIdOfNames и IDispatch::Invoke. Первый метод по имени функции возвращает ее идентификатор (DispId), а второй - вызывает по этому идентификатору функцию выполнения.
В обоих этих методах в качестве входного параметра присутствует языковый идентификатор - LCID. И Excel, как многоязыковая система, использует этот идентификатор для определения используемого языка. Т.е., если вызвать Formula() с параметром языка 0x0409 (en-Us), то функция внутри будет оперировать с передаваемыми данных, считая их англоязычными, если передать 0x0419 (ru), то, соответственно, русскоязычными.
Dax при вызове подставляет константу LOCALE_USER_DEFAULT, что соответствует языку интерфейса по умолчанию, что для русского интерфейса Windows аналогично вызову Invoke() с параметром 0x0419 (ru) - отсюда и проблемы с интерпретацией буфера. Excel требует вводить имена функций на русском.
В VBA при вызове подставляется другая константа LOCALE_NEUTRAL (0x0000). Эту константу Excel интерпретирует как код языка по умолчанию и требует вводить английские (точнее, американские) имена функций.
Фактически, свойства Formula и FormulaR1C1 позволяют использовать любой язык, поддерживаемый Excel

Свойства FormulaLocal и FormulaR1C1Local игнорируют передаваемый LCID и всегда используют LOCALE_USER_DEFAULT, что и приводит к одинаковым результатам при вызове из AX всех этих свойств

Ну и в заключение, я набросал небольшую DLL, с помощью которой можно делать языконезависимый вызов свойств Formula и FormulaR1C1 из Ax
X++:
static void ExcelFormula(Args _args)
{
    COM xlApp;
    COM wbk;
    COM rng;
    COMVariant cv;
    int iDispatch;

    DLL             _DLL      = new DLL("ComCall.dll");
//Получение DispId для свойства Formula
    DllFunction     FormulaDispId = new DllFunction(_dll, "FormulaDispId");
//Получение DispId для свойства FormulaR1C1
    DllFunction     FormulaR1C1DispId = new DllFunction(_dll, "FormulaR1C1DispId");

//Установка свойства Formula для указанного объекта Range
    DllFunction     SetFormula = new DllFunction(_dll, "Set_Formula");
//Получение свойства Formula для указанного объекта Range
//первый вызов - для получения размера используемого буфера результата
    DllFunction     GetFormulaLen = new DllFunction(_dll, "Get_Formula");
//второй вызов - для получения свойства
    DllFunction     GetFormula = new DllFunction(_dll, "Get_Formula");
    int             dispId;
    int             dispIdR1C1;
    Binary          buf;
    int             len;
    ComVariant      var;
    ;
    FormulaDispId.returns(ExtTypes::DWord);
    FormulaDispId.arg(ExtTypes::DWord);

    FormulaR1C1DispId.returns(ExtTypes::DWord);
    FormulaR1C1DispId.arg(ExtTypes::DWord);

    SetFormula.returns(ExtTypes::DWord);
    SetFormula.arg(ExtTypes::DWord, ExtTypes::DWord, ExtTypes::String);

    //Для получения размера буфера
    GetFormulaLen.returns(ExtTypes::DWord);
    GetFormulaLen.arg(ExtTypes::DWord, ExtTypes::DWord, ExtTypes::Dword);

    //Для получения результата буфера
    GetFormula.returns(ExtTypes::DWord);
    GetFormula.arg(ExtTypes::DWord, ExtTypes::DWord, ExtTypes::Pointer);

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

    wbk = xlApp.Workbooks();
    wbk = wbk.Add();
    rng = xlApp.Range('A1');

    iDispatch = rng.interface();

    dispId = FormulaDispId.call(iDispatch);
    dispIdR1C1 = FormulaR1C1DispId.call(iDispatch);

    SetFormula.Call(iDispatch, dispId, '=sum(B1:D1)');

    cv = rng.Text();
    print cv.bStr();

    len = GetFormulaLen.call(iDispatch, dispId, 0);
    buf = New Binary(len+1);
    GetFormula.call(iDispatch, dispId, buf);
    print "dll " + buf.string(0);

    len = GetFormulaLen.call(iDispatch, dispIdR1C1, 0);
    buf = New Binary(len+1);
    GetFormula.call(iDispatch, dispIdR1C1, buf);
    print "dll R1C1 " + buf.string(0);

    var = rng.formula();
    print "Dax " + var.bStr();

    rng = xlApp.Range('A2');
    rng.Formula('=sum(B1:D1)');
    cv = rng.Text();

    print cv.bStr();

    wbk.Close(False); // чтобы не оставался скрытый экземпляр Excel
    xlApp.Quit();
    pause;
}
Несколько комментариев
  1. Первый параметр, передаваемый во все функции - интерфейс IDispatch требуемого объекта. Можно получить с помощью вызова com.Interface(). DLL работает только c объектом RANGE
  2. Пары функций Set_Formula() и Get_Formula() позволяет работать как со свойством Formula, так и FormulaR1C1
    Второй параметр - значение DispId требуемой функции (эти значения можно получить с помощью FormulaDispId() и FormulaR1C1DispId()), если передавать 0, то будет использоваться вызов свойства Formula.
    Если необходим массовый вызов функций, то рекомендую сначала получить DispID и сохранить его, а затем подставлять это значение в вызовы, что увеличит скорость работы (не будет постоянных поисков необходимого dispId)
  3. Третий параметр для Set_Formula() - строка, содержащая в себе необходимую формулу на английском языке. Функция возвращает 1 в случае успешного выполнения[/B]
  4. Третий параметр для Get_Formula() - ссылка на буфер, в который будет помещена строка с формулой (или другим значением, содержащимся в ячейке) на английском языке. Функция возвращает размер требуемого буфера (без учета нулевого символа в конце). Можно использовать двумя способами.
    Первый способ - сначала получить размер буфера, передав в третий параметр 0. После этого создать буфер требуемого размера и еще раз сделать вызов, передав уже его в вызов третьим параметром
    Второй способ - создать буфер большого размера и подставлять в вызов его. В этом случае возможно получение исключения, если данные все-таки будут больше
Вложения
Тип файла: zip ComCall.zip (35.3 Кб, 129 просмотров)
__________________
Axapta v.3.0 sp5 kr2
За это сообщение автора поблагодарили: Wamr (5), tolstjak (1), ZVV (2), Antoncheg (1).
Старый 01.07.2009, 19:31   #9  
online
Владимир Максимов
Участник
КОРУС Консалтинг
 
1,658 / 1162 (42) ++++++++
Регистрация: 13.01.2004
Записей в блоге: 3
Цитата:
Сообщение от tolstjak
С комментариями все работает, а если убрать комментарии со строк

// fld = flds.Item('Zakaz' ); fld.Value(inventTrans.TransRefId );
// fld = flds.Item('Voucher' ); fld.Value(inventTrans.Voucher );

выдается ошибка: Вариантный тип, который используется методом СОМ - объекта, не поддерживается.

В чем может быть засада??

Заранее благодарен.

P.S. Ах 30 SP1
office 2003 SP3
Поля с правым выравниванием.

Хотя, вообще-то, в COM-объекты крайне не желательно передавать данные "как есть" (за исключением целочисленных). Лучше "оборачивать" их в ComVariant. В данном случае примерно так:

X++:
   ComVariant _comVariant;
    ;

    // и добавляем записи (выбранные поля) в Recordset в оперативной памяти
    rstAxa.AddNew();
    fld = flds.Item('Zakaz' );
    _comVariant = new ComVariant();
    _comVariant.bStr(inventTrans.TransRefId);
    fld.Value(_comVariant);

    fld = flds.Item('Voucher' ); 
    _comVariant.bStr(inventTrans.Voucher);
    fld.Value(_comVariant);
За это сообщение автора поблагодарили: tolstjak (1).
Старый 01.07.2009, 21:27   #10  
tolstjak is offline
tolstjak
Участник
 
440 / 16 (1) ++
Регистрация: 05.01.2003
Цитата:
Сообщение от Владимир Максимов Посмотреть сообщение
Поля с правым выравниванием.

Хотя, вообще-то, в COM-объекты крайне не желательно передавать данные "как есть" (за исключением целочисленных). Лучше "оборачивать" их в ComVariant. В данном случае примерно так:

X++:
   ComVariant _comVariant;
    ;

    // и добавляем записи (выбранные поля) в Recordset в оперативной памяти
    rstAxa.AddNew();
    fld = flds.Item('Zakaz' );
    _comVariant = new ComVariant();
    _comVariant.bStr(inventTrans.TransRefId);
    fld.Value(_comVariant);

    fld = flds.Item('Voucher' ); 
    _comVariant.bStr(inventTrans.Voucher);
    fld.Value(_comVariant);
Все получилось. Большое спасибо.
__________________
Александр
Теги
ado, excel, экспорт

 

Похожие темы
Тема Автор Раздел Ответов Посл. сообщение
Исследование скорости экспорта данных из Axapta в Excel (коллективный эксперимент) Gustav DAX: База знаний и проекты 79 13.02.2014 13:18
[Excel] - Несколько версий Excel на машине клиента Андре DAX: Программирование 11 07.08.2007 13:45
Вызов Item() для коллекций Excel Владимир Максимов DAX: Программирование 15 17.08.2006 19:47
"Такая строка уже сущ." при переносе из Excel! Zelenhof DAX: Программирование 2 13.01.2003 13:41

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

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

Рейтинг@Mail.ru
Часовой пояс GMT +3, время: 20:39.
Powered by vBulletin® v3.8.5. Перевод: zCarot
Контактная информация, Реклама.