none
Programmatically save a copy of a Microsoft Project Online project RRS feed

  • Question

  • I need to save local copies of all my projects stored in my PWA instance at Microsoft Project Online frequently. I have hundreds of them so doing it manually is not an option.

    I've been able to connect to the PWA instance with Project Server CSOM and .NET and read the data. But I couldn't find a way within the Microsoft.ProjectServer.Client namespace to export this data and save it locally to any MS Project-readable file.

    Is this even possible at all?

    Alternatively, any other way to achieve this not involving the Project Server CSOM would be welcome.

    Edit: I've also tried to obtain the data through the Project Online REST API, so I can get a XML file that I could use as if it were a MSPDI file. But neither MS Project Professional (the desktop application) nor MPXJ library recognize it, I might need to do some heavy transformation and that doesn't guarantee it will even work..
    Tuesday, August 13, 2019 12:00 PM

Answers

  • Thanks Ben, your answers helped me to get into this ugly, working C# prototype:

    private static void start() { var objProcess = Process.Start(@"winproj.exe", "/s https://PWA-URL"); } static void Main(string[] args) { var backgroundThread = new Thread(new ThreadStart(start)); backgroundThread.Start(); Thread.Sleep(10000); ApplicationClass objProject = new ApplicationClass { Visible = false }; object oMissing = System.Reflection.Missing.Value; object oFile = @"<>\test-project"; object oFormat = "MSProject.mpp"; object oReadOnly = true; objProject.DisplayAlerts = false; objProject.FileOpen(oFile, oReadOnly, PjMergeType.pjDoNotMerge, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oFormat, oMissing, PjPoolOpen.pjPoolReadOnly, oMissing, oMissing, oMissing, oMissing); objProject.FileSaveAs(@"C:\Windows\Temp\LocalCopy.mpp");
    objProject.Quit(PjSaveType.pjDoNotSave); }

    Windows Credentials are used to connect to the PWA, although different user and password could specified but I haven't tried. One should import Microsoft.Office.Interop.MSProject.dll to compile the project.


    Wednesday, August 14, 2019 1:44 PM

All replies

  • Hi JohnMeyers,

    I don't believe there is any way to write/create a .mpp file other than using project.  The only way I know to do this would be to use VBA from MSProject to open each project in Read mode (otherwise you'll hit checkout/checkin issues), and then save the file locally as an MPP plan.  You would need a list of source files, which of course you could put into an Excel file using the Rest API.    If you check out Rod Gill's website / book, he might have some pointers for you.

    The bigger question of course looms, which is what is the business requirement for this, and perhaps there is a better way to solve that requirement.


    Ben Howard [MVP] | web | blog | book | downloads | P2O

    Tuesday, August 13, 2019 12:19 PM
    Moderator
  • Thanks for your reply Ben,

    Wouldn't authentication against O365 + my employer's login page be a problem with VBA? I don't have any experience with that so I'm not sure about its limitations.

    Sadly Rod Gill's book stops in MS Project 2010 so I think wouldn't apply to my scenario in Project Online. I'll try to see if that's the case though.

    My requirement is to work around the fact that administrative backups present on Project Server on-prem are not available on Project Online. This is a blocker for me and my users, as we handle hundreds of projects within dozens of instances and the risk of human error is high.

    I've explored third-party commercial vendors that offer this, but I'd like to explore any home-made options.

    Tuesday, August 13, 2019 3:42 PM
  • If you are logged in with MS Project and using VBA, you will already be authenticated.   Rod's book would give you enough to start with, VBA in MS Project hasn't changed that much over the years...   I used to for the reverse once, to save .mpp plans into a Project Server db.

    Re lack of Admin backup, you need to think that a plan is a working document, firstly remove the ability to delete the plan from the users, secondly educate them, thirdly, if they are still concerned, get them to save locally themselves.   Hundreds of projects in dozens of instances, that's a lot of .mpp data.   


    Ben Howard [MVP] | web | blog | book | downloads | P2O

    Tuesday, August 13, 2019 3:58 PM
    Moderator
  • Thanks Ben, your answers helped me to get into this ugly, working C# prototype:

    private static void start() { var objProcess = Process.Start(@"winproj.exe", "/s https://PWA-URL"); } static void Main(string[] args) { var backgroundThread = new Thread(new ThreadStart(start)); backgroundThread.Start(); Thread.Sleep(10000); ApplicationClass objProject = new ApplicationClass { Visible = false }; object oMissing = System.Reflection.Missing.Value; object oFile = @"<>\test-project"; object oFormat = "MSProject.mpp"; object oReadOnly = true; objProject.DisplayAlerts = false; objProject.FileOpen(oFile, oReadOnly, PjMergeType.pjDoNotMerge, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oFormat, oMissing, PjPoolOpen.pjPoolReadOnly, oMissing, oMissing, oMissing, oMissing); objProject.FileSaveAs(@"C:\Windows\Temp\LocalCopy.mpp");
    objProject.Quit(PjSaveType.pjDoNotSave); }

    Windows Credentials are used to connect to the PWA, although different user and password could specified but I haven't tried. One should import Microsoft.Office.Interop.MSProject.dll to compile the project.


    Wednesday, August 14, 2019 1:44 PM
  • Thanks for sharing John, I'm sure it will be of help to someone in the future :) 

    Ben Howard [MVP] | web | blog | book | downloads | P2O

    Wednesday, August 14, 2019 3:20 PM
    Moderator
  • Hi All,

    I am attempting to do use this code to process updates to a project and I'm encountering errors with the ApplicationClass (the error I get is: interop type ApplicationClass cannot be embedded.  Use the applicable interface instead) requiring me to replace that with Application.  When I use Application my objProject  (keeping it visible) does not show the project server connection.  In other words when you do a file/open there is no "Show me list of all projects"  meaning its not connected to Project Online.  I am also on the fly changing the default project server instance in the registry which works great.  Below is a code snippet of an extract piece that works fine when the project is cached but again doesn't seem to connect to Project Online.

    If I have the project cached this works but if I go into project pro remove the cache and try for a direct connect to project server no joy.  

    I see that you're using the Process.Start for winproj and I've tried that and it throws errors wanting a file name to load.  How is the objProcess and the objProject working together?  

    My goal is to open a project online project read/write via c# make updates and close it.  I don't understand why it doesn't open the project online connection.  Is the winproj.exe piece required and if so why?  If someone has another code example or ideas I'd really appreciate the help.

    public static List<FromTaskLinksClient> GetProjectTaskLinks(string OnlinePWA, string UserName, string Password, List<string> Projects, string OnPrem)
            {
                string PWAInstance = OnlinePWA.Replace("https://", "");
                string[] PWAInstance2 = PWAInstance.Split('/');
                int i = PWAInstance2.Count() - 1;
                string PWAInstance3 = PWAInstance2[i];
                SetDefaultPWAInstance.SetInstanceToYes(PWAInstance3);
                
                List <FromTaskLinksClient> fromTaskLinksClients = new List<FromTaskLinksClient>();

                foreach (var inproject in Projects)
                {
                    Application objProject = new Application();
                    //{
                    //    Visible = true
                    //};

                    objProject.Visible = false;

                    object oMissing = System.Reflection.Missing.Value;

                    object oFile = @"<>\Dependencies"; //+ inproject.ToString();
                    object oReadOnly = true;
                    object oFormat = "MSProject.mpp";
                    object oUserID = UserName.ToString();
                    object oDatabasePassword = Password.ToString();
                   
                    objProject.DisplayAlerts = false;

                    objProject.FileOpenEx(oFile, oReadOnly, PjMergeType.pjDoNotMerge, oMissing, oMissing, oMissing, oMissing, oUserID, oDatabasePassword, oFormat, oMissing, PjPoolOpen.pjPoolReadOnly, oMissing, oMissing, oMissing, oMissing);

                    Microsoft.Office.Interop.MSProject.Project project = objProject.ActiveProject;
                    
                    foreach (Microsoft.Office.Interop.MSProject.Task task in project.Tasks)
                    {
                        if (task.Predecessors.Contains("+") || task.Predecessors.Contains("-"))
                        {
                            FromTaskLinksClient fromTaskClientLinks = new FromTaskLinksClient();
                            string Name = task.Name;
                            fromTaskClientLinks.ProjectName = inproject.ToString();
                            fromTaskClientLinks.TaskPredValue = task.Predecessors;
                            fromTaskClientLinks.TaskPred_Id = Guid.Parse(task.Guid);
                            fromTaskClientLinks.TaskPred_Name = task.Name;
                            fromTaskClientLinks.TaskPred_OutLineLevel = task.OutlineLevel;
                            fromTaskClientLinks.TaskPred_OutLineNumber = task.OutlineNumber;
                            fromTaskClientLinks.ProjectId = Guid.Empty;
                            fromTaskLinksClients.Add(fromTaskClientLinks);    
                        }
                    }
                    objProject.Quit(PjSaveType.pjDoNotSave);
                    
                    
                }
                SetDefaultPWAInstance.SetInstanceToNo(PWAInstance3);
                return fromTaskLinksClients;
            }


    Don Landry

    Monday, October 14, 2019 3:00 PM
  • Hi All,

    I've resolved the Application Class issue by going into the properties within Visual Studio of the Microsoft.Office.Interop.MSProject and changing the property Embed Interop Types from true to false.  This allows me to access the application class.  The only issue I have is when I use winproj.exe to open project it throws this error.  Can anyone tell me how to suppress errors when winproj.exe starts?

    

    The code now looks like this..  I tried setting objProcess.EnableRaisingEvents to false but that didn't work.

    public static List<FromTaskLinksClient> GetProjectTaskLinks(string OnlinePWA, string UserName, string Password, List<string> Projects, string OnPrem)
            {
                string PWAInstance = OnlinePWA.Replace("https://", "");
                string[] PWAInstance2 = PWAInstance.Split('/');
                int i = PWAInstance2.Count() - 1;
                string PWAInstance3 = PWAInstance2[i];
                SetDefaultPWAInstance.SetInstanceToYes(PWAInstance3);
                var objProcess = Process.Start(@"winproj.exe", " / s " + OnlinePWA);
                objProcess.EnableRaisingEvents = false;
                
                

                //Microsoft.Office.Interop.MSProject.ApplicationClass applicationClass = new ApplicationClass
                List <FromTaskLinksClient> fromTaskLinksClients = new List<FromTaskLinksClient>();

                foreach (var inproject in Projects)
                {
                    ApplicationClass objProject = new ApplicationClass();

                    
                    //{
                    //    Visible = true
                    //};

                    objProject.Visible = false;

                    object oMissing = System.Reflection.Missing.Value;

                    object oFile = @"<>\" + inproject.ToString();
                    object oReadOnly = true;
                    object oFormat = "MSProject.mpp";
                    object oUserID = UserName.ToString();
                    object oDatabasePassword = Password.ToString();
                   
                    objProject.DisplayAlerts = false;

                    objProject.FileOpen(oFile, oReadOnly, PjMergeType.pjDoNotMerge, oMissing, oMissing, oMissing, oMissing, oUserID, oDatabasePassword, oFormat, oMissing, PjPoolOpen.pjPoolReadOnly, oMissing, oMissing, oMissing, oMissing);

                    Microsoft.Office.Interop.MSProject.Project project = objProject.ActiveProject;
                    
                    foreach (Microsoft.Office.Interop.MSProject.Task task in project.Tasks)
                    {
                        if (task.Predecessors.Contains("+") || task.Predecessors.Contains("-"))
                        {
                            FromTaskLinksClient fromTaskClientLinks = new FromTaskLinksClient();
                            string Name = task.Name;
                            fromTaskClientLinks.ProjectName = inproject.ToString();
                            fromTaskClientLinks.TaskPredValue = task.Predecessors;
                            fromTaskClientLinks.TaskPred_Id = Guid.Parse(task.Guid);
                            fromTaskClientLinks.TaskPred_Name = task.Name;
                            fromTaskClientLinks.TaskPred_OutLineLevel = task.OutlineLevel;
                            fromTaskClientLinks.TaskPred_OutLineNumber = task.OutlineNumber;
                            fromTaskClientLinks.ProjectId = Guid.Empty;
                            fromTaskLinksClients.Add(fromTaskClientLinks);    
                        }
                    }
                    objProject.Quit(PjSaveType.pjDoNotSave);
                    
                    
                }
                SetDefaultPWAInstance.SetInstanceToNo(PWAInstance3);
                return fromTaskLinksClients;
            }


    Don Landry


    • Edited by Don Landry Monday, October 14, 2019 6:15 PM
    Monday, October 14, 2019 6:14 PM