Creating and modifying MS Project files programmatically
In my last two articles we looked at the MS Project object model and how to customize the MS Project UI. In this article, we’ll take what we’ve learned and explore how you can use data from other sources to either create or add to your MS Project files.
- Create an MS Project file from scratch
- Adding tasks, resources and milestones to a MS Project
- Import tasks and resources from Outlook into Project
- Export Project’s tasks and contacts into Outlook
- Import tasks from Excel into MS Project
Create an MS Project file from scratch
Creating a MS Project file is relatively straight-forward. You’ll first need to create a new reference to the MS Project Application object and add a new project to its Projects collection by calling the Projects collections’ Add method.
The Add method accepts three parameters, the first is DisplayProjectInfo. Setting this property to true, will display the Project Information dialog with which the user can specify certain project properties.
The second parameter, Template, specifies the file name and path for a template to use, for creating the project. FileNewDialog is the last parameter and when set to true, displays a dialog where the user can select the template to use when creating the new project file. If the Template parameter is set to true, this parameter will be ignored.
The following code will create a new Microsoft Project file after displaying the project properties and template dialog windows:
static void Main(string[] args) { MSProject.Application projApp = null; MSProject.Projects projects = null; MSProject.Project project = null; try { projApp = new MSProject.Application(); projects = projApp.Projects; project = projects.Add(true, null, true); project.SaveAs("C:\\Temp\\YourProject.mpp"); projApp.Quit(); } finally { if (project != null) Marshal.ReleaseComObject(project); if (projects != null) Marshal.ReleaseComObject(projects); if (projApp != null) Marshal.ReleaseComObject(projApp); } }
Adding tasks, resources and milestones to a MS Project
To add tasks and resources to the newly created project you would need to first get a reference to the newly added project and its Tasks collection property. You would then call the Add method of the Tasks collection in order to add a new task to the project.
Interestingly, to add a resource to a task you do not need to create a resource by adding it to the projects’ Resources collection. By setting the task objects’ ResourcesNames property, MS Project will automatically add the resource to the projects’ resource list. You can also add more than one resource at a time by setting the ResourceNames property to multiple resource names separated by the list separator character specified in the Windows Regional and Language Options.
To mark a task as a milestone, set its Milestone property to true. The code below, adds four tasks to the project passed into the method as a parameter. Note how we assign two resources to the first task and set the third task as a milestone:
static void AddTasksResourceMilestone(MSProject.Project project) { MSProject.Tasks tasks = null; MSProject.Task task1 = null; MSProject.Task task2 = null; MSProject.Task task3 = null; MSProject.Task task4 = null; try { tasks = project.Tasks; task1 = tasks.Add("Write and Sing Song"); task1.Start = project.Start; task1.Duration = 480; task1.ResourceNames = "John,Paul"; task2 = tasks.Add("Play Guitar"); task2.Start = ((DateTime)task1.Finish).AddDays(1); task2.Predecessors = task1.ID.ToString(); task2.Duration = 960; task2.ResourceNames = "George"; task3 = tasks.Add("Play Drums"); task3.Start = ((DateTime)task2.Finish).AddDays(1); task3.Milestone = true; task3.Predecessors = task2.ID.ToString(); task3.Duration = 1440; task3.ResourceNames = "Ringo"; task4 = tasks.Add("Play Bass"); task4.Start = ((DateTime)task3.Finish).AddDays(1); task4.Predecessors = task3.ID.ToString(); task4.Duration = 480; task4.ResourceNames = "Ringo"; } finally { if (task4 != null) Marshal.ReleaseComObject(task4); if (task3 != null) Marshal.ReleaseComObject(task3); if (task2 != null) Marshal.ReleaseComObject(task2); if (task1 != null) Marshal.ReleaseComObject(task1); if (tasks != null) Marshal.ReleaseComObject(tasks); } }
Import tasks and resources from Outlook into MS Project
With what we know so far, we can import tasks relatively easy from Outlook to MS Project and vice-versa. We’ll first create a new ribbon tab for Outlook (using Add-in Express for Office and .net) with which the user can import the selected tasks or contacts to MS Project as either tasks or resources.
The following code will prompt the user to choose the Microsoft Project file to which they want to add the selected Outlook tasks. It will then loop through the selected items in Outlook, check whether the user selected Outlook task items and then add it to the MS Project file’s active project. We’ll add the following code to the “Add selected tasks as MS Project Tasks” button’s OnClick event:
private void createTasksRibbonButton_OnClick(object sender, IRibbonControl control, bool pressed) { MSProject.Application msProjApp = null; Outlook.Explorer currExplorer = null; Outlook.Selection selection = null; Outlook.TaskItem task = null; object item = null; MSProject.Project activeProject = null; MSProject.Tasks projectTasks = null; try { diagFileOpen.ShowDialog(); if (!string.IsNullOrEmpty(diagFileOpen.FileName)) { msProjApp = new MSProject.Application(); msProjApp.FileOpenEx(diagFileOpen.FileName); activeProject = msProjApp.ActiveProject; projectTasks = activeProject.Tasks; currExplorer = OutlookApp.ActiveExplorer(); selection = currExplorer.Selection; for (int i = 1; i <= selection.Count; i++) { item = selection[i]; if (item is Outlook.TaskItem) { task = item as Outlook.TaskItem; projectTasks.Add(task.Subject); } Marshal.ReleaseComObject(item); } msProjApp.FileCloseEx(MSProject.PjSaveType.pjSave); msProjApp.Quit(); } } finally { if (projectTasks != null) Marshal.ReleaseComObject(projectTasks); if (activeProject != null) Marshal.ReleaseComObject(activeProject); if (selection != null) Marshal.ReleaseComObject(selection); if (currExplorer != null) Marshal.ReleaseComObject(currExplorer); if (msProjApp != null) Marshal.ReleaseComObject(msProjApp); } }
To add Outlook contacts to a MS Project file as resources, add the following code to the “Add selected contacts as MS Project Resources” button’s OnClick event:
private void createResourcesRibbonButton_OnClick(object sender, IRibbonControl control, bool pressed) { MSProject.Application msProjApp = null; Outlook.Explorer currExplorer = null; Outlook.Selection selection = null; Outlook.ContactItem contact = null; object item = null; MSProject.Project activeProject = null; MSProject.Resources projectResources = null; try { diagFileOpen.ShowDialog(); if (!string.IsNullOrEmpty(diagFileOpen.FileName)) { msProjApp = new MSProject.Application(); msProjApp.FileOpenEx(diagFileOpen.FileName); activeProject = msProjApp.ActiveProject; projectResources = activeProject.Resources; currExplorer = OutlookApp.ActiveExplorer(); selection = currExplorer.Selection; for (int i = 1; i <= selection.Count; i++) { item = selection[i]; if (item is Outlook.ContactItem) { contact = item as Outlook.ContactItem; projectResources.Add(contact.FullName); } Marshal.ReleaseComObject(item); } msProjApp.FileCloseEx(MSProject.PjSaveType.pjSave); msProjApp.Quit(); } } finally { if (projectResources != null) Marshal.ReleaseComObject(projectResources); if (activeProject != null) Marshal.ReleaseComObject(activeProject); if (item != null) Marshal.ReleaseComObject(item); if (selection != null) Marshal.ReleaseComObject(selection); if (currExplorer != null) Marshal.ReleaseComObject(currExplorer); if (msProjApp != null) Marshal.ReleaseComObject(msProjApp); } }
Export MS Project’s tasks and contacts into MS Outlook
The reverse of what we’ve done is also possible. Before we export resources as contacts and tasks as tasks in Outlook, we’ll first add a new ribbon tab in MS Project:
To export MS Project tasks into Outlook, use the following code:
private void importTasksRibbonButton_OnClick(object sender, IRibbonControl control, bool pressed) { Outlook.Application outlookApp = null; MSProject.Project currProject = null; MSProject.Selection selection = null; MSProject.Tasks selectedTasks = null; MSProject.Task pjTask = null; Outlook.TaskItem olTask = null; try { outlookApp = new Outlook.Application(); currProject = MSProjectApp.ActiveProject; selection = MSProjectApp.ActiveSelection; selectedTasks = selection.Tasks; for (int t = 1; t <= selectedTasks.Count; t++) { pjTask = selectedTasks[t]; olTask = outlookApp.CreateItem( Outlook.OlItemType.olTaskItem) as Outlook.TaskItem; olTask.Subject = pjTask.Name; olTask.Save(); Marshal.ReleaseComObject(pjTask); Marshal.ReleaseComObject(olTask); } outlookApp.Quit(); } finally { if (selectedTasks != null) Marshal.ReleaseComObject(selectedTasks); if (selection != null) Marshal.ReleaseComObject(selection); if (currProject != null) Marshal.ReleaseComObject(currProject); if (outlookApp != null) Marshal.ReleaseComObject(outlookApp); } }
Exporting contacts from MS Project into Outlook will work in a similar fashion. This time you’ll need to use the MS Project Selection objects Resources property in order to get a list of selected resources in MS Project:
private void importContactsRibbonButton_OnClick(object sender, IRibbonControl control, bool pressed) { Outlook.Application outlookApp = null; MSProject.Project currProject = null; MSProject.Selection selection = null; MSProject.Resources selectedResources = null; MSProject.Resource resource = null; Outlook.ContactItem contact = null; try { outlookApp = new Outlook.Application(); currProject = MSProjectApp.ActiveProject; selection = MSProjectApp.ActiveSelection; selectedResources = selection.Resources; for (int r = 1; r <= selectedResources.Count; r++) { resource = selectedResources[r]; contact = outlookApp.CreateItem( Outlook.OlItemType.olContactItem) as Outlook.ContactItem; contact.FullName = resource.Name; contact.Save(); Marshal.ReleaseComObject(resource); Marshal.ReleaseComObject(contact); } outlookApp.Quit(); } finally { if (selectedResources != null) Marshal.ReleaseComObject(selectedResources); if (selection != null) Marshal.ReleaseComObject(selection); if (currProject != null) Marshal.ReleaseComObject(currProject); if (outlookApp != null) Marshal.ReleaseComObject(outlookApp); } }
Import tasks from MS Excel into MS Project
Lastly, we’ll add a ribbon tab in MS Excel to import the selected cells into MS Project as tasks. We’ll add one more ribbon tab, this time for MS Excel.
We’ll assume the user will select two columns in Excel, one containing the task name and the other the task start date, for example:
The following code will then loop through the selected Excel rows and add the tasks in MS Project, after prompting the user to select the target MS Project file:
private void importExcelTasksRibbonButton_OnClick(object sender, IRibbonControl control, bool pressed) { MSProject.Application msProjApp = null; MSProject.Project activeProject = null; MSProject.Tasks projectTasks = null; MSProject.Task task = null; Excel.Range selectedRange = null; Excel.Range taskNameRange = null; Excel.Range taskDateRange = null; try { diagFileOpen.ShowDialog(); if (!string.IsNullOrEmpty(diagFileOpen.FileName)) { msProjApp = new MSProject.Application(); msProjApp.FileOpenEx(diagFileOpen.FileName); activeProject = msProjApp.ActiveProject; projectTasks = activeProject.Tasks; selectedRange = ExcelApp.Selection as Excel.Range; for (int i = 1; i <= selectedRange.Rows.Count; i++) { taskNameRange = (Excel.Range)selectedRange[i, 1]; string taskName = taskNameRange.Value2.ToString(); task = projectTasks.Add((taskName)); taskDateRange = (Excel.Range) selectedRange[i, 2]; task.Start = taskDateRange.Value; Marshal.ReleaseComObject(task); } msProjApp.FileCloseEx(MSProject.PjSaveType.pjSave); msProjApp.Quit(); } } finally { if (taskDateRange != null) Marshal.ReleaseComObject(taskDateRange); if (taskNameRange != null) Marshal.ReleaseComObject(taskNameRange); if (selectedRange != null) Marshal.ReleaseComObject(selectedRange); if (projectTasks != null) Marshal.ReleaseComObject(projectTasks); if (activeProject != null) Marshal.ReleaseComObject(activeProject); if (msProjApp != null) Marshal.ReleaseComObject(msProjApp); } }
Thank you for reading. Until next time, keep coding!
Available downloads:
This sample MS Project add-in was developed using Add-in Express for Office and .net:
39 Comments
projApp = new MSProject.Application();
I am getting an error on below line
projApp = new Microsoft.Office.Interop.MSProject.Application();
Error:- Retrieving the COM class factory for component with CLSID {36D27C48-A1E8-11D3-BA55-00C04F72F325} failed due to the following error: 80070005 Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED)).
Hi JON,
Mmmm. It could be due to the user you’re executing the code as does not have adequate permissions. Make sure the user executing the code has have permission to execute MS Project.
Hope this helps!
Hi, really helpful and in detail. Thanks a lot!
Hi,
Very helpful post. But I need to add custom column. Can you please help me out.
Hi Sunil,
Please have a look at the approach described here.
Hope it helps!
Hi,
Thanxs for reply, but I was not able to make that useful for myself. Can you help with more detailed explanations.
Hi Sunil,
You can use the following code to add you own field to the Tasks table:
MSProject.Project activeProject = null;
MSProject.Tables taskTables = null;
MSProject.Table taskTable = null;
MSProject.TableFields taskTableFields = null;
try
{
activeProject = MSProjectApp.ActiveProject;
taskTables = activeProject.TaskTables;
taskTable = taskTables[“Entry”];
taskTableFields = taskTable.TableFields;
taskTableFields.Add(MSProject.PjField.pjTaskBCWP, MSProject.PjAlignment.pjCenter, 20,”My Custom Field”);
MSProjectApp.TableApply(taskTable);
}
finally
{
if (taskTableFields != null) Marshal.ReleaseComObject(taskTableFields);
if (taskTable != null) Marshal.ReleaseComObject(taskTable);
if (taskTables != null) Marshal.ReleaseComObject(taskTables);
if (activeProject != null) Marshal.ReleaseComObject(activeProject);
}
It looks like you cannot add your own field you need to use one of the existing fields and give it your own title.
Hope this helps!
Hi,
Thank you so much. I really appreciate your response.
Hi,
Have you written any blog to read MSP file in C#. If yes, then may I get link of that blog. I tried to search your site, but didn’t found it.
Hi Sunil,
I’m afraid not. I havent written such an article :)
Hi,
how to change default project name?
If I’m writing:
project.Name = “New project”;
then project.Name is still Project1
Regards,
Kamil
Hello Kamil,
According to https://msdn.microsoft.com/EN-US/library/ff865171.aspx, the Project.Name property is read-only (I believe they chose a very strange way to inform the developer about this). You can saveAs the project to rename it. Here’s the VBA macro I tested:
Sub test()
Debug.Print Me.Name
Me.SaveAs “d:\MyTestProject.mpp”
Debug.Print Me.Name
End Sub
It prints:
Project1
MyTestProject.mpp
Private Sub Application_NewProject(ByVal pj As Microsoft.Office.Interop.MSProject.Project) Handles Application.NewProject
Dim newTask As MSProject.Task
newTask = pj.Tasks.Add _
(“This text was added by using code”)
newTask.Start = DateTime.Now
newTask.Duration = “3”
newTask.ResourceNames = “Rob Caron, Kelly Krout”
End Sub
I want the aboove code to be converted into the Vb.net and make Add-In and run project 13
can Anyone help in Conversion Iam getting Errors and not able to add Visual basic for Application Refrences from visual studio community 15
Hello Neeraj,
The code looks correct to me. Please find more details about converting from VB to VB.NET at https://msdn.microsoft.com/en-us/library/office/aa192490%28v=office.11%29.aspx.
Although adding a reference to VBA from Visual Studio is possible, I don’t understand why would you need to do this?
Excues me, is there a way to generate a detached collection of tasks, and then embed them into the project?
P.S. Thanks for the article. Very informative.
Thank you, Eugene.
This isn’t possible. You need to use the Tasks.Add method to create a task. Find more info about the Tasks collection object and how to use it at https://msdn.microsoft.com/en-us/library/ff866326.aspx.
Thank you. I had asked a similar question on stackoverflow.com. May I refer you answer from there?
Sure.
How to read MS Project all Task Data on any button click event?
Hello Shailesh,
You need to study the Project Object model to find this out; the starting point is at https://msdn.microsoft.com/en-us/vba/vba-project. You can also use the Macro Recorder to find many details about the object model: start the recorder, perform some action in the Project UI e.g. select a task and move it. The Macro Recorder will create a VBA macro showing the classes/methods involved in the process. You need to use these classes/members in your code.
Hi Andrei Smolin,
Articles are really helping hand for every one.
My Question: Can we add Summary task programatically using VSTO Add-in c#?
If possible can you share sample code?
Hello Kedar,
Any program that uses the Project object model has the same of classes/menbers. You can record a VBA macro while performing some steps in the Project UI e.g. while creating a summary task. You should study the macro to understand what classes/members are involved in the process and use these classes/members in your code.
We don’t have such a sample code.
Thank you so much for the prompt answer. Really appreciated.
I have tried generating the VBA model but it seems does not help me out.
Since I am using Office Interop classes and c#. It is difficult for me to convert generated VBA class in C# code.
Any link where this has been implemented as sample would be great help
Kedar,
There’s no such an example. I suggest that you check how the add-in project described in the blog above invokes classes/members defined in the corresponding interop and compare with your VBA macro or with some other VBA macro. Although the languages are different (C# and VB/VBA), they invoke the Project object model.
Thanks for the reply.
I got the solution of this by below way. Hope it will help others.
Microsoft.Office.Interop.MSProject.Task task;
string t1, t2;
for (int i = 0; i < 5; i++)
{
task = objProject.Tasks.Add(
"Task_" + i.ToString(), i + 1);
t1 = "05/12/2012";
t2 = "12/12/2012";
/*TimeSpan span = Convert.ToDateTime(t2) – Convert.ToDateTime(t1);
task.Duration = span.Hours;*/
task.Start = t1;
task.Finish = t2;
task.ResourceNames = "Someone";
//task.OutlineLevel = 1;
//task.Text1 = "Task_TestTask" + i.ToString();
//also tried with this one
/*Microsoft.Office.Interop.MSProject.Task newTask = task.OutlineChildren.Add("otraSubTarea_" + i.ToString(), task.ID + 1);
newTask.Start = "12/12/2012";
newTask.Finish = "12/12/2012";
newTask.OutlineIndent();*/
Microsoft.Office.Interop.MSProject.Task subTask = objProject.Tasks.Add("SubTaskName_" + i.ToString(), task.ID + 1);
subTask.Start = "05/12/2012";
subTask.Finish = "12/12/2012";
subTask.OutlineIndent();
}
Thank you very much!
He,
Just need one help.
I need to track any task change event like. Any data change in task, any summary task is added or selected task is updated as subtask.
Can you provide any example about how to add method to handle above events in one method in VSTO?
Sample code will be of great help.
I came to know about App_ProjectBeforeTaskChange method but I am not sure about how it is used or it can be used.
Your help will greatly appreciated.
Hello Kedar,
Note that the ProjectCalculate event is raised after every change; see https://msdn.microsoft.com/en-us/VBA/Project-VBA/articles/application-projectcalculate-event-project. You can use this event together with the WindowSelectionChange event (https://msdn.microsoft.com/en-us/VBA/Project-VBA/articles/application-windowselectionchange-event-project) to know what field(s) of what task(s) is updated. Also, if a task(s) can be modified by clicking a Ribbon control, you may use a Ribbon command to handle clicking that control.
We don’t have such an example.
Need help.
Please provide an example of ProjectAfterSave event.
Hello Kedar,
We don’t have such an example. What problem do you have with that event?
I am facing one issue in Project VSTO addin.
I would like to move one existing task from one parent to another parent grammatically. I heard that copy and paste is not good approach. I tried with copying ID to temp column and sort it.
Issue: When I actually moved task, it shows in screen that it is under another parent but it keeps the parent child relationship with old parent.
Is that any way, I can remove existing parent child relation with old parent and add as a child in new parent
I hope I am clear with my issue
Hello Kedar,
I don’t know an answer to your question. I suggest studying the Project object model by recording VBA macros while performing the required things in the Project UI and getting reference information about the classes and methods involved at https://msdn.microsoft.com/en-us/vba/vba-project. This is exactly the way that I would use myself. Also, you can use a macro to stop its execution and study the actual values of Project objects.
You can also ask Project-related questions at https://social.msdn.microsoft.com/Forums/en-US/home?forum=officegeneral and VSTO-related questions on the VSTO forum at https://social.msdn.microsoft.com/Forums/en-US/home?forum=vsto.
At – projApp = new Microsoft.Office.Interop.MSProject.Application();
I am getting below error –
An unhandled exception of type ‘System.Runtime.InteropServices.COMException’ occurred in PrjXlsRpt.exe
Additional information: Retrieving the COM class factory for component with CLSID {36D27C48-A1E8-11D3-BA55-00C04F72F325} failed due to the following error: 80010001.
Hello Ramya,
0x80010001 stands for RPC_E_CALL_REJECTED. I suppose the COM server (the Project application) is already running. Or, it is busy with something.
But how can I specify the Cost for a task’s resource?
Hello Ron,
Available in some Office applications (Project is among them), the Macro Recorder creates a VBA macro while you perform actions in the UI of the Office application. The VBA macro generated shows classes and their members involved. You use these classes and class members in your code.
• Click Record Macro on the Developer tab.
• Perform some actions in the UI of the application.
• Click Stop Recording.
• Go to the VBA IDE to see the macro just recorded: press {Alt+F11} or see the Visual Basic button on the Developer tab.
How can you use the APIs to sort the entire set of tasks by the Resource Name?
using MSProject = Microsoft.Office.Interop.MSProject; and with a populated list of tasks.
Hello Steve,
The Tasks object doesn’t provide such a way; see https://learn.microsoft.com/en-us/office/vba/api/project.tasks(object). You need to scan all items in the Tasks collection checking each task.