Показать сообщение отдельно
Старый 16.04.2010, 11:41   #10  
Владимир Максимов is offline
Владимир Максимов
Участник
КОРУС Консалтинг
 
1,713 / 1201 (44) ++++++++
Регистрация: 13.01.2004
Записей в блоге: 3
У нас, в основном, работа с Excel происходит в терминальном режиме на бездисковых станциях. Поэтому есть своя специфика. Не знаю, можно ли замеченные проблемы применить к работе собственно на сервере. Тем не менее, перечислю с чем столкнулся и как исправил

Создание новой сессии. Подключение к терминалу. Первый запуск Excel

В этот момент буфер обмена вообще не инициализирован. Любая попытка что-то вставить через pasteSpecial(1) заканчивается крахом. Необходима принудительная инициализация буфера обмена. Ну, например, открыть блокнот, что-то там написать, выделить, Ctrl+C, Ctrl+V.

Однако тот же процесс инициализации буфера можно сделать и в процессе вставки в Excel. В результате, метод insertText() принимает такой вид

X++:
// Вставка в ячейку Excel через буфер обмена текстовой информации
// Код взят с сайта [url]http://forum.mazzy.ru/index.php?showtopic=385&hl=excel[/url]
// Можно вставлять много ячеек одной командной используя управляющие коды перехода по ячейкам
// Для перехода на соседние ячейки в теле текста следует использовать коды:
// num2char(9) - клавиша Tab - следующая ячейка в текущей строке - символ "\t"
// num2char(13) - клавиша Enter - следующая ячейка в первом столбце области _bookMark - символ "\n"
public void insertText(BookMark _bookMark, TextBuffer _text, int _workSheet = 1)
{
    COM         XLSWorkSheet,
                XLSrange;
    TextBuffer  tempBuffer,             // To store clipboard contents
                tempBufferDouble;
    int         nextI,
                maxI = 5;
    str         errorMsg;

    int                 errorNum;
    ;

    // Если содержимое TextBuffer вообще не введено, то PastSpecial даст ошибку,
    // а если заведена пустая строка, то нет смысла делать PastSpecial
    if (! _text.size())
    {
        return;
    }

    // Initializing XLSWorkSheet object
    XLSWorkSheet    = this.getWorkSheet(_workSheet);
    if (!XLSWorkSheet)
    {
        throw error("@DIS6043");
    }

    // Initializing XLSRange object
    XLSrange        = this.findRange(_bookMark,_workSheet);
    if (!XLSrange)
    {
        throw error("@SYS27391");
    }
    XLSrange.select();

    for (nextI = 1; nextI <= maxI; nextI++)
    {
        if (nextI > 1)
        {
            sleep(1000);
        }

        try
        {
            // Storing clipboard contents
            tempBuffer = new TextBuffer();
            tempBuffer.fromClipboard();

            // Инициализирую буфер обмена, если он по каким-то причинам не был инициализирован
            //      такое случаяет в момент входа в терминальном режиме, особенно на бездисковых станциях
            XLSrange.value2("Выделите данную ячейку, нажмите Ctrl+C и повторите выполнение отчета");
            XLSrange.copy();
            XLSrange.value2("");

            // Preparing text to be inserted
            // Теоретически, данная функция возвращает 0, если вставить не удалось
            // но фактически, она возвращает результат факта отправки данных в буфер обмена,
            // а не результат реального попадания этих данных в буфер. Т.е. всегда возвращает 1

            _text.toClipboard();

            // Поэтому факт реального попадания данных в буфер обмена определяем дополнительно
            // Эта проверка имеет смысл только для самоконтроля
            // Все равно будет исключение на pasteSpecial() и по try...catch уйдем на следующий шаг цикла

            // Inserting text from clipboard
            XLSWorkSheet.pasteSpecial(COMVariant::createFromInt(1)); // 1 - "Text only" mode

            // Restoring clipboard contents

            tempBuffer.toClipboard();

            // В случае успеха, прерываю цикл
            break;
        }
        catch (Exception::Error)
        {
            // Скорее всего, ошибка будет на pasteSpecial(), т.е. надо восстановить буфер
            tempBuffer.toClipboard();
            // в случае ошибки удаляю последнюю строку infolog,
            // которая генерится автоматически ошибкой COM
            if (infolog.line())
            {
                infolog.clear(infolog.line()-1);
            }
            // предпринимаю очередную попытку сделать вставку через буфер
        }
    }   // for (nextI)

    // Если выход из цикла for произошел "штатно", то значение счетчика будет больше максимально допустимого значения
    if (nextI > maxI)
    {
        if (errorMsg)
        {
            error(errorMsg);
        }
        throw error("Ошибка при копировании данных через буфер обмена.");
    }
}
Однако даже явный "пинок" буфера обмена далеко не всегда заставляет его инициализироваться. Поэтому следующий вариант - это отображение созданного экземпляра Excel перед его наполнением. Т.е. открытие Excel выглядит так

X++:
    excel = new ComExcelDocument_RU();
    excel.newFile(fileName,true);
    excel.visible(false);

( Excel )

    excel.visible(true);
Точнее, это все "прошито" внутри кода открытия файла, но суть именно в этом.

Сразу после открытия файла сделать его видимым, что позволяет инициализировать много чего из очень разных настроек MS Office, тут же сделать не видимым, заполнить данными и снова отобразить.

Разумеется, это сопровождается мельканием окна Excel. Но, пока я не нашел других способов однозначно инициализировать буфер обмена.