如何解决提取大邮箱多元数据时的HRESULT操作停止错误?
大型Outlook邮箱元数据提取PowerShell脚本HRESULT错误问题
问题现象
- 应用于小型邮箱(约9000封邮件)时可正常运行;
- 应用于大型邮箱(约25万封邮件)且仅提取单个元数据字段时也可正常运行;
- 应用于大型邮箱且提取多个元数据字段时,会输出以下HRESULT错误,仅捕获33146封邮件,生成不完整的表格:
Exception from HRESULT: 0xB5940115 At C:\file\path\path\name.ps1:15 char:18 + foreach ($item in ($folder |Select-Object -ExpandProperty "I ... + + CategoryInfo :OperationStopped: (:) [], COMException + FullyQualifiedErrorID: System.Runtime.InteropServices.COMException Exception from HRESULT: 0xBBC40115 At C:\file\path\path\name.ps1:13 char:14 + foreach ($folder in $ParentFolder.Folders) + + CategoryInfo :OperationStopped: (:) [], COMException + FullyQualifiedErrorID: System.Runtime.InteropServices.COMException Exception from HRESULT: 0xBFE40115 At C:\file\path\path\name.ps1:13 char:14 + foreach ($folder in $ParentFolder.Folders) + + CategoryInfo :OperationStopped: (:) [], COMException + FullyQualifiedErrorID: System.Runtime.InteropServices.COMException
已尝试的解决方法
- 搜索错误码,未找到相关结果;
- 搜索FullyQualifiedErrorID,未找到相关结果;
- 使用DataTable、提取EntryID结合单字段、按1000封分批处理、静默跳过错误,均未解决;
- 最新尝试按文件夹大小处理(小于1000封直接导出,大于1000封分批导出),但无CSV输出,不确定原因。
初始完整脚本
function Get-MailFromOutlookFolder { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [Object] $ParentFolder ) $items = @() foreach ($folder in $ParentFolder.Folders) { foreach ($item in ($folder | Select-Object -ExpandProperty "Items")) { if ($item.Class -eq 43) { # process email $PR_SMTP_ADDRESS = "http://schemas.microsoft.com/mapi/proptag/0x39FE001E" #$smtpAddress = $item.PropertyAccessor.GetProperty($PR_SMTP_ADDRESS) $items += $item | Select-Object -Property "ConversationTopic", "ReceivedTime", "SenderName", "ReceivedByName", "BCC", "CC","Size", "SenderEmailAddress", @{ "Label" = "Folder"; "Expression" = { $_.Parent.Name } }, @{name="Attachments";expression={$_.Attachments|%{$_.DisplayName}}} } } # process (sub)folder items $items += Get-MailFromOutlookFolder -ParentFolder $folder } return $items } $outlook = New-Object -Com "Outlook.Application" $mapi = $outlook.GetNamespace("MAPI") $recipient = $mapi.CreateRecipient("name@domain.ca") $folder = $mapi.GetSharedDefaultFolder($recipient, [Microsoft.Office.Interop.Outlook.OlDefaultFolders]::olFolderInbox).Parent $folder1 = $folder.parent.folders('Online Archive - name@domain.ca') $results = Get-MailFromOutlookFolder -ParentFolder $folder1 $results | Export-Csv -Path "C:\Fakefolder\email_account_name_2025-06-20.csv"
更新尝试的脚本
$table = New-Object System.Data.DataTable $table.Columns.Add("EntryID", [System.String]) #$table.Columns.Add("ReceivedTime", [System.DateTime]) $table.Columns.Add("AttachmentNames", [System.String]) #$table.Columns.Add("Body", [System.String]) function Get-MailFromOutlookFolder { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [Object] $ParentFolder ) foreach ($folder in $ParentFolder.Folders) { foreach ($item in ($folder | Select-Object -ExpandProperty "Items")) { if ($item.Class -eq 43) { $attachments = @() foreach ($att in $item.Attachments) { $attachments += $att.FileName } # Clean and truncate Body field #$body = $mail.Body -replace "(`r`n|`n|`r)", " " #$maxLength = 32000 #if ($body.Length -gt $maxLength) { # $body = $body.Substring(0, $maxLength) #} $row = $table.NewRow() $row["EntryID"] = $item.EntryID $row["AttachmentNames"] = ($attachments -join "; ") #$row["Body"] = $item.EntryID #if ($mail.ReceivedTime) { # $row["ReceivedTime"] = $mail.ReceivedTime # } else { # $row["ReceivedTime"] = [DBNull]::Value # } $table.Rows.Add($row) } } } $outlook = New-Object -Com "Outlook.Application" $mapi = $outlook.GetNamespace("MAPI") $recipient = $mapi.CreateRecipient("fake@fake.ca") $folder = $mapi.GetSharedDefaultFolder($recipient, [Microsoft.Office.Interop.Outlook.OlDefaultFolders]::olFolderInbox).Parent $folder1 = $folder.parent.folders('Online Archive - fake@fake.ca') $results = Get-MailFromOutlookFolder -ParentFolder $folder1 $results | Export-Csv -Path "C:\fake\fake_2025-07-04.csv"
2025-07-21更新
制作了带计数器的脚本,每处理1000封就导出并清空DataTable,该方法有效但存在问题:邮件数少于1000的文件夹会被跳过,如8001封的文件夹会遗漏第8001封。
后续尝试按文件夹邮件数处理的脚本如下,但无CSV输出:
$ErrorActionPreference = 'SilentlyContinue' #Make table with appropriate columns $table = New-Object System.Data.DataTable $table.Columns.Add("EntryID", [System.String]) $table.Columns.Add("AttachmentNames", [System.String]) $table.Columns.Add("Body", [System.String]) $table.Columns.Add("ConversationTopic", [System.String]) #make counters and empty hashtable $counter = 0 $counter2 = 0 $myDictionary = @{} #Get a hashtable listing folder name as keys and total items as values function Get-MailFromOutlookFolder { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [Object] $ParentFolder ) foreach ($folder in $ParentFolder.Folders) { $counter = 0 foreach ($item in ($folder | Select-Object -ExpandProperty "Items")) { $counter++} $key = $folder.FolderPath $value = $counter $myDictionary[$key] = $value } } #Run Hashtable so that folders are processed in groups of 1000 function Traverse-NestedHashtable { param ( [Parameter(Mandatory=$true)] [Hashtable]$myDictionary, [Object] $ParentFolder ) foreach ($key in myDictionary) { #Process folders with less than 1000 items if ($counter -lt 1000) { $folder = $key foreach ($item in ($folder | Select-Object -ExpandProperty "Items")) { if ($item.Class -eq 43) { $attachments = @() foreach ($att in $item.Attachments) { $attachments += $att.FileName } # Clean and truncate Body field $body = $mail.Body -replace "(`r`n|`n|`r)", " " $maxLength = 32000 if ($body.Length -gt $maxLength) { $body = $body.Substring(0, $maxLength) } $row = $table.NewRow() $row["EntryID"] = $item.EntryID $row["AttachmentNames"] = ($attachments -join "; ") $row["Body"] = $item.Body $row["ConversationTopic"] = $item.ConversationTopic $table.Rows.Add($row) $folderSafe = $folder.FolderPath -replace '[\\/:*?"<>|]', '_' $counterString = $counter.ToString() $filePath = "C:\foo\" + $folderSafe + "_" + $counterString + ".csv" $table | Export-Csv -Path "$filePath" -notypeinformation $table.Clear()}}} #Process folders with more than 1000 items if ($counter -gt 1000) { $folder = $key foreach ($item in ($folder | Select-Object -ExpandProperty "Items")) { $counter2++ if ($item.Class -eq 43) { $attachments = @() foreach ($att in $item.Attachments) { $attachments += $att.FileName } # Clean and truncate Body field $body = $mail.Body -replace "(`r`n|`n|`r)", " " $maxLength = 32000 if ($body.Length -gt $maxLength) { $body = $body.Substring(0, $maxLength) } $row = $table.NewRow() $row["EntryID"] = $item.EntryID $row["AttachmentNames"] = ($attachments -join "; ") $row["Body"] = $item.Body $row["ConversationTopic"] = $item.ConversationTopic $table.Rows.Add($row) $folderSafe = $folder.FolderPath -replace '[\\/:*?"<>|]', '_' #When number of items is 1000 exactly, send the collected items in the datatable to spreadsheet if ($counter2 % 1000 -eq 0) { $counterString = $counter.ToString() $filePath = "C:\foo\" + $folderSafe + "_" + $counterString + ".csv" $table | Export-Csv -Path "$filePath" -notypeinformation $table.Clear()} #When number of items is the number in the folder exactly, send the collected items in the datatable to spreadsheet if (($counter2 - $counter) % $counter -eq 0) { $counterString = $counter.ToString() $filePath = "C:\foo\" + $folderSafe + "_" + $counterString + ".csv" $table | Export-Csv -Path "$filePath" -notypeinformation $table.Clear()} }}}}} $outlook = New-Object -Com "Outlook.Application" $mapi = $outlook.GetNamespace("MAPI") $recipient = $mapi.CreateRecipient("foo@foo.ca") $folder = $mapi.GetSharedDefaultFolder($recipient, [Microsoft.Office.Interop.Outlook.OlDefaultFolders]::olFolderInbox).Parent $folder1 = $folder.parent.folders('Online Archive - foo@foo.ca') Get-MailFromOutlookFolder -ParentFolder $folder1 Traverse-NestedHashtable -myDictionary -ParentFolder $folder1
内容的提问来源于stack exchange,提问作者oymonk
相关产品推荐
相关产品推荐

