Code: Select all
/*
;Last tested 20220917134118 with AHK v 1.1.34.03
Notes/Tutorial in iterating/enumerating through
Microsoft Outlook Items Collection Com Object
https://learn.microsoft.com/en-us/office/vba/api/outlook.items
Provided because looping (especially when the contents of
the collection change) can be incomplete or run out of bounds
frequently, and the syntax required to avoid that can be
somewhat counterintuitive.
*/
/* THREE CASES OF ITERATION/ENUMERATION
1 - BEGNIGN LOOPS: loops where items are not
directly removed from the collection and any
modification to items does not disqualify the item
from being in the collection.
2 - DIRECT REMOVAL LOOPS: Loops where items are
directly removed.
3 - DISQUALIFICATION LOOPS: Loops where items are
modified within the loop such that they would no longer
qualify to be included in the collection as originally
constructed.
CONCLUSIONS:
Items collection has index lower bound of 1, not 0.
(E.g., items[1].)
Items collection has index upper bound of count.
(E.g., items[items.count].)
While loops re-evaluate the count() expression
prior to the opening brace each time around the loop.
The remove[index] method of the items collection keeps
the count expression before the while loop braces (e.g.,
while items.count > 0 {) in sync with removals in the body
of the loop. But the index needs to either be the first
item (e.g., items.remove[1]) or the the last item
(e.g., items.remove[items.count]).
The remove[index] method not only removes the item from
a collection, but also destroys the item from the store such
that it cannot be used. It is also only callable with the
index. Hence, it is not very useful.
The items[index].delete method is superior because it
moves the item to the deleted items folder. But it doesn't
keep the count expression before the while loop in sync with
deletions in the body of the loop. As a result, the while
expression before the braces in the loop needs to account for
this.
Using a counter variable > 0 as the while loop expression
and decrementing the counter after calling delete
(e.g., item[items.count].delete, counter--) works without
error.
You can avoid having to use a counter variable by simply
having the count expression decrement itself on each time
around the loop (e.g., while items,count-1 > 0 {).
*/
/*
CONFIRMED SUCCESSES BY CASE
CASE 1 - Benign Loops
Case 1.1 - Looping forwards:
for i, in is{
msgbox % "a_index: " . a_index
i.display
}
while i:=is.getNext {
msgbox % "a_index: " . a_index
i.display
}
loop, % is.count {
msgbox % "a_index: " . a_index
is.item[a_index].display
}
Case 1.2 - Looping backwards:
loop, % is.count {
msgbox % "a_index: " . a_index
is.item[(is.count+1)-a_index].display
}
CASE 2 - Direct Removal Loops
Case 2.1 - Removing each item (not leaving any)
2.1.1 - Looping forward
while is.count-1 > 0 {
msgbox % "a_index: " . a_index
msgbox % is.count
is.item[is.count].delete
}
counter := is.count
while counter > 0 {
msgbox % "a_index: " . a_index
msgbox % counter
is.item[is.count].delete
msgbox % counter--
}
2.1.2 - Looping backwards
loop, % i:=is.count {
msgbox % "a_index: " . a_index
msgbox % i
is.item[i--].delete
}
while is.count > 1 { ; note the "1" is not "0"
msgbox % "a_index: " . a_index
msgbox % is.count
is.item[is.count].delete
}
while is.count > 0 { ; note the "0"
msgbox % "a_index: " . a_index
msgbox % is.count
is.getLast.delete
}
Case 2.2 - Selectively removing items (leaving some,
potentially)
Case 2.1.2 - Looping forwards (see Case 3.1 below)
Case 2.2.2 - Looping backwards
; uses i as static number of count
; uses p as position counter, relative to a_index
loop, % i:=is.count {
msgbox % "a_index: " . a_index
msgbox % p := i+1 - a_index
if a_index in 1,3,5
is.item[p].delete
}
CASE 3 - Disqualification Loops
Case 3.1 - Looping forward
; push qualifying items into an array in forwards loop
; then modify the items popped from the array in forwards loop
s := "@SQL=""urn:schemas:httpmail:subject"" = 'in'"
rs := is.restrict("@SQL=""urn:schemas:httpmail:subject"" = 'in'")
a := []
for k, in rs
a.push(k)
loop, % a.count() {
i := a.pop()
if (i.subject == "in")
i.subject := "out"
i.save
}
Case 3.2 - Looping backwards
; push qualifying items into an array in a backwards loop
; then modify the items popped from the array in a forwards loop
s := "@SQL=""urn:schemas:httpmail:subject"" = 'in'"
rs := is.restrict("@SQL=""urn:schemas:httpmail:subject"" = 'in'")
a := []
loop, % i:=is.count
a.push(is.item[i--])
loop, % a.count() {
i := a.pop()
if (i.subject == "in")
i.subject := "out"
i.save
}
*/
/* TESTING ASSUMPTIONS (used in every test of code block that follows)
; f = F*older
f := [SCRIPT TO SET THIS TO AN OUTLOOK FOLDER OBJECT]
;https://learn.microsoft.com/en-us/office/vba/api/outlook.folder
; is = I*temS*
is := f.items
loop, 5
; i = I*tem
i:=is.add(6),i.subject:="in",i:=i.save,i:=""
*/
; TEST RESULTS:
; below works without error
/*
while is.count()-1 > 0 {
msgbox % "a_index: " . a_index
msgbox % is.count()
is.item[iscount()].delete
}
*/
; below works without error
/*
counter := is.count()
while counter > 0 {
msgbox % "a_index: " . a_index
msgbox % counter
is.item[is.count()].delete
msgbox % counter--
}
*/
; below unsuccessful
; deletes two at a time
; returns array out of bounds error at end
/*
while is.count() > 0 {
msgbox % "a_index: " . a_index
msgbox % iscount()
is.item[is.count()].delete
is.remove[is.count()]
msgbox % is.count()
}
*/
;below returns array index out of bounds
;is.item[0].display
; below successful without error
; but unsure where the removed item actually goes
/*
while is.count() > 0 {
msgbox % "a_index: " . a_index
msgbox % is.count()
is.remove[is.count()]
msgbox % is.count()
}
*/
; below successful without error
; but unsure where the removed item actually goes
/*
while iscount() > 0 {
msgbox % "a_index: " . a_index
msgbox % is.count()
is.remove[1]
msgbox % is.count()
}
*/
;below throws operation failed error 0x80041200
/*
while is.count() > 0 {
msgbox % "a_index: " . a_index
msgbox % is.count()
is.item[1].delete ; failed here on a_index 8, is.count() 9
msgbox % is.count()
}
*/
;below works fine and returns the last item in the collection
;MsgBox % isitem[iscount()].LastModificationTime
;below silently fails: works down to 5 iteis
/*
while (is.count() > 0){
msgbox % "a_index: " . a_index
msgbox % is.count
is.remove(a_index)
msgbox % is.count
}
*/
;below silently fails: works down to 5 iteis
/*
issort("LastModificationTime",1)
for k, in ms{
msgbox % "a_index: " . a_index
msgbox % is.count
is.remove(a_index)
msgbox % is.count
}
*/