Visual Basic Code of the Week (COTW)
http://www.codeoftheweek.com
Issue #97
Online Version at http://www.codeoftheweek.com/membersonly/bi/0097.html (paid subscribers only)
All content and source code is Copyright (c) 1999 by C&D Programming Corp. No part of this issue can be reprinted or distributed in any manner without express written permission of C&D Programming Corp. Word, Excel and Visual Basic are trademarks of Microsoft Corp.

Cool Software

Download a free copy of NetMon - Your Internet Performance Monitor at http://www.codeoftheweek.com/netmon/index.html

Requirements

In this Issue

In this issue we show how to send an email message using the Winsock control and a single class with one method and a few properties. This class is limited to sending a single text based message to a single user.

Questions? Email us at questions@codeoftheweek.com.

cSimpleSMTP

This issue allows you to easily add the ability to send email using the SMTP protocol to your applications. This is the standard email method used on the Internet. Most large email systems support this as well (such as Novell Groupwise, Lotus Notes and Microsoft Exchange).

If you want to send more than one message simultaneously you will need to create multiple instances of this class and multiple copies of the winsock control. We have tested this and it does work. You can use this idea to create a very fast mailer. If you are interested in a very fast bulk mailer which can be used for sending out newsletters or emails like Code of the Week, let us know at mailer@codeoftheweek.com

If you have any questions about using this class, let us know at questions@codeoftheweek.com

Properties

Public SMTPHost As String

IP address or domain name of the SMTP server or host. An example of this would be smtp.codeoftheweek.com

Public SMTPPort As Long

The SMTP port number of the SMTP server. The default is 25.

Public DomainName As String

This is the domain you are sending your email from. This should be something like abc.com or hotmail.com

Public FromUsername As String

Your fully qualified username. It should be in the format user@domain.com

Public ToUsername As String

The fully qualified username you are sending mail to.

Public MessageText As String

Any message text. It can contain carriage return/line feeds to support multi-line messages.

Public Subject As String

The subject of the message you are sending.

Public WithEvents WinSockControl As Winsock

You must assign this property with the Winsock control you have dropped on your form. This class will not work without assigning this property.

Methods

Public Sub SendMessage()

Once you have set all the above properties you call this method to actually send the message. This routine will raise an error if the message can not be sent. It will also raise an error if it is in the process of sending a message and you call it recursively.

Returns

See source code.

Sample Usage

This sample will send a message to the smtp.codeoftheweek.com mail server. It will be addressed to billgates@microsoft.com and from info@codeoftheweek.com. It assumes you have a form called Form1 which has the WinSock control on it called wsControl.

    Dim SMTP As New cSimpleSMTP

    Set SMTP.WinSockControl = Form1.wsControl
    SMTP.SMTPHost = "smtp.codeoftheweek.com"
    SMTP.DomainName = "codeoftheweek.com"
    SMTP.FromUsername = "info@codeoftheweek.com"
    SMTP.ToUsername = "billgates@microsoft.com"
    SMTP.Subject = "Where's VB7?"
    SMTP.MessageText = "Bill, Please get VB7 out ASAP.  Thanks, COTW"
    SMTP.SendMessage

Source Code

Create a new class module and paste this source code into it. You should name this class cSimpleSMTP. If you have any questions, email us at help@codeoftheweek.com

'----------------------------------------------------------------------
'
'   Module Name:    cSimpleSMTP
'   Written By:     C&D Programming Corp.
'   Create Date:    4/99
'   Copyright:      Copyright 1999 by C&D Programming Corp.  Source
'                   code may not be reproduced except for use in a
'                   compiled executable.  All rights reserved.  If
'                   you would like to reprint any or all of this
'                   code please email us at info@codeoftheweek.com
'----------------------------------------------------------------------
'
'   Enhancements to make this more robust:
'       - Timeout errors within the SendData and RecData functions
'         it should eventually error out with a sckError, but it
'         would be nice to make it user controllable.
'       - Better mechanism to filter the error messages up to the
'         client.
'       - Add ability to send attachments (although that would
'         be more than "SimpleSMTP")
'       - Ability to send to multiple recipients
'
'
'
'   Sample Usage:
'
'    Dim SMTP As New cSimpleSMTP
'
'    Set SMTP.WinSockControl = Form1.wsControl
'    SMTP.SMTPHost = "smtp.codeoftheweek.com"
'    SMTP.DomainName = "codeoftheweek.com"
'    SMTP.FromUsername = "info@codeoftheweek.com"
'    SMTP.ToUsername = "billgates@microsoft.com"
'    SMTP.Subject = "Where's VB7?"
'    SMTP.MessageText = "Bill, Please get VB7 out ASAP.  Thanks, COTW"
'    SMTP.SendMessage
'
'

Option Explicit

' GetTickCount returns the number of milliseconds that Windows
' since Windows has started.
'
' The internal timer will wrap around to zero if Windows is
' run continuously for approximately 49 days.
'
Private Declare Function GetTickCount Lib "kernel32" () As Long

Public SMTPHost As String           ' IP address or domain name
Public SMTPPort As Long             ' port number of the SMTP server. Default is 25.
Public DomainName As String         ' like abc.com (this is the domain you are sending from)
Public FromUsername As String       ' any username in the format abc@abc.com
                                    ' should be your username.
Public ToUsername As String         ' any username in the format abc@abc.com
Public Subject As String            ' any text string
Public MessageText As String        ' any message text

' Winsock control which was dropped on a form
'
Public WithEvents WinSockControl As Winsock

' some status flags to make it easier to manage certain states.
Private mbSendComplete As Boolean
Private mbRecComplete As Boolean

Private Sub LogMessage(sData As String)
    Debug.Print sData
    ' you can do anything you need to here to save the log data or
    ' dump it into a text box or something like that.
    ' maybe raise an event to show status messages to a client application.
End Sub

Private Sub WinSockControl_DataArrival(ByVal bytesTotal As Long)
    Dim sData As String
    Dim lCode As Long

    WinSockControl.GetData sData, vbString, bytesTotal
    On Error Resume Next
    lCode = Val(sData)  ' will return non-zero if the string begins with a
                        ' number.  We trap for an error just in case someone
                        ' tries to send a very large numeric string.  We do
                        ' not want this routine to crash out.
    If Err Then
        lCode = 0
    End If
    On Error GoTo 0
    LogMessage "Received " & bytesTotal & " bytes of data."
    LogMessage "Status Code: " & lCode  ' useful for working with the SMTP host
    LogMessage "Raw Data: " & sData
    mbRecComplete = True
End Sub

Private Sub WinSockControl_SendComplete()
    mbSendComplete = True
    mbRecComplete = False
End Sub

Public Sub SendMessage()
    Dim sData As String
    Dim sMsg As String
    Static bInRoutine As Boolean

    If bInRoutine Then
        Err.Raise 5, "SendMessage", "Already in process of sending message."
    End If
    bInRoutine = True
    WinSockControl.RemoteHost = SMTPHost
    WinSockControl.RemotePort = SMTPPort
    WinSockControl.Connect
    If WaitFor(sckConnected) Then
        SendData "HELO " & DomainName & vbCrLf
        RecData
        SendData "MAIL FROM: " & FromUsername & vbCrLf
        RecData
        SendData "RCPT TO: " & ToUsername & vbCrLf
        RecData
        SendData "DATA" & vbCrLf
        RecData
        SendData "Subject: " & Subject & vbCrLf & MessageText & vbCrLf & "." & vbCrLf
        RecData
        SendData "QUIT" & vbCrLf
        RecData
    End If
    Wait 2  ' let's make sure the data is completely sent.
    WinSockControl.Close
    bInRoutine = False
    Exit Sub

Handler:
    bInRoutine = False
    Err.Raise Err.Number, "SendMessage", Err.Description
    If WinSockControl.State <> sckClosed Then
        WinSockControl.Close
    End If
End Sub

Private Function RecData() As Boolean
    While Not mbRecComplete And WinSockControl.State <> sckError
        DoEvents
    Wend
End Function

Private Function SendData(sData As String)
    mbSendComplete = False
    WinSockControl.SendData sData
    While Not mbSendComplete And WinSockControl.State <> sckError
        DoEvents
    Wend
End Function

Private Function WaitFor(lState As Long) As Boolean
    While WinSockControl.State <> lState And WinSockControl.State <> sckError
        DoEvents
    Wend
    If WinSockControl.State = sckError Then
        WaitFor = False
    Else
        WaitFor = True
    End If
End Function

Private Sub Class_Initialize()
    SMTPPort = 25
End Sub

Private Sub WinSockControl_Error(ByVal Number As Integer, Description As String, ByVal Scode As Long, ByVal Source As String, ByVal HelpFile As String, ByVal HelpContext As Long, CancelDisplay As Boolean)
    LogMessage "error: " & Number & " - " & Description & " source: " & Source
End Sub

Private Sub WinSockControl_SendProgress(ByVal bytesSent As Long, ByVal bytesRemaining As Long)
    LogMessage "sending: " & bytesSent & " remaining: " & bytesRemaining
End Sub

Private Sub WinSockControl_Close()
    LogMessage "closed"
End Sub

Private Sub WinSockControl_Connect()
    LogMessage "connected"
End Sub

Private Sub WinSockControl_ConnectionRequest(ByVal requestID As Long)
    LogMessage "connection request " & requestID
End Sub

'----------------------------------------------------------------------
'
'   Module Name:    basWait - integrated into cSimpleMAPI for
'                   ease of distribution.
'   Written By:     C&D Programming Corp.
'   Create Date:    5/98
'   Copyright:      Copyright 1998 by C&D Programming Corp.  Source
'                   code may not be reproduced except for use in a
'                   compiled executable.  All rights reserved.  If
'                   you would like to reprint any or all of this
'                   code please email us at info@codeoftheweek.com
'----------------------------------------------------------------------

Public Function Wait(ByVal lSeconds As Long, _
                     Optional ByRef bAbortFlag As Boolean = False, _
                     Optional ByRef lElapsed As Long) As Boolean

    Dim lStartTime As Long

    lStartTime = GetTickCount
    lElapsed = 0
    While (lElapsed < lSeconds) And (Not bAbortFlag)
        ' Convert to seconds (use integer math for extra speed
        lElapsed = (GetTickCount - lStartTime) \ 1000
        DoEvents
    Wend
    Wait = Not bAbortFlag
End Function

This document is available on the web

Paid subscribers can view this issue in HTML format. There is no additional source or information in the HTML formatted document. It just looks a little better since we have included some HTML formatting. Just point your browser to link at the top of this document.

Other links

Contact Information

C&D Programming Corp.
PO Box 20128
Floral Park, NY 11002-0128
Phone or Fax: (212) 504-7945
Email: info@codeoftheweek.com
Web: http://www.codeoftheweek.com