One of the principles of the Information Age is that anybody with a blog like mine or a Code Project account can write an article viewable to millions of people around the world. As a support engineer, I love and hate this at the same time. Take the following Code Project article for example…
This article is full of very useful information about how to use System.Web.Mail and would probably help a novice get a good start. I don’t mean to call this author out as the only person who is spreading false information, the fact is he or she probably got a lot of this information from another source to begin with. However there are a number of inaccuracies that don’t properly communicate how System.Web.Mail works and ultimately led to developers giving me a call. (Which is fine, that is my job and I’m more than happy to help!)
I’d like to address a common inaccuracy right now. The following line of code is lifted from the article I mentioned above…
SmtpMail.SmtpServer.Insert(0,”127.0.0.1″); //specifying the real SMTP Mail Server.
The intention in specifying an SmtpServer is to specify the name of the SMTP server you wish you relay your mail through. Setting this property will send the mail via port through the server mapped to the name or IP address provided. In this case the author is intending to send mail via port through the local SMTP service. There are two problems with this line of code, one relates to proper usage of the SMTP Service and the second relates to understanding what the Insert function does.
Error 1 – Understanding the SMTP Service, System.Web.Mail, and CDOSYS
I will expand on this in a later feature article so I will try to be brief here. System.Web.Mail is simply a managed wrapper for CDOSYS which is a COM component that sends simple SMTP mail to remote and local services via port or local pickup directory. When using CDOSYS directly you specify which “sendusing” method to use when sending mail. When using System.Web.Mail you simply set SmtpMail.SmtpServer to send via port or leave it blank to send via local pickup.
If you have a local SMTP Service running (which you should if you are sending a lot of mail, say from a web server) you only leverage the SMTP services Queue folder, retry attempts, delay sending, and general management when you send via pickup. If you send via port you do just that, you are connecting to the SMTP port (usually 25) and either successfully or unsuccessfully sending mail in one shot. When sending via port, if you can’t connect to the SMTP server or successfully transmit the message your code will throw an exception. However, when you send via pickup directory, you are simply writing an EML file to the local directory (usually C:\inetpub\mailroot\pickup\), you will not see any errors unless you cannot write the file to disk. The SMTP Service will handle sending the message, retrying failures, and sending NDRs. Therefore I STRONGLY recommend that you send via local pickup directory if at all possible.
But let’s get back to this code sample. This code specifies the intention that the mail should be sent through the local server via port, which would require that the SMTP service be running on the local machine. In that case you should go ahead and send mail via pickup by commenting out this line.
Error 2 – Taking a closer look at Insert(0, “127.0.0.1”)
Now that we have clarified how System.Web.Mail is used to specify the send using method, let’s take a look at what is actually happening with this line of code. We will start by looking at the data type of the static property SmtpMail.SmtpServer. You will notice that its datatype is string. So when we call the Insert method of SmtpServer we are actually calling the Insert method off of any string. Let’s take a look at the Insert method to see what it actually does…
“Inserts a specified instance of String at a specified index position in this instance.“
Okay, sounds alright to me so far. We definitely want to insert “127.0.0.1” or some other IP address or server name into the SmtpServer property value. However, you will notice that this Insert method returns to a string. What is the returned string?..
“A new String equivalent to this instance but with value inserted at position startIndex.”
The modified string is returned, the original string is NOT changed!
So what does this mean for our line of code from the article? If you notice we don’t do anything with the return value from the Insert method. We just call the method and move on. If you were to print the value of SmtpMail.SmtpServer to the debug window right after this line you will notice that SmtpServer has no value. What happens when we don’t set the SmtpServer in System.Web.Mail? That’s right, we send via local pickup. In this case we are specifying the local server with “127.0.0.1” so we are required to have an SMTP service running anyway. However what if we changed this line of code to specify our Exchange server or coporate SMTP relay?..
SmtpMail.SmtpServer.Insert(0,”SMTPHost”); //specifying the real SMTP Mail Server.
The intention here is that we will send mail via port to a server named “SMTPHost”. However, what is really happening? We are simply writing out an EML file to the local SMTP directory. “SMTPHost” is never contacted about this message and it may or may not get sent depending on if you happen to have SMTP services installed on your local machine.
Here is my theory on how this code sample came about. If you remember in my description of Error 1, I mentioned that when you send via pickup you are simply creating a file on the local hard disk. When you send via port you are connecting to a remote machine using whatever network credentials you have an attempting to transmit a message over the wire to a server. Most likely someone was trying to send mail through “SMTPHost” with code like this…
SmtpMail.SmtpServer = “SMTPHost”; //specifying the real SMTP Mail Server.
But for whatever reason (misspelling, network connectivity problems, authentication, etc.) the were getting an error (“Could not access ‘CDO.Message’ ” is most common.) when they tried to send the message. Then they noticed that if they use the Insert method that they don’t get any errors at all so they used it. What they never realized (until they read this post) is that their code doesn’t do anything near what they intended it to do.
..In conclusion, you should only set the SmtpServer if you want to send via port to a particular SMTP server and if you set the SmtpServer use SmtpServer = “servername” and don’t get caught up in the “Insert Method Myth”. If you want to send via pickup directory, just leave the SmtpServer property empty.