Bypassing Outlook's Outbox (or creating a custom outbox)

  • Thread starter Thread starter Ekin
  • Start date Start date
E

Ekin

Here's my problem: When the user creates a new email (or forwards, replies,
etc), I want the email to go to my MAPIFolder instead of Outbox. When I try
to do this at Application_ItemSend by Item.Move(), I'm getting the mail
deleted or moved error for obvious reasons.

Note that I don't want Outlook to send the emails, I want to handle that
myself. So SaveSentMessageFolder doesn't help. If only there was a
OutboxFolder member of the MailItem class! :)

Any ideas how I could achieve this?

Ekin
 
Please clarify what you're trying to accomplish. The Outbox is only a temporary holding area for messages that have not been delivered to the downstream server.

Also, why are you creating messages in the first place if you don't want Outlook to send them?
 
I'm trying to move the mail item to a temporary folder (myOutbox), process it
(which may take up to 10-20 seconds), and pass it back to Outlook's default
Outbox.

See my ItemSend below.

Private Sub ThisApplication_ItemSend(ByVal Item As Object, ByRef Cancel
As Boolean) Handles Me.ItemSend
If (Item.Parent.EntryID = _outlookOutbox.EntryID) OrElse _
(Item.Parent.EntryID = _myOutbox.EntryID) Then
Cancel = False
Else
Item.Move(_myOutbox)
Cancel = True
End If
End Sub

I also tried to do the same with Inspectors but got the same "The item has
been moved or deleted" error.

Ekin
 
Oh, the Item is moved correctly and the rest of my code works as it should.
So it's just a matter of avoiding the nasty Outlook error...

Ekin
 
Thank you, Sue, but that didn't work.

When I debug in VS and follow the ThisApplication_ItemSend through line by
line, I can get to End Sub without any errors; when I hit F10 on End Sub, the
annoying "The item has been moved or deleted" message is thrown by Outlook
--VS Interop isn't even aware of it! So as far as VB is concerned, there is
no error.

Problem remains...

Ekin
 
It might work better if you do that in item.Send rather than in the
application wide event. Also, generally it's better to cancel the send, set
a flag and check the flag later using a timer or something to first get out
of the send event before you try to move the item. At least that's what I've
found doing similar things.
 
Thank you Ken. I tired with Item.Send (via inspectors) and had no luck.
Managing a dictionary of EntryIDs and going through them inside a timer tick
doesn't sound too reliable, mainly because the user may do something else
with the email if it remains open.

In my ItemSend, I added the following lines:
CloneMailItem(Item).Move(_myOutbox)
DeleteMailItem(Item)
Cancel = True

DeleteMailItem does item.Delete(), Marshal.ReleaseComObject(item) and
item=Nothing.

CloneMailItem creates a NEW MailItem object and gets a member by member copy
of the input item. newItem.To = item.To, newItem.CC = item.CC, .... (Binary
serialization doesn't work as MailItem isn't serializable.)

Now, the new item doesn't have any pointers back to Item in ItemSend and
everything works fine without "Send operation failed because the item was
deleted" and "The item has been moved or deleted" errors / notifications.

So if I could ditch the CloneMailItem function and use newitem =
item.Move(_myOutbox) and somehow dispose the pointer from newitem to Item, my
problem would be solved. Marshal.ReleaseComObject(newitem) and newitem =
Nothing don't do the trick.

Any ideas?

Ekin
 
As long your timer interval is not 2 minutes, the user won't have a chance
to do anything.
Thee important part is that your cannot delete or move the message while
Outlook is still processing the event callback. If the timer interval is 0,
it will fire immediately after Outlook runs the Windows message loop, but by
that time Outlook will be out of the event callback.

Dmitry Streblechenko (MVP)
http://www.dimastr.com/
OutlookSpy - Outlook, CDO
and MAPI Developer Tool
 
Right, it works with the following structure, even though Outlook's response
doesn't feel "natural" with a 100ms timer:

ThisApplication_Startup
_ToBeProcessed = New Collection
_moveTimer = New Timer
_moveTimer.Interval = 100
_moveTimer.Start()

ThisApplication_ItemSend
_ToBeProcessed.Add(Item)
Cancel = True

Private Sub _moveTimer_Tick(ByVal sender As Object, ByVal e As
System.EventArgs) Handles _moveTimer.Tick
If _ToBeProcessed.Count > 0 Then
For x As Integer = 1 To _ToBeProcessed.Count
CType(_ToBeProcessed(x), MailItem).Move(_myOutbox)
_ToBeProcessed.Remove(x)
Next
End If
End Sub

Do all the MVPs here think this really is the way to do it?

With the timer_tick running so frequently,
- What's the strain on system resources?
- Are there any concurrency issues? (is the function executed if it's
already being processed since the last timer tick?)

Ekin

Ps: Dmitry, re your "you cannot delete or move the message while Outlook is
still processing the event callback" comment. item.Delete():
Marshal.ReleaseComObject(item): item = Nothing does exactly that in ItemSend,
unless you meant something else...
 
Releasing your object reference is not the same as moving or deleting the
actual object.

I use a timer control for things like that in Send(). In the timer handler I
disable the timer don't re-enable it. After all, an item is sent only once
in an Inspector. I only use it when needed, so normally there's no overhead.

When the timer runs and fires there's minimal overhead. I usually use
something like 10 ms timer interval, although in managed code you might want
to use a longer interval.

For slightly less overhead than a timer control you can use the Win32 API
timers, although by the time you implement everything your overhead is
probably about the same as the control.
 
Thank you Ken, your reassuarance is highly appreciated.

Do you know how VB timer object is implemented - or specifically if each
"tick" is executed in a separate thread? If not, I don't have a problem. If
so, shall I stop/start the timer in each tick, i.e.
timer.stop
(code...)
timer.start
or is there a better way? I've been through MSDN and Google but I can't find
any info on this...

Ekin
 
I have no idea how a VB.NET timer object is implemented internally. I'd
guess it's based on using Win32 API timers under the hood, but that's just a
guess.

Just start the timer, wait for it to fire its event and disable the timer in
the event handler and don't re-enable it again. Timers should work on the
main thread, not a different thread, unless you implement a background
worker class. That's not something you'd want to do anyway, all Outlook
accesses should be done from the main thread and not from any separate
threads. Doing so tends to crash Outlook or hang it.
 
Back
Top