windows-nt/Source/XPSP1/NT/com/rpc/runtime/dbglib/dbgprint.cxx
2020-09-26 16:20:57 +08:00

652 lines
24 KiB
C++

/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
DbgPrint.cxx
Abstract:
Functions for printing debug information
Author:
Kamen Moutafov (kamenm) Dec 99 - Feb 2000
Revision History:
--*/
#include <precomp.hxx>
#include <wincrypt.h>
#include <rpctrans.hxx>
void PrintTimeInSeconds(RPC_CHAR *HeaderString, DWORD TimeInMilliseconds,
PRPCDEBUG_OUTPUT_ROUTINE PrintRoutine)
{
PrintRoutine("%S (in seconds since boot):%d.%d (0x%X.%X)\n", HeaderString,
TimeInMilliseconds / 1000, TimeInMilliseconds % 1000,
TimeInMilliseconds / 1000, TimeInMilliseconds % 1000);
}
void PrintDebugCellID(RPC_CHAR *HeaderString, DebugCellID CellID,
PRPCDEBUG_OUTPUT_ROUTINE PrintRoutine)
{
PrintRoutine("%S: 0x%X.%X\n", HeaderString, CellID.SectionID, CellID.CellID);
}
const RPC_CHAR *CallStatusStrings[] = {L"Allocated", L"Active", L"Dispatched"};
#define UnknownValueLiteral (L"Unknown/Invalid")
const RPC_CHAR *UnknownValue = UnknownValueLiteral;
const RPC_CHAR *ConnectionAuthLevelStrings[] = {L"Default", L"None", L"Connect", L"Call", L"Packet",
L"Packet Integrity", L"Packet Privacy", UnknownValueLiteral, UnknownValueLiteral};
const RPC_CHAR *ConnectionAuthServiceStrings[] = {L"None", L"NTLM", L"Kerberos/Snego", L"Other"};
const RPC_CHAR *ProtocolSequenceStrings[] = {L"TCP", L"UDP", L"LRPC", UnknownValueLiteral, UnknownValueLiteral,
L"SPX", UnknownValueLiteral, L"IPX", L"NMP", UnknownValueLiteral, UnknownValueLiteral, L"NB",
UnknownValueLiteral, UnknownValueLiteral, UnknownValueLiteral, L"DSP", L"DDP", UnknownValueLiteral,
UnknownValueLiteral, L"SPP", UnknownValueLiteral, UnknownValueLiteral,
L"MQ", UnknownValueLiteral, L"HTTP"};
const int FirstProtocolSequenceTowerID = TCP_TOWER_ID;
const int LastProtocolSequenceTowerID = HTTP_TOWER_ID;
RPC_CHAR *GetProtocolSequenceString(int ProtocolSequenceID)
{
RPC_CHAR *CurrentString;
if ((ProtocolSequenceID < FirstProtocolSequenceTowerID)
|| (ProtocolSequenceID > LastProtocolSequenceTowerID))
{
CurrentString = (RPC_CHAR *) UnknownValue;
}
else
{
CurrentString = (RPC_CHAR *) ProtocolSequenceStrings[
ProtocolSequenceID - FirstProtocolSequenceTowerID];
}
ASSERT(CurrentString != NULL);
return CurrentString;
}
void PrintCallInfoHeader(PRPCDEBUG_OUTPUT_ROUTINE PrintRoutine)
{
PrintRoutine("PID CELL ID ST PNO IFSTART THRDCELL CALLFLAG CALLID LASTTIME CONN/CLN\n");
PrintRoutine("----------------------------------------------------------------------------\n");
}
void PrintCallInfoBody(IN DWORD ProcessID, IN DebugCellID CellID,
IN DebugCallInfo *CallInfo, PRPCDEBUG_OUTPUT_ROUTINE PrintRoutine)
{
PrintRoutine("%04x %04x.%04x %02x %03x %08lx %04x.%04x %08lx %08lx %08lx ",
ProcessID, CellID.SectionID, CellID.CellID, CallInfo->Status,
CallInfo->ProcNum, CallInfo->InterfaceUUIDStart,
CallInfo->ServicingTID.SectionID, CallInfo->ServicingTID.CellID,
CallInfo->CallFlags, CallInfo->CallID, CallInfo->LastUpdateTime);
if (CallInfo->CallFlags & DBGCELL_LRPC_CALL)
{
PrintRoutine("%04x.%04x\n", (DWORD)CallInfo->PID, (DWORD)CallInfo->TID);
}
else
{
PrintRoutine("%04x.%04x\n", (DWORD)CallInfo->Connection.SectionID,
(DWORD)CallInfo->Connection.CellID);
}
}
void GetAndPrintCallInfo(IN DWORD CallID OPTIONAL, IN DWORD IfStart OPTIONAL,
IN int ProcNum OPTIONAL, IN DWORD ProcessID OPTIONAL,
PRPCDEBUG_OUTPUT_ROUTINE PrintRoutine)
{
CallInfoEnumerationHandle h;
RPC_STATUS Status;
DebugCallInfo *NextCall;
DebugCellID CellID;
DWORD CurrentPID;
PrintRoutine("Searching for call info ...\n");
Status = OpenRPCDebugCallInfoEnumeration(CallID, IfStart, ProcNum, ProcessID, &h);
if (Status != RPC_S_OK)
{
PrintRoutine("OpenRPCDebugCallInfoEnumeration failed: %d\n", Status);
return;
}
PrintCallInfoHeader(PrintRoutine);
do
{
Status = GetNextRPCDebugCallInfo(h, &NextCall, &CellID, &CurrentPID);
if (Status == RPC_S_OK)
{
PrintCallInfoBody(CurrentPID, CellID, NextCall, PrintRoutine);
/*
// print the information we obtained
PrintRoutine("%04x %04x.%04x %02x %03x %08lx %04x.%04x %08lx %08lx %08lx ",
CurrentPID, CellID.SectionID, CellID.CellID, NextCall->Status,
NextCall->ProcNum, NextCall->InterfaceUUIDStart,
NextCall->ServicingTID.SectionID, NextCall->ServicingTID.CellID,
NextCall->CallFlags, NextCall->CallID, NextCall->LastUpdateTime);
if (NextCall->CallFlags & DBGCELL_LRPC_CALL)
{
PrintRoutine("%04x.%04x\n", (DWORD)NextCall->PID, (DWORD)NextCall->TID);
}
else
{
PrintRoutine("%04x.%04x\n", (DWORD)NextCall->Connection.SectionID,
(DWORD)NextCall->Connection.CellID);
}
*/
}
}
while (Status == RPC_S_OK);
if (Status != RPC_S_DBG_ENUMERATION_DONE)
{
PrintRoutine("Enumeration aborted with error %d\n", Status);
}
FinishRPCDebugCallInfoEnumeration(&h);
}
void PrintDbgCellInfo(IN DebugCellUnion *Container, IN DebugCellUnion *EndpointContainer OPTIONAL,
PRPCDEBUG_OUTPUT_ROUTINE PrintRoutine)
{
RPC_CHAR *CurrentString;
DWORD LocalFlags;
BOOL fFirstTime;
char EndpointString[DebugEndpointNameLength + 1];
int ConnectionAuthLevel;
int ConnectionAuthService;
HANDLE LocalIPAddress;
HANDLE LocalIPAddress2;
int i;
HANDLE LocalIPAddressElement;
HANDLE LocalSessionID;
switch(Container->genericCell.Type)
{
case dctFree:
PrintRoutine("Free cell\n");
break;
case dctCallInfo:
PrintRoutine("Call\n");
if ((Container->callInfo.Status < CallStatusFirst)
|| (Container->callInfo.Status > CallStatusLast))
{
CurrentString = (RPC_CHAR *)UnknownValue;
}
else
{
CurrentString = (RPC_CHAR *)CallStatusStrings[Container->callInfo.Status];
}
PrintRoutine("Status: %S\n", CurrentString);
PrintRoutine("Procedure Number: %d\n", Container->callInfo.ProcNum);
PrintRoutine("Interface UUID start (first DWORD only): %X\n", Container->callInfo.InterfaceUUIDStart);
PrintRoutine("Call ID: 0x%x (%d)\n", Container->callInfo.CallID, Container->callInfo.CallID);
PrintDebugCellID(L"Servicing thread identifier", Container->callInfo.ServicingTID, PrintRoutine);
PrintRoutine("Call Flags:");
LocalFlags = Container->callInfo.CallFlags;
fFirstTime = TRUE;
if (LocalFlags & DBGCELL_CACHED_CALL)
{
PrintRoutine(" cached");
fFirstTime = FALSE;
}
if (LocalFlags & DBGCELL_ASYNC_CALL)
{
if (fFirstTime == FALSE)
{
PrintRoutine(", ");
}
PrintRoutine("async");
fFirstTime = FALSE;
}
if (LocalFlags & DBGCELL_PIPE_CALL)
{
if (fFirstTime == FALSE)
{
PrintRoutine(", ");
}
PrintRoutine("pipe");
fFirstTime = FALSE;
}
if (LocalFlags & DBGCELL_LRPC_CALL)
{
if (fFirstTime == FALSE)
{
PrintRoutine(", ");
}
PrintRoutine("LRPC");
fFirstTime = FALSE;
}
if (fFirstTime == TRUE)
{
PrintRoutine("none");
}
PrintRoutine("\n");
PrintTimeInSeconds(L"Last update time", Container->callInfo.LastUpdateTime, PrintRoutine);
if (LocalFlags & DBGCELL_LRPC_CALL)
{
PrintRoutine("Caller (PID/TID) is: %x.%x (%d.%d)\n", Container->callInfo.PID,
Container->callInfo.TID, Container->callInfo.PID, Container->callInfo.TID);
}
else
PrintDebugCellID(L"Owning connection identifier", Container->callInfo.Connection, PrintRoutine);
break;
case dctThreadInfo:
PrintRoutine("Thread\n");
PrintRoutine("Status: ");
switch (Container->threadInfo.Status)
{
case dtsProcessing:
PrintRoutine("Processing\n");
break;
case dtsDispatched:
PrintRoutine("Dispatched\n");
break;
case dtsAllocated:
PrintRoutine("Allocated\n");
break;
case dtsIdle:
PrintRoutine("Idle\n");
break;
default:
PrintRoutine("Unknown (%d)\n", Container->threadInfo.Status);
}
PrintRoutine("Thread ID: 0x%X (%d)\n", Container->threadInfo.TID, Container->threadInfo.TID);
if ((Container->threadInfo.Endpoint.CellID == 0) && (Container->threadInfo.Endpoint.SectionID == 0))
{
PrintRoutine("Thread is an IO completion thread\n");
}
else
{
PrintDebugCellID(L"Associated Endpoint:", Container->threadInfo.Endpoint, PrintRoutine);
}
PrintTimeInSeconds(L"Last update time", Container->threadInfo.LastUpdateTime, PrintRoutine);
break;
case dctEndpointInfo:
PrintRoutine("Endpoint\n");
PrintRoutine("Status: ");
switch (Container->endpointInfo.Status)
{
case desAllocated:
PrintRoutine("Allocated\n");
break;
case desActive:
PrintRoutine("Active\n");
break;
case desInactive:
PrintRoutine("Inactive\n");
break;
default:
PrintRoutine("Unknown (%d)\n", Container->endpointInfo.Status);
}
CurrentString = GetProtocolSequenceString(Container->endpointInfo.ProtseqType);
PrintRoutine("Protocol Sequence: %S\n", CurrentString);
memcpy(EndpointString, Container->endpointInfo.EndpointName,
sizeof(Container->endpointInfo.EndpointName));
EndpointString[DebugEndpointNameLength] = 0;
PrintRoutine("Endpoint name: %s\n", EndpointString);
break;
case dctClientCallInfo:
PrintRoutine("Client call info\n");
PrintRoutine("Procedure number: %d\n", Container->clientCallInfo.ProcNum);
PrintRoutine("Interface UUID start (first DWORD only): %X\n", Container->clientCallInfo.IfStart);
PrintRoutine("Call ID: 0x%x (%d)\n", Container->clientCallInfo.CallID,
Container->clientCallInfo.CallID);
PrintDebugCellID(L"Calling thread identifier", Container->clientCallInfo.ServicingThread,
PrintRoutine);
PrintDebugCellID(L"Call target identifier", Container->clientCallInfo.CallTargetID, PrintRoutine);
ASSERT(sizeof(Container->clientCallInfo.Endpoint) < sizeof(EndpointString));
memcpy(EndpointString, Container->clientCallInfo.Endpoint, sizeof(Container->clientCallInfo.Endpoint));
EndpointString[ClientCallEndpointLength] = 0;
PrintRoutine("Call target endpoint: %s\n", EndpointString);
break;
case dctCallTargetInfo:
PrintRoutine("Call target info\n");
CurrentString = GetProtocolSequenceString(Container->callTargetInfo.ProtocolSequence);
PrintRoutine("Protocol Sequence: %S\n", CurrentString);
PrintTimeInSeconds(L"Last update time", Container->callTargetInfo.LastUpdateTime, PrintRoutine);
ASSERT(sizeof(Container->callTargetInfo.TargetServer) < sizeof(EndpointString));
memcpy(EndpointString, Container->callTargetInfo.TargetServer,
sizeof(Container->callTargetInfo.TargetServer));
EndpointString[sizeof(Container->callTargetInfo.TargetServer)] = 0;
PrintRoutine("Target server is: %s\n", EndpointString);
break;
case dctConnectionInfo:
PrintRoutine("Connection\n");
LocalFlags = Container->connectionInfo.Flags;
fFirstTime = TRUE;
PrintRoutine("Connection flags: ");
if (LocalFlags & 1)
{
PrintRoutine("Exclusive\n");
}
else
{
PrintRoutine("None\n");
}
ConnectionAuthLevel =
(Container->connectionInfo.Flags & ConnectionAuthLevelMask) >> ConnectionAuthLevelShift;
ConnectionAuthService =
(Container->connectionInfo.Flags & ConnectionAuthServiceMask) >> ConnectionAuthServiceShift;
PrintRoutine("Authentication Level: %S\n", ConnectionAuthLevelStrings[ConnectionAuthLevel]);
PrintRoutine("Authentication Service: %S\n", ConnectionAuthServiceStrings[ConnectionAuthService]);
PrintRoutine("Last Transmit Fragment Size: %d (0x%X)\n",
Container->connectionInfo.LastTransmitFragmentSize);
PrintDebugCellID(L"Endpoint for the connection", Container->connectionInfo.Endpoint, PrintRoutine);
PrintTimeInSeconds(L"Last send time", Container->connectionInfo.LastSendTime, PrintRoutine);
PrintTimeInSeconds(L"Last receive time", Container->connectionInfo.LastReceiveTime, PrintRoutine);
PrintRoutine("Getting endpoint info ...\n");
switch(EndpointContainer->endpointInfo.ProtseqType)
{
case TCP_TOWER_ID:
case UDP_TOWER_ID:
case HTTP_TOWER_ID:
// IP address of some sort
PrintRoutine("Caller is");
LocalIPAddress = Container->connectionInfo.ConnectionID[1];
LocalIPAddress2 = Container->connectionInfo.ConnectionID[0];
if (LocalIPAddress2 == 0)
{
PrintRoutine("(IPv4): ");
for (i = 0; i < 4; i ++)
{
LocalIPAddressElement = (HANDLE)((ULONGLONG)LocalIPAddress & 0xFF);
LocalIPAddress = (HANDLE)((ULONGLONG)LocalIPAddress >> 8);
PrintRoutine("%d", HandleToUlong(LocalIPAddressElement));
if (i < 3)
{
PrintRoutine(".");
}
else
{
PrintRoutine("\n");
}
}
}
else
{
PrintRoutine("(IPv6 - last two DWORDS): ");
PrintRoutine("%d::%d\n", HandleToUlong(LocalIPAddress2), HandleToULong(LocalIPAddress));
}
break;
case NMP_TOWER_ID:
LocalSessionID = Container->connectionInfo.ConnectionID[0];
if (LocalSessionID)
{
PrintRoutine("Cannot determine caller for remote named pipes\n");
}
else
{
LocalSessionID = Container->connectionInfo.ConnectionID[1];
PrintRoutine("Process object for caller is 0x%X\n", LocalSessionID);
}
break;
default:
CurrentString = GetProtocolSequenceString(EndpointContainer->endpointInfo.ProtseqType);
PrintRoutine("Cannot determine caller for this type of protocol sequence %S (%d)\n",
CurrentString, EndpointContainer->endpointInfo.ProtseqType);
}
break;
case dctUsedGeneric:
break;
default:
PrintRoutine("Invalid cell type: %d\n", Container->genericCell.Type);
}
}
void GetAndPrintDbgCellInfo(DWORD ProcessID, DebugCellID CellID,
PRPCDEBUG_OUTPUT_ROUTINE PrintRoutine)
{
DebugCellUnion Container;
DebugCellUnion EndpointContainer;
RPC_STATUS Status;
PrintRoutine("Getting cell info ...\n");
Status = GetCellByDebugCellID(ProcessID, CellID, &Container);
if (Status != RPC_S_OK)
{
PrintRoutine("Getting cell info failed with error %d\n", Status);
return;
}
if (Container.genericCell.Type == dctConnectionInfo)
{
Status = GetCellByDebugCellID(ProcessID, Container.connectionInfo.Endpoint, &EndpointContainer);
if (Status != RPC_S_OK)
{
PrintRoutine("Getting endpoint info failed with error %d\n", Status);
return;
}
}
PrintDbgCellInfo(&Container, &EndpointContainer, PrintRoutine);
}
void PrintEndpointInfoHeader(PRPCDEBUG_OUTPUT_ROUTINE PrintRoutine)
{
PrintRoutine("PID CELL ID ST PROTSEQ ENDPOINT \n");
PrintRoutine("-------------------------------------------------------------\n");
}
void PrintEndpointInfoBody(IN DWORD ProcessID, IN DebugCellID CellID,
IN DebugEndpointInfo *EndpointInfo, PRPCDEBUG_OUTPUT_ROUTINE PrintRoutine)
{
RPC_CHAR *ProtseqName;
char CurrentEndpoint[DebugEndpointNameLength + 1];
ProtseqName = GetProtocolSequenceString(EndpointInfo->ProtseqType);
CurrentEndpoint[DebugEndpointNameLength] = 0;
memcpy(CurrentEndpoint, EndpointInfo->EndpointName, DebugEndpointNameLength);
// print the information we obtained
PrintRoutine("%04x %04x.%04x %02x %14S %s\n",
ProcessID, CellID.SectionID, CellID.CellID, EndpointInfo->Status,
ProtseqName, CurrentEndpoint);
}
void GetAndPrintEndpointInfo(IN char *Endpoint OPTIONAL, PRPCDEBUG_OUTPUT_ROUTINE PrintRoutine)
{
DWORD CurrentPID;
RPC_STATUS Status;
DebugEndpointInfo *NextEndpoint;
EndpointInfoEnumerationHandle h;
DebugCellID CellID;
PrintRoutine("Searching for endpoint info ...\n");
Status = OpenRPCDebugEndpointInfoEnumeration(Endpoint, &h);
if (Status != RPC_S_OK)
{
PrintRoutine("OpenRPCDebugEndpointInfoEnumeration failed: %d\n", Status);
return;
}
PrintEndpointInfoHeader(PrintRoutine);
do
{
Status = GetNextRPCDebugEndpointInfo(h, &NextEndpoint, &CellID, &CurrentPID);
if (Status == RPC_S_OK)
{
PrintEndpointInfoBody(CurrentPID, CellID, NextEndpoint, PrintRoutine);
}
}
while (Status == RPC_S_OK);
if (Status != RPC_S_DBG_ENUMERATION_DONE)
{
PrintRoutine("Enumeration aborted with error %d\n", Status);
}
FinishRPCDebugEndpointInfoEnumeration(&h);
}
void PrintThreadInfoHeader(PRPCDEBUG_OUTPUT_ROUTINE PrintRoutine)
{
PrintRoutine("PID CELL ID ST TID ENDPOINT LASTTIME\n");
PrintRoutine("---------------------------------------------\n");
}
void PrintThreadInfoBody(IN DWORD ProcessID, IN DebugCellID CellID,
IN DebugThreadInfo *ThreadInfo, PRPCDEBUG_OUTPUT_ROUTINE PrintRoutine)
{
// print the information we obtained
PrintRoutine("%04x %04x.%04x %02x %08x %04x.%04x %08x\n",
ProcessID, CellID.SectionID, CellID.CellID, ThreadInfo->Status,
ThreadInfo->TID, ThreadInfo->Endpoint.SectionID,
ThreadInfo->Endpoint.CellID, ThreadInfo->LastUpdateTime);
}
void GetAndPrintThreadInfo(DWORD ProcessID, DWORD ThreadID OPTIONAL, PRPCDEBUG_OUTPUT_ROUTINE PrintRoutine)
{
DebugThreadInfo *NextThread;
RPC_STATUS Status;
ThreadInfoEnumerationHandle h;
DebugCellID CellID;
DWORD CurrentPID;
PrintRoutine("Searching for thread info ...\n");
Status = OpenRPCDebugThreadInfoEnumeration(ProcessID, ThreadID, &h);
if (Status != RPC_S_OK)
{
PrintRoutine("OpenRPCDebugThreadInfoEnumeration failed: %d\n", Status);
return;
}
PrintThreadInfoHeader(PrintRoutine);
do
{
Status = GetNextRPCDebugThreadInfo(h, &NextThread, &CellID, &CurrentPID);
if (Status == RPC_S_OK)
{
PrintThreadInfoBody(CurrentPID, CellID, NextThread, PrintRoutine);
}
}
while (Status == RPC_S_OK);
if (Status != RPC_S_DBG_ENUMERATION_DONE)
{
PrintRoutine("Enumeration aborted with error %d\n", Status);
}
FinishRPCDebugThreadInfoEnumeration(&h);
}
void PrintClientCallInfoHeader(PRPCDEBUG_OUTPUT_ROUTINE PrintRoutine)
{
PrintRoutine("PID CELL ID PNO IFSTART TIDNUMBER CALLID LASTTIME PS CLTNUMBER ENDPOINT\n");
PrintRoutine("------------------------------------------------------------------------------\n");
}
void PrintClientCallInfoBody(IN DWORD ProcessID, IN DebugCellID CellID,
IN DebugClientCallInfo *ClientCallInfo,
IN DebugCallTargetInfo *CallTargetInfo,
PRPCDEBUG_OUTPUT_ROUTINE PrintRoutine)
{
char TempString[DebugEndpointNameLength + 1];
ASSERT(sizeof(TempString) > sizeof(CallTargetInfo->TargetServer));
ASSERT(sizeof(TempString) > sizeof(ClientCallInfo->Endpoint));
memcpy(TempString, ClientCallInfo->Endpoint, sizeof(ClientCallInfo->Endpoint));
TempString[sizeof(ClientCallInfo->Endpoint)] = 0;
// print the information we obtained
PrintRoutine("%04x %04x.%04x %04x %08lx %04x.%04x %08lx %08lx %02x %04x.%04x %s\n",
ProcessID, CellID.SectionID, CellID.CellID, ClientCallInfo->ProcNum,
ClientCallInfo->IfStart, ClientCallInfo->ServicingThread.SectionID,
ClientCallInfo->ServicingThread.CellID, ClientCallInfo->CallID,
CallTargetInfo->LastUpdateTime, CallTargetInfo->ProtocolSequence,
ClientCallInfo->CallTargetID.SectionID, ClientCallInfo->CallTargetID.CellID,
TempString);
}
void GetAndPrintClientCallInfo(IN DWORD CallID OPTIONAL, IN DWORD IfStart OPTIONAL,
IN int ProcNum OPTIONAL, IN DWORD ProcessID OPTIONAL,
PRPCDEBUG_OUTPUT_ROUTINE PrintRoutine)
{
DWORD CurrentPID;
DebugCellID CellID;
DebugClientCallInfo *NextClientCall;
DebugCallTargetInfo *NextCallTarget;
RPC_STATUS Status;
CallInfoEnumerationHandle h;
PrintRoutine("Searching for call info ...\n");
Status = OpenRPCDebugClientCallInfoEnumeration(CallID, IfStart, ProcNum, ProcessID, &h);
if (Status != RPC_S_OK)
{
PrintRoutine("OpenRPCDebugClientCallInfoEnumeration failed: %d\n", Status);
return;
}
PrintClientCallInfoHeader(PrintRoutine);
do
{
Status = GetNextRPCDebugClientCallInfo(h, &NextClientCall, &NextCallTarget, &CellID, &CurrentPID);
if (Status == RPC_S_OK)
{
if ((NextCallTarget != NULL) && (NextCallTarget->Type != dctCallTargetInfo))
{
PrintRoutine("Inconsistent information detected - skipping ...\n");
continue;
}
PrintClientCallInfoBody(CurrentPID, CellID, NextClientCall, NextCallTarget,
PrintRoutine);
}
}
while (Status == RPC_S_OK);
if (Status != RPC_S_DBG_ENUMERATION_DONE)
{
PrintRoutine("Enumeration aborted with error %d\n", Status);
}
FinishRPCDebugClientCallInfoEnumeration(&h);
}