0

I am new to C ++ and I am designing an application that requires getting information from the internet, my problem is that if there is no internet connection, or this is slow, my application freezes for seconds. My application also has a hook to the keyboard so when my application freezes, Windows also does.

My application is for Windows only.

This is a simulation of my problem:

std::string instruction;
std::string otherValue;
int timerId;

int main(int argc, char* argv[])
{
    StartKeysListener(); //Keyboard Hook

    timerId = SetTimer(nullptr, 0, 5000, static_cast<TIMERPROC>(TimerCallback));

    MSG msg;
    while (GetMessageA(&msg, nullptr, 0, 0) > 0) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
}

VOID CALLBACK TimerCallback(HWND hwnd, UINT message, UINT idTimer, DWORD dwTime)
{
    DownloadInstructions();
}

std::string DownloadInstructions(std::string url)
{
    std::string response = HttpRequest(url); //HttpRequest is a function that uses Windows SOCKET to send an http request

    if (!response.empty())
    {
        instruction = response.substr(0, 5);
        otherValue = response.substr(6, 15);
    }
}

I have tried calling the "DownloadInstructions" function on a new thread using std::async but I do not know how to return the server response to a callback to update the "instruction" and "otherValue" variables.

How can i solve this problem? I could not find what I'm looking for on Google.

tadman
  • 208,517
  • 23
  • 234
  • 262
Joseph
  • 335
  • 1
  • 3
  • 13
  • Post a custom window message to the UI window. The window message could just put the data into WPARAM and LPARAM or it could pass a pointer in LPARAM.to a struct containing the data For example http://stackoverflow.com/questions/19144112/how-can-worker-threads-communicate-with-main-ui-thread – Jerry Jeremiah Apr 26 '17 at 22:56
  • if your service is RESTful you could use Casablanca. Rolling your own async I/O will be challenging. https://casablanca.codeplex.com/wikipage?title=Http%20Client%20Tutorial – Steve Townsend Apr 26 '17 at 22:58

1 Answers1

2

Given you are using a Win32 style message loop, the usual way to pass information back to the main thread from a worker thread is to send a message. (It doesn't look like your sample code would quite work as written as you need at least a dummy HWND though.)

Custom messages (as opposed to predefined system messages) can be defined as an offset from WM_APP, so you can do something like:

#define WM_INSTRUCTIONS_LOADING (WM_APP + 0)
#define WM_INSTRUCTIONS_READY   (WM_APP + 1)
/* ... */

Then in DownloadInstructions you can notify the main thread that the download is complete by using the PostMessage API (which sends an asynchronous message) where the HWND parameter is passed in to the thread from your main loop:

PostMessage(WM_INSTRUCTIONS_READY, hwndMain, 0, 0);

Now it doesn't look like DownloadInstructions is actually running in another thread. You're using the SetTimer API which will just send a WM_TIMER message to your event loop at some point in the future. The message will still be processed in your event loop, and thus on the main thread.

You really do need to spawn a separate thread if you want it to be truly asynchronous. The mechanism for using std::async and is quite different to using CreateThread. The std::async call will spawn a thread and return you a std::future which you need to join on to get the result. This is probably not a great match for your current design. If you just spawn DownloadInstructions as a separate thread and pass it some context (at least the HWND to post back to) via the void* argument pointer, that should be sufficient to do what you describe.

Now having said all that, are you sure you want to write a C++ app using the rather old-fashioned and low-level C Win32 API? There are numerous libraries and frameworks for C++ which provide a much higher level API to do all these sorts of things more easily.

gavinb
  • 19,278
  • 3
  • 45
  • 60
  • _User messages (as opposed to predefined system messages) can be defined as an offset from WM_USER_ -- that should be [`WM_APP`](https://msdn.microsoft.com/en-us/library/windows/desktop/ms644930(v=vs.85).aspx) because predefined controls may use messages in the WM_USER range. – zett42 Apr 27 '17 at 12:43
  • True, but that is only an issue if you are subclassing an existing window. For a window class you register yourself, there's no overlap. Anyway, changed post accordingly. (Relevant http://stackoverflow.com/questions/30843497/wm-user-vs-wm-app) – gavinb Apr 27 '17 at 13:34