How does this script (sending email using Outlook) work? - vbscript

Can anyone help me understand how MAPI works? I have this simple vbscript that uses MAPI to send an email out. It works perfectly fine, but, I don't know how it is doing that...what's happening in the background?
Const ForReading = 1
Set args = WScript.Arguments
sqlFile = args.Item(0)
logFile = args.Item(1)
Dim ToAddress
Dim FromAddress
Dim MessageSubject
Dim MyTime
Dim MessageBody
Dim MessageAttachment
Dim ol, ns, newMail
MyTime = Now
ToAddress = "my#email.com"
MessageSubject = "Subject goes here"
MessageBody = "Body message goes here."
MessageAttachment = ""&logFile&""
Set ol = WScript.CreateObject("Outlook.Application")
Set ns = ol.getNamespace("MAPI")
Set newMail = ol.CreateItem(olMailItem)
newMail.Subject = MessageSubject
newMail.Body = MessageBody & vbCrLf & MyTime
newMail.RecipIents.Add(ToAddress)
newMail.Attachments.Add(MessageAttachment)
newMail.Send
Thanks in advance...

What you are doing in this code above is actually using the installed version of Outlook to send the mail message. Outlook may very well be using MAPI, but the API interface you are actually using here is COM based automation of Outlook. Code such as this will utilize Outlook to send email through any mail transportation system that Outlook can be configured to use. However, that is a lot of overhead just to send an email message. Depending on what email server(s) you might have local to you on your network or via there may be much more efficient ways to send email. But if this works fine and meets your needs currently, don't take that to mean that there is anything wrong with doing it the way you have above. It is all about understanding the tools available to you and how to best apply them to your particular problem space.
In your code, you are controlling Outlook and telling it to create an email message. You are passing off the message to Outlook and then Outlook is actually using the configured profile it has to determine how to hand off the message to a configured Exchange server or other installed/configure mail transport agents (mta).

Related

How to set unlimited timeout just for exact tasks

We have a web based windows software with thousands of user. For some actions we send SMS and notification to many users. I don't want to set a long timeout on whole IIS settings. How can I set unlimited timeout only for sending messages?
this is the function that sends SMS to thousands of users:
for x=1 to 1000000
call sendSMS(PhoneNumber(x),"test message")
next
function sendSMS(PhoneNumber,message)
posturl="http://URL TO SMS SERVICE"
posturl=posturl & "username=user"
posturl=posturl & "&password=password"
posturl=posturl & "&dstaddress=" & PhoneNumber
posturl=posturl & "&message=" & server.Urlencode(message)
set xmlhttp = server.Createobject("MSXML2.ServerXMLHTTP")
xmlhttp.Open "POST",posturl,false
xmlhttp.send
myresult= xmlhttp.responseText
end function
I think you're looking for the Server.ScriptTimeout property. You can set it to override the maximum amount of time that the script can run just for that request.
Server.ScriptTimeout = 5000 ' or whatever
Note that it'd generally be best practice to not actually have the connection stay open for a long-running request, but put some sort of message in some sort of queue and have some sort of back-end service that handles the batch processing. But this approach may be adequate depending on your exact requirements.

Close Open Excel Instance

Could someone please let me know if the following simple VBScript is correct?
It is supposed to close Excel after other processes have run (and left Excel open), but it doesn't work.
Set MyApp = CreateObject("Excel.Application")
MyApp.Quit
CreateObject creates a new object. If I understand your question correctly you want to attach to already running (orphaned) Excel processes to terminate them. You can do that with GetObject:
On Error Resume Next
Do
Set xl = GetObject(, "Excel.Application")
status = Err.Number
If status = 0 Then
For Each wb in xl.Workbooks
wb.Close False 'discard changes in open workbooks
Next
xl.Quit
ElseIf status <> 429 Then
WScript.Echo Err.Number & ": " & Err.Description
WScript.Quit 1
End If
Until status = 429
On Error Goto 0
Note that this will try to close all running Excel instances, discarding all changes in open workbooks. If you want it to save changes in open workbooks change the argument of the Close method to True. If you have Excel instances you want to keep running, you need to add code to exclude them from being closed.
Note also, that this will not forcibly terminate unresponsive instances. You'd need to kill the process for that:
Set wmi = GetObject("winmgmts://root/cimv2")
For Each xl In wmi.ExecQuery("SELECT * FROM Win32_Process WHERE Name = 'excel.exe'")
xl.Terminate
Next
Try this please.
ThisWorkbook.Saved = True
Application.Quit
CreateObject creates a COM object, so your
Set MyApp = CreateObject("Excel.Application")
starts a new Excel process. Use GetObject to "retrieve an existing object with the specified ProgID". See this for theory and praxis.

Outlook Meeting Requests auto accept and delete

We have a large amount of meeting requests we need to send out. We would like to prevent the users getting large amount of meeting requests to accept so I have found online the following script to auto accept meetings and then delete from inbox. This is the script:
Sub AutoAcceptMeetings(oRequest As MeetingItem)
If oRequest.MessageClass <> "IPM.Schedule.Meeting.Request" Then
Exit Sub
End If
Dim oAppt As AppointmentItem
Set oAppt = oRequest.GetAssociatedAppointment(True)
Dim oResponse
Set oResponse = oAppt.Respond(olMeetingAccepted, True)
oResponse.Send
oRequest.Delete
End Sub
What I now require is the ability to force all mailboxes in our exchange to run this script when receiving a Meeting request with a specified heading in the subject line.
You would need to create a COM addin that runs that code and install it on all machines where you want to auto accept meeting requests.
If you do not want to check the subject of a request, you can configure each mailbox to auto accept all requests (File | Options | Calendar | Auto accept or decline).

How to know a winsock has completed receiving file?

I'm using the Winsock control:
Private Sub Form_Load()
Winsock1.Connect "stackoverflow.com", 80
End Sub
Private Sub Winsock1_Close()
Winsock1.Close
End Sub
Private Sub Winsock1_Connect()
Winsock1.SendData "GET /questions/8624871/vb6-alternative-to-inet-webbrowser-control HTTP/1.1" & vbCrLf & "Host: stackoverflow.com" & vbCrLf & vbCrLf
End Sub
Private Sub Winsock1_DataArrival(ByVal bytesTotal As Long)
Dim s As String
Winsock1.GetData s, vbString
RichTextBox1.Text = RichTextBox1.Text & s
End Sub
How can I know that the control has completed receiving the file when the header doesn't contain Content-Length?
I've heard of some ways, like when Winsock1.state is 0 it means that the connection is closed, but sometimes it remains in some other state, like 7, so I need another solution.
You need to parse the received HTTP headers. RFC 2616 Section 4.4 explains how they tell you the length of the data and how the data needs to be read.
There are a number of components you can use in VB6 for making HTTP requests. Some come with VB6, some are part of Windows now. Most of these are far better than rolling your own HTTP on top of TCP using the Winsock control. Header processing is just one of the things they can assist you in.
If you still want to roll your own HTTP client, you should read the HTTP spec rather than guessing.
HTTP transfers use the Content-Length header to determine the length of the data. If the header is missing, then the end of data is signalled by the connection closing.

CDO.Message - to many connections

I am writing a newsletter application using CDO.Message. But get an error back that we have to many connections. Seems they have a limit of 10 simultaneous connections.
So, is there a way to send several messages on one connection, or disconnect faster?
There is a cdo/configuration/smtpconnectiontimeout parameter, but I think that's more about how long the sender will try.
(If we send,ant it fails, it will succeed again after some minutes, probably meaning that the connection is disconnected).
(We are using CDO partly because we are pulling the HTML message body from a webserver)
Edit:
Public Sub ipSendMail(ByVal toEmail As String, ByVal fromEmail As String, ByVal subject As String, ByVal url As String)
Dim iMsg As Object
Set iMsg = CreateObject("CDO.Message")
iMsg.From = fromEmail
iMsg.To = toEmail
iMsg.Subject = subject
iMsg.CreateMHTMLBody(url)
iMsg.Configuration.Fields.Item _
("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2
iMsg.Configuration.Fields.Item _
("http://schemas.microsoft.com/cdo/configuration/smtpserver") = "relay.wwwwwwwwww.net"
iMsg.Configuration.Fields.Item_
("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = 25
iMsg.Configuration.Fields.Item _
("http://schemas.microsoft.com/cdo/configuration/smtpconnectiontimeout") = 0
iMsg.Configuration.Fields.Update()
iMsg.Send()
Set iMsg = Nothing
End Sub
Try to use SMTP instead of CDO, System.Web.Mail.SmtpMail
You could implement a queue, that is processed by a background thread. The background thread would only send one message at a time.
You can store the email in a database table, which is processed by a scheduled task or a stored procedure. Those can again send one mail at a time, and have the advantage of being able to retry, if it goes wrong.
Ordinarily you only need one connection regardless of how many messages you are sending.
Perhaps you are not releasing something that you should be.
Edit: Just a thought, the SMTP server you are sending to, it wouldn't happen to be host on an XP box perhaps for testing reasons?
Edit: Ok so your SMTP server is fine.
What platform is the server supplying the result of the URL?
I know that CDO can be quirky at times, so these are the possible suggestions that I would make:
A queue would probably work the best for you. After that, I would consider setting up a local SMTP server without inbound connection limits that uses a smarthost to queue up your outbound messages. (This could actually be written fairly easily. The "S" is for "Simple" and it actually is.)
If all else fails... You could always roll your own mailer component implementing RFCs 2821 and 2822 (or whatever the latest and greatest RFCs are for SMTP and message format)
EDIT: If the newsletter you are sending out is identical for all recipients, you can address it to a dummy recipient (i.e. newsletter#yourdomain.com) and BCC it to the recipient list (or a subset of the recipient list). Just be careful not to get flagged as unsolicited commercial email. Let your provider know what you are doing. They have to deal with the complaints, and you are the one paying the bill. Letting them know that complaints would be mostly unwarranted (and few and far between) will help to assuage their natural risk aversion.

Resources