다음을 통해 공유


MYTH: SmtpMail.SmtpServer.Insert(0,"127.0.0.1") Actually Does Something

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...

https://www.codeproject.com/useritems/Techblaster.asp

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.

Comments

  • Anonymous
    December 01, 2005
    I installed release of DotNet2005 VisualStudio on WinXP. Now can't send emails with Mail.MailMessage object using DotNet2003 code

    If change to smtpServer.insert it works on a windows application, but not ASP.Net application.

    This is what I am doing:
    Dim oMail As New Mail.MailMessage
    With oMail
    .To = SendTo
    .From = From
    .BodyFormat = MailFormat.text
    .subject = "Bad"
    .Body = "test"
    .Prority = Prority.normal
    end with
    Mail.SmtpMail.SmtpServer = "127.0.0."
    Mail.SmtpMail.Send(oMail)
  • Anonymous
    December 02, 2005
    The comment has been removed
  • Anonymous
    January 11, 2006
    This is great stuff, thanks for the logic behind why this really works.
  • Anonymous
    February 06, 2006
    It's really a helpful site. I met the same scary error "Could not access 'CDO.Message' object". But when I drilled down the exception layers, I found my asp.net application threw the UnauthorizedAccessException exception with source CDO.Message object. I have tried some of your suggestions but none worked. And I have checked the CDO.Message permissions in the register table.

    Some guy suggests to change the machine.config of the .net framework. But I think it's not a good idea.

    I'm confused about the cause now. Who can help me?

    Thanks you all in advance.
  • Anonymous
    February 07, 2006
    Are you specifying a server name?  If so, does that SMTP server require authentication?

    If you are sending via pickup, what kind of authentication are you using?  If you are using integrated and have impersonation = true then you need to give Everyone write permissions on the local pickup directory.  If you are using Anonymous then you need to give the IUSR_SERVERNAME account permission to write to pickup.
  • Anonymous
    February 17, 2006
    Actually the mail are written in the pickup directory, but dosen't seem to reach de SendTo direction that i specified
  • Anonymous
    February 20, 2006
    If the mail is written to the pickup directory then your code is working fine which is the good news!  Now you need to look into the local SMTP server configuration to understand why the mail is not going out.  

    From your comment it appears that the mail is hanging in the pickup directory.  This sounds like the SMTP Server might not be turned on locally.  Make sure the SMTP Service is started.

    If the EML files are going straight to the Queue folder and ultimately to the badmail folder, inspect your TO adn FROM fields to ensure they are valid.  Also look at the smart host configuration on the SMTP server, you will most likely want to route mail from your local SMTP to your mail server or SMTP gateway for name resolution and delivery.
  • Anonymous
    June 14, 2006
    could you check this thread about the insert method, probably people have seen something about the method as they are claiming using insert solves some of their problem http://groups.google.com/group/microsoft.public.dotnet.framework.aspnet/browse_frm/thread/f2ef95e6fb1e0112/17e4601e26e355ba?hl=en&lr=&ie=UTF-8&oe=UTF8&newwindow=1&rnum=75&prev=/groups%3Fq%3Dcdo.message%2Bgroup:microsoft.public.dotnet.*%26num%3D50%26hl%3Den%26lr%3D%26ie%3DUTF-8%26oe%3DUTF8%26newwindow%253#17e4601e26e355ba
  • Anonymous
    June 15, 2006
    Unfortunately, I can't post to that thread anymore because it has been closed by the moderator.  But yes, this is another example of this myth getting propagated through the web...
  • Anonymous
    June 22, 2006
    Whats said here is good info, but not completely accurate.  My code proves it false.  For me, using SmtpMail.SmtpServer = "SMTPHost", gave errors.  Then I replaced it with SmtpMail.SmtpServer.Insert(0,"SMTPHost");

    WORKED - thru and thru! email from the right server to the right destinations.

    I'd hate to see someone else have this painful problem and not try this extremely simple solution!

    -Shefali
  • Anonymous
    June 22, 2006
    The comment has been removed
  • Anonymous
    July 30, 2006
    this is not at all helpfull
  • Anonymous
    July 30, 2006
    Neither is that comment without more detail ;)

    I'd love to have more information as to why this is not helpful so that I can make it so.  Let me know what I can do to make this more useful.
  • Anonymous
    August 02, 2006
    Hi, my SMTP server is behind a proxy, which requires authentication. How do I provide this authentication??
  • Anonymous
    August 26, 2006
    Very many thanks for a good work. Nice and useful. Like it!
  • Anonymous
    August 31, 2006
    hi, i tried this code still not working,
    i am getting the error cdo
  • Anonymous
    September 01, 2006
    What error are you getting?  Are you trying to send via pickup or port?
  • Anonymous
    November 07, 2006
    We have an article published now which addresses my earlier post about the myths regarding SmtpServer.Insert...