问题
MFC application getting stuck when adding elements into list control unable to click anyother button or even close button when sniffing network packets.
The Whole code is as shown below:
// SnifferSampleDlg.cpp : implementation file
//
#include "stdafx.h"
#include "SnifferSample.h"
#include "SnifferSampleDlg.h"
#include "afxdialogex.h"
#include <Windows.h>
#include <WinSock2.h>
#include <mstcpip.h>
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
#pragma comment(lib,"ws2_32.lib") //For winsock
// CSnifferSampleDlg dialog
//typedef struct ip_hdr
//{
// unsigned char ip_header_len:4; // 4-bit header length (in 32-bit words) normally=5 (Means 20 Bytes`enter code here` may be 24 also)
// unsigned char ip_version :4; // 4-bit IPv4 version
// unsigned char ip_tos; // IP type of service
// unsigned short ip_total_length; // Total length
// unsigned short ip_id; // Unique identifier
//
// unsigned char ip_frag_offset :5; // Fragment offset field
//
// unsigned char ip_more_fragment :1;
// unsigned char ip_dont_fragment :1;
// unsigned char ip_reserved_zero :1;
//
// unsigned char ip_frag_offset1; //fragment offset
//
// unsigned char ip_ttl; // Time to live
// unsigned char ip_protocol; // Protocol(TCP,UDP etc)
// unsigned short ip_checksum; // IP checksum
// unsigned int ip_srcaddr; // Source address
// unsigned int ip_destaddr; //destination address
//} IPV4_HDR;
typedef struct tcp_header
{
unsigned short source_port; // source port
unsigned short dest_port; // destination port
unsigned int sequence; // sequence number - 32 bits
unsigned int acknowledge; // acknowledgement number - 32 bits
unsigned char ns :1; //Nonce Sum Flag Added in RFC 3540.
unsigned char reserved_part1:3; //according to rfc
unsigned char data_offset:4; /*The number of 32-bit words in the TCP header.
This indicates where the data begins.
The length of the TCP header is always a multiple
of 32 bits.*/
unsigned char fin :1; //Finish Flag
unsigned char syn :1; //Synchronise Flag
unsigned char rst :1; //Reset Flag
unsigned char psh :1; //Push Flag
unsigned char ack :1; //Acknowledgement Flag
unsigned char urg :1; //Urgent Flag
unsigned char ecn :1; //ECN-Echo Flag
unsigned char cwr :1; //Congestion Window Reduced Flag
////////////////////////////////
unsigned short window; // window
unsigned short checksum; // checksum
unsigned short urgent_pointer; // urgent pointer
} TCP_HDR;
int total = 0,icmp = 0, igmp = 0,tcp = 0 ,udp = 0 ,others = 0;
CSnifferSampleDlg::CSnifferSampleDlg(CWnd* pParent /*=NULL*/)
: CDialogEx(CSnifferSampleDlg::IDD, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CSnifferSampleDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, IDC_LIST1, m_listCtrl);
}
BEGIN_MESSAGE_MAP(CSnifferSampleDlg, CDialogEx)
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_BUTTON1, &CSnifferSampleDlg::OnBnClickedButton1)
END_MESSAGE_MAP()
// CSnifferSampleDlg message handlers
static void AddData(CListCtrl &ctrl, int row, int col, LPWSTR str)
{
LVITEM lv;
lv.iItem = row;
lv.iSubItem = col;
lv.pszText = str;
lv.mask = LVIF_TEXT;
if(col == 0)
ctrl.InsertItem(0,str);
/*ctrl.RedrawItems( 0, tcp);*/
else
ctrl.SetItem(&lv);
}
BOOL CSnifferSampleDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// TODO: Add extra initialization here
m_listCtrl.InsertColumn(0, L"Source");
m_listCtrl.SetColumnWidth(0, 100);
m_listCtrl.InsertColumn(1,L"Destination");
m_listCtrl.SetColumnWidth(1, 100);
m_listCtrl.InsertColumn(2, L"Protocol");
m_listCtrl.SetColumnWidth(2, 90);
m_listCtrl.SendMessage(LVM_SETEXTENDEDLISTVIEWSTYLE, 0,
LVS_EX_FULLROWSELECT);
return TRUE; // return TRUE unless you set the focus to a control
}
// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.
void CSnifferSampleDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialogEx::OnPaint();
}
}
// The system calls this function to obtain the cursor to display while the user drags
// the minimized window.
HCURSOR CSnifferSampleDlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
int CSnifferSampleDlg::Startbutton()
{
SOCKET ListenSocket;
struct sockaddr_in saServer;
hostent* localHost;
char* localIP;
WSADATA wsaData;
int iResult;
tcp=0;
//Initialising Winsock....
iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
if (iResult != 0)
{
AfxMessageBox(L"WSAStartup() failed");
return 1;
}
//Creating RAW socket for listening
ListenSocket = socket (AF_INET,SOCK_RAW,IPPROTO_IP);
localHost = gethostbyname("");
localIP = inet_ntoa (*(struct in_addr *)*localHost->h_addr_list);
saServer.sin_family = AF_INET;
saServer.sin_addr.s_addr = inet_addr(localIP);
saServer.sin_port = htons(5150);
// Bind the listening socket using the
// information in the sockaddr structure
iResult = bind( ListenSocket,(SOCKADDR*) &saServer, sizeof(saServer) );
if( iResult != 0){
AfxMessageBox(L"Binding the address with the socket failed ");
return 1;
}
AfxMessageBox(L"Binding successful");
int in_buffer=1, out_buffer;
if (WSAIoctl(ListenSocket,SIO_RCVALL, &in_buffer,sizeof(in_buffer), 0, 0,(LPDWORD) &out_buffer , 0 , 0) == SOCKET_ERROR)
{
AfxMessageBox(L"WSAIoctl() failed...");
return 1;
}
AfxMessageBox(L"Socket Set");
CString strText;
int nColumnCount = m_listCtrl.GetHeaderCtrl()->GetItemCount();
StartSniffing(ListenSocket);
closesocket(ListenSocket);
WSACleanup();
return 0;
}
void CSnifferSampleDlg::StartSniffing(SOCKET sniffer){
char *Buffer = (char *)malloc(65536);
int mangobyte;
if (Buffer == NULL)
{
printf("malloc() failed.\n");
return;
}
do
{
mangobyte = recvfrom(sniffer , Buffer , 65536 , 0 , 0 , 0);
if(mangobyte > 0)
{
ProcessPacket(Buffer,mangobyte);
/*if(tcp==50)
break;*/
}
else
{
printf( "recvfrom() failed.\n");
}
}
while (mangobyte > 0);
free(Buffer);
}
void CSnifferSampleDlg::ProcessPacket(char* buffer,int size){
ip.ValueAssign(buffer);
++total;
//Check the Protocol and do accordingly...
switch (ip.ip_protocol)
{
case 1: //ICMP Protocol
++icmp;
break;
case 2: //IGMP Protocol
++igmp;
break;
case 6: //TCP Protocol
++tcp;
ProcessTCPPacket(buffer,size);
/*if(tcp==50)
break;*/
break;
case 17: //UDP Protocol
++udp;
break;
default: //Some Other Protocol like ARP etc.
++others;
break;
}
printf("TCP : %d UDP : %d ICMP : %d IGMP : %d Others : %d Total : %d\r",tcp,udp,icmp,igmp,others,total);
}
void CSnifferSampleDlg::ProcessTCPPacket(char* buffer,int size){
unsigned short iphdrlen;
SOCKADDR_IN source;
char* source_addr;
char* destination_addr;
source.sin_addr.s_addr = ip.ip_srcaddr;
source_addr=inet_ntoa(source.sin_addr);
SOCKADDR_IN destination;
wchar_t source_address[20];
wchar_t destination_address[20];
mbstowcs_s(0,source_address,source_addr,strlen(source_addr)+1); //method used to convert char* to LPWSTR
destination.sin_addr.s_addr = ip.ip_destaddr;
destination_addr=inet_ntoa(destination.sin_addr);
mbstowcs_s(0,destination_address,destination_addr,strlen(destination_addr)+1);
m_listCtrl.InsertColumn(3,L"");
m_listCtrl.SetColumnWidth(3, 80);
m_listCtrl.SetRedraw(FALSE);
AddData(m_listCtrl,0,0,source_address);
/*AddData(m_listCtrl,0,1,destination_address);
AddData(m_listCtrl,0,2,L"TCP");*/
m_listCtrl.SetRedraw(TRUE);
m_listCtrl.DeleteColumn(3);
}
void CSnifferSampleDlg::OnBnClickedButton1()
{
Startbutton();
}
Even when I used threading it is not working fine.
Packet sniffing is actually a continues process until a stop button is pressed. But here in this program when sniffing is done no other buttons are able to be pressed, the application getting stuck. When I restrict the sniffing to say 20 or 50 then after that much packet only we can do anything else. But restricting the application to 20 or 50 packets is a big disadvantage.
回答1:
First of all you should definitely put your socket-based packet listener and parser into a thread. The main thread is dedicated for GUI tasks only.
You need to enable virtual mode for your list control. In this mode, the list view control doesn't host any data itself. All it knows is rows that should be currently displayed. The actual data is requested on demand. That makes your application responsible for managing the data it displays.
- Find out more at: https://msdn.microsoft.com/en-us/library/ye4z8x58.aspx
Here is short example on how to use it:
You need to set the number of items in the list control. The list thinks it has this number of items even though we have not added anything:
The m_DataArray
is defined as CArray<CDataItemInfo> m_DataArray
;
m_DataListCtrl.SetItemCountEx((int)m_DataArray.GetSize(),
LVSICF_NOSCROLL|LVSICF_NOINVALIDATEALL);
...
BEGIN_MESSAGE_MAP(CListCtrlTestDlg, CDialog)
ON_NOTIFY(LVN_GETDISPINFO, IDC_DATA_LIST, OnGetDispInfoList)
END_MESSAGE_MAP()
void CListCtrlTestDlg::OnGetDispInfoList(NMHDR* pNMHDR, LRESULT* pResult)
{
NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
LV_ITEM& LstItem = pDispInfo->item;
int nItem = LstItem.iItem;
if(nItem > m_DataArray.GetSize()-1)
return;
const CDataItemInfo& ItemData = m_DataArray[nItem];
if (LstItem.mask & LVIF_TEXT)
{
//Copy the text to the LV_ITEM structure
//Maximum number of characters is in LstItem.cchTextMax
lstrcpyn(LstItem.pszText, ItemData.GetColumnText(LstItem.iSubItem), LstItem.cchTextMax);
}
}
来源:https://stackoverflow.com/questions/33411941/mfc-application-getting-stuck-when-adding-list-control-elements