How to keep users from having two "Inbox" Explorers open at the same time?

  • Thread starter Thread starter John Riddle
  • Start date Start date
J

John Riddle

Hello,

I have a set of mail journaling macros in the "ThisOutlookSession" that ask
the user if they'd like to journal an item once the selection changes to a
new item. It works by Setting myExp = Application.ActiveExplorer at the
Application_Startup event. Then whenever the CurrentFolder is "Inbox", if
the selection changes it asks you if you'd like to journal the previous
item. All works well except that if the user has two explorers open, none of
the selection change events work. Therefore, the users might open a new
window as a contacts window and then switches it to the inbox and starts
reading emails, none of the events occur.

I've tried using the NewExplorer event to Set any new explorers as myExp,
but can't figure out a way to toggle setting myExp to whichever explorer is
the ActiveExplorer. There seems to be no reliable way to keep myExp set to
whichever Explorer is the Active Explorer. Can anyone give me suggestions?

Thanks,

John
 
Hi John,

you need to keep the reference for each Explorer in an instance of a
class modul.
 
Michael,

Thank you very much for responding! I've never used a class module before.
Could you give me an example or more detail on how to accomplish this with a
class module?

Thanks!

John
 
Hi John,

I was sure, I can find an example if you would ask for it, but I can´t.
My tough, so I had to write it myself.

I hope nothing important is missing...

Please copy the first part in "ThisOutlookSession, the second in a class
modul, in this example called "cExplorer".

<ThisOutlookSession>
Option Explicit
Private WithEvents m_olExplorers As Outlook.Explorers
Private m_colExplorers As VBA.Collection
Private m_lNextKey As Long

Private Sub Application_Startup()
On Error Resume Next
Set m_colExplorers = New VBA.Collection
Set m_olExplorers = Application.Explorers
m_olExplorers_NewExplorer Application.ActiveExplorer
End Sub

Public Sub CloseExplorer(ByVal lKey As Long)
m_colExplorers.Remove CStr(lKey)
End Sub

Private Sub m_olExplorers_NewExplorer(ByVal Explorer As
Outlook.Explorer)
Dim oExplorer As cExplorer

Set oExplorer = New cExplorer
oExplorer.Init ObjPtr(Me), m_lNextKey, Explorer
m_colExplorers.Add oExplorer, CStr(m_lNextKey)
m_lNextKey = m_lNextKey + 1
End Sub
</ThisOutlookSession>

<cExplorer.cls>
Option Explicit
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _
(ByRef Destination As Any, ByRef Source As Long, ByVal Length As
Long)
Private m_lParentPtr As Long
Private m_lKey As Long
Private WithEvents m_olExplorer As Outlook.Explorer

Private Function GetObjByPtr(ByVal lPtr As Long) As Object
On Error GoTo CLEANUP
Dim obj As Object
If lPtr = 0 Then Exit Function
CopyMemory obj, lPtr, 4
Set GetObjByPtr = obj
CLEANUP:
CopyMemory obj, 0&, 4
End Function

Public Sub Init(ByVal lParentPtr As Long, _
ByVal lKey As Long, _
olExplorer As Outlook.Explorer _
)
m_lParentPtr = lParentPtr
m_lKey = lKey
Set m_olExplorer = olExplorer
End Sub

Private Sub m_olExplorer_Close()
' If you use "ThisOutlookSession" as parent then you can declare _
it here (or any other object of course)
' dim oParent as ThisOutlookSession
' set oParent=GetObjByPtr(m_lParentPtr)
' oParent.CloseExplorer m_lKey
GetObjByPtr(m_lParentPtr).CloseExplorer m_lKey
End Sub

Private Sub m_olExplorer_SelectionChange()
' here your code goes
End Sub
</cExplorer.cls>
 
Michael,

THANK YOU VERY MUCH! This works like a charm. Although, I don't entirely
understand how it works. Is there a way that you could comment it for me so
that I can understand for the future and (more importantly) apply these
techniques to future issues as they arise?

Thank You,

John
 
Michael,

There seems to be only one bug to my code now. When I have two explorers
open. Whichever one was the first one that I had a selection change with
(i.e. Open one explorer to Inbox, move the selection from one item to
another, it asks if I'd like to journal the one that I was just working
with. I say "yes" (or "no") and everything is fine.). Later, if I open
another explorer (i.e. right-click the contacts folder and select "Open in
new window") then get a new mail notification, if I don't switch back to the
first Explorer to go to the Inbox, but instead simply select the Inbox from
my "Contacts" explorer, all the events work as they should, asks me to
journal and then journals the item, but when finished it activates Explorer
#1 (which may now be viewing my Tasks folder) instead of continuing to let
me view my inbox in the second explorer. I've pasted my code below that is
in my class module. Do you see anything wrong?

Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (ByRef
Destination As Any, ByRef Source As Long, ByVal Length As Long)
Private m_lParentPtr As Long
Private WithEvents m_olExplorer As Outlook.Explorer
Private m_lKey As Long
Private LastSelected As MailItem

Private Function GetObjByPtr(ByVal lPtr As Long) As Object
On Error GoTo CLEANUP
Dim obj As Object
If lPtr = 0 Then Exit Function
CopyMemory obj, lPtr, 4
Set GetObjByPtr = obj
CLEANUP:
CopyMemory obj, 0&, 4
End Function

Public Sub Init(ByVal lParentPtr As Long, ByVal lKey As Long, olExplorer As
Outlook.Explorer)
m_lParentPtr = lParentPtr
m_lKey = lKey
Set m_olExplorer = olExplorer
End Sub

Private Sub m_olExplorer_Close()
' If you use "ThisOutlookSession" as parent then you can declare it here (or
any other object of course)
'Dim oParent As ThisOutlookSession
'Set oParent = GetObjByPtr(m_lParentPtr)
'oParent.CloseExplorer m_lKey
GetObjByPtr(m_lParentPtr).CloseExplorer m_lKey
End Sub

Private Sub m_olExplorer_SelectionChange()
If m_olExplorer.CurrentFolder.Name = "Inbox" Then
Select Case m_olExplorer.CurrentFolder.Parent.Name
Case "Hotmail", "Mailbox - Administrator"
Exit Sub
Case Else
On Error GoTo DeletedItem
Set currSelection = Application.ActiveExplorer.Selection.Item(1)
If TypeName(LastSelected) = "Empty" Then
Set LastSelected = currSelection
Else
On Error GoTo DeletedItem
If LastSelected.EntryID <> currSelection.EntryID Then
ProcessLastSelected
End If
End If
End Select
Exit Sub
DeletedItem:
Set LastSelected = currSelection
Exit Sub
End If
End Sub

Sub ProcessLastSelected()
Set theItem = LastSelected
Select Case LastSelected.BillingInformation
Case ""
JournalResponse = JournalForm(LastSelected, "Inbox")
If JournalResponse = 6 Then
LastSelected.BillingInformation = "N"
LastSelected.Save
Set myOlInboxItems = Nothing
Set jItem = LastSelected.Copy
jItem.Move newJournal
Set myOlInboxItems =
Application.Session.GetDefaultFolder(olFolderInbox).Items
Set jItem = Nothing
Else
LastSelected.BillingInformation = "N"
LastSelected.UnRead = False
LastSelected.Save
Set LastSelected = myOlExp.Selection(1)
End If
End Select
Set LastSelected = currSelection
End Sub


Again, thank you very, very much for your help.

John
 
Hi John,

I´ll try that commentation with pleasure.

<ThisOutlookSession>
' Collection to store a reference on each opened Explorer.
' That´s necessary for receiving events from that explorers.
Private m_colExplorers As VBA.Collection

' Counter for unique id´s. Important to identify
' which Explorer can be removed if it is closing.
Private m_lNextKey As Long

' Is called automatically if OL starts
Private Sub Application_Startup()
' ...code
' Forces manually the creation of your first reference
' because the Explorer is created already before you
' could receive that event.
m_olExplorers_NewExplorer Application.ActiveExplorer
End Sub

' Is called from within the class modul if an Explorer
' closes to remove the reference on it from the collection.
Public Sub CloseExplorer(ByVal lKey As Long)
End Sub

' Is Called from OL if a new Explorer opens
Private Sub m_olExplorers_NewExplorer(ByVal Explorer As
Outlook.Explorer)
' ...
' Creates a new instance of your class module, then called "object".
' Without this you can´t work with the classes method´s.
Set oExplorer = New cExplorer

' Calls a method of that object and provides it with data
' (The meaning of that data I´ll explain later.)
oExplorer.Init ObjPtr(Me), m_lNextKey, Explorer

' Increasing the counter ensures the unique of each value
m_lNextKey = m_lNextKey + 1
End Sub
</ThisOutlookSession>

<cExplorer.cls>
' A generell method for copying data from one address
' to another.
' Handle this with care! Writing data to wrong
' addresses can crash an app!
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _
(ByRef Destination As Any, ByRef Source As Long, ByVal Length As
Long)

' Stores not the object reference on the parent but the address
' of that object.
' This is called a "weak reference". In VBA this is not
' necessary. Anyway, I´m used to do so (from VB).
Private m_lParentPtr As Long

' Returns an object reference from it´s address.
Private Function GetObjByPtr(ByVal lPtr As Long) As Object

' Called from the parent object first.
Public Sub Init(ByVal lParentPtr As Long, _
ByVal lKey As Long, _
olExplorer As Outlook.Explorer _
)

' Called from OL if an Explorer closes.
Private Sub m_olExplorer_Close()
' Calling the parent´s method that will remove this object
' from it´s collection. After doing that this object will
' terminate because (or if) there is no more reference on this.
GetObjByPtr(m_lParentPtr).CloseExplorer m_lKey
End Sub


Ok, I hope, now it´s a little clearer
 
Without testing your code I would have to guess.

Set LastSelected = myOlExp.Selection(1)

I can´t see which Explorer myOlExp is. This seems like you set
LastSelected on a selection regardless of the ActiveExplorer.

Have you tried step by step (F8) your code? Maybe you can identify the
line which causes that switching.

You could set back the focus by calling Explorer.Activate after
ProcessLastSelected. But I suppose that would flicker on the screen.
 
Michael,

Thank you so much for all your help. I'm sorry for the slow response. I've
been out of town and just getting back to this. I'll do some more
troubleshooting on the issue of the wrong explorer being activated after the
operation. You said that copying the memory space over wasn't necessary in
VBA, but that you were used to doing it that way. What would be the
alternative. I really don't want to mess with memory if I don't have to.

Thanks again,

John
 
Hi John,
You said that copying the memory space over wasn't necessary in
VBA, but that you were used to doing it that way. What would be the
alternative.

Hm, I´ve read our thread again but I don´t know, what do you mean.
Please, would you like to explain that to me?
 
The problem of handling Explorer events discretely for any open Explorer is
normally solved by using an Explorer wrapper class that handles the Explorer
events within it and by placing each Explorer class as it's created into an
Explorers wrapper collection, as shown in the ItemsCB COM addin template.

However, if an Explorer is opened on another user's mailbox using File, Open
then there isn't enough information available about the mailbox to do much
with that Explorer. Opening another Explorer view on the default mailbox
doesn't present that problem.

I've never found it necessary to use a copy memory function for any of that.
 
Thanks, now I know, what "copying the memory space over" is meaning.

As I wrote, in VBA that isn´t necessary. I copy my examples from VB
because I don´t use VBA. In VB I find this method very helpful to avoid
circular references.

--
Viele Grüße
Michael Bauer


Ken Slovak - said:
The problem of handling Explorer events discretely for any open Explorer is
normally solved by using an Explorer wrapper class that handles the Explorer
events within it and by placing each Explorer class as it's created into an
Explorers wrapper collection, as shown in the ItemsCB COM addin template.

However, if an Explorer is opened on another user's mailbox using File, Open
then there isn't enough information available about the mailbox to do much
with that Explorer. Opening another Explorer view on the default mailbox
doesn't present that problem.

I've never found it necessary to use a copy memory function for any of that.
 
Back
Top