Показать сообщение отдельно
Старый 06.10.2008, 17:01   #24  
Gustav is offline
Gustav
Moderator
Аватар для Gustav
SAP
Лучший по профессии 2009
 
1,858 / 1152 (42) ++++++++
Регистрация: 24.01.2006
Адрес: Санкт-Петербург
Записей в блоге: 19
Визуальная имитация раннего связывания COM-объектов
Известно, что с 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)');
За это сообщение автора поблагодарили: kashperuk (3), aidsua (1).