Что нового

Ошибка при повторном/новом открытии/присоединении файла Excel, возможно из-за неправильного закрытия файла Excel

DyadyaGenya

Знающий
Сообщения
300
Репутация
10
Доброго времени суток. Есть некоторый скрипт, который в конце обрабатывает файл excel. Вроде все работает хорошо, но если не выходить из скрипта и запустить заново обработку какого-нибудь файла excel, то файл открывается и выдает ошибку (см. скрин) и больше ничего не делает. Если перезапустить скрипт полностью, то все опять работает нормально.
Аналогично вылетает ошибка при сохранении файла без переименования.
Подозреваю, что это связано с закрытием файла excel. Делаю так:
Код:
Func _Exel()
Local $oExcel = _Excel_Open()
Local $sWorkbook = $g_sPath & $Otchet
Local $oWorkbook = _Excel_BookAttach($sWorkbook)
If @error Then Exit MsgBox($MB_SYSTEMMODAL, "Excel UDF: _Excel_BookOpen Example 1", "Error opening '" & $sWorkbook & "'." & @CRLF & "@error = " & @error & ", @extended = " & @extended & '  ошибка')
...........
; Сохранение файла с новым именем
$aOtchet = StringSplit($Otchet, '.')
$Otchet1 = @ScriptDir & "\" & $Otchet
Local $sWorkbook = @ScriptDir & "\" & $aOtchet[1] & '.' & $aOtchet[2] & '.' & $aOtchet[3] & '.' & $aOtchet[4] &"  1.xlsx"
ConsoleWrite($sWorkbook & @CR)
_Excel_BookSaveAs($oWorkbook, $sWorkbook, $xlWorkbookDefault, True)

; Ниже варианты закрытия файла, но ошибка вылетает и в том случае, если файл не закрывать
;_Excel_Close($oExcel, False, True)
; и
;_Excel_BookClose($oWorkbook, False)
;_Excel_Close($oExcel, False, True)
 

Вложения

  • Скриншот 05-12-2020 22.51.23.jpg
    Скриншот 05-12-2020 22.51.23.jpg
    17.3 КБ · Просмотры: 5
Последнее редактирование:

ra4o

AutoIT Гуру
Сообщения
1,165
Репутация
246
Здесь есть ньюансы - работаете ли Вы с файлами Excel после скрипта ? Если нет, то пробуйте закрывать экземпляр приложения Excel (_Excel_Close) или в зависимости от логики выносить вне цикла _Excel_Open и не подключаться к нему повторно. Просто по данному отрезку кода невозможно дать однозначное решение.
 
Автор
D

DyadyaGenya

Знающий
Сообщения
300
Репутация
10
по данному отрезку кода невозможно дать однозначное решение.
Могу всю функцию для работы с этим файлом дать. Или нужно показать ещё что-то помимо функции работы с этим экселевским файлом?
 

Norm

Продвинутый
Сообщения
278
Репутация
74
Если перезапустить скрипт полностью, то все опять работает нормально.
У меня возможно похожая проблема.
Происходит многократная обработка в цикле. Ошибок нет,
но в конце остается висеть процесс excel.exe в фоновом режиме, пока скрипт не закрою.
Этот процесс как-то связан с последним экземпляром последнего документа. Хотя я его сохранил и закрыл перед _Excel_Close .
Решил пока только с помощью прибивания этого процесса.
или в зависимости от логики выносить вне цикла _Excel_Open и не подключаться к нему повторно.
Есть ли у Вас подобный пример?
У себя я просто проверяю существование объекта от _Excel_Open, и если он есть, то продолжаю с ним работать.
Но в конце всё равно остается висеть процесс.
 
Автор
D

DyadyaGenya

Знающий
Сообщения
300
Репутация
10
У себя я просто проверяю существование объекта от _Excel_Open, и если он есть, то продолжаю с ним работать.
О, а как это сделать. Ато уже запутался, где ещё искать можно
Сообщение автоматически объединено:

но в конце остается висеть процесс excel.exe в фоновом режиме, пока скрипт не закрою.
Кстати, у меня вроде не висит. Проверяю процесс эксплорером и диспетчером задач.
 

ra4o

AutoIT Гуру
Сообщения
1,165
Репутация
246
Процесс Excel.exe может оставаться, если в процессе работы до закрытия _Excel_Close() скрипт вылетает с ошибкой , при нормальной работе этого процесса оставаться не должно. Но если так уж хочется его закрыть (он может быть не один) , тогда как-то так:
Код:
While ProcessExists('excel.exe')
    ProcessClose('excel.exe')
WEnd

Сообщение автоматически объединено:

Есть ли у Вас подобный пример?
Пример на словах опишу, например, если Вы хотите по очереди обработать все файлы excel в каком-то каталоге, то
получаем список файлов в массив
открываем Excel (_Excel_Open())
Далее в цикле открываем каждую книгу, работаем с ней и закрываем книгу:(_Excel_BookOpen()........_Excel_BookClose())
После завершения работы со всеми книгами после цикла закрываем Excel - _Excel_Close()
 
Последнее редактирование:
  • Like
Реакции: Norm

Norm

Продвинутый
Сообщения
278
Репутация
74
О, а как это сделать. Ато уже запутался, где ещё искать можно
Кстати, у меня вроде не висит. Проверяю процесс эксплорером и диспетчером задач.
Код:
If Not IsObj($oExcel) Then
      $oExcel = _Excel_Open()
EndIf


Процесс Excel.exe может оставаться, если в процессе работы до закрытия _Excel_Close() скрипт вылетает с ошибкой , при нормальной работе этого процесса оставаться не должно.
Как я и говорил выше, ошибок нет и всё корректно обрабатывает. При закрытии с помощью _Excel_Close() @error остается на нуле.
Поэтому пришлось прибегнуть к некорректному приёму с закрытием процесса.

Далее в цикле открываем каждую книгу, работаем с ней и закрываем книгу:(_Excel_BookOpen()........_Excel_BookClose())
После завершения работы со всеми книгами после цикла закрываем Excel - _Excel_Close()
У меня примерно такой же сценарий, только книги создаются (в виде CSV) и обрабатываются в нём же.
(10 листов в одну книгу)
Начало Цикла

Создаю CSV-1
Создаю объект и открываю формуляр главной книги. (с проверкой объекта от повторного открытия (смотри выше) )
Открываю CSV-1> перекидываю содержимое на отдельный лист главной книги. Закрываю CSV-1
... ... ... ...
Завершение цикла
Сохраняю и закрываю главную книгу.
_Excel_BookClose($exMPP)
_Excel_Close($oExcel,True,True)
@error = 0

Похожий случай читал на форуме у соседей, но там при закрытии ошибка выходила, поскольку в Excel какой-то плагин стоял.
Короче не мой случай.
 
Последнее редактирование:
Автор
D

DyadyaGenya

Знающий
Сообщения
300
Репутация
10
Пример на словах опишу, например, если Вы хотите по очереди обработать все файлы excel в каком-то каталоге, то
получаем список файлов в массив
У меня чуток другая ситуация. Я слежу за каталогом с помощью
Код:
_WinAPI_ReadDirectoryChanges($hDirectory, BitOR($FILE_NOTIFY_CHANGE_FILE_NAME, $FILE_NOTIFY_CHANGE_DIR_NAME), $pBuffer, 8388608, 1)

К сожалению получить сразу имя файла не получается, приходится брать его из массива.
Появившийся новый файл обрабатываю, сохраняю с новым именем в другой каталог и закрываю. А вот уже при открытии нового у меня в массив списка для открытия файлов попадает временный файл прошлого открытого и временный нового открытого (начинаются на ~$). А "исходник" не попадает, в том числе и предыдущий, хотя по задумке там и не должно быть старых имен, только один новый. Но в массиве только временные файлы.
Пробовал чистить буфер с помощью ClipPut ("") и _ClipBoard_Empty(), пробовал обрезать знак временного файла (обрезает, но не открывает), пробовал чистить массив Global $aData[0][0] в нем все равно остаются только временные файлы.
Закрытие экселя с помощью цикла While... Wend тоже ничего не дало.
Сохраняю и закрываю главную книгу
Закрытие книги и самого экселя тоже ничего не дает:
Код:
_Excel_BookClose($oWorkbook)
_Excel_Close($oExcel,True,True)

И ещё небольшое уточнение. Файл то вроде как открывает, но ничего с ним не делает и выдает ошибку открытия уже старого временного файла.
Сообщение автоматически объединено:

Правда файл открывает потому что так зашито в другой программе, которая его создает и к которой у меня нет доступа. А вот у меня после работы с ним он видимо остается висеть в памяти эксель.
Сообщение автоматически объединено:

Ещё небольшое уточнение. Выдает ошибку -2147352570
 
Последнее редактирование:

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,671
Репутация
2,481
В примере _Excel_BookAttach, почему не _Excel_BookOpen?
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,671
Репутация
2,481
Или нужно показать ещё что-то помимо функции работы с этим экселевским файлом?
У меня есть такая давняя привычка - когда пытаюсь решить сложную проблему в уже работающем скрипте, я сначала создаю копию скрипта где воспроизвожу проблему отдельно, т.е без всего функционала, только то что нужно чтобы увидеть проблему, и тогда легче что то решать, да и проще другим показать чтобы помогли решить.

Вот мои наблюдения и видимо решение поставленной проблемы:
Код:
#include <WinAPIFiles.au3>
#include <Excel.au3>

Global $g_sPath = @DesktopDir & '\test'
DirCreate($g_sPath)

$hDirectory = _WinAPI_CreateFileEx($g_sPath, $OPEN_EXISTING, $FILE_LIST_DIRECTORY, BitOR($FILE_SHARE_READ, $FILE_SHARE_WRITE), $FILE_FLAG_BACKUP_SEMANTICS)
$pBuffer = _WinAPI_CreateBuffer(8388608)

While 1
    $aData = _WinAPI_ReadDirectoryChanges($hDirectory, BitOR($FILE_NOTIFY_CHANGE_FILE_NAME, $FILE_NOTIFY_CHANGE_DIR_NAME), $pBuffer, 8388608, 1)
   
    If Not @error Then
        ;_Excel($aData[1][0]) ; Тут видимо кроется первая ошибка, т.к при переименовании файла, в таблицу попадает пара (старое и новое имя файла)
        _Excel($aData[2][0]) ; Вот так работает
    EndIf
WEnd

Func _Excel($Otchet)
    ; Флажок для предотвращения повторного срабатывания для временного файла который создаёт эксель при сохранений
    Local Static $bFlag = False
   
    ; Если флаг ниже установлен, сбрасываем его и выходим из функции
    If $bFlag Then
        $bFlag = False
        Return
    EndIf
   
    Local $oExcel = _Excel_Open(False, False)
    Local $sWorkbook = $g_sPath & '\' & $Otchet
    Local $oWorkbook = _Excel_BookOpen($oExcel, $sWorkbook, False, False)
   
    If @error Then
        MsgBox($MB_SYSTEMMODAL, "_Excel_BookOpen", "Error opening '" & $sWorkbook & "'." & @CRLF & "@error = " & @error & ", @extended = " & @extended & '  ошибка')
        Exit
    EndIf
   
    ; Устанавливаем флаг на момент сохранения
    $bFlag = True
   
    ; Сохранение файла с новым именем
    _Excel_BookSaveAs($oWorkbook, StringTrimRight($sWorkbook, 5) & ' (1).xlsx', $xlWorkbookDefault, True)
   
    ; Закрытие экселя
    _Excel_BookClose($oWorkbook, False)
    _Excel_Close($oExcel, False, True)
EndFunc


Кстати рекомендую вместо _WinAPI_ReadDirectoryChanges использовать библиотеку RDC, там есть асинхронный режим.
 
Последнее редактирование:
Автор
D

DyadyaGenya

Знающий
Сообщения
300
Репутация
10
В примере _Excel_BookAttach, почему не _Excel_BookOpen?
Потому что разрабовская программа, которую обслуживаем сама открывает файл и приходится работать с уже открытым. Можно конечно закрыть и открыть файл заново, но решил, что так будет лучше.
когда пытаюсь решить сложную проблему в уже работающем скрипте, я сначала создаю копию скрипта где воспроизвожу проблему отдельно
Так же стараюсь делать. Так и проблему с отправкой почты решил, если это можно назвать решением )
Вот мои наблюдения и видимо решение поставленной проблемы
Уже в субботу буду смотреть
рекомендую вместо _WinAPI_ReadDirectoryChanges использовать библиотеку RDC, там есть асинхронный режим.
Почитаю обязательно. Спасибо.
Сообщение автоматически объединено:

В субботу потому что сходу не получилось. Я и сам пробовал увеличить индекс, привязывал к максимальному индексу, но все равно выбивает ошибку. Возможно не там это делаю. Нужно будет внимательней посмотреть.
 
Последнее редактирование:

ra4o

AutoIT Гуру
Сообщения
1,165
Репутация
246
Правда файл открывает потому что так зашито в другой программе, которая его создает и к которой у меня нет доступа
Возможно в этом и причина ? Другая программа держит Эксель , пробуйте закрыть Эксель и уже открывать скриптом (_Excel_BookOpen) и дальше с ним работать.
 
Автор
D

DyadyaGenya

Знающий
Сообщения
300
Репутация
10
Что не получилось? пример не работает?
Тогда успел попробовать два варианта. Прямо ваш пример. Подкидывал в тестовую папку экселевский файл. И в таком виде как вы предлагаете не работает.
Код:
;_Excel($aData[1][0]) ; у меня вот так работает
_Excel($aData[2][0]) ; А вот так не работает вообще, ругается на неверный параметр. У вас написано наоборот.

И пробовал "модифицировать" свой скрипт на основе вашего примера. Тоже не получается
Код:
;_Excel($aData[1][0]) ; вот так работает при первом открытии
_Excel($aData[2][0]) ; А вот так не работает вообще, ругается на неверный параметр.

А когда пробую сохранить в мониториговую папку файл посредством разрабовской проги, то ваш скрипт файл открывает, но только для чтения.
Возможно в этом и причина ? Другая программа держит Эксель , пробуйте закрыть Эксель и уже открывать скриптом (_Excel_BookOpen) и дальше с ним работать.
Возможно, но видимо закрытие процесса ProcessClose("EXCEL.EXE") неправильное или этого не достаточно, потому что при попытке открыть файл, эксель вроде как его восстанавливает. Так что буду пробовать.
 
Последнее редактирование:

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,671
Репутация
2,481
Подкидывал в тестовую папку экселевский файл. И в таком виде как вы предлагаете не работает.
А так:
Код:
While 1
    $aData = _WinAPI_ReadDirectoryChanges($hDirectory, BitOR($FILE_NOTIFY_CHANGE_FILE_NAME, $FILE_NOTIFY_CHANGE_DIR_NAME), $pBuffer, 8388608, 1)
   
    If Not @error Then
        If UBound($aData) <= 2 Then
            _Excel($aData[1][0])
        Else
            _Excel($aData[2][0])
        EndIf
    EndIf
WEnd


ваш скрипт файл открывает, но только для чтения
Значит он уже открыт для записи в другой программе.
 
Автор
D

DyadyaGenya

Знающий
Сообщения
300
Репутация
10
Если только ваш пример, то срабатывает хорошо.
Если подставляю в свою, то к сожалению работает только с первым файлом. Если открыть второй, то та же ошибка.
Если в вашем примере формировать и открывать файл программой разрабов, то любой открывает только для чтения.
Ещё решил подробней проверить что конкретно попадает в массив. После первого запуска скрипта в массиве только один элемент. При повторном сохранении нового файла в массив попадает 7 элементов. И в вашем примере нормально отображается новый элемент, а у меня в функции старый в вариациях временного файла. При этом, если обнулить с помощью Global массив, то все равно в него попадают те же 7 элементов.
Уточнение. В моем скрипте при открытии второго, третьего и т.д файла массив сразу полный. Один файл только при открытии первого файла. В вашем же примере при появлении в папке нового файла сперва один элемент в массиве, потом 7 не зависимо от числа новых файлов.
Мало того, в моем скрипте при открытии второго файла в массив попадает даже файл из предыдущего запуска.
Вставляю проверку так:
Код:
While 1
    $aData = _WinAPI_ReadDirectoryChanges($hDirectory, BitOR($FILE_NOTIFY_CHANGE_FILE_NAME, $FILE_NOTIFY_CHANGE_DIR_NAME), $pBuffer, 8388608, 1)
_ArrayDisplay($aData, '1')
    If Not @error Then
        If UBound($aData) <= 2 Then
            _Excel($aData[1][0])
        Else
            _Excel($aData[2][0])
        EndIf
    EndIf
WEnd

Сообщение автоматически объединено:

Да, и если в своем скрипте добавляю цикл While 1... WEnd как в вашем примере, то после закрытия первого файла скрипт вешается.
 

Вложения

  • Скриншот 26-12-2020 22.10.15.jpg
    Скриншот 26-12-2020 22.10.15.jpg
    50 КБ · Просмотры: 4
Последнее редактирование:

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,671
Репутация
2,481
Если только ваш пример, то срабатывает хорошо.
Это говорит о том, что у тебя в скрипте неправильно использование функции.

Ещё решил подробней проверить что конкретно попадает в массив
То что и должно. В справку загляни, зачем гадать.

Я уже выше рекомендовал использовать RDC, в твоём случае это решит все проблемы, т.к на момент выполнения операции над экселем, нужно приостанавливать слежение за изменениями.
 
Автор
D

DyadyaGenya

Знающий
Сообщения
300
Репутация
10
Это говорит о том, что у тебя в скрипте неправильно использование функции.
Это я понимаю )
Перенес установку папки для мониторинга внутрь функции и все заработало. Раньше назначал эту папку до создания окна. Теперь в функции которая нажимает кнопку создания разрабовского отчета и слежения за новыми файлами в папке.
То что и должно. В справку загляни, зачем гадать.
Думал то я одно, а получилось другое. Теперь только один файл в массиве
Я уже выше рекомендовал использовать RDC
Это уже потом попробую. Ночью 30 )) Хочу с этим вариантом разобраться окончательно. Почему-то на другом компе работа с экселем вылетает.
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,671
Репутация
2,481
По идее правильнее будет так:

Код:
#include <WinAPIFiles.au3>
#include <Excel.au3>

Global $sValidFName_Pattern = '^(?!~\$).*?\.xlsx$'
Global $sRenamed_FName = ''

Global $g_sPath = @DesktopDir & '\test'
DirCreate($g_sPath)

$hDirectory = _WinAPI_CreateFileEx($g_sPath, $OPEN_EXISTING, $FILE_LIST_DIRECTORY, BitOR($FILE_SHARE_READ, $FILE_SHARE_WRITE), $FILE_FLAG_BACKUP_SEMANTICS)
$pBuffer = _WinAPI_CreateBuffer(8388608)

While 1
    $aData = _WinAPI_ReadDirectoryChanges($hDirectory, BitOR($FILE_NOTIFY_CHANGE_FILE_NAME, $FILE_NOTIFY_CHANGE_DIR_NAME), $pBuffer, 8388608, 1)
   
    If Not @error Then
        For $i = 1 To $aData[0][0]
            Switch $aData[$i][1] ;Action
                Case $FILE_ACTION_ADDED
                    ConsoleWrite('Added: ' & $aData[$i][0] & @CRLF)
                    _Excel($aData[$i][0])
                Case $FILE_ACTION_REMOVED
                    ConsoleWrite('Removed: ' & $aData[$i][0] & @CRLF)
                Case $FILE_ACTION_MODIFIED
                    ConsoleWrite('Modified: ' & $aData[$i][0] & @CRLF)
                Case $FILE_ACTION_RENAMED_OLD_NAME
                    ConsoleWrite('Renamed (Old Name): ' & $aData[$i][0] & @CRLF)
                Case $FILE_ACTION_RENAMED_NEW_NAME
                    ConsoleWrite('Renamed (New Name): ' & $aData[$i][0] & @CRLF)
            EndSwitch
        Next
    EndIf
WEnd

Func _Excel($Otchet)
    Local $sWorkbook = $g_sPath & '\' & $Otchet
    
    ;Ничего не делаем если: файл не существует, не является валидным эксель файлом, или это наш переименованный файл
    If Not FileExists($sWorkbook) Or Not StringRegExp($Otchet, $sValidFName_Pattern) Or $Otchet = $sRenamed_FName Then
        Return
    EndIf
    
    Local $oExcel = _Excel_Open(False, False)
    Local $oWorkbook = _Excel_BookOpen($oExcel, $sWorkbook, False, False)
   
    If @error Then
        MsgBox($MB_SYSTEMMODAL, "_Excel_BookOpen", "Error opening '" & $sWorkbook & "'." & @CRLF & "@error = " & @error & ", @extended = " & @extended & '  ошибка')
        Exit
    EndIf
    
    $sRenamed_FName = StringTrimRight($Otchet, 5) & ' (1).xlsx'
    
    ; Сохранение файла с новым именем
    _Excel_BookSaveAs($oWorkbook, $g_sPath & '\' & $sRenamed_FName, $xlWorkbookDefault, True)
    
    ; Закрытие экселя
    _Excel_BookClose($oWorkbook, False)
    _Excel_Close($oExcel, False, True)
EndFunc
 
Автор
D

DyadyaGenya

Знающий
Сообщения
300
Репутация
10
Global $sValidFName_Pattern = '^(?!~\$).*?\.xlsx$'
Не подумал для этих целей использовать регулярку, обрезал значок временного файла.
И тоже думал уже добавить какую-то проверку на валидность и на выполнение каждого блока операций (удаление, сортировка и тп.). Потому что иногда складывается ощущение, что не успевает на некоторых операциях. Пока поставил задержки, но не всегда помогает. А вот проверять валидность даже не мог пока придумать как осуществить. Спасибо. Позже буду пробовать (30-го).
Только вот не понял, почему
А не:
Код:
For $i = 1 To UBound($aData)

Сообщение автоматически объединено:

If Not FileExists($sWorkbook) Or Not StringRegExp($Otchet, $sValidFName_Pattern) Or $Otchet = $sRenamed_FName Then
Да, и ещё, не подскажите как проверить какое именно условие сработало, оставив вашу строку и не переписывая её на ElseIf для каждого блока?
 
Последнее редактирование:
Верх