Send instant message on Office Communicator using Excel VBA - vba

I want to send instant message using Office Communicator and Excel VBA. I use an excel sheet containing a list of Email IDs.
**A B C**
Serial No Name Email
1 abc abc.abc#abc.com
2 xyz xyz.xyz#xyz.com
3 pqr pqr.pqr#pqr.com
I wrote the below code to send message. But it is not working. I have enabled Communicator reference in VBA.
Sub sendIM()
Dim msgr As CommunicatorAPI.IMessengerConversationWndAdvanced
Dim ToUser As String
Dim message As String
Application.ScreenUpdating = True
For Each cell In Columns("C").Cells.SpecialCells(xlCellTypeConstants)
On Error Resume Next
If cell.Value Like "?*#?*.?*" Then
ToUser = Chr(34) & cell.Value & Chr(34)
'MsgBox ToUser
message = "Hi " & Cells(cell.Row, "B").Value _
& vbNewLine & vbNewLine & _
"HOW ARE YOU"
Set msgr = Messenger.InstantMessage(ToUser)
msgr.SendText (message)
End If
Next cell
Application.ScreenUpdating = True
End Sub
For a single email id, it is working. I use the below mentioned code to send single message.
Sub sendIM()
Dim msgr As CommunicatorAPI.IMessengerConversationWndAdvanced
Dim ToUser As String
Dim message As String
Application.ScreenUpdating = True
ToUser = "abc.abc#abc.com"
message = "hai"
On Error Resume Next
Set msgr = Messenger.InstantMessage(ToUser)
msgr.SendText (message)
Application.ScreenUpdating = True
End Sub
But I need to loop through the sheet so that the message is sending to everyone. What changes I have to make this work ?
Note: The Email Ids here I mentioned is not real.

I have never worked with Office Communicator but since you are saying that the second code works then try this. (UNTESTED)
Sub SendIM()
Dim msgr As CommunicatorAPI.IMessengerConversationWndAdvanced
Dim ToUser As String, message As String
Dim aCell As Range
Dim ws As Worksheet
'~~> Change this to the relevant sheet
Set ws = ThisWorkbook.Sheets("Sheet1")
Application.ScreenUpdating = False
With ws
'~~> Why On Error Resume next? If you know what error you are going to get
'~~> Then simply handle it. For the time being, I am skipping the record
'~~> Also keeping it out of the loop
On Error GoTo SkipIT
For Each aCell In .Columns("C").Cells.SpecialCells(xlCellTypeConstants)
If aCell.Value Like "?*#?*.?*" Then
ToUser = aCell.Value '<~~ Don't need quotes
message = "Hi " & .Cells(aCell.Row, "B").Value _
& vbNewLine & vbNewLine & _
"HOW ARE YOU"
Set msgr = Messenger.InstantMessage(ToUser)
msgr.SendText (message)
DoEvents '<~~ Let excel send the message. Give it time
End If
SkipIT:
Next aCell
End With
Application.ScreenUpdating = True
End Sub
EDIT
An improved version. Takes care of error handling
Sub sendIM()
Dim msgr As CommunicatorAPI.IMessengerConversationWndAdvanced
Dim ToUser As String, message As String
Dim aCell As Range
Dim ws As Worksheet
'~~> Change this to the relevant sheet
Set ws = ThisWorkbook.Sheets("Sheet1")
Application.ScreenUpdating = False
With ws
For Each aCell In .Columns("C").Cells.SpecialCells(xlCellTypeConstants)
If aCell.Value Like "?*#?*.?*" Then
ToUser = aCell.Value '<~~ Don't need quotes
message = "Hi " & .Cells(aCell.Row, "B").Value _
& vbNewLine & vbNewLine & _
"HOW ARE YOU"
'~~> Only place I can think an error could happen
On Error Resume Next
Set msgr = Messenger.InstantMessage(ToUser)
'~~> Check if the object is created
If Not msgr Is Nothing Then msgr.SendText (message)
Set msgr = Nothing
On Error GoTo 0
DoEvents '<~~ Let excel send the message. Give it time
End If
Next aCell
End With
Application.ScreenUpdating = True
End Sub

Related

Copy row based on content and paste it in different sheets which are selected based on the content of the row

We've created a order sheet for all our machines, the main sheet is 'Order Sheet'.
And we're sending this sheet to the purchasing department at the end of the day.
When we run the macro to email the file, we wanted the macro to also copy each row to the specific machine worksheet. Eg. rows marked as 'Slicer' to go to the 'Slicer' sheet, 'blender' to 'blender', etc.
This is what I've got so far:
Sub PrintToNetwork()
ActiveWorkbook.Save
Range("A2:N25").Font.Size = 11
Dim OutApp As Object
Dim OutMail As Object
Dim answer As Integer
answer = MsgBox("Are you sure you want to Print & Send the sheet?", vbYesNo + vbQuestion, "Empty Sheet")
If answer = vbYes Then
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.CreateItem(0)
On Error Resume Next
With OutMail
.To = ""
.CC = ""
.BCC = ""
.Subject = "Retail Order Sheet"
.Body = "Hi Andy, Please order."
.Attachments.Add ActiveWorkbook.FullName
.Send
End With
On Error GoTo 0
Set OutMail = Nothing
Set OutApp = Nothing
Range("A1:N25").Select
ActiveSheet.PageSetup.PrintArea = "$A$1:$N$25"
oldprinter = Application.ActivePrinter
For i = 0 To 15
curNePrint = Format(i, "00")
On Error Resume Next
Application.ActivePrinter = "\\10.17.0.9\CCFN_Retail_MFP_BW on Ne" & curNePrint & ":"
Next i
ActiveWindow.Selection.PrintOut Copies:=1
Application.ActivePrinter = oldprinter
On Error GoTo 0
Else
End If
End Sub
Assuming the rows' location on the destination worksheet is determined by examining the same column as the one containing the worksheet names, something like the following might do the trick.
The DispatchRows sub scans prngWorksheetNames, looking for worksheets that exist by name.
You must call DispatchRows by passing it the range containing the worksheet names. For example, if the source worksheet names are on worksheet Summary, range C2:C50, you'd call DispatchRows ThisWorkbook.Worksheets("Summary").Range("C2:C50").
Option Explicit
'Copies entire rows to worksheets whose names are found within prngWorksheetNames.
'ASSUMPTION: on the destination worksheet, a copied row is appended at the lowest empty spot in the same column as prngWorksheetNames.
Public Sub DispatchRows(ByVal prngWorksheetNames As Excel.Range)
Dim lRow As Long
Dim rngWorksheetName As Excel.Range
Dim sDestWorksheetTabName As String
Dim oDestWs As Excel.Worksheet
Dim bScreenUpdating As Boolean
Dim bEnableEvents As Boolean
On Error GoTo errHandler
bScreenUpdating = Application.ScreenUpdating
bEnableEvents = Application.EnableEvents
Application.ScreenUpdating = False
Application.EnableEvents = False
For lRow = 1 To prngWorksheetNames.Rows.Count
Set rngWorksheetName = prngWorksheetNames.Cells(lRow, 1)
sDestWorksheetTabName = CStr(rngWorksheetName.Value)
If TryGetWorksheetByTabName(ThisWorkbook, sDestWorksheetTabName, oDestWs) Then
'Make sure there are no active autofilters on the destination worksheet, as they would typically interfere with the copy operation.
If oDestWs.FilterMode Then
oDestWs.ShowAllData
End If
'Copy and paste.
rngWorksheetName.EntireRow.Copy
oDestWs.Cells(oDestWs.Rows.Count, prngWorksheetNames.Column).End(xlUp).Offset(1).EntireRow.PasteSpecial xlPasteAll
End If
Next
Cleanup:
On Error Resume Next
Set rngWorksheetName = Nothing
Set oDestWs = Nothing
Application.CutCopyMode = False
Application.EnableEvents = bEnableEvents
Application.ScreenUpdating = bScreenUpdating
Exit Sub
errHandler:
MsgBox Err.Description, vbExclamation + vbOKOnly, "Error"
Resume Cleanup
End Sub
'Returns True, and a reference to the target worksheet, if worksheet psName is found by name on pwbkHost.
Public Function TryGetWorksheetByTabName(ByVal pwbkHost As Excel.Workbook, ByVal psName As String, ByRef pshtResult As Excel.Worksheet) As Boolean
Set pshtResult = Nothing
On Error Resume Next
Set pshtResult = pwbkHost.Worksheets(psName)
TryGetWorksheetByTabName = Not pshtResult Is Nothing
End Function
Here is very simple script to achieve what you want. Insert in your code appropriately, or call it from your macro. I tested this many times to make sure it works.
Sub CopyLines()
Dim mySheet
Dim LastRow As Long
Dim LastShtRow As Long
Dim j
LastRow = Sheets("Order Sheet").Cells(Rows.Count, 1).End(xlUp).Row
For j = 2 To LastRow Step 1
mySheet = Range("B" & j).Value
LastShtRow = Sheets(mySheet).Cells(Rows.Count, 1).End(xlUp).Row
Range("A" & j & ":" & "N" & j).Copy
Sheets(mySheet).Range("A" & LastShtRow + 1).PasteSpecial xlPasteValues
Next j
Application.CutCopyMode = False
End Sub

Opening Outlook through VBA crashes for unknown reason

I am using this code to open a list of email-adresses directly in outlook. The email list consists of roughly 100 email-adresses.
If I let the code run for only parts of the list it works just fine for all different parts, but as soon as I let it run for the whole list I get a runtime error '5. Does anyone have a suggestion what causes this problem? I would be very thankful.
If ActiveWorkbook.Worksheets("Output").Range("I10").Value = "Wahr" Then
Dim strAddress As String
Dim lastCell As Long
Dim i As Integer
Worksheets("Output").Activate
lastCell = Range("B" & Rows.Count).End(xlUp).Row
For i = 13 To lastCell
If strAddress = "" Then
strAddress = Cells(i, 2).Value
Else
strAddress = strAddress & ";" & Cells(i, 2).Value
End If
Next i
ActiveWorkbook.FollowHyperlink Address:="mailto:" & strAddress 'this line gives me the error
End If
EDIT: The weird thing is, that it doesnt really matter which "groups" I choose. It seems to be a question of how many adresses I pick.
Not sure what you mean by I am using this code to open a list of email-adresses directly in outlook.
The code appears to create a single blank email with each cell in B13 downwards providing the email addresses?
Maybe this code below will help.
It uses late binding (so no references needed) to get a reference to Outlook, it then creates an email and adds the email addresses to it as recipients before finally displaying it. You can change the .Display to .Send to send the email rather than just display it.
Public Sub Test()
Dim oOL As Object
Dim oMail As Object
Dim rLastCell As Range
Dim rAddRange As Range
Dim rCell As Range
Set oOL = CreateOL
With ThisWorkbook.Worksheets("Output")
Set rLastCell = .Cells(.Rows.Count, 2).End(xlUp)
Set rAddRange = .Range("B13", rLastCell)
End With
Set oMail = oOL.CreateItem(0)
With oMail
For Each rCell In rAddRange
.Recipients.Add rCell.Value
Next rCell
.Display
End With
End Sub
Public Function CreateOL() As Object
Dim oTmpOL As Object
On Error GoTo ERROR_HANDLER
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'Creating an instance of Outlook is different from Excel. '
'There can only be a single instance of Outlook running, '
'so CreateObject will GetObject if it already exists. '
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Set oTmpOL = CreateObject("Outlook.Application")
Set CreateOL = oTmpOL
On Error GoTo 0
Exit Function
ERROR_HANDLER:
Select Case Err.Number
Case Else
MsgBox "Error " & Err.Number & vbCr & _
" (" & Err.Description & ") in procedure CreateOL."
Err.Clear
End Select
End Function

How can make Sheet to activate in VBA from a variable? (A cell particularly)

I am actually quite new to VBA but I am doing some coding to streamline my office work. I understand this would be some amateur level questions to most of you but I tried to google for quite a while and I do not find satisfactory answer.
I have an excel write up that based on the inputted parameters, It should ultimately refer to the correct sheet -> copy the selected cells -> Generate an e-Mail with the body pasting the copied cells along with an attachment
I can do most of the parts, just that I cannot reference the "Correct Sheet" as a variable in my codes. Please shed some lights on for me. Thank you.
Here are most of the codes, the rest are irrelevant and too clumsy to paste all I guess.
Sub GenerateEmail()
Dim olApp As Object
Dim olMailItm As Object
Dim iCounter As Integer
Dim Dest As Variant
Dim SDest As String
Dim StrAtt1 As String
Dim rng As Range
Set rng = Nothing
On Error Resume Next
Set rng = Sheets("test").Range("A1:Q500").SpecialCells(xlCellTypeVisible)
On Error GoTo 0
If rng Is Nothing Then
MsgBox "The selection is not a range or the sheet is protected" & _
vbNewLine & "please correct and try again.", vbOKOnly
Exit Sub
End If
With Application
.EnableEvents = False
.ScreenUpdating = False
End With
Set olApp = CreateObject("Outlook.Application")
Set olMailItm = olApp.CreateItem(0)
On Error Resume Next
With olMailItm
SDest = ""
StrAtt1 = ThisWorkbook.Path & "\PDF\" & Sheets("Email_Generator").Range("B16")
.To = Worksheets("Email_Generator").Range("B14")
.CC = "Myself"
.BCC = ""
.Subject = Worksheets("Email_Generator").Range("B18")
.HTMLBody = RangetoHTML(rng)
.attachments.Add StrAtt1
.Display
End With
Set olMailItm = Nothing
Set olApp = Nothing
End Sub
Specifically, I would like this code "Sheets("test") as a Cell in Sheet "Test" that is a variable based on the paramters I have inputted in my excel so that this code will reference to the correct worksheet
Set rng = Sheets("test").Range("A1:Q500").SpecialCells(xlCellTypeVisible)
But when I identify the sheet as a named sheet e.g. Sheets("Email1"), it perfectly works, just that it cannot become a variable.
I hope this post is not too long to read because I tried to be as specific as possible. Thank you to all who reads this and tries to help. I really appreciate it.
This function to return the worksheet name selected by the user from an InputBox. If the user cancels or enters an invalid number then the function returns a zero length string.
Sub TestFunction()
Dim SheetName As String
Dim rng As Range
SheetName = getSheetNameFromInputBox
If Len(SheetName) = 0 Then
MsgBox Prompt:="Try Again", Title:="Invalid Sheet"
Exit Sub
End If
Set rng = Sheets(SheetName).Range("A1:Q500").SpecialCells(xlCellTypeVisible)
MsgBox rng.Address(External:=True)
End Sub
Function getSheetNameFromInputBox() As String
Dim ws As Worksheet
Dim Prompt As String
Dim result
For Each ws In ThisWorkbook.Worksheets
If Not ws.Name = "Sheet3" Then
Prompt = Prompt & ws.Index & ") " & ws.Name & vbCrLf
End If
Next
result = InputBox(Prompt:=Prompt, Title:="Enter the Worksheet number", Default:=1)
On Error Resume Next
If IsNumeric(result) Then getSheetNameFromInputBox = Worksheets(CInt(result)).Name
On Error GoTo 0
End Function

Call Function To Send Email Without So Much Code In Excel

I have an excel spreadsheet that select pre-defined cells and from this creates and email when a user presses a button. This worked fine when I had about 3 to 4 rows of data but now I have over 500 rows.
What I would like to do is instead of duplicating the code for each row is have one function that gets called on each time. I want the code to work out the row from a link at the end of the Row (which I also need to figure out how to link to the VBA, I know how to do it via a button but a link at the end of each row would be much better). The Link will say send email. If the user presses this link, then it will select the row the link is on and send the email. Hope that makes sense. I just wanted 1 function this could be called from. Instead of having to duplicate the code each time for each row.
Any good ways of doing this? Please see my code and spreadsheet below.
Sub SendEmail()
Dim objOutlook As Outlook.Application
Set objOutlook = New Outlook.Application
Dim objEmail As Outlook.MailItem
Set objEmail = objOutlook.CreateItem(olMailItem)
objEmail.Subject = Cells(2, 1).Text
objEmail.Body = "============" & vbNewLine & Cells(2, 3).Text & vbNewLine & "============" & vbNewLine & Cells(2, 6).Text
objEmail.To = Cells(2, 5).Text
objEmail.SentOnBehalfOfName = "test#test.com"
objEmail.Display
End Sub
I have also attached an example of my spreadsheet. Please note the full on spreadsheet has over 500 records. This is a much condensed version:
>> LINK to sample workbook
You can also try below:
Sub SendEmail(r As Range)
Dim objOutlook As Outlook.Application
Set objOutlook = New Outlook.Application
Dim objEmail As Outlook.MailItem
Set objEmail = objOutlook.CreateItem(olMailItem)
With objEmail
.Subject = r.Value2
.Body = "============" & vbNewLine & r.Offset(0, 2).Value2 & vbNewLine & _
"============" & vbNewLine & r.Offset(0, 5).Value2
.To = r.Offset(0, 4).Value2
.SentOnBehalfOfName = "test#test.com"
.Display
End With
End Sub
Then test it:
Sub Test()
Dim lr As Long, cel As Range
With Sheets("SheetName")
lr = .Range("A" & .Rows.Count).End(xlUp).Row
If lr = 1 Then Msgbox "No email to send": Exit Sub
For Each cel In .Range("A2:A" & lr)
SendEmail cel
Next
End With
End Sub
Edit: To send mail when hyperlink is pressed, you can use a worksheet event.
Private Sub Worksheet_FollowHyperlink(ByVal Target As Hyperlink)
Application.EnableEvents = False
On Error GoTo halt
If Target.Name = "Send Mail" Then '<~~ Check which hyperlink is pressed
'*** This will call the SendEmail routine above and pass
'*** the range where the hyperlink is on
'*** Take note of the Offset(0, -5). I just based it on your screen shot
'*** where your subject is 5 cells from the cell with Send mail
'*** Adjust it to your actual target range
Application.Run SendEmail, Target.Range.Offset(0, -5)
'SendEmail Target.Range.Offset(0, -5)
End If
moveon:
Application.EnableEvents = True
Exit Sub
halt:
MsgBox Err.Description
Resume moveon
End Sub
I used Application.Run so that you don't have to worry whether your SendEmail sub routine is Public or not. If you decide to just make it Public in a Module, you can use the commented line.
Use the row from the selection. Select your row, then get the row from the selected range, and use it in your code for the cells(iRow, 1)
Sub SendEmail()
Dim ActSheet As Worksheet
Dim SelRange As Range
Dim iRow As Integer
Set ActSheet = ActiveSheet
Set SelRange = Selection
iRow = SelRange.Row
Dim objOutlook As Outlook.Application
Set objOutlook = New Outlook.Application
Dim objEmail As Outlook.MailItem
Set objEmail = objOutlook.CreateItem(olMailItem)
objEmail.Subject = Cells(iRow , 1).Text
objEmail.Body = "============" & vbNewLine & Cells(iRow , 3).Text & vbNewLine & "============" & vbNewLine & Cells(iRow , 6).Text
objEmail.To = Cells(iRow , 5).Text
objEmail.SentOnBehalfOfName = "test#test.com"
objEmail.Display
End Sub
Here how you get all the rows and run your sub on all the rows.
Sub sendEmailFromAllRows()
'Getting the last used row
With Sheets("YourSheetName")
If Application.WorksheetFunction.CountA(.Cells) <> 0 Then
lastrow = .Cells.Find(What:="*", _
After:=.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
Else
lastrow = 1
End If
End With
'Calling your sub to send the mail for each row
For i = 2 To lastrow
SendEmail (i)
Next i
End Sub
Sub SendEmail(iRow As Integer)
Dim objOutlook As Outlook.Application
Set objOutlook = New Outlook.Application
Dim objEmail As Outlook.MailItem
Set objEmail = objOutlook.CreateItem(olMailItem)
objEmail.Subject = Cells(iRow, 1).Text
objEmail.Body = "============" & vbNewLine & Cells(iRow, 3).Text & vbNewLine & "============" & vbNewLine & Cells(iRow, 6).Text
objEmail.To = Cells(iRow, 5).Text
objEmail.SentOnBehalfOfName = "test#test.com"
objEmail.Display
objEmail.Send
End Sub

Scan Excel column for specific word in VBA

Currently im working on a database Excel spreadsheet and im currently using VBA to implement some automatic functions to the system. Im new to VBA so i need your help :)
My question is this: I have a statues column in which the user needs to select from a drop list 'Complete' or 'In progress'. I need a program which can scan a specific column (example S3) for the word 'Complete'. Once the word is detected, the system will automatically send an email to a specific user tell him that the task has been complete.
Can anyone help me?
Thanks! :)
Update: I have coded the following to search for the word complete and send an email to the user (this is a rough idea)
Sub For_Loop_With_Step()
Dim lCount As Long, lNum As Long
Dim MyCount As Long
MyCount = Application.CountA(Range("S:S"))
For lCount = 1 To MyCount - 1 Step 1
If Cells(lCount + 2, 19) = "Complete" Then
Call Send_Email_Using_VBA
Else
MsgBox "Nothing found"
End If
Next lCount
MsgBox "The For loop made " & lNum & " loop(s). lNum is equal to " & lNum
End Sub
.
Sub Send_Email_Using_VBA()
Dim Email_Subject, Email_Send_From, Email_Send_To, _
Email_Cc, Email_Bcc, Email_Body As String
Dim Mail_Object, Mail_Single As Variant
Email_Subject = "Testing Results"
Email_Send_From = "fromperson#example.com"
Email_Send_To = "toperson#example.com"
'Email_Cc = "someone#example.com"
'Email_Bcc = "someoneelse#example.com"
Email_Body = "Congratulations!!!! You have successfully sent an e-mail using VBA !!!!"
On Error GoTo debugs
Set Mail_Object = CreateObject("Outlook.Application")
Set Mail_Single = Mail_Object.CreateItem(0)
With Mail_Single
.Subject = Email_Subject
.To = Email_Send_To
.cc = Email_Cc
.BCC = Email_Bcc
.Body = Email_Body
.send
End With
debugs:
If Err.Description <> "" Then MsgBox Err.Description
End Sub
Try this (Tried And Tested)
Screenshot:
Option Explicit
Sub Sample()
Dim ws As Worksheet
Dim i As Long, lRow As Long
Dim ExitLoop As Boolean
Dim aCell As Range, bCell As Range
'~~> Set this to the relevant sheet
Set ws = ThisWorkbook.Sheets("Sheet1")
With ws
'~~> Find the word in the relevant column. 19 is S Column
Set aCell = .Columns(19).Find(What:="Complete", LookIn:=xlValues, _
LookAt:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False)
If Not aCell Is Nothing Then
'~~> Update Col T appropriately
'~~> This is required so that mail doesn't go for the same row again
'~~> When you run the macro again
Set bCell = aCell
If Not .Range("T" & aCell.Row).Value = "Mail Sent" Then
If SendEmail = True Then
.Range("T" & aCell.Row).Value = "Mail Sent"
Else
.Range("T" & aCell.Row).Value = "Error: Mail Not Sent"
End If
End If
Do While ExitLoop = False
Set aCell = .Columns(19).FindNext(After:=aCell)
If Not aCell Is Nothing Then
If aCell.Address = bCell.Address Then Exit Do
If Not .Range("T" & aCell.Row).Value = "Mail Sent" Then
If SendEmail = True Then
.Range("T" & aCell.Row).Value = "Mail Sent"
Else
.Range("T" & aCell.Row).Value = "Error: Mail Not Sent"
End If
End If
Else
ExitLoop = True
End If
Loop
End If
End With
End Sub
Function SendEmail() As Boolean
Dim OutApp As Object, OutMail As Object
On Error GoTo Whoa
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.CreateItem(0)
With OutMail
.To = "toperson#example.com"
.Subject = "Testing Results"
.Body = "Your Message Goes Here"
.Display
End With
DoEvents
SendEmail = True
LetsContinue:
On Error Resume Next
Set OutMail = Nothing
Set OutApp = Nothing
On Error GoTo 0
Exit Function
Whoa:
SendEmail = False
Resume LetsContinue
End Function