PDA

Click to See Complete Forum and Search --> : SMTP mail program refuses to work unless VB is installed


Bill Adams
Mar 16th, 2000, 03:26 AM
This code reads an input file to get the SMTP mail server, sender, recipient, subject, and message text. It then proceeds to send e-mail until end-of-file.

The code runs perfectly on machines where VB has been installed. It refuses to run on machines where VB has not been installed. You can install VB on a "fresh" machine and immediately uninstall it and the code will still work fine.

As information, I used the add-in package and deployment wizard to create an installation package. All the .DLLs needed to run VB programs are included in the package as well as winsock.

The installation goes smoothly. When requested for a reboot (due to the VB support file garbage), I do so and run the setup app again (this installs the app on the second go-round). It completes normally.

The code will run on non-VB machines, but for some reason either cannot connect with the server or cannot hear back from the server if VB has not been installed on the particular machine...Therefore, it just sits there.

Again, install the app on a machine upon which VB has been installed in the past and, presto, it works flawlessly. What is it that VB installation does that allows this code to work?


For the sample to work, you need to have a form and add a winsock control to it.


Dim spacepos As Integer
Dim Holder As String
Dim txtServer As String
Dim txtTo As String
Dim txtFrom As String
Dim txtSubject As String
Dim txtText As String
Dim sCommand As String
Dim StrStatus As String
Dim outtext As String
Dim X As Long
Dim I As Long
Dim InputString(5000) As String * 500

Private Sub Form_Load()
Text1.Text = Now

' Open Input File
Open "c:\InMsg.txt" For Input As #1

If Err <> 0 Then
Msg = "Error: Input file cannot be opened. Terminating Session"
MsgBox Msg
End If

I = 1
While Not EOF(1)
Line Input #1, InputString(I)
I = I + 1
Wend

' Data is now in an array so we can close the input file.
Close #1


' Open Output File
Open "c:\outmsg.txt" For Output As #2

If Err <> 0 Then
Msg = "Error: Output file cannot be opened. Terminating Session"
MsgBox Msg
End If

X = 1
Getnext

End Sub
Sub Getnext()

' We'll check to see if the counter (X) = the number of records in the array. If so end
' as the last number in the array is a blank record. If we're not ready to end, call sendit.
' Sendit will send the message and increment the counter (X) by 1 and send the code back
' here to see if it needs to stop.

If X >= I Then
Close #2
End
Exit Sub
End If

sendit

End Sub
Sub sendit()

' This logic decodes the input file and initiates communication from the server. Then, the
' code will wait for a response from the server. See wsTCP_DataArrival.

' Yeah, this is a funky delimiter strategy
txtServer = Left(InputString(X), InStr(InputString(X), "\-/") - 1)
txtTo = Left(InputString(X), InStr(InputString(X), "\\-//") - 1)
txtTo = Mid(txtTo, InStr(txtTo, "\-/") + 3)
txtFrom = Left(InputString(X), InStr(InputString(X), "\\\-///") - 1)
txtFrom = Mid(txtFrom, InStr(txtFrom, "\\-//") + 5)
txtSubject = Left(InputString(X), InStr(InputString(X), "\\\\-////") - 1)
txtSubject = Mid(txtSubject, InStr(txtSubject, "\\\-///") + 7)
txtText = Mid(InputString(X), InStr(InputString(X), "\\\\-////") + 9)

' Replace spaces in From party with "_"
Holder = txtFrom
spacepos = InStr(Holder, Chr$(32))

' SMTP standards say the From party cannot have spaces in it. Replace them here.
While spacepos > 0
Mid(Holder, spacepos, 1) = "_"
spacepos = InStr(Holder, Chr$(32))
Wend

txtFrom = Holder

' Start communicating with the server.
Me.wsTCP.Close
Me.wsTCP.RemotePort = 25
Me.wsTCP.RemoteHost = txtServer
Me.wsTCP.Connect

End Sub

Private Sub wsTCP_DataArrival(ByVal bytesTotal As Long)

' This logic waits on a reply from the server. Depending on the reply, the server will send
' farm out the response to subs. If a message is sent ok, or if there is an error, the code
' writes a log entry and loops back to check for the end of the input file.

On Error Resume Next

Dim MsgIn As String
Dim sCode As Integer

Me.wsTCP.GetData MsgIn
sCode = Val(Left(MsgIn, 3))


If sCode = 220 Then
Send220
Exit Sub
End If

If sCode = 250 Then
send250
Exit Sub
End If

If sCode = 354 Then
send354
Exit Sub
End If

If sCode = 221 Then
outtext = "Message sent successfully." & " Server = " & txtServer & " Recipient = " & txtTo & " Sender = " & txtFrom & " Subject = " & txtSubject & " Message = " & txtText
Print #2, outtext

X = (X + 1)
Getnext
Exit Sub
End If

If sCode = 501 Then
outtext = "Error occurred sending message." & " Server = " & txtServer & " Recipient = " & txtTo & " Sender = " & txtFrom & " Subject = " & txtSubject & " Message = " & txtText
Print #2, outtext

X = (X + 1)
Getnext
Exit Sub
End If

'If you got here, something bad and unexpected happened so exit the sub
outtext = "Error occurred sending message." & " Server = " & txtServer & " Recipient = " & txtTo & " Sender = " & txtFrom & " Subject = " & txtSubject & " Message = " & txtText
Print #2, outtext

Getnext
Exit Sub

End Sub
Private Sub Send220()
Me.wsTCP.SendData "HELO " & txtServer & vbCrLf
End Sub
Private Sub send250()
Select Case sCommand
Case ""
sCommand = "MAIL FROM:"
Me.wsTCP.SendData sCommand & txtFrom & vbCrLf
Case "MAIL FROM:"
sCommand = "RCPT TO:"
Me.wsTCP.SendData sCommand & txtTo & vbCrLf
Case "RCPT TO:"
sCommand = "DATA"
Me.wsTCP.SendData sCommand & vbCrLf
Case Else
sCommand = ""
StrStatus = "message sent"
Me.wsTCP.SendData "QUIT" & vbCrLf
End Select

End Sub
Private Sub send354()

Me.wsTCP.SendData "DATE: " & Format(Now, "h:mm:ss") & vbCrLf _
& "FROM: " & txtFrom & vbCrLf _
& "TO: " & txtTo & vbCrLf _
& "SUBJECT: " & txtSubject & vbCrLf & vbCrLf _
& txtText & vbCrLf & "." & vbCrLf
End Sub

thinh
Mar 20th, 2000, 10:33 AM
Are you sure the smtp server is correct? Your application doesn't have a Sub rotine to message the user if the smtp is correct if not it just gonna try to connect to the server and stop by itself... that's what i think is wrong...

Bill Adams
Mar 20th, 2000, 07:25 PM
Yes, the SMTP server name is correct. All I have to do to get the code to work is install VB, uninstall VB and, everything works perfectly. I have no idea what VB changes, but it does something.

Bill Adams

Mar 21st, 2000, 05:13 PM
Yes, the SMTP server name is correct. All I have to do to get the code to work is install VB, uninstall VB and, everything works perfectly. I have no idea what VB changes, but it does something.

Bill Adams


Try checking the versions of the files you use(dll's ocx's(winsock.dll etc)), check the version on the target machine, and check the versions on your machine

Bill Adams
Mar 21st, 2000, 06:44 PM
I wrote a VB app that would get all the filenames of the .dlls in the system32 directory. I ran the app on a "fresh" machine. I then installed VB and uninstalled VB. I ran the VB app again to suck off the file names and compared the two listings. I found 16 files that VB left behind.

I proceeded to copy the 16 .dlls to another "fresh" machine and tried to register them (some registered some would not). I then installed my SMTP app, but again, no success.

To test for a problem with winsock, I created a VB chat application that uses winsock. I had no problem at all with that app functioning on a "fresh" machine. based on this, the problem does not appear to be related to winsock.

In regards to checking the versions of .dlls, this is a good idea, but the only .dll that I am aware my SMTP application is using is winsock. I am distributing winsock in my installation package.

At this point, I am really stumped! VB must do something behind the scenes in regards to communication enabling, parameter setting, etc.

Mar 21st, 2000, 09:05 PM
What kind of error do you get(if you get an error at all).

Try inserting some debug code(write somthing to a file at the start of every sub or something and look where it stops, compare the file with a file an a working machine)). Save all the data you send and receive to a file, etc.

Bill Adams
Mar 21st, 2000, 09:20 PM
The code completes the:

' Start communicating with the server.
Me.wsTCP.Close
Me.wsTCP.RemotePort = 25
Me.wsTCP.RemoteHost = txtServer
Me.wsTCP.Connect

Therefore, I cannot tell whether the Me.wsTCP.Connect is being sent into oblivion or whether there is something preventing the data arrival of the "handshake" from the server. The code generates no error.

Because the code never receives anything back it just sits there, waiting for a response from the server which doesn't come.

We have Visual Studio and reportedly, there is a tracer utility (Spy++?) that shows windows processes that fire whenever anything happens. Apparently, you can selectively turn on the types of processes you want to monitor. Maybe this will help me trouble-shoot.

Perhaps I can turn off reporting for everything except communications functions and see what the SMTP code does behind the scenes. Maybe this will give insight into which specific Windows components I need to worry about in the installation package.

Has anyone used this before?

Mar 21st, 2000, 09:30 PM
Are you trapping the error event of the winsock?

You could also check the state of the winsock, maybe it stays on sckConnecting(6) or sckError(9)
It should become sckConnected(7)

Bill Adams
Mar 21st, 2000, 11:10 PM
The code generates no error. I have not checked the state of the winsock control, however. I suppose I could put in a message box and a timer that would report the state.

I would have to recompile the project and install on another "fresh" machine since the code runs flawlessly on my VB machine.

Mar 22nd, 2000, 04:03 PM
Actually Bill's method is better for he waits until the server is ready to receive a response.

Bill Adams
Mar 22nd, 2000, 06:46 PM
Man do I feel like a dummy,...sort of.

I tried running the above code on a non-VB machine again yeasterday and it DOES work in this form. BUT this was originally coded as a SMTP CONTROL. In that form, it DOES NOT work.

As long as I have a form with a winsock control on it (compile as an .exe) the code will work. If I instantiate a winsock control (no form) and compile the code as a control (.ocx), the code only works if VB is installed. (I do register the SMTP control, etc., etc.).

Following is the code for the control. It is courtesy of Igor Ostrovsky of Ostrosoft. I have made some modifications from the original code. Igor is distributing it as freeware at www.ostrosoft.com:

Option Explicit

Dim strServ As String
Dim strTo As String
Dim strFrom As String
Dim strSubj As String
Dim strMsg As String
Dim strStatus As String
Dim strwsStatus As String
Dim J As Integer

' *****!!!!!*****
' Dim winsock control here
Private WithEvents wsTCP As Winsock

Public Event ConnectSMTP()
Public Event SendSMTP()
Public Event CloseSMTP()
Public Event ErrorSMTP(ByVal Number As Integer, Description As String)

Private sCommand As String

Public Property Let Server(ByVal strTemp As String)
strServ = strTemp
PropertyChanged "Server"
End Property

Public Property Let SendTo(ByVal strTemp As String)
strTo = strTemp
PropertyChanged "SendTo"
End Property

Public Property Let MessageSubject(ByVal strTemp As String)
strSubj = strTemp
PropertyChanged "MessageSubject"
End Property

Public Property Let MessageText(ByVal strTemp As String)
strMsg = strTemp
PropertyChanged "MessageText"
End Property

Public Property Let MailFrom(ByVal strTemp As String)
strFrom = strTemp
PropertyChanged "MailFrom"
End Property

Public Property Get Server() As String
Server = strServ
End Property

Public Property Get SendTo() As String
SendTo = strTo
End Property

Public Property Get MessageSubject() As String
MessageSubject = strSubj
End Property

Public Property Get MessageText() As String
MessageText = strMsg
End Property

Public Property Get MailFrom() As String
MailFrom = strFrom
End Property

Public Property Get Status() As String
Status = strStatus
End Property

Public Property Get wsStatus() As String
wsStatus = strwsStatus
End Property

Public Sub Connect()
On Error Resume Next


' ******!!!!!*****
' Instantiation without a form

Set wsTCP = New Winsock

wsTCP.RemotePort = 25
wsTCP.RemoteHost = strServ
wsTCP.Connect

strStatus = "Connecting to SMTP server"

J = 1
For J = 1 To 20
Sleep 100
strwsStatus = wsTCP.State
DoEvents
Next J

If Err.Number > 0 Then
RaiseEvent ErrorSMTP(Err.Number, Err.Description)
Err.Clear
End If
End Sub

Private Sub UserControl_ReadProperties(PropBag As PropertyBag)
Server = PropBag.ReadProperty("Server")
SendTo = PropBag.ReadProperty("SendTo")
MailFrom = PropBag.ReadProperty("MailFrom")
MessageSubject = PropBag.ReadProperty("MessageSubject")
MessageText = PropBag.ReadProperty("MessageText")
End Sub

Private Sub UserControl_WriteProperties(PropBag As PropertyBag)
PropBag.WriteProperty "Server", Server
PropBag.WriteProperty "SendTo", SendTo
PropBag.WriteProperty "MailFrom", MailFrom
PropBag.WriteProperty "MessageSubject", MessageSubject
PropBag.WriteProperty "MessageText", MessageText
End Sub

Private Sub wsTCP_Connect()
strStatus = "Connected to SMTP server"
End Sub

Private Sub wsTCP_DataArrival(ByVal bytesTotal As Long)
On Error Resume Next
Dim MsgIn As String: Dim sCode As Integer
wsTCP.GetData MsgIn
sCode = Val(Left(MsgIn, 3))
Select Case sCode
Case 220
RaiseEvent ConnectSMTP
wsTCP.SendData "HELO " & strServ & vbCrLf
Case 221
wsTCP_Close
Exit Sub
Case 250
Select Case sCommand
Case "MAIL FROM:"
sCommand = "RCPT TO:"
wsTCP.SendData sCommand & strTo & vbCrLf
Case "RCPT TO:"
sCommand = "DATA"
wsTCP.SendData sCommand & vbCrLf
Case ""
sCommand = "MAIL FROM:"
wsTCP.SendData sCommand & strFrom & vbCrLf
Case Else
sCommand = ""
strStatus = "Message sent"
wsTCP.SendData "QUIT" & vbCrLf
RaiseEvent SendSMTP
End Select
Case 354
wsTCP.SendData "DATE: " & Format(Now, "h:mm:ss") & vbCrLf _
& "FROM: " & strFrom & vbCrLf _
& "TO: " & strTo & vbCrLf _
& "SUBJECT: " & strSubj & vbCrLf & vbCrLf _
& strMsg & vbCrLf & "." & vbCrLf
Case Is > 400
sCommand = ""
strStatus = "SMTP session error"
RaiseEvent ErrorSMTP(sCode, Mid(MsgIn, 5))
wsTCP.SendData "QUIT" & vbCrLf
End Select

If Err.Number > 0 Then
strStatus = "SMTP control error"
RaiseEvent ErrorSMTP(Err.Number, Err.Description)
Err.Clear
End If
End Sub

Private Sub wsTCP_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)
strStatus = "SMTP connection error"
RaiseEvent ErrorSMTP(ByVal Number, Description)
wsTCP.Close
End Sub

Private Sub wsTCP_Close()
wsTCP.Close
strwsStatus = wsTCP.State
Set wsTCP = Nothing
strStatus = "SMTP session closed"
RaiseEvent CloseSMTP
End Sub



Sorry guys for somewhat of a wild goose chase. I did not anticipate that the code would behave differently if the winsock control was instantiated within the SMTP code instead of being on a form.

I still want to get this to work as a control, however, as it would be infinitely more portable. Do you have any suggestions that would get this code to work correctly on non-Vb machines as a .ocx?

THANKS!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

thinh
Mar 23rd, 2000, 08:50 AM
It only works if the user have a winsock control which u call from when in VB. Remember u used that winsock control to create your control. it only work with out a control is only when you use raw API for the SMTP control that you created..

Bill Adams
Mar 23rd, 2000, 06:21 PM
Why?

Mar 26th, 2000, 04:16 PM
It will work, just make sure you distribute the winsock.ocx with your program.

Build your ocx, create a dependency file(through the package and deployment wizard), this will create a file wich contains all the neccassery files for your ocx(winsock.ocx, vbdll's).

Next create a setup for your program, include the ocx(now vb will see and read the dependecy file and notice that you use the winsock and it will add the winsock in your package.).

Hope this helps.

Bill Adams
Mar 26th, 2000, 06:23 PM
I did create such an installation package that included winsock...