Страницы

среда, 28 декабря 2011 г.

Shell.Application. Асинхронное выполнение методов.

Последнее время я обретаюсь в материях, имеющих мало общего со скриптовыми технологиями. Однако когда ко мне обратился товарищ с просьбой написать скрипт для удаления файлов старше недели, я не раздумывая принял решение потратить на это несколько минут - мелочь, а приятно, казалось бы...

Ни разу не напрягаясь пишу несколько строчек кода и с чувством глубокого удовлетворения отправляю листинг заказчику.

Минут через 15 он вспоминает, что нужно удалять только файлы с определенным расширением - резервные копии какой-то базы данных. Нет проблем - еще пара минут и слегка подрихтованный код повторно отправляется в путешествие по просторам сети.

Спустя еще некоторое время товарищ обращается с очередной просьбой - не мешало бы удалять файлы не только из указанного каталога, но и из подкаталогов. ОК - чашка чаю, немного рекурсии - получите и распишитесь.

Как оказалось, с распиской в получении я сильно поспешил. Вскоре легенда в очередной раз обновилась. Резервные копии внезапно оказались настолько дороги заказчику, что он отказался расставаться с ними любым иным способом, кроме как через корзину. Что ж, назвался клизмой - полезай... :).

Пробираюсь к корзине через Shell.Application, перемещаю файлы в корзину с помощью метода MoveHere...Никакого эффекта.
Не засек на какой минуте недоумения мне пришла мысль проверить с помощью MsgBox какой путь получает метод MoveHere, но после добавления строчки MsgBox oFile.Path сразу за проверкой даты модификации и расширения файла, и последующего запуска скрипта я начал что-то подозревать. Скрипт переместил все файлы кроме одного. В завершение, заменив MsgBox на WScript.Sleep и переместив эту строку на этаж ниже я получил рабочий код.
''''''''''''''''''''''''''''''''''''''''''''''''
' Удаляем файлы в корзину по дате и расширению
''''''''''''''''''''''''''''''''''''''''''''''''
sFolderPath = "c:\temp\" 'каталог с файлами
'текущая дата минус количество дней = дата, старше которой надо удалять
dDateDif = DateAdd("d", -7, Date)
sExt = "vbs" 'расширение

Set fso = CreateObject("Scripting.FileSystemObject")
Set recyclebin = CreateObject("Shell.Application").Namespace(10)

DeleteOlderThan sFolderPath

Function DeleteOlderThan(sPath)
 For Each oFile In fso.GetFolder(sPath).Files
  If oFile.DateLastModified < dDateDif And LCase(fso.GetExtensionName(oFile.Name)) = LCase(sExt) Then
   'перемещаем файлы в корзину
   recyclebin.MoveHere(oFile.Path)
   'без Sleep - никак, если файлы большого размера - добавляем миллисекунд
   WScript.Sleep 10 
  End If
 Next 
 For Each oFolder In fso.GetFolder(sPath).SubFolders
  DeleteOlderThan oFolder.Path  
 Next
End Function
Отправляю листинг по известному адресу с наилучшими пожеланиями. Ракетные шахты открыты - несколько минут превратились чуть ли не в час времени :).

Позже я вспомнил, что уже встречался с подобным поведением методов Shell.Application - в скрипте создания zip-архивов.
''''''''''''''''''''''''''''''''''''''''''''''''
' Архивируем все файлы текущего каталога
''''''''''''''''''''''''''''''''''''''''''''''''
sZipFolderPath = "c:\temp" 'каталог для zip-архивов

With CreateObject("Scripting.FileSystemObject")
 'путь к текущему каталогу
 sFolderPath = .GetParentFolderName(WScript.ScriptFullName)
 'путь к zip-архиву
 sZipPath = sZipFolderPath & "\" & .GetBaseName(sFolderPath) & ".zip"
 'создаем пустой zip-файл
 .CreateTextFile(sZipPath, True).Write "PK" & Chr(5) & Chr(6) & String(18, vbNullChar)
End With
'копируем файлы в архив
With CreateObject("Shell.Application")
 .NameSpace(sZipPath).CopyHere .NameSpace(sFolderPath).Items
End With
'без Sleep - никак, если размер каталога большой - добавляем секунд
WScript.Sleep 1000
Вывод:
- методы Shell.Application (по крайней мере некоторые из них) выполняются асинхронно, поэтому следует принимать во внимание, что скрипт может завершить свою работу прежде, чем они успеют отработать
- в процессе планирования решения казалось бы самых несложных задач следует делать по возможности максимальный "ефрейторский зазор" :)