Delphi实现NetBIOS广播收发
点击次数:53 次 发布日期:2008-11-09 08:40:55 作者:源代码网
|
源代码网推荐 源代码网推荐 之所以这样是有原因的。NetBIOS协议短小精悍,非常适用于小型局域网,特别是一些对实时性要求较高的网络环境。NetBIOS的广播功能由于有开发使用方便、系统开销小的优点,所以在很多场合仍然被大量使用。笔者由于工作需要,在一个航天测控软件的编制中就使用了NetBIOS广播功能。 源代码网推荐 源代码网推荐 我原以为这是件很简单的工作,因为WIN32API中提供了一个Netbios函数,里面封装了所有函数和数据结构,用起来很方便,在BC和VC下都如此。可是由于这次是使用流行的Delphi作编译器,却遇到了意想不到的麻烦:号称全面移植WIN32API的Delphi中偏偏没有Netbios函数!这下顿时让我方寸大乱。怎么办?总不能从底层干起吧?而且时间也不允许。在冷静下来之后,我忽然想到,既然WIN95支持NetBIOS,那么系统就一定会提供DLL支持,编译器本身是没有底层支持的。于是我在机器中搜索,果然,在SYSTEM目录下有一个Netbios.dll,用快速查看将其打开,在导出表部分显示如下: 源代码网推荐 源代码网推荐 导出表: 源代码网推荐 源代码网推荐 序数 入口 名称 源代码网推荐 0000 00001a37 NetbiosAddthd 源代码网推荐 0001 000019eb NetbiosDelete 源代码网推荐 0002 00001a96 NetbiosDelthd 源代码网推荐 0003 000019b1 NetbiosInitialize 源代码网推荐 0004 0000186b PostRoutineCaller 源代码网推荐 0005 0000102e _Netbios 源代码网推荐 源代码网推荐 源代码网推荐 注意到那个0005号_Netbios导出函数了吗?那就是我需要的!经过紧张的试验调试,证明它和WIN32API手册上的Netbios完全一样。剩下的工作就比较简单了,定义一个NCB(Netbios控制块)记录,将NCB数据结构封装在里面;声明一个后处理例程以及消息处理过程,以完成广播数据的接收和发送。有关NCB数据结构的详细内容以及NetBIOS广播的原理,限于篇幅我就省略了。需要的朋友可以查看BC或VC的Help或相关书籍。下面是有关的Delphi源代码。 源代码网推荐 源代码网推荐 /////////Netbios单元/////////// 源代码网推荐 源代码网推荐 unit netbios; 源代码网推荐 源代码网推荐 interface 源代码网推荐 源代码网推荐 源代码网推荐 uses windows,messages,Forms,SysUtils; 源代码网推荐 源代码网推荐 type 源代码网推荐 源代码网推荐 {$X+}{$A+} 源代码网推荐 源代码网推荐 file://声明一个NCB记录指针。 源代码网推荐 源代码网推荐 源代码网推荐 PNCB=^NCB; 源代码网推荐 源代码网推荐 file://声明一个后处理例程的过程类型。 源代码网推荐 源代码网推荐 POST=procedure(var ncbR:PNCB); 源代码网推荐 源代码网推荐 file://以下是NCB记录,教训1:将上面的编译选项置为{$A+}以取消数据对齐。如果在广播中有浮点数的话,数据对齐会让你大吃苦头!我已经有过惨痛教训!:( 源代码网推荐 源代码网推荐 NCB=record 源代码网推荐 源代码网推荐 ncb_command:UCHAR; 源代码网推荐 源代码网推荐 ncb_retcode:UCHAR; 源代码网推荐 源代码网推荐 ncb_lsn:UCHAR; 源代码网推荐 源代码网推荐 ncb_num:UCHAR; 源代码网推荐 源代码网推荐 ncb_buffer:PCHAR; 源代码网推荐 源代码网推荐 ncb_length:WORD; 源代码网推荐 源代码网推荐 ncb_callname:array [1..16] of UCHAR; 源代码网推荐 源代码网推荐 ncb_name:array [1..16] of UCHAR; 源代码网推荐 源代码网推荐 ncb_rto:UCHAR; 源代码网推荐 源代码网推荐 ncb_sto:UCHAR; 源代码网推荐 源代码网推荐 ncb_post:POST; 源代码网推荐 源代码网推荐 ncb_lana_num:UCHAR; 源代码网推荐 源代码网推荐 ncb_cmd_cplt:UCHAR; 源代码网推荐 源代码网推荐 ncb_reserve:array [1..10] of UCHAR; 源代码网推荐 源代码网推荐 ncb_event:HANDLE; 源代码网推荐 源代码网推荐 end; 源代码网推荐 源代码网推荐 file://声明自己的Netbios函数。教训2:一定要使用pascal调用规范,否则,嘿嘿!! 源代码网推荐 源代码网推荐 function NetbiosSR(ncbX:PNCB):UCHAR;pascal; 源代码网推荐 源代码网推荐 file://初始化NCB。 源代码网推荐 源代码网推荐 procedure InitNCB(var ncbY:PNCB); 源代码网推荐 源代码网推荐 file://后处理例程,注意使用远指针。 源代码网推荐 源代码网推荐 procedure postrout(var ncbR:PNCB);stdcall;far; 源代码网推荐 源代码网推荐 var 源代码网推荐 源代码网推荐 char_buffer:array[0..511]of UCHAR; 源代码网推荐 源代码网推荐 int_buffer:array[1..512]of Byte; 源代码网推荐 源代码网推荐 implementation 源代码网推荐 源代码网推荐 file://调用系统的Netbios。dll中的Netbios函数标号是6。Delphi搜索外部文件的顺序是当前目录→系统目录→其他目录,别忘了保证存在Netbios.dll。 源代码网推荐 源代码网推荐 function NetbiosSR(ncbX:PNCB):UCHAR;external 源代码网推荐 源代码网推荐 ‘netbios"" index 6; 源代码网推荐 源代码网推荐 procedure InitNCB(var ncbY:PNCB); 源代码网推荐 源代码网推荐 var 源代码网推荐 源代码网推荐 x:integer; 源代码网推荐 源代码网推荐 begin 源代码网推荐 源代码网推荐 ncbY.ncb_command:=0; 源代码网推荐 源代码网推荐 ncbY.ncb_retcode:=0; 源代码网推荐 源代码网推荐 ncbY.ncb_lsn:=0; 源代码网推荐 源代码网推荐 ncbY.ncb_num:=0; 源代码网推荐 源代码网推荐 源代码网推荐 ncbY.ncb_length:=512; file://数据缓冲长度,最大512B。 源代码网推荐 源代码网推荐 for x:=1 to 16 do 源代码网推荐 源代码网推荐 begin 源代码网推荐 源代码网推荐 ncbY.ncb_callname[x]:=0; 源代码网推荐 源代码网推荐 ncbY.ncb_name[x]:=0; 源代码网推荐 源代码网推荐 end; 源代码网推荐 源代码网推荐 ncbY.ncb_rto:=0; 源代码网推荐 源代码网推荐 ncbY.ncb_sto:=0; 源代码网推荐 源代码网推荐 源代码网推荐 ncbY.ncb_lana_num:=0; 源代码网推荐 源代码网推荐 源代码网推荐 ncbY.ncb_cmd_cplt:=0; 源代码网推荐 源代码网推荐 for x:=1 to 10 do 源代码网推荐 源代码网推荐 ncbY.ncb_reserve[x]:=0; 源代码网推荐 源代码网推荐 ncbY.ncb_event:=0; 源代码网推荐 源代码网推荐 end; 源代码网推荐 源代码网推荐 file://后处理例程的作用是当接收到广播消息时,立即向相应窗口发送消息。我在这里偷了点懒,以广播方式发送一个定时器消息。如果你愿意可以向指定窗口发送自定义消息,这样要复杂一些。 源代码网推荐 源代码网推荐 首先,要把指定窗口的句柄传递给后台处理例程。通常这是做不到的,但可以利用一些技巧做到。在NCB记录后面紧挨着声明一个句柄类型,然后把指定窗口的句柄赋值给它的实例变量;这样句柄变量的地址与NCB是连续的。在后处理中通过指针或汇编语句将ncbR的地址移到最后一个字节+1,就是窗口句柄的起始地址。明白吗?至于自定义消息,需要重新编译连接库,限于篇幅我就不罗嗦了,有兴趣的可以自己尝试。 源代码网推荐 源代码网推荐 procedure postrout(var ncbR:PNCB); 源代码网推荐 源代码网推荐 begin 源代码网推荐 源代码网推荐 sendMessage(wnd_BROADCAST,WM_TIMER,0,0); 源代码网推荐 源代码网推荐 end; 源代码网推荐 源代码网推荐 end. 源代码网推荐 源代码网推荐 ////////窗口单元////////// 源代码网推荐 源代码网推荐 unit broadcast; 源代码网推荐 源代码网推荐 interface 源代码网推荐 源代码网推荐 uses 源代码网推荐 源代码网推荐 Windows,Messages,SysUtils,Classes,Graphics,Controls,Forms,Dialogs,netbios; 源代码网推荐 源代码网推荐 type 源代码网推荐 源代码网推荐 Tmain=class(TForm) 源代码网推荐 源代码网推荐 private 源代码网推荐 源代码网推荐 {Private declarations} 源代码网推荐 源代码网推荐 file://消息处理过程,注意消息宏要与后处理中的一致。 源代码网推荐 源代码网推荐 procedure post_main(var Message:TMessage);message WM_TIMER; 源代码网推荐 源代码网推荐 public 源代码网推荐 源代码网推荐 {Public declarations} 源代码网推荐 源代码网推荐 end; 源代码网推荐 源代码网推荐 var 源代码网推荐 源代码网推荐 main: Tmain; 源代码网推荐 源代码网推荐 ncbname:UCHAR; 源代码网推荐 源代码网推荐 ncbRock:PNCB; 源代码网推荐 源代码网推荐 post_add:POST; 源代码网推荐 源代码网推荐 implementation 源代码网推荐 源代码网推荐 {$R *.DFM}{$A-}{$I-} 源代码网推荐 源代码网推荐 /////////主窗口建立过程///////// 源代码网推荐 源代码网推荐 procedure Tmain.FormCreate(Sender: TObject); 源代码网推荐 源代码网推荐 var 源代码网推荐 源代码网推荐 ret:UCHAR; 源代码网推荐 源代码网推荐 i,x,y:integer; 源代码网推荐 源代码网推荐 p:single; 源代码网推荐 源代码网推荐 begin 源代码网推荐 源代码网推荐 new(ncbRock); 源代码网推荐 源代码网推荐 randomize();i:=0; 源代码网推荐 源代码网推荐 FillChar(char_buffer,sizeof(char_buffer),0); 源代码网推荐 源代码网推荐 post_add:=@postrout; 源代码网推荐 源代码网推荐 file://取后处理例程的地址。 源代码网推荐 源代码网推荐 ncbRock.ncb_buffer:=@char_buffer; file://取数据缓冲区的地址。 源代码网推荐 源代码网推荐 InitNCB(ncbRock); 源代码网推荐 源代码网推荐 ret:=9; 源代码网推荐 源代码网推荐 ncbname:=random(100); 源代码网推荐 源代码网推荐 ncbRock.ncb_name[1]:=ncbname; 源代码网推荐 源代码网推荐 ncbRock.ncb_command:=$30; 源代码网推荐 源代码网推荐 file://加名,ret为0加名成功。 源代码网推荐 源代码网推荐 while ((i<10)and(ret<>0)) do 源代码网推荐 源代码网推荐 begin 源代码网推荐 源代码网推荐 ret:=netbiosSR(ncbRock); 源代码网推荐 源代码网推荐 i:=i+1; 源代码网推荐 源代码网推荐 end; 源代码网推荐 源代码网推荐 if ret<>0 then 源代码网推荐 源代码网推荐 begin 源代码网推荐 源代码网推荐 for i:=1 to 20 do 源代码网推荐 源代码网推荐 messagebeep(-1); 源代码网推荐 源代码网推荐 MessageDlg(‘网络通信无法实现!您需要关闭程序重新运行."",mtWarning, 源代码网推荐 源代码网推荐 [mbOk],0); 源代码网推荐 源代码网推荐 end 源代码网推荐 源代码网推荐 else if ret=0 then 源代码网推荐 源代码网推荐 begin 源代码网推荐 源代码网推荐 ncbRock.ncb_post:=post_add; 源代码网推荐 源代码网推荐 ncbRock.ncb_command:=$a3; file://异步接收方式字。 源代码网推荐 源代码网推荐 ncbRock.ncb_event:=0; 源代码网推荐 源代码网推荐 ncbRock.ncb_length:=512; 源代码网推荐 源代码网推荐 ret:=netbiosSR(ncbRock); 源代码网推荐 源代码网推荐 end; 源代码网推荐 源代码网供稿. |
