VC++ 2022 Unaccountable Abort() 阻塞 UI,而假设中止的工作线程继续无限期正常工作 (!?)

问题描述 投票:0回答:1

我不太擅长C++编程,但我真的无法解决这个问题。 我的项目如下: 通过TCP协议连接WIFI服务器。服务器不断发送文本行:没问题。它系统地连接起来。专用线程循环接收文本并将其显示在编辑窗口中。当我单击“断开连接”按钮时,它应该终止。但我无法单击该按钮,因为我收到一条消息(取消、重试、忽略),告诉我 Abort() 已被调用。同时,消息仍然显示,并行调试窗口向我显示线程正在轻松地工作。

这是我的 WndProc 的代码:


LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_ACTIVATEAPP:
            {   Create_incoming_NMEA_messages_box();
                Create_info_message_box();
                Create_push_button();
            }

    case WM_COMMAND:
        {
            int wmId = LOWORD(wParam);
            
            switch (wmId)
            {
            case IDM_ABOUT:
                DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
                break;
            case IDM_EXIT:
                DestroyWindow(hWnd);
                break;

            case IDC_MAIN_BUTTON:     //   connect - disconnect
            {
                if (!Connected)
                {
                    if (Connect_addr(szServer, szPort) == 0)   //  == 0  => connected 
                    {
                        Connected = true;
                        SendMessage(hEditOut, WM_SETTEXT, NULL, (LPARAM)"Connected at Host =        10.0.0.1 / Port = 10111");
                        Button_SetText(hWndButton, "Disconnect");


                        thread TCPThread(Loop_while_connected);       //  thread constructed and    launched                                                                    

                    }
                }
                else {
                    Connected = false;
                    SendMessage(hEditOut, WM_SETTEXT, NULL, (LPARAM)"Disconnected");
                    //TCPThread.join();
                    Button_SetText(hWndButton, "Connect");
                }

            }
            break;
            default:
                return DefWindowProc(hWnd, message, wParam, lParam);
            }
        }
        break;
    case WM_PAINT:
        {

            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
           
            EndPaint(hWnd, &ps);
        }
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

准确来说,Connect_addr(szServer, szPort) 函数如下:

int Connect_addr(char* host, char* port)
{

    // Set up Winsock   INITIALIZATION 
    WSADATA WsaDat;
    int nResult = WSAStartup(MAKEWORD(2, 2), &WsaDat);
    if (nResult != 0)
    {
        MessageBox(hWnd, "Winsock initialization failed", "Critical Error", MB_ICONERROR);
        SendMessage(hWnd, WM_DESTROY, NULL, NULL);

    }
    struct addrinfo  hints;                 // defines address type  
    struct addrinfo* result, * rp;

    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_INET;    // Allow IPv4 or IPv6 
    hints.ai_socktype = SOCK_STREAM; // TCP socket 
    hints.ai_flags = 0;
    hints.ai_protocol = IPPROTO_TCP;


    int s = getaddrinfo(host, port, &hints, &result);   


    // getaddrinfo() returns a list of address structures.
    //  Try each address until we successfully connect(x).      x  =  socket ID 
    //  If socket(x) (or connect(x)) fails, we (close the socket
    //  and) try the next address. 

    int resconn = -1;  // result of the connection attempt

    for (rp = result; rp != NULL; rp = rp->ai_next)     // rp = pointer to the chained list of all IP addresses managed by the client machine
    {
        ConnectSocket = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
        if (ConnectSocket == -1)
            continue;

        resconn = connect(ConnectSocket, rp->ai_addr, rp->ai_addrlen);
        if (resconn == -1)
        {
            resconn = WSAGetLastError();
            MessageBox(0, "Non connect\u00E9", "Erreur connexion", MB_ABORTRETRYIGNORE);
        }
        else    break;                  // Success 

    }
    freeaddrinfo(result);           // No longer needed 
    return resconn;
}

这个功能很好用。问题似乎出在 WindProc 代码中的线程 TCPThread(Loop_while_connected)(案例 IDC_MAIN_BUTTON)。

这里的线程函数是Loop_while_connected:

const unsigned HISTORY_BUFF_SIZE = 1000000;
char szHistory[HISTORY_BUFF_SIZE] = { 0 };  //  NMEA messages accumulating buffer
int nKr = 0; //  sums the received Kr to reset szHistory to start before saturating the buffer
const unsigned int Buff_Size = 10240;

void Loop_while_connected()
{  
    int inDataLength = 0;

    if (Connected)
        do
        {
            char szIncoming[Buff_Size];
            ZeroMemory(szIncoming, Buff_Size);
            inDataLength = 0;

            inDataLength = recv(ConnectSocket, szIncoming, Buff_Size, 0);


            if (inDataLength > 0)

            {
                strncat_s(szHistory, _countof(szHistory), szIncoming, inDataLength);
                nKr += inDataLength;                

                SendMessage(hEditIn, WM_SETTEXT, sizeof(szIncoming) - 1, reinterpret_cast<LPARAM>(szHistory));
                PostMessage(hEditIn, EM_SETSEL, 0, -1);     // Select all
                PostMessage(hEditIn, EM_SETSEL, -1, -1);    // Unselect and stay at the end pos
                PostMessage(hEditIn, EM_SCROLLCARET, 0, 0); // Set scrollcaret to the current Pos
                
            }

            if (nKr >= HISTORY_BUFF_SIZE - Buff_Size) {
                ZeroMemory(szHistory, sizeof(szHistory));
                nKr = 0;                
            }
        
        } while (Connected);
}

事情是这样开始的:

enter image description here

当我单击“连接”时,它会连接,但会在文本始终滚动时发出类似这样的错误消息:

enter image description here

现在,如果我按指示点击“重试调试”,线程实际上会停止,并且调试窗口会在销毁过程结束时显示错误。

enter image description here

我肯定在某个地方做了一些愚蠢的事情。我已经检查过我的线程在启动后就可以加入,但我仍然无法理解为什么我在断开连接时在 WndProc 过程中对 TCPThread.join() 的调用被编译器拒绝。也许两个问题都是相关的? 提前感谢您的帮助。

multithreading debugging asynchronous abort launching
1个回答
0
投票

事实上,那里没有什么愚蠢的事情。 Microsoft VC++ Win32 似乎不符合 C++ 11 多线程(构造函数/析构函数)。 像这样的线

    thread TCPThread(Loop_while_connected);

编译没有问题,但最终在运行时出现错误。

解决方案是像这样使用_beginthread来代替

(HANDLE)_beginthread(Loop_while_connected, 0,NULL);

假设函数 Loop_while_connected 通过将指向线程 ID 的指针作为其标头中的参数传递来与对 _beginthread 的调用保持一致,如下所示:

void Loop_while_connected(void* pThrID)

{ }

© www.soinside.com 2019 - 2024. All rights reserved.