| 
| Autor | Beitrag |  
| Narses 
          
  Beiträge: 10183
 Erhaltene Danke: 1256
 
 W10ent
 TP3 .. D7pro .. D10.2CE
 
 | 
Verfasst: So 18.12.05 02:30 
 
Moin!
 Ich habe endlich mal diese leidige Ping-API-Wrapper-Unit auf einen aktuellen Stand gebracht, der AFAIK konform zur aktuellen Doku im MSDN ist. Konkret heißt das:
 Es wird versucht, die IPHLPAPI.DLL zu verwenden; erst wenn das nicht klappt, wird als Fallback auf die ICMP.DLL zurückgegriffen. Damit sollte optimale Kompatibilität gewährleistet sein (getestet auf W98SE, W2Ksp4, WXPsp1+2, W7). Da die ICMP.DLL nie zum "offiziellen" Kanon des Systems gezählt hat, die IP-Helper-API das jetzt aber ist, sollte mit diesem Verhalten immer ein Ergebnis erzielt werden können.
Es kann nicht nur ein ICMP-Status-Reply empfangen werden, sondern auch mehrere, so dass ältere Anfragen nicht mehr zu Problemen führen können.
Die Returncodes der API-Funktionen werden sauber ausgewertet, so dass eine feine Unterscheidung zwischen System- und funktionalen Fehlern möglich ist (es gibt auch eine eigene Fehlertext-Auflösung).
Es sind zwei WSA-GetHostByName-Wrapper-Funktionen enthalten, die sowohl eine, als auch alle IP-Adressen eines Hosts ermitteln können (auch die des lokalen PCs!).
Synchrone und asynchrone Ping-Ausführung (per Thread mit Callback) möglich; mit dieser Unit ist ein threadbasierter Ping ganz leicht durchzuführen, so dass die Anwendung nicht während des Ping-Vorgangs "stehen" bleibt.
 Hier zunächst die Unit:
 												| 1:2:
 3:
 4:
 5:
 6:
 7:
 8:
 9:
 10:
 11:
 12:
 13:
 14:
 15:
 16:
 17:
 18:
 19:
 20:
 21:
 22:
 23:
 24:
 25:
 26:
 27:
 28:
 29:
 30:
 31:
 32:
 33:
 34:
 35:
 36:
 37:
 38:
 39:
 40:
 41:
 42:
 43:
 44:
 45:
 46:
 47:
 48:
 49:
 50:
 51:
 52:
 53:
 54:
 55:
 56:
 57:
 58:
 59:
 60:
 61:
 62:
 63:
 64:
 65:
 66:
 67:
 68:
 69:
 70:
 71:
 72:
 73:
 74:
 75:
 76:
 77:
 78:
 79:
 80:
 81:
 82:
 83:
 84:
 85:
 86:
 87:
 88:
 89:
 90:
 91:
 92:
 93:
 94:
 95:
 96:
 97:
 98:
 99:
 100:
 101:
 102:
 103:
 104:
 105:
 106:
 107:
 108:
 109:
 110:
 111:
 112:
 113:
 114:
 115:
 116:
 117:
 118:
 119:
 120:
 121:
 122:
 123:
 124:
 125:
 126:
 127:
 128:
 129:
 130:
 131:
 132:
 133:
 134:
 135:
 136:
 137:
 138:
 139:
 140:
 141:
 142:
 143:
 144:
 145:
 146:
 147:
 148:
 149:
 150:
 151:
 152:
 153:
 154:
 155:
 156:
 157:
 158:
 159:
 160:
 161:
 162:
 163:
 164:
 165:
 166:
 167:
 168:
 169:
 170:
 171:
 172:
 173:
 174:
 175:
 176:
 177:
 178:
 179:
 180:
 181:
 182:
 183:
 184:
 185:
 186:
 187:
 188:
 189:
 190:
 191:
 192:
 193:
 194:
 195:
 196:
 197:
 198:
 199:
 200:
 201:
 202:
 203:
 204:
 205:
 206:
 207:
 208:
 209:
 210:
 211:
 212:
 213:
 214:
 215:
 216:
 217:
 218:
 219:
 220:
 221:
 222:
 223:
 224:
 225:
 226:
 227:
 228:
 229:
 230:
 231:
 232:
 233:
 234:
 235:
 236:
 237:
 238:
 239:
 240:
 241:
 242:
 243:
 244:
 245:
 246:
 247:
 248:
 249:
 250:
 251:
 252:
 253:
 254:
 255:
 256:
 257:
 258:
 259:
 260:
 261:
 262:
 263:
 264:
 265:
 266:
 267:
 268:
 269:
 270:
 271:
 272:
 273:
 274:
 275:
 276:
 277:
 278:
 279:
 280:
 281:
 282:
 283:
 284:
 285:
 286:
 287:
 288:
 289:
 290:
 291:
 292:
 293:
 294:
 295:
 296:
 297:
 298:
 299:
 300:
 301:
 302:
 303:
 304:
 305:
 306:
 307:
 308:
 309:
 310:
 311:
 312:
 313:
 314:
 315:
 316:
 317:
 318:
 319:
 320:
 321:
 322:
 323:
 324:
 325:
 326:
 327:
 328:
 329:
 330:
 331:
 332:
 333:
 334:
 335:
 336:
 337:
 338:
 339:
 340:
 341:
 342:
 343:
 344:
 345:
 346:
 347:
 348:
 349:
 350:
 351:
 352:
 353:
 354:
 355:
 356:
 357:
 358:
 359:
 360:
 361:
 362:
 363:
 364:
 365:
 366:
 367:
 368:
 369:
 370:
 371:
 372:
 373:
 374:
 375:
 376:
 377:
 378:
 379:
 380:
 381:
 382:
 383:
 384:
 385:
 386:
 387:
 388:
 389:
 390:
 391:
 392:
 393:
 394:
 395:
 396:
 397:
 398:
 399:
 400:
 401:
 402:
 403:
 404:
 405:
 406:
 407:
 408:
 409:
 410:
 411:
 412:
 413:
 414:
 415:
 416:
 417:
 418:
 419:
 420:
 421:
 422:
 423:
 424:
 425:
 426:
 427:
 428:
 429:
 430:
 431:
 432:
 433:
 434:
 435:
 436:
 437:
 438:
 439:
 440:
 441:
 442:
 443:
 444:
 445:
 446:
 447:
 448:
 449:
 450:
 451:
 452:
 453:
 454:
 455:
 456:
 457:
 458:
 459:
 460:
 461:
 462:
 463:
 464:
 465:
 466:
 467:
 468:
 469:
 470:
 471:
 472:
 473:
 474:
 475:
 476:
 477:
 478:
 479:
 480:
 481:
 482:
 483:
 484:
 485:
 486:
 487:
 488:
 489:
 490:
 491:
 492:
 493:
 494:
 495:
 496:
 497:
 498:
 499:
 500:
 501:
 502:
 503:
 504:
 505:
 506:
 507:
 508:
 509:
 510:
 511:
 512:
 513:
 514:
 515:
 516:
 517:
 518:
 519:
 520:
 521:
 522:
 523:
 524:
 525:
 526:
 527:
 528:
 529:
 530:
 531:
 532:
 533:
 534:
 535:
 536:
 537:
 538:
 539:
 540:
 541:
 542:
 543:
 544:
 545:
 546:
 547:
 548:
 549:
 550:
 551:
 552:
 553:
 554:
 555:
 556:
 557:
 558:
 559:
 560:
 561:
 562:
 563:
 564:
 565:
 566:
 567:
 568:
 569:
 570:
 571:
 572:
 573:
 574:
 575:
 576:
 577:
 578:
 579:
 580:
 581:
 582:
 583:
 584:
 585:
 586:
 587:
 588:
 589:
 590:
 591:
 592:
 593:
 594:
 595:
 596:
 597:
 598:
 599:
 600:
 601:
 602:
 603:
 604:
 605:
 606:
 607:
 608:
 609:
 610:
 611:
 612:
 613:
 614:
 615:
 616:
 617:
 618:
 619:
 620:
 621:
 622:
 623:
 624:
 625:
 626:
 627:
 628:
 629:
 630:
 631:
 632:
 633:
 634:
 635:
 636:
 637:
 638:
 639:
 640:
 641:
 642:
 643:
 644:
 645:
 646:
 647:
 648:
 649:
 650:
 651:
 652:
 653:
 654:
 655:
 656:
 657:
 658:
 659:
 660:
 661:
 662:
 663:
 664:
 665:
 666:
 667:
 668:
 669:
 670:
 671:
 672:
 673:
 674:
 675:
 676:
 677:
 678:
 679:
 680:
 681:
 682:
 683:
 684:
 685:
 686:
 687:
 688:
 689:
 690:
 691:
 692:
 693:
 694:
 695:
 696:
 697:
 698:
 699:
 700:
 701:
 702:
 703:
 704:
 705:
 706:
 707:
 708:
 709:
 710:
 711:
 712:
 713:
 714:
 715:
 716:
 717:
 718:
 719:
 720:
 721:
 722:
 723:
 724:
 725:
 726:
 727:
 728:
 729:
 730:
 731:
 732:
 733:
 734:
 735:
 736:
 737:
 738:
 739:
 740:
 741:
 742:
 743:
 744:
 745:
 746:
 747:
 748:
 749:
 750:
 751:
 752:
 753:
 754:
 755:
 756:
 757:
 758:
 759:
 760:
 761:
 762:
 763:
 764:
 765:
 766:
 767:
 768:
 769:
 770:
 771:
 772:
 773:
 774:
 775:
 776:
 777:
 778:
 779:
 780:
 781:
 782:
 783:
 784:
 785:
 786:
 787:
 788:
 789:
 790:
 791:
 792:
 793:
 794:
 795:
 796:
 797:
 798:
 799:
 800:
 801:
 802:
 803:
 804:
 805:
 806:
 807:
 808:
 809:
 810:
 811:
 812:
 813:
 814:
 815:
 816:
 817:
 818:
 819:
 820:
 821:
 822:
 823:
 824:
 
 | unit Ping;
 
 interface
 
 uses
 Windows, WinSock;
 
 
 
 
 const
 PING_DEFAULT_TIMEOUT = 1000;
 
 PING_ERR_BASE = $20000000;   PING_OK                  = 0;                  PING_GENERAL_ERROR       = PING_ERR_BASE +1;   PING_LOAD_DLL            = PING_ERR_BASE +2;   PING_ICMP_INVALID_HANDLE = PING_ERR_BASE +3;   PING_WSASTARTUP          = PING_ERR_BASE +4;
 type
 in_addr_list = array of in_addr;
 
 TAsyncPingResult = record
 RefID: Integer;         IPv4: in_addr;          RTT,                    Timeout,                ErrorCode: Integer;   end;
 
 TPingCallback = procedure(PingResult: TAsyncPingResult) of Object;
 
 var
 DllHandle: THandle;
 
 LastError: Integer;
 
 
 
 
 
 function ErrorToText(const ErrorCode: Integer): ShortString;
 
 
 function LastErrorText: ShortString;
 
 
 
 function GetIPByName(const Hostname : AnsiString;
 var   IPv4     : in_addr
 ): Boolean; overload;
 
 
 
 function GetIPByName(const Hostname : AnsiString;
 var   IPv4List : in_addr_list
 ): Boolean; overload;
 
 
 
 function Execute(const Hostname : AnsiString;
 const Timeout  : Word = PING_DEFAULT_TIMEOUT
 ): Integer; overload;
 
 
 
 function Execute(const IPv4    : in_addr;
 const Timeout : Word = PING_DEFAULT_TIMEOUT
 ): Integer; overload;
 
 
 
 function ExecuteAsync(const RefID    : Integer;
 const Hostname : AnsiString;
 Callback       : TPingCallback;
 const Timeout  : Word = PING_DEFAULT_TIMEOUT
 ): Boolean; overload;
 
 
 
 function ExecuteAsync(const RefID   : Integer;
 const IPv4    : in_addr;
 Callback      : TPingCallback;
 const Timeout : Word = PING_DEFAULT_TIMEOUT
 ): Boolean; overload;
 
 
 
 
 implementation
 
 uses
 Classes;
 const
 IPHLPAPI_DLL   = 'IPHLPAPI.DLL';   ICMP_DLL       = 'ICMP.DLL';       MAX_ECHO_REPLY = 2;
 IP_STATUS_BASE = 11000;
 IP_SUCCESS               = 0;
 IP_BUF_TOO_SMALL         = IP_STATUS_BASE +  1 + PING_ERR_BASE;
 IP_DEST_NET_UNREACHABLE  = IP_STATUS_BASE +  2 + PING_ERR_BASE;
 IP_DEST_HOST_UNREACHABLE = IP_STATUS_BASE +  3 + PING_ERR_BASE;
 IP_DEST_PROT_UNREACHABLE = IP_STATUS_BASE +  4 + PING_ERR_BASE;
 IP_DEST_PORT_UNREACHABLE = IP_STATUS_BASE +  5;
 IP_NO_RESOURCES          = IP_STATUS_BASE +  6;
 IP_BAD_OPTION            = IP_STATUS_BASE +  7;
 IP_HW_ERROR              = IP_STATUS_BASE +  8;
 IP_PACKET_TOO_BIG        = IP_STATUS_BASE +  9;
 IP_REQ_TIMED_OUT         = IP_STATUS_BASE + 10;
 IP_BAD_REQ               = IP_STATUS_BASE + 11;
 IP_BAD_ROUTE             = IP_STATUS_BASE + 12;
 IP_TTL_EXPIRED_TRANSIT   = IP_STATUS_BASE + 13;
 IP_TTL_EXPIRED_REASSEM   = IP_STATUS_BASE + 14;
 IP_PARAM_PROBLEM         = IP_STATUS_BASE + 15;
 IP_SOURCE_QUENCH         = IP_STATUS_BASE + 16;
 IP_OPTION_TOO_BIG        = IP_STATUS_BASE + 17;
 IP_BAD_DESTINATION       = IP_STATUS_BASE + 18;
 IP_GENERAL_FAILURE       = IP_STATUS_BASE + 50;
 
 type
 PIPOptionInformation = ^TIPOptionInformation;
 TIPOptionInformation = record
 Ttl         : Byte;                       Tos         : Byte;                       Flags       : Byte;                       OptionsSize : Byte;                       OptionsData : ^Byte;                    end;
 
 TIcmpEchoReply = record
 Address       : in_addr;                  Status        : ULONG;                    RoundTripTime : ULONG;                    DataSize      : ULONG;                    Reserved      : ULONG;                    Data          : Pointer;                  Options       : PIPOptionInformation;   end;
 
 TIcmpCreateFile = function: THandle; stdcall;
 TIcmpCloseHandle = function(IcmpHandle: THandle): BOOL; stdcall;
 TIcmpSendEcho = function(IcmpHandle         : THandle;
 DestinationAddress : in_addr;
 RequestData        : Pointer;
 RequestSize        : Word;
 RequestOptions     : PIPOptionInformation;
 ReplyBuffer        : Pointer;
 ReplySize          : DWORD;
 Timeout            : DWORD
 ): DWORD; stdcall;
 
 TPingThread = class(TThread)
 FResolve: Boolean;
 FHostname: AnsiString;
 FPingResult: TAsyncPingResult;
 FCallback: TPingCallback;
 protected
 procedure Execute; Override;
 procedure DoCallbackVCL;
 public
 constructor Create(const RefID: Integer;
 const Hostname: AnsiString;
 Callback: TPingCallback;
 const Timeout: Word); overload;
 constructor Create(const RefID: Integer;
 const IPv4: in_addr;
 Callback: TPingCallback;
 const Timeout: Word); overload;
 end;
 
 var
 IcmpCreateFile: TIcmpCreateFile;
 IcmpCloseHandle: TIcmpCloseHandle;
 IcmpSendEcho: TIcmpSendEcho;
 
 
 function ErrorToText(const ErrorCode: Integer): ShortString;
 begin
 case ErrorCode of
 PING_OK:
 Result := 'OK';
 PING_GENERAL_ERROR:
 Result := 'GENERAL_ERROR';
 PING_LOAD_DLL:
 Result := 'LOAD_LIBRARY_FAILED';
 PING_ICMP_INVALID_HANDLE:
 Result := 'ICMP_INVALID_HANDLE';
 PING_WSASTARTUP:
 Result := 'WSASTARTUP_FAILED';
 
 WSANOTINITIALISED:
 Result := 'WSANOTINITIALISED';
 WSAENETDOWN:
 Result := 'WSAENETDOWN';
 WSAHOST_NOT_FOUND:
 Result := 'WSAHOST_NOT_FOUND';
 WSATRY_AGAIN:
 Result := 'WSATRY_AGAIN';
 WSANO_DATA:
 Result := 'WSANO_DATA';
 WSANO_RECOVERY:
 Result := 'WSANO_RECOVERY';
 WSAEINPROGRESS:
 Result := 'WSAEINPROGRESS';
 WSAEFAULT:
 Result := 'WSAEFAULT';
 WSAEINTR:
 Result := 'WSAEINTR';
 
 IP_BUF_TOO_SMALL:
 Result := 'IP_BUF_TOO_SMALL';
 IP_DEST_NET_UNREACHABLE:
 Result := 'IP_DEST_NET_UNREACHABLE';
 IP_DEST_HOST_UNREACHABLE:
 Result := 'IP_DEST_HOST_UNREACHABLE';
 IP_DEST_PROT_UNREACHABLE:
 Result := 'IP_DEST_PROT_UNREACHABLE';
 IP_DEST_PORT_UNREACHABLE:
 Result := 'IP_DEST_PORT_UNREACHABLE';
 IP_NO_RESOURCES:
 Result := 'IP_NO_RESOURCES';
 IP_BAD_OPTION:
 Result := 'IP_BAD_OPTION';
 IP_HW_ERROR:
 Result := 'IP_HW_ERROR';
 IP_PACKET_TOO_BIG:
 Result := 'IP_PACKET_TOO_BIG';
 IP_REQ_TIMED_OUT:
 Result := 'IP_REQ_TIMED_OUT';
 IP_BAD_REQ:
 Result := 'IP_BAD_REQ';
 IP_BAD_ROUTE:
 Result := 'IP_BAD_ROUTE';
 IP_TTL_EXPIRED_TRANSIT:
 Result := 'IP_TTL_EXPIRED_TRANSIT';
 IP_TTL_EXPIRED_REASSEM:
 Result := 'IP_TTL_EXPIRED_REASSEM';
 IP_PARAM_PROBLEM:
 Result := 'IP_PARAM_PROBLEM';
 IP_SOURCE_QUENCH:
 Result := 'IP_SOURCE_QUENCH';
 IP_OPTION_TOO_BIG:
 Result := 'IP_OPTION_TOO_BIG';
 IP_BAD_DESTINATION:
 Result := 'IP_BAD_DESTINATION';
 IP_GENERAL_FAILURE:
 Result := 'IP_GENERAL_FAILURE';
 
 else       Result := 'NO_ERROR_TEXT';
 end;
 end;
 
 function LastErrorText: ShortString;
 begin
 Result := ErrorToText(LastError);
 end;
 
 function CheckErrorCode(ErrorCode: Integer): Integer;
 begin
 Result := ErrorCode;
 if ( (Result = WSAHOST_NOT_FOUND) or
 (Result = WSATRY_AGAIN) or
 (Result = WSANO_RECOVERY) or
 (Result = WSANO_DATA) ) then
 Inc(Result,PING_ERR_BASE);
 end;
 
 
 function GetIPByName(const Hostname: AnsiString;
 var IPv4: in_addr
 ): Boolean;
 var
 WSAData: TWSAData;
 HostInfo: PHostEnt;
 begin
 Result := FALSE;   IPv4.S_addr := -1;
 LastError := PING_WSASTARTUP;   if (WSAStartup($0101, WSAData) = 0) then     try
 if (Hostname <> '') then
 HostInfo := WinSock.GetHostByName(PAnsiChar(Hostname))
 else
 HostInfo := WinSock.GetHostByName(NIL);
 if Assigned(HostInfo) then begin
 IPv4.S_addr := PInAddr(HostInfo^.h_addr_list^)^.S_addr;
 
 LastError := PING_OK;         Result := TRUE;       end
 
 else         LastError := WSAGetLastError;
 finally       WSACleanUp;
 end;
 end;
 
 
 function GetIPByName(const Hostname: AnsiString;
 var IPv4List: in_addr_list
 ): Boolean;
 var
 WSAData: TWSAData;
 HostInfo: PHostEnt;
 AddrList: ^PInAddr;
 i: Integer;
 begin
 Result := FALSE;   IPv4List := NIL;
 LastError := PING_WSASTARTUP;   if (WSAStartup($0101, WSAData) = 0) then     try
 if (Hostname <> '') then
 HostInfo := WinSock.GetHostByName(PAnsiChar(Hostname))
 else
 HostInfo := WinSock.GetHostByName(NIL);
 if Assigned(HostInfo) then begin
 i := 0;
 AddrList := Pointer(HostInfo^.h_addr_list);
 while Assigned(AddrList^) do begin
 Inc(i);
 Inc(AddrList);
 end;
 SetLength(IPv4List,i);         Move(HostInfo^.h_addr_list^^,IPv4List[0],i*SizeOf(in_addr));
 LastError := PING_OK;         Result := TRUE;       end
 
 else         LastError := WSAGetLastError;
 finally       WSACleanUp;
 end;
 end;
 
 
 function Execute(const IPv4: in_addr;
 const Timeout: Word = PING_DEFAULT_TIMEOUT
 ): Integer;
 var
 Handle: THandle;
 ReplyBuffer: array[0..MAX_ECHO_REPLY] of TIcmpEchoReply;
 ReplyCount,
 i: Integer;
 begin
 LastError := PING_LOAD_DLL;   Result := -2;
 if (DllHandle <> 0) then begin         LastError := PING_ICMP_INVALID_HANDLE;     Handle := IcmpCreateFile;
 if (Handle <> INVALID_HANDLE_VALUE) then       try
 ReplyCount := IcmpSendEcho(Handle,
 IPv4,
 NIL, 0,
 NIL,
 @ReplyBuffer[0], SizeOf(ReplyBuffer),
 Timeout);
 
 if (ReplyCount > 0) then begin           Result := -1;
 Dec(ReplyCount);
 if (ReplyCount > MAX_ECHO_REPLY) then
 ReplyCount := MAX_ECHO_REPLY;
 
 i := 0;
 while ( (Result < 0) and (i <= ReplyCount) ) do begin
 LastError := CheckErrorCode(ReplyBuffer[i].Status);             if (LastError = IP_SUCCESS) then               Result := ReplyBuffer[i].RoundTripTime;             Inc(i);
 end;
 end
 
 else begin           LastError := CheckErrorCode(GetLastError);           if (LastError = IP_REQ_TIMED_OUT) then             Result := -1;         end;
 
 finally         IcmpCloseHandle(Handle);
 end;
 end;
 end;
 
 
 function Execute(const Hostname: AnsiString;
 const Timeout: Word = PING_DEFAULT_TIMEOUT
 ): Integer;
 var
 IP: in_addr;
 begin
 if (GetIPByName(Hostname, IP)) then         Result := Execute(IP, Timeout)
 
 else     case LastError of       WSAHOST_NOT_FOUND, WSATRY_AGAIN:
 Result := -1;       else
 Result := -2;     end;
 end;
 
 
 function ExecuteAsync(const RefID: Integer;
 const IPv4: in_addr;
 Callback: TPingCallback;
 const Timeout: Word = PING_DEFAULT_TIMEOUT
 ): Boolean;
 begin
 Result := (DllHandle <> 0);   if (Result) then     TPingThread.Create(RefID, IPv4, Callback, Timeout);
 end;
 
 
 function ExecuteAsync(const RefID: Integer;
 const Hostname: AnsiString;
 Callback: TPingCallback;
 const Timeout: Word = PING_DEFAULT_TIMEOUT
 ): Boolean;
 begin
 Result := (DllHandle <> 0);   if (Result) then     TPingThread.Create(RefID, Hostname, Callback, Timeout);
 end;
 
 
 constructor TPingThread.Create(const RefID: Integer;
 const IPv4: in_addr;
 Callback: TPingCallback;
 const Timeout: Word);
 begin
 inherited Create(TRUE);   FreeOnTerminate := TRUE;
 FPingResult.RefID := RefID;
 FPingResult.Timeout := Timeout;
 FPingResult.IPv4 := IPv4;
 FResolve := FALSE;   FCallback := Callback;
 Suspended := FALSE;
 end;
 
 constructor TPingThread.Create(const RefID: Integer;
 const Hostname: AnsiString;
 Callback: TPingCallback;
 const Timeout: Word);
 begin
 inherited Create(TRUE);   FreeOnTerminate := TRUE;
 FPingResult.RefID := RefID;
 FPingResult.Timeout := Timeout;
 FResolve := TRUE;   FHostname := Hostname;
 FCallback := Callback;
 Suspended := FALSE;
 end;
 
 procedure TPingThread.Execute;
 var
 WSAData: TWSAData;
 HostInfo: PHostEnt;
 Handle: THandle;
 ReplyBuffer: array[0..MAX_ECHO_REPLY] of TIcmpEchoReply;
 ReplyCount, i: Integer;
 begin
 FPingResult.RTT := -2;   FPingResult.ErrorCode := PING_GENERAL_ERROR;
 
 if (FResolve) then begin         FResolve := FALSE;     FPingResult.IPv4.S_addr := -1;
 FPingResult.ErrorCode := PING_WSASTARTUP;     if (WSAStartup($0101, WSAData) = 0) then       try
 if (FHostname <> '') then
 HostInfo := WinSock.GetHostByName(PAnsiChar(FHostname))
 else
 HostInfo := WinSock.GetHostByName(NIL);
 if Assigned(HostInfo) then begin
 FPingResult.IPv4.S_addr := PInAddr(HostInfo^.h_addr_list^)^.S_addr;
 
 FPingResult.ErrorCode := PING_OK;           FResolve := TRUE;         end
 
 else begin           FPingResult.ErrorCode := WSAGetLastError;           if ( (FPingResult.ErrorCode = WSAHOST_NOT_FOUND)
 or
 (FPingResult.ErrorCode = WSATRY_AGAIN) ) then
 FPingResult.RTT := -1;         end;
 
 finally         WSACleanUp;
 end;
 end
 
 else     FResolve := TRUE;
 if (FResolve) then begin
 FPingResult.ErrorCode := PING_ICMP_INVALID_HANDLE;     Handle := IcmpCreateFile;
 if (Handle <> INVALID_HANDLE_VALUE) then       try
 ReplyCount := IcmpSendEcho(Handle,
 FPingResult.IPv4,
 NIL, 0,
 NIL,
 @ReplyBuffer[0], SizeOf(ReplyBuffer),
 FPingResult.Timeout);
 
 if (ReplyCount > 0) then begin           FPingResult.RTT := -1;
 Dec(ReplyCount);
 if (ReplyCount > MAX_ECHO_REPLY) then
 ReplyCount := MAX_ECHO_REPLY;
 
 i := 0;
 while ( (FPingResult.RTT < 0) and (i <= ReplyCount) ) do begin
 FPingResult.ErrorCode := CheckErrorCode(ReplyBuffer[i].Status);             if (FPingResult.ErrorCode = IP_SUCCESS) then               FPingResult.RTT := ReplyBuffer[i].RoundTripTime;             Inc(i);
 end;
 end
 
 else begin           FPingResult.ErrorCode := CheckErrorCode(GetLastError);           if (FPingResult.ErrorCode = IP_REQ_TIMED_OUT) then             FPingResult.RTT := -1;         end;
 
 finally         IcmpCloseHandle(Handle);
 end;
 end;
 
 if Assigned(FCallback) then     Synchronize(DoCallbackVCL); end;
 
 procedure TPingThread.DoCallbackVCL;
 begin
 FCallback(FPingResult);
 end;
 
 
 initialization
 LastError := PING_LOAD_DLL;
 DllHandle := LoadLibrary(PChar(IPHLPAPI_DLL));
 if (DllHandle <> 0) then begin
 @IcmpCreateFile := GetProcAddress(DllHandle, 'IcmpCreateFile');
 if (NOT Assigned(IcmpCreateFile)) then begin
 FreeLibrary(DllHandle);
 DllHandle := 0;
 end;
 end;
 if (DllHandle = 0) then begin
 DllHandle := LoadLibrary(PChar(ICMP_DLL));
 if (DllHandle <> 0) then begin
 @IcmpCreateFile := GetProcAddress(DllHandle, 'IcmpCreateFile');
 if (NOT Assigned(IcmpCreateFile)) then begin
 FreeLibrary(DllHandle);
 DllHandle := 0;
 end;
 end;
 end;
 if (DllHandle <> 0) then begin
 @IcmpCloseHandle := GetProcAddress(DllHandle, 'IcmpCloseHandle');
 @IcmpSendEcho := GetProcAddress(DllHandle, 'IcmpSendEcho');
 LastError := 0;
 end;
 
 
 finalization
 if (DllHandle <> 0) then
 FreeLibrary(DllHandle);
 
 end.
 |  Öffnen, kompilieren, die Ping.dcu in das \Lib-Verzeichnis, optional die Ping.pas in ein Source-Verzeichnis legen (nicht notwendig, die .dcu reicht), dann kann über uses ..., Ping;  auf die Unit zugegriffen werden. Es kann für den Typ in_addr  (IPv4-Adresse) nötig sein, noch WinSock  in die uses-Klausel einzufügen.
 Ich habe noch eine Demo-Anwendung beigelegt, die die Verwendung veranschaulicht.
 Die Unit ist frei verwendbar, unter der Voraussetzung, dass mein Autorenverweis erhalten bleibt (mind. Erwähnung in den Credits einer Anwendung, die auf die Unit zugreift).
 Bitte testet doch mal ausgiebig und sagt mir eure Meinung dazu, Danke.    //EDIT: seit Version 1.04 ist die Unit auch für Unicode-Delphi-Versionen geeignet. Getestet mit D2k10.
 cu
 Narses
Einloggen, um Attachments anzusehen!
 
_________________ There are 10 types of people - those who understand binary and those who don´t.
 
 Zuletzt bearbeitet von Narses am Sa 10.01.15 17:11, insgesamt 3-mal bearbeitet
 Für diesen Beitrag haben gedankt: heizer66, Schimmelreiter, storestore
 |  |  |  
| reichemi 
          Beiträge: 41
 
 WinXP home + prof, SUSE 9.2
 Delphi 6
 
 | 
Verfasst: Fr 03.03.06 13:03 
 
hallo!
 ich hab mir gerad deine unit sowie die demo runtergeladen, und muss sagen: das sieht seeehr gut und vielversprechend aus!! gefällt mir    beim quelltext-durchschauen ist mir aber aufgefallen: warum läßt du dir beim IcmpSendEcho()-Aufruf nicht auch die IPOptionInformation zurückgeben und schreibst diese mit in den TAsyncPingResult-Record (im asynchronen fall)? hattest du einen grund, oder gab es einfach keinen bedarf dafür?   |  |  |  
| Narses  
          
  Beiträge: 10183
 Erhaltene Danke: 1256
 
 W10ent
 TP3 .. D7pro .. D10.2CE
 
 | 
Verfasst: Fr 03.03.06 14:28 
 
Moin und    im Forum!
 	  |  reichemi hat folgendes geschrieben: |  	  | ich hab mir gerad deine unit sowie die demo runtergeladen, und muss sagen: das sieht seeehr gut und vielversprechend aus!! gefällt mir  | 
 Danke für das Lob!    	  |  reichemi hat folgendes geschrieben: |  	  | beim quelltext-durchschauen ist mir aber aufgefallen: warum läßt du dir beim IcmpSendEcho()-Aufruf nicht auch die IPOptionInformation zurückgeben und schreibst diese mit in den TAsyncPingResult-Record (im asynchronen fall)? hattest du einen grund, oder gab es einfach keinen bedarf dafür?  | 
 Ich hab keinen Grund gesehen, das zurückzuliefern. Hast du einen sinnvollen Grund gefunden?    Ausser der RTT und einem möglichst umfangreichen, aber zentralen Fehlerstatus braucht man bei einem Ping doch eigentlich nix...    Bis für Vorschläge offen!
 cu
 Narses_________________ There are 10 types of people - those who understand binary and those who don´t.
 |  |  |  
| reichemi 
          Beiträge: 41
 
 WinXP home + prof, SUSE 9.2
 Delphi 6
 
 | 
Verfasst: Fr 03.03.06 17:07 
 |  |  |  
| Narses  
          
  Beiträge: 10183
 Erhaltene Danke: 1256
 
 W10ent
 TP3 .. D7pro .. D10.2CE
 
 | 
Verfasst: Fr 03.03.06 21:19 
 
Moin!
 	  |  reichemi hat folgendes geschrieben: |  	  | joaaa.... eigentlich  aber dagegen sprechen zwei dinge: 1) ich finde die TTL noch ganz interessant
  | 
 Echt? Wozu hast du die (bei einem Ping!) jemals gebraucht...    	  |  reichemi hat folgendes geschrieben: |  	  | 2) sollte man bei einer komponente doch dem programmierer möglichst viele informationen anbieten, und ihm die auswahl der für ihn sinnvollen infos überlassen -- oder?  | 
 Ja, aber nach der Maxime: soviel wie nötig, so knapp wie möglich.    Versteh mich recht, ich hab nix dagegen die z.B. TTL auch mit abzuliefern, aber es sollte auch einen Sinn haben. Funktionen, die parameterüberladen sind, nutzen selten viele davon aus, und nur weil es die Info grundsätzlich gibt, muss man sie ja nicht unbedingt immer gleich weiterreichen.
 Abgesehen davon: für genau diesen Fall habe ich ja den Quelltext veröffentlicht.    Wenn dir eigene Erweiterungen einfallen (die aber eher für dich spezifisch wichtig sind), dann kannste du dir die Unit ja nach deinem Ermessen für dich erweitern...
 	  |  reichemi hat folgendes geschrieben: |  	  | 	  |  Narses hat folgendes geschrieben: |  	  | vom 05.03.-21.03. offline | 
 na da hab ich ja glück gehabt, dass ich dich noch erwischt hab
  | 
 Irgendwann muss der Mensch auch mal Urlaub machen.    Abgesehen davon, ich verbringe glaub ich im Moment viel zuviel Zeit im DF, ich sollte auch mal wieder etwas Abstand nehmen      cu
 Narses_________________ There are 10 types of people - those who understand binary and those who don´t.
 |  |  |  
| reichemi 
          Beiträge: 41
 
 WinXP home + prof, SUSE 9.2
 Delphi 6
 
 | 
Verfasst: Fr 03.03.06 21:27 
 
	  |  Narses hat folgendes geschrieben: |  	  | Abgesehen davon: für genau diesen Fall habe ich ja den Quelltext veröffentlicht.  | 
 stimmt schon    mal sehen ob ichs mir noch dazu programmier...
 trotzdem danke nochmal für die super unit und die schnelle antwort!   |  |  |  
| reddevil 
          Beiträge: 23
 
 
 
 
 | 
Verfasst: Mo 06.03.06 16:03 
 
Hallo
 Zuerst einmal großes Lob an Dich. Du hast eine sehr schöne und nützliche Unit geschrieben.
 Was mir jedoch aufgefallen ist, wenn oft die Funktion ExecuteAsync mit einem unaufgelösten Hostname aufgerufen wird, so steigt der Speicherverbrauch des Programms an. Er scheint linear mit der Anzahl der Aufrufe zu steigen, könnte also irgendwelcher Speicher sein der nicht wieder freigegeben wird.
 Wenn anstelle des Hostname die Funktion mit der entsprechenden in_addr Struktur aufgerufen wird, so tritt dieser Effekt nicht auf. Ich würde daher vermuten, dass "der Fehler" zwischen Zeile 688 und 724 liegt (konnte ihn jedoch nicht finden).
 Um den oben beschriebenen Effekt festzustellen reicht es in deinem PingDemo Programm einen Host (z.B. www.heise.de)  etwa 20mal in die Hostliste hinzufügt und anschließend mehrfach asynchron anzupingen. Den Speicherbedarf kann man dabei im Taskmanager beobachten.
 Ich hoffe du oder jemand anderes kann dieses Problem beheben.
 Natürlich wünsche ich dir noch einen schönen Urlaub    red |  |  |  
| Narses  
          
  Beiträge: 10183
 Erhaltene Danke: 1256
 
 W10ent
 TP3 .. D7pro .. D10.2CE
 
 | 
Verfasst: Di 21.03.06 01:16 
 
Moin!
 	  |  reddevil hat folgendes geschrieben: |  	  | Zuerst einmal großes Lob an Dich. Du hast eine sehr schöne und nützliche Unit geschrieben. | 
 Danke für das Lob!    	  |  reddevil hat folgendes geschrieben: |  	  | Was mir jedoch aufgefallen ist, wenn oft die Funktion ExecuteAsync mit einem unaufgelösten Hostname aufgerufen wird, so steigt der Speicherverbrauch des Programms an. Er scheint linear mit der Anzahl der Aufrufe zu steigen | 
 Ja, weil du (zu)viele Threads startest, die du gar nicht brauchst. Nebenbei: ein Programm sollte AFAIK nicht mehr als 16 Threads laufen haben.
 	  |  reddevil hat folgendes geschrieben: |  	  | Ich würde daher vermuten, dass "der Fehler" zwischen Zeile 688 und 724 liegt (konnte ihn jedoch nicht finden). | 
 Es ist kein Fehler in der Unit, sondern in der "Benutzung".    Im Anhang ist ein kleines Beispielprogramm, wie man das asynchrone Anpingen einer Liste von Hosts mit meiner Ping-Unit lösen könnte. Der "Trick" besteht darin, über die Callback-Funktion eine Ereignisverkettung aufzubauen. So laufen nie mehr als 2 Ping-Threads gleichzeitig (und brauchen auch nicht mehr Speicher, als nötig).
 cu
 Narses
 Hinweis: Falls die Anhänge nicht da sind, die Seite (ggfs. auch mehrfach) neu laden, dann tauchen die Anhänge irgendwann auf (ist ein Bug in der aktuellen Forensoftware).  
Einloggen, um Attachments anzusehen!
 |  |  |  
| Luckie Ehemaliges Mitglied
 Erhaltene Danke: 1
 
 
 
 
 | 
Verfasst: Di 21.03.06 07:54 
 |  |  |  
| reddevil 
          Beiträge: 23
 
 
 
 
 | 
Verfasst: Di 21.03.06 10:48 
 
Hallo
 Ich habe mir dein neues Beispielprogramm angeschaut und festgestellt, dass dort das Speicherproblem nicht auftritt. Allerdings find ich das Programm auch relativ "sinnfrei", denn so wie du es dort umgesetzt hast, könnte man auch direkt syncron pingen.
 
 Den von mir oben beschriebenen Effekt kannst du auch schon mit nur zwei Einträgen in der Hostliste deines PingDemo-Programmes feststellen. Die etwa 20 Einträge von mir waren nur gewählt, damit der Effekt deutlicher wird.
 Bei nur einem Eintrag in der Hostliste steigt der Speicherverbrauch bei mir nicht an.
 Daher könnte es vielleicht auch an irgendwelchen "nicht thread-sicheren" Windowsfunktionen liegen.
 
 red
 |  |  |  
| Narses  
          
  Beiträge: 10183
 Erhaltene Danke: 1256
 
 W10ent
 TP3 .. D7pro .. D10.2CE
 
 | 
Verfasst: Di 21.03.06 11:25 
 
Moin!
 	  |  Luckie hat folgendes geschrieben: |  	  | 	  |  Narses hat folgendes geschrieben: |  	  | Nebenbei: ein Programm sollte AFAIK nicht mehr als 16 Threads laufen haben. | 
 Warum? Wie kommst du auf diese Zahl? In meinem
  LUCKIEPING erzeuge ich 255 Threads auf einen Schlag.  | 
 Meine das mal als Empfehlung gelesen zu haben; aber da du so wehement dagegen hältst, bin ich mir schon nicht mehr so sicher...    	  |  Luckie hat folgendes geschrieben: |  	  | Nein, es ist ein Fehler in deinem Code. Du hast wahrschenlich da irgendwo ein Speicherleck. | 
   Naja, wenn du das sagst... dann werde ich mich mal mit "Räucherwerk auf dem Klo einschließen und nachdenken", wie man so sagt.    	  |  reddevil hat folgendes geschrieben: |  	  | Ich habe mir dein neues Beispielprogramm angeschaut [...] Allerdings find ich das Programm auch relativ "sinnfrei", denn so wie du es dort umgesetzt hast, könnte man auch direkt syncron pingen. | 
 Hmm, also von "sinnfrei" kann mal nicht wirklich die Rede sein.    Wenn du das synchron machst, dann bleibt die GUI "hängen", weil keine Ereignisverarbeitung mehr stattfindet (während der Ping-executes), das passiert mit dem PingListe-Beispiel nicht.
 cu
 Narses |  |  |  
| reddevil 
          Beiträge: 23
 
 
 
 
 | 
Verfasst: Di 21.03.06 11:39 
 
Sorry, ich habe mich schlecht ausgedrückt.
 Das syncrone Pingen müsste man natürlich in einen extra Thread auslagern, man würde sich dann aber das häufige Thread-erzeugen und beenden in deinem Programm ersparren.
 Ich fände es sehr gut wenn du den Fehler finden würdest, also viel Erfolg auf dem Klo.   |  |  |  
| Narses  
          
  Beiträge: 10183
 Erhaltene Danke: 1256
 
 W10ent
 TP3 .. D7pro .. D10.2CE
 
 | 
Verfasst: Mi 22.03.06 15:48 
 
Moin!
 	  |  reddevil hat folgendes geschrieben: |  	  | viel Erfolg auf dem Klo.  | 
 Danke, scheint genutzt zu haben!    Um das Ergebnis vorwegzunehmen: die Unit ist IMHO fehlerfrei, das ist kein Speicherleck .    Hier die Begründung:
 Ich habe das im Anhang befindliche Testprogramm gestartet und den Speicherverbrauch nach jeweils einem Klick auf den Button aus dem Taskmanager abgeschrieben:
 		                       Quelltext 
 									| 1:2:
 3:
 4:
 
 | 3616,3892,3948,3948,3972,3964,3968,4000,4180,4032,4248,4044,4048,4140,4100 (nur gewartet),4224,4076,4268,4108,4144,4320,4160,4200,
 4324,4144,4164,4284,4216,4376,4232,4412,4468,4340,4312,4524,4404,
 4592,4408,4592,4472,4664,4536,4504,4696,4552,4760,4692,4828
 |  Interpretation: Es ist zwar ein eindeutiger Aufwärtstrend sichtbar (durchschnittlich 127kb zunehmend, 116kb abnehmend), aber da auch im ersten markierten Fall 204kb freigegeben wurden, kann ich nicht an ein Speicherleck glauben. Ich denke vielmehr, dass es sich um die "normale" Fluktuation des Delphi-Speichermanagers handelt (besonders deshalb, weil im zweiten markierten Fall scheinbar eine garbage-collection stattgefunden hat).
 Fazit: Ich schätze, meine Unit stellt im asynchronen Modus lediglich den Delphi-MemoryManager bloss...    Wenn jemand tatsächlich ein Speicherleck finden sollte (was ich nicht kategorisch ausschließen will!), dann bin ich über jede Info dankbar. Ich kann keinen Thread-basierten Fehler erkennen, IMHO ist das ExecuteAsync threadsave!
 Hinweis: Da in meinem ersten Demoprogramm auch das Log-Memo mit Text gefüllt wird, wenn man Ping-Aufträge erstellt, könnte der zunehmende Speicherverbrauch auch an dieser Stelle Begründung finden...    cu
 Narses
Einloggen, um Attachments anzusehen!
 |  |  |  
| reddevil 
          Beiträge: 23
 
 
 
 
 | 
Verfasst: Do 23.03.06 09:46 
 
	  |  Narses hat folgendes geschrieben: |  	  | Hinweis: Da in meinem ersten Demoprogramm auch das Log-Memo mit Text gefüllt wird, wenn man Ping-Aufträge erstellt, könnte der zunehmende Speicherverbrauch auch an dieser Stelle Begründung finden...  | 
 Das kann ich ausschließen. Ich habe die Textausgabe auf das Log-Memo auskommentiert und der Speicherzuwachs war dennoch da.
 An einen "Fehler" im MemoryManager will ich nicht glauben, allerdings weiss ich auch nicht woran es liegt. |  |  |  
| Narses  
          
  Beiträge: 10183
 Erhaltene Danke: 1256
 
 W10ent
 TP3 .. D7pro .. D10.2CE
 
 | 
Verfasst: Do 23.03.06 10:02 
 
Moin!
 	  |  reddevil hat folgendes geschrieben: |  	  | An einen "Fehler" im MemoryManager will ich nicht glauben, allerdings weiss ich auch nicht woran es liegt. | 
 Von einem Fehler redet auch niemand, da aber in der Hostname-Variante Strings auf den (in den threadsave-mode geschalteten) Heap gelegt werden und die Threads nicht alle synchron dazu terminieren, wird es einfach ein "Schweizer-Käse"-Problem sein, schätze ich. Wenn du in Zeile 637 "Hostname" durch '' ersetzt, tritt der Speicher-Effekt auch nicht mehr (so) auf (in meinem 2. Test-Programm, dass nur den Call macht).
 Fazit: IMHO ist da kein Speicherleck, das ist ein Thread-Heap-Problem mit den Strings (prinzipbedingt). Ich will das Speicherleck nicht ausschließen, aber in separaten Tests, in denen ich alle Elemente des TPingThread.Execute getestet habe, ist der Speicherzuwachs nach Terminieren der Threads wieder abgebaut worden. Sobald die Strings ins Spiel kamen, wurde der Speicher nicht mehr vollständig abgebaut, sondern nach dem im letzten Post beschriebenen Verhalten.
 Also, ohne neue Erkenntnisse lasse ich das so und unterstelle keinen Fehler in der Unit.    cu
 Narses |  |  |  
| Zyklame 
          Beiträge: 41
 Erhaltene Danke: 1
 
 Win 7 Professional
 Delphi XE, Visual Studio 2010
 
 | 
Verfasst: Mi 05.04.06 10:16 
 
Vieleicht hat das Problem mit dem Delphi Speichermanager zu tun:
www.dsdt.info/inside.../speichermanager.php |  |  |  
| Hendi48 
          Beiträge: 271
 
 
 
 
 | 
Verfasst: Fr 17.08.07 18:04 
 
Wo krieg ich denn dieses TPing her? Ich find das nur für Delphi 3 aber ich brauchs für D2007 =( |  |  |  
| Narses  
          
  Beiträge: 10183
 Erhaltene Danke: 1256
 
 W10ent
 TP3 .. D7pro .. D10.2CE
 
 | 
Verfasst: So 19.08.07 23:16 
 
Moin!
 	  |  Hendi48 hat folgendes geschrieben: |  	  | Wo krieg ich denn dieses TPing her? | 
 Was für ein TPing?    Im ersten Beitrag (wie hier üblich...   ) ist doch die komplette Unit und im Anhang eine Demo...          cu
 Narses_________________ There are 10 types of people - those who understand binary and those who don´t.
 |  |  |  
| Bookworm 
          Beiträge: 29
 
 Win XP SP2
 Delphi 2005 PE
 
 | 
Verfasst: Do 30.08.07 21:19 
 
Irgendwie komm ich damit nicht klar    Ich meine, die fertig kompilierte Demo zeigt mir schon, dass es eigentlich genau das ist, was ich suche. Aber meine bescheidenen Delphi-Kenntnisse beinhalten leider nicht, wie ich aus diesem Unit-Quelltext die DCU mache, die ich später bei uses einbinden kann. Und die eigentliche Ping-Funktion finde ich auch nicht    Asche auf mein Haupt
 Bookworm |  |  |  
| Narses  
          
  Beiträge: 10183
 Erhaltene Danke: 1256
 
 W10ent
 TP3 .. D7pro .. D10.2CE
 
 | 
Verfasst: Do 30.08.07 21:25 
 
Moin!
 Speicher den Unit-Quelltext als Ping.pas im Verzeichnis deines Programms ab. Pack in die uses -Klausel am Anfang des Programms:
 		                       Delphi-Quelltext 
 dann kannst du die Unit benutzen.
 cu
 Narses_________________ There are 10 types of people - those who understand binary and those who don´t.
 |  |  |  |