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 个数据包是一个很大的缺点。

  1. 首先你一定要把你的基于套接字的数据包 侦听器和解析器合并到一个线程中。主线程专用于 仅限 GUI 任务。

  2. 您需要为列表控件启用虚拟模式。 在这种模式下,列表视图控件本身不承载任何数据。 它只知道当前应该显示的行。请求实际数据 要求。这使您的应用程序负责管理 它显示的数据。

  3. 了解更多信息: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);
    }

}