在用TIdIcmpClient做PING功能的时候发现,它有个PacketSize属性,默认是1024。因为记得Windows自带的PING程序的包大小是32字节,所以尝试将其改为32,结果发现执行IcmpClient.Ping()的时候发生了非法内存访问。幸好Indy是有源代码的,于是查看了IdIcmpClient.pas,发现了一些问题,在此发帖出来,与大家共同商讨。(Indy 版本10.1.5,下载自http://www.atozed.com/indy/plus/installVCL.en.iwp) 首先在IdIcmpClient第85行增加一个常量定义
Id_ICMP_HSIZE = 12;  // Icmp包头长度(按标准应为8字节,但Indy组件还为其增加了一个DWORD字段)修正1, IdIcmpClient.pas, Line 860-869
SetLength(FbufIcmp,FPacketSize);
应改成
SetLength(FbufIcmp,FPacketSize+Id_ICMP_HSIZE);
因为需加上实际发送的Icmp包头长度。同样第862行定义接收缓存区大小时,应将if  Self.FIPVersion = Id_IPv4 then
begin
  SetLength(FbufReceive,FPacketSize+Id_IP_HSIZE);
end
else
begin
  SetLength(FbufReceive,FPacketSize+(Id_IPv6_HSIZE*2));
end;改为if  Self.FIPVersion = Id_IPv4 then
begin
  SetLength(FbufReceive,FPacketSize+Id_ICMP_HSIZE+Id_IP_HSIZE);
end
else
begin
  SetLength(FbufReceive,FPacketSize+Id_ICMP_HSIZE+(Id_IPv6_HSIZE*2));
end;否则会引起接收错误。修正2, IdIcmpClient.pas, Line 666
iDataSize := DEF_PACKET_SIZE + 8;
FillBytes(FbufIcmp, iDataSize, 0);
这里是对用户ICMP数据进行初始化,而DEF_PACKET_SIZE和iDataSize似乎根本就该存在(其他地方也没有再使用这两个常/变量)。由于控件定义了PacketSize这个属性允许让用户自由更改包数据的大小,因此不该用常量将初始化的范围定死。此处应改为
FillBytes(FbufIcmp, Length(FbufIcmp), 0);
修正3, IdIcmpClient.pas, Line 683
IdGlobal.CopyTIdString(Buffer,FBufIcmp,12);
这里是以用户自定义的Icmp数据填充ICMP包的数据段,填充的位置是从第12字节(由0起计)开始,但没有考虑指针越界(缓存区溢出),应改为
IdGlobal.CopyTIdString(Copy(Buffer, 1, Length(FBufIcmp)-Id_ICMP_HSIZE),FBufIcmp,12);
修正4, IdIcmpClient.pas, Line 379
FillBytes(FbufReceive, sizeOf(FbufReceive),0);
这里出现了一个明显的错误(虽然问题不大),sizeOf()在C语言里面是可以取一个数组的大小,而在Delphi里应该用Length()才对,故应改为
FillBytes(FbufReceive, Length(FbufReceive),0);以下是用CommView截取到的Windows的PING程序发送的ICMP完整数据包: 以太网头部、IP头部、以及ICMP帧 0x0000   00 0B AB 05 09 49 00 0D-88 48 91 F9 08 00 45 00   ..?.I..圚戼..E.
                                                   ~~ ~~
         ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ Enternet II (12 Bytes)0x0010   00 3C BB 14 00 00 80 01-05 59 80 28 3D 01 80 28   .<?..€..Y€(=.€(
         ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~
0x0020   3D 02 00 00 A3 50 02 00-B0 0B 61 62 63 64 65 66   =.....?abcdef
                                       ~~ ~~ ~~ ~~ ~~ ~~
               ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ Icmp Head (8 Bytes)
         ~~ ~~ IP (20 Bytes)
0x0030   67 68 69 6A 6B 6C 6D 6E-6F 70 71 72 73 74 75 76   ghijklmnopqrstuv
         ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~
0x0040   77 61 62 63 64 65 66 67-68 69                     wabcdefghi
         ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ Icmp Data Row (32 Bytes)