Мой макрос сохраняет вложение в формате PDF от одного отправителя / темы. Как заставить его обрабатывать несколько отправителей / предметов? - PullRequest
0 голосов
/ 01 ноября 2019

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

Приведенный ниже код работает только для одного отправителя. Как добавить больше отправителей и несколько папок?

Private WithEvents Items As Outlook.Items

Private Sub Application_Startup()
    Dim olApp As Outlook.Application
    Dim objNS As Outlook.NameSpace
    Set olApp = Outlook.Application
    Set objNS = olApp.GetNamespace("MAPI")
    Set Items = objNS.GetDefaultFolder(olFolderInbox).Items
End Sub

Private Sub Items_ItemAdd(ByVal item As Object)

On Error GoTo ErrorHandler

    'Only act if it's a MailItem
    Dim Msg As Outlook.MailItem
    If TypeName(item) = "MailItem" Then
        Set Msg = item

    'Change variables to match need. Comment or delete any part unnecessary.
        If (Msg.SenderName = "Marc, Test") And _
        (Msg.Subject = "Heures") And _
        (Msg.Attachments.Count >= 1) Then

    'Set folder to save in.
    Dim olDestFldr As Outlook.MAPIFolder
    Dim myAttachments As Outlook.Attachments
    Dim Att As String

    'location to save in.  Can be root drive or mapped network drive.
    Const attPath As String = "C:\Users\NAEC02\Test\"


    ' save attachment
   Set myAttachments = item.Attachments
    Att = myAttachments.item(1).DisplayName
    myAttachments.item(1).SaveAsFile attPath & Att

    ' mark as read
   Msg.UnRead = False



End If
End If


ProgramExit:
  Exit Sub

ErrorHandler:
  MsgBox Err.Number & " - " & Err.Description
  Resume ProgramExit
End Sub


1 Ответ

0 голосов
/ 03 ноября 2019

Прежде чем ответить на ваш вопрос, прокомментируйте существующий код.


Вы запускаете этот код в 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...