|
16.04.2009, 18:15 | #1 |
Участник
|
Цитата:
Предлагаю Antoncheg'у и всем желающим поучаствовать в небольшом эксперименте, запустив следующий маленький джобик. Разыскиваются люди, у которых в боксе будет выдано значение 2.
русский офис 2007
__________________
aLL woRk aNd nO JoY MAKes jAck a dULL Boy |
|
16.04.2009, 19:23 | #2 |
Moderator
|
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 |
Axapta Retail User
|
|
|
24.06.2009, 16:34 | #4 |
Участник
|
Давно не заглядывал в этот топик, вот результат #NAME?
Офис 2003 сп3 английский Последний раз редактировалось Antoncheg; 24.06.2009 в 17:09. |
|
24.06.2009, 17:32 | #5 |
Moderator
|
соответственно, для этого вашего случая описанный выше метод excelFormulaLanguage вернет "English" и для range.Formula() надо будет использовать идентификатор функции "CONCATENATE" (а не "СЦЕПИТЬ").
Последний раз редактировалось Gustav; 25.06.2009 в 00:36. |
|
30.06.2009, 13:14 | #6 |
Участник
|
Да, но если на разных машинах установлены разные версии офиса, становится проблематично...
|
|
01.07.2009, 16:24 | #7 |
Участник
|
Недавно стал разбираться с возможностью использования данного функционала при формировании отчетов в Эксел. Столкнулся с проблемой, которая для остальных возможно не является таковой.
Ее суть: 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 |
Участник
|
Проблемы со свойствами 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; }
__________________
Axapta v.3.0 sp5 kr2 |
|
|
За это сообщение автора поблагодарили: Wamr (5), tolstjak (1), ZVV (2), Antoncheg (1). |
01.07.2009, 19:31 | #9 |
Участник
|
Цитата:
Сообщение от 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 |
Участник
|
Цитата:
Сообщение от Владимир Максимов
Поля с правым выравниванием.
Хотя, вообще-то, в 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);
__________________
Александр |
|