2
votes

I have a MFC application where it tries to get a page using cURL. When running the app, the dialog becomes unresponsive for a moment. Is there any way to fix this?

static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp)
{
    ((std::string*)userp)->append((char*)contents, size * nmemb);
    return size * nmemb;
}
    
static std::string GetPage(const char* url)
{
    CURL *curl;
    CURLcode res;
    std::string page;
    curl = curl_easy_init();
    if (curl) {
        curl_easy_setopt(curl, CURLOPT_URL, url);
        curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &page);

        res = curl_easy_perform(curl);

        curl_easy_cleanup(curl);
    }
    return page;
}

InitInstance():

    CWinApp::InitInstance();
    CEpicDlg dlg;
    m_pMainWnd = &dlg;
    dlg.Create(IDD_MAIN);
    dlg.ShowWindow(SW_SHOW);
    GetPage("http://example.com/page");
    dlg.RunModalLoop(MLF_NOIDLEMSG);
1
Call GetPage("http://example.com/page"); on a worker thread, then post its results to show them in dialog on GUI thread. - marcinj
@marcinj WaitForSingleObject is now freezing the dialog. I need WaitForSingleObject since I need to wait for it to get the page and then continue. - I suck at everything

1 Answers

4
votes

As noted earlier, you have to run the function in a separate worker thread.

CreateThread + WaitForSingleObject may behave like a blocking function, depending on how it is implemented...

One alternative is to run the worker thread and continue in GUI thread. Once the worker thread is finished, use synchronization objects to let the GUI thread know about the status. Or use PostMessage to notify the GUI thread that the worker thread is finished. Make sure the url string data is not shared between the two threads simultaneously without synchronization...

Note, use CDialog::DoModal and initialize data in OnInitDialog. You may also want to override OnCancel to properly cleanup before exiting the window.

#define WM_USER1 (WM_USER + 1)
DWORD WINAPI thread_proc(LPVOID ptr)
{
    //simulate a blocking function (don't put Sleep in final code)
    Sleep(2000);
    HWND hwnd = (HWND)ptr;
    if(IsWindow(hwnd)) PostMessage(hwnd, WM_USER1, 0, 0);
    return 0;
}

class CMyDialog : public CDialog
{
public:
    CMyDialog(int id, CWnd* wnd) : CDialog(id, wnd) {}
    BOOL OnInitDialog()
    {
        CDialog::OnInitDialog();
        CreateThread(nullptr, 0, thread_proc, (LPVOID)m_hWnd, 0, nullptr);
        return TRUE;
    }
    LRESULT on_wm_user1(WPARAM, LPARAM)
    {
        MessageBox(L"finished");
        return 0;
    }
    DECLARE_MESSAGE_MAP()
};

BEGIN_MESSAGE_MAP(CMyDialog, CDialog)
    ON_MESSAGE(WM_USER1, on_wm_user1)
END_MESSAGE_MAP()