custom action: save or delete email after sending

G

Guest

Hi. I would like to create a custom action that pops up a message box on
sending an email. This will ask me whether I would like this saved to my sent
items folder or not. This will make organising my sent items much easier
later on. Any ideas...?
 
K

Ken Slovak - [MVP - Outlook]

A better solution would be to use an Application_ItemSend event handler that
will fire every time an item is sent and show a message box asking whether
to save to Sent Items and if the answer is no to set the DeleteAfterSubmit
Boolean property to True.

If macros are enabled and this code is in ThisOutlookSession you will have
what you want, but I'd think it would get annoying after a while to be
prompted after sending every email.

Private Sub Application_ItemSend(ByVal Item As Object, Cancel As Boolean)
Dim answer As Long

If Item.Class = olMail Then
answer = MsgBox("Do you want to save this email: " & _
Item.Subject & " to Sent Items?", vbYesNo)

If answer = vbNo Then
Item.DeleteAfterSubmit = True
End If
End If
End Sub
 
G

Guest

That's perfect thanks. I know this might result in a small amount of pain,
but I am sure it will be worth it when it comes to organising my sent emails.
A bells and whistles version of this would be a form which pops up after
sending an email which shows my outlook folders and asks me to which folder I
want file the sent email with a "DELETE EMAIL" as the top option. Painful I
know but I will have to do this some time anyway...
Cheers.
 
G

Guest

Sorry, one little snag. When I click on send, the PC freezes. I have to click
on the Microsoft Outlook tab on the taskbar which brings the message box to
the front and then select YES/NO. Any ideas on how to solve this (minimise
current window?)
Thanks again
 
K

Ken Slovak - [MVP - Outlook]

Using WordMail, correct? That's a problem. Word is being subclassed by
Outlook and WordMail items are a weird mix of in and out of process threads.
The bottom line is that the parent window of the message box is most likely
the Outlook Explorer window and not the WordMail window.

The best way to fix that is to display a dialog box non-modally, get its
hWnd and use that in a call to SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0,
SWP_NOSIZE + SWP_NOMOVE). After that call to set your dialog on top of all
other windows you can then make the dialog modal.

An easier alternative that doesn't involve hacks with Win32 API calls would
be to not use WordMail.

If you decide to use the hack approach all those consts and the declaration
for SetWindowPos are in the MSDN library.
 
G

Guest

Hi. If you add "+ vbSystemModal" to the MsgBox statement, it works ok with MS
Word as the editor. The email closes and then you can see the message box
which is fine (before, the email froze after sending).
 
G

Guest

Oops. it seems freezing still happens, depending on what is open at the time.
So I am trying to get the other API hack that you suggested working. Just to
check, I currently use a standard Msgbox statement, but in order to get its
hwnd, do I need to create a new form instead (that looks exactly like a
msgbox) and then use the statement SetWindowPos(Form1.hwnd, HWND_TOPMOST, 0,
0, 0, 0, SWP_NOMOVE Or SWP_NOSIZE) or is there a way of getting the hwnd of a
standard msgbox?
Thanks a lot.
 
K

Ken Slovak - [MVP - Outlook]

I personally don't like to try to control a MsgBox that way. I prefer to
create an equivalent form and open it non-modally. I then set it to the top
and make it modal in the Activate event of the form. That will work even
with WordMail if you get the right window handle.
 
R

Robin

Hi Ken
I have come back this and am still struggling. I am struggling to get the
hWnd for the UserForm. (error: compile error, method or data member not
found). I am struggling to get anything on the internet. The code so far:

Public HWND_TOPMOST As Integer
Public SWP_NOSIZE
Public SWP_NOMOVE
Public SWP_NOACTIVATE

Private Declare Sub setWindowPos Lib "User" (ByVal hWnd As Long, ByVal
hWndInsertAfter As Long, ByVal X As Long, ByVal Y As Long, ByVal cx As Long,
ByVal cy As Long, ByVal wFlags As Long)

Sub Application_ItemSend(ByVal Item As Object, Cancel As Boolean)
Dim objFolder As Outlook.MAPIFolder
Dim oSent As Outlook.MAPIFolder
Dim objNS As Outlook.NameSpace

WND_TOPMOST = -1
SWP_NOSIZE = &H1
SWP_NOMOVE = &H2
SWP_NOACTIVATE = &H10

If Item.Class = olMail Then
UserForm1.Show
Call setWindowPos(UserForm1.hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE
Or SWP_NOMOVE Or SWP_NOACTIVATE)
If answer = "No" Then 'No from UserForm1, I do not want to save the email

On Error Resume Next
Set objNS = Application.GetNamespace("MAPI")
Set oSent = objNS.GetDefaultFolder(olFolderSentMail)
Set objFolder = oSent.Folders("Temp Sent") 'Assume this is a mail
folder

If objFolder Is Nothing Then
MsgBox "This folder doesn't exist!", vbOKOnly + vbExclamation,
"INVALID FOLDER"
End If

If Application.ActiveInspector Is Nothing Then 'Require that this
procedure be called for an open item
Exit Sub
End If

If Application.ActiveInspector.CurrentItem.Class <> olMail Then
Exit Sub
End If

Set Item.SaveSentMessageFolder = objFolder

Set objFolder = Nothing
Set oSent = Nothing
Set objNS = Nothing

End If
End If

End Sub

Your help would be greatly appreciated...
 
K

Ken Slovak - [MVP - Outlook]

This is a VBA user form? If so there is no hWnd property exposed. You have
to get the hWnd using the Win32 API function FindWindow().

FindWindow() takes 2 arguments: window class and caption. You can leave the
window class as a null string or you can use a tool like Spy++ to get the
class name when a UserForm is being displayed. The caption would be whatever
you set it to. After that you use the hWnd that you got for the other calls.
hWnd would be a 32-bit unsigned integer, in VBA terms a Long.
 
R

Robin

hi Ken. Thanks for your comments. I am trying the FindWindow with ShowWindow
functions - not sure if you think it will work. "ShowWindow window, 5" should
in theory show UserForm1 unless I am mistaken. My syntax seems to be killing
me though (because I am a hacking a bit, as you can tell). I get "invalid use
of string" with NULL or "bad dll calling convention" with vbNullString. Am
also not sure if UserForm1 should be in qoutes. Am struggling to get past
this point. Can you help?

Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal
lpClassName As String, ByVal lpWindowName As String)
Private Declare Function ShowWindow Lib "user32" (ByVal hwnd As Long, ByVal
nCmdShow As Long)

Public Sub Application_ItemSend(ByVal Item As Object, Cancel As Boolean)
Dim objFolder As Outlook.MAPIFolder
Dim oSent As Outlook.MAPIFolder
Dim objNS As Outlook.NameSpace
Dim dlg As UserForm1
Dim window As Long

If Item.Class = olMail Then
Set dlg = New UserForm1
dlg.answer = 0
dlg.Show ' display the userform

window = FindWindow(Null, "UserForm1")
ShowWindow window, 5

If dlg.answer = 1 Then
etc
 
K

Ken Slovak - [MVP - Outlook]

Both arguments are string values for FindWindow(). If you don't want to
supply a caption or class name you would use "" as the argument. In this
case for a UserForm you would use this:

className = "ThunderDFrame"
caption = "form caption, whatever it is"

I made a quickie user form with a caption of "Foobar" so my call would be:

caption = "Foobar"
hWnd = FindWindow(className, caption)

or for a null for the class name:

hWnd = FindWindow("", caption)

I got className = "ThunderDFrame" by displaying the user form and then
looking at the open windows in Spy++.

However, one problem with this is that userForm.Show() displays the user
form modally by default so any code after that Show() line wouldn't execute
until the user form was closed unless you explicitly show the form
non-modally. So the call would have to be:
userForm.Show vbModeless

Then your FindWindow() code would be called when the window (user form) is
still open. Of course then you have to do something to let the form work and
stop continued execution of your code until the form is closed, something
like a loop looking for a global Boolean flag that's set by the form when
it's closing and is cleared by your calling code just before the form is
opened.

I'm not sure why you'd want to call ShowWindow with an argument of SW_SHOW
after having already opened the window with UserForm.Show? Is there a
specific reason for that?
 
R

Robin

Hi Ken
I think I have cracked it, although I don't think I could tell you how
(probably just ended up confusing Outlook into submission). Not for the
fainthearted. Anyway, thanks a ton for your help.

FOR THE COMMANDBUTTON FROM THE USERFORM:

Public answer As Integer
Public hWnd As Long
Public HWND_TOPMOST As Integer
Public SWP_NOSIZE As Integer
Public SWP_NOMOVE As Integer
Public SWP_NOACTIVATE As Integer
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal
lpClassName As String, ByVal lpWindowName As String) As Long

Private Sub CommandButton2_Click()

hWnd = FindWindow(vbNullString, "UserForm1")

answer = 1
Me.Hide

End Sub

IN THE MODULE:

Public HWND_TOPMOST As Integer
Public SWP_NOSIZE As Integer
Public SWP_NOMOVE As Integer
Public SWP_NOACTIVATE As Integer
Private Declare Function SetWindowPos Lib "user32" (ByVal hWnd As Long,
ByVal hWndInsertAfter As Long, ByVal X As Long, ByVal Y As Long, ByVal cx As
Long, ByVal cy As Long, ByVal wFlags As Long) As Long

Public Sub Application_ItemSend(ByVal Item As Object, Cancel As Boolean)

Dim objFolder As Outlook.MAPIFolder
Dim oSent As Outlook.MAPIFolder
Dim objNS As Outlook.NameSpace
Dim dlg As UserForm1
Dim temp As Long

HWND_TOPMOST = -1
SWP_NOSIZE = &H1
SWP_NOMOVE = &H2
SWP_NOACTIVATE = &H10

If Item.Class = olMail Then
Set dlg = New UserForm1
dlg.answer = 0
dlg.Show vbModeless

temp = SetWindowPos(dlg.hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE Or
SWP_NOMOVE Or SWP_NOACTIVATE)

dlg.Hide
dlg.Show vbModal
dlg.Hide

If dlg.answer = 1 Then
etc
 
K

Ken Slovak - [MVP - Outlook]

I'm glad it's working for you but the code doesn't make sense to me.

In the module code you're showing the user form non-modally, then using
dlg.hWnd in SetWindowPos. Since a user form has no hWnd you aren't passing a
valid hWnd to SetWindowPos. You should be using FindWindow just after you
show the user form to get the hWnd at that point, then pass that to
SetWindowPos.

Then after you show the user form modally you hide it. You do realize that
the line "dlg.Hide" won't be executed until the user form is closed, right?

I'd also be hesitant about using HWND_TOPMOST with SetWindowPos, that makes
the form topmost even when other applications are switched to. So if say
Word is also running, or IE, switching to that app in the task bar would
still leave the user form on top, thereby annoying users. I'd be inclined to
be using SetWindowLong there instead SetWindowPos or using SetWindowLong in
addition to SetWindowPos with a HWND_TOP argument instead of HWND_TOPMOST.

I'd also probably be using the class argument with FindWindow, that way in
case there happen to be 2 windows showing with the same caption you are sure
that you're finding the user form window and not some other one.
 
M

M.D'Souza

Hi I'm trying to something similar only instead of saving the reply I would
like it to delete the original message from the inbox. Is this doable?
 
G

Guest

Yes, that is quite possible. You can trap the MailItem's Send event and
throw your dialog there. The ActiveExplorer.Selection object should contain
a reference to the message that is being replied to, or one of the messages
in the Application.Inspectors collection. In either situation, you can loop
through the collection and compare the Subject property with the current
reply message.

--
Eric Legault [MVP - Outlook]
MCDBA, MCTS (Messaging & Collaboration, SharePoint Infrastructure, MOSS 2007
& WSS 3.0 Application Development)
Collaborative Innovations
-> Try Picture Attachments Wizard For Microsoft Outlook <-
Web: http://www.collaborativeinnovations.ca
Blog: http://blogs.officezealot.com/legault
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top