31.01.2007, 16:54 | #1 |
Участник
|
Проблема с импортом из Excel через COM
Здравствуйте!
Проблема такая: импортируются данные из excel-файла через COM: excelApplication = new COM(#EXCEL); ... и т.д. Импорт проходит успешно. Но, если в процессе импорта открыть ЛЮБОЙ файл excel, содержащий макросы, и появится предупреждение системы безопасности, то возникает ошибка при обращении к методам COM-объекта. Например: if (excelApplication) { excelApplication.quit(); excelApplication.finalize(); } Ошибка: COM-объект не имеет метода quit. При этом excelApplication в отладчике уже не _Application, но и не null, а EXCEL остается в памяти. Не подскажите как с этим бороться? |
|
31.01.2007, 20:45 | #3 |
Участник
|
По-моему, человек спрашивал про другой случай. У него проблема с контекстом безопасности. Код ошибки не сообщите?
Самое простое, но не самое лучшее, - смените уровень безопасности. Хотя, если вы сторонними файлами Excel не пользуетесь, то почему нет? Можно подписывать свои проекты, как советует Microsoft. На подписанные макросы ругаться при открытии не будет. |
|
31.01.2007, 20:55 | #4 |
Участник
|
Цитата:
Конечно, есть простое решение - рекомендация пользователю не трогать Excel во время импорта |
|
31.01.2007, 23:14 | #5 |
Участник
|
По поводу ошибки с макросами - перед открытием файла сделайте вызов excelApplication .AutomationSecurity(1) (1 - открывать с включенными макросами без запроса; 2 - открывать в зависимости от параметров безопасности; 3 - открывать с отключенными макросами без запроса)
2 olesh Запрос на открытие файла в режиме реадонли регулируется параметром Notify метода Open() коллекции Workbooks. Если его значение TRUE, то файл открывается без запроса
__________________
Axapta v.3.0 sp5 kr2 |
|
01.02.2007, 08:26 | #6 |
Участник
|
2 AndyD
Еще раз повторю - проблема не при открытии файла в Аксапте!
Т.е. делаем так - запускаем некий импорт в Аксапте из файла 1.xls, импорт идет, занимает какое-то время. В это время юзер решает открыть файл 2.xls в екселе обычным способом. При этом, если в нем есть макросы или файл редактируется другим пользователем, то ексель показывает соответствующий диалог. Тут же импорт в Аксапте вываливается с ошибкой "нет метода у объекта" (варианты - нет quit у excelApplication или value у range). При этом ошибка с отсутствием метода value сразу приводит к вываливанию стека в infolog, в catch она не ловится, в результате процесс excel.exe остается в памяти. Аналогично себя ведет и стандартный импорт из ексельных файлов в Аксапту (который Администрирование / Пер. опер. / Экспорт/Импорт). Последний раз редактировалось olesh; 01.02.2007 в 08:28. |
|
01.02.2007, 10:18 | #7 |
Участник
|
Вот так и выявляются на форуме люди, работающие вместе
Цитата:
ошибка с отсутствием метода value сразу приводит к вываливанию стека в infolog, в catch она не ловится, в результате процесс excel.exe остается в памяти.
Аналогично себя ведет и стандартный импорт из ексельных файлов в Аксапту (который Администрирование / Пер. опер. / Экспорт/Импорт). |
|
01.02.2007, 10:31 | #8 |
Участник
|
|
|
01.02.2007, 10:37 | #9 |
Участник
|
А мне больше всего нравится импорт через tsv
|
|
01.02.2007, 10:39 | #10 |
Участник
|
Как вариант можно попробовать пересохранять файл Excel в XML-формате (думаю, это тоже можно автоматизировать, и происходить это будет быстрее, чем импорт), а потом уже работать с этим XML без привлечения Excel'я...
|
|
01.02.2007, 10:39 | #11 |
Участник
|
А кто это?.. В смысле csv?
Последний раз редактировалось gl00mie; 01.02.2007 в 10:42. |
|
01.02.2007, 11:02 | #12 |
Участник
|
Tab separated value
|
|
01.02.2007, 17:12 | #13 |
Участник
|
2 olesh
Проблема в том, что новый файл открывается в том же процессе, где идет обработка. В результате, при появлении окна с пользовательским запросом, блокируются обращения через COM-интерфейсы. Как только окно закрывается обработка может быть продолжена. В принципе, можно настроить таким образом, что бы экселевские файлы открывались в отдельных процессах. Минусы - если пользователь откроет файл и затем снова это сделает (из проводника), то переключения на уже открытый не произойдет и появится окошко с сообщением об открытии только для чтения и т.д. В общем что надо сделать: Открыть regedit.exe. Найти ветку "HKEY_CLASSES_ROOT\Excel.Sheet.*\shell\Open" (вместо звездочки - номер версии. У меня Excel 2003 и стоит 8. В общем, найти с таким номером, что бы была подветка shell). В ветке Open будут две подветки: Command и ddeexec. ddeexec надо будет удалить. В ветке Command изменить значение строкового ключа command (у меня ?r=^Vn-}f(ZXfeAR6.jiEXCELFiles>!De@]Vz(r=f`1lfq`?R& /e) - в конце после /e приписать " %1". Если ключа command не будет - добавьте " %1" в значение по умолчанию После этого файлы должны открываться в раздельных процессах. Перед изменениями экспортируйте эту ветку, что бы восстановить значения в случае проблем.
__________________
Axapta v.3.0 sp5 kr2 |
|
|
За это сообщение автора поблагодарили: olesh (1). |
01.02.2007, 18:46 | #14 |
Участник
|
Цитата:
Править реестр у сотни юзеров, конечно, не вариант. |
|
01.02.2007, 22:56 | #15 |
Участник
|
Можно сделать так.
X++: class ExcelFuncs { } static client ComVariant GetValue(Com Range) { ComDispFunction func = new ComDispFunction(range, "Value", ComDispContext::PropertyGet); ComVariant var = new ComVariant(ComVariantInOut::Out_retVal, ComVariantType::VT_ERROR); int line; int i; str s; ; SetPrefix("CallWasRejectedByCallee"); while (true) try { line = infolog.line(); func.call(var); return var; } catch { if (infolog.line() > line) { s = infolog.text(line+1); if (strscan(s, "Value", 1, strlen(s))) { s = substr(s, strscan(s, "0x", 1, strlen(s))+2, 8); if (s == "80010001") { sleep(100); infolog.cut(line+1); continue; } } } throw Exception::Error; } throw error("Ошибка при получении значения!"); } X++: static void Sample(Args _args) { ComExcelDocument_Ru excel = new ComExcelDocument_Ru(); Com doc; Com app; Com Sheet; Com Range; ComVariant var; ; excel.open("c:\\Temp\\TestExcel.xls", true); doc = excel.getComDocument(); app = doc.Application(); sheet = app.ActiveSheet(); range = sheet.range("A1:J1"); info("aaa"); while (true) { // info("bbb"); var = ExcelFuncs::GetValue(range); } } Если закрыть окно Excel, то произойдет окончание работы job'а по exception'у. Хотя, мне это решение не нравится. Во-первых, нет возможности явно определить код возврата из вызова функции (при получении значения, не равного S_OK, ядро генерирует exception). Например, если раскомментарить строку info("bbb"), то в инфолог будет выводиться форматная строка без передачи параметров (вместо имени функции и кода ошибки будут %1, %2 и т.д.) (проверял на SP3 и SP5 без роллапов) Во-вторых, ожидание пока пользователь как-то отреагирует на появившиеся диалоги (возможность ввода данных в ячейки оставлена для примера). Напомню, что пока будут выведены эти окна, интерфейсы не будут работать (будет возвращаться ошибка 0x80010001, либо не будут находиться какие-то методы). Еще есть такой вариант - написать Comобъект-обертку над методами Excel'я с возможностью получения кода возврата из экселевских методов. Тогда пропадет неоднозначность, правда, второй пункт и в этом случае останется в силе.
__________________
Axapta v.3.0 sp5 kr2 |
|
|
За это сообщение автора поблагодарили: kvg6 (1). |
02.02.2007, 11:02 | #16 |
Участник
|
Цитата:
Сообщение от AndyD
Проблема в том, что новый файл открывается в том же процессе, где идет обработка. В принципе, можно настроить таким образом, что бы экселевские файлы открывались в отдельных процессах. Минусы - если пользователь откроет файл и затем снова это сделает (из проводника), то переключения на уже открытый не произойдет и появится окошко с сообщением об открытии только для чтения и т.д.
Последний раз редактировалось gl00mie; 02.02.2007 в 11:04. |
|
02.02.2007, 11:32 | #17 |
Участник
|
Еще один способ - отключение реагирования на DDE запросы в процессе-обработчике. Перед закрытием Excel параметр восстановить обратно (В обязательном порядке!!! Иначе открыть файл из проводника не получится).
X++: static void ExcelImportTest(Args _args) { ComExcelDocument_Ru excel = new ComExcelDocument_Ru(); com doc; com app; com sheet; com range; ComVariant var; boolean ignore; ; excel.open("c:\\Temp\\Test.xls", false); doc = excel.getComDocument(); app = doc.application(); ignore = app.IgnoreRemoteRequests(); app.IgnoreRemoteRequests(true); try { sheet = app.activesheet(); range = sheet.range("A1:J1"); while (true) var = range.value(); } catch { info("catch"); // из обработки вываливаемся по брейку } app.IgnoreRemoteRequests(ignore); app.quit(); }
__________________
Axapta v.3.0 sp5 kr2 |
|
|
За это сообщение автора поблагодарили: fomenka (1), gl00mie (3), Romsrs (1). |
02.02.2007, 12:01 | #18 |
Участник
|
Всем большое спасибо!
2 AndyD Отдельное спасибо! Последний способ помог! |
|
10.06.2008, 10:34 | #19 |
Участник
|
Цитата:
Сообщение от AndyD
Еще один способ - отключение реагирования на DDE запросы в процессе-обработчике. Перед закрытием Excel параметр восстановить обратно (В обязательном порядке!!! Иначе открыть файл из проводника не получится).
X++: static void ExcelImportTest(Args _args) { ComExcelDocument_Ru excel = new ComExcelDocument_Ru(); com doc; com app; com sheet; com range; ComVariant var; boolean ignore; ; excel.open("c:\\Temp\\Test.xls", false); doc = excel.getComDocument(); app = doc.application(); ignore = app.IgnoreRemoteRequests(); app.IgnoreRemoteRequests(true); try { sheet = app.activesheet(); range = sheet.range("A1:J1"); while (true) var = range.value(); } catch { info("catch"); // из обработки вываливаемся по брейку } app.IgnoreRemoteRequests(ignore); app.quit(); } |
|