0

I'm writing a win32 form application and drawing it with Direct2D. I have a few cross threaded functions to do animations on it and I'm doing web requests with WinHTTP. The issue is, when I use any WinHttp functions (even just opening an HINTERNET session), it will cause the thread not to terminate properly. After I run the 'login' process once, the program cannot exit calmly. I've posted the relevant code below:

//the login process
void __cdecl processloginasync(void* arg)
{
    //getting text from textboxes, etc.

    if(usernamestr.find(L'@') != wstring::npos && usernamestr.find(L".") != wstring::npos) {
        swapdrawmode(1);
        _beginthread(loadwheel,NULL,arg);

        void* result = NULL;
        unsigned sz = 0;
        int rescode = web_request(L"appurl.mywebsite.com/func.php",ss.str().c_str(),result,sz);
        //other code to handle the reply...
        swapdrawmode(0);
    }
    else {
        error_str = L"Invalid email address.";
        err = TRUE;
    }

    if(err == TRUE) {
        textopacity = 0;
        animatemode = 0;
        _beginthread(animatetext,NULL,arg);
    }
    //I realize I haven't called 'free' on result, I'll fix that.
}

//the web_request function
int web_request (const wchar_t* server, const wchar_t* object, void*& dest, unsigned& size)
{
    vector<void*> retval;
    vector<unsigned> szs;
    HINTERNET hSess = NULL, hConn = NULL, hReq = NULL;
    int res = 0;
    DWORD dwDownloaded = 0;
    DWORD dwSize = 0;
    DWORD retcode = NULL;
    short err = FALSE;

    const wchar_t* accepted_types[] = {
        L"image/*",
        L"text/*",
        NULL
    };

    hSess = WinHttpOpen(L"smartCARS2 Web/1.1",WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
    if(hSess)
        hConn = WinHttpConnect(hSess,server,INTERNET_DEFAULT_HTTP_PORT, NULL);
    else {
        err = TRUE;
        retcode = HTTP_OPEN_FAILED;
    }
    if(hConn)
        hReq = WinHttpOpenRequest(hConn, NULL, object, NULL, WINHTTP_NO_REFERER,accepted_types,NULL);
    else {
        err = TRUE;
        retcode = HTTP_CONN_FAILED;
    }
    if(hReq)
        res = WinHttpSendRequest(hReq, WINHTTP_NO_ADDITIONAL_HEADERS, NULL, WINHTTP_NO_REQUEST_DATA, NULL, NULL, NULL);
    else {
        err = TRUE;
        retcode = HTTP_OPENREQ_FAILED;
    }
    if(res)
        res = WinHttpReceiveResponse(hReq, NULL);
    else {
        err = TRUE;
        retcode = HTTP_SEND_REQ_FAILED;
    }

    DWORD tsize = 0;
    if(res) {
        do {
            dwSize = 0;
            if(!WinHttpQueryDataAvailable(hReq, &dwSize)) {
                retcode = HTTP_COULD_NOT_QUERY_SIZE;
                err = TRUE;
                break;
            }
            if(!dwSize)
                break;
            tsize += dwSize;
            void* rets = malloc(dwSize + 1);
            if(!rets) {
                break;
            }
            if(!WinHttpReadData(hReq, (void*)rets, dwSize,  &dwDownloaded)) {
                retcode = HTTP_COULD_NOT_READ_DATA;
                err = TRUE;
                break;
            }
            if(!dwDownloaded)  {
                retcode =  HTTP_COULD_NOT_DOWNLOAD;
                err = TRUE;
                break;
            }
            szs.push_back(dwSize);
            retval.push_back(rets);

        } while(dwSize > 0);
    }
    size = tsize;
    unsigned int sz = retval.size();
    dest = malloc(tsize);
    tsize = 0;
    for(unsigned i = 0; i < sz; i++) {
        memcpy((BYTE*)dest + tsize,retval[i],szs[i]);
        free(retval[i]);
        tsize += szs[i];
    }
    if(hSess)
        WinHttpCloseHandle(hSess);
    if(hConn)
        WinHttpCloseHandle(hConn);
    if(hReq)
        WinHttpCloseHandle(hReq);
    if(err == TRUE)
        return retcode;
    return 0;
}
WhozCraig
  • 65,258
  • 11
  • 75
  • 141
  • Following your code is a bit difficult. Can you define what you mean by terminating calmly? – Mike Weir Aug 19 '13 at 14:13
  • I apologize for that, I posted a lot because I'm only partially sure where the issue resides. I mean that if you close the program (by clicking the X), despite the threads having seemed to end (they do not trigger breakpoints in their proc), the process continues running. – Collin Biedenkapp Aug 19 '13 at 14:17
  • You may also consider whether `animatetext` and `loadwheel` are properly being shutdown as well. None of these threads seem to be waited on for proper completion. I'd consider using `_beginthreadex()` to get a valid wait-able handle and `WaitForSingleObject()` or `WaitForMultipleObjects` in the appropriate places in you code. – WhozCraig Aug 19 '13 at 14:24
  • Like @Codeguard answers, find out what is preventing the exit of your main thread - the one with the main window that receives the WM_CLOSE message. – Martin James Aug 19 '13 at 15:50
  • The WndProc receives WM_CLOSE and posts the exit message. The 'main' function also returns 0, although the zombie process continues. – Collin Biedenkapp Aug 19 '13 at 16:30
  • OK, what's left.. static dtors? – Martin James Aug 19 '13 at 17:54

1 Answers1

0

As far as I know, as soon as the main thread terminates, the others are not waited for. So the problem is probably in your main thread. You just need to attach a debugger if not already being debugged (Debug | Attach to process in VS) to a zombie process and press "Break all", then use "Threads" and "Call stack" windows to figure what's happening.

Codeguard
  • 7,787
  • 2
  • 38
  • 41
  • This makes sense, however, I tested without creating any threads at all and the issue persisted, only caused by a call to any WinHTTP functions. The Break All does show several threads, but none of them are directly user created. – Collin Biedenkapp Aug 19 '13 at 16:34
  • I've found that calling 'exit(0);' at the end of main causes it to terminate, but I question whether or not that's good practice. – Collin Biedenkapp Aug 19 '13 at 16:51
  • From what you say, I think you have a problem in one of global destructors. Please copy the stack of the main thread (you can for example find it by ID, remembering it at start) in the zombie process. – Codeguard Aug 20 '13 at 07:13
  • The main thread returns 0 and ends, as do the subsequent animation threads. There are no user-created running, they are all something like '_NtWaitForWorkViaWorkerFactory@16' and only exist once a WinHTTP call is made (my dtors are fine). – Collin Biedenkapp Aug 20 '13 at 13:53
  • I think you should try to reduce your application as much as you can, while the problem still exists. – Codeguard Aug 20 '13 at 17:11