Here is part of the add-in code that exports Outlook Tasks into Excel. It works the same as the previous code that exports Calendar items, except we set a reference to the default Tasks folder (not Calendar), and I've also incorporated some new code which makes the sub more efficient by writing the data to an array, which is then dumped to the worksheet in one pass.
Note that the Object Model Guard (OMG) is in play here, depending on the properties you reference. For example, the Body Property of a TaskItem in the code below will trigger the OMG. There are probably other properties that will also trigger the OMG, like trying to access the names or email addresses of anyone associated with the task (Owner, Status update recipients, etc).
The code below (and the code to export Calendar items) can be ported to Outlook almost as-is, which should avoid the OMG. In fact, I went ahead and did so with the Tasks code, and the Body Property did not trigger the OMG.
Excel version:
Sub GetTasksData(StartDate As Date, Optional EndDate As Date)
' -------------------------------------------------
' Notes:
' If Outlook is not open, it still works, but much slower (~8 secs vs. 2 secs w/ Outlook open).
' End Date is optional, if you want to pull from only one day, use: Call GetTasksData("7/14/2008")
' -------------------------------------------------
Dim olApp As Outlook.Application
Dim olNS As Outlook.Namespace
Dim myTaskItems As Outlook.Items
Dim ItemstoCheck As Outlook.Items
Dim ThisTask As Outlook.TaskItem
Dim rng As Excel.Range
Dim rngStart As Excel.Range
Dim rngHeader As Excel.Range
Dim MyBook As Excel.Workbook
Dim i As Long
Dim NextRow As Long
Dim ColCount As Long
Dim MyItem As Object
Dim StringToCheck As String
Dim arrData() As Variant
Application.ScreenUpdating = False
' if no end date is specified, EndDate variable will be "12:00:00 AM"
' the requestor only wants one day, so set EndDate = StartDate
If EndDate = "12:00:00 AM" Then
EndDate = StartDate
End If
If EndDate < StartDate Then
MsgBox "Those dates seem switched, please check them and try again.", vbInformation
GoTo ExitProc
End If
If EndDate - StartDate > 28 Then
' ask if the requestor wants so much info
If MsgBox("This could take some time. Continue anyway?", vbInformation + vbYesNo) = vbNo Then
GoTo ExitProc
End If
End If
' get or create Outlook object and make sure it exists before continuing
On Error Resume Next
Set olApp = GetObject(, "Outlook.Application")
If Err.Number <> 0 Then
Set olApp = CreateObject("Outlook.Application")
End If
On Error GoTo 0
If olApp Is Nothing Then
MsgBox "Cannot start Outlook.", vbExclamation
GoTo ExitProc
End If
' hook into default Tasks folder
Set olNS = olApp.GetNamespace("MAPI")
Set myTaskItems = olNS.GetDefaultFolder(olFolderTasks).Items
' ------------------------------------------------------------------
' the following code adapted from:
' http://www.outlookcode.com/article.aspx?id=30
' http://weblogs.asp.net/whaggard/archive/2007/03/21/retrieving-your-
' outlook-appointments-for-a-given-date-range.aspx
'
With myTaskItems
.Sort "[StartDate]", False
.IncludeRecurrences = True
End With
'
StringToCheck = "[StartDate] >= " & Quote(StartDate) & " AND [DueDate] <= " & Quote(EndDate)
Debug.Print StringToCheck
'
Set ItemstoCheck = myTaskItems.Restrict(StringToCheck)
Debug.Print ItemstoCheck.Count
' ------------------------------------------------------------------
If ItemstoCheck.Count > 0 Then
' we found at least one task
' check to make sure we have actual tasks, not infinite recurrence issues
If ItemstoCheck.item(1) Is Nothing Then GoTo ExitProc
Set MyBook = Excel.Workbooks.Add
MyBook.Sheets(1).Name = Format(StartDate, "MMDDYYYY") & " - " & Format(EndDate, "MMDDYYYY")
Set rngStart = MyBook.Sheets(1).Range("A1")
Set rngHeader = Range(rngStart, rngStart.Offset(0, 3))
' with assistance from Jon Peltier http://peltiertech.com/WordPress and
' http://support.microsoft.com/kb/306022
rngHeader.Value = Array("Subject", "Body", "Start Date", "Due Date")
ColCount = rngHeader.Columns.Count
' now that we know how many rows and columns we need,
' resize the array accordingly
ReDim arrData(1 To ItemstoCheck.Count, 1 To ColCount)
For i = 1 To ItemstoCheck.Count
Set ThisTask = ItemstoCheck.item(i)
arrData(i, 1) = ThisTask.Subject
arrData(i, 2) = ThisTask.Body
arrData(i, 3) = Format(ThisTask.StartDate, "MM/DD/YYYY HH:MM AM/PM")
arrData(i, 4) = Format(ThisTask.DueDate, "MM/DD/YYYY HH:MM AM/PM")
Next i
rngStart.Offset(1, 0).Resize(ItemstoCheck.Count, ColCount).Value = arrData
Else
MsgBox "There are no tasks during the time you specified. Exiting now.", vbCritical
End If
ExitProc:
Set myTaskItems = Nothing
Set olNS = Nothing
Set olApp = Nothing
StringToCheck = vbNullString
Set ItemstoCheck = Nothing
Set MyBook = Nothing
Set rngStart = Nothing
Set rngHeader = Nothing
Set ThisTask = Nothing
Erase arrData
Application.ScreenUpdating = True
End Sub
Function Quote(MyText)
' from Sue Mosher's excellent book "Microsoft Outlook Programming"
Quote = Chr(34) & MyText & Chr(34)
End Function
Don't forget to include the Quote UDF when copying this code.
I used an array declared as Variant to store the data returned from Outlook. It's a simple matter to determine the size of the array; since each row represents a separate task, the number of rows is going to be the number of tasks returned by the Restrict Method (ItemstoCheck.Count). The number of columns is even easier; we know how many fields we want to export, so it's just a count of those fields. In the above example, we are exporting four fields, so a count of the number of columns will return the needed value.
The ReDim statement is used here to re-size the array to make it exactly as large as needed.
Everything is structured, variable-wise, so that we can easily add or remove columns without needing to re-work large parts of the code. The array size is automatically calculated based on the number of items found and the number of fields we want to export, so if you wanted to export more (or less), simply edit the string values in the Array() Function and add corresponding lines inside the For Loop for arrData to manage.
To call the code above, simply pass two dates to it, as follows:
Sub GetTasks()
Call GetTasksData("8/11/2008", "9/12/2008")
End Sub
Here is the Outlook version, which is merely a slightly reworked version of the above code. This code also requires the Quote UDF, shown above. The OMG is so heinous that I recommend coding inside Outlook whenever possible, if you need to access anything protected from it (like email addresses). You can always instantiate Excel, Word, or Access from it and do what you need.
Outlook version:
Sub GetTasksData(StartDate As Date, Optional EndDate As Date)
' -------------------------------------------------
' Notes:
' End Date is optional, if you want to pull from only one day, use: Call GetTasksData("7/14/2008")
' -------------------------------------------------
Dim olApp As Outlook.Application
Dim olNS As Outlook.NameSpace
Dim myTaskItems As Outlook.Items
Dim ItemstoCheck As Outlook.Items
Dim ThisTask As Outlook.TaskItem
Dim xlApp As Excel.Application
Dim rng As Excel.Range
Dim rngStart As Excel.Range
Dim rngHeader As Excel.Range
Dim MyBook As Excel.Workbook
Dim i As Long
Dim NextRow As Long
Dim ColCount As Long
Dim MyItem As Object
Dim StringToCheck As String
Dim arrData() As Variant
' if no end date is specified, EndDate variable will be "12:00:00 AM"
' the requestor only wants one day, so set EndDate = StartDate
If EndDate = "12:00:00 AM" Then
EndDate = StartDate
End If
If EndDate < StartDate Then
MsgBox "Those dates seem switched, please check them and try again.", vbInformation
GoTo ExitProc
End If
If EndDate - StartDate > 28 Then
' ask if the requestor wants so much info
If MsgBox("This could take some time. Continue anyway?", vbInformation + vbYesNo) = vbNo Then
GoTo ExitProc
End If
End If
Set olApp = Outlook.Application
' hook into default Tasks folder
Set olNS = olApp.GetNamespace("MAPI")
Set myTaskItems = olNS.GetDefaultFolder(olFolderTasks).Items
' ------------------------------------------------------------------
' the following code adapted from:
' http://www.outlookcode.com/article.aspx?id=30
' http://weblogs.asp.net/whaggard/archive/2007/03/21/retrieving-your-
' outlook-appointments-for-a-given-date-range.aspx
'
With myTaskItems
.Sort "[StartDate]", False
.IncludeRecurrences = True
End With
'
StringToCheck = "[StartDate] >= " & Quote(StartDate) & " AND [DueDate] <= " & Quote(EndDate)
Debug.Print StringToCheck
'
Set ItemstoCheck = myTaskItems.Restrict(StringToCheck)
Debug.Print ItemstoCheck.Count
' ------------------------------------------------------------------
If ItemstoCheck.Count > 0 Then
' we found at least one task
' check to make sure we have actual tasks, not infinite recurrence issues
If ItemstoCheck.Item(1) Is Nothing Then GoTo ExitProc
Set xlApp = Excel.Application
xlApp.ScreenUpdating = False
Set MyBook = xlApp.Workbooks.Add
xlApp.Visible = True
MyBook.Sheets(1).Name = Format(StartDate, "MMDDYYYY") & " - " & Format(EndDate, "MMDDYYYY")
Set rngStart = MyBook.Sheets(1).Range("A1")
Set rngHeader = Range(rngStart, rngStart.Offset(0, 3))
' with assistance from Jon Peltier http://peltiertech.com/WordPress and
' http://support.microsoft.com/kb/306022
rngHeader.Value = Array("Subject", "Body", "Start Date", "Due Date")
ColCount = rngHeader.Columns.Count
' now that we know how many rows and columns we need,
' resize the array accordingly
ReDim arrData(1 To ItemstoCheck.Count, 1 To ColCount)
For i = 1 To ItemstoCheck.Count
Set ThisTask = ItemstoCheck.Item(i)
arrData(i, 1) = ThisTask.Subject
arrData(i, 2) = ThisTask.Body
arrData(i, 3) = Format(ThisTask.StartDate, "MM/DD/YYYY HH:MM AM/PM")
arrData(i, 4) = Format(ThisTask.DueDate, "MM/DD/YYYY HH:MM AM/PM")
Next i
rngStart.Offset(1, 0).Resize(ItemstoCheck.Count, ColCount).Value = arrData
xlApp.ScreenUpdating = True
Else
MsgBox "There are no tasks during the time you specified. Exiting now.", vbCritical
End If
ExitProc:
Set myTaskItems = Nothing
Set olNS = Nothing
Set olApp = Nothing
Set xlApp = Nothing
StringToCheck = vbNullString
Set ItemstoCheck = Nothing
Set MyBook = Nothing
Set rngStart = Nothing
Set rngHeader = Nothing
Set ThisTask = Nothing
Erase arrData
End Sub
Note that the code above is early bound and requires a reference to the Excel library. The key changes are: We reference the Outlook Application Object directly, instead of using GetObject or CreateObject. We have to qualify Excel references with the Excel.Application object, xlApp, instead of "Application." Otherwise it's nearly identical, and avoids the OMG considerations.
Enjoy,
JP





Hi, I was wondering if I could get some assistance with a problem I was having exporting data from Outlook to Excel. I want to parse out a specific set of numbers from the subject line of the e-mail.
The subject line goes something like this.
Joe Snuffy Case 0901150993 – P3 – CASE TITLE: User is missing Attachmate X-treme v.9.0 on his new laptop./ Joe Snuffy 11:39 AM / 00024695 / Closed
I don't have any problems pulling the first case number but the second one is a problem to get consistently because it's location varies. Currently I have this.
tmpkey = "/" testString = msg.Subject tmpStringA = Right(testString, (Len(testString) - InStr(testString, tmpKey))) tmpStringA = Right(tmpStringA, (Len(tmpStringA) - InStr(tmpStringA, tmpKey))) tmpStringA = Left(tmpStringA, InStr(Trim(tmpStringA), " ")) rng.Value = Trim(tmpStringA)Sometimes this fails to pick up the second case number. Should this be edited or could I find a way to search for the second / and take so many characters after the second /?
If the subject format is always the same, and the number you want to parse is always between the 2nd and 3rd forward slashes:
'extract' will contain the value you are looking for. I saved it as a String so the leading zeros won't be dropped.
HTH
Thank you very much, that is working perfectly! All very simple, I won't forget the lesson there.
The important thing to remember is that when you're doing the second and third search, you need to add 1 to the previous position, otherwise your code will just find the same character over and over. That is why there's a "+1" in the first argument of the Instr function.
On second thought, the "+2" and "-1" are there to remove the padded spaces, but your Trim function call will remove them just as well, without having to hard code numbers into the formula.
HTH
JP,
I was successful using code you provided here to create Outlooks tasks from Excel as part of a project planning workbook. However, I'd like to provide the user the ability to (from Excel), click on a button and have it reference the previously created task in Outlook and open it up (based on 'Subject' line).
Is that easily doable? I'm pretty comfortable with Excel VBA, but aside from creating emails, I haven't spent much time referencing Outlook objects.
Here is what I've tried doing so far (don't laugh…). Thanks in advance for any help!
Sub getTask()
Dim olApp As Object ' Outlook.Application
Dim olnameSpace As Object ' Outlook.Namespace
Dim oltaskFolder As Object ' Outlook.MAPIFolder
Dim oltasks As Object ' Outlook.Items
Dim oltask As TaskItem
Set olApp = New Outlook.Application
Set olnameSpace = olApp.GetNamespace("MAPI")
Set oltaskFolder = olnameSpace.GetDefaultFolder(13) ' olFolderTasks
Set oltasks = oltaskFolder.Items
olApp.oltasks("This is the title").Display
End Sub
Never mind JP; I was able to get it to work; this allows the user to open an existing task in Outlook (based on the subject line) from Excel. Below is my successful code.
Sub OpenExistingTaskFromOutlook() Dim myolApp As Outlook.Application Dim myNameSpace As Outlook.Namespace Dim myFolder As Outlook.MAPIFolder Set myolApp = CreateObject("Outlook.Application") Set myNameSpace = myolApp.GetNamespace("MAPI") Set myFolder = myNameSpace.GetDefaultFolder(olFolderTasks) On Error GoTo ErrorHandler myFolder.Items("Ken's task").Display Exit Sub ErrorHandler: MsgBox "There are no items to display." End Subthanks for this macro, i was looking for something like this. i need to only change this
myFolder.Items("Ken's task").Displayto use a cell as the task subject. here is the edited code:
Sub OpenExistingTaskFromOutlook() Dim myolApp As Outlook.Application Dim myNameSpace As Outlook.Namespace Dim myFolder As Outlook.MAPIFolder Set myolApp = CreateObject("Outlook.Application") Set myNameSpace = myolApp.GetNamespace("MAPI") Set myFolder = myNameSpace.GetDefaultFolder(olFolderTasks) On Error GoTo ErrorHandler For Each cell In Columns("J").Cells.SpecialCells(xlCellTypeConstants) If cell.Value Like "?*@?*.?*" And _ Cells(cell.Row, "F").Value = "Done" Then myFolder.Items("Ken's task").Display Exit Sub ErrorHandler: MsgBox "There are no items to display." End If Next End Subi will like this code to use the row with the word "Done" in cell "F" to use the task subject in cell "M".
thanks!
It would be something like
myFolder.Items("Ken's task").Subject = Cells(cell.Row, "M").Value
Hi JP. Your code works great. However, I've discovered one problem that has me baffled.
The code is not finding any of my Tasks that were created by drag-and-drop method! I create most of my Tasks in Outlook simply by dragging emails onto the Task button in the Outlook Navigation Pane (lower left in OL2007). None of these Tasks are returned in the macro results. This is true if the Tasks were created by drag-and-drop onto the Tasks button or onto the Tasks Folder in the folder list. All other Tasks are in the results.
Do you know if there is something unique about Tasks created by drag-and-drop?
I've discovered it is not only Tasks created by drag-and-drop that are not included. In fact, the only Tasks that are included in the macro results are Tasks that I create by double-clicking in the 'Click here to add a new Task' box at the top of the Task window and this is true only when the window is in the 'To-Do List' view when I created the Task. If the window is set to any other view, such as 'Tasks', tasks that are created by the 'Click here to add a new Task' are not returned by the macro. Also, Tasks that are created by drag-and-drop and Tasks created by clicking on the 'New' button in the Outlook standard Toolbar are not found by the macro.
However, I've found that if, while in any Task window view, I select any Task that is NOT being included in the macro results and right-click on it in the 'Sort by: Flag Status' column and select any status (including the status that is ALREADY set for the item), the Task will appear in the macro results from that point on. (I don't even have to actually change its Flag Status… I just have to click on it in the Flag Status column).
Would you have any idea of what I could do to ensure all Tasks get included in the results from the moment they get created?
The procedures above both accept a start and end date, and only extract tasks found within those dates (start date and due date). When you drag and drop the new tasks, are you including the current date in the date range?
You are correct! When using the drag-and-drop process (as well as several other methods), a Start Date is not set and I've only been giving them a Due Date. When using the 'Click here to add a new Task' box while in the 'To-Do List' view, a Start Date is automatically set.
Is it possible to change the code so that no Start Date is needed?
Sure, just take out the parameters from the first line, so it just reads
Sub GetTasksData()
Then remove lines 30-46 (the references to StartDate and EndDate) and line 71 (the .Sort line). Remove line 75 (the StringToCheck line) and edit line 78 so it looks like this:
Set ItemstoCheck = myTaskItems
A few other minor edits are needed (lines 88, 110, 111) but that should be it.
Hi,
Thanks for the macro it works well.
Only problem is that on my work computer (were i need to use it) we have 2003 version of excel and possibly low memory allocation to excel by IT.
When it hits the line were the array data is to be put into the cells i get the error "runtime 7- out of memory".
If i change the array part of the code from =thistask.body to = format(thistask.body,text) it pulls only 255 characters.
but atleast the macro works in this instance.
i really need to be able to extract all the information from the notes body into the excel sheet.
If i manually go through and copy/paste from outlook to excel it works.
If anyone can help me out ASAP it would be good as my boss needs this macro up and running before end of year.
Thanks guys,
Mick
It would help if you mention which version you are using (the one that works from Excel, or the one that works from Outlook).
Try limiting the amount of tasks returned by the procedure by using a smaller date range when calling the function. If that works, you'll have to call the procedure multiple times to retrieve all the tasks you want.
Hi thanks for your reply,
I am using Excel 2003 vba to import the task data from outlook 2003.
It doesn't seem to matter how many tasks are imported i.e one or fifty, it only extracts 255 characters.
If i leave the array part of the code as = thistask.body then i get the "run time 7- out of memory" error.
I think this may be the problem, i need it work with the above line.
if i change the array part of the code to = format(thistask.body,text) it works but pulls only 255 characters.
I have even tried importing and placing the task notes into a text box rather than a cell but still only 255 characters.
Any other ideas?
Essentially i am trying to set up a job tracking system with greater capabilities than Outlook. Maybe you know of some examples that already exist?
Cheers,
Mick
Without seeing your code, it's hard to say why it's only extracting 255 characters. Have you changed the above code?
Hi,
Yes, i had to alter a section of the array code as indicated in my previous post for it to pull the 255 characters. Else it pulls nothing.
I actually stepped through the code a few times… the array works until it gets to a task with too much text and /or attachments and then provides an error… hence none of the tasks notes make it through. note most of my tasks have thousands of characters.
I think the easiest way to see if it is a computer hardware or code problem is for someone else to give it a go.
I will post the code as it is once i get home from work. It has additional sections added to it but the body of it remains similar.
If you would be so kind as to try it out and see if it works (or doesnt) for you then this may lead to some answers.
If you try it, remember to add heaps of text to a few of the task bodies in Outlook so that the scenario will match with mine.
Cheers,
Mick
HI
Thank you very much for your code, it's work fine with my tasks, id it a way I can change for the tasks on my exchange server
Thank you so much for your help
Hi JP,
Works like a charm! Even a complete vba-newbie like me had it running within a couple of minutes!
Only one thing; I'm exporting about 20 date fields from my tasks, but if one field hasn't been filled in, it stops exporting fields furthermore. Is there any workaround this?
Greetz, Marco
If a date field is blank, it should return either "" (empty String) or Outlook's default date (1/1/4501). Post the relevant portion of your code and maybe I can figure out what is going on.
Thanx for your response. I've pasted the (I think) relevant part of the code. As you can see I've changed little. Relevant is that I've made a few fields on my custom-made task form. These fields are mostly date fields and not all fields have to be filled-in. The build-up of the array (in my own words) seems to stop at the first field which is not filled-in.
Would be wonderful if you have a solution!
Greetz Marco
rngHeader.Value = Array("Subject", "Category", "Status", "Date_Complain", "Date decision", "Start date", "12 weeks", "Extend?", "Extend untill") ColCount = rngHeader.Columns.Count ' now that we know how many rows and columns we need, ' resize the array accordingly ReDim arrData(1 To ItemstoCheck.Count, 1 To ColCount) For i = 1 To ItemstoCheck.Count On Error Resume Next Set ThisTask = ItemstoCheck.Item(i) arrData(i, 1) = ThisTask.Subject arrData(i, 2) = ThisTask.Categories arrData(i, 3) = ThisTask.Status arrData(i, 4) = ThisTask.ItemProperties.Item("Date_Complain").Value arrData(i, 5) = ThisTask.ItemProperties.Item("Date_decision").Value arrData(i, 6) = ThisTask.ItemProperties.Item("Starting date").Value arrData(i, 7) = ThisTask.ItemProperties.Item("12 weeks").Value arrData(i,
= ThisTask.ItemProperties.Item("ExtendCheck").Value
arrData(i, 9) = ThisTask.ItemProperties.Item("ExtendDate").Value
Next i
rngStart.Offset(1, 0).Resize(ItemstoCheck.Count, ColCount).Value = arrData
xlApp.ScreenUpdating = True
Else
MsgBox "There are no tasks during the time you specified. Exiting now.", vbCritical
End IfSolved it!
Your response brought me on the right trail: I've added a " " like this
arrData(i, 4) = ThisTask.ItemProperties.Item("Date_Complain").Value & " "
and everything was exported to excel in no-time!
Thx!
Marco