About Those Pesky Unique IDs - AronGahagan/cpt-dev GitHub Wiki

Table of Contents

  1. Task Unique IDs
  2. Assignment Unique IDs
  3. Assignment Unique IDs in a Master Project
  4. Master Project Unique IDs

Task Unique IDs

  • The displayed Task Unique ID in a Master/Subproject is the result of "clock math": ([inserted subproject UID] * 4194304) + [task UID native] - we'll call this the [task UID inserted] as opposed to [task UID native]
  • The [task UID native] can thus be derived: [task UID native] = [task UID inserted] Mod 4194304
  • The [inserted subproject UID] can thus be derived: Round(([task UID inserted]-[task UID native])/4194304,1)
  • HOWEVER, something weird happens when a subproject is inserted, deleted, and later inserted again: the inserted subproject gets a new "Task UID" but the [task UID inserted] values retain the number they got when the subproject was inserted the first time. THEREFORE the inserted subproject UID should always be derived at runtime from an actual [task UID inserted]

Assignment Unique IDs

  • The displayed Assignment Unique ID in the Task Usage view is off by 2^20 (or 1,048,576)1: [displayed assignment UID] = [assignment UID returned via VBA] - 1048576 or [displayed assignment UID] = [assignment UID returned via VBA] - (2 ^ 20). Note that in 64 bit versions of Office VBA, exponents must be written as 2 ^ 20 (note spacing) and not 2^202.
  • Oddly, in a standalone project file, you can reference a particular Assignment in VBA using either the displayed UID or the UID returned via VBA.
  • Further, and incredibly, you can pass any Assignment UID to the Assignments object of any other task in the file and manipulate the values. (This confirms earlier suspicions that Assignments are stored as a separate object/table/collection in a normalized RDBMS, but that object/table/collection is not directly exposed via VBA.)

1: https://stackoverflow.com/a/42334614/3481213

2: https://stackoverflow.com/questions/51264287/vba-power-operator-not-working-as-expected-in-64-bit-vba

Assignment Unique IDs in a Master Project

  • The displayed assignment UID in a master project is the result of the formula: [master assignment UID] = (([inserted project summary UID] + 1) * 4194304) + [source assignment UID]
  • The [inserted project summary UID] can be derived: lngInsertedUID = ActiveProject.Subprojects(oTask.Project).InsertedProjectSummary.UniqueID where oTask is the parent of the oAssignment under scrutiny.
  • The [source assignment UID] can be derived by: lngAssignmentSourceUID = [displayed assignment UID] Mod 4194304 or, if you're extracting the Assignment UID by VBA, lngAssignmentSourceUID = oTask.Assignments(1).UniqueID - (2 ^ 20).
  • The Assignment UID returned by VBA is high by 1,048,576 (or 2^20) but is the same whether in the standalone project or the master project. However, in a master project, you cannot reference a particular Assignment in VBA using the UID returned by VBA. It must be referenced by the displayed [master assignment UID].
  • The [master assignment UID] can then be calculated: [master assignment UID] = ((lngInsertedUID + 1) * 4194304) + lngAssignmentSourceUID - and it is this value that must be passed to oTask.Assignments.UniqueID(lngAssignmentMasterUID) in order to operate on a particular assignment with certainty that you're targeting the exact Assignment in view.

Master Project Unique IDs

  • EDIT: the only way to accurately get at the [inserted subproject UID] is to derive it from an inserted task's calculated UID at runtime. Some of the below is therefore a bit obsolete...
  • The displayed Unique ID in a master project is the result of the formula: [master task UID] = (([inserted project summary UID] + 1) * 4194304) + [source task UID]
  • To get the [inserted project summary UID], where oLink is a TaskDependency object, oTask is a Task object and the task whose predecessor you're trying to find, use oLink.From.Project. If oLink.From.ExternalTask = True then you'll need to clean up the result.
...
If oLink.To.Guid = oTask.Guid Then 'limit to predecessor(s)
    strProject = oLink.From.Project 'returns server project with "<>\" or a file path
    If oLink.From.ExternalTask = True Then
        strProject = Replace(strProject,".mpp","") 'strips the file extension, if it exists
        strProject = Mid(strProject,InstRev(strProject,"\")-1) 'strips off the path for local or server paths
    End if
    lngInsertedUID = ActiveProject.Subprojects(strProject).InsertedSummaryTask.UniqueID
...
  • Note that you need to get lngInsertedUID--whether oLink.From.ExternalTask = True or not--in order to derive the [master task UID].
  • To get the [source task UID]: If oLink.From.ExternalTask = True then oLink.From.UniqueID refers not to the "giver" task UID in the external project, but to the UID of the ghost task in the "receiver" project. Use oLink.From.GetField(185073906) Mod 4194304. If oLink.From.ExternalTask = False then you can use oLink.From.UniqueID.
    If oLink.From.ExternalTask Then
        lngLinkSourceUID = oLink.From.GetField(185073906) Mod 4194304
    Else
        lngLinkSourceUID = oLink.From.UniqueID
    End If
  • Finally, to get the [master task UID] of the Predecessor, use:
    lngLinkMasterUID = ((lngInsertedUID + 1) * 4194304) + lngLinkSourceUID 
  • Now you can execute a Find or refer to the Predecessor by its Unique ID as it exists in the Master Project in your code (ActiveProject.Tasks.UniqueID(lngLinkMasterUID)). (EditGoTo will not work, though, because it relies on a Task's ID...which is useless in a Master/Sub situation.)

Interesting note: I heard recently that it was the pre-Deltek MPM (Microframe Project Manager) folks that originally designed how master/subs UIDs worked.

⚠️ **GitHub.com Fallback** ⚠️