Прежде чем ответить на ваш вопрос, прокомментируйте существующий код.
Вы запускаете этот код в Outlook. Вам не нужно olApp
. Ссылка на приложение Outlook требуется только в том случае, если вы пытаетесь получить доступ к своей электронной почте из Excel или другого продукта Office.
Я удивлен, как часто я вижу On Error GoTo ErrorHandler
, потому что я никогда не находилиспользуйте из этого оператора.
Если я кодирую для себя, я хочу остановить выполнение оператора, вызывающего проблему, чтобы я мог понять, что происходит, не догадываясь из сообщения об ошибке. Если выполнение оператора, вызывающего ошибку, останавливается, я могу перезапустить код, если смогу немедленно исправить ошибку.
Если я занимаюсь разработкой для клиента, в худшем случае мне нужно удобное для пользователя сообщение. Err.Number & " - " & Err.Description
это не моя идея удобного сообщения. Это даже не говорит мне, какое письмо вызвало проблему. Для клиента у меня было бы что-то вроде:
Dim ErrDesc as String
Dim ErrNum as Long
: : :
On Error Resume Next
Statement that might give an error
ErrNum = Err.Num
ErrDesc = Err.Description
On Error GoTo 0
If ErrNum <> 0 Then
Code to handle errors that can occur with
this statement in a user-friendly manner.
End If
Сегодня Dim Att As String
хорошо, потому что вы помните, что такое Att
. Вы будете помнить, когда обновите этот макрос через шесть или двенадцать месяцев? Знает ли коллега, обновляющий этот макрос, что такое Att
? Я бы назвал это AttName
или, возможно, AttDsplName
.
Вы говорите, что код сохраняет вложения PDF, но вы не проверяете это. К макросу VBA логотипы, изображения, подписи и другие файлы также являются вложениями. Также вы предполагаете, что вложение, которое вы хотите сохранить, - Attachments(1)
. Если имеется несколько вложений, логотипы, изображения и подписи могут быть на первом месте.
У вас есть:
'Set folder to save in.
Dim olDestFldr As Outlook.MAPIFolder
Вы не задаете olDestFldr
и не перемещаетепо электронной почте в другую папку. Вы хотите сделать это?
Теперь к вашему вопросу. Я включил код для двух методов достижения вашей цели, и я обсуждаю еще два метода. Однако, прежде чем показать вам код, я подозреваю, что мне нужно познакомить вас с вариантами. Рассмотрим:
Dim A As Long
Dim B As String
Dim C As Double
Dim D As Variant
Я объявил от A до C как длинное целое число, строку и двойное число. Эти переменные никогда не могут быть ничем иным и должны использоваться в соответствии с правилами для их типа. Я могу написать A = A + 1
или A = A * 5
. При условии, что новое значение для A не превышает максимальное значение для длинного целого числа, эти операторы подойдут. Но я не могу написать A = "House"
, потому что «Хаус» не является целым числом. Я могу написать B = "House"
, потому что «Дом» - это строка. Я могу написать B = "5"
, а затем A = A + B
, потому что VBA выполнит неявные преобразования, если сможет. То есть VBA может преобразовать строку "5" в целое число 5 и добавить ее к A
.
Я также могу написать:
D = 5
D = D + A
D = "House"
D
- это вариант, который означает, что онможет содержать любой тип данных. Здесь я присваиваю 5 D
, затем добавляю A
, поэтому для этих двух операторов D
содержит целое число. Затем я передумал и присвоил строку D
. Это не очень разумный код, но это действительный код. D
может содержать гораздо больше, чем целое число и строка. В частности, он может содержать массив. Обратите внимание:
ReDim D(0 To 2)
D(0) = "House"
D(1) = A + 5
D(2) = 3.7
После оператора ReDim все выглядит так, как будто D
был преобразован в массив, и я использую синтаксис массива для доступа к элементам D. D(0)
содержит "House", D(1)
содержит на 5 больше, чем текущее значение A
, а D(2)
содержит двойное 3,7.
Я могу добиться того же эффекта с:
D = Array("House", A + 5, 3.7)
Я уверен, что вы согласны с этимлегче. Array
- это функция, которая может принимать большое количество параметров и возвращает массив Variant, содержащий те параметры, которые я присвоил D
. Обычно я не советую смешивать типы в массиве вариантов, поскольку очень легко запутаться. Тем не менее, это действительно VBA, и я нашел его бесценным с особенно трудными проблемами. Обычно я бы не использовал функцию Array
, я бы написал:
D = VBA.Array("House", A + 5, 3.7)
При VBA.Array
нижняя граница массива гарантированно равна нулю. При Array
нижняя граница зависит от оператора Option Base
. Я никогда не видел, чтобы кто-нибудь использовал оператор Option Base
, но я не люблю рисковать тем, что мой код будет изменен кем-то, добавившим этот оператор. Выполните поиск «Оператор VBA Option Base», чтобы узнать, что делает этот оператор.
Следующий код демонстрирует мой первый метод достижения вашей цели:
Option Explicit
Sub Method1()
Dim DiscFldrCrnt As Variant
Dim DiscFldrs As Variant
Dim Inx As Long
Dim SenderNameCrnt As Variant
Dim SenderNames As Variant
Dim SubjectCrnt As Variant
Dim Subjects As Variant
SenderNames = VBA.Array("Doe, John", "Early, Jane", "Friday, Mary")
Subjects = VBA.Array("John's topic", "Jane's topic", "Mary's topic")
DiscFldrs = VBA.Array("DoeJohn", "EarlyJane", "FridayMary")
For Inx = 0 To UBound(SenderNames)
SenderNameCrnt = SenderNames(Inx)
SubjectCrnt = Subjects(Inx)
DiscFldrCrnt = DiscFldrs(Inx)
' Code to process SenderNameCrnt, SubjectCrnt and DiscFldrCrnt
Debug.Print SenderNameCrnt & " " & SubjectCrnt & " " & DiscFldrCrnt
Next
End Sub
Если вы копируете этот код в модуль, вы можете запустить его и посмотреть, что он делает. Если вы медленно прорабатываете его, вы должны понимать, что он делает. При необходимости возвращайтесь с вопросами, но чем больше вы сможете открыть для себя, тем быстрее вы будете развивать свои собственные навыки.
Примечание: папки на дисках имеют названия, такие как «DoeJohn». Я предполагаю, что у вас будет что-то вроде «C: \ Users \ NAEC02 \ Test \» в качестве корневой папки, и вы сохраните вложение в «C: \ Users \ NAEC02 \ Test \ DoeJohn \».
Я использую этот метод, когда у меня есть небольшое количество значений, которые мне нужно связать. Это зависит от того, связаны ли SenderNames(#)
, Subjects(#)
и DiscFldrs(#)
. Поскольку количество различных комбинаций увеличивается, может быть трудно поддерживать три массива в шаге. Method2
решает эту проблему.
Sub Method2()
Dim DiscFldrCrnt As Variant
Dim Inx As Long
Dim SenderNameCrnt As Variant
Dim SubjectCrnt As Variant
Dim TestValues As Variant
TestValues = Array("Doe, John", "John's topic", "John", _
"Early, Jane", "Jane's topic", "Jane", _
"Friday, Mary", "Mary's topic", "Mary")
For Inx = LBound(TestValues) To UBound(TestValues) Step 3
SenderNameCrnt = TestValues(Inx)
SubjectCrnt = TestValues(Inx + 1)
DiscFldrCrnt = TestValues(Inx + 2)
' Code to process SenderNameCrnt, SubjectCrnt and DiscFldrCrnt
Debug.Print SenderNameCrnt & " " & SubjectCrnt & " " & DiscFldrCrnt
Next
End Sub
Здесь я поместил все значения в один массив. Если я хочу добавить нового отправителя, я добавляю еще три элемента в конец массива, с которыми мне проще работать. Для кода для обработки три значения Method1
и Method2
идентичны.
Принципиальным недостатком Method2
по сравнению с Method1
является то, что общее количество значений уменьшается. Мне нравится видеть весь мой код, поэтому мне не нравятся операторы, которые превышают ширину экрана. Это ограничивает мои строки до 100 символов. Я использую символ продолжения, чтобы распределить оператор по нескольким строкам, но в каждом выражении может быть не более 24 строк продолжения. С Method1
я распределяю значения по трем массивам и, следовательно, по трем операторам, чтобы у меня было в три раза больше значений. На практике это не реальный предел. И Method1
, и Method2
становятся слишком сложными для управления до того, как будут достигнуты пределы VBA.
Реальный недостаток Method1
и Method2
заключается в том, что каждое изменение требует услуг программиста. Если обслуживание пользователей важно, я использую Method3
, который читает текстовый файл в массивы, или Method4
, который читает из листа Excel. Я не включил код для Method3
или Method4
, но могу добавить один или оба, если вам нужна эта функциональность. Я считаю, что большинство пользователей предпочитают рабочий лист, но те, у кого есть любимый текстовый редактор, предпочитают текстовый файл.
В середине обоих Method1
и Method2
У меня есть:
' Code to process SenderNameCrnt, SubjectCrnt and DiscFldrCrnt
Debug.Print SenderNameCrnt & " " & SubjectCrnt & " " & DiscFldrCrnt
Вынужно заменить эти операторы вариацией существующего кода. У меня нет простого метода тестирования следующего кода, поэтому он не проверен, но он должен дать вам старт.
Это новая версия Items_ItemAdd
, предназначенная для работы с любым из моих методов.
Private Sub Items_ItemAdd(ByVal Item As Object)
Const DiscFldrRoot As String = "C:\Users\NAEC02\Test\"
' * There is no need to write Outlook.MailItem because (1) you are within Outlook
' and (2) there is no other type of MailItem. You only need to specify Outlook
' for folders since there are both Outlook and Scripting folders. Note:
' "Scripting" is the name of the library containing routines for disc folders.
' * Do not spread your Dim statements throughout your sub. There are languages
' where you can declare variables within code blocks but VBA is not one of those
' languages. With VBA, you can declare variables for an entire sub or function,
' for an entire module or for an entire workbook. If you spread your Dim
' statements out it just makes them hard to find and you are still declaring
' them at the module level.
Dim DiscFldrCrnt As Variant
Dim InxA As Long
Dim Msg As MailItem
Dim SenderNameCrnt As Variant
Dim SubjectCrnt As Variant
' You also need the arrays from whichever of Method1 or Method2 you have chosen
If TypeName(item) = "MailItem" Then
' Only interested in MailItems
Set Msg = Item
' Code from Method1 or Method2 with the code below in the middle
End If
End Sub
Вставьте тело Method1
или Method2
, в зависимости от того, что вы выбрали, в середине кода выше. Затем вставьте следующий код в середину этого кода.
With Msg
If .Attachments.Count = 0 Then
' Don't bother to check MailItem if there are no attachments
Else
If .Subject <> SubjectCrnt Then
' Wrong subject so ignore this MailItem
ElseIf .SenderName <> SenderNameCrnt Then
' Wrong sender name so ignore this MailItem
Else
' SenderName and Subject match so save any PDF attachments
For InxA = 1 to .Attachments.Count
If LCase(Right$(.Attachments(InxA).DisplayName, 4)) = ".pdf" Then
' Warning: SaveAsFile overwrites existing file with the same name
.Attachments(InxA).SaveAsFile DiscFldrRoot & DiscFldrCrnt & _
.Attachments(InxA).DisplayName
End If
End With
Next
End If
End With