一步一步开发sniffer(Winpcap+MFC)(六)千呼万唤始出来,不抱琵琶也露面——将解析数据写到GUI上

最后一章是要将解析数据写的GUI上,先来回顾一下GUI长什么样,这样就对要在界面上写什么数据心中有数了,如下这两图:

 


可以看出,要写在GUI上的数据主要有五个部分:

1、  参数设置:网卡接口、过滤项

2、  数据包捕获列表,显示数据包简要信息

3、  树形目录,显示被选中的数据包头详细信息

4、  文本框,显示被选中的数据包十六进制信息

5、  统计信息:各类包的数量

限于篇幅接下来不会对所有信息是怎么放、每个控件怎么操作一一讲解了。首先会简单讲解一下是如何将数据放到界面上的;同时再对一些要点:第2点信息中如何根据数据包类型显示不同颜色背景,第4点信息中如何将数据包内容以格式化的方式显示出来做一下讲解。

一、控件写数据的基本操作

由于我们选择的是对话框的形式的界面,所以主界面只有一个,放置在主界面上的各个控件都可以通过主界面的this指针调用,并设置控件的值,例如:

          this->m_listCtrl.SetItemText(nItem,2,buf);

其中m_listCtrl是放置于主界面上的一个列表控件,至于参数是什么意思查MSDN吧。各种控件上的数据也就基本通过这样的方式调用

 

程序中我们新开了一个线程来处理数据,线程中每收到一个数据包都需要更新一下界面,这样就可以实时看到捕获的数据及统计信息了,这需要我们把主界面的this指针传递给线程,如下:

         m_ThreadHandle=CreateThread(NULL,0,lixsinff_CapThread,this,0,threadCap);

线程处理函数原型如下:

         DWORD WINAPI lixsinff_CapThread(LPVOID lpParameter);

这里的lpParameter就是刚刚传递进来的this指针了,在函数中使用如下:

         Cmcf6Dlg*pthis = (Cmcf6Dlg*) lpParameter;

其他的使用就跟前面一样了。

        

有时候需要根据事件来显示数据,那么控件的事件怎么添加呢,一个简单的办法就是打开资源视图,选择某一控件,再点击“闪电”形的按钮,就可以看到IDE已经早就为控件设置好各种事件了,你只需要选择某一事件,并增加自定义函数就行了。如下图

                  

二、两个要点

下面两个要点是我在显示数据的时候遇到的实际困难,因为其他的数据都是直接将数据写到控件里就完事了,而这里则要先做一些预处理,将这个贴出来节省大家时间。

2.1根据不同协议显示不同颜色

从下面这幅图可以看到,List控件有一个事件是NM_CUSTOMDRAW,每次有新的一行加入的时候,都触发该事件,然后调用相关的处理函数进行自定义绘制,可以注册一个该事件,代码见下         

   

   
voidCmcf6Dlg::OnNMCustomdrawList1(NMHDR *pNMHDR, LRESULT *pResult)
{
         //LPNMCUSTOMDRAWpNMCD = reinterpret_cast(pNMHDR);
         LPNMLVCUSTOMDRAWpNMCD = (LPNMLVCUSTOMDRAW)pNMHDR;
         *pResult= 0;
         //TODO: 在此添加控件通知处理程序代码
         if(CDDS_PREPAINT==pNMCD->nmcd.dwDrawStage)
         {
                   *pResult= CDRF_NOTIFYITEMDRAW;
         }elseif(CDDS_ITEMPREPAINT ==pNMCD->nmcd.dwDrawStage){
                   COLORREFcrText;
                   charbuf[10];
                   memset(buf,0,10);
                   POSITION pos =this->m_localDataList.FindIndex(pNMCD->nmcd.dwItemSpec);
                   struct datapkt * local_data =(struct datapkt *)this->m_localDataList.GetAt(pos);
                   strcpy(buf,local_data->pktType);
 
                   if(strcmp(buf,"IPV6")==0)
                            crText= RGB(111,224,254);
                   elseif(strcmp(buf,"UDP")==0)
                            crText= RGB(194,195,252);                               
                   elseif(strcmp(buf,"TCP")==0)
                                     crText= RGB(230,230,230);
                   elseif(strcmp(buf,"ARP")==0)
                                     crText= RGB(226,238,227);
                   elseif(strcmp(buf,"ICMP")==0)
                                     crText= RGB(49,164,238);
                   elseif(strcmp(buf,"HTTP")==0)
                                     crText= RGB(238,232,180);
                   elseif(strcmp(buf,"ICMPv6")==0)
                                     crText= RGB(189,254,76);
 
                   pNMCD->clrTextBk=crText;
                   *pResult= CDRF_DODEFAULT;
         }
}

         首先通过下面这段代码获得新加入到List列表中的数据位置

                    POSITION pos = this->m_localDataList.FindIndex(pNMCD->nmcd.dwItemSpec);

         然后通过下面代码获得新加入行中存储的数据

                   structdatapkt * local_data = (struct datapkt *)this->m_localDataList.GetAt(pos);

         最后根据数据中对应的协议设置不同的显示颜色。这样,一个界面友好的列表就设置好了。 

 2.2对数据格式化显示

          主要通过下面这个函数实现,下面这个函数主要做了两件事:1、将数据是16进制的形式显示;2、将数据以字符形式显示。代码相信大家都看得懂,就不做过多解释了。

void print_packet_hex(const u_char* pkt,intsize_pkt,CString *buf)
{
         inti=0,j = 0,rowcount;
          u_char ch;
 
          char tempbuf[256];
          memset(tempbuf,0,256);
 
         for(i= 0;iAppendFormat(_T("%04x:  "),(u_int)i);
                   rowcount= (size_pkt-i) > 16 ? 16 : (size_pkt-i);                         
 
                   for(j = 0; j < rowcount; j++)              
                            buf->AppendFormat(_T("%02x  "),(u_int)pkt[i+j]);       
 
                   //不足16,用空格补足
                   if(rowcount<16)
                            for(j=rowcount;j<16;j++)
                                               buf->AppendFormat(_T("    "));  
 
 
                   for(j = 0; j < rowcount; j++)
                   {
            ch = pkt[i+j];
            ch = isprint(ch) ? ch : '.';
                             buf->AppendFormat(_T("%c"),ch);
                   }
 
                   buf->Append(_T("\r\n"));
                   if(rowcount<16)
                            return;
         }
}


至此,如何应用MFC写一个sniffer就完整地结束了,未讲清楚的,可直接参考代码,这里可进行代码下载

http://download.csdn.net/download/litingli/4110529





你可能感兴趣的:(一步一步开发sniffer(Winpcap+MFC)(六)千呼万唤始出来,不抱琵琶也露面——将解析数据写到GUI上)