00001
00007 #include "stdafx.h"
00008 #include "kdcomdisp.h"
00009 #include "reporter.h"
00010
00011
00012
00013
00014
00015
00016
00017
00018 KD_RECV_CODE KdComDispatcher::KdCompReceivePacketLeader(ULONG ,
00019 ULONG *pSignature,
00020 PKD_CONTEXT pContext)
00021 {
00022 ASSERT(pContext && pSignature);
00023 unsigned repeatCount = 0;
00024 char ch = 0, lastCh = 0;
00025 bool BreakInRequested = false;
00026 for (;;)
00027 {
00028 if (m_Pipe.Receive(&ch, 1) != 1)
00029 {
00030 if (BreakInRequested)
00031 {
00032 pContext->BreakInRequested = TRUE;
00033 return KD_RECV_CODE_FAILED;
00034 }
00035 else
00036 {
00037 unsigned err = m_Pipe.GetLastOperationError();
00038 switch (err)
00039 {
00040 case ERROR_PIPE_LISTENING:
00041 case ERROR_PIPE_NOT_CONNECTED:
00042 case ERROR_NO_DATA:
00043 Sleep(10);
00044 break;
00045 }
00046 return KD_RECV_CODE_TIMEOUT;
00047 }
00048 }
00049 switch (ch)
00050 {
00051 case '0':
00052 case 'i':
00053 break;
00054 case 'b':
00055 BreakInRequested = true;
00056
00057 default:
00058 g_pReporter->GetStatusPointer()->BytesDropped++;
00059 repeatCount = 0;
00060 continue;
00061 }
00062
00063 if (!repeatCount)
00064 {
00065 lastCh = ch;
00066 repeatCount++;
00067 continue;
00068 }
00069
00070 if (ch != lastCh)
00071 repeatCount = 1, lastCh = ch;
00072 else
00073 {
00074 if (++repeatCount >= 4)
00075 {
00076 if (BreakInRequested)
00077 pContext->BreakInRequested = TRUE;
00078 *pSignature = (ch == '0') ? '0000' : 'iiii';
00079 KD_DEBUGGER_NOT_PRESENT = false;
00080 return KD_RECV_CODE_OK;
00081 }
00082 }
00083 }
00084 }
00085
00086 void KdComDispatcher::KdpSendControlPacket(ULONG PacketType, ULONG NextPacketId)
00087 {
00088 static char __unused[sizeof(KD_PACKET_HEADER) == 16];
00089 static unsigned LastSentID = 0;
00090 KD_PACKET_HEADER hdr;
00091 hdr.Signature = 'iiii';
00092 hdr.PacketType = (USHORT)PacketType;
00093 hdr.TotalDataLength = 0;
00094 if (NextPacketId)
00095 hdr.PacketID = LastSentID = NextPacketId;
00096 else
00097 hdr.PacketID = LastSentID;
00098 hdr.Checksum = 0;
00099 if (KdTraceEnabled && (g_pReporter->GetStatusPointer()->DebugLevel >= DebugLevelTraceKdCom))
00100 {
00101 TCHAR tsz[512];
00102 _sntprintf(tsz, __countof(tsz), _T("Sending control packet (type = %d, id = %08X)... "),
00103 hdr.PacketType,
00104 hdr.PacketID);
00105 g_pReporter->LogLineIfEnabled(tsz);
00106 }
00107 m_Pipe.Send(&hdr, sizeof(hdr));
00108 if (KdTraceEnabled && (g_pReporter->GetStatusPointer()->DebugLevel >= DebugLevelTraceKdCom))
00109 {
00110 g_pReporter->LogLineIfEnabled(_T("ok\r\n"));
00111 }
00112 }
00113
00114 KD_RECV_CODE __stdcall KdComDispatcher::KdReceivePacket(ULONG PacketType,
00115 PKD_BUFFER FirstBuffer,
00116 PKD_BUFFER SecondBuffer,
00117 PULONG PayloadBytes,
00118 PKD_CONTEXT KdContext)
00119 {
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131 if (0)
00132 SimulateWindowsTermination();
00133
00134 ASSERT(KdContext);
00135 #ifdef KDCLIENT_REPORT_PERFORMANCE_INFORMATION
00136 g_pReporter->GetStatusPointer()->OSDetected = true;
00137 #endif
00138
00139 if (KdTraceEnabled)
00140 {
00141 TCHAR tsz[512];
00142 switch (PacketType)
00143 {
00144 case KdPacketAcknowledge:
00145 if (g_pReporter->GetStatusPointer()->DebugLevel >= DebugLevelTraceKdCom)
00146 {
00147 _sntprintf(tsz, __countof(tsz), _T("KdReceivePacket(%d)\r\n"),
00148 PacketType);
00149 g_pReporter->LogLineIfEnabled(tsz);
00150 }
00151 break;
00152 case KdCheckForAnyPacket:
00153 if (g_pReporter->GetStatusPointer()->DebugLevel >= DebugLevelTraceKdComAll)
00154 g_pReporter->LogLineIfEnabled(_T("KdReceivePacket(): checking for new packets\r\n"));
00155 break;
00156 }
00157 }
00158
00159
00160 if (PacketType == KdCheckForAnyPacket)
00161 {
00162 bool hasData = m_Pipe.HasDataInBuffer();
00163 #ifdef KDCLIENT_REPORT_PERFORMANCE_INFORMATION
00164 g_pReporter->GetStatusPointer()->DebuggerConnected = m_Pipe.IsClientConnected();
00165 #endif
00166 if (!hasData)
00167 return KD_RECV_CODE_TIMEOUT;
00168 char ch = 0;
00169 if (!m_Pipe.Receive(&ch, 1))
00170 return KD_RECV_CODE_TIMEOUT;
00171 if (ch != 'b')
00172 return KD_RECV_CODE_TIMEOUT;
00173 return KD_RECV_CODE_OK;
00174 }
00175
00176 if (FirstBuffer)
00177 FirstBuffer->Length = 0;
00178 if (SecondBuffer)
00179 SecondBuffer->Length = 0;
00180
00181 for (;;)
00182 {
00183 KD_PACKET_HEADER header = {0,};
00184 KD_RECV_CODE status = KdCompReceivePacketLeader(PacketType, &header.Signature, KdContext);
00185 #ifdef KDCLIENT_REPORT_PERFORMANCE_INFORMATION
00186 g_pReporter->GetStatusPointer()->DebuggerConnected = m_Pipe.IsClientConnected();
00187 #endif
00188 if (status != KD_RECV_CODE_TIMEOUT)
00189 KdCompNumberRetries = KdCompRetryCount;
00190 if (status != KD_RECV_CODE_OK)
00191 {
00192 if (KdTraceEnabled && (g_pReporter->GetStatusPointer()->DebugLevel >= DebugLevelTraceKdComAll))
00193 {
00194 TCHAR tsz[512];
00195 _sntprintf(tsz, __countof(tsz), _T("KdCompReceivePacketLeader(): %s\r\n"), (status == KD_RECV_CODE_TIMEOUT) ? _T("timeout") : _T("error"));
00196 g_pReporter->LogLineIfEnabled(tsz);
00197 }
00198 return status;
00199 }
00200 size_t size = sizeof(header) - sizeof(header.Signature);
00201
00202 if (m_Pipe.Receive(&header.PacketType, size, false) != size)
00203 return KD_RECV_CODE_TIMEOUT;
00204
00205 if (KdTraceEnabled && (g_pReporter->GetStatusPointer()->DebugLevel >= DebugLevelTraceKdCom))
00206 {
00207 TCHAR tsz[512];
00208 _sntprintf(tsz, __countof(tsz), _T("Got packet (expected %d) (signature = %08X, id = %08X, type = %d, data = %d, checksum = %d)\r\n"),
00209 PacketType,
00210 header.Signature,
00211 header.PacketID,
00212 header.PacketType,
00213 header.TotalDataLength,
00214 header.Checksum);
00215 g_pReporter->LogLineIfEnabled(tsz);
00216 }
00217
00218
00219 if ((header.Signature == 'iiii') && (header.PacketType == KdPacketRetryRequest))
00220 return KD_RECV_CODE_FAILED;
00221
00222 if (header.Signature == 'iiii')
00223 {
00224 switch (header.PacketType)
00225 {
00226 case KdPacketAcknowledge:
00227 if ((PacketType == 4) && (header.PacketID == (KdCompNextPacketIdToSend & 0xFFFFF7FF)))
00228 {
00229 KdCompNextPacketIdToSend ^= 1;
00230 return KD_RECV_CODE_OK;
00231 }
00232 break;
00233 case KdPacketResynchronize:
00234 if (KdTraceEnabled && (g_pReporter->GetStatusPointer()->DebugLevel >= DebugLevelTraceKdCom))
00235 {
00236 TCHAR tsz[512];
00237 _sntprintf(tsz, __countof(tsz), _T("Resync (got %d, sent %d, seq cnt %d)\r\n"),
00238 m_PacketsGotFromLastReset,
00239 m_PacketsSentFromLastReset,
00240 m_SequentialResetCount);
00241 g_pReporter->LogLineIfEnabled(tsz);
00242 }
00243 if ((m_PacketsGotFromLastReset <= KdMinPacketCountForSuccessfulLink) && (m_PacketsSentFromLastReset <= KdMinPacketCountForSuccessfulLink))
00244 m_SequentialResetCount++;
00245 KdResetPacketNumbering(false);
00246
00247
00248
00249
00250
00251
00252
00253
00254
00255
00256
00257
00258
00259
00260
00261
00262
00263
00264
00265 m_Pipe.DiscardBufferedData();
00266 KdpSendControlPacket(KdPacketResynchronize, 0);
00267 #ifdef KDCLIENT_REPORT_PERFORMANCE_INFORMATION
00268 g_pReporter->GetStatusPointer()->ResyncCount++;
00269 #endif
00270 return KD_RECV_CODE_FAILED;
00271 case KdPacketRetryRequest:
00272 return KD_RECV_CODE_FAILED;
00273 }
00274 }
00275 else
00276 {
00277 if (PacketType != KdPacketAcknowledge)
00278 {
00279 if (header.TotalDataLength <= KdMaxBufferSize)
00280 {
00281 if (header.TotalDataLength >= FirstBuffer->MaxLength)
00282 {
00283 *PayloadBytes = header.TotalDataLength - FirstBuffer->MaxLength;
00284 if (m_Pipe.Receive(FirstBuffer->pData, FirstBuffer->MaxLength, false) == FirstBuffer->MaxLength)
00285 {
00286 FirstBuffer->Length = FirstBuffer->MaxLength;
00287 if (!(*PayloadBytes) || (m_Pipe.Receive(SecondBuffer->pData, *PayloadBytes, false) == *PayloadBytes))
00288 {
00289 SecondBuffer->Length = (USHORT)*PayloadBytes;
00290 unsigned char ch = 0;
00291 if (m_Pipe.Receive(&ch, 1) && (ch == 0xAA))
00292 {
00293 if (PacketType != header.PacketType)
00294 {
00295 KdpSendControlPacket(KdPacketAcknowledge, header.PacketID);
00296 continue;
00297 }
00298 else if ((header.PacketID == 0x80800000) || (header.PacketID == 0x80800001))
00299 {
00300 if (header.PacketID != KdCompPacketIdExpected)
00301 {
00302 KdpSendControlPacket(KdPacketAcknowledge, header.PacketID);
00303 continue;
00304 }
00305 }
00306 int checksum = KdpComputeChecksum(FirstBuffer->pData, FirstBuffer->Length);
00307 checksum += KdpComputeChecksum(SecondBuffer->pData, SecondBuffer->Length);
00308 if (checksum == header.Checksum)
00309 {
00310 KdpSendControlPacket(KdPacketAcknowledge, header.PacketID);
00311 KdCompPacketIdExpected ^= 1;
00312 if (++m_PacketsGotFromLastReset >= KdMinPacketCountForSuccessfulLink)
00313 m_SequentialResetCount = 0;
00314 #ifdef KDCLIENT_REPORT_PERFORMANCE_INFORMATION
00315 g_pReporter->GetStatusPointer()->BytesReceived += header.TotalDataLength + sizeof(KD_PACKET_HEADER) + 1;
00316 g_pReporter->GetStatusPointer()->PacketsReceived++;
00317 #endif
00318 m_PacketLogger.OnSendReceivePacket(g_pReporter->GetStatusPointer()->LogAllPackets != 0,
00319 false,
00320 PacketType,
00321 FirstBuffer,
00322 SecondBuffer,
00323 KdContext);
00324
00325 return KD_RECV_CODE_OK;
00326 }
00327 }
00328 }
00329
00330 }
00331 }
00332 }
00333 KdpSendControlPacket(KdPacketRetryRequest, 0);
00334 continue;
00335 }
00336 if (header.PacketID == KdCompPacketIdExpected)
00337 {
00338 KdpSendControlPacket(KdPacketRetryRequest, 0);
00339 KdCompNextPacketIdToSend ^= 1;
00340 return KD_RECV_CODE_OK;
00341 }
00342 else
00343 {
00344 KdpSendControlPacket(KdPacketAcknowledge, header.PacketID);
00345 continue;
00346 }
00347 }
00348 }
00349
00350 return KD_RECV_CODE_TIMEOUT;
00351 }
00352
00353 static bool FixVerifierUnicodeReport(PKD_BUFFER pDataBuffer)
00354 {
00355 char *pText = (char *)pDataBuffer->pData;
00356 char *pZero = NULL;
00357 char *pEnd = pText + pDataBuffer->Length;
00358 for (int i = 0; i < pDataBuffer->Length; i++)
00359 if (!pText[i])
00360 {
00361 pZero = &pText[i];
00362 break;
00363 }
00364 if (!pZero)
00365 return false;
00366 if (!strstr(pText, "Driver Verifier:"))
00367 return false;
00368
00369 char *pSrc = pZero + 1;
00370 while (pSrc < pEnd)
00371 {
00372 if (!pSrc[0])
00373 {
00374 pSrc++;
00375 continue;
00376 }
00377 *pZero = *pSrc;
00378
00379 pZero++;
00380 pSrc++;
00381 }
00382
00383
00384 if (pZero < pEnd)
00385 *pZero = 0;
00386 return true;
00387 }
00388
00389 bool __stdcall KdComDispatcher::KdSendPacket(ULONG PacketType,
00390 PKD_BUFFER FirstBuffer,
00391 PKD_BUFFER SecondBuffer,
00392 PKD_CONTEXT KdContext)
00393 {
00394 m_PacketLogger.OnSendReceivePacket(g_pReporter->GetStatusPointer()->LogAllPackets != 0,
00395 true,
00396 PacketType,
00397 FirstBuffer,
00398 SecondBuffer,
00399 KdContext);
00400
00401 #ifdef KDCLIENT_REPORT_PERFORMANCE_INFORMATION
00402 g_pReporter->GetStatusPointer()->OSDetected = true;
00403 #endif
00404 #ifdef KDCLIENT_ENABLE_TRACE_ASSIST
00405 switch (PacketType)
00406 {
00407 case KdPacketType3:
00408 if (FirstBuffer && SecondBuffer && (*((ULONG *)FirstBuffer->pData) == 0x3230))
00409 {
00410 FixVerifierUnicodeReport(SecondBuffer);
00411 if (g_pReporter->GetStatusPointer()->TraceAssistUpdatePending)
00412 {
00413 m_TraceAssistant.ReloadParams();
00414 g_pReporter->GetStatusPointer()->TraceAssistUpdatePending = false;
00415 }
00416 if (m_TraceAssistant.TraceLine((char *)SecondBuffer->pData, SecondBuffer->Length))
00417 {
00418 #ifdef KDCLIENT_REPORT_PERFORMANCE_INFORMATION
00419 g_pReporter->GetStatusPointer()->BytesSent += (FirstBuffer->Length + SecondBuffer->Length + sizeof(KD_PACKET_HEADER) + 1);
00420 g_pReporter->GetStatusPointer()->PacketsSent++;
00421 #endif
00422 return true;
00423 }
00424 }
00425 break;
00426 }
00427 #endif
00428
00429 ASSERT(FirstBuffer);
00430 KD_PACKET_HEADER header;
00431 unsigned checksum = KdpComputeChecksum(FirstBuffer->pData, FirstBuffer->Length);
00432 unsigned totalLength = FirstBuffer->Length;
00433 if (SecondBuffer)
00434 {
00435 totalLength += SecondBuffer->Length;
00436 checksum += KdpComputeChecksum(SecondBuffer->pData, SecondBuffer->Length);
00437 }
00438
00439 header.Signature = '0000';
00440 header.PacketType = (USHORT)PacketType;
00441 header.Checksum = checksum;
00442 header.TotalDataLength = totalLength;
00443 KdCompNumberRetries = KdCompRetryCount;
00444
00445 if (KdTraceEnabled && (g_pReporter->GetStatusPointer()->DebugLevel >= DebugLevelTraceKdCom))
00446 {
00447 TCHAR tsz[512];
00448 _sntprintf(tsz, __countof(tsz), _T("Sending normal packet (type = %d, id = %08X, len = %d)...\r\n"),
00449 header.PacketType,
00450 KdCompNextPacketIdToSend,
00451 header.TotalDataLength);
00452 g_pReporter->LogLineIfEnabled(tsz);
00453 }
00454
00455 unsigned RetryNumber = 0;
00456
00457 for (;;)
00458 {
00459
00460
00461
00462 if (RetryNumber)
00463 {
00464
00465
00466
00467
00468
00469
00470
00471
00472
00473
00474
00475
00476
00477
00478 {
00479
00480
00481 KD_DEBUGGER_NOT_PRESENT = TRUE;
00482 KdResetPacketNumbering(true);
00483 if (KdTraceEnabled && (g_pReporter->GetStatusPointer()->DebugLevel >= DebugLevelTraceKdCom))
00484 g_pReporter->LogLineIfEnabled(_T("Acknowledgment timeout\r\n"));
00485 return false;
00486 }
00487 }
00488
00489 if (KdTraceEnabled && (g_pReporter->GetStatusPointer()->DebugLevel >= DebugLevelTraceKdComAll))
00490 {
00491 TCHAR tsz[512];
00492 _sntprintf(tsz, __countof(tsz), _T("KdSendPacket(): sending (%d)\r\n"),
00493 header.PacketType);
00494 g_pReporter->LogLineIfEnabled(tsz);
00495 }
00496
00497 header.PacketID = KdCompNextPacketIdToSend;
00498 m_Pipe.Send(&header, sizeof(header));
00499 m_Pipe.Send(FirstBuffer->pData, FirstBuffer->Length);
00500 if (SecondBuffer && SecondBuffer->Length)
00501 m_Pipe.Send(SecondBuffer->pData, SecondBuffer->Length);
00502 unsigned char ch = 0xAA;
00503 m_Pipe.Send(&ch, 1);
00504 #ifdef KDCLIENT_REPORT_PERFORMANCE_INFORMATION
00505 g_pReporter->GetStatusPointer()->DebuggerConnected = m_Pipe.IsClientConnected();
00506 #endif
00507 switch (KdReceivePacket(KdPacketAcknowledge, NULL, NULL, NULL, KdContext))
00508 {
00509 case KD_RECV_CODE_TIMEOUT:
00510 KdCompNumberRetries--;
00511 RetryNumber++;
00512 break;
00513 case KD_RECV_CODE_OK:
00514 KdCompNextPacketIdToSend &= 0xFFFFFFF7;
00515 KdCompRetryCount = KdContext->RetryCount;
00516 if (++m_PacketsSentFromLastReset >= KdMinPacketCountForSuccessfulLink)
00517 m_SequentialResetCount = 0;
00518 if (KdTraceEnabled && (g_pReporter->GetStatusPointer()->DebugLevel >= DebugLevelTraceKdCom))
00519 g_pReporter->LogLineIfEnabled(_T("Packet acknowledged\r\n"));
00520 #ifdef KDCLIENT_REPORT_PERFORMANCE_INFORMATION
00521 g_pReporter->GetStatusPointer()->BytesSent += (header.TotalDataLength + sizeof(KD_PACKET_HEADER) + 1);
00522 g_pReporter->GetStatusPointer()->PacketsSent++;
00523 #endif
00524 return true;
00525 }
00526 }
00527 }
00528
00529 void KdComDispatcher::ReportProtocolVersionError(int GuestVersion, int HostVersion)
00530 {
00531 g_pReporter->GetStatusPointer()->ProtocolMismatchStatus = MAKELONG(GuestVersion, HostVersion);
00532 }
00533
00534
00535
00536 typedef unsigned SystemTerminationPacket[60];
00537
00538 typedef struct _DBGKD_LOAD_SYMBOLS64
00539 {
00540 ULONG PathNameLength;
00541 ULONG64 BaseOfDll;
00542 ULONG64 ProcessId;
00543 ULONG CheckSum;
00544 ULONG SizeOfImage;
00545 BOOLEAN UnloadSymbols;
00546 } DBGKD_LOAD_SYMBOLS64, *PDBGKD_LOAD_SYMBOLS64;
00547
00548 static void InitializeSystemTerminationPacket(SystemTerminationPacket Packet)
00549 {
00550 memset(Packet, -1, sizeof(SystemTerminationPacket));
00551 Packet[0] = 0x00003031;
00552 Packet[1] = 6;
00553 Packet[2] = 1;
00554
00555
00556
00557 const int kDbgkdLoadSymbols64Offset = 8;
00558
00559 PDBGKD_LOAD_SYMBOLS64 pSym = (PDBGKD_LOAD_SYMBOLS64)&Packet[kDbgkdLoadSymbols64Offset];
00560
00561 pSym->PathNameLength = 0;
00562 pSym->BaseOfDll = -1;
00563 pSym->ProcessId = 0;
00564 pSym->CheckSum = 0;
00565 pSym->SizeOfImage = 0;
00566 pSym->UnloadSymbols = TRUE;
00567 }
00568
00569 #define DbgKdContinueApi 0x00003136
00570 #define DbgKdContinueApi2 0x0000313C
00571
00572
00573
00574
00575
00576
00577 void KdComDispatcher::SimulateWindowsTermination()
00578 {
00579 SystemTerminationPacket packet;
00580 InitializeSystemTerminationPacket(packet);
00581
00582 m_PacketLogger.OnWindowsTerminationSimulated();
00583
00584 KD_BUFFER first = {0,}, second = {0,};
00585 first.Length = sizeof(packet);
00586 first.pData = (PUCHAR)packet;
00587 KD_CONTEXT ctx = {0,};
00588
00589 bool RequireAdditionalTerminationPacket = false;
00590
00591 while (!KdSendPacket(7, &first, &second, &ctx))
00592 {
00593 if (!m_Pipe.IsClientConnected())
00594 {
00595 m_PacketLogger.OnWindowsTerminationSimDone("debugger disconnected");
00596 return;
00597 }
00598 RequireAdditionalTerminationPacket = true;
00599 }
00600
00601
00602 static char szBuf1[0x38], szBuf2[KdMaxBufferSize];
00603
00604 ULONG unused = 0;
00605
00606 for (;;)
00607 {
00608 if (!m_Pipe.IsClientConnected())
00609 {
00610 m_PacketLogger.OnWindowsTerminationSimDone("debugger disconnected");
00611 return;
00612 }
00613
00614 first.MaxLength = sizeof(szBuf1);
00615 first.pData = (PUCHAR)szBuf1;
00616 second.MaxLength = sizeof(szBuf2);
00617 second.pData = (PUCHAR)szBuf2;
00618
00619 KD_RECV_CODE code = KdReceivePacket(2, &first, &second, &unused, &ctx);
00620 if (!m_Pipe.IsClientConnected())
00621 {
00622 m_PacketLogger.OnWindowsTerminationSimDone("debugger disconnected");
00623 return;
00624 }
00625
00626 if (code == KD_RECV_CODE_TIMEOUT)
00627 {
00628 m_PacketLogger.OnWindowsTerminationSimDone("timeout");
00629 return;
00630 }
00631 if (code != KD_RECV_CODE_OK)
00632 continue;
00633
00634 if ((*((unsigned *)szBuf1) == DbgKdContinueApi2) || (*((unsigned *)szBuf1) == DbgKdContinueApi))
00635 {
00636 if (RequireAdditionalTerminationPacket)
00637 {
00638 first.Length = sizeof(packet);
00639 first.pData = (PUCHAR)packet;
00640 if (!KdSendPacket(7, &first, &second, &ctx))
00641 {
00642 m_PacketLogger.OnWindowsTerminationSimDone("type 7 packet not acknowledged");
00643 return;
00644 }
00645 RequireAdditionalTerminationPacket = false;
00646 continue;
00647 }
00648 m_PacketLogger.OnWindowsTerminationSimDone("continue command received");
00649 return;
00650 }
00651
00652
00653
00654
00655
00656 KdSendPacket(2, &first, &second, &ctx);
00657 RequireAdditionalTerminationPacket = true;
00658 }
00659 m_PacketLogger.OnWindowsTerminationSimDone("unexpected");
00660 }