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

  • Comments 18

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

http://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.

  • 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)
  • These are exactly the symtoms that I talk about above. If you look at the value of SMTPServer after you call SMTPServer.Insert you will notice it is blank.

    What this tells System.Web.Mail is that you want to send mail via the local pickup directory ("C:\inetpub\mailroot\pickup\"). The underlying code in System.Web.Mail then attempts to write an EML file out to this directory.

    The reason the Windows application works is that you are logged into the machine with permissions to write to this directory and the application is running in that context.

    In an ASP.NET application you might have Anonymous authentication turned on in your virtual directory's Directory Security settings in IIS Manager. In this case the web application runs in the context of IUSR_SERVERNAME which doesn't have permission to write to the local file system.

    Try this to get your web application to work...

    1) Comment out the line of code that sets the SmtpServer name. If you are going to use the local SMTP service you shouldn't set any server in your code.

    2) Open Explorer and navigate to your pickup directory ("C:\inetpub\mailroot\pickup" is the default) and give IUSR_SERVERNAME full permissions on the pickup folder.

    Let me know if that works for you...
  • This is great stuff, thanks for the logic behind why this really works.
  • 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.
  • 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.
  • Actually the mail are written in the pickup directory, but dosen't seem to reach de SendTo direction that i specified
  • 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.
  • 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
  • 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...
  • 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
  • Please, please, please, read the section titled "Taking a closer look at Insert(0, "127.0.0.1")" again.  I definitely appreciate criticism and your interest in my blog but in this case you are incorrect.

    Try the following things and tell me the results...

    > Change SmtpMail.SmtpServer.Insert(0,"SMTPHost"); to SmtpMail.SmtpServer.Insert(0,"ThisCodeDoesNothing");, does it work?

    > Add a Debug.Print line after your Insert and print out the value of SmtpMail.SmtpServer, is empty?

    > Comment out the Insert line all together, does your code still work?

    > Stop the SMTP Service on the machine this code runs on, does the mail go through still?

    The point of the blog post is not to say that your code won't work if you use Insert.  The point is that Insert is a string function which returns a value.  That line of code in your comment sets no values at all.  

    It is important to understand what happens when you don't set an SmtpServer value.  The mail is sent via the pickup directory instead a direct connection to a remote SMTP server.
  • this is not at all helpfull
  • 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.
  • Hi, my SMTP server is behind a proxy, which requires authentication. How do I provide this authentication??
  • Very many thanks for a good work. Nice and useful. Like it!
Page 1 of 2 (18 items) 12