|  | 
|  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 во время импорта   | 
|  | 
|  01.02.2007, 10:18 | #5 | 
| Участник | 
			
			Вот так и выявляются на форуме люди, работающие вместе   Цитата: 
		
			ошибка с отсутствием метода value сразу приводит к вываливанию стека в infolog, в catch она не ловится, в результате процесс excel.exe остается в памяти. Аналогично себя ведет и стандартный импорт из ексельных файлов в Аксапту (который Администрирование / Пер. опер. / Экспорт/Импорт). | 
|  | 
|  01.02.2007, 10:31 | #6 | 
| Участник | |
|  | 
|  31.01.2007, 23:14 | #7 | 
| Участник | 
			
			По поводу ошибки с макросами - перед открытием файла сделайте вызов excelApplication .AutomationSecurity(1) (1 - открывать с включенными макросами без запроса; 2 - открывать в зависимости от параметров безопасности; 3 - открывать с отключенными макросами без запроса) 2 olesh Запрос на открытие файла в режиме реадонли регулируется параметром Notify метода Open() коллекции Workbooks. Если его значение TRUE, то файл открывается без запроса 
				__________________ Axapta v.3.0 sp5 kr2 | 
|  | 
|  01.02.2007, 08:26 | #8 | 
| Участник | 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:37 | #9 | 
| Участник | 
			
			А мне больше всего нравится импорт через tsv
		 | 
|  | 
|  01.02.2007, 10:39 | #10 | 
| Участник | 
			
			А кто это?.. В смысле csv?
		 Последний раз редактировалось gl00mie; 01.02.2007 в 10:42. | 
|  | 
|  01.02.2007, 11:02 | #11 | 
| Участник | 
			
			Tab separated value
		 | 
|  | 
|  01.02.2007, 17:12 | #12 | 
| Участник | 
			
			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 | #13 | 
| Участник | Цитата: Править реестр у сотни юзеров, конечно, не вариант. | 
|  | 
|  02.02.2007, 11:02 | #14 | 
| Участник | Цитата: 
		
			Сообщение от AndyD
			   Проблема в том, что новый файл открывается в том же процессе, где идет обработка. В принципе, можно настроить таким образом, что бы экселевские файлы открывались в отдельных процессах. Минусы - если пользователь откроет файл и затем снова это сделает (из проводника), то переключения на уже открытый не произойдет и появится окошко с сообщением об открытии только для чтения и т.д. Последний раз редактировалось gl00mie; 02.02.2007 в 11:04. | 
|  | 
|  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:32 | #16 | 
| Участник | 
			
			Еще один способ - отключение реагирования на 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 | #17 | 
| Участник | 
			
			Всем большое спасибо! 2 AndyD Отдельное спасибо! Последний способ помог! | 
|  | 
|  10.06.2008, 10:34 | #18 | 
| Участник | Цитата: 
		
			Сообщение от 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(); }  Может кто в курсе ка полечить?   | 
|  |