2

I'm currently writing a VBA macro for MS Project. I want to loop through all the tasks in an integrated master file (file that contains linked subprojects) and write the original unique ID values of the tasks. I know that inserted subprojects get a "seed" value (some multiple of 4194304) and all of the tasks' UIDs in the integrated file become the seed value + the original UID value

If I want to get the original UID of a task, how can I find out what the subproject's seed value is? I've tried using the Index property of the project object the task belongs to, then multiplying that by 4194304, but the Index property does not seem to always return the correct subproject index value.

Sub test()

Dim t As Task
Dim subProjIndex As Integer
Dim originalUID As Long
Dim baseSeedVal As Long

baseSeedVal = 4194304

For Each t In Application.ActiveProject.Tasks
    subProjIndex = Application.Projects(t.Project).Index 'does not always give me the correct subproject index value
    originalUID = t.UniqueID - (baseSeedVal * subProjectIndex)
Next t

End Sub
Kenny Arnold
  • 406
  • 2
  • 8

1 Answers1

1

There are two separate questions here, 1) the question in the body of the post and 2) the title of the post.

If I want to get the original UID of a task...

This answer is straight-forward:

originalUID = t.UniqueID Mod 4194304

Is there a way to get a subproject's seed value programmatically?

Sometimes it is necessary to find the subproject task's Unique ID as it is in the Master Project. This comes up mainly when dealing with external links (e.g. the predecessor or successor to a task is in a different project).

This code populates a dictionary that contains the name of each subproject and the base master uid. Given the native task uid, this allows you to find the task in the master project by it's master task uid.

Dim SubProjects As New Scripting.Dictionary

Const MasterUidSeed = 4194304

Function GetSubProjectInfo(ByVal prj As MSProject.Project) As Scripting.Dictionary

    ShowAllTasks

    Dim Subs As New Scripting.Dictionary
    Dim SubProj As Subproject
    
    For Each SubProj In ActiveProject.SubProjects

        Application.Find "Unique ID", "equals", SubProj.InsertedProjectSummary.UniqueID
        Application.SelectCellDown

        Dim UIDBase As Long
        UIDBase = (Application.ActiveCell.Task.UniqueID \ MasterUidSeed) * MasterUidSeed

        Subs.Add SubProj.SourceProject.Name, UIDBase

    Next

    Set GetSubProjectInfo = Subs

End Function

 Sub ShowAllTasks()

    ' expands all tasks to make sure all collapsed subprojects are loaded; presumes a task view is active
    With Application
        .SummaryTasksShow (True)
        .FilterClear
        .SelectAll
        .OutlineShowAllTasks
        .SelectBeginning
    End With

End Sub

See this SO post for how to use Dictionaries in VBA.

More information about how master project task uids work and why using the subproject Index can lead to unexpected results:

Unique IDs within a master project

When tasks are added to a project, an incremental Unique ID is assigned, starting with 1. When projects are combined within a master project, the Unique IDs are changed by adding a seed value so that there are no duplicates within the master project.

The seed value is based on an internal, subproject "index". The first subproject's tasks are given a seed value of 4194304, the second subproject's tasks have a seed value of 8388608 (4194304 * 2), and so forth. This "index" is different from the Index property of the subproject which refers to the current position of the subrproject within the master.

If a subproject is removed from the master, it's "index" is not re-used. Similarly, if subprojects are rearranged in the master, the "index" values do not change. Therefore, you cannot use the Index property of the subproject object to obtain the internal "index" value that is used to create the seed as that property simply indicates the order of subprojects.

Rachel Hettinger
  • 7,927
  • 2
  • 21
  • 31
  • Rachel, thanks for your answer. So from your code snippet to populate the dictionary with the Master UID seeds, I gather that the UID of task right below the inserted project summary task always corresponds to the numerical order the subproject was inserted into the master project correct? Even though the UID of that summary line in the source project may be different? If not, then to get the Master UID seed I presume you would want to round down to the nearest whole number. – Kenny Arnold Apr 26 '21 at 19:17
  • The Project Summary Task will have a native UID in the master schedule (e.g a low number) but its own tasks will have a new UID in the master based on the seed assigned when the subproject was added. By ensuring that all tasks are visible and expanded, the task below the project summary task will be part of the subproject unless the subproject as no tasks. And once we find that task we can deduce the seed by using integer division. – Rachel Hettinger Apr 26 '21 at 19:33
  • Right so I'm looking at this line from your answer: UIDBase = (Application.ActiveCell.Task.UniqueID \ MasterUidSeed) * MasterUidSeed So if the value that returned by Application.ActiveCell.Task.UniqueID is not an even multiple of the MasterUidSeed value, we would need to round down to the nearest whole number after we divide by MasterUidSeed before multiplying by MasterUidSeed, correct? – Kenny Arnold Apr 27 '21 at 20:20
  • That’s what integer division does—it discards the fractional part. See [https://mathworld.wolfram.com/IntegerDivision.html](https://mathworld.wolfram.com/IntegerDivision.html). – Rachel Hettinger Apr 28 '21 at 01:42