1

Тема: VBS, Outlook: Копирование сообщений — как убить алерт?

Outlook 2010. Простенький VBS скрипт копирует прошлогодние сообщения из одной папки (расположенной в ящике на сервере Exchange) в другую (расположенную в локальном pst-шнике).
Эпизодически, ни с того ни с сего, в Outlook всплывает сообщение:

Невозможно открыть пользовательскую форму. Вместо нее будет использована форма Outlook. Объект не поддерживает требуемое действие. [OK]

и, пока его не ткнешь, скрипт висит на строке с копированием.
Что за бред? Какая нах "пользовательская форма"?
И, собственно, главный вопрос: Как предотвратить появление этого долбанного сообщения?

Set oOApp = CreateObject("Outlook.Application")
Set oSrcFolder = oOApp.GetNamespace("MAPI").Folders.Item(folder0)
Set oDestFolder = oOApp.GetNamespace("MAPI").Folders.Item(folder1)
On Error Resume Next
For Each Item In oSrcFolder.Items
  If Year(Item.CreationTime) < 2016 Then
    WScript.Echo Item.CreationTime
    Item.Copy.Move oDestFolder
  End If
Next

P.S. Хотя код VBS, запостил в треде VBA, т.к. полагаю что тематически это более верно. Да и в VBA можно использовать почти без изменений.

2

Re: VBS, Outlook: Копирование сообщений — как убить алерт?

mozers
Нет возможности тестировать. Вопрос только, чем альтернатива с Shell.Application не годна?

3

Re: VBS, Outlook: Копирование сообщений — как убить алерт?

Flasher
А Вы знаете способ копирования почтовых сообщений Outlook с помощью Shell.Application? Я - нет.

4

Re: VBS, Outlook: Копирование сообщений — как убить алерт?

mozers пишет:

на сервере Exchange

Всё, понял какой сервер. Отбой.

5 (изменено: mozers, 2016-01-20 15:43:56)

Re: VBS, Outlook: Копирование сообщений — как убить алерт?

Допетрил:
Все дело в том, что в почтовом ящике, помимо нормальных сообщений порой присутствует всякая хрень (например, отзывы сообщений). Когда Outlook, побуждаемый скриптом, доходит до копирования таких сообщений, то тогда он и выдает вышеупомянутый алерт "Невозможно открыть пользовательскую форму...".
Как избавиться от этих назойливых алертов и обеспечить бесперебойное копирование? - очевидно копировать только "нормальные" сообщения, пропуская все остальное. Отличить обычные сообщения от всякой ерунды можно анализируя имя класса сообщения. В данном случае имя класса должно начинаться с текста "IPM.Note":

If Left(Item.MessageClass, 8) = "IPM.Note" Then
  Item.Copy.Move oDestFolder
End If

Вопрос решен!

6

Re: VBS, Outlook: Копирование сообщений — как убить алерт?

Увы, тема не закрыта.
Обнаружилось что копируются не все сообщения. Почему ???
Удалил On Error Resume Next. Добавил запись в лог. Ошибок - нет, а копируется только 70% всех писем.
Это пипец какой то...

7

Re: VBS, Outlook: Копирование сообщений — как убить алерт?

Предполагаю, что тут может быть так же как в случае работы с zip архивами через Shell.Application. Там процесс добавления элементов и извлечения асинхронный. Если файл большого размера, то он не успевает быстро извечься. В случае с письмами, думаю это может быть из-за объёмных вложений. Можно попробовать, наращивать счётчик копирования и проверять items.count в конечной папке. Вставить это в цикл со Sleep(100) и ждать совпадения количества файлов со счётчиком.

P.S но это лишь предположение. Проверить пока не на чем к сожалению.

Передумал переделывать мир. Пашет и так, ну и ладно. Сделаю лучше свой !

8

Re: VBS, Outlook: Копирование сообщений — как убить алерт?

Xameleon
Не понимаю что мне даст счетчик. Я и без него знаю что часть сообщений почему то не копируется. Сравнивал копируемое сообщение с некопируемым по всем атрибутам. Нет никаких существенных различий! Непонимаю...
ИМХО - просто тупой баг Outlook.Application - самого глюкавого из всех майкрософтовских ActiveX.

9

Re: VBS, Outlook: Копирование сообщений — как убить алерт?

mozers, предполагаю, что счётчик даст возможность определить момент когда копирование текущего сообщения дошло до конца и можно переходить к следующему, а до того продолжать крутить цикл с ожиданием.

Если через Shell.Application производить копирование больших файлов, то на экране появляется прогрессбар. При этом "за кулисами" происходит создание временного файла в Temporary директории, и пока идёт процесс копирования, файл в конечном каталоге не появляется. Только по концу процесса происходит перемещение временного файла в конечную директорию с нужным именем. Поэтому иногда такую задачу решают с помощью счётчика. Проще говоря - мы начали копировать и нарастили счётчик i++. В нём уже значение 1. Теперь крутим цикл и проверяем когда items.Count сменится с 0 на 1. И так далее.

Я имею в виду нечто вроде этого:


Option Explicit

Dim oOApp, oSrcFolder, oDestFolder, i
Set oOApp = CreateObject("Outlook.Application")
'... 
'Присвоение значений folder0, folder1
'... 
Set oSrcFolder = oOApp.GetNamespace("MAPI").Folders.Item(folder0)
Set oDestFolder = oOApp.GetNamespace("MAPI").Folders.Item(folder1)
On Error Resume Next
For Each Item In oSrcFolder.Items
	If Left(Item.MessageClass, 8) = "IPM.Note" and Year(Item.CreationTime) < 2016 Then
		WScript.Echo Item.CreationTime
		Item.Copy.Move oDestFolder
		i = i + 1
		While oDestFolder.Items.Count < i
			WScript.Sleep 100
		Wend
	End If
Next

P.S Проверить опять же не могу, так как нет у меня аутлука (.

Передумал переделывать мир. Пашет и так, ну и ладно. Сделаю лучше свой !

10 (изменено: mozers, 2016-02-09 10:35:23)

Re: VBS, Outlook: Копирование сообщений — как убить алерт?

Xameleon, спасибо за идею. Нет не помогло, но заставило думать в правильном направлении.
"Правильным направлением" оказался подсчет кол-ва перемещаемых файлов.
Да, я дико извиняюсь. Задача изначально была сформулирована неверно. На самом деле мне надо не скопировать сообщения, а переместить их в другую папку. Я полагал что тут нет принципиальной разницы, поэтому и написал "скопировать". На деле оказывается что в этом вся проблема.
Подсчет выявил престранную вещь: Если я копирую Item.Copy.Move oDestFolder, то в папку назначения попадают все выбранные сообщения.
Если я меняю эту строчку на Item.Move oDestFolder, то в папку назначения перемещается ровно половина из выбранных сообщений (точнее - каждое 2е). Походу что то я делаю неправильно...
P.S. Вариант сначала скопировать все файлы, а потом махом удалить на источнике скопированные прошу не предлагать.

11

Re: VBS, Outlook: Копирование сообщений — как убить алерт?

Ошибка оказалась настолько банальной, что и признаться стыдно.
Её знают все, кто хотя бы раз пробовал удалять в цикле данные из массива.
И решение тут - классическое - удалять с конца, а не с начала.
Т.е. вместо For Each Item In oSrcFolder.Items следует писать

For i = oSrcFolder.Items.Count To 1 Step -1
	Set Item = oSrcFolder.Items(i)

12

Re: VBS, Outlook: Копирование сообщений — как убить алерт?

mozers

Нашёл ещё несколько примеров реализации:

Тут решается за счёт метода FindNext
https://msdn.microsoft.com/en-us/librar … 60683.aspx


Dim myNameSpace As Outlook.NameSpace 
Dim myInbox As Outlook.Folder 
Dim myDestFolder As Outlook.Folder 
Dim myItems As Outlook.Items 
Dim myItem As Object 

Set myNameSpace = Application.GetNamespace("MAPI") 
Set myInbox = myNameSpace.GetDefaultFolder(olFolderInbox) 
Set myItems = myInbox.Items 
Set myDestFolder = myInbox.Folders("Personal Mail") 
Set myItem = myItems.Find("[SenderName] = 'Dan Wilson'") 
While TypeName(myItem) <> "Nothing" 
	myItem.Move myDestFolder 
	Set myItem = myItems.FindNext 
Wend 

Тут вообще как-то иначе решается: http://superuser.com/a/476193

А тут всё-таки используют For Each. Интересно работает ли это ?
http://www.extendoffice.com/documents/o … -read.html

Sub MoveInbox2Reviewed()
On Error Resume Next
Set oOutlook = CreateObject("Outlook.Application")
Set oNamespace = oOutlook.GetNamespace("MAPI")
Set oFolderSrc = oNamespace.GetDefaultFolder(olFolderInbox)
Set oFolderDst = oFolderSrc.Folders("Reviewed")
Set oFilteredItems = oFolderSrc.Items.Restrict("[UnRead] = False")
For Each oMessage In oFilteredItems
    oMessage.Move oFolderDst
Next
End Sub

P.S Вижу, что в большинстве случаев используют oFolderSrc.Items.Restrict. Т.е работают не c текущими сообщениями в каталоге, а с результатом фильтрации. Может это тоже решает проблему ?

Передумал переделывать мир. Пашет и так, ну и ладно. Сделаю лучше свой !

13

Re: VBS, Outlook: Копирование сообщений — как убить алерт?

Xameleon пишет:

P.S Вижу, что в большинстве случаев используют oFolderSrc.Items.Restrict. Т.е работают не c текущими сообщениями в каталоге, а с результатом фильтрации. Может это тоже решает проблему ?

Нет. С результатом фильтрации работать удобнее, т.к. мы можем заранее прикинуть какое кол-во сообщений будем перемещать. Но используя потом For Each для перемещения опять наступаем на те же грабли. Так что последний пример - вранье!
С двумя первыми вариантами можно поиграться, по логике должно работать, но тут (я уже начинаю капризничать) мы узнаем количество перемещенных сообщений только по окончании процесса.
Так что, самый лучший и надежный вариант:

Set oOApp = CreateObject("Outlook.Application")
Set oSrcFolder = oOApp.GetNamespace("MAPI").Folders.Item(folder0)
Set oDestFolder = oOApp.GetNamespace("MAPI").Folders.Item(folder1)

WScript.Echo "В исходной папке: " & oSrcFolder.Items.Count
WScript.Echo "В получателе: " & oDestFolder.Items.Count

strFilter = "[ReceivedTime] < '1/1/2016'"
Set oFilterRecItems = oSrcFolder.Items.Restrict(strFilter)

WScript.Echo "Собираемся переместить: " & oFilterRecItems.Count

j = 0
For i = oFilterRecItems.Count To 1 Step -1
	Set Item = oFilterRecItems(i)
	If Left(Item.MessageClass, 8) = "IPM.Note" Then
		WScript.StdOut.Write Item.ReceivedTime & vbCr
		Item.Move oDestFolder
		j = j + 1
	End If
Next

WScript.Echo "                   "
WScript.Echo "Реально перемещено: " & j
WScript.Echo "В исходной папке: " & oSrcFolder.Items.Count
WScript.Echo "В получателе: " & oDestFolder.Items.Count