添加列表控件元素时MFC应用程序卡住[关闭]

Posted

技术标签:

【中文标题】添加列表控件元素时MFC应用程序卡住[关闭]【英文标题】:MFC Application getting stuck when adding list control elements [closed] 【发布时间】:2015-10-29 10:38:37 【问题描述】:

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 任务。 【参考方案1】:

    首先,您绝对应该将基于套接字的数据包 侦听器和解析器进入一个线程。主线程专用于 仅限 GUI 任务。

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

    了解更多信息:https://msdn.microsoft.com/en-us/library/ye4z8x58.aspx

以下是如何使用它的简短示例:

您需要设置列表控件中的项目数。即使我们没有添加任何内容,该列表仍认为它具有此数量的项目:

m_DataArray 定义为CArray&lt;CDataItemInfo&gt; 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);
    


【讨论】:

我是新人所以我真的不知道如何实现虚拟列表控件 阅读 MSDN 以了解如何执行此操作。此外,在 codeproject.com 上有很多关于该主题的文章。 使用线程应用程序后,它实际上并没有正确显示详细信息,列表控件正在闪烁。 你不应该直接从线程更新你的CListCtrl。线程应该将解析后的数据放入某种std::list&lt;CPacketInfo&gt; 容器数据结构中,并通知主线程(可能通过PostMessage)数据可用于可视化。请注意,必须保护数据容器(使用临界区或类似机制)。 能否请您提供类似的问题解决链接或示例链接来解决此问题

以上是关于添加列表控件元素时MFC应用程序卡住[关闭]的主要内容,如果未能解决你的问题,请参考以下文章

求教:关于MFC控件中的滚动条事件........

猎豹MFC--列表控件ListControl

如何将复选框控件添加到 mfc 中的列表控件子项

MFC_2.6 使用菜单列表和控件

怎么读取列表控件中的数据

MFC 列表控件滚动显示额外的网格线