Send email via SMTP example using .net

Started by Jeff, November 29, 2017, 12:40:32 PM

Previous topic - Next topic

Jeff

Does anyone have an example of sending an email via SMTP example using .net?
Jeff

td

"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

Jeff

How do you capture an error from this sample? In samples I see there is a "try/catch". But I don't think it's applicable in WB.

try {

client.Send(message);

}

catch (Exception ex) {

Console.WriteLine("Exception caught in CreateTestMessage2(): {0}",

ex.ToString() );

}

}


One last question, does anyone have an example of sending an email via Exchange Web Service?

Thanks.
Jeff

stanl

Are you asking for code for an Internal Exchange Server w/out Outlook - or Web Mail.  Because I think with Exchange you just need to access system.Net.Mail.SmtpClient and system.Net.NetworkCredential. But that being said, we tried it from our corporate headquarters and it gets squashed by IT security. A co-worker discovered a way to break through with VB NET but it relies on a third party dll.

JTaylor

Not sure if it would see a .NET attempt via WinBatch the same as the Postie but probably close enough...if so, as Stan noted, you will have to have your email administrator configure the server to allow the messages.  By default it blocks such messages.

Jim

td

Quote from: Jeff on November 30, 2017, 11:40:39 AM
How do you capture an error from this sample? In samples I see there is a "try/catch". But I don't think it's applicable in WB.

try {

client.Send(message);

}

catch (Exception ex) {

Console.WriteLine("Exception caught in CreateTestMessage2(): {0}",

ex.ToString() );

}

}

You use WinBatch's built in error handling as try-catch block exception handling is a property of the language not the dotNet  framework.   See IntControl 73 in the Consolidated WIL Help file for details.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

td

Quote from: Jeff on November 30, 2017, 11:40:39 AM

One last question, does anyone have an example of sending an email via Exchange Web Service?


Apparently not.  Not sure if this is what you are referring to but MSFT's Exchange Web Services has it's own managed assembly - Microsoft.Exchange.WebServices (in Microsoft.Exchange.WebServices.dll) -  and I am not aware of any examples in the Tech Database.  MSFT does have extensive documentation of the assembly on their MSDN Website.  A quick glance does not reveal anything that you counldn't do using the WinBatch CLR hosting functionality.   I guess this would be your chance to break new ground for the rest of us.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

stanl

I tried that with a simple powershell script to send a test message. It fails with the AutodiscoverUrl method again saying it is blocked as an insecure redirection. If I manually attempt to access the url

https://autodiscover.[mydomain].com/autodiscover/autodiscover.xml it returns 'invalid request' (even after entering my credentials).

td

If you are talking about SMTP then I believe Jim already covered that ground.  If you are talking about MSFT's EWS API, I am not at all familiar with it but it should work if used properly.  MSFT has examples in their documentation that wouldn't be there if they didn't think it works.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

stanl

Quote from: td on December 01, 2017, 01:20:43 PM
If you are talking about SMTP then I believe Jim already covered that ground.  If you are talking about MSFT's EWS API, I am not at all familiar with it but it should work if used properly.  MSFT has examples in their documentation that wouldn't be their if they didn't think it works.

Agreed. But some context is important. From my job, logged into the corporate network, I can automate emails from either my google or mindspring accounts, but not from my Outlook/Exchange given corporate security measures. The AutodiscoverUrl method I referred to in my last post has a second parameter, a delegate callback - and the WB CLR does not do delegates. But for the general purpose of this thread, below is the simple Powershell steps (which can be interpreted into WB CLR, or called directly from it)


Add-Type -Path "C:\Program Files (x86)\Microsoft\Exchange\Web Services\2.1\Microsoft.Exchange.WebServices.dll"


$service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2013_SP1)

$service.Credentials = New-Object Microsoft.Exchange.WebServices.Data.WebCredentials -ArgumentList  [email address], "[Password]"
$service.AutodiscoverUrl('[email address]', '{@True}')
$message = New-Object Microsoft.Exchange.WebServices.Data.EmailMessage -ArgumentList $service
$message.Subject = 'Test is a test'
$message.Body = 'This message is being sent through EWS with PowerShell'
$message.ToRecipients.Add('[recipient]')
$message.SendAndSaveCopy()



so it really isn't that hard - if it works.

JTaylor

Sounds like the issue is what I mentioned.   The Server knows if you are not a "secure" client and it will block any attempt by anything other than the Exchange/Outlook client, unless explicitly permitted.   I don't think it will matter what method you attempt to use.   

Jim

td

That begs the question - what constitutes a secure client?  The server has to recognize something in the exchange between client and server to permit an action.  If it isn't just credentials then what is it?  A user agent string?  A quick reading of the documentation seems to suggest that the correct user-agent string and, of course,  credentials are all that are needed.  If that is the case then the it would just be a matter of getting someone with server admin privileges to add the script's user agent string to an "approved" list.   Another option would be to somehow spoof the scripts user agent string so that it matched an approved user agent string. 

MSFT's EWS documentation makes much of the distinction between authentication and authorization.   Authentication is provided by either NTLM or OAuth.  NTLM requires a domain connection and is supported by dotNet.  But this is likely a different discussion.

"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

td

Quote from: stanl on December 02, 2017, 03:21:19 AM

Agreed. But some context is important. From my job, logged into the corporate network, I can automate emails from either my google or mindspring accounts, but not from my Outlook/Exchange given corporate security measures. The AutodiscoverUrl method I referred to in my last post has a second parameter, a delegate callback - and the WB CLR does not do delegates. But for the general purpose of this thread, below is the simple Powershell steps (which can be interpreted into WB CLR, or called directly from it)

The  AutodiscoverUrl method has two signatures with only one requiring a delegate so the method is usable in a WIL script.

Quote
so it really isn't that hard - if it works.

It is quite simple.   Simpler than I had imaged.  It should easily convert to a WIL script.  A converted and tested WIL script example would make a nice addition to the Tech Database.

"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

stanl

Quote from: td on December 02, 2017, 02:07:48 PM

It is quite simple.   Simpler than I had imaged.  It should easily convert to a WIL script.  A converted and tested WIL script example would make a nice addition to the Tech Database.

And I would be willing to test and report back specific security errors.

stanl

OMG! Thank you Tony.   I removed the second parameter in the Powershell script:


$service.AutodiscoverUrl('[email address]',)


and it worked perfectly. Seems if I knew what I was doing in the first place, I wouldn't need an overloaded callback.   :o

EDIT:

Quote
It should easily convert to a WIL script

Yeah, wish I had time, but need to expand the basic PS script to handle delayed posting. Also not sure about the initial namespace.


td

Here is a very rough translation to WIL script.  Needs a lot of work but may be of use to the original poster as a starting point for a 'real' script.

Code (winbatch) Select
;; Completely untested, full of bugs and guaranteed to fail.
ObjectClrOption('appbase', 'C:\Program Files (x86)\Microsoft\Exchange\Web Services\2')
ObjectClrOption('use', 'Microsoft.Exchange.WebServices')

enumVersion = ObjectType('Microsoft.Exchange.WebServices.Data.ExchangeVersion', some number here)  ; Need the assembly to find version number
objEws = ObjectCreate(' Microsoft.Exchange.WebServices.Data.ExchangeService', emumVersion)

objEws.Credentials = ObjectCreate('Microsoft.Exchange.WebServices.Data.WebCredentials', '[email address]', '[Password]')
objEws.AutodiscoverUrl('[email address]')
objMessage = ObjectCreate('Microsoft.Exchange.WebServices.Data.EmailMessage', objEws)
objMessage.Subject = 'Test is a test'
objMessage.Body = 'This message is being sent through EWS with WinBatch CLR hosting'
objMessage.ToRecipients.Add('[recipient]')
objMessage.SendAndSaveCopy()

"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

stanl

Nice.... I changed to

Code (WINBATCH) Select

ObjectClrOption('useany', 'Microsoft.Exchange.WebServices')


and found the version number by holding ctrl, right clicking on Outlook icon in notification area then clicking connection status. Should have results of test later today.

[EDIT]  my version is 15.0.1104.4000  but the line   [where nVersion = 15.0.1104

Code (WINBATCH) Select

oVersion = ObjectType('Microsoft.Exchange.WebServices.Data.ExchangeVersion', nVersion)


gives error as unsupported Variant Type.

td

Generally, you only need to use the 'useany' option when the assembly resides in the GAC but it doesn't hurt in this case.

Looks like one of my many typos.  Try something like this:

Code (winbatch) Select
enumVersion = ObjectClrType('Microsoft.Exchange.WebServices.Data.ExchangeVersion', '15.0.1104.4000')
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

stanl

getting closer

Code (WINBATCH) Select

oVersion = ObjectClrType('Microsoft.Exchange.WebServices.Data.ExchangeVersion','15.0.1104.4000' ) ;works now
oEws = ObjectCreate(' Microsoft.Exchange.WebServices.Data.ExchangeService', oVersion) ; error as invalid class string



[EDIT]

Probably should be ObjectClrNew

td

"Microsoft.Exchange.WebServices.Data.ExchangeVersion" so it is likely and integer value.  So the version passed as a constructor parameter might simply be the number 7 except that is is cast as the enumeration "Microsoft.Exchange.WebServices.Data.ExchangeVersion" so that the CLR knows which constructor to call.   So an alternative may be the following:

Code (winbatch) Select
enumVersion = ObjectClrNew('Microsoft.Exchange.WebServices.Data.ExchangeVersion')
curVersion   = ObjectClrType('Microsoft.Exchange.WebServices.Data.ExchangeVersion',enumVersion.Exchange2013_SP1)
oEws = ObjectCreate('Microsoft.Exchange.WebServices.Data.ExchangeService', curVersion) ;


Where the supported version are listed as:

        Exchange2007_SP1   - Exchange Server 2007 Service Pack 1 (SP1).
        Exchange2010    - Exchange Server 2010.
        Exchange2010_SP1   - Exchange Server 2010 Service Pack 1 (SP1).
        Exchange2010_SP2   - Exchange Server 2010 Service Pack 2 (SP2).
        Exchange2013   - Exchange Server 2013.
        Exchange2013_SP1   - Exchange Server 2013 Service Pack 1 (SP1).

I would also be wise to remove the lead space from ' Microsoft.Exchange.WebServices.Data.ExchangeService' .
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

stanl

OK. Down to the wire,  I'm using an INI file to hold values but this works now up to

Code (WINBATCH) Select

ObjectClrOption('appbase', cDLL)
ObjectClrOption('useany', 'Microsoft.Exchange.WebServices')
oEws = ObjectClrNew('Microsoft.Exchange.WebServices.Data.ExchangeService')
oVersion = ObjectClrType('Microsoft.Exchange.WebServices.Data.ExchangeVersion', nVersion )

oEws.Credentials = ObjectClrNew('Microsoft.Exchange.WebServices.Data.WebCredentials', cEmail, cPwd)
oEws.AutodiscoverUrl(cEmail)
oMessage = ObjectClrNew('Microsoft.Exchange.WebServices.Data.EmailMessage', oEws)
oMessage.Subject = 'Test is a test'

oMessage.Body = 'This message is being sent through EWS with WinBatch CLR hosting'  ;Error - see attached jpeg
                                                                                                                                ;comment line out and script will send message w/out body

oMessage.ToRecipients.Add('slittlefield1@gmail.com')
oMessage.SendAndSaveCopy()



td

First, an apology for getting sloppy with regard to the enumeration.  I know better but was trying to do too many things as once.

It appears that the "Body" method is actually a class object.  So it needs to be constructed somehow.  Maybe

Code (winbatch) Select
objBody = ObjectCreate('Microsoft.Exchange.WebServices.Data.MessageBody', 'This message is being sent through EWS with WinBatch CLR hosting')
objMessage.Body = objBody


It is just a shot in the dark but it might work.  If it doesn't, it may be because the name space isn't quit right or the constructor parameter needs to be typed using ObjectClrType.  For whatever reasons the CLR is a bit inconsistent about these things.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

stanl

This worked. Parameters are in the INI file, but script is adaptable. I used the body object text, as there is a bodytype that takes text or HTML.

Code (WINBATCH) Select

IntControl(73,1,0,0,0)
cINI=DirScript():"ews.ini"
If ! FileExist(cINI) Then Terminate(@TRUE,"Cannot Continue","Configuration File Is Missing ":cINI)
cDLL=IniReadPvt("Main","dll","",cINI)
cEmail=IniReadPvt("Main","email","",cINI)
cPwd=IniReadPvt("Main","pwd","",cINI)
nVersion=IniReadPvt("Main","version","",cINI)

ObjectClrOption('appbase', cDLL)
ObjectClrOption('useany', 'Microsoft.Exchange.WebServices')
oEws = ObjectClrNew('Microsoft.Exchange.WebServices.Data.ExchangeService')
oVersion = ObjectClrType('Microsoft.Exchange.WebServices.Data.ExchangeVersion', nVersion )

oEws.Credentials = ObjectClrNew('Microsoft.Exchange.WebServices.Data.WebCredentials', cEmail, cPwd)
oEws.AutodiscoverUrl(cEmail)
oMessage = ObjectClrNew('Microsoft.Exchange.WebServices.Data.EmailMessage', oEws)
oMessage.Subject = 'Test Message'
oBody = ObjectClrNew('Microsoft.Exchange.WebServices.Data.MessageBody')
oBody.Text = 'This message is being sent through EWS with WinBatch CLR hosting'
oMessage.Body = oBody

oMessage.ToRecipients.Add('destination@something.com')
oMessage.SendAndSaveCopy()

Exit

td

Thank you for your patience, and for testing and debugging.  I should have taken a little more time to read the class documentation before posting the original hack. 

Hopefully, it helps the OP.   It will definitely find a place in the Tech Database.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

td

Started looking at the example more carefully before creating a Tech Database article and notice that this line:

oVersion = ObjectClrType('Microsoft.Exchange.WebServices.Data.ExchangeVersion', nVersion )

does not do anything.  The only reason to create a typed version number is to pass it to the EWS service object constructor that accepts it but that is not the constructor being use on the line:

oEws = ObjectClrNew('Microsoft.Exchange.WebServices.Data.ExchangeService')

I will remove line begining "oVersion =" for the Tech Database example.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

stanl

Quote from: td on December 06, 2017, 10:00:07 AM
Started looking at the example more carefully before creating a Tech Database article and notice that this line:

oVersion = ObjectClrType('Microsoft.Exchange.WebServices.Data.ExchangeVersion', nVersion )

does not do anything.  The only reason to create a typed version number is to pass it to the EWS service object constructor that accepts it but that is not the constructor being use on the line:

oEws = ObjectClrNew('Microsoft.Exchange.WebServices.Data.ExchangeService')

I will remove line begining "oVersion =" for the Tech Database example.

I commented out that line and my script ran fine.

td

"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

Jeff

Thanks for the in-depth comments, answers and samples. We have an application sitting at client sites that is capable of sending a report email via SMTP or Exchange Web Services. Whoever developed the app, did not add a button to send a test email. Since the app was built on .net my goal was to enhance my existing WB script to send a test email via SMTP or Exchange web services.

Jeff
Jeff

Jeff

I am taking the time to tinker with this. I am using the W18531 article as a beginer testing script. I have insalled the redistibutable Microsoft Exchange dlls. Version 2.1.  I keep getting a 1261 stop message, oEws.AutodiscoverUrl(cEmail). It seems to me their is supposed to be some referal that needs to take place. I have included a screen shot of the extended error message as well. Can someone tell me what else I need to do? I am specifically testing this agianst an customer using micorosofts hosted exchange, office365.

I am sorry to keep posting for help, but I am not a programer by trade and a little blind to the hosted 365 environment. Thanks all!
Jeff

td

You can't use delegates with WinBatch CLR hosting so that is not an option.  Even if you could, it may not fix the problem anyway.  The AutodiscoverUrl method is using your local DNS server to obtain the IP address of the URL in question.  Once it gets the IP address it does whatever it is going to do using the HTTP or HTTPS protocol.   It could be that in your case the default is to use the HTTP protocol and the server is redirecting the request to the HTTPS protocol.   That redirection may be what is causing the problem but it is just a guess.

Unfortunately, I do not have access to an Exchange server that supports EWS and do not have experience with EWS so I can't test anything nor have any hope of sounding knowledgeable.  Several articles on various sites discuss problems with the AutodiscoverUrl method and setting up a proper URL so Google may be your best bet.  Here is but one of many that turned up with a simple search for the method name:

https://msdn.microsoft.com/en-us/library/office/dd633692(v=exchg.80).aspx 
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

Jeff

Jeff

Jeff

Ok, now I am trying to specify the web service end point. Everything I see, it should look like what I have below, but I get a clr typ not found. <head banging on desk>

oEws.Credentials = ObjectClrNew('Microsoft.Exchange.WebServices.Data.WebCredentials', cEmail, cPwd)
;oEws.AutodiscoverUrl(cEmail)
oEws.Url = ObjectClrNew('Microsoft.Exchange.WebServices.Url',cServer)
message("",oEws.Url)
Jeff

td

You didn't say which line is producing the error but the error is likely the result of not providing parameter type information for to one of the class constructors.  You need to provide type information because constructors are often overloaded and the CLR uses type information to create a constructor (or method) signature.  It then matches the signature to the correct constructor.  You would do well to look up the documentation for the classes you want to use on MSFT's website and then use the ObjectClrType function to create typed parameter values to pass to whichever constructor.    There are examples show how to use the ObjectClrType function in the dotNet section of the Tech Support Database. 
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

stanl

Quote from: Jeff on February 02, 2018, 12:53:10 PM
Ok, now I am trying to specify the web service end point. Everything I see, it should look like what I have below, but I get a clr typ not found. <head banging on desk>

oEws.Credentials = ObjectClrNew('Microsoft.Exchange.WebServices.Data.WebCredentials', cEmail, cPwd)
;oEws.AutodiscoverUrl(cEmail)
oEws.Url = ObjectClrNew('Microsoft.Exchange.WebServices.Url',cServer)
message("",oEws.Url)

Jeff, I think you commented out the wrong line - try uncommenting then commenting out the previous line.

td

Quote from: Jeff on February 02, 2018, 12:53:10 PM
Ok, now I am trying to specify the web service end point. Everything I see, it should look like what I have below, but I get a clr typ not found. <head banging on desk>

oEws.Credentials = ObjectClrNew('Microsoft.Exchange.WebServices.Data.WebCredentials', cEmail, cPwd)
;oEws.AutodiscoverUrl(cEmail)
oEws.Url = ObjectClrNew('Microsoft.Exchange.WebServices.Url',cServer)
message("",oEws.Url)

Code (winbatch) Select
;;; MSFT documentation.
;;; https://msdn.microsoft.com/EN-US/library/txt7706a
;;; https://msdn.microsoft.com/en-us/library/microsoft.exchange.webservices.data.webcredentials(v=exchg.80).aspx

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Completely UNTESTED guess!!!!!
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
objEws = ObjectClrNew('Microsoft.Exchange.WebServices.Data.ExchangeService')
objEws.Credentials = ObjectClrNew('Microsoft.Exchange.WebServices.Data.WebCredentials', cEmail, cPwd)
objEws.Uri = ObjectClrNew('System.Uri', cUrl)

"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade