Quản lý email outlook qua VBA cần giúp đỡ (7 người xem)

Liên hệ QC

Người dùng đang xem chủ đề này

  • Tôi tuân thủ nội quy khi đăng bài

    dohungmo

    Thành viên mới
    Tham gia
    4/4/13
    Bài viết
    40
    Được thích
    1
    Nhờ anh/chị hỗ trợ viết giúp em một file VBA có thể tích hợp với Outlook để hỗ trợ quản lý email hàng ngày một cách thuận tiện hơn. Cụ thể, em cần file này thực hiện được các chức năng sau:
    1. Hiển thị địa chỉ email của người gửi.
    2. Hiển thị tiêu đề email (Subject).
    3. Hiển thị đường link dẫn trực tiếp đến email trong Outlook.
    4. Hiển thị tên tệp đính kèm (nếu có).
    5. Hiển thị ngày và giờ nhận email.
    6. Có nút “Refresh” để cập nhật danh sách email mới.
    7. Cập nhật email theo kiểu tích lũy:
      • Ví dụ: nếu bắt đầu sử dụng file từ ngày 01/05/2024, thì file chỉ cập nhật các email nhận sau ngày này, và không lấy các email trước thời điểm đó.
      • Mỗi lần nhấn “Refresh”, danh sách email mới sẽ được bổ sung thêm vào file, không làm mất các email đã được lưu trước đó.
    Em cảm ơn.
     

    File đính kèm

    Lần chỉnh sửa cuối:
    Giải pháp
    Nhờ anh/chị hỗ trợ viết giúp em một file VBA có thể tích hợp với Outlook để hỗ trợ quản lý email hàng ngày một cách thuận tiện hơn. Cụ thể, em cần file này thực hiện được các chức năng sau:
    1. Hiển thị địa chỉ email của người gửi.
    2. Hiển thị tiêu đề email (Subject).
    3. Hiển thị đường link dẫn trực tiếp đến email trong Outlook.
    4. Hiển thị tên tệp đính kèm (nếu có).
    5. Hiển thị ngày và giờ nhận email.
    6. Có nút “Refresh” để cập nhật danh sách email mới.
    7. Cập nhật email theo kiểu tích lũy:
      • Ví dụ: nếu bắt đầu sử dụng file từ ngày 01/05/2024, thì file chỉ cập nhật các email nhận sau ngày này, và không lấy các email trước thời điểm đó.
      • Mỗi lần nhấn “Refresh”, danh sách email mới sẽ được bổ sung thêm vào file, không làm...
    Nhờ anh/chị hỗ trợ viết giúp em một file VBA có thể tích hợp với Outlook để hỗ trợ quản lý email hàng ngày một cách thuận tiện hơn. Cụ thể, em cần file này thực hiện được các chức năng sau:
    1. Hiển thị địa chỉ email của người gửi.
    2. Hiển thị tiêu đề email (Subject).
    3. Hiển thị đường link dẫn trực tiếp đến email trong Outlook.
    4. Hiển thị tên tệp đính kèm (nếu có).
    5. Hiển thị ngày và giờ nhận email.
    6. Có nút “Refresh” để cập nhật danh sách email mới.
    7. Cập nhật email theo kiểu tích lũy:
      • Ví dụ: nếu bắt đầu sử dụng file từ ngày 01/05/2024, thì file chỉ cập nhật các email nhận sau ngày này, và không lấy các email trước thời điểm đó.
      • Mỗi lần nhấn “Refresh”, danh sách email mới sẽ được bổ sung thêm vào file, không làm mất các email đã được lưu trước đó.
    Em cảm ơn.
    Gửi bạn dùng thử xem sao, cũng lâu lắm rồi không động tay vào code Outlook VBA, hoang mang từ thời điểm Microsoft quyết định đẩy The New Outlook làm ứng dụng chính, dần dần sẽ thay thế ứng dụng Outlook cũ (hay còn gọi là The Classic Outlook), mấy năm trước còn hăng hái dịch hẳn một cuốn sách về chủ đề này dày gần 1000 trang.

    1746166933961.png

    Module1:
    Mã:
    Option Explicit
    
    Public Sub ListMails()
        Dim objSh As Excel.Worksheet
        On Error Resume Next
        Set objSh = Sheet1
        If Err.Number <> 0 Then
            Call MsgBox("The required worksheet 'Sheet1' was not found.", vbExclamation, "Error: Worksheet Not Found")
            Exit Sub
        End If
        On Error GoTo 0
        Dim objOlApp As Outlook.Application
        On Error Resume Next
        Set objOlApp = GetObject(, "Outlook.Application")
        If Err.Number = 429 Then
            Call MsgBox("This macro requires a running instance of Outlook. Please try again later!", vbExclamation, "Error: Outlook Not Running")
            Exit Sub
        End If
        On Error GoTo 0
        Dim objFld As Outlook.Folder
        Set objFld = objOlApp.Session.PickFolder
        If objFld Is Nothing Then
            Call MsgBox("Please select a folder to continue", vbExclamation, "Error: Folder Not Specified")
            Exit Sub
        End If
        objSh.Range("C1").Value2 = objFld.FolderPath
        Dim dteDate As Date
        dteDate = CDate(objSh.Range("C2").Value2)
        Dim colItems As Collection
        Set colItems = GetMailsByDate(objOlApp, objFld, dteDate)
        If colItems.Count = 0 Then
            Call MsgBox("No mail item was found that matched the criteria 'after " & CStr(dteDate) & "'", vbInformation, "Search Result")
            Exit Sub
        End If
        Dim lngLastRow As Long, lngRowsCount As Long
        lngRowsCount = objSh.Rows.Count
        lngLastRow = objSh.Cells(lngRowsCount, 1).End(xlUp).Row
        If lngLastRow > 5 Then objSh.Range("A5:G" & CStr(lngLastRow)).ClearContents
        Dim arrResult As Variant, i As Long, j As Long, objResult As MailInfo, strAttFileNames As String
        ReDim arrResult(1 To colItems.Count, 1 To 7) As Variant
        For i = 1 To colItems.Count
            arrResult(i, 1) = i
            Set objResult = colItems.Item(i)
            arrResult(i, 2) = objResult.SenderEmailAddress
            arrResult(i, 3) = objResult.Subject
            If objResult.HasAttachments Then
                For j = 1 To objResult.Attachments.Count
                    If j = objResult.Attachments.Count Then
                        strAttFileNames = strAttFileNames & objResult.Attachments.Item(j)
                    Else: strAttFileNames = strAttFileNames & objResult.Attachments.Item(j) & ", "
                    End If
                Next
                arrResult(i, 4) = strAttFileNames
            End If
            arrResult(i, 5) = objResult.EntryId
            arrResult(i, 6) = objResult.ReceivedTime
        Next
        objSh.Range("A5").Resize(UBound(arrResult), UBound(arrResult, 2)).Value2 = arrResult
        lngLastRow = objSh.Cells(lngRowsCount, 1).End(xlUp).Row
        Dim objCell As Excel.Range, objLinkRange As Excel.Range
        Set objLinkRange = objSh.Range("E5:E" & CStr(lngLastRow))
        For Each objCell In objLinkRange
            objSh.Hyperlinks.Add objCell, "", objCell.Value2, "Open Mail", "Click here to open mail from Outlook"
        Next
    End Sub
    
    Public Function GetMailsByDate(ByVal OlApp As Outlook.Application, ByVal Fld As Outlook.Folder, ByVal DateVal As Date) As Collection
        If OlApp Is Nothing Or Fld Is Nothing Then Exit Function
        Dim objMail As Outlook.MailItem
        Dim dteDate As Date, objPA As Outlook.PropertyAccessor
        Set objMail = OlApp.CreateItem(olMailItem)
        Set objPA = objMail.PropertyAccessor
        dteDate = objPA.LocalTimeToUTC(DateVal)
        objMail.Close olDiscard
        Dim strFilter As String
        strFilter = "@SQL=" & Quote("urn:schemas:httpmail:datereceived") & " >= " & SingleQuote(dteDate)
        Const PR_SENDER_EMAIL_ADDRESS As String = "http://schemas.microsoft.com/mapi/proptag/0x0C1F001F"
        Const PR_SUBJECT As String = "http://schemas.microsoft.com/mapi/proptag/0x0037001F"
        Const PR_HASATTACH As String = "http://schemas.microsoft.com/mapi/proptag/0x0E1B000B"
        Dim objTbl As Outlook.Table, colResults As Collection, objResult As MailInfo, objRow As Outlook.Row, colAttachmentNames As Collection, objAtt As Outlook.Attachment
        Set colResults = New Collection
        Set objTbl = Fld.GetTable(strFilter)
        With objTbl
            .Columns.RemoveAll
            .Columns.Add "EntryId"
            .Columns.Add PR_SENDER_EMAIL_ADDRESS
            .Columns.Add PR_SUBJECT
            .Columns.Add PR_HASATTACH
            .Columns.Add "urn:schemas:httpmail:datereceived"
            Do Until .EndOfTable
                Set objResult = New MailInfo
                Set objRow = .GetNextRow
                With objResult
                    .HasAttachments = objRow(4)
                    .Subject = objRow(3)
                    .SenderEmailAddress = objRow(2)
                    .ReceivedTime = objRow.UTCToLocalTime(5)
                    If .HasAttachments Then
                        Set colAttachmentNames = New Collection
                        Set objMail = OlApp.Session.GetItemFromID(objRow(1))
                        For Each objAtt In objMail.Attachments
                            colAttachmentNames.Add objAtt.Filename
                        Next
                        objMail.Close olDiscard
                        Set .Attachments = colAttachmentNames
                    End If
                    .EntryId = objRow(1)
                End With
                colResults.Add objResult
            Loop
        End With
        Set GetMailsByDate = colResults
    End Function
    
    Private Function Quote(ByVal Text As String) As String
        Quote = Chr(34) & Text & Chr(34)
    End Function
    
    Private Function SingleQuote(ByVal Text As String) As String
        SingleQuote = Chr(39) & Text & Chr(39)
    End Function

    MailInfo.cls:

    Mã:
    Option Explicit
    
    Private m_SenderEmailAddress As String
    Private m_Subject As String
    Private m_EntryId As String
    Private m_HasAttachments As Boolean
    Private m_Attachments As New Collection
    Private m_ReceivedTime As Date
    
    Public Property Get SenderEmailAddress() As String
        SenderEmailAddress = m_SenderEmailAddress
    End Property
    
    Public Property Let SenderEmailAddress(Value As String)
        m_SenderEmailAddress = Value
    End Property
    
    Public Property Get Subject() As String
        Subject = m_Subject
    End Property
    
    Public Property Let Subject(Value As String)
        m_Subject = Value
    End Property
    
    Public Property Get EntryId() As String
        EntryId = m_EntryId
    End Property
    
    Public Property Let EntryId(Value As String)
        m_EntryId = Value
    End Property
    
    Public Property Get HasAttachments() As Boolean
        HasAttachments = m_HasAttachments
    End Property
    
    Public Property Let HasAttachments(Value As Boolean)
        m_HasAttachments = Value
    End Property
    
    Public Property Get Attachments() As Collection
        Set Attachments = m_Attachments
    End Property
    
    Public Property Set Attachments(Value As Collection)
        Set m_Attachments = Value
    End Property
    
    Public Property Get ReceivedTime() As Date
        ReceivedTime = m_ReceivedTime
    End Property
    
    Public Property Let ReceivedTime(Value As Date)
        m_ReceivedTime = Value
    End Property
     

    File đính kèm

    Lần chỉnh sửa cuối:
    Upvote 0
    Giải pháp
    Gửi bạn dùng thử xem sao, cũng lâu lắm rồi không động tay vào code Outlook VBA, hoang mang từ thời điểm Microsoft quyết định đẩy The New Outlook làm ứng dụng chính, dần dần sẽ thay thế ứng dụng Outlook cũ (hay còn gọi là The Classic Outlook), mấy năm trước còn hăng hái dịch hẳn một cuốn sách về chủ đề này dày gần 1000 trang.

    View attachment 308041

    Module1:
    Mã:
    Option Explicit
    
    Public Sub ListMails()
        Dim objSh As Excel.Worksheet
        On Error Resume Next
        Set objSh = Sheet1
        If Err.Number <> 0 Then
            Call MsgBox("The required worksheet 'Sheet1' was not found.", vbExclamation, "Error: Worksheet Not Found")
            Exit Sub
        End If
        On Error GoTo 0
        Dim objOlApp As Outlook.Application
        On Error Resume Next
        Set objOlApp = GetObject(, "Outlook.Application")
        If Err.Number = 429 Then
            Call MsgBox("This macro requires a running instance of Outlook. Please try again later!", vbExclamation, "Error: Outlook Not Running")
            Exit Sub
        End If
        On Error GoTo 0
        Dim objFld As Outlook.Folder
        Set objFld = objOlApp.Session.PickFolder
        If objFld Is Nothing Then
            Call MsgBox("Please select a folder to continue", vbExclamation, "Error: Folder Not Specified")
            Exit Sub
        End If
        objSh.Range("C1").Value2 = objFld.FolderPath
        Dim dteDate As Date
        dteDate = CDate(objSh.Range("C2").Value2)
        Dim colItems As Collection
        Set colItems = GetMailsByDate(objOlApp, objFld, dteDate)
        If colItems.Count = 0 Then
            Call MsgBox("No mail item was found that matched the criteria 'after " & CStr(dteDate) & "'", vbInformation, "Search Result")
            Exit Sub
        End If
        Dim lngLastRow As Long, lngRowsCount As Long
        lngRowsCount = objSh.Rows.Count
        lngLastRow = objSh.Cells(lngRowsCount, 1).End(xlUp).Row
        If lngLastRow > 5 Then objSh.Range("A5:G" & CStr(lngLastRow)).ClearContents
        Dim arrResult As Variant, i As Long, j As Long, objResult As MailInfo, strAttFileNames As String
        ReDim arrResult(1 To colItems.Count, 1 To 7) As Variant
        For i = 1 To colItems.Count
            arrResult(i, 1) = i
            Set objResult = colItems.Item(i)
            arrResult(i, 2) = objResult.SenderEmailAddress
            arrResult(i, 3) = objResult.Subject
            If objResult.HasAttachments Then
                For j = 1 To objResult.Attachments.Count
                    If j = objResult.Attachments.Count Then
                        strAttFileNames = strAttFileNames & objResult.Attachments.Item(j)
                    Else: strAttFileNames = strAttFileNames & objResult.Attachments.Item(j) & ", "
                    End If
                Next
                arrResult(i, 4) = strAttFileNames
            End If
            arrResult(i, 5) = objResult.EntryId
            arrResult(i, 6) = objResult.ReceivedTime
        Next
        objSh.Range("A5").Resize(UBound(arrResult), UBound(arrResult, 2)).Value2 = arrResult
        lngLastRow = objSh.Cells(lngRowsCount, 1).End(xlUp).Row
        Dim objCell As Excel.Range, objLinkRange As Excel.Range
        Set objLinkRange = objSh.Range("E5:E" & CStr(lngLastRow))
        For Each objCell In objLinkRange
            objSh.Hyperlinks.Add objCell, "", objCell.Value2, "Open Mail", "Click here to open mail from Outlook"
        Next
    End Sub
    
    Public Function GetMailsByDate(ByVal OlApp As Outlook.Application, ByVal Fld As Outlook.Folder, ByVal DateVal As Date) As Collection
        If OlApp Is Nothing Or Fld Is Nothing Then Exit Function
        Dim objMail As Outlook.MailItem
        Dim dteDate As Date, objPA As Outlook.PropertyAccessor
        Set objMail = OlApp.CreateItem(olMailItem)
        Set objPA = objMail.PropertyAccessor
        dteDate = objPA.LocalTimeToUTC(DateVal)
        objMail.Close olDiscard
        Dim strFilter As String
        strFilter = "@SQL=" & Quote("urn:schemas:httpmail:datereceived") & " >= " & SingleQuote(dteDate)
        Const PR_SENDER_EMAIL_ADDRESS As String = "http://schemas.microsoft.com/mapi/proptag/0x0C1F001F"
        Const PR_SUBJECT As String = "http://schemas.microsoft.com/mapi/proptag/0x0037001F"
        Const PR_HASATTACH As String = "http://schemas.microsoft.com/mapi/proptag/0x0E1B000B"
        Dim objTbl As Outlook.Table, colResults As Collection, objResult As MailInfo, objRow As Outlook.Row, colAttachmentNames As Collection, objAtt As Outlook.Attachment
        Set colResults = New Collection
        Set objTbl = Fld.GetTable(strFilter)
        With objTbl
            .Columns.RemoveAll
            .Columns.Add "EntryId"
            .Columns.Add PR_SENDER_EMAIL_ADDRESS
            .Columns.Add PR_SUBJECT
            .Columns.Add PR_HASATTACH
            .Columns.Add "urn:schemas:httpmail:datereceived"
            Do Until .EndOfTable
                Set objResult = New MailInfo
                Set objRow = .GetNextRow
                With objResult
                    .HasAttachments = objRow(4)
                    .Subject = objRow(3)
                    .SenderEmailAddress = objRow(2)
                    .ReceivedTime = objRow.UTCToLocalTime(5)
                    Set colAttachmentNames = New Collection
                    Set objMail = OlApp.Session.GetItemFromID(objRow(1))
                    For Each objAtt In objMail.Attachments
                        colAttachmentNames.Add objAtt.Filename
                    Next
                    .EntryId = objRow(1)
                    Set .Attachments = colAttachmentNames
                    objMail.Close olDiscard
                End With
                colResults.Add objResult
            Loop
        End With
        Set GetMailsByDate = colResults
    End Function
    
    Private Function Quote(ByVal Text As String) As String
        Quote = Chr(34) & Text & Chr(34)
    End Function
    
    Private Function SingleQuote(ByVal Text As String) As String
        SingleQuote = Chr(39) & Text & Chr(39)
    End Function

    MailInfo.cls:

    Mã:
    Option Explicit
    
    Private m_SenderEmailAddress As String
    Private m_Subject As String
    Private m_EntryId As String
    Private m_HasAttachments As Boolean
    Private m_Attachments As New Collection
    Private m_ReceivedTime As Date
    
    Public Property Get SenderEmailAddress() As String
        SenderEmailAddress = m_SenderEmailAddress
    End Property
    
    Public Property Let SenderEmailAddress(Value As String)
        m_SenderEmailAddress = Value
    End Property
    
    Public Property Get Subject() As String
        Subject = m_Subject
    End Property
    
    Public Property Let Subject(Value As String)
        m_Subject = Value
    End Property
    
    Public Property Get EntryId() As String
        EntryId = m_EntryId
    End Property
    
    Public Property Let EntryId(Value As String)
        m_EntryId = Value
    End Property
    
    Public Property Get HasAttachments() As Boolean
        HasAttachments = m_HasAttachments
    End Property
    
    Public Property Let HasAttachments(Value As Boolean)
        m_HasAttachments = Value
    End Property
    
    Public Property Get Attachments() As Collection
        Set Attachments = m_Attachments
    End Property
    
    Public Property Set Attachments(Value As Collection)
        Set m_Attachments = Value
    End Property
    
    Public Property Get ReceivedTime() As Date
        ReceivedTime = m_ReceivedTime
    End Property
    
    Public Property Let ReceivedTime(Value As Date)
        m_ReceivedTime = Value
    End Property
    cảm ơn bác nhiều lắm ạ . Bác ơi , file này chuẩn luôn theo yêu cầu nhưng nó không quét hết tất cả mail đến trong ngày ạ .
     
    Lần chỉnh sửa cuối:
    Upvote 0
    cảm ơn bác nhiều lắm ạ . Bác ơi , file này chuẩn luôn theo yêu cầu nhưng nó không quét hết tất cả mail đến trong ngày ạ .
    Bạn kiểm tra lại kết quả xem sao nhé.
    Lưu ý chỗ này, máy tính của mình đang để định dạng là MM/dd/yyyy, còn kết quả hiển thị trong bảng tính là dd/MM/yyyy.

    1746177542354.png

    Chuỗi lọc cũng chỉ có một điều kiện, @SQL="urn:schemas:httpmail:datereceived" >= 'ngày, giờ', tức là lọc ra những thư đã nhận được sau 'ngày, giờ'.
     
    Upvote 0
    Bạn kiểm tra lại kết quả xem sao nhé.
    Lưu ý chỗ này, máy tính của mình đang để định dạng là MM/dd/yyyy, còn kết quả hiển thị trong bảng tính là dd/MM/yyyy.

    View attachment 308043

    Chuỗi lọc cũng chỉ có một điều kiện, @SQL="urn:schemas:httpmail:datereceived" >= 'ngày, giờ', tức là lọc ra những thư đã nhận được sau 'ngày, giờ'.
    1746177886788.png
    em đã định dạng theo lời bác mà nó chỉ record được bằng này cái mail trên tổng số 30 cái mail ạ
     
    Upvote 0
    Upvote 0
    Em đã thử rồi ạ , mà nó chỉ hiện ra 1 số email thôi . Vẫn bị sót nhiều lắm bác ạ . Bác view lại file được không ạ
     
    Upvote 0
    Em đã thử rồi ạ , mà nó chỉ hiện ra 1 số email thôi . Vẫn bị sót nhiều lắm bác ạ . Bác view lại file được không ạ
    Bạn có đang dùng Exchange không? Nếu có thì thử kiểm tra cài đặt Offline Settings, bật chế độ Cached Exchange Mode để Outlook tải xuống tất cả các thư từ máy chủ Exchange. Có lẽ VBA chỉ có thể quét được những thư đã được tải về máy tính.

    1746180258367.png
     
    Upvote 0
    Cập nhật code bài viết #2: Bổ sung điều kiện, chỉ lấy thông tin về tập tin đính kèm từ những thư có chứa tập tin đính kèm (PR_HASATTACH = 1), giúp tăng tốc độ xử lý kết quả.
     
    Upvote 0
    Upvote 0
    Bác ơi , giờ em mơi phát hiện ra , nó chỉ quét trong inbox thôi . Còn trong subfolder thì nó không quét nên mới có tình trạng này. Việc này xử lý thế nào bác.
    Nếu vậy thì rắc rối đấy, phương thức Folder.GetTable thực chất là dạng gói gọn hai phương thức IMAPIContainer::GetContentsTable và macro HrQueryAllRows của Outlook MAPI, chỉ áp dụng cho thư mục được chọn thôi và không áp dụng cho các thư mục con. Nếu vậy thì cần lấy danh sách các thư mục con nằm trong thư mục mẹ rồi gọi phương thức Folder.GetTable cho từng thư mục, mất thời gian hơn nhiều. Nếu đưa hết thư ra thư mục mẹ thì tốt quá, truy vấn SQL đúng một lần là xong, nhanh gọn lẹ.
     
    Upvote 0
    Web KT

    Bài viết mới nhất

    Back
    Top Bottom