Outlook COM Mark email as read

Ask your PowerShell-related questions, including questions on cmdlet development!
Forum rules
Do not post any licensing information in this forum.

Any code longer than three lines should be added as code using the 'Select Code' dropdown menu or attached as a file.
jvierra
Posts: 14951
Last visit: Sun Oct 17, 2021 11:52 am
Answers: 14
Has voted: 3 times
Been upvoted: 11 times

Re: Outlook COM Mark email as read

Post by jvierra »

Nothing wrong, that is how COM objects work always. Note that a COM you create is just a proxy for another actives process. If you interact and change things the proxied process will do dynamic adjustments to teh changes. This is true for all COM activations.

Most people who are not trained in computer science or software engineering wouldn't know this without studying COM. Note that most do not even know what "COM" stands for which would give a clue as to what it is and that it is different than linear procedural coding.

Note that you ahve two issues of dynamically changing things. One is WinForms and the other is teh remote COM activated process. Both are independent of each other so there wil always be the appearance of a mismatch. Normal linear explanations will almost always get you into deeper trouble.

User avatar
localpct
Posts: 353
Last visit: Mon Sep 27, 2021 5:48 am

Re: Outlook COM Mark email as read

Post by localpct »

appreciate the fwp and you're willingness to walk through this with me

looks like this is my key
for($item = $inbox.Items.Count; $item -ge 1; $item--) {
$inbox.Items.Item($item).Delete()
}

really do appreciate you.

jvierra
Posts: 14951
Last visit: Sun Oct 17, 2021 11:52 am
Answers: 14
Has voted: 3 times
Been upvoted: 11 times

Re: Outlook COM Mark email as read

Post by jvierra »

"Delete" and "unread" change the members of the collection. As I noted this will cause members to be skipped as the collection pointer (index) is changing. Since the beginning of COM we have always retrieved the index of the collection then just loop through the indexes and change teh object. The index has to be an identifier for teh collection members that never changes. IN Outlook that is the mail item ID. No other property can be assumed to be non-changing when a collection member is changed. Use the MailItemId to alter the target mail items.

In Outlook whenever you ask for a "Find" or "Search" the object returned is a dynamic collection. That means that the members will change as you alter the members. This is an old thing that has been known for over two decades.

User avatar
localpct
Posts: 353
Last visit: Mon Sep 27, 2021 5:48 am

Re: Outlook COM Mark email as read

Post by localpct »

mail item id, is that entry ID?

found some links that pointed me here
https://docs.microsoft.com/en-us/office ... tem.Delete

Helps with more of the puzzle

User avatar
localpct
Posts: 353
Last visit: Mon Sep 27, 2021 5:48 am

Re: Outlook COM Mark email as read

Post by localpct »

Final answer:
  1. $outlook = New-Object -ComObject Outlook.Application
  2. $namespace = $outlook.GetNameSpace("MAPI")
  3. $inbox = $namespace.Folders.Item('some@email.com').Folders.Item('REPORTS')
  4.  
  5. for ($item = $inbox.Items.Count; $item -ge 1; $item--) {
  6.     $($inbox.items.item($item).attachments).SaveAsFile((Join-Path 'C:\Data' $($inbox.items.item($item).attachments).filename ))
  7.     $inbox.Items.Item($item).Delete()
  8. }
  9.  
  10. $outlook.Quit()
I had to learn it doesn't always move as fast as you would expect it. With a cool off of about 1.5 mins from the time I executed the script, deleted the new CSV files and move the items back into the Reports folder to re test and now the results are consistent. I am running the script on a core connected VM using VSCode and have outlook open on my VPN connected laptop. Curious the results in the office on Monday.

jvierra
Posts: 14951
Last visit: Sun Oct 17, 2021 11:52 am
Answers: 14
Has voted: 3 times
Been upvoted: 11 times

Re: Outlook COM Mark email as read

Post by jvierra »

Yes, you can reverse enumerate any stable collection. "Folder" items are stable collections and remain stable under reveres enumeration and change.

User avatar
localpct
Posts: 353
Last visit: Mon Sep 27, 2021 5:48 am

Re: Outlook COM Mark email as read

Post by localpct »

Now that I’ve figured this out

Do you have any enhancements or different ways your script would look?

jvierra
Posts: 14951
Last visit: Sun Oct 17, 2021 11:52 am
Answers: 14
Has voted: 3 times
Been upvoted: 11 times

Re: Outlook COM Mark email as read

Post by jvierra »

As there are many approaches to doing this as long as you don't try to enumerate teh collection in a loop. Using the index of the collection in reverse will avoid the issue.

I would use more direct methods that don't create new refernces to teh COM objects. Every time you store a reference then it increments teh reference count on the objects so Outlook COM will stay in memory and will seem to be stuck.

Here is how I would do it using your reverse indexing, I also hint that better nomenclature is best if others will need to look at the code. Also, a simple index can be stated as "i". "$item" is not an index reference it implies an object. Use $ix, $I, $idx or something simple to let others see that it is a simple indexing operation which is what we use "for" loops for in most cases.

Learning how COM objects work, are activated and refenced is best but if this is a one shot deal then you have enough now.
  1. Try{
  2.    
  3.     $outlook = New-Object -ComObject Outlook.Application
  4.     $mapi = $outlook.GetNameSpace('MAPI')
  5.     $reports = $mapi.Stores['someone@somewhere.com'].Session.Folders[1].Folders['Reports']
  6.    
  7.     for ($i = $reports.Items.Count; $i -ge 1; $i--){
  8.         $filepath = Join-Path 'C:\Data' $reports.items.item($i).attachments.filename
  9.         # WARNING - if more than one attachment this will fail.
  10.         $reports.items.item[$i].attachments.SaveAsFile($filepath)
  11.         $reports.Items.Item[$i].Delete()
  12.     }
  13. }
  14. Catch{
  15.     Throw $_
  16. }
  17. $outlook.Quit()
  18. # release COM objects here. (mapi, reports, outlook) - release all references.
This is mostly tested as I ran it through Outlook against an Exchange Online instance. Full testing is beyond the scope of this and almost all forums.

User avatar
localpct
Posts: 353
Last visit: Mon Sep 27, 2021 5:48 am

Re: Outlook COM Mark email as read

Post by localpct »

easy to read
easy to implement
thanks so much for this.

jvierra
Posts: 14951
Last visit: Sun Oct 17, 2021 11:52 am
Answers: 14
Has voted: 3 times
Been upvoted: 11 times

Re: Outlook COM Mark email as read

Post by jvierra »

Here is something that you might find helpful:

https://github.com/PoshCode/PowerShellPracticeAndStyle

Locked