I'm mostly trying to understand code that it's working:
Sub sendOutlookEmail()
Dim oApp As Outlook.Application
Dim oMail As MailItem
Set oApp = CreateObject("Outlook.Application")
Set oMail = oApp.CreateItem(olMailItem)
oMail.Body = "Body of the email"
oMail.Subject = "Test Subject"
oMail.To = "Someone#somewhere.com"
oMail.Send
Set oMail = Nothing
Set oApp = Nothing
End Sub
I learnt that it's best practice to create objects by early binding, as in
Dim oApp as New Outlook.Application
rather than by late binding, as in
Dim oApp as Outlook.Application
Set oApp = CreateObject("Outlook.application")
So I was trying to reduce the code.
1. Are both oApp and oMail objects?
Or is oMail a property or method of the oApp object (maybe a method that creates a new object)? If so:
Since oApp is an object Outlook.Application
and oMail is an object such as oApp.CreateItem(olMailItem)
I was trying to define straight away the oMail object by early binding like so:
Dim oMail as New Outlook.Application.CreateItem(olMailItem)
But that gives me a Syntax error.
And the following:
Dim oMail as New Outlook.Application.olMailItem
Gives me a Type mismatch error.
So:
2. Is there a way to create only the oMail, straight away?
Or do I have to create first the oApp object first anyway, in order to be able to create the oMail item (that is, another object dependent on the first)?
3. Is there any way to define the oMail object by early binding in just one line?
I'm new in programming, I hope I've explained myself properly and that my questions make some kind of sense heheh.
Have a nice day!
Yes, they are both objects.
No, only Outlook.Application object is creatable. All other objects (MAPIFolder, MailItem, Recipient, etc). are retrieved from a call to other Outlook objects, (e.g., Application.CreateItem)
Ultimately, early binding vs late binding makes zero difference. Early binding gives you Intellisense and compile-time errors. It can also be slightly faster (not that you would notice that in VBA). Functionality-wise, the two are the same.
There is no best practice when using early or late binding. Both approaches are valid and can be used. For example, in cases when you don't want or can't add a COM reference your choice would be the late-binding technology which allows declaring everything like object and keep calling properties and methods using a vtable. But at the same time the early-binding technology prevents developers from doing mistakes by adding COM references and using intellisense suggestions when coding and typing property and method calls. Moreover, it works faster than late-biding. You can read more about the two technologies in the Using early binding and late binding in Automation article. But that is your choice, not the best practice. If you don't have any arguments for the late-binding, I'd go with early-binding.
Both are COM objects. Most of the time COM objects are created by calling properties and methods of other objects when dealing with the Outlook object model, only the Outlook Application class is instantiated in the code. For example, a new MailItem instance can be created by calling the CreateItem method. But also you may consider creating a new MailItem instance in a specific folder. In that case you need to use the Items collection (see the Folder.Items property) which provides the Add method. It returns the new Outlook item.
No, you must run Outlook (or create a new instance programmatically or connect to the existing one) to be able to create a new MailItem object in the code.
You can declare the object in a single line of code. But you need to instantiate it somewhere. And that doesn't depend on the late or early binding technologies. The principle is the sample. To see the difference between the two let's consider the following:
In the case of late-binding technology used the declaration may look like that:
Dim mailItemObj as Object
In the case of early-binding technology that looks in the following way:
Dim mailItemObj as Outlook.MailItem
That's it!
P.S. You may find the How to create and show a new Outlook mail item programmatically: C#, VB.NET article helpful.
Related
This visual basic code related to outlook used to work without problems
Sub cmdExample()
Dim myOlApp As Object
Set myOlApp = CreateObject("Outlook.Application")
Set myoSession = myOlApp.Session
Set myoCalendar = myoSession.GetDefaultFolder(olFolderCalendar)
End Sub
Now I obtain the runtime error 5 (Invalid procedure call or argument)
Reason found from debugging: at runtime olFolderCalendar is empty (by the way, same problem for other enumerations like olAppointmentItem, olBusy). My workaround in the above code is calling .GetDefaultFolder(9).
However I would rather use the enumerations and I would like to understand why all of them are empty.
If you want to pass literal constants instead of numbers you need to add a COM reference to Outlook. Read more about that in the How to Add an Object Library Reference in VBA article.
Also, you may try to use the Logon method before getting the folder. See NameSpace.Logon for more information.
Am trying to write a outlook VB macro, to achieve following:
Triggers when a new email arrives.
Go to a folder inside Inbox - temp1.
Check if there is an existing email which matches the subject of
this new email.
Delete the old email.
Code:
Option Explicit
Private objNS As Outlook.NameSpace
Private WithEvents objItems As Outlook.Items
Private Sub Application_Start()
Dim olApp As Outlook.Application
Dim objWatchFolder As Outlook.Folder
Set olApp = Outlook.Application
Set objNS = Application.GetNamespace("MAPI")
'Set the folder and items to watch:
Set objWatchFolder = objNS.GetDefaultFolder(olFolderInbox).Folders("temp1")
Set objItems = objWatchFolder.Items
Set objWatchFolder = Nothing
End Sub
Private Sub objItems_ItemAdd(ByVal Item As Object)
Dim intCount As Integer
Dim objVariant As Variant
For intCount = objItems.Count To 1 Step -1
Set objVariant = objItems.Item(intCount)
If objVariant.Subject = Item.Subject And objVariant.SentOn < Item.SentOn
Then
objVariant.Delete
Else
End If
Next
End Sub
Issue:
Macro not getting triggered.
Notes:
Have Trust center settings updated to enable macros.
Have added this code in Class Module
When I do F5, it doesn't show any MACRO that it can run.
Seeking some expert help pls!
(1) You need to handle the NewMailEx event of the Application class which is fired when a new item is received in the Inbox. The NewMailEx event fires when a new message arrives in the Inbox and before client rule processing occurs. You can use the Entry ID returned in the EntryIDCollection array to call the NameSpace.GetItemFromID method and process the item. Use this method with caution to minimize the impact on Outlook performance. However, depending on the setup on the client computer, after a new message arrives in the Inbox, processes like spam filtering and client rules that move the new message from the Inbox to another folder can occur asynchronously. You should not assume that after these events fire, you will always get a one-item increase in the number of items in the Inbox.
(2) It looks like you already know how to get the a subfolder of Inbox:
Set objWatchFolder = objNS.GetDefaultFolder(olFolderInbox).Folders("temp1")
(3) To find items with the same subject you need to use the Find/FindNext or Restrict methods of the Items class instead of iterating over all items in the folder. Read more about these methods in the following articles:
How To: Use Find and FindNext methods to retrieve Outlook mail items from a folder (C#, VB.NET)
How To: Use Restrict method to retrieve Outlook mail items from a folder
Also if you need to find items in multiple folders you may find the AdvancedSearch method of the Application class helpful. The key benefits of using the AdvancedSearch method in Outlook are:
The search is performed in another thread. You don’t need to run another thread manually since the AdvancedSearch method runs it automatically in the background.
Possibility to search for any item types: mail, appointment, calendar, notes etc. in any location, i.e. beyond the scope of a certain folder. The Restrict and Find/FindNext methods can be applied to a particular Items collection (see the Items property of the Folder class in Outlook).
Full support for DASL queries (custom properties can be used for searching too). You can read more about this in the Filtering article in MSDN. To improve the search performance, Instant Search keywords can be used if Instant Search is enabled for the store (see the IsInstantSearchEnabled property of the Store class).
You can stop the search process at any moment using the Stop method of the Search class.
See Advanced search in Outlook programmatically: C#, VB.NET for more information.
(4) Use the Delete method for removing items.
Items.ItemAdd Event (Outlook) describes how to apply the ItemAdd event in a Class module. You would then use Application_Startup in ThisOutlookSession to initialize objItems.
What you have is code for the ThisOutlookSession module.
You are watching the ItemAdd event on the temp1 folder, not the Inbox.
I was reading about how to declare FileSystemObjects objects and I found confusing information. Is it because there are different ways to declare it?
I let you some of the ways I found to declare and define FileSystemOjbect objects:
Dim FSO As FileSystemObject
Set FSO = New FileSystemObject
Dim FSO As New FileSystemObject
Dim FSO As Object
Set FSO = CreateObject("scripting.filesystemobject")
Which is the right way to declare FileSystemObject objects?
All 3 ways are correct. You have hit 2 different approaches to using objects.
The former 2 ways mean "Early Binding".
The last way means "Late Binding".
The middle way is about a shortcut to the the 1st way, but not fully.
It is better avoided by novice VBA users in complex code,
as any reference to the object variable creates a new instance of the object,
if the object variable=Nothing
Early binding:
One has to have linked the used libraries/modules in VBA - Tools - References,
in this time Microsoft Scripting Runtime Library
If the module/code is not present on the target computer, execution will fail.
Early binding is reportedly significantly faster.
Early binding offers at development the Intellisense-editor suggestion of object methods and properties and named constants
Late Binding:
No need of linking used external libraries/modules - better intermachine portability.
Late binding is reportedly slower.
Late binding does not offer Intellisense and the object specific constants has to be either explicitly declared either provided by their value.
See e.g. a conditional code compilation, based on the Project-wide conditional compilation argument Earlybinding :
Sub EarlyVsLateBindingtest()
#If Earlybinding Then
Dim oFS As Scripting.FileSystemObject
Set oFS = New Scripting.FileSystemObject
#Else
Const TemporaryFolder = 2
Dim oFS As Object
Set oFS = CreateObject("Scripting.FileSystemObject")
#End If
oFS.GetSpecialFolder (TemporaryFolder)
End Sub
https://superuser.com/questions/615463/how-to-avoid-references-in-vba-early-binding-vs-late-binding/1262353?noredirect=1#comment1859095_1262353
See also
https://wordmvp.com/FAQs/InterDev/EarlyvsLateBinding.htm
https://support.microsoft.com/en-gb/help/245115/using-early-binding-and-late-binding-in-automation
I want to go through this function the opposite way. I found that you can't decrement through a for...each loop. I need to make a for...next loop and decrement through the mail items.
Sub ProcessItemTesting(StartFolder As Outlook.MAPIFolder, Item As Object)
Dim myOlApp As New Outlook.Application
Dim objFolder As Outlook.MAPIFolder
Dim objItem As Object
Dim OlMail As Object
Dim mySaveName As String
Dim myExt As String
Dim strFolder As String
Dim myitem
Set myitem = myOlApp.CreateItem(olMailItem)
For Each OlMail In StartFolder.Items
If (Now - OlMail.SentOn) > 90 Then
If OlMail.Attachments.Count > 0 Then
If TypeName(OlMail) = "MailItem" Then
For x = 1 To OlMail.Attachments.Count
If UCase(OlMail.Attachments.Item(x).Filename) Like "*STRING*" Then
mySaveName = OlMail.Attachments.Item(x).Filename
myExt = Split(mySaveName, ".")(1)
Select Case myExt
Case "xls", "xlsm", "xlsx"
mySaveName = strFolder & "\" & mySaveName
OlMail.Attachments.Item(x).SaveAsFile mySaveName
Case Else
'do nothing
End Select
End If
Next
End If
End If
End If
Next
End Sub
This loop goes through my mailbox FIFO. It takes too long to execute as I am looking for the most recent instance, so I want LIFO.
So, my question is, how do I form this for...next loop?
Specifically, I don't know how to:
Get the count for the loops end test parameter - [StartFolder.Items.count?]
Address the OlMail item object in each iteration - [OlMail(x)?]
First of all, take a look at the Getting Started with VBA in Outlook 2010 article in MSDN which explains the basics.
Iterating through all items in the folder is not a really good idea. I'd suggest using the Find/FindNext or Restrict methods of the Items class instead. The latter applies a filter to the Items collection, returning a new collection containing all of the items from the original that match the filter. So, you just need to iterate through a small number of items and do whatever you need without checking properties each time. You can read more about these methods in the following articles:
How To: Use Find and FindNext methods to retrieve Outlook mail items from a folder (C#, VB.NET)
How To: Use Restrict method to retrieve Outlook mail items from a folder
Also you may find the AdvancedSearch method of the Application class helpful. he key benefits of using the AdvancedSearch method in Outlook are:
The search is performed in another thread. You don’t need to run another thread manually since the AdvancedSearch method runs it automatically in the background.
Possibility to search for any item types: mail, appointment, calendar, notes etc. in any location, i.e. beyond the scope of a certain folder. The Restrict and Find/FindNext methods can be applied to a particular Items collection (see the Items property of the Folder class in Outlook).
Full support for DASL queries (custom properties can be used for searching too). You can read more about this in the Filtering article in MSDN. To improve the search performance, Instant Search keywords can be used if Instant Search is enabled for the store (see the IsInstantSearchEnabled property of the Store class).
You can stop the search process at any moment using the Stop method of the Search class.
See Advanced search in Outlook programmatically: C#, VB.NET for more information.
I had similar such issue and this is how I had solved it. Let me know if this helps. The given below code is a copy paste of what I had done.
'Get the totals emails count in the given folder
bTotalEmails = OlMapFld.Items.Count
For iCounter = bTotalEmails To 1 Step -1
'this will be used instead of OlMail as per your code.
OlMapFld.Items.Item(iCounter)
Next
I like to use early binding in my VBA projects, since I like the auto-complete of method names, etc. during development. I also like the confidence of knowing that the compiler will warn me if I've mis-spelled a method name.
However, to use early binding I need to add a reference to the relevant library (for example, the "Microsoft Scripting Runtime"). That's fine for "standard" libraries like that, but sometimes I want to use a library that may or may not be present on the user's machine.
Ideally, I'd like to display a useful message if the library is not present (such as "xyz is not installed on this computer, and so this feature cannot be used"). If I was using only late binding, then I could do this:
Dim o As Object
Set o = CreateObject("foo", "bar")
If o Is Nothing Then
MsgBox "nope"
End If
But, if I've added a reference to the library in order to use early binding, then if the library is not present I get a compile error when my VBA project is loaded. Thus, none of the code runs (including the code to detect the non-existence of the library).
Is there any way around this catch-22?
You could create a class module as a proxy for an object library --- wrap all the methods, properties, and constants needed by your other procedures.
All those procedures would use the proxy class the same way, so you wouldn't need to revise those procedures to switch between early and late binding. And Intellisense would show you everything you expose with the proxy class.
The class would be a single point of control to switch between early and late binding. You mentioned Excel as one example:
#Const DevStatus = "PROD"
#If DevStatus = "DEV" Then
Private objApp As Excel.Application
Private objBook As Excel.Workbook
Private objSheet As Excel.Worksheet
#Else 'assume PROD
Private objApp As Object
Private objBook As Object
Private objSheet As Object
#End If
If there is a possibility Excel may not be installed on any users' machines, you can check its availability during class initialize.
Dim blnExcelAvailable As Boolean
Private Sub Class_Initialize()
blnExcelAvailable = IsExcelAvailable()
End Sub
Private Function IsExcelAvailable() As Boolean
Dim blnReturn As Boolean
Dim objTest As Object
On Error GoTo ErrorHandler
Set objTest = CreateObject("Excel.Application")
blnReturn = True
ExitHere:
On Error GoTo 0
Set objTest = Nothing
IsExcelAvailable = blnReturn
Exit Function
ErrorHandler:
blnReturn = False
GoTo ExitHere
End Function
Then your procedures which use the proxy class could check a property to see whether Excel is available.
Public Property Get ExcelAvailable() As Boolean
ExcelAvailable = blnExcelAvailable
End Property
I think this approach is possible, and it satisfies your requirements AFAICT. However, I'm unsure whether it's reasonable. Back to the example of Excel, you could do something like this for a manageable subset of its object model. But if you need all or most of its methods, properties, and constants, the proxy class would be a huge undertaking.
Personally I wouldn't use this approach. It's less work for me to manage early/late binding as mwolfe02 and JP. described. However my impression is this is more burdensome in your situation, so perhaps you're willing to invest more effort than I am in something like this.
Not really.
However, one way I've dealt with this in development is to have two separate declaration lines. I comment one or the other depending on whether I am doing dev work or releasing to production. You can leave everything else alone (including the CreateObject line) and then you just need to remember to switch the commented line and add/remove the reference itself.
For example:
Dim o As foo.bar 'Comment out for production'
'Dim o As Object ''Comment out for dev work'
Set o = CreateObject("foo", "bar")
If o Is Nothing Then
MsgBox "nope"
End If