Известно, что с COM-объектами Аксапта работает через позднее связывание. Это означает, что при портировании в Х++, например, кода VBA из Excel, нам придется расписать отдельными операторами X++ все точки, разделяющие объекты в операторах VBA.
Допустим, у нас есть такой код VBA, созданный макрорекордером и нами слегка подправленный, который в Excel записывает значение 123 в ячейку C2 активного листа активной книги, затем делает шрифт этой ячейки жирным ("болдит") и, наконец, заливает ячейку светложелтым цветом:
Код:
Sub Macro1()
Range("C2").FormulaR1C1 = "123"
Range("C2").Font.Bold = True
Range("C2").Interior.ColorIndex = 36 'Light Yellow
End Sub
Чтобы воспроизвести его в Аксапте, нужно будет (помимо полного квалифицирования объекта Range от объекта Application) расписать все точки между объектами и методами. Получится примерно так:
X++:
{
COM application;
COM workbook = SysExcelApplication::construct().workbooks().add().comObject();
COM range;
COM font;
COM interior;
;
application = workbook.Parent();
application.Visible(true);
range = application.Range('C2');
range.FormulaR1C1(123);
font = range.Font();
font.Bold(true);
interior = range.Interior();
interior.ColorIndex(36);
}
Нам приходится выделять специальные переменные для промежуточных объектов - font и interior. Хотя наша цель лишь заболдить и закрасить ячейку, и больше эти объекты нам не понадобятся.
Вместо нескольких таких переменных, истинный объектный смысл каждой из которых нас в данном случае мало интересует, можно использовать одну фиктивную. Обычно я называю ее comTemp - для акцентирования ее "временности" и "промежуточности". С такой "болван"-переменной можно написать операции следующим образом:
X++:
COM comTemp;
..........
comTemp = range.Font();
comTemp . Bold(true);
comTemp = range.Interior();
comTemp . ColorIndex(36);
Часто в случае только двух точек можно исхитриться записать их в одной строке вообще без промежуточной переменной:
X++:
COM::createFromObject( range.Font()).Bold( true );
COM::createFromObject( range.Interior()).ColorIndex(36);
Если не обращать внимания на устрашающие начала операторов с вызова статического метода COM::createFromObject, то получается почти как на VBA. Но, к сожалению, таким двоеточием всё и ограничивается. Использовать более двух точек не получится даже вложенными друг в друга вызовами COM::createFromObject или COM::createFromVariant (последний иногда бывает нужен для выделения COM-объекта из свойства Item, например, при обращении к одной ячейке листа через семейство Cells). Попытка сконструировать такое вложение вызовет ошибку компиляции.
Таким образом, для реализации следующего "многоточечного" оператора VBA:
Код:
Application.ActiveWorkbook.Worksheets.Item(1).Cells.Item(2,3).Font.Bold = True
в X++ нам потребуются примерно такие строчки:
X++:
comTemp = application.ActiveWorkbook();
comTemp = comTemp.Worksheets();
comTemp = comTemp.Item(1);
comTemp = comTemp.Cells();
comTemp = COM::createFromVariant(comTemp.Item(2,3));
comTemp = comTemp.Font();
comTemp.Bold(true);
И чисто визуально это иногда удручает.
В целях "борьбы" с описанным явлением я наваял совсем небольшой метод, с использованием функции runbuf, который позволяет существенно сократить количество строк код и придать вызову внешнюю похожесть на оператор VBA:
X++:
{
COM application;
COM workbook = SysExcelApplication::construct().workbooks().add().comObject();
COM range;
COM comTemp;
;
application = workbook.Parent();
application.Visible(true);
range = application.Range('C2');
range.FormulaR1C1(123);
comTemp = comEarlyBindingImitation( application,
'ActiveWorkbook()','Worksheets()','Item(1)','Cells()','~Item(2,3)','Font()');
comTemp.Bold(true);
}
Текст статического метода comEarlyBindingImitation - для помещения в Global или аналогичный по смыслу класс:
X++:
static COM comEarlyBindingImitation( COM _com0,
str _com1 = '',
str _com2 = '',
str _com3 = '',
str _com4 = '',
str _com5 = '',
str _com6 = '',
str _com7 = '',
str _com8 = '')
{
str myjob;
str getSourcePart(str _com = '')
{
if (_com)
if (subStr(_com,1,1) != '~')
return strFmt('c=c.%1; ', _com);
else
return strFmt('c=COM::createFromVariant(c.%1); ',
subStr(_com,2,strLen(_com)-1));
else
return '';
}
;
myjob = 'COM comEaBIm(COM _com0) {COM c;; c = _com0; ';
myjob += getSourcePart(_com1);
myjob += getSourcePart(_com2);
myjob += getSourcePart(_com3);
myjob += getSourcePart(_com4);
myjob += getSourcePart(_com5);
myjob += getSourcePart(_com6);
myjob += getSourcePart(_com7);
myjob += getSourcePart(_com8);
myjob += 'return c; }';
return runbuf(myjob, _com0);
}
Не обошлось без минимального "встроенного языка" метода
Тильда в строке ~Item(2,3) означает, что к Item(2,3) надо применить COM::createFromVariant. Знаю об этом из личного опыта по работе с объектами Range('...').Item(i, j) и Cells().Item(i, j).
Интересно, что терминальные свойства (типа Bold или ColorIndex) можно тоже включить в это "раннее связывание" и тогда вообще обойтись одним оператором для одной операции. Следующие два оператора работают как надо:
X++:
comEarlyBindingImitation(application,
'Range("C2")','Font()','Bold(true)');
comEarlyBindingImitation(application,
'Range("C2")','Interior()','ColorIndex(36)');