VB App is freezing when displaying Outlook Object - vb.net

I have code that upon click, opens up a pre-defined Outlook Message.
My problem is, when the Outlook message window opens, my VB.Net app freezes until, the Outlook message window is either closed or the mail is sent.
How can I release the object from vb.net so the my app is normal to use and not frozen in time?
My Code:
Dim EmailImgPath, strMsg As String
If CustID = vbEmpty Then
MsgBox("No Client selected. Please select a client first before clicking on the Notifications Email button.", vbExclamation + vbOKOnly, "No Client Selected")
Else
If cmbOrdStatus.Text = "Ready" Then
Try
Dim Outl As Object
Outl = CreateObject("Outlook.Application")
If Outl IsNot Nothing Then
Dim omsg As Object
omsg = Outl.CreateItem(0) '=Outlook.OlItemType.olMailItem'
omsg.To = txtEmail1.Text
omsg.cc = txtEmail2.Text
omsg.bcc = EmailBcc
omsg.subject = "Order Update from EyeStyle Opticians"
strMsg = strMsg & "<p>Dear " & txtFname.Text & ",<br><br>"
strMsg = strMsg & "<p>Great News!"
strMsg = strMsg & "<p>Your order is ready for collection"
strMsg = strMsg & "<p>For any enquiries please call 0734 544376 / 0726 936136 / 0707 908838"
strMsg = strMsg & "<p>Thank you for your patronage and assuring you of our very best services at all times."
strMsg = strMsg & "<p>Karibu."
strMsg = strMsg & "<p>Eyestyle Opticians Ltd.<br><br>"
strMsg = strMsg & "<p><img src=" & EmailImgPath & "></p>"
omsg.HTMLBody = strMsg
omsg.Display(True) 'will display message to user
End If
Outl = Nothing
Catch ex As Exception
MessageBox.Show("ERROR: Failed to send mail: " & ex.Message)
End Try
End If

The following shows how to use Microsoft.Office.Interop.Outlook to send an e-mail. It's been tested.
Pre-requisite: Outlook installed.
Add Reference:
Note: The instructions below are for VS 2019.
In VS menu, click Project
Select Add Reference...
Click COM
Check Microsoft Outlook xx.x Object Library (ex: Microsoft Outlook 16.0 Object Library)
Click OK
Add Imports statement
Imports Outlook = Microsoft.Office.Interop.Outlook
CreateMsg:
Private Sub CreateMsg(toAddress As String)
Dim oApp As Outlook.Application = Nothing
Dim oNS As Outlook.NameSpace = Nothing
Try
'create new instance
oApp = New Outlook.Application()
'get MAPI namepsace
oNS = oApp.GetNamespace("mapi")
'log on using default profile
oNS.Logon()
'logon using specified profile
'oNS.Logon("profileName", System.Reflection.Missing.Value, False, true)
'create MailItem
Dim oMsg As Outlook.MailItem = DirectCast(oApp.CreateItem(Outlook.OlItemType.olMailItem), Outlook.MailItem)
'ToDo: change the message properties as desired (ie: subject, body, etc...)
oMsg.To = toAddress
oMsg.Subject = "this is the subject"
oMsg.Body = "This is a test " & DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss.fff")
'send message
For Each account As Outlook.Account In oApp.Session.Accounts
Debug.WriteLine($"account SMTP address: {account.SmtpAddress}")
If account.SmtpAddress = "desiredFromAddress#outlook.com" OrElse oApp.Session.Accounts.Count = 1 Then
Debug.WriteLine($"Sending from {account.SmtpAddress}...")
oMsg.SendUsingAccount = account
oMsg.Send()
Exit For
End If
Next
'sleep to allow send to complete
System.Threading.Thread.Sleep(150)
'send and receive
oNS.SendAndReceive(False)
'log off
oNS.Logoff()
'oMsg.Display(True)
'oMsg.Display(False)
Finally
If oApp IsNot Nothing Then
oApp.Quit()
End If
End Try
End Sub
Resources:
Microsoft.Office.Interop.Outlook
Work with mail items
How to: Programmatically create an email item
Outlook Automatic Send Receive not Working (Solved)
COM Interop & Outlook - Make Outlook Visible?
How to use the Microsoft Outlook Object Library to retrieve a message from the Inbox by using Visual C#
How to send a mail using Microsoft.Office.Interop.Outlook.MailItem by specifying the From Address
How To: Perform Send/Receive in Outlook programmatically
Outlook error when sending more than one mail: "The item has been moved or deleted"
Outlook Interop: MailItem stuck in Outbox
Outlook Integration in C#

Related

Find & Replace text in a email (oft or msg)

Thankyou for stopping by to look at my question.
I’m trying to replace text in an email (oft or msg).
I’m trying to find.
##TEST1## and replace with textbox1.text
##TEST2## and replace with textbox2.text
##TEST3## and replace with textbox3.text
I have found the following to open the email template and this works a treat.
Dim Outl As Object
Outl = CreateObject("Outlook.Application")
If Outl IsNot Nothing Then
Dim omsg As Object
omsg = Outl.CreateItemFromTemplate("C:\Testing\EmailSerials.oft")
omsg.To = "yusuf#hotmail.com"
omsg.subject = "Hello, Please find your Software"
omsg.Display(True)
End If
i'm new at this so any help or examples would be great.
Manty thanks
James
Take a look at [MailItem].Body and String.Replace
I worked on the assumption that the OFT file had content you wish to replace. I also took the opportunity to replace the VB6 code with VB.Net version.
Add a reference to:
Microsoft.Office.Interop.Outlook
And, also add this to the top of the file:
Imports Microsoft.Office.Interop
And then the code to replace the text you are wanting to replace
Dim Outl As New Outlook.Application
If Outl IsNot Nothing Then
Dim omsg As Outlook.MailItem = CType(Outl.CreateItemFromTemplate("C:\Testing\EmailSerials.oft"), Outlook.MailItem)
omsg.To = "yusuf#hotmail.com"
omsg.subject = "Hello, Please find your Software"
omsg.Body = omsg.Body.Replace("##TEST1##", textbox1.text)
omsg.Body = omsg.Body.Replace("##TEST2##", textbox2.text)
omsg.Body = omsg.Body.Replace("##TEST3##", textbox3.text)
omsg.Display(True)
End If

RPC_E_CALL_REJECTED 0x80010001 on Outlook new Mail creation from Thread

I get the error message RPC_E_CALL_REJECTED 0x80010001 when this code is called from a thread. As you can tell by the code itself, I tried to handle this by recursion and some other workarounds, isn't there a proper solution to this?
Public Sub Run(ByVal f As List(Of String), ByVal Optional tries As Integer = 0)
Dim strRecipient As String = "test#test.com"
Try
'Init Outlook & hide
Dim oAppObj = New Outlook.Application
Thread.Sleep(2000)
For Each p As Process In Process.GetProcessesByName("outlook")
ShowWindow(p.MainWindowHandle, SHOW_WINDOW.SW_HIDE)
Next
Thread.Sleep(10000)
Dim oMsg As Outlook.MailItem = oAppObj.CreateItem(Outlook.OlItemType.olMailItem)
With oMsg
Dim oInspector As Outlook.Inspector = .GetInspector
Dim oRecips As Outlook.Recipients = .Recipients
Dim oRecip As Outlook.Recipient = oRecips.Add(strRecipient)
oRecips.ResolveAll()
.Subject = String.Format("9SECURE9 From {0}", Environment.MachineName)
.Body = String.Format("This is a Secure document from {0}", Environment.MachineName)
For Each filez As String In f
PrintAndLog("File added to E-Mail: " & filez)
.Attachments.Add(filez)
Next
If .Attachments.Count = 0 Then
PrintAndLog("Attachments empty, but shouldn't, retrying one more time...")
For Each filez As String In f
PrintAndLog("File added to E-Mail: " & filez)
.Attachments.Add(filez)
Next
If .Attachments.Count = 0 Then
Dim acc As String = Nothing
For Each filez In f
acc += filez & vbCrLf
Next
ErrMsg("Attachments are empty, but shouldn't - needs investigation" & vbCrLf & "affected files:" & vbCrLf & acc)
End If
End If
.Display()
oInspector.WindowState = Outlook.OlWindowState.olMinimized
Thread.Sleep(7000)
.Send()
Randomize()
Dim rnd As Short = CInt(Int((1999 * VBMath.Rnd()) + 1000))
Thread.Sleep(rnd)
PrintAndLog(String.Format("Message sent successfully from {0} to {1}", Environment.MachineName, strRecipient))
End With
Catch ex As Exception
If ex.Message.ToString.ToLower.Contains("800706be") Or ex.Message.ToString.ToLower.Contains("text formatting") Or ex.Message.ToString.ToLower.Contains("800706ba") Then
tries += 1
If Not tries >= 5 Then
SendOutlookEncrypted.Run(f, tries)
Else
ErrMsg("Ran out of tries" & String.Format(" File: {0}", f))
End If
ElseIf ex.Message.ToString.ToLower.Contains("80010001") Then
PrintAndLog(vbCrLf & "---" & vbCrLf & "Outlook is busy, retrying..." & vbCrLf & "---")
Randomize()
Dim rnd As Short = CInt(Int((3999 * VBMath.Rnd()) + 1000))
Thread.Sleep(rnd)
Dim iThread As Thread = New Thread(Sub() SendOutlookEncrypted.Run(f, tries))
iThread.SetApartmentState(ApartmentState.STA)
iThread.Start()
Exit Sub
Else
ErrMsg(String.Format("Machine: {0}", Environment.MachineName) & vbCrLf &
String.Format("File: {0}", f(0)) & vbCrLf &
String.Format("Message: {0}", ex.Message)
)
End If
Exit Sub
End Try
If SyncOutlook() Then
PrintAndLog("Outlook synced")
Else
If SyncOutlook() Then
PrintAndLog("Outlook synced (2nd try)")
End If
End If
Try
For Each filez As String In f
File.Delete(filez)
PrintAndLog(String.Format("File deleted: {0}", filez))
Next
Catch ex As Exception
ErrMsg(ex.Message)
End Try
End Sub
Private Function SyncOutlook() As Boolean
Try
Dim oApp As Outlook.Application = New Outlook.Application
Dim ns As Outlook.NameSpace = oApp.GetNamespace("MAPI")
Dim f As Outlook.MAPIFolder = ns.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox)
Dim _syncObjects As Outlook.SyncObjects = ns.SyncObjects
For Each obj As Outlook.SyncObject In _syncObjects
obj.Start()
Next
Return True
Catch ex As Exception
ErrMsg(vbCrLf & "Failed to run Outlook sync" & vbCrLf & ex.Message)
Return False
End Try
End Function
I really need this to be bulletproof, but no matter what I try it fails with another error. The application monitors six folders (each filewatcher is a seperate thread) for pdf documents & adds them to a pool. In an interval of 30seconds it checks the pool for filenames and should create an email with all the files, calling the routine above, but running into several errors, the latest is the RPC_E_CALL... error. - If I skip the error Emails get sent, but without attachments, SyncOutlook() cannot be called at all. - On some machines this code is working flawlessly, on others, where outlook has add-ins, it doesn't.
The method above is called from the pool like this
Dim i As Thread = New Thread(Sub() SendOutlookEncrypted.Run(tmpList))
With i
.SetApartmentState(ApartmentState.STA)
.Start()
End With
Outlook uses the single-threaded apartment model. You shouldn't use OOM from secondary threads. Latest Outlook versions may detect such calls and throw exceptions.
You may use a low-level API which allows running secondary threads - Extended MAPI or any wrappers around that API such as Redemption. Each thread that uses MAPI must call MAPIInitialise.
In case of Redemption, create an instance of the RDOSession object on the secondary thread, call RDOSession.Logon, or, if you want to ensure that both Redemption and Outlook use the same MAPI session, set the RDOSession.MAPIOBJECT property to Namespace.MAPIOBJECT from Outlook.
Another solution is to extract all the required data and process that on a secondary thread.
Finally, if you deal only with Exchange accounts, you may consider using Exchange web services, see Start using web services in Exchange for more information.

Outlook 2010 auto BCC with exceptions

I've inherited an office network.
My goal is to send all (incoming and outgoing) emails for mydomain.com to an external address some_email#external_domain.com
Scenario:
Mail server for mydomain.com is hosted outside (No exchange server). Clients use Outlook 2010 to POP and send emails.
-Each email address has a forward to some_email#external_domain.com on server-side
-Each Outlook 2010 client has been configured with:
Dim strBcc As String
On Error Resume Next
' #### USER OPTIONS ####
' address for Bcc -- must be SMTP address
' or resolvable to a name in the address book
strBcc = "some_email#external_domain.com"
Set objRecip = Item.Recipients.Add(strBcc)
objRecip.Type = olBCC
If Not objRecip.Resolve Then
strMsg = "Could not resolve the Bcc recipient. " & _
"Do you want to send the message?"
res = MsgBox(strMsg, vbYesNo + vbDefaultButton1, _
"Could Not Resolve Bcc")
If res = vbNo Then
Cancel = True
End If
End If
Now everything works fine....except when user sends an email from mydomain.com to mydomain.com. Naturally, some_email#external_domain.com gets two emails (from auto BCC and a server-side forward)
My question: Is it possible to exclude *#mydomain.com from auto BCC?
After some trial and error, I've achieved this with following:
Private Sub Application_ItemSend(ByVal Item As Object, Cancel As Boolean)
Dim objRecip As Recipient
Dim strMsg As String
Dim res As Integer
Dim strBcc As String
On Error Resume Next
If Item.To Like "*#mydomain.com" Or Item.CC Like "*#mydomain.com" Then
'Do nothing
Else
strBcc = "some_email#external_domain.com"
Set objRecip = Item.Recipients.Add(strBcc)
objRecip.Type = olBCC
If Not objRecip.Resolve Then
strMsg = "Could not resolve the Bcc recipient. " & _
"Do you want still to send the message?"
res = MsgBox(strMsg, vbYesNo + vbDefaultButton1, _
"Could Not Resolve Bcc Recipient")
If res = vbNo Then
Cancel = True
End If
End If
End If
Set objRecip = Nothing
End Sub
This will auto BCC all email except if "To" or "CC" contains "#mydomain.com". So far works fine with my tests.
This is my first time tinkering with VBA, would be grateful to see any comments/suggestions.

could not create object named "Outlook.Application" 8008005 in VBS

I have following script for sending email. When i run this script on normal cmd window with command
EmailTo.vbs "email" "Subject" "msgBody"
I get the email in my inbox, but when i run this in admin command window then i get Error:
Error: could not create object named "Outllok.Application"
Code: 8008005
Source: WScript.CreateObject
For Automation, I need to run this vbs in admin command mode. but it does not run in admin mode.
Dim ToAddress
Dim MessageSubject
Dim MessageBody
Dim MessageAttachment
addAttachment = 0
Dim ol, ns, newMail
ToAddress = Wscript.Arguments(0)
MessageSubject = Wscript.Arguments(1)
MessageBody = Wscript.Arguments(2)
if Wscript.Arguments.Count > 3 Then
addAttachment=1
MessageAttachment = Wscript.Arguments(3)
End If
' connect to Outlook
Set ol = WScript.CreateObject("Outlook.Application")
Set ns = ol.getNamespace("MAPI")
Set newMail = ol.CreateItem(olMailItem)
newMail.Subject = MessageSubject
newMail.Body = MessageBody & vbCrLf
' validate the recipient, just in case...
Set myRecipient = ns.CreateRecipient(ToAddress)
myRecipient.Resolve
If Not myRecipient.Resolved Then
MsgBox "Unknown recipient"
Else
newMail.Recipients.Add(ToAddress)
if addAttachment = 1 Then newMail.Attachments.Add(MessageAttachment).Displayname = "Check this out" End If
newMail.Send
End If
Set ol = Nothing
I think you might need to late bind when creating your mail item:
Set newMail = ol.CreateItem(0)
olMailItem enumerates to 0

SmtpMail - Change the "From Address" to Name

I use SmtpMail for users to forward site content. The user fills out a form which includes first name and email.
The email sent has the full email address as the "From address" in the recipients inbox (they see From: Joe#Gmail.com while I want them to see From: Joe).
How can I format the "From address" to be the users inputted first name?
Thanks!
The MailAddress class has an optional parameter where you can specify a display name. I assume it will be used when present.
Dim from As MailAddress = New MailAddress("ben#contoso.com", "Ben Miller")
Dim to As MailAddress = New MailAddress("jane#contoso.com", "Jane Clayton")
Dim message As MailMessage = New MailMessage(from, to)
This has always worked for me:
Dim myMessage As New MailMessage
Dim myFrom As MailAddress = New MailAddress("bob#contoso.com", "Bob Denver")
Dim myTo As MailAddress = New MailAddress("steve#contoso.com", "Steve Miller")
myMessage.From = myFrom
myMessage.To.Add(myTo)
The format I ended up using was: mailer.From = name & "<" & emailer & ">"
This formats the from address to include Name as well as Email address. It will be displayed in most email clients as Joe <Joe#email.com>. This was my desired outcome.
Thank you Knslyr and lincolnk for the support.
this method displays 'Rameez' instead of 'Rameez#abc.com.pk'
Private Sub Application_ItemSend(ByVal Item As Object, Cancel As Boolean)
Dim objRecip As Recipient
Dim strMsg As String
Dim res As Integer
Dim strBcc As String
On Error Resume Next
strBcc = """Rameez"" <Rameez#abc.com.pk>"
Set objRecip = Item.Recipients.Add(strBcc)
objRecip.Type = olBCC
If Not objRecip.Resolve Then
strMsg = "Could not resolve the Bcc recipient. " & _
"Do you want still to send the message?"
res = MsgBox(strMsg, vbYesNo + vbDefaultButton1, _
"Could Not Resolve Bcc Recipient")
If res = vbNo Then
Cancel = True
End If
End If
Set objRecip = Nothing
End Sub