MFC 应用程序在添加列表控件元素时卡住
MFC Application getting stuck when adding list control elements
MFC 应用程序在向列表控件中添加元素时卡住,无法单击任何其他按钮,甚至无法在嗅探网络数据包时关闭按钮。
整个代码如下图:
// 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();
}
即使我使用线程,它也无法正常工作。
数据包嗅探实际上是一个持续的过程,直到按下停止按钮。但是在这个程序中,当嗅探完成后,无法按下其他按钮,应用程序就会卡住。当我将嗅探限制为 20 或 50 时,那么在那么多数据包之后我们只能做任何其他事情。但是将应用程序限制在 20 或 50 个数据包是一个很大的缺点。
首先你一定要把你的基于套接字的数据包
侦听器和解析器合并到一个线程中。主线程专用于
仅限 GUI 任务。
您需要为列表控件启用虚拟模式。
在这种模式下,列表视图控件本身不承载任何数据。
它只知道当前应该显示的行。请求实际数据
要求。这使您的应用程序负责管理
它显示的数据。
- 了解更多信息:https://msdn.microsoft.com/en-us/library/ye4z8x58.aspx
以下是有关如何使用它的简短示例:
您需要设置列表控件中的项数。即使我们没有添加任何东西,列表也认为它有这个数量的项目:
m_DataArray
定义为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);
}
}
MFC 应用程序在向列表控件中添加元素时卡住,无法单击任何其他按钮,甚至无法在嗅探网络数据包时关闭按钮。
整个代码如下图:
// 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();
}
即使我使用线程,它也无法正常工作。
数据包嗅探实际上是一个持续的过程,直到按下停止按钮。但是在这个程序中,当嗅探完成后,无法按下其他按钮,应用程序就会卡住。当我将嗅探限制为 20 或 50 时,那么在那么多数据包之后我们只能做任何其他事情。但是将应用程序限制在 20 或 50 个数据包是一个很大的缺点。
首先你一定要把你的基于套接字的数据包 侦听器和解析器合并到一个线程中。主线程专用于 仅限 GUI 任务。
您需要为列表控件启用虚拟模式。 在这种模式下,列表视图控件本身不承载任何数据。 它只知道当前应该显示的行。请求实际数据 要求。这使您的应用程序负责管理 它显示的数据。
- 了解更多信息:https://msdn.microsoft.com/en-us/library/ye4z8x58.aspx
以下是有关如何使用它的简短示例:
您需要设置列表控件中的项数。即使我们没有添加任何东西,列表也认为它有这个数量的项目:
m_DataArray
定义为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);
}
}