Home > Back-end >  How to Solve "Out of Memory" in Outlook VBA?
How to Solve "Out of Memory" in Outlook VBA?

Time:02-03

I write a VBA project to process the order emails in a folder. However, when running the code, I will always get "Out of Memory" error.

I search online and see many suggests to clear the memory, so I add codes like below:

        Set objItem = Nothing
        Set objMailItem = Nothing
        Redim arrLines(0)

But that seems not working.

Below is the VBA codes:

Option Explicit

Private Sub btnStart_Click()
  Dim StartDate As Date
  Dim EndDate As Date
  
  StartDate = DateValue("October 1, 2015")
  EndDate = DateValue("January 28, 2023")
  
  Call ProcessOrderEmails(StartDate, EndDate)
End Sub

Sub ProcessOrderEmails(StartDate As Date, EndDate As Date)
    Dim objCurFolder As Folder
    Dim objItem As Object
    Dim objMailItem As MailItem
    Dim nCSVFileNum As Integer
      
    '   Create the CSV file
    nCSVFileNum = FreeFile

    If Dir("E:\Temp\OrderStat.csv") <> "" Then
      Kill ("E:\Temp\OrderStat.csv")
    End If
    
    Open "E:\Temp\OrderStat.csv" For Output Lock Write As #nCSVFileNum
    
    '   Get statistics
    Set objCurFolder = Application.ActiveExplorer.CurrentFolder
    
    For Each objItem In objCurFolder.Items
        If TypeOf objItem Is MailItem Then
            Set objMailItem = objItem
            
            ' Check if the mail is in the date range
            If (objMailItem.SentOn >= StartDate) And (objMailItem.SentOn <= EndDate) Then
              Select Case objMailItem.SenderEmailAddress
                Case "[email protected]"
                  Print #nCSVFileNum, ProcessRegNowOrderEmail(objMailItem)
              End Select
            End If
        End If
        
        ' Set objItem to nothing to free memory
        Set objItem = Nothing
        Set objMailItem = Nothing
    Next
    
    '   Close the file
    Close nCSVFileNum
End Sub

Private Function ReplaceNewLine(strText As String, strNewLine As String) As String
  ReplaceNewLine = Replace(strText, vbCrLf, strNewLine)
  ReplaceNewLine = Replace(ReplaceNewLine, vbCr, strNewLine)
  ReplaceNewLine = Replace(ReplaceNewLine, vbLf, strNewLine)
End Function

Private Function SplitLines(strText As String) As Variant
  SplitLines = Split(ReplaceNewLine(strText, vbNewLine), vbNewLine)
End Function

' strEntryName should include :, like this RegNow OrderItemID:
Private Function GetEntryValue(strEntryLine As String, strEntryName As String, ByRef strEntryValue) As Boolean
  Dim strLine As String
  
  ' Initialize result to False by default
  GetEntryValue = False
  
  ' Parse the line
  strLine = ReplaceNewLine(Trim(strEntryLine), "")
  
  If InStr(1, strLine, strEntryName, vbTextCompare) > 0 Then
    strEntryValue = Trim(Replace(strLine, strEntryName, "", 1, -1, vbTextCompare))
    GetEntryValue = True
  End If
End Function

Private Function ProcessRegNowOrderEmail(objMailItem As MailItem) As String
  Dim arrLines() As String
  Dim strLine As String
  Dim strOrderID As String
  Dim strProduct As String
  Dim strProfit As String
  Dim I As Integer

  arrLines = SplitLines(objMailItem.Body)
  
  For I = LBound(arrLines, 1) To UBound(arrLines, 1)
    Call GetEntryValue(arrLines(I), "RegNow OrderItemID:", strOrderID)
    Call GetEntryValue(arrLines(I), "Product Name:", strProduct)
    Call GetEntryValue(arrLines(I), "Profit:", strProfit)
  Next I
  
  ProcessRegNowOrderEmail = "RegNow," & strOrderID & "," & strProduct & "," & strProfit
  
  ReDim arrLines(0)
End Function

Below is a sample email to be processed:

********** DO NOT REPLY TO THIS EMAIL **********

*** Transaction Identification ***
Date:               2017-03-14 02:14:14 (Pacific Standard Time) 
RegNow OrderID:     XXXXXX-XXXXX
RegNow OrderItemID: XXXXXX-XXXXX

*** Gift Information ***
Gift:               no
Pickup:             no

*** Product Information ***
Item #:             #####-#
Product Name:       My Product
Quantity:           1
Tax:                0.00 USD
Total:              199.95 USD
Profit:             189.15

Update:

The error is caused by the following line:

If InStr(1, strLine, strEntryName, vbTextCompare) > 0 Then

when strLine contains Japanese characters, as below:

Address2: パティオたまプラーザ308

After searching online, I find similar errors, as below: [VBA][excel] Occurred error When Using 'Japanese - Katakana' in 'inStr'

https://social.msdn.microsoft.com/Forums/en-US/06df9b54-ad75-4c18-9577-84e52b6e03a1/how-can-i-use-the-japanese-for-instr-vba-?forum=exceldev

But the OP said the problem will not reoccur so there are no solutions for the problem.

CodePudding user response:

Apparently memory allocated to objItem in a For Each cannot be freed.

Change to an indexed For Next so there is no objItem.

Sub ProcessOrderEmails(StartDate As Date, EndDate As Date)

    Dim objCurFolder As Folder
    Dim objMailItem As MailItem
    Dim nCSVFileNum As Integer
      
    '   Create the CSV file
    nCSVFileNum = FreeFile

    If dir("E:\Temp\OrderStat.csv") <> "" Then
      Kill ("E:\Temp\OrderStat.csv")
    End If
    
    Open "E:\Temp\OrderStat.csv" For Output Lock Write As #nCSVFileNum
    
    '   Get statistics
    Set objCurFolder = Application.ActiveExplorer.currentFolder
    
    Dim curFolderItems As Items
    Set curFolderItems = objCurFolder.Items
    
    Dim curFolderItemsCount As Long
    curFolderItemsCount = curFolderItems.count
    
    Dim i As Long
    
    For i = 1 To curFolderItemsCount
    
        If TypeOf curFolderItems(i) Is MailItem Then
        
            Set objMailItem = curFolderItems(i)
            
            With objMailItem
                
                ' Check if the mail is in the date range
                If (.SentOn >= StartDate) And (.SentOn <= EndDate) Then
                    
                    Select Case .senderEmailAddress
                        Case "[email protected]"
                            Print #nCSVFileNum, ProcessRegNowOrderEmail(objMailItem)
                        
                    End Select
                End If
            
            End With
            
            ' free memory
            Set objMailItem = Nothing
            
        End If
        
    Next
    
    '   Close the file
    Close nCSVFileNum
End Sub

Appears there is something else involved. With your original code using objItem I can generate a file with over 30,000 entries.

Unlikely this will be any better but rather than assigning objMailItem, you could use curFolderItems(i) directly.

Sub ProcessOrderEmails(StartDate As Date, EndDate As Date)

    Dim objCurFolder As Folder
    Dim nCSVFileNum As Integer
    
    Dim pathFile As String
    pathFile = "E:\Temp\OrderStat.csv"
        
    '   Create the CSV file
    nCSVFileNum = FreeFile
    Debug.Print nCSVFileNum
    
    If dir(pathFile) <> "" Then
        Kill pathFile
    End If
    
    Open pathFile For Output Lock Write As #nCSVFileNum
            
    '   Get statistics
    Set objCurFolder = Application.ActiveExplorer.currentFolder
    
    Dim curFolderItems As Items
    Set curFolderItems = objCurFolder.Items
    
    Dim curFolderItemsCount As Long
    curFolderItemsCount = curFolderItems.count
            
    Dim i As Long
    
    Dim j As Long
    
    ' for testing the limit
    'For j = 1 To Int(1000 / curFolderItemsCount)   1
    
        For i = 1 To curFolderItemsCount
        
            If TypeOf curFolderItems(i) Is MailItem Then
            
                Dim n As Long
                n = n   1
                Debug.Print n
                
                With curFolderItems(i)
                    
                    ' Check if the mail is in the date range
                    If (.SentOn >= StartDate) And (.SentOn <= EndDate) Then
                        
                        'Select Case .senderEmailAddress
                            'Case "[email protected]"
                                Print #nCSVFileNum, ProcessRegNowOrderEmail(curFolderItems(i))
                          
                        'End Select
                    End If
                    
                End With
            End If
            
        Next
        
    'Next
    
    '   Close the file
    Close nCSVFileNum
End Sub

CodePudding user response:

It is difficult to see how labels you are not interested in are processed.

This code will process specified labels only.

Option Explicit

Function ParseTextLinePair(strSource As String, strLabel As String)

    ' https://learn.microsoft.com/en-us/previous-versions/office/developer/office-2007/dd492012(v=office.12)

    Dim intLocLabel As Integer
    Dim intLocCRLF As Integer
    Dim intLenLabel As Integer
    Dim strText As String

    ' locate the label in the source text
    intLocLabel = InStr(strSource, strLabel)
    intLenLabel = Len(strLabel)
    
    If intLocLabel > 0 Then
        intLocCRLF = InStr(intLocLabel, strSource, vbCrLf)
        If intLocCRLF > 0 Then
            intLocLabel = intLocLabel   intLenLabel
            strText = Mid(strSource, intLocLabel, intLocCRLF - intLocLabel)
        Else
            intLocLabel = Mid(strSource, intLocLabel   intLenLabel)
        End If
    End If
    
    ParseTextLinePair = Trim(strText)
    
End Function

Private Function ProcessRegNowOrderEmail_Label(objMailItem As MailItem) As String
    
    Dim strOrderID As String
    Dim strProduct As String
    Dim strProfit As String
    
    strOrderID = ParseTextLinePair(objMailItem.body, "RegNow OrderItemID:")
    strProduct = ParseTextLinePair(objMailItem.body, "Product Name:")
    strProfit = ParseTextLinePair(objMailItem.body, "Profit:")

    ProcessRegNowOrderEmail_Label = "RegNow," & strOrderID & "," & strProduct & "," & strProfit

End Function

Replace

Print #nCSVFileNum, ProcessRegNowOrderEmail(objMailItem)

with

Print #nCSVFileNum, ProcessRegNowOrderEmail_Label(objMailItem)
  • Related