/*++ Copyright (c) 2000 Microsoft Corporation Module Name: SvrUtil.cxx Abstract: Utility functions for querying RPC Server debug data Author: Kamen Moutafov (kamenm) Dec 99 - Feb 2000 Revision History: --*/ #include #include typedef struct tagServerEnumerationState { int CurrentPosition; int NumberOfProcesses; // the actual size is NumberOfProcesses ULONG ProcessUniqueId[1]; } ServerEnumerationState; RPC_STATUS StartServerEnumeration(ServerEnumerationHandle *pHandle) { ServerEnumerationState *pNewState; void *pProcessDataBuffer = NULL; NTSTATUS NtStatus; int CurrentAllocatedSize = 0x6000; SYSTEM_PROCESS_INFORMATION *pCurrentProcessInfo; unsigned char *pCurrentPos; int NumberOfProcesses; int i; BOOL fResult; do { if (pProcessDataBuffer) { fResult = VirtualFree(pProcessDataBuffer, 0, MEM_RELEASE); ASSERT(fResult); } CurrentAllocatedSize += 4096 * 2; pProcessDataBuffer = VirtualAlloc(NULL, CurrentAllocatedSize, MEM_COMMIT, PAGE_READWRITE); if (pProcessDataBuffer == NULL) return RPC_S_OUT_OF_MEMORY; NtStatus = NtQuerySystemInformation(SystemProcessInformation, pProcessDataBuffer, CurrentAllocatedSize, NULL); } while (NtStatus == STATUS_INFO_LENGTH_MISMATCH); if (!NT_SUCCESS(NtStatus)) return RPC_S_OUT_OF_MEMORY; // walk the buffer - on first pass, we just count the entries pCurrentPos = (unsigned char *)pProcessDataBuffer; pCurrentProcessInfo = (SYSTEM_PROCESS_INFORMATION *)pCurrentPos; NumberOfProcesses = 0; while (TRUE) { // we skip idle process and zombie processes if (pCurrentProcessInfo->UniqueProcessId != NULL) { NumberOfProcesses ++; } // is there a place to advance to? if (pCurrentProcessInfo->NextEntryOffset == 0) break; pCurrentPos += pCurrentProcessInfo->NextEntryOffset; pCurrentProcessInfo = (SYSTEM_PROCESS_INFORMATION *)pCurrentPos; } pNewState = (ServerEnumerationState *) new char [ sizeof(ServerEnumerationState) + (NumberOfProcesses - 1) * sizeof(ULONG)]; // implicit placement // pNewState = new ((NumberOfProcesses - 1) * sizeof(ULONG)) ServerEnumerationState; if (pNewState == NULL) { fResult = VirtualFree(pProcessDataBuffer, 0, MEM_RELEASE); ASSERT(fResult); return RPC_S_OUT_OF_MEMORY; } new (pNewState) ServerEnumerationState; // make the second pass - actual copying of data pCurrentPos = (unsigned char *)pProcessDataBuffer; pCurrentProcessInfo = (SYSTEM_PROCESS_INFORMATION *)pCurrentPos; i = 0; while (TRUE) { // we skip idle process and zombie processes if (pCurrentProcessInfo->UniqueProcessId != NULL) { pNewState->ProcessUniqueId[i] = PtrToUlong(pCurrentProcessInfo->UniqueProcessId); i ++; } // is there a place to advance to? if (pCurrentProcessInfo->NextEntryOffset == 0) break; pCurrentPos += pCurrentProcessInfo->NextEntryOffset; pCurrentProcessInfo = (SYSTEM_PROCESS_INFORMATION *)pCurrentPos; } ASSERT(i == NumberOfProcesses); fResult = VirtualFree(pProcessDataBuffer, 0, MEM_RELEASE); ASSERT(fResult); // make the data available to the user pNewState->CurrentPosition = 0; pNewState->NumberOfProcesses = NumberOfProcesses; *pHandle = pNewState; return RPC_S_OK; } RPC_STATUS OpenNextRPCServer(IN ServerEnumerationHandle Handle, OUT CellEnumerationHandle *pHandle) { ServerEnumerationState *ServerState = (ServerEnumerationState *)Handle; int CurrentPosition; RPC_STATUS RpcStatus; ASSERT(ServerState != NULL); ASSERT(pHandle != NULL); do { CurrentPosition = ServerState->CurrentPosition; if (CurrentPosition >= ServerState->NumberOfProcesses) return RPC_S_INVALID_BOUND; ServerState->CurrentPosition ++; RpcStatus = OpenRPCServerDebugInfo(ServerState->ProcessUniqueId[CurrentPosition], pHandle); } while(RpcStatus == ERROR_FILE_NOT_FOUND); return RpcStatus; } void ResetServerEnumeration(IN ServerEnumerationHandle Handle) { ServerEnumerationState *ServerState = (ServerEnumerationState *)Handle; ASSERT(ServerState != NULL); ServerState->CurrentPosition = 0; } void FinishServerEnumeration(ServerEnumerationHandle *pHandle) { ServerEnumerationState *ServerState; ASSERT (pHandle != NULL); ServerState = *(ServerEnumerationState **)pHandle; ASSERT(ServerState != NULL); delete ServerState; *pHandle = NULL; } DWORD GetCurrentServerPID(IN ServerEnumerationHandle Handle) { ServerEnumerationState *ServerState = (ServerEnumerationState *)Handle; ASSERT(ServerState != NULL); // -1, because the CurrentPosition points to the next server return (DWORD)ServerState->ProcessUniqueId[ServerState->CurrentPosition - 1]; } // a helper function // whenever we detect an inconsistency in one of the lists, // we can call this function, which will determine what to do // with the current section, and will transfer sections between // the OpenedSections list and the InconsistentSections list void InconsistencyDetected(IN LIST_ENTRY *OpenedSections, IN LIST_ENTRY *InconsistentSections, IN LIST_ENTRY *CurrentListEntry, IN OpenedDbgSection *pCurrentSection, BOOL fExceptionOccurred) { LIST_ENTRY *NextEntry; LIST_ENTRY *LastEntry; // if an exception occurred, throw away this section altogether if (fExceptionOccurred) { // save the next entry before we delete this one NextEntry = CurrentListEntry->Flink; RemoveEntryList(CurrentListEntry); CloseDbgSection(pCurrentSection->SectionHandle, pCurrentSection->SectionPointer); delete pCurrentSection; CurrentListEntry = NextEntry; pCurrentSection = CONTAINING_RECORD(CurrentListEntry, OpenedDbgSection, SectionsList); // if the bad section was the last on the list, // there is nothing to add to the grab bag - just // return if (CurrentListEntry == OpenedSections) { return; } } // the chain is broken - we need to throw the rest of the list in // the grab bag // unchain this segment from the opened sections list LastEntry = OpenedSections->Blink; OpenedSections->Blink = CurrentListEntry->Blink; CurrentListEntry->Blink->Flink = OpenedSections; // chain the segment to the inconsistent sections list CurrentListEntry->Blink = InconsistentSections->Blink; InconsistentSections->Blink->Flink = CurrentListEntry; InconsistentSections->Blink = LastEntry; LastEntry->Flink = InconsistentSections; } RPC_STATUS OpenRPCServerDebugInfo(IN DWORD ProcessID, OUT CellEnumerationHandle *pHandle) { RPC_STATUS RpcStatus; HANDLE SecHandle; PVOID SecPointer; int Retries = 10; BOOL fConsistentSnapshotObtained = FALSE; BOOL fNeedToRetry; CellSection *CurrentSection; DWORD SectionNumbers[2]; OpenedDbgSection *pCurrentSection; // each section as it is opened, is linked on one of those lists // if the view of the sections is consistent, we link it to opened // sections. Otherwise, we link it to InconsistentSections LIST_ENTRY OpenedSections; LIST_ENTRY InconsistentSections; LIST_ENTRY *CurrentListEntry; DWORD *pActualSectionNumbers; BOOL fExceptionOccurred; LIST_ENTRY *LastEntry; BOOL fFound; int NumberOfCommittedPages; BOOL fConsistencyPass = FALSE; DWORD LocalPageSize; SectionsSnapshot *LocalSectionsSnapshot; BOOL fResult; RpcStatus = InitializeDbgLib(); if (RpcStatus != RPC_S_OK) return RpcStatus; LocalPageSize = GetPageSize(); // loop until we obtain a consistent snapshot or we are out of // retry attempts. We declare a snapshot to be consistent // if we manage to: // - open all sections // - copy their contents to a private memory location // - verify that the section chain is still consistent after the copying // For this purpose, when we copy all the sections, we make one more // pass at the section chain to verify it is consistent using the special // flag fConsistencyPass. InconsistentSections.Blink = InconsistentSections.Flink = &InconsistentSections; OpenedSections.Blink = OpenedSections.Flink = &OpenedSections; while (Retries > 0) { // on entry to the loop, the state will be this - OpenSections will // contain a consistent view of the sections. Inconsistent sections // will be a grab bag of sections we could not bring into // consistent view. It's used as a cache to facilitate quick // recovery // we are just starting, or we are recovering from an inconsistency // found somewhere. As soon as somebody detects an inconsistency, // they will jump here. First thing is to try to establish what // part of the chain is consistent. Walk the open sections for // this purpose. We walk as far as we can, and then we declare // the rest of the sections inconsistent, and we throw them in // the grab bag SectionNumbers[0] = SectionNumbers[1] = 0; CurrentListEntry = OpenedSections.Flink; fNeedToRetry = FALSE; while (CurrentListEntry != &OpenedSections) { pCurrentSection = CONTAINING_RECORD(CurrentListEntry, OpenedDbgSection, SectionsList); if ((SectionNumbers[0] != pCurrentSection->SectionNumbers[0]) || (SectionNumbers[1] != pCurrentSection->SectionNumbers[1])) { fNeedToRetry = TRUE; } else { __try { // attempt to read the numbers of the next section // we do this within try/except since server may free this // memory and we will get toast SectionNumbers[0] = pCurrentSection->SectionPointer->NextSectionId[0]; SectionNumbers[1] = pCurrentSection->SectionPointer->NextSectionId[1]; fExceptionOccurred = FALSE; // note that the SectionNumbers array will be used after the end of // the loop - make sure we don't whack them } __except (EXCEPTION_EXECUTE_HANDLER) { fExceptionOccurred = TRUE; fNeedToRetry = TRUE; } } if (fNeedToRetry) { // if this is the first section, the server went down. There is no // legal way for the server to have inconsistent first section if (CurrentListEntry == OpenedSections.Flink) { RpcStatus = ERROR_FILE_NOT_FOUND; goto CleanupAndExit; } InconsistencyDetected(&OpenedSections, &InconsistentSections, CurrentListEntry, pCurrentSection, fExceptionOccurred); fNeedToRetry = TRUE; break; } CurrentListEntry = CurrentListEntry->Flink; } // walking is complete. Did we detect inconsistency? if (fNeedToRetry) { Retries --; fConsistencyPass = FALSE; continue; } else if (fConsistencyPass) { // this is the only place we break out of the loop - // the consistency pass has passed break; } // whatever we have in the opened sections list is consistent // if there was something in the list keep reading, // otherwise, start reading if (IsListEmpty(&OpenedSections)) { pActualSectionNumbers = NULL; } else { pCurrentSection = CONTAINING_RECORD(OpenedSections.Blink, OpenedDbgSection, SectionsList); // we re-use the section numbers from the loop above. They can be 0 at // this point if the last section got dropped pActualSectionNumbers = SectionNumbers; } // make a pass over the sections, opening each one, but only if // case we're missing parts of the chain or this is the first time. // Otherwise, skip this step while ((SectionNumbers[0] != 0) || (SectionNumbers[1] != 0) || (pActualSectionNumbers == NULL)) { // we know which section we're looking for // first, search the grab bag. We can only do this for a non-first // section. The first section never goes to the grab bag // pActualSectionNumbers will contain the section we're looking for fFound = FALSE; if (pActualSectionNumbers) { CurrentListEntry = InconsistentSections.Flink; while (CurrentListEntry != &InconsistentSections) { pCurrentSection = CONTAINING_RECORD(CurrentListEntry, OpenedDbgSection, SectionsList); // it is impossible that a well behaving server will have // opened a different section with the same numbers, because we // keep the section object opened. if ((pActualSectionNumbers[0] == pCurrentSection->SectionNumbers[0]) && (pActualSectionNumbers[1] == pCurrentSection->SectionNumbers[1])) { // found something RemoveEntryList(CurrentListEntry); // if we had already made a copy of this one, free it, as it is // probably inconsistent if (pCurrentSection->SectionCopy) { fResult = VirtualFree(pCurrentSection->SectionCopy, 0, MEM_RELEASE); ASSERT(fResult); pCurrentSection->SectionCopy = NULL; } fFound = TRUE; break; } CurrentListEntry = CurrentListEntry->Flink; } } if (fFound == FALSE) { // nothing in the grab bag - try to open it the normal way RpcStatus = OpenDbgSection(&SecHandle, &SecPointer, ProcessID, pActualSectionNumbers); if (RpcStatus == ERROR_FILE_NOT_FOUND) { // if this is the first time, this is not a server - bail out if (pActualSectionNumbers == NULL) { goto CleanupAndExit; } // not the first time - we have an inconsistent view - need to retry fNeedToRetry = TRUE; break; } else if (RpcStatus != RPC_S_OK) { goto CleanupAndExit; } pCurrentSection = new OpenedDbgSection; if (pCurrentSection == NULL) { RpcStatus = RPC_S_OUT_OF_MEMORY; CloseDbgSection(SecHandle, SecPointer); goto CleanupAndExit; } pCurrentSection->SectionHandle = SecHandle; if (pActualSectionNumbers) { pCurrentSection->SectionNumbers[0] = pActualSectionNumbers[0]; pCurrentSection->SectionNumbers[1] = pActualSectionNumbers[1]; } else { pCurrentSection->SectionNumbers[0] = pCurrentSection->SectionNumbers[1] = 0; } pCurrentSection->SectionPointer = (CellSection *) SecPointer; pCurrentSection->SectionCopy = NULL; } // either we have found this in the grab bag, or we have just opened it // both ways, try to get the section numbers we expect for the next section __try { // load the section numbers that we expect for the next iteration of the // loop SectionNumbers[0] = pCurrentSection->SectionPointer->NextSectionId[0]; SectionNumbers[1] = pCurrentSection->SectionPointer->NextSectionId[1]; pActualSectionNumbers = SectionNumbers; fExceptionOccurred = FALSE; } __except (EXCEPTION_EXECUTE_HANDLER) { fExceptionOccurred = TRUE; } if (fExceptionOccurred) { delete pCurrentSection; CloseDbgSection(SecHandle, SecPointer); fNeedToRetry = TRUE; break; } InsertTailList(&OpenedSections, &pCurrentSection->SectionsList); } if (fNeedToRetry) { Retries --; fConsistencyPass = FALSE; continue; } // at this point, we have opened all the sections // now we need to allocate memory for the snapshots and to do the copying CurrentListEntry = OpenedSections.Flink; while (CurrentListEntry != &OpenedSections) { pCurrentSection = CONTAINING_RECORD(CurrentListEntry, OpenedDbgSection, SectionsList); __try { // do all the allocation and copying only if it hasn't been done yet if (pCurrentSection->SectionCopy == NULL) { NumberOfCommittedPages = pCurrentSection->SectionPointer->LastCommittedPage; pCurrentSection->SectionCopy = (CellSection *)VirtualAlloc(NULL, NumberOfCommittedPages * LocalPageSize, MEM_COMMIT, PAGE_READWRITE); if (pCurrentSection->SectionCopy == NULL) { RpcStatus = RPC_S_OUT_OF_MEMORY; goto CleanupAndExit; } memcpy(pCurrentSection->SectionCopy, pCurrentSection->SectionPointer, NumberOfCommittedPages * LocalPageSize); pCurrentSection->SectionID = pCurrentSection->SectionPointer->SectionID; pCurrentSection->CommittedPagesInSection = NumberOfCommittedPages; } fExceptionOccurred = FALSE; } __except (EXCEPTION_EXECUTE_HANDLER) { fExceptionOccurred = TRUE; } if (fExceptionOccurred) { if (pCurrentSection->SectionCopy) { fResult = VirtualFree(pCurrentSection->SectionCopy, 0, MEM_RELEASE); ASSERT(fResult); pCurrentSection->SectionCopy = NULL; } // the section got out of sync InconsistencyDetected(&OpenedSections, &InconsistentSections, CurrentListEntry, pCurrentSection, fExceptionOccurred); fNeedToRetry = TRUE; break; } CurrentListEntry = CurrentListEntry->Flink; } if (fNeedToRetry) { Retries --; fConsistencyPass = FALSE; continue; } else { fConsistencyPass = TRUE; } } // if we managed to get a consistent view, unmap the shared sections and // save the opened section list if (Retries != 0) { ASSERT(fConsistencyPass == TRUE); ASSERT(fNeedToRetry == FALSE); ASSERT(!IsListEmpty(&OpenedSections)); CurrentListEntry = OpenedSections.Flink; while (CurrentListEntry != &OpenedSections) { pCurrentSection = CONTAINING_RECORD(CurrentListEntry, OpenedDbgSection, SectionsList); CloseDbgSection(pCurrentSection->SectionHandle, pCurrentSection->SectionPointer); pCurrentSection->SectionHandle = NULL; pCurrentSection->SectionPointer = NULL; pCurrentSection->SectionNumbers[0] = pCurrentSection->SectionNumbers[1] = 0; CurrentListEntry = CurrentListEntry->Flink; } // save the opened section list CurrentListEntry = OpenedSections.Flink; pCurrentSection = CONTAINING_RECORD(CurrentListEntry, OpenedDbgSection, SectionsList); LocalSectionsSnapshot = new SectionsSnapshot; if (LocalSectionsSnapshot != NULL) { LocalSectionsSnapshot->CellIndex = 0; LocalSectionsSnapshot->FirstOpenedSection = pCurrentSection; LocalSectionsSnapshot->CurrentOpenedSection = pCurrentSection; // unchain the opened sections // terminate the chain with NULL OpenedSections.Blink->Flink = NULL; OpenedSections.Blink = OpenedSections.Flink = &OpenedSections; // that's the only place where we return success *pHandle = (CellEnumerationHandle)LocalSectionsSnapshot; RpcStatus = RPC_S_OK; } else { // let the CleanupAndExit code destroy the lists RpcStatus = RPC_S_OUT_OF_MEMORY; } } else { // we couldn't get a consistent snapshot of the server and // we ran out of retries RpcStatus = RPC_S_CANNOT_SUPPORT; } CleanupAndExit: // walk the two lists, and free all sections on them CurrentListEntry = OpenedSections.Flink; while (CurrentListEntry != &OpenedSections) { pCurrentSection = CONTAINING_RECORD(CurrentListEntry, OpenedDbgSection, SectionsList); // advance the pointer while we haven't freed the stuff CurrentListEntry = CurrentListEntry->Flink; if (pCurrentSection->SectionCopy) { fResult = VirtualFree(pCurrentSection->SectionCopy, 0, MEM_RELEASE); ASSERT(fResult); } if (pCurrentSection->SectionHandle) { ASSERT(pCurrentSection->SectionPointer); CloseDbgSection(pCurrentSection->SectionHandle, pCurrentSection->SectionPointer); } delete pCurrentSection; } CurrentListEntry = InconsistentSections.Flink; while (CurrentListEntry != &InconsistentSections) { pCurrentSection = CONTAINING_RECORD(CurrentListEntry, OpenedDbgSection, SectionsList); // advance the pointer while we haven't freed the stuff CurrentListEntry = CurrentListEntry->Flink; if (pCurrentSection->SectionCopy) { fResult = VirtualFree(pCurrentSection->SectionCopy, 0, MEM_RELEASE); ASSERT(fResult); } if (pCurrentSection->SectionHandle) { ASSERT(pCurrentSection->SectionPointer); CloseDbgSection(pCurrentSection->SectionHandle, pCurrentSection->SectionPointer); } delete pCurrentSection; } return RpcStatus; } DebugCellUnion *GetNextDebugCellInfo(IN CellEnumerationHandle Handle, OUT DebugCellID *CellID) { SectionsSnapshot *Snapshot = (SectionsSnapshot *)Handle; OpenedDbgSection *CurrentSection, *NextSection; DebugCellGeneric *CurrentCell; int CurrentCellIndex; DebugCellGeneric *LastCellForCurrentSection; DWORD LocalPageSize = GetPageSize(); ASSERT(Handle != NULL); CurrentSection = Snapshot->CurrentOpenedSection; LastCellForCurrentSection = GetLastCellForSection(CurrentSection, LocalPageSize); if (Snapshot->CellIndex == 0) { #ifdef _WIN64 Snapshot->CellIndex = 2; #else Snapshot->CellIndex = 1; #endif } CurrentCell = GetCellForSection(CurrentSection, Snapshot->CellIndex); while (TRUE) { // did we exhaust the current section? if (CurrentCell > LastCellForCurrentSection) { // try to advance to the next one if (CurrentSection->SectionsList.Flink) { CurrentSection = CONTAINING_RECORD(CurrentSection->SectionsList.Flink, OpenedDbgSection, SectionsList); Snapshot->CurrentOpenedSection = CurrentSection; #ifdef _WIN64 Snapshot->CellIndex = 2; #else Snapshot->CellIndex = 1; #endif LastCellForCurrentSection = GetLastCellForSection(CurrentSection, LocalPageSize); CurrentCell = GetCellForSection(CurrentSection, Snapshot->CellIndex); continue; } return NULL; } CellID->CellID = (USHORT) Snapshot->CellIndex; Snapshot->CellIndex ++; if ((CurrentCell->Type == dctCallInfo) || (CurrentCell->Type == dctThreadInfo) || (CurrentCell->Type == dctEndpointInfo) || (CurrentCell->Type == dctClientCallInfo)) { CellID->SectionID = (USHORT)CurrentSection->SectionID; return (DebugCellUnion *)CurrentCell; } CurrentCell = (DebugCellGeneric *)((unsigned char *)CurrentCell + sizeof(DebugFreeCell)); } return NULL; } void ResetRPCServerDebugInfo(IN CellEnumerationHandle Handle) { SectionsSnapshot *LocalSnapshot = (SectionsSnapshot *)Handle; ASSERT(Handle != NULL); LocalSnapshot->CellIndex = 0; LocalSnapshot->CurrentOpenedSection = LocalSnapshot->FirstOpenedSection; } void CloseRPCServerDebugInfo(IN CellEnumerationHandle *pHandle) { SectionsSnapshot *LocalSnapshot; OpenedDbgSection *DbgSection; LIST_ENTRY *CurrentListEntry; ASSERT(pHandle != NULL); LocalSnapshot = (SectionsSnapshot *)*pHandle; ASSERT(LocalSnapshot != NULL); ASSERT(LocalSnapshot->FirstOpenedSection != NULL); DbgSection = LocalSnapshot->FirstOpenedSection; do { // advance while we can CurrentListEntry = DbgSection->SectionsList.Flink; // free the section ASSERT(DbgSection->SectionCopy); VirtualFree(DbgSection->SectionCopy, 0, MEM_RELEASE); delete DbgSection; // calculate next record. Note that this will not AV even if // CurrentListEntry is NULL - this is just offset calculation DbgSection = CONTAINING_RECORD(CurrentListEntry, OpenedDbgSection, SectionsList); } while (CurrentListEntry != NULL); delete LocalSnapshot; *pHandle = NULL; } typedef struct tagRPCSystemWideCellEnumeration { ServerEnumerationHandle serverHandle; CellEnumerationHandle cellHandle; } RPCSystemWideCellEnumeration; RPC_STATUS OpenRPCSystemWideCellEnumeration(OUT RPCSystemWideCellEnumerationHandle *pHandle) { RPCSystemWideCellEnumeration *cellEnum; RPC_STATUS Status; DebugCellUnion *NextCell; ASSERT(pHandle != NULL); *pHandle = NULL; cellEnum = new RPCSystemWideCellEnumeration; if (cellEnum == NULL) return RPC_S_OUT_OF_MEMORY; cellEnum->cellHandle = NULL; cellEnum->serverHandle = NULL; Status = StartServerEnumeration(&cellEnum->serverHandle); if (Status != RPC_S_OK) { delete cellEnum; return Status; } Status = OpenNextRPCServer(cellEnum->serverHandle, &cellEnum->cellHandle); // if we're done, we will get RPC_S_SERVER_INVALID_BOUND - ok to // just return to caller if (Status != RPC_S_OK) { FinishServerEnumeration(&cellEnum->serverHandle); delete cellEnum; return Status; } *pHandle = (RPCSystemWideCellEnumerationHandle) cellEnum; return RPC_S_OK; } RPC_STATUS GetNextRPCSystemWideCell(IN RPCSystemWideCellEnumerationHandle handle, OUT DebugCellUnion **NextCell, OUT DebugCellID *CellID, OUT DWORD *ServerPID OPTIONAL) { RPCSystemWideCellEnumeration *cellEnum = (RPCSystemWideCellEnumeration *)handle; RPC_STATUS Status; ASSERT(cellEnum != NULL); // loop skipping empty servers do { *NextCell = GetNextDebugCellInfo(cellEnum->cellHandle, CellID); // this server is done - move on to the next if (*NextCell == NULL) { CloseRPCServerDebugInfo(&cellEnum->cellHandle); Status = OpenNextRPCServer(cellEnum->serverHandle, &cellEnum->cellHandle); // if we're done with all servers, we will get RPC_S_SERVER_INVALID_BOUND - ok to // just return to caller. Caller needs to call us back to finish enumeration if (Status != RPC_S_OK) { // remember that this failed so that we don't try to clean it up // when finishing the enumeration cellEnum->cellHandle = NULL; return Status; } } } while(*NextCell == NULL); if (ServerPID && (*NextCell != NULL)) { *ServerPID = GetCurrentServerPID(cellEnum->serverHandle); } return RPC_S_OK; } DebugCellUnion *GetRPCSystemWideCellFromCellID(IN RPCSystemWideCellEnumerationHandle handle, IN DebugCellID CellID) { RPCSystemWideCellEnumeration *cellEnum = (RPCSystemWideCellEnumeration *)handle; return GetCellByDebugCellID(cellEnum->cellHandle, CellID); } void FinishRPCSystemWideCellEnumeration(IN OUT RPCSystemWideCellEnumerationHandle *pHandle) { RPCSystemWideCellEnumeration *cellEnum; ASSERT(pHandle != NULL); cellEnum = (RPCSystemWideCellEnumeration *)*pHandle; ASSERT(cellEnum != NULL); if (cellEnum->cellHandle) { CloseRPCServerDebugInfo(&cellEnum->cellHandle); } FinishServerEnumeration(&cellEnum->serverHandle); delete cellEnum; *pHandle = NULL; } RPC_STATUS ResetRPCSystemWideCellEnumeration(IN RPCSystemWideCellEnumerationHandle handle) { RPCSystemWideCellEnumeration *cellEnum = (RPCSystemWideCellEnumeration *)handle; RPC_STATUS Status; ASSERT(cellEnum != NULL); if (cellEnum->cellHandle) { CloseRPCServerDebugInfo(&cellEnum->cellHandle); cellEnum->cellHandle = NULL; } ResetServerEnumeration(cellEnum->serverHandle); Status = OpenNextRPCServer(cellEnum->serverHandle, &cellEnum->cellHandle); if (Status != RPC_S_OK) { // remember that this failed so that we don't try to clean it up // when finishing the enumeration cellEnum->cellHandle = NULL; } return Status; }