-
Notifications
You must be signed in to change notification settings - Fork 1
About Those Pesky Unique IDs
Aron Gahagan edited this page Feb 29, 2024
·
7 revisions
- Task Unique IDs
- Assignment Unique IDs
- Assignment Unique IDs in a Master Project
- Master Project 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]
- 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 as2 ^ 20
(note spacing) and not2^20
2. - 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
- 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
whereoTask
is the parent of theoAssignment
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 tooTask.Assignments.UniqueID(lngAssignmentMasterUID)
in order to operate on a particular assignment with certainty that you're targeting the exact Assignment in view.
- 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 aTaskDependency
object,oTask
is aTask
object and the task whose predecessor you're trying to find, useoLink.From.Project
. IfoLink.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
--whetheroLink.From.ExternalTask = True
or not--in order to derive the [master task UID]. - To get the [source task UID]: If
oLink.From.ExternalTask = True
thenoLink.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. UseoLink.From.GetField(185073906) Mod 4194304
. IfoLink.From.ExternalTask = False
then you can useoLink.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'sID
...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.