Solved

vbSendEmail poSendMail_SendFailed usage

Posted on 2011-03-11
12
699 Views
Last Modified: 2012-05-11
I'm trying to get poSendMail_SendFailed but I don't understand the class structure.  Below is the code that I put into poSendMail_SendFailed.  I get this error:
   vbsendmail 503.5.5.0 sender already specified

I'm thinking that I can't call vbSendEmail  from inside the error handler.  I could set a global flag but there must be a better way.  Any ideas?
Private Sub poSendMail_SendFailed(Explanation As String)
' vbSendMail 'SendFailed Event
  Dim strSubject As String
  Dim strMessage As String
  
'  MsgBox ("Your attempt to send mail failed for the following reason(s): " & vbCrLf & Explanation)
  strSubject = "DTL Database E-Mail could not be sent"
  strMessage = "Your attempt to send mail failed for the following reason(s): " & vbCrLf & Explanation
  Call Send_EMail_to_Man("Maintenance_Email", strSubject, strMessage & vbNewLine & "(EM-41)")
End Sub

Open in new window

0
Comment
Question by:schmir1
  • 8
  • 4
12 Comments
 
LVL 84
Comment Utility
Assuming this is the same situation you were working with earlier, where you've sunk the Events from the poSendMail class into your vbSendMail class:

You really should NOT retry this, at least in my opinion. What should happen in this case is the top level class (the vbSendMail class) should do nothing more than report back to the UI and clean up (destroy objects, close connections, log errors, etc etc), and then your UI logic should take over from there.

What I would do is build a WithEvents setup for vbSendEmail, and then Raise a relevant event back to the UI. To do that, at the top of your vbSendMail class, add a new Event:

Event SendMailFailed()

In that vbSendMail class, add code like this to the SendFailed event:

Private Sub poSendMail_SendFailed(Explanation As String)
  RaiseEvent SendMailFailed()
End Sub

Back on the UI layer, make SURE to declare your vbSendMail class using the WithEvents keyword. If you're doing this from a form, then you'd declare that at the top of the form's Code module, in the General Declarations section of the Form:

[General Declarations]

Private WithEvents mclsSendMail As cSendMail

Once you've done this, the Properties and Methods of that object (mclsSendMail) are available to you. Locate the object in the leftside dropdown, then examine the rightside dropdown. You should see the SendMailFailed event listed there. Select it, and Access will build the VBA stubs to allow you to write code.

So when your cSendMail is informed of an Error in the poSendMail class (via the SendFailed event), you then just pass that along to the UI, where you can decide what to do with it.

You can also pass along the Explanation text to the UI if you wish. Just declare your Event like this:

Event SendMailFailed(FailCode As String)

And then modify the SendFailed code like this:

Private Sub poSendMail_SendFailed(Explanation As String)
  RaiseEvent SendMailFailed(Explanation)
End Sub

This will pass that text along to the UI layer, and you can do with it what you wish - restart the process, inform the user, etc etc.
0
 

Author Comment

by:schmir1
Comment Utility
Thanks for all the detail.  I've got one problem.  My common e-mail code is in a common module, not a form module.  Access give me a "Only valid in object Module" compile error on the WithEvents for this line:
  Private WithEvents mclsSendMail As cSendMail

I suppose I could put it in with the Switchboard code or I could create another dummy form and move all my e-mail code there (not the class stuff).  What do you think?  Or is there any way to make a common module work with WithEvents?
0
 

Author Comment

by:schmir1
Comment Utility
I'm getting the RaiseEvent to fire but not catching it in my Switchboard form module.  Note:  This module is not the same as what calls SMTP_EMail_Sender if that makes a difference.  As noted above, my e-mail code is in a common module except for poSendMail_SendFailed.
This code is in cSendMail Class Module.  It does break as indicated in the code comment
------------------------------------
Option Compare Database
Option Explicit
Private WithEvents poSendMail As vbSendMail.clsSendMail  'need to be a class module for WithEvents to work
Event SendMailFailed(FailCode As String)

Private Sub poSendMail_SendFailed(Explanation As String)
  RaiseEvent SendMailFailed(Explanation)  '*************breakpoint does happen here
End Sub
---------------------------------------


This code is in my Switchboard Module and it does NOT break as indicated in the code comment
------------------------------------------------
Option Compare Database
Option Explicit
Private WithEvents mclsSendMail As cSendMail

Private Sub mclsSendMail_SendMailFailed(FailCode As String)
'Note: vbSendMail 'SendFailed Event
  Dim strSubject As String
  Dim strMessage As String

'  MsgBox ("Your attempt to send mail failed for the following reason(s): " & vbCrLf & Explanation)
  strSubject = "DTL Database E-Mail could not be sent"   '**************Break does NOT occur here
  strMessage = "Your attempt to send mail failed for the following reason(s): " & vbCrLf & FailCode
  Call Send_EMail_to_Man("Maintenance_Email", strSubject, strMessage & vbNewLine & "(EM-41)")
End Sub
-----------------------------------------

Open in new window

0
 
LVL 84
Comment Utility
You can't use WithEvents in a Standard Module, as the VBA Compiler has warned. If you want to use WithEvents, you'll have to move the vbSendMail code to a Class module, and then declare that new Class using WithEvents. This will "sink" the events into the host container, and allow you to process those events as they occur.

You would be far, far better off moving this to a proper Class module and working from there. You could kludge this sort of behavior by include a Function in your receiving form, and having your vbSendMail call that function, but this really negates the concept of modular programming. Not that I'm a slave to that, but if you're working with OOP principles you should strive to adhere to those principles.

0
 

Author Comment

by:schmir1
Comment Utility
Let me see if I understand what you are saying.  You are saying to move the code from the vbSendMail.dll into a class module then use it.  How would I use it?  Below is my class module now (cSendMail):
Option Compare Database
Option Explicit
' *****************************************************************************
' Required declaration of the vbSendMail component (withevents is optional)
' You also need a reference to the vbSendMail component in the Project References
' *****************************************************************************
Private WithEvents poSendMail As vbSendMail.clsSendMail  'need to be a class module for WithEvents to work
Event SendMailFailed(FailCode As String)

Private Sub poSendMail_SendFailed(Explanation As String)
  RaiseEvent SendMailFailed(Explanation)  'breakpoint does happen here
End Sub

Public Sub SMTP_EMail_Sender(strRecipient As String, strSubject As String, strMessage As String, Optional strForm As String, Optional strCC As String)
'This routine sends e-mail using vbSendMail.dll
  Dim strServer As String
  Dim strFromDisplayName As String
  Dim strFrom As String
  
  Set poSendMail = New clsSendMail
  strServer = "mail.medtronic.com"
  strFrom = "robert.schmitt@medtronic.com"
  strFromDisplayName = "DTL Database"
  
  With poSendMail
    ' **************************************************************************
    ' Optional properties for sending email, but these should be set first
    ' if you are going to use them
    ' **************************************************************************
    .SMTPHostValidation = VALIDATE_NONE         ' Optional, default = VALIDATE_HOST_DNS
    .EmailAddressValidation = VALIDATE_SYNTAX   ' Optional, default = VALIDATE_SYNTAX
    .Delimiter = ";"                            ' Optional, default = ";" (semicolon)

    ' **************************************************************************
    ' Basic properties for sending email
    ' **************************************************************************
    .SMTPHost = strServer                  ' Required the fist time, optional thereafter
    .from = strFrom                       ' Required the fist time, optional thereafter
    .FromDisplayName = strFromDisplayName         ' Optional, saved after first use
    .Recipient = strRecipient                     ' Required, separate multiple entries with delimiter character
    .RecipientDisplayName = strRecipient      ' Optional, separate multiple entries with delimiter character
    .CcRecipient = strCC                        ' Optional, separate multiple entries with delimiter character
'    .CcDisplayName = txtCcName                  ' Optional, separate multiple entries with delimiter character
'    .BccRecipient = txtBcc                      ' Optional, separate multiple entries with delimiter character
    .ReplyToAddress = strFrom              ' Optional, used when different than 'From' address
    .Subject = strSubject                  ' Optional
    .Message = strMessage                     ' Optional
'    .Attachment = Trim(txtAttach.Text)          ' Optional, separate multiple entries with delimiter character

    ' **************************************************************************
    ' Additional Optional properties, use as required by your application / environment
    ' **************************************************************************
'    .AsHTML = bHtml                             ' Optional, default = FALSE, send mail as html or plain text
'    .ContentBase = ""                           ' Optional, default = Null String, reference base for embedded links
'    .EncodeType = MyEncodeType                  ' Optional, default = MIME_ENCODE
'    .Priority = etPriority                      ' Optional, default = PRIORITY_NORMAL
'    .Receipt = bReceipt                         ' Optional, default = FALSE
'    .UseAuthentication = bAuthLogin             ' Optional, default = FALSE
'    .UsePopAuthentication = bPopLogin           ' Optional, default = FALSE
'    .UserName = txtUserName                     ' Optional, default = Null String
'    .Password = txtPassword                     ' Optional, default = Null String, value is NOT saved
'    .POP3Host = txtPopServer
'    .MaxRecipients = 100                        ' Optional, default = 100, recipient count before error is raised
    
    ' **************************************************************************
    ' Advanced Properties, change only if you have a good reason to do so.
    ' **************************************************************************
    ' .ConnectTimeout = 10                      ' Optional, default = 10
    ' .ConnectRetry = 5                         ' Optional, default = 5
    ' .MessageTimeout = 60                      ' Optional, default = 60
    ' .PersistentSettings = True                ' Optional, default = TRUE
    ' .SMTPPort = 25                            ' Optional, default = 25

    ' **************************************************************************
    ' OK, all of the properties are set, send the email...
    ' **************************************************************************
    ' .Connect                                  ' Optional, use when sending bulk mail
    .Send                                       ' Required
    ' .Disconnect                               ' Optional, use when sending bulk mail
  End With
End Sub

Open in new window

0
 

Author Comment

by:schmir1
Comment Utility
Not sure why I'm not catching the event.  What could I try now?
0
Top 6 Sources for Identifying Threat Actor TTPs

Understanding your enemy is essential. These six sources will help you identify the most popular threat actor tactics, techniques, and procedures (TTPs).

 

Author Comment

by:schmir1
Comment Utility
I found out that I needed to set the mcMyMail to nothing before re-using it.  That was causing it to not work at all.  I got it working but with a kludge of using global flag and string.
0
 
LVL 84
Comment Utility
You should set and reset the Class module each time you need it. I'm not sure why you're using a global flag for something of this nature - just do this:

Build a new instance of the class
Use that class
Destroy that instance

0
 

Author Comment

by:schmir1
Comment Utility
Could you explain how to do this?

Is this build new instance?
  Set mcMyMail = New cSendMail

Is this destroy that instance?
      Set mcMyMail = Nothing
0
 

Author Comment

by:schmir1
Comment Utility
Also, the global flag is needed because I could never figure out how to catch the signalled event?
0
 
LVL 84

Accepted Solution

by:
Scott McDaniel (Microsoft Access MVP - EE MVE ) earned 500 total points
Comment Utility
Yes, those steps are correct. As far as "catching" the event, remember that you must "fire" the event from your cSendMail class module. You appear to be doing this correctly.

You must then "catch" the event. This is done in whatever object is currently managing your mcMyMail object. For example, if you declare this in a Form, then you will need to catch the event in that form. This is done by setting that form's Class module to handle your mcMyMail.SendMailFailed event. THAT event is the one the UI should be working with.

The basic course of events is this:

poSendMail encounters an error, and fires the SendFailed event.
mcMyMail catches that event (due to the event sink in mcMyMail).
mcMyMail fires it's own event name SendMailFailed
UI catches mcMyMail.SendMailFailed and performs some action (shows messagebox, rolls back changes, etc etc)

0
 

Author Closing Comment

by:schmir1
Comment Utility
Great answer.  

That's what I was doing wrong.  I was trying to catch the event in a form that wasn't the same as the original module that created the object.  I need to move some code around to fix it the right way (as you described).
0

Featured Post

How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

Join & Write a Comment

QuickBooks® has a great invoice interface that we were happy with for a while but that changed in 2001 through no fault of Intuit®. Our industry's unit names are dictated by RUS: the Rural Utilities Services division of USDA. Contracts contain un…
Experts-Exchange is a great place to come for help with solutions for your database issues, and many problems are resolved within minutes of being posted.  Others take a little more time and effort and often providing a sample database is very helpf…
In Microsoft Access, learn how to “cascade” or have the displayed data of one combo control depend upon what’s entered in another. Base the dependent combo on a query for its row source: Add a reference to the first combo on the form as criteria i…
With Microsoft Access, learn how to start a database in different ways and produce different start-up actions allowing you to use a single database to perform multiple tasks. Specify a start-up form through options: Specify an Autoexec macro: Us…

763 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

6 Experts available now in Live!

Get 1:1 Help Now