У нас, в основном, работа с 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. Но, пока я не нашел других способов однозначно инициализировать буфер обмена.