} // end constructor

        public static DbgSystemInfo[] BuildSystemTree(DbgEngDebugger debugger)
        {
            uint originalSysId = DEBUG_ANY_ID;

            uint[]          sysIds   = debugger.GetDbgEngSystemIds();
            DbgSystemInfo[] sysInfos = new DbgSystemInfo[sysIds.Length];
            if (0 != sysIds.Length)
            {
                originalSysId = debugger.GetCurrentDbgEngSystemId();
            }

            for (int i = 0; i < sysIds.Length; i++)
            {
                debugger.SetCurrentDbgEngSystemId(sysIds[i]);
                if (debugger.IsKernelMode)
                {
                    sysInfos[i] = new DbgKmSystemInfo(sysIds[i]);
                }
                else
                {
                    // User-mode
                    uint[] dbgEngProcIds;
                    uint[] osPids;
                    debugger.GetProcessIds(out dbgEngProcIds, out osPids);
                    sysInfos[i] = new DbgUmSystemInfo(sysIds[i],
                                                      dbgEngProcIds,
                                                      osPids);
                }
            }

            if (originalSysId != DEBUG_ANY_ID)
            {
                debugger.SetCurrentDbgEngSystemId(originalSysId);
            }

            return(sysInfos);
        } // end BuildSystemTree()
        } // end BuildSystemTree()

        public static void CompareTrees(DbgSystemInfo[] oldTree,
                                        DbgSystemInfo[] newTree,
                                        out List <DbgUmSystemInfo> umProcessesRemoved,
                                        out List <DbgUmSystemInfo> umProcessesAdded,
                                        out List <DbgKmSystemInfo> kmTargetsRemoved,
                                        out List <DbgKmSystemInfo> kmTargetsAdded)
        {
            umProcessesRemoved = new List <DbgUmSystemInfo>();
            umProcessesAdded   = new List <DbgUmSystemInfo>();
            kmTargetsRemoved   = new List <DbgKmSystemInfo>();
            kmTargetsAdded     = new List <DbgKmSystemInfo>();

            List <uint> sysIdsRemoved;
            List <uint> sysIdsAdded;
            List <uint> sysIdsBoth;

            DiffCompactSet(oldTree.Select((si) => si.SystemIndex).ToArray(),
                           newTree.Select((si) => si.SystemIndex).ToArray(),
                           out sysIdsAdded,
                           out sysIdsRemoved,
                           out sysIdsBoth);

            // This dictionary maps a system id to "before" and "after" DbgSystemInfos.
            // (the values will be an array of 2 DbgSystemInfo objects: [0] is "before"
            // and [1] is "after")
            var       both      = new Dictionary <uint, DbgSystemInfo[]>();
            const int idxBefore = 0;
            const int idxAfter  = 1;

            foreach (var newInfo in newTree)
            {
                if (sysIdsAdded.Contains(newInfo.SystemIndex))
                {
                    if (newInfo.IsKernelTarget)
                    {
                        kmTargetsAdded.Add((DbgKmSystemInfo)newInfo);
                    }
                    else
                    {
                        umProcessesAdded.Add((DbgUmSystemInfo)newInfo);
                    }
                }
                else
                {
                    Util.Assert(sysIdsBoth.Contains(newInfo.SystemIndex));
                    var bothArray = new DbgSystemInfo[] { null, newInfo };
                    both.Add(newInfo.SystemIndex, bothArray);
                }
            } // end foreach( newInfo )

            foreach (var oldInfo in oldTree)
            {
                if (sysIdsRemoved.Contains(oldInfo.SystemIndex))
                {
                    if (oldInfo.IsKernelTarget)
                    {
                        kmTargetsRemoved.Add((DbgKmSystemInfo)oldInfo);
                    }
                    else
                    {
                        umProcessesRemoved.Add((DbgUmSystemInfo)oldInfo);
                    }
                }
                else
                {
                    Util.Assert(sysIdsBoth.Contains(oldInfo.SystemIndex));
                    both[oldInfo.SystemIndex][idxBefore] = oldInfo;
                }
            } // end foreach( oldInfo )

            foreach (var kvp in both)
            {
                if (kvp.Value[idxBefore].IsKernelTarget)
                {
                    // We don't have to peer into kernel targets to look for added/removed
                    // user-mode processes.
                    Util.Assert(kvp.Value[idxAfter].IsKernelTarget);
                    continue;
                }

                DbgUmSystemInfo beforeInfo = (DbgUmSystemInfo)kvp.Value[idxBefore];
                DbgUmSystemInfo afterInfo  = (DbgUmSystemInfo)kvp.Value[idxAfter];

                List <uint> procIdsRemoved;
                List <uint> procIdsAdded;
                List <uint> procIdsBoth;

                DiffCompactSet(beforeInfo.ProcessIds,
                               afterInfo.ProcessIds,
                               out procIdsAdded,
                               out procIdsRemoved,
                               out procIdsBoth);

                if (0 != procIdsAdded.Count)
                {
                    // TODO: If we have to deal with a lot of processes, this could be
                    // optimized in a few ways: 1) we could take advantage of the fact
                    // that DiffCompactSet returns things in sorted order, so that we
                    // don't have to search the entire OS pid list each time; or 2) we
                    // could just use an array of DEBUG_ANY_ID and get the OS ids
                    // on-demand.
                    //
                    // For the removed processes, though... we might not have any other
                    // way to get the OS pid.
                    uint[] osPids = new uint[procIdsAdded.Count];
                    for (int i = 0; i < osPids.Length; i++)
                    {
                        bool found = false;
                        for (int j = 0; j < afterInfo.ProcessIds.Count; j++)
                        {
                            if (afterInfo.ProcessIds[j] == procIdsAdded[i])
                            {
                                osPids[i] = afterInfo.ProcessOsIds[j];
                                found     = true;
                            }
                        }
                        Util.Assert(found);
                    }

                    umProcessesAdded.Add(new DbgUmSystemInfo(kvp.Key, procIdsAdded, osPids));
                }

                if (0 != procIdsRemoved.Count)
                {
                    uint[] osPids = new uint[procIdsRemoved.Count];
                    for (int i = 0; i < osPids.Length; i++)
                    {
                        bool found = false;
                        for (int j = 0; j < beforeInfo.ProcessIds.Count; j++)
                        {
                            if (beforeInfo.ProcessIds[j] == procIdsRemoved[i])
                            {
                                osPids[i] = beforeInfo.ProcessOsIds[j];
                                found     = true;
                            }
                        }
                        Util.Assert(found);
                    }
                    umProcessesRemoved.Add(new DbgUmSystemInfo(kvp.Key, procIdsRemoved, osPids));
                }
            } // end foreach( sameInfo )
        }     // end CompareTrees()