|
15.04.2010, 10:37 | #1 |
Участник
|
Экспорт в Excel: ComExcelDocument_RU - нужно сделать серверным
Дабы не ставить Excel на все машины, массово используется OS Office, идея формировать отчеты на сервере, сохранять файл на сетевые шары, и после того как отчет сформирован и сохранен, выдавать пользователю временную ссылку (в диалоге) на файл, одним щелчком открывается ссылка, пользователь забирает файл, после закрытия диалога, временная шара удаляется.
ComExcelDocument_RU - нужно сделать серверным, но метод WorkSheet.pasteSpecial отказывается работать, причем с любыми параметрами: XLSWorkSheet.pasteSpecial(1); // xlAll = 1 XLSWorkSheet.pasteSpecial(3); // xlValues = 3 XLSWorkSheet.PasteSpecial(COMVariant::createFromStr("Текст")); XLSWorkSheet.pasteSpecial(-4163); ... неизвестная ошибка ... метод pasteSpecial() завершен не верно ниче не помогает. Кто поможет? да... , чуть не забыл, если работать через, insertvalue то все норм., я жеработаю с буфером, вставляю данны блоками. Последний раз редактировалось kair84; 15.04.2010 в 12:15. |
|
15.04.2010, 12:37 | #2 |
Moderator
|
Вот это читали? Параметры pastespecial()
И может покажете фрагмент кода пошире, чем просто один оператор? Вы буфер как заполняете перед тем, как pasteSpecial применять? |
|
15.04.2010, 12:51 | #3 |
Участник
|
да смотрел, вот код:
X++: //Import from Mazzy forum public void InsertText(BookMark _bookMark, TextBuffer _text, int _workSheet = 1) { COM XLSWorkSheet, XLSrange; TextBuffer tempBuffer; // To store clipboard contents ; if (! m_comDocument) { throw error(StrFmt("@DIS6401", this.getApplicationName())); } else { // 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(); // Storing clipboard contents tempBuffer = new TextBuffer(); tempBuffer.fromClipboard(); // Preparing text to be inserted _text.toClipboard(); // Inserting text from clipboard XLSWorkSheet.pasteSpecial(1); // 1 - "Text only" mode // XLSWorkSheet.pasteSpecial(3); // XLSWorkSheet.PasteSpecial(COMVariant::createFromStr("Текст")); // XLSWorkSheet.pasteSpecial(-4163); // Restoring clipboard contents tempBuffer.toClipboard(); }// if return; } |
|
15.04.2010, 13:19 | #4 |
Moderator
|
Попробуйте сделать по аналогии с этим моим сообщением: Параметры pastespecial()
А именно: 1. Закомментируйте у себя строку "XLSrange.select();" - она ни к чему в моем случае 2. Вместо "XLSWorkSheet.pasteSpecial(1);" напишите "XLSrange.pasteSpecial(1);" Плюс обратите внимание вот на это: Ускорение экспорта в Excel - и там про терминал. Вы уверены, что буфер, передающийся в процедуру - тот, который нужен? Не происходит ли подмена на локальный или наоборот? Последний раз редактировалось Gustav; 15.04.2010 в 13:30. |
|
15.04.2010, 14:20 | #5 |
Участник
|
|
|
|
За это сообщение автора поблагодарили: Gustav (2). |
15.04.2010, 15:09 | #6 |
Участник
|
To Gustav: смотрел конечно, у меня тоже все работало замечательно, пока не возникла необходимость формировать файл на сервере
XLSrange.pasteSpecial(1); то же не прокатило, ошибка в СOM объекте. To Максимов: это и есть этот метод, и он круто работает, но только на клиенте. я и класс отчета серверным делал, типа сам буфер уже бутет формироваться на сервере, нифига, есть идея передавать через файл. думаю может то что AOS работает как сервис, и у него проблема с буфером, потому как в 2-х уровневом тоже все работает. |
|
15.04.2010, 16:10 | #7 |
Moderator
|
Цитата:
Проверьте свой TextBuffer внутри своего метода перед pasteSpecial. |
|
15.04.2010, 16:47 | #8 |
Участник
|
Излогаешь вполне понятно, я прочитал тот пост, у меня др. случай, сожержание буфера корректное.
Кроме AOSа, серьезного там ничего нет, буфер по идее не используется. Последний раз редактировалось kair84; 15.04.2010 в 16:53. |
|
16.04.2010, 09:11 | #9 |
Участник
|
В свете новых веяний - не кошерно использовать Excel для серверной автоматизации.
Рекомендуется формировать сразу xlsx файлы посредством MS OpenXML SDK (_http://openxmldeveloper.org), или сторонних продуктов. Скорость формирования выше в разы, по сравнению с COM автоматизацией. |
|
|
За это сообщение автора поблагодарили: S.Kuskov (2). |
16.04.2010, 11:41 | #10 |
Участник
|
У нас, в основном, работа с 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("Ошибка при копировании данных через буфер обмена."); } } X++: excel = new ComExcelDocument_RU(); excel.newFile(fileName,true); excel.visible(false); ( Excel ) excel.visible(true); Сразу после открытия файла сделать его видимым, что позволяет инициализировать много чего из очень разных настроек MS Office, тут же сделать не видимым, заполнить данными и снова отобразить. Разумеется, это сопровождается мельканием окна Excel. Но, пока я не нашел других способов однозначно инициализировать буфер обмена. |
|
16.04.2010, 14:19 | #11 |
Участник
|
Серьезно - если есть время уйти от этих танцев с бубнами, посмотрите на вариант переделки класса ComExcelDocument_RU для работы через OpenXML. Если у пользователей не у всех еще 2007 - можно ставить дополнение для открытия файлов xlsx в 2003 office или вообще бесплатный Excel Viewer от MS.
|
|
30.04.2010, 13:04 | #12 |
Участник
|
С XLSrange.value2 отрабатывает, в ошибку не валится, но вставляет пустой буфер, видимо класс TxtBuffer от имени процесса AOSа не может передать данные в ClipBoard операционки, с видимым не видимым excel, тоже не получается, в обоих случаях эксель остается не видимым. есть вариант запихнуть данные в буфер через WinAPI, но как это сделать я пока не знаю
Кто работал с буфером через WinAPI ? как в него передать форматированную строку? если можно примеры... |
|
30.04.2010, 15:42 | #13 |
Участник
|
Нашел, как через WinAPI, но не помогло, может буфер только в приложениями работает, а с сервисными процессами нет?
|
|
13.05.2010, 16:52 | #14 |
Участник
|
Нашел способ: из буфера в файл, из файла макросом в эксель, но не построчная. Макрос выполняет импорт данных из указанного файла (в котором текст с табуляторами) в указанную ячейку.
|
|
13.05.2010, 17:04 | #15 |
Moderator
|
Цитата:
P.S. А вообще, если бы вы перешли на использование вывода в Excel при помощи ADODB.Recordset, то у этого объекта есть два метода Save и Open и они бы замечательно вам подошли. После заполнения рекодсета в оперативной памяти можно сохранить его в файле при помощи recordset.Save(имяФайла). Далее файл передаете куда надо. И наконец на другом компе открываете recordset.Open(имяТогоЖеФайла). Файл можно даже не смотреть - он записывается в специальном, не читаемом человеком, формате, но можно принудительно указать и XML (если сильно хочется). Последний раз редактировалось Gustav; 13.05.2010 в 17:35. |
|